☰
AI Agent生产落地:解析Harness、Loop与Graph三层架构
2026/10/29 7:25:34 网站建设 项目流程

我见过太多团队开始做 Agent 的时候,把百分之九十的精力花在选模型、调提示词上,结果一到生产就全露馅:工具调用触发上下文爆掉、Agent 在同一个问题上绕了十几圈没人发现、一次高并发把下游服务直接压垮。其实决定 Agent 能不能真正落地的,往往不是模型聪明不聪明,而是你给它搭的骨架。这两年开源社区里反复冒出三个关键词:Harness、Loop、Graph。我的理解是:Harness 是外层控制壳,Loop 是 Agent 的推理循环,Graph 是把循环变成显式状态图。如果把 Agent 比作司机,Harness 是驾驶舱,Loop 是道路,Graph 是地图导航。三者分工完全不同,最终在生产环境里谁也替代不了谁。

1. 为什么要把 Agent 拆成三层看待

1.1 模型、工具与控制逻辑,是三件不同的事

不少新手把 Agent 理解成“大模型+工具”,写好 prompt 就等着模型自己调用工具,好像一切都能自动完成。但实际跑过就会发现,模型只负责“下一步做什么”,工具负责产生具体副作用,而连接模型和工具的那部分——上下文怎么传、失败怎么重试、请求怎么限流、结果怎么校验——是另一类完全不同的工作,也就是控制逻辑。控制逻辑恰恰是最容易被低估的部分。

Harness、Loop、Graph 本质上都在控制层面,但职责不一样:

  • Harness:为“单次动作”提供边界,决定这一个工具调用能不能做、怎么做、做多久;
  • Loop:为“多个动作”提供动态决策回路,决定做完之后要不要继续、要不要换思路;
  • Graph:为“动作之间的关系”提供静态路径,决定先做什么、后做什么、哪些可以并行、哪些要等待。

把这三者叠在一起,就得到一套很清晰的架构:最外层 Graph 定义整条流程,中间 Loop 承担循环和重试,最内 Harness 封装单次工具调用。这跟只用一段 ReAct Prompt 写出来的脚本完全是两个工程级别。

1.2 为什么顺序是 Harness、Loop、Graph

从实现难度和演化路径来排,Harness 最基础,Loop 依赖 Harness 的结果,Graph 要在一个相对稳定的 Loop 上再抽象一层。如果你一上来就画一个大而全的 Graph,节点里却连工具超时校验都没做,大概率会在执行中间挂掉,整个流程都被卡死;反过来,当每个动作都被 Harness 约束好,再用 Loop 去做多轮任务就会顺利很多,最后等到 Loop 变得不可观测、需求需要被拆分,Graph 才值得出现。

所以本文按这个顺序写,从最底层的地基开始。

2. Harness:把 Agent 放进可控的执行容器

2.1 Harness 不是又一个框架,而是一层约束

这几年“Harness”这个词在 AI 工程里越来越常见,但它不是某个具体软件产品带来的新概念。你可以先把它理解成包住 Agent 的外壳:模型在壳里拥有自己的工具白名单、上下文窗口、历史记录、配额和超时设置,壳外面的代码则负责监督它的一举一动。

生产里最典型的场景是:Agent 要调用“查询订单”的 API,如果不做任何约束,模型一旦跑偏就可能去调“删除订单”的接口。Harness 要做的第一件事,就是定义工具清单和调用权限:哪些函数可以被调用,函数签名和 schema 是什么,哪些参数需要过滤。第二件事是定义请求上下文,包括系统提示词、历史消息、时间限制和 token 预算。

很多现代框架都内置了部分 Harness 能力,但显然不够,因为安全边界必须完全由你自己的代码控制,不能依赖模型的自觉。

2.2 Harness 与模型之间要有“动静分离”

做过几年后端的人都知道动静分离。Agent 开发里同样有一个原则:Harness 是静态边界,它不随模型输入变化;Prompt 是动态部分,随任务变化。凡是可靠性要求极高的规则,都应该放在 Harness 层强制执行。

比如你希望所有 API Key 都只存在于环境变量,不进入 Prompt;希望工具 schema 里就标注哪些是只读操作,而不是在 system prompt 里说“请只读”;希望模型看到的永远是脱敏后的结果。这些都是 Harness 的职责。

一旦把访问密钥、内部 URL、系统状态这些信息塞进 Prompt,就相当于把控制权交给了生成模型。Prompt 是可被任务内容影响的输入变量,而 Harness 是由代码强制执行的硬规则,不依赖模型“自觉遵守”。凡是“绝对可靠、不可绕过”的规则,都应该做在 Harness 层,包括:

  • 工具调用参数校验;
  • 文件路径白名单;
  • 网络出口域名白名单;
  • 请求超时;
  • 响应内容里的敏感信息过滤。

