☰
Spring AI开发跃迁指南(第二章:急速上手2——Spring AI ChatClient 构建人工智能应用的高级配置入门)
2026/9/26 10:55:31 网站建设 项目流程

1. 从一次“配置全写死在代码里”的翻车说起

如果你已经能用 Spring Boot 跑通一个 Hello World 级别的接口,那接下来大概率会碰到这个场景:项目里要接大模型,第一版代码把 API Key、模型名、系统提示词全硬编码在@Service里,本地跑得挺欢,一换环境就全崩。我试过最典型的一次,测试环境 Key 和线上 Key 混用,排查了半天才发现是配置文件没抽离。

Spring AI 的ChatClient就是来解决这类问题的。它是什么?一句话:它是 Spring 生态里操作对话模型的统一门面,把提示词、顾问链、记忆、参数配置都收敛到一套 Fluent API 上。能做什么?你可以用它构建带默认系统提示词的对话服务、挂载 Advisor 做 RAG 检索增强、接入 ChatMemory 维护多轮上下文。适合谁?有 Spring Boot 基础、想把 AI 能力嵌进现有 Java 工程的开发者,而不是只想在网页上聊两句的人。

这一篇聚焦“高级配置入门”,也就是在本地工程里把ChatClient和 TaoToken 的统一 Key/API 通道接起来,交付可复制的application.yml、ChatClient配置骨架、Advisor 与 ChatMemory 装配片段,最后给出启动验证和对话连通性检查动作。全程按能跟做的步骤走,不堆概念。

2. 前置准备:TaoToken 统一 Key 与 API 通道

在写配置之前,先把“通道”这件事说清楚。TaoToken 提供的是统一的 API 入口,你拿一个 Key,就能在 Spring AI 里通过 OpenAI 兼容协议访问多种模型,不用为每个模型厂商单独维护一套 SDK 和鉴权逻辑。对 Java 工程来说,这意味着application.yml里只需要维护一份 base-url 和 api-key。

你需要先拿到两样东西:一个是 API Key,一个是确认要用的模型名。Key 在控制台的 API Keys 页面创建,模型名在文档里能查到当前可用的列表。这两个信息后面会直接写进配置文件。

注意:Key 属于敏感凭证,不要提交到 Git 仓库。建议用环境变量注入,或者在本地用application-local.yml并加入.gitignore。

相关入口我放在这里,按需取用:创建和管理 Key 走 API Keys 页面,接入参数和协议细节看接入文档,想先在网页上验证模型通不通可以用模型对话,长期做编码或 Agent 类任务可以了解 Coding Plan。

3. 可复制配置:application.yml 与 ChatClient 骨架

3.1 application.yml 里的通道配置

Spring AI 的 OpenAI starter 支持自定义 base-url,这正是接入统一通道的关键。下面这份配置可以直接复制,把占位符替换成你自己的值即可。

spring: ai: openai: # 统一 API 通道地址,注意结尾不要带多余斜杠 base-url: https://taotoken.net/api # 从控制台创建的 Key,建议用环境变量注入 api-key: ${TAOTOKEN_API_KEY} chat: options: # 按文档里当前可用的模型名填写 model: gpt-4o-mini temperature: 0.7 # 日志级别,调试 Advisor 时非常有用 logging: level: org.springframework.ai.chat.client.advisor: DEBUG

这里有几个容易踩的点。第一,base-url不要写成带/v1的完整路径,Spring AI 的 OpenAI 客户端会自己拼接,写多了会 404。第二,api-key用${}占位,启动时通过环境变量传入,避免明文落盘。第三,temperature这类参数属于可移植选项,不同模型支持程度不一样,先按默认值跑通再调。

3.2 ChatClient 配置骨架

ChatClient的构建推荐用Builder注入的方式,而不是每次 new 一个。这样默认系统提示词、默认 Advisor 都能在构建期一次性装配好。

