返回 AICAP-180
B3 · Day 22agent loop + 评测统计

Tool 设计契约

- 承接昨天:昨天(Day 21)确立了「该不该上 agent」的判据,并看清 agentEval.ts 缺一条真正的工具回路。今天往前一步:在写回路之前,先把 agent 与外界打交道的唯一接口——工具(tool)——的契约定清楚。

阶段: B3 · agent loop + 评测统计(Day 21-30) 标签: #tools-for-agents #json-schema #mcp #structured-errors

今日导引(由浅入深)

  • 承接昨天:昨天(Day 21)确立了「该不该上 agent」的判据,并看清 agentEval.ts 缺一条真正的工具回路。今天往前一步:在写回路之前,先把 agent 与外界打交道的唯一接口——工具(tool)——的契约定清楚
  • 为什么是地基:工具是 agent 的「API 表面」,契约不清,模型就会乱填参数、读不懂错误、被整页 JSON 撑爆上下文。这是 B1→B18 能力曲线上「让模型可靠地动手」的地基。
  • 通向明天:明天(Day 23)转去补统计基础(置信区间),为 Day 26-28 的模型 A/B 对比铺路;工具契约(今天)+ 回路骨架(Day 24)+ 统计(Day 23/28)三者拼成 B3 的完整闭环。
  • 最小可判定产出:盘点本仓 in-process 工具的 schema 覆盖率 n/总数,并核对缺 schema 的工具补上 JSON-Schema 入参 + 结构化错误——只盘点本地,无需 key

1. 机理精读

工具是 agent 的 API 表面:命名即文档。

  • 模型选择调哪个工具、怎么填参数,几乎只依据工具的 name + description + 入参 schema——它看不到源码
  • 因此命名要动词 + 对象清晰(assess_structuring 而非 check),description 要写清「做什么、返回什么、什么时候用」。
  • Anthropic《Writing Tools for Agents》(2025-09) 的核心论点是:为 agent 写工具不同于为人写函数——人会读源码、问同事,模型只读这张契约表,所以契约的清晰度直接决定调用正确率。

错误必须结构化、可被模型读懂。

  • 工具失败时不能抛裸异常或返回自然语言乱码,要返回模型能解析、能据此自我修正的结构化错误,典型形如 {error, code, retryable}
  • code 让模型区分「参数错(改参数重试)」与「资源不存在(换策略)」;retryable 告诉它该不该重试。
  • 这把「错误恢复」从碰运气变成可控分支——本仓 toolRegistry.ts 用 JSON-RPC 错误码(INVALID_PARAMS=-32602TOOL_NOT_FOUND=-32001INTERNAL_ERROR=-32603)落地了这一思想。

返回要 token 经济:别塞整页 JSON。

  • 工具返回的每个 token 都会进 transcript、占下一步上下文预算、按价计费——这直接接回 B1 的 cost 度量。
  • 返回整页原始 JSON 既贵又稀释信号;设计上要做「服务端裁剪」:只回模型需要的字段。
  • 例:assess_structuring 只回 {flagged, justUnderCount, threshold, reason} 而非全部原始交易明细。
  • 这与 Day 21 的 context engineering 一脉相承——上下文是稀缺资源,工具返回是其主要污染源之一。

每个工具都需 JSON-Schema 入参约束。

  • 没有 schema,模型就会乱填参数类型、漏必填字段、塞非法枚举值。
  • schema 是「让模型在调用前就被约束」的护栏:服务端在 tools/call 时按声明的 schema 先校验,校验不过直接返回 INVALID_PARAMS + 具体错误路径,而不是让坏参数流进 handler。
  • 安全维度(MCPTox 基准 2026:工具投毒攻击成功率最高 72%):schema 校验是零信任工具防线的最小演示,但生产还需权限边界、来源校验、审计——本仓装置不覆盖。
  • 参考 Anthropic《Writing Tools for Agents》(2025-09) 与 MCP 2026-07-28 spec 的 tool 定义规范。

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

今天是代码盘点日。本仓有两层「工具」实现,要分清:

(A) src/agent/mcp/tools.ts——已带 schema 的真实工具规格(3 个):

  • bpe_report(tools.ts:17):inputShape = { texts: z.array(z.string()).min(1), merges: z.number().int().min(1).max(2000).optional() },handler 调 trainBpe + tokenReport,返回裁剪后的 {vocab, totalChars, totalTokens, meanCharsPerToken}
  • paired_bootstrap(tools.ts:36):inputShape = { a: z.array(z.number()).min(1), b: z.array(z.number()).min(1) },handler 调 pairedBootstrap(a,b,{iters:2000}),返回 {deltaMean, ci95, n}
  • assess_structuring(tools.ts:49):inputShape = { deposits: z.array(z.number().positive()).min(1), thresholdUsd: z.number().positive().optional() },handler 返回 {flagged, justUnderCount, threshold, reason}——典型的「token 经济返回 + 结构化结论」。
  • 三个工具全部有 Zod inputShape(注释明示「exposed to MCP clients as a JSON Schema and validated on every tools/call」),即覆盖率 3/3 已带 schema。

(B) src/agent/mcp/toolRegistry.ts——MCP 协议形状的教学装置(schema 校验引擎):

  • McpToolRegistry.register(spec, handler)(toolRegistry.ts:151):要求 spec.inputSchema.type === 'object',重名抛错,名字须匹配 ^[a-zA-Z][\w.-]*$
  • validate(schema, value)(toolRegistry.ts:112):递归校验 type / enum / minLength / minimum / maximum / required,返回错误路径数组(空 = 通过)。
  • call(name, args)(toolRegistry.ts:183):先 validate,不过抛 McpCallError(INVALID_PARAMS),handler 抛错包成 INTERNAL_ERROR——这正是「结构化错误 {code, message, data}」的落地。
  • handle(req)(toolRegistry.ts:199):JSON-RPC 2.0 dispatch,永不抛错,所有失败折成 response.error(符合 JSON-RPC 语义)。
  • 诚实标注(文件头):这是「教学模拟」,实现的是 MCP 2026-07-28 stateless 语义的协议形状(JSON-RPC 信封 + tools/list + tools/call + schema 校验),不是网络 server——没有 HTTP transport、没有 OAuth、没有 Tasks 扩展、没有 MCP Apps。真实 Streamable-HTTP MCP server 属 B6 待建

三工具契约速查表:

工具入参 schema (Zod)返回(token 经济裁剪后)
bpe_reporttexts: string[]≥1, merges?: int 1..2000{vocab, totalChars, totalTokens, meanCharsPerToken}
paired_bootstrapa: number[]≥1, b: number[]≥1{deltaMean, ci95, n}
assess_structuringdeposits: number[]>0, thresholdUsd?: >0{flagged, justUnderCount, threshold, reason}

一次 schema 拒绝的报文形状(handle 返回): 若对 assess_structuring{deposits: "9000"}(应为数组),validate 返回 ["$.deposits: expected array, got string"]handle 折成:

{ "jsonrpc": "2.0", "id": 1,
  "error": { "code": -32602, "message": "invalid params for assess_structuring",
             "data": ["$.deposits: expected array, got string"] } }

模型读到 code:-32602(INVALID_PARAMS)+ data 里的具体路径,就能定位「deposits 该填数组」并自我修正——这就是「结构化、可被模型读懂的错误」落到报文上的样子。

盘点结论:本仓 in-process 工具规格(tools.ts 的 3 个)已**全部带 schema(3/3)**且返回结构化、token 经济;校验/结构化错误由 toolRegistry.tsvalidate + McpCallError 提供。真实网络 MCP server 仍是 B6 待建。

