Skip to content

13-RAG(检索增强生成)

📅 适用于 LangChain.js v1.x | 👶 零基础友好 💡 注意: 需要 Node.js 20+


📖 什么是 RAG?

RAG = Retrieval-Augmented Generation(检索增强生成)

简单说:RAG 是一种让 AI 先查资料再回答的技术。


🤔 为什么需要 RAG?

大模型的局限性

❌ 知识截止:模型训练数据有截止日期
❌ 无法访问私有数据:公司文档、个人笔记等
❌ 幻觉问题:可能编造不实信息
❌ 无法更新:训练后知识固定

RAG 的解决方案

用户提问


┌─────────────────┐
│   检索相关文档   │ ←── 从知识库/数据库中查找
└─────────────────┘


┌─────────────────┐
│   组装上下文    │ ←── 问题 + 相关文档
└─────────────────┘


┌─────────────────┐
│   AI 生成回答    │ ←── 基于检索到的信息
└─────────────────┘


返回准确答案

🧱 RAG 的核心组件

┌─────────────────────────────────────────────────────────┐
│                      RAG 系统                            │
├─────────────────────────────────────────────────────────┤
│                                                          │
│  ┌──────────────┐  ┌──────────────┐  ┌──────────────┐  │
│  │  文档加载    │  │  文本分割    │  │  向量化      │  │
│  │  (Loader)    │  │  (Splitter)  │  │  (Embedding) │  │
│  │              │  │              │  │              │  │
│  │ 读取文件/URL │  │ 切分成小块   │  │ 转为向量    │  │
│  └──────────────┘  └──────────────┘  └──────────────┘  │
│         │                │                │             │
│         ▼                ▼                ▼             │
│  ┌─────────────────────────────────────────────────┐   │
│  │              向量存储 (VectorStore)              │   │
│  │                                                 │   │
│  │  存储文档向量,支持相似度搜索                    │   │
│  └─────────────────────────────────────────────────┘   │
│                          │                              │
│                          ▼                              │
│  ┌──────────────┐  ┌──────────────┐  ┌──────────────┐  │
│  │  检索器      │  │  提示词      │  │  大模型      │  │
│  │  (Retriever) │  │  (Prompt)    │  │  (LLM)       │  │
│  │              │  │              │  │              │  │
│  │ 搜索相关文档 │  │ 组装上下文   │  │ 生成回答    │  │
│  └──────────────┘  └──────────────┘  └──────────────┘  │
└─────────────────────────────────────────────────────────┘

📦 安装依赖

bash
npm install @langchain/core @langchain/openai @langchain/community zod

💡 RAG 完整流程示例

步骤 1:准备知识库

javascript
import { ChatOpenAI } from "@langchain/openai";
import { HumanMessage, SystemMessage } from "@langchain/core/messages";
import { RecursiveCharacterTextSplitter } from "@langchain/textsplitters";

// 示例文档(实际应用中可能从文件/数据库加载)
const documents = [
  `公司名称:未来科技有限公司
成立时间:2020 年
总部地点:北京市海淀区
主营业务:人工智能软件开发、技术咨询
员工人数:500 人`,
  
  `产品 A:智能客服系统
功能:自动回答客户问题、工单管理
价格:9999 元/年
适用场景:电商、金融、教育行业`,
  
  `产品 B:数据分析平台
功能:数据可视化、报表生成、预测分析
价格:19999 元/年
适用场景:企业决策、市场分析`,
  
  `联系方式
地址:北京市海淀区中关村大街 1 号
电话:400-123-4567
邮箱:contact@futuretech.com
工作时间:周一至周五 9:00-18:00`
];

// 文本分割
const splitter = new RecursiveCharacterTextSplitter({
  chunkSize: 200,      // 每块最大 200 字符
  chunkOverlap: 20,    // 块之间重叠 20 字符
});

const splitDocs = await splitter.splitText(documents.join("\n\n"));
console.log("分割后的文档块数:", splitDocs.length);
splitDocs.forEach((doc, i) => {
  console.log(`\n块${i + 1}:`, doc);
});

步骤 2:创建简单 RAG(不使用向量存储)

javascript
import { ChatOpenAI } from "@langchain/openai";
import { HumanMessage, SystemMessage } from "@langchain/core/messages";
import { RecursiveCharacterTextSplitter } from "@langchain/textsplitters";

const model = new ChatOpenAI({
  modelName: "MiniMax/MiniMax-M2.5",
  apiKey: "你的 API Key",
  temperature: 0,
  configuration: {
    baseURL: "https://api-inference.modelscope.cn/v1",
  },
});

// 知识库文档
const knowledgeBase = [
  "公司成立于 2020 年,总部位于北京。",
  "主要产品有智能客服系统(9999 元/年)和数据分析平台(19999 元/年)。",
  "联系电话:400-123-4567,邮箱:contact@futuretech.com。",
  "工作时间:周一至周五 9:00-18:00。"
];

