# M03 检索、上下文与记忆

> 本章产出：一个有权限过滤、来源引用和失败分解的检索链路，以及可测量的上下文预算策略。

## 1. 为什么“接了向量数据库”还会答错

用户问：“包裹在运输中，能直接退款吗？”检索器找到了包含“退款”的旧政策，模型认真引用了它，结论仍然错误。另一次检索拿到了当前政策，却属于另一商店；还有一次检索找对了政策，但上下文截断删掉了“仅适用于物流确认丢失”的限定。

这里分别是版本错误、权限错误和上下文选择错误。增加Embedding维度不能直接修复它们。RAG是从数据治理到答案证据的完整链路，而不只是相似度搜索。

## 2. 数据进入索引之前，先定义身份与版本

每个文档块保存`doc_id`、`chunk_id`、`tenant_id`、`policy_version`、有效时间、正文、来源以及访问控制标签。文档更新需要建立新版本并使旧索引失效，不能把旧政策的引用ID悄悄映射到新内容。

分块的目标是保留可独立理解的语义单元。退款条件与例外最好在同一个块里；过长的表格则保留表头与上级主题。复制太多重叠段落会浪费上下文，也可能让重复内容压倒其他证据。

查询时先根据认证身份限定可见文档，再在可见集合中检索。若后端只能全局检索后过滤，也必须保证不可见内容不进入日志、模型或缓存，并考虑过滤后召回不足。课程默认用先过滤的简单实现。

## 3. 用词法检索建立一个可解释基线

BM25用词频、文档长度和词的稀有程度对文档打分。一个常见形式是：

`score(d,q) = Σ IDF(t) × tf(t,d)(k1+1) / [tf(t,d)+k1(1-b+b×|d|/avgdl)]`

本课程选`IDF(t)=ln(1+(N-df(t)+0.5)/(df(t)+0.5))`，`k1=1.2`，`b=0.75`。这些是教学配置，不代表对中文售后最优。查询词在简单实现中去重，以免不小心重复累加。

中文分词必须明确。参考实现将连续中文串拆成双字片段，并保留拉丁字母/数字词元。这是为了无需外部依赖地复现实验，不是生产分词推荐。它会漏掉跨词的语义，正好作为后续优化对象。

先测试三个可解释问题：查询中“丢失”是否召回丢件政策？“还没收到”能否匹配同一概念？“退款”这个宽泛词会不会把例外条款挤出前列？不要跳过基线直接依赖复杂系统。

## 4. 混合检索与重排解决不同的问题

向量检索可以补充词面不同的语义匹配，但也可能把主题相近、条件不同的内容混在一起。词法检索容易精确命中订单号、错误码和产品名。二者可以通过排序融合组合。

本课程用倒数排名融合RRF举例：`RRF(d)=Σ 1/(k+rank_i(d))`，排名从1开始；缺席某列表贡献0。它不要求两种原始分数在同一尺度。课程互动默认`k=60`只是常见演示配置，不是调优结论。

重排器在小候选集合上重新判断相关性；它不能弥补候选集合里根本没有正确文档的情况。先测Recall@k，再测重排后的MRR或nDCG，最后测答案。不要只看一个端到端分数而不知道瓶颈在何处。

Anthropic的Contextual Retrieval提供了在索引时补充块级上下文的思路。课程扩展会让你比较原始块与带文档主题的块，但不照搬其报告的改进幅度。[S04](../../references/SOURCES.md#s04)

<!-- interaction:I06 -->

## 5. 有引用，不等于被证据支持

“引用了P02”只能证明系统输出了一个标识，不能证明结论由P02支持。我们分别检查证据存在、权限允许、版本有效、语义支持，以及引用粒度是否足够定位原文。

对于“物流仍在运输中”，正确回答应该引用当前政策的条件，指出当前订单尚不满足丢件退款条件，并说明可采取的下一步。它不应把“运输中可能丢失”偷换成“已确认丢失”。

回答找不到证据时，允许明确说明不足或请求澄清。不能要求模型每次都给确定结论，同时又惩罚所有合理拒答。

## 6. 上下文预算是一项调度决策

设可用上下文上限为W，系统与工具定义占S，保留的历史占H，检索证据占R，预计输出预算占O，安全余量为M，则必须满足`S+H+R+O+M≤W`。真实token数与具体模型分词器有关；网页卡片中的token值是固定教学输入，不用中文字符数冒充实际token。

给你1200个输入预算，候选内容包括：当前订单事实200、有效政策300、用户明确限制100、旧工具输出700、历史寒暄400。最便宜的策略不是把所有内容等比例缩短，而是保留决定动作正确性的事实，先去掉无关或可重新获取的内容。

压缩不能改变事实优先级。摘要里“用户同意退款”不是审批记录；摘要里的订单余额也不是执行时的最终余额。业务事实应该在需要写操作时重新读取与验证。

<!-- interaction:I07 -->

## 7. 四种上下文策略，怎样公平比较

Full history保留全部记录，作为小任务基线；Sliding window只保留最近若干轮，简单但可能丢掉早期约束；Summary compaction把较早内容压缩成结构化摘要，有额外模型费用和信息失真风险；Observation masking用受授权的引用替换较老的大工具结果，需要支持按需重新读取。

比较时固定任务、模型配置和总预算，至少记录成功率、输入/输出token、摘要费用、重新检索次数与总延迟。若摘要策略降低每轮token却导致更多轮调用，总成本仍可能上升。

上下文工程强调持续选择进入模型的信息，是一个有用的分析视角；“长上下文必然差”或“摘要必然提升效果”都不是本课程结论。[S03](../../references/SOURCES.md#s03)

## 8. 记忆不等于把聊天记录永远保存

任务内状态、会话记忆和跨会话偏好应该分开。临时订单事实有时效；用户偏好需要来源和更正机制；授权记录由业务系统保存；原始工具输出可能包含敏感信息，不能默认写入长期向量库。

一个长期记忆对象可以包含`subject`、`fact`、`source`、`observed_at`、`expires_at`和`confidence`。但confidence不是真实性证明。发生冲突时要按数据权威、时间和用户更正处理，而不是简单选择相似度最高的记忆。

跨租户共享记忆默认禁止。删除原文后，相应索引、摘要和缓存也需要明确的失效策略。课程测试通过两个租户的相似订单验证这一边界。

## 9. 完成标准

L03要求先实现确定性词法基线，再用预先给定的第二组排名演示RRF；真实Embedding属于可选扩展，不能把给定排名当成已跑模型的成绩。报告至少一个召回失败、一个版本错误和一个上下文丢约束案例。

你需要能够解释：检索失败与生成失败如何区分，为什么权限过滤在模型之前，为什么会话摘要不能授权退款，以及压缩如何可能增加而不是减少总成本。

<!-- knowledge:K03 -->
<!-- knowledge:K04 -->
