Appearance
第11章 · Embedding、RAG 与知识边界
hash-vector/lexical baseline 不冒充 live semantic embedding 质量。
先修:第4章的概率目标、第5章的 tokenizer、第10章的行为评估。
本章目标
- 理解 embedding、chunking、召回、rerank、引用和索引更新的因果链路。
- 实现一个使用合成/公开文档的最小 RAG,先过滤可见文档再排序。
- 用命中、未命中、冲突、越权和 prompt-injection fixture 测量召回、引用和拒答。
公式与 shape
向量检索可从 cosine similarity 开始:
固定评估集中的基本指标:
引用正确率是“被证据直接支持的引用主张数 / 全部引用主张数”,无依据断言率是“无证据支持的可核验主张数 / 全部可核验主张数”。
| 对象 | shape | 约束 |
|---|---|---|
| query embedding | (D,) 或 (B, D) | 与文档 embedding 维度一致 |
| document matrix | (N, D) | chunk ID 与源文档稳定绑定 |
| top-k results | (B, K) | 先 ACL/filter,再 score/rank |
| evidence chunk | record | 含 chunk ID、source、score 和 citation |
数学桥接:第2章 → 第11章
本章的 RAG 和 embedding 检索建立在向量相似度之上,而向量相似度的核心就是第2章 §4 的点积:
向量检索与 dot_product:cosine similarity magnitude(v) 的推广(二维标量 → N 维向量范数),分子就是 dot_product(q, d) = Σ q[i]·d[i]。当 query 和 document 在同一个 embedding 空间时,"语义相近" 直接等价于 "点积大"——这和 attention 中
Embedding 空间与 entropy:第2章 §6 的 entropy(probs) 衡量一个分布的不确定性。在 RAG 中,如果模型对自身回答的不确定性高(输出分布的熵大),检索系统应该被更多地依赖——这就是 "知道自己的边界" 的量化版本。recall@k 衡量检索系统能否覆盖 gold chunk,本质上是减少模型"编造"的概率。
Chunking 与 tokenizer 的类比:第5章的 BPE 把文本切成 token,RAG 把文档切成 chunk——两者都在"保持语义完整性的前提下拆分序列"。Chunk 太大浪费检索精度,太小丢失上下文;这和 BPE 词表大小选择的率失真权衡同构(第2章 §6 直觉)。
前端类比:embedding 检索 = CSS transform: translate(x, y) 后的 getBoundingClientRect() 碰撞检测——两个元素在坐标空间的距离越近,判定为"相关"的概率越高。RAG pipeline = Vue Router 的 navigation guard(前置路由守卫)——先过滤不可见路由(ACL),再匹配最佳组件(retrieve),最后渲染(generate),顺序不可颠倒。
交互观察
切换锚点词观察最近邻如何落在同一语义簇;拖动 perplexity 看全局簇间结构与簇内局部结构的此消彼长。注意这是手工坐标的教学地图,不是真实 embedding 模型的输出——它只负责建立“语义相近则向量相近”的直觉,本章的 recall/citation 证据仍来自离线 fixture 与测试。
从零实践
用固定文档 fixture 实现 chunking、稳定 ID、索引 hash 和简单 lexical/BM25 或 embedding ranking。
为不同 principal 建立可见集合;在排序前过滤不可见 chunk,答案只能引用可见 evidence。
对命中、未命中、冲突、越权和 evidence 中含有指令句的样例分别运行。
当前入口与测试:
bashPYTHONPATH=python python -m pytest python/tests/test_rag_acl.py python/tests/test_embedding_rerank.py -q
当前 python/agent_core/rag.py 已实现确定性 alias-only query rewrite、BM25 风格索引、ACL 前置、稳定 chunk citation、结构化 claim 冲突拒答和无证据拒答;embedding.py 提供无权重下载的 hash-vector shape baseline 与透明 lexical rerank。固定四查询离线报告的 recall@1/recall@3 和 rerank hit rate 均为 1.0(gold 分母 3),citation traceable rate 为 1.0(分母 3),越权 chunk 返回数为 0,unsupported claim rate 为 0.0。这些结果只说明合成 fixture 的接口与排序契约;hash-vector 是教学 baseline,不冒充语义 embedding 质量。详见 evidence/11-rag-eval-v1.json。
故障注入与预期信号
| 注入 | 预期失败信号 | 修复后证据 |
|---|---|---|
| 把 evidence 文本当 system 指令 | 文档中的恶意句改变工具/回答流程 | evidence 以转义 data block 渲染 |
| 结构化 claim 出现相互冲突值 | 确定性答案掩盖冲突 | 拒答并保留可审计 citations,等待人工 resolution |
| chunk ID 随运行顺序变化 | citation 无法重放或追溯 | 稳定 ID、source 和 index hash 一致 |
论文与延伸
- Retrieval-Augmented Generation for Knowledge-Intensive NLP Tasks(Lewis 等,2020)
- 选读:ColBERT: Efficient and Effective Passage Search via Contextualized Late Interaction over BERT。
前端/Agent 迁移
RAG 是一个受数据可见性和证据链约束的信息系统,不是单一向量数据库调用。
- Embedding ≈ CSS transform + getBoundingClientRect:token → vector 是给每个词一个”坐标”,cosine similarity 衡量两个词在语义空间中的距离——就像
getBoundingClientRect()返回元素的可视坐标,用距离判断两个元素是否在同一个语义区域。 - RAG ≈ Vue Router navigation guard:检索是路由守卫——在渲染组件(生成回答)之前,先检查是否有匹配的权限/证据(chunk),没有就跳转去补充。
- Chunking ≈ 代码分割 / dynamic import:长文档按 token 预算切分,就像
import()按路由懒加载 chunk——太大会拖慢首屏(context window),太小会丢失依赖(语义断裂)。 - Citation ≈ Source map:前端可把 citation 当作可展开的 source map;Agent 应将”检索到的证据”与”模型生成的解释”分开记录,未命中时进入拒答状态。
口述与自测(不看资料,5–10 分钟)
- 按“ACL filter → chunk/index → retrieve → rerank → citation/拒答”口述一条查询;解释为什么 ACL 必须在排名前,而不是在答案生成后过滤。
- 对照 RAG 论文的知识增强思路,说明 recall@k、citation correctness 与 unsupported-claim rate 分别测什么;给出一个 prompt injection 或冲突证据时必须拒答的理由。
- 用第2章的
dot_product([1,0], [0,1]) = 0和dot_product([1,0], [1,0]) = 1解释 cosine similarity 的几何直觉:正交向量不相似,平行向量最相似;embedding 检索就是在这个几何空间里找最近的邻居。 - 用第2章的
entropy解释为什么模型不确定性高时需要更多检索:当模型输出的熵大时,说明它对答案不确定,RAG 检索提供的外部证据能降低这种不确定性——recall@k 就是衡量检索系统能否把正确的 evidence chunk 带回。 - 对比第5章 BPE 的 merge 策略和 RAG 的 chunk 策略:两者都是"在保持语义的前提下拆分长序列";BPE 按频次合并,RAG 按语义边界切分——过度合并或 chunk 过大都会丢失信息。
实验与参考
动手实验
把检索增强生成的四个工程约束钉死:ACL 过滤必须发生在相似度排序之前;召回率 recall@k 达可接受阈值;每条回答必须可回链到具体 chunk;无证据时拒答而非编造。
环境准备
bash
cd <仓库根>
export PYTHONPATH="$PWD/python"命令与预期输出
上方「从零实践」第 4 步已跑过同一条命令(test_rag_acl.py + test_embedding_rerank.py);这里补充其独立判定信号:
text
.................. [ 100% ]
2 passed in 3.18s
判定信号:
ACL 过滤节点出现在相似度排序之前
recall@5 ≥ 0.85(人工标注的 gold chunk 集合)
每条回答附带 chunk_id 引用、回链命中
无检索命中时返回拒答而非生成内容概念图
图:RAG 检索-生成-安全三层架构 — 检索侧把用户查询编码为向量、做 top-k 相似度检索、ACL 权限过滤和 rerank 重排;生成侧把检索结果组装为 prompt、调用对齐后的 LLM 生成带引用的回答;安全层处理空召回(拒答兜底)和权限不足(降级回答)两种边界情况。第5章 BPE 的 token 边界决定 chunk 切分策略,第6章的 embedding 层提供 Query 和 chunk 的向量空间。
故障注入清单
| 故障 | 表现 | 修复 |
|---|---|---|
| 先排序后 ACL | 越权文档进入 top-k 并被拼入 prompt 造成越权回答 | 检索流水线调换顺序,ACL 必须在排序之前 |
| chunk 过大 | 引用无法定位到具体段落,回链模糊、用户难以核查 | 控制 chunk 长度在 200 至 500 token 之间并保留偏移量 |
| embedding 未归一化 | 余弦距离退化为点积相似度,长文档主导排序 | 在 embedding 推理后做 L2 归一再写入索引 |
| 检索为空仍生成 | 模型基于先验自由发挥产生幻觉内容 | 召回数量为零时走拒答分支并提示证据不足 |
资源 / 成本 / 隐私
默认使用合成/公开许可文档和本地 hash-vector/lexical baseline,预计 gross cost 为 0;不上传文档到第三方 embedding API。每个 chunk 带 ACL,私人资料不得进入索引。
Evidence
仓库当前机器证据(只读快照)
evidence/module-manifest-v1.json 中 11.evidence 指向当前文件:evidence/11-runtime-v1.json。这是当前 checkout 的脱敏机器运行记录,只覆盖该 JSON 记录的命令、指标、产物和已知失败;它不是学习者提交,也不能推出学习者已完成本章。更完整的固定四查询离线报告见 evidence/11-rag-eval-v1.json,也不替代 live semantic retrieval 或学习者证据。
学习者提交模板(待填写,不是当前机器证据)
复制下面模板并填写自己的真实运行结果。所有 <...> 都是未填写状态;actual 和 artifacts 尤其不能被当作已运行或已通过。artifacts 必须替换为本次提交中真实存在的仓库相对路径。
yaml
schema: learn-llm.evidence.v1
module: 11-rag
commit: <learner-commit-sha>
verified_at: <iso-date>
environment: <sanitized-python-device>
seed: 9
commands:
- PYTHONPATH=python python -m pytest python/tests/test_rag_acl.py -q
- PYTHONPATH=python python <learner-rag-evaluation-fixture>
metrics:
- name: recall_at_k
expected: <versioned-threshold>
actual: <recorded-value>
- name: citation_correctness
expected: <versioned-threshold>
actual: <recorded-value>
- name: unsupported_claim_rate
expected: <versioned-threshold>
actual: <recorded-value>
artifacts:
- <learner-repo-relative-artifact-path>
cost:
gross_usd: 0
credit_usd: 0
licenses:
- source: <source>
version: <version>
license: <license>
attribution: <attribution>
redistribution: <redistribution>
known_failures:
- <sanitized-failure-or-none>没有 ACL 前置、拒答、citation trace 和固定分母时,本章保持 gate;当前离线指标不宣称 live semantic retrieval 通过。