RAG 是什么?从关键词搜索到语义搜索的完整过程


前面讲上下文工程的时候,我们提到过一个问题:

大模型本身并不知道你的私有资料。

它不知道你公司的内部文档,不知道数据库里的最新记录,也不知道你本地项目里刚写完的代码。

如果只靠模型训练时学到的知识,它很容易出现两种情况:

  • 不知道最新信息
  • 一本正经地胡说

这就是为什么现在很多 AI 应用不会只把问题丢给大模型,而是会在回答前先去"找资料"。

这个过程,就是理解 RAG 的入口。

RAG 的全称是 Retrieval-Augmented Generation,中文一般叫"检索增强生成"。

拆开看就是三步:

text 复制代码
Retrieval   检索
Augmented   增强
Generation  生成

一句话说:

RAG 不是让大模型凭空回答,而是先从外部资料里检索相关内容,再把这些内容作为上下文交给大模型生成答案。

为什么需要 RAG?

先看一个普通搜索问题。

假设我们有一批文章数据:

json 复制代码
[
  {
    "title": "如何使用 Nuxt.js 进行服务器端渲染",
    "category": "前端开发"
  },
  {
    "title": "使用 Nest.js 和 TypeScript 构建一个简单的微服务应用",
    "category": "后端开发"
  },
  {
    "title": "如何在 Vue.js 中使用 Vuetify 实现 Material Design 风格",
    "category": "前端开发"
  }
]

如果用户搜索:

text 复制代码
有哪些 Vue 相关的内容?

传统关键词搜索可以做:

sql 复制代码
title like '%Vue%'

这当然能找到一部分结果。

但关键词搜索有一个明显问题:

它更关心"字面上有没有出现这个词",不是真的理解语义。

比如:

text 复制代码
酸辣土豆丝的做法

和:

text 复制代码
马铃薯怎么做?

这两个问题在字面上不一样,一个叫"土豆",一个叫"马铃薯"。

但人一看就知道它们语义接近。

传统关键词搜索可能会漏掉这种关系。

RAG 的第一步,就是先解决"怎么找到真正相关的资料"。

从关键词搜索到语义搜索


语义搜索的核心是 Embedding。

Embedding 可以理解成:

把一段文字转换成一组数字。

这组数字不是随便来的,它会尽量保留文字的语义信息。

比如:

text 复制代码
Vue 组件库怎么做?
前端如何做可视化组件库?
Nuxt.js 服务器端渲染怎么实现?

这些文字会被转换成向量。

语义越接近,向量在空间里的距离通常也越近。

所以搜索时,不再只是判断某个词有没有出现,而是:

text 复制代码
用户问题 -> 转成向量
文章内容 -> 提前转成向量
计算相似度 -> 找到最接近的文章

这就是语义搜索。

一个最小 RAG Demo

我们用一个本地文章搜索的例子来理解。

项目结构大概是:

text 复制代码
posts-demo
├── app.service.mjs
├── create-embedding.mjs
├── semantic-search.mjs
└── data
    ├── posts.json
    └── posts-embedding.json

其中 posts.json 是原始文章数据。

每条数据只有两个字段:

json 复制代码
{
  "title": "如何使用 Nuxt.js 进行服务器端渲染",
  "category": "前端开发"
}

我们要做的事情是:

  1. 先把每篇文章转成 embedding
  2. 把 embedding 存到本地 JSON 文件里
  3. 用户输入问题时,把问题也转成 embedding
  4. 计算问题和文章之间的相似度
  5. 返回最相关的文章

这其实是 RAG 的前半段,也就是 Retrieval。

真正完整的 RAG,还会继续把检索结果放进 prompt,再让大模型生成最终答案。

第一步:准备 Embedding Client

项目里用 OpenAI SDK 兼容模式调用通义千问的 DashScope 接口:

js 复制代码
import OpenAI from 'openai';
import dotenv from 'dotenv';

dotenv.config();

export const client = new OpenAI({
  apiKey: process.env.DASHSCOPE_API_KEY,
  baseURL: 'https://dashscope.aliyuncs.com/compatible-mode/v1'
});

