AI 学习课程 / LESSON 18

第十八节:构建知识问答系统

把自己的学习资料变成可追溯的问答:先切分文件,再将问题和片段变为向量并检索,最后把证据放进回答上下文。你可以改问题、切分长度和上下文预算,亲眼看到候选答案和来源怎样变化。

本地 Markdown切分与重叠TF-IDF 稀疏向量余弦检索证据引用无需服务器

01 / 问答系统先找证据

从资料到可核对的回答

Markdown 文件
保留来源
→切成片段
chunk
→向量索引
TF-IDF
→检索片段
余弦相似度
→组织上下文
问题 + 证据
→回答 + 引用
逐句核对

本节实际执行资料切分、词面向量检索、上下文组装和原文摘录式回答。页面会显示可交给语言模型的完整提示词,但不会假装已经调用了语言模型。第十二节的玩具字符模型没有足够能力可靠回答这些问题,所以这里不把它接到答案生成步骤。

检索器

为问题找相关片段,决定哪些资料可能进入上下文。

回答器

本地演示只摘录原句;接入可靠语言模型后可组织自然语言答案。

引用核对

回答中的 [C编号] 必须指向真实片段,且该片段确实支持相邻说法。

检索分高不等于问题已被回答。页面显示的是候选证据摘录,需要读者核对;未知问题会尝试拒答,但固定相似度阈值不是可靠的事实判断器。

02 / 每片既要够短,也要保留上下文

比较两种切分长度

程序按 Markdown 的二级标题分节,再在每节内部尽量靠句号、问号或换行处切开;相邻片段允许少量重叠。每片保留文件、标题、行号与片段编号。调长片段可带来更多前后文,但也可能把不相关句子一并送去检索与提示词。

字符数用于这节教学演示;语言模型实际上下文限制按 Token 计算,不应把 500 字直接当成 500 Token。

03 / 让向量计算落到真实片段上

这节的向量是词面 TF-IDF,不是语义 Embedding

程序提取连续汉字的相邻二字片段,以及英文和数字串,统计出现次数。一个片段对应一条稀疏向量:没出现的特征权重为 0;出现越多、在资料库越少见的特征,权重越大。问题也按同一规则向量化,再计算夹角余弦。

TF-IDF(t,d) = [1 + ln 词频(t,d)] × [1 + ln((N+1)/(df(t)+1))] cos(q,d) = (q · d) / (||q|| × ||d||) N = 片段总数;df(t) = 含这个二字片段或英文词的片段数。

例如“目标网络”可产生“目标”“标网”“网络”几个词面特征。向量相似度会受共同字符影响,难以可靠找到只在语义上相似、字面却不同的表述。真正的语义 Embedding 通常由训练过的编码模型产生稠密向量;它是升级方向,仍须评估检索质量。

当前问题的部分特征

与首位片段共同贡献最大的特征

05 / 检索只是上半场

把来源一起交给语言模型

下面是程序为当前问题组装的实际提示词。它包含问题、进入预算的片段、每片来源标识,以及“不足时拒答”的要求。若以后接入语言模型,模型生成后仍要检查引用是否真的支持具体说法;格式像引用并不能证明正确。

原文摘录式演示

当前页面的回答只复制资料中的一句话并加上 [C编号];没有做事实推理、综合多片证据或调用模型。

未来的生成式步骤

将这份提示词交给你选择的本地或远程语言模型,再检查每条陈述、引用与“资料不足”场景。接入模型时还要管理输入长度、成本和私密资料传输。

检索片段是待引用的数据,其中若写着“忽略之前的要求”,不应把它当作系统命令。示例资料里专门放了一句这样的文字,可在「检索增强问答」文件中看到。

06 / 为下一节评估做好准备

三种失败要分开看

现象先看哪一步例子
正确片段没排进 top-k切分、特征、检索排序换了说法,二字片段几乎不重合
片段排进 top-k,却没进提示词上下文字数预算前面片段占完预算
证据已提供,回答仍错回答器与引用核验引用了 C2,C2 没支持那句话
资料根本没写答案拒答与资料覆盖范围本期资料没有量子纠错门槛

第十九节会用固定题集把检索质量与回答质量分开评估。这里先记录每次问题、切分参数、top-k、纳入片段、回答和引用,方便下一节复盘。

请试试「DQN 目标网络多久同步」和「量子纠错需要多少比特」:前者应能找到本课的具体设置,后者应提示资料不足。再把预算降到 120 字,观察有证据却没装进上下文时系统会怎样。

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 "广播如何对齐形状?"
  1. 挑 3~5 份自己的学习笔记,确认每份有清楚的标题和小节;运行程序,检查片段来源与行号。
  2. 准备两个有答案的问题和一个资料中没有答案的问题;检查前几名片段、原文摘录与拒答结果。
  3. 比较 --chunk-chars 120 和 --chunk-chars 260;记录哪一个更容易让正确证据进入 top-k,以及重叠是否造成重复结果。
  4. 读一遍程序构造的提示词,画出“检索前”和“交给语言模型后”分别有哪些责任。之后若接入模型,先确认资料是否适合传给该模型。
验收:能让自己的 Markdown 资料产生带出处的候选回答;能解释本地 TF-IDF 词面向量与训练得到的语义 Embedding 有何区别;能指出“找到了片段”和“生成的回答有据”是两个不同的检查点。
1. 本节正在运行的向量检索使用什么?
2. 片段排在 top-k 内,就一定能进入语言模型上下文吗?
3. 回答末尾出现 [C03] 就证明说法受到 C03 支持吗?
4. 页面中的“500 字预算”是模型真实的 500 Token 上限吗?
下一节:评估与改进。建立固定问题集,分别测检索是否找到证据、回答是否被证据支持,再根据错误类型做有依据的修改。