常见用例

RAG 系统

基于 LangGraph 构建检索增强生成 (Retrieval-Augmented Generation) 系统

📚 学习目标

学完这篇文章后,你将能够:

  • 理解 RAG 的基本工作流程
  • 构建包含“检索”、“评分”、“生成”等步骤的 LangGraph
  • 实现“自适应 RAG”:根据检索质量决定是否重新检索

前置知识

在开始学习之前,建议先阅读:

你需要了解:

  • 向量数据库和 Embedding 的基础概念

1️⃣ 什么是 RAG?

RAG (Retrieval-Augmented Generation) 通过在生成回答前先检索相关文档,解决了 LLM 知识过时和幻觉的问题。

经典 RAG 流程


2️⃣ 使用 LangGraph 构建 RAG

LangGraph 的优势在于它可以让 RAG 流程更加灵活,不仅仅是线性的“检索->生成”。

状态定义

import { Annotation } from '@langchain/langgraph';
import { Document } from '@langchain/core/documents';

const RAGState = Annotation.Root({
  question: Annotation<string>(),
  documents: Annotation<Document[]>({
    reducer: (_current, update) => update,
    default: () => [],
  }),
  answer: Annotation<string>(),
});

[!NOTE] 生产级 RAG 的“难点”不在 Graph,而在索引构建:chunking、embedding、向量库、metadata、增量更新。

节点实现

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

const model = new ChatOpenAI({ model: 'gpt-4o-mini', temperature: 0 });

const retrieveNode = async (state: typeof RAGState.State) => {
  const docs = await vectorStore.similaritySearch(state.question, 4);
  return { documents: docs };
};

const generateNode = async (state: typeof RAGState.State) => {
  const context = state.documents
    .map((d, i) => `[${i + 1}] ${d.pageContent}`)
    .join('\n\n');

  const messages = [
    new SystemMessage('你是严谨的知识库助手。只基于给定 Context 回答,并引用编号。'),
    new HumanMessage(`Context:\n${context}\n\nQuestion: ${state.question}`),
  ];

  const response = await model.invoke(messages);
  return { answer: String(response.content) };
};

代码解析

  1. 把每个文档加上编号 [1] [2] ...,你更容易在答案里做引用。
  2. System Prompt 明确“只基于 Context”,能显著降低幻觉。

3️⃣ 进阶:自适应 RAG (Adaptive RAG)

我们可以引入一个“评分节点”来评估检索到的文档是否真的与问题相关。

流程图

代码逻辑

import { Command } from '@langchain/langgraph';

const gradeDocumentsNode = async (state) => {
  const { documents, question } = state;
  const validDocs = [];
  
  for (const doc of documents) {
    const score = await graderModel.invoke({...}); // 让 LLM 打分
    if (score.isRelevant) validDocs.push(doc);
  }
  
  if (validDocs.length === 0) {
     return new Command({ goto: "rewrite_query" });
  }
  
  return { documents: validDocs };
};

为了避免死循环,建议加两层保护:

  1. recursionLimit:给整个 Graph 一个硬上限
  2. retry_count:在 state 里累计重写次数,超过阈值后降级(例如改用 Web Search 工具)

💡 练习题

  1. 思考题:在自适应 RAG 中,如果系统陷入了“检索 -> 不相关 -> 重写 -> 检索 -> 不相关”的死循环怎么办?(提示:使用状态中的 retry_count 字段或 recursionLimit)。
  2. 操作题:实现一个简单的 Corrective RAG (CRAG),当检索结果不佳时,回退到使用 Web Search 工具。

📚 参考资源

官方文档


✅ 总结

本章要点

  • LangGraph 让 RAG 可以拥有复杂的控制流(如循环、条件分支)。
  • 自适应 RAG 通过引入反馈环路(Feedback Loop)显著提高了问答质量。

下一步:我们将探讨如何利用 LangGraph 进行数据分析

登录以继续阅读

解锁完整文档、代码示例及更多高级功能。

立即登录

On this page