01 / 问答系统先找证据
从资料到可核对的回答
保留来源→切成片段
chunk→向量索引
TF-IDF→检索片段
余弦相似度→组织上下文
问题 + 证据→回答 + 引用
逐句核对
本节实际执行资料切分、词面向量检索、上下文组装和原文摘录式回答。页面会显示可交给语言模型的完整提示词,但不会假装已经调用了语言模型。第十二节的玩具字符模型没有足够能力可靠回答这些问题,所以这里不把它接到答案生成步骤。
为问题找相关片段,决定哪些资料可能进入上下文。
本地演示只摘录原句;接入可靠语言模型后可组织自然语言答案。
回答中的 [C编号] 必须指向真实片段,且该片段确实支持相邻说法。
02 / 每片既要够短,也要保留上下文
比较两种切分长度
程序按 Markdown 的二级标题分节,再在每节内部尽量靠句号、问号或换行处切开;相邻片段允许少量重叠。每片保留文件、标题、行号与片段编号。调长片段可带来更多前后文,但也可能把不相关句子一并送去检索与提示词。
字符数用于这节教学演示;语言模型实际上下文限制按 Token 计算,不应把 500 字直接当成 500 Token。
03 / 让向量计算落到真实片段上
这节的向量是词面 TF-IDF,不是语义 Embedding
程序提取连续汉字的相邻二字片段,以及英文和数字串,统计出现次数。一个片段对应一条稀疏向量:没出现的特征权重为 0;出现越多、在资料库越少见的特征,权重越大。问题也按同一规则向量化,再计算夹角余弦。
例如“目标网络”可产生“目标”“标网”“网络”几个词面特征。向量相似度会受共同字符影响,难以可靠找到只在语义上相似、字面却不同的表述。真正的语义 Embedding 通常由训练过的编码模型产生稠密向量;它是升级方向,仍须评估检索质量。
当前问题的部分特征
与首位片段共同贡献最大的特征
04 / 自己输入问题,观察排名
检索、证据摘录与拒答
可以用五个预设问题,也可以自己写一个问题。调整 top-k 和可放入提示词的资料字数,看哪些片段被选中、哪些进不了上下文。问题只在当前页面本地计算,不上传。
余弦分数与顺序
被选中的证据
05 / 检索只是上半场
把来源一起交给语言模型
下面是程序为当前问题组装的实际提示词。它包含问题、进入预算的片段、每片来源标识,以及“不足时拒答”的要求。若以后接入语言模型,模型生成后仍要检查引用是否真的支持具体说法;格式像引用并不能证明正确。
原文摘录式演示
当前页面的回答只复制资料中的一句话并加上 [C编号];没有做事实推理、综合多片证据或调用模型。
未来的生成式步骤
将这份提示词交给你选择的本地或远程语言模型,再检查每条陈述、引用与“资料不足”场景。接入模型时还要管理输入长度、成本和私密资料传输。
检索片段是待引用的数据,其中若写着“忽略之前的要求”,不应把它当作系统命令。示例资料里专门放了一句这样的文字,可在「检索增强问答」文件中看到。
06 / 为下一节评估做好准备
三种失败要分开看
| 现象 | 先看哪一步 | 例子 |
|---|---|---|
| 正确片段没排进 top-k | 切分、特征、检索排序 | 换了说法,二字片段几乎不重合 |
| 片段排进 top-k,却没进提示词 | 上下文字数预算 | 前面片段占完预算 |
| 证据已提供,回答仍错 | 回答器与引用核验 | 引用了 C2,C2 没支持那句话 |
| 资料根本没写答案 | 拒答与资料覆盖范围 | 本期资料没有量子纠错门槛 |
第十九节会用固定题集把检索质量与回答质量分开评估。这里先记录每次问题、切分参数、top-k、纳入片段、回答和引用,方便下一节复盘。
07 / 把演示换成自己的学习笔记
动手搭最小本地问答
配套程序:lesson-18-rag.py;示例资料位于 lesson-18-data/。在项目根目录运行默认演示:
uv run python lessons/lesson-18-rag.py
把自己的 .md 笔记放在一个目录,再查询:
uv run python lessons/lesson-18-rag.py --docs 我的笔记目录 --question "广播如何对齐形状?"
- 挑 3~5 份自己的学习笔记,确认每份有清楚的标题和小节;运行程序,检查片段来源与行号。
- 准备两个有答案的问题和一个资料中没有答案的问题;检查前几名片段、原文摘录与拒答结果。
- 比较
--chunk-chars 120和--chunk-chars 260;记录哪一个更容易让正确证据进入 top-k,以及重叠是否造成重复结果。 - 读一遍程序构造的提示词,画出“检索前”和“交给语言模型后”分别有哪些责任。之后若接入模型,先确认资料是否适合传给该模型。