Spring AI RAG 实战:Advisor 配置、动态过滤与模板定制

Feng 4 阅读 AI技术

Spring AI 把 RAG 做成了可插拔的 Advisor,不用自己拼检索和拼接逻辑。核心就两条路:用现成的 QuestionAnswerAdvisor / RetrievalAugmentationAdvisor,或者基于模块化架构自己搭一套。

RAG 本身解决的是模型在长内容、事实准确性和上下文感知上的短板。向量库里存的是模型训练时没见过的东西,检索出来再喂给模型,回答就有据可依。

依赖先加对

要用 Advisor 开箱能力,项目里得引入这个坐标:

少这个依赖,QuestionAnswerAdvisor 直接找不到类,编译期就报错。

QuestionAnswerAdvisor 怎么接进 ChatClient

数据先灌进 VectorStore,然后给 ChatClient 挂上 Advisor 实例,一次调用就完成检索加生成:

ChatResponse response = ChatClient.builder(chatModel)
    .build().prompt()
    .advisors(new QuestionAnswerAdvisor(vectorStore))
    .user(userText)
    .call()
    .chatResponse();

默认行为是对向量库里所有文档做相似性搜索。想收窄范围,SearchRequest 支持类似 SQL 的过滤表达式,而且这套表达式在所有 VectorStore 之间是可移植的,换库不用重写。

过滤条件有两个落点:建 Advisor 时写死,之后所有 ChatClient 请求都带上;或者运行时按请求临时给。

下面这个例子把相似度阈值设成 0.8,只取前 6 条:

var qaAdvisor = QuestionAnswerAdvisor.builder(vectorStore)
    .searchRequest(SearchRequest.builder()
        .similarityThreshold(0.8d)
        .topK()
        .build())
    .build();

0.8 这个阈值别照抄。阈值偏高,召回文档少,模型容易答不全;偏低会灌进一堆噪声,反而稀释上下文。建议按你自己的语料实测一轮再定。

运行时动态改过滤条件

固定过滤适合场景稳定的项目。多租户或者按内容类型切换时,用顾问上下文参数 filter_expression 在每次请求里覆盖:

ChatClient chatClient = ChatClient.builder(chatModel)
    .defaultAdvisors(QuestionAnswerAdvisor.builder(vectorStore)
        .searchRequest(SearchRequest.builder().build())
        .build())
    .build();

// Update filter expression at runtime
String content = this.chatClient.prompt()
    .user("Please answer my question XYZ")
    .advisors(a -> a.param(QuestionAnswerAdvisor.FILTER_EXPRESSION, "type == 'Spring'"))
    .call()
    .content();

FILTER_EXPRESSION 参数按传入表达式动态过滤搜索结果。这里有个坑:表达式语法由底层 VectorStore 决定,可移植的只是那套类似 SQL 的写法,真换库时仍要回归测一遍过滤是否生效,别默认行为完全一致。

自定义模板到底改哪一层

QuestionAnswerAdvisor 默认用一套模板把检索到的文档拼进用户问题。要改,走 .promptTemplate() 构建器方法传自己的 PromptTemplate 对象。

关键区别在这:这里的 PromptTemplate 控制的是顾问如何把检索上下文和用户查询合并。它跟 ChatClient 上配的 TemplateRenderer(.templateRender())不是一回事——后者管的是顾问运行之前,初始用户/系统提示内容怎么渲染。改错层,你会发现过滤后的文档根本没进最终 prompt。

自定义 PromptTemplate 可以接任意 TemplateRenderer 实现,默认走基于 StringTemplate 引擎的那套。

适用与不适用

适合:知识库问答、文档检索后作答、需要按元数据切分检索范围的场景。Advisor 模式把这些串起来,代码量很小。

不适合:对检索结果要做复杂重排、多路召回融合、或者需要精细控制 token 预算的场景。现成 Advisor 的定制点有限,硬塞进去反而别扭,这种情况自己基于模块化架构搭 RAG 流更划算。

验证是否真的检索到了内容,别只看最终回答像不像。把日志级别调高观察 Advisor 实际拼进 prompt 的文档片段,或者断点看 SearchRequest 的过滤结果,比盯输出靠谱。

Feng
这位作者很神秘,还没有填写简介。