ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

Agent三层架构实战拆解:Harness、Loop与Graph如何撑起生产级AI应用

Agent三层架构实战拆解:Harness、Loop与Graph如何撑起生产级AI应用 最近一个月我微信里至少有五个人来问我同一个问题harness 是不是就是 agent 换个说法问的人里有刚转行做 AI 应用开发的也有做了几年后端想转 Agent 方向的。我每次都要从“不是”开始解释然后讲 Harness、Loop、Graph 这三层到底是什么关系以及它们在生产环境里分别扛什么活。今天干脆把这段话整理成文章——不是科普是一个踩过坑的人对 Agent 工程三层架构的完整拆解顺便把最近 DeepSeek Harness、Claude Code 这些工具带火之后大家最关心的几个问题harness 和 agent 区别、loop engineering 到底在工程化什么、AI Agent 怎么扛并发、agent 框架与编排怎么选一次说清楚。1. Harness 不是 Agent 的“另一个名字”三层架构的第一个边界1.1 为什么 AI 圈突然都在聊 harness 这个词Harness 这个词本身来自工程领域原意是“安全绳”“操纵缰绳”。在 Agent 工程里它指的是给大模型的原始能力套上的一层“可操作的运行外壳”。最近 DeepSeek Harness、Claude Code 这些工具频繁出现在热搜里很多人的第一反应是“这不就是个 Agent 框架吗”但我更愿意把它理解成 Agent 的“底座”或者“工作台”。我举个例子你就明白了。假设大模型是一个能力很强但完全不了解职场规则的实习生你给他一个任务他可能会做、可能做偏、可能用错工具、可能越权操作。Harness 就是他入职时领到的工位、工牌、操作手册和权限卡规定他能用什么工具、只能碰哪些数据、工具调用失败后怎么重试、上下文满了怎么处理。Agent 强调的是“能自主行动的主体”而 Harness 强调的是“约束和支撑这个主体的运行环境”。所以我的结论非常直接Harness 不等于 Agent它是 Agent 运行的第一层基础设施。没有 Harness 的 Agent 是实验室里的玩具有了 Harness 的 Agent 才是能进生产系统的“员工”。1.2 三层架构的分工边界我习惯把 Agent 工程拆成三层每一层回答的问题完全不同层级核心问题典型产出常见实现Harness外壳Agent 能做什么、不能做什么工具注册表、权限策略、会话上下文管理、模型网关DeepSeek Harness、Claude Code CLI、自研运行时Loop循环Agent 如何一步步完成任务推理-行动-观察的迭代循环、终止条件、重试机制ReAct 模式、LangGraph 的循环节点、自研状态机Graph编排多个 Agent/循环如何协作节点拓扑、条件路由、并行分支、子图嵌套LangGraph、Snap Graph Builder、自研 DAG 引擎很多人把这三层混为一谈觉得“反正都是写代码让 Agent 干活”。但在我实际做项目的过程中这三层的故障表现完全不同Harness 出问题通常是“工具根本没加载进来”或者“权限拦截把正常请求也拦了”Loop 出问题通常是“Agent 陷入死循环”“答非所问地重复某个动作”Graph 出问题通常是“分支走错了”“并行任务的结果对不上”。如果你连自己在调哪一层都不知道排查效率会非常低。1.3 为什么模型本身不能直接跑生产这个问题我几乎每次分享都要强调你直接调 GPT、Claude、DeepSeek 的 API让它“帮我写一个爬虫脚本”它能做到。你让它“每天早上 9 点检查服务器告警如果是磁盘满了就清理然后发一条通知到钉钉”它也能做到——但你敢让它自己跑吗不敢的原因有三层。第一模型输出是概率性的同一个 prompt 可能这次输出合法工具调用、下次就输出一段废话这需要 Harness 层的解析器和校验器去兜底。第二工具调用格式很脆弱模型返回的 JSON 稍微多一个逗号、少一个结尾括号解析就崩了需要 Harness 层做容错修复。第三模型没有权限概念它不知道“这个 API 是高危操作需要用户确认”这是 Harness 层必须强加的安全边界。我在一个内部项目里做过一次实验让同一个 Agent 在裸 API 模式下和加了 Harness 的模式下各自跑 100 次文件操作类任务。裸 API 模式下大概有 15 次出现工具参数格式错误导致执行失败还有 4 次模型直接“自由发挥”输出了不存在的工具名加了 Harness 之后通过解析容错和工具名模糊匹配失败率降到了 3 次以下。这就是 Harness 的价值——它不负责让模型变聪明它负责让模型变可靠。2. LoopAgent 真正的心脏循环不是 while True2.1 ReAct 循环的最小单元长什么样Loop 这一层是 Agent 最核心的运行机制。业界最经典的实现是 ReActReasoning Acting模式也就是“推理—行动—观察”的循环。很多人第一次听这个概念觉得很简单不就是 while True 里面调用几次模型嘛。真做过的都知道这个循环里全是细节。我用伪代码描述一个最小可运行的 Agent Loopdef agent_loop(task, harness): state {task: task, history: [], step: 0} while not should_stop(state): thought harness.reason(state) # 模型推理下一步该做什么 action harness.parse_action(thought) # 从推理文本中解析出工具调用 result harness.execute_action(action) # 通过 Harness 执行工具 state[history].append((thought, action, result)) state[step] 1 return state[history]看起来很简单但每一行都有坑。reason这一步要考虑上下文窗口够不够历史太长要压缩或裁剪parse_action这一步要处理模型输出带 markdown 代码块、带额外解释文本、甚至带拼写错误的情况execute_action这一步要处理工具超时、工具崩溃、工具返回结果太大等等问题。整个 Loop 的工程质量决定了 Agent 是像一个靠谱员工一样把事情做完还是像一个喝了酒的新人一样东一榔头西一棒槌。2.2 循环终止条件不是 while True是状态收敛我见过很多初学者的 Agent 代码循环写得很开心但完全没有想清楚“什么时候停下来”。结果就是任务已经做完了Agent 还在继续输出“我已经完成了任务还有什么需要做的吗”然后自己回答“让我再检查一遍”接着又把工具重新调用了一遍。设计终止条件是我认为 Loop 工程里最重要的一件事。我在生产里常用的终止条件有四类任务完成标志模型的输出中包含明确的完成信号比如“任务已完成”“结果是”并且不再包含工具调用意图。最大迭代次数这是硬保底防止任何情况下死循环。我一般根据任务复杂度设 10 到 30 步超过就标记失败并返回当前进度。工具连续失败次数某个工具连续失败 N 次说明要么工具坏了要么 Agent 的思路错了要及时止损而不是无限重试。置信度收敛模型连续多次输出的计划基本一致且没有新的信息输入就可以判定局势已稳。Loop Engineering 这个热词最近被反复提及在我看来它核心就是在讲这些终止条件和状态维护的工程设计。Loop 不是一个编程概念它是一个可靠性工程概念。2.3 多窗口会话的循环调度与上下文污染很多 Agent 产品都支持“多窗口”功能——一个窗口跑一个独立任务互不干扰。热词里那个“loop 多窗口”指的就是这个场景。听起来很简单但一旦落地你会遇到一个特别隐蔽的问题上下文污染。我举个例子。我有一个会话 A 在要求 Agent 分析一份 Python 项目的代码结构另一个会话 B 在要求 Agent 写一份周报。两个会话如果共用一个全局的history列表或者共用一个 session 对象A 会话里执行的工具结果就会出现在 B 会话的上下文里然后 Agent B 写周报的时候可能突然开始分析代码因为它“看到”了不该看到的内容。有一次我排查一个诡异 bugAgent 在回复中引用了一份完全无关的日志文件内容查了很久才发现是内存里全局变量last_result被两个会话交替覆盖了。那个 bug 的修复方案很简单给每个 Loop 实例独立的状态容器确保每个窗口的state生命周期完全隔离。这个道理说起来简单但在实际代码里全局变量、静态变量、被复用的连接对象都是上下文污染的温床。你想排查这种问题最有效的手段就是给每一条 trace 打上 session_id然后检查日志里有没有跨会话的串用记录。还有一个序列化层面的坑要提醒大家上下文对象如果要持久化比如存 Redis 或数据库务必设计好序列化方案。之前热词里有个“self referencing loop detected for property mem_memberinfo”的问题就是典型的循环引用导致序列化失败——你的上下文中如果直接引用了父对象一存库就炸。3. Graph从单循环到编排图复杂任务的控制流工程3.1 为什么单循环不够用Loop 解决的是“一个 Agent 自主完成一个任务”的问题。但真实业务里哪有这么简单我接手过的一个需求用户上传一份合同系统需要先做 OCR 提取文本然后做合同风险点检查如果发现风险条款再调用法律知识库生成修改建议最后生成一份对比报告发送给用户邮箱。这个流程里有多个环节有前后依赖有分支条件如果强行用一个大 Loop 跑完你会面临两个问题第一单个 Loop 里的模型上下文会被整个流程塞满跑到后面模型已经忘掉了前面 OCR 的细节。第二风险检查和修改建议是两个不同领域的子任务让同一个 Agent 在两个领域之间来回切换效果远不如让两个专业 Agent 各干各的。Graph 层解决的就是这个问题。所谓 Graph其实就是把多个 Agent 节点、工具节点、条件节点组织成一张有向图每个节点执行一个明确的任务边定义了节点之间的流转关系。LangGraph 把这种模式发扬光大之后Graph 几乎成了 Agent 编排的事实标准。热词里的 Snap Graph Builder 也是一类图构建工具它提供可视化方式把流程结构搭出来再映射到执行引擎。3.2 节点与边的设计状态机思维是基础我设计 Graph 时一般把节点分成几类节点类型职责示例Agent 节点调用 LLM 做推理决策风险判断 Agent、报告生成 Agent工具节点执行确定性操作OCR 服务、数据库查询、邮件发送条件节点根据上游结果做分支路由检查结果是否包含高风险条款并行节点同时执行多个无依赖的子任务多语言翻译、多源数据核查人工审批节点暂停流程等待用户确认高危操作前的人工确认我把 Graph 设计的一条经验总结成一句话把决策交给 Agent把执行交给工具把流转交给图引擎。Agent 节点负责“想”工具节点负责“做”图引擎负责“流向”。这样即使某个 Agent 节点临时换成了 GPT-4o 或者 DeepSeek整个流程骨架都不用动。3.3 子图嵌套、局部重放和可回退设计图做得大了以后必然会复杂所以 Graph 一定要支持子图。我做过一个客服工单系统主图是“工单接入 → 意图识别 → 分类处理 → 结果反馈”而“分类处理”这个节点内部其实是一个完整子图里面包含“退款申请”“技术排查”“人工转接”三个分支每个分支又是一个小型 Agent 流程。子图的价值不仅是代码组织上的更重要的是它可以做局部重放。如果“技术排查”这个子图执行到一半因为外部服务超时挂了重放时只需要重放这个子图而不需要把前面“意图识别”的整个流程重新跑一遍。这个能力在长流程场景里极其重要因为每个 Agent 节点的调用都有 token 成本和时间成本。另外Graph 的边上加条件时一定要设计“默认分支”。我吃过一次亏图里只写了 A → B条件满足和 A → C条件不满足结果条件判断函数因为上游数据格式异常直接抛错整个流程卡死在那个节点。后来我在每个条件节点都增加了一个 default 兜底分支要么走重试要么走人工处理永远不让流程卡死。这个经验我认为所有做 Graph 编排的人都应该记下来。4. 生产环境三件大事并发、安全、可观测性4.1 AI Agent 怎么扛并发状态隔离与连接复用“AI Agent 怎么扛并发”这个热搜词背后其实是很多团队把 Agent 当成普通 Web 服务在部署然后发现各种诡异问题。核心原因在于普通 Web 接口是无状态的一个请求进来处理完就结束了而 Agent 是有状态的一个任务要跑很多步每一步都要保存中间状态。我在前面的文章里反复强调状态隔离这里再展开讲一下并发场景下的具体方案。第一层是会话状态隔离每个请求进来就创建一个独立的运行上下文包括 history、step 计数、变量存储这个上下文只能被当前请求读写。第二层是模型调用的连接复用Agent 每跑一步就要请求一次 LLM API如果你的并发量到了 50 个 Agent 同时跑模型端限流会让你直接崩掉。我的做法是给模型客户端配置连接池 指数退避重试同时在应用层控制同一时刻调用模型 API 的并发数上限。第三层是工具执行隔离Agent 调用的工具如果涉及文件系统、数据库或者外部命令必须限制在同一会话的沙箱目录或独立事务中否则多个 Agent 同时写同一个文件结果不可预期。我做过一个压测20 个并发会话同时让 Agent 写日志分析报告每个会话读取不同的日志目录。最初版本把所有 Agent 的工具执行都放到同一个临时目录跑完一看三分之一的结果文件被互相覆盖了。改成每个会话独立临时目录之后问题彻底消失。所以我的结论是Agent 的并发首先是状态的并发不是请求的并发。4.2 工具调用的安全边界白名单、权限分级和沙盒Agent 生产环境最大的安全风险不是模型本身而是它手里那堆工具。模型再聪明它也只是生成文本真正执行操作的是 Harness 层调用的工具。一个能执行 Shell 命令的 Agent如果不对工具做权限控制就等于给一个不设防的实习生发了服务器 root 权限。我在项目中的安全策略有三层。第一层是工具白名单Agent 只能调用 Harness 注册表里显式声明过的工具不在白名单里的调用一律拒绝——包括模型自己“发明”的工具名。不少模型会输出一个它幻想出来的工具名称比如send_email_to_customer即使你没定义过这个东西。白名单校验就是挡这一层的。第二层是权限分级每个工具声明自己的风险等级比如“读取文件”是低风险“执行 Shell 命令”“发送外网请求”是高危操作高危操作必须经过用户确认或额外审批。第三层是环境沙盒严重高危的工具放到容器里跑限制文件访问范围、网络访问范围、CPU/内存配额。这层我在内部系统里的实现是给每个会话分配一个独立的工作目录配合权限处理。这些安全策略看起来麻烦但一旦你的 Agent 要从“内部演示”走向“真实业务”它们就是生命线。我在社区里见过不止一个团队因为没做权限分级Agent 在生产环境误删了数据。这种事故一次就能让人长记性。4.3 追踪与调试trace 才是 Agent 的日志系统传统应用排查看日志就够了Agent 应用不行。因为 Agent 的运行是一连串的思维链决策每步的输入输出、每一步调了哪个工具、为什么走到这个分支缺了任何一环你都无从判断它是“想错了”还是“工具错了”。我给自己的项目搭了一套最小可用的追踪方案每条 Agent 运行生成一个 trace_idtrace 里记录从任务开始到结束的每一个事件包括模型输入输出、工具的入参与返回值、每一步的耗时、终止原因。任何一步出现问题都靠 trace 回放来定位。这套追踪还有一个额外的好处它把 Agent 的决策过程变成了可审查的记录。合规审计、故障责任认定、prompt 效果对比全都基于 trace。我甚至建议把 trace 的采样率设为 100%不要只记错误。因为 Agent 的 bug 经常是“这次没报错但结果不对”你只有对比成功和失败两条 trace 才能发现问题出在哪一轮推理。5. 踩坑复盘插件加载失败、上下文污染和 RPA 对接5.1 “harness failed to load plugins”的完整排查链路这个报错我应该不是第一次遇到但每次遇到都有人来问。这里把排查链路完整梳理一遍下次你遇到可以直接照着走。第一步看日志确认报错发生在加载阶段还是执行阶段。如果发生在加载阶段最常见的原因是插件路径配置错误。我自己踩过的一个坑在 Linux 上部署时插件目录用的是 Windows 风格的相对路径plugins\nlp_tool加载器根本找不到目录报错信息就是 failed to load plugins。改成plugins/nlp_tool之后立刻就好了。第二步检查依赖缺失。Harness 插件本质上是一段动态加载的代码它依赖的第三方库如果没装好加载时就会报错。这里一定要用插件的独立依赖声明不要指望主程序的环境里刚好有它需要的版本。我遇到过插件 A 依赖pandas 1.x主程序环境里装了pandas 2.x接口不兼容加载失败。排查方式是逐个 import 插件的依赖模块看哪个抛异常。第三步检查版本冲突和动态加载安全策略。有些 Harness 框架默认禁止加载任意路径下的插件只允许加载特定目录下的已签名插件。如果插件带签名校验但证书过期也会报 failed to load plugins。这种情况的定位方法是把日志级别调到 DEBUG看加载器是在哪一步拒绝的。第四步别忽略权限问题。插件目录如果部署在 NFS 或者容器挂载卷上读取权限不对同样加载失败。我在内网环境部署的时候就碰到过插件文件在宿主机上权限是 755但容器内用户是 nobody读不了文件报错提示却是 generic load failure根本看不出是权限问题。这种问题只能靠ls -l和sudo -u nobody cat去验证。5.2 多窗口上下文污染的根因与修复前面提过 context 污染的典型场景这里再给一个具体的排查案例大家遇到类似问题可以照着排查。现象三个会话窗口分别处理三个不同的任务有一段时间所有窗口的回答都开始“串台”A 任务窗口里出现了 B 任务的背景信息。我当时的排查链路是这样第一查全局变量和类变量的定义确认没有在模块级别共享history、messages、context之类的容器。第二查 session 存储中间件看是否把不同会话的 ID 错配了。第三查序列化缓存确认 Redis 里每个 key 存的是对应会话的数据。最后定位到的原因让我很意外是一个工具函数内部用了functools.lru_cache装饰器缓存了上一次调用的结果而工具函数的入参里没有 session_id所以两个会话调用同一个工具时第二个会话拿到了第一个会话的缓存结果。修复方式很直接给工具函数的缓存 key 增加 session_id 维度或者干脆在工具调用层面禁止跨会话共享任何缓存。这个案例给我最大的教训是上下文污染不一定是你主动设计了共享状态更可能是某个隐性的缓存机制——装饰器、连接池、单例对象——在你看不到的地方偷偷串了数据。排查时一定要把“缓存”和“连接复用”这两个词贴在显示器上。5.3 与 RPA 对接的落地体验最后说说 Harness RPA 的落地实现这也是热搜词里我很感兴趣的一个方向。很多人问Agent 和 RPA 都有自动化能力两者什么关系我的理解是Agent 负责“决定做什么”RPA 负责“稳定地执行网页和桌面操作”。RPA 擅长的是流程固定、重复性高、对操作精度有要求的场景Agent 擅长的是面对不确定情况做推理决策。我在一个合同录入项目里把两者结合过Agent 读取 PDF 合同提取关键字段然后把字段填入一个老旧的关系型业务系统。老系统没有 API只有网页界面Agent 自己点网页容易点错且无法稳定处理弹窗于是我们让 Agent 生成结构化的操作指令RPA 负责执行网页点击和表单填写。落地时遇到的最大坑是超时和元素定位。RPA 脚本如果等待某个元素超过 30 秒就会抛异常而网页加载慢是常态。我们最终的方案是Agent 生成指令时附带一个“预期等待时间”参数RPA 根据这个参数动态调整超时同时 RPA 每执行完一个步骤回传页面截图给 AgentAgent 根据截图判断是否继续。这套“Agent 决策 RPA 执行 截图反馈”的闭环比任何一端单独干效果都稳。还有一点大家要注意RPA 本身也是工具必须注册进 Harness 的工具表里并且定义好入参和出参的 schema。我开始时直接把 RPA 封装成一个“万能执行器”Agent 想怎么调就怎么调结果模型经常生成根本无法执行的自由文本指令RPA 端解析直接懵。后来我们把工具入参改成严格的结构化格式反而一次成功率大大提升——因为模型在明确的 schema 约束下输出规范度的表现好得多。关于三层架构我最后再补充一个个人观点不要把 Harness、Loop、Graph 当成三个必须同时上的“豪华套餐”。如果你只有一个简单的内部工具型 AgentHarness Loop 两层就够Graph 反而会增加复杂度如果你的系统要处理多步骤、多分支、多人协作的流程那 Graph 的编排价值立刻就能体现出来。架构永远是跟着问题走的不是跟着热点走的。
返回列表