这里有两个关键点:

  • DASHSCOPE_API_KEY 放在 .env 里,不要写死到代码中
  • baseURL 指向 DashScope 的 OpenAI 兼容接口

这样后面就可以像调用 OpenAI SDK 一样调用 embedding 接口。

第二步:把文章转成向量

原始文章数据是人能看懂的文本。

但语义搜索需要向量。

所以我们先写一个脚本,把所有文章提前向量化:

js 复制代码
import fs from 'fs/promises';
import { client } from './app.service.mjs';

const inputFilePath = './data/posts.json';
const outputFilePath = './data/posts-embedding.json';

const data = await fs.readFile(inputFilePath, 'utf-8');
const posts = JSON.parse(data);

const postsWithEmbedding = [];

for (const { title, category } of posts) {
  const response = await client.embeddings.create({
    model: 'text-embedding-v4',
    input: `标题:${title},分类:${category}`
  });

  postsWithEmbedding.push({
    title,
    category,
    embedding: response.data[0].embedding
  });
}

await fs.writeFile(
  outputFilePath,
  JSON.stringify(postsWithEmbedding, null, 2)
);

这一步做完后,posts-embedding.json 里就不只是文章标题了,还会多出一个 embedding 字段。

它看起来大概是这样:

json 复制代码
{
  "title": "如何使用 Nuxt.js 进行服务器端渲染",
  "category": "前端开发",
  "embedding": [0.0123, -0.0345, 0.0876]
}

真实 embedding 会很长。

这个 demo 里每条 embedding 是 1024 维。

可以把它理解成:

模型给这篇文章生成了一张"语义坐标"。

以后搜索时,不用每次重新处理所有文章,只要读取已经保存好的 embedding 即可。

第三步:把用户问题也转成向量

文章已经向量化了。

用户输入问题时,也要做同样的事情。

比如用户输入:

text 复制代码
有哪些前端可视化相关内容?

程序会调用 embedding 接口:

js 复制代码
const response = await client.embeddings.create({
  model: 'text-embedding-v4',
  input: answer
});

const { embedding } = response.data[0];

现在,用户问题也变成了一组向量。

接下来就可以比较:

text 复制代码
用户问题的向量
和
每篇文章的向量
谁更接近?

第四步:计算相似度

向量之间怎么比较接近程度?

常见做法是余弦相似度。

代码是:

js 复制代码
const cosineSimilarity = (v1, v2) => {
  const dotProduct = v1.reduce((acc, curr, i) => acc + curr * v2[i], 0);

  const lengthV1 = Math.sqrt(v1.reduce((acc, curr) => acc + curr * curr, 0));
  const lengthV2 = Math.sqrt(v2.reduce((acc, curr) => acc + curr * curr, 0));

  return dotProduct / (lengthV1 * lengthV2);
};

不用被公式吓到。

可以先这么理解:

余弦相似度不是看两个向量"数值差多少",而是看它们"方向像不像"。

方向越接近,说明语义越接近。

所以我们可以对每篇文章算一个分数:

js 复制代码
const results = posts
  .map(item => ({
    ...item,
    similarity: cosineSimilarity(embedding, item.embedding)
  }))
  .sort((a, b) => b.similarity - a.similarity)
  .slice(0, 3);

最后返回相似度最高的前三篇文章。

这就是最小语义搜索。

这和 RAG 有什么关系?

到这里,我们其实还没有完成完整 RAG。

我们完成的是:

text 复制代码
Retrieval:检索

也就是从资料库中找到最相关的内容。

完整 RAG 还需要后两步:

text 复制代码
Augmented:把检索结果塞进 prompt,补充上下文
Generation:让大模型基于这些上下文生成答案

完整流程应该是:

text 复制代码
用户问题
  ↓
问题向量化
  ↓
向量相似度检索
  ↓
取出 Top K 相关资料
  ↓
把资料放进 prompt
  ↓
LLM 基于资料生成答案

也就是说:

语义搜索解决的是"找资料"。

RAG 解决的是"找完资料以后,让模型基于资料回答"。

RAG 不是让模型重新学习

这里有一个常见误解:

很多人以为 RAG 是把资料"训练进模型"。

不是。

RAG 不修改模型参数。

它更像考试时开卷。

