工具提示工程:让 agent 会用工具而不是猜工具
把工具定义当成写给模型看的提示:用清楚边界、typed schema、示例结果和可恢复错误,让 agent 选得准、传得对、错了能改。
为什么只记结论不足以掌握工具提示工程:让 agent 会用工具而不是猜工具
学习“工具提示工程:让 agent 会用工具而不是猜工具”时,第一步不是记住结论,而是冻结输入、上下文、版本和成功标准。只有这些条件明确,工具提示工程:让 agent 会用工具而不是猜工具的含义才不会随着样例变化。正文已有的概念说明要与结构图、运行轨迹和失败样本互相印证,不能只凭最终输出看似正确就宣布完成。
结构分析从先打个比方开始:列出参与者、职责、连接方向、生命周期和所有权,再沿正常路径追踪数据或控制流。每一条边都要说明为什么存在、谁创建、谁消费、谁负责清理;如果边界被跨越,必须能在证据中找到第一处异常。
机制验证要把工具定义就是给模型看的提示写成可以执行的条件。正常样本证明主路径,恰好边界样本验证等号和空值,单故障样本只破坏一个假设。三类样本使用同一份观察指标,避免因为测试口径变化而把偶然结果误认成规律。
成本分析同时记录时间、空间、延迟、耦合、可维护性和不可逆操作。描述要写边界,而不只写功能不是一句“可能失败”,而是可复现输入、预期停点、实际轨迹、错误分类与清理步骤。任何自动重试都要有次数、预算和幂等边界。
方案比较不能只列优点。需要给出直接实现、当前方案和至少一个替代方案,逐项比较复杂度、扩展点、故障隔离和团队认知成本。当问题规模很小或变化轴稳定时,更简单的实现往往更好;模式与框架必须由真实变化压力证明。
实现阶段把大结论拆成可检查的中间产物:配置快照、结构清单、状态转移、输入输出样本、日志摘要和测试结果。每个产物带来源与生成命令,下一阶段只消费已通过门禁的版本,避免旧缓存或隐式默认值污染结论。
解释结果时必须区分相关性与因果性、接口承诺与实现细节、设计意图与运行事实。对“参数 schema 要替模型消歧”的判断要由独立证据支持,并明确适用范围;一旦输入分布、版本、硬件或组织边界改变,就重新运行最小实验。
复盘从首个分叉开始,而不是从最后一个报错倒推。先比较冻结输入,再比较第一份结构化中间产物,随后检查状态、约束和副作用。这样可以把复杂系统的排错范围收缩到一个阶段,避免在多个层次同时修改造成新的不确定性。
迁移到真实项目时,先选择一个最小但有代表性的切片,保存改造前基线,再逐步引入“工具提示工程:让 agent 会用工具而不是猜工具”中的机制。每一步只改变一个变量并保留回滚点;性能、正确性、安全性和可理解性至少各有一项可量化指标。
最终验收要求读者能脱离页面重新画出结构、口述关键链路、实现最小版本、构造一个反例并解释失败位置。若只能复述名词而不能预测中间状态,说明知识仍停留在识记层,需要回到图示和实验重新验证。
本页用、
、、、建立统一坐标。先预测这些概念在结构图和运行轨迹中的位置,再操作实验控件;如果结果与预测不一致,停止在首个分叉,不要用后续补丁掩盖早期错误。
权威目录与核心概念逐项对照
- 工具提示工程:让 agent 会用工具而不是猜工具
- 先打个比方
- 工具定义就是给模型看的提示
- 描述要写边界,而不只写功能
- 参数 schema 要替模型消歧
- 返回结果语义会改变下一次选择
- 错误要帮助模型恢复
- 工具定义要靠 eval 迭代
可复现的最小实现
先把决策记录写成机器可读结构:
{
"unit": "工具提示工程:让 agent 会用工具而不是猜工具",
"inputFrozen": true,
"scenario": "normal | boundary | single-fault",
"firstDivergence": null,
"cleanupRequired": true
}再用同一条执行链处理三类样本:
type Evidence = { stage: string; expected: string; actual: string };
function verify(sample: unknown, expected: readonly Evidence[]) {
const trace = runFromCleanState(sample);
return expected.find((item, index) => trace[index]?.actual !== item.expected);
}最后保存回归门禁,禁止失败样本静默通过:
normal -> complete, invariant preserved
boundary -> complete or explicit rejection
singleFault -> stop at first divergence, no stale output用统一评分解释实验结果:
本章回顾
- 工具提示工程:让 agent 会用工具而不是猜工具必须绑定冻结输入和明确成功标准。
- 先打个比方必须能画成结构并沿边追踪责任。
- 工具定义就是给模型看的提示要由正常、边界和单故障样本共同验证。
- 描述要写边界,而不只写功能必须保存第一处偏离和清理重建步骤。
- 参数 schema 要替模型消歧决定方案是否可以进入下一阶段。
术语表
先打个比方
想象夜班前台面前有一排按钮。按钮名字像“处理一下”“查东西”,旁边没有说明,也不写什么时候不能按。新人当然会犹豫,甚至按错。
更稳的做法,是把每个按钮贴上清楚标签:什么情况按、什么情况别按、要先填哪些信息、按完会看到什么回执,出错时还告诉他该补哪张表。
这一章解决的就是“按钮说明怎么写”。没有好说明,系统不是不会行动,而是靠猜;一旦猜错,就会查错、退错、重复调用,甚至把小问题办成事故。
工具定义就是给模型看的提示
在 agent 系统里,不是 SDK 注释,也不是只给后端同事看的接口文档。它会被放进模型上下文,直接影响模型“要不要用、用哪个、参数怎么填、拿到结果后怎么继续”。
Anthropic 在 Appendix 2 里提醒,工具规格要投入和整体 prompt 一样多的提示工程注意力。原因很简单:如果描述只写“查询数据”,模型要猜它查订单、查退款、查知识库还是查用户;如果描述写清“仅用于查询已存在订单状态,不会发起退款”,模型就少走很多岔路。
把工具说明写给模型看时,优先回答四个问题:
- 什么时候应该用它
- 什么时候不要用它
- 参数必须长什么样
- 返回结果代表什么,下一步该怎么读
描述要写边界,而不只写功能
工具描述最常见的问题,是只说“能做什么”,不说“何时用/不用”。对人类来说,团队知识能补齐空白;对模型来说,空白就是猜测空间。
下面这张对照图把同一组客服工具拆成两种写法。左边像内部接口清单,右边才像 agent-ready contract:工具之间边界互斥,风险动作有条件,查询和执行不会混在一起。
一个好描述通常包含:
- 触发条件:用户意图或状态满足什么才用
- 排除条件:哪些相似需求不要用
- 风险边界:是否会修改状态、扣款、退款、通知用户
- 证据要求:调用前必须已有的字段或上一步结果
参数 schema 要替模型消歧
这里的不是“把后端入参暴露出去”这么简单。它应该像给新同事写
docstring:字段名越具体,枚举越明确,模型越不容易把 query、id、payload
这类空洞字段填错。
好的 schema 会做三件事:
- 用具体字段名替代万能字段,例如
order_id、refund_reason - 用枚举压住开放文本,例如
status | eligibility | timeline - 把格式写进说明,例如订单号前缀、日期格式、金额单位
返回结果语义会改变下一次选择
工具调用不是终点。模型会读返回结果,再决定下一步是继续查询、执行动作、向用户解释,还是转人工。所以必须明确。
比如知识库工具返回 confidence: "low",不是让模型硬答,而是提示它扩大检索、换关键词或请求人工;退款资格工具返回 eligible: false,应该带上原因和可选替代方案,而不是只返回 false。
错误要帮助模型恢复
失败不可怕,裸失败才可怕。的目标不是安慰用户,而是让 agent 下一轮能修正自己。
把 500、KeyError、invalid input 直接丢给模型,它只能原样重试或乱猜。更好的错误返回要包含:
code:稳定错误码,便于路由和统计message:人话说明哪里错retryable:是否值得改参数后重试fix:下一步应该补什么、改什么、换哪个工具
工具定义要靠 eval 迭代
最后一个关键点是。不要凭感觉改描述;要拿真实或合成任务跑一批,看模型在哪些地方选错、漏填、重复调用、遇错不会恢复。
一次工具 eval 至少记录四类结果:
- 工具选择是否正确
- 参数是否完整、格式是否正确
- 返回结果是否被正确引用
- 错误返回是否引导出正确恢复动作
如果某类错误反复出现,优先改工具定义本身:描述加边界、schema 加枚举、返回值加语义字段、错误信息加修复建议。Anthropic 的建议也很接近这个思路:把自己放到模型视角里测试工具,并根据模型犯错方式迭代。
动手看:从猜工具到会用工具
猜一猜:如果用户说“我想知道这单还能不能退”,一个工具描述只写“处理订单”,模型更可能查状态、发起退款,还是先判断资格?
这张反馈图的重点是:工具定义写得越像“按钮说明书”,模型越能先选查询类工具,读到资格结果后再决定是否需要退款动作;不是一上来就按最危险的按钮。
代码对照:含糊工具 vs agent-ready typed contract
先看一个容易误用的版本。它把查询、退款、知识库都藏在一个 action 字段里,模型要同时猜 intent、参数和风险。
export const supportTool = {
name: "handle_order",
description: "处理订单相关问题",
input_schema: {
type: "object",
properties: {
action: { type: "string" },
id: { type: "string" },
reason: { type: "string" },
},
},
};export const getOrderStatus = {
name: "get_order_status",
description:
"查询已存在订单的物流、支付和退款状态。只读工具;不要用它发起退款或修改订单。",
input_schema: {
type: "object",
required: ["order_id"],
properties: {
order_id: {
type: "string",
description: "订单号,如 ORD-2026-00042。",
},
},
},
};左边的问题不是字段少,而是边界空。右边明确“只读”“不要退款”“订单号格式”,模型更容易在用户只想查状态时选它,而不是误触发动作。
再看退款工具。这里的目标不是让退款变简单,而是把高风险动作写成有前置条件的契约。
export const refund = {
name: "refund",
description: "给用户退款",
input_schema: {
type: "object",
properties: {
order: { type: "string" },
amount: { type: "number" },
},
},
};export const createRefundRequest = {
name: "create_refund_request",
description:
"在 check_refund_eligibility 返回 eligible=true 后,为订单创建退款申请。不要用于查询退款政策。",
input_schema: {
type: "object",
required: ["order_id", "refund_reason", "eligibility_check_id"],
properties: {
order_id: { type: "string" },
refund_reason: { enum: ["damaged", "late", "duplicate", "other"] },
eligibility_check_id: { type: "string" },
},
},
};注意 eligibility_check_id:它把“先查资格”变成参数要求,而不是靠模型记住流程。这就是 Anthropic 提到的 poka-yoke 思路:把参数改到更难犯错。
最后看返回值和错误。它们会成为下一轮输入,所以也要写成模型可读的信号。
return {
ok: false,
error: "INVALID_ORDER",
};return {
ok: false,
code: "ORDER_ID_FORMAT_INVALID",
message: "order_id 必须形如 ORD-2026-00042。",
retryable: true,
fix: "请向用户确认订单号,或先调用 search_orders_by_email。",
};右边多出的不是废话,而是 agent 的下一步路线图:能不能重试、该补什么、可替代工具是什么。
容易踩的坑
小结
- 工具定义会进入模型上下文,本质上是提示
- 描述要同时写“用”和“不用”的边界
- 参数 schema 要用字段、枚举和格式消歧
- 返回结果与错误会决定下一轮选择
- 工具定义要靠 eval 发现误用再迭代