跳到正文
docai.cc

DOCAI · 课程 · L3 · 40 分钟

搭一个最小可用的 RAG

三份文档、一次向量化、一次检索、一次回答:不装框架,用最少代码看清 RAG 的每一步在哪出错。

你将学会

  • 能说清 RAG 的三个步骤各自负责什么
  • 能用手头的文档跑通一次检索增强问答
  • 能判断一次答错是检索没找到,还是找到了没用上

为什么先手写一遍

框架会把检索、拼接、调用包在一起。出了问题,你分不清是切分错了、检索没命中,还是模型没用上材料。

手写一遍只要几十行,但每一步都看得见。RAG 是什么先读一遍再动手。

三步

1. 切分与向量化

把文档切成 200–500 字的块,每块调用一次 embedding 接口,存成「向量 + 原文」。

chunks = split(docs, size=300, overlap=50)
vectors = [client.embeddings.create(model=EMB, input=c).data[0].embedding
           for c in chunks]

块太大,检索不精准;太小,上下文被切碎。先按段落切,是最省事的起点。

2. 检索

把用户问题也向量化,算余弦相似度,取前 3–5 块。

q = client.embeddings.create(model=EMB, input=question).data[0].embedding
top = sorted(zip(vectors, chunks), key=lambda x: cosine(q, x[0]), reverse=True)[:4]

3. 生成

把命中的原文拼进提示,并要求只依据材料回答。

只依据下面的材料回答;材料里没有的,直接说「材料未提及」。

材料: {top chunks}

答错了,怎么定位

现象 问题在 怎么验证
材料里明明有,答「未提及」 检索没命中 打印 top 块,看是否包含答案
检索到了,答案仍不对 提示没约束住 换成「逐字引用原文再回答」
答案掺了材料外的内容 幻觉 见什么是幻觉

先看检索结果,再看生成。 九成的「RAG 跑不通」停在第一步。

什么时候该上框架

当你要处理成千上万份文档、需要增量更新和权限过滤时。三份文档的场景,手写更好维护。

自测

  1. 为什么「材料里有、模型说没有」通常不是模型的问题?
  2. 块大小调大,检索精度和上下文完整性各会怎么变?
  3. 你的检索结果里混进了不相关段落,最直接的两个调整是什么?

涉及的概念