LangChain.js:RAG 深度解析与全栈实践
本文介绍了基于LangChain.js的检索增强生成(RAG)技术实现方案。RAG通过结合信息检索与大模型生成能力,有效解决了传统LLM的知识时效性、私有数据访问和回答可信度等问题。文章详细讲解了RAG工作流程:首先使用Document Loaders加载多种格式数据源(如PDF、网页、CSV等),然后通过文本分割和向量嵌入构建语义索引库,最后利用检索链将相关文档片段与大模型推理结合生成答案。文中
在大语言模型(LLM)的落地实践中,检索增强生成(Retrieval-Augmented Generation, RAG)已成为提升模型回答准确性、时效性和可控性的关键技术。

本文将基于 LangChain.js(截至 2025 年 12 月的最新稳定版本)的最新标准,带你通过代码实战,逐步了解 RAG 的底层机制与工程实现。
什么是 RAG?

RAG 是一种结合信息检索(Retrieval)与文本生成(Generation)的技术范式。其核心思想是:
- 在用户提问时,先从外部知识库中检索与问题最相关的文档片段;
- 将这些片段作为上下文,连同原始问题一起输入给大语言模型;
- 模型基于增强后的上下文生成更准确、有依据的回答。
相比纯参数化的大模型,RAG 具备以下优势:
- 动态更新知识:无需重新训练模型即可引入新数据;
- 减少幻觉(Hallucination):回答有据可依;
- 领域定制性强:可针对特定业务场景构建私有知识库。
为什么需要 RAG?

尽管当前主流 LLM(如 GPT-4、Claude、Qwen 等)具备强大的通用能力,但仍存在明显局限:
| 问题 | RAG 的解决方案 |
|---|---|
| 知识截止于训练数据 | 实时接入最新文档、数据库或网页 |
| 对私有/内部数据无感知 | 构建专属向量知识库 |
| 回答缺乏引用来源 | 可追溯答案出处 |
| 容易“一本正经地胡说八道” | 基于真实文档生成,降低幻觉 |
因此,RAG 成为企业级 AI 应用(如智能客服、知识助手、法律咨询等)的首选架构。
如何导入不同格式的数据源?

LangChain.js 提供了丰富的 Document Loaders,支持从多种格式加载原始文本。以下是常见数据源的加载方式(v0.3+ 语法):
1. 加载本地文本文件(.txt)
import { TextLoader } from "@langchain/classic/document_loaders/fs/text";
const loader = new TextLoader("./data/faq.txt");
const docs = await loader.load();
2. 加载 PDF 文件
import { PDFLoader } from "@langchain/community/document_loaders/fs/pdf";
const loader = new PDFLoader("./data/manual.pdf", {
splitPages: true, // 按页分割
});
const docs = await loader.load();
💡 需安装
pdf-parse:npm install pdf-parse
3. 加载网页内容
import { BrowserbaseLoader } from "@langchain/community/document_loaders/web/browserbase";
const loader = new BrowserbaseLoader(["https://example.com"], {
textContent: true,
});
const docs = await loader.load();
4. 加载 CSV 或 JSON
import { CSVLoader } from "@langchain/community/document_loaders/fs/csv";
// 或
import { JSONLoader } from "@langchain/community/document_loaders/fs/json";
const csvLoader = new CSVLoader("./data/products.csv", "description");
const jsonLoader = new JSONLoader("./data/faq.json", ".[].answer");
const csvDocs = await csvLoader.load();
const jsonDocs = await jsonLoader.load();
如何存储和索引数据?
RAG 的核心在于将文本转化为向量表示,以便进行语义相似度检索。LangChain.js 通过 Embeddings + VectorStore 实现这一过程。

