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

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

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

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

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

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

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

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 的细节散落到所有节点中,而应通过适配器统一处理:

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 只是一个黑盒循环。至少应该记录:

  • taskIdrunIdnodeIdturn
  • 每次模型调用的延迟、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 是把这些器官装配起来、让一个任务真正活着运行并最终结束的执行控制平面。


参考与关联

外部资料

相关推荐
众人皆醒我独醉2 小时前
Triton Inference Server:NVIDIA 的推理"瑞士军刀"——LLM 只是它的一种负载
人工智能·面试·ai编程
Shawn_Shawn2 小时前
google ADK VS Langchain-ai
后端·llm·agent
元Y亨H2 小时前
大模型技术 LangGraph 概述
llm
愚农搬码2 小时前
AI Agent 目前最大的瓶颈是什么?
llm·agent·ai编程
Keepreal4962 小时前
《从零构建大模型》——从头实现GPT模型进行文本生成
gpt·深度学习·llm
武子康3 小时前
模型发布可以按天看,生产默认模型不能按天切:一套可回退的 30 天验收流程
人工智能·llm·agent
程序员-Benothing4 小时前
Java ForkJoinPool 详解:从分治思想到高性能并行计算
java·开发语言·后端·面试·职场和发展
CodeLinghu5 小时前
LangSmith Evaluate实战评估Agent
人工智能·python·语言模型·llm
用户3126874877205 小时前
AI Agent 开发实战(九):Grill Me 反问式规划
llm·ai编程