而把语气、性格、流程偏好这类可以让模型自由发挥的约束,一律放在 Prompt 层。

2.3 最小可用 Harness 配置长什么样

实际工程里,我习惯先写一个harness_config配置结构,字段不多,但每一条都要较真:

配置字段作用常见推荐值
tool_allowlist允许模型调用的工具 ID 列表默认全空,按业务白名单添加
max_tool_calls单次请求最多调用几次工具5 到 20
tool_call_timeout单个工具最长执行时间10 秒到 30 秒
context_budget每次请求的 token 预算占模型上下文上限的 60%
sandbox_network是否允许访问外网默认 false
retry_rule重试次数与退避策略最多 3 次,指数退避

这些不是可选项,而是你上线前必然要回答的问题:如果 Agent 已经调了 20 次工具还没完成,是不是已经进入死循环?如果某个工具卡了 30 秒,是继续等还是放弃当前动作?为每个字段设好默认值,后面排错至少能少走一半弯路。

2.4 用 Rust 实现 Harness 时的注意事项

如果 Agent 的运行时准备用 Rust 来做,请一定把 Harness 拆成独立模块,不要写成一个大而全的 struct。Rust 里最常见的坑是:把模型客户端、工具注册表、历史存储全塞进同一个结构体,最后每个方法都要持有&mut self,并发一上来直接锁死。

正确做法是把 Harness 拆成三块:Executor负责保存当前上下文和状态;ToolRegistry负责工具注册表和参数校验;BufferManager负责 token 和配额。三者通过 trait 组合起来,并发测试和依赖替换都更好做。

生产里出现高并发问题,很多时候不是模型不快,而是 Harness 的代码本身把所有请求串行化了。模型推理的网络 IO 可以异步等待,但配置解析、日志写入、工具状态更新这些都不应该去抢同一个锁。

3. Loop:Agent 的推理循环,生产态的第一道闸门

3.1 ReAct Loop 是基础形态

Loop 最常见的形式是 ReAct:模型先生成 reasoning 和 action,然后调用工具读取 observation,再进入下一轮。这个循环的价值在于,它让 Agent 能够根据工具返回结果调整下一步动作,相当于把单次大模型问答变成了一次自主任务处理。

但 Loop 的成本是明摆着的:每多走一轮,就多一次模型调用,多消耗几百个 token,而且失败概率会逐轮累积。如果只有 Loop 而没有约束,Agent 可能 20 轮还在原地打转。所以 Loop 层真正要回答的核心问题永远是三个:什么时候继续、什么时候停止、停止之后怎么办。

我实际遇到过一个线上问题:Agent 被要求完成一份市场报告,它连续调用搜索工具 14 次,每次都搜同一个短语,只是因为返回结果在模型复述时略有差别,它就觉得还需要再试。如果没有轮次预算,这个请求能一直烧钱烧到服务超时。这是 Loop 没有刹车的典型症状。

3.2 刹车系统:轮次预算、成功条件和相似度检测

给 Loop 装刹车,至少从四个维度入手:

  • 轮次预算:单次完整请求最多执行 N 轮。这是最简单也最重要的底线。
  • 成功条件:在进入 Loop 之前定义好“什么结果算完成”,代码层面判断,满足条件立刻结束。
  • 重复检测:把最近几轮的 observation 做哈希或向量相似度对比,连续高度相似就直接结束并提示用户。
  • 低置信度信号:如果模型连续两轮没有产生工具调用,或 reasoning 里出现“我不确定”“信息不足”等信号,触发提前退出。

轮次预算别拍脑袋统一设一个数。不同任务复杂度差很多,我给的建议是按任务类型配置:纯检索任务上限 5 轮,分析型任务上限 10 轮,长文档报告任务可以放到 20 轮。但不管多少,必须有上限。这是生产安全的底线。

3.3 Loop 内部的重试与幂等

工具不是零故障的,所以 Loop 里必须考虑重试。但重试不能只是“再调一次”,你必须考虑幂等。扣款接口重复调用会造成二次扣款,文件写入接口重复调用会追加重复内容,消息通知接口重复调用会发两条消息。

在设计上,我要求每个工具调用都做三件事:

  1. 为每个外部请求生成request_id,放进请求头或参数里;
  2. 在工具内维护request_id的去重表,同一个 ID 重复请求时直接返回上一次结果;
  3. 如果工具本身不支持幂等,Loop 层就禁止自动重试,改为人工确认。

幂等做不好,Loop 越勤奋越危险,这是最常见的线上事故来源。

3.4 Loop 不是越复杂越好

很多人看到某些编排框架支持复杂循环,就觉得一定要上死循环分支。其实不是这样,简单的 Loop 能解决 80% 的问题,成本和调试难度都更低。只要 Harness 配置到位,Loop 就是最稳的基础。

