AI 满嘴跑火车怎么办?给它配个"小抄"

第 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 不是一个新模型,也不是训练方法,它是"先查资料,再回答问题"的工作流程。 大模型还是那个大模型,只是从闭卷考试换成了开卷考试。

完整流程:顾客问"会员怎么打折?"

flowchart LR A[&#34;顾客问:会员怎么打折?&#34;] --> B[&#34;找小抄:去店里资料库<br/>翻出最相关的几页&#34;] B --> C[&#34;递小抄:资料+问题<br/>打包成一份提示词&#34;] C --> D[&#34;照着答:AI 看着资料<br/>写出回答&#34;] D --> E[&#34;带出处的回答&#34;]

一步步看:

  1. 顾客提问,系统不急着问 AI
  2. 系统去店里的"数字档案柜"翻出最相关的三页:《会员章程》第 3 条、最近的满减活动海报、店长上周的通知。数字档案柜是什么,后面会学到,它有个正经名字叫向量数据库。
  3. 把这三页加上顾客的问题,打包成一份"小抄"。
  4. AI 看着小抄回答:"根据《会员章程》第 3 条,会员周三全场 8.8 折......(来源:会员章程.pdf)"
  5. 要是啥也没翻到,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 查数据库。
相关推荐
默_笙25 分钟前
🏝 Docker 就是"房地产开发":从施工图纸到小区物业的容器化指南
前端·javascript
计算机魔术师27 分钟前
Rohan Paul 谈用时间跨度衡量智能体能力,并引用 OpenAI 自动化研究实习生里程碑
前端
kyriewen28 分钟前
我手写了个极简版 React Router——才搞懂 v6 为什么砍了这么多 API
前端·javascript·程序员
a11177634 分钟前
Shirone 二次元博客主题 开源
前端·开源
IT_陈寒1 小时前
Python的切片赋值把我坑惨了,这不是bug是特性
前端·人工智能·后端
掘金酱1 小时前
【社区公告】致每一位掘友:关于这次调整, 想再说几句
前端·人工智能
CoderLiu1 小时前
程序化工具调用(PTC)与动态工作流引擎:深入大模型工具调用的架构演进与实践
前端·人工智能·后端
码农胖大海1 小时前
我的第一个产品,只有一段提示词
前端·ai编程·产品
“AI国潮设计-小江”1 小时前
Python实战 | SDXL精准控制“普宁英歌舞×星空蛋糕”IP落地,附核心Prompt与商用授权思路
开发语言·人工智能·python·prompt·aigc
邋遢道1 小时前
# 企业级 Agent 从 0 到 1(二):技术选型与最小骨架
前端·chrome