
做 Agent 的人应该都遇到过这个场景大模型单聊的时候有问必答一旦放进真实业务里就像一位没有手、没有笔记本、也没有图书馆借书卡的专家只能凭记忆里的那点东西硬撑。问它“帮我拉一下上季度的渠道数据”它要么道歉说自己没有访问权限要么一本正经地编一串数字。所谓 AI Agent本质上是把这些缺失的“器官”——大模型的推理能力、记忆系统、知识检索RAG、以及工具调用——组装起来让模型从“能说”变成“能做事”。这篇文章我不打算聊什么宏大概念也不会画那些看了更糊涂的架构图。我会把 Agent 架构里最常见的四个核心模块拆开讲大模型、记忆、RAG、工具调用它们各自负责什么、内部怎么做、互相之间怎么协同。内容以落地为主适合正在做 Agent 应用开发的工程师、准备把大模型接入业务的产品和技术负责人也适合那些对大模型有一定了解、想搞明白“Agent 到底是怎么组装起来”的爱好者。全程有实操思路、有参数参考、有踩坑记录看完你至少能徒手设计出一个可运行的 Agent 原型。1. 先想清楚Agent 到底在解决什么问题1.1 大模型是大脑Agent 才是完整的人很多人会把“大模型”和“Agent”混为一谈这是第一个思维误区。大模型本质上是一个概率生成器给它一段输入它预测下一段最合理的输出。它可以在对话里展示出惊人的理解力但这份理解力是“无根”的——没有状态不能记住昨天的对话没有外部知识源训练截止日之后的世界对它来说是一片空白没有执行能力它无法真的去查询数据库、调用接口或者操作软件。Agent 做的事情就是给这个“大脑”配上记忆、配上知识库、配上可以操作外部世界的工具。我习惯用一个类比大模型像一个只有大脑皮层的专家知识面广、推理能力强但他没有手、没有眼睛、也没有笔记本。Agent 是给这位专家装上了这些器官让他可以翻笔记本、查资料、动手执行任务。这样一来“脑”还是那个“脑”但能力边界从“能回答”扩展到“能完成”。这个区别决定了整个架构设计的方向你不是在选一个更强的模型而是在设计一套怎么让模型和其他组件协作的系统。系统设计的好坏往往比模型本身的智商更能决定 Agent 的上限。同一个模型配合合理的记忆和工具设计可以稳定完成复杂任务配合糟糕的编排连简单问题都会答得颠三倒四。1.2 四个模块各守一道防线Agent 的架构设计有一个非常朴素的思路每个模块解决一个明确的短板。用表格看会更清楚核心模块解决的问题实际类比大模型意图理解、推理、决策、内容生成大脑记忆跨会话状态、上下文维持、用户偏好笔记本RAG外部知识、私有文档、实时信息获取图书馆工具调用操作真实系统、获取结构化数据、执行动作手和脚这四个模块不是可选项而是彼此咬合的关系。没有记忆Agent 每次对话都是“失忆”的用户说“我刚才提过的条件你忘了”会直接尬住。没有 RAG私有领域的知识无从谈起模型只能靠训练数据里的过期内容硬猜。没有工具调用Agent 就永远停留在“嘴上分析”无法真正完成取数、发消息、改配置这类操作。而大模型作为中枢把其他三个模块串联起来像一个调度员随时判断当前任务该调动哪个模块、怎么解释模块返回的结果。所以设计 Agent 架构的第一步不是急着写代码而是把任务的链路画出来一个请求进来之后经过哪些判断、哪些模块、最终怎么收尾。这个链路清晰了后续的开发和排查都会轻松很多。2. 记忆模块怎么落地短期窗口、长期档案与记忆污染2.1 短期记忆上下文窗口的管理是门手艺大模型的上下文窗口就算再大也不可能无限装下所有历史对话。我在做项目时最常遇到的问题就是对话进行到第 30 轮把完整历史塞进 Prompt 里Token 消耗暴涨、响应变慢而且模型还会被早期无关的信息干扰。短期记忆的设计本质上就是对上下文窗口的资源管理。目前比较实用的是“滑动窗口 摘要压缩”的混合策略。滑动窗口很好理解只保留最近 N 轮对话原文比如 10 到 20 轮更早的内容不再进入 Prompt。但只做滑动窗口会丢失重要信息所以每隔几轮就把窗口外的历史用大模型压缩成一段摘要作为“记忆概述”保留下来。实际用的时候Prompt 里的结构大概是任务指令 历史摘要 最近对话原文 当前问题。这样一来模型既有长期脉络又有近期细节且 Token 开销可控。我踩过的一个坑是滑窗边界设置得太死。比如用户在第 25 轮说“我之前说的那个价格是指含税价”如果你只保留近 20 轮这个信息早就被挤出窗口了模型自然无法正确理解。后来的改进是在滑动窗口之外专门设置一个“关键约束区”只要对话中出现像“含税价”“截止时间”“排除项”这类的关键信息就单独抽出来长期保留。这本质上已经有点往长期记忆靠了但它很管用。2.2 长期记忆按主题归档的“Agent 档案库”长期记忆要解决的是跨会话的问题。用户今天说的偏好下次对话时最好还能记住。实现长期记忆的常见方案有两类向量化记忆和结构化记忆。向量化记忆的做法是把历史对话、用户偏好、任务结论等文本切成片段用 embedding 模型转成向量存进向量数据库。下次用户发起新问题时先把问题向量化在库里做相似度检索把相关的历史记忆捞出来放进 Prompt。这个方案的优点是灵活能处理没有固定结构的自然语言信息缺点是召回不准时容易把不相关的旧记忆带进来。结构化记忆则是用固定的数据格式记录关键信息比如用户偏好、实体关系、任务状态{ user_profile: { region: 华东, contact_channel: email, language: 中文 }, preferences: { report_style: 简版, data_granularity: 按周 }, task_state: { current_project: 某跨平台系统, next_deadline: 2025-06-30 } }结构化记忆的优点是精准、稳定缺点是需要预先定义好 schema无法覆盖所有场景。我现在的惯用做法是两者结合核心的偏好和状态用结构化 JSON 管理零散的信息和过程性内容走向量库。需要特别强调的是记忆的写入时机。不是所有对话内容都值得写入长期记忆。一次性的临时需求、随口一提的闲聊、带时效性的状态都不应该污染长期档案。我会在每次对话结束后让模型先做一个“记忆提取”判断这条对话里有没有值得长期保存的信息如果有再格式化写入。这个过程会多花一点时间和 Token但能避免长期记忆变成垃圾场。2.3 两个最容易踩的记忆坑第一个坑是长短记忆冲突。比如长期记忆里写着用户“偏好邮件通知”但这次会话里用户明确说了“不要发邮件直接打电话”。如果短期记忆和长期记忆同时塞进 Prompt模型就会犹豫甚至输出矛盾的内容。现在的处理办法是在 Prompt 中给记忆标出优先级明确告诉模型“近期对话中的明确指令优先于历史偏好记录”并且尽量让短期记忆里的新指令覆盖长期记忆中的旧配置。第二个坑是记忆膨胀和成本失控。向量库里的记忆如果不加管理会随着使用时间不断膨胀检索量变大、命中率下降、存储费用上升。我建议给每条长期记忆加上时间戳和访问频次定期清理过期的条目比如三个月未命中、且与当前任务无关的历史记忆可以归档或者删除。还要为记忆检索设置一个上限比如每次最多返回 5 条相关记忆避免无关信息挤占上下文空间。3. RAG 模块给 Agent 装上“实时知识库”3.1 RAG 的标准流水线先归档再召回RAGRetrieval-Augmented Generation检索增强生成是解决大模型“知识过期”和“私有知识缺失”的主流方案。它的核心思路很直接模型不知道答案没关系先让它从外部知识库里检索相关内容再基于检索结果来生成回答。离线阶段要做的是知识归档把文档清洗、去噪按语义切分成适宜检索的片段chunk然后逐段用 embedding 模型转成向量写入向量数据库。在线阶段则是收到用户问题 → 问题向量化 → 在向量库中召回最相似的 Top-K 个片段 → 将片段拼接进 Prompt → 模型基于片段生成回答。这里最容易影响效果的是 chunk 切分。chunk 太小信息被切碎一块完整的意思在多个片段里分散chunk 太大噪声增多检索精度下降。就我自己测试的经验通用文档用 300 到 500 字左右的 chunk 比较稳相邻 chunk 之间保留 10% 到 20% 的重叠能有效避免语义被切断。检索召回数量 top_k 我一般控制在 5 到 8 个再往上拉高命中的低质片段反而会稀释核心信息。embedding 模型的选型也有讲究。不要盲目追求大参数模型而是看你的语料是什么语言、什么领域。如果是中文业务文档优先选中文语料表现好的 embedding 模型如果是代码或英文技术文档可以调整选型。模型之间没有绝对的好坏最终都要用你自己领域的测试集跑一遍看召回效果。3.2 Agent 场景里 RAG 的三个特殊之处普通问答式 RAG 和 Agent 里的 RAG 是有区别的。普通 RAG 是一次性检索、一次性生成流程固定Agent 场景下检索往往是动态的、多轮的、甚至要跟工具调用联动。动态检索很好理解Agent 在完成任务的过程中会根据当前子任务临时发起检索而不是从一开始就把所有检索结果都查出来。比如 Agent 要先分析问题发现自己缺少某份背景资料才触发一次 RAG 检索。多跳检索则更复杂一些Agent 需要先查一轮得到一个中间结果再用这个结果作为线索去检索下一轮有点像查资料时顺着参考文献一路追下去。这种情况下每一轮检索的结果都应该被记录下来作为后续推理的依据。还有一个很容易被忽视的问题检索结果和工具返回结果发生冲突时该信谁比如 RAG 里的文档写着“某产品线 2025 年 Q2 销售额持平”但 BI 工具里拉出来的实时数据显示实际下降了 15%。如果不做处理模型就可能把两个互相矛盾的数据都写进答案或者随机采信一个。我的做法是在 Prompt 中给不同来源的信息打上“来源标签”比如 [知识库] 和 [实时工具]并设置优先级规则——实时工具数据优先作为事实依据知识库内容用于背景解释和原因分析冲突时要在回答里显式指出差异。3.3 检索不出好东西按这个顺序排查如果你的 RAG 总是召回一些不相关的内容别急着换 embedding 模型按下面这个顺序排查先看 chunk 切分是否合理。如果检索命中的片段语义不完整大概率是切分出了断层需要调整 chunk 大小和重叠率。再看用户问题的检索改写。用户很少会用精确的术语提问原始 query 直接拿去向量化效果经常不如改写后的 query。让大模型把用户问题改写成一个更规范的检索语句比如“华东区销量下降的原因”改写成“华东区 2025 年 Q2 销量下降原因分析”能明显提升召回质量。接下来检查召回数量和排序。如果 top_k 召回的结果里混着大量低相关片段可以尝试加一层 rerank 粗排模型先向量召回 30 个候选再用 cross-encoder 精排选出 5 个最相关的。最后还要确认 Prompt 里的指令是否明确。如果模型总是不按检索内容回答那就要在 Prompt 里写得强硬一点比如“在缺乏检索依据的情况下必须明确回答不知道禁止编造”。4. 工具调用模块让 Agent 真的能“动手”4.1 工具调用的本质模型只出意图执行器动手工具调用也叫 Function Calling经常被误解为“模型自己去执行函数”实际上模型并没有执行能力。它的本质是模型根据用户的意图从你提供的工具清单里选择一个工具并生成结构化的调用参数然后由外部代码真正去执行这个工具最后把执行结果传回给模型做下一步推理。一次完整的工具调用循环是这样的系统把可用工具列表名称、描述、参数定义注入 Prompt模型根据当前任务输出一个或几个工具调用指令格式为“工具名 参数字典”外部执行器解析指令做参数校验调用对应的函数或 API执行结果返回给模型有时候是结构化数据有时候是文本模型根据结果继续推理生成最终回答或发起下一次工具调用。工具定义的格式直接影响调用成功率。每个工具的描述要像“搜索词”一样精准让模型能快速判断什么时候该用这个工具。参数定义要列出名称、类型、是否必填、示例值例如{ name: query_sales_data, description: 按地区和日期范围查询销售数据适用于销售分析类问题, parameters: { type: object, properties: { region: {type: string, description: 地区如华东、华南}, start_date: {type: string, description: 起始日期格式 YYYY-MM-DD}, end_date: {type: string, description: 结束日期格式 YYYY-MM-DD} }, required: [region, start_date, end_date] } }描述写得模糊、参数定义不清晰是工具调用失败的头号原因。模型宁可选择不调用任何工具也不会去猜一个它看不懂的复杂工具。4.2 工具注册与路由别把所有工具塞进一个 Prompt很多做 Agent 的新手容易犯一个错误把自己系统里所有 API 都注册成工具一股脑塞给模型。工具数量一旦超过十几个模型的选择准确率就会明显下降还会出现答非所问、选错工具的情况。Prompt 窗口再大也不能这么挥霍。我建议分两层做工具路由。第一层是“意图分流”在模型之前先用一个轻量分类器或者规则把请求分为几个大类比如“数据分析类”“日程操作类”“知识查询类”然后只把对应类别下的工具列表注入 Prompt。第二层才是在这个精简后的工具集里让模型做精确选择。这样做的好处是既降低了模型的选择难度也能有效控制 Token 开销还能避免跨域工具之间的误用。还有一个细节工具的描述文本需要根据实际使用效果不断调整。模型对工具的理解完全来源于描述文字描述中如果加入了恰当的触发词和典型场景比如“适用于销售分析类问题”往往能把工具调用的准确率提高不少。4.3 工具层最常翻车的三个地方工具调用是最能体现“看着简单、用起来全是坑”的模块。第一个坑是参数幻觉。模型不是计算器它生成的参数有时会脱离实际比如恶意地编造一个不存在的用户 ID或者把一个日期参数生成成了非法格式。所以外部执行器必须做参数校验而不是直接拿模型给的参数去调接口。校验不通过时把错误信息返回给模型让它重新生成参数。第二个坑是工具结果过长。有些工具会返回几十条 JSON 记录直接塞回 Prompt 会占掉大块上下文还会干扰模型对重点信息的提取。常规做法是先压缩结果保留结构化摘要、只截取前 N 条记录、对主要字段做聚合统计。如果工具支持筛选条件甚至可以追加一轮调用让工具先按模型需求做聚合。第三个坑是安全边界。Agent 一旦有工具权限就意味着它能操作真实系统权限如果给得过大后果可能很难收拾。我在设计工具时坚持最小权限原则Agent 默认只允许查询类的只读工具对写入、删除、发送类操作要么单独二次授权要么通过人工审批流程。不要为了方便一股脑把管理员的全部权限都交给 Agent。5. 协同机制拆解一条任务的完整旅程5.1 从用户消息到最终回复的模块流转把四个模块装在一起协同的过程可以拆成一条清晰的链路。以一条典型的企业知识库问答请求为例整个流程是这样的用户提问 → 写入短期记忆 → 大模型解析意图 → 判断是否需要外部知识 → 触发 RAG 检索 → 判断是否需要实时数据 → 触发工具调用 → 汇总所有信息 → 大模型生成回答 → 更新长期记忆 → 输出结果每一步都有一个明确的“负责人”。意图解析和生成回答由大模型负责历史信息和用户偏好从记忆模块读取知识库相关内容来自 RAG实时操作和结构化数据来自工具调用。各个模块的数据都会汇聚到 Prompt 里由大模型统一处理和判断。这里有一个很容易忽略的环节信息的组织方式。当三种来源的信息同时出现在 Prompt 里时如果没有清晰的段落划分模型就会“串味”分不清哪个是历史偏好、哪个是知识库内容、哪个是实时数据。我在设计 Prompt 时会刻意用分组标签把所有外部信息隔开并在指令里声明每个来源的优先级和使用方式模型回答的稳定性就会上一个台阶。5.2 Agent Loop循环什么时候该停真正的 Agent 并不是“问一句答一句”的直线模型它往往要经历多轮“思考-行动-观察”的循环。我把这个循环简化为三步观察当前状态和已有信息决定下一步行动行动触发工具调用或检索观察结果判断是否还需要继续或者是否可以生成最终回答。这个循环需要设计终止条件否则容易出现无限循环。我的项目里一般设置了三道保险模型自身判断任务已完成输出最终答案达到最大迭代次数上限一般设置在 8 到 10 轮强制结束循环中出现重复的相同工具调用且没有新增信息触发中断。有一回测试一个自动定票的 Agent它在订票确认环节反复调用同一个查询接口原因是因为上一步工具返回里的某个字段它没解析对就一直重试。后来我在循环逻辑里加了“相同工具调用超过三次且参数相同就中止”的规则把问题解决了。这类异常在真实场景里非常常见建议你在设计循环时提前考虑。5.3 一个具体例子某销售数据分析 Agent用一个虚构的场景把上面的协同串起来。假设你在做一个“销售数据分析 Agent”用户问“帮我看看这个月华东区销量为什么大幅下降。”第一步大模型解析意图发现这是一个数据分析类请求。记忆模块的短期记忆记住了“当前对话客户想看华东区”长期记忆里调出了用户偏好“报告用简版格式”。第二步大模型认为缺少实时数据触发工具调用 query_sales_data参数是华东区、本月、环比上月。工具返回销售数据结果显示两大主力产品线下滑明显。第三步大模型不满足于这个现象想知道下滑的可能原因于是触发 RAG 检索从公司知识库里检索“华东区 产品线 销量下滑 原因”相关文档召回了两份资料一份提到渠道政策调整一份提到竞品低价策略。第四步模型把所有信息汇总短期记忆里的对话目标、长期记忆里的用户偏好、工具返回的实时数据、RAG 检索到的背景资料统一加工成一份简版分析报告并指出“实时数据表明下滑集中在产品线 A知识库资料显示同期渠道政策有调整建议进一步核查”。第五步系统把这次分析的关键结论写入长期记忆方便用户下次追问时能直接关联。这整个过程看起来并不复杂但每一环都依赖前面模块输出的质量。记忆不准、检索不到、工具参数错误任何一个环节出问题最终回答都会失真。6. 落地实操与排障手记6.1 框架选型从快速验证到深度定制做 Agent 落地第一个躲不开的问题是用编排框架还是自研。各种开源的 Agent 编排框架确实可以帮你快速搭出原型省去写底层流程的功夫。但用的时间长了你会发现框架带来的抽象有时也是一种负担框架封装好的 Agent Loop、工具调用方式、记忆策略不一定匹配你的场景调试起来反而更费劲。我的建议是分阶段走。在快速验证阶段依赖主流的编排框架和云平台的 Agent 服务尽快搭出闭环验证核心场景的可行性。等业务逻辑跑通、产品方向明确以后再考虑逐步替换先自研 Prompt 管理、再自研工具调用层、再自研记忆和检索策略。自研不是目的可控制性才是。大团队建议早点自研粘合层因为框架的更新节奏和你的业务深度绑定之后维护成本会越来越高小团队则建议先框架后迁移用最低成本验证价值。6.2 常见问题速查表我在实际项目里积累了一些高频问题和对应的排查思路整理成一张速查表方便大家遇到类似情况时直接对照故障现象排查思路参考方案RAG 检索经常返回无关内容检查 chunk 切分是否合理检查 query 是否需要改写检查 top_k 召回数量调整 chunk 大小和重叠率加 query 改写引入 rerank 精排工具调用频繁报参数错误查看模型生成的参数是否合法检查工具描述是否清晰外部执行器做参数校验优化工具描述和参数示例Agent 反复执行无用操作是否出现相同调用循环是否缺乏终止条件限制最大迭代次数增加重复调用中断规则回答前后矛盾短期和长期记忆冲突多个信息来源无优先级在 Prompt 中声明优先级新指令优先于历史偏好调用链路过长、Token 消耗高工具结果过大未压缩记忆检索范围过大工具结果摘要化记忆检索设置上限精简工具列表6.3 调试与观测没有日志就无法优化Agent 这种多模块协作的系统最怕的就是“黑盒”。用户给了一个指令系统内部做了七八个步骤最后输出一个答案如果中间任何一步出了问题你根本不知道是哪一步。所以我在做任何 Agent 项目时第一件事就是把结构化日志搭起来。每条请求分配一个 trace_id日志里记录每个关键事件用户输入、意图解析结果、记忆命中情况、RAG 召回片段、工具调用参数和返回结果、模型中间输出、最终回答。每个事件记录模块名、耗时和 Token 消耗。排查问题时只需要捞出一条 trace_id就能把整个执行链路完整还原出来问题出在哪个环节一目了然。大模型应用的调试方法和传统软件有一个很大的区别没有 debugger 可以逐步断点跟踪很多时候只能靠日志和中间产物来还原过程。这就更需要把“过程留痕”做到位。我还会用“影子模式”做灰度测试新版本 Agent 先只记录所有决策和工具调用过程不执行真实操作跑一段时间和旧版本做对比确认没问题后再切换线上流量。这种方式成本不高但能挡住大量潜在事故。踩过几次坑之后我的体会是Agent 不是把四个模块堆在一起就能跑的真正决定上限的是模块之间的接缝处理。记忆怎么写入、检索结果怎么排序、工具结果怎么压缩、冲突信息怎么仲裁这些细节在每个项目里都需要反复调优。你不需要一开始就追求完美架构先把最小闭环跑通再把日志铺好然后一个一个坑去填平。这套方法论比我见过的任何花哨设计都更可靠。