返回学习路线/阶段 04 · 连接你的知识与记忆
LESSON 10 / 18

RAG:先找证据,再回答

亲手做一个小型检索器,理解切块、召回与上下文拼接。

35 分钟 · 含动手练习进阶RAG检索

学完这一课,你将能够

  • 区分检索与生成阶段
  • 保存文档来源和版本
  • 解释 Top-k 与召回质量的关系

学习目标

  • 分清检索增强生成中的“检索”和“生成”。
  • 为文本片段保存来源、版本和可定位标识。
  • 实现一个离线词项检索器,观察召回失败。

前置要求:Python 数据结构与上下文预算。 本节使用人工标签演示检索,不需要向量数据库或模型 API。

为什么需要 RAG

模型可能不知道你的公司政策,也不会自动获取刚更新的文档。 检索增强生成通常先从受控资料中找到相关片段,再把片段交给生成步骤。 回答可以引用这些来源,方便用户核查。

RAG 不会自动保证事实正确。 错误可能来自文档过时、切块丢失语境、没有召回正确片段,或生成时误读证据。 排查时先看检索结果,再看生成结果,避免一律归因于提示词。

数据流分成两个阶段

建立索引:收集文档 → 提取文本 → 切块 → 保存来源 → 建立检索表示。

回答问题:理解查询 → 检索 → 选择证据 → 生成答案 → 检查引用。

第一个阶段通常在资料新增或更新时执行,第二个阶段在用户提问时执行。 资料权限必须在检索和取回原文时保持一致,不能先检索所有人的文档再让模型自行过滤。

切块不是平均切字符

按章节、标题和语义边界切分,通常比任意固定长度更容易保留意义。 例如“不得报销”与后面的例外条件不应被拆得毫无关联。 片段可以保存所属标题,并保留到原文位置的映射。

建议至少保存:chunk_id、document_id、version、text 和来源位置。 文档更新后,应替换旧版本的索引,或明确标记有效时间。

从最简单的检索开始

保存为 retrieve.py 并运行。 这里查询也使用标签列表,避免把中文分词细节混进第一版实现。

CHUNKS = [
    {
        "id": "expense-v2-1", "source": "报销制度", "version": "v2",
        "text": "报销申请需在消费后 30 天内提交,并附有效发票。",
        "terms": ["报销", "时间", "发票"],
    },
    {
        "id": "leave-v1-1", "source": "请假制度", "version": "v1",
        "text": "年假申请需提前 3 个工作日提交。",
        "terms": ["年假", "申请", "时间"],
    },
    {
        "id": "remote-v1-1", "source": "远程办公指南", "version": "v1",
        "text": "远程办公需要在团队日历中标记工作地点。",
        "terms": ["远程", "办公", "日历"],
    },
]

def retrieve(query_terms, top_k=2):
    query = set(query_terms)
    ranked = []
    for chunk in CHUNKS:
        overlap = query.intersection(chunk["terms"])
        score = len(overlap)
        if score > 0:
            ranked.append({**chunk, "score": score})
    ranked.sort(key=lambda item: (-item["score"], item["id"]))
    return ranked[:top_k]

for query in [["报销", "时间"], ["远程"], ["餐补"]]:
    results = retrieve(query)
    print("查询:", query)
    for result in results:
        print(result["id"], result["score"], result["text"])
    if not results:
        print("没有相关证据")

“报销、时间”应把报销制度排在请假制度前面。 “餐补”没有匹配内容,应返回空结果,而不是随机挑一条。 这里的分数只是匹配词数量,不是事实正确率或置信度。

再理解向量检索

Embedding 把文本映射成可计算相似度的向量,适合匹配不同措辞但语义相近的内容。 词项检索擅长精确名称与关键词,向量检索擅长语义关联,实际可组合使用。 检索器应该用自己的问题集评估,而不是因为用了向量数据库就假设更好。

top_k 表示最多取多少条候选。 过小可能遗漏证据,过大可能引入无关内容并挤占上下文。 必要时对候选再次排序,或根据问题把相邻片段一起取回。

练习与验收

新增一份“差旅政策”,保存标题、版本、来源 ID 和标签。 写六个查询,其中至少两个没有答案。

  • 每个有答案问题的目标证据都进入前两条结果。
  • 未知问题不会被强制匹配。
  • 输出保留来源和版本。
  • 能指出一种因为关键词不一致导致的漏召回。

延伸阅读

下一课区分知识库与记忆,避免把所有历史混成一个大文本。

让这一课,真正成为你的收获

完成练习后标记完成,也可以随时回来复习。

笔记与进度保存在当前浏览器,无需登录

AgentStudy · Learn by building.以理解为起点,以作品为答案