[{"data":1,"prerenderedAt":46},["ShallowReactive",2],{"article-agent\u002F02-schema-design":3},{"_path":4,"_dir":5,"_draft":6,"_partial":6,"_locale":7,"title":8,"description":9,"date":10,"tags":11,"body":14,"_type":40,"_id":41,"_source":42,"_file":43,"_stem":44,"_extension":45},"\u002Farticles\u002Fagent\u002F02-schema-design","agent",false,"","02-Tool Schema 设计规范：好写法 vs 坏写法对照","通过同一货币换算工具的好坏两份 Schema 对比实验，总结四条 Schema 设计黄金法则：description 写「什么时候用」、参数类型正确、明确 required 字段、用 enum 锁死可选値。","2026-06-13",[12,13],"Agent开发","人工智能",{"type":15,"children":16,"toc":35},"root",[17,25],{"type":18,"tag":19,"props":20,"children":22},"element","h3",{"id":21},"直接看代码注释",[23],{"type":24,"value":21},"text",{"type":18,"tag":26,"props":27,"children":29},"pre",{"code":28},"\"\"\"练习 6：Tool Schema 设计 —— 坏 schema vs 好 schema 对照实验\n\n为什么提前做这个练习？\n    因为练习 2（多工具选择）依赖它。模型选不选对工具、传不传对参数，\n    90% 取决于 schema 写得好不好，而不是模型笨。\n\n本练习用【同一个货币换算工具】，写两份 schema：\n    - BAD_TOOLS ：常见的新手写法（毛病全集）\n    - GOOD_TOOLS：规范写法\n然后把【同样的问题】分别喂给模型，打印它吐出的参数，差异一眼可见。\n\n四条要对比的 schema 设计规范：\n    1) description 写【什么时候用】，不是【做什么】   ← 黄金规则，最重要\n    2) 参数用对类型（金额是 number，不是 string）\n    3) 明确列出 required 必需字段\n    4) 用 enum 锁死可选值（货币代码只能是固定几种）\n\"\"\"\n\nimport json\nimport os\n\nfrom dotenv import load_dotenv\nfrom openai import OpenAI\n\nload_dotenv()\n\nclient = OpenAI(\n    api_key=os.environ[\"DEEPSEEK_API_KEY\"],\n    base_url=os.environ[\"DEEPSEEK_BASE_URL\"],\n)\nMODEL = os.environ[\"MODEL\"]\n\n\n# ── 坏 schema：把新手常犯的错全踩一遍 ──────────────────────────────────\nBAD_TOOLS = [\n    {\n        \"type\": \"function\",\n        \"function\": {\n            \"name\": \"convert\",\n            # 毛病①：description 只说“做什么”，没说“什么时候用”。\n            #         模型据此很难判断一个问题到底该不该调它。\n            \"description\": \"货币转换\",\n            \"parameters\": {\n                \"type\": \"object\",\n                \"properties\": {\n                    # 毛病②：金额本该是数字，这里写成 string，模型可能回 \"100\" 甚至 \"一百\"。\n                    \"amount\": {\"type\": \"string\"},\n                    # 毛病③：没有 enum、没有 description，\n                    #         模型会随手填“美元\u002F人民币\u002FUSD\u002Fdollar”各种花样，下游没法用。\n                    \"from\": {\"type\": \"string\"},\n                    \"to\": {\"type\": \"string\"},\n                },\n                # 毛病④：没有 required，模型可能漏传参数。\n            },\n        },\n    }\n]\n\n\n# ── 好 schema：四条规范全部做到 ───────────────────────────────────────\nGOOD_TOOLS = [\n    {\n        \"type\": \"function\",\n        \"function\": {\n            \"name\": \"convert_currency\",\n            # 规范①（黄金规则）：description 说清【什么时候用】+【边界】，\n            #   让模型自己就能判断该不该调、什么情况不该调。\n            \"description\": (\n                \"当用户想把一笔【具体金额】从一种货币换算成另一种货币时调用，\"\n                \"例如“100美元是多少人民币”。\"\n                \"仅用于金额换算；如果用户只是闲聊或问汇率以外的问题，不要调用。\"\n            ),\n            \"parameters\": {\n                \"type\": \"object\",\n                \"properties\": {\n                    # 规范②：用对类型——金额是 number。\n                    \"amount\": {\n                        \"type\": \"number\",\n                        \"description\": \"要换算的金额，纯数字\",\n                    },\n                    # 规范④：用 enum 锁死取值，逼模型输出标准货币代码而非随意文字。\n                    \"from_currency\": {\n                        \"type\": \"string\",\n                        \"enum\": [\"USD\", \"CNY\", \"EUR\", \"JPY\"],\n                        \"description\": \"源货币代码\",\n                    },\n                    \"to_currency\": {\n                        \"type\": \"string\",\n                        \"enum\": [\"USD\", \"CNY\", \"EUR\", \"JPY\"],\n                        \"description\": \"目标货币代码\",\n                    },\n                },\n                # 规范③：明确必需字段，缺一不可。\n                \"required\": [\"amount\", \"from_currency\", \"to_currency\"],\n            },\n        },\n    }\n]\n\n\ndef ask(tools: list, question: str) -> str:\n    \"\"\"把一个问题 + 一份 schema 发给模型，返回它的“决定”（调用了什么\u002F传了什么）。\n\n    注意：这里【故意不真正执行函数】，因为本练习只关心\n    “模型在这份 schema 下做出了什么决定、吐出了什么参数”。\n    \"\"\"\n    resp = client.chat.completions.create(\n        model=MODEL,\n        messages=[{\"role\": \"user\", \"content\": question}],\n        tools=tools,\n    )\n    msg = resp.choices[0].message\n\n    if not msg.tool_calls:\n        return \"（未调用工具）直接回答：\" + (msg.content or \"\")\n\n    call = msg.tool_calls[0]\n    # 原样打印参数字符串，方便观察类型\u002F格式问题（\"100\" 还是 100、USD 还是 美元）\n    return f\"调用 {call.function.name}，参数 = {call.function.arguments}\"\n\n\n# 三个测试问题：第 1 个正常换算，第 2 个是边界（只问汇率不换算），第 3 个完全无关。\nQUESTIONS = [\n    \"100美元是多少人民币？\",\n    \"美元和人民币的汇率大概是多少？\",\n    \"你好，帮我写一首关于春天的诗。\",\n]\n\n\nif __name__ == \"__main__\":\n    for q in QUESTIONS:\n        print(\"=\" * 60)\n        print(f\"问题：{q}\")\n        print(f\"  ❌ 坏 schema → {ask(BAD_TOOLS, q)}\")\n        print(f\"  ✅ 好 schema → {ask(GOOD_TOOLS, q)}\")\n    print(\"=\" * 60)\n    print(\n        \"\\n观察重点：\\n\"\n        \"  · 参数类型：坏 schema 的 amount 常是字符串 \\\"100\\\"，好 schema 是数字 100。\\n\"\n        \"  · 货币格式：坏 schema 可能填“美元\u002F人民币”，好 schema 受 enum 约束输出 USD\u002FCNY。\\n\"\n        \"  · 何时调用：好 schema 的 description 写清了边界，边界\u002F无关问题更不容易误触发。\"\n    )\n",[30],{"type":18,"tag":31,"props":32,"children":33},"code",{"__ignoreMap":7},[34],{"type":24,"value":28},{"title":7,"searchDepth":36,"depth":36,"links":37},2,[38],{"id":21,"depth":39,"text":21},3,"markdown","content:articles:agent:02-schema-design.md","content","articles\u002Fagent\u002F02-schema-design.md","articles\u002Fagent\u002F02-schema-design","md",1781678620539]