聊到 AI Agent,很多团队卡在工程实现上。我最近在帮几个团队做 Agent 落地,发现大家手里都有一个能跑通 Demo 的脚本,但真要把它变成“上线后不出乱子”的服务,往往会死在上下文管理、工具调用、记忆污染这些细节上。做 Agent 工程,我的经验是先抓住两件事:一是把 Agent 拆成七要素去理解,二是用七个决策点去约束实现。所谓七要素,是指目标设定、环境感知、规划拆解、记忆管理、工具调用、行动执行、反馈反思;所谓七个决策点,是你在工程化过程中绕不开的模型选型、编排架构、记忆策略、工具协议、上下文管理、任务规划、可观测性。这篇文章会把每个要素落到代码里的位置,把每个决策点背后的取舍讲清楚。适合正在做 AI 应用、被 Agent 稳定性折磨的开发者;如果你是刚入门,也可以把它当作 Agent 的学习路线,照着搭一个最小闭环。
1. 先拆清楚:七要素分别对应工程里的哪个模块
1.1 Agent 与普通接口调用的本质区别
很多人理解的 AI Agent 是“多轮对话 + 调用工具”,这在工程上还不够。普通接口调用是输入输出固定、错误可预期;Agent 则是一个可以自主决策的控制循环。它接收一个目标,自己决定下一步调用哪个工具、什么时候结束、如果失败如何修正。这个区别决定了你不能用写 CRUD 的思路来处理 Agent。
生活化类比一下:普通程序像自动售货机,你投币选货,它给你一瓶水;Agent 更像一个帮你跑腿办事的人,你说“去买菜准备晚餐”,他需要自己列清单、看冰箱库存、决定去哪个菜市场、买回来发现缺盐还会再跑一趟。这个人之所以能完成复杂任务,不是因为“嘴皮子厉害”,而是因为他有目标、有记忆、有工具、有反馈。Agent 工程实现要做的事,就是把这类能力拆成模块,然后串成一个闭环。
这个闭环的链条是:目标设定 -> 环境感知 -> 规划拆解 -> 记忆读写 -> 工具调用 -> 行动执行 -> 反馈反思。任何一环缺失,Agent 都只能算一个“会聊天的接口”。我在跟团队评审方案时,会先让他们把这个链条画出来,再谈技术选型,否则后面一定会陷入“模型很聪明但系统很呆”的尴尬。
1.2 七要素逐项拆解:每个要素都有明确的工程落点
按我的工程习惯,七要素最终都会落到代码里。下面这表格是我在方案评审时常用的对照:
| 七要素 | 工程落点 | 常见的坑 |
|---|---|---|
| 目标设定 | 系统提示词、任务状态定义、终止条件 | 目标太模糊,模型不知道什么时候算完成 |
| 环境感知 | 输入解析、业务数据接入、用户/环境上下文 | 只关注用户直接输入,忽略外部状态 |
| 规划拆解 | 规划 Prompt、任务状态机、子任务队列 | 让模型自由发挥,没有流程兜底 |
| 记忆管理 | 短期对话 Buffer、工作记忆、长期向量库 | 所有内容都塞进上下文,几千字后效果断崖 |
| 工具调用 | Function Schema、工具注册表、执行器 | Schema 描述不清,模型反复传错参数 |
| 行动执行 | 工具结果返回、外部 API 调用、落库 | 忽略执行异常,模型拿错误结果继续推理 |
| 反馈反思 | 结果校验、错误重试、事后评测 | 没有反馈闭环,错了也不知道 |
目标设定并不是写一句“你是一个助手”就完了。工程上要定义什么叫做“完成”。比如会议纪要助手,完成可能是“输出一份结构化纪要和 3 条待办”,而不是“聊到 10 轮后自动停止”。没有显式的终止条件,模型往往会多走好几轮,Token 消耗直接翻倍。所以我会在系统提示词里写清“当且仅当你已经输出最终结果时,才返回 finish”。
环境感知容易被忽视。Agent 拿到的不只是用户发来的一句话,还包括当前时间、用户身份、业务上下文、上次任务的残留状态。感知层是 Agent 的输入边界,要在进入主循环前统一封装,不要允许模型随意思考到边界外。比如会议纪要助手的感知应该是“用户原始转写文本 + 当前日期 + 参会人列表”,而不是让模型去猜时间。
规划拆解是七要素里的核心难点。真正可落地的 Agent 不会让模型一次性从目标跳到最终结果,而是把目标拆成若干步。工程上可以是一个TaskStateMachine,每一步记录pending / running / done / failed。模型负责生成步骤,状态机负责推进步骤,两者配合才不容易跑飞。记忆管理和工具调用这些点,后面七个决策点里我会展开讲。先记住一个原则:七要素不是并列的七八个功能点,它们是一条控制链。工程实现本质上就是把这条控制链稳定、可控、可观测地串起来。
2. 七个决策点:每个选择都是在花钱买确定性
七要素解决的是“Agent 需要哪些零件”,真正动手写代码时,你还会遇到一系列取舍。我称之为七个决策点。之所以强调“决策”,是因为每个选择都有明确的成本和收益,没有标准答案,只有适合场景的答案。这些决策点越早想清楚,后面返工越少。
2.1 模型选型:先定推理上限,再谈优化和降本
很多人选模型只看榜单分数,但要真正跑 Agent,至少要关注五个维度:指令遵循能力、函数调用稳定性、上下文长度、响应延迟、单次调用成本。其中函数调用稳定性可以这样测:准备 20 个典型工具调用场景,让模型输出符合你 schema 的结构化 JSON,统计一次解析成功的比例。我实测下来,不同模型在这项上的差距远大于在通用问答上的差距。
参数上可以做一个简单的计算。假设你的 Agent 每轮任务平均需要 8 次工具调用,模型 A 的函数调用成功率是 95%,模型 B 是 70%。表面看差距不大,但 8 次调用全部成功的概率:A 是 0.95 的 8 次方,大约 66%;B 是 0.7 的 8 次方,只有 5.7%。也就是说,B 在实际任务中几乎很难从头到尾不重试。这就是为什么选型不能只看“谁说话聪明”。
延迟也要单独测。Agent 是串行循环,一次任务要多次调用模型。如果单次响应要 4 秒,8 次调用就是 32 秒起步,用户早就没耐心了。我一般会在选型阶段做一张表,记录模型、单轮延迟、单次成本、20 个函数调用的成功率、支持的最大上下文。先定推理上限,后续的优化和降本才有抓手。
提示:不要在生产环境把模型切换当成纯配置项,至少留出一个影子环境做回归评测。模型升级或者降级,函数调用行为都会变,尤其要注意这类隐性变化。
2.2 编排架构:单链路、多 Agent、还是人机协同
第二个决策点是架构形态。目前主流的 Agent 架构无非三种:单 Agent、多 Agent、人机协同。单 Agent 就是“一个模型 + 一堆工具 + 一段循环”,简单直接,适合任务边界清晰、工具数量在 20 个以内的小型自动化场景。多 Agent 是把不同角色拆成多个模型实例,比如一个“规划者”只负责拆任务,一个“执行者”只负责调工具,一个“审查者”只负责结果校验。它的优点是并行、可以让每个 Agent 的指令更聚焦,代价是状态同步复杂、Token 消耗可能翻倍。
很多人一上来就想搭多 Agent 天团,结果链路一长,日志都看不懂。我的建议是:先跑通单 Agent,当出现以下信号再拆分:单个系统提示词超过 2000 字、工具超过 20 个、任务需要不同领域知识协作。否则单 Agent 的维护成本最低,也最容易排查问题。
人机协同也是被低估的选项。不是所有步骤都需要机器自动做,把高风险操作设计成“Agent 生成建议,人来点击确认”,反而能更快上线。决策点不是“Agent 能不能全自动”,而是“哪些环节能安全地自动化”。我做过一个自动发消息的项目,最终上线版本就是所有外发消息都走人工确认,这才敢让它在生产环境跑。
2.3 记忆策略:短期 Buffer、任务状态、长期知识库的分配
记忆是 Agent 工程最容易被做坏的一层。很多团队直接把所有历史对话塞进 messages 数组,最后 Token 爆掉。合理的记忆至少分三层:短期记忆是当前轮次的工作区,通常是一个messages数组,保留最近 4~8 轮对话,一定要有长度上限。工作记忆是正在执行的任务状态,比如“当前步骤 3 / 5,已完成文件上传,下一步等待确认”,以结构化字段存在内存或 Redis 中,而不是存在自然语言里。长期记忆才是外部存储,可以是一个向量数据库,也可以是一张摘要表。
长期记忆别盲目上向量库。如果你的场景是查“上次会议结论是什么”,用 SQLite 存一张摘要表,按日期和任务 ID 查询,几十毫秒返回,效果不比向量检索差。向量库适合语义召回,比如“找一份和这份方案类似的文档”,这时才需要 embedding。我在轻量项目里常用sqlite-vss,在重量级场景才接独立的向量服务。
记忆更新也要有纪律:不是每次工具结果都值得写进长期记忆,只有在任务完成或者出现明确结论时才写入。写入前做一次摘要,而不是把原始往返过程全部存下来。否则长期记忆会变成垃圾堆,查出来的东西根本不能用于决策。我给这个原则起名叫“记忆入库前先提炼”,它能让 Agent 在长时间运行后仍然保持稳定。
2.4 工具调用协议:Function Calling 的 Schema、校验与容错
工具层是 Agent 的“手”。最常见的失败来自 Function Calling 的 Schema 设计不严谨。比如参数描述模糊,模型把日期格式传成“明天”;或者没有定义枚举,模型自创状态值。工程上要做到:每个参数明确类型、必填、取值范围、示例值;描述里写清业务口径。拿日期字段来说,schema 里要写“格式 YYYY-MM-DD,例如 2025-01-20”,而不是只写“日期”。
工具注册表建议集中管理。一个工具就是一个函数,但入参和出参都要做一层封装。封装内部负责:记录调用时间、参数、结果、异常;捕获异常后把错误信息转换为模型能读的“观察文本”。例如工具超时,返回给模型的不应该是堆栈,而是“查询超时,请稍后重试或改用其他关键字”。模型看到的是业务提示,不是一堆英文报错。
我还会在工具执行前做一层参数校验,不直接信任模型的输出。明明category只能是“工作/生活/学习”,模型传了“其他”,那就立即返回校验失败,而不是让业务代码去处理脏数据。校验失败也算一次观察,模型看到后会重新生成。工具权限也很重要,涉及删除、发送消息这类操作,默认走人工确认,这比任何提示词约束都可靠。
2.5 上下文管理:Token 是硬约束,裁剪要讲策略
先解释一个热搜词:AI Agent token 是什么意思。Token 是模型处理文本的最小单位,可以粗略理解成一个“字”。Agent 每次调用模型都要把系统提示、历史、工具结果拼进去,长度直接影响成本和速度。上下文窗口是硬约束,超出后模型会截断,效果断崖。理解了这一点,再看上下文管理,本质上就是在有限的 Token 里塞进信息密度更高的内容。
上下文裁剪最忌讳简单“掐头去尾”。很多人的做法是只保留最后几轮消息,结果把系统提示和任务状态也丢了,模型越跑越迷。我一般把上下文拆成四个区:固定系统提示区,严格控制字数;任务状态区,放当前目标、已完成步骤、下一步计划;工具区,只放当前步骤可能用到的工具的 schema,而不是全部工具;历史区,用滑动窗口保留最近几轮,更早的内容先做摘要。
控制 Token 的另一个技巧是“结果摘要”。工具返回几百行数据,不要直接塞给模型,而是先调用一个摘要函数把结果压成两三句话。比如查询数据库返回 50 行,摘要成“共 50 条记录,其中 12 条状态为待办,最近一条是今天上午 X”。模型拿这个信息做决策完全够用。这个习惯能让同样的上下文窗口承载更长的任务链路,也是成本优化最有效的手段之一。
2.6 任务规划:状态机兜底,模型只做动态决策
Agent 要不要自己规划,是另一个决策点。全自由规划看起来很美好,但线上稳定性差。更稳的组合是“状态机兜底 + 模型做动态选择”。你要处理的任务流程如果是相对固定的,比如“查资料 -> 分析 -> 生成报告”,那就先用状态机把这三个阶段串起来。模型在每个阶段内部选择调用哪个工具、如何解读结果,而不是决定整个流程走向。
如果流程本身不确定,再考虑 ReAct 式的动态决策,也就是“思考 -> 行动 -> 观察”循环。ReAct 实现简单,把思考过程写进 prompt 即可,但它的问题是没有强制步骤,模型容易反复走回头路。Plan-and-Execute 是另一种选择,先让模型输出完整计划,再按计划执行,适合任务跨度大、需要提前看到全局的场景,但问题在于计划可能与现实不匹配,需要执行中重新规划。
工程落地时,我会在 Agent 代码里设置max_steps,比如 8 步,同时记录每一步的工具名和参数。一旦模型连续 3 步没有产生新信息,就强制停止并触发应急预案。任务规划不是让模型越自由越好,而是给它一个边界,在边界内做决策。这样既能保留模型灵活性,又能避免不可控的飘逸行为。
2.7 可观测性与评测:没有数据就不算工程
最后但绝对不可省的是可观测性。Agent 的行为是概率性的,必须有日志、追踪和评测。至少记录:每次模型调用的输入片段长度、输出、Token 数、耗时、工具名、参数、结果、异常、重试次数、进入 Agent 时的目标和退出时的状态。有了这些,问题排查才不是猜。我见过太多团队等到线上出问题才补日志,结果 Agent 复现不了,只能干瞪眼。
评测是另一件容易被省略的事。我给每个 Agent 准备一个回归集,不用太大,30~50 条典型输入就够。每做完一次改动,跑一遍回归。评测有两种:脚本断言适合有确定结果的,比如“调用了某个工具且参数正确”;LLM 裁判适合开放结果,比如“会议纪要是否包含结论”。我习惯双轨一起用,先脚本断言卡硬指标,再用 LLM 裁判评估质量。
可观测性和评测不是上线后才补的,而是写第一行 Agent 代码时就要一起写。后面第 4 章的问题清单里你会看到,很多诡异故障都是靠日志和评测集定位的。没有这些基础设施,你只能在“碰运气”和“猜”之间反复横跳。
3. 实操过程:用七要素加七个决策搭一个能用的 Agent 骨架
理论说多了容易飘,我拿一个最小但完整的例子走一遍。为了让思路不受语言限制,我先说结论:这套骨架不限定语言,如果你追求极致的并发性能,完全可以用 Rust 写 Agent runtime,很多高性能服务的实践都是这么做的。但大多数团队起点在 Python,所以下面代码我统一用 Python 表达,你迁移到其他语言只是语法差异,核心循环是一样的。
3.1 明确需求:做一个“会议纪要与任务跟进助手”
这个例子足够小,但能覆盖 Agent 的大部分工程问题。需求是:输入一段会议录音转写文本,Agent 要输出结构化会议纪要,并把其中的待办任务写入任务系统,下次用户问“我上周有哪些任务”时能从任务系统查出结果。
套到七要素上:目标是“完成纪要和任务入库”;感知是用户输入的转写文本和当前日期;规划是先摘要、再提取任务、再写入系统;记忆是保存最近几次会议摘要;工具是save_tasks和query_tasks;行动是实际写入和查询;反馈是写入成功后的校验。七要素全会用上,而且每一项都有明确的代码落点,不会为了演示而造概念。
3.2 Agent 主循环:最小的感知-决策-执行闭环
直接看代码。逻辑很简单:模型每轮输出一个结构化动作,finish表示完成,tool表示调用工具;工具结果再放回记忆,进入下一轮决策。
import json from dataclasses import dataclass, field from typing import Callable, Dict, List, Any @dataclass class AgentConfig: max_steps: int = 8 system_prompt: str = ( "你是一个会议助手。你需要根据用户输入输出会议纪要," "并使用工具保存/查询任务。每次回复必须是一个 JSON," '格式为{"type": "tool", "name": "...", "args": {...}}' '或{"type": "finish", "answer": "..."}' ) tools: Dict[str, Callable] = field(default_factory=dict) class MemoryBuffer: def __init__(self, size: int = 6): self.messages: List[Dict[str, str]] = [] self.size = size def add(self, message: Dict[str, str]) -> None: self.messages.append(message) if len(self.messages) > self.size: self.messages.pop(0) def snapshot(self) -> List[Dict[str, str]]: return list(self.messages) class Agent: def __init__(self, config: AgentConfig): self.config = config self.memory = MemoryBuffer() self.trace = [] def call_llm(self, messages: List[Dict[str, str]]) -> str: # 替换成真实模型调用,返回字符串 raise NotImplementedError def parse_action(self, text: str) -> Dict[str, Any]: # 容错解析:剥离 markdown 代码块后 json.loads cleaned = text.strip() if cleaned.startswith("```json"): cleaned = cleaned.split("\n", 1)[1].rsplit("```", 1)[0].strip() return json.loads(cleaned) def execute_tool(self, name: str, args: Dict[str, Any]) -> str: if name not in self.config.tools: return f"工具不存在: {name}" try: result = self.config.tools[name](**args) return str(result) except Exception as exc: return f"工具执行失败: {exc}" def run(self, user_input: str) -> Dict[str, Any]: self.memory.add({"role": "user", "content": user_input}) for step in range(self.config.max_steps): messages = [{"role": "system", "content": self.config.system_prompt}] + self.memory.snapshot() llm_text = self.call_llm(messages) self.memory.add({"role": "assistant", "content": llm_text}) self.trace.append({"step": step, "llm": llm_text}) action = self.parse_action(llm_text) if action["type"] == "finish": return {"status": "success", "answer": action["answer"], "trace": self.trace} if action["type"] == "tool": observation = self.execute_tool(action["name"], action["args"]) self.trace[-1]["observation"] = observation self.memory.add({"role": "tool", "content": observation}) else: return {"status": "failed", "error": "未知 action 类型", "trace": self.trace} return {"status": "failed", "error": f"超过最大步骤 {self.config.max_steps}", "trace": self.trace}这段代码短,但它包含了 Agent 主循环的完整链路:感知通过user_input进入 memory;决策由call_llm完成;工具执行在execute_tool里;工具结果作为 observation 回到 memory;循环直到模型输出finish。没有复杂架构,但足够开始调试。真实模型接入时,只需要把call_llm替换成具体 SDK,再在parse_action里做更健壮的解析即可。
3.3 工具注册和记忆落地:让 Agent 真正能“干活”
主循环只是为了跑通流程,真正让 Agent 有用的是工具层。我建议用装饰器把工具和 schema 绑在一起,集中管理。下面是一个简化示范:
tools_registry: Dict[str, Dict[str, Any]] = {} def register_tool(name: str, description: str, parameters: dict): def decorator(func: Callable): tools_registry[name] = {"func": func, "description": description, "parameters": parameters} return func return decorator @register_tool( name="save_tasks", description="把待办任务批量写入任务系统", parameters={ "type": "object", "properties": { "tasks": { "type": "array", "items": { "type": "object", "properties": { "title": {"type": "string", "description": "任务标题"}, "due_date": {"type": "string", "description": "截止日期,格式 YYYY-MM-DD"} }, "required": ["title", "due_date"] } } }, "required": ["tasks"] } ) def save_tasks(tasks: List[Dict[str, str]]) -> str: # 这里替换成数据库写入 return f"已保存 {len(tasks)} 条任务" @register_tool( name="query_tasks", description="按日期范围查询任务列表", parameters={ "type": "object", "properties": { "start_date": {"type": "string", "description": "开始日期"}, "end_date": {"type": "string", "description": "结束日期"} }, "required": ["start_date", "end_date"] } ) def query_tasks(start_date: str, end_date: str) -> str: # 替换成数据库查询 return "2024-01-01 ~ 2024-01-07 共 5 条任务,其中 2 条未完成"工具声明有两个关键点:description 要写业务口径,比如 due_date 是“截止日期”而不是“日期”;parameters 里每个字段都要有示例,因为模型是参考 schema 生成 JSON。如果 schema 写得含糊,模型给你传{"tasks": "买牛奶"}这种错误结构一点也不意外。生产环境里可以把这些 schema 自动拼进系统提示词,只放当前任务可能用到的部分,进一步省 Token。
记忆落地方面,用上面的MemoryBuffer当短期记忆足够跑 Demo。生产环境建议把 Buffer 升级成 Redis 按session_id隔离,再给每条消息打时间戳。长期记忆在轻量场景里用一张 MySQL 表保存会议摘要和结论,查询时先按会议 ID 精确匹配,不行再做全文检索。部署的时候用一个容器跑 Agent 服务,按session_id动态创建 Agent 实例,任务结束就释放,避免内存泄露。
3.4 稳定运行的三个细节:解析修复、失败重试、步骤上限
代码里parse_action目前只会剥一个代码块。实际模型可能输出中文引号、多余逗号、截断的 JSON。我的经验是做三级修复:先用正则把常见错误替换掉,比如中文冒号、中文逗号换成英文;再用括号配对检查,如果缺右括号就补上;最后实在解析不了,把“格式错误,请重新输出 JSON”作为消息返回给模型重试。因为 Agent 循环里有max_steps,重试次数天然受限,不会无限烧钱。
失败重试要区分模型层失败和工具层失败。模型层失败,比如返回非 JSON,走上面的修复重试。工具层失败,要先把异常转成业务错误信息。例如写入任务时数据库连接断了,返回给模型“连接失败,请稍后重试”比直接把 Traceback 丢给模型更有效。模型读不懂堆栈,它只需要知道下一步可以怎么做。
步骤上限要结合任务类型设置。我一般先设 8 步,跑一周看日志里的平均步数,再设置成平均值的 2 倍。步数设太小会把正常任务截断,设太大又会让死循环烧更多 Token。这个值不是拍脑袋定的,而是靠可观测性数据持续校准。等任务模式稳定下来,再对它做个性化调整。
4. 常见问题与排查技巧实录
4.1 工具调用死循环:同一个工具翻来覆去执行
这是我见过最多的故障。现象:Agent 反复调用同一个工具,参数还一样,模型每次都按同一个错误结论继续。原因有两类:一是工具返回的信息不足以让模型做出下一步判断,模型只能重复试;二是模型已经掉进了“重复 action”的惯性,缺乏外部约束。
排查顺序是看 trace 里每次工具返回的 observation。如果 observation 是“查询无结果”,那问题出在工具返回信息设计上,要改成返回“无结果,建议换个关键词或检查筛选条件”;如果 observation 每次都成功,但模型仍然重复调用,那要在 system prompt 里加一句“你已经调用过这个工具,如果结果没有变化,不要再次调用同一个工具”。代码层面还可以统计:同一工具连续调用超过 N 次,强制改走人工处理或直接结束。
4.2 JSON 解析失败:模型输出不是合法结构化数据
现象:模型返回了正常文字,但你的json.loads直接报错。原因很多:返回里带了 markdown 代码块、注释、中文标点,甚至把引号写成了弯引号。处理方法是三步:先提取代码块;再做 normalize 替换常见错误;最后如果还失败,就把错误信息拼进下一次模型调用的 user 消息,让它重新生成。
还有一类情况是模型在 JSON 里写了注释。JSON 标准不允许注释,所以你得在解析前把注释行删掉。更稳的做法是在 system prompt 里强调“不要加代码块、不要加注释、不要加任何解释,只输出 JSON”。即使这样,解析修复依然要有。不要把“模型一定会输出合法 JSON”当成假设,要当成一个需要兜底的环节。
4.3 上下文越长越“蠢”:历史信息污染决策
现象:Agent 跑了几十轮后,反而忘记早期目标,开始回答无关内容。原因往往是上下文中塞进了太多中间观察和旧摘要,模型注意力被稀释。解决思路是三层清理:对话历史只保留最近 4 轮;工具输出在写入 memory 前先压缩;系统提示和工具 schema 保持不变。
具体操作是:把每次工具返回按长度阈值判断,超过 200 字就先调用一次摘要,只把摘要放进上下文。旧对话中的关键结论可以抽到“任务状态区”,比如“已完成步骤:上传文件;当前步骤:等待确认”,不重要的寒暄直接丢。上下文管理的本质是把信息密度提升,而不是无脑扩窗口,扩到 100 万也一样会被无效信息填满。
4.4 并发状态串线:多个任务复用同一个实例
现象:A 用户的任务还没跑完,B 用户发起请求后,A 的工具调用参数里出现了 B 的数据。原因很明显:Agent 实例里的 memory 是共享的,没有按会话隔离。我在代码里强调过 MemoryBuffer 必须绑定 session_id。正确的做法是每次请求创建新的 Agent 实例,或者用一个 session_id 到 Agent 的字典管理,任务结束就释放实例。
另一个隐形问题是工具函数闭包里的全局变量。比如任务系统客户端是单例,但查询参数却写在全局变量里,并发一高就会串。工具函数应该只接收调用参数,所有业务对象都通过上下文注入,并且尽量做到无状态。这块在代码评审时要重点看,表面上单测都能过,并发压测才会暴露。
下面是一个排查速查表,我贴在团队文档里当第一级排查手册:
| 现象 | 大概率原因 | 建议处理 |
|---|---|---|
| 反复调用同一工具 | 工具观察信息不足 | 增强工具返回的业务提示 |
| 偶发 JSON 解析失败 | 模型输出格式不稳定 | 加解析修复和重试 |
| 上下文越长效果越差 | 历史数据过多 | 摘要 + 滑动窗口 |
| 并发任务数据串了 | Agent 实例共享 | 按 session_id 隔离 |
最后说一个我做 Agent 工程落地最大的体会:模型负责聪明,工程负责稳定。你不需要把每个 Agent 都做成全自动多智能体,只要把七要素接到代码、七个决策点落实到流程、日志和评测能跟上,一个能上线的 Agent 就已经跑起来了。再分享一个小技巧:每次上线前,我会故意往评测集里塞几条边界输入,比如空字符串、重复命令、超长文本,这些地方往往比正常路径更容易暴露问题。希望这篇文章能帮你少踩几个坑。