返回 AICAP-180
B5 · Day 42tool/context engineering + 指标树

tool 设计反模式

昨天(Day 41)确立了 context engineering 的总命题:agent 的输入是有限 token 预算,工具描述是预算的主要消费者之一。

阶段: B5 · tool/context engineering + 指标树(Day 41-50) 标签: #tool-design #anti-patterns #action-space #error-contract

今日导引(由浅入深)

昨天(Day 41)确立了 context engineering 的总命题:agent 的输入是有限 token 预算,工具描述是预算的主要消费者之一。

今天顺着这条线往里钻一层——工具不只是 token 成本,它还是 agent 的动作空间:工具设计得好不好,直接决定模型选对/选错动作的概率。在 B1→B18 曲线上,这是从「度量输入」过渡到「度量动作面质量」的一步。

明天(Day 43)会把这些质量诉求抽象成指标树的叶子;Day 45 则会据今天标注的反模式真正动手重设计 v2 工具集。

今天的「最小可判定产出」是:一份逐工具的反模式标注清单 + 一个可离线跑通的 baseline harness(真实通过率需 key,标「待跑」)。

1. 机理精读

定义。 工具(tool)是 agent 能调用的外部动作的接口声明(name + description + 输入 schema + 返回契约)。

一组工具构成 agent 的动作空间;空间的形状(多大、多歧义、多易错)直接影响策略的错误率。坏工具设计抬高错误率,不是因为模型笨,而是因为动作空间本身充满陷阱。

五类坏味(参考 Anthropic《Writing effective tools for agents》2025-09)。

  1. 重叠工具(overlapping tools):两个工具职责交叉,模型面临「该选哪个」的选择困难,等价于在动作空间里制造了多个近义入口,徒增决策噪声。
  2. 模糊 description(vague description):描述没说清「何时用 / 何时不用」,模型只能猜调用时机——这是工具误用的头号来源。
  3. 胖参数(fat schema)inputSchema 过宽(自由 string、无 enum、无 required 约束),取值域太大,模型更容易填出非法或语义错误的参数。
  4. 隐式状态(implicit state):工具行为依赖外部不可见状态(如「上次调用设置过的某个开关」),破坏了无状态可推理性,让同样入参产生不同结果。
  5. 无错误契约(no error contract):失败时不返回结构化反馈,模型拿不到「为什么失败、下一步怎么办」的信号,无法自我恢复。

为什么这样分类。 这五类分别对应动作空间的五个维度:入口唯一性(重叠)、时机可判定性(模糊 description)、参数可约束性(胖参数)、状态可推理性(隐式状态)、失败可恢复性(错误契约)。

把坏味按维度切开,重设计时就能逐维施治(Day 45 的三原则正是对前三类的针对性修复)。

一个粗糙直觉:设单步选错概率为 p,一个任务平均 k 步工具调用,则全程不出错的概率约 (1-p)^k。重叠/模糊工具把单步 p 抬高,k 越长放大越狠——这解释了为何「动作面清晰」对多步 agent 比对单步 chat 重要得多(呼应 Day 39 的 pass^k 脆弱性)。

关键权衡:工具数量 vs 表达力。 工具拆得越细、约束越严,单工具越不易错,但工具数变多又抬高 context token 与选择面(回到 Day 41 的信噪比)。

所以反模式治理不是无脑加约束,而是在「动作面尽量小且清晰」与「能力覆盖足够」之间取平衡——这与 Day 40「能简单就别复杂」一脉相承。

与相邻概念的边界。 本仓的 toolRegistry.ts 实现的是 MCP「工具暴露给 LLM 的契约层」的进程内形状(schema 校验 + tools/list + tools/call),它是机制;今天讨论的反模式是这套机制上设计层的好坏判据。

机制保证「非法参数被 reject」,但拦不住「合法但歧义的工具让模型选错」——后者只能靠设计层(description 写负例、enum 收窄)解决。

2. 推导 / 手算 / 代码走读

代码日:对照 src/agent/mcp/toolRegistry.ts 标注反模式,并走读 baseline harness 的离线可验路径。

toolRegistry.ts 提供的「机制层防线」(这些已绿,是反模式治理的下界):

  • validate(schema, value):递归校验,支持 enumvalue not in enum)、minLengthminimum/maximumrequired(缺字段报 required property missing)。
    • 含义:给参数加 enum/required 就能在调用前挡掉非法值——Day 45 收窄 schema 的可执行性正来自这里。
  • register(spec, handler):重名抛 already registered、非法 name 抛错、inputSchema.type !== 'object' 抛错。
    • 含义:杜绝「静默覆盖」这种隐式状态来源(反模式 4 的机制层防线)。
  • call(name, args):先 validate,失败抛 McpCallError(INVALID_PARAMS, errs);handler 抛错包成 INTERNAL_ERROR
    • 含义:这是结构化错误契约的最小实现(带 code + message + data)。坏味 5「无错误契约」在机制层已被堵住,但工具 handler 自己若返回模糊错误文本仍会复发。
  • handle(req):JSON-RPC 2.0 dispatch,永不抛错、所有失败折成 response.error——给模型稳定的失败信封。