步骤 1:切分文档(Chunking)
使用 RecursiveCharacterTextSplitter 是最常用的策略,它会优先按段落、句子分隔符切分,保持语义完整性。
import { RecursiveCharacterTextSplitter } from "@langchain/textsplitters";
const splitter = new RecursiveCharacterTextSplitter({
chunkSize: 1000, // 每个块的大小(字符数)
chunkOverlap: 200, // 重叠部分,防止上下文在切分处丢失
});
const splitDocs = await splitter.splitDocuments(docs);
console.log(`分割后文档数量: ${splitDocs.length}`);
步骤 2:Embedding 与 向量存储 (VectorStore)
我们需要一个 Embedding 模型将文本转为向量,以及一个 VectorStore 来存储这些向量。
这里以 OpenAI 和 MemoryVectorStore (内存存储,适合测试) 为例。生产环境推荐使用 Chroma, Pinecone 或 Supabase。
import { OpenAIEmbeddings } from "@langchain/openai";
import { MemoryVectorStore } from "langchain/vectorstores/memory";
// 如果使用 Chroma: import { Chroma } from "@langchain/community/vectorstores/chroma";
// 初始化 Embedding 模型
const embeddings = new OpenAIEmbeddings({
model: "text-embedding-3-small", // 性价比极高的模型
apiKey: process.env.OPENAI_API_KEY,
});
// 创建向量库并存入文档
const vectorStore = new MemoryVectorStore(embeddings);
await vectorStore.addDocuments(splitDocs);
console.log("向量库构建完成!");
其他支持的 VectorStore:Pinecone、Supabase、Weaviate、FAISS(via
@langchain/community)
如何检索并生成答案?
这是 RAG 的灵魂所在。我们这里使用 Agent 来调用 Tool 进行内容检索,最后通过大模型润色后返回结果。
import { RecursiveCharacterTextSplitter } from "@langchain/textsplitters";
import { TextLoader } from "@langchain/classic/document_loaders/fs/text";
import { OpenAIEmbeddings } from "@langchain/openai";
import { MemoryVectorStore } from "@langchain/classic/vectorstores/memory";
import { ChatOpenAI } from "@langchain/openai";
import { tool } from "@langchain/core/tools";
import { z } from "zod";
import { createAgent } from "langchain";
/**
* 基于LangChain框架实现的文档检索增强生成(RAG)核心流程
* 流程:加载文档 → 文档切分 → 生成向量 → 向量存储 → 构建检索工具 → 创建智能体 → 执行问答
*/
async function main() {
// 1. 加载本地文本文档
// TextLoader是LangChain提供的文本文件加载器,支持加载txt等纯文本格式文件
const loader = new TextLoader("./langchain.txt");
// 执行加载操作,返回Document类型的数组,每个Document包含文本内容和元数据(如文件路径)
const docs = await loader.load();
// 2. 文档切分:解决大文本无法一次性嵌入的问题,同时保留上下文关联性
const splitter = new RecursiveCharacterTextSplitter({
chunkSize: 400, // 每个文本块的最大字符数,根据模型上下文窗口和嵌入模型限制调整
chunkOverlap: 50, // 相邻文本块的重叠字符数,防止切分导致上下文断裂(如句子被截断)
});
// 执行文档切分,将原始文档拆分为多个小文本块
const splitDocs = await splitter.splitDocuments(docs);
// 3. 初始化嵌入模型:将文本转换为向量表示(Embedding)
const embeddings = new OpenAIEmbeddings({
model: "", // 指定嵌入模型名称(如text-embedding-3-small),需根据实际使用的模型填写
apiKey: "", // OpenAI兼容接口的API密钥,需替换为实际密钥
configuration: { baseURL: "https://ark.cn-beijing.volces.com/api/v3" }, // 火山方舟OpenAI兼容接口地址
});
// 4. 初始化向量存储:将切分后的文本块向量存入内存(仅测试用,生产环境建议用Pinecone/Chroma等)
const vectorStore = new MemoryVectorStore(embeddings);
// 将切分后的文档转换为向量并添加到向量存储中
await vectorStore.addDocuments(splitDocs);
// 5. 定义检索工具的输入参数校验规则
// 使用zod库定义schema,确保输入的query是字符串类型,避免非法参数
const retrieveSchema = z.object({ query: z.string() });
// 6. 创建自定义检索工具:供智能体调用的核心工具
const retrieve = tool(
// 工具执行逻辑:根据用户查询检索相似文档
async ({ query }) => {
// 向量相似性检索:返回最相似的2个文档(top-k=2,可根据需求调整)
const retrievedDocs = await vectorStore.similaritySearch(query, 2);
// 序列化检索结果:格式化文档信息(来源+内容),便于LLM理解
const serialized = retrievedDocs
.map(
(doc) => `Source: ${doc.metadata.source}\nContent: ${doc.pageContent}`
)
.join("\n");
// 返回序列化后的文本(供LLM生成回答)和原始文档(供后续处理)
return [serialized, retrievedDocs];
},
{
name: "retrieve", // 工具名称,智能体通过该名称调用工具
description: "检索与查询相关的信息", // 工具描述,LLM根据描述判断是否调用该工具
schema: retrieveSchema, // 输入参数校验规则
responseFormat: "content_and_artifact", // 响应格式:内容+原始数据
}
);
// 7. 初始化大语言模型(LLM):用于生成回答的核心模型
const model = new ChatOpenAI({
temperature: 0.7, // 生成温度:0-1之间,越高回答越随机,越低越精准
model: "", // 指定对话模型名称(如gpt-3.5-turbo),需替换为实际模型
configuration: { baseURL: "https://ark.cn-beijing.volces.com/api/v3" }, // 火山方舟接口地址
apiKey: "", // 模型API密钥,需替换为实际密钥
});
// 8. 创建智能体(Agent):整合LLM和工具,自动决策是否调用检索工具
const agent = createAgent({ model: model, tools: [retrieve] });
// 9. 调用智能体执行问答:用户查询“道家七十二福地”
const response = await agent.invoke({
messages: [{ role: "user", content: "道家七十二福地" }], // 构造用户消息
});
// 输出智能体的回答结果(包含LLM生成的最终回答、工具调用记录等)
console.log(response);
}
// 执行主函数,捕获并打印异常
main().catch(console.error);
总结
通过 LangChain.js,我们能够以模块化、声明式的方式快速搭建一个生产就绪的 RAG 系统。关键在于:
- 数据预处理:合理加载、清洗、切分;
- 向量索引:选择合适的 Embedding 与 VectorStore;
- 检索生成协同:设计 Prompt 与 Chain 逻辑,确保上下文有效利用。
RAG 不仅是技术方案,更是连接 LLM 与企业知识资产的桥梁。掌握它,你就掌握了构建可信、可控、可解释 AI 应用的核心能力。
📚 参考资源
- LangChain.js 官方文档
- RAG 论文:Retrieval-Augmented Generation for Knowledge-Intensive NLP Tasks
- GitHub 示例仓库:
langchain-ai/langchainjs/examples/rag
更多推荐



所有评论(0)