Agent 工程从原型到生产:Harness、Loop、Graph 三层架构拆解

发布时间:2026/10/26 22:41:46
Agent 工程从原型到生产:Harness、Loop、Graph 三层架构拆解 Agent 工程这个方向最近一年从能跑通 Demo到能扛住生产流量之间隔着的已经不是模型能力而是工程架构。我接触过不少团队模型选型讨论得热火朝天一到上线就发现工具调用乱成一锅粥、多轮循环停不下来、状态在节点之间丢失、排查问题只能靠翻日志。这些问题的根子基本都落在三个词上——Harness、Loop、Graph。它们不是三个并列的时髦名词而是三层递进的架构职责Harness 管模型和外界怎么对话Loop 管一次任务怎么推进和收敛Graph 管多个环节怎么编排和容错。把这三层分清楚Agent 项目从原型走向生产才有章法。这篇内容我会按这三层逐层拆开讲清楚每层解决什么问题、核心机制怎么设计、生产环境里最容易踩的坑在哪适合正在做 Agent 开发、准备把原型推上生产的工程师也适合想理解 Agent 架构全貌的技术负责人。1. 先把三层职责掰开Harness、Loop、Graph 到底各管什么很多人第一次听到这三个词会觉得它们是同一件事的不同叫法。实际上它们处在完全不同的抽象层级混在一起谈就会导致架构设计时职责不清。我习惯用一个类比把 Agent 想象成一家餐厅。Harness 是服务员和传菜窗口的规范负责把顾客需求准确传达给厨房、把菜品准确端回来Loop 是厨师做一道菜的过程尝味、调整、再尝味直到达标Graph 是整个后厨的流水线编排谁先谁后、哪条线出问题怎么补救。三者缺一不可但绝不能互相替代。1.1 Harness 的本质模型与外部世界之间的适配层Harness 这个词直译是马具很形象——它是套在模型这匹马身上、让它能拉动外部世界这套车的装置。从工程角度看Harness 负责的是模型输入输出的规范化和工具调用的桥接。模型本身只会输出文本它不知道什么是文件系统、什么是数据库、什么是 HTTP 请求。Harness 要做的就是把模型输出的结构化意图翻译成真实世界的操作再把操作结果翻译回模型能理解的上下文。一个完整的 Harness 通常包含这几块提示词模板管理、工具Tool/Skill的注册与描述、模型输出的解析与校验、工具执行结果的回填、以及上下文窗口的管理。这里面最容易被低估的是工具描述的质量。我见过太多项目工具函数写得没问题但描述写得含糊模型根本不知道该在什么场景调用它结果就是要么不调用要么乱调用。工具描述本质上也是提示词工程的一部分它决定了模型的工具选择准确率。1.2 Loop 的本质单次任务的推进与收敛机制Loop 解决的是一次任务怎么从开始走到结束。模型一次输出往往不够需要多轮调用工具、看结果、再决策、再调用。这个循环什么时候继续、什么时候停止、最多转多少圈、每圈带多少上下文都是 Loop 要管的事。它最核心的两个指标是收敛性和成本可控性。收敛性指的是循环能不能在合理轮次内到达终点。我踩过最典型的坑是模型陷入调用工具→结果不理想→换个说法再调用同一个工具的死循环转了二十几圈还在原地。这不是模型笨而是 Loop 缺少终止条件设计和进展检测。成本可控性则是指每一轮都要把历史上下文重新喂给模型轮次一多token 消耗是平方级增长的。所以 Loop 设计里上下文裁剪、摘要压缩、状态外置都是必须考虑的手段。1.3 Graph 的本质多环节、多分支的编排与容错当任务复杂到需要多个不同角色的 Agent 协作或者需要条件分支、并行执行、失败重试时单靠一个 Loop 就不够了这时候需要 Graph。Graph 把整个流程建模成节点和边每个节点是一个处理单元可能是一次模型调用、一次工具执行、或者一个子 Loop边定义了流转条件。它的价值在于把复杂的控制流显式化让流程可观测、可干预、可恢复。Graph 最实用的能力是断点续跑和分支容错。生产环境里一个长流程跑到一半某个外部服务挂了如果没有 Graph 的状态持久化整个任务就得从头再来前面的 token 全白烧。有了 Graph每个节点执行完就把状态落盘失败时从断点恢复只重跑失败节点。这个能力在长任务场景下能省下大量成本。层级核心职责关键指标典型失效模式Harness模型与外部世界的适配工具选择准确率、输出解析成功率工具描述含糊导致误调用Loop单任务推进与收敛平均轮次、收敛率、单任务成本死循环、上下文爆炸Graph多环节编排与容错流程成功率、断点恢复率状态丢失、分支条件错误2. Harness 工程工具描述、输出解析与上下文管理Harness 是三层里最贴近模型的一层也是最需要抠细节的一层。它做得好不好直接决定了模型能不能稳定地用对工具、说对话。我把它拆成三个关键子系统来讲。2.1 工具描述怎么写才能让模型选对工具描述不是写给人看的文档是写给模型看的使用说明书。我总结了几条实操经验。第一描述里必须包含什么时候用和什么时候不用。只写这个工具能查询天气是不够的要写当用户询问某地当前或未来天气时使用当用户只是闲聊提到天气时不要调用。第二参数描述要给出格式示例。模型对格式的敏感度远高于对自然语言描述的敏感度一个date: 格式为 YYYY-MM-DD例如 2024-03-15比传入日期有用得多。第三工具数量要克制。我实测下来单个 Harness 暴露给模型的工具超过 15 个之后选择准确率会明显下降。如果业务确实需要很多能力应该做分层路由先用一个轻量模型或规则把请求分类再只把相关工具集暴露给主模型。还有一个容易被忽略的点工具返回结果的格式。如果工具返回一大坨原始 JSON模型解析起来很吃力还占上下文。正确做法是在 Harness 层做一次结果摘要把关键字段提取出来用简洁的结构化文本回填给模型。这一步能显著降低后续轮次的 token 消耗。2.2 输出解析别指望模型永远输出合法 JSON让模型输出 JSON 是常见做法但永远不要假设它一定合法。我见过模型在 JSON 里塞注释、用单引号、漏逗号、把数字写成字符串的各种情况。所以 Harness 的解析层必须做三件事容错解析先尝试标准解析失败后用宽松解析器、schema 校验用 JSON Schema 或 Pydantic 之类的工具校验字段类型和必填项、失败重试解析失败时把错误信息回填给模型让它重新输出。重试也要有上限一般 2 到 3 次就够了再多说明提示词本身有问题。重试时把你上次输出的 JSON 在第 X 行解析失败错误是 Y请重新输出这样的具体反馈给模型修复成功率会高很多。如果连续失败应该降级到兜底策略要么返回一个安全的默认响应要么转人工而不是让流程卡死。2.3 上下文窗口最贵的资源要精打细算上下文窗口是 Agent 运行中最贵的资源因为每一轮都要把历史重新喂进去。Harness 层必须有一套上下文管理策略。我的做法是分层保留系统提示词和工具定义永远保留这部分可以靠提示词缓存降低成本最近 N 轮对话完整保留更早的历史做滚动摘要用一个便宜的小模型定期把旧对话压缩成要点工具返回的大块结果只保留摘要和关键字段原始数据存到外部存储需要时再按引用取回。提示上下文管理里有个反直觉的点——不是保留越多越好。无关的历史信息会干扰模型判断反而降低准确率。我做过对比把无关历史裁掉之后同一任务的工具选择准确率提升了将近一成。3. Loop 工程收敛条件、轮次控制与成本治理Loop 是 Agent 的心跳它决定了任务能不能自己走到终点。这一层做不好前面 Harness 再精致也白搭因为流程根本停不下来或者停错了地方。3.1 终止条件不止是模型说完成了最朴素的终止条件是模型输出一个任务完成的信号。但只靠这个非常危险模型可能过早宣布完成也可能永远不宣布。我的做法是多重终止条件并行判断模型显式声明完成、达到最大轮次上限、连续 N 轮没有产生新的有效动作、检测到重复动作、外部超时。任意一个触发就终止然后根据触发原因决定是正常返回还是走异常处理。这里有个细节值得展开重复动作检测。实现方式是把每一轮的工具调用工具名加参数做哈希存到一个集合里如果连续出现相同哈希就判定为原地打转。这个机制帮我拦下过很多次死循环。检测到重复后不要直接终止而是给模型一个你刚才已经调用过这个工具且结果相同请换一种思路的提示往往能把它拉回正轨。3.2 轮次上限怎么定算一笔成本账轮次上限不能拍脑袋定。我的方法是先估算单轮平均 token 消耗再乘以可接受的单任务成本上限反推出轮次上限。举个例子假设单轮平均消耗 3000 token含历史上下文你希望单任务成本控制在 5 万 token 以内那轮次上限大概就是 15 轮左右。当然这是粗略估算实际还要看任务复杂度分布。更精细的做法是动态轮次预算。简单任务给少一点复杂任务给多一点甚至可以在 Loop 运行过程中根据当前进展动态调整剩余预算。如果模型连续几轮都在有效推进可以适当放宽如果一直在原地打转就提前收紧。这种动态策略比固定上限更省成本。3.3 上下文在 Loop 中的膨胀问题Loop 每转一圈上下文就长一截这是成本失控的主因。除了前面 Harness 层讲的滚动摘要Loop 层还要做动作结果的选择性回填。不是每个工具结果都需要完整带回下一轮。比如一个搜索工具返回了 20 条结果模型可能只需要其中 3 条最相关的那就在回填前做一次筛选或摘要。我常用的一个技巧是给工具结果打重要性标签。关键结果比如最终答案、错误信息完整保留辅助结果比如中间查询只保留摘要。这个标签可以由工具自己声明也可以由 Harness 根据结果类型推断。实测下来这个策略能把长任务的 token 消耗降低三到四成。终止条件触发场景后续处理模型声明完成正常结束返回结果达到轮次上限任务过于复杂或卡住返回部分结果并标记未完成连续无有效动作模型陷入空转提示换思路或终止重复动作检测原地打转提示换思路多次触发则终止外部超时整体耗时超限中断并保存状态4. Graph 工程状态持久化、分支容错与可观测性当 Agent 从单打独斗变成团队协作Graph 就成了必需品。它把整个流程的控制流从隐式的代码逻辑变成显式的图结构带来的是可观测、可干预、可恢复三大好处。4.1 状态持久化断点续跑的基石Graph 最核心的价值是状态持久化。每个节点执行完就把当前状态包括已完成的步骤、中间结果、上下文摘要写入持久化存储。这样一旦流程中断可以从最后一个成功节点恢复而不是从头再来。状态存储的选型要看场景轻量场景用文件或 SQLite 就够生产环境建议用 Redis 加数据库的组合Redis 存热状态数据库存冷备份。状态设计有个关键原则状态要可序列化、要精简。不要把整个上下文塞进状态里只存恢复所需的最小信息。大块的中间数据存到对象存储状态里只放引用。我见过把几十兆上下文塞进状态的实现恢复时序列化反序列化就耗掉好几秒完全失去了断点续跑的意义。4.2 分支与容错让流程能应对意外Graph 的边可以带条件这让流程能根据中间结果走不同分支。比如一个内容审核流程检测到违规就走人工复核分支正常就走发布分支。分支设计的难点在于条件要互斥且完备不能出现两个条件同时满足或者都不满足的情况。我的做法是给每条边明确优先级并且设置一个默认分支兜底。容错方面Graph 支持节点级重试和降级路径。某个节点失败时先按配置重试几次注意重试要幂等否则会重复产生副作用重试仍失败则走降级路径。降级路径可以是换一个备用工具、跳过该节点、或者转人工。关键是降级决策要显式配置而不是藏在代码的 try-catch 里这样运维时才能一眼看清流程的容错策略。4.3 可观测性出问题时能一眼定位Graph 的显式结构天然适合做可观测性。每个节点的进入、退出、耗时、输入输出、状态变化都可以打点上报。我建议至少记录这几类信息节点执行轨迹谁在什么时候执行了什么、关键决策点分支走了哪条、为什么、异常与重试哪里失败了、重试了几次、资源消耗每个节点花了多少 token 和时间。这些数据汇总起来就能回答生产环境里最常被问的问题这个任务为什么慢为什么失败钱花在哪了。没有这套可观测性Agent 上线之后就是个黑盒出了问题只能靠猜。我踩过的坑是早期没做节点级打点结果一个长流程偶发失败排查了整整两天才定位到是某个外部接口的偶发超时。5. 三层如何协同一个完整任务的流转拆解把三层单独讲清楚之后更重要的是理解它们怎么协同。我用一个自动调研并生成报告的任务来串一遍这个任务足够典型能覆盖三层的交互。5.1 任务入口与 Graph 编排任务进来先由 Graph 接管。Graph 定义了这样一条流程解析需求节点 → 拆解子问题节点 → 并行调研节点多个子 Loop 并行→ 汇总节点 → 生成报告节点 → 校验节点。其中并行调研是一个扇出节点会启动多个独立的 Loop 实例每个负责一个子问题。Graph 在这里的职责是编排和状态管理它记录哪些子问题已完成、哪些还在跑、哪些失败了需要重试。这个设计的好处是并行加速和故障隔离。某个子问题的调研卡住了不影响其他子问题某个子问题失败只重跑那一个 Loop不用整个任务重来。如果没有 Graph 这层这些都得在代码里手写既难维护又难观测。5.2 单个 Loop 内部的 Harness 调用每个调研 Loop 内部就是 Harness 和 Loop 的配合。Loop 负责推进先让模型规划要查什么然后通过 Harness 调用搜索工具拿到结果后判断信息是否充分不充分就继续查充分了就输出小结。Harness 在这里负责把搜索工具的描述、参数格式、结果摘要都处理好让模型能顺畅地调用。这里有个协同细节Loop 的终止条件要和 Graph 的节点完成条件对齐。比如 Graph 期望这个调研节点在 8 轮内完成那 Loop 的轮次上限就应该设成 8 或者略低避免 Loop 跑太久拖垮整个 Graph 的节奏。这种跨层的参数对齐是三层协同里最容易被忽略的地方。5.3 状态在层间的传递三层之间传递的核心是状态。Graph 持有全局状态每个节点执行时把需要的状态传给 LoopLoop 执行过程中通过 Harness 产生的新信息要能回写到 Graph 的状态里。这个传递要遵循最小必要原则只传当前节点需要的不要一股脑全传否则状态会越来越臃肿。我推荐的做法是给状态定义清晰的 schema每个节点声明自己读哪些字段、写哪些字段。这样既能保证传递的准确性又能在出问题时快速定位是哪个节点写坏了状态。这个 schema 最好用代码定义比如 Pydantic 模型而不是散落在各处的字典操作这样类型检查和文档都能自动生成。6. 生产环境里那些文档不会写的坑前面讲的都是应该怎么做这一节讲实际会怎么翻车。这些坑我基本都亲自踩过写出来希望能帮你少走弯路。6.1 工具幂等性重试带来的副作用Graph 支持节点重试Loop 也可能因为各种原因重放动作这就要求工具必须幂等。我踩过最惨的一次是一个发送通知的工具没有做幂等重试机制触发后用户收到了三条重复通知。修复方式很简单给工具加一个幂等键比如任务 ID 加动作 ID执行前先查这个键是否已执行过。但问题是很多工具在开发时根本没想到会被重试等上线才发现。所以我的建议是所有有副作用的工具默认都要考虑幂等。写操作、发送操作、支付操作一个都不能漏。幂等键的生成要稳定不能每次调用都变否则幂等就失效了。6.2 上下文里的污染问题Loop 跑久了上下文里会积累很多中间结果其中有些是错误的或者过时的。比如模型早期的一次错误工具调用返回了错误信息这个错误信息一直留在上下文里会持续干扰后续判断。我遇到过模型被自己早期的错误结论带偏越走越远的情况。解决办法是主动清理上下文。当某个中间结论被后续证据推翻时要显式地在上下文里标注此前的结论 X 已被推翻原因是 Y而不是让它默默留在那里。更好的做法是维护一个结论区只保留当前有效的结论被推翻的移出或标记失效。这个机制能显著提升长任务的稳定性。6.3 并发下的状态竞争Graph 支持并行节点这就带来了状态竞争问题。多个并行 Loop 同时往全局状态里写如果不加控制就会互相覆盖。我见过两个并行调研节点同时更新已收集信息字段结果后写的把先写的覆盖了丢了一半数据。解决方案是状态分区每个并行节点写自己独立的状态分区最后由汇总节点合并。如果确实需要写共享状态就要用乐观锁或版本号机制写入时检查版本冲突则重试。这个在单机开发时不容易暴露一上分布式就原形毕露所以并行设计要提前考虑。6.4 模型版本变更带来的连锁反应生产环境里模型会升级而模型升级往往会让原本调好的提示词和工具描述失效。我经历过一次模型小版本更新工具选择准确率从九成多掉到七成排查半天才发现是新模型对工具描述里的某些措辞理解变了。所以模型版本要锁定升级前必须跑回归测试用一批固定的测试用例验证工具选择、输出格式、终止判断这些关键行为有没有退化。回归测试集要提前积累把线上遇到过的典型 case 都收进去。每次模型升级或提示词大改都跑一遍。这个投入在早期看起来麻烦但能避免上线后的灾难。坑根因修复方案重复通知工具不幂等加幂等键执行前查重被早期错误带偏上下文污染维护结论区显式标注推翻并行丢数据状态竞争状态分区或乐观锁升级后行为退化模型版本变更锁定版本跑回归测试7. 从原型到生产我的落地节奏建议最后聊聊落地节奏。很多团队一上来就想搭一套完整的 Graph 编排结果复杂度爆炸反而拖慢了进度。我的建议是分层递进先把 Harness 做扎实确保单次工具调用稳定再加 Loop让单任务能自己跑完最后才上 Graph处理多环节编排。每一层都跑稳了再往上加这样问题定位也简单。具体节奏上Harness 阶段重点打磨工具描述和输出解析用一批测试用例把工具选择准确率做到九成以上。Loop 阶段重点调终止条件和轮次预算用真实任务跑出收敛率和成本分布。Graph 阶段才引入状态持久化和并行编排这时候前面的稳定性已经打好底子Graph 的复杂度才可控。还有一点可观测性要从第一天就做不要等出问题才补。哪怕最开始只是简单地把每轮的工具调用和结果打到日志里也比什么都没有强。等流程复杂了再补可观测性成本会高得多。我在实际项目里的体会是Agent 工程的难点从来不在模型本身而在这些看不见的工程细节。Harness 的工具描述差一句话Loop 的终止条件少一个Graph 的状态设计臃肿一点累积起来就是生产环境的稳定性灾难。把这三层各自的职责守好层与层之间的接口对齐Agent 才能真正从 Demo 变成能扛事的生产系统。这套架构不是银弹但它提供了一套清晰的思考框架让你在遇到问题时知道该往哪一层去找答案。

关于本文作者

来自尧图内容编辑团队

尧图内容编辑团队 内容团队

尧图内容编辑团队

本文由尧图网络内容编辑团队执笔。团队由资深项目经理、前端工程师与设计师组成,所有内容均来自亲手交付的真实项目,先讲清问题、再给出可落地的解法。尧图深耕北京网站建设十年,服务过京华建材集团、智造科技等各行业客户,把一线经验沉淀为可复用的行业观察。

  • 十年建站经验,覆盖建材、制造、服务、文创等
  • 项目经理把关选题与事实准确性
  • 工程师与设计师联合撰写专业细节
  • 统一编辑规范,保证文风与排版一致
  • 每月复盘转化数据,迭代选题方向

延伸阅读

相关资讯与近期热门内容

深度阅读推荐

建站决策前值得细读的三篇

网站改版的5个关键决策
2024-08-12

网站改版的5个关键决策

什么时候该改版、改到什么程度、如何避免流量掉光,京华建材集团改版复盘给出答案。

获取专属建站方案

看完文章,把您的行业与预算告诉我们,免费获取一份量身定制的官网建设方案与报价。

立即免费咨询