import org.springframework.ai.chat.client.ChatClient; import org.springframework.ai.chat.client.advisor.SimpleLoggerAdvisor; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; @Configuration public class ChatClientConfig { @Bean public ChatClient chatClient(ChatClient.Builder builder) { return builder // 默认系统提示词,后续每次对话都会带上 .defaultSystem("你是一个严谨的 Java 技术助手,回答尽量给出可运行的代码示例。") // 默认挂载日志顾问,方便观察请求与响应 .defaultAdvisors(new SimpleLoggerAdvisor()) .build(); } }

defaultSystem的作用是简化重复输入。比如你希望这个 ChatClient 永远扮演“Java 技术助手”,就不用每次在prompt()里再写一遍系统提示词。defaultAdvisors则是把顾问链固化下来,后面所有通过这个 ChatClient 发起的调用都会经过它。

3.3 带参数的默认系统提示词

默认提示词还能带占位参数,这在需要动态切换角色或注入业务变量时很有用。构建时写模板,调用时传参。

@Service public class ActorInfoService { private final ChatClient chatClient; public ActorInfoService(ChatClient.Builder builder) { this.chatClient = builder .defaultSystem("生成演员 {actor} 的相关信息,只输出事实性内容。") .build(); } public String generate(String actor, String message) { return chatClient.prompt() // 通过 system 的 lambda 形式注入参数 .system(it -> it.param("actor", actor)) .user(message) .call() .content(); } }

对应的 Controller 很直白,把两个参数透传进去就行。

@RestController public class ActorInfoController { private final ActorInfoService service; public ActorInfoController(ActorInfoService service) { this.service = service; } @GetMapping("/ai/actor") public String actor(@RequestParam String actor, @RequestParam String message) { return service.generate(actor, message); } }

启动后访问http://localhost:8080/ai/actor?actor=刘亦菲&message=介绍她的教育情况,就能看到系统提示词里的{actor}被替换成了实际值,模型回答也会围绕这个演员展开。这就是带参数默认提示词的价值:模板固定,变量运行时注入。

4. Advisor 与 ChatMemory 装配:让对话有上下文

4.1 Advisor 是什么,为什么像 AOP

Advisor 的核心思想是对模型交互的输入输出做拦截和增强,和 Spring AOP 的拦截器非常像。它能在提示词发给模型前动态添加上下文,也能在结果返回后做过滤或结构化解析。常见用途有四类:动态修改提示词、结果后处理、跨轮次上下文管理、业务规则注入。

在ChatClient的 Fluent API 里,通过advisors()方法挂载。这里有个关键规则:添加顺序决定执行顺序,每个 Advisor 依次修改提示或上下文,再把变更传给下一个。

ChatClient.create(chatModel).prompt() .advisors( new MessageChatMemoryAdvisor(chatMemory), new QuestionAnswerAdvisor(vectorStore) ) .user(userText) .call() .content();

上面这段里,MessageChatMemoryAdvisor先执行,把对话历史作为消息集合加进提示;然后QuestionAnswerAdvisor基于用户问题和刚加入的历史去向量库检索,返回更相关的上下文。顺序反了,检索质量会明显下降。

4.2 ChatMemory 的几种实现与选择

ChatMemory接口负责会话历史的存储,提供添加消息、检索消息、清除历史三类方法。目前有四种实现,选哪种取决于你的持久化需求。

实现存储位置特点
InMemoryChatMemory内存最简单,重启即丢,适合本地调试
CassandraChatMemoryCassandra支持 TTL,可设置记忆有效期
Neo4jChatMemoryNeo4j图结构存储,适合关系型上下文
JdbcChatMemory关系库目前自动配置支持 PostgreSQL 和 MariaDB

本地开发阶段,我建议先用InMemoryChatMemory把链路跑通,确认 Advisor 顺序和记忆注入都正常,再换成持久化实现。下面是一个内存记忆加消息顾问的装配片段。

@Configuration public class MemoryConfig { @Bean public ChatMemory chatMemory() { return new InMemoryChatMemory(); } @Bean public ChatClient memoryChatClient(ChatClient.Builder builder, ChatMemory chatMemory) { return builder .defaultSystem("你是一个有记忆的助手,请结合历史对话回答。") .defaultAdvisors(new MessageChatMemoryAdvisor(chatMemory)) .build(); } }

如果你需要持久化,JdbcChatMemory 的自动配置最省事,引入对应 starter 后,它会基于 JDBC 驱动自动建ai_chat_memory表。想手动控制就自己 create,把JdbcTemplate传进去。

JdbcChatMemory.create( JdbcChatMemoryConfig.builder() .jdbcTemplate(jdbcTemplate) .build() );

4.3 RAG 与日志顾问

RAG(检索增强生成)解决的是大模型在长篇内容、事实准确性和上下文感知上的局限。Spring AI 提供模块化架构,你可以自己搭 RAG 流,也可以用开箱即用的QuestionAnswerAdvisor。知识库体系后面会单独用一章展开,这里先知道它挂在 Advisor 链上即可。

调试阶段最实用的是SimpleLoggerAdvisor,它记录 ChatClient 的请求和响应。建议加到链的末尾,这样能看到前面所有 Advisor 处理完之后的最终提示词。

ChatResponse response = ChatClient.create(chatModel).prompt() .advisors(new SimpleLoggerAdvisor()) .user("你是谁") .call() .chatResponse();

配合application.yml里把org.springframework.ai.chat.client.advisor的日志级别设为 DEBUG,控制台就能看到完整的请求体。如果默认日志不够用,还能自定义序列化函数,只打印你关心的字段。

SimpleLoggerAdvisor customLogger = new SimpleLoggerAdvisor( request -> "Custom request: " + request.userText, response -> "Custom response: " + response.getResult() );

5. 启动验证与对话连通性检查

配置写完后,别急着写业务,先做三步验证。

第一步,启动应用,观察日志里有没有base-url和模型名的加载信息。如果启动就报鉴权错误,多半是 Key 没注入成功,检查环境变量名和application.yml里的占位符是否一致。

第二步,用 curl 直接打你的接口,确认返回不是空字符串。

curl "http://localhost:8080/ai/actor?actor=刘亦菲&message=介绍她的教育情况"

正常返回应该是一段围绕该演员的文本。如果返回 401,检查 Key;返回 404,检查base-url是否多写了路径;返回超时,检查网络出口和模型名是否拼错。

第三步,验证记忆是否生效。连续调两次同一个会话接口,第二次问“我刚才问了什么”,如果模型能答出上一轮内容,说明MessageChatMemoryAdvisor装配正确。这一步是很多人容易忽略的,记忆没生效往往是因为 Advisor 没挂上,或者每次调用都新建了 ChatClient。

6. 本篇常见错误排查

错误一:defaultSystem不生效。检查是不是在prompt()里又调用了.system()覆盖了默认值。默认系统提示词和运行时系统提示词是叠加关系,但如果你在运行时传了新的 system,行为会以运行时为准。

错误二:Advisor 顺序导致检索结果差。记住记忆顾问要在检索顾问之前。顺序错了,检索时拿不到历史上下文,RAG 效果会打折。

错误三:JdbcChatMemory 自动建表失败。目前自动配置只支持 PostgreSQL 和 MariaDB,用 MySQL 需要手动建表或自己 create。另外确认spring.ai.chat.memory.jdbc.initialize-schema没有被设成 false。

错误四:日志顾问看不到输出。检查日志级别是否设成了 DEBUG,以及SimpleLoggerAdvisor是否真的加进了链里。加在链末尾能看到最完整的提示词。

错误五:Key 泄露风险。不要把 Key 写进application.yml提交到仓库。用环境变量或本地 profile,并在.gitignore里排除。

排障和接入相关的细节,可以对照 API Keys 页面和接入文档逐项核对;想先确认模型本身通不通,用模型对话页面发一条消息最快;如果是要长期做编码或 Agent 类任务,Coding Plan 的通道配置和这里略有不同,可以单独看。

把上面这套配置跑通之后,你手里就有了一个可复用的 ChatClient 骨架:默认提示词、带参数模板、Advisor 链、ChatMemory 都装配好了。接下来往里面加业务逻辑,或者换成持久化记忆、接入向量库做 RAG,都是在这个骨架上扩展,不用再动通道配置。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询