baseline 一侧走读 scripts/run-agent-eval.ts + src/agent/eval/tasks.ts

  • EVAL_TASKS29 条;与「工具/动作」直接相关的探针样本:
    • planning-tool-selection:给两个工具 getTokenPrice / getWalletBalance,问「查 0xABC 持有多少 ETH 调哪个、传什么参」,codeCheck 断言选 balance 工具 + 传地址——测「工具歧义」反模式;
    • robustness-tool-failure:价格 API 返回 503,要求「重试/报故障且不得编造价格」——测「错误契约缺失」反模式。
  • scripts/run-agent-eval.ts 的 key 行为:
    • 无 key 时是 dry-run:打印「Would run 29 tasks on …」并 exit 0;
    • 有 key(默认 provider deepseekDEEPSEEK_API_KEY)时才真正调用 runTaskEval 并落 JSON 报告;
    • 所以「harness flow 本身可离线验证」字面属实——离线能跑通流程,只是不出真实通过数。

反模式标注表骨架(今天要填的产出形状):

| 工具 | 重叠 | 模糊desc | 胖参数 | 隐式状态 | 无错误契约 | 重设计动作(→D45) |
|------|------|---------|--------|---------|-----------|-----------------|
| …    | ✓    |         | ✓      |         |           | 与 X 合并 + 加 enum |

每命中一类坏味打一个 ✓,最后一列写 Day 45 的处置动作(合并/写负例/收窄)。命中「重叠」且与另一工具职责交叉的,配成对登记——它们是 Day 45 合并的输入。

3. 今日实战

  1. 逐个对照 src/agent/mcp/toolRegistry.ts 当前注册的工具,在笔记/审计文件里给每个工具打上命中的坏味标签(重叠 / 模糊 description / 胖参数 / 隐式状态 / 无错误契约)。
  2. 重点标注 Day 45 要合并的重叠工具对,以及 description 缺「何时用/何时不用」的工具——为重设计列待办。
  3. 用 DeepSeek-V3(OpenRouter provider,已 wired)跑 src/agent/eval/tasks.ts 中 tool-use 子集,经 scripts/run-agent-eval.ts / pnpm eval:agent 记录当前 baseline 通过数。
  4. 无 key 时先用离线 fixture 跑通 harness,产出 completion% 占位 + commit,证明 flow 可用。
  5. 把标注表里命中「重叠」的工具配成对、命中「胖参数」的标出待 enum 化的字段——直接作为 Day 45 重设计的输入清单(避免明天再回头体检一遍)。

4. 今日实测 / 产出

  • 反模式标注清单可离线产出(纯设计层判断,无需 key)。
  • baseline = N/5待跑(需 OPENROUTER_API_KEY);可先用离线 fixture 跑通 harness 出 completion% + commit」。
  • harness flow 本身可离线验证(无 key 时 dry-run,打印 Would run 29 tasks)。

(诚实状态:标注清单可立即产出;真实 baseline 通过数依赖模型 key,标「待跑」,未升级为已完成。)

5. 常见误区 / 陷阱

  1. 把 endpoint 1:1 映射成工具:每个 REST endpoint 包一个工具,制造大量重叠 + 胖参数,是最常见的旧 wrapper 思维(见 SOTA 黑名单)。
  2. description 只写"做什么"不写"何时用/何时不用":模型缺的是调用时机判据,不是功能说明。
  3. 以为机制层校验能救设计层歧义validate() 能挡非法参数,挡不住「两个合法工具二选一选错」——歧义只能靠设计治。
  4. 拿 dry-run 的占位数当真实 baseline:无 key 时 harness 不调模型,completion% 是 fixture 占位,不能当作模型能力证据。

6. 学习资源(每条带 YYYY-MM)

  • Anthropic, Writing effective tools for agents(2025-09)——五类工具设计原则与反模式的主线来源。
  • Anthropic, Effective context engineering for AI agents(2025-09)——工具声明作为 context 预算主要消费者的上位视角(承接 Day 41)。
  • Anthropic, Building Effective Agents(2024-12)——「能简单就别复杂」,约束动作空间的总原则。
  • 本仓代码:src/agent/mcp/toolRegistry.tsvalidate / call / 错误契约)、src/agent/eval/tasks.tsplanning-tool-selectionrobustness-tool-failure)、scripts/run-agent-eval.ts(dry-run / 真跑)。
  • MCP specification(2026-07-28 定稿,RC 2026-05-21)——stateless + 零信任 schema 校验,与「结构化错误契约」方向一致的规范背景。

SOTA检查 (2026-06 更新)

  • 当前主流方案:Anthropic tool-writing 指南(2025-09)仍 current;2026 共识是「少而清晰的工具 + 显式错误契约 + 收窄 schema」。
  • 是否仍 SOTA:是。MCP 2026-07-28 规范也强调 stateless 与零信任校验,与「结构化错误契约」方向一致。
  • 过时黑名单(AVOID):把每个 API endpoint 1:1 映射成工具的旧 wrapper 思路(制造重叠 + 胖参数);description 只写功能不写时机;handler 返回纯文本错误而非结构化反馈。
  • 下次复查点:OpenRouter 上 DeepSeek-V3 的模型 id 当周重验(id/价格会变,影响 baseline 复现);关注 MCP 规范对工具描述/错误契约是否新增约束。

衔接

  • 昨天:Day 41 — context engineering 范式(工具声明是 token 预算的主要消费者)。
  • 今天:工具是 agent 的动作空间,五类反模式(重叠/模糊/胖参数/隐式状态/无错误契约)抬高错误率。
  • 明天:Day 43 — 指标树与 North Star(把工具/动作质量诉求抽象成可测叶子指标)。