Agent 五大工程体系:Prompt、Context、Loop、Graph 与 Harness
本文面向已经接触 LangChain、LangGraph、MCP 或 Claude Code 的开发者。文章提出一套用于分析 Agent 系统的工程框架:Prompt、Context、Loop、Graph 与 Harness。它不是官方标准,也不是要求所有系统都采用相同实现,而是一种帮助我们划分控制对象、定位故障和理解 Agent Runtime 的工程视角。
一张图先看懂:五大工程如何合成一个 Agent Runtime
!tip 先记住这条主线 Harness 提供运行边界 → Graph 组织工作节点 → 每个节点运行 Loop → 每轮 Loop 构造 Context → Context 中组织 Prompt → 模型行动并接受验证;验证不通过,就把缺口带回下一轮。
其中,Graph 是空间结构 ,负责"怎么拆、怎么连";Loop 是时间结构 ,负责"何时继续、何时停止"。它们不是互相替代,而是正交组合:一个 Graph 可以被外部
/loop触发,而 Graph 中的每个节点又可以拥有自己的内部 Loop。

读图只需要抓住四层关系
text
Harness:给系统提供边界、状态、安全、观测和呈现
↓ 承载
Graph:把任务拆成节点,用数据依赖连接节点
↓ 放大一个节点
Loop:让节点通过"观察 → 行动 → 验证"持续尝试
↓ 每一轮都要构造输入
Context:选择、压缩、隔离这一轮模型需要知道的信息
↓ 在选中的信息中组织语言
Prompt:规定模型应该如何理解任务和输出结果
!important 最容易混淆的地方 Graph 和 Loop 不是上下级替代关系,而是两个方向:Graph 是空间,Loop 是时间。 Graph 决定"有几个节点以及它们如何连接";Loop 决定"某个节点这一轮失败后是否继续"。因此图中只展开了一个节点的内部 Loop:真实系统可以有很多节点,而每个节点都可以拥有不同的 Prompt、Context 和 Loop 策略。
目录
- 第一部分:为什么需要这张地图
- 第二部分:五层总览
- 第三部分:逐层详解(含技术归位)
- 第四部分:层级关系
- 第五部分:贯穿示例
- 第六部分:常见混淆点
- 第七部分:学习路径
- 第八部分:Agent Runtime
第一部分:为什么需要这张地图
1.1 问题的起点:一个真实的困惑
"到底什么是上下文工程?什么又是提示词工程?memory、skill 都算提示词工程吗?"
这个困惑几乎每个人都会遇到,因为它不是知识量的问题,而是缺少分层视角。当你学过的机制越多------MCP、Memory、Skill、Hook、Workflow、MCP Tasks------就越会发现它们看起来都是"往上下文里塞文本",于是所有东西糊成一片。
但它们的控制对象完全不同。一个机制属于哪一层,取决于它"动的是什么":
- 动的是文本措辞 → 提示词工程
- 动的是哪些内容进入模型的眼睛 → 上下文工程
- 动的是运行的时机与停止条件 → 循环工程
- 动的是多个工作单元的连接结构 → 图工程
- 动的是运行环境与运营机制 → Harness 工程
用这个透镜回看你的所有学习板块,每一块都能归位。
1.2 为什么现在必须分层
在 2022-2023 年,Agent 就是"一个模型 + 一段提示词",分不分层无所谓。但现在一个真实 Agent 系统包含:记忆系统按需注入、工具结果回流、子代理各持独立上下文、定时器反复触发、多节点并行验证、跨进程恢复任务句柄......
当"输入内容"本身是动态的,优化表达(提示词)就只是整个输入设计(上下文工程)的一个子集;当"运行"本身是持续的,单轮执行(Agentic Loop)就被循环与图这两层包裹。 分层的意义不是学术分类,而是定位问题的工具:系统出故障时,你得知道该修哪一层。
第二部分:五层总览------一个控制对象的透镜
2.1 五层一览表
| 层 | 一句话定位 | 控制对象 | 核心问题 | 典型机制 | 该层薄弱的症状 |
|---|---|---|---|---|---|
| Prompt | 说得好 | 文本措辞 | 怎么写,模型更易理解与服从? | 指令措辞、角色、few-shot、CoT、格式约束 | 单次输出质量差、不守格式 |
| Context | 知道什么 | 信息构成 | 什么内容该进、该砍、何时进、永不进? | 检索、压缩、注入时机、隔离 | 幻觉、遗忘、Token 浪费 |
| Loop | 何时再跑 | 时间维度 | 什么时候再次运行?凭什么停止? | 四类循环、验证器、安全阀 | 过早停止或无限循环 |
| Graph | 怎么组织 | 空间维度 | 工作如何拆成节点、连边、并行、验证、收敛? | 节点契约、数据依赖边、fan-out/fan-in、路由 | 串行慢、重复劳动、结果不可验证 |
| Harness | 如何运营 | 系统维度 | 怎么让结构安全、持久、可观测地运行? | 权限、状态存储、调度、观测、评估 | 出事故没人知道、不可恢复、黑箱 |
!important 读表方法 不要背表格,要练习"归位":看到一个机制(比如 Auto Memory),先问"它控制什么?"------它决定哪些用户偏好进入上下文,动的是内容构成,所以是 Context 层,哪怕它以文本形式存在。
2.2 嵌套关系速览
五层是包含关系,从内到外一层套一层:
- Prompt ⊂ Context:措辞只是"已经选定的内容"里的表达优化。
- Context ⊂ 单轮执行:上下文是每一轮模型的输入构成。
- Loop 与 Graph 分别从时间与空间两个维度操作这个内核:Loop 决定"一轮结束之后怎么办",Graph 决定"多个执行单元怎么摆"。
- Harness 包裹一切:给上面所有层提供运行环境。
第三部分:逐层详解(含技术归位)
!example 本部分的结构 每一层先讲"它控制什么",然后把具体技术逐个归位------不仅说"属于哪层",还说清"它在这层里具体控制什么、边界在哪"。关系架构图见 Agent五大工程体系.技术归位图,本文是图的可读版。
3.1 Prompt Engineering ------ 控制"怎么说"
核心问题:这段指令怎么写,模型更容易理解与服从?
它操作的对象是上下文窗口内的文本措辞:怎么把任务说清楚、怎么设角色、怎么给示例、怎么约束输出格式。所有技巧------指令清晰化、few-shot、CoT、角色扮演、格式约束------都在"文本表达"这个平面上。
具体技术归位:
| 技术 | 在 Prompt 层的具体作用 | 边界(它不负责什么) |
|---|---|---|
| SKILL.md 的指令内容 | skill 被加载后,其中的指令文本如何措辞、如何组织步骤、如何给示例 | 不负责"什么时候被加载"(那是 Context 层) |
| CLAUDE.md 的规则措辞 | 项目规则如何用语言写清楚,模型才容易遵守 | 不负责"什么时候注入、注入多少"(那是 Context 层) |
| few-shot / 角色设定 / CoT | 通过示例与思维链引导单次输出的质量 | 示例本身是内容,进入上下文属于 Context 层;措辞优化才是 Prompt 层 |
| 输出格式约束(文本层面) | "请用 JSON 返回"这类指令性约束 | 强制的 Schema 校验是运行时行为,属于 Graph 层(节点契约) |
关键判断 :同样一段指令,"怎么写"是 Prompt 层;"这段指令进不进得了上下文"是 Context 层。SKILL.md 正好横跨两边------内容属 Prompt,description 触发属 Context。这就是你觉得"skill 算提示词工程"的由来,但它只是半边。
它的边界 :措辞再好,内容错了也白搭。一个把无关文件塞满上下文的模型,提示词写得再漂亮也会跑偏。Prompt 优化的是"在给定内容上的输出质量",它不负责内容本身。
3.2 Context Engineering ------ 控制"模型知道什么"
核心问题:什么内容该进上下文?什么该砍掉?何时注入?什么永不进入?
它操作的对象是上下文窗口的构成,有四个动作:选什么进来(检索/注入)、压缩什么(对于进入上下文窗口的内容选择如何处理)、何时注入、隔离什么。
具体技术归位:
| 技术 | 在 Context 层的具体作用 | 关键机制(你学过的) |
|---|---|---|
| Memory 记忆系统 | 决定哪些历史信息进入上下文:指令记忆、长期记忆、工作记忆、摘要记忆、AutoDream | 六维记忆体系、四层优先级注入、@include 递归、双阈值摘要触发 |
| RAG / 检索注入 | 从外部库检索与任务相关的片段进入上下文 | 向量检索、Top-K 选择、查询改写 |
| CLAUDE.md / @import | 决定项目规则何时注入、注入多少;<200 行的压缩策略 | 启动注入、模块化导入、双轨注入(系统提示 + 上下文) |
| Skill 触发机制 | description 是"瘦入口"------决定哪个 skill 被加载进上下文,而非一次全装 | Skills 瘦入口、按需加载 |
| Compaction / 摘要 | 长对话压缩为摘要,腾出窗口空间 | 断点续传、压缩边界算法 |
| 工具结果截断/摘要 | 大输出(文件、日志、命令结果)截断或摘要后再进入上下文 | 有意图压缩 |
| 子代理上下文隔离 | 子代理只继承父级需要的部分,不继承全部------并行任务互不污染 | 隔离给子代理、worktree 隔离 |
| 上下文窗口(模型层) | 窗口大小决定可承载的信息总量------这是硬约束 | 启动账本:请求前 ~7850 token 的构成审计 |
!warning 为什么 Prompt 和 Context 最容易混 因为它们在操作层面都是"写文本"。判断标准只有一条:你是在优化一段已存在文本的措辞(Prompt),还是在决定哪些文本进入模型的眼睛(Context)? 一个 Skill 正好横跨两边:SKILL.md 的内容是 Prompt 层,而"description 决定何时被加载"是 Context 层。
3.3 Loop Engineering ------ 控制"何时再次运行、何时停止"
核心问题:Agent 什么时候再次运行?它凭什么知道已经完成?
它操作的对象是时间维度。一次 Agentic Loop(观察→推理→工具→验证→继续/结束)是单轮的执行结构;Loop Engineering 研究这轮之后怎么办,即 Loop Engineering 与 Graph Engineering:从持续执行到图结构编排 的整个前半部分。
具体技术归位:
| 技术 | 在 Loop 层的具体作用 | 关键机制 |
|---|---|---|
/goal(Goal-based) |
Agent 自己按"继续"键,按到可验证证据出现或触及 maxTurns | 验证器与执行者分离、完成定义 |
/loop(Time-based) |
定时器按"继续"键,到点重新观察外部世界 | intervalMs、先观察后行动、外部完成才停 |
| Turn-based(普通对话) | 人类按"继续"键------最原始的循环 | 人驱动回合制 |
Proactive / /schedule |
外部事件或云端计划触发------无人值守 | 事件驱动、云端调度 |
| 验证器 / 评估器 | 独立于执行者的客观检查:测试命令、脚本、规则引擎、另一模型 | 不信任 Agent 自报完成(claimedDone) |
| 安全阀(maxTurns / Token 预算) | 防止目标达不到时无限循环 | 到达上限 ≠ 成功,诚实报告缺口 |
| loop-until-dry(收敛循环) | 直到连续 N 轮无新发现才停止 | seen 去重、dryRounds 计数、去重对象必须是全量 seen |
| 幂等设计 | 重复执行不重复创建资源、不重复发消息 | 幂等键、状态检查 |
和 Graph 的分工 :Loop 回答"何时 ",Graph 回答"如何组织 "。同一个 loop-until-dry 循环里,循环本身是 Loop 层的,循环内部并行搜索的多个 finder 是 Graph 层的。
3.4 Graph Engineering ------ 控制"多个节点如何组织"
核心问题:工作如何拆成节点?边是什么?如何并行、路由、验证、收敛?
它操作的对象是空间维度------多个工作单元的连接结构。==关键认知:节点 = 边界清晰的工作单元;边 = 数据依赖(不是时间顺序);拓扑 = fan-out / fan-in / diamond / 路由;验证门 = 边上的验证器。==
具体技术归位:
| 技术 | 在 Graph 层的具体作用 | 关键机制 |
|---|---|---|
| SubAgent | 一个节点:独立上下文、独立 system prompt、结构化返回 | 注册→匹配→启动→执行→返回 五阶段 |
| Agent Team | 节点之间的协作层:直接通信、任务共享、审批链 | SendMessage 协议、Leader-Worker / Peer / 评委组、Veto Power |
| Workflow(agent/parallel/pipeline/phase) | 图运行的编排原语:fan-out、barrier、pipeline、loop | parallel 是 barrier;pipeline 无 barrier;phase 分组 |
| Worktree | 并行修改的写隔离:每个节点在独立工作树 | 需要并行写入时才加隔离成本 |
| 节点契约(Schema) | 节点输入输出的机器可验证边界 | JSON Schema / TypeScript 类型、bounded input/output |
| 路由 | 分类节点决定后续路径,模型判断 + 代码控制边 | 风险分级走不同路径 |
| 验证门(验证节点) | 边上/汇点前的独立验证器,尝试反驳上游结果 | 多视角验证、多数投票 |
| LangGraph | 图运行时/框架:状态图、条件边、checkpoint、interrupt | 是"图运行时的实现",不等于 Graph Engineering 本身 |
为什么"Agent 越多不一定越强":图结构只有在节点边界、数据契约和验证门都清楚时才产生收益。每个小步骤都调模型,是让代码能干的活付推理成本;没有结构化输出,下游节点仍要猜测上游文本。
3.5 Harness Engineering ------ 控制"系统如何运营"
核心问题:怎么让上面所有层安全、持久、可观测、可交互地运行?
它操作的对象是系统维度------运行环境与运营机制。一个成熟 Harness 的七件套(Execution、Orchestration、State、Evaluation、Safety、Observability、Presentation)。
具体技术归位:
| 技术 | 在 Harness 层的具体作用 | 对应七件套 |
|---|---|---|
| MCP Server / Client | 外部能力的标准接入:工具、资源、提示词;Host 与 Server 的边界 | Execution |
| MCP Tasks | 长任务的时间形态:taskId、进度、审批、恢复、durable store | State |
| MCP Apps | 运行状态的空间形态:UI Resource、Host/App 通信、安全边界(iframe/CSP/AppBridge) | Presentation |
| Hook 事件系统 | 生命周期钩子:PreToolUse 拦截危险命令、Stop/SubagentStop 审计 | Safety + Observability |
| Plugin | 能力的打包分发:skills/agents/hooks/MCP/LSP/monitors 的统一清单 | Execution + 分发 |
| Sandbox / 权限模式 | 命令与工具的执行边界:只读/自动/危险操作的隔离 | Safety |
| Session 管理 / 断点续传 | 会话级状态保存与恢复 | State |
| Agent View | 后台会话的观测:状态机、attach、恢复、清理 | Observability |
| Scheduler / Cron | 时间与事件触发器(Proactive 循环的载体) | Orchestration |
| Evals / 评测 | 完成证据、验证输出、拒绝错误结果 | Evaluation(当前缺口) |
!important Harness 与 Graph 的边界 Graph 是设计图 (节点怎么连、数据怎么流);Harness 是厂房(水电、安全规程、监控室、应急预案)。同一个 Graph 可以跑在没有 Harness 的玩具环境里,也能跑在完整的 Runtime 上------后者才可运营。这也是你求职目标(Agent Harness/Runtime)的核心所指。
第四部分:层级关系------嵌套与流向
4.1 一次请求如何穿过所有层

