RAG(Retrieval-Augmented Generation,检索增强生成)是一种结合了信息检索和语言模型生成能力的架构模式。它允许语言模型在回答问题时,先从外部知识库中检索相关信息,再基于这些信息生成答案,从而提升回答的准确性和时效性。


🧠 RAG 的核心思想

传统的 LLM 是静态训练的,其知识截止于训练数据的时间。而 RAG 架构通过引入外部知识源,使得模型可以在推理阶段动态获取最新的、特定领域的信息,从而:

  • 回答训练数据中没有的信息
  • 减少“幻觉”(hallucination)
  • 提高领域相关任务的表现

🔍 RAG 的基本流程

  1. 用户提问:用户提供一个问题。
  2. 语义检索:系统使用一个检索器(retriever)从外部知识库中查找与问题相关的文档或段落。
  3. 上下文拼接:将检索到的内容作为上下文(context),与原始问题一起输入给语言模型。
  4. 生成回答:语言模型基于上下文生成最终的回答。

🧩 RAG 的核心组件

组件 作用
Document Loader 从本地或远程加载文档(如 PDF、TXT、HTML、数据库等)
Splitter / Chunker 将长文档切分为较小的文本块(chunks),便于向量化
Embedding Model 将每个文本块转换为向量(embedding)表示
Vector Store / Database 存储向量,并支持高效相似度检索(如 FAISS、Qdrant、Weaviate、Chroma 等)
Retriever 根据用户查询,从向量库中检索最相关的文档片段
Prompt Template 构建包含检索结果的提示模板
Language Model (LLM) 使用增强后的提示生成自然语言回答

📦 LangChain4j 中的 RAG 实现

LangChain4j 提供了对 RAG 架构的完整支持,包括以下模块:

1. Document Loaders

  • 支持多种格式:
    • FileSystemDocumentLoader(读取本地文件)
    • UrlDocumentLoader(抓取网页内容)
    • PdfDocumentLoader
    • PlainTextDocumentLoader
    • 自定义 DocumentLoader 接口
Document document = FileSystemDocumentLoader.loadDocument(Paths.get("data/sample.txt"));

2. Text Splitter

用于将大文本分割成小块(chunks)

TextSplitter textSplitter = new RecursiveCharacterTextSplitter(500, 50);
List<Document> chunks = textSplitter.split(document);

3. EmbeddingModel

LangChain4j 支持多种嵌入模型:

  • OpenAI Embeddings
  • HuggingFace Inference API
  • Local models via Ollama

示例(OpenAI):

EmbeddingModel embeddingModel = new OpenAiEmbeddingModel(openAiApiKey);

4. VectorStore

LangChain4j 支持多个向量数据库:

  • Qdrant
  • Weaviate
  • Chroma(通过 REST API)
  • In-memory VectorStore(适合测试)

示例(使用内存向量库):

InMemoryVectorStore vectorStore = new InMemoryVectorStore();
vectorStore.addAll(embeddingModel.embedAll(chunks));

5. Retriever

根据查询语句从向量库中检索 top-k 相关文档:

ContentRetriever retriever = VectorStoreContentRetriever.builder()
    .vectorStore(vectorStore)
    .embeddingModel(embeddingModel)
    .maxResults(3)
    .build();

6. PromptTemplate + LLM

将检索到的内容插入提示模板,然后交给 LLM 生成回答:

PromptTemplate promptTemplate = PromptTemplate.from(
    "Answer the following question based on the context provided:\n\n" +
    "Context: {{context}}\n\n" +
    "Question: {{question}}"
);

String answer = chatLanguageModel.generate(promptTemplate.apply(Map.of(
    "context", retriever.retrieve(query),
    "question", query
)));

🧪 示例代码(Spring Boot 中的 RAG 应用)

下面是一个完整的 Spring Boot RAG 示例结构:

✅ Controller

@RestController
@RequestMapping("/rag")
public class RagController {

    private final RagService ragService;

    public RagController(RagService ragService) {
        this.ragService = ragService;
    }

