
做 Agent 两年多踩过的坑比写过的代码还多。我最大的感受是“AI Agent”这个词被各种营销文用烂了但真正的工程骨架很少有人讲清楚。很多人以为 Agent 就是“大模型 Prompt 几个 API”真上了生产才发现麻烦全在那些模型之外的细节里状态怎么维护、记忆怎么存、任务拆到多细、循环什么时候该停、工具返回异常怎么恢复。这篇文章我想从“七要素”和“七个决策点”两条线把 Agent 的工程实现拆开尽量用我做过的项目讲清楚每一个选择的理由而不是丢给你一堆概念。这篇内容适合两类人一类是刚接触 Agent 开发想搞清楚它到底由哪几块构成的新手另一类是已经用 LangChain、LangGraph 或手写循环做过 demo但一上生产就各种翻车想系统排查问题的开发者。我会从要素拆解讲到决策点最后给一套能直接抄的工程骨架和踩坑清单。1. 先给 Agent 画个像它到底由哪几块拼图构成网上有个很流行的说法叫“七要素”我自己的工程实践里也基本认同这个拆法目标、规划、记忆、工具、上下文、执行、反馈。但我要提醒一句七要素不是七个模块各管各的它们更像一张网任何一个要素改动都会牵动另外几个。下面按我对工程实现的优先级一个个说。1.1 目标与任务定义一切工程的起点很多人把 Agent 的目标理解成一句用户 Prompt比如“帮我写一篇行业报告”。但在工程里目标必须是一段机器可解析的结构化指令至少要包含三件事任务边界、输出格式、验收标准。任务边界防止 Agent 跑偏去调用无关工具输出格式方便下游解析验收标准是 Agent 自我判断“该不该停”的依据。举个实际例子。之前做一个市场调研 Agent用户输入“调研一下国内咖啡连锁品牌的扩张情况”如果直接丢给模型它可能给你列一堆品牌简介就结束了。但我在系统 Prompt 里把目标重写成了这样任务边界只调研门店数在 500 家以上的品牌不包含独立咖啡馆。输出格式输出 Markdown 表格字段包括品牌名、门店数、同比增长率、数据来源 URL。验收标准必须覆盖至少 6 个品牌每个数据必须附带来源链接数据无法获取时明确标“未知”。这样改了之后Agent 最后产出的质量立刻上了一个台阶。原因不复杂大模型在没有明确验收标准时倾向于“差不多就交差”这是它的惯性一旦你给了可检查的标准它才会老老实实去核对每一项。这里的工程要点是目标不要用人话要用数据结构和校验规则去写。我建议把目标定义独立成一个 JSON 字段由业务方配置而不是靠用户 Prompt 自由发挥。1.2 规划与任务拆解从“想做”到“该做”规划是 Agent“智商”的主要体现也是工程上最容易失控的一个环节。早期我做 Agent 时总想让它自己规划路径结果模型经常把简单任务拆成 20 步或者两个步骤之间毫无依赖关系却非要串行执行。后来我总结出了一条经验规划分两层一层是模型动态规划一层是工程静态约束两者叠加而不是只靠模型。工具层面的静态约束包括确定哪些步骤必须串行哪些可以并行哪些步骤是可选的分支哪些是必经的主链路。这个约束不是模型生成的而是你用代码或配置写死的。模型只负责在约束内部做决策比如“先查 A 还是先查 B”。动态规划部分我常用两种模式如果任务依赖链很短比如只有两三步直接让模型输出整份步骤列表然后逐个执行。如果任务步骤不确定比如涉及外部 API 调用后才知道下一步那就用 ReAct 循环走一步看一步。一句话能用静态约束卡死的别交给模型自由发挥模型规划只负责“约束下的填缝”。1.3 记忆短期记忆和长期记忆的区别很多新人把记忆理解成一个统一的东西其实工程里必须二分。短期记忆指的是当前会话内要用的信息包括用户的原始诉求、前面几步的工具返回、模型自己的推理中间过程。这些信息一般直接放在上下文列表里不需要持久化。问题在于上下文窗口有限当工具返回很大时历史消息会膨胀得很快。我的做法是给短期记忆设置一个“重要信息槽”像是用户 ID、任务 ID、关键决策点这些字段单独抽出来放一个结构化 dict每次调用模型前注入到 system prompt这样即使中间消息被压缩了关键状态也不丢。长期记忆是指跨会话的知识沉淀比如用户的偏好、历史任务结果、领域知识。这部分必须外部化通常用向量库或普通数据库存储。但不一定非要上向量检索如果记忆条数只有几千条直接上 MySQL 加 LIKE 查询也比盲目上向量库更稳。记忆的价值只有在“被正确召回”时才存在而召回的质量取决于你如何构建索引标签而不只是 embedding 有多好。1.4 工具调用Agent 的“手脚”工具是 Agent 与世界交互的唯一方式但工程上它远不止“封装一个 API”那么简单。一个工具定义至少要包含三部分让模型理解的语义描述、让代码执行的参数结构、让系统安全可控的权限边界。语义描述部分要写到模型能准确判断“什么场景该用我”的程度。比如一个天气查询工具不要只写“查询天气”要写清楚“接收城市名返回当前天气仅适用于中国主要城市其他城市返回不支持”。信息越具体模型选错工具的概率越低。参数结构部分必须做严格的 JSON Schema 定义。别指望模型自己生成完美参数一定要在代码层做二次校验。之前我遇到过一个统计工具模型把“start_time”格式传成了“2024年1月1日”而不是时间戳工具层直接崩了。后来我所有工具的参数都加了一层 Pydantic 校验类型不匹配就返回一个固定格式的错误提示给模型让它自行修正。权限边界部分很多新手会忽略。Agent 能调用的工具绝不能是“全量权限”比如一个数据库查询工具绝对不能让模型传任意 SQL。正确做法是对普通用户限制为白名单查询对敏感操作设计一个“人工审批”的中间态工具不是直接执行而是先生成一个待确认动作由主流程的人审了再跑。1.5 上下文与环境Agent 的“世界模型”上下文这个词有两个含义。一个是单次请求里的 token 上下文一个是 Agent 运行时所处的环境状态。工程上后者比前者更容易被忽略。环境状态包括当前用户是谁、当前权限是哪些、当前任务的物理边界比如只能读某个目录、当前时间、当前可用工具集合。这些信息不该靠模型去猜而应该每轮都注入到系统提示词里。你可以把它们理解成 Agent 的“出厂设置”。我踩过一个典型坑做一个定时任务 Agent让它每天早上 8 点跑一次报表。因为模型不知道“现在是哪天”它会在第二次执行时带着上一次的缓存去算增量结果报表数据全部重复。后来我把系统提示词里加了一行“你当前执行日期是 {YYYY-MM-DD}所有指标均按当天边界计算”问题立刻消失。环境感知不是模型能力问题是我们没有把环境参数传给它的工程问题。1.6 执行引擎把决策变成动作执行引擎是 Agent 的骨架代码它负责把模型的决策一层层翻译成可执行的调用。很多框架已经把这一步做成了黑盒但我建议你还是手写实现一遍否则出 bug 时根本不知道在哪层丢的。一个最小执行引擎其实就是 while 循环模型输出 → 判断是工具调用还是最终回答 → 执行工具 → 把结果放回上下文 → 再调用模型。但工程上要加的东西很多循环上限、单步超时、并发调度、幂等控制、失败重试。这些不是可选项是上线前必须补齐的。我见过最严重的生产事故是一个 Agent 的删除操作在执行后没有同步更新状态导致下一次循环又去删除同一份数据第二次返回“资源不存在”模型以为失败于是换了种方式再试一遍结果产生了一个脏数据。后来我在执行引擎里强制加了两条规则任何写操作在执行前必须查一次当前状态任何操作的返回结果必须先更新任务状态再返回给模型顺序不能反。1.7 反馈与反思Agent 的“自省回路”最后这个要素最容易被砍掉但恰恰是它决定了 Agent 是“能用”还是“好用”。没有反馈机制Agent 就像一个从不回头看自己答案的考生第一遍写成什么样就交什么样。反馈机制分三个层次底层是程序性校验比如工具返回的 HTTP 状态码是否 200、JSON 能否解析、金额字段是否大于 0中间层是规则校验比如“查询结果必须包含不少于 3 个字段”“答案里不允许出现‘我应该’这类不确定措辞”上层才是模型的自我反思让模型对照原始目标和当前结果判断是否满足。一个成本很低的实现是在 Agent 最终回答前加一轮“裁判”调用。把原始目标、当前答案、验收标准一起交给另一个 prompt问“这个答案是否满足要求如果不满足缺什么”。然后用裁判输出决定是否重新执行循环。多花一轮 token但能把任务成功率提升 20% 以上这笔账非常划算。2. 从七要素到七个决策点真正决定工程成败的选择七要素是你理解 Agent 的显微镜七个决策点则是你构建 Agent 时的方向盘。同样一套要素决策不同做出来的完全就是两个东西。下面我把每个决策点的思考方式讲透而不是直接告诉你标准答案。2.1 模型选型能力、成本、延迟三选二模型选型是第一个决策也是最难反悔的决策。你后面对上下文管理和工具调用的所有适配几乎都依赖模型的接口能力。我的决策顺序是先看函数调用能力再看上下文长度和价格最后才是模型智商。函数调用能力指的是模型能否支持原生 tools 参数并返回结构化 tool_call而不是在文本里吐 JSON。这个能力直接决定你执行引擎的复杂程度。如果不支持原生函数调用你得自己去解析模型文本里的“调用指令”脆弱得离谱稍微一个格式变化就崩。很多团队一上来就迷信大参数模型结果成本爆表。我建议做一个小实验再定拿 50 条真实任务样本分别在候选模型上跑同一套最小 Agent 骨架统计成功率、平均轮数、每轮平均延迟、总 token 成本。综合四个指标打分而不是只看“谁答得聪明”。实际上大部分场景下中档模型 好工具设计已经能打赢高档模型 粗糙工具。考虑 API 限流也很关键特别是工具较多时每一轮循环都可能触发一次模型调用倍数放大。建议选支持较高 RPM每分钟请求数的供应商否则低峰期没问题高峰期直接排队。2.2 记忆架构设计什么时候该上向量库这个决策必须建立在“先量化记忆容量”的基础上。审计历史记录平均每轮工具返回多少 token单次任务最长多少轮如果最坏情况下把这些信息全塞进上下文也只占窗口的一半那向量库就是过度设计直接全量放上下文反而效果最好因为模型能看到最原始的信息。什么时候该上三种情况一是单轮上下文已经超过模型窗口 80%二是任务跨会话必须回查历史成果三是记忆里沉淀了大量可复用的领域知识。只有到这一步才值得引入向量库否则系统的复杂度和出故障的面都会大增。真上了向量库后最大的坑不是检索精度而是“检索结果过期”。用户昨天的偏好今天可能就变了但你如果优先召回 top1 旧记忆Agent 就会按过时信息执行。我的方案是给每条记忆加一个 time-to-live 字段召回时以“相关度 × 时间衰减系数”排序并且始终把“当前会话的信息”放在比历史记忆更高的优先级。2.3 工具层设计参数即契约工具层的核心决策不是“API 怎么连”而是“契约怎么定”。工具函数就是 Agent 和外部世界之间的接口契约契约写得越严谨模型就越不容易越权或犯错。我实践下来一个工具有三点必须定义到窒息参数类型绝不能只写“string”或“integer”要把枚举值、格式、示例全写进去。模型不会被文档说服但会被 JSON Schema 逼着格式化。返回结构工具返回值必须统一成 JSON 结构至少包含 status成功失败、data业务数据、error错误信息三个字段。这样 Agent 和上层代码都能拿到稳定结构去判断下一步。错误码语义要区分“参数错误”和“业务错误”。参数错误返回提示模型“你传的参数有问题”业务错误返回提示模型“条件不满足但参数没错换条路走”。两者混淆的话模型会陷入相同的无效重试。另外工具列表不宜太多。模型在工具选择上的表现跟工具数量呈明显负相关工具一多就乱选。如果工具超过 15 个优先考虑分组二级路由。先让模型选“分类”再在分类内选“工具”。2.4 编排策略ReAct 还是 Plan-and-Execute这是七要素里“规划”和“执行”结合方式的选择也是工程新手最爱纠结的地方。我的结论很直接如果任务是“边看边走”的类型比如“根据搜索结果的不断调整查询语句”用 ReAct 循环如果任务是“流程基本确定”的类型比如“开发一个 Django 页面的完整流程”用 Plan-and-Execute。ReAct 的好处是灵活坏处是发散。模型每走一步都有一个决策机会这也意味着每一步都可能犯错且 token 消耗随步数线性上涨。生产场景我给 ReAct 套了两条硬约束最大步数设为一个固定值常见是 5-8 步超了强制进入“总结现状并求助”流程每一步只能调用一个核心工具避免模型同时操纵多个副作用操作。Plan-and-Execute 的好处是省钱可控坏处是“计划赶不上变化”。当外部 API 返回和假设不一致时计划必须支持“重新规划”这个动作。我的实现是执行过程中每 3 步检查一次计划命中率发现两步未按计划推进就把当前状态和原计划一起塞给模型重新生成新计划。2.5 上下文管理窗口再大也不够用我见过很多团队选模型时盯的是“256k 上下文”仿佛买了大房子就不用扫地了。实际一旦跑起来256k 一样分分钟被工具日志、网页抓取全文、历史对话填满。上下文管理不是选个窗口就完事而是必须安排“压缩策略”。我的分层策略是这样的原始缓冲区保留最近 10-20 条消息完整保留不压缩。摘要层超出原始缓冲区的历史消息定期用模型生成段摘要压缩成原来的 1/10。关键事实卡从摘要里再抽取几条“必须遵守的硬性事实”常驻在系统提示词里。压缩节奏要看任务长度。短任务可以不压缩长任务每 10 步压缩一次。压缩时要注意一个细节工具调用记录里的“数据值”可以压缩但“动作顺序”不要压太狠否则模型搞不清哪些敏感操作已经发生过。2.6 退出与迭代策略怎么停止 AgentAgent 不停下来再好的设计也会在成本和安全上翻车。我把退出条件分为四类任务完成工具结果全部满足验收标准且模型输出最终回答。安全边界连续 N 步没有取得实质进展或模型请求执行某个高风险动作但被拒绝。预算上限token 总消耗超过阈值强制停止并保留中间结果。人工确认等待用户点击“确认完成”才终止。其中最容易漏的是“重复无进展”检测。不只是限制总步数而是要判断两步之间工具返回是否有实质变化。我之前做一个网络爬虫 Agent它反复请求同一个页面每次页面内容不同比如广告位变了它就觉得有进展结果空转了 15 轮。后来我加了个“结果指纹”对主要返回内容做哈希连续三次哈希相同就直接停止。2.7 部署与监控Agent 上线之后的运维很多 Agent demo 能跑但一上线多用户同时使用就崩。原因就是只做了同步循环没考虑并发和持久化。生产环境我建议做成异步架构任务进来先落库状态机在后台 worker 里跑每一步的 token 消耗、耗时、工具结果都写日志。用户看到的实时进度就是从状态机里读出来的。监控指标上别看平均成功率这种大水漫灌的指标要看分层分布按任务类型的成功率定位哪个领域 Agent 最弱。按步骤画的 token 消耗找出哪几轮调用最费钱。按工具的失败率确认哪些外部依赖最不稳。日志还有一个额外用处把失败样本收集起来定期做“失败回放”对系统 Prompt 的修正提供依据。我每个月都会挑 5 个典型失败案例逐个重跑调试这比任何测试集都有效。3. 落地实操从零开始搭一个最小可用的 Agent 工程理论讲再多不如直接跑一个最小闭环。下面这套骨架是我常用的结构不依赖重型框架核心就是一个循环 一个工具注册表 一个记忆管理器。理解了它你再去看 LangGraph 这类框架时会发现所有概念都能映射到这几行代码里。3.1 工程骨架怎么搭我按三层组织代码入口层接收请求构造任务记录写入一个 task dict这里为了演示先不接数据库。循环层负责调用模型、解析响应、执行工具、维护消息列表。工具层维护工具注册表和每个工具的真实实现。消息列表是整个 Agent 的主线。系统提示词、用户提问、模型决策、工具结果全都按顺序堆在里面。注意工具结果的 role 必须是“tool”并且要带 tool_call_id才能正确关联模型发起的这次调用。3.2 工具注册与函数调用代码实战我先定义两个工具一个查天气一个做计算。这个例子足够说明工具注册的完整流程。import json import math # 工具定义给模型看的 schema TOOLS [ { type: function, function: { name: query_weather, description: 查询指定城市的天气。仅支持中国大陆主要城市其他城市返回不支持。, parameters: { type: object, properties: { city: {type: string, description: 城市名如北京、上海} }, required: [city] } } }, { type: function, function: { name: calc_square_root, description: 计算一个非负实数的平方根。, parameters: { type: object, properties: { value: {type: number, description: 非负实数} }, required: [value] } } } ] # 工具注册表给代码执行用的映射 TOOL_REGISTRY {} TOOL_REGISTRY.register(query_weather) def query_weather(city: str): # 实际项目里替换成真实天气 API return json.dumps({status: ok, data: {city: city, weather: 多云, temp: 25}, error: None}, ensure_asciiFalse) TOOL_REGISTRY.register(calc_square_root) def calc_square_root(value: float): if value 0: return json.dumps({status: failed, data: None, error: 输入值必须是非负实数}, ensure_asciiFalse) return json.dumps({status: ok, data: {result: math.sqrt(value)}, error: None}, ensure_asciiFalse)这里我展示了三种返回值形态成功返回固定的 JSON、参数错误返回错误提示。记得所有工具都返回字符串因为模型 API 要求消息内容是字符串。返回时最好把中文用 ensure_asciiFalse 保持可读便于排查日志。3.3 带反思的循环核心调度代码接下来是最关键的循环层。这里我用一个不依赖具体厂商的抽象函数call_model()代指模型接口实际使用时替换为你的 SDK 调用。from typing import Optional SYSTEM_PROMPT 你是智能助手。你有权限调用以下工具。请严格按照工具的 JSON Schema 传参。 当用户问题得到解决后直接给出最终中文回答不要再调用工具。 如果工具返回错误请根据错误信息修正参数并重试如果连续两次错误直接如实告诉用户当前遇到的问题。 def run_agent(user_query: str, max_steps: int 6) - str: messages [ {role: system, content: SYSTEM_PROMPT}, {role: user, content: user_query}, ] for step in range(max_steps): resp call_model(messagesmessages, toolsTOOLS, temperature0.2) message resp.choices[0].message # 情况一模型要求调用工具 if message.tool_calls: messages.append(message.model_dump()) for tool_call in message.tool_calls: fn_name tool_call.function.name try: fn_args json.loads(tool_call.function.arguments) except json.JSONDecodeError: fn_args {} tool_func TOOL_REGISTRY.get(fn_name) if tool_func is None: tool_result json.dumps({status: failed, data: None, error: f未知工具: {fn_name}}, ensure_asciiFalse) else: tool_result tool_func(**fn_args) messages.append({ role: tool, tool_call_id: tool_call.id, content: tool_result, }) continue # 进入下一轮模型调用 # 情况二模型直接给出最终回答 final_answer message.content return final_answer return 已达最大步数限制任务未能完成。请提供更多上下文或拆分任务后重试。这个循环里三个细节是关键。一是temperature0.2而不是默认 1.0Agent 的每一步决策都应该是低随机性的太高会让模型偶尔不选正确工具而是自己凭空编。二是工具执行结果不管成功失败都以结构化字符串写回消息列表模型才能看到完整上下文。三是continue的使用工具调用后必须回到模型层再决策一次而不是直接往下 return。如果你想把框架换成 LangGraph逻辑也是一样的只是把 for 循环换成节点函数把 messages 换成显式的 state 对象。理解这段原生代码再去看框架文档你会轻松得多。3.4 参数计算与调优为什么 temperature 不能设太高在工程里每一个参数都应该有依据而不是随手填。我说几个最常见的参数及其量化逻辑。最大步数max_steps先统计 50 条历史任务的平均步数比如均值为 4P90 是 6。那么设 6 最为合理设 3 会频繁砍任务设 10 会放大 token 消耗。我的建议是P90 1既保留余量又不放任空转。每次调用的 token 量评估工具 schema 大概占 500-800 token系统提示词 300 左右历史消息是变量。假设平均每轮完整输入是 2500 token输出是 300 token那么 6 步会话的总消耗约为 6 × 2800 16800 token。如果模型 API 价格是每千输入 token 0.002 元、每千输出 0.008 元单次会话成本约 0.002 × 6 × 2500 / 1000 0.008 × 6 × 300 / 1000 0.03 0.0144 0.0444 元。日活一万每用户三次会话日成本约 1332 元。看到没有步数、上下文长度、并发量都直接决定账单这些数字必须在做记忆压缩和步数控制之前就算明白。对于工具返回体积我还习惯对所有工具结果做一个字数截断。超过 1500 字的返回直接截断并在末尾追加一句“以上内容已截断总字数 XXX如需完整数据请直接用 ID 请求”。因为模型对超长工具返回的理解效率会骤降截断反而提升准确率。4. 踩坑实录常见问题与排查技巧这一节是我最想写的因为每一行都是拿钱和时间换来的。我按症状整理了高频问题附带排查路径你可以当速查表用。症状可能原因排查与解决模型始终不调用工具直接口述答案System Prompt 没约束“必须用工具”或工具描述太差在 prompt 里加“遇到问题先选工具不要凭记忆回答”检查工具 description 是否列了使用场景调用了错误工具工具 schema 之间的场景描述区分度低重写两个工具的 description必要时给每个工具加一个“不适用场景”字段工具参数总是传错格式JSON Schema 里没给示例、没限制枚举每个参数都加示例值和大模型可读的格式说明连续调用同一个工具但没有进展缺少重复检测对工具返回内容做哈希连续两次相同强制停止上下文在长任务中爆掉没有压缩策略或压缩不及时每 10 步启动摘要压缩把关键事实卡常驻模型说“我已经完成任务”但结果明显残缺验收标准不够明确把目标定义改成可量化指标增加一轮裁判模型做核对工具权限过宽模型执行了高风险操作工具封装时没有做权限校验每个写操作在执行前必须校验权限敏感操作改成人工审批并发量一大模型 API 返回大量限流错误触发 RPM 限制在调用层增加令牌桶限流异步批量任务要排队下面挑三个细分场景详细说。4.1 模型答非所问环境信息缺失有一次我做内部数据问答 Agent用户问“上周的销售额比目标差多少”。模型直接回答“根据公开信息无法获取贵公司销售数据”但它明明有查数工具。后来排查发现工具本身是好的问题是模型不知道“销售数据从哪个指标表查”它按常识以为这是无法获取的敏感数据。解决办法是在系统提示词里加上一句业务上下文“你有权访问公司内部数据所有‘销售额’相关查询都应调用 get_kpi_data 工具不要要求用户提供额外权限。”模型不是做不到是不知道边界。把“边界声明”写清楚比反复调 prompt 技巧都管用。4.2 Token 超限与上下文溢出压缩不是删数据Token 超限是长任务最容易出现的。但压缩时切忌只保留摘要把工具调用的“动作顺序”也压掉否则模型会在状态混乱时重复执行同一个删除动作。我的压缩模板固定保留三个区块已完成的动作列表一句话一条、当前待解决问题、重要的业务数值。其余对话过程可以随意压但这三块是硬信息任何情况下都不能丢。另一个容易被忽略的地方是工具的 schema 本身也占 token且系统每次调用都会重复发送。工具数量多时schema 开销会占输入 token 的大头。优化手段包括精简 description 长度、参数属性名缩短、去掉不必要的说明字段一个工具从 200 token 压到 120 完全可行。4.3 工具调用格式错误与重试策略原生 function calling 格式稳定性已经很高但偶尔还会出现参数解析异常。排查时先看原始返回是 arguments 字段就不是合法 JSON还是字段名与 schema 不符。如果是前者可以用一个“安全解析”函数先 json.loads失败后尝试提取括号内的子串再 loads。重试策略要区分是否值得重试。参数错误值得重试因为它可能是模型一时格式错乱而业务错误比如“订单不存在”重试大概率还是不存在这时应把错误返回给模型让它换思路而不是换格式。我在工具返回里强制区分了 error 字段的来源类型循环层看到“typeparam_error”则允许最多重试一次看到“typebiz_error”则直接要求模型换路径。4.4 死循环与最大步数控制的经验值死循环是最让人头痛的问题有的是模型的问题也有的是我们代码逻辑漏了退出分支。除了总步数限制我强烈建议加两个机制信号量和熔断。信号量监控 Agent 是否有某个工具连续调用超过 N 次N 建议是 3。连续 N 次就在下一次模型输入里强制注入“你已经按照同样方式尝试了 N 次必须更换策略”。这比单纯终止更友好给了模型一次自救的机会。熔断如果出现某种错误模式的次数达到阈值比如 3 次同一工具报同一错误整个 Agent 直接进入“安全暂停”状态不再调用任何工具只生成一份失败报告。这保证了用户侧得到的是明确的故障信息而不是无意义的等待。4.5 记忆陈旧与检索不一致记忆系统上线后最隐蔽的 bug 是“明明存了新记忆但召回的还是旧信息”。比如用户后来改了偏好Agent 却沿用 30 天前的旧偏好去推荐。我排查后发现原因是向量库检索的得分差距很小旧记忆排到了前面。给时间衰减加权重后这个问题立刻好转。还有一个经验不要把所有信息都进记忆要分级。全局知识如领域规则放知识库用静态检索用户画像如偏好、历史订单放 KV 存储按用户 ID 精准读取会话过程日志不进向量库直接落 ES 或对象存储供审计和回放。多一套检索链路就多一层出错风险能简单绝不复杂。我自己的体会是Agent 工程走到后面真正难的已经不是“怎么让模型聪明”而是“怎么设计可靠的边界”。边界包括工具权限、步数限制、上下文压缩策略、退出条件、审计日志。这些边界没有一个是模型给你的全是工程代码里一行行垒出来的。如果你刚开始做 Agent可以先跑通最小循环然后立刻补上“最大步数、重复检测、工具参数校验”这三件事再去追求更聪明的模型或更复杂的记忆方案。这三件小事能拦住生产环境八九成的事故比任何花哨架构都值钱。