Agent生产化实践:Harness、Loop、Graph三层架构全解析 开头做Agent项目的时候我栽过一个特别典型的跟头提示词、工具调用、任务流转全堆在一个大循环里模型一换、需求一改整个系统就跟着推倒重来。后来在生产环境里反复调试了半年多我才慢慢意识到问题从来不在模型选得够不够强而在于我没想清楚一件事——一套Agent系统里到底哪一层该负责什么。今天想聊的这套东西就是我沉淀下来的分层思路Harness、Loop、Graph 三层架构。它不是某个开源框架的专属概念而是一种工程组织方式用来回答Agent生产化过程中最头疼的三个问题流程怎么编排、循环怎么收敛、环境怎么隔离。如果你正在做Agent开发或者正在被能跑但不好改的Agent项目折磨这篇文章大概能给你一些可以直接抄走的经验。1. 为什么Agent工程需要三层架构一次重构带给我的教训1.1 单循环大杂烩的典型死法一年多以前我接手过一套已经上线的Agent服务。它的结构非常直接一个大while循环模型返回什么指令我就执行什么执行完接着调模型直到模型自己说任务完成。听上去很灵活但真跑起来之后问题一个接一个。最常见的是模型开始反复调用同一个查询工具把库存接口打到报警却始终给不出结论后来想优化想让它先做一轮资料整理再给答案却发现提示词、工具定义、上下文格式全都要改改完还连带弄坏了一批原有行为。那个阶段我的直观感受是这个系统像是把业务流程、模型推理、工具调用焊死在了同一根管道里任何一环变动都会引发连锁反应。复盘之后我归纳出三个致命缺陷。第一个是边界缺失模型可以访问所有注册工具没有任何权限分层一次错误调用就可能造成线上事故。第二个是流程不可编排所有逻辑都在循环里靠模型自由发挥无法显式表达先做什么、后做什么、什么条件下做什么。第三个是模型强耦合提示词里处处写着特定模型的格式要求工具定义也按某个模型的脾气调过参数换模型等于重写整个Agent。这三个缺陷基本就是大部分个人Agent项目刚起步时的真实写照。如果你现在觉得自己的Agent代码能跑、但不好改大概率也踩在这三块石头上。1.2 三层架构的基本分工为了根治这些问题我重新整理了整个系统把它拆成了三层并且规定每一层只解决一类问题。Graph层是任务编排层负责定义整个任务流程长什么样——有多少个阶段、每个阶段什么条件进入、哪些步骤可以并行、哪些地方需要人工审批。它的存在让流程这个东西变成了可配置、可看见、改得动的对象而不是散落在代码分支里的隐式逻辑。Loop层是智能体执行层负责单一智能体在一次任务中的感知-思考-行动循环。它不管全流程怎么编排只管当前这一步怎么走完。你可以把它理解成Agent的中央处理器每拿到一个观察结果就决定下一步行动。Harness层是运行时环境层负责为上面两层提供稳定的运行底座包括提示词管理、上下文窗口、工具注册与权限控制、安全策略、生命周期与可观测性。用一句话概括这三层的关系Graph决定做什么Loop决定怎么做Harness决定在哪里做、能做什么、不能做什么。它们之间是单向依赖的Graph调用LoopLoop运行在Harness之上。正因为依赖是单向的每一层才能独立替换、独立测试这也是这套架构后来能支撑我快速迭代的关键。2. Harness层Agent的外壳、安全边界与运行时环境2.1 Harness和Agent到底差在哪先解决一个概念问题harness和agent的区别到底是什么很多文章把这两个词混着用但工程上必须分得清清楚楚。Agent指的是那个能做决策的智能体本体它包含三样东西记忆上下文、推理能力模型调用和行动能力工具调用。而Harness是承载Agent的整套工程装置说得直白一点就是Agent外面的驾驶舱和安全带。拿开源社区里频繁出现的deepseek harness这类项目举例它们做的其实不是重新发明一个Agent而是给某个模型底座套上一套成熟的harness统一管理提示词、规范工具定义、提供文件操作和代码执行环境、输出结构化日志。这种harness化思路之所以流行是因为它解决了真实痛点——模型底座可以换但工程设施是通用的。你换了一个模型harness不用动Loop不用动甚至Graph都不用动只要底层适配器换一下即可。2.2 Harness层到底要管哪些事我归纳了五件事这是Harness层必须管好的少一件都会在生产环境出问题。第一提示词管理。系统提示词、任务提示词、工具使用说明要分层组织不能全写死在一个字符串里。第二上下文窗口管理。窗口总会被填满Harness层必须负责裁剪、摘要化、遗忘策略否则Loop层无以为继。第三工具注册与权限控制。哪些工具当前任务可用哪些需要管理员审批哪些工具调用结果要脱敏都属于Harness的职责。第四生命周期管理。Agent从启动、运行、暂停、恢复、终止到优雅退出整个生命周期都需要Harness来管理。第五可观测性。每一次模型调用、工具调用、状态转移都要能追踪这是后面调试和成本分析的基础。这五件事有一个共同特点它们都不是智能本身但没有它们智能在真实环境里跑不起来。就像一辆性能再好的车没有可靠的刹车、仪表盘和安全带你也不敢上高速。2.3 设计harness时的几个关键取舍提示词分层是我的第一个关键决策。我把提示词拆成三层系统级提示词描述全局规则任务级提示词描述当前任务的约束实例级提示词由运行时动态填充比如当前用户的会话信息。这样做的好处是当某次任务需要调整约束时不至于改动全局规则测试范围能被严格控制住。工具权限的最小化是第二个关键决策。我见过太多Agent项目把所有工具开放给模型结果模型在歧义状态下访问了不该访问的服务。现在我的做法是给每个工具标注所需的权限等级Harness在调用前做拦截校验。对于有外部副作用的工具比如发送消息、写数据库、删除文件强制要求人工确认这就是人在环human-in-the-loop。实测下来这堵墙帮我挡掉了大量潜在事故。上下文管理的策略性压缩是我踩过最多坑的地方。很多人一见到上下文快满了就粗暴截断但这样会直接丢掉关键决策信息。更好的做法是先做分层摘要全局任务信息保底保留对话细节按时间衰减做摘要工具结果只保留结构化结论。这套策略我调整了差不多三周才稳定下来之后长任务的成功率明显上了一个台阶。3. Loop层思考-行动-观察的循环到底该怎么设计3.1 ReAct循环不是简单的while TrueAgent的Loop层核心是经典的ReAct模式Thought推理、Action行动、Observation观察不断循环直到任务收敛。但工程实现上它绝不是一个while True那么简单。我现在的实现里每一轮循环都是一个结构化的状态机状态输入包含当前任务目标、历史上下文摘要、上一轮的观察结果、本轮可用的工具列表。模型推理模型根据上述输入产生 Thought 和下一步 Action。行动执行Harness层校验并执行 Action返回结构化 Observation。收敛判断根据本轮结果判断是继续循环、进入人工确认、还是终止任务。每一轮循环都产出一条带唯一 trace_id 的记录这样出了问题能直接定位到具体轮次、具体调用。这段逻辑用伪代码看会更直观while current_step max_steps: # 从Harness取当前状态喂给模型做推理 action agent.act(state) # Harness负责校验并执行工具调用返回结构化观察 observation harness.execute(action) # 更新上下文状态准备下一轮 state harness.update_state(state, observation) current_step 1 # 是否收敛由评估器判断而不是由模型说了算 if evaluator.is_done(state): break这里的重点在最后一行是否结束不应该只听模型自己说任务完成而要由一个独立的评估器结合结构化状态来判断否则模型很容易在信息不足时过早收尾或者相反永远觉得自己还能再优化一轮。3.2 循环终止让Agent该停时就停循环设计里最容易被忽略的是终止条件。模型驱动的循环天然有停不下来的趋势因为模型总倾向于多做一些尝试。生产环境里循环失控的直接后果是成本和可用性双杀。某些任务跑到四五十轮用户早就不耐烦了账单倒是很诚实。我现在会硬性设置三个保险最大轮次上限、无进展熔断、任务完成评估器。先说最大轮次上限根据任务类型设定。复杂分析任务我一般给30轮普通问答任务给8轮超过上限强制终止并转人工。这里要注意上限不能一刀切按任务类型分级才有意义。其次是无进展熔断记录每轮是否产生了有意义的中间状态如果连续N轮都没有新增有效信息就触发熔断不再无谓地调用模型。这个机制尤其适合那些模型反复调用同一个工具、却拿不到新信息的场景。最后是关键节点嵌入轻量评估器判断当前结果是否满足任务完成条件满足则立刻停止。这个任务完成评估器是我后来才加上去的效果非常显著。原本很多任务会多跑好几轮加完之后平均轮次降了大概30%结果质量反而没有下降因为多余的轮次往往是在重复调工具、重复读取相同的数据。3.3 Loop engineering把循环变成可持续优化的工程单元loop engineering这个词在社区里越来越流行它指的不是写一个循环而是把一个Agent的循环过程当成一个独立的工程单元来设计和优化。我认为核心有三个优化方向。第一个方向是状态结构优化。每一轮循环中间状态的Schema要稳定、可扩展否则后面做Graph编排时会很痛苦。我吃过亏最初状态字段是动态拼出来的结果Graph想去读某个字段发现不同任务里这个字段的类型都不一样后来全部收敛成了固定Schema再加可选扩展字段问题才解决。第二个方向是工具调用策略。当模型不确定该调用哪个工具时与其让它瞎猜不如在工具注册表里设计一个澄清工具——意图不明确时先调用澄清工具向用户提问绝不让模型直接猜一个权限敏感的接口。第三个方向是多窗口并行。当单个Loop处理不过来时拆成多个并行的Loop实例每个维护独立的上下文窗口上层再用调度器汇总结果。这是我在处理高并发任务时最有效的手段。关键点是让每个Loop实例只处理一个子任务互不共享上下文只在汇总节点做信息交换避免上下文串扰。有些项目把这种模式叫loop 多窗口本质上就是把并发的压力从单点循环转移到多实例调度上。4. Graph层把多循环编排成一张可管理的任务地图4.1 单循环的边界就是Graph的起点单一Loop再优化也有边界一个循环只适合处理线性的、无分支任务。一旦任务出现以下特征就说明你需要上一个Graph层了。比如任务需要多个不同角色或子任务协作存在条件分支不同用户意图要走向不同流程有需要并行执行的步骤比如同时收集多个数据源流程中嵌入了人工审批节点或者同一个Agent需要被复用在不同流程里。Graph层解决的核心问题就是把这些复杂度从代码分支和模型的自由发挥中解放出来变成一张看得见、改得动的图结构。我特别喜欢的一个类比是Graph是作战地图Loop是行军节奏Harness是军规和后勤保障。没有地图部队模型再能打也会迷路没有节奏部队会空耗弹药没有军规部队可能碰触雷区。4.2 Graph的核心设计节点、边、状态Graph的工程实现包含三个核心要素。第一个是节点每个节点是一个原子操作单元。常见的有LLM节点调用模型做推理、工具节点执行某个工具、条件节点做路由判断、子图节点嵌套另一个Graph。第二个是边定义节点之间的流转关系可以是无条件跳转也可以带条件判断。第三个是全局状态整张图共享一个状态对象节点可以读取和写入状态字段状态Schema决定了整张图的内存模型。这里有一个关键设计要点Graph里的LLM节点本质上就是跑一小段Loop。所以严格来说Graph是Loop的编排者而不是替代者。单节点内的循环负责把这一步想清楚图级别的边负责把整个流程串起来。如果你的Graph节点内部没有自己的小循环它就只能做一次性判断很多需要多轮推理才能完成的子任务就做不好。为了保证自研框架的可扩展性我的节点接口设计很简单class GraphNode(Protocol): def run(self, ctx: GraphContext) - NodeResult: 读取全局状态执行节点逻辑写入新的状态所有节点只需要实现这个接口图之间的流转关系全由配置文件描述。这样加一个新节点不用动图引擎代码只写一个类和一段配置就行。4.3 工具选型LangGraph、Snap Graph Builder与自研方案的取舍现在市面上Graph编排工具不少我简单对比几个主流选型希望能帮你少走弯路。方案优点缺点适合场景LangGraph生态全、状态管理能力强、支持条件边/并行/持久化学习曲线陡、调试门槛偏高复杂生产系统Snap Graph Builder可视化拖拽、上手快、适合快速原型复杂逻辑下灵活性稍弱快速验证流程原型自研图引擎完全可控、能和现有业务系统贴合紧密需自行维护基础能力特殊状态管理需求/长期复用我自己的做法是原型阶段用可视化工具跑通确认流程之后如果复杂度可控就再抽象成自己的一套轻量配置化Graph。自研Graph引擎的核心代码也就几百行关键是节点注册表的设计——让节点实现统一接口再通过配置文件描述图结构。这个方案在团队内部落地后新流程上线的时间从几天缩短到了几小时。5. 三层协同的生产实践一个并发Agent服务的落地方案5.1 依赖方向与分层演进生产环境里三层架构的代码组织方式我的习惯是分模块维护并且严格遵守单向依赖方向harness模块在最底层不依赖上层任何模块loop模块依赖harness实现循环逻辑graph模块依赖loop和harness负责编排。这样的单向依赖让每一层都可以单独测试、单独替换。举个例子我想试验一个新的模型底座只需要改harness里的适配器loop不变、graph不变。反过来我想调整业务流程只需要改graph配置loop和harness完全不用动。这种各改各的体验是在单循环大杂烩时期根本不敢想的。另外我把Agent的运行状态尽量无状态化放到Redis或数据库里而不是留在进程内存中。这样即使某个实例崩溃了也能从最近的状态快照恢复不至于整个任务重头再来。这点在长时间运行的任务里格外重要我遇到过好几次实例重启的情况快照恢复机制帮了大忙。5.2 并发、成本与可观测性的通盘考虑Agent生产化绕不开并发问题热搜里那个ai agent怎么扛并发的问题我在实践中是这样处理的。首先明确一点不能把一个Agent实例同时塞进多个任务请求因为Loop的上下文是有状态的一旦串掉就全错。正确的做法是实例池化——每个用户任务分配一个独立的Agent实例也就是一个独立的Loop加独立上下文任务结束就销毁。Graph层的并行分支则另开子Loop实例互不共享上下文只在汇总节点做状态合并。成本控制上我引入两级预算。Graph预算决定整个任务最多允许多少次模型调用、多少tokenLoop预算决定单个循环段的上限超出预算一律熔断转人工。模型匹配也很重要重推理节点用强模型轻量分类节点用便宜的小模型靠harness的模型适配器实现任意切换。可观测性在并发场景下尤其重要。我给每个任务生成一个trace_id它在Graph层、Loop层、Harness层全线传递所有日志都带同一个trace_id。排查线上问题时先通过trace_id找到整个任务的生命周期再定位到具体轮次、具体工具调用效率比原来翻日志快得多。有一次线上反馈某个任务结果不对我顺着trace_id一看发现是第7轮工具调用时读到了旧缓存而不是模型推理错误整个定位过程不到五分钟。5.3 模型切换与灰度发布的操作路径三层架构带来的一个重要红利是模型可以灰度切换。我们做模型升级时不是全局替换而是通过harness的路由策略比如先让5%的新任务用新模型观察指标正常后逐步放开。因为上层逻辑和模型不耦合灰度切换不需要改业务代码只需要改配置。具体操作上我会在harness的适配器里加一个可配置的模型路由表按任务类型、用户ID哈希、或者随机百分比来决定走哪个模型通道。配合可观测性数据对比新老模型在任务完成率、平均轮次、工具调用成功率上的差异。这个流程走顺之后每次模型升级的回归风险都被控制在很小的范围内。有一次新模型在其他方面表现更好但在某个特定任务类型上工具调用格式总出错因为灰度机制存在这个问题只影响了5%的任务回滚修改只花了二十分钟。6. 生产环境踩过的坑与复盘6.1 把业务逻辑写进Loop模型一换全盘崩这是我踩过的第一个大坑。早期我把先查库存再报价这种业务规则写进循环提示词里模型确实能执行但很不稳定经常跳过查库存直接报价。后来靠修改提示词勉强约束住换模型后同样的提示词完全失效。复盘后我发现根因是业务规则属于流程约束应该放在Graph层用显式节点和边来表达而Loop层只保留通用的观察-思考-行动能力。把业务逻辑从Loop中剥离出来后Loop变得非常稳定换模型的影响面也大大缩小。现在我的编码习惯是如果一段逻辑必须被强制执行绝不依赖模型的理解能力一定把它做成显式的流程节点。6.2 Graph设计过度小任务也给复杂流程图同样要警惕的是反面——Graph过度设计。有一段时间我沉迷于把每一个小任务都画成一张复杂的图条件分支、并行节点、人工审批一应俱全。结果就是图本身的维护成本比写代码还高配置改起来战战兢兢团队新人上手也困难。后来我定了条原则能用单Loop解决的任务绝不引入Graph只有确认当前Loop已经出现分支多、状态多、复用多的迹象时才升级到Graph。工程上软件复杂度应该跟着需求走而不是跟着架构师的想象力走。这个原则让我后来避免了很多自我感动式的重构。6.3 上下文溢出与模型幻觉叠加的连锁事故还有一次线上事故让我印象很深。某个长任务跑到中段上下文窗口接近上限Harness触发了粗暴截断策略把任务背景摘要给截掉了。接下来模型在缺失上下文的情况下继续运行开始一本正经地编造库存数据。用户看到数字是错的但格式很规范直到下游系统报警才发现。这次事故之后我把上下文压缩升级为分层摘要加关键信息锁定任务背景这样的全局信息永远不会被截断对话细节则按重要程度分级处理。这件事也验证了一件事上下文管理是Harness层真正的护城河这活你交给Loop层是干不好的。后来我再遇到类似的Agent内部状态丢失问题第一反应永远是检查Harness的上下文策略而不是怀疑模型能力。6.4 我现在的演进路线与最后一些经验如果让我总结一条从零到一的路径大概是这样的先用单Loop加Harness跑通最核心的闭环验证模型能力和工具调用链路等任务开始出现分支、需要复用或并行时再引入Graph编排每次改动都保证单向依赖不破Graph依赖LoopLoop依赖Harness把可观测性当成和功能一样重要的事从第一天就加trace。现在再回头想Agent工程并没有那么多玄学。模型的智能是底座但一个能在生产环境稳定跑下去的系统靠的是Harness把环境管住、Loop把循环管稳、Graph把流程管清。这套三层架构也许不是唯一答案但它确实帮我解决了一连串实际问题从并发瓶颈到成本失控从模型切换风险到线上事故定位。如果你正好也在单循环大杂烩里挣扎我建议先别急着上更炫的框架动手给系统画出第一条边界把环境、循环、流程分开你会发现维护成本和事故率都会有肉眼可见的下降。