模型本身还是原来的模型。

只是回答问题前,系统先帮它翻出几页相关资料,然后把资料放到它面前。

所以 RAG 的优势是:

  • 更新知识更方便
  • 不需要重新训练模型
  • 可以接入私有知识库
  • 可以减少幻觉
  • 可以让回答更可追溯

但它也有边界。

如果检索阶段找错了资料,后面的生成也会跟着跑偏。

所以 RAG 的效果,很大程度取决于检索质量。

RAG 和普通搜索有什么不同?

普通搜索更像:

text 复制代码
用户输入关键词
系统返回相关文档列表

RAG 更像:

text 复制代码
用户提出问题
系统先找资料
再让大模型根据资料组织答案

区别在最后一步。

搜索引擎通常把结果列表交给用户。

RAG 会把结果交给大模型,让模型帮用户读、整理、归纳、回答。

所以 RAG 适合这些场景:

  • 企业知识库问答
  • 客服机器人
  • 法律文档检索
  • 项目代码库问答
  • 课程资料问答
  • 内部制度查询
  • 产品文档助手

用户不想自己翻一堆资料。

用户想直接问:

text 复制代码
这个项目里登录逻辑在哪里?

或者:

text 复制代码
公司报销超过 5000 元要走什么流程?

这时 RAG 就很有价值。

生产环境里还缺什么?

这个 demo 用 JSON 文件保存 embedding。

这适合学习。

但真实项目里,数据量一大,就需要专门的向量数据库或支持向量检索的存储。

比如:

  • Milvus
  • PostgreSQL + pgvector
  • Elasticsearch 向量检索
  • 向量数据库服务

真实 RAG 系统通常还要考虑:

  • 文档怎么切块
  • chunk 多大合适
  • metadata 怎么存
  • 召回 Top K 取多少
  • 检索结果要不要重排
  • prompt 怎么组织上下文
  • 引用来源怎么返回
  • 敏感资料怎么做权限控制
  • 检索不到资料时怎么回答

所以 RAG 看起来只是"检索 + 生成",但真正做成产品时,它其实是一个上下文工程系统。

小结

RAG 的核心不是让模型变成数据库。

它真正解决的是:

大模型回答前,如何先拿到正确的外部资料。

关键词搜索只能匹配字面文本。

Embedding 让我们可以按语义检索资料。

语义搜索找到相关内容后,再把内容放进 prompt,让 LLM 基于上下文生成答案,这就是 RAG。

所以可以这样记:

text 复制代码
Embedding 让资料可检索
Retrieval 找到相关资料
Augment 把资料塞进上下文
Generation 让模型基于资料回答

RAG 是上下文工程里最常见、也最容易落地的一种方式。

它把大模型从"凭记忆回答",推进到了"带资料回答"。

相关推荐
fthux19 分钟前
RenoPit 能为普通业主做什么?看懂图纸、审查合同,提前发现装修坑
javascript·人工智能·ai·开源·github·chrome扩展·open source·edge扩展·firefox扩展
火山引擎开发者社区3 小时前
火山引擎混合云 veStack 智算平台 Day0 适配 Kimi K3
人工智能
RSABLOCKCHAIN3 小时前
AI Agents in LangGraph-2
人工智能·python
cd_949217213 小时前
哪些AI工具适合生成游戏开发用的3D道具模型?从道具草稿到引擎测试的选择方法
人工智能·3d
FII工业富联科技服务4 小时前
工业设备运维如何Agentic化?拆解Factory Brain的设备运维架构
大数据·运维·人工智能·架构·制造
糖果店的幽灵4 小时前
人已经用 WorkBuddy 找工作拿了面试,你还在一份份手工改简历
人工智能·面试·职场和发展·langgraph
qq29535 小时前
2026 财搭子决策模拟器:多智能体投研架构能力拆解
大数据·人工智能·架构
满怀冰雪5 小时前
12-PaddlePaddle, 飞桨, 分类模型, 训练循环, 损失函数, 优化器, cross_entropy, Adam
人工智能·深度学习·分类·paddlepaddle
冬奇Lab6 小时前
AI 评测系列(08):评测 CI/CD——持续质量门控
人工智能