Bobolo
  • Home
  • Search
  • Me

RAG AI Embedding

博客 RAG 问答:从检索增强到可评测

15 Min Read

Thu Aug 20 2026

Written By Bobolo

给自己的博客加一个「能基于文章内容回答的 AI 助手」,是我学习 RAG(Retrieval-Augmented Generation,检索增强生成)的实践项目。这篇文章把它的概念、实现、评测,以及与长上下文窗口的对比完整梳理一遍。

什么是 RAG

RAG 的思路一句话:让 LLM 先「查资料」,再照着资料回答。

传统做法是直接把问题丢给大模型,模型凭训练时记住的知识回答。RAG 在中间加了一个「检索」环节,本质是给 LLM 装一个外部记忆

  1. 把知识库(博客文章)切块、向量化,建成索引(离线);
  2. 提问时把问题向量化,检索出最相关的片段;
  3. 把这些片段拼进 prompt,让 LLM 严格基于片段回答,并附上来源。

为什么需要 RAG

对应 LLM 直接问答的四个硬伤:

  • 幻觉:LLM 会一本正经地编造。RAG 让它「照着资料说」,有据可依。
  • 知识过时:模型有训练截止日期,答不出训练后的新东西。RAG 用你最新的语料。
  • 私有/专有知识:模型不知道你的内部文档、个人笔记。RAG 把这些「喂」进去。
  • 不可溯源:普通回答说不出「这句话出自哪」。RAG 能给出来源引用。

相比微调(fine-tuning)这种「把知识学进模型」的方案,RAG 更轻:换语料 = 重建索引,不用重训模型,便宜且灵活。

什么场景适合用 RAG

共同特征:语料相对稳定、需要溯源、体量大到塞不进上下文、答案要可验证。

  • 企业知识库 / 内部文档问答(客服、HR、IT 工单)
  • 产品文档 / API 文档问答
  • 个人知识管理(博客、笔记、第二大脑)
  • 医疗 / 法律 / 金融等对「有据可依」要求高的垂直问答
  • 代码库问答 / 编程助手

为什么博客适合用 RAG

  • 内容边界清晰:语料就是自己的文章,干净可控,没有脏数据。
  • 更新频率低:索引可以离线预生成,运行成本几乎为零。
  • 体量小而典型:二十几篇文章,足够覆盖「读者想知道的文章内容」。
  • 给读者实在价值:把「搜文章」升级成「问问题」,从给链接变成给答案。
  • 天然的评测素材:文章本身就是「标准答案」的来源,好建 golden 数据集。

RAG 的核心难点

① 文档怎么切(chunking)

  • 切太碎:上下文丢失,语义不完整;
  • 切太大:检索粒度粗、噪音多、还可能塞不进上下文窗口。

常见策略:

  • 固定长度 + overlap(滑动窗口,避免关键句被拦腰切断);
  • 按段落 / 句子切(保住语义边界);
  • 按标题 / 章节结构切(保住完整性);
  • 语义切分(用 embedding 找语义转折点)。

技术博客的特殊点:代码块要保留(我的做法),中英混合、代码与正文混合都会给切分增加难度。

② 怎么提高检索准确率

  • embedding 选型:多语言能力、领域适配、向量维度;
  • 混合检索:向量检索 + 关键词检索(BM25)加权融合,互补长短;
  • rerank 重排序:先粗召回 top-N,再用精排模型重排,提升头部准确率;
  • 查询改写 / 扩展:把用户口语化问题改写成更利于检索的形式;
  • chunking 是地基:切得不好,后面所有优化都打折扣。

③ 其它难点

  • 幻觉控制(prompt 约束「不知道就说不知道」);
  • 评测(没有 golden 数据就没法量化,LLM-as-judge 自身也有偏差);
  • 成本与延迟的权衡;
  • 冷启动(新内容要先重建索引)。

架构:两阶段

整个系统分成互不干扰的两段,核心是把「算文档向量」和「算问题向量」拆开:

RAG 流程图

  • ① 离线建索引(构建时):把 23 篇 MDX 文章解析、分块,用 embedding 模型向量化,产物是一个静态 JSON(search-index.json),随代码一起部署,运行时不加载任何模型。
  • ② 在线问答(运行时):问题向量化 → 和所有 chunk 算余弦相似度 → 取 top-K 片段拼成上下文 → DeepSeek 生成答案 + 来源引用。

实现细节

几个关键选型,都是「小体量 + 免费 + 能上线」的取舍:

