☰
Agent用户记忆与知识库搭建:从RAG检索到Dify流水线实战
2026/9/26 7:24:38 网站建设 项目流程

1. 这半个月我到底在补哪块短板

写这套AI Agent学习笔记之前,我先说说一个很现实的感受:跑通一个调用大模型的Agentdemo并不难,难的是让这个Agent在连续对话里像"有记性的人"一样工作。很多人一开始做Agent,重点全放在工具调用、提示词、工作流编排上,等到自己写个简单的助手机器人,用户昨天说过的偏好,今天再问一次,它又当全新对话处理,体验非常割裂。

所以第三篇笔记我专门把"用户记忆"和"知识库"放在一起梳理。这两个东西看起来是两块独立的功能,实际在Agent系统里它们经常要配合使用:记忆解决"我了解你"的问题,知识库解决"我知道这个世界/我们这个领域有某些事实"的问题。搞清楚两者边界,远比单独学会某个向量数据库的API重要。

这篇笔记适合的人群,我大概判断是这样几类:想把Agent真正做成可长期使用的产品的人、正在搭个人知识库或企业知识库并考虑跟Agent结合的人、以及已经把Agent框架跑通但卡在连续对话体验上的开发者。内容会有一部分原理拆解,也有一部分我的实操踩坑记录,尽量做到看完能直接往自己项目里迁移。

先明确一个前提:我这里提到的Agent,泛指以大语言模型(LLM)为核心、能完成多步任务和自主决策的智能体系统。像DeepSeek这样的大模型本身是"大脑引擎",而Agent则是在引擎之上组织记忆、工具、知识库和决策逻辑的整套应用架构。这个理解一定要先建立起来,否则后面讨论记忆和知识库的时候,很容易把"模型自身上下文"和"Agent外挂系统"混为一谈。

2. 用户记忆的三种存法:短期、长期、还有画像

2.1 短期记忆:会话上下文怎么管理和裁剪

短期记忆,说白了就是一次会话内Agent要记住的内容。这个最直观的形态就是"对话历史"。但很多人忽略的是:对话历史并不是越多越好,LLM的上下文窗口虽然越来越大,可塞进去的内容越多,响应延迟越高、注意力会被稀释、花在无效token上的成本也越高。

我自己的项目里对短期记忆管理用了三层策略:

  • 固定轮次裁剪:只保留最近N轮对话,比如6到10轮,超出部分的原始内容丢进长期记忆层,不在上下文里继续占地方。
  • 摘要压缩:如果对话确实很长且很多关键信息需要保留,就调用一次模型对早期对话做摘要,把摘要替原始对话塞进上下文。这相当于"人工制造一个更紧凑的记忆容器"。
  • 遗忘信号:用户明确表示"刚才说的不算"或"别按那个来",这种信号要识别出来,并主动对短期记忆做局部删除或覆盖,否则Agent会把矛盾的前置信息一起带进下一轮推理。

这些原则听起来简单,但真正实现的时候,你会发现最大的坑是"裁剪后还能不能正确引用早期信息"。如果只是简单砍掉前面的轮次,用户可能在第五轮问"我刚才说的那个文件的路径是什么",而路径在第二轮,已经被裁掉了。所以短期记忆不能只有裁剪,裁剪前一定要做关键信息提取,把文件的路径、用户的偏好、具体的数字参数这类硬信息,及时写入结构化字段或长期记忆层。

2.2 长期记忆:向量化存储与用户画像提取

长期记忆解决的场景是跨会话。用户这周来和下周来,Agent不应该当陌生人。实现思路现在比较成熟,核心是两步:提取+存储检索。

提取指的是从对话里挖出"值得长期记住的东西"。拿用户画像举例,可能包含姓名、称呼、所在城市、职业、常用时间偏好、语气偏好、禁忌话题等。如果你只用一个prompt让模型输出JSON,比如像下面这样:

{ "user_profile": { "name": "张三", "city": "上海", "preferred_time": "早上9点到下午3点", "communication_style": "简洁", "known_topics": ["Java开发", "AI Agent", "家庭收纳"] } }

然后每次对话结束后把提取到的画像字段做一次合并更新,存到单独的记录里,这就完成了"记忆写入"这一半。

另一半是"记忆召回"。召回有两种主流思路:一种是基于规则/结构化查询,比如直接读用户表的cached_profile字段;另一种是基于语义相似度,把所有历史记忆片段向量化后存进向量库,每次新对话来了,用当前用户的问题或状态向量去检索最相关的历史片段。两者可以结合:结构化的画像直接读,开放性的历史事件明细靠向量检索。

我自己实际项目的经验是,这里千万不能“全存全召回”。存得越多,召回时反而越容易抓住无关紧要的细节,导致Agent在回答一个"今天天气怎么样"的问题时,把用户三个月前说的"我不吃香菜"也当成重要信息给塞进提示词里,污染输出质量。

2.3 动态记忆的更新时机:什么时候写、什么时候改、什么时候删

记忆的更新时机比存储方式更容易被忽视,但恰恰是"像不像真人"的关键。

我不会在每轮对话结束后都无脑更新长期记忆。无脑写入会造成两个问题:一是噪音太多,后面召回时精确度断崖下跌;二是用户的偏好可能只是在某个特定上下文里成立,会被错误推广成全局特征。比如用户某天说"我今天赶时间,回答简短点",如果你把它写进全局用户画像"希望回答简短",以后用户在周末想深聊某技术方案时,Agent还在一味地压缩回答,体验就很糟。

我的更新策略目前是这样:

  • 权限分级:一句话里的信息,分为"临时状态"(今天赶时间、临时在出差)、"短期偏好"(这周在研究Java并发)、"稳定属性"(姓名、职业、城市)。只有后两者才写入长期记忆。
  • 时间衰减:对用户偏好字段增加时间戳,超过一定时间没有再次出现,就给它的权重打折或降到"候选待确认"状态。
  • 主动确认:比较关键且影响后续行为的画像变更,Agent可以反问一句"你希望我一直按这个来吗",比如换了城市、切换了技术方向。这种交互成本不高,却能避免长期跑偏。

记忆删除同样重要。用户明确要求"记住的东西忘掉",这不能只是删除一条向量记录,还要把可能包含该信息的中间产物(比如历史会话摘要)一并处理。对产品而言,这不仅是隐私合规要求,也是用户信任的底线。

3. 知识库与RAG:给Agent装一个外部"资料室"

3.1 知识库和用户记忆的边界划分

知识库和用户记忆在技术上很像,都是把信息存起来、需要时再检索出来,但它们的边界如果不划清楚,系统会越来越混乱。

我自己的定义方式很简单:知识库是"关于世界、领域和组织的客观事实";用户记忆是"关于这个对话对象的私有事实"。比如"公司的报销流程是什么"属于知识库,“张三上次报销时被会计要求补过一次发票”属于用户记忆。前者可以被所有用户共享,后者只能被该用户或授权系统访问。

把这两个混在一个向量库里存,是很多初学者会踩的坑。检索的时候,如果知识库文档片段和用户记忆混在一起,你很难在召回阶段准确控制"当前请求到底应该看到谁的记忆"。轻则回答错误,重则一个用户的问题把另一个用户的信息给检索出来,这在任何真实产品里都是事故。

所以架构上我建议至少在存储层面分库,或者给数据加严格的namespace/tenant标签。业务上,知识库可以有多套维度:企业制度库、产品文档库、行业知识库,甚至不同团队维护的专属知识库。用户记忆则可以拆成"通用记忆(所有会话可用)"和"场景记忆(只在某个项目/某个任务里可用)"。

3.2 RAG检索链路拆解:切片、向量化、召回、重排

