项目背景与目标
前面的文章分别验证了加载、切分、Embedding、VectorStore、混合检索、两步式 RAG、多轮改写和评估。本项目把它们收束成一个离线可运行的巡检知识库工作台。
项目只使用 Python 标准库和完全自创的数据,首次运行不需要模型账号或向量数据库。它用字符哈希向量模拟稠密检索,用简化词频分数模拟关键词检索,用 RRF 融合,用抽取式 Demo Generator 生成带来源回答。这个后端用于验证工程链路,不代表生产模型质量;换成真实 Embedding、BM25、Reranker 和 LLM 后,上层协议与评估集保持不变。
验收目标:
- 文档按版本切分,生成稳定 Chunk ID 和内容哈希;
- 索引可以重复构建,不产生重复 Chunk;
- 查询同时经过关键词和向量两路召回,并按权限过滤;
- 回答附带合法来源,证据不足时拒答;
- 追问可以根据当前线程历史补全设备实体;
- 固定数据集输出检索、答案、引用、拒答和延迟指标。
整体架构
documents.jsonl
↓ 校验、清洗、版本化切分
index.json ───────────────┐
↓ │
问题 → 独立查询改写 → 权限过滤
↓ │
关键词召回 + 哈希向量召回 │
↓ RRF 融合 │
候选 Chunk ──────────────┘
↓
上下文与来源编号 → Demo Generator → 答案/拒答
↓
trace.jsonl + eval.jsonl 离线评估
项目文件位于 examples/07.17-rag-workbench:

在线问答链产生答案、来源和 Trace,离线评估再把失败样本反馈到数据、索引和检索配置。
examples/07.17-rag-workbench/
├── rag_workbench.py
├── documents.jsonl
├── eval.jsonl
└── README.md
核心流程
数据与索引契约
每条文档都包含 doc_id/title/version/scope/text。Chunk ID 由文档 ID、版本和序号组成,内容哈希用于检查实际文本变化。索引头记录算法、维度、Chunk 大小和构建时间。
真实增量更新可按 doc_id + version 对比:先完整写入新版本,运行查询集验收,再切换索引别名并删除旧版本。示例数据量很小,因此 build 直接确定性重建,重复运行输出相同 Chunk 集合。
检索与权限
查询先从当前线程历史补全 AX-数字 实体,再只保留 public 或当前用户范围的 Chunk。关键词路和向量路分别排序,RRF 根据名次融合;原始分数不直接相加。
过滤发生在候选排序前。若先检索全库再过滤,未授权内容可能进入进程并挤掉合法候选。
生成与证据不足
Demo Generator 从候选中选取与查询有词项交集的句子,附加 [S1] 编号;没有足够词项交集时返回“现有知识库无法确认”。项目同时返回来源对象,前端不需要解析自然语言来猜文档。
替换为真实 LLM 时,应继续保留:上下文预算、来源编号、拒答提示、引用编号校验和原始候选记录。
运行项目
cd "examples/07.17-rag-workbench"
python rag_workbench.py build
python rag_workbench.py ask "关键告警需要通知谁?" --scope team-a --trace trace.jsonl
python rag_workbench.py ask "AX-3 恢复后观察多久?" --scope team-a
python rag_workbench.py evaluate
一次问答输出类似:
{
"query": "关键告警需要通知谁?",
"answer": "关键告警必须立即通知值班负责人 [S1]。",
"refused": false,
"sources": [{"id": "S1", "chunk_id": "duty:v2:000", "title": "告警值班规则"}]
}
评估命令逐条打印结果,并给出汇总:
{"cases":4,"retrieval_recall":1.0,"answer_accuracy":1.0,"citation_validity":1.0,"refusal_accuracy":1.0}
具体结果以实际运行输出为准。固定数据很小且算法是教学实现,高分只证明示例链路通过,不代表能够外推到真实知识库。
结果、评估与阶段复盘
eval.jsonl 同时包含:
- 精确规则问答;
- 同义表达;
- 设备编号问题;
- 知识库中没有依据、应当拒答的问题。
每条记录分别检查相关文档是否进入 Top-k、答案是否包含必要事实、引用是否属于本次来源,以及应拒答时是否拒答。接入真实模型后还应增加逐断言忠实性、人工正确性、P50/P95 延迟、Token、成本和失败率。
这个项目让我更清楚地看到:RAG 不是“向量库 + Prompt”的两个组件,而是一条数据产品链路。解析、版本、权限、检索、拒答、引用和评估中的任何一层失效,最终流畅的文字都可能掩盖错误。
遇到的问题
中文关键词被英文式分词破坏
示例同时保留中文单字和双字片段,并保留英文数字连续串。生产系统应根据领域词表选择分词器,并专门测试设备编号、错误码和中英文混排。
RRF 总会给候选一个分数
融合名次不代表证据足够。Demo Generator 额外检查查询与句子的词项交集;真实系统还需要校准检索阈值和不可回答数据集。
引用存在但不支持答案
项目的引用合法性只检查编号属于当前来源。更强的忠实性评估要把答案拆成原子断言,逐条验证来源支持关系。
追问中的实体不明确
只有历史中存在明确的 AX-数字 时才替换“它”。无法确定时保留原问题并触发拒答或澄清,不能随意选择最近名词。
改进方向
- 用真实 Embedding 与 BM25 替换教学实现,保存模型 revision;
- 接入持久向量库和服务端元数据过滤,增加更新与删除集成测试;
- 加入 Cross-Encoder Rerank,并在同一评估集上比较收益和延迟;
- 使用真实 LLM 生成,同时加入结构化回答和逐断言引用验证;
- 将线程状态持久化并加入认证主体隔离、历史压缩和过期策略;
- 接入 LangSmith 或自建 OpenTelemetry 追踪,建立发布前回归门槛;
- 增加提示注入、越权查询、敏感信息和恶意文档测试集。
小结
这个离线项目完成了第 07 阶段的最小闭环:数据可版本化,检索可解释,权限在检索前生效,回答有来源,证据不足会拒答,修改后能用固定数据集回归。下一阶段进入 Agent 时,这条确定的 RAG 链应继续作为可靠工具,而不是被不受控的循环替代。
许可协议:CC BY-NC 4.0
更新于 1 小时前
觉得文章有帮助?点个赞吧!
0 条评论


