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=-32602、TOOL_NOT_FOUND=-32001、INTERNAL_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_report | texts: string[]≥1, merges?: int 1..2000 | {vocab, totalChars, totalTokens, meanCharsPerToken} |
paired_bootstrap | a: number[]≥1, b: number[]≥1 | {deltaMean, ci95, n} |
assess_structuring | deposits: 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.ts 的 validate + McpCallError 提供。真实网络 MCP server 仍是 B6 待建。
3. 今日实战
- Read
src/agent/mcp/tools.ts与src/agent/mcp/toolRegistry.ts,数清缺 schema 的工具数,算覆盖率 n/总数(本仓tools.ts为 3/3)。 - 核对每个工具的入参约束是否完整(
bpe_report的merges上限 2000、assess_structuring的deposits须为 positive 等)。 - 核对错误返回是否结构化:
toolRegistry.ts的McpCallError(code, message, data)+ JSON-RPC{code, message, data}信封即{error, code, retryable}思想的落地。 - 用一个非法入参(如给
bpe_report传merges: 5000越上限、或texts: []空数组)走registry.call/handle,确认返回INVALID_PARAMS+ 具体错误路径而非崩溃。 - 检查返回是否 token 经济:三工具均只回裁剪后的标量/小对象,无整页原始 JSON。
- 把覆盖率数字(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.ts(validate/McpCallError/ JSON-RPChandle)— 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 — 评测统计基础(点估计会骗人,必须报置信区间)