现在做知识库基本都绕不开RAG(检索增强生成)。我刚开始学的时候以为RAG就是“文档灌进向量库完事”,后来实际调参才发现链路里每个环节都直接影响答案质量。

标准链路我按四个环节记录:

第一是文档切片。文档不能整篇丢进向量库,因为检索单元太大,召回的相关片段里会混杂大量无关内容;太小又会导致上下文信息不完整。我实测下来,面向通用企业文档时,200到500字的切片配合一定重叠(通常50到100字)是比较稳的区间。表格、代码块这种特殊格式最好单独处理,不要硬切。

第二是向量化。这里最关键的决策是选择嵌入模型(embedding model)。不同模型对中文的支持差异很大,我踩过坑之后固定了自己的原则:优先选在中文语料上效果有明确评测的模型,而不是单纯看英文benchmark。如果你面向专业领域,比如法律、医疗、工业PLC编程,通用嵌入模型的效果往往一般,有条件时要用领域语料做微调或至少做对比评测。

第三是召回。召回阶段的超参数主要在top_k和score阈值。top_k设太少了可能漏掉关键内容,设太多了又会给后续生成阶段塞入太多噪声。我的经验是先粗调top_k到10到20,然后看检索结果的精准率;score阈值没有一个万能数字,因为不同嵌入模型的分数分布不一样,必须用自己的数据集做试验。

第四是重排。重排这一步很多人会省略,但在知识库质量要求较高的场景下,加一个重排模型能够有效把召回的候选片段按真正的相关性再做一次排序,让答案引用更精准。重排就不像简单向量检索那样只看语义相似度了,它会把用户Query和候选片段一起输入模型,输出相关性分数。我通常在top_k拉得比较宽的时候才加这层,避免窄召回时重排也救不回来。

3.3 匹配度不理想的常见原因和调试方法

关键词里有"怎么提高匹配度",这应该是绝大多数做知识库的人都遇到的痛点。我自己总结了一套排查链:

  • 第一步看召回不召回。如果某个问题根本检索不到相关内容,先检查这个文档片段是否被正确向量化、是否进了正确的集合,再检查用户Query和文档术语是否存在"同义不同形"的问题,比如用户说"工资"而文档写的是"薪酬"。这种情况下,考虑在Query理解阶段做一次术语扩展。
  • 第二步看召回准不准。如果内容召回了但排序靠前的是次要内容,通常是切片质量或者嵌入模型领域适配性问题。我遇到过的最典型案例是,一份PLC编程手册里把"启动条件"和"故障复位"放在同一个切片里,用户问启动条件时,检索回来的片段里一半内容在讲故障复位,直接把模型带偏了。调整切片粒度或者做小段合并后有明显改善。
  • 第三步看生成对不对。如果文档已经正确召回,但模型回答时还是没用上,或者用了一段影响判断的无关内容,这时候就要考虑提示词里给知识库内容的指令是不是不够清晰。我常用的写法是要求模型"只能依据提供的资料回答,资料中找不到的信息要明确说不知道",并且把知识库内容放在Prompt中比较靠前的位置,给它足够的注意力权重。

这个链路调试起来很费时间,但没有捷径。我能给的唯一经验就是:每一次调参都要有可复现的测试集,把50到100个高频问题固化成回归集,每次改动后跑一遍,看整体指标而不是单个案例。

4. 从个人到企业的三种落地方式

4.1 Dify知识库流水线:从导入Excel到多路召回配置

关键词里出现了"dify知识库流水线"和"dify本地知识库搭建",说明不少人在用Dify这类低代码工具搭知识库。Dify也确实是我见过的上手门槛最低的方案之一。

