Spring AI 全模块 API 字典一览(1.1.0 版)
定位:这是一份全量字典——§1~§12 按模块收录实战项目用到的(和它依赖的)全部 Spring AI API:方法签名、传参、返回值、真实用例。
用法:写代码卡住 →Ctrl+F12看类全貌 → 翻本文对签名;每条尽量附实战用例(以类名标注),没标的是官方通用 API。
版本:Spring AI 1.1.0 + Spring AI Alibaba 1.1.2.0 + Boot 3.5.6 + Java 21;模型接入走spring-ai-starter-model-openai(DashScope 兼容模式)。
§1 模型接入:ChatModel 层
配置入口(application.yml):spring.ai.openai.api-key / base-url / chat.options.model → starter 自动装配 ChatModel Bean,任何组件 @Autowired 直用。
| ChatModel 方法 | 传什么 | 返回什么 | 实战用例 |
|---|---|---|---|
call(String message) | 一句话 | String 纯文本 | PlanExecuteAgent |
call(Message... messages) | 若干条消息 | String | —(少用) |
call(Prompt prompt) | Prompt 对象 | ChatResponse | ContextCompactor |
stream(Prompt prompt) | Prompt | Flux<ChatResponse> | — |
stream(String message) | 一句话 | Flux<ChatResponse> | — |
ChatResponse 内部字段解剖(响应侧其实只有两层包装):
ChatResponse
├─ generations : List<Generation> 一般只取第一个 → getResult()
│ └─ Generation
│ ├─ output : AssistantMessage ← 模型本轮说的话
│ │ ├─ text : String 正文(getText())
│ │ └─ toolCalls : List<ToolCall> 想调的工具getToolCalls(),可为 null/空
│ └─ metadata : ChatGenerationMetadata
└─ metadata : ChatResponseMetadata token 用量(usage)、id 等
- 快捷链:
response.getResult().getOutput().getText()(见 TemplateStrategy) getResults()返回List<Generation>(多候选时用,一般只用 getResult)- 判空三连:chunk / getResult() / getOutput() 都可能为 null(WebSearchReactAgent 里就是这么防的;流式里另加
.filter(Objects::nonNull))
§2 Prompt 与 Message 家族
请求侧内部字段解剖——先纠正一个常见误解:Prompt 没有单独的 system/user/assistant 字段。它只有两个字段:一个消息列表 + 一个参数对象;System/User/Assistant 是列表里每个元素的角色类型:
Prompt
├─ instructions : List<Message> ← 字段1:消息列表,顺序 = 模型看到的顺序
│ ├─ [0] SystemMessage (MessageType.SYSTEM) 人设/规则 —— 通常一条,放最前
│ ├─ [1] UserMessage (MessageType.USER) 用户说的话
│ ├─ [2] AssistantMessage (MessageType.ASSISTANT) 模型上一轮的回答(多轮时)
│ └─ [3] ToolResponseMessage (MessageType.TOOL) 工具执行结果(ReAct 时)
└─ options : ChatOptions ← 字段2:参数,可为 null(温度/工具都藏在这)
Message是接口,只有 4 个实现类;每个实现用getMessageType()报告自己的角色,枚举MessageType= SYSTEM / USER / ASSISTANT / TOOL- 判断一条消息是谁的:
instanceof或getMessageType()(TokenEstimator 里用的是 instanceof) - 发给模型时,每条消息序列化成
{"role":"system|user|assistant|tool", "content":"..."}——curl/F12 里看到的 JSON 就是这么来的;"角色"只是消息上的一个标注字段 - 多轮对话的真相:所谓记忆,就是每次把 [0]~[n] 全量重放、只追加本轮新消息,[2] 那种 AssistantMessage 就是从 ChatMemory 里回灌的历史回答
| 类 | 怎么创建 | 角色 | 实战用例 |
|---|---|---|---|
SystemMessage | new SystemMessage(String) | 人设与规则,每轮单独加、不进记忆 | WebSearchReactAgent |
UserMessage | new UserMessage(String) 或 builder().text(...).build() | 用户输入 | FileReactAgent |
AssistantMessage | new AssistantMessage(String);带工具时 builder().toolCalls(...).build() | 模型回复(读取或回填) | SimpleReactAgent / WebSearchReactAgent |
ToolResponseMessage | builder().responses(List<ToolResponse>).build() | 工具执行结果回填 | WebSearchReactAgent |
Prompt | 见下方构造器全表 | 消息+选项的打包 | PlanExecuteAgent |
Prompt 构造器全表(5 个重载)——它内部只有两个字段:instructions(消息列表)和 options(参数,可为 null),所以所有写法殊途同归:
| 构造器 | 说明 |
|---|---|
new Prompt(String contents) | 一句话,内部自动包成单条 UserMessage |
new Prompt(String contents, ChatOptions options) | 一句话 + 参数 |
new Prompt(Message... messages) | 可变参数 ≡ new Prompt(List.of(m1, m2)) |
new Prompt(List<Message> messages) | 消息列表(实战项目最常用,见 PlanExecuteAgent) |
new Prompt(List<Message> messages, ChatOptions options) | 列表 + 参数——工具、温度都从这进 |
读回:prompt.getInstructions() → List<Message>;prompt.getOptions() → ChatOptions(可能为 null)。
三种"发消息"最终汇成同一个 Prompt:
chatModel.call("你好")≡chatModel.call(new Prompt("你好"))≡chatClient.prompt().user("你好").call().content()。
区别只在谁帮你包装:ChatClient 帮你包消息;要塞 options 就走 Prompt 双参构造或.options()。
两个关键 record(不可变数据载体):
AssistantMessage.ToolCall(id, type, name, arguments) // 访问器 id() / name() / arguments()
// arguments 是 JSON 字符串,流式下会跨 chunk 分片(见 §5)
ToolResponseMessage.ToolResponse(id, name, responseData) // id 必须原样回流配对(WebSearchReactAgent)
- 多模态:
UserMessage.builder()挂 Media(图片理解,见 FileManageService) - 实战惯例:XML 风格标签包裹字段,帮模型识别边界:
"<question>" + q + "</question>"、<fileid>(见 FileReactAgent)
§3 ChatOptions:每次调用的旋钮
| 类型 | 适用 | 备注 |
|---|---|---|
ChatOptions | 通用参数 | model / temperature / topP / maxTokens / stopSequences |
ToolCallingChatOptions | 通用参数 + 工具四件套 | 实战项目全 Agent 用它(见 WebSearchReactAgent) |
OpenAiChatOptions 等 | 厂商专用参数 | 示例项目未直接用 |
ToolCallingChatOptions.builder() 全方法:
| 工具相关(放链式前面) | 通用参数(放后面) |
|---|---|
toolCallbacks(ToolCallback...) | model(String) |
toolNames(String...) | temperature(Double) |
internalToolExecutionEnabled(Boolean) | maxTokens(Integer) |
toolContext(Map) | topP / topK / stopSequences |
- 链式顺序坑:工具方法返回工具 Builder,通用方法返回通用 Builder——先工具后通用怎么写都不断,反过来可能断
- 唯一传入口:
new Prompt(messages, options)第二参数——忘传 = 全白设 internalToolExecutionEnabled(false)是实战项目的灵魂开关:关掉框架自动执行工具,自己接管循环(§5)
§4 ChatClient:高层流式 API
获取 ChatClient 的三种方式:
| 方式 | 签名 | 场景 |
|---|---|---|
| 快速创建 | ChatClient.create(chatModel) | 不需要预设任何默认值 |
| Builder(实战项目主用) | ChatClient.builder(chatModel) → 返回 ChatClient.Builder | 预设默认 system/选项/工具后 build() |
| 派生 | existing.mutate() → 返回 ChatClient.Builder | 保留现有默认值、微调后重建(见 EmbeddingService) |
ChatClient.Builder 全方法(每个都返回 Builder 本身,可任意链式,build() 收口):
| 方法 | 作用 |
|---|---|
defaultSystem(String) | 全局系统提示(另有 Consumer 重载:s -> s.text(...).param(...)) |
defaultUser(String) | 全局用户消息模板 |
defaultOptions(ChatOptions) | 默认参数——实战项目在这塞了 ToolCallingChatOptions |
defaultToolCallbacks(ToolCallback...) / (List<ToolCallback>) / (ToolCallbackProvider) | 默认工具 |
defaultToolNames(String...) | 只放行指定名字的工具 |
defaultAdvisors(Advisor...) / (List<Advisor>) | 默认拦截器 |
build() | 产出 ChatClient 实例 |
prompt() 返回的请求 spec——每次请求可覆盖默认值,不传就用 Builder 预设(WebSearchReactAgent 用了 .messages):
| 方法 | 作用 |
|---|---|
.messages(List<Message>) | 直接塞消息列表(实战项目主链路) |
.system(String) / .user(String) | 临时设一条 system / user 消息 |
.options(ChatOptions) | 本次请求参数(覆盖 defaultOptions) |
.toolCallbacks(...) / .toolNames(...) | 本次请求的工具 |
.toolContext(Map<String, Object>) | 给工具透传上下文 |
.advisors(Advisor...) | 本次请求的拦截器 |
请求-终端两段式:
chatClient.prompt() // 开始一次请求(进入请求 spec)
.messages(messages) // 组装本次请求
.stream() // 或 .call() —— 进入终端 spec
.chatResponse() // → Flux<ChatResponse>
| 终端调用 | 返回 | 场景 |
|---|---|---|
.call().content() | String | 一次性拿文本 |
.call().entity(Class) | Java 对象 | 结构化输出一步到位(§7) |
.call().chatResponse() | ChatResponse | 要元数据时 |
.stream().content() | Flux<String> | 流式纯文本 |
.stream().chatResponse() | Flux<ChatResponse> | 实战主链路:要拿工具调用(见 WebSearchReactAgent) |
chatClient.mutate():从现有 client 派生新 client,不重建默认配置(见 EmbeddingService)- 两套怎么选:一次性调用用 ChatModel 直调(简单);Agent 主链路用 ChatClient(可挂默认选项/工具/Advisor)
§5 工具调用(Tool Calling)
四步全链路:
| 步骤 | API | 实战用例 |
|---|---|---|
| ① 声明工具 | 方法上 @Tool(description="...") + 参数 @ToolParam(description="...") | — |
| ② 注册 | ToolCallbacks.from(Object... sources) → ToolCallback[](扫 @Tool 方法) | AgentController |
| ③ 交给模型 | ToolCallingChatOptions.builder().toolCallbacks(tools).internalToolExecutionEnabled(false) | WebSearchReactAgent |
| ④ 手动执行 | callback.call(argsJson) → String(重载带 ToolContext) | WebSearchReactAgent |
手动执行全套 API(以下用例均出自 WebSearchReactAgent 的手动 ReAct 循环):
| API | 作用 |
|---|---|
output.getToolCalls() | 模型要用的工具列表 |
AssistantMessage.builder().toolCalls(list).build() | 先回填模型意图 |
callback.call(tc.arguments()) | 执行(参数就是 JSON 串,别自己再解析) |
new ToolResponse(id, name, result) | 结果按 id 配对 |
ToolResponseMessage.builder().responses(list).build() | 再回填结果 → 下一轮 |
callback.getToolDefinition().name() | 按名字找工具 |
流式特有坑:工具参数 JSON 会跨 chunk 分片,必须按 id() 合并拼接(WebSearchReactAgent 的 mergeToolCall);阻塞版循环用 call() 没这个问题。
错误处理铁律:工具抛异常也要回填同 id 的错误 ToolResponse,否则下一轮可能被模型接口拒绝。
外部工具(MCP):Tavily 搜索经 MCP 协议接入,转换成同一套 ToolCallback(§10),与本地工具无差别合并(示例项目自写 ToolMergeUtils.mergeTools,见 AgentController)。
§6 ChatMemory:跨请求记忆
| API | 作用 | 实战用例 |
|---|---|---|
MessageWindowChatMemory.builder().maxMessages(n).build() | 滑动窗口记忆 | BaseAgent |
add(String conversationId, Message) | 追加一条 | BaseAgent |
add(String, List<Message>) | 批量追加 | — |
get(String conversationId) | 取窗口内全部历史 | BaseAgent |
clear(String conversationId) | 清空会话 | — |
MessageWindowChatMemory 内部结构:一个 ChatMemoryRepository(默认 InMemoryChatMemoryRepository——本质是个内存 Map,存所有会话的全部消息)+ maxMessages(读取时只裁最近 n 条返回)。这解释了"跨请求但不跨重启"这个结论:会话数据住在内存 Map 里,应用一停就没了。想要跨重启:换 JdbcChatMemoryRepository,或像示例项目一样自己从 MySQL 回灌(见 BaseAgent)。Builder 链只有两步:MessageWindowChatMemory.builder().maxMessages(30).build()。
conversationId是隔离键:不同会话互不可见- 两层分工:内存 ChatMemory 管"喂模型的上下文",跨重启靠示例项目手写的 MySQL 回灌——
createPersistentChatMemory从会话表把历史 add 回去(BaseAgent 实现,AgentController 调用)
§7 结构化输出
| API | 作用 | 实战用例 |
|---|---|---|
new BeanOutputConverter<T>(Class) | 简单类型 | PptIntentRecognizer |
new BeanOutputConverter<T>(new ParameterizedTypeReference<List<T>>() {}) | 泛型集合必须用这个 | PlanExecuteAgent / BaseAgent |
.getFormat() | 生成 JSON 格式约束,拼进提示词 | PlanExecuteAgent |
.convert(String json) | 模型输出 → Java 对象 | PptIntentRecognizer |
MapOutputConverter / ListOutputConverter | 简单结构 | — |
chatClient.call().entity(Class) | ChatClient 一步到位(内部就是 BeanOutputConverter) | — |
实战项目用到的转换类型:List<PlanTask>(计划)、CritiqueResult(自评)、PptIntentResult(意图)、PptSchema(PPT 结构)。
§8 流式三件套:Flux / Sinks / Schedulers
Flux 操作符(按实战项目出场频率):
| 操作符 | 一句话 | 实战用例 |
|---|---|---|
map | 逐元素转换 | 取文本 |
filter | 丢弃 null 块 | — |
doOnNext | 旁路观察(累积/解析) | WebSearchReactAgent |
doOnComplete | 正常结束收尾(存记忆在这) | WebSearchReactAgent |
doOnError / doOnCancel | 出错 / 用户点停止 | WebSearchReactAgent |
doFinally | 无论怎么结束都执行(存库) | WebSearchReactAgent |
subscribeOn(Schedulers.boundedElastic()) | 切换执行线程——阻塞代码必须挪到弹性线程 | — |
publishOn(Schedulers.boundedElastic()) | 切换下游处理线程 | WebSearchReactAgent |
collectList() / block() | 收齐/阻塞——只允许在 boundedElastic 上 | — |
Sinks(手动往流里塞数据):
Sinks.Many<String> sink = Sinks.many().unicast().onBackpressureBuffer();
sink.tryEmitNext(event); // 逐条塞事件
sink.tryEmitComplete(); // 收口
return sink.asFlux(); // 交给 controller
SSE 产出口:@GetMapping(produces = "text/event-stream;charset=UTF-8") 返回 Flux<String> → Spring 自动把每个元素包成一条 data:... 事件(见 AgentController)。忘写 produces = 不再是流。
§9 RAG 向量库(PGVector)
建库(按表名动态建库的工厂):
PgVectorStore.builder(jdbcTemplate, embeddingModel)
.distanceType(PgVectorStore.PgDistanceType.COSINE_DISTANCE) // 余弦距离
.indexType(PgVectorStore.PgIndexType.HNSW) // 索引类型
.removeExistingVectorStoreTable(false)
.build()
.afterPropertiesSet(); // 触发表/索引初始化(不存在则自动建)
检索四件套(EmbeddingService,文件问答的向量检索路径):
| API | 作用 |
|---|---|
vectorStore.similaritySearch(SearchRequest) | 相似检索 → List<Document> |
SearchRequest.builder().query(q).topK(5).filterExpression(f).build() | 查询条件;SearchRequest 只有 4 个字段:query / topK / similarityThreshold(0~1,越低召回越多) / filterExpression,示例项目用了其中 3 个 |
new FilterExpressionBuilder().eq("fileid", fileId).build() | 元数据过滤(只搜本文件) |
Document.getId() / getText() / getScore() | 取内容(id 去重) |
写入:vectorStore.add(List<Document>)(上传文件时入库)。
查询扩展(进阶,Multi-Query):chatClientBuilder(chatClient.mutate()).numberOfQueries(3).includeOriginal(true).build() → expand(query) → List<Query>,一个问题变三个检索视角(见 EmbeddingService)。
EmbeddingModel:embed(String) / embed(Document)——通常被 VectorStore 内部自动调用,业务代码不直调。
配置键:embeddings.store.host / port / database / user / password(application.yml)。
§10 MCP 客户端(接外部工具)
Tavily 搜索接入三件套(AgentController,MCP 客户端侧):
| 步骤 | API |
|---|---|
| 传输层 | HttpClientStreamableHttpTransport.builder(url).requestBuilder(带 Authorization 头).build() |
| 客户端 | McpClient.sync(transport).requestTimeout(Duration.ofSeconds(300)).build() → McpSyncClient |
| 握手 | client.initialize() |
| 变成本地工具 | SyncMcpToolCallbackProvider.builder().mcpClients(List.of(tavilyMcp)).build().getToolCallbacks() → ToolCallback[] |
转换后与本地工具完全同构——这就是"工具统一抽象"的价值。MCP 服务端的搭建不在本文范围(常见传输方式:stdio / SSE / Streamable HTTP)。
§11 Advisor 扩展点
- 概念:挂在 ChatClient 调用链上的拦截器,可改请求、改响应、加记忆、打日志
- 接口:
org.springframework.ai.chat.client.advisor.api.Advisor - 挂载:
ChatClient.Builder.defaultAdvisors(advisors)——示例项目当前传空列表,纯扩展位;细粒度挂载用.prompt().advisors(...) - 常见内置:
SimpleLoggerAdvisor(请求日志)、MessageChatMemoryAdvisor(记忆托管给 ChatClient)——示例项目选择手动管理记忆,没有启用
§12 application.yml 配置键总表(示例项目)
| 键 | 管什么 |
|---|---|
spring.ai.openai.api-key / base-url / chat.options.model | 模型接入(DashScope 兼容模式) |
tavily.api-key / tavily.mcp-url | 搜索工具(MCP) |
grsai.nanobanana.api-key | 图像生成 |
embeddings.store.* | PGVector 向量库连接 |
minio.* | 文件存储 |
skills.directory | 技能目录 |
spring.datasource.* / spring.data.redis.* | MySQL / Redis 基础设施 |
更多推荐

所有评论(0)