写作

Agent Runtime 设计笔记

原生 Tool Calling 让 Oracle 的决策更干净,但可靠性仍然取决于状态、继承、验证与恢复。

2026 年 1 月,我们把 Oracle 的 Planner 从生成 XML 改成了原生 Tool Calling。

这看起来是一次明确的现代化。模型不再先写出一段需要解析的计划文本,而是直接选择工具并提交结构化参数。中间少了一层格式约定,调用意图更清楚,理论上也能减少解析失败和不必要的推理成本。

我当时反复追问:这是不是现代 Agent 更标准的做法?如果模型已经原生支持 Tool Calling,我们是不是终于可以删掉许多自建的 Planner 逻辑?

改造完成后,答案反而变得更清楚了。

Tool Calling 确实是一种更好的模型接口。但它不是 Agent Runtime。

从格式变成协议

让模型生成 XML、JSON 或其他结构化文本,本质上是用 Prompt 发明一种临时协议。模型必须记住标签、字段和嵌套关系,系统再把文本解析成能够执行的动作。

这条路径可以工作,却会把大量复杂度放在本不重要的地方。一个缺失的标签、额外的解释或不完整的字段,都可能让一个正确的判断变成无法执行的结果。为了兼容这些变化,系统会不断增加修复、fallback 和解析分支。

原生 Tool Calling 提供了更清晰的边界:系统声明可用工具和参数,模型返回自己选择的调用。模型负责表达意图,协议负责承载意图。

这是正确的改造。它删除了偶然复杂度,也让模型与系统之间的责任更容易理解。

但结构化不等于有效。参数仍然可能为空,字段仍然可能不符合业务约束,模型也可能在错误的状态下选择一个语法完全正确的工具。协议只保证一项决定能够被表达,不能保证它值得执行。

当 Planner 也成为一个 Tool

我们还进一步尝试把 Planner 本身变成工具。

这个想法很自然。Planning 不应该只发生在任务开始之前。新材料可能推翻旧判断,某一步可能失败,用户也可能在中途改变要求。Agent 应该能够在需要时重新规划,而不是被最初的计划锁住。

当 Planner 成为 Tool,规划就从系统外部预先规定的阶段,变成 Agent Loop 中的一种能力。模型可以判断什么时候需要拆解目标,什么时候应该继续执行,什么时候需要重新组织剩余工作。

这让 Oracle 更灵活,也立即带来了另一个问题:谁来判断 Planner 是否被调用得合理?

模型可能反复规划而不行动,也可能持续调用工具却不结束。一次调用看起来合理,连续几十次调用却可能已经偏离目标。Tool Calling 并不知道任务预算、停止条件或哪些动作已经完成。

我们不得不为调用次数增加安全边界,也必须区分什么时候应该继续、暂停、重试或停止。到这里,问题已经不再是 Planner 的 Prompt 写得够不够好,而是运行系统如何约束一连串模型决定。

Runtime 从 Tool Call 结束的地方开始

一次 Tool Call 只是模型提出的行动请求。它与真正完成行动之间还有很长的距离。

Runtime 至少需要回答这些问题:

  • 这个工具在当前任务状态下是否允许调用;
  • 参数在语法正确之外,是否满足真实业务约束;
  • 调用开始前后,哪些状态必须被持久化;
  • 超时之后应该恢复、重试,还是承认结果未知;
  • 同一个动作被重复提交时,如何避免重复产生副作用;
  • 任务已经偏离、耗尽预算或完成时,谁负责让它停下。

这些问题不会因为模型原生支持工具而消失。相反,模型越容易发起行动,Runtime 的边界越重要。

Tool Calling 让决策变得便宜。Runtime 决定廉价的决策是否会变成昂贵的错误。

追问不是再追加一条消息

改造 Tool Calling 的同一时期,我们还在处理 Oracle 的中途追问。

在普通聊天里,追问通常只是把一条新消息追加到历史末尾。但 Agent 已经完成了一部分工作,产生了计划、文件和中间结果。用户在其中一个节点继续提问时,他可能是在修正原任务,也可能是在已有成果上开启一个新分支。

如果把全部历史原样塞给新任务,系统很容易重复已经完成的工作,也可能继承过时的约束。如果完全创建一个独立任务,它又会忘记此前已经获得的结果。

所以我们开始显式表达父任务与子任务之间的关系。新的分支可以拥有独立生命周期,同时继承真正相关的已完成事项、材料和要求。

这让我重新理解了 Context Engineering。它不只是把更多对话放进 Prompt,而是在决定工作的哪些部分仍然有效、哪些部分应该被继承,以及新的判断属于哪一条状态分支。

Context 不是一段文本。它是工作的 lineage。

Context Caching 也不是 Memory

我们当时还研究了模型的 Context Caching,希望降低 Planner 多轮运行时的延迟和 Token 消耗。

缓存当然有价值。相同的长前缀不必每次重新处理,可以让重复推理更快、更便宜。但缓存保存的是一段输入的计算结果,不是系统对任务的理解。

它不会知道某个文件已经失效,不会判断用户的追问属于旧任务还是新分支,也不会决定哪个状态才是当前事实。把更多历史命中缓存,只能更高效地重复那段历史。

Memory 的问题是应该记住什么,Context 的问题是当前应该使用什么,Caching 解决的则是重复计算能否更便宜。它们相关,却不是同一层能力。

如果把缓存当成 Memory,系统可能获得更低的延迟,却保留同样混乱的状态。

原生 Tool Calling 仍然重要

看到这些问题,并不意味着改造没有价值。

原生 Tool Calling 给模型和系统之间提供了一条更干净的接口。Planner 不再需要用格式技巧伪装成可执行结构,工具也可以拥有明确的参数与结果。它让我们能够把注意力从解析器转移到真正重要的运行问题上。

从这个意义上说,它最大的价值不是直接让 Oracle 成为 Agent,而是暴露了 Agent 仍然缺少什么。

当格式问题被拿走之后,剩下的是状态权威、执行生命周期、上下文继承、失败恢复、预算和停止条件。这些才是 Runtime 的工作。

Agent 不是一个支持工具的模型

一个模型支持 Tool Calling,只说明它能够提出结构化行动。一个 Agent System 还必须让这些行动在时间中保持一致,并对它们产生的后果负责。

模型需要判断,Runtime 需要保存事实;模型可以选择下一步,Runtime 必须知道上一步究竟有没有发生;模型可以重新规划,Runtime 必须确保新计划不会无意间复制旧世界。

真正现代的 Agent,不是 Prompt 更短、Tool Call 更原生,或者模型能够连续调用更多工具。它的现代性来自职责被放在正确的位置:

模型负责提出决定,Runtime 负责让决定成为可靠行动。

Tool Calling 解决的是“模型想做什么”。

Agent Runtime 解决的是“这件事如何真的发生,并且发生之后系统仍然知道自己在哪里”。

文章数据

次阅读条评论

讨论

评论区

关于文章本身,也欢迎留下不同意见或补充。

留下评论

评论会立即公开,并可在当前浏览器中删除。

发布时会进行一次隐私友好的人机验证。