搞 Agent 开发的人,最近应该没少被这几个词刷屏:Harness、Loop、Graph。不管你是用 LangChain、LlamaIndex,还是自己在裸写 LLM 调用,只要做到生产级,迟早会撞上这三层东西。说实话,市面上讲 Agent 的文章很多,但大多停留在“什么是 Agent”“怎么调一次 API”,真正把 Harness、Loop、Graph 这三层架构拆开讲清楚、讲透生产落地的内容很少。这篇我就从工程角度,把这三层到底是什么、各自解决什么问题、合在一起怎么跑,以及我在实际项目里踩过的坑,一次性说清楚。
如果你正准备从 demo 级 Agent 往生产级 Agent 演进,或者已经在做多智能体系统、复杂任务编排,这篇文章应该能帮你省掉不少试错成本。我会尽量说人话,把每个概念揉碎了讲,也会给到可以直接抄的配置思路和排查方法。
1. 为什么 Agent 工程需要三层架构
1.1 单体脚本式 Agent 的致命短板
先聊聊大部分人刚开始写 Agent 时的状态。最初我写 Agent 就是一个脚本:用户提问,拼接 prompt,调一次大模型,解析结果,返回。跑通几个 demo 之后觉得很爽,感觉已经掌握 Agent 了。但一旦把任务复杂度提上来,比如让 Agent 自己决定要不要查数据库、要不要调外部 API、要不要分步思考,问题马上就来了。
第一是失控。模型答到一半开始胡说,或者在一个错误的工具调用上反复重试,作为开发者你根本没法干预,只能眼睁睁看着 token 烧完。第二是复用性差。换一个场景,脚本就得重写,工具调用的逻辑和模型交互的逻辑全耦合在一起,改一处崩一片。第三是复杂任务根本处理不了。真正的生产任务往往不是“一步问答”,而是“多步行动”,需要条件判断、循环、分支、并行,这些单靠“调一次模型”完全做不到。
这就像你雇了一个很聪明但完全没有项目管理经验的实习生,你给他一个终极目标,他可能会在某个细节上钻牛角尖出不来,也可能南辕北辙跑偏,你还不知道怎么拦他。单体脚本式 Agent 就是这个状态,聪明但不可控。
1.2 三层架构的分工逻辑
Harness、Loop、Graph 这三层,本质上是在拆解一个核心问题:怎么让一个不可预测的模型,在一个可控的框架里,完成一个确定的任务。
Harness 管的是“环境”。Agent 能看什么、能摸到什么、能调用什么工具、能访问什么数据、权限边界在哪。它是 Agent 的工作台和安全围栏,决定了 Agent 的能力边界。
Loop 管的是“节奏”。Agent 怎么循环思考、怎么决定下一步动作、什么时候该停下来。它是 Agent 的内部控制节律,决定了 Agent 的行为模式。
Graph 管的是“路径”。多个任务节点怎么编排、条件分支怎么走、哪些步骤可以并行、哪些必须串行。它是 Agent 的流程轨道,决定了 Agent 执行复杂任务时的整体路径。
一句话总结:Harness 管资源,Loop 管思考,Graph 管流程。三者合在一起,才构成一个真正能在生产环境里跑起来的 Agent 系统。下面每一层我详细展开说。
2. Harness 层:Agent 的骨架与运行环境
2.1 Harness 到底在管什么
Harness 这个词,原意是“马具”,引申出来就是“驾驭、约束”。在 Agent 工程里,Harness 指的就是包裹在模型外面那层工程装置,它负责把模型和一个可操作的世界连接起来,同时严格限制模型的操作边界。
我最早理解 Harness,是把它类比成给模型配的操作系统。模型本身只是一个大脑,它没有手、没有眼睛、没有记忆。Harness 给它装了手(工具调用接口)、装了眼睛(上下文检索)、装了记事本(记忆系统)、装了保险锁(权限控制)。没有 Harness,模型再聪明也只能聊聊天;有了 Harness,模型才算真正“长在”一个可以干活的环境里。
具体来说,Harness 层至少包含以下几块核心能力:
- 工具注册与调用框架:Agent 能调哪些工具、每个工具的入参 schema 是什么、调用结果怎么返回给模型
- 上下文管理:哪些信息应该注入 prompt、哪些信息要检索后再注入、上下文窗口满了怎么处理
- 权限与安全边界:哪些操作允许、哪些操作必须人工审批、高危动作怎么拦截
- 记忆系统:短期记忆(当前任务上下文)和长期记忆(跨会话的知识)怎么存取
- 沙箱与隔离:Agent 执行的代码、访问的网络、操作的文件系统,是否在隔离环境中
2.2 Harness 工程的关键设计原则
做 Harness 层时,最容易犯的错误是“塞太多”。我见过不少团队,Harness 里又是向量库、又是多轮记忆、又是各种插件,结果 Agent 的 prompt 被撑到好几万 token,模型根本分不清哪些信息对当前任务有用,效果反而很差。
Harness 工程要守几条原则:
最小权限原则。给模型配工具时,只给当前任务真正需要的,不要一股脑全塞。比如一个只做信息查询的 Agent,完全不需要给它配“写数据库”的工具。工具越多,模型选错工具的概率越高,被攻击的面也越大。我做过一个项目,财务 Agent 一开始连“删除账单”的工具都配上了,测试时模型真的在某次误判中把一条正常账单删了,幸好数据有备份。之后我定了一条铁律:凡是不在当前流程内的写操作工具,一律不注册。
工具描述要写清楚。这事很多人忽视。模型是靠工具描述来决定什么时候调用哪个工具的,工具描述含糊,模型就会瞎猜。我总结了一个公式:工具描述 = 这个工具能干什么 + 什么时候该用 + 什么时候不该用 + 一个典型示例。举个例子,“get_user_order” 这个工具,别只写“获取用户订单”,要写“获取指定用户在指定时间范围内的订单列表,适用于用户查询购买记录;如果是查询退款记录请用 refund_query;输入 userId 必填,startDate 和 endDate 可选”。
上下文注入要有优先级。Harness 往 prompt 里塞信息时,不能想到什么塞什么。我的做法是分层注入:系统指令(固定)在最前,工具定义其次,然后动态检索到的任务相关上下文,最后是当前轮次的对话历史。动态注入的内容要设一个 token 预算,超出就截断或摘要。不然随着对话变长,模型注意力会被历史垃圾信息稀释,越来越“健忘”。
2.3 Harness 与 Agent 框架的关系
很多人问“Harness 和 Agent 到底啥区别”。我的理解是:Agent 是一个逻辑概念,代表一个具备自主行动能力的智能体;Harness 是这个智能体的具体工程实现框架。同一个 Agent 需求,可以用 LangGraph 写,可以用 CrewAI 写,也可以完全自己撸一套,这些都是 Harness 的不同实现方式。
框架帮你省了造轮子的时间,但框架不等于全部。框架只提供通用骨架,真正让你 Agent 好用、可控、安全的部分,恰恰是你往骨架里填的那些业务细节:工具 schema 设计、权限策略、上下文检索策略、失败重试策略。这些才是 Harness 工程的核心工作量所在。
我可以给一个比较务实的方法论:如果项目复杂度不高,直接用现成框架的 Harness 能力即可(LangGraph 的 prebuilt、OpenAI 的 function calling 封装都够用);如果项目复杂度高,比如涉及多部门数据隔离、复杂审批流、高频工具调用,那必须在框架之上自己再包一层 Harness 适配层,否则后期改一处动全身,维护成本会高到你怀疑人生。
3. Loop 层:Agent 的控制中枢
3.1 从“一步响应”到“循环思考”
这是 Agent 和我前面说的“单体脚本”最大的分水岭。单体脚本是一次模型调用出结果,而 Loop 让模型可以多轮“思考-行动-观察-再思考”,直到任务完成为止。业界管这个叫Agent Loop,也有人叫Reasoning-Action Loop或者ReAct Loop。
为什么需要这个循环?因为复杂任务几乎不可能一步到位。比如你要 Agent 帮你调研某行业最近一个月的动态,它得先搜索、读几篇文章、提取重点,发现信息不够,再换关键词搜第二轮,再总结。这个“搜完发现不够再搜”的过程,就是 Loop 在起作用。
一个标准的 Agent Loop 四阶段是这样的:
- 感知与解读:模型接收当前状态(用户需求 + 上下文 + 上一轮工具调用结果),理解当前处境
- 决策与规划:模型决定下一步是“调用某个工具”“直接回答”还是“结束任务”
- 行动与调用:执行工具调用(或直接生成回复)
- 观察与反思:工具返回结果被注入上下文,模型评估结果是否满足任务目标,判断要继续还是结束
- 不满足则回到第 1 步,满足则输出最终结果
这看起来不复杂,但真正的难点在于:怎么让这个循环稳定、不跑偏、不失控、成本可控。
3.2 Loop 工程的核心参数与实践经验
做 Loop 工程时,有几个核心参数你必须认真设计,我一个个说:
最大迭代次数(max_iterations)。这是最基础的止损阀。没有它,模型可能在一个死循环里反复调用同一个工具出不来。我踩过很惨的一次:Agent 查一个不存在的数据,模型反复调用查询工具,每次查不到就重试,几十次调用之后才发现不对劲。所以从第一天起就设 max_iterations=8~10,超过就强制终止并让 Agent 输出目前已知信息+承认失败原因。这个数字根据任务复杂度调整,简单问答类 5 够,复杂调研类 15~20。
停止条件设计。循环不能只靠“模型自己觉得该停了”来判断,必须有多重停止条件:迭代上限、时间上限、token 消耗上限、关键节点完成。我常做的组合是:迭代次数上限 + 总耗时上限(比如 5 分钟)+ 工具调用失败连续 N 次强制退出。
失败重试策略。工具调用失败处理是 Loop 里最容易被低估的环节。失败的策略不是“直接重试”,而是“把错误信息喂给模型,让它判断如何修正”。有些失败是临时性的(网络超时),重试没问题;有些失败是永久性的(参数传错、权限不够),重试一万次也没用,必须让模型基于错误信息调整调用参数或换个工具。我在工程上的做法是:先重试一次,如果还失败,就把错误信息原样注入,让模型自己决定是修正参数再试、换替代工具,还是放弃该步骤继续执行。
上下文压缩与摘要。Loop 跑多轮之后,历史记录越来越长,token 成本直线上升,而且模型注意力会被稀释。解决方案是回合摘要:每 N 轮把之前的对话记录压缩成摘要,只保留关键信息。这就像人做会议记录,不能每秒钟都记下来,只记结论和行动项就好。我常用的策略是每 5 轮做一次摘要,或者在上下文接近窗口的 70% 时触发自动摘要。
3.3 Loop 里面的“反思”机制
现在很流行的反思机制(Reflection),本质上是在 Loop 里插入一个特殊节点:让模型站在“批评者”的角度,审视自己刚才的输出,找出漏洞和可改进的地方,再基于反思结果生成更完善的回答。生产级 Agent 里,反思节点一般放在两个位置:复杂任务最终输出前,或者工具调用连续失败后。
反思有个副作用就是费 token。一次反思等于额外一次甚至两次模型调用。所以我的建议是:轻量任务别上反思,复杂任务在关键节点上反思一次就够了,不要把反思做成必经之路。我做过对比实验,简单问答任务加了反思之后,回答质量提升不到 5%,但延迟翻倍、成本翻三倍,非常不划算。复杂任务(比如生成分析报告、写长文档),反思带来的质量提升比较明显,值得上。
3.4 Model Context Protocol 在 Loop 里的价值
这里额外提一下,Harness 层的信息接入方式,业界逐渐在向 Model Context Protocol(MCP)这类标准化协议靠拢。MCP 解决的是什么问题?它用一套统一协议把工具、数据源、上下文从具体的 Agent 实现里解耦出来。有了 MCP,你在 Loop 里“观察”的步骤,就变成了标准化的“资源读取”操作,可以在不同 Agent 实现之间无缝复用。这不是必须品,但模块多了之后,MCP 的标准化优势会给你省很多事。
4. Graph 层:从单循环到多节点工作流编排
4.1 为什么单层 Loop 不够
单个 Agent Loop 能解决“一件事反复做”的问题,但生产环境里的大部分任务,根本不是一个“简单的线性循环”能跑完的。真实任务通常长这样:
- 可能是“先做 A,如果 A 成功就做 B,否则做 C”
- 可能是“同时查三个数据源,全部到齐后汇总分析”
- 可能是“普通操作自动执行,高危操作需要人工审批后再继续”
- 可能是“前台 Agent 接到任务后,分发给多个专家 Agent 协作完成,最后统一汇总”
这些都需要——Graph。
Graph 就是把 Agent 任务的执行路径显式建模为一张有向图。图里的节点是“执行单元”,可能是 LLM 调用、工具调用、子 Agent、人工审批;图里的边是“流转条件”,决定了下一步往哪走。Graph 本质上是把你的任务流程从“藏在代码 if-else 里”升级成“显式可见、可配置、可监控的一张图”。
4.2 Graph 和 Loop 怎么配合
很多人搞不清 Graph 和 Loop 的关系,觉得是两套并列的东西。其实它们是嵌套关系:Graph 的每个节点内部,可以再跑一个 Loop。比如你画了一个三节点的 Graph:第一步搜集数据,第二步分析数据,第三步生成报告。其中“搜集数据”这个节点内部,跑的就是一个 Agent Loop,循环搜索-提取-再搜索;而“分析数据”节点内部,又是另一个 Loop。Graph 管流程骨架,Loop 管每个节点内部的智能行为。
以 LangGraph 为例,它的 StateGraph 就非常典型。StateGraph 维护一个全局状态对象,每个节点执行时可以读写这个状态,节点之间通过条件边决定流转方向。你可以给状态加“迭代次数”字段,实现节点内部的循环控制,也可以定义“是否需要人工确认”字段,走到关键节点时停下来等人工审批。这种模式代码写起来非常直观,业务路径一目了然,排障时也能直接定位到具体节点。
4.3 Graph 工程落地的关键设计
我做了几个 Graph 工程后,总结了一套比较稳妥的设计套路:
节点粒度要适中。Graph 的节点切得太细,整张图几十个节点,配置和维护成本非常高;切得太粗,一个节点里塞了太多逻辑,分支条件根本写不清楚。我习惯的粒度是一个节点对应一个“不可再拆解的业务动作”,比如“查订单”“算价格”“发通知”。一个节点内部可以调多个工具,但对外只暴露一个业务目标。
条件边是 Graph 的灵魂。Graph 相比普通流程引擎最大的优势是条件边可以由模型动态决定。比如分析任务中,模型先看数据完整度,决定接下来是“继续补数据”还是“开始写结论”。这个决策由 LLM 做,而不是写死代码。我的做法是给这类条件边单独配一个小模型调用,专门输出结构化的决策结果(比如 JSON 格式的下一步分支选择),再把结果传给路由逻辑。这样决策清晰、可审计、好排障。
人工介入节点必须显式建模。生产环境里,有些操作是绝对不能全自动的。比如“发送对外邮件”“执行退款”“删除数据”。这些操作在 Graph 里要单独建模成一个人工审批节点,Agent 走到这里就停下来,等审批通过后继续,拒绝则走另一条分支。这个设计一开始就要想好,不然后期加审批节点就要改动整张图结构。
可观测性要内建。Graph 跑起来之后,每个节点的进入时间、退出时间、状态、输入输出摘要、token 消耗,全部要记录。这一步不是为了好看,是为了生产排障。我有一个线上事故,Graph 在第三个节点处疑似死循环,就是因为没记录节点退出时间,排查了很久都没定位到问题。修复方法很简单——给每个节点加一个 start_time / end_time 字段和超时检查器,并在所有流转路径上打结构化日志。从此再没被这种问题坑过。
4.4 并行与容错:Graph 在生产环境的进阶玩法
Graph 相比线性 Loop 的另一个大优势,是天然支持并行。比如在“多源信息汇总”类任务里,查 A 数据库和查 B 数据库没有先后依赖,就可以建模成两个并行节点。并行度设置挺有讲究:并发太高,下游模型一次要处理的内容太多,容易漏信息;并发太低,又浪费响应时间。我的经验是控制在 3~5 个并行分支比较合适,每个分支返回的结果先做结构化整理,再统一合并交给下游分析节点。
容错方面,Graph 工程最常用的手段是替代节点。就是每个关键节点配一个降级方案:主节点是“调用付费商业 API”,降级节点是“调用开源本地模型”;主节点是“查询线上数据库”,降级节点是“查询离线缓存副本”。条件边检测到主节点失败或超时,就自动走到降级节点。这个思路用在 Graph 层特别顺手,因为降级路径只用改边。用熟了之后你再看单体 Agent,会明显感觉它无计可施——因为单体 Agent 根本没有显式的流控能力让它在运行时换路径。
5. 生产环境落地:那些坑和实战思路
5.1 三层架构常见问题速查
我把实际项目里高频遇到的三层架构问题整理成了一张表,方便大家对照排查。
| 问题现象 | 可能原因 | 排查方式 | 解决方案 |
|---|---|---|---|
| Agent 反复调用同一工具停不下来 | 未设置迭代次数上限 | 检查 Loop 的终止条件配置 | 设置 max_iterations,连续失败 N 次强制退出 |
| 模型总是选错工具 | 工具描述模糊、工具过多 | 查看调用日志中模型对工具的选择记录 | 精简工具列表、用“使用场景+示例”重写工具描述 |
| 对话超过三轮后效果明显变差 | 上下文过长、无摘要机制 | 查看每轮调用发送的 token 数 | 增加上下文摘要/压缩机制,超过窗口 70% 触发压缩 |
| Graph 在某个节点卡住 | 节点超时机制缺失 | 检查该节点 start_time 距当前时间 | 给所有节点加超时检查和降级路径 |
| 高成本警告 | 反思/循环太频繁 | 查看单任务模型的调用次数 | 控制反思次数、压缩循环、缩短上下文 |
| Agent 访问了不该访问的数据 | Harness 权限配置过宽 | 审查工具权限和数据集访问名单 | 按最小权限原则重新规划,写操作工具单独审批 |
| 多分支汇总时部分分支结果丢失 | 并行分支的结果未写回公共状态 | 检查每个分支在结束时是否更新状态对象 | 在每个并行节点的出口统一做状态写入校验 |
5.2 三层项目从 0 到 1 的一个落地路径建议
先说一个适用于大多数项目的基础架构模板。我平时的做法是:Graph 层用状态图承载核心业务路径,Loop 层在每个节点内部复用同一套 Agent 循环引擎,Harness 层则会单独拆一个 service 管理工具注册、权限校验和上下文装配,所有模型交互都穿过这一层。这样三层各司其职:Graph 管路径、Loop 管思考、Harness 管环境。
然后是几个实操上的建议:
- 先在低复杂度任务上验证 Loop,不要一上来就上全套 Graph。先把“一个 Agent 循环搞定一个中等任务”跑稳,再扩展成多节点编排。
- Harness 权限第一版宁可多做限制,然后逐步放开。一上来就给 Agent 全部工具权限,后期收紧的成本很高,因为模型已经习惯了能随便调用。
- Graph 的每一条边、每一个节点都打日志,这个写在最前面是因为线上排障时它是唯一救命的东西。日志要记录:节点名、进入时间、退出时间、模型调用次、token 数、工具调用情况、状态快照。
- 先定义评估集,再调参。Agent 工程调参非常容易陷入“改一下试一次”的循环,没有评估集你甚至不知道改动到底让效果变好了还是变差了。我会给每个 Agent 准备 20~50 条代表性测试任务,每次改动后全部跑一遍,对比通过率和耗时,用数据说话。
- 预留人在回路。全自动化是理想态,但生产环境一定要在关键路径上留人工审批和人工接管的口子。“让它干活,但干了什么我随时看得见;不该干的,干之前它会先来问我”——这是生产级 Agent 的安全底线。
5.3 实测体会:三层架构给我的最大好处
最后说点我个人的真实感受。我经历过“纯脚本 Agent”和“三层架构 Agent”两个阶段,最大的心理变化是:从“不知道它下一步要干嘛”变成“它每一步我基本都能预期”。
纯脚本阶段,每次跑一个新需求,我都像拆盲盒,完全不知道模型会走哪条路,出问题也没法定位。换成三层架构后,每一个动作都有明确归属:效果不达预期,大概率是 Loop 的参数没调好;路径跑偏,大概率是 Graph 的条件边设计不合理;工具乱调,大概率是 Harness 的权限和描述有问题。这种“错误有归因”的感觉,对工程化推进太重要了。
这也是为什么我对 Agent 工程的前景比较乐观——现在已经有越来越多标准化的工程实践在沉淀,而不是像早期那样全靠玄学和运气。把你的 Agent 拆成骨架(Harness)、节律(Loop)、流程(Graph)三层,一层一层搭稳,它就能从玩具走向生产线。希望这篇文章能帮你绕开一些我踩过的坑,少烧一些冤枉的 token。