只有当你遇到下面这几种情况,才考虑升级成 Graph:

  • 需要多个工具并行执行;
  • 不同分支需要不同的重试策略;
  • 流程中途需要插入人工审批;
  • 需要支持审计和回放。

在这几种场景下,纯 Loop 会写成一坨 if-else,Graph 能让你把控制逻辑明确画出来。

4. Graph:把循环变成显式状态机,终于可观察、可回放、可审计

4.1 Graph 不是另一套 Loop,而是把控制权交回给你

Graph 是 Agent 编排的核心概念:节点表示动作或状态,边表示转移条件。相比 Loop,Graph 最大的优势是显式性——每一轮怎么走、分支怎么选、哪里可以并行,全部在代码里画出来,而不是靠模型临场发挥。线上出问题时,看状态流转日志就能定位到具体节点。

注意词义:大家常说的 DAG 不是唯一选项。如果流程天然需要循环,那 DAG 反而实现不了。Agent 场景里很多流程就是循环,所以工程上常用的其实是“有向带环图”,节点在满足条件时能回到更早的节点。这不是什么问题,带环图在条件控制下不会变成死循环。

4.2 节点、边、状态:最小 Graph 需要什么

先看节点。最常见的几类节点:

  • 入口节点:校验输入、加载配置;
  • 规划节点:让模型把用户任务拆成若干子任务;
  • 工具节点:执行具体工具调用,通常在 Harness 保护下;
  • 判断节点:根据结果决定走哪条边;
  • 汇总节点:整合所有输出,生成最终结果。

再看状态。Graph 运行时需要维护一个共享状态对象,每个节点都能读写。但不要把大模型的完整输入输出塞进状态里,否则状态会越来越庞大,难以序列化,也无法恢复。一般状态只存必要数据,比如任务 ID、当前步骤、工具结果摘要、关键信息、错误计数。完整的 Prompt 历史单独放存储层,不放进状态。

最后是边。边分无条件边和条件边。无条件边像“规划后必进入工具执行”,条件边像“工具返回 success 则进入汇总,否则进入重试”。重试次数的计数最好由状态里的字段控制,不要指望模型在 Prompt 里自己记住次数。

4.3 一个最小可行的生产图示例

这里不绑定具体框架,只画流程逻辑。

输入:用户要求“查询近 7 天订单数据,并生成周报”。

流程:

  1. 入口节点:检查用户是否有权限、日期参数能否解析;
  2. 规划节点:把任务拆成“查询订单数”和“统计变化率”两个子任务;
  3. 执行节点:并行调用库存、订单、支付三个工具,每个工具跑在 Harness 内;
  4. 校验节点:检查三个结果是否都有效,有一个失败就进入重试子图;
  5. 汇总节点:把结果交给大模型生成周报;
  6. 出口节点:返回最终内容,写入审计日志。

这条链路中,Graph 是主流程状态机,Harness 在每个工具节点内部负责超时和安全校验,Loop 出现在重试子图里。三者并不矛盾,而是各司其职。

4.4 检查点与回放:生产环境的“后悔药”

Graph 相对 Loop 的一大优势是可恢复性。如果整个流程很长,比如一个 Agent 任务要跑 10 分钟,中途进程崩溃,Loop 只能从头开始,而 Graph 可以定期把状态序列化到外部存储,重启后从最近检查点恢复。

实现检查点时有几个注意点:

  • 状态必须能序列化,不要把连接池、文件句柄放进去;
  • 每个检查点记录节点 ID 和输入摘要,不要记录完整工具响应,避免存储膨胀;
  • 恢复时先重新建立外部连接,再恢复业务状态。

回放则是把一次完整请求的节点执行顺序和中间状态记录下来,遇到线上问题,可以拿同一批输入重新跑一遍,对比两次差异。没有 Graph 之前,我只能翻日志手工拼接执行轨迹,非常痛苦。

5. 三层融合落地:从快速原型到生产架构

5.1 推荐演进路线:先 Loop,再加 Harness,最后 Graph

如果你正带着团队做新功能,我的建议是别一上来就画大图。先做一个基于 Harness + Loop 的最小闭环,让流程能跑通 80% 的业务。这个阶段简单、快、容易调整。等数据量上来,流程变复杂,再把 Loop 拆进 Graph 的节点里。每一步都有成果,排错范围不会瞬间膨胀。

相反,如果一开始就上 Graph,你会遇到一堆新问题:状态怎么命名?边写在哪里?并行分支怎么收敛?这些问题在 Loop 阶段根本不会出现。Graph 应该是“需要时再引入”,而不是“从一开始就必须”。

不过 Harness 一定不要晚于 Loop 加。只要有工具调用,Harness 必须第一时间跟上,因为它管安全边界。这个优先级不要颠倒。

