06.03概念更新于 2026年9月17日

工具设计原则

粒度、命名、面向任务而非接口、返回值精简、错误信息可行动。

06.03 · 课时讲解

工具设计原则

按 Agent 要完成的任务设计工具,而不是把后端接口逐个翻译过去。

1 / 12

一句话理解:工具要按“Agent 要完成的任务”来设计,而不是把后端接口逐个翻译过去;好工具粒度合适、名字清楚、返回精简,出错时还会告诉模型下一步怎么办。

最省事的做法,是把现有的 50 个 REST 接口一对一包装成 50 个工具。结果通常很糟:模型要连调五六个接口才能拼出一个答案,上下文被大段 JSON 塞满,工具之间职责重叠导致频繁选错。Anthropic 在工程博客里总结过一句话:工具不是越多越好。

工具是 Agent 和业务系统之间的接口层。它的使用者不是前端工程师,而是一个上下文有限、按 Token 付费、会被噪音干扰的模型。设计时要围绕这个使用者来考虑。

原则一:面向任务,而不是面向接口

传统 API 为程序设计:原子化、可组合、字段齐全。程序调用十次接口几乎不花成本;Agent 每多调一次,就多一轮推理、多一段上下文,也多一次出错的机会。

以 SEO 诊断 Agent 为例:

接口直译式 面向任务式
list_pages() 返回全站 3000 个页面 search_pages(query, issue_type) 按问题类型筛选页面
get_page(url)、get_meta(url)、get_links(url) 三个接口 audit_page(url) 一次返回标题、描述、H1、状态码、内链数与问题列表
get_rankings(keyword, date) 逐天查询 get_ranking_change(keyword, from, to) 直接返回变化与拐点

以诊断一个页面为例,两种设计在调用过程上的差别一目了然:

接口直译式与面向任务式工具的对比:三次调用对一次调用 接口直译式:一个接口一个工具 面向任务式:一步专家动作一个工具 模型 get_page get_meta get_links 上下文:三段原始 JSON 每多一次调用:多一轮推理、多一段上下文 也多一次出错的机会 模型 audit_page(url) 上下文:一份精简的诊断结果 标题、H1、状态码与问题列表一次返回 内部由程序完成多次接口调用
图 1 · 同样诊断一个页面:接口直译要三轮调用,面向任务只要一轮

Anthropic 的例子也类似:与其提供 list_users、list_events、create_event,不如提供一个 schedule_event,内部完成查空闲和创建日程;与其让模型 list_contacts 再自己翻找,不如直接给 search_contacts。

判断标准是:人类专家完成这一步时,脑子里的动作是什么? 电商客服排查售后时想的是“看这单卡在哪一步”,而不是“先调订单接口,再调支付接口,再调发货接口”。另外,OpenAI 的指南建议把总是连续调用的函数合并成一个,已知的参数不要让模型填。

当然也别合并过头。如果一个工具需要 action 参数在查询、修改、删除之间切换,读写就混在一起了,权限和审批很难区分对待。读和写至少要分开,这一点在 安全的工具设计 里展开。

原则二:粒度适中,数量克制

工具数量直接影响选择准确率和成本。OpenAI 的指南给过一个“软建议”:一轮开始时可用的函数最好少于 20 个;工具更多时,可以用工具搜索(tool search)等机制按需加载,而不是全部塞进上下文。Claude 同样提供工具搜索和 defer_loading,思路见 渐进式披露。

粒度可以用三个问题来校准:

  1. 这个工具单独调用有意义吗? 只返回一个 ID、必须再查一次才有用的工具,通常可以合并。
  2. 两个工具的触发场景能说清楚区别吗? 说不清就合并,或者在描述里互相指路。
  3. 它的风险等级一致吗? 同一个工具里既有只读又有写入,就应该拆开。

原则三:命名让选择变简单

命名规则在 工具定义三要素 里讲过,这里补充设计层面的两点:

  • 按业务域加命名空间:orders_get_order、payments_get_payment、tickets_search_tickets。接入的系统越多,前缀越重要。
  • 名字体现粒度与副作用:search_* 返回列表,get_* 返回单个对象,create_*、update_* 有副作用,preview_* 只预演不执行。

原则四:返回值精简、高信号

工具返回的每个字符都会进入上下文,占用 Token,也分散模型的注意力(见 上下文腐化)。常见做法:

  • 只返回下一步推理需要的字段。 订单系统返回 80 个字段,工具只挑出状态、卡点、异常原因等 8 个。
  • 用有语义的标识。 Claude 的文档建议返回有意义且稳定的标识(如 slug),而不是模型看不懂的内部引用。能返回“杭州主仓”,就不要只返回 warehouse_id: 33010612。
  • 提供详略开关。 Anthropic 的做法是加一个 response_format 参数,让模型按需选择简洁或详细;在他们的例子里,简洁版只用了约三分之一的 Token。
  • 分页、过滤与截断要有合理的默认值。 列表默认返回前 20 条,并告诉模型“共 356 条,可用 page 翻页或用 status 过滤”。

