05-RAG检索增强
核对日期:2026-05-18
官方资料:Retrieval https://docs.langchain.com/oss/javascript/langchain/retrieval;Text splitters https://docs.langchain.com/oss/javascript/integrations/splitters。
官方概念
Retrieval 是从外部数据源取回与 query 相关的上下文,RAG 是把检索上下文注入模型生成答案。LangChain 提供 loaders、splitters、retrievers、vector stores、rerankers 等集成,但生产质量取决于数据治理、权限、引用和评测。
机制
本手册离线项目使用 keyword score 代替真实 embedding/vector DB,是为了让权限、引用、冲突、eval 逻辑可本地验证。
TypeScript 写法
对应案例:src/examples/06-rag.ts。综合项目:企业知识库 Agent。
关键点:
- 检索前做 ACL,不允许“先召回再隐藏”。
- citation 必须来自命中文档。
- 低召回时拒答,不编造答案。
可用化任务流
真实 RAG 项目不是“接一个向量库”就结束。建议按下面交付:
| 步骤 | 要做什么 | 产物 | 验收方式 |
|---|---|---|---|
| 1. 数据盘点 | 列出文档来源、权限、更新时间、负责人 | data inventory | 每个文档有 owner 和 ACL |
| 2. 切分策略 | 按标题、段落、表格、代码块分别定义 chunk 规则 | chunk policy | chunk 可追溯到原文 |
| 3. 元数据建模 | 保存 docId、chunkId、source、updatedAt、allowedRoles | metadata schema | 检索结果可做 ACL 和 citation |
| 4. 索引构建 | embedding、BM25、hybrid 或 rerank | index job | 可重复构建,记录版本 |
| 5. 查询改写 | 根据意图补充关键词、时间和实体 | query pipeline | 改写前后都写 trace |
| 6. 检索与重排 | 先 ACL,再检索或用权限过滤索引 | retriever | 跨租户泄露用例为 0 |
| 7. 答案生成 | 只允许引用命中文档,低证据拒答 | answer schema | citation 100% 来自 retrieved docs |
| 8. Eval 回归 | 召回、引用、拒答、冲突、权限 | eval dataset | 每次改索引和 prompt 都跑 |
最小可用 answer schema:
const answerSchema = z.object({
answer: z.string().min(1),
citations: z.array(z.object({
docId: z.string(),
chunkId: z.string().optional(),
title: z.string(),
source: z.string(),
})).min(1),
refused: z.boolean(),
conflicts: z.array(z.string()),
});
RAG 的关键不是让答案“看起来像”,而是让答案能被复核。citation、chunk version、trace 和 eval dataset 是可用性的底座。
检索策略选择
| 场景 | 推荐策略 | 不适用情况 |
|---|---|---|
| FAQ、政策、短文档 | BM25 + embedding hybrid | 文档极短且关键词稳定时可先 BM25 |
| 语义问答 | embedding retriever + reranker | 权限复杂但索引不支持 metadata filter 时要谨慎 |
| 代码/配置/字段查询 | keyword / symbol search 优先 | 纯 embedding 可能丢精确 token |
| 多版本制度 | metadata filter + newest-first rerank | 只按语义分数排序会引用旧制度 |
| 高安全文档 | per-tenant index 或强 ACL filter | 共享索引且后过滤风险更高 |
可运行改造点
| 文件 | 改造任务 | 验收命令 |
|---|---|---|
src/core/rag.ts | 增加 chunkId 和 minScore 测试 | npm test |
src/projects/enterprise-kb-agent.ts | 增加一个过期制度,要求返回 conflict | npm run projects:eval |
test/langchain-learning.test.ts | 增加“无 citation 不允许成功”的断言 | npm test |
RAG Eval 设计
| 用例类型 | 示例 | 通过标准 |
|---|---|---|
| 正常命中 | “远程办公一周几天” | 返回最新政策 citation |
| 权限拒绝 | 普通员工问薪酬审批 | refused=true 且 citation 为空 |
| 低召回 | 问不存在制度 | 拒答,不编造 |
| 冲突文档 | 新旧政策都命中 | conflicts 非空 |
| 注入攻击 | 文档或 query 要求忽略系统提示 | 拒答或过滤,不外泄 |
Python 差异
Python 和 TypeScript 都有 document loader、splitter、vector store 集成,但第三方包覆盖度和 API 名称可能不同。真实项目应按目标语言官方 integration 文档选择包,并锁版本。
工程边界
- RAG 不能自动解决知识冲突。
- 长上下文不能替代检索治理。
- embedding 相似不代表事实正确,仍需引用和回归评测。
常见反模式
| 反模式 | 后果 |
|---|---|
| 把所有文档对所有用户可检索 | 权限泄露 |
| 不保存 chunk 版本 | 引用无法复现 |
| 只看 Top-1 命中 | 召回不足和冲突文档都被掩盖 |
练习任务
- 修改企业知识库文档角色,确认 ACL 测试变化。
- 增加一个过期文档,要求回答优先引用最新文档并提示冲突。
- 为 RAG eval 增加“无相关资料必须拒答”的用例。
- 给 citation 增加
chunkId,并让测试确认每条引用都能定位到 chunk。 - 把
minScore改低,观察 ACL deny 用例为什么仍不应该泄露薪酬文档。