// 简单相似度搜索(基于关键词匹配)
function simpleSearch(query, docs, topK = 2) {
  // 中文分词:按字符分割(简单方式,适合中文)
  const queryWords = query.split('').filter(c => c.trim());

  const scored = docs.map((doc, index) => {
    const docLower = doc.toLowerCase();
    let score = 0;
    queryWords.forEach(word => {
      if (docLower.includes(word)) score += 1;
    });
    return { index, doc, score };
  });

  // 按分数排序,返回前 K 个
  return scored
    .sort((a, b) => b.score - a.score)
    .slice(0, topK)
    .map(item => item.doc);
}

// RAG 主函数
async function ragQuery(question) {
  console.log("\n📝 用户问题:", question);
  
  // 1. 检索相关文档
  const relevantDocs = simpleSearch(question, knowledgeBase, 2);
  console.log("\n📚 检索到的文档:");
  relevantDocs.forEach((doc, i) => {
    console.log(`  ${i + 1}. ${doc}`);
  });
  
  // 2. 组装提示词
  const context = relevantDocs.join("\n");
  
  const prompt = new SystemMessage(`
你是一个客服助手。请根据以下参考资料回答问题。
如果资料中没有答案,请说"抱歉,我没有找到相关信息"。

参考资料:
${context}
`);
  
  // 3. 生成回答
  const response = await model.invoke([
    prompt,
    new HumanMessage(question)
  ]);
  
  console.log("\n🤖 AI 回答:", response.content);
  return response.content;
}

// 测试
await ragQuery("公司什么时候成立的?");
await ragQuery("智能客服系统多少钱?");
await ragQuery("怎么联系你们?");

🎯 使用向量存储的 RAG

⚠️ 注意: LangChain v1.x 中 MemoryVectorStore 已被移除或移动。 以下示例展示概念,实际项目中可以使用简单 RAG 或等待官方更新。

概念示例(了解原理)

javascript
// 这是概念示例,展示向量存储的工作原理
// 实际项目中可以使用简单 RAG(上面的示例)或等待官方更新

import { ChatOpenAI } from "@langchain/openai";
import { HumanMessage, SystemMessage } from "@langchain/core/messages";

// 向量存储的工作流程:
// 1. 文档 → Embedding → 向量
// 2. 查询 → Embedding → 向量
// 3. 相似度搜索 → 找到最相似的文档
// 4. 组装上下文 → 生成回答

// 简单实现(不使用向量存储)
const documents = [
  "Python 是一种高级编程语言,由 Guido van Rossum 于 1989 年发明。",
  "JavaScript 主要用于网页开发,可以在浏览器中运行。",
  "Java 是一种面向对象的编程语言,广泛用于企业级应用。",
  "C++ 是一种高性能语言,常用于游戏开发和系统编程。",
  "Go 语言由 Google 开发,适合构建高性能的后端服务。"
];

// 简单相似度搜索(基于字符匹配)
function simpleSearch(query, docs, topK = 2) {
  // 中文分词:按字符分割
  const queryWords = query.split('').filter(c => c.trim());

  const scored = docs.map((doc, index) => {
    const docLower = doc.toLowerCase();
    let score = 0;
    queryWords.forEach(word => {
      if (docLower.includes(word)) score += 1;
    });
    return { index, doc, score };
  });

  // 按分数排序,返回前 K 个
  return scored
    .sort((a, b) => b.score - a.score)
    .slice(0, topK)
    .map(item => item.doc);
}

const model = new ChatOpenAI({
  modelName: "MiniMax/MiniMax-M2.5",
  apiKey: "你的 API Key",
  temperature: 0,
  configuration: {
    baseURL: "https://api-inference.modelscope.cn/v1",
  },
});

// RAG 查询
async function queryRAG(question) {
  console.log("📝 用户问题:", question);

  // 1. 检索相关文档
  const relevantDocs = simpleSearch(question, documents, 2);

  console.log("\n📚 检索到的文档:");
  relevantDocs.forEach((doc, i) => {
    console.log(`  ${i + 1}. ${doc}`);
  });

  // 2. 组装上下文
  const context = relevantDocs.join("\n");

  // 3. 生成回答
  const prompt = new SystemMessage(`
你是一个编程助手。请根据以下参考资料回答问题。
如果资料中没有答案,请如实告知。

参考资料:
${context}
`);

  const response = await model.invoke([
    prompt,
    new HumanMessage(question)
  ]);

  console.log("\n🤖 AI 回答:", response.content);
  return response.content;
}

// 运行
await queryRAG("Python 是谁发明的?");
await queryRAG("哪种语言适合游戏开发?");

输出示例

📝 用户问题:Python 是谁发明的?

📚 检索到的文档:
  1. Python 是一种高级编程语言,由 Guido van Rossum 于 1989 年发明。
  2. Go 语言由 Google 开发,适合构建高性能的后端服务。