换个角度看,工具层就是模型与业务系统之间的一道过滤器:

工具层过滤返回值:原始记录经过筛选与翻译,只把高信号字段交给模型 订单系统原始记录 80 个字段 · 历史 · 审计 工具层:筛选与翻译 挑字段 · 换语义 · 加提示 返回给模型 8 个字段 + 下一步提示 warehouse_id: 33010612 全部状态变更与审计字段 response_format 详略开关 分页 · 过滤 · 截断提示 仓库:杭州主仓 卡点:支付尚未到账
图 2 · 返回值要经过工具层过滤:只给下一步推理需要的字段

对比一下同一次查询的两种返回:

{
  "order_id": "ORD2026090142",
  "status": "pending_shipment",
  "blocker": { "code": "PAYMENT_PENDING", "meaning": "支付尚未到账,仓库不会发货" },
  "last_update": { "time": "2026-09-16T18:02:00+08:00", "by": "支付系统" },
  "days_since_last_update": 1,
  "next_actions_hint": "可调用 get_payment 查看支付流水,或用 search_tickets 查看客户是否已提交售后工单"
}

而不是把订单表的整行记录、所有历史状态变更和内部审计字段一股脑儿返回。

原则五:错误信息要可行动

模型读到错误后会尝试自我修正,前提是错误信息告诉它 哪里错了、下一步怎么做。Claude 的文档举过例子:与其返回 "failed",不如返回“触发限流,请 60 秒后重试”。

不可行动 可行动
Error 500 订单系统暂时不可用(HTTP 503)。不要重复调用,请告知用户稍后再试。
invalid param order_id 格式错误:收到 "2026090142",应为 ORD 加 10 位数字。请向用户确认完整订单号。
not found 未找到订单 ORD2026090142。常见原因:订单号输错,或订单超过 90 天已归档。请让用户核对订单号。
permission denied 当前用户无权查看该订单(不属于其所在账号)。不要尝试其他订单号,请提示用户联系管理员。

错误要通过工具结果(Claude 的 is_error: true,MCP 的 isError: true)返回给模型,而不是直接抛异常让整个 Agent 崩掉。这部分和 错误恢复与重试 相衔接。

用评测驱动工具设计

工具设计好不好,靠评测说话,而不是靠感觉。Anthropic 推荐的流程是:准备一批需要多次调用工具的真实任务,让 Agent 跑,观察它在哪里选错、重复调用、读漏结果,再改工具。关注这几个信号:

  • 同一个任务平均调用几次工具?有没有明显多余的调用?
  • 哪些工具从来没被用过?哪些总被误用?
  • 工具结果平均多少 Token?有没有超长的返回?

评测集怎么搭,见 构建评测集 与 结果评估与轨迹评估。

常见误区

  • “工具越原子化越灵活。” 对程序是这样,对模型不是。原子化意味着更长的调用链,每一步都可能出错。
  • “把完整数据返回,让模型自己挑。” 模型确实能挑,但要付 Token 成本,还会被无关字段干扰。
  • “一个万能工具最省事。” run_query(sql) 或 call_api(path, body) 看似万能,却把设计责任全推给了模型,还打开了安全缺口。

设计要点

拿到研发的工具清单时,逐条确认:

  • 每个工具对应专家工作流里的哪一步?有没有“必须连调才有意义”的工具可以合并?
  • 初始暴露的工具有多少个?超过 20 个时,是否考虑命名空间与按需加载?
  • 返回值是否只保留了推理需要的字段?列表是否有分页、过滤和默认上限?
  • 每类错误的返回文案,能否让模型知道下一步做什么?
  • 有没有评测任务覆盖每个工具的典型用法和易混淆场景?

练习

为「订单售后诊断 Agent」设计一组只读工具(45 分钟):

  1. 写下客服专家处理一个售后订单的步骤(例如:看订单状态 → 看支付与退款流水 → 看发货记录 → 看历史售后工单 → 给结论)。
  2. 按“面向任务”原则设计 4–5 个工具,例如 get_order、get_payment、get_shipment、search_tickets,写出名称、一句话用途和返回字段清单。
  3. 为每个工具写两条可行动的错误信息。
  4. 自查:有没有哪两个工具总是连续调用、应该合并?

延伸阅读

下一步

工具设计得再好用,只要能写数据、能查任意数据,就有风险。下一课「安全的工具设计」讲怎么在工具层把风险挡住。

试试:上下文、MCP、沙箱、Prompt 注入、外链