我的笔记散落在几百个 Markdown 文件里,想找某个结论时,全文搜索经常搜不到——因为我记不清当时用的是哪个词。于是我决定用 RAG(检索增强生成)给自己做一个知识库问答。

整体流程

RAG 的思路其实很朴素:先从资料里找出相关片段,再把片段和问题一起交给大模型回答。拆开来是四步:

  1. 切分:把长文档切成小段;
  2. 向量化:用 Embedding 模型把每段文字变成向量;
  3. 检索:把问题也变成向量,找出最相近的几段;
  4. 生成:把这几段作为上下文,让模型基于它们回答。

分块:最容易被忽视的一步

一开始我按固定 500 字硬切,结果经常把一个完整的论述切成两半,检索出来的片段前言不搭后语。后来改成按标题层级切分,每段再带上它所属的标题路径,效果立刻好了很多。

type Chunk = { id: string; heading: string; text: string };

function splitByHeading(doc: string, file: string): Chunk[] {
  const sections = doc.split(/\n(?=#{1,3} )/);
  return sections.map((section, i) => ({
    id: `${file}#${i}`,
    heading: section.match(/^#{1,3} (.*)/)?.[1] ?? file,
    text: section.trim(),
  }));
}

经验是:让每个片段能独立读懂,比追求固定长度更重要。

检索:先求召回,再求精准

只取最相似的 3 段时,经常漏掉关键信息。我现在的做法是先多召回一些(比如 20 段),再用一个更精细的排序步骤挑出最相关的 5 段。另外把关键词检索和向量检索结合起来,专有名词、版本号这类内容会更稳。

生成:让模型知道“不知道”

在 Prompt 里明确要求:只根据提供的资料回答,资料里没有就直说“没找到”,并标出引用了哪篇笔记。这一条规则,让幻觉少了很多,也让我能快速回到原文核对。

小结

做完之后最大的感受是:RAG 的效果,七分靠数据处理,三分靠模型。与其换更大的模型,不如先把分块和检索打磨好。

文章作者:Lxx

文章链接:https://lanjiangtao.cn/posts/rag-knowledge-base/

版权声明:本文为原创内容,转载请注明来自 Lxx的日常。

评论

评论区即将开放,欢迎先通过侧边栏的方式联系我。

输入关键词开始搜索,支持标题、标签和正文。