Agent 五大工程体系:Prompt、Context、Loop、Graph 与 Harness

344 阅读31分钟

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。

image.png

读图只需要抓住四层关系

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 嵌套关系速览

五层是包含关系,从内到外一层套一层:

graph TD
    subgraph Harness[Harness 工程<br>系统如何运营]
        subgraph Graph[Graph 工程<br>多节点如何组织]
            subgraph LoopT[Loop 工程<br>何时再次运行]
                subgraph LoopI[Agentic Loop 单轮执行]
                    subgraph Context[Context 工程<br>模型知道什么]
                        Prompt[Prompt 工程<br>怎么表达]
                    end
                end
            end
        end
    end
  • 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、loopparallel 是 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 storeState
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 一次请求如何穿过所有层

image.png

读图方式:外层负责"决定",内层负责"执行"。Harness 决定这次运行是否允许、花多少预算;Graph 决定跑哪个节点组合;每个节点的内部是一次 Loop;每一轮 Loop 先构建上下文;上下文里最终拼出一段提示词交给模型。输出回来后,验证器(Loop 层)决定是否再来一轮;最终结果回到状态存储和观测系统(Harness 层)。

4.2 五层的控制权迁移

这张图还揭示了 Agent 工程的历史主线——控制权从内层向外层迁移:

时代谁在控制对应的工程层
2022-2023提示词(人写)Prompt
2024检索与 RAGContext
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 五层设计

image.png

  • 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 这个例子教会什么

  1. 五个层各管一件事,缺一层系统都会坏:没有 Context 隔离,扫描节点互相污染;没有 Graph 并行,三个目录串行等 3 倍时间;没有 Loop 的完成定义,Agent 扫一半就"觉得"扫完了;没有 Harness 权限门,它会擅自批量删除死链。
  2. 模型只在真正需要它的地方出现:去重是代码、复验是验证器、只有"综合成报告"和"判断死链语义"需要模型——这是 Graph 层的模型分层思想。
  3. 技术归位的练习:"定时触发"是 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-outGraph节点组织
SubAgent 独立上下文Context隔离边界
SubAgent 作为节点Graph节点组织
Agent Team 通信Graph节点协作
MCP Task 持久句柄Harness状态存储与恢复
Sandbox 权限隔离Harness安全边界
Plugin 打包分发Harness能力分发
maxTurns 安全阀Loop停止条件
节点 Schema 契约Graph数据边界
MCP Apps 界面Harness呈现与人工介入

6.3 判断口诀

问"怎么说"        → 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、驱动模型与工具反复交互、管理状态与权限、执行验证与停止协议,并支持观测、暂停、恢复和人工介入的执行控制平面。

这句话里有几个必须同时成立的条件:

  1. 它接收的是任务实例,而不只是一次字符串请求:任务要有 taskId、输入、预算、权限、运行状态和生命周期。
  2. 它能够驱动模型与工具交互:模型产生工具调用,Runtime 检查权限并执行工具,再把工具结果送回模型。
  3. 它管理跨轮状态:保存消息、工具结果、节点输出、checkpoint、重试次数和当前状态,而不是只依赖进程内变量。
  4. 它拥有控制能力:能决定继续、暂停、等待人工输入、重试、失败或完成。
  5. 它承担运行责任:记录日志和指标,限制 token、时间、并发和权限,并在进程中断后尽可能恢复任务。

因此,Agent Runtime 的本质不是“让模型回答得更聪明”,而是:

让一次模型调用变成一个受约束、有状态、可验证、可恢复、可运营的任务执行过程。

它通常不是一个单独的标准产品。不同系统可能把它实现成一个进程、一组服务、一个 SDK、一个队列消费者,或者嵌在某个 Agent Framework 中。Agent Runtime 是一种工程职责和运行时抽象,不是固定品牌名称。

8.2 为什么单独调用 LLM API 还不是 Runtime

下面只是一次模型调用:

const response = await client.messages.create({
  model: "some-model",
  messages: [{ role: "user", content: "检查这个项目" }],
});

它可以得到文本,但通常没有负责:

  • 解析和执行模型返回的工具调用;
  • 检查工具权限;
  • 把工具结果重新放回消息历史;
  • 判断是否继续下一轮;
  • 保存任务状态;
  • 处理超时、取消和重试;
  • 验证交付结果;
  • 在进程重启后恢复;
  • 记录一次运行的完整 trace。

所以,LLM API 是模型能力的入口,Agent Runtime 是围绕模型能力建立的执行系统。

可以用这个关系理解:

LLM API       = 提供一次模型推理
Agentic Loop  = 描述模型如何与工具反复交互
Agent Runtime = 负责让这个交互过程在真实系统中安全、持久、可控制地运行

一个 while 循环可以实现最小 Agentic Loop,但不能自动成为成熟 Runtime。Runtime 还要处理循环之外的生命周期、状态、权限、资源、恢复和运营问题。

8.3 Runtime 到底运行什么对象

Runtime 运行的不是抽象的“Agent 名字”,而是一个任务实例。可以把它看成下面的数据结构:

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,至少要有以下八个部分:

flowchart LR
    trigger[触发器] --> scheduler[调度与排队]
    scheduler --> executor[执行器]
    executor --> loop[Agentic Loop]
    loop --> state[状态与 Checkpoint]
    loop --> policy[权限与资源策略]
    loop --> evaluator[验证与停止协议]
    executor --> observe[日志与 Trace]
    state --> recover[暂停 / 恢复]
    recover --> executor
    evaluator -->|继续| loop
    evaluator -->|完成 / 失败| terminal[终态结果]

