做 Agent 项目这一年多我最大的感受是2025 年Agent 早就不是“调模型聊天”的延伸品而是要把模型能力、工具边界、记忆策略、工程可靠性这几件事真正捏在一起。很多人一上来就想着“不就是一个 while 循环里让模型调用工具吗”但真跑起来就会发现提示词稍微一换、工具一多、场景一复杂循环就开始乱跳指令还没执行完就胡乱编结果上下文越聊越臃肿最后整个系统变成不可维护的黑盒。这篇文章不写概念科普直接讲 AI Agent 搭建全流程中用到的架构思路、组件取舍、实操步骤和踩坑记录。我自己是从“能回答问题”的机器人一路迭代到“能自己拆任务、调工具、做校验”的 Agent中途踩过不少坑下面的内容和做法都经过了实际业务场景的验证。不管你是刚准备起步还是已经在调 Agent 的稳定性这篇文章都值得照着走一遍。1. 从“能聊”到“能做”Agent 的整体架构思路拆解1.1 2025 年搭 Agent先分清楚你要做哪一种很多人一开口就说“我要做一个 Agent”但 Agent 和 Agent 之间的复杂度差着好几个量级。我一般把 Agent 分成三类先对号入座再动手省得设计过度或者设计不足。第一类是单轮工具型 Agent。输入一句话直接映射到一个工具调用不需要多步推理。比如“帮我查一下订单号 10086 的物流状态”本质上就是意图识别加参数抽取然后调接口。这类 Agent 的工程重心在意图识别准确率和参数抽取的稳定性上模型用的逻辑并不复杂。第二类是ReAct 循环型 Agent。模型需要观察当前状态、思考下一步动作、调用工具、读取结果再决定是否继续。这类是 2025 年最常见的生产级形态典型的“拆任务 调工具 校验结果”都在这层实现。它的难点在于循环怎么控制、上下文怎么管理、工具多了怎么不让模型选错。第三类是多 Agent 协作型。把一个大任务拆成多个子任务分别交给专门的小 Agent 去做再由一个调度 Agent 汇总。这个形态听起来性感但工程复杂度呈指数上升消息通信、任务同步、结果冲突处理都是额外成本。我见过不少团队一上来就搞多 Agent结果一半时间在调 Agent 之间的“吵架”问题。我的建议很直接先做第二类把 ReAct 循环跑稳再决定要不要演进到第三类。大部分业务场景一个配置良好的单 Agent 循环就能覆盖没必要为了架构上的“先进”给自己挖坑。1.2 核心决策工具、记忆与循环边界不管哪类 Agent有三样东西是躲不掉的工具、记忆、循环。这三样恰恰决定了 Agent 的上限和下限。工具是 Agent 的“手”。你给的每个工具都像一次接口授权模型只能在你圈定的范围内干活。工具不是越多越好而是越“边界清晰”越好。一个“通用数据库查询”工具会让模型产生无限遐想而一个“根据客户ID查询订单列表”的工具则会让模型显得专业且克制。记忆是 Agent 的“工作台和档案柜”。短期记忆对应对话上下文中期记忆对应会话摘要长期记忆则用来沉淀用户偏好、业务规则、历史决策。很多 Agent 表现差不是模型不行而是记忆层级没设计好该长期记住的被挤出了上下文该短期用的却被当成知识点写进长期库。循环是 Agent 的“决策节奏”。模型不是神它每一步的自信程度是不同的。循环里必须显式处理什么时候继续行动什么时候停下来向用户确认什么时候宣布任务失败。我见过太多 Agent 死循环在同一个工具调用上原因就是循环边界没设好——没有最大步数没有重复动作检测没有中途确认机制。这一节的核心就一句话架构设计不是为了“显得智能”而是为了“可控制地完成业务目标”。后面每一步实操都围绕这句话展开。2. 核心组件的选型逻辑与落地取舍2.1 LLM 调用层上下文管理是第一个门槛Agent 跑起来后最直接的工程问题就是上下文爆炸。一个 ReAct 循环每走一步至少要新增一条思考记录、一条工具调用记录、一条工具返回结果。工具返回结果如果是个十几 KB 的 JSON几轮下来 token 消耗直接起飞模型注意力也会被无关字段带偏。我常用的做法是把上下文划分成几个固定区域按优先级入场。系统提示词只讲角色、规则、输出格式任务描述放用户最新目标工具列表只挂当前场景需要的子集对话历史则用滚动窗口加摘要的方式裁剪。每次循环前我会做一个上下文裁剪动作把超长的工具返回截断成“前 200 字摘要 关键字段提取 结束标记”把超过 N 轮的历史压缩成一段结构化摘要。这块的取舍逻辑是宁可让模型少看到一点历史细节也不要让它淹没在无关信息里。上下文不是越长越好模型在长上下文里的中后段注意力会明显衰减这是实测下来非常稳定的现象。还有一个容易忽略的点系统提示词不要参与滚动裁剪。有些实现为了省 token把系统提示词连同历史一起压缩或丢弃结果模型输出格式随机漂移工具参数频繁漏字段。系统提示词应该始终在场它才是 Agent 行为稳定性的压舱石。2.2 工具注册与调用协议让 Agent 真正动手工具接入层我建议严格采用“函数签名 参数校验 结果归一化”三段式设计。函数签名告诉模型这个工具是干什么的、有哪些参数、参数类型是什么参数校验发生在模型输出之后、真实调用之前宁可拒绝调用也不要传错参数结果归一化则是把工具返回的各种格式统一成模型容易消费的文本形态。工具描述文本在这里非常关键。描述写得好不好直接决定模型能不能在多个工具之间做出正确路由。我给团队定的规则是每个工具必须写清楚“适用场景、边界条件、典型输入输出示例”示例比抽象描述有用得多。实测下来带一个输入输出示例的工具被模型正确调用的概率比只写文字描述的高出不少。工具调用过程中还有一个安全死角模型可能把真实参数和指令文本混在一起。比如一个工具参数是“备注信息”如果备注里带有看似指令的内容模型有可能把它当成新的执行意图。解决方案很简单工具参数一律按结构化数据传递禁止把自由文本无缝拼接进指令型字段对敏感操作发送消息、删除数据、修改配置必须有额外确认参数并且参数值由系统注入而非模型自由生成。2.3 记忆系统短期工作区与长期仓库的分工记忆系统的设计很多教程讲得都很玄乎但我总结下来就三个词分层、衰减、可回滚。分层是指记忆分三级运行第一级是当前会话的短期工作区存放本轮循环的中间结果任务结束就清空第二级是会话摘要把已经聊过的内容压缩成结构化要点用于多轮交互第三级是长期仓库存用户偏好、业务实体、历史决策检索时机由业务触发条件决定。衰减是指记忆要设“保质期”。我碰到过很尴尬的情况Agent 把三个月前的一个临时偏好当成当下指令来执行。后来我强制要求长期记忆写入时必须带上时间和置信度字段检索时按时间和场景权重做排序近期且高置信的记忆优先进入上下文。可回滚是指记忆系统的改动要留痕。长期记忆里如果写入了错误结论Agent 会在后续会话中反复被误导。所以在记忆写入接口上要设计版本字段和来源标识当执行结果与记忆冲突时系统能定位到具体是哪条记忆导致的行为偏移。3. 实操过程从零跑通一个可用的 Agent3.1 最小闭环手写一个 ReAct 循环先别急着上重型框架我建议每个人都手写一个最小的 ReAct 循环把原理跑通再谈别的。下面的伪代码是我在生产项目里用过的简化版去掉业务细节后长这样# 简化示例一个最小可用的 Agent 循环 def run_agent(task, tools, max_steps8): messages [ {role: system, content: build_system_prompt(tools)}, {role: user, content: task}, ] step 0 while step max_steps: response llm.chat(messages) if response.has_final_answer(): return validate_and_format(response.final_answer) action response.action # 期望模型输出结构化动作 if not validate_action(action, tools): messages.append({role: user, content: 动作不合法请重新选择工具}) step 1 continue tool_result call_tool(action) messages.append({role: tool, content: truncate(tool_result, 800)}) step 1 return {status: max_steps_exceeded, partial_result: messages[-1][content]}这段代码看起来很简略但它已经把 ReAct 循环的核心要素都包含了系统提示词构建、动作校验、工具调用、结果截断、最大步数限制。我刻意没让模型输出“自然语言思考过程”再解析而是直接要求它输出结构化动作原因是自然语言解析在生产环境里太容易出意外了。循环里的每一步都值得注意。比如validate_action这一步很多人忽略结果模型输出一个不存在的工具名代码直接抛异常崩溃。校验层应该检查工具名是否存在、参数是否齐全、参数类型是否匹配全部通过才允许执行真实调用。3.2 关键参数与提示词模板系统提示词是整个 Agent 行为的根基。我常用的模板结构是角色定义、工具使用规则、输出格式、安全边界、终止条件。下面是一个可以套用的模板框架你是业务助手 Agent负责完成用户指派的任务。 工具使用规则 1. 只能调用下面列出的工具禁止猜测不存在的工具。 2. 调用工具前先输出你选择该工具的原因用 reason: 开头。 3. 参数必须完整缺少必填参数时向用户提问补充。 4. 工具返回结果不满足要求时最多重试两次然后汇报失败。 输出格式 严格输出 JSON字段为 {thought: 你的思考, action: 工具名或none, params: {...}, final: 最终答案或空} 安全边界 - 不得执行任何删除类操作。 - 涉及金额、合同、个人信息确认时必须先向用户复核。 终止条件 - 任务完成时输出 final 字段。 - 无法完成时明确说明原因禁止编造结果。这个模板看起来简单但每一行都是踩坑后的产物。比如“禁止猜测不存在的工具”就是防模型幻觉调用的重试次数限制是防死循环的“涉及金额必须先复核”则是业务安全兜底。提示词里最容易被忽视的是输出格式的稳定性。我用 JSON 输出协议的原因很简单解析稳定、字段可扩展、方便后续做轨迹评估。不少团队喜欢用自然语言让模型“自由发挥”但自由发挥意味着不可控。我的经验是Agent 场景下宁可牺牲一点表达多样性也要换取行为可预测性。3.3 加入 RAG 与检索增强Agent 跑通基础循环之后第二个大需求一般是“让 Agent 能回答私有知识问题”。这一步就需要接入 RAG 或者检索工具了。我的做法不是把 RAG 作为一个前置模块把文档塞进上下文而是把“检索”本身做成一个工具暴露给 Agent。也就是说Agent 在主循环里自主决定“当前这个问题需要查知识库”然后调用search_knowledge_base(query)工具再基于检索结果组织答案。这样做有三个好处。第一上下文按需加载不会一上来就把所有文档都塞进窗口第二Agent 可以多次检索、修正关键词、对比不同文档片段行为更接近真人查资料的过程第三检索工具的返回结果同样走上一节说的结果截断逻辑不会造成上下文爆炸。检索实现上我一般是“向量召回 关键词召回 重排”的经典三段式。向量召回负责语义相似关键词召回负责命中精确实体重排则把两路结果合并后按相关度重新排序。实际部署时这套组合比单纯向量检索稳定得多尤其在处理专有名词、编号、产品型号这类文本时关键词召回几乎不可或缺。别把 RAG 想得太复杂。对于中小规模知识库一个retrieve_top_k(query, k5)的工具函数就够用了真正的难点反而在文档切片策略上。我建议切片时保留段落标题和上下文锚点这样检索出来的每一段都能追溯来源Agent 引用时不容易张冠李戴。4. 评测、调试与失败模式排查4.1 评测集怎么建才算有效Agent 的评测比传统模型评测麻烦得多因为答案不是唯一的过程也不是线性的。我建评测集时会分成三个维度任务完成度、过程合规度、资源消耗度。任务完成度看最终结果对不对比如“查询订单 10086 的物流状态并返回预计到达时间”完成标准是答案里包含正确的物流状态和到达时间。过程合规度看执行路径是否合理比如有没有在知道订单号的情况下还去调用模糊搜索工具有没有绕过必要确认步骤。资源消耗度看步数和 token 消耗一个 Agent 用了 15 步才完成一个本应 3 步完成的任务即使结果对了也不合格。评测集规模不用贪大但覆盖面要全。我一般会准备三类样本一类是标准顺利场景验证主流程一类是边界场景比如缺少必要参数、工具返回空结果、用户中途改需求一类是恶意场景比如提示注入、要求 Agent 忽略系统指令、诱导执行危险操作。这三类样本各占三分之一跑一遍就能把 Agent 的短板照出来。4.2 可观测性每一轮决策都要留痕Agent 调试最大的痛点是“黑盒”。模型内部怎么想的你不知道它为什么选这个工具你不知道它在哪一步开始跑偏你也不知道。所以可观测性是 Agent 工程的刚需不是可选项。我的做法是给每一次完整任务分配一个request_id然后把循环里的每一步都落成结构化日志当前思考内容、选择的工具、传入参数、工具返回摘要、上下文 token 消耗、步数序号。日志统一收集到集中查询平台方便按request_id拉出整条执行轨迹。有了轨迹日志之后调试就变成了对时间线的复盘。比如用户反馈“Agent 回答错了”我先看轨迹发现它在第二步检索到的文档片段本身就不相关第三步却被这个不相关片段带偏了方向于是问题定位到检索质量而不是模型能力。这样的排查路径在没有轨迹日志的情况下几乎是不可完成的。4.3 常见失败模式速查与修复建议我在生产环境里最常碰到的几类故障基本可以列成一个固定的排查清单失败现象根因方向排查思路修复建议Agent 死循环反复调用同一工具循环终止条件缺失查看轨迹日志中工具名和参数是否重复增加重复动作检测连续 2 次相同动作强制终止并汇报工具调用参数总是缺字段提示词对参数说明不足检查错误日志中缺参的字段在工具描述中增加必填参数示例最终答案与工具结果不一致结果摘要截断过度对比原始工具返回与截断后内容调整截断策略保留关键字段增加结果校验提示Agent 开始“编造”不存在的工具工具列表路由混乱查看它生成的动作名强制系统提示词声明工具白名单动作校验层兜底拒绝上下文很快耗尽历史与工具结果未裁剪查看每步 messages 的 token 数增加滚动窗口和工具结果截断这些问题的共性根因有两个一个是循环控制不够严格另一个是上下文管理不够主动。所以我在工程上一直强调Agent 的可靠不是靠模型自觉而是靠系统结构兜底。所有可能出错的地方都要有显式的检查、重试、终止机制。5. 上线之后性能、成本与安全边界的取舍5.1 缓存与并发别让 Agent 的服务端先垮掉Agent 上线后第一个挑战不是模型不够聪明而是服务端扛不住。一个 Agent 任务可能包含多次模型调用每次模型调用都是几十上百毫秒的延迟如果用户并发上来后端接口被拖垮是分分钟的事。我的第一道防线是多级缓存。语义缓存加在模型调用层之前当用户输入与历史输入的语义相似度超过阈值时直接返回缓存结果不再触发完整 Agent 循环。实测下来这类缓存能挡住 20% 到 30% 的重复请求。工具调用结果也单独缓存比如“查订单状态”这类幂等查询短时间内重复查询没必要再打一次上游接口。RAG 检索结果同样可以按 query 做短期缓存这一步对降低上游服务压力的效果非常明显。第二道防线是并发控制与超时管理。我给每个 Agent 任务设置了整体超时时间和单步超时时间任何一个环节卡死都不至于拖死整个服务。同时用信号量或者队列限制同时进行的 Agent 任务数量超出上限的请求排队等待。这个在业务上叫削峰填谷用户体验上略有一点排队等待但总比接口直接超时强得多。5.2 权限隔离与敏感操作防护Agent 的能力边界一旦含糊安全隐患就会直接暴露。我见过一个很典型的案例某团队给 Agent 配了一个“通用数据修改工具”结果 Agent 在处理一个普通查询任务时因为把用户输入误解为修改指令直接改动了一条线上数据。这类问题的根因不是模型蠢而是工具的权限设计没有跟上 Agent 的能力范围。我的安全策略分成三层。第一层是工具权限分级查询类工具默认放开修改类工具要求二次确认删除类工具默认不配给 Agent必须走人工审批通道。第二层是数据隔离Agent 能访问的数据要严格按业务租户隔离工具函数内部校验用户上下文是否符合数据归属权限不能把“能查”和“能查所有人的数据”混为一谈。第三层是内容过滤对 Agent 生成的文本做敏感信息检测防止它在回答中泄露其他用户或内部系统的数据。风险偏好可以不同但有一条底线不能妥协涉及资金、隐私、删除的操作Agent 只能“提议”不能“执行”。这个原则在初期的架构设计阶段就要定下来后面改起来成本极高。5.3 降级策略Agent 不行的时候要有兜底方案Agent 模型服务总有抖动的时候。我的做法是给 Agent 套三层降级方案第一层当主模型响应超时或报错切换到备用的轻量模型先做部分简单任务第二层当完整 Agent 循环失败降级为“直接检索 固定模板回答”的模式保证用户至少能得到一个不完美但可用的答复第三层当所有模型都不可用时返回人工客服入口或明确的错误提示绝不空着不回应。实际过程中我还发现一个很值得做的点降级路径要记录触发原因。每次降级都带上是哪一步失败、失败原因是什么、切换到了什么降级策略。一段时间后统计降级触发分布能反过来指导优化优先级——哪个环节最脆弱就先补哪个环节的工程能力。Agent 系统的鲁棒性本质上是设计出来的不是模型能力带来的。把降级路径想清楚用户体验的底线就稳住了。Agent 搭建做到后期我最大的体感是把“技术兴奋”放下开始老老实实做工程定义清晰的工具边界、控制循环的每一步、记录每次决策轨迹、建好评测集兜住回归。2025 年做 Agent拼的不是谁的模型调用姿势更花哨而是谁能把系统做到可预测、可观测、可回滚。如果让我给一个最直接的落地建议那就是先手写一个最小循环跑明白再逐步叠加记忆、检索、安全、缓存这些工程组件每一步都配好轨迹日志再往前走。最后再分享一个小技巧每次 Agent 没有按预期执行时先不要急着改提示词先翻轨迹日志定位它是在哪一步开始跑偏的。你会发现绝大多数问题的根源都不在模型而在我们自己的系统设计上。