首页 / 资讯中心 / 文章详情

Agent生产级落地:Harness、Loop、Graph三层架构实践

Agent生产级落地:Harness、Loop、Graph三层架构实践 ★ FEATURED ARTICLE
接手过的 Agent 项目多了之后你会发现一个规律单 Agent 的 Demo 人人都能跑通多 Agent 的流程也有一堆开源框架可以用但真正到了生产环境问题往往出在最容易被忽略的工程外壳上——模型输出不稳定、工具调用失败没人管、循环跑飞停不下来、编排全写死在代码里。我这两年在这上面踩了不少坑后来逐步把项目拆成 Harness、Loop、Graph 三层来看才算是找到了一套能扛住线上流量、也方便团队协作的落地方式。这篇文章就把这套三层架构的拆法、各自的职责边界以及生产环境里的实践细节完整梳理一遍。需要先说明的是Harness、Loop、Graph 并不是某个官方标准里的固定名词而是我在多个 Agent 项目里沉淀出的一套工程分层方法论。Harness 解决“Agent 怎么被安全地包起来跑”Loop 解决“Agent 怎么正确地反复思考与行动”Graph 解决“多个 Agent 或者多步骤任务怎么编排成一条可靠流水线”。三者加在一起才构成一个真正能上生产的 Agent 系统。1. Agent 项目失控的现场为什么单靠提示词走不通1.1 一个典型的“跑得通但不敢上线”的项目先说一个我实际接手过的项目原型。当时团队用某大模型 API 做了一个智能客服助手核心逻辑很简单用户提问模型判断是否需要调用订单查询工具需要就调用拿到结果后组织语言回复。Demo 阶段一切顺利模型能正确识别大部分意图工具调用偶尔出错但重试一次就好。可一旦进入生产压力测试问题接连冒出来同样的用户问题今天走的是订单查询明天变成了直接回复“我无法访问您的订单信息”工具接口超时后Agent 不仅没有重试反而继续编造了一个订单状态最离谱的一次模型陷入了自我对话的死循环连续调用了十几次同一个工具把第三方接口的配额直接打满。事后复盘时我们发现这根本不是某一个环节出了问题而是整个系统缺少分层治理。提示词只能约束模型“说什么”约束不了“怎么调用工具”“什么时候该停下”“多个任务之间怎么流转”这些都需要在提示词之外的工程层面去兜底。于是我们开始把所有逻辑重新分层三层架构就是从这种混乱现场里长出来的。1.2 从三层视角重新拆解 Agent 工程把 Agent 工程拆成 Harness、Loop、Graph 三层不是拍脑袋为了显得专业而是因为每一层解决的是完全不同性质的问题。Harness 解决的是“环境与外围”。模型本身是无状态的它需要一个外壳来承载配置、密钥、工具注册表、日志追踪、限流熔断这些基础设施。就像一台发动机必须装进车架里配上仪表盘、油箱和安全带才能上路Harness 就是 Agent 的“车架与仪表盘”。Loop 解决的是“单次任务的思考与行动循环”。Agent 的本质是一个循环接收信息、推理决策、执行动作、观察结果、再次推理。这个循环怎么设计终止条件、怎么处理连续失败、怎么管理上下文是 Loop 层要回答的问题。Graph 解决的是“多任务、多角色之间的编排”。当系统里有多个 Agent 或者一条复杂的多步骤流水线时谁先执行、谁后执行、某个分支失败后是重试还是走另一个分支这些问题不能写死在 Loop 的代码里必须抽象成一张可配置、可观测的图。我一直跟团队强调一个类比Harness 是“让 Agent 跑得稳”Loop 是“让 Agent 跑得对”Graph 是“让 Agent 们跑得有序”。如果只关注其中一层其他两层迟早会变成事故现场。2. Harness 层把模型“包”进可控的实验环境2.1 Harness 到底管什么Harness 这个词的本意是“马具”引申义就是“束缚与控制”。在 Agent 工程里Harness 层负责的是所有“模型之外的工程配套”。我梳理过的核心职责有五块配置管理模型名称、温度参数、API 地址、密钥、超时时间等所有可变参数集中管理不能散落在代码里。工具注册与白名单Agent 能调用哪些工具、不能调用哪些工具必须在 Harness 层显式声明而不是等模型自己“想起来”用哪个。运行环境隔离每个 Agent 任务运行时的会话隔离、临时目录、环境变量保证不同任务之间不互相污染。日志与追踪记录每一次模型请求、工具调用、中间思考过程这是事后排查问题的唯一抓手。限流、重试与熔断模型 API 和工具接口都可能抖动Harness 层要做统一的容错策略。很多团队把这一层的东西散落在业务代码里今天在这个函数里读环境变量明天在那个模块里初始化客户端最后导致同一个 Agent 在不同环境里行为不一致。我的建议是无论项目多小都要在一开始就建立一个独立的 Harness 模块把上述五类能力收敛进来。2.2 工具链封装与插件加载的常见坑工具链封装是 Harness 层最容易出问题的地方。我见过最典型的报错是 “harness failed to load plugins”这个错误在不少插件化框架里都会出现根因通常是两类一类是插件目录配置错误框架按照配置去找.jar或者.dll文件路径不对自然加载失败另一类是插件依赖冲突插件 A 依赖某个库的 1.0 版本框架自身用的是 2.0运行时类加载报错。给一个实践结论封装工具时不要直接暴露裸函数给模型。比如查天气工具模型需要传的参数应该是“城市名”而不是“API URL AppKey 签名”这一堆东西。每个工具都应该封装成一个“语义化输入、标准化输出”的接口模型只需要使用人能理解的参数复杂逻辑全部藏在 Harness 内部。封装之后工具返回的结构也要统一建议至少包含以下字段字段含义示例success调用是否成功true / falsedata成功时返回的数据{city: 上海, temp: 28}error失败时的错误信息timeout after 3selapsed_ms调用耗时毫秒120有了统一的返回结构Loop 层才能基于 success 字段做重试 or 放弃的决策否则模型面对一段非结构化报错文本很容易产生幻觉。2.3 可观测性让黑盒 Agent 变得可调试Harness 层最容易被忽略、但生产价值最高的能力是可观测性。模型推理本身是个黑盒你不可能像调试普通代码那样打日志看中间变量。但我们可以把 Agent 的“行为轨迹”完整记录下来。我在项目里会强制记录三类日志。第一类是模型交互日志包括完整的请求体、响应体、token 消耗用于分析模型是否产生了预期外的输出。第二类是工具调用日志包括触发了哪个工具、入参什么、返回什么、耗时多久用于定位工具链路的问题。第三类是决策日志记录当前循环到了第几步、模型选择走哪个分支、为什么退出循环用于复现 Agent 的“思考路径”。有一次生产事故用户反馈某类问题总是答非所问。我们检查模型交互日志发现模型其实准确理解了用户意图但在工具调用之前一次上下文整理步骤中系统错误地把一个 JSON 字符串截断了导致模型看到的是损坏的查询结果。如果没有完整的工具调用日志这个问题几乎无法定位。Harness 层的另一个重点是限流与熔断。很多 Agent 项目对模型 API 的调用量预估过于乐观生产环境一上线并发一高API 直接 429。我在 Harness 里会统一封装一个调用网关支持令牌桶限流、失败重试指数退避、连续失败熔断。熔断之后快速失败返回给 Loop 层让 Agent 走兜底话术而不是让用户一直转圈。3. Loop 层核心循环是 Agent 的发动机3.1 ReAct 范式与循环状态机如果说 Harness 是外壳那 Loop 就是 Agent 的心脏。几乎所有现代 Agent 都遵循 ReAct 范式Reason思考当前情况加 Act执行一个动作反复交替直到任务完成。写成伪代码就是while not finished: thought llm.reason(context) action extract_action(thought) if action.type finish: break result execute_tool(action) context.add_observation(result)这段循环看起来简单但生产环境的复杂全在“状态”里。我建议不要用零散的 while 循环意识去写而是要显式设计一个循环状态机。我常用的状态集合是READY循环初始化完成等待模型产生第一个决策THINKING模型正在推理尚未返回可执行动作ACTING已确定动作正在执行工具调用OBSERVING工具返回结果正在整合进上下文RETRYING工具调用失败准备重试FINISHED任务正常完成输出最终回复ABORTED达到最大步数或检测到异常强制终止状态机的好处是所有异常路径都有明确处理入口。比如 ACTING 状态如果连续出现三次说明可能陷入了重复调用工具的怪圈状态机可以自动切换到 ABORTED而不是继续放任模型循环。3.2 循环的退出条件与兜底策略写 Loop 层代码时最重要的不是“怎么循环”而是“什么时候不循环”。模型天然没有“停止”的概念尤其在对话式 Agent 里它很容易为了凑流程而继续追加行动。我设置了四个硬性退出条件模型显式输出 finish 标志表示它认为任务已完成。循环次数超过上限一般单次任务我设为 5 到 10 次视任务复杂度调整。Token 消耗超过预算阈值。连续工具调用均失败且已经没有重试价值。第 4 条需要展开说说。连续失败不能简单地“重试 N 次”而要考虑失败的类型。我常把失败分为可重试超时、限流、网络抖动和不可重试参数非法、权限不足、业务不存在。可重试的走指数退避比如第 1 次等 500ms、第 2 次等 1s、第 3 次等 2s不可重试的直接放弃并输出提示话术不要浪费次数。兜底策略也很关键。当任务注定无法完成时Agent 的输出必须是一个明确的“无法完成 原因 已做尝试”而不是一个看似成功但内容空洞的回复。我会在系统提示词里写死这个要求同时 Loop 层代码也会在 ABORTED 状态下强制重写最后一条回复给用户的永远是可解释的失败信息。3.3 记忆管理短期上下文与长期存储的边界Loop 层另一个绕不开的话题是记忆。很多人对 Agent 记忆的理解就是“把历史消息全塞进 prompt”但这在生产环境很快会撞上两个问题一是 token 成本爆炸二是长上下文导致模型注意力分散反而答不准。我的分层做法是短期上下文只保留当前任务的“必要信息”。具体来说保留用户最初的目标、最近的 3 轮关键推理、最近一次工具调用结果把更早的中间推理过程压缩成摘要。中期用滑动窗口保留最近 N 条对话并做重写压缩长期则进入向量数据库按用户或会话维护可检索记忆块。有一回我们给 Agent 加了长期记忆后发现它开始“记忆错乱”——把前几天 A 用户的偏好回复给了 B 用户。查了很久发现是检索层召回时没有做严格的会话隔离。后来在 Harness 和 Loop 之间加了一层记忆路由所有记忆读写都必须带上 session_id 与 user_id 双重过滤问题才消失。记忆还有一个容易踩的坑模型会主动把错误信息写进记忆。比如工具返回失败原因“订单不存在”模型把这句话存成了事实“用户订单不存在”。长期下来记忆库里全是脏数据。我现在的做法是凡是工具调用结果一律以结构化方式直接注入上下文不允许模型转述后再存入记忆。4. Graph 层从单循环到多智能体编排4.1 为什么需要图循环解决不了协作问题Loop 层的粒度是一个任务内部但生产系统通常由多个 Agent 或多项子任务组成。比如一个“企业知识库问答机器人”主 Agent 负责理解问题但遇到需要查数据库的问题时它要发起一个 SQL Agent遇到需要查询线上文档时它又要发起一个检索 Agent。这里面的协作关系是主 Agent 是编排者子 Agent 是执行者。这个关系如果用 Loop 硬写代码会变成一团乱麻。你需要判断上一个 Agent 返回了什么、根据结果决定调下一个还是回头重新问主 Agent、某个 Agent 超时了怎么处理。这些事情全部描述清楚后你会发现最自然的表达方式就是一张图节点是动作或 Agent边是流转条件。Graph 层还有一个重要作用是支持人机协同。比如在自动化审批 Agent 流程里财务数据校验通过了就自动走完校验异常就进入人工审核分支。这个分支用图来表达非常直观用循环来实现则极其痛苦。4.2 Graph 设计中的路由、并行与重试子图在建图时我最常用的设计模式是三种顺序链、并行扇、条件路由。顺序链最简单适合流水线式任务。比如“先查用户信息再查订单信息最后生成回复”每个节点依次执行。注意顺序链里如果某一个节点失败后续节点是否还需要执行要提前定义。通常我会把所有下游节点标记为 SKIPPED而不是让整条链路挂起。并行扇适合“需要汇总多个数据源”的场景。比如要做一个市场分析报告需要同时抓取竞品价格、用户评论、销售数据三个任务互不依赖可以并行跑。并行扇的实现要注意两个参数最大并发数和汇聚超时时间。有一个节点迟迟不返回不能无限等下去超时后要么标记该节点失败并继续汇聚已有结果要么整体失败走重试。条件路由最复杂也最容易写错。比如主 Agent 决定调用哪个子 Agent 时判断逻辑可能是基于模型输出的分类结果。实际项目中我见过很多次模型把用户问题分错了类路由到错误的 Agent导致答非所问。我的建议是对于关键路由不要让模型直接输出目标 Agent 名称而是让模型输出“用户问题所属领域”这一抽象标签再由代码做路由映射。这样即使领域标签判断不准确后续也方便增加规则修正。重试子图是 Graph 层容易被忽略的高级玩法。普通重试是在 Loop 层对单个工具调用重试但 Graph 层经常需要重试“一整段子流程”。举个例子一个订单同步流程是“读取订单源数据 - 格式转换 - 写入目标数据库”如果写入失败重新跑的时候不能直接从第一步再来因为转换结果可能是可复用的。我会把“格式转换”和“写入”单独建一个重试子图失败时只重放这一段而不是从源头重新开始。4.3 图与代码的边界别把所有逻辑都塞进图里Graph 层引入之后很容易走向另一个极端把业务逻辑全部可视化配置化代码里反而什么都不剩。我吃过这个亏。有一次团队为了“让业务运营也能编排流程”把价格计算规则也放进了图里让一个超大规模 DAG 动态处理。结果性能极差每次任务都要加载几百个节点元数据而一旦价格规则改版改图比改代码还痛苦。我现在给团队的边界原则很简单稳定的业务步骤、明确的数据处理逻辑写在代码里图的节点只做调用。容易变化的编排关系、需要观测与人工介入的分支放在图里配置。图的粒度控制在“动作或子任务”级别不放“字段级计算”。Graph 层的价值在于“看清流程的全貌”和“快速调整流转路径”而不是替代代码去做一切。一张好的 Agent 编排图应该看起来像一张清晰的地铁线路图而不是一团乱麻的神经网络示意图。5. 一次生产事故复盘三层联动的排查链路5.1 事故表象与最初误判讲一个真实的事故案例。当时我们上线了一个“智能工单分类 自动分派”系统流程设计为工单进入 - 主 Agent 分析问题类型 - 分成“故障报修”“业务咨询”“投诉建议”三类 - 路由到不同的处理 Agent - 若处理 Agent 无法解决升级人工。上线第三天运营反馈大量工单被错误分派到了“人工升级”队列人工处理好几个小时用户投诉量暴涨。我们最初怀疑是主 Agent 的分类能力不行于是调了大量 prompt把它分析的工单文本拆出来人工看发现模型判断得其实非常准确。排除了分类问题后又怀疑子 Agent 的执行逻辑出错把子 Agent 的日志调出来后发现它们也几乎都能正确处理分类后的任务。这就像 Debug 时各个模块都正常但整体链路就是错。最后问题定位用了接近两个小时。5.2 从 Harness 到 Loop 再到 Graph 的定位过程我们还是按三层架构的顺序做了排查。先看 Harness 层。检查配置发现工单处理链路的超时时间设置得非常紧张各子 Agent 的模型调用和工具调用加在一起很容易超时。超时后Graph 层配置的“超时兜底分支”会把工单默认路由到人工队列。也就是说不是 Agent 处理不了而是它压根没被给到足够时间处理。再看 Loop 层。子 Agent 的核心循环上限设置为 3 步而业务方希望先查历史工单、再查知识库、最后生成回复正常需要 4 到 5 步。循环被强制终止时按照我们兜底策略会把任务标记为“未能完成”这个失败信号又触发了 Graph 层的重试逻辑。重试了两轮还是超时于是最终走入了人工升级分支。最后看 Graph 层。整条链路的依赖设计是线性的主 Agent 完成后必须等待子 Agent 完成才能结束整个流程。子 Agent 内部的操作是串行的每一步都等模型推理加工具调用累计时间非常长。这里其实完全可以用并行节点把“查历史工单”和“查知识库”两个操作同时发起将 Loop 步数从 5 压到 3 步以内。5.3 修复方案与验证结果最终修复做了三处调整正好对应三层架构。Harness 层把工单链路的超时时间调整为动态超时基于子 Agent 规划的步数乘以单步超时时间计算总时限同时在工具调用网关中对知识库接口增加降级策略查询超时先返回空结果而不是直接报错。Loop 层根据业务实际需求把标准工单循环上限从 3 调高到 6但增加了每步动作的“有效性检查”——如果模型连续两次输出相同且无新信息的动作直接判定为低效循环并提前终止。Graph 层把“查历史工单”和“查知识库”两个独立节点改为并行节点汇聚后再进入回复生成节点。整个链路的平均耗时从原来的 45 秒降到了 18 秒人工升级率从 17% 降回 2% 以内。这次事故让我意识到三层架构不只是写代码时的一种组织方式更是排查问题时的一套诊断清单。以后每次线上出问题我都会先问三个问题是不是 Harness 的配置或外围能力出问题了是不是 Loop 的循环条件或记忆管理出问题了是不是 Graph 的编排或路由设计出问题了按次序排查基本不会再像无头苍蝇一样乱翻日志。6. 落地建议三层架构的实施顺序与团队分工6.1 从哪一层开始建设如果你正在启动一个新 Agent 项目我的建议是不要一上来就铺开三层全部搭建而是按以下顺序循序渐进第一步先把 Harness 做扎实。哪怕只是单 Agent 的 Demo也要把配置管理、工具封装、日志追踪这三件事做好。这花不了多少时间但能让你后续排查问题时有据可依。第二步完善 Loop 层的循环设计与终止条件。把退出条件、重试策略、兜底输出写清楚。这一层直接决定了 Agent 单点的可靠性是上线前的必检项。第三步当业务出现“多个 Agent 或复杂多步流程”时再引入 Graph。Graph 最重要的是一开始就明确节点粒度和路由规则不要等图上节点超过二十个再来重构那时候改造成本会很高。顺序不是绝对的但如果项目处于早期、流程还没定型过早做 Graph 反而会拖慢迭代速度——流程每天都在变调整图的成本比调整代码更高。6.2 每层需要什么样的测试与验收标准三层架构每个层级都有对应的测试重点团队在写用例时最好分开来写。Harness 层的核心是配置与容错测试。我会重点覆盖缺配置时是否能快速报错并提示缺失项工具调用超时、限流触发时能否按预期重试或熔断日志是否完整记录了请求与响应。Loop 层的核心是行为与终止测试。重点覆盖正常任务能否在规定步数内完成并正确退出连续失败后是否按策略重试或放弃循环是否会陷入死循环并触发 ABORTED上下文过长时摘要压缩是否正常。Graph 层的核心是编排与路由测试。重点覆盖各节点是否按预期顺序执行条件分支在不同输入下是否走对路径某个节点失败时下游是否被正确标记为 SKIPPED并行节点超时后是否按配置汇聚。每次发布前我都会要求三层测试覆盖率至少达到 90%而且每一层必须准备至少一条人为构造的异常用例。Agent 项目最大的特征不是正常路径有多好而是异常路径必须比正常路径更稳。6.3 最后分享几个实际经验从我个人经验来说有几个细节如果不注意三层架构哪怕搭好了也会反复踩坑。第一配置中心的价值怎么强调都不为过。很多项目跑得好好的某天突然线上行为跟测试环境不一致最后发现是某个环境变量写死在了某个服务里。所有模型参数、工具地址、超时时间都应该走配置中心并且区分环境线上不要用别人的默认值。第二不要迷信“全自动”。Graph 层如果设计得足够清晰人工介入点应该是非常明确的。哪些分支需要人工审批哪些异常需要人工复核在做图时就要提前留好入口。我见过很多团队嘴上说要全自动生产环境出了几次事故后又悄悄加了人工但因为没有预留节点每次加人工都要改架构非常痛苦。第三模型还是会犯低级错误。即使你把 Harness、Loop、Graph 都做到位也不能完全指望模型永不犯错。在关键业务场景关键字段必须做规则校验。比如工单分配对象模型输出一个部门名称代码里就应该有一张部门白名单不在白名单内直接重新走一次推理而不是信任模型的字符串输出。第四成本控制要放在 Harness 层做。每次模型调用都消耗 token我建议在 Harness 层做 token 用量统计设定单任务预算超过预算自动终止并能输出“任务过于复杂请简化问题”之类的提示。这不仅能省钱还能倒逼业务梳理需求粒度。7. 写在最后把三层架构当成习惯而不是理论说回这套三层架构本身。Harness、Loop、Graph 这三个词没有多神秘它们只是把 Agent 工程里那些“一定要做但不能散着做”的事情收敛成了清晰的分层结构。Harness 让 Agent 跑得稳Loop 让 Agent 跑得对Graph 让 Agent 们跑得有序三句话足够解释它们各自的价值。我实际带团队做项目后的一个明显体会是新同学接手代码时如果项目本身有三层清晰的划分他们上手速度会快很多因为每一层都可以独立阅读和理解。反之如果所有逻辑混在一个巨型 Agent 类里哪怕注释写得再好改起来也极度痛苦。最后再分享一个小技巧每次新项目启动我会把三层结构的职责清单打印出来贴在工位上。出 bug 时先对号入座别急着改代码。多数情况下bug 的根因并不在你第一眼看到的那一行而在某一层你没检查到的配置或边界条件里。这个习惯帮我省下了大量无效调试的时间希望也能帮到你。
阅读完成 · 觉得有帮助?
咨询建站