说实话,这两年“AI Agent”这个词已经被聊烂了。身边不少人兴致勃勃接了大模型接口,写了几段提示词,搭出一个能“自动规划、自动调用工具”的demo,演示的时候全场眼前一亮。可真到生产环境里跑上一周,问题就全出来了:任务做到一半突然停住、工具参数传错、模型开始胡编乱造、上下文越滚越长导致行为越来越怪。最后大家得出结论:Demo Agent一推就倒,根本不敢放开用。
我也经历过这个阶段。后来接手了一个客服工单分类与处理Agent的模拟项目,被各种“不靠谱”折磨了很久,才慢慢摸清了一套构建可靠Agent的方法论。这篇文章就把我实际踩坑、逐步沉淀下来的思路完整写出来。核心聚焦在三个问题上:可靠性问题到底从哪来、架构上怎么设计才能兜住底、具体实现和排障时该盯住哪些环节。适合正在从demo走向真实场景的开发者,也适合想系统理解Agent可靠性设计的产品和技术同学。
1. 为什么Agent总在关键时刻掉链子:不可靠的根源要拆开看
想解决可靠性问题,第一步是把“不可靠”拆成具体的现象。只笼统说“模型不行”或者“逻辑太复杂”,没法下手。我个人习惯把它归因成三个源头,每个源头对应不同的解决手段。
1.1 模型行为本身的不确定性:抽卡式输出是天然属性
大模型天生是概率模型。同样一句话,温度调到0也不是每次输出都一样;温度越高,发散越明显。这在单次问答里问题不大,但Agent的任务往往是一连串决策,只要其中某一步的决策发生了微小偏移,后面所有步骤都会跟着跑偏。
我遇到过一个很典型的例子:客服Agent在判断工单优先级时,模型大部分时间会遵循“高优=VIP客户+故障类+影响面大”的逻辑,但偶尔会把“客户语气着急”也当作高优条件,导致一个普通咨询被转成了紧急工单,整个处理链路被带偏。
再往深一层说,模型还有幻觉问题。在执行“查询订单状态”这类工具调用时,Agent即使没有拿到数据库返回,也可能会基于历史对话“脑补”出一个合理的订单状态。这种问题最阴险——表面看任务完成了,实际上整个结果都是虚构的,后续所有决策都建立在假数据上。所以只看“模型聪明不聪明”远远不够,必须在架构层面对模型输出做约束和校验。
1.2 长链路执行中的状态失控:上下文越长,越容易丢关键信息
Agent任务和普通问答最大的区别在于“多步执行”:第一步的结果要传给第二步,第二步的结果又影响第三步的决策。这套流程看着顺理成章,但做起来就会发现模型工作记忆是有限的。
上下文窗口是变大了,但“能把文字塞进去”和“能准确回忆并利用”是两回事。当对话历史累积到一定程度,模型往往会遗忘早期步骤里的关键约束,甚至被中间某个看起来很重要、实际无关的细节带跑。
更普遍的问题比遗忘更隐蔽:状态被覆盖。初期我实现过一个很朴素的Agent状态类,所有中间变量塞进一个大dict,每步执行完就往里追加。结果有一次工具返回了新的客户ID,和原有订单ID用了同一个字段名,覆盖了旧值,后续查询全部基于错误ID进行。这个问题的本质是状态管理没有做结构化隔离,混乱只是时间问题。
1.3 工具调用的脆性:外部环境根本不受你控制
Agent的现实世界里,工具是最大的不确定性来源。模型能力再稳定,只要下游接口出了问题,任务一样失败。我把这些失败归纳成几类:
- 接口结构变化,比如API返回字段从
order_no改成了orderId,Agent解析不到数据; - 网络超时或限流,比如批量查询时被服务端限速,Agent拿不到响应;
- 参数校验失败,工具要求ISO日期格式,模型给了个“明天下午3点”这种自然语言;
- 权限与依赖问题,工具依赖的环境变量没配好,报错信息Agent又看不懂。
工具调用失败的可怕之处在于,它不是个小概率事件,而是必然会发生的。任何一种外部依赖只要发生一次异常,整条链路就得停下来等待处理。所以构建可靠Agent时,工具层的容错设计不是“可选优化”,而是“必做功课”。
1.4 可靠的定义要落地:用指标取代感觉
聊可靠不能停留在“感觉还比较稳”。在做可靠性改造之前,我先把“可靠”拆成了五个可量化的指标,后面所有设计都围绕这些指标展开:
| 指标 | 含义 | 例子 |
|---|---|---|
| 任务成功率 | 完整链路跑通并得到最终结果的占比 | 100个工单里,99个正确完成分类与处理 |
| 部分完成率 | 任务没全做完,但已完成部分可被利用 | 查询完成但邮件通知失败,查询结果仍保留 |
| 失败可恢复性 | 失败后能否通过重试或降级路径继续推进 | 主API超时后自动切换备用数据源 |
| 结果可验证性 | 最终输出能否被程序或模型二次校验 | 生成的SQL在实际库上试跑,通过后再执行 |
| 成本可控 | 可靠性的提升不能靠无限烧token实现 | 多次失败重试消耗的token控制在任务预算内 |
这五个指标不是学术定义,是我实际运维时用来衡量“这版改造有没有用”的标尺。后续的架构设计和代码实现,本质上都是在往这些指标上使劲。
2. 架构层先把底兜住:让每一步都有校验、有兜底、有退路
很多人一开始构建Agent时,想着把“思考能力”全交给模型,自己只写一个循环:模型想一步,执行一步。这种架构最大的问题是——所有环节都依赖模型一个人做对。真正可靠的Agent设计,应该反过来:把能做主的事全部工程化,把模型当做一个强力的决策器,但绝不让它独自承担全流程。
2.1 计划-执行-验证:把单次“神判断”拆成可追踪的三层
我采用的架构核心是经典的“计划-执行-验证”三段式,但做了不少工程化改造。它的核心思想很简单:不让模型在一次输出里同时完成“理解目标、拆解步骤、执行每个步骤、检查结果”四件事,而是把每一步拆开,每层各司其职。
计划层负责接收任务描述,输出一份结构化行动计划。这一步只动脑、不动手。我在设计计划层时,会强制模型按照固定schema输出JSON,包含目标、步骤列表、每个步骤的依赖关系、预期产物。一旦输出格式不合法,就重试解析,不让“半格式化的脏数据”进入下一步。
执行层按计划逐条执行。每执行完一步,把真实结果写回状态区,而不是让模型凭记忆猜测。这里的关键原则是:执行层的每一步动作必须“可观测”——无论成功还是失败,都要留下结构化日志,后面排查时一眼能定位到是哪一步出了问题。
验证层是我特别强调的一层。很多Agent任务失败,不是前面的计划或执行出问题,而是模型压根没发现自己做错了。我在每完成一个步骤或最终产出后,都会加一次独立验证。验证的方式有两种,一种是确定性规则校验,比如结果里有没有关键字段、金额是不是负数这种程序能直接判断的;另一种是模型自校验,让LLM用“挑剔视角”检查上一步结果与原始目标是否一致。这也是整个架构里性价比最高的一环。
这三层拆开以后有一个直接好处:出问题时,报错可以精确到层。比如“计划层给出的步骤不可执行”,和“执行层调用工具失败”,处理方式完全不同,排查成本差了好几倍。
2.2 显式状态管理:把记忆从模型脑子里搬出来
Agent执行过程中,记忆就是个“必须强管控”的东西。所谓强管控,就是要有一套外部状态存储来管理所有变量,而不是指望模型通过对话历史记住它们。
我把Agent的记忆拆成两个层级:
- 短期任务态,记录当前任务的中间结果、当前在第几步、待执行列表。这一层用结构化状态对象管理,读写都走代码逻辑,不经过模型思考;
- 长期记忆态,记录跨任务复用的知识,比如用户偏好、历史处理模式。这一层会定期做摘要和压缩,避免把无关的历史信息全部塞进上下文。
这么做最直接的好处就是防止“上下文爆炸”。之前我的Agent跑到十几步时经常行为漂移,后来一查,发现每一轮的思考、工具返回都完整堆在对话历史里,几万字的噪声让模型彻底迷失了重点。引入外部状态管理之后,每步只把“与当前决策相关的精简摘要”发给模型,行为稳定性提升非常明显。
状态管理还有一条铁律:所有被使用的变量必须初始化,且要有类型约束。我曾经因为状态字段混用吃过亏,后来把所有状态字段做成了dataclass或Pydantic模型,字段名冲突、类型错误在运行前就被拦截掉。
2.3 工具层要做防御式设计:接口是会变的,Agent要扛得住
工具调用层的可靠性设计,直接影响整个系统的下限。我这里有几条实际有效的经验:
工具描述要“厚”而不是“薄”。很多Agent开发者在注册工具时,只写一句“查询订单信息”,剩下的全靠模型悟。这是不对的。工具描述应当像给新员工写操作手册一样:参数格式、字段含义、返回值示例、常见的失败情况、需要满足的前置条件。模型虽然聪明,但它没有你对你系统的背景知识,描述越明确,误调用和传错参数的概率越低。
统一错误返回格式。我把所有工具的错误返回包装成同一种结构:success: false、error_code、error_message、retryable标志。Agent看到这种结构后,可以快速判断这个错误是永久性的还是暂时的,从而决定是放弃还是重试。如果每个工具报错格式都不一样,Agent就无法统一处理,只能在某一步彻底卡死。
降级路径一定要备好。核心工具不可用时,必须有一个“低配版”方案。比如主数据查询API超时,自动切到只读副本;实时预测接口挂了,先用规则流程顶上。这个思想跟微服务里的降级一样,但Agent场景里很少人提前设计,所以一旦遇到外部故障,整个Agent就是瘫痪状态。
3. 实操记录:从零搭一个带可靠性的Agent骨架
理论说再多,不如直接看能跑的代码。下面是我在一个客服工单分类与处理Agent里实际沉淀下来的骨架。它不依赖任何特定框架,只依赖模型接口和基础数据结构,你可以直接平移到自己的项目里。
3.1 技术选型:够用就好,别被花活带偏
我先说说选型思路。模型方面,我选的是通用对话大模型的API,支持JSON输出模式和系统提示词约束。没有选择非常“重”的自托管方案,因为项目阶段最需要的是快速迭代。编排层没有上复杂的社区框架,直接用了一个极简的自研执行循环。理由很简单:社区框架抽象太多,出了问题反而更难看懂。自研几十行核心代码,任何一步都能自己控制。
状态存储用了关系型数据库加内存对象的组合。执行过程中的短期状态放在内存,跨会话需要持久化的数据写数据库。队列方面,任务提交走的是简单的优先队列,没有引入额外的消息中间件。
这些选型不一定适合所有场景。如果任务并发高、链路长,还是需要更完善的基础设施。但如果你和我一样处于“先把可靠性跑通”的阶段,这个组合足够轻量又足够灵活。
3.2 核心代码骨架与实现细节
下面这段代码是整个Agent的骨架,我把它尽量压缩成最核心的表达。它包含四个部分:状态类、计划器、执行器、验证器。
from dataclasses import dataclass, field from typing import Any, Callable, Optional import json # ---------- 状态管理 ---------- @dataclass class AgentState: task: str # 原始任务描述 plan: list[dict] = field(default_factory=list) # 结构化计划 step_index: int = 0 # 当前执行到的步骤 memory: dict[str, Any] = field(default_factory=dict) # 跨步骤关键状态 step_results: list[dict] = field(default_factory=list) # 每一步的真实结果 retries: int = 0 # 当前步骤重试次数 max_retries: int = 2 # 最大重试次数 final_result: Optional[dict] = None # 最终输出 def update(self, key: str, value: Any) -> None: # 所有状态更新必须走这里,保证可追溯 self.memory[key] = value self.step_results.append({"step": self.step_index, "key": key, "value": value})状态类我觉得最重要的设计是update方法。所有写状态的操作都必须经过它,这样每一步修改都进了step_results,后面排查时能精确还原状态变化轨迹。这个习惯救了我很多次。
# ---------- LLM 调用封装与 JSON 解析 ---------- def llm_call(prompt: str, temperature: float = 0.2, schema: Optional[dict] = None): # 这里实际会调模型接口 # 注意:带 schema 时开启 JSON 输出模式,温度调低 raw = model_api.chat(prompt=prompt, temperature=temperature) return parse_json_with_retry(raw, schema=schema) # ---------- 计划器 ---------- PLAN_SYSTEM_PROMPT = """ 你是一个任务规划器。你的任务是基于用户目标生成结构化行动计划。 要求: 1. 输出必须是合法JSON,格式:{"goal": str, "steps": [{"id": int, "action": str, "tool": str, "args": dict, "depends_on": list}]} 2. steps不得超过5步,每一步只做一件具体的事 3. 每一步必须写明需要的工具和参数,不允许模糊描述 4. 如果任务本身包含明显不可执行的部分,明确标记为需要用户确认 """ def planner(state: AgentState) -> None: prompt = f"用户目标:{state.task}" plan_data = llm_call(PLAN_SYSTEM_PROMPT + prompt, temperature=0.1) state.plan = plan_data["steps"] state.step_index = 0计划器里有个容易被忽视的点:depends_on。我要求模型显式写出步骤间的依赖关系。别看这个字段简单,它让执行器可以判断哪些步骤可以并行、哪些必须串行,也方便在后续步骤失败时快速回溯依赖链。
# ---------- 工具注册与错误包装 ---------- class ToolRegistry: def __init__(self): self._tools = {} def register(self, name: str, executor: Callable, schema: dict, description: str): self._tools[name] = {"executor": executor, "schema": schema, "description": description} def call(self, name: str, args: dict) -> dict: tool = self._tools.get(name) if not tool: return {"success": False, "error_code": "TOOL_NOT_FOUND", "retryable": False} try: result = tool["executor"](**args) return {"success": True, "result": result, "retryable": False} except TimeoutError: return {"success": False, "error_code": "TIMEOUT", "retryable": True} except ValueError as e: return {"success": False, "error_code": "INVALID_ARG", "error_message": str(e), "retryable": False} except Exception as e: return {"success": False, "error_code": "UNKNOWN", "error_message": str(e), "retryable": True}ToolRegistry把工具调用和错误处理集中到了一处。所有工具返回结构都一样,执行器对这一层非常信任。每条错误信息里的retryable字段是给执行器做重试判断用的。判断逻辑简单但有效:超时和未知异常可以重试,参数错误和工具不存在重试再多次也没用。
# ---------- 执行器 ---------- EXECUTE_SYSTEM_PROMPT = """ 你是任务执行器。根据当前计划步骤选择工具,并生成严格符合工具schema的参数。 不需要解释,只输出JSON:{"tool": str, "args": dict} 如果缺少必要信息,输出:{"tool": "ask_user", "args": {"question": "..."}} """ def executor(state: AgentState, registry: ToolRegistry): # 检查所有依赖步骤是否已完成 step = state.plan[state.step_index] for dep_id in step.get("depends_on", []): if state.step_results[deps_ok(dep_id)] is None: return {"success": False, "error_code": "DEP_NOT_DONE"} # 让模型根据当前状态决定调用什么工具、传什么参数 decision_prompt = f"当前任务目标:{state.task}\n当前步骤:{step}\n当前状态:{json.dumps(state.memory, ensure_ascii=False)}" decision = llm_call(EXECUTE_SYSTEM_PROMPT + decision_prompt) if decision.get("tool") == "ask_user": return {"ask_user": decision["args"]["question"]} # 实际调用工具 result = registry.call(decision["tool"], decision["args"]) if result["success"]: state.update(f"step_{state.step_index}_result", result["result"]) state.step_index += 1 state.retries = 0 else: return result return {"success": True, "result": result}执行器的核心设计是“决策和执行分离”:模型只负责决定调用哪个工具、传什么参数,真正执行工具、处理异常都由代码负责。这一步隔离很重要——模型永远不直接接触外部系统,外部系统的异常也就无法直接污染模型的世界观。
执行器还有一个细节:ask_user这个特殊工具。当Agent发现关键信息缺失时,可以选择直接问用户,而不是硬着头皮瞎猜。这个设计我第一次加上时觉得有点“不够智能”,后来才发现这正是可靠性的重要组成部分——聪明的Agent要知道自己什么时候该停下来。
# ---------- 验证器 ---------- VERIFY_SYSTEM_PROMPT = """ 你是任务验证器。你的任务是检查任务最终结果是否真正满足了用户原始目标。 请以挑剔视角审查,重点关注: 1. 结果与原始目标是否一致,有没有遗漏要求 2. 是否有数值、日期、关键字段错误 3. 是否存在模型编造的信息 输出JSON:{"passed": bool, "reason": str, "risks": [str]} """ def verifier(state: AgentState): final_state = state.memory prompt = f"原始目标:{state.task}\n最终状态:{json.dumps(final_state, ensure_ascii=False)}" verdict = llm_call(VERIFY_SYSTEM_PROMPT + prompt, temperature=0.0) return verdict # ---------- 主循环 ---------- def run_agent(task: str, registry: ToolRegistry): state = AgentState(task=task) planner(state) while state.step_index < len(state.plan): step = state.plan[state.step_index] result = executor(state, registry) if not result.get("success"): if result.get("retryable") and state.retries < state.max_retries: state.retries += 1 continue state.final_result = {"status": "failed", "reason": result.get("error_message"), "partial": state.step_results} return state.final_result if state.step_index >= len(state.plan): break verdict = verifier(state) if not verdict["passed"]: # 验证不通过的情况:尝试让计划器生成修复方案 repair_plan = planner_for_repair(state, verdict["reason"]) if repair_plan: state.plan = repair_plan # 从头执行修复计划 state.step_index = 0 continue state.final_result = {"status": "success", "result": state.memory, "verdict": verdict} return state.final_result主循环里最容易被忽略的是验证不通过后的“修复分支”。我最初的做法是验证不通过就直接标记失败。后来调整成:验证器给出失败原因,计划器根据原因生成修复计划,然后重新进入执行循环。这种“验证-修复-重执行”的机制,把很多本来会直接失败的任务救了回来。
比如计划器一开始漏掉了工单必须附带客户等级信息,执行器执行完发现结果里根本没这个字段,验证器标记不通过并说明原因,计划器重新补一步查询客户等级,再次执行后验证通过。整个过程不需要人介入,成本只是多消耗了一次规划调用和一次工具调用。
3.3 关键参数配置心得:几个直接影响可靠性的设置
这些参数看似不起眼,实际影响非常大。我把调优过程中沉淀下来的配置经验整理成了表格:
| 参数项 | 推荐值 | 作用与说明 |
|---|---|---|
| 模型温度 | 0.1~0.2 | 温度越高输出越发散。规划、执行、验证都用低温,牺牲一点创意换确定性 |
| JSON输出模式 | 开启 | 强制模型输出合法JSON,避免解析失败,格式问题是可靠性的隐形杀手 |
| 最大重试次数 | 2 | 超过两次重试基本说明问题不可自愈,继续重试只会烧钱。重试间隔建议指数退避 |
| 单任务最大步数 | 5~8 | 超过这个步数,链路复杂度飙升,错误概率指数增加。长任务拆成子任务更靠谱 |
| 验证器温度 | 0.0 | 验证环节温度必须拉到最低,不需要任何创造性,只需要严格审查 |
| 上下文摘要长度 | 尽量精简 | 每次只注入与当前决策相关的摘要,防止无关历史信息干扰决策 |
温度是我第一个调的参数。刚做Agent时看到有人推荐“温度高一点有创造性”,结果任务执行得越来越奔放,各种奇怪的工具调用和参数层出不穷。后来所有决策环节全部降到0.1左右,可靠性立刻上升一个台阶。
步骤数上限则是另一个被低估的参数。我之前遇到过Agent规划出12步的长任务,结果第9步出问题时,前面8步的成果也全部作废。后来加了一条规则:超过5步的任务,必须拆成多个子任务分阶段执行,每个子任务单独验证。这个改动起效明显——长的链路天然易碎,能拆就拆。
4. 从运行到排障:把可靠性建立在可观测、可回归的基础上
代码写好了、架构搭对了,并不代表可靠性工作结束。系统上线后才是真正的考验。这节我分享运行时观测和故障排查的实战经验。
4.1 高频故障速查表
我梳理了运行期间遇到最多的几类问题,做成速查表,新的问题出现时先从这里排查:
| 故障现象 | 根因 | 排查思路 | 处理方式 |
|---|---|---|---|
| Agent反复调用同一个工具,结果都一样 | 工具返回了可重试错误但执行器没记录重试状态 | 检查重试计数与退避逻辑 | 确认retryable标记,加入次卡控 |
| 结果看起来合理但和事实不符 | 模型幻觉,工具调用实际失败但Agent脑补了结果 | 检查工具返回日志是否真实包含数据 | 在验证器中引入事实性校验 |
| 早期步骤信息被遗忘 | 上下文被无关中间过程淹没 | 检查每轮注入的context长度 | 启用摘要机制,精简上下文 |
| 任务中途停下不继续 | 执行器把ask_user结果当成了终态 | 检查状态机是否处理用户回复后的继续逻辑 | 补充用户反馈回填流程 |
| 计划步骤依赖错误 | 模型没有正确理解依赖关系 | 检查计划JSON中的depends_on字段 | 在计划器中增加依赖合法性校验 |
这五个问题里,“模型脑补结果”是我最想强调的一个。它的典型表现是:Agent调用的工具返回了失败,但模型的输出却是一段完整的分析结果,好像数据真的查到了一样。一旦这种结果进入状态区,后续所有步骤都建立在一个捏造的事实上,最终任务展示出来“看起来成功”,实际上是虚假成功。我在验证器中专门加了一条规则:如果工具调用未成功,任何涉及该工具结果的判断都必须标记为风险。
4.2 用日志与追踪还原每一步
排查Agent故障,最大的困难在于“链路太长、状态太多、因果链复杂”。我踩坑得到的教训是:没有结构和追踪的日志,排查Agent问题等于大海捞针。
我在实际项目里落地的日志方案分三层:
- 运行日志,记录每一次模型请求、工具调用、状态更新,附带时间戳和耗时;
- 决策追踪,记录每一步模型输出的完整内容以及它读取到的上下文摘要,方便复盘模型为什么会做这个决策;
- 结果审核,记录验证器的每一次判定结果和理由,用于评估整套系统的误判率。
三层日志里,决策追踪是最容易被忽略、又最有价值的。比如某次Agent做了个错误决策,光看运行日志只能看到“调了某个工具,传了某个参数”,但不知道它为什么这么传。结合决策追踪里注入的上下文,就能发现原来是某一步工具返回的格式里有个隐藏字段,模型误读成了另一个意思。
链路追踪同样重要。我给每次任务分配一个唯一的trace_id,从计划生成到最终验证,所有日志都带上这个ID。排查时只要拿到一个trace_id,整条执行链都能串起来。
4.3 回归测试:没有基准集,一切优化都是自我感动
可靠性优化的最大陷阱是“修一个bug,引出三个新问题”。模型输出的随机性决定了每次改动都可能改变整体行为。我在项目中期引入了回归测试机制,才让优化变得可持续。
我的做法是维护一个“基准任务集”:包含几十个覆盖典型场景的测试任务,每个任务都有期望结果或者至少有关键检查点。任何一次提示词调整、参数修改、工具描述变更,都要先跑一遍整个基准集,对比通过率变化。
为了提高回归效率,我把基准任务分成三个等级:
- 核心任务,比如“正确查询订单状态并回复客户”,必须100%通过;
- 常规任务,比如“处理退货流程”,要求通过率不低于95%;
- 探索任务,比如“处理异常或模糊需求”,通过率可放宽,但需要记录失败模式。
每次优化后只看三个指标:核心任务通过率降没降、平均token消耗涨没涨、失败原因有没有新增模式。这三个指标比任何感觉都可靠。
4.4 一批值得提前规避的典型坑
除了上面提到的速查表问题,我再补充几个比较容易踩、但很少人提前预警的坑:
一是“伪并行”的依赖陷阱。如果计划器生成多个步骤,且这些步骤都修改同一个状态字段,并行执行会互相覆盖。我这个项目里就遇到过两个步骤同时写customer_id,结果其中一个写错了值,另一个已经用它做完了查询。我的解决办法是:计划生成时,凡是存在写同一字段的步骤,自动降级为串行。
二是“验证器也吃幻觉”的问题。验证器本身也是模型,也可能犯错。尤其当模型对结果“过于自信”时,验证器可能跟着一起漏过问题。为了防这个,我在验证器之外增加了少量确定性规则校验。规则校验优先,模型校验为辅。比如客服工单的处理结果必须包含工单号,这条规则直接用正则检查,根本不过模型。
三是“成本失控”的问题。验证器、修复流程、重试机制都会增加额外调用,可靠性提升和成本提升是伴生的。我的经验是给每个任务设置一个token预算,比如单个任务最多消耗多少token,超过就直接终止,标记为需要人工介入。这避免出现“一个简单的查询任务因为反复重试烧掉巨额成本”的极端情况。
5. 写在最后:可可靠性不是一次改造,而是一套习惯
这篇文章里写的方法,是我在构建客服场景Agent时一步步踩出来的。说实话,没有哪一条是惊天动地的创新,大部分都是很朴素的工程手段:状态显式化、错误统一化、验证结构化、观测完整化。但正是这些朴素手段组合在一起,把Agent的可靠性从“看运气”提升到了“可预期”。
如果你正在做自己的Agent,我的建议是从最简单的环节入手——先把执行器里所有的工具错误统一格式,再加验证器,再加回归集。不要一上来就追求复杂的记忆系统和多Agent协作。基础打牢了,后面加什么功能都稳;基础不牢,功能越多越容易翻车。
最后再分享一个我个人的习惯:每次Agent出现奇怪故障时,我不急着改提示词,而是先问自己三个问题——模型是不是读到了错误的状态?工具返回是不是与预期不一致?验证器是不是漏掉了该查的维度?大多数时候,答案就藏在这三个问题里。