本次学习第三章用户记忆和知识库
课程链接:ai-agent-book/chapter3/README.md at main · bojieli/ai-agent-book · GitHub
目录
一、前期理论知识储备
二、实验运行
三、实验结果分析
一、前期理论知识储备
动手做实验之前,先来对三个名词作一个初步的了解和串联。
1、Sparse Vector Search Engine (BM25) / 稀疏向量搜索引擎(BM25)
简单来说BM25是计算你的文档中每个词出现的频率从而计算词的权重,搜索引擎和rag里都用到了这个技术。比如当你跟一个人面对面对谈时,他的一句话里多次出现了同一个词,那么你的大脑也会自动摄取这个词相关的一切信息,从而你也就能知道你们这次的谈话基本会围绕什么话题去展开。BM25就是将你的原文本转化为稀疏向量,他是根据【关键词】权重(一个词出现的频率越高,权重也越高)去做相关性打分,从而能判断一篇文档和你搜索的关键词有多匹配。bm25是传统关键词检索算法。
而稀疏向量搜索引擎是现代机器学习产出的稀疏表征,他和bm25同属【稀疏检索】大类。但稀疏向量是神经网络模型输出的高维稀疏向量,属于学习出来的向量也可以存在向量数据库里。两者都可以和稠密向量搭配使用(bm25输出的是分数,稀疏向量输出的是向量)
2、Hybrid Retrieval Pipeline with Neural Reranking / 混合检索流水线与神经重排序
混合检索就是上面说的稠密向量检索+稀疏检索(bm25 or splade稀疏检索),也能看出本章学习内容环环相扣,可以吧前面三章的内容串联起来(chapter1学的是ai agent=llm+context+tools;chapter2学的是上下文工程,当我们已经拿到素材之后,如何压缩、裁剪、控制token,塞进上下文窗口;chapter3就是解决素材怎么选,从哪里开始选、RAG检索流水线、混合召回、长期用户记忆)
顺着这个公示再深入理解他们的关系:混合检索 =语义召回(稠密) + 关键词 / 实体召回(稀疏支路)。两条支路(稠密+稀疏)并行召回文档得到两份排名-->之后把两组结果分数融合吧前面两份文档融合为一份形成固定的候选池排名-->再通过计算召回率recall和精准率precision的计算来诊断这两份排名-->假如1)召回阶段指标计算值低就改混合检索 2)召回没问题,精确率低调RFF融合或送入 Rerank 重排。由此我们可以深入的了解这些新学名词的联动闭关,混合检索负责捞文档,RRF 合并结果;分阶段指标用来判断 bug 是出在捞文档这一步,还是后面排序环节
3、User Memory System / 用户记忆系统
这一块主要是讲agent用户长期记忆模块,也就是用来记录和大模型对话的我们的信息、偏好、历史诉求、跨会话持久保存。他分为三块对话短期记忆、外部知识库记忆、用户长期记忆(就是吧用户信息持久保存到向量数据库,这样的好处就是哪怕关掉会话再打开大模型依然记得我们的信息)。工程流程是:记忆抽取->记忆写入向量数据库->新会话发起提问->把记忆片段拼入prompt->记忆维护
4、三者的关联关系
混合/稠密向量检索对象是业务知识库,user-memory存的是用户的信息。底层技术一样但存的内容不同。
Q1:为什么要吧这些业务和用户信息分为两块独立内容去存储,并且最后并行召回吧拿到的片段合并到一起呢?
答案是,业务信息存储的是静态数据,回答“事实是什么?”,所有人查询的信息都是公用的,并且回答的信息是客观事实;而用户数据是动态的,回答“这个用户目前的偏好是什么?想要什么?有什么约束?”,这类信息是独立去存储的,需要隔离数据。如果合并放到同一个库检索,查询的时候会混在一起。检索知识库文档时,很容易误召回大量别的用户记忆,造成信息污染、隐私泄露。
Q2:为什么后续又可以合并到一起?不是之前提到混在一起会造成用户记忆信息污染
合并到一起指的只是吧两段素材拼在一起喂给prompt,不再涉及数据库检索。前面说的用户记忆信息污染是指模型错吧别人的用户信息拿成你的。
二、实验运行
实验3-1 user-memory
本次实验核心是要对比 4 种用户长期记忆存储方案,并且用「三层评估框架」测试记忆好坏。 4 套记忆存储策略(从简单到复杂):
- Simple Notes:纯文本笔记,直接记录用户事实。最简单;但是信息杂乱,检索困难。
- Enhanced Notes:增强笔记,写入时 LLM 先做摘要再保存,语义更强。
- JSON Cards:结构化卡片,固定字段(事实、来源、时间),方便检索、更新。
- Advanced JSON Cards:进阶结构化卡片,支持记忆冲突检测、记忆合并、旧记忆覆盖,解决新旧信息矛盾。
第一步,进入我们的项目目录里
cd ai-agent-book source .venv/bin/activate cd chapter3/user-memory第二步,在已经激活的环境里安装好我们要用的依赖包
pip install -r requirements.txt -i https://pypi.tuna.tsinghua.edu.cn/simple第三步,在shell里配制好我们模型的api-key,这样我们就可以本地去掉模型
nano .env MOONSHOT_API_KEY=sk-xxxxxxxxxxxxxxxxxxxx --nano 保存退出:‘Ctrl+O’ 回车保存 → ‘Ctrl+X’ 退出.ENV文件中配置我们要调用的模型参数(找到.env文件里的这几个参数,修改到对应你要调用的信息)
PROVIDER=doubao DOUBAO_API_KEY=粘贴方舟生成的sk‑xxxx MODEL_NAME=doubao‑seed‑2.1‑pro MEMORY_MODE=notes MODEL_TEMPERATURE=0.3 MODEL_MAX_TOKENS=4096这次我选择接入的是豆包模型,首先要去火山方舟官网注册账号->创建aki-key->激活模型->完成实名认证->在模型推理板块点开预置推理接入点->接入跑通我们前期选择激活的模型
火山方舟 - 首页
step1:注册账号->创建aki-key
复制好我们的api-key
step2:在模型管理处激活我们要接入的模型(这一步需要实名认证,不然会影响后续api调用)
step3:点开推理->预置推理接入点->接入我们的模型
step4:调整我们本地的代码文件的参数
1)定位 doubao provider 源码文件,在终端运行:
nano config.py快捷键control+w,粘贴“doubao-seed-1-6-25061”,可以看到代码里调用的模型名词是写死的,将参数改成你要调用的模型名词,我的是“doubao-seed-1-6-250615"
2)再打开.env文件,配制参数
3)吧MODEL_NAME改成ARK_MODEL,因为我们在config文件里看到的调用函数是,当代码没找到ARK_MODEL要调用的模型,就默认去调用第二个参数的模型。所以.en文件这里为了和config里的参数对齐,要改下模型名称
配制好模型,现在可以开始运行代码了
python3 quickstart.py运行“python3 quickstart.py”的结果是下图,结论分析放到第三块深挖
python3 main.py --mode interactive --user your_name三、实验结果分析
(重点分析quickstart.py实验,其他简单带过)
1、实验3-1的结论分析:这次的模型输出的内容可以复用之前学过的Agent = LLM + Context + Tools这个公式。llm就是我们的豆包模型“doubao-seed-1-6-25061”,context当前对话+持久化记忆库,tools是【add_memory/update_memory/delete_memory】三个记忆操作工具。
可以看到,每一轮的对话大模型都在单独调用llm.......以此可以推导出普通对话 Context 只保本次聊天历史;User Memory 把用户的事实、偏好抽成结构化笔记,永久保存,后续每次提问自动加载进上下文。日志概览(后面会放详细的日志解析过程):
拆解日志:
这一段日志的意思是新开 Session,记忆不会丢失,会自动加载历史记忆。程序启动对话前,自动读取本地保存的 4 条记忆,放进 LLM 上下文。
round2里用户输入“switched from PyTorch to JAX”,llm开始调用工具update_memory,具体执行逻辑为“You’re building a **recommendation-system project**. My earlier note said it used **PyTorch**, but you’ve now clarified that you recently switched to **JAX for better performance**, so I’ll treat JAX as your current framework.”。这也是长期记忆的关键能力,他会去维护、修改过期的事实,防止旧的信息污染后续回答。
round3里客户问llm基于我的情况推荐工具,在【FINAL MEMORY STATE】可以看到打印出来的4条记忆里,其中一个已经更新过了。4 条结构化笔记,带 tag、唯一 ID、更新时间,就是持久化记忆的存储结果。