5.2 生产链路长这样:路由 → Harness → Graph → Loop → 工具

完整的生产链路一般是:

  1. 外层 HTTP 服务接收请求,做鉴权和路由;
  2. 进入 Harness,初始化上下文、配额、日志和审计;
  3. Harness 内部根据业务类型启动对应 Graph;
  4. Graph 节点中可能包含多个 Loop 或嵌套 Loop;
  5. 每次工具调用再由 Harness 做超时和参数校验。

如果把 Graph 单独拆成一个工作流服务,不要去和模型服务强耦合,这样后续替换模型供应商、升级工具接入方式都会更灵活。Harness 应该包住 Graph,因为安全边界必须在流程序外层。Graph 只负责描述业务流,不负责安全决策。

5.3 并发控制与可观测性

很多人问“AI Agent 怎么扛并发”。我的答案很简单:把 Agent 当有状态服务处理,不能像普通 API 那样无脑把请求丢给模型。至少要做到:

  • 并发数限制:对整个 Agent 集群设置最大在途任务数,避免几十个大模型请求同时打爆下游;
  • 工具限流:即使 Agent 是异步的,工具层也必须配令牌桶或信号量;
  • 状态隔离:每个请求都有独立的 Graph 状态、Harness 快照,互不共享;
  • 全链路可观测:记录请求 ID、Graph 节点 ID、每次工具调用的耗时和错误码。

只要这四件事做到位,并发压力就不算玄学。

6. 生产环境避坑实录

6.1 状态不可序列化导致恢复失败

有一次我把 Redis 连接池直接放进了 Graph state,做检查点时状态序列化抛异常,排查很久才反应过来。后来我把 state 里的对象限制为基本 JSON 类型,字符串、数字、布尔、数组、字典,凡是连接和句柄统统放外部,靠 ID 引用。

6.2 上下文窗口截断引发工具参数幻觉

对话历史太长时,为了塞进模型窗口,我只保留最后 20 条消息,结果模型忘了之前要查询的订单 ID,就自己编了一个 ID 去调工具。ID 当然不存在,返回空,模型又把空结果当成“订单不存在”告诉用户。

现在我的做法是:截断历史时不能只按条数截,还要保留一份“关键上下文快照”,例如任务目标、实体 ID 列表、已经确认过的事实。这些关键信息压缩成短字段,固定放在当前问题之前,模型就不会凭空编参数。

6.3 Loop 反复调用同一个工具

即使有轮次预算,工具调用量还是可能失控。原因是模型认为上一次调用“不算彻底失败”,只是对结果不满意,于是换参数再调。优化方案是在循环观察阶段加入相似度检测:如果当前工具入参与上一轮相似度超过 0.85,把两条结果并排展示给模型,并明确提示“这两次调用可能是在做同一件事,请确认是否值得继续”。这个提示很有用,它没有禁止调用,只是把决策权交回给模型,减少无意义绕圈。

6.4 超时丢结果

某次工具调用 30 秒没返回,Harness 直接超时并返回空。但服务端其实是异步处理,结果 40 秒后到了回调。由于没有把 request_id 和 result 关联,这个结果直接丢了。后来我给每个工具调用都分配 request_id,并要求工具服务在回调时带上原 ID,问题才解决。

超时不能只做一个最简单的丢弃,还要设计“超时后结算”流程。允许结果晚到,但必须登记到交易表,最终核对时确认这个请求到底处理完成没有。

7. 常见问题快查表

下面这些是实际咨询里最常被问的问题,整理成速查表:

现象可能原因处理建议
工具被反复调用但结果相同缺少重复检测,上下文不完整加入相似度阈值,把前后两次结果并排展示给模型
Agent 很快答非所问上下文被简单截断,关键信息丢失保留关键上下文快照,不按条数简单裁剪
生产高并发打挂下游缺少工具限流在 Harness 外层加令牌桶,设置最大并发在途数
进程重启后任务丢失没有检查点,或状态不可序列化引入 Graph checkpoint,状态限制为 JSON 类型
超时后结果消失超时逻辑只做失败不做结算用 request_id 登记,允许结果晚到
模型在 Graph 里走错分支条件边没有明确阈值,状态不规范把判断逻辑放到代码里,不依赖模型自述

最后说一点个人体会:Harness、Loop、Graph 不是三个竞争框架,而是三个需要叠加的工程层次。很多人把 Graph 讲得很玄,但实际业务里,多数情况用 Loop 加合理的 Harness 就绰绰有余。Graph 是当可控性、可恢复性和审计需求真正成为硬指标时,才需要进一步引入的工具。不要因为别人在用 Graph 就觉得自己的 Loop 不够高级——跑得好、可维护、可解释,才是真正的高级。如果你的团队正打算从单轮 Prompt 走向真正的 Agent 生产,先把这三层边界画清楚,再谈其他。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询