Skip to content

上节 RAG 我们是手动创建 Document 对象来存入向量数据库的,但实际知识来源多种多样:Word、PDF、网页、YouTube、X 推文等。所以需要 Loader 从各种来源加载文档,加载后的文档可能很大,还需要 Splitter 分割成小块再向量化。Loader 在 @langchain/community 包下,有 180+ 种;Splitter 在 @langchain/textsplitters 包下。我们用 CheerioWebBaseLoader 加载网页文章,用 RecursiveCharacterTextSplitter 分割,然后走完 RAG 检索生成流程。

RAG 的 Loader 和 Splitter:从各种来源加载文档并分割成小块

上节手动创建了 Document 对象,但真实场景知识来源很多:

  • 网页文章
  • Word / PDF 文档
  • YouTube 视频字幕
  • X(Twitter)推文
  • ……

需要两步处理:Loader 加载Splitter 分割 → 再走 RAG 流程。

Loader:从各种来源加载文档

Loader 负责把不同来源的内容统一转成 Document 对象。

bash
pnpm install cheerio @langchain/community
js
import { CheerioWebBaseLoader } from '@langchain/community/document_loaders/web/cheerio';

const cheerioLoader = new CheerioWebBaseLoader(
  'https://juejin.cn/post/7233327509919547452',
  {
    selector: '.main-area p',  // 用 CSS 选择器提取网页内容
  }
);

const documents = await cheerioLoader.load();
// 返回 Document[],每个包含 pageContent 和 metadata

Loader 有 180+ 种,社区维护,都在 @langchain/community 包下。

Splitter:把大文档分割成小块

加载后的 Document 可能很大,直接向量化效果差,需要分割。

bash
pnpm install @langchain/textsplitters
js
import { RecursiveCharacterTextSplitter } from '@langchain/textsplitters';

const textSplitter = new RecursiveCharacterTextSplitter({
  chunkSize: 500,       // 每个分块的字符数
  chunkOverlap: 50,     // 分块之间的重叠字符数(保证上下文连贯)
  separators: ['。', '!', '?'],  // 分割符,优先按句子分割
});

const splitDocuments = await textSplitter.splitDocuments(documents);

三个关键参数:

  • chunkSize:每块多大(字符数)
  • chunkOverlap:相邻块重叠多少字符(避免语义被截断)
  • separators:优先用什么符号分割(从前往后尝试)

完整流程:Loader → Splitter → RAG

js
// 1. Loader 加载网页
const documents = await cheerioLoader.load();

// 2. Splitter 分割
const splitDocuments = await textSplitter.splitDocuments(documents);

// 3. 向量化存入数据库
const vectorStore = await MemoryVectorStore.fromDocuments(splitDocuments, embeddings);

// 4. 检索 + 生成(和上节一样)
const retriever = vectorStore.asRetriever({ k: 2 });
const retrievedDocs = await retriever.invoke(question);

// 5. 构建 prompt 并调用大模型
const context = retrievedDocs
  .map((doc, i) => `[片段${i + 1}]\n${doc.pageContent}`)
  .join('\n\n━━━━━\n\n');

const prompt = `你是一个文章辅助阅读助手,根据文章内容来解答:

文章内容:
${context}

问题: ${question}

你的回答:`;

const response = await model.invoke(prompt);

流程图

text
各种来源(网页/PDF/Word/YouTube...)

      Loader 加载 → Document 对象(可能很大)

    Splitter 分割 → 多个小 Document

    嵌入模型向量化 → 存入向量数据库

    用户提问 → 语义检索 → 拿到相关文档

    塞进 prompt → 大模型生成回答

要点

  • Loader 解决"从哪来" — 180+ 种 loader,覆盖主流文档来源,统一输出 Document 对象
  • Splitter 解决"太大怎么办" — 按分隔符 + 字符数递归分割,chunkOverlap 保证上下文不断裂
  • Loader 在 @langchain/community,Splitter 在 @langchain/textsplitters — 社区维护的包
  • 先分割再向量化 — 大文档直接向量化效果差,分割后检索更精准