文章目录
前言
在 Spring AI 中,Advisor API 本质上就是对话调用链上的拦截器。它位于用户 Prompt 和大模型调用之间,可以在请求发出前对 Prompt 做增强,也可以在响应返回后做处理。
用 Advisor 实现向量数据库的 RAG 检索,核心是 QuestionAnswerAdvisor。这个内置拦截器的工作方式很直接:拦截用户 Prompt,在绑定的 VectorStore 中执行相似度搜索,把检索到的文档片段拼进 Prompt 的 {question_answer_context} 占位符,再交给模型生成答案。
根据检索范围的固定还是动态 ,有两种实现方式。两者都通过 defaultAdvisors 注册,区别只在调用时是否额外传入过滤参数。
方式一:全局共用一份知识库
QuestionAnswerAdvisor 在构建时就绑定了 VectorStore,注册为 defaultAdvisors 后,每次对话自动走同一个检索逻辑,对整个向量库执行相似度搜索。
java
@Configuration
public class RagConfig {
@Bean
public QuestionAnswerAdvisor questionAnswerAdvisor(VectorStore vectorStore) {
return QuestionAnswerAdvisor.builder(vectorStore)
.searchRequest(SearchRequest.builder()
.topK(4)
.similarityThreshold(0.8)
.build())
.build();
}
@Bean
public ChatClient chatClient(ChatModel chatModel,
QuestionAnswerAdvisor questionAnswerAdvisor) {
return ChatClient.builder(chatModel)
.defaultAdvisors(questionAnswerAdvisor) // 全局注册
.build();
}
}
调用时不需要在每次 prompt() 里重复指定任何东西:
java
String answer = chatClient.prompt()
.user("Spring AI 的 Advisor 是什么?")
.call()
.content();
topK 控制返回的文档片段数量,similarityThreshold 是相似度得分下限。两者的配合逻辑是:先按阈值过滤低质量候选,再取前 K 个 。similarityThreshold 设为 0.8 意味着只接受相似度 80 分以上的文档片段。
这种方式适合所有对话共享同一套知识的场景,配置最简单,但无法按用户或业务类型收窄检索范围。
方式二:每次对话动态过滤
同样注册为 defaultAdvisors,但允许在运行时通过 FILTER_EXPRESSION 参数 覆盖或细化检索范围。过滤表达式采用类似 SQL 的语法,可移植于所有 VectorStore 实现。
java
@Bean
public ChatClient chatClient(ChatModel chatModel, VectorStore vectorStore) {
QuestionAnswerAdvisor qaAdvisor = QuestionAnswerAdvisor.builder(vectorStore)
.searchRequest(SearchRequest.builder()
.topK(4)
.build())
.build();
return ChatClient.builder(chatModel)
.defaultAdvisors(qaAdvisor) // 同样注册为默认 Advisor
.build();
}
调用时传入过滤条件:
java
String answer = chatClient.prompt()
.user("我的订单有什么问题?")
.advisors(a -> a.param(
QuestionAnswerAdvisor.FILTER_EXPRESSION,
"user_id == '12345' && type == 'order'"))
.call()
.content();
底层会先通过元数据字段缩小候选集,再在候选集内执行向量相似度计算。如果同时在 Advisor 构建时设置了基础过滤表达式,运行时传入的表达式优先级更高,会覆盖构建时的配置。
这种方式适合多租户、多业务线、需要权限隔离的场景,同一份向量库可以为不同对话提供不同范围的检索结果。
两种方式的核心区别
两种实现都使用了 defaultAdvisors ,QuestionAnswerAdvisor 都是通过 ChatClient.builder().defaultAdvisors(...) 注册的。区别只在于调用时是否额外传入运行时参数来收窄检索范围。
| 维度 | 方式一:全局共用 | 方式二:动态过滤 |
|---|---|---|
| defaultAdvisors | 有 | 有 |
| 检索范围 | 整个向量库 | 过滤后的子集 |
| 调用时的 advisors() | 不传 | 传 FILTER_EXPRESSION |
| 隔离能力 | 无 | 按用户/租户/业务类型隔离 |
| 适用场景 | 统一知识库问答 | 多租户、多业务线、权限控制 |
方式一是"注册后就固定",方式二是"注册后每次还能改"。
一个配置层面的细节
QuestionAnswerAdvisor 的默认 similarityThreshold 是 0.0,意味着接受所有匹配结果,不管相似度多低。在生产环境中,这通常不是想要的行为。设置一个合理的阈值,可以让模型在检索不到相关内容时更诚实地表示"不知道",而不是基于弱相关片段给出看似自信的错误回答。
阈值和 topK 的交互也需要留意:高阈值配高 topK,如果没有任何文档越过阈值线,模型会退回使用自己的知识库回答;阈值越低,越多的低质量匹配会进入 Prompt,可能干扰模型判断。
如需深入了解Agent核心架构设计、工具调用机制、记忆与上下文工程、多智能体协作模式、规划与推理策略、LangGraph状态图原理、中间件扩展体系、生产级Agent落地实践等内容,请持续关注本专栏《Agent智能体开发从入门到精通》系列文章。