Agent Engineering 101|08|Tool Runtime

Function calling 只是模型表达工具意图的语法。Tool Runtime 才是工具执行、参数校验、错误反馈、并发和权限边界所在。

Agent Engineering 101|08|Tool Runtime

核心结论

配套代码仓库: github.com/llm-101/mini-agent-harness

读完本文,你应该能回答

  • function calling 为什么不等于完整工具系统?
  • 工具调用从模型输出到结果回灌经过哪些步骤?
  • schema、校验、权限、错误和并发分别在哪里处理?
  • 工具结果为什么必须重新进入上下文?

本篇在系列中的位置

  • 上一篇:07 Tree History 说明了行动历史如何组织。
  • 本篇:本文解释模型意图如何变成真实世界里的工具执行。
  • 下一篇:09 Context Builder 会说明下一轮模型调用如何看到工具结果和工作集。

贯穿例子

本系列会反复使用同一个任务来连接各章:

用户说:“帮我修复这个 repo 里的 failing tests。”

在这个任务里,Tool Runtime 负责校验 read/bash/edit 的参数,执行真实文件和命令操作,捕获 stdout/stderr/error,并把结果变成下一轮模型能使用的 tool result。读者要关注的是:function calling 只是意图格式,工具生命周期才是真正的工程边界。

  • Tool Runtime 是模型意图和真实世界动作之间的边界。它把 tool name、arguments 和 schema 转换成受控执行,并把结果重新包装成 ToolResult。
  • 模型输出 tool call 并不等于工具已经安全执行。真实系统必须决定工具是否存在、参数是否完整、在哪个 cwd 执行、错误如何回传、是否并行、结果如何截断。
  • mini-agent-harness 中,Tool Runtime 把模型意图和真实副作用隔开:模型提出动作,runtime 负责校验、执行、记录和回灌。

定义

Tool Runtime 是模型意图和真实世界动作之间的边界。它把 tool name、arguments 和 schema 转换成受控执行,并把结果重新包装成 ToolResult。

为什么要单独看这一层?

工具是模型接触真实世界的地方。读文件、运行命令、修改代码都可能失败,也可能有风险;因此工具执行必须有自己的生命周期和治理边界。

Tool Runtime Boundary

边界

这一层的职责可以拆成几个稳定部分:

  • Tool Schema:name、description、parameters 暴露给模型
  • ToolRegistry:注册与列出工具
  • Validation:required 参数缺失先返回 isError
  • Execution Context:cwd 与 AbortSignal 约束执行环境
  • Error Capture:异常转成 ToolResult,而不是炸出 loop
  • Execution Mode:sequential 或 parallel,结果顺序稳定

这一层的边界可以用一个问题检验:如果它的内部实现变化,模型适配、工具执行、状态存储和产品外壳是否都不需要跟着重写?如果答案是否定的,说明这个边界还没有真正收束变化。

代码锚点

本篇主要对应这些模块:

  • src/tools/tool-runtime.ts
  • src/tools/builtin.ts
  • src/tools/file-mutation-queue.ts
  • src/tools/truncate.ts

阅读代码时建议先看类型,再看运行路径。

类型定义告诉你这一层暴露什么 contract;运行路径告诉你这个 contract 在 Agent 执行中何时被消费、何时被写回、何时被产品层看见。

运行流程

Tool Call to Tool Result

一次典型执行可以概括为:

  1. Model Intent:toolCall content
  2. Policy / Validate:schema required
  3. Execute:tool.execute(input, context)
  4. Wrap Result:content + isError
  5. Return Context:ToolResultMessage
  • File Tools:read/write/edit/find/grep/ls
  • Shell Tool:bash in cwd
  • Mutation Queue:文件写入串行化

这里最容易被忽略的是“中间态”。生产级 Agent 不是只关心最终答案;它还要在运行过程中展示进度、捕获错误、记录 usage、允许取消,并把可恢复状态写回 session。

读者抓手:工具调用生命周期

阶段 输入 Harness 职责 输出
Declare 工具定义 暴露 name、description、schema 模型可见的工具列表
Propose 模型输出 解析 tool call 意图 ToolCallContent
Validate 参数 schema 校验、路径/权限检查 可执行请求或拒绝原因
Execute 请求 调用外部世界,捕获 stdout/stderr/error ToolResult
Record 执行结果 写回 session,发出 event 下一轮上下文材料
Govern 高风险动作 审批、plan mode、policy hook allow/deny/ask

Function calling 只覆盖 Propose 的语法。真正的 Tool Runtime 覆盖的是整条生命周期。

可迁移伪实现:工具执行生命周期

下面的伪代码是机制抽象,不对应真实 API 或文件结构。它只用来说明这一层的控制点:

const validationError = validateRequiredParams(tool, input);
if (validationError) return { content: validationError, isError: true };
try {
  return await tool.execute(input, context);
} catch (error) {
  return { content: String(error), isError: true };
}

这个草图的价值在于说明控制点,而不是提供可复制的库代码。

真正的工程实现还要处理错误、取消、并发、token 预算、日志、权限、序列化和 provider 差异。

工程原则

将这一层从 Agent 系统中拆出来,通常带来四个直接收益。

第一,可替换。

外部系统、模型 provider、工具集合或产品外壳变化时,核心运行时不必整体重写。

第二,可观测。

边界清晰后,事件、trace、usage、错误和状态迁移都有稳定落点。

第三,可恢复。

只要状态写入 session,运行时就可以在进程重启、工具失败或长任务中断后继续推理。

第四,可治理。

权限、脱敏、审批、路径保护和执行策略可以放在稳定 hook 或 runtime boundary 上,而不是只靠 prompt 约束。

和 Agent Harness 的关系

Agent = Model + Harness 这个公式的重点,不是把模型之外的所有东西都称为“工程杂活”。

相反,它提示我们:模型之外存在一套必须被设计的运行时系统。

Tool Runtime 就是这套系统中的一个切面。它不替代模型能力,也不替代产品体验;它让模型能力可以被组织成可执行、可观察、可恢复、可治理的任务流程。

小结

Tool Runtime 的核心价值,是把一类容易扩散的复杂性收束到明确边界中。

mini-agent-harness 中,这个边界被刻意写得较小,方便阅读和教学。但它对应的问题并不小:只要一个 Agent 要长期运行、调用工具、管理上下文、支持 UI、保存状态并处理失败,这个边界就会出现。

下一步可以继续沿着系列计划,把这些边界组合成完整 Agent Harness:模型边界、工具边界、状态边界、上下文边界、扩展边界和产品外壳边界。

参考资料

  • OpenAI Agents / Responses API 文档:用于理解现代模型调用、工具调用和结构化响应的运行时边界。
  • Anthropic Claude Messages / Tool Use / Agent Skills 文档:用于理解 content blocks、tool_use/tool_result、stop reason、Skills 与长任务上下文管理。
  • Model Context Protocol Specification:用于理解工具、资源和 prompts 的连接层边界。
  • Martin Fowler, “Agent = Model + Harness”:用于理解模型之外的 harness 概念。