1. Agent 工程的三层架构总览
1.1 为什么需要分层:从"能跑"到"能上生产"的鸿沟
很多人第一次接触 Agent 开发,都是从一段几十行的脚本开始的:调一个模型接口,塞几个工具函数,写个 while 循环判断有没有工具调用,有就执行、没有就返回。这段代码在 Demo 阶段跑得挺好,但只要你想把它放到真实业务里,问题就会像潮水一样涌上来——工具调用失败了怎么办?模型陷入死循环反复调同一个工具怎么破?多轮对话的上下文越来越长、Token 成本失控怎么办?多个 Agent 之间要协作,谁来编排、谁来兜底?
这些问题的本质,是"能跑"和"能上生产"之间隔着一整套工程体系。我把这套体系拆成三层来看:Harness(执行外壳)、Loop(循环控制)、Graph(编排拓扑)。这三层不是某个框架的官方术语,而是我在多个 Agent 项目落地过程中总结出来的一套心智模型,它能帮你快速定位一个问题到底该在哪一层解决。
打个比方,如果把 Agent 比作一辆车:Graph 是导航地图,决定从起点到终点走哪条路、经过哪些路口;Loop 是发动机和变速箱,负责把每一次"感知—决策—行动"的动力循环起来;Harness 则是底盘、安全带和气囊,保证这辆车在颠簸路面上不散架、出了事故能兜住。三者缺一不可,但职责边界必须清晰,否则代码会迅速腐化成"一锅粥"。
1.2 三层各自的职责边界与协作关系
先把三层的定义说清楚,后面所有内容都围绕这个展开。
Harness 层解决的是"单次执行如何稳定可靠"的问题。它管的是:一次模型调用怎么发、工具怎么注册和调用、超时和重试怎么配、异常怎么捕获、日志和可观测性怎么埋、Token 和成本怎么统计。你可以把它理解成 Agent 的"运行时容器",它不关心业务逻辑,只关心执行过程本身是否健壮。
Loop 层解决的是"一次任务如何推进到完成"的问题。它管的是:什么时候该继续循环、什么时候该终止、循环的最大轮次是多少、上下文如何裁剪和压缩、中间状态如何保存、人在回路(Human-in-the-loop)的介入点在哪里。Loop 是 Agent 的"心跳",它决定了 Agent 是走一步看一步,还是能持续推进直到目标达成。
Graph 层解决的是"复杂任务如何拆解和编排"的问题。它管的是:任务被拆成哪些节点、节点之间的依赖和流转关系是什么、条件分支怎么走、并行分支怎么合并、子图如何嵌套、失败节点如何回退。Graph 是 Agent 的"骨架",它把一个大目标拆成一张有向图,让每个节点只专注做一件小事。
三层的协作关系是自上而下的:Graph 定义拓扑,Loop 驱动每个节点内部的循环,Harness 保障每一次原子执行。反过来,Harness 的异常会上抛给 Loop 决定是否重试,Loop 的终止信号会上抛给 Graph 决定下一步走哪个节点。理解这个双向的数据流,是设计好 Agent 系统的前提。
1.3 一个典型 Agent 请求在三层中的流转
我用一个具体场景把三层串起来:用户说"帮我查一下北京明天的天气,如果下雨就提醒我带伞,顺便看看有没有合适的室内活动推荐"。
在Graph 层,这个任务会被拆成几个节点:解析意图→查询天气→条件判断(是否下雨)→ 分支 A:生成带伞提醒/ 分支 B:跳过提醒→搜索室内活动→汇总输出。注意这里有个条件分支,这就是 Graph 存在的意义——它让"如果下雨"这种逻辑变成显式的边,而不是藏在某个 if-else 里。
在Loop 层,每个节点内部可能都需要多轮循环。比如搜索室内活动这个节点,Agent 可能先搜一次,发现结果不理想,调整关键词再搜,最多循环 3 次。Loop 负责控制这个"搜索—评估—再搜索"的过程,并在达到最大轮次或结果满意时终止。
在Harness 层,每一次搜索工具的调用、每一次模型生成,都要经过 Harness。它负责给搜索工具设 5 秒超时、失败重试 2 次、把请求和响应都记进日志、统计这次调用消耗了多少 Token。
这样拆下来,你会发现每一层的问题都能被精确定位:如果搜索总是超时,那是 Harness 的超时配置问题;如果 Agent 反复搜同一个词停不下来,那是 Loop 的终止条件没设计好;如果任务整体跑偏了,那是 Graph 的节点拆分或流转逻辑有问题。这就是分层最大的价值——让问题有处可查,让修改有处可下手。
2. Harness 层:把单次执行做到稳如老狗
2.1 Harness 到底管什么:一份职责清单
很多人会把 Harness 和 Agent 框架混为一谈,其实 Harness 的边界很清晰。我列一份职责清单,你可以对照自己的代码看看哪些该收进 Harness:
- 模型调用封装:统一不同模型供应商的接口差异,处理流式和非流式、处理多模态输入、处理函数调用(Function Calling)的格式转换。
- 工具注册与调度:工具的声明、参数校验、调用、结果序列化。工具是 Agent 的手脚,Harness 要保证手脚听使唤。
- 超时与重试:每个模型调用、每个工具调用都要有独立的超时和重试策略,不能一刀切。
- 异常捕获与降级:网络抖动、限流、模型返回格式错误,这些都要在 Harness 层兜住,转成 Loop 能理解的错误信号。
- 可观测性:结构化日志、调用链路追踪、Token 统计、成本核算。没有这些,线上出问题你只能靠猜。
- 安全与权限:工具调用的权限校验、敏感信息的脱敏、输入输出的内容过滤。
注意:Harness 层不要掺入任何业务逻辑。一旦你在 Harness 里写了"如果用户问天气就调天气工具"这种判断,说明你把 Graph 的活干到 Harness 里了,后面必然混乱。
2.2 工具注册与调用的工程细节
工具是 Agent 能力的来源,但工具注册这件事,坑比想象中多。我踩过的几个典型坑:
第一个坑是参数校验缺失。模型生成的工具调用参数经常是"看起来对但实际不能用"的。比如它给一个日期参数传了"明天",而你的工具期望的是2024-01-15这种格式。如果你不在 Harness 层做参数校验和规范化,错误就会一路传到工具内部,报一个莫名其妙的错。我的做法是在工具注册时声明参数的 JSON Schema,Harness 在调用前先校验,校验失败就把错误信息回传给模型让它重新生成。
第二个坑是工具描述写得太随意。工具的描述(description)是模型决定要不要调用它的唯一依据。我见过有人写"查询数据",模型根本不知道查什么数据、什么时候该用。好的工具描述应该包含:这个工具做什么、什么场景下用、参数的含义和格式、返回什么。这本质上是在给模型写"使用说明书"。
第三个坑是工具返回结果过大。有些工具一返回就是几千字的文本,直接塞进上下文,Token 瞬间爆炸。Harness 层应该对工具返回结果做截断或摘要,比如超过 2000 字符就截断并提示"结果过长已截断"。
下面是一个工具注册的简化示例,用 Python 伪代码展示结构:
class ToolRegistry: def __init__(self): self.tools = {} def register(self, name, description, param_schema, handler, timeout=10, max_retries=2): self.tools[name] = { "name": name, "description": description, "param_schema": param_schema, "handler": handler, "timeout": timeout, "max_retries": max_retries, } def invoke(self, name, params): tool = self.tools.get(name) if not tool: return {"error": f"工具 {name} 不存在"} # 参数校验 if not validate_schema(params, tool["param_schema"]): return {"error": "参数格式不正确,请检查后重试"} # 带超时和重试的调用 for attempt in range(tool["max_retries"] + 1): try: result = call_with_timeout(tool["handler"], params, tool["timeout"]) return truncate_result(result, max_len=2000) except TimeoutError: if attempt == tool["max_retries"]: return {"error": "工具调用超时"} except Exception as e: if attempt == tool["max_retries"]: return {"error": f"工具调用失败: {str(e)}"}这段代码看着简单,但每一行都是踩坑换来的。参数校验、超时、重试、结果截断,缺一个都会在生产环境给你颜色看。
2.3 超时、重试与降级策略的参数选择
超时和重试的参数不能拍脑袋定,要有依据。我的经验值是这样的:
| 调用类型 | 超时时间 | 重试次数 | 重试间隔 | 说明 |
|---|---|---|---|---|
| 模型生成(非流式) | 60s | 2 | 指数退避 1s/2s | 模型生成慢,超时给足 |
| 模型生成(流式) | 首包 10s,整体 120s | 1 | 2s | 流式首包超时说明网络或服务有问题 |
| 外部 API 工具 | 10s | 2 | 固定 1s | 大部分 API 10s 足够 |
| 数据库查询 | 5s | 1 | 500ms | 数据库慢查询要单独优化 |
| 文件读写 | 3s | 0 | - | 本地操作失败重试意义不大 |
重试策略上,我强烈建议用指数退避而不是固定间隔。原因是:如果服务端是因为过载而失败,你固定间隔重试只会加剧过载。指数退避给服务端喘息时间,成功率更高。但要注意加一个最大间隔上限,比如不超过 10 秒,否则用户等太久。
降级策略要分场景。模型调用失败,可以降级到备用模型;工具调用失败,可以返回"暂时无法获取该信息"让 Agent 继续走其他分支;如果关键路径上的调用全挂了,那就得触发 Loop 层的终止,给用户一个明确的失败提示,而不是让 Agent 卡在那里空转。
实操心得:重试一定要区分"可重试错误"和"不可重试错误"。网络超时、限流(429)可以重试;参数错误(400)、权限错误(403)重试多少次都没用,直接失败。我见过有人无脑重试,结果一个参数错误重试了 5 次,白白浪费了 5 倍的时间和成本。
2.4 可观测性:没有日志的 Agent 就是黑盒
Agent 的可观测性比传统服务更重要,因为它的行为是不确定的。同样一个输入,模型可能这次调 A 工具,下次调 B 工具。没有详细的日志,你根本不知道线上发生了什么。
我在 Harness 层埋的日志包括:每次模型调用的完整请求和响应(脱敏后)、每次工具调用的参数和结果、每次调用的耗时和 Token 消耗、每次异常的堆栈和上下文。这些日志用结构化的 JSON 格式输出,方便后续检索和分析。
除了日志,还有两个东西值得投入:调用链路追踪和成本看板。链路追踪能让你看到一个请求经过了哪些节点、每个节点花了多久,快速定位瓶颈。成本看板能让你知道每个用户、每个功能花了多少钱,这在商业化场景里是刚需。
我见过一个团队,Agent 上线后成本失控,一查发现是某个工具返回结果特别大,导致上下文暴涨,每次调用 Token 是正常情况的 10 倍。如果他们有成本看板,这个问题第一天就能发现,而不是等到月底账单出来才傻眼。
3. Loop 层:让 Agent 持续推进而不失控
3.1 循环的终止条件:Agent 工程最容易翻车的地方
Loop 层最核心的问题就一个:什么时候停。这个问题看似简单,实则是最容易翻车的地方。我总结了几种常见的终止条件,实际项目中往往是组合使用:
- 目标达成:模型明确表示任务完成,不再调用工具,直接返回最终答案。这是最理想的终止。
- 最大轮次:设置一个硬上限,比如最多循环 10 轮。这是兜底,防止无限循环。
- 无进展检测:连续 N 轮没有产生新的有效信息(比如反复调同一个工具、返回结果高度相似),强制终止。
- 超时终止:整个任务设置一个总超时,比如 5 分钟,到点就停。
- 人工中断:在关键节点设置人工确认,人可以选择继续或终止。
这几种条件要组合使用,缺一不可。只靠"目标达成",模型可能永远不认为任务完成;只靠"最大轮次",可能在任务快完成时被强行打断。我的经验是:最大轮次 + 无进展检测 + 超时三个兜底条件必须有,目标达成作为正常终止路径。
无进展检测的实现有个小技巧:不要只比较工具名,要比较工具名 + 参数。如果模型连续两次用完全相同的参数调同一个工具,那基本可以判定它卡住了。这时候可以给模型一个提示:"你已经用相同参数调用过这个工具了,请换一个思路或直接给出答案。"
3.2 上下文管理:Token 成本与信息完整性的平衡
Loop 每转一圈,上下文就会增长一截。如果不加管理,几轮下来上下文就爆了,要么超出模型窗口限制,要么 Token 成本高得离谱。上下文管理是 Loop 层的核心工作之一。
我的策略是分层管理:把上下文分成"系统提示"、"任务目标"、"历史对话"、"工具结果"四部分,分别处理。
- 系统提示:固定不变,始终保留。
- 任务目标:固定不变,始终保留。这是 Agent 的"北极星",不能丢。
- 历史对话:滑动窗口 + 摘要。保留最近 N 轮完整对话,更早的对话压缩成摘要。
- 工具结果:按需保留。如果工具结果已经被模型消化并体现在后续推理中,可以替换成一句"已获取 XX 信息"。
摘要的生成时机很关键。我的做法是:当上下文 Token 数超过阈值(比如模型窗口的 70%)时,触发一次摘要,把最老的一批对话压缩。摘要本身也是一次模型调用,所以要控制频率,不能每轮都摘要。
注意:摘要会丢失信息,所以关键信息(比如用户明确提出的约束条件)要单独提取出来,放在"任务目标"里,不参与摘要。我踩过的坑是:用户说"预算不超过 500 元",结果摘要时把这个约束丢了,Agent 推荐了一堆超预算的东西。
3.3 人在回路:什么时候该让人介入
全自动的 Agent 听起来很酷,但在生产环境里,关键节点让人介入往往更靠谱。人在回路(Human-in-the-loop)的介入点设计,是 Loop 层的重要考量。
我一般在这几个场景设置人工介入:高风险操作前(比如删除数据、发送邮件、支付)、模型置信度低时(比如模型自己说"我不确定")、涉及敏感信息时(比如处理用户隐私数据)、成本超过阈值时(比如某个任务已经花了不少钱还没完成)。
介入的实现方式有两种:阻塞式和异步式。阻塞式是 Agent 停下来等人确认,确认后才继续;异步式是 Agent 先把能做的做完,把需要确认的部分挂起,人确认后再继续。阻塞式体验好但效率低,异步式效率高但实现复杂。我的建议是:高风险操作必须阻塞式,其他场景可以异步。
3.4 循环中的状态保存与恢复
Agent 任务可能跑很久,中间可能因为各种原因中断(服务重启、网络故障、用户关闭页面)。如果每次中断都从头开始,体验极差。所以 Loop 层要支持状态保存和恢复。
状态保存的核心是:把每一轮的关键状态(当前轮次、已执行的动作、已获取的信息、下一步计划)序列化存下来。恢复时,从上次的状态继续,而不是从头来。
这里有个设计选择:存快照还是存事件。存快照是每轮存一份完整状态,恢复时直接加载;存事件是只存增量变化,恢复时重放事件。快照简单但占空间,事件省空间但恢复慢。我的经验是:轮次少的任务用快照,轮次多的任务用事件 + 定期快照。
状态存储的介质也有讲究。内存最快但重启就丢;Redis 快且支持持久化,适合大多数场景;数据库最稳但慢,适合对可靠性要求极高的场景。我一般用 Redis 存运行中的状态,任务完成后归档到数据库。
4. Graph 层:复杂任务的编排拓扑
4.1 从线性链到有向图:什么时候需要 Graph
不是所有 Agent 都需要 Graph。如果你的任务就是"用户问一句,Agent 答一句",那 Loop 层就够了,硬套 Graph 反而是过度设计。Graph 的价值在任务变复杂时才体现出来。
我判断要不要上 Graph,看三个信号:任务有明确的多阶段(比如先调研、再分析、再生成报告)、阶段之间有条件分支(比如根据调研结果决定走哪条分析路径)、需要并行处理(比如同时查多个数据源再汇总)。满足任意两个,就值得上 Graph。
从线性链到有向图的演进,本质是把"隐式的流程控制"变成"显式的拓扑结构"。线性链里,流程控制藏在代码的 if-else 和循环里,改起来牵一发动全身;有向图里,流程控制就是节点和边,改流程就是改图,清晰得多。
4.2 节点设计:粒度、职责与复用
Graph 的节点设计,核心是粒度。节点太粗,一个节点干太多事,又变成了隐式流程;节点太细,图变得极其复杂,维护成本高。
我的经验法则是:一个节点只做一件可以用一句话描述清楚的事。比如"查询天气"是一个节点,"根据天气生成建议"是另一个节点。如果一个节点的描述里出现了"并且"、"然后",那它可能该拆了。
节点的职责要单一,但节点本身要可复用。比如"调用某个 API 获取数据"这种节点,应该设计成通用的,通过参数区分不同的 API,而不是每个 API 写一个节点。复用的节点多了,图的维护成本会大幅下降。
节点之间通过**状态(State)**通信。每个节点读取状态、处理、写回状态。状态的结构要提前设计好,它是节点之间的"契约"。我见过有人状态结构随意改,导致节点之间对不上,调试起来极其痛苦。
4.3 条件分支与并行分支的实现
条件分支是 Graph 的灵魂。它的实现方式是:节点执行完后,根据结果决定走哪条边。比如"查询天气"节点执行完,如果结果是"下雨",走"生成带伞提醒"这条边;否则走"跳过提醒"这条边。
条件分支的实现要注意:条件要穷举。如果只写了"下雨"和"不下雨"两条边,但实际出现了"查询失败"的情况,图就卡住了。所以每个条件分支都要有默认边(fallback),处理意外情况。
并行分支是提升效率的利器。比如"同时查天气、查活动、查交通",三个节点并行执行,最后汇总。并行的实现有两种:真并行(多线程/多进程)和伪并行(依次执行但逻辑上独立)。真并行快但复杂,伪并行简单但慢。我的建议是:IO 密集型的任务用真并行,CPU 密集型的任务谨慎用(可能反而更慢)。
并行分支的难点在合并。多个并行节点的结果怎么汇总?我的做法是定义一个"汇总节点",它等待所有并行分支完成,然后统一处理。如果某个分支失败了,汇总节点要能处理部分成功的情况,而不是整个任务失败。
4.4 子图嵌套与错误回退
复杂任务往往可以拆成多个子任务,每个子任务本身又是一张图。这就是子图嵌套。子图的好处是:关注点分离。主图只关心子任务之间的流转,子图内部怎么实现,主图不关心。
子图嵌套的实现,关键是状态的隔离与传递。子图有自己的内部状态,但对外只暴露必要的输入输出。这样主图改起来不会影响子图,子图改起来也不会影响主图。
错误回退是 Graph 层的兜底机制。当某个节点失败时,不是直接终止整个任务,而是回退到上一个稳定状态,尝试其他路径。比如"查询天气"失败了,可以回退到"询问用户城市"节点,让用户确认城市后再试。
回退的实现要记录检查点(Checkpoint)。每个关键节点执行前,保存一份状态快照。失败时,从最近的检查点恢复。检查点的粒度要权衡:太密占空间,太疏回退代价大。我的经验是:在"有副作用"的节点前必须存检查点,其他节点可以按需存。
5. 三层架构的协作与生产实践
5.1 一个完整案例:从需求到落地的三层设计
我把前面提到的"天气 + 活动推荐"案例完整走一遍,展示三层如何协作。
Graph 层设计:节点包括解析意图、查询天气、天气判断、生成提醒、搜索活动、汇总输出。边包括:解析意图→查询天气;查询天气→天气判断;天气判断→ 下雨走生成提醒,不下雨走搜索活动;生成提醒→搜索活动;搜索活动→汇总输出。
Loop 层设计:搜索活动节点内部有循环,最多搜 3 次,每次根据上次结果调整关键词。终止条件是:找到 3 个以上满意结果,或达到最大轮次,或连续两次结果高度相似。
Harness 层设计:天气 API 超时 10s、重试 2 次;搜索工具超时 15s、重试 1 次;模型调用超时 60s、重试 2 次。所有调用记录日志和 Token 消耗。
这个案例里,三层各司其职:Graph 管流程,Loop 管搜索的迭代,Harness 管每次调用的稳定性。任何一层出问题,都能快速定位。
5.2 性能与成本的优化手段
Agent 的性能和成本,是生产环境的两大命脉。我总结几个实用的优化手段:
缓存:模型调用和工具调用都可以缓存。相同的输入直接返回缓存结果,能省大量时间和成本。但要注意缓存的失效策略,尤其是涉及实时数据的场景。
模型分级:不是所有节点都需要用最强的模型。简单的意图解析用小模型,复杂的推理用大模型。我见过一个项目,所有节点都用最贵的模型,成本是优化后的 5 倍。
并行化:能并行的节点尽量并行。前面说的并行分支,能把总耗时从"各节点之和"降到"最慢节点的时间"。
上下文精简:前面 Loop 层讲的上下文管理,直接决定了 Token 成本。精简上下文是最有效的降本手段之一。
提前终止:如果任务已经能给出满意答案,不要为了"完整"而继续跑。比如搜索活动已经找到 3 个很好的结果,就没必要再搜第 4 个。
5.3 常见架构反模式与避坑指南
最后说说我见过的几个典型反模式,都是血泪教训:
反模式一:Harness 里写业务逻辑。表现是 Harness 代码里出现大量 if-else 判断业务场景。后果是 Harness 越来越臃肿,无法复用。正确做法是把业务逻辑上移到 Graph 或 Loop。
反模式二:Loop 没有兜底终止条件。表现是只靠"目标达成"终止,结果模型陷入死循环。后果是任务永远跑不完,成本失控。正确做法是必须有最大轮次、无进展检测、超时三重兜底。
反模式三:Graph 节点粒度过细。表现是一张图有几十个节点,每个节点只做一行代码的事。后果是图极其复杂,维护成本高。正确做法是节点粒度适中,一个节点做一件有意义的事。
反模式四:状态结构随意变更。表现是节点之间通过全局变量通信,状态结构随时改。后果是节点之间对不上,调试困难。正确做法是提前设计好状态结构,作为节点之间的契约。
反模式五:没有可观测性。表现是线上出问题只能靠猜。后果是排查效率极低,问题反复出现。正确做法是从第一天就埋好日志、链路追踪、成本统计。
实操心得:三层架构不是银弹,不要为了分层而分层。小项目可能只需要 Harness + Loop,硬加 Graph 是过度设计。分层的目的是让问题有处可查、让修改有处可下手,如果达不到这个目的,分层就是负担。
6. 常见问题与排查技巧实录
6.1 Agent 卡住不动了怎么办
这是最常见的问题。排查思路是自下而上:先看 Harness 层有没有调用超时或失败,再看 Loop 层是不是陷入了无进展循环,最后看 Graph 层是不是卡在某个节点等不到输入。
具体操作:先查日志,看最后一次成功的调用是什么、之后发生了什么。如果日志显示某个工具调用一直超时,那是 Harness 的超时配置问题;如果日志显示模型反复调同一个工具,那是 Loop 的终止条件问题;如果日志显示某个节点执行完就没有下文了,那是 Graph 的边配置问题。
6.2 Token 消耗异常怎么定位
Token 消耗异常,八成是上下文管理出了问题。排查步骤:先看单次调用的 Token 数,如果单次就很大,那是上下文里塞了太多东西;再看调用次数,如果次数很多,那是 Loop 循环太多或 Graph 节点太多。
定位到具体原因后,对应的解法是:上下文太大就做摘要和截断;循环太多就优化终止条件;节点太多就合并节点。我一般会做一个 Token 消耗的看板,按用户、按功能、按节点维度统计,异常一眼就能看出来。
6.3 工具调用失败率高的排查路径
工具调用失败率高,先分类:是超时、是参数错误、还是服务端错误。超时的话,看是不是超时时间设太短,或者工具本身太慢;参数错误的话,看是不是工具描述不清楚导致模型生成错误参数;服务端错误的话,看是不是限流或服务不稳定。
我遇到过一个案例:某个工具失败率特别高,查下来发现是模型生成的参数里日期格式不对。解法是在工具描述里明确写清楚日期格式,并在 Harness 层加参数校验和自动修正。改完之后失败率从 30% 降到了 2%。
6.4 常见问题速查表
| 问题现象 | 可能原因 | 排查方向 | 解决手段 |
|---|---|---|---|
| Agent 卡住不动 | 工具超时/循环无终止/节点卡住 | 查日志看最后成功调用 | 调超时/加终止条件/修边配置 |
| Token 消耗异常 | 上下文过大/循环过多/节点过多 | 看单次 Token 和调用次数 | 摘要截断/优化终止/合并节点 |
| 工具失败率高 | 超时短/参数错/服务端问题 | 分类统计失败原因 | 调超时/改描述加校验/加降级 |
| 任务结果跑偏 | Graph 节点拆分不当 | 看哪个节点输出偏离 | 重新设计节点和边 |
| 成本失控 | 模型选型不当/无缓存 | 看各节点成本占比 | 模型分级/加缓存/精简上下文 |
| 恢复后重复执行 | 状态保存不完整 | 看检查点内容 | 补全状态/加幂等处理 |
6.5 几个独家避坑技巧
技巧一:给每个节点加"心跳日志"。节点开始和结束时各打一条日志,这样一眼就能看出卡在哪个节点。没有心跳日志,排查卡顿问题就像大海捞针。
技巧二:工具调用加"幂等键"。如果工具调用可能重试,一定要加幂等键,避免重复执行有副作用的操作。比如"发送邮件"这种工具,重试时如果没幂等键,用户会收到两封邮件。
技巧三:模型输出加"格式校验"。模型返回的 JSON 经常有格式问题(多一个逗号、少一个引号)。在 Harness 层加格式校验和自动修复,能大幅降低下游节点的失败率。
技巧四:关键路径加"熔断"。如果某个工具连续失败 N 次,暂时熔断它,直接返回降级结果,避免一直重试拖垮整个任务。熔断一段时间后再半开重试。
技巧五:定期做"混沌测试"。故意让某些工具超时、返回错误,看 Agent 能不能优雅处理。我见过太多系统在正常路径下跑得很好,一遇到异常就崩了。混沌测试能提前暴露这些问题。
这套三层架构,我在多个项目里反复验证过,它最大的价值不是某个具体的技术点,而是提供了一套定位问题和组织代码的框架。当你面对一个复杂的 Agent 系统时,先问自己:这个问题属于哪一层?想清楚这个,解决路径就清晰了。