恒美微站
首页
关于我们
建站服务
主题模板
案例展示
资讯中心
联系我们
生产级AI Agent架构:Harness、Loop、Graph三层拆分实战
首页
资讯中心
/
生产级AI Agent架构:Harness、Loop、Graph三层拆分实战
生产级AI Agent架构:Harness、Loop、Graph三层拆分实战
发布时间:2026/10/3 11:27:11
先说个真实的场景我手上有个 Agent 项目早期就是“模型 一堆工具函数”直接跑上线第一周就被打回原形——模型自己改着改着参数就跑偏了工具调用的上下文越堆越乱两个子任务并行的时候还会互相抢资源。后来我狠下心把系统重构成了Harness、Loop、Graph三层架构才真正把 Agent 从“能跑 demo”推进到“能上生产”。这篇文章就是围绕这三层架构把我踩过的坑、做过的取舍、以及生产环境里的实操细节一次性讲清楚适合正在搞 Agent 框架选型、或者已经在做 Agent 项目但觉得“哪里不对劲”的开发者参考。Harness 负责管住 Agent 的手脚Loop 负责管住 Agent 的节奏Graph 负责管住 Agent 的路径。这三层分开看都不复杂但组合起来就是一套完整的 Agent 工程骨架。下面我会从设计思路开始逐层拆解最后给出一套可以直接抄的落地组合方案。1. 整体设计思路为什么要拆成三层1.1 三层架构的职责边界我先用一句话概括这三层的关系Harness 是 Agent 的运行容器Loop 是 Agent 的行为循环Graph 是 Agent 的任务编排。Harness 层解决的是“Agent 能碰什么”。它把大模型的能力封装在一个可控的壳里模型只能通过壳暴露的工具、参数、权限去做事。很多做 Agent 的团队把火力全放在提示词上指望 prompt 让模型“别乱来”但 prompt 是软的Harness 是硬的。真正生产级的项目靠的是 Harness 做硬性限制——模型拿不到的东西就是拿不到模型调不了的工具就是调不了这比任何提示词都可靠。Loop 层解决的是“Agent 怎么持续做事”。一个真实任务很少是模型一句话就能完成的往往需要“推理 - 调用工具 - 观察结果 - 再推理”的多次迭代。Loop 要处理的就是这个迭代过程怎么退出循环、怎么控制步数、怎么在循环里插入人工审批和异常恢复。业内常说的 ReAct 模式其实就是最朴素的 Loop但生产环境里的 Loop 要复杂得多必须有中断、恢复、回退机制。Graph 层解决的是“多个环节怎么串起来”。Agent 任务往往不是单线流程而是有分支、有并行、有先后依赖的多步骤过程。Graph 把任务拆成节点节点之间的边表示依赖关系这样既能做 DAG 级别的调度又能在某个节点失败时做局部重试而不是整个流程推倒重来。1.2 为什么不是“一个框架搞定”市面上很多 Agent 框架试图用一个东西同时解决上述三个问题比如直接在框架里既管工具调用又管流程编排。但这种大而全的设计到了生产环境很容易出问题。举一个实际例子我在一个项目里需要让 Agent 既能调用内部 API又能操作数据库还要能读写文件。如果用单一框架把所有能力都揉在一起工具列表会越来越长模型在做决策时经常选错工具。拆分层之后Harness 按业务域维护了 A/B 两套工具集Graph 决定什么时候把控制权交给哪套 HarnessLoop 负责在这个切换过程中保持状态一致。我实测下来工具选择的准确率提升了接近三成。除了准确性分层还带来了运维上的好处。Loop 层出问题只需要看循环日志Graph 层出问题只需要看节点状态Harness 层出问题只需要看资源配额和审计日志。问题边界清晰定位速度就快——这个优势在生产环境救过我很多次。团队在做技术选型时不要被“全家桶”框架迷惑。先想清楚你的 Agent 是偏“重执行”还是“重编排”偏重执行就强化 Harness偏重编排就强化 Graph。把三层搭成乐高积木比搭成混凝土更灵活。2. Harness 层给 Agent 套上缰绳2.1 Harness 的核心组成Harness 层我自己总结了四个关键部件工具白名单、参数校验器、沙箱环境、审计日志。工具白名单好理解就是只暴露需要的工具。这里有个容易被忽略的细节不仅要限制“有什么工具”还要限制“每个工具的调用频率和资源额度”。比如一个 Agent 可以在循环里反复调用某个敏感接口如果不限制频率一次任务就能打爆下游服务。我在 Harness 里加了基于令牌桶的频率控制每个工具独立配额实测下来下游服务的抖动率下降了非常多。参数校验器解决的是“工具参数不合法”的问题。模型生成的 JSON 参数经常出现类型错误、越界值、缺字段等情况直接在 Harness 这层做强校验能避免脏数据流入业务系统。我一般用 JSON Schema 做第一道校验再用代码做第二道业务校验。比如调用支付接口时金额必须大于 0 且小于单笔限额这类规则写死在 Harness 里模型再聪明也绕不过去。沙箱环境解决的是“执行环境不可控”的问题。有网友问过“deepseek harness 附带skill怎么部署到内网服务器”本质上关心的是 Harness 的隔离能力。我的做法是把模型调用、工具执行都放在独立的容器或进程里通过网络策略限制进出流量宿主机只暴露必要的端口。这样即使工具被恶意调用影响面也被控制住了。审计日志是 Harness 的“黑匣子”。每一条工具调用、每一次参数校验失败、每一个被拒绝的敏感操作都记录在案。这不仅是排查问题的依据也是后续优化 Loop 和 Graph 的原料——我经常翻审计日志看哪些工具被反复拒绝调用从而反推提示词或工具设计哪里有缺陷。2.2 Harness 接入模型的注意事项接入模型时最关键的是一句话不要让模型直接接触真实世界的接口。我在生产环境里做了个中间层叫“工具代理”。模型只认识工具代理暴露的语义化接口而工具代理内部再去调用真实 API。这样做的好处是当真实 API 发生变更时只需要改工具代理的适配逻辑模型侧的提示词和工具描述完全不用动。另一个实操细节是上下文窗口管理。模型能看到的工具描述总量是有限的Harness 需要动态决定在上下文中放入哪些工具描述。我常用的做法是“按需注入”——根据当前任务阶段从工具注册中心拉取最相关的几个工具描述而不是一次性把几百个工具全部塞进上下文。实测下来这能把模型的工具选择准确率提升不少同时减少 token 消耗。Harness 部署在内网时要额外注意三个点模型服务的访问走内部域名不要走公网网关减少链路耗时和鉴权复杂度把 Harness 的配置工具列表、配额、黑白名单外置到配置中心改配置不用重新发布代码模型返回的内容要过一层内容过滤防止注入的提示词或恶意输出进入日志系统。3. Loop 层控制 Agent 的呼吸节奏3.1 Loop 的循环结构与退出机制Loop 的核心结构是“观察 - 思考 - 行动 - 观察”但生产级 Loop 必须回答一个问题什么时候停。我在代码里常用一个状态机来管循环生命周期。状态的流转大概是这样class AgentLoop: def __init__(self, max_steps10, max_token_per_step2000): self.state READY self.step_count 0 self.max_steps max_steps self.max_token_per_step max_token_per_step self.history [] def run(self, task): while self.state ! FINISHED: if self.step_count self.max_steps: self.state TIMEOUT break observation self.observe() # 获取当前环境/工具结果 thought self.think(observation) # 调用模型推理 action self.decide(thought) # 选择工具动作或结束标记 if action EXIT: self.state FINISHED else: self.execute(action) self.step_count 1 self.history.append(action) return self.state退出条件至少要有三种模型主动判断任务完成达到最大步数强制退出异常分支触发兜底策略。没有强制退出机制就是事故现场——我有一次同事的 Agent 在没有 max_steps 限制的情况下因为模型一直认为“还需要再调一次接口”硬生生跑了几个小时账单感人。除了步数控制还有一个容易踩坑的是 token 预算控制。每一步的输入输出都要累计进总预算单步 token 超限要能降级处理比如截断历史、压缩摘要、或者直接进入人工确认分支。3.2 循环中的自我修正与人工打断Agent 在循环中会犯错这是常态。Loop 层要做的不是“防止犯错”而是“低成本纠错”。我在循环里加了一个“自我校验”环节模型每次给出行动方案时先让一个轻量校验模型或者同一模型的低温度采样检查这个方案是否有明显瑕疵比如工具参数类型错误、步骤重复、上下文缺失。校验不通过就退回上一步重试最多重试两次否则挂起转人工。人工打断机制也很重要尤其是在生产系统里Agent 的正确率不可能做到 100%。我的设计是Loop 在每次执行敏感操作删除操作、发送消息、修改数据库前都会检查是否开启“人工审批闸门”。闸门开着就把待执行动作放进审批队列等人工确认后再放行。这套机制在我们内部叫“人在回路”它不是降低效率而是保证 Agent 敢放手做事的前提——因为有兜底所以更激进。3.3 一个工程细节Loop 的状态持久化Loop 跑着跑着挂了怎么办答案是Loop 的每一步状态都要持久化。我把 Loop 的状态拆成两层——元数据层和上下文层。元数据包括当前步数、状态机状态、重试次数存到 Redis上下文层包括历史对话、工具返回结果、模型思考过程存到对象存储或数据库。这样当服务重启或者进程被杀时可以拿持久化的状态恢复 Loop而不是让整个任务从头再来。这里有一个我趟过的坑不要把所有上下文一次性塞进恢复的消息列表。恢复时只挑当前任务阶段最相关的部分比如最近 3 步的完整日志 所有关键结论无关的过程性内容做摘要否则 Agent 恢复后反而会被历史信息带偏。4. Graph 层精心规划 Agent 的行动地图4.1 从“单 Agent 循环”到“多节点编排”Loop 解决的是单 Agent 的单线任务但真实生产系统几乎没有单线任务。我接手过的 Agent 项目里常常出现这样的场景先要收集信息 - 然后做信息分析 - 再根据分析结果调用不同业务系统 - 最后生成报告。如果用单 Loop 硬做流程分支和异常路径会让循环逻辑爆炸性地复杂。Graph 层把流程设计成有向无环图DAG。每个节点是一个“可执行的单元”节点之间通过边表示依赖关系。比如节点 A收集用户输入节点 B调用检索服务节点 C调用数据库节点 D汇总 B 和 C 的结果依赖 B、C节点 E生成最终报告依赖 D这种设计的最大好处是“并行”和“局部容错”变得非常自然。B 和 C 没有依赖关系就可以并行执行D 失败后只需重跑 D不需要重跑 A。实现 Graph 时不需要迷信重型编排引擎。我常用的方案是维护一个节点列表和依赖映射自己写一个轻量调度器就够用了关键代码甚至不到 200 行。当然如果团队已经有成熟的 workflow 基座直接复用也行——但前提是节点间的数据传递协议要统一不然编排层迟早变成蜘蛛网。4.2 节点间数据传递与状态共享Graph 节点之间必然要传递数据。我见过最混乱的写法是每个节点自己去共享存储里读全量上下文然后各自过滤。这在节点少的时候还好节点一多数据血缘根本查不清楚。我的实践是所有节点只通过统一的“上下文总线”交换数据。节点 A 的输出显式声明为topic_A.output_field节点 B 的输入显式声明为topic_A.output_field或topic_C.some_field。调度器负责检查每个节点的上游数据是否齐备齐备才允许节点启动。这样每个节点都是“输入输出即契约”很好测试也好替换。还需要设计好“全局共享信息”和“局部私有信息”的边界。比如用户 ID、任务 ID 这种全局信息放在固定字段里节点内部产生的临时变量一律不进全局区。这样既不会污染其他节点也方便归因。4.3 Graph 的容错与重试策略节点执行失败是常态Graph 层必须设计重试策略。我在生产环境里用了三层顺序同节点重试失败后延迟 1s / 3s / 5s 递增重试最多 3 次适合瞬时抖动类错误子图重试同节点重试仍失败则回退到该节点的“最近成功子图”从上游最近的稳定点重放避免全图重启全图熔断如果核心节点连续失败超过阈值直接终止整个任务并通知人工。除了重试还要设计“跳过与降级”逻辑。比如检索服务挂了但数据库里有一份昨天生成的缓存快照Graph 可以跳过检索节点直接用快照。生产系统要的不是“永远不失败”而是“失败后有合理的路可走”。5. 三层架构的落地组合从代码到部署5.1 一个完整示例构建带收敛判断的 AG-News 分类 Agent为了把三层架构串起来我拿一个常见的文本分类场景做演示。假设任务是给定一条新闻Agent 需要判断它属于哪个类别如果置信度不足则继续检索更多信息直到确定或达到最大轮数。Harness 层暴露两个工具search_news(query)和predict_category(text)。两个工具都做了参数校验和超时控制。Loop 层维持循环每次模型得到分类结果后检查置信度是否高于阈值比如 0.85。低于阈值就触发search_news去补充证据然后重新分类。Graph 层把整体流程拆成三个节点nodes { ingest: {type: input, next: [classify]}, classify: { type: agent_loop, max_steps: 3, next: [judge] }, judge: { type: checker, threshold: 0.85, next_ok: [finish], next_retry: [classify] } }这个例子虽然简单但已经能看出三层的配合Graph 决定整体流程走向Loop 控制单节点内的迭代次数Harness 保证每一步的工具访问都安全可控。5.2 部署流程与配置管理部署时我的做法是每个层独立成服务或独立成模块Log 和配置分开。Harness 层独立成一个 sidecar 服务所有工具调用走这个 sidecarLoop 层是“逻辑进程”依赖 Harness 的接口和状态存储Graph 层是“调度进程”只负责节点调度不直接接触模型和工具。这样可以单独扩缩容信息收集类的任务可以把 Graph 调度实例多加几个模型调用密集的任务可以把 Loop 实例多加几个而 Harness 保持稳定。关于配置我用 YAML 统一管理部署时通过环境变量注入密钥。永远不要把模型服务的 API Key 写在代码里也不要直接写到镜像里。用配置中心或密钥管理系统上线前自动拉取即可。5.3 启动失败与插件加载问题很多人在部署 Harness 相关的工程时遇到“harness failed to load plugins”这类报错。我整理了一下常见原因和处理方法报错现象可能原因排查方法plugins 目录下文件加载不出来插件版本与主框架不兼容检查插件 manifest 中的 API 版本号降级或升级到匹配版本启动时提示某入口未激活插件的入口类名和配置里不一致打开插件配置文件确认入口声明路径是否正确内网部署后插件连接超时插件需要访问公网更新源把插件依赖的静态资源离线导入内网配置镜像源日志显示无操作权限插件运行用户权限不足检查系统用户/容器用户是否对工作目录有读写权限这类问题的通用解法是先看日志里第一个报错的时间点再往前翻 5 条日志通常真正的根源在“加载过程中”而不是“加载失败时”。6. 生产环境的问题排查与避坑实录6.1 并发场景下的状态隔离有网友问过“AI Agent 怎么扛并发”这个问题的核心不在模型而在共享状态。多用户同时跑 Agent 时绝不能共用一个 Loop 状态实例。我踩过的坑是把 Agent 实例做成全局单例结果用户 A 的上下文被用户 B 的任务覆盖双方都看到对方的敏感数据。后来我改成“每个任务一个会话上下文”按任务 ID 做隔离共享连接池只负责底层模型的调用不保存业务状态。这样并发量提升的同时数据安全问题也解决了。另一个并发坑是工具调用层的限流。多个 Agent 同时调用同一个数据库工具数据库连接池很容易被打满。我在 Harness 里给数据库工具单独配了连接池大小限流不跟其他工具共用线程池效果立竿见影。6.2 上下文管理中的自引用问题模型上下文里曾经出现过“self referencing loop detected”类似的报错——不是说模型报错而是我们自己的上下文序列化逻辑出了问题。比如某个节点尝试把一个包含自身引用的对象塞进历史列表导致序列化爆炸。排查方法是在写入历史记录前做一次深度拷贝或者限定只保留可 JSON 序列化的字段。我在代码里加了一个简单的过滤函数把所有非基本类型的字段剔除掉只存文本和数字结果。这样虽然损失了一些对象关系信息但换来了稳定性和日志可读性很值得。6.3 与 RPA 系统协同的边界问题不少人问“Harness RPA 怎么落地”这是一个很实战的场景。我的经验是Harness 不直接去调 RPA 的机器人而是通过“任务队列”间接对接。Agent 在 Harness 里判断需要执行 RPA 流程时向队列投递一个带参数的 RPA 任务RPA 机器人从队列领取任务并回传执行状态。这层解耦带来一个好处Agent 不会被 RPA 的长时间执行阻塞Loop 可以先去处理其他事情等 RPA 任务完成通知到达后再继续推进 Graph 的后续节点。如果把 RPA 封装成一个同步工具直接调用Loop 会卡死在等待响应上整个 Agent 系统的吞吐都会受影响。6.4 安全加固的三条铁律最后提三条安全铁律生产系统必须刻在脑子里Agent 的 API 权限永远走最小授权。模型能访问的数据和操作必须比业务人员的最少权限还少。工具调用的结果不要直接拼进下一轮模型的系统提示词。先经过解析和校验防止恶意内容污染模型的判断。所有敏感操作必须留痕。即使不加人工审批也一定要有可追溯的审计日志——这既是合规要求也是事故复盘的基础。我做 Agent 工程这几年最大的体会是Agent 能力的上限由模型决定但下限由工程架构决定。Harness、Loop、Graph 这三层架构不是理论空谈而是我一遍遍被线上事故教育后总结出的实践骨架。先按这三层理清楚职责边界再去填代码、调模型、做优化整个系统的稳定性和可维护性会明显上一个台阶。如果只记住一句话那就是把约束做在结构里把容错做在流程里把安全做在骨子里。