🤖 AI 回答:根据参考资料,Python 是由 **Guido van Rossum** 于 1989 年发明的。

🔄 RAG 工作流程详解

┌─────────────────────────────────────────────────────────────┐
│                    RAG 完整流程                              │
└─────────────────────────────────────────────────────────────┘

【索引阶段】(离线)
1. 加载文档 → 2. 文本分割 → 3. 向量化 → 4. 存储到向量数据库

【查询阶段】(在线)
1. 用户提问


2. 问题向量化


3. 相似度搜索 → 找到最相关的文档片段


4. 组装提示词 → 问题 + 相关文档


5. AI 生成回答


6. 返回答案

📊 文本分割策略

1. 按字符数分割

javascript
import { RecursiveCharacterTextSplitter } from "@langchain/textsplitters";

const splitter = new RecursiveCharacterTextSplitter({
  chunkSize: 500,      // 每块最大 500 字符
  chunkOverlap: 50,    // 重叠 50 字符,保持上下文连贯
});

const chunks = await splitter.splitText(longText);

2. 按段落分割

javascript
import { RecursiveCharacterTextSplitter } from "@langchain/textsplitters";

const splitter = new RecursiveCharacterTextSplitter({
  chunkSize: 1000,
  chunkOverlap: 0,
  separators: ["\n\n", "\n", "。", "!", "?", "。", " ", ""],  // 优先按段落分割
});

3. 按代码分割

javascript
import { RecursiveCharacterTextSplitter } from "@langchain/textsplitters";

const splitter = RecursiveCharacterTextSplitter.fromLanguage("python", {
  chunkSize: 500,
  chunkOverlap: 0,
});

const codeChunks = await splitter.splitText(pythonCode);

🎯 实际应用场景

场景 1:公司知识库问答

javascript
const companyDocs = [
  "员工手册:工作时间 9:00-18:00,周末双休。",
  "报销流程:每月 15 日前提交上月发票,财务审核后打款。",
  "请假制度:年假 5 天,病假需提供医院证明。",
  "福利:五险一金、年度体检、节日礼品。"
];

// 员工提问:"年假有几天?"
// RAG 检索到"请假制度"相关文档
// AI 回答:"根据公司制度,年假有 5 天。"

场景 2:产品文档问答

javascript
const productDocs = [
  "安装步骤:1.下载安装包 2.运行安装程序 3.选择安装路径 4.完成安装",
  "系统要求:Windows 10+, 8GB 内存,50GB 硬盘空间",
  "常见问题:无法启动请检查防火墙设置"
];

// 用户提问:"安装需要什么配置?"
// RAG 检索到"系统要求"相关文档
// AI 回答:"需要 Windows 10 以上系统,8GB 内存,50GB 硬盘空间。"

场景 3:论文/文献检索

javascript
const papers = [
  "论文 A:深度学习在图像识别中的应用,准确率 95%",
  "论文 B:自然语言处理的最新进展,BERT 模型介绍",
  "论文 C:强化学习在游戏 AI 中的应用案例"
];

// 研究者提问:"图像识别的准确率是多少?"
// RAG 检索到"论文 A"
// AI 回答:"根据论文 A,深度学习在图像识别中准确率达到 95%。"

⚠️ 注意事项

1. 文档质量

javascript
// ✅ 好的文档:结构清晰、信息准确
const goodDocs = [
  "公司名称:XX 科技,成立于 2020 年。",
  "产品 A 价格:999 元/年,功能包括..."
];

// ❌ 差的文档:混乱、错误信息
const badDocs = [
  "大概可能也许是 2020 年吧",
  "价格不太确定,好像 999 块"
];

2. 检索数量

javascript
// 太少:可能遗漏关键信息
const retriever = vectorStore.asRetriever(1);  // 只返回 1 个

// 太多:增加 token 消耗,可能混淆
const retriever = vectorStore.asRetriever(20);  // 返回 20 个

// ✅ 推荐:3-5 个
const retriever = vectorStore.asRetriever(3);  // 返回 3 个

3. 提示词设计

javascript
// ✅ 好的提示词
const prompt = new SystemMessage(`
你是一个客服助手。请根据以下参考资料回答问题。
如果资料中没有答案,请说"抱歉,我没有找到相关信息"。
不要编造资料中没有的内容。

参考资料:
{context}
`);

// ❌ 差的提示词
const prompt = new SystemMessage("回答问题:");  // 没有说明使用参考资料

📝 本章小结

  • RAG = 检索 + 生成,让 AI 先查资料再回答
  • 核心组件:文档加载、文本分割、向量化、检索、生成
  • 可以解决知识截止、私有数据、幻觉问题
  • 向量存储支持相似度搜索
  • 提示词设计很重要

🏃 下一章

14-VectorStore(向量存储) →


🔗 更多资源

基于 LangChain.js v1.x 版本