Vanilla RAG Memory Benchmark
August 7, 2026 · View on GitHub
一个面向服务器部署的朴素 RAG 基线,同时提供 Agent Memory Leaderboard 兼容的同步 Add/Search API、本地 Smoke 和公开数据回放工具。
当前版本:0.6.0
系统边界
项目包含两条互相独立但复用 Embedding 的链路:
离线基准:JSONL -> 切块 -> Embedding -> FAISS -> Top-k -> Answer -> 指标
赛事服务:Add -> SQLite 持久化向量 -> user_id 隔离 -> Search -> 证据列表
赛事服务只返回记忆证据,不生成最终答案。官方平台负责 Answer 和 Eval。 本地工具只能用于预检和公开数据分析,不能生成官方 Smoke/Full 成绩。
课程比赛提交时以平台页面要求为准;如果平台接收 GitHub 仓库地址,由平台拉取
本仓库并构建运行,不需要参赛者提供公网服务器。仓库不得提交 .env、API 密钥、
本地数据库、模型缓存、公开评测原始数据或运行结果。
服务器要求
- Linux 服务器;
- Python 3.10 至 3.12,或 Docker;
- 首次启动能访问 Hugging Face,或者提前准备本地模型目录;
- 建议至少 8 GB 内存;完整公开集实验建议使用 GPU;
- 自行托管参赛时需要公网 HTTPS 地址。
默认模型是 BAAI/bge-small-en-v1.5。可以通过 EMBEDDING_MODEL 改成
Hugging Face 模型名或服务器本地目录。建立数据库以后不要直接更换模型;不同
模型的向量维度可能不一致。如需更换,应使用新的数据库文件重新写入数据。
英文 BGE 模型默认使用其检索查询前缀。其他模型可通过
EMBEDDING_QUERY_PREFIX 设置前缀;显式设置为空字符串可关闭前缀。模型和查询
前缀共同构成数据库的 embedding 身份。
Python 部署
cd vanilla-rag-benchmark
python -m venv .venv
source .venv/bin/activate
pip install -r requirements.txt
export MEMORY_API_KEY='replace-with-a-long-random-secret'
export MEMORY_DB_PATH='artifacts/memory.db'
export EMBEDDING_MODEL='BAAI/bge-small-en-v1.5'
export MEMORY_RETRIEVAL_MODE='hybrid'
export MEMORY_CONTEXT_MESSAGES='0'
export MEMORY_LEXICAL_WEIGHT='0.5'
export MEMORY_OPTION_VIEW_WEIGHT='0'
export MEMORY_RESULT_WINDOW='1'
export MEMORY_RESULT_WINDOW_SEED_K='20'
uvicorn vanilla_rag.api:app --app-dir src --host 0.0.0.0 --port 8000 --workers 1
第一次启动需要加载或下载模型,因此 /health 可能需要等待。正式部署建议在
Uvicorn 前增加 Nginx/Caddy HTTPS 反向代理。当前 SQLite 方案使用单个 Uvicorn
worker,避免每个进程重复加载模型。
Docker 部署
docker build -t vanilla-rag-memory:0.6.0 .
docker run --rm -p 8000:8000 \
-e MEMORY_API_KEY='replace-with-a-long-random-secret' \
-e EMBEDDING_MODEL='BAAI/bge-small-en-v1.5' \
-e MEMORY_RETRIEVAL_MODE='hybrid' \
-e MEMORY_CONTEXT_MESSAGES='0' \
-e MEMORY_LEXICAL_WEIGHT='0.5' \
-e MEMORY_OPTION_VIEW_WEIGHT='0' \
-e MEMORY_RESULT_WINDOW='1' \
-e MEMORY_RESULT_WINDOW_SEED_K='20' \
-v "$(pwd)/artifacts:/app/artifacts" \
vanilla-rag-memory:0.6.0
模型缓存较大,正式服务器可额外挂载 Hugging Face 缓存目录。不要把密钥写入 Dockerfile、镜像、GitHub 或公开 URL。
数据库会记录首次使用的 embedding 模型身份。如果后续配置成不同模型,服务会 拒绝打开已有数据库,避免不同模型的向量被静默混用。
也可以复制 .env.example 为服务器上的 .env,修改密钥后使用 Compose:
docker compose up --build -d
docker compose logs -f memory-api
赛事 API
Health
curl http://127.0.0.1:8000/health
Health 不需要鉴权。Add 和 Search 支持以下任意一种鉴权:
X-Api-Key: <MEMORY_API_KEY>
Authorization: Bearer <MEMORY_API_KEY>
Authorization: Token <MEMORY_API_KEY>
Add
curl -X POST http://127.0.0.1:8000/add \
-H 'Content-Type: application/json' \
-H "X-Api-Key: $MEMORY_API_KEY" \
-d '{
"request_id":"local:sample:chunk-0",
"messages":[{"role":"user","timestamp":1704067200000,"content":"Ada Lovelace wrote notes about the Analytical Engine."}],
"user_id":"local:sample:user-0",
"session_id":"local:sample:session-0"
}'
Add 是同步接口。返回 HTTP 200 时,数据已写入 SQLite 且可以立即 Search。相同
request_id 和相同内容重复提交是幂等的;同一 ID 配合不同内容返回 HTTP 409。
Search
curl -X POST http://127.0.0.1:8000/search \
-H 'Content-Type: application/json' \
-H "X-Api-Key: $MEMORY_API_KEY" \
-d '{
"query":"Who wrote notes about the Analytical Engine?",
"options":["A. Ada Lovelace","B. Grace Hopper"],
"user_id":"local:sample:user-0",
"top_k":100
}'
Search 只访问完全相同 user_id 下的记忆,按相关性降序返回最多 top_k 条证据。
0.6.0 默认使用 MEMORY_RETRIEVAL_MODE=hybrid,以 Dense + BM25/RRF
召回种子消息。Search 对前 20 个种子各补充同一会话中的前后相邻消息,去重后再用原始
排名补足 top_k。该结果窗口不改变已存向量,且严格保持用户和会话隔离。
MEMORY_RETRIEVAL_MODE=dense 保留 0.3.0 的朴素稠密基线,hybrid 保留原始的
Dense + BM25/RRF 实验模式。MEMORY_CONTEXT_MESSAGES、MEMORY_OPTION_VIEW_WEIGHT、
MEMORY_RRF_K 和 MEMORY_LEXICAL_WEIGHT 控制增强检索。上下文编码会改变已存向量的
语义身份;从 dense 切换到启用上下文的 enhanced 时必须使用新的数据库文件重新写入。
0.6.0 公开数据验证
0.6.0 冻结配置使用 BAAI/bge-small-en-v1.5、Dense + BM25/RRF、词法权重
0.5、RRF k=60、结果窗口 1 和种子数 20。在 LoCoMo-Refined 的 1,382 道
公开题上,Top K=100 的 Evidence Recall 为 0.927652。使用官方评测代码中的开放题
Answer 模板和 Accuracy Judge 模板,并以 gpt-4o-mini 生成及评分时,Judge Accuracy
为 76.9175%(1,063/1,382)。该实验用于公开数据回归,不是官方 Smoke 或 Full 成绩。
0.5.0 公开数据验证
0.5.0 在带原始会话时间戳的公开 LoCoMo10 固定 200 题分层样本上验证。相对于相同
Embedding、BM25 权重和 Top K=90 的无结果窗口基线,Evidence Recall 从 0.7924 提升到
0.8763,零召回问题从 29 降至 14。使用 gpt-4o-mini 的端到端 Judge Accuracy 从
49.5% 提升到 54.0%,逐题比较为胜 21、负 12。该实验仅用于公开数据回归,不是官方
Smoke 或 Full 成绩。
0.4.0 公开数据验证
0.4.0 冻结前在公开 LoCoMo10 数据的 1,533 个有答案、有证据问题上进行了配对验证,
每个系统使用相同消息、BAAI/bge-small-en-v1.5 和 Top K=90。该实验没有读取或分析
任何官方 Smoke 私有负载。
| 模式 | Evidence Recall@90 | 零召回问题 | 全召回问题 | Answer literal hit |
|---|---|---|---|---|
dense (0.3.0 基线) | 0.7965 | 224 | 1,124 | 0.3183 |
enhanced (0.4.0) | 0.8822 | 109 | 1,267 | 0.3399 |
逐题比较为增强版胜 228、平 1,259、负 46;10 个会话的平均 Evidence Recall 均未下降。 16 路并发、160 次 Search 的本地负载测试无失败,Search P95 为 0.349 秒。以上结果仅用于 公开数据回归和工程选型,不是官方榜单成绩。
本地 Smoke
服务启动后执行:
python scripts/local_smoke.py \
--base-url http://127.0.0.1:8000 \
--api-key "$MEMORY_API_KEY"
脚本会验证 Health、Add 字段回传、幂等、冲突、Search Schema、选择题、空用户和 跨用户隔离。它不会联系赛事官网,也不会消耗官方 Smoke 配额。
完成后重启容器,再执行一次脚本并手动搜索之前写入的用户,以验证挂载目录和 数据库恢复。正式 Smoke 前还应在服务器上进行并发和长时间稳定性测试。
安装依赖并配置环境变量后,可先执行服务器环境检查和小规模负载测试:
python scripts/server_preflight.py
python scripts/load_test.py \
--base-url http://127.0.0.1:8000 \
--api-key "$MEMORY_API_KEY" \
--users 8 --searches-per-user 10 --concurrency 4
本地赛事流程回放
scripts/local_benchmark.py 接受规范化 JSONL。每行代表一个隔离样本:
{"id":"sample-001","session_id":"session-001","messages":[{"role":"user","timestamp":1704067200000,"content":"memory text"}],"questions":[{"id":"q-001","question":"question text","answer":"gold answer","options":["A","B"]}]}
示例回放:
python scripts/local_benchmark.py \
--input data/sample_competition.jsonl \
--output artifacts/sample_api_results.jsonl \
--base-url http://127.0.0.1:8000 \
--api-key "$MEMORY_API_KEY" \
--run-label sample-v1 \
--top-k 100
脚本按官方的 20 条消息/2,000 词边界组装 Add,逐题调用 Search,并计算简单的
answer_in_context_rate。公开数据集需要先转换成上述规范格式。该指标用于排错,
不是官方总分。
LoCoMo-Refined 和 LongMemEval 的常见公开格式可以用内置转换器处理:
python scripts/prepare_public_data.py locomo \
--input /data/locomo.json \
--output data/normalized/locomo.jsonl
python scripts/prepare_public_data.py longmemeval \
--input /data/longmemeval.json \
--output data/normalized/longmemeval.jsonl
公开仓库可能调整字段;转换后先抽查 messages、questions 和
evidence_texts,再进行整套回放。其他数据集使用本节展示的规范 JSONL 作为
稳定接入边界。
离线 Vanilla RAG
服务器安装依赖后:
export PYTHONPATH=src
python -m vanilla_rag.cli index \
--documents data/sample_documents.jsonl \
--output artifacts/sample_index \
--chunk-size 160 \
--chunk-overlap 40
python -m vanilla_rag.cli ask \
--index artifacts/sample_index \
--question "Who developed the Analytical Engine?" \
--top-k 3 \
--generator none
python -m vanilla_rag.cli evaluate \
--index artifacts/sample_index \
--questions data/sample_questions.jsonl \
--top-k 3 \
--generator none \
--output artifacts/sample_results.jsonl
自定义模型建索引时,模型名称会写入索引元数据。Ask/Evaluate 不指定
--embedding-model 时会自动复用该名称。
启用端到端回答:
export OPENAI_API_KEY='...'
export OPENAI_BASE_URL='https://api.openai.com/v1'
python -m vanilla_rag.cli evaluate \
--index artifacts/sample_index \
--questions data/sample_questions.jsonl \
--top-k 5 \
--generator openai \
--chat-model gpt-4o-mini \
--output artifacts/llm_results.jsonl
离线指标包括真实的多相关文档 Recall@k、MRR、答案证据覆盖率、EM 和 Token F1。
没有相关文档标注或没有启用生成时,对应汇总字段返回 null,不会伪装成零分。
HotpotQA 转换
python -m vanilla_rag.cli prepare-hotpot \
--input /data/hotpot_dev_distractor_v1.json \
--output-dir data/hotpot_dev
HotpotQA 是普通多跳 RAG 基准,不是当前 AML 官网列出的正式文本套件。LoCoMo、 LongMemEval、PersonaMem、CLBench 和 BEAM 等公开集应转换为本项目的规范 JSONL 后再进行 API 回放。
测试
PYTHONPATH=src python -m unittest discover -s tests -v
当前单元测试不需要下载模型,覆盖切块、指标、SQLite 持久化、幂等和用户隔离。 FastAPI、SentenceTransformer、Docker 和真实模型的端到端验证需要在安装完整依赖 的服务器上执行。
评测数据清理
查看数据库统计:
python scripts/memory_admin.py --db artifacts/memory.db stats
按照评测 user_id 前缀删除某次运行的数据:
python scripts/memory_admin.py --db artifacts/memory.db \
purge-prefix --user-prefix 'eval:<run_id>:' --confirm
删除前应保留官方要求的必要审计信息,但不得保留评测原文副本超过许可期限。
正式评测前检查
- 固定 Git Commit、镜像标签、依赖、模型和配置;
- 本地 Smoke 全部通过;
- 重启恢复、并发、限流和长时间运行通过;
- 公开数据集无缺失样本,跨用户泄漏率为零;
- 公网 HTTPS Health/Add/Search 可从外部访问;
- 使用官方 Smoke 修正集成差异;
- 最后才启动 Full。
公开赛事契约摘要见 docs/competition-contract.md。