Dify里搭知识库的步骤,我大概这样操作:

  • 准备文档:将Excel、PDF、Markdown等格式的文档整理干净,尽量避免表格里有多级表头或合并单元格,因为解析时很容易丢信息。
  • 新建知识库:在Dify控制台直接创建数据集,选择"导入已有文档",有API同步和手动上传两种方式。我一般先用小文件测试解析效果,再批量上传。
  • 配置索引方式:Dify提供高质量模式和经济模式,高质量模式会对文档做更细致的分段和清洗,需要消耗更多embedding额度;经济模式适合私人小规模知识库。对应到流水线,就是切分策略和索引策略的选择问题。
  • 设置召回模式:Dify支持向量检索、全文检索、混合检索。关键词里的"多路召回"通常就是要开混合检索,把向量相似度和关键词匹配结合起来。两者互补:向量检索擅长语义理解,全文检索擅长精确匹配术语,比如产品编号、设备型号。
  • 关联到Agent或工作流:把知识库挂接到Agent工具上,运行后测试几个真实提问,检查召回命中率和生成答案质量。

如果只是本地玩一下,可以在自己电脑上用Docker把Dify跑起来,模型选择配置成DeepSeek或其他支持OpenAI兼容接口的本地或云端模型。这样做的好处是不把自己的文档同步到第三方平台,适合隐私敏感但规模不大的场景。

4.2 Obsidian+Workbuddy:个人知识库的轻量组合

关键词里还有"obsidian知识库搭建"和"obsidian+workbuddy知识库搭建",这个组合我在个人笔记场景里试过,验证了Oi笔记本来承载Agent知识源是可行的,但一定不是把Obsidian笔记当数据库用。

我的做法是把Obsidian当成"知识来源管理端",日常用双链笔记记录想法,但对知识库系统真正可用的是里面的Markdown文件。通过Workbuddy这类工具,将指定文件夹下的文档同步到向量库,或者直接在Agent里配置“按路径读取+切片+向量化”流水线,这样既能保留Obsidian的双链和信息组织习惯,又能让Agent检索到这些内容。

实际操作中有一件事要特别注意:Obsidian笔记中大量存在的双链语法、模板变量、Callout块,如果不做清洗就直接送去切片,会产生非常多无效字符碎片,影响向量质量。我在同步之前会写一个简单的预处理脚本,把wiki链接转成纯文本,把Callout信息简化成普通引用块,再进入切片流程。

从测试结果看,个人知识库场景下,能不能提升匹配度,很大程度上取决于笔记本身的质量。如果一篇笔记标题是“杂记”,内容一会儿说工作一会儿说生活,那再好的检索也救不了。我会建议在Obsidian里就保持每篇笔记主题单一、段落清晰,这比任何后端的调参都更有效。

4.3 企业级Java Agent平台:知识库与记忆服务化的架构取舍

关键词里出现了"企业级java ai agent应用平台""spring cloud + spring ai开发自己的agent""jenkins ai agent",看得出部分读者已经在企业级场景里做Agent落地了。企业级和个人级最大的区别在于:不能把知识库和用户记忆写死在某个Agent实例里,而是要把它们变成独立的服务。

我自己在做Java后端技术栈时,比较推荐的拆分思路是这样的:

  • 知识库服务:独立部署一个服务,负责文档解析、切片、向量化、索引管理、检索API。Agent应用只通过REST接口做召回,不在Agent进程内直接维护向量索引。这样文档更新、模型升级、权限控制都能单独灰度。
  • 用户记忆服务:提供读写用户画像和历史记忆的API,比如getMemory(userId, context)、updateMemory(userId, memoryEvent)。内部可以接Redis做短期缓存,接向量库做长期记忆检索。
  • 编排层:Spring AI或自研的Agent编排逻辑统一调用LLM、知识库服务、记忆服务、外部工具。编排层不保存状态,或者只在会话维度保存短期上下文,所有需要跨会话的信息都走用户记忆服务。

这个架构的好处是每个组件都能独立扩缩容和替换,坏处是链路变长,延迟会增加。单次问答如果是纯LLM调用可能只需要一两秒,加了知识库召回、记忆召回、重排之后可能变成三到五秒。企业级场景通常可以接受,但个人小项目就没必要照搬这种重架构,Dify或者单机脚本反而更合适。

