第 1 课:RAG 是什么?给小帅的 AI 配个"小抄"
学完这一节,你能带走什么
- 一句话说清 RAG 是干嘛的:先查资料,再回答问题。
- 知道它治的是 AI 的什么病:不知道还硬编,也就是"幻觉"。
- 分得清三种方案各自什么时候用:RAG、微调、长上下文。
- 亲手跑通一个最小版 RAG:建资料柜 → 找小抄 → 递给 AI → 照着答。
开头:小帅的 AI 店员,开业第一天就翻车
楼下开面馆的小帅最近迷上了 AI。他花五分钟接上了一个大模型,给自己的店做了个"AI 店员",挂进微信里。心里想的是从此不用回顾客消息,躺着赚钱。
结果开业第一天就翻车了,连翻三次。
顾客问:"你家牛肉面辣吗?" AI 店员秒回:"我们的牛肉面选用澳洲和牛,微辣回甘,入口即化。"小帅看完血压上来了,他家用的明明是本地黄牛肉,牛肉面根本不辣。
顾客又问:"你们营业到几点?" AI 店员自信满满:"本店 24 小时营业,深夜也可下单。"小帅差点把手机扔了,他每天九点准时关门,回家打游戏。
最离谱的是顾客问:"明天会不会下雨?" AI 店员一本正经:"明天多云转晴,适合来店里吃面。"好家伙,它连天气预报都敢编。
小帅崩溃了:"这 AI 怎么满嘴跑火车啊!"
真相是:AI 没撒谎,它是真不知道,但又不好意思说不知道。 它就像一个读书很多、从没进过小帅店里的路人,突然被拉来当店员,只能靠猜,而且猜得比谁都自信。
它的三个"难言之隐"
- 店里的资料它一眼没看过。菜单、会员规则、营业时间,这些是小帅的"私有资料",大模型训练的时候压根没见过。
- 脑子里的信息停在某一天。它的知识有截止日期,之后发生的事一概不知。
- 不知道就硬编。这是大模型的职业病,学名叫"幻觉",编得越细,越像真的。
小帅的三个办法
小帅开始想办法,路人告诉他三条路。
办法一:把店规刻进它脑子(微调) 相当于给员工做脑部手术,把菜单刻进去。贵,慢,还得准备一堆数据。最要命的是,下个月小帅换了新菜单,又得开一次刀。❌
办法二:每次把整本店手册塞给它(长上下文) 300 页手册直接全塞。结果它只看进去了第一页和最后一页,还特别费钱费时间。❌
办法三:给它配个小抄(RAG) ✅ 不动脑子,也不塞全书。顾客问啥,就先帮它翻到对应的那页,递到眼前:"来,看着这页回答。"
那 RAG 到底是个啥?
RAG 全称是 Retrieval-Augmented Generation,翻译过来叫"检索增强生成"。名字唬人,拆开就是三个动作:
- R · 找小抄(检索):顾客问"牛肉面辣不辣",系统冲进店里的资料柜,翻出菜单页。
- A · 递小抄(增强):把菜单页和顾客的问题打包在一起,递给 AI。
- G · 照着答(生成):AI 看着菜单回答:"牛肉面不辣,可以单独加辣。"
一句话:RAG 不是一个新模型,也不是训练方法,它是"先查资料,再回答问题"的工作流程。 大模型还是那个大模型,只是从闭卷考试换成了开卷考试。
完整流程:顾客问"会员怎么打折?"
一步步看:
- 顾客提问,系统不急着问 AI。
- 系统去店里的"数字档案柜"翻出最相关的三页:《会员章程》第 3 条、最近的满减活动海报、店长上周的通知。数字档案柜是什么,后面会学到,它有个正经名字叫向量数据库。
- 把这三页加上顾客的问题,打包成一份"小抄"。
- AI 看着小抄回答:"根据《会员章程》第 3 条,会员周三全场 8.8 折......(来源:会员章程.pdf)"
- 要是啥也没翻到,AI 老老实实说:"这个我不太清楚,建议您直接问店长。"
这里藏着整节课最值钱的一句话:大模型擅长照着资料写总结,不擅长把资料全背下来。 RAG 就是扬长避短,不逼它背,让它抄,但抄得有凭有据。
为什么这招能治"跑火车"?
- 有凭据:回答基于翻到的原文,不是瞎猜。
- 能追责:每条回答都标了出处,错了知道是谁的锅。
- 好更新:菜单换了,把资料柜里的旧页换掉就行,模型一个字不用改。
- 能保密:店里的机密资料不交给模型训练,只在回答问题那一刻临时翻一眼。
丑话说在前头:RAG 不是万能药
- 小抄没翻对页,后面答得再漂亮也是错的。所以接下来的课程,我们会死磕"怎么翻得准"。
- 小抄本身写错了,AI 会一本正经地跟着错。垃圾进,垃圾出。
- 想让 AI 学会新本事,比如用方言回顾客、写打油诗?那是微调的活,RAG 不接。
判断口诀
菜单常改、要注明出处、资料要保密 → 用 RAG 想改 AI 的性格和本领 → 用微调 偶尔查一两页小资料 → 直接塞长上下文
动手环节:给小帅的店跑通最小版 RAG
光说不练假把式。接下来用一个文件、几十行代码,给小帅的店做出一个最小版 RAG。不装框架,不用向量数据库,所有零件手工搭,目的就是让你看清里面到底发生了什么。
先搭台子
bash
mkdir my-rag-demo
cd my-rag-demo
npm init -y
npm install openai
再看代码
新建 minimal-rag.mjs,把下面代码粘进去:
javascript
import OpenAI from "openai";
// 接上大模型:换成你的 Key 和服务商地址(DeepSeek、通义、智谱等都行)
const client = new OpenAI({
apiKey: process.env.OPENAI_API_KEY,
baseURL: process.env.OPENAI_BASE_URL || "https://api.openai.com/v1",
});
// 两个模型各司其职:一个管"说话",一个管"把文字变成数字"
const CHAT_MODEL = "gpt-4o-mini"; // 对话模型,换成你用的
const EMBED_MODEL = "text-embedding-3-small"; // 向量模型,换成你用的
// ── 第 1 步:准备"小抄"(小帅店里的资料) ──
const menuDocs = [
"牛肉面:本地黄牛肉,不辣,可以单独加辣,28 元一碗。",
"营业时间:每天 10:00 到 21:00,周二休息。",
"会员规则:会员周三全场 8.8 折,生日当天免费送煎蛋。",
"地址:幸福路 88 号,地铁 3 号线幸福站 B 口出来左转。",
];
// ── 第 2 步:把每条资料变成向量,存进"资料柜" ──
async function buildStore() {
const res = await client.embeddings.create({
model: EMBED_MODEL,
input: menuDocs,
});
return res.data.map((item, i) => ({
text: menuDocs[i],
vector: item.embedding,
}));
}
// ── 第 3 步:余弦相似度------量一量两个向量有多"像"(可以不用纠结公式, 有现成的向量数据库可以使用不用自己算) ──
function cosineSimilarity(a, b) {
let dot = 0, normA = 0, normB = 0;
for (let i = 0; i < a.length; i++) {
dot += a[i] * b[i];
normA += a[i] * a[i];
normB += b[i] * b[i];
}
return dot / (Math.sqrt(normA) * Math.sqrt(normB));
}
// ── 第 4 步:找小抄------把问题也变成向量,挑出最像的两条资料 ──
async function findNotes(question, store) {
const res = await client.embeddings.create({
model: EMBED_MODEL,
input: question,
});
const questionVector = res.data[0].embedding;
return store
.map((doc) => ({
text: doc.text,
score: cosineSimilarity(questionVector, doc.vector),
}))
.sort((a, b) => b.score - a.score)
.slice(0, 2)
.map((doc) => doc.text);
}
// ── 第 5 步:递小抄 + 照着答------资料和问题打包一起放到Prompt中并且要求AI 照着说 ──
async function askAI(question) {
const store = await buildStore();
const notes = await findNotes(question, store);
const res = await client.chat.completions.create({
model: CHAT_MODEL,
messages: [
{
role: "system",
content:
"你是小帅面馆的店员。只能根据下面的资料回答顾客问题。" +
"资料里没有的内容,一律回答『这个我不太清楚』,不许瞎编。\n\n" +
"【店内资料】\n" + notes.join("\n"),
},
{ role: "user", content: question },
],
});
console.log("顾客问:", question);
console.log("店员答:", res.choices[0].message.content);
}
// 试两个问题:一个店里答得上,一个店里答不上
await askAI("牛肉面辣吗?");
await askAI("明天会下雨吗?");
填上钥匙,跑起来
powershell
$env:OPENAI_API_KEY = "你的API Key"
$env:OPENAI_BASE_URL = "https://api.openai.com/v1"
node minimal-rag.mjs
国内的同学推荐用硅基流动(SiliconFlow),一个 Key 同时搞定对话和向量,把环境变量换成:
powershell
$env:OPENAI_BASE_URL = "https://api.siliconflow.cn/v1"
# CHAT_MODEL 换成 "deepseek-ai/DeepSeek-V3"
# EMBED_MODEL 换成 "BAAI/bge-m3"
跑起来大概是这样:
顾客问: 牛肉面辣吗?
店员答: 牛肉面不辣,是本地黄牛肉,如果想加辣可以单独加辣,28 元一碗。
顾客问: 明天会下雨吗?
店员答: 这个我不太清楚,我没有相关资料。
看见没?第一个问题它翻到了菜单页,照着答。第二个问题资料柜里根本没有,它就老实闭嘴了。跑火车治好了。
这几十行代码里到底发生了什么
buildStore:把四条店规一条条变成向量(一串数字),相当于给每页资料拍一张数字照片,存档。findNotes:把顾客的问题也变成向量,和柜子里的照片比"像不像"(相似度),挑最像的两条。这就是 R 和 A 的前半段。askAI:把挑出来的资料塞进 system 提示词,等于把"小抄"递到 AI 眼前,再让它回答。这就是 A 的后半段和 G。- 防瞎编的秘诀:system 提示词里那句"不许瞎编"不是魔法。真正的底气是,相关资料根本没进提示词,AI 想编都没素材。
这个版本刻意做得很糙:没切块、没真向量库、资料写死在代码里、每次提问都重新建库。别急,这些"糙"正是后面课程 3 到 6 要逐个升级的地方。现在你已经亲眼见过 RAG 的完整骨架了。
本课小结
- RAG 就是先查资料再回答,三个动作:找小抄 → 递小抄 → 照着答。
- 它治的是"不知道"和"瞎编",不是"不够聪明"。
- 它不训练模型,只是给 AI 配了个随身小抄。
- RAG 能答多好,天花板由"找小抄"决定,翻得准,才答得好。
- 今天已经跑通最小版闭环:建资料柜 → 找小抄 → 打包 → 回答。
预告一句:从下一节开始,我们要给小帅的店做一个完整的 RAG 问数程序。它不光能翻资料回答问题,还能自己写 SQL 去数据库里查数,你问"上个月卖了多少碗牛肉面",它就真去账本里把数字算出来。今天这节课是地基,后面的每一块砖都压在它上面。
课后小练习
- 用一句话跟完全不碰电脑的朋友解释 RAG。
- 把
menuDocs换成你自己的四条信息,比如作息、爱好、家乡,再问 AI 三个问题,看看它答得准不准。 - 试着把"明天会下雨吗"换成"你们店里卖奶茶吗",观察它这次会怎么回答,想想为什么。
- 预习:下一节开始给小帅的店做完整的 RAG 问数程序,AI 不光能翻资料,还会自己写 SQL 查数据库。