环节方案为什么
分块按段落/句子切,约 600 字,保留代码块技术博客的代码是重要检索素材
Embeddingbge-m3(1024 维,SiliconFlow 免费 API)多语言、中文好;API 免下载、国内直连
向量存储直接存 JSON + 内存算余弦1024 个 chunk 太小,用不上向量数据库
生成DeepSeek,system prompt 强制「严格基于片段,不知道就说不知道」控制幻觉

为什么用向量检索而不是关键词检索? 这是中文场景的硬伤:关键词检索靠空格/标点切词,而中文没有空格——「序列化在前端领域有什么用?」会被切成一个整词,什么都匹配不到。向量检索直接把语义距离算出来,不受分词影响。

部署上的一个坑:环境变量的读取。Cloudflare Pages 的运行时(workerd)不会把 dashboard 的变量塞进 process.env,本地 Node 却会。所以要用 Qwik 平台无关的 env.get('KEY'),而不是 process.env.KEY

RAG vs 长上下文窗口(Long Context)

两者解决同一个问题——「让模型用上它没记住的知识」,但思路相反:

  • RAG:知识放外部(索引),只把「检索到的相关几段」塞进有限的上下文;
  • 长上下文:把知识全部塞进模型的上下文窗口(现在动辄 128K、1M token)。
维度RAG长上下文
知识放哪外部索引,按需检索全部塞进上下文
成本低(只付检索片段的 token)高(token 线性增长,每次请求重复计费)
延迟
可溯源强(明确来自哪段)
知识更新易(重建索引即可)每次重新塞
全局关联弱(只看到片段)强(信息完整)
工程复杂度高(建索引、评测、调参)

RAG 的优势:成本低、延迟低、可溯源、易增量更新、避免「注意力稀释」、隐私更好(按需检索,不必把全部数据暴露给模型)。

RAG 的劣势:检索可能漏/错(相关片段没召回,答案就缺);有工程复杂度;跨文档的全局关联推理弱。

长上下文的优势:零预处理、信息完整不遗漏、跨文档关联与全局推理强。

长上下文的劣势:成本高、延迟高、「lost in the middle」(长上下文里模型对中间信息的注意力会下降)、无关内容会污染注意力、知识更新要重塞。

怎么选

  • 语料大 / 稳定 / 要溯源 / 预算敏感 → RAG
  • 语料小 / 一次性 / 要全局关联推理 → 长上下文
  • 工程上常混合:RAG 粗筛 + 长上下文精读(把检索到的片段放进长上下文里做深度推理)。

评测:怎么量化「效果好」

「感觉还行」不算数。我给 14 个典型问题建了 golden 数据集(问题 + 应命中的文章 + 参考答案),分两层评测:

  • 检索层(免费、离线):Recall@K、MRR —— 评「找没找对文章」;
  • 生成层(LLM-as-Judge):faithfulness(有没有编造)、relevance(答没答对)—— 评「答得好不好」。
pnpm eval:retrieval   # 检索层,免费
pnpm eval:generation  # 生成层,调 DeepSeek 打分

跑出来的结果:

指标结果
检索Hit@5(召回率)100%
检索MRR0.929
生成faithfulness4.86 / 5
生成relevance4.0 / 5

优化:一个完整的「调参 → 评测 → 看指标」闭环

评测的真正价值,是让优化有据可依。以 relevance 为例:

  1. 初版 top-K = 5,relevance 只有 3.79。逐题看,发现「Redux 源码」「V8 原理」这类深度问题检索到的片段信息量不够。
  2. 把 top-K 提到 8,relevance 涨到 4.0,faithfulness 不变。
  3. 但还有 3 题卡住——追查发现是源文章本身太浅(《浅析 Redux 源码》),不是检索的锅。

这一步区分出了 RAG 优化里最重要的两种问题:参数问题(调 top-K、换分块策略能修)和语料问题(答案根本不在库里,调参数没用)。能说清这个,比调任何参数都值钱。

总结

RAG 的工程化,核心就三件事:

  1. 把「检索」和「生成」解耦,各自独立优化;
  2. 用向量检索扛住中文语义,用「严格基于片段」的 prompt 压幻觉;
  3. 用 golden 数据集 + 两层评测,把「好不好」变成可回归的数字。

这套东西体量虽小,但五脏俱全——正是理解 RAG 完整链路的最小实现。

Powered by Bobolo

Copyright © Bobolo Blog 2021