02:结构化输出
Chatbot 的自然语言回答很好读,但不好接代码。旅游计划如果要给前端渲染,最好是稳定的结构。
from __future__ import annotations
from pathlib import Path
from typing import Any
from agent_patterns_lab.runtime import Message, MockLLM, SchemaValidationError, Tracer, structured_complete
def main() -> None:
tracer = Tracer()
model = MockLLM(
[
'{"city":"Hangzhou","items":["West Lake","Tea Museum"]}',
'{"city":"Hangzhou","morning":"West Lake","afternoon":"China National Tea Museum","evening":"Hefang Street","packing":["umbrella","light jacket","comfortable shoes"]}',
]
)
messages = [
Message(
role="system",
content=(
"Return ONLY JSON with keys: city, morning, afternoon, evening, packing. "
"packing must be a list of strings."
),
),
Message(role="user", content="Create a one-day Hangzhou itinerary."),
]
def parse_itinerary(value: Any) -> dict[str, Any]:
if not isinstance(value, dict):
raise SchemaValidationError("expected a JSON object")
required = ["city", "morning", "afternoon", "evening", "packing"]
for key in required:
if key not in value:
raise SchemaValidationError(f'missing key "{key}"')
if not isinstance(value["packing"], list) or not all(isinstance(x, str) for x in value["packing"]):
raise SchemaValidationError('"packing" must be a list of strings')
return value
itinerary = structured_complete(
model,
messages,
parser=parse_itinerary,
schema_hint='{"city":"...","morning":"...","afternoon":"...","evening":"...","packing":["..."]}',
tracer=tracer,
)
print(itinerary)
trace_path = tracer.export_jsonl(Path(".traces") / "10_structured_output.jsonl")
print(f"[trace] {trace_path}")
if __name__ == "__main__":
main()
运行:
uv run python examples/10_structured_output.py
新问题
我们希望模型输出:
{
"city": "Hangzhou",
"morning": "...",
"afternoon": "...",
"evening": "...",
"packing": ["..."]
}
但模型可能少字段、字段类型不对,或者在 JSON 外面多说几句。
新模式:Structured Output
structured_complete(...) 做三件事:
- 从模型输出里提取 JSON。
- 用
parse_itinerary(...)校验字段。 - 如果失败,把错误反馈给模型,让它重试修复。
这一步之后,旅游助手不只是“会说”,而是能给代码一个稳定对象。
它还是不够
结构化输出只能保证格式,不保证事实。
如果模型说“明天晴天”,它可能只是猜的。旅游规划需要天气、路线、开放时间这些外部信息。
下一步:让代码提供工具。看 03:工具调用。