关键词里提到的"jenkins ai agent"更偏向工程领域:把Agent接入构建部署流程里,让它能理解构建日志、发布记录、代码库变更。这时候知识库的角色是"历史构建经验和故障排查手册",用户记忆的角色则是"每个开发者负责的模块和偏好",整体思路是一致的,只是文档来源变成了CI/CD流水线的输出。这种结合一旦跑通,价值会非常大,因为它真正把Agent嵌进了日常的工程协作里。

5. 一个问句走完记忆+知识库全流程的实战

5.1 输入阶段:检索画像、解析Query

为了把前面几块理论串起来,我拿一个实际场景做一个完整拆解。假设用户李工在Agent里问:帮我看看上次说的那个设备故障排查文档,明天早上我想发给供应商。

这条消息乍一看是一个简单的检索请求,但正常Agent完整链路要做的远不止“知识库搜一下”。输入阶段需要同时做三件事:

  • 解析Query的核心意图:设备故障排查文档属于知识库检索请求,"发给供应商"是一个附带动作识别,"明天早上"是时间偏好暗示。
  • 读取用户画像:从记忆服务里读取李工的姓名、公司角色、常用文档偏好、最近的故障处理记录。这样系统才知道“上次说的那个”具体是哪一次的上下文。
  • 判断记忆的时间跨度:如果画像和记忆里存在一个"上次设备故障排查"的事件记录,就直接把该记录的ID或文档路径作为检索强约束;如果没有,才退化成纯语义检索。

实际做的时候,"上次说的那个"这种指代能否解决,就靠这层的画像和记忆召回能否命中。很多人以为这是纯粹的意图识别问题,其实它是记忆检索问题。

5.2 组装阶段:Prompt里怎么拼接记忆和知识库结果

检索完成之后,Agent要把这些信息组装成一次完整的LLM调用。组装顺序和格式,是我觉得可以分享的一部分实操经验。

我通常会把Prompt从结构上分为四块:

  • 用户画像块:简短列明李工的姓名、偏好、历史相关事件摘要,让模型知道在跟谁对话、对方大概什么背景。这部分不宜过长,两三行就够。
  • 知识库检索块:把召回的文档片段按相关性排序列出,每段标注来源文档名和切片编号。这样模型既能参考原文,又知道如果信息不够就直说。
  • 当前指令块:用户这次的Query以及系统要求它完成的动作,比如“先确认文档版本,再生成发给供应商的邮件草稿”。
  • 格式约束块:要求输出格式、语言风格、以及哪些情况不能编造。

比较典型的错误是把知识库原文整个当作对话历史填进去。知识库片段数量一多,模型会把不相关的细节当成上下文重点,导致回答啰嗦且偏离核心。我的做法是宁可只给前3到5个最高相关的片段,也不要为了“看起来全面”把所有召回结果都堆进去。相关性不够的片段——哪怕排在第4名——也可能带来负面干扰。

组装层面的另一条经验是,Prompt里的记忆和知识库要区分"事实"和"引用"。用户记忆里的画像信息,模型容易当作普遍事实使用;知识库内容则应该被当作参考资料。这两类信息在Prompt里用不同的标签或分隔符隔开,能在一定程度上控制模型对它们的信任程度。

5.3 更新阶段:对话结束后回写哪些关键信息

一次问答生成完成并不等于这个链路结束,更新阶段的记忆回写是这个系统能否形成"越用越懂你"效果的关键。我一般在每个有效对话结束后,异步执行一次记忆抽取任务。

拿李工的案例来说,这次对话产生了至少三类值得回写的记忆:

  • 高频事件记录:“李工正在处理一个设备故障排查文档,涉及供应商联系事项”。这是一条带时间戳的短期事件。
  • 偏好信号:从“明天早上想发给供应商”可以推断李工倾向于提前一天准备对外沟通材料。如果类似信号多次出现,才能升格到偏好。
  • 知识调用痕迹:平时技术文档里哪些内容被高频检索和引用,可用来反向优化知识库索引。不是说直接把用户历史复制进知识库里,而是统计哪些文档切片经常被引用、回答采纳率高,这些切片未来在重排时可以被赋予更高权重。