1. 触发器与调度器

触发器负责回答“任务为什么现在开始”,例如用户消息、定时器、Webhook、队列事件或上一个节点的输出。

调度器负责回答“现在是否真的执行”,包括:

  • 是否有可用 worker;
  • 是否达到并发上限;
  • 是否需要排队;
  • 是否需要按优先级调度;
  • 是否已经存在相同的幂等任务。

/loop、Cron 和事件订阅主要解决触发问题;它们本身并不等于完整 Runtime。

2. Agent 实例化器

它把一个 Agent 定义转换成一次具体运行:

  • 选择模型;
  • 加载 system prompt 和 Skill;
  • 注册允许使用的工具;
  • 创建独立上下文;
  • 绑定用户、项目、权限和预算;
  • 创建 taskId 与初始状态。

这一步解释了为什么同一个 Agent 定义可以同时运行很多次:每次运行都有独立的任务实例和状态。

3. 模型适配器与消息上下文

Runtime 不应该把模型 SDK 的细节散落到所有节点中,而应通过适配器统一处理:

type ModelAdapter = {
  complete(input: {
    messages: Message[];
    tools: ToolDefinition[];
  }): Promise<ModelResponse>;
};

适配器负责模型请求和响应格式;Context Builder 负责决定本轮发送哪些消息、记忆和工具结果。这样可以把“模型供应商差异”和“上下文构造策略”分开。

4. 工具注册表与工具执行器

Runtime 要维护工具注册表,并在模型请求工具时经过统一执行器:

模型提出 tool_call
    ↓
Runtime 检查工具是否存在
    ↓
检查参数 Schema
    ↓
检查权限、沙箱和审批策略
    ↓
执行工具或进入 input_required
    ↓
记录耗时、结果和错误
    ↓
生成 tool_result 返回模型

工具执行器不是简单的函数调用。它还要考虑:

  • 工具是否允许当前 Agent 使用;
  • 参数是否符合 Schema;
  • 当前用户是否有权限;
  • 是否需要人工确认;
  • 是否超时;
  • 失败是否可重试;
  • 重试是否会造成重复写入。

5. Agentic Loop 驱动器

这是 Runtime 的核心执行内核:

读取当前状态
  → 构造 Context
  → 调用 Model
  → 判断文本回复还是 Tool Call
  → 检查权限并执行 Tool
  → 写回 Tool Result
  → 运行验证器
  → 继续、暂停或结束

最小教学实现如下。它不是某个产品的内部源码,而是展示 Runtime 如何把模型、工具、状态和停止条件连起来:

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 版本。

恢复过程通常是:

读取 taskId
  → 加载最近 checkpoint
  → 检查状态是否仍可恢复
  → 重新获得 worker 租约
  → 从未完成的节点或 Loop 轮次继续

这里要区分几个概念:

  • Memory:帮助模型获得有用信息,是 Context 的机制;
  • Checkpoint:帮助 Runtime 恢复执行位置,是 Runtime State 的机制;
  • MCP Task:一种可以暴露长任务状态、进度和终态的任务接口;
  • /loop:触发多次运行的时间机制,不自动提供长任务恢复。

7. 策略、安全与资源控制

Runtime 是 Agent 与真实世界之间的控制边界。它需要在执行前和执行中控制:

  • 允许使用哪些工具;
  • 能读取和写入哪些目录;
  • 是否允许网络访问;
  • 是否需要人工批准;
  • 最大 token、轮数、时间和费用;
  • 单用户和全局并发量;
  • 是否允许后台运行;
  • 任务取消后如何停止正在进行的工具。

Prompt 可以告诉模型“不要删除文件”,但真正的安全边界不能只靠 Prompt。Prompt 是建议,Runtime Policy 才是约束。

8. 验证、终态与错误处理

Runtime 不能把模型返回的“完成了”直接当作成功。它需要把任务推进到明确终态:

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,而不是继续猜测。例如:

发现需要删除 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 看成其中负责执行控制的核心。

一个更完整的结构是:

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 的一次实际运行是:

Cron 触发
  → 创建 taskId
  → 读取上次 checkpoint
  → 加载只读权限和预算
  → Graph 路由到多个目录扫描节点
  → 每个 SubAgent 创建自己的 Context
  → 节点内部运行 Model ↔ Tool Loop
  → 保存每轮状态和工具结果
  → reduce 合并发现
  → 验证器复验死链
  → 生成报告
  → 将状态写为 completed
  → 通过 Agent View 或 MCP Apps 呈现

如果扫描过程中发现需要删除文件,则流程变成:

发现批量删除建议
  → Runtime 将任务置为 input_required
  → 保存当前 checkpoint
  → 展示影响范围
  → 等待人工批准
  → 批准后恢复执行,拒绝则 cancelled

这时才真正看出 Runtime 的价值:它不是让模型“想出一个答案”,而是把模型的局部判断放进一个有状态、有边界、有证据、有生命周期的执行过程中。

[!success] 最终记忆句 Agentic Loop 是心脏,Graph 是骨架,Context 是输入系统,Prompt 是语言接口,Harness 是边界与基础设施;Agent Runtime 是把这些器官装配起来、让一个任务真正活着运行并最终结束的执行控制平面。


参考与关联

外部资料