Agent 运行时与 Harness:从教科书循环到生产级运行时
核心论点:能上生产的 Agent harness(指套在 LLM 外面、把工程约束"编织"进运行时的那层骨架),不是 LangChain 文档里那个"think → act → observe"的单循环,而是路由层 + 多策略执行器 + 生产守卫的三层结构。把延迟、韧性、安全、可观测性直接编进运行时本身,而不是等出事后再打补丁。
一、教科书里的 Agent 循环,为什么不够
所有 Agent 入门文章都会画同一张图:LLM 产出想法 → 选工具 → 执行 → 观察结果 → 再思考……直到给出最终答案。ReAct 范式干净、优雅,但它偷偷假设了四件事:
- LLM 永远在线、且调用免费(可用性与成本两件事);
- 工具总是成功返回;
- 用户输入是干净的;
- 没人需要盯着它。
生产环境把这四条全破了:LLM 有速率限制和偶发超时,且每 token 都要计费;远程工具会限流、返回空、部分成功;用户输入带 typo、情绪甚至攻击;合规还要求"高风险动作必须人确认"。
所以 harness 真正要解决的,不是"怎么跑循环",而是循环之外的工程问题:请求进来前怎么净化、循环怎么设上限、出错怎么兜底、谁在盯着。这就是本文要拆的"生产级 harness"。
二、Harness 的三层结构:路由层 + 执行器 + 守卫
第一篇从分层视角讲了"系统由哪些层组成",本篇换一个切面:把运行时单独拎出来,看一次请求在 harness 内部实际经过了哪些闸门。先看全貌——Shop-Agent 的 harness 不是单一 loop,而是三段:
注:图上把 Plan-Execute 与 RAG 兜底合并为一类——它们走的是同一套固定步骤执行器。纠纷协调器带分布式锁、不跑通用循环,属于特化路径,不计入"多策略"三选一。
第一个反直觉结论:harness = 路由层 + 多策略执行器,而不是一个万能循环。路由层负责"该用哪个执行器",每个执行器负责"在自己的循环里把事做成"。把这两层分开,守卫才能被复用,而不是在每个执行器里重写一遍。
顺带区分两个"三":导语说的"三层"是纵向的处理阶段(路由层 / 执行器 / 守卫),而"多策略三选一"是横向的执行器分工(ReAct / 固定步骤 / 直接分发),两者不是同一个三。
三、路由层:harness 的第一道闸门
路由层是 harness 的入口,它本身不跑 LLM 循环,而是把生产约束前置到循环之外。逐一看它立下的几道闸门:
- 同义词归一化(规则 + 本地模型,零 LLM 成本):轻度清洗,失败就回退原文本,绝不阻塞流程。
- Token 预算截断:用 tokenizer 精确估算(中文 ≈ 1.5 token/字),超限就重建不可变(frozen)的请求对象。这避免了一个超长输入把后续整条链路拖垮。
- 情绪检测级联(规则 <1ms + 本地模型 ~30ms):本地模型优先,保持延迟可控。
- 舆情风险 → 立即升级:检测到紧急情况直接短路整条管线,返回"已升级高级专员",连意图识别都不做。这是 fail-closed(出问题时默认拒绝而非放行)的教科书示例。
- 意图识别 → 路由分发:命中远程调用走参数抽取 + 门控;复杂意图走 ReAct;简单意图走直接分发;纠纷类走协调器。
- 纠纷协调前的分布式锁:锁的 key 由"会话号 + 订单号"拼接,TTL 5 分钟。防止接口重试或消息队列"至少一次"投递导致的重复触发——这是 harness 给"幂等"上的第一道锁。
- 输出内容安全过滤:规则引擎,零 LLM 成本。
- 全链路 trace + A/B 注入:整条管线共享一个 handler,实验分组在出口注入。
论点:这些守卫前置到路由层,意味着无论请求最终落到哪个执行器,净化、截断、情绪、安全、可观测都已经发生。这正是"守卫是 harness 一等公民"的体现(下文「守卫不是补丁」一节收口成表)。
四、执行器一:ReAct 循环(递归上限即护栏)
ReAct 执行器(图编排框架驱动)是自由循环执行器。它做对了几件生产化的事:
- 工具选择前置:按意图过滤 + embedding 重排(把文本转成向量、用相似度匹配),只把相关工具喂给 LLM,而不是几百个全量 Function Calling(把工具描述全塞进 prompt 让模型自己挑)(呼应《软预过滤工具选择方案》)。
- 输入安全过滤:规则引擎拦截,不进循环。
- 图状态存档(checkpointer):状态存档器把执行到一半的图状态存下来,是人在回路
interrupt()的必需品。 - 递归上限 = 基础步数 × 2 + 2:基础步数设为 5 → 上限 12。这是 harness 给"循环"装的断路器——把教科书里可能无限转圈的 ReAct,变成有限状态机。没有它,一个钻牛角尖的 Agent 会一直烧 token。(这个值在图默认配置与每次调用的配置里双重设定,防止漏配。)
- 人在回路:当退款申请这类工具被调用,置待审批标志,把图状态按会话级标识存下,返回等待确认。后续人工审批后,用同一标识 resume 恢复,而不是从头重跑。
- 推理链抽取:从图的消息里抽出最终答案 + 中间步骤(think→act→observe 的痕迹),作为可审计的推理链。
关键论点:递归上限是循环的断路器,状态存档器是循环的暂停键。这两样东西,才是 ReAct 从"demo 玩具"变成"生产组件"的分水岭。
五、执行器二:Plan-and-Execute 固定步骤
固定步骤执行器是第二个执行器,但它不跑自由循环,而是固定四步:
- 理解 / 改写查询:并检测"元描述"陷阱(如"问题类型:流程咨询"),命中就回退原始问题,避免改写丢失意图。
- 安全审查:门控不通过就生成降级回复直接返回。
- 并行检索:向量检索 + 图查询并行,且复用路由层预计算的 embedding,省掉一次 embedding API 调用。
- 生成 + 质量评估:打"质量分"与"是否解决"标志,并上报 Prometheus 指标。
为什么需要第二个执行器?因为不是所有任务都该跑自由 ReAct。知识问答(即 RAG 兜底)是确定性步骤流,用固定四步比自由循环更可控、更便宜、更易观测。harness 的"多策略"本质,是按任务复杂度选执行器:简单查询走直接分发,多步推理走 ReAct,知识问答走固定步骤。这就是前面那张分发图的真正含义。
六、守卫不是补丁,是 harness 的一等公民
把前面散落的守卫收口成一张表,能看清 harness 的设计哲学——守卫分层、前置、复用,而不是事后打补丁:
| 守卫点 | 机制 | 失败语义 |
|---|---|---|
| 输入归一化 | 规则 / 本地模型 | 回退原文本,不阻塞 |
| Token 截断 | tokenizer 精确估算 | 重建请求,防拖垮链路 |
| 情绪 / 舆情 | 级联检测 + 升级 | 短路管线(fail-closed) |
| 意图路由 | 意图识别 + 复杂度门控 | 选对执行器 |
| 工具选择 | 意图过滤 + embedding 重排 | 降维候选集 |
| 输入 / 输出安全 | 规则引擎(零 LLM 成本) | 拦截 / 过滤 |
| 幂等 | 分布式锁(TTL 5 分钟) | 跳过重复触发 |
| 人在回路 | 状态存档 + interrupt | 暂停等待人工 |
| 可观测 | 全链路 trace + 指标 | 全链路 trace + 指标 |
这张表也回答了开篇的问题:为什么教科书循环不够?因为守卫必须前置且分层,否则每个执行器都要各自重写一遍安全、截断、可观测——那才是真正的维护噩梦。
七、Harness 与外围设施的关系
此外,多工具部分成功、跨服务上下文同步等失败模式不在本文范围,见《AI Agent 分布式系统》系列的《工具调用部分成功》《分布式上下文存储与同步》两篇。
八、要点
- harness ≠ 单一 ReAct 循环,而是路由层 + 多策略执行器 + 生产守卫的三层结构。
- 路由层把生产约束前置到循环之外;执行器按任务复杂度分工(直接分发 / ReAct / 固定步骤)。
- 递归上限是循环的断路器,状态存档器是循环的暂停键——这两样是 ReAct 生产化的分水岭。
- 守卫是 harness 的一等公民:分层、前置、复用,而不是每个执行器各写一遍。
- harness 与 LLM 网关、监控 Agent 是配套的;网关管"出得去",监控管"看得见"。
更多推荐



所有评论(0)