我们团队最近半年一直在搞一套 Agent 系统,从最开始拿着大模型 API 写几个 ReAct 示例的玩具原型,到后来真刀真枪接进业务线当生产工具用,中间踩了不少坑,也沉淀出一些自己的方法论。很多朋友问 Agent 到底怎么搭,网上资料大多是概念科普或者碎片化 demo,真正讲清楚"从零怎么想、怎么设计、怎么落地"的不多。这篇文章我就把这半年折腾下来的思路、理想形态和一次真实落地过程完整拆开讲,希望能给正在搞 Agent 或者准备入坑的人一点参考。
1. 内容整体设计与思路拆解
1.1 到底什么算一个合格的 Agent 系统
在聊搭建方法之前,得先把"什么是 Agent"这件事掰扯清楚。现在市面上对 Agent 的定义非常混乱,有些人把拿模型 API 套了个 system prompt 就叫 Agent,有些人把工作流编排也叫 Agent,还有一些人干脆把能调工具的都统称 Agent。我个人比较认同的一个判断标准是:Agent 的核心在于"自主决策 + 多步规划",它不是一个单次请求返回结果的接口,而是能够根据目标自己拆解步骤、自己调用工具获取信息、根据执行结果调整下一步动作的程序实体。
举个例子,如果你想做一个"自动写行业调研报告"的系统,简单封装的大模型调用是:用户说"帮我看看新能源行业",模型直接吐出一篇泛泛而谈的文章,没有数据支撑也没有最新动态。而一个合格的 Agent 系统应该是:先拆解任务(收集行业动态、找龙头企业数据、分析政策变化、生成报告大纲),然后逐步执行(调用搜索工具、爬取指定网站、读取结构化数据、将结果写入报告模板),每一步执行完检查结果是否符合预期,不符合就重新尝试或者换一个方案。
这个区别非常关键。它不是 1 和 0 的区别,而是一个"有没有闭环"的区别。没有闭环的只是一个 API 封装,有闭环的才算是一个系统。
我有一个比较朴素的判断方法:如果这个程序跑起来之后,中间任何一步都不需要人来干预,且能在面对失败时自己调整策略,那它就是一个 Agent。反过来,如果中途断了就要人工介入、换个输入就要改代码,那离"系统"还有距离。
1.2 从业务问题反推:Agent 不是银弹
很多人一听 Agent 就觉得啥都能做,这个认知误区得第一时间纠正。Agent 适合解决的是"目标明确、步骤可拆解、工具可调用"的问题,而不是"随便聊聊"的问题。
我见过一些团队拿 Agent 做开放域的聊天机器人,动不动就烧掉十几万的模型调用费用,效果还很一般。为什么?因为开放域对话本质上是"生成",不需要复杂的工具调用,Agent 的规划能力在这里完全发挥不出来,反而因为多了规划层导致响应变慢、成本变高。
在做整体设计之前,一定要先回答几个业务层面的问题:
- 用户的核心诉求是什么?是检索信息、操作流程、生成分析,还是对话陪伴?
- 这个任务是否需要多步决策?如果单次 prompt 就能解决,根本不需要 Agent。
- 可用的工具集有哪些?工具的质量决定了 Agent 能力的上限,Agent 本身只是"调度器"。
- 出错成本有多高?如果 Agent 答错会造成严重后果,就必须严格控制自主性,加审核环节。
我自己的经验是:技术选型永远排在业务问题拆解之后。你先把任务链条想清楚,哪个环节需要规划、哪个环节需要工具、哪个环节需要人工兜底,然后再去选框架、选模型、设计 prompt。顺序反了,后面全是坑。
1.3 三个关键设计原则
在我做过的几次 Agent 落地中,有三条原则是反复验证过的,几乎适用于所有场景。
第一,最小自主性原则。永远不要一上来就让 Agent 全自主执行所有步骤,而是从"每一步都确认"开始,逐步放权。这有点像是带新人:先让他做完每一步都跟你汇报,你认为没问题了,再让他并行处理多个步骤。Agent 的自主性越高,对底层模型质量和工具稳定性的要求也越高,这两个东西跟不上,全自主就是灾难。
第二,工具优先,模型兜底。Agent 的聪明程度其实取决于它能调用什么工具。模型负责的是"决定调用哪个工具、怎么传参、怎么解读结果",工具负责"把事真正做成"。你能给 Agent 接上精确的数据库查询接口,它就不需要靠记忆回答数据类问题;你能给它接上内部 API,它就能操作真实业务。所以设计 Agent 的时候,一半以上的精力都应该花在"如何把现有能力封装成干净的、可被模型调用的工具"上。
第三,失败是常态,设计时就把失败当成一等公民。Agent 在真实环境中的成功率不可能 100%,甚至 70% 就算不错了。关键在于:失败之后怎么办?是重试、换方案,还是上报人工?这个决策逻辑必须提前设计好,不能等跑挂了再想。
2. 核心细节解析与实操要点:理想形态长什么样
2.1 一个完整 Agent 系统的四层结构
基于我做过的项目和看过的一些优秀开源实现,我总结出一个比较理想的 Agent 系统分层结构。它不一定适合所有场景,但可以作为设计时的参考坐标系。
自下而上分别是:
第一层是工具层。包括搜索 API、数据库查询接口、内部业务系统 API、代码执行环境、文件读写模块等。这一层的核心要求是:每个工具的输入输出都有明确的 Schema 定义,并且有足够的容错能力。模型调用工具时经常会出现参数幻觉,比如把一个字符串传给了要求 int 的字段,工具层必须能给出清晰的报错信息,方便模型自己纠正。
第二层是记忆层。包括短期记忆(当前任务上下文)和长期记忆(跨会话的历史信息、用户偏好、领域知识)。短期记忆一般直接用上下文窗口管理,但要注意控制 token 消耗;长期记忆则需要向量数据库或者结构化存储,在需要的时候检索相关片段灌回上下文。
第三层是规划层。这是 Agent 区别于普通程序的核心。常见实现方式有:ReAct 模式的循环推理(思考-行动-观察)、任务分解(把目标拆成子任务再逐个解决)、以及带反思机制的规划(执行完一轮之后总结经验,下一轮避免同样的错误)。规划层的设计决定了 Agent 的"智商上限"。
第四层是执行与反馈层。负责把规划层产出的动作序列真正跑起来,收集执行结果并反馈给规划层。同时,执行过程中产生的异常与部分成功信息需要被结构化记录,不能简单丢给模型一段报错文本就完事。
我实际搭建的时候,是把这四层对应到代码模块里的:tools/ 放工具封装,memory/ 放记忆管理,planner/ 放规划逻辑,executor/ 放执行器。模块边界清晰,后面调试和维护都会轻松很多。
2.2 规划层的三种主流模式对比
规划层是大家最关心的,也是命名最混乱的部分。市面上讲 Agent 规划基本跑不出三种模式,我直接对比一下。
| 模式 | 核心理念 | 适用场景 | 优点 | 缺点 |
|---|---|---|---|---|
| ReAct 循环 | 思考-行动-观察交替进行,直到完成任务 | 工具调用为主的任务,比如问答、检索 | 简单直接、容易实现、对模型要求相对低 | 步骤多时容易跑偏,缺乏全局规划 |
| Plan-and-Execute | 先整体拆分任务成子任务列表,再逐步执行 | 复杂多步骤任务,比如报告生成、数据分析 | 有全局视野,步骤可控,方便人工介入 | 初期计划可能不准确,执行后需动态调整 |
| 反思与自我修正 | 每轮执行后总结错误经验,用于后续步骤 | 高质量要求场景,比如代码生成、写作 | 能持续改进,最终效果上限高 | 额外消耗 token,延迟增加 |
我自己做生产系统的时候,最常用的是 ReAct 和 Plan-and-Execute 的混合模式:先让模型基于整体目标做一个粗粒度的步骤规划,然后在执行每一步的时候用 ReAct 循环来动态决策具体动作。这种混合设计兼顾了全局稳定性和局部灵活性。
还有一点想特别提醒:规划不是越细越好。步骤拆得太细,会导致每一小步都要调用一次模型,延迟叠加、成本翻倍,而且每一步都有出错概率,链条越长,整体失败率越高。我见过有人把一个简单问答拆成 8 个步骤,结果 6 个步骤都在做无用功。合理的做法是"能一步做完的不拆两步,必须拆开控制的才拆"。
2.3 记忆管理:理想形态中的隐形难点
记忆是 Agent 系统里最容易被低估的部分。很多初版 demo 不做记忆,模型每次都是"全新开始",在单轮任务里问题不大,但只要涉及多轮交互或者需要复用历史信息,系统智商立刻归零。
短期记忆的痛点在于上下文管理。每一步工具调用的输入输出都会占用 token,如果全塞进上下文,几轮之后就把窗口撑爆了。我常用的手段包括:用摘要压缩历史对话、只保留与当前目标相关的关键片段、把工具返回的原始大文本存到外部存储而不是塞回上下文。
长期记忆则需要解决"存什么"和"怎么取"两个问题。存什么:不是所有交互都值得存,只存那些对未来有价值的信息,比如用户偏好、项目关键约束、已经验证过的结论。怎么取:靠向量检索相关度,但相关度不等于有用度,所以通常还需要一个 rerank 环节或者基于规则的过滤。我在实际项目里是把长期记忆按不同类型分表存储的,比如"用户画像表"、"项目事实表"、"历史决策表",查询的时候按类型分别检索再合并。
记忆这一块做得好不好,直接影响用户对 Agent 的"聪明感"。有时候你觉得某个 Agent 很懂你,其实不是它推理能力多强,而是它的记忆层把你之前的需求和偏好都调出来了。
2.4 工具调用:理想 Agent 的能力边界
我始终认为,Agent 的能力边界就是它工具集的边界。模型再聪明,没有好工具调用,也只能凭空想象;工具足够强,即使模型弱一些,也能干出漂亮活。
工具设计的核心要求是单一职责 + 参数明确。单一职责很好理解,一个工具就做一件事,不要搞一个大而全的函数。参数明确意味着:参数名要让模型一看就懂、参数类型要严格校验、必填可选要划分清楚。我在实践中还发现一个很有用的小技巧:在工具描述里不只是写"这个工具是干嘛的",还要写清楚"什么情况下用这个工具、什么情况下不要用",模型看到这种提示之后,工具选择的准确率会提升不少。
工具层的容错设计也很重要。工具调用过程中网络超时、API 限流、数据格式异常都是常态。我一般会在工具封装里做三层防护:第一层是参数合法性校验;第二层是异常捕获并转换成模型能看懂的友好报错;第三层是超时控制和重试机制。有了这三层,模型在大多数异常情况下都能自行恢复,不需要人工介入。
另外要提醒的是:给 Agent 接工具的时候,一定要有权限和审计的意识。Agent 操作的每个动作都要留有日志,尤其是涉及写操作、删除操作、费用操作的场景,必须有审批或限制阀值。这不仅是安全考虑,也是生产系统的基本素养。
3. 实操过程与核心环节实现:一次真实落地记录
3.1 项目背景:我们到底要解决什么问题
说一个我最近完整落地的项目(以下细节做了脱敏处理,不影响参考价值)。
某团队内部沉淀了大量技术文档、项目方案和复盘记录,分散在多个内部系统和文档平台里。团队成员每天要花大量时间翻文档找资料,很多同事反馈"知道有这个东西,但找不到在哪"。老板希望做一个内部知识问答助手,让员工用自然语言提问,系统自动从各个来源找到答案并给出引用出处。
听起来是个标准的 RAG 场景,但实际需求比标准 RAG 复杂得多:
- 文档分布在多个数据源,有的是结构化表格,有的是非结构化 Markdown,还有的是扫描件转出来的文本。
- 很多问题的答案不在单篇文档里,而是需要跨文档拼接。
- 有些问题需要调用内部 API 获取实时数据,比如"最近某项目的进度到哪了"。
- 用户期望答案有权威出处,不能只给一段不知来源的总结。
所以这个项目本质上是一个需要"检索-分析-调用-回答"链路的 Agent 系统,而不是单纯向量检索加生成。
3.2 技术选型:框架、模型、存储的取舍
技术选型我分三块来说:Agent 框架、模型、存储。
Agent 框架我选择了 Python 生态里的 LangChain。当时也对比了几种方案:直接用原生代码编排 LLM 调用、用 LangChain、用更重量级的框架。最终选 LangChain 的原因很实际:它对各种工具和模型提供商的适配最全,社区案例最多,出问题容易搜到答案。虽然 LangChain 的抽象层级多、调试起来偶尔让人抓狂,但对比自己从零写一套工具调用和记忆管理模块的开发成本,还是划算的。后来为了保持代码可控,核心的规划逻辑没有被框架牵着鼻子走,而是用框架做工具调用和数据连接的"翻译层",规划控制流放在自己的业务代码里。
模型方面,我用了"强模型做规划 + 快模型做执行"的组合策略。规划模型负责理解用户意图、拆解任务、决定下一步动作,需要强的推理能力;执行模型负责总结单个文档内容、生成回答文本,需要速度快、成本低。两个模型拆分之后,整体延迟降了约 30%,成本降了一半以上。
存储方面,文档向量化后放进向量数据库,同时保留了文档本身的关系型元数据(标题、作者、更新时间、来源系统),方便后续做过滤和权限控制。当时选向量库的时候比较看重两点:一是检索性能要能撑住百万级向量,二是要方便做元数据过滤。可以先用开源方案验证,后续数据量大了再迁移到云服务,成本和灵活性都兼顾了。
3.3 核心流程实现:从提问到回答的全链路
我直接贴一下这个 Agent 系统的核心流程实现,用的是伪代码加注释的方式,方便理解每一步在做什么。
# 主流程:用户提问 -> Agent 规划 -> 工具执行 -> 生成回答 def ask_agent(question: str, user_info: dict) -> str: # 1. 记忆召回:先从长期记忆里捞相关信息 memory_context = memory_retrieve(question, user_info) # 2. 规划:让规划模型产出执行计划 plan = planner_agent( question=question, memory_context=memory_context, available_tools=get_tool_schemas() ) # 3. 按计划执行,每个步骤都走 ReAct 循环 collected_evidence = [] for step in plan.steps: result = execute_step_with_react(step) collected_evidence.append(result) # 如果某一步失败,让规划模型决定是重试还是调整计划 if not result.success: plan = planner_agent( question=question, previous_steps=collected_evidence, error=result.error, mode="repair" ) # 4. 生成最终回答,要求带上引用来源 answer = generator_agent( question=question, evidence=collected_evidence, regulations=["答案必须基于证据,不可臆造", "每个关键结论标注来源索引"] ) return answer这个流程看起来简单,核心逻辑其实都在函数内部。我重点讲两个关键模块的实现细节。
第一个是execute_step_with_react,它本质上是"思考 -> 调用工具 -> 观察结果"的循环,伪代码如下:
def execute_step_with_react(step) -> StepResult: max_iterations = 5 current_input = step.task for i in range(max_iterations): # 让模型决定下一步动作 action = model_act( task=current_input, tools=step.available_tools, past_attempts=step.history ) if action.type == "call_tool": tool_result = call_tool(action.tool_name, action.parameters) step.history.append((action, tool_result)) if tool_result.is_error: # 把错误信息反馈给模型,让它自己修正 current_input = f"你刚才的调用出错了,请根据错误修正。错误信息: {tool_result.error}" else: # 工具调用成功,看看是否足以完成当前步骤 current_input = f"工具已返回结果,如果足够解决问题就结束本步骤,否则继续调用其他工具。结果: {tool_result.content}" elif action.type == "finish": return StepResult(success=True, output=action.final_answer, history=step.history) return StepResult(success=False, error="超过最大循环次数", history=step.history)第二个是工具层封装。这里我特别想讲一下"如何让工具对模型友好"。光写一个函数签名是不够的,模型真的会"看不懂"。我最终采用的是比较啰嗦但很有效的工具描述方式:每个工具的 schema 里除了参数定义,还写了详细的注意事项和使用场景。
比如文档搜索工具的描述是这么写的:
工具名称: search_internal_docs 用途: 在内部文档库中搜索与关键词相关的文档,返回文档标题、摘要、来源链接 使用场景: - 用户提问涉及具体项目、技术方案、历史决策时使用 - 需要给回答补充内部资料引用时必用 - 不确定问题答案时优先使用此工具收集事实 不要使用此工具的情况: - 用户只是闲聊 - 问题答案需要实时业务数据(这种场景应该用 query_business_data) 参数: - keyword: 字符串,必填,搜索关键词,建议提取用户问题中的核心名词 - doc_type: 字符串,选填,限定文档类型,可选值 [project, tech, decision] - limit: 整数,选填,返回条数,默认 5,最大 20把这套描述喂给模型之后,工具选择的准确率提升非常明显。下图是我记录的一组对比(脱敏后的统计口径相同):
| 版本 | 工具选择准确率 | 回答引用覆盖率 | 用户满意率 |
|---|---|---|---|
| 只有函数签名 | 76.2% | 61.5% | 68.3% |
| 完整工具描述 | 92.8% | 88.4% | 86.7% |
同样的模型,只是换了一套工具描述,效果天差地别。所以你在做 Agent 的时候,如果感觉模型老是选错工具,先别急着换大模型,把工具定义好好打磨一遍,收益往往比换模型更大。
3.4 记忆与上下文的工程细节
这个系统上线之初有一个很头疼的问题:用户多轮提问之后,Agent 会把前面的上下文全部保留,导致 token 消耗越来越大,响应越来越慢。后来我做了一套两级记忆管理方案。
第一级是会话摘要。每当历史对话超过一定长度,就调用成本较低的模型把前面的关键信息压缩成一段摘要,替换掉原始对话。摘要里保留的信息包括:用户已经问过的问题、已有的结论、用户提到的偏好、尚未完成的遗留事项。
第二级是事实缓存。如果 Agent 在某个步骤中通过工具查询到了一个关键事实(比如"某项目当前处于联调阶段"),这个事实会被单独提取出来,和问题一起放入短期记忆结构体,后续步骤不需要重新查询。
class SessionMemory: def __init__(self, max_short_term_tokens: int = 4000): self.short_term = [] # 最近的对话和工具结果 self.summary = "" # 历史压缩摘要 self.max_short_term_tokens = max_short_term_tokens def add_exchange(self, user_question, assistant_parts): self.short_term.append({ "q": user_question, "a": assistant_parts, "key_facts": extract_key_facts(assistant_parts) }) self._maybe_compress() def _maybe_compress(self): total_tokens = estimate_tokens(self.short_term) if total_tokens > self.max_short_term_tokens: old_exchanges = self.short_term[:-2] # 保留最近两轮 compressed = summarize_exchange(old_exchanges) self.summary = merge_summary(self.summary, compressed) self.short_term = self.short_term[-2:] def build_context(self) -> str: context = f"历史摘要:{self.summary}\n" for item in self.short_term: context += f"用户:{item['q']}\n助手:{item['a']}\n" return context这套方案上线之后,长会话的 token 消耗减少了大概 40%,而且没出现"失忆"问题。如果对话特别长,可以再加一个定时任务把旧的会话摘要归档到长期记忆库,进一步释放空间。
3.5 工具链集成与异常处理实录
讲几个真实环境里遇到的工具链异常,这些是文档上不会写但极容易踩的坑。
第一个是内部系统的 API 限流。Agent 一旦跑起来,调用频率比人肉点击高得多。某内部系统的 API 单账号限频是每分钟 60 次,Agent 并行执行任务时两三分钟就把配额打满了。后来我做了三件事:给工具调用加上"频率控制器",每个工具的 QPS 单独限制;加了退避重试机制,遇到限流错误等几秒再试;在规划层提示模型"这个工具比较慢,尽量减少调用次数"。改完之后基本不会触发限流了。
第二个是文档解析的脏数据问题。很多内部文档导出之后格式乱七八糟,有的表格没表头、有的 PDF 扫描件文字顺序是乱的。如果不做清洗直接切块,向量检索结果质量会非常差。我们当时花了两周时间专门写一套文档清洗流水线:统一转成 Markdown 格式、修复表格结构、去除页眉页脚、识别乱码段落并标记为低质量文本。清洗之后,检索命中率上升了约 25 个百分点。
第三个是时间相关的问题。用户问"最近的版本发布了没",这个"最近"在不同语境下含义不同。处理办法是让 Agent 在调用工具之前先识别问题中的时间意图,如果没有明确时间就默认取"近 30 天"并在回答中说明。否则模型自己猜一个时间范围,容易答非所问。
3.6 回答质量的评估与调优闭环
Agent 系统的评估比传统搜索复杂,因为它是生成式的,每次输出都可能有差异。我建了一套三层评估机制:
第一层是自动化规则评估。检查回答是否引用了足够的证据来源、是否包含无法溯源的绝对断言、是否在答案中使用了"我不知道"来表达信息不足。这层成本最低,能拦下明显的问题。
第二层是模型评估。用一个评估专用模型(不参与回答生成的那个)对回答打分,维度包括:与证据的一致性、信息完整性、表达清晰度。这层可以批量跑,用几百个真实问题样例,每次修改完 prompt 或工具定义之后跑一遍,对比得分的变化。
第三层是真实用户反馈。在回答最后加一个"有用/没有用"的反馈按钮,收集用户的显式数据。上线一个月后,我们根据反馈数据发现一个有趣的规律:用户满意的回答往往都有一个共同特征——引用了具体的、可点击的文档链接。于是我们把"每个关键结论必须附带来源链接"从建议改成了强制要求,整体满意度又升了一截。
调优闭环的核心机制是把这三层反馈汇总起来,定期形成问题列表。比如某段时间发现"用户问项目进度时,Agent 老是给出文档里的旧信息,而不是调实时接口",那就说明意图识别规则需要调整。这种问题列表驱动的迭代比凭空猜效率高得多。
4. 常见问题与排查技巧实录
4.1 Agent 回答"看似合理实际错误"怎么破
这是 Agent 系统最危险的问题,因为它不像系统报错那样明显,用户轻易发现不了错误,久而久之就对系统失去信任。我们的经验是从三个方向同时堵。
第一,强制证据链。在 prompt 中明确要求"回答中每一个关键判断必须标注来源编号,没有来源支撑的内容不得写入回答"。生成阶段结束后,加一道校验程序,检查每个断言是否真的能在证据里找到出处,找不到就重写或删掉。这道程序性校验比其他任何 prompt 技巧都管用。
第二,低置信度触发拒答。设定一个置信度阈值,Agent 在生成阶段会给自己一个置信度评估。如果低于阈值,宁可回答"抱歉,我没有在内部文档中找到可靠答案,建议联系 XX 团队确认",也不要尝试猜测。刚开始我们担心拒答会降低用户体验,实际上用户对"诚实说不知道"的容忍度远高于"胡说八道"。
第三,关键数字二次确认。凡是涉及"日期、金额、人员、版本号"这类精确信息,Agent 必须通过工具查询获得,不允许从记忆中直接引用。这个规则用 prompt 约束并不完全可靠,我后来在工具层做了限制:记忆检索模块默认不包含精确数字类信息,必须走对应查询工具才能拿到。
4.2 大模型规划跑偏、循环不收敛
模型在复杂任务中容易出现"轴了"的情况,比如一直重复调用同一个工具、在一个子任务上圆圈打转。我们的处理办法是在规划层设置护栏。
第一个护栏是最大迭代次数和最大工具调用次数的硬限制。这两条是硬编码的,模型无论如何都突破不了。超过上限就强制结束当前步骤,向用户返回"该步骤未能完成"的提示。
第二个护栏是轨迹检查。每次循环之后检查历史动作序列,如果发现连续三次调用了同一个工具且参数没有明显变化,就判定为"重复循环",打断循环并要求模型换一种策略。
第三个护栏是分步确认。对于关键环节,比如"准备执行写操作"或"准备发送外部请求",Agent 必须先输出"计划执行什么操作、操作原因、预期结果",交给人确认后才真正执行。这个不仅防止跑偏,还解决了不少合规问题。
4.3 LangChain 与开源框架的调试心得
用 LangChain 这类框架时,最大的痛点是黑盒化——内部抽象太多,出了问题不知道卡在哪一层。我的做法是"框架只做翻译,不做大脑"。
具体来说,框架负责的事情有:标准化调用各家模型、把工具函数包装成模型能识别的 Schema、提供向量存储的连接器。而所有涉及 Agent 核心决策的环节,包括规划 prompt、执行循环、记忆增删策略,都写在自有代码里,不依赖框架的高级抽象。这样做的代价是少了一些现成的算子,但换来了对系统每一步的完全掌控。真出了问题,我可以单独测试每一个环节,定位时间缩短了数倍。
还有一个小技巧:把每一步模型的原始输入输出都记录下来,存成可分级的日志。调试 Agent 和调试普通程序最大的不同是:普通程序你查堆栈就行,Agent 你得"读心"。而模型做一个决策的原因往往就藏在它的输入上下文里——哪个工具描述吸引了它、哪个历史信息误导了它,翻日志才能找到根因。
4.4 常见问题速查表
我把调试期间遇到的高频问题整理成一个速查表,方便遇到类似情况时对照排查。
| 症状 | 可能原因 | 排查顺序 |
|---|---|---|
| Agent 选错工具 | 工具描述不清晰、相似工具过多 | 先检查工具描述是否有歧义,再检查工具数量是否过多 |
| 回答引用来源错误 | 检索阶段召回不准确 | 检查文档切块大小、向量检索 topK、是否缺少 rerank |
| 某一步骤反复失败 | 工具依赖的参数格式太复杂 | 简化参数,把复杂参数封装成对象或预设模板 |
| 对话稍长就"失忆" | 短期记忆被压缩太狠 | 提高摘要保留粒度,关键事实单独抽出来存 |
| 调用成本暴涨 | 规划步骤过碎、无意义的工具调用过多 | 调大步骤粒度,设置工具调用上限 |
| 回答风格不稳定 | 生成 prompt 约束不够 | 增加风格约束、固定回答模板、加 few-shot 示例 |
| 系统响应太慢 | 串行调用模型太多 | 将无依赖的工具调用改为并行,模型切换为更快版本 |
4.5 避坑清单:一些用真金白银换来的经验
最后分享几条不按套路出牌的经验,每一条都是踩过坑之后才总结出来的。
第一条,不要在 prompt 里写"你要一步步思考"。这句话对一个模型可能有用,但对生产 Agent 来说,它会导致模型把每个细节都拆成天书般的步骤,token 花了很多,效果却没提升。更好的做法是明确告诉它目标、可用资源、约束条件,让它自由发挥。
第二条,工具命名要像人话。工具名不是给程序看的,是给模型看的。get_data_v3这种名字模型理解不了,get_project_progress_by_name这种名字一看就懂。命名清晰,工具选择准确率会有一个明显的提升。
第三条,测试集要跟上迭代速度。每次改 prompt 或工具定义,都必须跑一遍回归测试集。我们有一个 200 条问题的固定测试集,每轮改动之后全部跑一遍,用模型打分对比前后差异。没有这个测试集,你会发现每次改动都是一次"盲改",效果像抽奖。
第四条,上线前一定要加"半自动"模式。不要一上来就完全无人值守,前两周运行期,Agent 的每个关键决策都输出给运维人员过目,攒一批真实运行日志,把高频失败的场景处理完之后,再逐步切换到自动模式。
结尾:一点个人体会
这套 Agent 系统从立项到稳定运行,前后花了将近四个月。如果让我重新做一次,我会把更多时间花在工具设计和数据清洗上,而不是焦虑地研究最新的模型推理技巧。Agent 的骨架是工程,血肉是工具和数据,模型只是驱动它们的动力源。
最后分享一个一直提醒自己的判断标准:一个 Agent 系统好不好,不是看它跑起来多酷,而是看它能不能在没人盯着的时候,默默把事情做好,出了问题也知道如何求助。能做到这一点,再朴素的技术栈都是好系统。