Spring AI 核心模块讲解
Spring AI 核心模块
建议搭配源码一起阅读.
AI commons
这一模块为 Spring AI 的各个上层能力(chat client、model、RAG、vector-store 等)提供通用基础,涵盖内容与媒体抽象、文档与元数据、模板渲染、分块与计数、评估与观测工具、文件读取与写入等。掌握这个模块能让你把不同部件“串起来”形成完整的数据处理和提示工程链路。
一、内容与媒体(org.springframework.ai.content)
- Content 接口
- getText()/getMetadata():统一“文本 + 元数据”的内容抽象,Message(聊天消息)和 Document(文档)都实现了它。便于跨模块处理内容格式与元数据。
- Media 与 MediaContent
- Media:表达媒体数据与 MIME 类型,支持 data(byte[] 或 URI 字符串)、id(模型可能返回媒体引用)、name(供模型引用用,需注意避免 prompt 注入)。提供 Format 内部类列出常用 MimeType 常量。
- MediaContent:在 Content 的基础上增加 getMedia(),作为“多模态内容”接口(UserMessage、某些 Document 可实现)。
- 用途:让 UserMessage 支持图片、音频、视频等附件;TokenCountEstimator 会对 Media 的 MIME 和数据做 token 估算(见 tokenizer)。
二、文档体系与 ETL(org.springframework.ai.document)
- Document
- 统一“文本或媒体”文档结构:id、text 或 media 二选一;metadata 为扁平键值(用于向量库存储);score 表示相似度或相关性分数(检索或 RAG 时用)。
- 默认 ContentFormatter(DefaultContentFormatter),支持 getFormattedContent(metadataMode) 把内容+元数据拼接;用于 token 计数、检索预处理等。
- Builder:支持 text()/media()/metadata()/score() 构建;设置父 id 与 chunk 元数据(分块器会写 parent_document_id、chunk_index、total_chunks)。
- IdGenerator(id 子包):RandomIdGenerator(UUID),JdkSha256HexIdGenerator(基于内容计算哈希 id)等。
- DocumentReader/Writer/Transformer
- DocumentReader(Supplier<List<Document>>):统一读取接口,doc-reader 子模块(pdf、tika、jsoup 等)都实现它。
- DocumentWriter(commons 与 vector-store 都有):把文档写入存储(例如 FileDocumentWriter 写文件,VectorStore 接口也继承了 DocumentWriter)。
- DocumentTransformer:对文档做转换,如分块(splitter)、格式化或清洗。
- ContentFormatter/DefaultContentFormatter
- 组合文本与元数据输出,便于分块、计数、入库时统一格式(比如在 TokenCountBatchingStrategy 中用来估算每个文档的 token 数)。
三、模板渲染(org.springframework.ai.template)
- TemplateRenderer(函数式接口)
- apply(template, variables):渲染模板字符串。chat 模块内的 PromptTemplate 默认使用 ST 实现(spring-ai-template-st 模块的 StTemplateRenderer)。
- NoOpTemplateRenderer、ValidationMode(在 commons 提供基础抽象,上层可使用 ST 或自定义)。
四、分块与计数(org.springframework.ai.transformer.splitter、org.springframework.ai.embedding、org.springframework.ai.tokenizer)
- TextSplitter(抽象类)
- 实现 DocumentTransformer.apply(List<Document>),把每个文档拆成多个短文档,复制父文档的 ContentFormatter(可选),并在新文档 metadata 中填入 parent_document_id、chunk_index、total_chunks。
- split(Document/ List<Document>) 简捷方法;核心抽象方法 splitText(String) 由子类实现。
- TokenTextSplitter
- 按 token 数进行分块(默认 chunkSize=800,保留标点,控制最小 chunk 字符数与最小嵌入长度,最大 chunk 数等);使用 JTokkit 的 CL100K_BASE 编码(与 OpenAI 兼容)做编码/解码。
- 用于“长文档分块 → 嵌入 → 入库”,是 RAG 的第一步的常见组件。
- BatchingStrategy 与 TokenCountBatchingStrategy(embedding 包)
- BatchingStrategy:将一批 Document 拆成多个子批次,保证批次内 token 总数不超过上限(默认 8191,保留 10% 富余用于安全)。
- TokenCountBatchingStrategy:用 JTokkitTokenCountEstimator 估算文档 token 数,按照 maxInputTokenCount(扣除 reservePercentage)分配批次。保证顺序不变,便于 embeddings 与文档一一对应。
- TokenCountEstimator 与 JTokkitTokenCountEstimator(tokenizer 包)
- JTokkitTokenCountEstimator:对字符串、MediaContent、Iterable<MediaContent> 估算 token 数;二进制媒体数据会转 Base64 计数;Media 的 MIME 类型也计入 token。
五、评估(org.springframework.ai.evaluation)
- Evaluator、EvaluationRequest/Response:统一了评估器接口(用于质量评估、事实核查等),具体评估器(如 Relevancy、FactChecking)在其他模块定义,但基础接口在 commons 中。
六、观测与日志(org.springframework.ai.observation、conventions)
- AiOperationMetadata:记录操作类型与 Provider(如 operation=“inference”、provider=“SPRING_AI”);在 chat-client、model、vector-store 观测上下文中使用。
- ObservabilityHelper:把 key-values 或 strings 拼接成日志友好字符串(统一日志格式)。
- conventions:AiObservationAttributes、AiObservationMetricAttributes/Names、AiProvider、SpringAiKind、VectorStoreObservationAttributes 等,提供统一的标签字典与枚举。上层模块(chat、vector-store、image 等)都引用这些,保证指标一致性。
七、读取与写入(org.springframework.ai.reader、org.springframework.ai.writer)
- reader:
- JsonReader、TextReader:读取 JSON 或纯文本为 Document;ExtractedTextFormatter 抽取文本;JsonMetadataGenerator/EmptyJsonMetadataGenerator 为 JSON 提取元数据。
- writer:
- FileDocumentWriter:把 Document 写入到文件(常用于把分块后的文档持久化或调试输出)。
- util:ResourceUtils.getText(uri),便捷读取 classpath:/、file:/、http:/ 资源为字符串;JacksonUtils、ParsingUtils、LoggingMarkers 等通用工具。
八、典型使用场景(小示例)
1) 文档读取 → 分块 → 嵌入批处理 → 写入向量库
- 读取
- 通过某个 DocumentReader(如 pdf-reader、tika-reader)得到 List<Document>。
- 分块
- TokenTextSplitter.split(documents),每个 chunk 带 parent_document_id 等元数据。
- 嵌入批处理
- 使用 TokenCountBatchingStrategy 做批次划分,EmbeddingModel.embed(List<Document>, options, batchingStrategy) 得到向量列表。
- 入库
- VectorStore.add(documents)(vector-store 模块),并结合 metadata(如 source、page、chunk_index)方便后续检索过滤。 2) 模板渲染 + 提示工程
- TemplateRenderer 支持把模板文本与变量合并渲染为最终 Prompt 内容;在 chat 模块 PromptTemplate 中默认使用 ST 渲染器。
- 结合 PromptTemplate.create(ChatOptions) 输出带选项的 Prompt,然后交给 ChatModel/ChatClient 调用。
九、与上层模块的关系
- chat-client:UserMessage 支持 Media;DefaultChatClientUtils 会利用 PromptTemplate 渲染 system/user 文本;TokenCountEstimator、LastMaxTokenSizeContentPurger 能辅助提示长度治理。
- RAG:Document/Reader/Transformer/Formatter、TokenTextSplitter、BatchingStrategy 是 RAG 数据管线的基础;VectorStoreDocumentRetriever(在 RAG)使用 metadata 过滤。
- vector-store:DocumentMetadata 里的扁平键值有利于 Filter DSL;ObservabilityHelper 与 VectorStoreObservationAttributes 保持指标一致;SimpleVectorStore(在 vector-store 模块)直接用 Document 的 text/metadata。
十、最佳实践与注意事项
- Document 只允许“text 或 media”二选一;metadata 需扁平基本类型(string、int、float、boolean),便于跨向量库一致存储与过滤。
- 分块策略与批处理策略需配合嵌入模型的 token 上限与性能,预留 reservePercentage 是非常有用的缓冲。
- 计数估算对多模态输入要计 MIME 与数据体积(binary → Base64);避免过大的媒体数据导致过多 token。
- 模板渲染时,变量可包含 Resource;PromptTemplate 内部会读取资源为字符串渲染。
- 观测统一:使用 AiOperationMetadata + conventions,配合上层的 Observation handler 输出 prompt/completion/检索结果,利于调试与审计。
AI model
这一模块是 Spring AI 的“核心域模型层”,为聊天、嵌入、图片、音频、内容审核、工具调用、结构化输出、观测指标等提供统一抽象与公共能力,几乎所有上层模块(chat client、RAG、vector-store、各 provider)都建立在它的 API 上。
一、总体架构与核心抽象
- Model<TReq,TRes>
- 通用 AI 模型接口:call(request) → response。具体模型族(聊天、嵌入、图片、音频、审核)都以此为基础。
- 请求/响应/结果统一结构
- ModelRequest、ModelResponse、ModelResult:规范“请求载荷”“响应载荷”和“单次结果”的基本形态,便于上层统一处理与观测。
- 观测与日志(Micrometer Observation)
- 在 chat/embedding/image/moderation 等各模型族下提供 ObservationContext、ObservationConvention、ObservationDocumentation、Handler,统一打点并打印 prompt/completion 内容。
二、聊天模型
1) 核心接口与类型(org.springframework.ai.chat.model)
- ChatModel extends Model<Prompt, ChatResponse>, StreamingChatModel
- 同时支持非流式和流式。默认提供便捷方法:
- call(String message)/call(Message…):快速创建 Prompt 并获取第一条 Generation 的文本输出;
- stream(Prompt):返回 Flux<ChatResponse>;StreamingChatModel 还提供 stream(String)/stream(Message…) 返回 Flux<String>(自动取文本)。
- getDefaultOptions():返回 ChatOptions 默认值。
- 同时支持非流式和流式。默认提供便捷方法:
- ChatResponse
- generations: List<Generation>(通常第一条 getResult() 代表主要输出)
- metadata: ChatResponseMetadata(模型标识、用量、限流等信息,并可挂接自定义键值)
- hasToolCalls()/hasFinishReasons(Set<String>):辅助判断是否有工具调用请求、是否以某些 finish reason 结束。
- Generation
- assistantMessage: AssistantMessage(输出消息),metadata: ChatGenerationMetadata(finishReason 等)。
- MessageAggregator
- 流式场景消息聚合支持(与 chat-client 中的聚合器配合)。
2) 消息与提示(org.springframework.ai.chat.messages、org.springframework.ai.chat.prompt)
- Message 及子类
- SystemMessage:系统提示,常用于“角色/规则/指令”;
- UserMessage:用户提示,支持多模态 Media(图片/音频等),builder 支持 text(Resource) 与 media(…);
- AssistantMessage:模型输出,包含文本与工具调用(ToolCalls);
- ToolResponseMessage:工具执行后的响应消息,放回到对话上下文。
- Prompt(模型请求)
- 核心构造:
- new Prompt(String) / new Prompt(Message) / new Prompt(List<Message>) / new Prompt(message, ChatOptions)
- 获取与增强:
- getInstructions(): List<Message>;getContents(): 拼接内容;
- getSystemMessage() / getUserMessage() / getUserMessages(): 快速定位消息;
- copy(): 深拷贝提示;
- augmentSystemMessage(Function / SystemText) / augmentUserMessage(Function/UserText):就地增强系统/用户消息(例如为 RAG 注入 context)。
- 核心构造:
- PromptTemplate(模板渲染)
- 使用 TemplateRenderer(默认 ST 模板)渲染模板字符串 + 变量;
- render()/create() 系列方法快速输出 Message/Prompt;
- 支持 Resource 变量(自动读取文本);支持 create(ChatOptions) 输出带选项的 Prompt;
- 与 chat-client 的模板渲染器一致,确保可替换成自定义渲染器。
- ChatOptions(跨模型可移植的常用参数)
- 包含 model、maxTokens、temperature、topK/topP、presencePenalty、frequencyPenalty、stopSequences 等;
- DefaultChatOptions/DefaultChatOptionsBuilder 提供默认实现;copy() 便于不可变拷贝。
三、结构化输出(org.springframework.ai.converter)
- StructuredOutputConverter<T> + FormatProvider
- convert(String) 将模型输出文本解析为类型 T;getFormat() 返回引导 LLM 按指定结构输出的提示片段。
- BeanOutputConverter<T>
- 基于 JSON Schema(victools + Jackson)为目标类型生成 schema,并提供 getFormat() 提示 LLM 输出符合 RFC8259 的 JSON;
- 自动去掉 ```json 代码块,使用 ObjectMapper 解析文本 → 目标类型;
- 结合 chat-client 的 ResponseSpec.entity()/responseEntity() 使用,非常常见。
- MapOutputConverter、ListOutputConverter 等
- 把输出解析为 Map/List 等通用形态。
四、工具调用(org.springframework.ai.model.tool)
1) ToolCallingChatOptions(对话 + 工具配置)
- toolCallbacks:注册可执行工具(ToolCallback);
- toolNames:按名字注册工具(通过 resolver 解析为 ToolCallback);
- internalToolExecutionEnabled:是否由模型内部执行工具(true),或由调用方自己执行(false)。chat-client 的 ToolCallAdvisor 会把此开关设为 false,以在 Advisor 链中循环执行工具;
- toolContext:上下文(例如多租户信息、历史等),会通过 ToolContext 在执行时注入。
- 提供 mergeToolNames/mergeToolCallbacks/mergeToolContext/validateToolCallbacks 等工具方法。
2) ToolCallingManager(工具循环驱动)
- DefaultToolCallingManager
- resolveToolDefinitions:根据 ToolCallingChatOptions 中的 toolNames + toolCallbacks,解析 ToolDefinition 列表;
- executeToolCalls(Prompt, ChatResponse):
- 从 ChatResponse 中找到带 ToolCalls 的 AssistantMessage;
- 构建 ToolContext(包含 ToolContext.TOOL_CALL_HISTORY 等);
- 遍历每个 ToolCall:
- 解析工具名与 JSON 参数(支持流式场景参数为空的兜底 "{}");
- 从 options 的 toolCallbacks 或 resolver 中找到 ToolCallback;
- 调用 ToolCallback.call(jsonArgs, toolContext),将结果记录为 ToolResponseMessage.ToolResponse;
- 根据工具 metadata 的 returnDirect 规则决定是否“把工具结果直接返回给调用方”;
- 返回 ToolExecutionResult(包含会话历史 + 是否 returnDirect)。
- 观测:ToolCallingObservationContext + Convention + Documentation,记录工具调用的参数与结果,利于审计与调试。
- ToolCallback 与 Provider(tool 包)
- FunctionToolCallback/MethodToolCallback 两种方式把函数或方法暴露为工具;
- ToolDefinition(工具名、参数 schema 等)与 ToolMetadata(是否 returnDirect 等);
- ToolCallbackResolver(Static/SpringBean/Delegating)按名字解析 ToolCallback;
- ToolExecutionExceptionProcessor:统一处理工具异常并生成模型可读的错误消息。
五、嵌入模型(org.springframework.ai.embedding)
- EmbeddingModel extends Model<EmbeddingRequest, EmbeddingResponse>
- embed(String)/embed(Document)/embed(List<String>) 返回向量;dimensions() 返回向量维度;
- embed(List<Document>, EmbeddingOptions, BatchingStrategy):批量嵌入(分批策略),用于 RAG 文档入库;
- 默认批处理策略(commons 中):TokenCountBatchingStrategy,根据文本 token 估算做批次切分,避免超限。
六、图片与音频模型族(org.springframework.ai.(image / audio.tts))
- ImageModel:图片生成接口,配套 ImagePrompt、ImageOptions、ImageResponse、ImageGeneration 等结构;
- TextToSpeechModel / StreamingTextToSpeechModel:TTS 生成语音流与音频文件,配套 TextToSpeechPrompt、Speech、TextToSpeechOptions 等;
- 它们都与 chat/embedding 一样提供 Observation 支持。
七、内容审核(org.springframework.ai.moderation)
- ModerationModel、ModerationPrompt/Response/Generation、Categories/CategoryScores 等
- 用于对文本内容做安全与合规性判断(坏词、仇恨、暴力等类别),与 SafeGuardAdvisor 配合使用非常合适。
八、观测与日志(chat/embedding/image/moderation 下的 observation 包)
- ChatModelObservationContext/Convention/Documentation、Prompt/Completion/Meter Handler
- 统一在模型调用前后打点;把 prompt 内容、完成内容打印到日志;记录用量指标。
- 嵌入、图片、审核模型族也有对应的 Observation 套件,保持一致性。
九、与上层模块的协同方式
- 与 spring-ai-client-chat 的协作
- ChatClient 会构造 Prompt(包含 system/user/messages/ChatOptions),调用 ChatModel.call/stream;
- ChatClient 的 ToolCallAdvisor 会把 ToolCallingChatOptions.internalToolExecutionEnabled=false,绕过模型内部工具循环,转而在 advisor 链中执行工具;调用 DefaultToolCallingManager 统一处理工具返回、是否 returnDirect 等;
- ChatClient 的 StructuredOutputValidationAdvisor 会结合 BeanOutputConverter.getFormat() 提供 JSON Schema,并在不合格时做重试。
- 与 RAG 模块的协作
- RAG 的 RetrievalAugmentationAdvisor 会用 Prompt.augmentUserMessage/augmentSystemMessage 将检索到的文档 context 注入到 Prompt 中;整个增强过程使用的 API 都在 model 层。
十、常见用法示例(精简示意)
- 结构化输出(Java)
- 使用 BeanOutputConverter 或 MapOutputConverter:
- ChatClient.prompt().user("给我一个用户对象JSON").call().entity(User.class)
- 或 responseEntity(new ParameterizedTypeReference<List<User>>(){})
- 使用 BeanOutputConverter 或 MapOutputConverter:
- 工具调用(用户侧执行)
- 在 ChatOptions(ToolCallingChatOptions)中设置 toolCallbacks/toolNames/toolContext;
- ChatClient + ToolCallAdvisor 会驱动工具循环,DefaultToolCallingManager 执行工具并把 ToolResponseMessage 放回到会话历史;
- Prompt 模板渲染
- PromptTemplate.builder().template("Hello {name}").variables(Map.of("name","Spring AI")).create()
- 在 ChatClient.builder().defaultTemplateRenderer(…) 或 RequestSpec.templateRenderer(…) 替换渲染器。
十一、最佳实践与注意事项
- Prompt.copy 与 augment* 方法:在增强提示时避免直接修改原始对象,使用 copy/augment 更安全。
- StructuredOutputConverter.getFormat():务必把该格式提示加入用户消息(chat-client 已集成),否则解析可能失败。
- 工具名:确保 ToolCallback 的名字不重复;LLM 可能“适配/截断”工具名,DefaultToolCallingManager 有警告与解析机制,但建议工具名简短明确。
- 嵌入批处理:长文档建议结合 TokenCountBatchingStrategy 与文档切分(commons 的 TextSplitter/TokenTextSplitter),避免超 token 限。
- 观测:结合 PromptContent/Completion Handler 开启日志与指标,便于排查问题与做用量统计。
chat client
这一模块内容覆盖模块定位、包结构、核心 API、实现细节、扩展点(Advisor 体系)、观测指标、Kotlin 扩展、依赖与构建、测试要点以及入门建议。
一、模块定位与整体设计
- spring-ai-client-chat 提供一个统一的 ChatClient(聊天客户端)编程接口,用于以“无状态”方式调用各类 ChatModel(如 OpenAI、Anthropic、Bedrock 等)的对话与流式输出能力。
- 模块强调三点: 1) 流畅的构建与调用体验:通过 ChatClient.builder/chatClient.prompt().user/system…/.call()/.stream() 等链式 API。 2) 面向切面的扩展机制:基于 Advisor 链(CallAdvisor/StreamAdvisor)在模型调用前后插入通用处理,如日志、上下文记忆、结构化输出校验、工具调用循环、安全审查等。 3) 观测与日志:Micrometer Observation 为 ChatClient 和 Advisor 的执行过程提供打点与日志输出。
二、包结构总览(org.springframework.ai)
- chat.client
- ChatClient 接口与内部的规格接口(RequestSpec、PromptUserSpec、PromptSystemSpec、AdvisorSpec、CallResponseSpec、StreamResponseSpec、Builder)
- DefaultChatClient、DefaultChatClientBuilder、DefaultChatClientUtils 等实现类和工具类
- ChatClientRequest/ChatClientResponse record 封装、ResponseEntity 类型
- ChatClientMessageAggregator 流式消息聚合器、ChatClientAttributes 通用上下文键、ChatClientCustomizer 定制回调
- observation 子包:ChatClientObservationContext/Convention/Documentation 等观测类,以及 Prompt/Completion Handler
- advisor 子包:Advisor 体系(api、链实现、内置 Advisor、observation)
- evaluation
- FactCheckingEvaluator 与 RelevancyEvaluator(评估器接口及默认实现)
- Kotlin 扩展(src/main/kotlin/org/springframework/ai/chat/client/ChatClientExtensions.kt)
- 针对 CallResponseSpec 的泛型 reified 扩展,简化 Kotlin 下的 entity()/responseEntity() 调用
三、ChatClient 核心 API(org.springframework.ai.chat.client.ChatClient)
- 工厂与构建
- ChatClient.create(ChatModel[, ObservationRegistry[, ChatClientObservationConvention[, AdvisorObservationConvention]]])
- ChatClient.builder(ChatModel[, ObservationRegistry[, ChatClientObservationConvention[, AdvisorObservationConvention]]])
- 通过 DefaultChatClientBuilder 封装默认配置(system 文本、user 文本、默认 options、默认工具、默认 advisors、默认模板渲染器等)。
- prompt 与请求规格
- prompt():返回 ChatClientRequestSpec,可继续配置;
- prompt(String content) / prompt(Prompt prompt):以字符串或 Prompt 初始化请求;
- ChatClientRequestSpec 支持:
- advisors(Consumer/数组/List):插入 Advisor(可携带参数);
- messages(List/数组):直接追加 Message;
- options(T extends ChatOptions):设置模型选项(含 ToolCallingChatOptions 等);
- toolNames/tools/toolCallbacks/toolCallbackProviders/toolContext:工具调用相关配置;
- system/user:配置系统提示与用户提示(支持从 Resource 读取文本并指定 Charset;支持 Map 参数渲染;支持 metadata;用户消息可附加多模态 Media);
- templateRenderer:指定模板渲染器(默认 ST 模板)。
- call() / stream():发起非流式或流式请求,返回 CallResponseSpec 或 StreamResponseSpec。
- 响应读取与结构化输出
- CallResponseSpec:
- entity(Class<T>、ParameterizedTypeReference<T>、StructuredOutputConverter<T>):将模型返回的文本内容按目标类型解析为实体(BeanOutputConverter 或自定义 StructuredOutputConverter)。
- chatClientResponse()/chatResponse()/content():访问底层 ChatResponse 或其文本内容。
- responseEntity(Class<T>/ParameterizedTypeReference<T>/StructuredOutputConverter<T>):同时返回完整 ChatResponse 与解析后的实体对象(ResponseEntity<ChatResponse,T>)。
- StreamResponseSpec:
- chatClientResponse()/chatResponse()/content():返回 Flux 流式响应,支持聚合器将片段合并为完整 AssistantMessage。
- CallResponseSpec:
四、DefaultChatClient 实现关键点
- DefaultChatClient 持有 DefaultChatClientRequestSpec 作为“默认请求”副本来源;每次 prompt() 都会复制该默认请求。
- DefaultChatClientUtils.toChatClientRequest(DefaultChatClientRequestSpec)
- 将 RequestSpec 渲染为实际 ChatClientRequest:
- 消息顺序:SystemMessage(带参数渲染)放最前;中间加入已经追加的 messages;最后是 UserMessage(可媒体与 metadata);
- ChatOptions 按需升级为 ToolCallingChatOptions(当设置了工具名/回调/上下文时)并合并工具相关配置;
- ToolCallbackProvider 延迟解析为 ToolCallback。
- 将 RequestSpec 渲染为实际 ChatClientRequest:
- call()/stream()
- 通过 buildAdvisorChain() 构造 BaseAdvisorChain(DefaultAroundAdvisorChain),将用户配置的 Advisor 加入链中,并在链底部附加 ChatModelCallAdvisor 与 ChatModelStreamAdvisor 作为终端调用器;
- 调用前创建 ChatClientObservationContext(包含 request、advisors、是否 stream、输出格式等),经 Micrometer Observation 装饰;
- 对于结构化输出(entity/responseEntity 的 StructuredOutputConverter),默认将输出格式字符串(如 JSON schema 或提示)注入 ChatClientRequest.context,供 ChatModelCallAdvisor 在最终调用前追加到用户消息。
- 流式聚合
- ChatClientMessageAggregator:对 Flux<ChatClientResponse> 聚合,将多个分段结果合并为一个连续 AssistantMessage,保持 context 聚合。
五、请求与响应封装
- ChatClientRequest record
- 成员:Prompt prompt、Map<String,Object> context;
- builder()/mutate()/copy() 提供不可变构造与拷贝;校验 context 不可为 null,key 不能为 null。
- ChatClientResponse record
- 成员:ChatResponse chatResponse(可空)、Map<String,Object> context;
- builder()/mutate()/copy() 同上;常用于在 Advisor 之间传递完整响应与上下文。
- ResponseEntity<R,E>
- 将完整响应与转换后的实体一并返回,便于同时获取 ChatResponse(拿 metadata、工具调用信息等)与业务对象。
六、Advisor 体系(拦截与扩展机制)
- API 设计(advisor.api 包)
- Advisor extends Ordered:所有 Advisor 都有顺序(order),用于链内排序;
- CallAdvisor/StreamAdvisor:分别处理非流式与流式场景;
- BaseAdvisor:同时实现 CallAdvisor/StreamAdvisor,提供 before()/after() 两个抽象钩子与默认链式处理模板(包含对 streaming 的 Scheduler 处理与 finishReason 检测);
- CallAdvisorChain/StreamAdvisorChain:定义 nextCall()/nextStream();支持 copy(after) 创建“从某个 Advisor 之后开始”的新链。
- BaseChatMemoryAdvisor:对“聊天记忆”类 Advisor 的公共接口,用于从 context 确定 conversationId。
- 链实现(DefaultAroundAdvisorChain)
- 内部维护双端队列(Deque)保存 CallAdvisor/StreamAdvisor,并在 builder.pushAll() 时按 Ordered 排序;
- nextCall()/nextStream():弹出当前 advisor 执行,并通过 Micrometer Observation 对每个 Advisor 做打点(AdvisorObservationContext);
- copy(CallAdvisor after):从 after 之后的所有 advisor 构造一个新链,用于工具调用循环。
- 内置 Advisor
- ChatModelCallAdvisor/ChatModelStreamAdvisor:链底的终端调用器,直接调用 ChatModel.call()/stream(),并将 context 中的输出格式(ChatClientAttributes.OUTPUT_FORMAT)追加到用户提示文本;
- ToolCallAdvisor:
- 将工具调用循环从 ChatOptions 的“内部执行”改为“Advisor 链内执行”(设置 ToolCallingChatOptions.internalToolExecutionEnabled=false);
- 逻辑:循环调用剩余链(callAdvisorChain.copy(this).nextCall),若响应含 tool calls,则通过 ToolCallingManager.executeToolCalls 执行工具,若 returnDirect 则直接返回工具结果;否则将工具对话历史(conversationHistory)返回给模型继续迭代,直到没有 tool calls;
- 暂不支持流式(流式会抛异常)。
- StructuredOutputValidationAdvisor:
- 针对“结构化 JSON 输出”场景:根据目标类型自动生成 JSON Schema(victools+jackson+MCP),校验模型输出;校验失败时将错误信息追加到下一次用户消息,并重复调用,最多 maxRepeatAttempts 次;
- 仅支持非流式(流式抛异常)。
- PromptChatMemoryAdvisor:
- 将历史记忆注入系统提示(SystemMessage)中,默认模板包含 MEMORY 区块;
- 调用前:从 ChatMemory 获取当前会话的 USER/ASSISTANT 历史,模板渲染后替换系统提示,并把当前用户消息加入记忆;
- 调用后:把模型的 ASSISTANT 输出加入记忆;支持流式,使用聚合器将响应合并再 after()。
- MessageChatMemoryAdvisor:
- 将记忆直接作为历史消息追加到当前 Prompt 的 messages 列表;
- 同样在 before/after 中分别追加 USER/ASSISTANT 内容;支持流式。
- SimpleLoggerAdvisor:简单日志打印 request/response(可配置序列化函数与顺序),支持流式;
- SafeGuardAdvisor:敏感词拦截,在用户输入包含敏感词时直接返回固定失败回复,阻断后续调用,支持流式;
- LastMaxTokenSizeContentPurger:根据 TokenCountEstimator 计算并裁剪内容列表,保证不超出最大 token 约束(可用于记忆或文档裁剪)。
- AdvisorUtils:提供 onFinishReason() 等工具方法,用于检测流式输出结束等。
七、观测与日志
- ChatClientObservationContext/Convention/Documentation:
- Context 保存:request、advisors、是否流式、format(输出格式)、以及最终 response;
- Convention 设置低/高维标签:
- 低维:spring.ai.kind=chat_client、是否流式;
- 高维:advisors 名称列表、conversation.id(取自 ChatMemory.CONVERSATION_ID)、工具名(来自 ToolCallingChatOptions)。
- Documentation 枚举定义观测点与 KeyName。
- Handler
- ChatClientPromptContentObservationHandler:在 onStop 时将 Prompt 内容(SYSTEM/USER 等消息文本)输出日志;
- ChatClientCompletionObservationHandler:在 onStop 时将模型完成内容(结果文本)输出日志。
- Advisor 观测
- AdvisorObservationContext/Convention/Documentation:对每个 Advisor 的执行打点,包含 advisorName、order 等信息。
八、Kotlin 扩展(ChatClientExtensions.kt)
- 提供泛型 reified 扩展:
- CallResponseSpec.entity<T>():自动构建 ParameterizedTypeReference<T> 传给 Java API;
- CallResponseSpec.responseEntity<T>():同上,返回 ResponseEntity<ChatResponse,T>;
- 让 Kotlin 使用结构化输出更简洁,不需手写 ParameterizedTypeReference。
九、学习与入门建议
- 快速上手步骤 1) 选择模型实现(例如 org.springframework.ai.openai 包的 OpenAiChatModel),创建 ChatModel 实例; 2) 使用 ChatClient.builder(chatModel) 构建客户端,设置默认 system/user、默认 options、工具、Advisor(如日志/记忆/结构化验证/安全审查); 3) 发起一次非流式调用:
- chatClient.prompt().user("你的问题").call().content() 获取文本;
- 或 .call().entity(MyType.class) 将输出解析为结构化类型; 4) 发起流式调用:
- chatClient.prompt().user("你的问题").stream().content() 返回 Flux<String>,可订阅并拼接; 5) 结构化输出:
- Java:call().responseEntity(new ParameterizedTypeReference<List<MyBean>>() {});
- Kotlin:call().responseEntity<MyBean>() 或 entity<MyBean>()(利用 reified 扩展); 6) 工具调用:
- 使用 options(ToolCallingChatOptions),设置 toolNames/toolCallbacks/toolContext;
- 如需在 Advisor 链内进行工具循环,添加 ToolCallAdvisor(注意其仅支持非流式)。 7) 记忆功能:
- 选择 PromptChatMemoryAdvisor(将历史记忆注入 system 提示)或 MessageChatMemoryAdvisor(直接追加历史消息);
- 默认对话 ID 可通过 context 传入 ChatMemory.CONVERSATION_ID,自定义会话隔离。 8) 观测与日志:
- 提供 ObservationRegistry(非 NOOP),可选择自定义 ChatClientObservationConvention 与 AdvisorObservationConvention;
- 使用 Prompt/Completion Handler 输出提示与完成内容,利于调试与审计。
- 设计要点理解
- Advisor 链是扩展核心,通过 Ordered 控制执行顺序。一般日志/安全审查优先级较高,结构化校验与模型调用靠后,ChatModelCall/StreamAdvisor 总是链底终端。
- DefaultChatClientUtils 的“系统-历史-用户”消息组织与模板渲染是构建 Prompt 的关键。
- 对结构化输出的支持不仅是“解析 JSON”,还提供“告知 LLM 期望输出格式”的注入机制与“校验-重试”策略。
- 工具调用可选择模型内部工具执行或 Advisor 外部循环执行(两者互斥),外部循环便于在工具过程加入更多治理(如日志、观测、校验)。
RAG
好的,下面为你详细介绍 spring-ai 项目中 “spring-ai-rag” 模块的代码与用法。这一模块为检索增强生成(Retrieval Augmented Generation, RAG)提供通用组件与一个可直接插入 ChatClient 的 Advisor,以便在模型调用之前完成检索、融合、后处理和查询增强,从而让模型在“有知识”的上下文里作答。
一、模块定位与整体架构
- 目标:把检索增强流程以模块化的方式提供给 Spring AI 的 ChatClient,遵循“Modular RAG Architecture”,让你可自由组合预检索处理、检索、合并、后处理、查询增强等“积木”,并通过 RetrievalAugmentationAdvisor 接入 ChatClient 的拦截链。
- 使用路径:在 ChatClient.builder(…).defaultAdvisors(…) 加入 RetrievalAugmentationAdvisor 后,调用 .prompt().user(…).call() 或 .stream() 时,RAG 在模型调用前完成检索与上下文拼装,并把检索到的文档加入到 prompt;在模型返回后还会把文档上下文挂载到 ChatResponse.metadata 中。
二、包结构与核心类
1) 顶层 Query
- org.springframework.ai.rag.Query
- 一个不可变的记录类,代表“用户查询对象”,含:
- text:查询文本(必填,不可空/空串)
- history:对话历史 List<Message>(允许空列表,不允许元素为 null)
- context:上下文 Map<String,Object>(允许空 Map,不允许 key 为 null)
- 提供 builder()/mutate() 便于构造和拷贝,常用于在 RAG 各阶段传递信息。
- Advisor 会用当前 Prompt 的用户文本和 instruction 列表(包含 system/user 等消息)来构造 Query。
- 一个不可变的记录类,代表“用户查询对象”,含:
2) RAG Advisor(与 ChatClient 的集成)
- org.springframework.ai.rag.advisor.RetrievalAugmentationAdvisor implements BaseAdvisor
- 这是最关键的接入点。它作为 ChatClient 的一个 Advisor,在“模型调用前/后”执行完整 RAG 流程。
- before(ChatClientRequest):
- 构造原始 Query:用 prompt 的 UserMessage 文本 + 全部 instructions 作为 history + 现有 context。
- 预检索(Pre-Retrieval):串行应用一组 QueryTransformer(如翻译、压缩、重写),得到 transformedQuery。
- 扩展(Expansion):可用 QueryExpander 把 transformedQuery 扩展为多条 Query(支持 LLM 扩展),提高召回。
- 检索(Retrieval):对每个 Query 并发调用 DocumentRetriever(默认你传一个,可以扩展到多个来源),收集文档集合。
- 合并(Join):把多个 query、多个来源的文档做归并(默认 ConcatenationDocumentJoiner:去重+按得分降序排序)。
- 后处理(Post-Retrieval):可配置 DocumentPostProcessor 列表(排名/去噪/压缩等),改善上下文质量。
- 查询增强(Generation/Augmentation):用 QueryAugmenter(默认 ContextualQueryAugmenter)将文档上下文与用户问题融合为新的 query text(即“上下文增强的提示”)。
- 更新 ChatClientRequest:把增强后的 query.text() 作为新的用户消息(augmentUserMessage),并把文档上下文放入 context 键 rag_document_context 中。
- after(ChatClientResponse):
- 将 rag_document_context 放到 ChatResponse.metadata 里,便于调用端后续查看本次回答所依据的文档上下文。
- 其他:
- Builder 可配置:queryTransformers、queryExpander、documentRetriever(必填)、documentJoiner、documentPostProcessors、queryAugmenter、taskExecutor(默认 ThreadPoolTaskExecutor,带上下文传播)、scheduler(用于流式场景)、order(执行顺序)。
- DOCUMENT_CONTEXT 常量键名:"rag_document_context"。
3) 预检索阶段(pre-retrieval)
- transformation(查询变换)接口与实现:
- QueryTransformer 接口:Function<Query, Query>,transform(query)。
- TranslationQueryTransformer:用 LLM 把 query 翻译到 embedding 模型支持的目标语言;若原本已是目标语言或未知则原样返回;支持自定义 PromptTemplate 与目标语言。
- CompressionQueryTransformer:用 LLM 将“conversation history + follow-up query”压缩为独立的、简洁的“可检索 query”,历史只保留 USER/ASSISTANT 信息;适合多轮对话后提问的情况。
- RewriteQueryTransformer:用 LLM将 query 改写为更适合检索系统(如向量库或搜索引擎)的版本,去冗余、提高精确性;支持自定义目标系统名称与 prompt。
- expansion(查询扩展)接口与实现:
- QueryExpander 接口:Function<Query, List<Query>>,expand(query)。
- MultiQueryExpander:用 LLM生成 N 条语义多样的“替代查询”,可选是否保留原始 query;用于拓宽检索覆盖面,提高召回;支持自定义 PromptTemplate、条数、保留原始等。
- 使用说明:该实现需要传入 ChatClient.Builder(会内部构建 ChatClient 用于生成扩展查询)。
4) 检索阶段(retrieval)
- search(文档检索):
- DocumentRetriever 接口:Function<Query, List<Document>>,retrieve(query)。
- VectorStoreDocumentRetriever:从向量库检索语义相似文档(vectorStore.similaritySearch),支持:
- similarityThreshold(默认为 SearchRequest.SIMILARITY_THRESHOLD_ACCEPT_ALL)
- topK(默认 SearchRequest.DEFAULT_TOP_K)
- filterExpression:可传 Supplier<Filter.Expression>(动态评估,比如多租户)、也可在 query.context 某次请求级设置 FILTER_EXPRESSION(字符串会用 FilterExpressionTextParser 解析,或直接传 Filter.Expression 对象);优先使用请求级 filter。
- Builder:vectorStore 必填;其余可选。
- join(合并):文档合并与去重/排序
- DocumentJoiner 接口:Function<Map<Query, List<List<Document>>>, List<Document>>。
- ConcatenationDocumentJoiner:把多 query、多源的文档直接拼接去重,保留首个出现的重复文档,并按文档 score 倒序排列;score 为空按 0.0 计算。
5) 后检索阶段(post-retrieval)
- DocumentPostProcessor 接口:BiFunction<Query, List<Document>, List<Document>>,用于对检索到的文档做二次处理,解决 “lost-in-the-middle”、上下文长度限制、去噪/去冗余/压缩等问题。
- 模块内只提供接口,你可以实现自己的排序器、裁剪器、压缩器等。
6) 生成阶段的查询增强(generation/augmentation)
- QueryAugmenter 接口:BiFunction<Query, List<Document>, Query>,augment(query, documents)。
- ContextualQueryAugmenter:
- 默认 PromptTemplate 会把文档内容放入“Context information is below.”的段落中,并列出规则(不编造、不加冗余模板句等);把 {query}/{context} 两个变量渲染为新 query 的文本。
- allowEmptyContext(默认 false):当没有检索到文档时,不允许空上下文则会切换到 emptyContextPromptTemplate(默认:礼貌地告诉用户知识库中没有答案);允许为空则直接返回原始 query。
- documentFormatter:默认把每个 Document.getText() 以换行拼接;可自定义(例如裁剪文档、提取关键段)。
- 构造时会用 PromptAssert 校验模板必须包含所需占位符。
7) 实用工具
- PromptAssert:一个小工具类,校验 PromptTemplate 是否包含指定占位符,缺失时抛异常;增强安全性与可维护性。
三、与 ChatClient 的整体协作
- 你可以在构建 ChatClient 时加入 RetrievalAugmentationAdvisor,它会介入“before/after”,在调用模型前把增强后的用户提示替换到 prompt,调用后把文档上下文写入 ChatResponse.metadata:
- 代码示例(集成向量库 + 翻译 + 多查询扩展 + 合并 + 上下文增强):
- Java(伪代码,聚合常见配置):
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59
VectorStore vectorStore = ... // 任选 Spring AI 提供的向量库实现(Redis/Qdrant/Chroma 等) DocumentRetriever retriever = VectorStoreDocumentRetriever.builder() .vectorStore(vectorStore) .similarityThreshold(0.75) .topK(8) // 可选:动态过滤(比如根据租户) .filterExpression(() -\> FilterExpressionBuilder.create().eq("tenantId", currentTenant()).build()) .build(); QueryTransformer translator = TranslationQueryTransformer.builder() .chatClientBuilder(ChatClient.builder(chatModel)) .targetLanguage("english") .build(); QueryTransformer compressor = CompressionQueryTransformer.builder() .chatClientBuilder(ChatClient.builder(chatModel)) .build(); QueryTransformer rewriter = RewriteQueryTransformer.builder() .chatClientBuilder(ChatClient.builder(chatModel)) .targetSearchSystem("vector store") .build(); QueryExpander expander = MultiQueryExpander.builder() .chatClientBuilder(ChatClient.builder(chatModel)) .numberOfQueries(3) .includeOriginal(true) .build(); DocumentJoiner joiner = new ConcatenationDocumentJoiner(); QueryAugmenter augmenter = ContextualQueryAugmenter.builder() .allowEmptyContext(false) .build(); RetrievalAugmentationAdvisor ragAdvisor = RetrievalAugmentationAdvisor.builder() .queryTransformers(translator, compressor, rewriter) .queryExpander(expander) .documentRetriever(retriever) .documentJoiner(joiner) // .documentPostProcessors(...) // 如需加排名、裁剪等 .queryAugmenter(augmenter) .build(); ChatClient client = ChatClient.builder(chatModel) .defaultAdvisors(ragAdvisor) .defaultSystem("You are a helpful assistant.") .build(); // 发起调用 var responseEntity = client.prompt() .user("请根据我们的知识库解释“Spring AI RAG”的设计要点") .call() .responseEntity(String.class); // 取完整响应与元数据(包含 rag_document_context 文档集合) ChatResponse chatResponse = responseEntity.getResponse(); var docs = chatResponse.getMetadata().get("rag_document_context"); String content = responseEntity.getEntity(); // 结构化输出或纯文本
- Java(伪代码,聚合常见配置):
- 代码示例(集成向量库 + 翻译 + 多查询扩展 + 合并 + 上下文增强):
- 与流式输出(stream)的协作:
- RetrievalAugmentationAdvisor 是 BaseAdvisor,支持流式管道;其 before() 仍会在流式调用前完成检索与拼装;after() 在流式完成后将文档上下文写入响应(BaseAdvisor 的流式逻辑会在完成时调用 after)。
四、重要的细节与约束
- 参数校验严格:Query 非空文本、列表/Map 不允许 null 元素;Builder 传入数组不允许含 null;vectorStore 必填;topK、similarityThreshold 都有边界;Prompt 模板必须包含特定占位符。
- 并发检索:RetrievalAugmentationAdvisor 默认使用 ThreadPoolTaskExecutor(核心4、最大16),并有 ContextPropagatingTaskDecorator 保证上下文传递。
- 文档合并策略:ConcatenationDocumentJoiner 以文档 id 去重,保留首次出现版本,按 score 降序排序,score 为 null 当作 0.0。
- 向量库过滤:VectorStoreDocumentRetriever 可在 Query.context 里以字符串表达式或 Filter.Expression 直接指定“本次请求的 filter”,优先级高于默认 Supplier;字符串会由 FilterExpressionTextParser 解析。
- 查询增强:默认 ContextualQueryAugmenter 对空上下文不“硬扯”,会以 emptyContextPromptTemplate 引导模型礼貌拒绝(避免模型凭空编造)。
- 对话历史使用:CompressionQueryTransformer 会把历史(只取 USER/ASSISTANT)与当前问题压缩为“独立 query”,适合多轮场景;Query.history 来自 prompt.getInstructions()。
五、pom 与依赖
- 模块坐标:artifactId=spring-ai-rag,父版本 1.1.0-SNAPSHOT。
- 依赖:
- spring-ai-client-chat(用于构建/调用 ChatClient 与 LLM)
- spring-ai-vector-store(向量库检索)
- 测试:spring-boot-starter-test、jackson-module-kotlin
- Java 版本:maven.compiler.source/target = 17。
六、实践建议与最佳配置
- 面向生产的 RAG 组合建议:
- 预检索:Translation(统一语言)→ Compression(独立 query)→ Rewrite(优化检索表达),按需选择与排序;
- 扩展:MultiQueryExpander(N 条变体,包含原始),提升召回;
- 检索:VectorStoreDocumentRetriever 顶 K + 阈值 + 动态过滤(如租户/权限);
- 合并:ConcatenationDocumentJoiner 或你自研的 joiner(例如 Reciprocal Rank Fusion);
- 后处理:自研 DocumentPostProcessor(排序/裁剪/压缩/去噪),控制总上下文长度与质量;
- 增强:ContextualQueryAugmenter,允许/不允许空上下文(建议不允许),并定制合适的模板规则。
- 与 ChatClient 其他 Advisor 的组合:
- 可在 RAG 之前插入 SafeGuardAdvisor(敏感词阻断)、SimpleLoggerAdvisor(日志);
- 与 Memory Advisors(PromptChatMemoryAdvisor/MessageChatMemoryAdvisor)配合,让 RAG 同时利用会话记忆与外部文档。
- 结构化输出:若希望模型返回结构化 JSON,可再叠加 StructuredOutputValidationAdvisor(在 client-chat 模块中),让最终回答符合 JSON Schema。
Vector Store
下面是对 “spring-ai-vector-store” 模块的详细完整总结与学习导览。这个模块为向量数据库(Vector Store)的通用抽象、过滤表达式 DSL、观测指标(Micrometer)以及一个内置的内存向量库实现(SimpleVectorStore)提供基础能力。它既可单独使用,也为 RAG 模块与各具体向量库实现(如 Redis、Qdrant、Chroma 等)提供统一接口。
一、模块定位与总体设计
- 提供统一的 VectorStore 接口(读写、检索)与 VectorStoreRetriever(只读检索)接口。
- 提供 SearchRequest(相似度检索参数封装)与 Filter DSL(可移植的元数据过滤表达式),支持文本 DSL 解析与程序式构建。
- 提供 Micrometer Observation 集成,统一打点与日志输出。
- 内置一个可运行的“内存向量库”实现 SimpleVectorStore,适合小型场景、测试或演示;支持持久化到 JSON 文件。
- 具体存储实现在独立模块(vector-stores 下的 spring-ai-redis-store、spring-ai-qdrant-store 等),通过统一接口集成。
二、核心 API
1) VectorStore 接口(org.springframework.ai.vectorstore.VectorStore)
- 关键方法:
- add(List<Document> documents):写入文档(内部会调用 EmbeddingModel 生成向量)。
- delete(List<String> idList):按 ID 删除文档。
- delete(Filter.Expression filterExpression)/delete(String filterExpression):按过滤表达式删除(文字表达式先解析为 Filter.Expression)。注意:抽象父类默认未实现 filter 删除,具体实现需覆盖。
- similaritySearch(SearchRequest request)/similaritySearch(String query):按嵌入相似度检索(支持 topK、相似度阈值、元数据过滤)。
- Builder(内部接口 VectorStore.Builder<T>):
- observationRegistry(…):Micrometer 观测注册器。
- customObservationConvention(…):自定义观测约定。
- batchingStrategy(…):嵌入模型批处理策略。
- build():构建具体 VectorStore 实例。
- VectorStoreRetriever(只读视图,暴露 similaritySearch(…),不暴露写操作),利于最小权限原则。
2) SearchRequest(org.springframework.ai.vectorstore.SearchRequest)
- 参数封装:
- query:查询文本,默认空串。
- topK:返回最近邻个数,默认 4(DEFAULT_TOP_K)。
- similarityThreshold:相似度阈值,默认 0.0(SIMILARITY_THRESHOLD_ACCEPT_ALL,接受所有)。范围 [0,1]。
- filterExpression:元数据过滤表达式(Filter.Expression),可选。
- 构建方式:
- builder().query("…").topK(8).similarityThreshold(0.75).filterExpression(expression/text)…
- 支持 filterExpression(String) 文本语法,自动用 FilterExpressionTextParser 解析为表达式树。
- 语义说明:
- similarityThreshold 是客户端侧过滤(非服务端),检索出来的候选结果会在客户端按该阈值过滤。
3) 文档结构(Document)
- 来自 spring-ai-model 模块,包含 id、text、metadata、score 等。vector-store 检索会给每个 Document 设置相似度得分(score),并可在 metadata 包含距离(DocumentMetadata.DISTANCE)。
三、过滤表达式 DSL(filter 包)
- Filter(表达式抽象):
- 支持操作符:AND、OR、EQ、NE、GT、GTE、LT、LTE、IN、NIN、NOT
- 组成元素:Key(字段名)、Value(常量或常量数组)、Expression(表达式三元组)、Group(用于优先级分组)
- FilterExpressionBuilder(程序式 DSL):
- 例子:
- eq("country","BG")
- and(eq("genre","drama"), gte("year",2020))
- in("genre", List.of("comedy","documentary","drama"))
- and(group(or(gte("year",2020), eq("country","BG"))), nin("city", List.of("Sofia","Plovdiv")))
- 支持 group(…)、not(…)、and(…)、or(…) 等链式构建。
- 例子:
- FilterExpressionTextParser(文本 DSL 解析,ANTLR4 语法):
- 支持 SQL-like 的 WHERE 子句子集(自动补 "WHERE" 前缀),例如:
- "country == "UK" && year >= 2020 && isActive == true"
"country == "BG" && (city NOT IN ["Sofia","Plovdiv"] price < 134.34)"
- 解析为 Filter.Expression(树结构),缓存结果,提高解析性能。
- 解析错误时抛出 FilterExpressionParseException,并记录语法错误信息。
- 支持 SQL-like 的 WHERE 子句子集(自动补 "WHERE" 前缀),例如:
- FilterHelper(辅助变换):
- negate(…):将表达式做逻辑“NOT”分发(NOT 分配律、比较操作符的互换、IN/NIN 互换等)。
- expandIn(…):将 IN 数组扩展为多个“==”的 OR 链。
- expandNin(…):将 NIN 数组扩展为多个“!=”的 AND 链。
- FilterExpressionConverter(表达式转换为具体存储的原生语言):
- SimpleVectorStoreFilterExpressionConverter:把表达式转换为 SpEL(Spring 表达式),用于 SimpleVectorStore 的元数据过滤(#metadata["key"] == "value")。特殊处理 IN/NIN(转 contains(…) 或 not in)。
四、内置向量库:SimpleVectorStore
- 位于 org.springframework.ai.vectorstore.SimpleVectorStore,是 AbstractObservationVectorStore 的子类。
- 构造:SimpleVectorStore.builder(EmbeddingModel) → build()
- 内部结构:
- Map<String, SimpleVectorStoreContent> store,线程安全 ConcurrentHashMap。
- EmbeddingModel 用于 embed(Document) 或 embed(query) 得到向量。
- 写入(doAdd):
- 调用 EmbeddingModel.embed(document),生成 embedding,封装为 SimpleVectorStoreContent(id、text、metadata、embedding)并写入 store。
- 如果 Document.id 有重复,覆盖(实际行为视具体需求,这里 put 会覆盖)。
- 删除(doDelete(List<String>)):按 id 移除;filter 删除暂未实现(父类默认 UnsupportedOperationException)。
- 检索(doSimilaritySearch):
- 把 query 转换为向量(embed(query))。
- 可选元数据过滤:若 SearchRequest.hasFilterExpression(),转换表达式为 SpEL,针对每个 SimpleVectorStoreContent 的 metadata 执行表达式,返回 true 则保留。
- 计算相似度:EmbeddingMath.cosineSimilarity(userQueryEmbedding, content.embedding),得到 score。
- 客户端侧阈值过滤:score >= similarityThreshold。
- 排序:按 score 降序;限制:limit(topK)。
- 返回 Document 列表:将 SimpleVectorStoreContent 转为 Document(metadata 中加入距离 1.0-score,score 赋值)。
- 持久化:
- save(File):把 store 转为 JSON 写入文件(ObjectMapper PrettyPrinter)。
- load(File/Resource):从 JSON 文件或 Resource 加载(反序列化为 Map)。
- 观测信息:
- createObservationContextBuilder(…):VectorStoreProvider.SIMPLE、collectionName="in-memory-map"、similarityMetric=cosine、dimensions=embeddingModel.dimensions()。
- 内部静态工具类 EmbeddingMath:cosineSimilarity、dotProduct、norm,安全校验向量长度与非零范数。
- SimpleVectorStoreContent:
- 不可变,包含 id/text/metadata/embedding;可从 text+metadata 用 IdGenerator 生成 id;toDocument(score) 会在 metadata 写入距离,并设置 Document.score。
五、Micrometer 观测
- AbstractObservationVectorStore:对 add、delete、similaritySearch 执行前后统一打点。调用 VectorStoreObservationDocumentation.AI_VECTOR_STORE 的 observation(…),记录上下文与指标标签。
- VectorStoreObservationContext:
- 通用标签:dbSystem(库类型)、operationName(add/delete/query)、collectionName、dimensions、fieldName、namespace、similarityMetric、queryRequest(SearchRequest)、queryResponse(List<Document>)。
- DefaultVectorStoreObservationConvention:
- 低维标签:spring.ai.kind=vector_store、db_system(库类型)、operation_name。
- 高维标签:集合名、向量维度、字段名、元数据过滤字符串、命名空间、查询内容、相似度度量、阈值、topK。
- VectorStoreQueryResponseObservationHandler:
- 在 onStop 时把“检索返回的文档文本内容”输出到日志,便于调试与审计。
六、配置属性与类型枚举
- SpringAIVectorStoreTypes:枚举常量字符串,标识各向量库类型(azure、chroma、elasticsearch、gemfire、hanadb、milvus、neo4j、opensearch、oracle、pgvector、pinecone、qdrant、redis、typesense、weaviate 等)。
- CommonVectorStoreProperties:
- initializeSchema(默认 false):是否在应用启动时初始化向量库的 schema,默认为不初始化。具体实现应读取并按需执行初始化逻辑。
- META-INF/additional-spring-configuration-metadata.json:
- 提示 spring.ai.vectorstore.type 配置项。
七、与 RAG 模块、具体向量库模块的协作
- RAG 模块中 VectorStoreDocumentRetriever 正是使用上述 VectorStore 接口进行 similaritySearch(SearchRequest 可携带 filter-expression)。
- 具体存储实现(如 spring-ai-redis-store、spring-ai-qdrant-store 等)在 vector-stores 目录的子模块中,都会实现 VectorStore 接口,并提供相应的 builder;它们可能支持 filter 的服务端原生执行,也可能在客户端进行变换。
八、示例与最佳实践
- 使用 SimpleVectorStore(内存演示):
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30
EmbeddingModel embeddingModel = ...; // 例如 OpenAI Embeddings 或本地 embeddings SimpleVectorStore store = SimpleVectorStore.builder(embeddingModel).build(); // 写入文档 List\<Document\> docs = List.of( Document.builder().id("doc1").text("Spring AI 提供 ChatClient 和 RAG 支持").metadata(Map.of("category","ai")).build(), Document.builder().id("doc2").text("Vector Store 用于语义检索,支持过滤表达式").metadata(Map.of("category","db")).build() ); store.add(docs); // 检索(无过滤) List\<Document\> result = store.similaritySearch( SearchRequest.builder() .query("什么是 RAG?") .topK(5) .similarityThreshold(0.5) .build() ); // 检索(带过滤,文本 DSL) List\<Document\> filtered = store.similaritySearch( SearchRequest.builder() .query("Vector Store 的用途") .filterExpression("category == 'db'") .build() ); // 保存/加载 store.save(new File("vector-store.json")); store.load(new File("vector-store.json"));
- 使用 FilterExpressionBuilder(程序式 DSL):
1 2 3 4 5 6 7 8 9 10 11
var b = new FilterExpressionBuilder(); var expr = b.and( b.eq("country","UK"), b.gte("year", 2020) ).build(); var sr = SearchRequest.builder() .query("政策") .filterExpression(expr) .build(); var res = store.similaritySearch(sr);
- 动态过滤(适配 Multi-Tenant 场景):
- 在具体向量库实现中通常支持 Supplier<Filter.Expression>(例如 RAG 模块利用 Query.context 设置请求级 filter),在 VectorStoreDocumentRetriever 中可优先使用请求上下文 filter。
- 相似度阈值与 topK 的取值:
- 阈值 [0,1],0.0 接受全部(禁用过滤);1.0 表示要求完全匹配(几乎不可达);topK>0,建议根据向量库性能与召回质量调节。
九、注意事项
- SimpleVectorStore 当前只支持“文本型 Document”(AbstractObservationVectorStore.validateNonTextDocuments 中限定),非文本内容暂不支持。
- delete(Filter.Expression) 在抽象类中默认抛 UnsupportedOperationException,使用文本表达式删除需要具体实现支持(例如某些外部库模块已实现服务器端过滤删除)。
FilterExpressionTextParser 使用 ANTLR4 解析 WHERE 语法子集;表达式需遵循语法(==、!=、>=、<=、IN、NOT IN、&&、 等),字符串应使用单引号或双引号。 - SimpleVectorStore 的过滤表达式通过 SpEL 在“metadata”上执行,键会转换为 "#metadata["key"]"。IN/NIN 会转换为 contains(…) 或 not in 替代逻辑(SimpleVectorStoreFilterExpressionConverter)。