检索增强问答与评估
本教程共 45 篇 · 第 37 篇 · 更新于 2026-08-16 · 约 9 分钟阅读
本节目标:用 QuestionAnswerAdvisor 完成知识问答,掌握检索质量的调优手段,学会用相关性评估、事实核查和 LLM-as-a-Judge 三种方式衡量 RAG 效果。
37.1 QuestionAnswerAdvisor:开箱即用的问答
第 32 章的流程,QuestionAnswerAdvisor 一步帮你做完。它接到用户问题,先对向量库做相似度检索,再把结果拼进提示词,最后让模型回答。
使用它需要引入独立依赖:
<dependency>
<groupId>org.springframework.ai</groupId>
<artifactId>spring-ai-vector-store-advisor</artifactId>
</dependency>
假设数据已经入库,配置 Advisor 后接入 ChatClient:
QuestionAnswerAdvisor qaAdvisor = QuestionAnswerAdvisor.builder(vectorStore)
.searchRequest(SearchRequest.builder()
.similarityThreshold(0.8) // 相似度低于 0.8 的文档不采用
.topK(6) // 最多取 6 条
.build())
.build();
ChatClient chatClient = ChatClient.builder(chatModel)
.defaultAdvisors(qaAdvisor)
.build();
String answer = chatClient.prompt("我们的退款政策是什么?").call().content();
searchRequest 在构造时配置,会对所有请求生效。上一章的 topK、相似度阈值、过滤表达式,在这里就是问答质量的三个旋钮。
动态过滤
多租户场景下,不同用户只能检索自己的文档。过滤条件可以按请求动态设置:
String answer = chatClient.prompt()
.user("我的订单什么时候发货?")
.advisors(a -> a.param(QuestionAnswerAdvisor.FILTER_EXPRESSION,
"tenant == 'user-123'"))
.call()
.content();
FILTER_EXPRESSION 参数在运行时覆盖默认过滤条件。租户 ID 从上下文取,一套代码服务所有租户。
自定义提示词模板
默认模板把检索结果和问题拼在一起。模板内容可以换成自己的,要求更严格的回答规则:
PromptTemplate customTemplate = PromptTemplate.builder()
.template("""
<query>
以下是上下文信息。
---------------------
<question_answer_context>
---------------------
只依据上下文信息回答,不要使用先验知识。
遵循以下规则:
1. 如果答案不在上下文中,直接说不知道。
2. 避免"根据上下文……""以上信息表明……"这类表述。
""")
.build();
QuestionAnswerAdvisor qaAdvisor = QuestionAnswerAdvisor.builder(vectorStore)
.promptTemplate(customTemplate)
.build();
自定义模板必须保留两个占位符:query(用户问题)和 question_answer_context(检索结果)。模板里的规则可以自由发挥,比如强制”不知道就说不知道”。
Note2.0 里
QuestionAnswerAdvisor.Builder.userTextAdvise()已废弃,统一用.promptTemplate()定制。
37.2 RetrievalAugmentationAdvisor:模块化编排
问答简单,复杂 RAG 流程要精细控制。RetrievalAugmentationAdvisor 是模块化版本,把第 32 章讲的四个阶段都做成可配置项。它需要 spring-ai-rag 依赖。
最朴素的用法,只配检索器:
import org.springframework.ai.rag.advisor.RetrievalAugmentationAdvisor;
import org.springframework.ai.rag.retrieval.search.VectorStoreDocumentRetriever;
import org.springframework.ai.rag.generation.augmentation.ContextualQueryAugmenter;
Advisor ragAdvisor = RetrievalAugmentationAdvisor.builder()
.documentRetriever(VectorStoreDocumentRetriever.builder()
.vectorStore(vectorStore)
.similarityThreshold(0.50)
.topK(3)
.build())
.build();
String answer = chatClient.prompt()
.advisors(ragAdvisor)
.user(question)
.call()
.content();
VectorStoreDocumentRetriever 是检索阶段的实现,支持动态过滤表达式。默认情况下,检索结果为空时,Advisor 会指示模型不回答。想放开,让模型自由发挥,用 ContextualQueryAugmenter 打开开关:
Advisor ragAdvisor = RetrievalAugmentationAdvisor.builder()
.documentRetriever(VectorStoreDocumentRetriever.builder()
.vectorStore(vectorStore)
.similarityThreshold(0.50)
.build())
.queryAugmenter(ContextualQueryAugmenter.builder()
.allowEmptyContext(true)
.build())
.build();
知识库问答建议保持默认:查不到就说不知道,别让模型瞎编。
检索前的查询加工
检索质量差,先别怪向量库,可能是问题本身不适合检索。检索前阶段(Pre-Retrieval)负责加工问题。
RewriteQueryTransformer 用模型改写问题,去噪、补全语义:
Advisor ragAdvisor = RetrievalAugmentationAdvisor.builder()
.queryTransformers(RewriteQueryTransformer.builder()
.chatClientBuilder(chatClientBuilder.build().mutate())
.build())
.documentRetriever(VectorStoreDocumentRetriever.builder()
.vectorStore(vectorStore)
.build())
.build();
对话场景用 CompressionQueryTransformer,把聊天历史和追问压缩成独立问题。“它第二大城市是哪”会被还原成”丹麦第二大城市是哪个”,否则向量检索根本匹配不上。
MultiQueryExpander 把一个问法扩成多个,提高召回:
MultiQueryExpander queryExpander = MultiQueryExpander.builder()
.chatClientBuilder(chatClientBuilder)
.numberOfQueries(3) // 生成 3 个变体
.includeOriginal(true) // 保留原始问题
.build();
Tip用
QueryTransformer时,把 ChatModel 的 temperature 调到 0。改写结果越稳定,检索质量越高,默认温度对改写任务通常偏高。
37.3 检索优化三板斧
RAG 效果不好,按这个顺序排查。
topK 调数量。 返回太少,上下文不全;返回太多,噪声稀释重点,token 成本也涨。从 4-6 起步,看答案质量调整。
相似度阈值调纯度。 检索结果像但不对,是幻觉重灾区。阈值设 0.7-0.8,宁缺毋滥。
过滤表达式收范围。 多租户、多分类数据必须过滤,否则跨域文档会污染上下文。
三个参数组合实验,每次只动一个,记录答案质量对比。这比拍脑袋改提示词有效得多。
37.4 评估:RAG 效果怎么量
RAG 上线前必须回答一个问题:效果到底好不好?人工抽查太慢,传统指标(BLEU、ROUGE)衡量不了语义。Spring AI 的思路是用模型评估模型。
核心接口是 Evaluator:
@FunctionalInterface
public interface Evaluator {
EvaluationResponse evaluate(EvaluationRequest evaluationRequest);
}
EvaluationRequest 装三样东西:用户原始问题、检索到的上下文、模型生成的回答。评估就是拿这三样去问评估模型:答得对不对、贴不贴上下文。
RelevancyEvaluator:相关性评估
RelevancyEvaluator 判断回答是否与上下文相关,专门用来检验 RAG 流程。典型用法是在集成测试里跑完问答后立刻评估:
@Test
void evaluateRelevancy() {
String question = "阿纳克莱图斯和比尔巴的冒险发生在哪里?";
RetrievalAugmentationAdvisor ragAdvisor = RetrievalAugmentationAdvisor.builder()
.documentRetriever(VectorStoreDocumentRetriever.builder()
.vectorStore(pgVectorStore)
.build())
.build();
ChatResponse chatResponse = ChatClient.builder(chatModel).build()
.prompt(question)
.advisors(ragAdvisor)
.call()
.chatResponse();
EvaluationRequest evaluationRequest = new EvaluationRequest(
question, // 用户问题
chatResponse.getMetadata().get(RetrievalAugmentationAdvisor.DOCUMENT_CONTEXT), // 检索上下文
chatResponse.getResult().getOutput().getText()); // 模型回答
RelevancyEvaluator evaluator = new RelevancyEvaluator(ChatClient.builder(chatModel));
EvaluationResponse evaluationResponse = evaluator.evaluate(evaluationRequest);
assertThat(evaluationResponse.isPass()).isTrue();
}
回答可以从 ChatResponse 的元数据里拿到检索上下文(DOCUMENT_CONTEXT 键),正好构成评估三要素。把这种测试挂进 CI,每次改检索参数、换模型都能自动回归。
FactCheckingEvaluator:事实核查
FactCheckingEvaluator 专门查幻觉:把”上下文”和”回答中的论断”分别作为 document 和 claim 交给评估模型,判断论断是否被上下文支持。
var evaluator = new FactCheckingEvaluator(ChatClient.builder(chatModel));
EvaluationRequest request = new EvaluationRequest(
"地球是离太阳第三近的行星……", // 上下文
Collections.emptyList(),
"地球是离太阳第四近的行星。"); // 待核查的论断
EvaluationResponse response = evaluator.evaluate(request);
assertFalse(response.isPass()); // 论断不被支持,评估不通过
有个实用的成本技巧:事实核查用小型专用模型。Bespoke 的 Minicheck 就是为这事训练的,跑在 Ollama 上,成本远低于旗舰模型。核查是高频动作,模型选型值得单独考虑。
LLM-as-a-Judge:让模型当裁判
更系统的评估方案是 LLM-as-a-Judge:用专门的评估模型给回答打分。研究显示,好的裁判模型和人类判断的一致性可达 85%,高于人类之间的一致性。
两种主流模式。直接打分:裁判按 1-4 分评估单个回答的质量。成对比较:裁判从两个候选回答里挑更好的,常用于 A/B 测试。
Spring AI 用 Recursive Advisor 实现自动打分-改进循环:生成回答 → 裁判打分 → 不及格就把反馈拼回提示词重试 → 直到达标或达到次数上限。完整示例见第 38 章的”评估流程”一节:生成用 Claude,评估用 Ollama 上的专用裁判模型,两个模型分离能减少偏误;打分结果通过结构化输出解析成 EvaluationResponse 记录(rating、evaluation、feedback)。
NoteRecursive Advisor 是实验特性:仅支持非流式调用,多次重试会放大模型调用成本,必须设置重试上限防止死循环。
LLM-as-a-Judge 的几条最佳实践:
- 用专门的裁判模型,而不是随手拿主力模型
- 生成和评估用不同模型,避免”自己评自己”
- temperature 设为 0,保证打分稳定
- 提示词用整数分制、给示例
- 高风险场景保留人工复核
37.5 评估体系怎么搭
生产级 RAG 的评估分三层。
回归测试层。 准备一批”问题-标准答案”的测试集,跑 RelevancyEvaluator 自动判断,挂 CI。检索参数改动、模型升级都能快速发现效果回退。
事实核查层。 对高风险的生成内容(医疗、法律、金融)用 FactCheckingEvaluator 逐条核查,不通过就不返回给用户。
人工抽检层。 LLM-as-a-Judge 跑批量评分,人工抽看低分样本,沉淀成新的测试用例。
三层配合,RAG 系统的质量才可控。
37.6 小结
QuestionAnswerAdvisor 让知识问答开箱即用,RetrievalAugmentationAdvisor 提供模块化编排,查询改写和扩展解决”问题不适合检索”。检索优化三板斧是 topK、相似度阈值、过滤表达式。评估用 Evaluator 体系:RelevancyEvaluator 看相关性,FactCheckingEvaluator 查事实,LLM-as-a-Judge 做批量打分,三层评估守住质量底线。
到这里,RAG 从原理、数据管道、向量库到问答评估的完整链路就通了。