记忆回写不要阻塞主流程,放在后台跑就行。更新完之后建议再做一次一致性检查:新记忆和旧画像字段是否冲突,如果冲突了,用追问确认或者标记为"待确认状态",而不是直接覆盖。

5.4 被忽略的隐私和边界问题

越聊越细,我再补一块很多技术笔记不太会花篇幅讲的内容:隐私边界。一个能长期记忆用户信息的Agent,如果不在产品设计层面处理好隐私边界,用户知道真相后会非常不安,甚至引发合规风险。

边界问题主要体现在三个方面:

  • 可见性:用户是否有办法查看Agent记住了自己哪些信息?理想情况下,要提供一个"记忆管理面板",让用户能直观看到自己的画像和记忆历史。
  • 可控性:用户说"删掉这些记忆"时必须真的删掉,而不是只在UI上做假删除、后端还留着向量数据。向量库里单条记录的删除并不总是很方便,尤其是一些商业向量数据库的删除操作有延迟或需要重建索引,这必须在架构设计阶段就考虑。
  • 可解释性:Agent回答如果依赖了用户记忆或知识库信息,最好在回复中给出可追溯的来源或提示。这比任何免责声明都更能建立信任。

我在自己的个人项目里一开始完全没考虑这些,觉得反正是自己用。但后来把它扩展成一个小产品给同事试用时,第一个反馈就是"你怎么还记得我上次说的那个事情,这有点吓人"。那之后我才开始认真对待记忆的可见性和可控性问题。

6. 关于记忆、知识库与Agent能力边界的一些个人体会

这份学习笔记写到这里,我自己最大的体会是:用户记忆和知识库,本质上是在给Agent搭建"内在大脑"和"外在资料室"两个协作者。模型本身的逻辑推理能力是底座,但底座再强,没有记忆就没有连续性,没有知识库就没有专业深度。

如果你正在规划自己的下一步学习路线,我会建议不要一上来就追求复杂的多Agent架构,而是先把记忆和知识库这条链路在一个简单的项目里走通。

这里有一个很具体的小项目可以练手:做一个"家庭整理顾问"Agent,它需要记住家庭成员对收纳风格的偏好,同时基于一个收纳知识库回答"小户型空间怎么利用"的问题。这个项目规模不大,但已经能把你对用户画像提取、短期记忆管理、RAG检索、记忆回写这几个核心能力的理解完整地串起来。

市场上现有的Agent产品和开源项目提供了很好的起点,但它们大多把记忆和知识库包装成了开箱即用的功能按钮,你如果只在界面上点点点,很难理解里面的取舍。我自己是从做一个最小实现开始的——用几百行Python代码加上一个开源向量库,把用户画像存储、知识库检索、Prompt组装整个流程写了一遍。写完那一刻,很多以前觉得"应该就是这样"的抽象概念突然变得非常具体。

最后再说一个和标题直接相关的细节:既然这是系列笔记的第三篇,前面可能已经覆盖了Agent框架、工具调用这些内容,但记忆和知识库这两个话题在绝大多数教程里都被排在最后或者直接跳过。我的建议恰恰相反——这两个能力最好早一点引入自己的项目,哪怕第一版实现非常粗糙。因为Agent给用户带来的体验飞跃,很多时候不是靠更聪明的推理,而是靠"它居然还记得我们上次聊了什么"和"它能回答我们专业领域里的小众问题了"。这两个从无到有的瞬间,是AI产品最接近"有温度"的时刻。我下篇笔记大概率会往多智能体协作和复杂任务编排方向写,到时候再把记忆在这些场景里的传递和同步问题展开聊。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询