读图方式:外层负责"决定",内层负责"执行"。Harness 决定这次运行是否允许、花多少预算;Graph 决定跑哪个节点组合;每个节点的内部是一次 Loop;每一轮 Loop 先构建上下文;上下文里最终拼出一段提示词交给模型。输出回来后,验证器(Loop 层)决定是否再来一轮;最终结果回到状态存储和观测系统(Harness 层)。
4.2 五层的控制权迁移
这张图还揭示了 Agent 工程的历史主线------控制权从内层向外层迁移:
| 时代 | 谁在控制 | 对应的工程层 |
|---|---|---|
| 2022-2023 | 提示词(人写) | Prompt |
| 2024 | 检索与 RAG | Context |
| 2024-2025 | 目标与定时器 | Loop |
| 2025-2026 | 编排结构 | Graph |
| 现在 | 运行时与运营 | Harness |
每次迁移,都是把原来由"人的注意力"承担的控制,交给系统承担。这也是面试时解释"为什么现在需要 Agent Runtime"的最有力叙事。
4.3 层与层的正交关系
嵌套不是唯一的连接方式,有两对关系是正交的:
- Loop × Graph(时间 × 空间) :一个 Graph 可以被
/loop定时触发;Graph 里的循环节点(loop-until-dry)是 Loop 思想在 Graph 内部的体现。两者回答不同维度的问题,组合使用:/loop触发 → Graph 执行 → 每轮按/goal风格验证。 - Context × Graph :每个节点有自己的上下文 (隔离是 Context 层动作),边上传的是结构化数据而非大段文本------Graph 的结构设计直接影响 Context 的规模。巨大共享 State 之所以是反模式,就是因为它在 Context 层制造了隐式依赖。
第五部分:贯穿示例------知识库健康检查 Agent
把五层放在同一个真实任务上,看它们如何各司其职。任务:每天定时扫描知识库的死链、缺失 Frontmatter 和结构异常,报告新增问题。
5.1 五层设计

