main
个人模块 README — B5
模块 B5:记忆文档存储与查找模块 | 负责人:张家为 20235947
1. 模块概述
1.1 模块名称
B5 — 记忆文档存储与查找模块 (Memory Storage & Retrieval)
1.2 模块说明
B5 是 Agent 系统的记忆系统,负责管理 Agent 对话的历史记忆和全局知识。它提供两种核心能力:
- 记忆加载(load_memory):在 Agent 启动时,根据用户指定的记忆 ID 或检索查询,从本地文件系统中读取记忆文档,注入到 System Prompt 中作为上下文
- 记忆保存(save_memory):在 Agent 对话结束后,将本轮对话的 messages、trace、final_answer 保存为 Markdown 记忆文档,并更新索引
B5 在系统流程中的位置:
STAGE 1(对话开始):
B1 → load_memory(ids, use_global, query) → B5 查找记忆文档 → 返回 selected_memory
STAGE 3(对话结束):
B1 → save_memory(conv_id, save_type, messages, trace, answer) → B5 写入记忆文档
B5 不参与 STAGE 2 的 Agent Loop,只在对话开始和结束两个阶段被调用。
1.3 完成情况概览
| 类型 | 完成情况 |
|---|---|
| 基础要求 | ✅ 全部完成:读取 memory.yaml 配置、按 ID 加载全局+对话记忆文档、按 max_memory_chars 截断、保存记忆文档(Markdown 格式)、维护 memory_index.json 元数据索引 |
| 进阶要求 | ✅ 5 项全部完成:关键词检索(BM25+中文分词)、向量检索(Sentence Transformers 语义相似度)、记忆压缩(Qwen 模型压缩冗余内容)、记忆更新(对比新旧内容+冲突检测)、向量检索 top-k 精确匹配 |
| 可独立运行的演示 | ✅ CLI 独立运行(查找/保存/压缩/更新四个子命令)+ Web Demo 集成 |
| 与团队系统集成情况 | ✅ 通过 load_memory() 和 save_memory() 被 B1 调用,是 B1 构建初始 messages 和保存对话产物的关键环节 |
2. 环境、模型与数据依赖
2.1 运行环境
| 项目 | 要求 |
|---|---|
| Python 版本 | 3.10 |
| 必要依赖 | PyYAML;进阶功能需要 jieba(中文分词)、rank-bm25(BM25 检索)、numpy、sentence-transformers(向量检索);压缩/更新需要 torch + transformers(加载 Qwen3.5-4B) |
| 是否需要模型 | 基础功能不需要;进阶压缩/更新需要 Qwen3.5-4B |
| 是否需要 GPU | 基础功能不需要;压缩/更新建议 GPU |
| 是否需要外部数据集 | 不需要 |
2.2 模型依赖
| 模型 | 来源 | 项目内相对路径 | 用途 |
|---|---|---|---|
Qwen3.5-4B |
https://www.modelscope.cn/models/Qwen/Qwen3.5-4B |
由 memory.yaml 配置 + b5_extension.py 中 MODEL_PATH 常量决定 |
记忆压缩(MemoryCompressor)和记忆更新(MemoryUpdater) |
all-MiniLM-L6-v2 |
HuggingFace 自动下载 | 缓存到 ~/.cache/huggingface/ |
向量检索(VectorRetriever)的语义编码模型 |
2.3 数据集或样例数据依赖
| 数据或文件 | 来源 | 项目内相对路径 | 用途 |
|---|---|---|---|
configs/memory.yaml |
项目自带 | agent/configs/ |
记忆存储路径、最大字符数配置 |
memory/memory_index.json |
项目自带 | agent/memory/ |
记忆元数据索引 |
memory/global/*.md |
项目自带 | agent/memory/global/ |
全局记忆文档 |
memory/conversations/*.md |
项目自带 | agent/memory/conversations/ |
对话记忆文档 |
data/memory_inputs/memory_save_input.json |
项目自带 | agent/data/memory_inputs/ |
保存记忆的样例输入 |
2.4 安装步骤
conda create -n b5_env python=3.10 -y
conda activate b5_env
cd agent
pip install -r requirements.txt
# 进阶功能额外依赖:
pip install jieba rank-bm25 sentence-transformers torch transformers
3. 文件结构与接口边界
3.1 文件结构
agent/
├── code/
│ ├── b5_memory.py # 核心:记忆加载和保存(457行)
│ └── b5_extension.py # 进阶:检索器+压缩器+更新器(739行)
│ ├── KeywordRetriever # BM25 关键词检索(jieba 分词)
│ ├── VectorRetriever # Sentence Transformers 向量检索
│ ├── MemoryCompressor # Qwen 模型压缩记忆文档
│ ├── MemoryUpdater # Qwen 模型对比新旧内容+合并
│ ├── load_all_memory_docs() # 加载全部记忆文档(用于检索器建索引)
│ └── load_memory_doc_by_path() # 加载单个文档(用于压缩/更新)
├── memory/
│ ├── memory_index.json # 记忆元数据索引
│ ├── global/ # 全局记忆目录
│ │ └── *.md
│ └── conversations/ # 对话记忆目录
│ └── *.md
├── configs/
│ └── memory.yaml # 记忆配置文件
└── B5_VERIFICATION.md # 模块验证说明书
3.2 接口边界
| 类型 | 来源 / 去向 | 数据格式 | 说明 |
|---|---|---|---|
| 输入 | B1 → load_memory() |
函数调用(config_path + ids + query + retrieval_type) | B1 在 Agent 启动时调用 |
| 输入 | B1 → save_memory() |
函数调用(config_path + conv_id + save_type + 产物路径) | B1 在 Agent 结束后调用 |
| 输出 | load_memory() → B1 |
selected_memory dict(含 docs 数组 + 状态 + 截断信息) |
注入到 System Prompt |
| 输出 | save_memory() → B1 |
saved_memory dict(含 memory_id + 路径 + 时间戳) |
保存确认 |
| 存储 | B5 → 磁盘 | Markdown 文件(memory/{type}/{conv_id}.md) |
记忆文档 |
| 存储 | B5 → memory_index.json |
JSON 对象 | 元数据索引 |
4. 基础要求实现与演示
4.1 基础功能说明
| 功能 | 入口函数 | 说明 |
|---|---|---|
| 记忆加载 | load_memory() |
按 ID 加载 + 全局记忆合并 + 按 max_memory_chars 截断 |
| 记忆保存 | save_memory() |
将 messages + trace + final_answer 写入 Markdown 文档 + 更新索引 |
| 索引管理 | memory_index.json |
记录 memory_id、类型、标题、摘要、路径、创建/更新时间 |
4.2 基础功能实现路径
| 文件 / 函数 | 作用 |
|---|---|
b5_memory.py:_memory_paths() |
读取 memory.yaml,解析 root_dir/global_dir/conversation_dir/index_path/max_chars |
b5_memory.py:_read_index() |
读取 memory_index.json,返回 {memory_id: metadata} 字典 |
b5_memory.py:load_memory() |
主加载流程:收集 ID → 逐个读取文件 → 截断 → 返回 selected_memory |
b5_memory.py:_parse_memory_markdown() |
解析记忆文档的 Markdown 结构(## Final Answer / ## Messages / ## Trace) |
b5_memory.py:save_memory() |
主保存流程:构建 Markdown → 写入文件 → 更新索引 |
b5_memory.py:_safe_conversation_id() |
校验 conversation_id 只含合法字符 |
4.3 基础功能演示命令
cd agent/code
# 查找记忆
python b5_memory.py --config ../configs/memory.yaml \
--select_memory_ids mem_conversation_conv_000 \
--use_global_memory true --query "Agent 系统如何调用工具?" \
--outdir ../outputs/B5_memory
# 保存对话记忆
python b5_memory.py --config ../configs/memory.yaml \
--save_type conversation \
--save_input_path ../data/memory_inputs/memory_save_input.json \
--outdir ../outputs/B5_memory
观察要点:
selected_memory.json包含匹配的记忆文档列表,每篇含memory_id、content、included_chars、truncatedsaved_memory.json确认保存成功,memory/conversations/conv_sample_001.md被创建memory_index.json被更新,新增或更新对应记忆的元数据
4.4 基础功能输出格式
| 输出文件 | 格式 | 说明 |
|---|---|---|
selected_memory.json |
JSON 对象 | 选中的记忆文档列表 + 状态 + 截断信息 + 错误列表 |
saved_memory.json |
JSON 对象 | memory_id + 类型 + 标题 + 路径 + 时间戳 |
memory_log.jsonl |
JSONL | 记忆查找/保存的累计日志 |
memory/{type}/{conv_id}.md |
Markdown | 记忆文档(含 Final Answer + Messages + Trace) |
5. 进阶要求实现与演示
5.1 选择的进阶要求
| 进阶要求 | 是否完成 | 对应文件 | 简要说明 |
|---|---|---|---|
| 进阶1:关键词检索 | ✅ | b5_extension.py:KeywordRetriever |
BM25 + jieba 中文分词 + 相关度打分 |
| 进阶2:向量检索 | ✅ | b5_extension.py:VectorRetriever |
Sentence Transformers 语义编码 + 余弦相似度 |
| 进阶3:记忆压缩 | ✅ | b5_extension.py:MemoryCompressor |
Qwen 模型压缩冗余内容,保持 Markdown 格式结构 |
| 进阶4:记忆更新 | ✅ | b5_extension.py:MemoryUpdater |
对比新旧内容,去重/合并/冲突检测 |
| 进阶5:top-k 检索 | ✅ | load_memory() 参数 |
keyword/vector 检索时返回 top_k 个最相关文档 |
5.2 三种检索模式对比
load_memory() 的 retrieval_type 参数支持三种模式:
| 模式 | 实现 | 原理 | 适用场景 |
|---|---|---|---|
id(默认) |
按 ID 逐个读文件 | 直接文件读取 | 精确加载已知记忆 |
keyword |
KeywordRetriever(BM25 + jieba) |
对全部记忆文档建 BM25 索引,用 query 分词后搜索 | 关键词匹配检索 |
vector |
VectorRetriever(Sentence Transformers) |
用 all-MiniLM-L6-v2 编码全部文档+query,计算余弦相似度 |
语义相似度检索 |
5.3 进阶1:关键词检索(BM25)
KeywordRetriever 实现:
- 中文分词:使用
jieba.cut()分词,过滤单字符 - 索引构建:
BM25Okapi对全部文档建索引 - 搜索:对 query 分词后计算 BM25 分数,返回 top_k 结果
- 相关性分数:
relevance_score字段标注 BM25 分数
5.4 进阶2:向量检索(Sentence Transformers)
VectorRetriever 实现:
- 编码模型:
all-MiniLM-L6-v2(轻量、多语言支持) - 文档编码:
model.encode()批量编码 + 归一化 - 相似度:
np.dot(embeddings, query_embedding)余弦相似度 - 阈值过滤:相似度 ≥ 0.1 才返回
5.5 进阶3:记忆压缩
MemoryCompressor 使用本地 Qwen3.5-4B 模型压缩记忆文档:
- 自动检测文档格式(standard:含章节结构 / simple:纯文本)
- 保持原有 Markdown 格式结构不变(标题层级、JSON 代码块、元信息列表)
- 只精简文本内容,不改变文档框架
- 支持
max_chars限制
# 手动压缩
python b5_memory.py --config ../configs/memory.yaml \
--compress memory/conversations/conv_001.md \
--outdir ../outputs/B5_memory
5.6 进阶4:记忆更新
MemoryUpdater 对比新旧内容并合并:
- 重复信息:去重
- 补充信息:合并到对应部分
- 矛盾信息:标记
> **冲突提醒**:[描述差异] - 保持原有 Markdown 格式结构不变
# 手动更新(需要 _new.md 文件)
python b5_memory.py --config ../configs/memory.yaml \
--update memory/conversations/conv_001.md \
--outdir ../outputs/B5_memory
6. 与团队系统的集成说明
B5 通过两个公开函数被 B1 调用:
# B1 调用位置:b1_agent_runtime.py 第371行
from b5_memory import load_memory
# 第376-379行:加载记忆
selected_memory = load_memory(
str(memory_file), runtime["selected_memory_ids"],
runtime["use_global_memory"], runtime["user_input"], str(output_dir),
retrieval_type=runtime.get("retrieval_type", "keyword"), top_k=5,
)
# 第631行:保存记忆
from b5_memory import save_memory
saved_memory = save_memory(
str(memory_file), runtime["conversation_id"], runtime["save_memory"],
str(output_dir / "messages.json"), str(output_dir / "trace.json"),
str(output_dir / "final_answer.md"), str(output_dir),
)
B5 的 load_memory() 返回的 selected_memory 被 B1 的 _memory_context() 函数转换为 XML 标签后注入 System Prompt。save_memory() 在 B1 的 Agent Loop 结束后调用,仅在 save_memory != "none" 且状态为 success 时触发。
7. 已知问题与后续改进
| 问题 | 当前原因 | 后续改进 |
|---|---|---|
| 向量检索首次加载模型较慢 | all-MiniLM-L6-v2 需从 HuggingFace 下载 |
可预下载到本地,或使用更轻量的模型 |
| 压缩/更新需加载 Qwen 模型 | 懒加载策略,但首次使用仍需等待 | 可预加载模型到内存常驻 |
| 记忆文档仅支持 Markdown 格式 | 设计如此,统一格式便于解析 | 可扩展支持更多格式 |
| 索引为单文件 JSON | 大规模记忆时文件变大 | 可考虑 SQLite 替代 |
Languages
Python
71.7%
HTML
28.3%