做 Agent 开发的人,迟早会在同一堵墙上撞一次:模型的上下文窗口撑爆,追问不到历史信息,回答开始靠猜。我早期做过一个客服类 Agent,对话超过三轮之后,它就开始忘记用户刚说过的需求,更别提调用之前沉淀的业务知识。问题不在模型,在记忆和知识库的设计。
那之后我把精力从"调 prompt"转向了"搭记忆体系",摸清了文件、数据库、RAG 和知识编译这四件事各自该扮演什么角色。这篇文章就围绕 Agent 记忆与知识库展开,从最基础的文件存储、结构化数据库,到当下最热门的 RAG 检索增强生成,再到很多人忽略但价值极高的"知识编译"环节,每一步的选择逻辑、实操细节和踩坑记录我都会讲。适合正在做 Agent 开发的人,也适合想搭建个人或团队知识库的朋友参考,读完你会对整套体系有一个可以落地复现的完整认知。
1. 先理清楚:Agent 的记忆到底是什么
1.1 别把"记忆"和"上下文"混为一谈
很多人一上来就买大上下文窗口的模型,恨不得一次塞进 20 万 token,以为这样 Agent 就有记忆了。这是个典型的误区。
上下文只是"一次对话中临时加载的信息",它是易失的,会话一结束就没了。而记忆是跨会话、跨任务持续存在的信息结构。你要的是一个能对用户说"我记得你上周提过那个需求"的 Agent,而不是一个只在这轮对话里不失忆的聊天机器。用上下文硬扛记忆,代价是成本和延迟双高,而且信息一多,模型注意力分散,召回质量反而下降。
从工程角度看,Agent 的记忆一般拆成三层:工作记忆(当前任务里必须握在手里的状态和数据)、情景记忆(历史对话、用户偏好、事件记录)、语义知识(事实、规则、概念,通常来自知识库)。工作记忆靠上下文窗口解决,情景记忆靠存储系统解决,语义知识靠知识库解决。三者用途不同,技术选型也完全不同。
1.2 记忆的四种实现形态
顺着上面的分层,实际项目里记忆大致有四种落地形态:
- 内存态:跑在进程里,存当前会话的临时状态,用字典、Redis 之类,进程一重启就没了。
- 文件态:把历史对话和行为记录落成 Markdown、JSON、JSONL 文件,适合永久保存、人工审阅,也方便接入 Obsidian 这类知识管理工具。
- 数据库态:用 SQLite、MySQL、PostgreSQL 存结构化数据,适合记录用户档案、任务状态、统计信息,查询能力强、一致性好。
- 语义态:把文本向量化之后放进向量库,配合 RAG 在对话时检索最相关的历史片段和知识条目,这是目前 Agent 记忆的主流实现。
我在实际项目里很少只用单一形态。最稳的组合是:文件做原始存档,数据库做结构化索引和状态管理,向量库做语义召回,三层相互配合。别指望一个组件解决所有问题。
1.3 记忆设计的核心指标:写得到、查得着、读得对
记忆系统的设计目标可以浓缩成三个能力:写入场景覆盖全,查询路径足够快,读取内容足够准。
"写得到"指的是 Agent 和用户交互的每个重要节点都要有落盘动作,不能只靠日志,要在业务逻辑里显式调用存储。"查得着"指的是检索入口要对齐业务场景,比如按用户 ID、按时间范围、按知识分类。"读得对"最关键也最难,检索出来的内容必须和当前问题语义相关,不能拿一堆噪音塞给模型。后面讲 RAG 的时候我会专门展开"读得对"怎么做。
2. 文件、数据库、RAG:三种载体到底怎么选
2.1 文件系统:最原始也最灵活的底座
文件是知识库的第一层地基。我见过不少团队一上来就上向量库,结果原始文档散落在各种群聊和网盘里,根本没有人维护源头。
文件承载的应该是"事实的原始形态":产品文档、会议纪要、操作手册、爬取到的网页正文。纯文本、Markdown 优先,PDF 和 Word 在解析阶段会带来大量格式噪音,能转 Markdown 就转。以 Obsidian 为代表的个人知识库工具天然适合这种用法,本地文件、双向链接、文件夹 Tag 组织,加一个 Git 仓库就能做版本管理。
文件的好处是透明、可控、离线可用,出问题能用文本编辑器直接排查。缺点也很明显:检索效率低、无法做复杂条件查询、容易产生大量重复副本。所以文件只适合做底座,不适合做查询层。
2.2 数据库:结构化信息的可靠容器
当记忆内容开始出现明确的字段结构时,就该上数据库了。比如用户档案有姓名、邮箱、偏好;任务表有状态、优先级、截止时间;会话记录有 session_id、user_id、时间戳。这类数据塞进 Markdown 里就是灾难,塞进 SQLite 里就是一条 SELECT 的事。
SQLite 是我在中小型 Agent 项目里的首选,单文件、零配置、支持标准 SQL,Python 标准库 sqlite3 直接可用,部署几乎无成本。数据量到几百万行之前,它的性能足够。团队级项目再考虑 MySQL 或 PostgreSQL,配合数据库同步工具把结构变更和备份链路管起来。PostgreSQL 还有 pgvector 扩展,能在一个数据库里同时搞定关系数据和向量检索,少维护一套系统,这个优势在中小项目里非常明显。
数据库解决的是"精确查询",解决不了"模糊语义匹配"。你想查"用户上次抱怨过什么",数据库得靠 LIKE 模糊匹配,效果差还费资源。要处理这类需求,就得把 RAG 请上场。
2.3 RAG:让 Agent 学会"按语义找资料"
RAG(检索增强生成)的核心逻辑不复杂:用户提问时,先从知识库里检索出最相关的片段,拼进 prompt 里喂给模型,让模型基于检索结果作答。它解决的核心痛点是模型知识时效性差和专业领域知识缺乏的问题,也顺带让答案可以溯源。
我在项目里落地 RAG 时,链路通常是这样:文档切块 → Embedding 模型转向量 → 向量库存储 → 查询时向量相似度检索 + 关键词检索混合 → Rerank 重排 → 拼装上下文喂给模型。其中最容易出问题的两个环节是切块和重排,后面实操部分细说。
值得提醒的是,RAG 不是"把文档扔进向量库就完事"。它需要持续维护:文档更新了要重新切块和向量化,检索效果变差要调参数,甚至要换 Embedding 模型。那些把 RAG 当一次性工程的团队,最后多半会得到一堆"看起来像那么回事但答案经常不对"的 Agent。
2.4 三种载体的分工架构
把三种载体拼在一起,会得到一个我常用的分层架构:
- 原始层:文件系统里的原始文档,人工可读、可追溯,是知识源头的唯一事实标准。
- 结构化层:数据库中的实体、关系、状态、统计,为业务逻辑提供精确数据支撑。
- 语义层:向量库中的语义索引,用于模糊问题和自然语言提问的快速召回。
- 编译层:经过知识编译后生成的工具描述、规则集、图谱结构,直接喂给 Agent 决策。
这套架构的好处是各层职责清晰,某一层出了问题可以在不推倒重来的前提下单独修复。我见过只用向量库的"全都要"方案,也见过只用数据库的"结构化狂魔"方案,实测下来,前者回答飘,后者答非所问,架构冗余一点反而更稳。
3. 知识编译:把零散信息变成 Agent 能真正用起来的知识
3.1 知识编译到底是什么
"知识编译"这个词听起来学术,其实说的是一个很朴素的事:把自然语言形态的原始知识,转化成 Agent 运行时可以直接消费的紧凑结构。
原始文档是给"人"读的,段落有铺垫、有废话、有隐含前提。但 Agent 的上下文窗口有限、推理时对长文本的注意力有限,直接塞原文既浪费 token 又容易引入噪音。知识编译要做的是"信息提纯":从文档里抽出实体、关系、规则、操作步骤、判断条件,重新组织成结构化的、可直接调用的形式。
3.2 知识编译的四种形态
我实践中常用四种形态,技术含量递增,适用场景也不同:
- 规则集:把"如果……那么……"类型的业务规则写成结构化规则文件(YAML/JSON 或决策表),Agent 在决策阶段直接读取。比如客服场景的退款规则、风控场景的拦截规则。
- 工具描述:把"怎么做"的过程编译成函数的 schema 描述(name、description、parameters),让 Agent 通过函数调用 API 来获取答案。这相当于把知识封装成了可执行动作。
- 知识图谱:把实体和关系抽取成三元组(实体-关系-实体),存入图结构。适合关联关系复杂的领域,比如故障排查场景中"现象-原因-解法"的网状知识。
- 提示词资产:把高价值回答逻辑沉淀成 system prompt 片段或 few-shot 示例,随请求动态组装。这是成本最低的编译形态,也最容易被维护者偷懒放任变质。
知识编译和 RAG 不是二选一。我的习惯是:RAG 负责"召回相关资料",知识编译负责"把资料变成决策依据"。比如让 Agent 做故障诊断,RAG 负责从手册里找出相关章节,知识编译输出的决策表负责告诉 Agent 先查什么、后查什么、什么条件下判定为根因。没有编译层,RAG 召回了一堆内容,Agent 仍然不知道怎么组织推理,回答就容易散。
3.3 知识编译的落地动作与工具
落地知识编译不需要额外引入重型系统。第一步是盘点知识类型:哪些知识适合写成规则、哪些适合写成函数、哪些必须保留原文。第二步是写转换脚本,用 LLM 做半自动抽取,人工审核后入库,这一步可以用 Dify 的流水线或 LangChain/LlamaIndex 的 extractor 能力辅助。第三步是关键,建立知识更新的回写机制,业务规则一变,编译层必须同步更新,否则 Agent 会用旧规则回答新问题。
4. 实操:从零搭建一套 Agent 记忆与知识库
4.1 需求场景与工具选型
我以一个典型场景为例:做一个"个人知识库问答 Agent",让它能回答你 Obsidian 笔记里的内容,同时记住你的偏好和最近关注的话题。项目规模不大,适合用轻量工具。
工具清单如下:文件层用 Obsidian 仓库 + Git;数据库层用 SQLite,装 sqlite3 标准库即可;向量库用 Chroma 或 SQLite 的 vec 扩展,个人项目别上 Milvus,太重了;RAG 编排用 LangChain 或 LlamaIndex,也可以直接用 Dify 搭流水线,省去自己写胶水代码;Embedding 模型优先用本地的 bge 系列或 OpenAI 的 embedding 接口,看预算和对隐私的要求。
4.2 数据采集与清洗
这一步决定了知识库的天花板,但我发现绝大多数人在这里偷懒。原始笔记里往往有大量无关内容:空行、无意义标签、过期的 TODO、重复粘贴的片段。直接切块向量化,垃圾进垃圾出。
我的流程是:先写脚本把仓库里所有 Markdown 文件读出来,过滤掉模板文件和配置目录;然后做统一格式处理,把标题层级规范化、压缩连续空行、去掉 HTML 残留;再用正则或 LLM 做首轮粗清洗,把明显噪音段落标记出来。清洗完的文件落到一个干净的目录,这个目录才进入后续的切块管线。文件级的 Git 提交建议按清洗前后分两次,方便回溯。
4.3 分块策略与参数选择
分块是最影响 RAG 效果又最容易被忽视的环节。块太大,检索出来内容相干性差,token 浪费严重;块太小,语义信息被切断,模型缺乏上下文无法作答。
我从项目中总结的经验值:通用文本块大小 512~1024 token,重叠 100~200 token。技术文档类可以适当放大到 1500 token,因为技术描述前后依赖强;短小的笔记条目按条切,别硬拼成一个大块。切块时尽量按 Markdown 标题层级做边界切分,保持标题和正文同块,这样检索到的块自带上下文,回答准确率明显更高。
写代码时注意:切块函数要幂等,同样的输入必须产出同样的输出块,否则增量更新时会出现重复向量。这个坑我踩过,费了半天排查才发现是正则切块对同一文档不同文件结尾处理不一致导致的。
4.4 向量化、检索链路与 Rerank
清洗和切块完成后,调用 Embedding 接口逐块向量化并写入向量库。注意记录每个块对应的文件路径、标题路径、块序号,这是定位能力的基础,没有元数据的向量库就是黑盒。
检索链路上我强烈建议做混合检索:向量检索召回语义相近内容,BM25 关键字检索召回术语精确命中的内容,把两路结果合并后做 Rerank。个人项目里 Rerank 可以用 bge-reranker 这样的轻量模型,效果提升非常明显。别看只多了一步,实测混合检索加 Rerank 比纯向量检索的答案可用率能高出二三十个百分点。
Agent 集成部分,检索到的知识块拼成固定格式的上下文,和用户问题一起发给模型。同时把 SQLite 里的用户偏好、历史会话摘要一并注入。上下文里要明确告诉模型:优先基于知识库内容作答,知识库没有覆盖就明确说不知道,不要编造。
4.5 记忆写入与知识编译的落地
记忆写入不能靠模型自觉,要在代码里显式编排。对话结束后,把结构化信息写入 SQLite(用户 ID、时间戳、对话摘要、提取出的偏好标签),把原始对话存成 JSONL 文件落盘。定期任务把前 N 条对话压缩成摘要,存入长期记忆表,可以在 SQLite 里用单独的表。
知识编译这一步,我用脚本加 LLM 抽取把 Obsidian 里的高价值笔记编译成工具描述和规则集。比如把"每周笔记模板"编译成 create_weekly_note 函数描述,把"会议记录规范"编译成规则集。实测之后你会发现,Agent 调用编译层知识时的稳定性和准确性,远高于让它临时去检索原文再自己发挥。
5. 常见问题速查与避坑记录
5.1 检索召回质量差
症状:问一个明确的问题,检索出来的知识块文不对题。排查顺序:先看切块是否跨标题、块内是否信息残缺;再看 Embedding 模型是否和领域文本匹配,中文内容用通用英文模型效果会明显变差;最后检查是否缺少 Rerank。我遇到最多的场景是切块时把标题切丢了,模型拿到一段"没有上下文"的正文,语义和原问题天然对不上。
5.2 数据同步不一致
文件层、数据库、向量库三份数据经常各改各的。解决方案是建立单一入口:所有文档变更都必须经过同一个导入脚本更新文件、数据库索引和向量库。这需要一个简单的文件监听脚本,也建议给向量库加一个内容哈希字段,每次导入时对比哈希,变了才重新向量化。不这么做,RAG 就会一直答旧内容。
5.3 上下文窗口被撑爆
上下文里同时塞了检索结果、历史摘要、规则集、用户档案,很快触顶。我的习惯是给每一类内容设预算:检索结果占总预算四成,历史摘要占两成,规则和任务指令占三成,留一成给当前问题。超出时先砍历史摘要,再砍低分检索结果。与其用超长上下文硬扛,不如让检索更精准、让编译层更浓缩。
5.4 增量更新成本高
知识库一更新,全量重新向量化既慢又费钱。正确姿势是增量更新:新文档只对它切块向量化,变更文档用内容哈希判断后只重嵌入变更块,删除文档时先去向量库删除对应 metadata 的记录。个人知识库这个规模,一套增量脚本配合定时任务完全够用。
6. 多模态与多 Agent 场景的扩展思考
如果只是单一 Agent 问答,上面的体系已经够用。但知识库一旦面向多 Agent 协作,就要考虑共享存储的访问控制和命名空间设计。不同 Agent 用同一套知识库时,要给每个知识条目加上 scope 字段区分归属,避免 A 场景的 Agent 检索到 B 场景的敏感数据。
多模态内容也得提前规划。笔记里的图片、PDF 扫描件里的文字,很早就被切开向量化,但视觉信息(结构图、表格)在纯文本切块里会丢失。稳妥做法是先做 OCR 和表格识别,转成结构化文字再进入同一套切块管线,文档扫描件如实测下来,先转文字再向量化的问答准确率比直接丢原始文件高很多。
Agent 之间共享记忆还有一个常见问题:同一个用户在不同 Agent 里的上下文互相不通。解决思路是把会话历史统一存在一份带 user_id 和 agent_id 的会话表里,各 Agent 按需读取,而不是各自维护一份私有的对话文件。这一点在项目早期设计好,后面不用返工。
7. 站在实践角度的几条经验
说几个我在真实项目里的体会,给正准备动手的朋友参考。
第一,别一上来就追求"全自动"。知识库系统里,自动采集、自动清洗、自动向量化听起来很美,但每个环节都需要人工兜底。我给 Agent 团队的建议是:先让文件层干净,再考虑自动化;先手工跑通一遍流水线,再写自动化脚本。自动化是流程跑顺之后的奖励,不是项目初期的目标。
第二,知识编译的投入产出比,比大多数人想象的高。同样是回答一个复杂问题,走"检索原文 + 堆上下文"路线的 Agent,回答质量波动很大;走"编译规则 + 精准调用"路线的 Agent,即使换底层模型,输出稳定性都很好。知识编译是在给 Agent 做"肌肉记忆",这个投入值得专门排期。
第三,任何记忆和知识库设计,都要考虑"忘记"的能力。存了海量过期信息却不知道清理,会让检索噪音越来越大。我在维护流程里加了定期归档任务:超过一年未引用的知识块自动降级,超过两年未引用的进入待删除清单。知识库不是仓库,堆太多不如存的精。
最后分享一个小技巧:所有检索问题排查的第一步,都是打开知识库的原始文件,用肉眼确认信息真的在里面。很多所谓"RAG 失效"的案例,根因是知识源头文件压根没更新,或者更新在另一个分支里没合并。先查源头,再查链路,能省下大量的排查时间。Agent 的记忆和知识库设计没有银弹,但有清晰的工程方法和足够多的实践样本。把这套体系跑通一遍之后,你会对 Agent 的能力边界有一个更准确、更踏实的判断。