- Harness 层 :
/loop每日触发(Loop 层技术在此充当触发器);权限为只读 + 批量修改必须人工确认(Sandbox + Hook);Token 预算上限;每轮日志与结果入库(MCP Task 句柄支持跨天恢复);审批界面(MCP Apps)。 - Graph 层:三个目录并行扫描(SubAgent 节点,无依赖 fan-out)→ reduce 用普通代码去重(不需要模型)→ 每条候选死链独立复验(验证门,验证器反驳"这是死链")→ 综合节点生成新增问题报告(唯一需要模型全局判断的地方)。所有节点有 Schema 契约。
- Loop 层 :扫描节点内部的 Agentic Loop 每轮:观察目录 → 推理下一步 → 扫描工具 → 验证结果;
/goal风格的完成定义------"扫描完整目录且结果通过 schema 校验";maxTurns防无限循环。 - Context 层 :每个扫描节点的上下文只注入:该目录路径、扫描规则(来自 CLAUDE.md 安全底线,@import 注入)、上次扫描结果(用于区分新旧);不注入整个知识库的全部笔记------那是 10 万行的上下文爆炸;子代理各自隔离。
- Prompt 层:每个节点指令的措辞:"只报告新增问题,不重复已知问题;发现批量操作先列影响范围等确认"------这是提示词层的表达优化。
5.2 这个例子教会什么
- 五个层各管一件事,缺一层系统都会坏:没有 Context 隔离,扫描节点互相污染;没有 Graph 并行,三个目录串行等 3 倍时间;没有 Loop 的完成定义,Agent 扫一半就"觉得"扫完了;没有 Harness 权限门,它会擅自批量删除死链。
- 模型只在真正需要它的地方出现:去重是代码、复验是验证器、只有"综合成报告"和"判断死链语义"需要模型------这是 Graph 层的模型分层思想。
- 技术归位的练习:"定时触发"是 Loop 层的技术;"每个节点的输入边界"是 Context 层;"人工审批门"是 Harness 层的 Hook + Apps。你在精读或看文章时遇到的任何机制,都可以用这个例子练归位。
第六部分:常见混淆点与判断口诀
6.1 三对最常混淆的关系
| 混淆对 | 区别 | 一句话判断 |
|---|---|---|
| Prompt vs Context | 措辞 vs 内容构成 | 你在"改话"还是在"决定哪些话被看到"? |
| Loop vs Graph | 时间 vs 空间 | 你在定"何时跑/何时停"还是在定"怎么组织"? |
| Graph vs Harness | 设计图 vs 运行环境 | 你在"画图"还是在"建厂房、定规程"? |
6.2 机制归位练习(用接触过的学习素材)
| 机制 | 归属层 | 理由 |
|---|---|---|
| Auto Memory 召回 | Context | 决定哪些记忆进入上下文 |
| CLAUDE.md 加载时机 | Context | 注入时机与压缩策略 |
| CLAUDE.md 内指令措辞 | Prompt | 已选内容中的表达 |
| SKILL.md 的指令内容 | Prompt | 文本措辞 |
| SKILL 的 description 触发 | Context | 决定是否/何时加载 |
| PreToolUse Hook 拦截 | Harness | 权限与安全门 |
| 工具结果截断摘要 | Context | 压缩 |
/goal 的验证器 |
Loop | 停止协议 |
parallel() fan-out |
Graph | 节点组织 |
| SubAgent 独立上下文 | Context | 隔离边界 |
| SubAgent 作为节点 | Graph | 节点组织 |
| Agent Team 通信 | Graph | 节点协作 |
| MCP Task 持久句柄 | Harness | 状态存储与恢复 |
| Sandbox 权限隔离 | Harness | 安全边界 |
| Plugin 打包分发 | Harness | 能力分发 |
| maxTurns 安全阀 | Loop | 停止条件 |
| 节点 Schema 契约 | Graph | 数据边界 |
| MCP Apps 界面 | Harness | 呈现与人工介入 |
6.3 判断口诀
text
问"怎么说" → Prompt Engineering
问"知道什么" → Context Engineering
问"何时跑、何时停" → Loop Engineering
问"怎么拆、怎么连" → Graph Engineering
问"怎么安全持久可观测地运营" → Harness Engineering
6.4 各层薄弱的失败模式(面试常问)
- Prompt 弱:模型能做但不听话、输出格式乱------最轻的问题,改措辞即可。
- Context 弱:上下文爆炸(Token 浪费、幻觉)、或关键信息缺失(遗忘)------Agent 最常见的失效根源。
- Loop 弱 :过早宣布完成(没有验证器)或无限循环(没有安全阀)------
/goal要解决的两件事。 - Graph 弱:把无依赖任务串行化、每个小步都调模型、循环不收敛------成本与延迟的失控。
- Harness 弱:出事故没人知道、进程死了任务丢了、权限过宽------不可运营。
第七部分:如何使用这张工程地图
这张地图的价值不在于背诵五个标签,而在于帮助你定位问题:先判断系统正在控制什么,再选择对应的工程手段。
第八部分:到底什么是 Agent Runtime?
8.1 先给一个准确的定义
!abstract 一句话定义 Agent Runtime 是一个把任务实例化为可执行 Agent、驱动模型与工具反复交互、管理状态与权限、执行验证与停止协议,并支持观测、暂停、恢复和人工介入的执行控制平面。
这句话里有几个必须同时成立的条件:
- 它接收的是任务实例,而不只是一次字符串请求 :任务要有
taskId、输入、预算、权限、运行状态和生命周期。 - 它能够驱动模型与工具交互:模型产生工具调用,Runtime 检查权限并执行工具,再把工具结果送回模型。
- 它管理跨轮状态:保存消息、工具结果、节点输出、checkpoint、重试次数和当前状态,而不是只依赖进程内变量。
- 它拥有控制能力:能决定继续、暂停、等待人工输入、重试、失败或完成。
- 它承担运行责任:记录日志和指标,限制 token、时间、并发和权限,并在进程中断后尽可能恢复任务。
因此,Agent Runtime 的本质不是"让模型回答得更聪明",而是:
让一次模型调用变成一个受约束、有状态、可验证、可恢复、可运营的任务执行过程。
它通常不是一个单独的标准产品。不同系统可能把它实现成一个进程、一组服务、一个 SDK、一个队列消费者,或者嵌在某个 Agent Framework 中。Agent Runtime 是一种工程职责和运行时抽象,不是固定品牌名称。
8.2 为什么单独调用 LLM API 还不是 Runtime
下面只是一次模型调用:
typescript
const response = await client.messages.create({
model: "some-model",
messages: [{ role: "user", content: "检查这个项目" }],
});
它可以得到文本,但通常没有负责:
- 解析和执行模型返回的工具调用;
- 检查工具权限;
- 把工具结果重新放回消息历史;
- 判断是否继续下一轮;
- 保存任务状态;
- 处理超时、取消和重试;
- 验证交付结果;
- 在进程重启后恢复;
- 记录一次运行的完整 trace。
所以,LLM API 是模型能力的入口,Agent Runtime 是围绕模型能力建立的执行系统。
可以用这个关系理解:
text
LLM API = 提供一次模型推理
Agentic Loop = 描述模型如何与工具反复交互
Agent Runtime = 负责让这个交互过程在真实系统中安全、持久、可控制地运行
一个 while 循环可以实现最小 Agentic Loop,但不能自动成为成熟 Runtime。Runtime 还要处理循环之外的生命周期、状态、权限、资源、恢复和运营问题。
8.3 Runtime 到底运行什么对象
Runtime 运行的不是抽象的"Agent 名字",而是一个任务实例。可以把它看成下面的数据结构:
typescript
type TaskInstance = {
taskId: string;
input: unknown;
status:
| "queued"
| "running"
| "input_required"
| "paused"
| "completed"
| "failed"
| "cancelled";
agent: {
model: string;
systemPrompt: string;
tools: string[];
contextPolicy: string;
};
budget: {
maxTurns: number;
maxTokens: number;
deadlineAt: number;
};
state: {
messages: unknown[];
toolResults: unknown[];
checkpoints: unknown[];
attempts: number;
};
};
这个实例至少包含五类信息:
| 信息 | Runtime 用它做什么 |
|---|---|
| 任务输入 | 知道要完成什么 |
| Agent 配置 | 知道使用哪个模型、提示词、工具和上下文策略 |
| 运行状态 | 知道任务现在处于排队、执行、等待、完成还是失败 |
| 资源预算 | 知道最多运行多少轮、消耗多少 token、持续多长时间 |
| 检查点 | 进程中断后知道从哪里继续 |
这就是"Runtime"这个词的关键:它运行的是有生命周期的任务对象,而不是一段孤立文本。
8.4 Agent Runtime 的最小职责
一个真正有用的 Runtime,至少要有以下八个部分:
1. 触发器与调度器
触发器负责回答"任务为什么现在开始",例如用户消息、定时器、Webhook、队列事件或上一个节点的输出。
调度器负责回答"现在是否真的执行",包括:
- 是否有可用 worker;
- 是否达到并发上限;
- 是否需要排队;
- 是否需要按优先级调度;
- 是否已经存在相同的幂等任务。
/loop、Cron 和事件订阅主要解决触发问题;它们本身并不等于完整 Runtime。
2. Agent 实例化器
它把一个 Agent 定义转换成一次具体运行:
- 选择模型;
- 加载 system prompt 和 Skill;
- 注册允许使用的工具;
- 创建独立上下文;
- 绑定用户、项目、权限和预算;
- 创建
taskId与初始状态。
这一步解释了为什么同一个 Agent 定义可以同时运行很多次:每次运行都有独立的任务实例和状态。
3. 模型适配器与消息上下文
Runtime 不应该把模型 SDK 的细节散落到所有节点中,而应通过适配器统一处理:
typescript
type ModelAdapter = {
complete(input: {
messages: Message[];
tools: ToolDefinition[];
}): Promise<ModelResponse>;
};
适配器负责模型请求和响应格式;Context Builder 负责决定本轮发送哪些消息、记忆和工具结果。这样可以把"模型供应商差异"和"上下文构造策略"分开。
4. 工具注册表与工具执行器
Runtime 要维护工具注册表,并在模型请求工具时经过统一执行器:
text
模型提出 tool_call
↓
Runtime 检查工具是否存在
↓
检查参数 Schema
↓
检查权限、沙箱和审批策略
↓
执行工具或进入 input_required
↓
记录耗时、结果和错误
↓
生成 tool_result 返回模型
工具执行器不是简单的函数调用。它还要考虑:
- 工具是否允许当前 Agent 使用;
- 参数是否符合 Schema;
- 当前用户是否有权限;
- 是否需要人工确认;
- 是否超时;
- 失败是否可重试;
- 重试是否会造成重复写入。
5. Agentic Loop 驱动器
这是 Runtime 的核心执行内核:
text
读取当前状态
→ 构造 Context
→ 调用 Model
→ 判断文本回复还是 Tool Call
→ 检查权限并执行 Tool
→ 写回 Tool Result
→ 运行验证器
→ 继续、暂停或结束
最小教学实现如下。它不是某个产品的内部源码,而是展示 Runtime 如何把模型、工具、状态和停止条件连起来:
typescript
type Message = {
role: "user" | "assistant" | "tool";
content: unknown;
};
type ToolCall = {
id: string;
name: string;
input: unknown;
};
type ModelResponse = {
stopReason: "end_turn" | "tool_use";
text?: string;
toolCalls?: ToolCall[];
};
type RuntimeContext = {
messages: Message[];
turn: number;
maxTurns: number;
};
type RuntimeDeps = {
model: {
complete(context: RuntimeContext): Promise<ModelResponse>;
};
tools: {
has(name: string): boolean;
execute(name: string, input: unknown): Promise<unknown>;
};
check(result: unknown): Promise<{ met: boolean; gaps: string[] }>;
save(context: RuntimeContext): Promise<void>;
};
async function runAgentTask(
input: string,
deps: RuntimeDeps,
): Promise<{ status: "completed" | "failed"; output?: string; gaps?: string[] }> {
const context: RuntimeContext = {
messages: [{ role: "user", content: input }],
turn: 0,
maxTurns: 8,
};
while (context.turn < context.maxTurns) {
context.turn += 1;
await deps.save(context);
const response = await deps.model.complete(context);
if (response.stopReason === "end_turn") {
const evaluation = await deps.check(response.text ?? "");
if (evaluation.met) {
return { status: "completed", output: response.text };
}
context.messages.push({
role: "user",
content: `验证未通过,请修复以下缺口:${evaluation.gaps.join(";")}`,
});
continue;
}
for (const call of response.toolCalls ?? []) {
if (!deps.tools.has(call.name)) {
return { status: "failed", gaps: [`工具不存在:${call.name}`] };
}
const result = await deps.tools.execute(call.name, call.input);
context.messages.push({
role: "tool",
content: { callId: call.id, result },
});
}
}
return {
status: "failed",
gaps: [`已达到最大轮数 ${context.maxTurns}`],
};
}
这段代码里真正属于 Runtime 的部分包括:
- 任务状态
context; - 轮次计数和
maxTurns; - 模型调用;
- 工具查找和执行;
- 工具结果写回消息;
- checkpoint 保存;
- 独立验证;
- 完成、继续和失败三种出口。
生产实现还必须补上取消信号、超时、token 预算、权限检查、结构化日志、重试、幂等和异常恢复。
6. 状态、Checkpoint 与恢复
如果 Runtime 只把状态放在内存里,进程退出后任务就消失了。可恢复 Runtime 至少需要保存:
- 当前任务状态;
- 最近一次完整消息或压缩摘要;
- 已执行的工具调用及结果;
- 当前 Graph 节点;
- 当前轮数和预算消耗;
- 重试次数;
- 人工等待请求;
- checkpoint 版本。
恢复过程通常是:
text
读取 taskId
→ 加载最近 checkpoint
→ 检查状态是否仍可恢复
→ 重新获得 worker 租约
→ 从未完成的节点或 Loop 轮次继续
这里要区分几个概念:
- Memory:帮助模型获得有用信息,是 Context 的机制;
- Checkpoint:帮助 Runtime 恢复执行位置,是 Runtime State 的机制;
- MCP Task:一种可以暴露长任务状态、进度和终态的任务接口;
/loop:触发多次运行的时间机制,不自动提供长任务恢复。
7. 策略、安全与资源控制
Runtime 是 Agent 与真实世界之间的控制边界。它需要在执行前和执行中控制:
- 允许使用哪些工具;
- 能读取和写入哪些目录;
- 是否允许网络访问;
- 是否需要人工批准;
- 最大 token、轮数、时间和费用;
- 单用户和全局并发量;
- 是否允许后台运行;
- 任务取消后如何停止正在进行的工具。
Prompt 可以告诉模型"不要删除文件",但真正的安全边界不能只靠 Prompt。Prompt 是建议,Runtime Policy 才是约束。
8. 验证、终态与错误处理
Runtime 不能把模型返回的"完成了"直接当作成功。它需要把任务推进到明确终态:
text
queued
→ running
→ input_required / paused
→ running
→ completed
→ failed / cancelled
每个终态都应该受到保护:已经 completed 的任务不能被迟到的 worker 随意改回 running;已经 cancelled 的任务不能被旧重试覆盖。
同时要区分:
- 模型拒绝回答;
- 工具执行失败;
- 验证未通过;
- 超过预算;
- 超时;
- 用户取消;
- Runtime 自身崩溃。
不同原因决定不同恢复策略,不能全部简单地"再调用一次模型"。
9. 可观测性与人工介入
没有观测能力,Runtime 只是一个黑盒循环。至少应该记录:
taskId、runId、nodeId、turn;- 每次模型调用的延迟、token 和费用;
- 工具名称、参数摘要、耗时和结果状态;
- 每次状态变更;
- 验证器反馈;
- 重试和取消原因;
- Graph 节点之间的输入输出关系。
当任务需要用户决定时,Runtime 应把状态变成 input_required,而不是继续猜测。例如:
text
发现需要删除 37 个文件
→ Runtime 暂停任务
→ 呈现影响范围
→ 等待用户批准或拒绝
→ 继续或取消
这就是 Human-in-the-loop 在 Runtime 中的具体形态:不是在 Prompt 里写一句"请询问用户",而是一个可持久化、可恢复的任务状态。
8.5 Agent Runtime 与五大工程的准确关系
五大工程可以理解为 Runtime 内部不同的设计职责,而不是五个独立软件:
| 工程 | 在 Agent Runtime 中负责什么 |
|---|---|
| Prompt Engineering | 定义模型应该如何理解任务、遵守规则和组织输出 |
| Context Engineering | 在每轮调用前构造模型本轮真正需要看到的输入 |
| Loop Engineering | 定义继续、停止、重试和反馈回路 |
| Graph Engineering | 定义多个节点的拓扑、数据依赖、路由和汇聚 |
| Harness Engineering | 提供执行、状态、安全、观测、评测和呈现基础设施 |
| Agent Runtime | 把这些设计装配起来,创建任务实例并实际驱动它运行 |
因此,Runtime 和 Harness 的关系可以这样精确区分:
- Harness 更强调"运行所需的基础设施和保护机制";
- Agent Runtime 更强调"任务实例如何被执行和推进";
- 实际工程中二者经常合并在同一个平台里,所以很多文章会混用这两个词;
- 为了学习和设计时不混淆,可以把 Harness 看成 Runtime 的外围能力集合,把 Runtime 看成其中负责执行控制的核心。
一个更完整的结构是:
text
Agent Runtime
├── Execution:模型、工具、Agentic Loop
├── Orchestration:Graph、路由、并行、队列
├── State:任务、消息、Memory、Checkpoint
├── Policy:权限、Sandbox、审批、预算
├── Evaluation:验证器、Evals、停止条件
├── Operations:日志、Trace、指标、重试、恢复
└── Presentation:状态查询、UI、人工介入
8.6 五个常见误解
误解一:Runtime 就是一个 while 循环
while 只能表达"重复执行"。Runtime 还要处理任务生命周期、状态、权限、恢复、验证和观测。Loop 是 Runtime 的执行内核之一,不是全部。
误解二:Runtime 就是 LangGraph 或某个框架
LangGraph、Agent SDK、Workflow SDK 都可以用来实现 Runtime 的一部分,但框架本身不自动拥有完整的生产能力。是否具备持久化、队列、权限、观测和恢复,要看具体实现。
误解三:Runtime 就是 Prompt + Tools
Prompt 决定模型如何行动,Tools 提供可执行能力;Runtime 决定这些能力何时被调用、是否被允许、结果如何写回、失败如何处理。
误解四:MCP Tasks 就是 Runtime
MCP Tasks 可以提供长任务的任务句柄、状态查询和输入等待接口,但它主要解决任务协议和状态暴露;模型循环、工具执行、权限、队列和验证仍然需要 Runtime 或其宿主系统负责。
误解五:只要有状态数据库就能恢复
状态存储只是恢复的必要条件,不是充分条件。真正可恢复还需要 checkpoint 语义、幂等工具、worker 租约、版本控制、未完成步骤识别和重复执行保护。
8.7 用一个具体任务识别 Runtime
以"每天检查知识库死链"为例,Runtime 的一次实际运行是:
text
Cron 触发
→ 创建 taskId
→ 读取上次 checkpoint
→ 加载只读权限和预算
→ Graph 路由到多个目录扫描节点
→ 每个 SubAgent 创建自己的 Context
→ 节点内部运行 Model ↔ Tool Loop
→ 保存每轮状态和工具结果
→ reduce 合并发现
→ 验证器复验死链
→ 生成报告
→ 将状态写为 completed
→ 通过 Agent View 或 MCP Apps 呈现
如果扫描过程中发现需要删除文件,则流程变成:
text
发现批量删除建议
→ Runtime 将任务置为 input_required
→ 保存当前 checkpoint
→ 展示影响范围
→ 等待人工批准
→ 批准后恢复执行,拒绝则 cancelled
这时才真正看出 Runtime 的价值:它不是让模型"想出一个答案",而是把模型的局部判断放进一个有状态、有边界、有证据、有生命周期的执行过程中。
!success 最终记忆句 Agentic Loop 是心脏,Graph 是骨架,Context 是输入系统,Prompt 是语言接口,Harness 是边界与基础设施;Agent Runtime 是把这些器官装配起来、让一个任务真正活着运行并最终结束的执行控制平面。