跳到主要内容《Agent Runtime 工程化》第一章 Agent Runtime 全景 | 极客日志编程语言Agent Runtime工程化陈堂会
《Agent Runtime 工程化》第一章 Agent Runtime 全景
学习 Agent Runtime,第一件事不是选框架,也不是抄一个 tool calling 示例。要先弄清楚:当一个 Agent 从“能回答”走向“能行动”,系统到底多承担了哪些责任。 很多团队第一次做 Agent,架构图都很相似:用户输入,调用模型,模型说要用工具,程序执行工具,再把结果塞回模型。这个图没有错,但它太薄。它像把报社写成“记者采访,编辑发稿
站点编辑3 浏览

书名:《Agent Runtime 工程化:从工具调用循环到可恢复执行系统》
作者:陈堂会
平台:极客日志(https://zeeklog.com)
X:@Megick_com
联系邮箱:[email protected]
章节:第一章 Agent Runtime 全景
第一章 Agent Runtime 全景
学习 Agent Runtime,第一件事不是选框架,也不是抄一个 tool calling 示例。要先弄清楚:当一个 Agent 从'能回答'走向'能行动',系统到底多承担了哪些责任。
很多团队第一次做 Agent,架构图都很相似:用户输入,调用模型,模型说要用工具,程序执行工具,再把结果塞回模型。这个图没有错,但它太薄。它像把报社写成'记者采访,编辑发稿'。真正的编辑部还要选题、核实、排版、审稿、撤稿、归档和纠错。Agent Runtime 也是这样:工具调用循环只是骨架,工程化 runtime 负责让它能在真实场景里站稳。
本章先建立全书的地基。读完后,你应该能把 Agent、Workflow、Tool、Runtime 分清楚;能说出一次 run 的关键环节;能解释一个 Agent 为什么会卡死、乱调工具、爆 token、越权或无法恢复;也能判断某个问题应该靠 prompt 修,还是应该回到 runtime 设计里修。
1.1 Agent、Workflow、Tool、Runtime 的边界
先把四个词摆正。许多工程讨论之所以绕,是因为大家用同一个词指不同东西。
Agent 是面向目标的执行体。它根据上下文选择下一步,可能直接回答,也可能调用工具。Agent 的核心不是'像人',而是'根据状态作决策'。如果一个系统只是在固定流程里调用一次模型,它未必是 Agent;如果它能根据观察结果调整下一步,就开始具备 Agent 的形态。
Workflow 是预先编排好的流程。它可以包含模型节点,但节点顺序通常由工程师写死,或由状态机明确控制。可靠系统常常会把 Agent 和 Workflow 混合使用:确定性强的部分交给 workflow,例如审批流、账单生成、数据同步;需要开放判断的部分交给 Agent,例如读代码、定位错误、选择排查路径。
Tool 是 runtime 暴露给模型或流程节点的外部能力,例如读文件、查数据库、执行命令、发 HTTP 请求、修改代码、创建工单。工具不是普通函数那么简单,因为它带着 schema、权限、审计、失败语义、输出策略和用户确认。
Runtime 是承载这一切的运行系统。它负责把用户意图、模型、工具、状态、权限、预算和观测串成一个可控执行过程。一个成熟 runtime 至少要回答这些问题:
- 本轮给模型哪些上下文?
- 模型输出的工具调用是否合法?
- 工具是否需要用户确认?
- 工具失败后该重试、降级、停止,还是让模型重新规划?
- 执行到第几步算失控?
- 文件已经改了,崩溃后如何恢复?
- 这次失败是模型判断错、工具错、上下文错,还是权限策略错?
普通 LLM App 主要生成内容;Agent Runtime 主要管理行动。这条线要分清。

图 1-1:Runtime 关系图谱。Agent 负责开放判断,Workflow 负责确定性编排,Tool System 提供外部能力;Runtime 把它们放进同一个可审计的执行边界里。
这里有个常见误区:把 Agent 写成一个'模型包装器',把 Workflow 写成'低级一点的 Agent'。这样理解会让设计变糊。更准确的划分是:凡是可以被工程师提前写清楚的路径,优先交给 Workflow;凡是需要模型根据上下文选择策略的地方,再交给 Agent;两者都不能绕过 Runtime,因为工具、权限、状态和 trace 都是系统责任。
| 问题 | 更像 Agent | 更像 Workflow | Runtime 必须介入 |
|---|
| 下一步是否可预先枚举 | 不完全可枚举,需要模型判断 | 可以枚举,流程稳定 | 记录选择依据 |
| 输入是否可信 | 多半不可信,来自用户或外部工具 | 通常经过前置校验 | 参数校验和边界检查 |
| 是否有副作用 | 可能有,尤其是工具调用后 | 可能有,但路径较固定 | 权限、checkpoint、审计 |
| 失败后如何处理 | 需要重新规划 | 走固定补偿分支 | 归因、恢复、trace |
如果你在设计会上说不清这四个问题,说明还没到选框架的时候。先把责任边界画清楚,再谈 LangGraph、MCP、OpenAI Agents SDK、Vercel AI SDK 或自研 runtime。
1.2 从 ReAct 到 Tool Calling,再到 Runtime
ReAct 论文提出把 reasoning 与 acting 交错起来:模型可以在最终答案之前产生行动,行动结果再反过来影响后续推理。这个思想后来进入了工具调用产品形态:模型输出结构化 tool call,应用执行工具,再把 tool result 或 observation 传回模型。
但要注意,ReAct 是一种行为范式,Tool Calling 是一种接口能力,Agent Loop 是 runtime 的控制结构。三者常被混用,却不在同一层。
| 概念 | 关注点 | 工程问题 |
|---|
| ReAct | 推理和行动交替 | 模型什么时候该想、什么时候该做 |
| Tool Calling | 模型输出结构化调用 | schema、参数校验、结果注入 |
| Agent Loop | 多轮控制循环 | 停止条件、预算、失败、状态 |
| Runtime | 整体执行系统 | 权限、观测、恢复、评测、成本 |
模型供应商提供 tool calling,并不等于你已经拥有 runtime。很多线上事故正发生在这条缝里:模型给了一个看似合理的工具调用,但参数越界;工具执行成功,却返回了过大的输出;输出被截断后,模型误读;模型继续调用写入工具,最后把错误扩大。
一个有经验的 runtime 工程师不会把'模型会调用工具'当作终点。更重要的问题是:谁验证工具参数?谁决定是否执行?谁保存执行证据?谁处理超时?谁在失败后告诉模型'这个观察是真的,这个动作不能重放'?这些问题都不属于模型本身。
1.3 一次用户请求如何变成一次 run
用户只看到一句话:'帮我修复这个测试失败。'Runtime 看到的是一条执行链。
第一步,系统要建立 run。它会记录用户输入、当前 workspace、可用工具、模型配置、权限策略、预算和 trace id。没有 run 的概念,后面就无法讨论 step、恢复和评测。
第二步,Context Builder 组装上下文。它不是把所有历史拼起来,而是选择当前目标、最近状态、相关文件、工具说明、安全策略和必要历史。上下文构建结果最好附带 debug report,说明哪些内容进入模型,哪些被摘要或丢弃。
第三步,模型生成下一步。模型可能返回最终答案,也可能返回一个或多个 tool call。Runtime 不能把模型输出当作可信事实,它要把输出当作一份待校验的计划。
第四步,Tool Validator 校验工具名和参数。工具不存在、参数缺字段、路径越界、命令不合法,都应该变成结构化 observation,而不是让进程崩掉。
第五步,Permission Gate 做权限裁决。只读工具可能自动执行;写文件需要 diff preview;联网要说明域名和用途;安装依赖要说明包名、版本和 lockfile 影响;破坏性操作默认拒绝或强确认。
第六步,Tool Executor 执行工具,并把结果归一化。工具输出不能只是一段字符串,至少要包含 status、content、metadata、duration、是否截断、是否可重试。
第七步,Message Store 把模型响应和工具 observation 写回历史。Trace Writer 同时记录 span、token、耗时、工具参数摘要、审批记录和错误归因线索。
第八步,Loop Controller 判断是否继续。模型返回 final、预算耗尽、连续重复错误、用户取消、权限拒绝、max steps 触发,都可能让 run 停止。
1.4 主循环:最小但不能缺责
for (let step = 0; step < maxSteps; step++) {
const context = await contextBuilder.build(session);
const response = await model.generate({ messages: context, tools });
if (response.type === "final") {
return response.text;
}
const calls = validateToolCalls(response.toolCalls, tools);
const approved = await permissionGate.review(calls, session.policy);
const observations = await toolExecutor.run(approved);
await session.append({ response, observations });
}
throw new Error("Agent stopped: max steps reached");
这段伪代码故意没有把逻辑藏进框架名里。Runtime 的关键责任全在其中:构建上下文、调用模型、校验工具、审批权限、执行工具、记录观察、判断停止。如果其中任何一步被省略,demo 仍然可能成功,生产系统却会变脆。
图 1-2:Agent Runtime 的主循环。模型只负责提出下一步;Runtime 负责校验、审批、执行、记录和停止。
把这段循环拆开看,每一步都有输入、输出和失败语义。
| 环节 | 输入 | 输出 | 常见失败 |
|---|
| Context Builder | session、run state、预算 | messages、debug report | 关键文件被丢弃、旧摘要误导 |
| Model Provider | messages、tools、signal | final 或 tool calls | JSON 不合法、模型超时、上下文超限 |
| Tool Validator | tool calls、registry | 合法调用或 observation | 工具不存在、参数越界 |
| Permission Gate | 调用计划、policy、preview | allow/ask/deny | 错批、漏批、审批状态未持久化 |
| Tool Executor | 已批准调用 | tool result | 超时、输出过大、副作用不确定 |
| Message Store | response、observation | 新历史状态 | 顺序错、callId 对不上 |
这张表比伪代码更接近真实工作。写 runtime 时,每个环节都应该能单独测试;线上排障时,也应该能从 trace 里看到每个环节的证据。
1.5 step、turn、run、trace、session、checkpoint
本书统一使用以下术语。术语不是装饰,它会决定团队怎么排障、怎么写测试、怎么读开源项目。
turn 是一次用户与系统的交互轮次。用户提出一个任务,是一个 turn 的开始。一个 turn 里可能发生多次模型调用和多次工具调用。
step 是 runtime 在一个 turn 内的一次推进。它可以是一轮模型决策,也可以是一个工具执行后的状态更新。一次 turn 可以有很多 step。
run 是一次完整执行,从收到用户请求到最终回答、失败或取消。长任务中,一个 run 可能跨进程恢复。
session 是用户和 Agent 的长期上下文容器。它包含历史消息、运行配置、workspace、用户偏好、权限策略等。
trace 是一次 run 的可观测记录。它不是聊天记录,而是工程记录:每个 step 的输入、输出、耗时、token、工具调用、错误、审批结果。
checkpoint 是可恢复状态的快照。它应当包含对话状态、runtime 状态、必要的文件变更信息,以及恢复时避免重复执行危险工具的标记。
这些词看似琐碎,实际是团队协作的地基。没有统一术语,就无法讨论'这个失败发生在第几个 step''是否应该从 checkpoint 恢复''trace 里看不到 tool result 是 runtime 漏记还是工具没返回'。
type RuntimeIds = {
sessionId: string;
runId: string;
turnId: string;
stepId: string;
traceId: string;
checkpointId?: string;
};
不要等系统复杂后再补 id。没有稳定 id,trace、eval、resume 和用户反馈都很难串起来。
1.6 为什么 Agent 会失控
写一个能演示的 Agent 很快。写一个能稳定跑的 Agent 很慢。慢在三件事上。
第一,模型输出不是普通函数返回值。它可能少字段、多字段、把 JSON 写坏、重复调用工具、误解工具描述,或者在工具失败后编一个像真的观察结果。Runtime 要把模型输出当作不可信输入处理。
第二,工具不是纯计算。读文件、写文件、执行命令、联网、安装依赖、调用数据库,都可能产生副作用。副作用一旦发生,就不能只靠'再问一次模型'解决。
第三,任务会变长。短任务只需要聪明;长任务需要记忆、预算、恢复和审计。Agent 进入工程场景后,常常要跑几十步,跨多个文件,经历超时、取消、权限确认和上下文压缩。
| 现象 | 表面原因 | Runtime 层真正要查 |
|---|
| 重复调用同一工具 | 模型执拗 | 是否有重复调用检测、same error stop |
| 读错文件 | 模型理解错 | repo map、文件摘要、路径校验是否清楚 |
| 爆 token | 上下文太长 | 是否有预算、摘要、工具输出降级 |
| 越权执行命令 | 模型太激进 | permission policy 是否生效 |
| 失败后编结果 | 模型幻觉 | observation 是否结构化、失败语义是否明确 |
| 中断后从头开始 | 状态丢失 | checkpoint 和 tool ledger 是否存在 |
所以,Runtime 工程师的价值不在于把模型'变聪明'。更现实的工作,是把一个聪明但不稳定的决策源,放进一个可治理的执行系统里。
1.7 实战落点:第一版 Runtime 应该先做什么
如果你准备从零写一个学习型 runtime,第一版不要贪多。先做六件事。
第一,定义统一消息结构。用户消息、模型消息、tool call、tool result 都要有明确类型,不能靠字符串拼接。
第二,定义 Tool Registry。哪怕只有三个工具,也要有 name、description、schema、riskLevel、timeoutMs、outputPolicy。
第三,写参数校验和 observation 反馈。非法参数不是程序异常,而是模型下一步需要看到的观察。
第四,加入 max steps 和停止原因。停止不是失败;不说明原因的停止才是失败。
第五,写最小 trace。先记录 model span、tool span、permission decision、duration、status。后面再接 OpenTelemetry 也不晚。
第六,保存 run state。入门阶段可以是 JSON 文件;后面再升级为数据库、checkpoint 或 event log。
这六件事做完,你得到的不是产品,但已经不是 demo。它有了 Runtime 的骨架。
1.8 本章练习
找一个你用过的 Agent 产品或开源项目,画出它的一次 run。不要画成'用户 -> 模型 -> 工具'三格图,至少要标出:
- 用户输入进入哪里;
- 上下文在哪里构建;
- 模型调用在哪里发生;
- 工具在哪里注册和校验;
- 权限审批在哪里发生;
- 工具结果如何回灌;
- trace/log 在哪里记录;
- 失败后从哪里恢复。
然后写一页分析:这个系统更像普通 LLM App,还是已经有 Runtime?证据是什么?
1.9 验收标准
读完本章,你应该能清楚解释 Agent、Workflow、Tool、Runtime 的边界;能说出 step、turn、run、trace、session、checkpoint 的区别;能解释一个 Agent 为什么会卡死、乱调工具、爆 token;也能看懂本书后续章节围绕的是同一个运行闭环,而不是一堆彼此孤立的知识点。
系列文章目录
相关免费在线工具
- Base64 字符串编码/解码
将字符串编码和解码为其 Base64 格式表示形式即可。 在线工具,Base64 字符串编码/解码在线工具,online
- Base64 文件转换器
将字符串、文件或图像转换为其 Base64 表示形式。 在线工具,Base64 文件转换器在线工具,online
- Markdown转HTML
将 Markdown(GFM)转为 HTML 片段,浏览器内 marked 解析;与 HTML转Markdown 互为补充。 在线工具,Markdown转HTML在线工具,online
- HTML转Markdown
将 HTML 片段转为 GitHub Flavored Markdown,支持标题、列表、链接、代码块与表格等;浏览器内处理,可链接预填。 在线工具,HTML转Markdown在线工具,online
- JSON 压缩
通过删除不必要的空白来缩小和压缩JSON。 在线工具,JSON 压缩在线工具,online
- JSON美化和格式化
将JSON字符串修饰为友好的可读格式。 在线工具,JSON美化和格式化在线工具,online