RAG 检索增强生成
约 874 字大约 3 分钟
布欧-Lewyon
2026-05-15
什么是 RAG
RAG(Retrieval-Augmented Generation)让 LLM 在回答时参考外部知识库中的内容,解决 LLM 知识过时和幻觉问题。
基本 RAG 实现
@Service
public class RAGService {
private final VectorStore vectorStore;
private final ChatClient chatClient;
private final TokenTextSplitter splitter;
public RAGService(VectorStore vectorStore, ChatClient.Builder builder) {
this.vectorStore = vectorStore;
this.chatClient = builder
.defaultSystem("你是一个知识库问答助手。基于提供的参考文档回答问题。")
.build();
this.splitter = TokenTextSplitter.builder()
.withChunkSize(500)
.withChunkOverlap(50)
.build();
}
// 添加知识
public void addKnowledge(String text, String source) {
Document doc = new Document(text, Map.of("source", source));
List<Document> chunks = splitter.apply(List.of(doc));
vectorStore.add(chunks);
}
// RAG 问答
public String ask(String question) {
// 1. 检索相关文档
List<Document> relevantDocs = vectorStore.similaritySearch(
SearchRequest.builder()
.query(question)
.topK(3)
.build());
// 2. 构建上下文
String context = relevantDocs.stream()
.map(Document::getContent)
.collect(Collectors.joining("\n\n---\n\n"));
// 3. 增强 Prompt
String prompt = """
请基于以下参考文档回答问题。
如果参考文档中没有相关信息,请说明无法回答。
参考文档:
%s
问题:%s
""".formatted(context, question);
// 4. 生成回答
return chatClient.call(
new Prompt(prompt, ChatOptionsBuilder.builder()
.withTemperature(0.3)
.build())
);
}
}QuestionAnswerAdvisor
Spring AI 提供了 QuestionAnswerAdvisor 简化 RAG 流程:
@Bean
public ChatClient ragChatClient(ChatClient.Builder builder, VectorStore vectorStore) {
return builder
.defaultSystem("基于知识库准确回答问题")
.defaultAdvisors(
new QuestionAnswerAdvisor(
vectorStore,
SearchRequest.builder()
.topK(4)
.similarityThreshold(0.5)
.build()
)
)
.build();
}@RestController
public class RAGController {
private final ChatClient chatClient;
@GetMapping("/rag/ask")
public String ask(@RequestParam String question) {
// Advisor 自动完成:向量检索 → 增强 Prompt → 生成回答
return chatClient.call(question);
}
}自定义 Prompt 模板
@Bean
public ChatClient customRagClient(ChatClient.Builder builder, VectorStore vectorStore) {
return builder
.defaultAdvisors(
new QuestionAnswerAdvisor(
vectorStore,
SearchRequest.builder().topK(5).build(),
"""
你是一个专业的技术支持客服。请基于以下知识库内容回答用户问题。
知识库内容:
{question_answer_context}
如果知识库中没有相关信息,请引导用户联系人工客服。
用户问题:{input_question}
注意事项:
- 只回答与知识库相关的问题
- 引用知识库中的具体内容
- 如果不确定,请说明
"""
)
)
.build();
}带来源引用的回答
public RAGResponse askWithSources(String question) {
List<Document> docs = vectorStore.similaritySearch(
SearchRequest.builder().query(question).topK(3).build());
String context = docs.stream()
.map(d -> "来源[%s]: %s".formatted(
d.getMetadata().get("source"), d.getContent()))
.collect(Collectors.joining("\n\n"));
String answer = chatClient.call(new Prompt("""
基于以下参考文档回答问题,并在回答末尾标注信息来源:
%s
问题:%s
""".formatted(context, question)));
// 提取引用信息
List<String> sources = docs.stream()
.map(d -> (String) d.getMetadata().get("source"))
.distinct()
.toList();
return new RAGResponse(answer, sources);
}
public record RAGResponse(String answer, List<String> sources) {}RAG 优化技巧
// 1. 调整 Chunk 大小
// 事实类问答用小块(200-300 Token),综合类问答用大块(500-1000 Token)
// 2. 多路召回
List<Document> docs1 = vectorStore.similaritySearch(
SearchRequest.builder().query(question).topK(3).build());
List<Document> docs2 = vectorStore.similaritySearch(
SearchRequest.builder().query(expandQuery(question)).topK(3).build());
// 合并去重
// 3. 重排序(ReRank)
List<Document> candidates = vectorStore.similaritySearch(
SearchRequest.builder().query(question).topK(20).build());
// 用 Cohere 或 Cross-Encoder 重排序
List<Document> reranked = reRank(question, candidates);
List<Document> top3 = reranked.subList(0, 3);
// 4. Hybrid Search(向量 + 全文搜索)小结
- RAG = 向量检索(知识库) + LLM 生成(回答)。
QuestionAnswerAdvisor自动完成 RAG 流程,无需手动编码。- Advisor 支持自定义 Prompt 模板和 SearchRequest 参数。
- 来源引用提升回答的可信度和可追溯性。
- 优化方向:Chunk 大小调整、多路召回、ReRank 重排序。
上一节:Document 加载与切分 下一节:项目架构与准备
