Anthropic《Writing effective tools for agents》系统解读:非确定性契约与 PPT Master 的脚本接口
原文:Writing effective tools for agents — with agents
实践项目:PPT Master
前置阅读:《Building effective agents》 · 《Effective context engineering》 · 《Effective harnesses》
第一篇解读介绍了 ACI 的概念,但那只是原文的一个附录。这一篇是同一主题的完整展开。
原文开篇给出的定位很关键:
Tools are a new kind of software which reflects a contract between deterministic systems and non-deterministic agents.
确定性系统与非确定性 Agent 之间的契约。 传统 API 是两个确定性系统之间的契约——调用方知道自己要什么,按签名传参即可。而 Agent 会误解、会选错、会在错误的时机调用,工具设计必须为这些情况留出余地。
一、为什么不能直接包装 API
原文指出的核心约束是上下文:
LLM agents have limited "context" (that is, there are limits to how much information they can process at once).
由此得出第一条设计原则:不是每个 API 端点都该成为一个工具。
Implement a few thoughtful tools targeting specific high-impact workflows.
原文的例子非常具体:
Instead of implementing a
list_users,list_events, andcreate_eventtools, consider implementing aschedule_eventtool which finds availability and schedules an event.
三个原子操作合并成一个面向工作流的工具。差别在于,前者要求 Agent 自己编排三次调用并处理中间结果,每一步都消耗上下文且可能出错;后者把编排逻辑放进了确定性代码。
二、评估驱动的开发流程
这是原文方法论的主干,也是最容易被跳过的部分。
构建评估任务
原文强调任务必须来自真实场景,并给出一个合格样例:
Schedule a meeting with Jane next week to discuss our latest Acme Corp project. Attach the notes from our last project planning meeting and reserve a conference room.
同时警告要避免:
Overly simplistic or superficial "sandbox" environments.
每个任务需要配套可验证的目标响应,可选地记录期望的工具调用序列。
收集多维指标
不只看准确率,还要看运行时间、工具调用次数、token 消耗、错误率。原文建议开启 interleaved thinking 以观察 Agent 的决策逻辑。
用 Agent 改进工具
这是文章标题里"with agents"的含义:
Claude is an expert at analyzing transcripts and refactoring lots of tools all at once.
把评估记录直接交给 Claude Code 分析,让它一次性重构大量工具。Anthropic 用这个流程改进了 Slack 和 Asana 的内部工具,准确率有明显提升。
顺序不能颠倒
先有评估任务,才知道工具在哪里失败;先有失败记录,Agent 才有东西可分析。没有评估的工具优化,是在猜测哪里需要改。
三、具体设计原则
命名空间
用前缀或后缀分组相关工具,如 asana_search / jira_search,或 asana_projects_search / asana_users_search。目的是帮助 Agent 在工具变多时仍能选对。
返回有意义的上下文
优先返回高信号信息,避免低层技术标识:
| 避免 | 改用 |
|---|---|
uuid | name |
256px_image_url | image_url |
mime_type | file_type |
原文还建议实现 response_format 参数,提供 DETAILED 和 CONCISE 两档。文中的 Slack 案例显示,concise 版本把响应从 206 token 减到 72 token,约为原来的三分之一。
Token 效率
实现分页、范围选择、过滤和截断,并设置合理默认值:
We restrict tool responses to 25,000 tokens by default.
截断时要给出清晰引导:
Encourage agents to pursue more token-efficient strategies, like making many small and targeted searches instead of a single, broad search.
错误响应
不要返回不透明的错误码或 traceback,而要提供 具体且可操作的改进建议。
工具描述
原文认为这是投入产出比最高的优化:
Even small refinements to tool descriptions can yield dramatic improvements.
描述应当像向新员工介绍工具一样明确,参数命名不能有歧义(user_id 优于 user)。文中提到 Claude Sonnet 在 SWE-bench Verified 上达到 SOTA,正得益于对工具描述的精确修订,错误率因此大幅下降。
原文还给了一个有趣的失败案例:Claude 在使用 web search 工具时会不必要地给 query 参数追加"2025",最终通过改进工具描述解决——模型的怪异行为,往往是描述留下的歧义造成的。
从概念转入实践
PPT Master 由数十个顶层 Python 入口和更多内部模块构成 Agent 的工具面。脚本数量会随拆分方式变化,以下关注接口边界而不是把某个计数当作长期事实。
四、工具整合:source_to_md 的调度器设计
PPT Master 用五个主要后端家族处理更广的输入集合:PDF;DOCX/HTML/EPUB/IPYNB 及可经 Pandoc 处理的 DOC、ODT、RTF、LaTeX、RST、Org、Typst;XLSX/XLSM;PPTX/PPTM/PPSX/PPSM/POTX/POTM;以及 URL。统一调度器还识别 Markdown 与纯文本。五个后端家族的入口是:
scripts/source_to_md/
├── pdf_to_md.py
├── doc_to_md.py
├── excel_to_md.py
├── ppt_to_md.py
└── web_to_md.py但暴露给 Agent 的主入口是一个统一调度器:
python3 scripts/source_to_md.py <file-or-url-or-dir> [...]这正是原文 schedule_event 例子的同构实现:Agent 不需要先判断文件类型再选择后端,只需交出路径。类型判断、后端路由、批量处理都在确定性代码里完成。
值得注意的是,底层后端仍然可以直接调用(文档里明确列出了直接调用方式)。这保留了在调度器判断失误时的逃生通道,同时不让常规路径承担选择成本。
同样的整合出现在 project_manager.py,它用子命令组织项目生命周期操作:
python3 scripts/project_manager.py init <project_name> --format ppt169
python3 scripts/project_manager.py import-sources <project_path> <sources...>
python3 scripts/project_manager.py validate <project_path>这是原文命名空间建议的另一种实现——不是给平铺的工具加前缀,而是用子命令建立层级。
五、错误响应:error_helper.py 的结构与暴露边界
原文要求错误信息提供"具体且可操作的改进建议"。PPT Master 有一个专门模块做这件事:
ERROR_SOLUTIONS = {
'missing_svg_output': {
'message': 'Missing svg_output directory',
'solutions': [
'Create the svg_output directory: mkdir svg_output',
'Place generated SVG files in this directory',
'Ensure SVG files follow naming convention: slide_XX_name.svg'
],
'severity': 'error'
},
...
}每个错误声明三个字段:发生了什么、可以怎么做(多条具体动作)、严重程度。
对照原文的要求,这个 数据结构 是合格的:solutions 里给的是可直接执行的命令和可检验的条件,而不是"请检查配置"这类无信息量的提示。severity 字段也能表达"必须停下"与"可以继续"。
但不能把结构存在直接写成主 ACI 已经使用它。当前只有 validate_project_structure(..., verbose=True) 才会把 solutions 拼入输出,而 project_manager.py validate 的主入口没有启用这个参数。因此,error_helper.py 更准确的定位是 已有的辅助能力或遗留接口;主验证入口当前并未把这些可操作建议暴露给 Agent。
六、Token 效率:用调用方式代替参数
原文的方案是给工具加 response_format 参数。PPT Master 达成了同样的目标,但走的是另一条路——在文档里规定调用纪律。
svg_quality_checker.py 的使用规则有两条:
| 场景 | 规则 | 理由 |
|---|---|---|
| 运行检查 | 不得用 tail / head / grep 过滤输出 | "One run already reports all pages"——过滤会导致"发现一个、修一个、再跑一次"的往返循环,每轮都累积上下文 |
| 检查通过 | 不要打开或 cat 完整 JSON | 成功时"use the exit status and terminal summary"就够了 |
| 检查失败且输出被截断 | 只读该次运行写入报告中的相关 issue 数组 | 避免整份报告进入上下文 |
这三条实际上实现了原文 DETAILED / CONCISE 的效果:失败时详细,成功时极简。
但实现方式有本质差异
原文把输出控制做进 工具参数,由工具自己保证;PPT Master 做进 文档纪律,依赖 Agent 遵守。
前者是机制,后者是约定。约定在 Agent 上下文被压缩、或规则文档没有被加载的路径上会失效。这一点留到第九节讨论。
七、超出原文的部分:prompt_audit.py
PPT Master 有一个原文没有对应物的工具——面向维护者的 prompt 预算审计:
python3 skills/ppt-master/scripts/prompt_audit.py --json它检查的内容包括:
| 检查项 | 失败级别 |
|---|---|
| 语料总量与热点文件的 token 上限 | 超预算报 error |
| 声明的加载集(路由/阶段场景)预算 | 超预算、文件未知、选择器漂移报 error |
| 加载覆盖率 | 语料文件不属于任何加载集且无豁免时报 error |
| 注册表声明(版式、模式、样式、图表等)的 ID 与数量 | 漂移报 error |
| Markdown 引用与声明的权威边 | 断链或未引用报 error |
| 跨文件精确/近似重复 | warning |
| Schema 多处定义 | warning |
其中预算策略的设计尤其克制:
Budgets are stable, deliberately rounded limits rather than mirrors of the current token count. Once set, do not raise or lower a passing ceiling, including to restore headroom after prompt growth.
上限一旦设定就不许调整,除非真的溢出。 这防止了预算被逐步稀释——如果每次接近上限就调高,预算就失去了约束意义。
笔者观点
这个工具把"上下文预算"从一个抽象概念变成了审计命令可检的指标,而且检的是 静态语料——Skill 文档本身的体量。
它同时呼应了三篇文章:Agent Skills 的分层加载(加载集覆盖率检查确保每个文件都属于某条加载路径)、上下文工程的注意力预算(token 上限)、以及本文的工具设计(结构化 JSON 输出与稳定错误信封 AUDIT_SETUP_ERROR)。
原文讨论的是"给 Agent 用的工具",这是"给维护 Agent 的人用的工具"——但它守护的正是 Agent 的上下文健康。
八、工具描述的实际载体
原文强调工具描述是投入产出比最高的优化点。PPT Master 的工具描述不在函数 docstring 里,而在 scripts/docs/ 下的独立 Markdown:
| 文档 | 行数 | 覆盖 |
|---|---|---|
svg-pipeline.md | 826 | SVG 处理链路 |
confirm_ui.md | 599 | 确认界面的完整协议 |
conversion.md | 592 | 源材料转换 |
prompt_audit.md | — | 审计工具用法与清单维护规则 |
这个安排与第二篇解读讨论的"代码的两种角色"一致:Agent 读 scripts/docs/ 理解接口,执行 scripts/ 下的 Python,不需要把源码读进上下文。
从工具描述的角度看,这些文档承担的正是原文说的"像向新员工介绍工具一样"的职责——而且因为不受 docstring 长度限制,可以包含完整的失败处理和恢复路径。
九、对照原文校准三个工具设计判断
1. 不能复用 --format 表达输出详略
第六节指出,PPT Master 的 token 效率部分依靠文档纪律实现。原文的做法是把输出详略做进工具参数,但具体项目仍要先检查已有 CLI 语义。
svg_quality_checker.py 的 --format 已用于声明期望画布格式,不能再赋予 concise|detailed 含义。更重要的是,检查器在失败时要求先审阅未过滤终端输出中的完整 issue 集、让 Agent 合并修复;只有终端被截断时才从同一次 JSON 报告定向读取相关字段,成功时则以退出状态和终端摘要收口。这是一项有意的往返成本设计,不应仅为形式上贴合原文而改写。
如果未来有实际证据表明失败输出经常造成上下文压力,可以新增名称不冲突的输出模式;在此之前,现行契约更准确的描述是“详细失败、简洁成功,并把完整 JSON 留在磁盘”。
2. 未设统一长度上限是一项取舍
原文给出的默认值是 25000 token。PPT Master 的检查器对多页 deck 的完整 JSON 报告没有声明长度上限——正常情况下由"成功时不读完整 JSON"的纪律规避;失败时先审阅终端完整问题集,只有终端输出被截断才读取落盘报告中的相关 issue 数组。
但直接截断也会破坏“单次检查返回完整失败集合、集中修复”的约束,并可能把后面的阻断问题藏掉。当前做法是在终端输出被截断时,从该次落盘报告中只读取相关 issue 数组。只有出现可复现的超大报告问题时,才适合补充按页或按 issue 类型查询的独立接口,而不是先删掉信息。
3. 展示样例不是现成的工具评估集
这是最需要先分清“展示结果”和“可重放输入”的一处。
原文的核心方法论是:构建真实任务评估集 → 运行并收集多维指标 → 把记录交给 Claude 分析并重构工具。
PPT Master 的当前目录清单包含 21 个展示项目,覆盖论文解读、品牌手册、城市更新、技术蓝图等多种题材,也保留了 Design Spec、SVG 和最终 PPTX 等输出。它们是 Generate 路线的结果样例,并不覆盖四条顶层路线;多数项目也没有标准化的 sources/ 输入目录,因此不能直接重放成端到端任务。
这些样例可以为未来的输出不变量和题材选择提供种子,但若要研究工具误用,仍需另行定义输入、路线、预期行为和评分规则。不能把“有展示结果”写成“已经有可复现的多路线评估素材”。
十、我的理解:工具设计是把不确定性挡在代码之外
核心观点
给 Agent 写工具的目标,不是让工具能力更强,而是 让 Agent 犯错更难。
每一个需要 Agent 自己判断的环节,都是一个可能出错的点。工具整合消灭的是"选哪个后端"的判断,默认值消灭的是"传什么参数"的判断,可操作的错误信息消灭的是"失败后该做什么"的判断。
按这个标准回看 PPT Master 的三处设计:
source_to_md.py调度器——消灭了类型判断;error_helper.py的 solutions 列表——具备减少失败后方向判断的结构,但当前主验证入口尚未暴露它;project_manager.py子命令——消灭了"该调哪个脚本"的判断。
第九节的三个判断说明,工具设计不能只按通用文章做表面对齐:参数名已有领域含义,完整失败集合承担修复语义,展示样例也缺少可重放输入。先验证真实失败,再改最小的拥有接口,才能避免为假设问题增加机制。
原文那句"even small refinements to tool descriptions can yield dramatic improvements"的另一面是:如果一件事可以由默认值决定,就不该写进描述让模型记住。
十一、下一步
Demystifying evals for AI agents— 本文第九节区分了展示样例与可复现任务;下一篇继续讨论什么证据才能支持工具或流程改动(系统解读)。