    @PostMapping("/ask")
    public String ask(@RequestBody String question) {
        return ragService.answer(question);
    }
}

✅ Service

@Service
public class RagService {

    private final ChatLanguageModel chatModel;
    private final ContentRetriever retriever;
    private final PromptTemplate promptTemplate;

    public RagService(ChatLanguageModel chatModel,
                      ContentRetriever retriever,
                      PromptTemplate promptTemplate) {
        this.chatModel = chatModel;
        this.retriever = retriever;
        this.promptTemplate = promptTemplate;
    }

    public String answer(String question) {
        List<Content> relevantContents = retriever.retrieve(question);
        String context = relevantContents.stream()
                .map(Content::text)
                .collect(Collectors.joining("\n"));

        Prompt prompt = promptTemplate.apply(Map.of("context", context, "question", question));
        return chatModel.generate(prompt.text());
    }
}

✅ Configuration

@Configuration
public class RagConfig {

    @Bean
    public EmbeddingModel openAiEmbeddingModel(@Value("${openai.api.key}") String apiKey) {
        return new OpenAiEmbeddingModel(apiKey);
    }

    @Bean
    public ContentRetriever vectorStoreRetriever(EmbeddingModel embeddingModel, VectorStore vectorStore) {
        return VectorStoreContentRetriever.builder()
                .vectorStore(vectorStore)
                .embeddingModel(embeddingModel)
                .maxResults(3)
                .build();
    }

    @Bean
    public PromptTemplate ragPromptTemplate() {
        return PromptTemplate.from(
            "Answer the following question based on the context provided:\n\n" +
            "Context: {{context}}\n\n" +
            "Question: {{question}}"
        );
    }
}

🗂️ 数据准备流程(初始化知识库)

可以在应用启动时加载并索引文档:

@Component
public class DataInitializer implements CommandLineRunner {

    private final VectorStore vectorStore;
    private final EmbeddingModel embeddingModel;

    public DataInitializer(VectorStore vectorStore, EmbeddingModel embeddingModel) {
        this.vectorStore = vectorStore;
        this.embeddingModel = embeddingModel;
    }

    @Override
    public void run(String... args) throws Exception {
        Document document = FileSystemDocumentLoader.loadDocument(Paths.get("data/sample.txt"));
        TextSplitter splitter = new RecursiveCharacterTextSplitter(500, 50);
        List<Document> chunks = splitter.split(document);
        vectorStore.addAll(embeddingModel.embedAll(chunks));
    }
}

🧠 进阶功能建议

功能 描述
✅ 多源数据支持 支持 PDF、Word、Markdown、数据库等多种数据源
✅ 持久化 VectorStore 使用 Qdrant、Weaviate 或 Redis 持久化向量数据
✅ 增量更新机制 支持定期重新加载文档并更新向量库
✅ 多租户支持 不同用户/组织使用不同的知识库
✅ 安全过滤 对敏感信息进行访问控制
✅ 日志与监控 记录每次检索和生成过程,用于调试和优化
✅ 缓存机制 缓存常见问题的答案以提高性能

📈 性能优化技巧

技巧 描述
🧱 分块大小调整 通常 200~500 tokens 是一个合理区间
📊 向量维度匹配 使用与模型匹配的嵌入维度(如 OpenAI 1536)
⚡ 异步检索 在大规模知识库中使用异步检索
📈 批量处理 对批量查询进行合并检索,减少请求次数
🧠 混合搜索 结合关键词搜索和语义搜索提高精度

🧩 适用场景

场景 说明
💬 智能客服 基于公司 FAQ、产品手册提供精准回答
📚 教育辅导 根据教材内容回答学生问题
🏥 医疗咨询 提供基于医学指南的辅助诊断建议
📰 新闻问答 利用最新新闻内容回答时效性问题
🧾 法律助手 根据法律条文提供合规建议

📚 参考资源


Logo

欢迎加入DeepSeek 技术社区。在这里,你可以找到志同道合的朋友,共同探索AI技术的奥秘。

更多推荐