3. 今日实战

  1. Read src/agent/mcp/tools.tssrc/agent/mcp/toolRegistry.ts,数清缺 schema 的工具数,算覆盖率 n/总数(本仓 tools.ts 为 3/3)。
  2. 核对每个工具的入参约束是否完整(bpe_reportmerges 上限 2000、assess_structuringdeposits 须为 positive 等)。
  3. 核对错误返回是否结构化:toolRegistry.tsMcpCallError(code, message, data) + JSON-RPC {code, message, data} 信封即 {error, code, retryable} 思想的落地。
  4. 用一个非法入参(如给 bpe_reportmerges: 5000 越上限、或 texts: [] 空数组)走 registry.call/handle,确认返回 INVALID_PARAMS + 具体错误路径而非崩溃。
  5. 检查返回是否 token 经济:三工具均只回裁剪后的标量/小对象,无整页原始 JSON。
  6. 把覆盖率数字(n/总数)写入笔记;明确「真实网络 MCP server 属 B6 待建,本日只补 in-process schema」。

为什么本仓同时有 Zod(tools.ts)和手写 validate(toolRegistry.ts)? 两者服务不同层:tools.ts 的 Zod inputShape 是「给真实 MCP server 暴露成 JSON Schema 并在 tools/call 校验」的生产路径;toolRegistry.ts 的手写 validate 是浏览器内教学装置,演示「schema → 校验 → JSON-RPC 错误」的协议形状(不引入运行时依赖)。两条路径都落地了「调用前先按 schema 约束」这一条原则。

4. 今日实测 / 产出

  • 产出:toolRegistry schema 覆盖率数字 (n/总数) 写入笔记。
  • 状态待跑(盘点本地即可,无需 key);本日盘点真实文件得出当前覆盖率——src/agent/mcp/tools.ts 的 3 个工具规格已全部带 Zod inputShape(3/3)
  • 真实网络 MCP server 为 B6 待建,本日只补 in-process schema,不实现 HTTP transport / OAuth / Tasks 扩展。

5. 常见误区 / 陷阱

  • 工具名含糊(check/process/handle)。 模型按名字选工具,含糊名 = 选错工具,要动词+对象。
  • 返回整页原始 JSON。 贵、稀释信号、污染上下文;服务端裁剪只回需要的字段。
  • 抛裸异常当错误返回。 模型读不懂栈,无法自我修正;要 {error, code, retryable} 结构化错误。
  • 把 in-process mock 当成真实 MCP server。 本仓是协议形状的教学装置,没有 transport/授权/跨进程;真实 server 是 B6 待建,勿在状态上升级
  • 以为有了 schema 校验就安全。 schema 只挡「格式非法」,挡不住「语义投毒」(恶意工具描述诱导模型)。MCPTox 基准 2026 显示工具投毒成功率最高 72%,生产需权限边界 + 来源校验 + 审计,本装置不覆盖。

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

  • Anthropic, Writing Tools for Agents(Building tools for AI agents)— 2025-09(工具契约/命名/结构化错误/token 经济,本日机理主线)
  • Model Context Protocol — Specification — 2026-07(最终规范 2026-07-28 定稿,RC 2026-05-21 锁定 stateless core)
  • 本仓代码:src/agent/mcp/tools.ts(3 个带 Zod inputShape 的工具规格)— 2026-06
  • 本仓代码:src/agent/mcp/toolRegistry.tsvalidate / McpCallError / JSON-RPC handle)— 2026-06

SOTA检查 (2026-06 更新)

  • 当前主流方案:MCP 是 2026 工具暴露给 LLM 的事实标准协议;最终规范 2026-07-28 尚未到(今日 06-23),server 实现须排在其后再定稿。
  • 是否仍 SOTA:是。当前按 2025 草案 / RC 2026-05-21 写 schema,7-28 后须重验字段(stateless core 移除初始化握手与协议级 session 头,长任务挪到 Tasks 扩展)。
  • 过时黑名单:勿用已弃用的旧 tool-call 私有协议;勿把 MCP 教学装置当真实网络 server。
  • 下次复查点:2026-07-28 MCP 最终规范定稿当周重验 tool 定义字段;周一 WebSearch「MCP spec 2026-07」「writing tools for agents 2026」。

衔接

  • 昨天:Day 21 — Agent loop 范式(什么时候才该用回路)
  • 今天:定 agent 的工具契约——命名/JSON-Schema/结构化错误/token 经济;本仓 in-process 工具 3/3 带 schema
  • 明天:Day 23 — 评测统计基础(点估计会骗人,必须报置信区间)