给 Agent 写工具:一个好 tool 长什么样

我见过一个团队为了让 Agent “更聪明”,把模型从中杯换成大杯,账单翻了三倍,效果几乎没动。后来定位下来,问题出在一个叫 query 的工具上:它的描述只有一句"查询数据库",返回的是一坨 4000 行的 JSON,里面塞满了 created_at_unix、tenant_uuid、row_version 这种字段。模型不是不聪明,是它每次调用完都得在一堆噪声里捞针,然后经常捞错。 把这个工具拆成两个、描述写清楚、返回值砍掉八成,中杯模型的表现就超过了原来大杯的版本。 这不是个例。Agent 能力的天花板,很多时候是工具设计,不是模型。 模型是你换不动的那部分——它由 Anthropic、OpenAI 训练,你只能选型;工具是你完全能控制的那部分。把精力花在能控制的地方,回报率高得多。 Anthropic 在 2026 年那篇《Writing effective tools for AI agents》里有一句话我很认同:工具是一种新的软件形态,它是确定性系统和非确定性 Agent 之间的契约。你不能再按"给另一个程序员写 API"的思路写工具——调用方变了,设计原则就得跟着变。 工具描述:你在跟模型"招标" 模型面对一组工具,做的事情和招标差不多:读每个工具的描述,判断"这个活该派给谁"。描述写得含糊,它就选错;描述之间边界不清,它就来回横跳。 最常见的坏味道是用实现细节代替使用场景。 1 2 3 4 5 6 7 8 9 10 11 12 13 # 反例 { "name": "db_query", "description": "对主库执行 SQL 查询" } # 正例 { "name": "search_orders", "description": "按用户 ID、时间范围或订单状态查询订单。 用于回答'用户买过什么''某笔订单到哪了'这类问题。 不要用它查商品库存——那是 search_inventory 的活。" } 差别在哪?反例描述的是"工具内部怎么干活"(执行 SQL),模型并不关心这个;它关心的是"什么时候该用我"。正例直接给出触发场景,还顺手划清了和邻居工具的边界。 ...

2026-05-17 · 3 min · Chico

让 LLM 输出可靠的结构化数据

你写了个 prompt,让 LLM 把一段用户评论解析成 JSON:情感、评分、关键词。本地跑了二十次,完美。上线。 三天后告警响了。某条响应里,LLM 在 JSON 后面多写了一句"希望这个分析对你有帮助!"。你的 json.loads() 当场抛异常,整条链路挂掉。 这不是小概率事件,是结构性问题。只要你还在用"自由文本里夹一段 JSON"的方式跟 LLM 要数据,这种崩溃就是迟早的——区别只是它发生在测试环境还是生产环境。 这篇讲清楚:为什么自由文本提 JSON 天生不可靠,2026 年有哪几种正经方案、各自的代价是什么,schema 怎么设计才不坑自己,以及最难的那块——流式场景下怎么拿到结构化数据。 为什么"让它输出 JSON"本身就是错的 先理解 LLM 在干什么。它做的是一件事:根据前面所有 token,预测下一个 token 的概率分布,然后采样。它没有“我现在要写一个合法 JSON"这种全局意识。 所以当你在 prompt 里写"请只返回 JSON,不要有多余文字”,你是在用一句话,对抗模型训练数据里成千上万条"先解释再给结果"的对话样本。大多数时候它听话,因为你的指令把概率压过去了。但只要某次采样,在该写 } 的位置,“希望"这个 token 的概率偶然爬到了第一,它就会写下去——而且一旦写下去,后面就会顺着"希望这个分析对你有帮助"这条最自然的路径滑下去。 常见的失败长这样: JSON 外面套了 ```json 代码块,或者前后有一段自然语言 字符串里有没转义的换行、引号 该是数字的字段写成了 "4.5"(带引号),或者写成 4.5分 嵌套对象少了一个括号,尤其是输出很长的时候 枚举字段返回了你没定义的值——你要 positive/negative/neutral,它给你个 mixed 这些都不是模型"笨”,是概率采样的必然结果。你不可能靠把 prompt 写得更恳切来根治它,你只能降低概率,没法归零。要归零,得换思路:不是请求它输出合法结构,而是从机制上让它没法输出不合法的结构。 五种方案,以及它们各自的代价 2026 年,从最弱到最强,实际可用的方案是这五种。关键不是"哪个最好",是搞清楚每个的边界。 flowchart TB A["LLM 要输出结构化数据"] --> B{"模型在哪?"} B -->|"闭源 API"| C{"要不要 100% 保证 schema?"} B -->|"自己部署的开源模型"| D["约束解码xgrammar / llguidance"] C -->|"要"| E["Structured Outputs / strict 工具"] C -->|"差不多就行"| F["JSON Mode(已是 legacy)"] E --> G["拿到必定合法的 JSON"] D --> G F --> H["只保证语法合法,schema 靠运气"] style E fill:#fde7c2,stroke:#e8b23c style D fill:#fde7c2,stroke:#e8b23c 1. 纯 prompt 约束。 就是开头那种"请只返回 JSON"。它的唯一价值是当作其他方案的补充——把字段含义、示例写清楚能提升内容质量。但别拿它当结构保证。如果你现在生产环境还在裸用这个,这篇文章后面的部分就是为你写的。 ...

2026-05-06 · 3 min · Chico