跳转到内容

第 15 课:Tool System 源码

从阶段二内置工具和自定义工具实践进入 Pi 源码,理解 built-in tools、schema、执行结果、错误包装、tool diff 和 tool output streaming。

Pi 的 Tool System 分成两层:coding-agent 层负责定义具体工具,agent-core 层负责按统一协议执行工具。低层 loop 不知道某个命令如何启动,也不知道编辑工具如何算 diff;它只知道如何校验参数、调用工具、接收 update、包装错误和生成 tool result message。

为什么工具的业务细节不应该散落在 agent loop 里?

  • 工具定义层描述 name、description、schema、execute 和可选渲染信息。
  • 包装层把 SDK 工具变成 core 能执行的统一工具。
  • 执行层处理参数校验、before/after hook、并行或串行策略。
  • 结果层把成功、失败和 partial update 都转成模型可读消息。

下面不是完整源码,而是把本课主线压缩成可以在文档里直接阅读的关键形状。读者即使不打开本地源码,也应该能看出运行时如何组织职责。

async function executePreparedTool(call: ToolCall) {
const tool = tools.find(call.name);
const args = validate(tool.parameters, call.arguments);
try {
const result = await tool.execute(args, { onUpdate: emitToolUpdate });
return toolResult(call.id, { ok: true, result });
} catch (error) {
return toolResult(call.id, { ok: false, error: summarize(error) });
}
}
  • 这段省略版源码展示了工具系统的公共协议。
  • 具体工具可以有自己的实现细节,但错误和结果必须统一包装,否则模型无法继续推理。
  • 如果要增加命令风险评分,应该先决定它属于工具结果、before hook 策略,还是审计事件。

为命令风险评分设计字段位置:放在工具结果、before hook 决策,还是 trace 事件里,并说明理由。

  • 能说明工具定义、包装、执行和结果四层。
  • 能解释为什么错误也要变成 tool result。
  • 能判断 tool diff 为什么属于具体工具而不是通用 loop 概念。
  1. 实践题:基于本课的省略版源码,补出一个最小实现草图,要求写清输入、输出、副作用和错误处理。
  2. 思考题:本课机制如果只靠 prompt 约束,而不放进 harness 或 runtime,会出现什么工程风险?