☰
Spring AI Alibaba Chat Example 配置 TaoToken:ChatModel 与 ChatClient 接入骨架
2026/9/25 17:47:20 网站建设 项目流程

1. 为什么要在 Spring AI Alibaba Chat Example 里换掉默认通道

Spring AI Alibaba Chat Example 是官方给出的多模型对话示例工程,核心价值在于用 ChatModel / ChatClient 这层统一抽象,把底层模型供应商的差异挡在业务代码之外。你写一次 Controller,换模型只改 application.yml,这是它最舒服的地方。但示例默认走的是各家自己的 API Key 和 endpoint,一旦你要在多个模型之间来回切、或者团队里几个人共用一套额度,Key 管理就会变得很碎。

我这次做的事很具体:保留 Chat Example 的骨架不动,只把 ChatModel 的 base-url 和 api-key 指向 TaoToken 的统一通道,让 ChatClient 的调用方式完全不变。这样做的直接好处是,你原来写的chatClient.prompt().user(...).call().content()一行都不用改,换模型只是换配置里的 model 名字。

适合谁看:已经在跑 Spring Boot 3.x + Spring AI Alibaba 的开发者,手里有 Chat Example 工程,想把它接到一个统一入口上;或者你正准备搭一个多模型对话服务,想先跑通最小骨架再谈扩展。下面从依赖、配置、Java 配置类到本地验证,一步步给全。

2. TaoToken 前置:Key、地址与依赖准备

TaoToken 在这里扮演的角色是统一 API 通道,兼容 OpenAI 风格的/v1/chat/completions接口。Spring AI 的 OpenAI starter 本身就是按这个协议实现的,所以接入方式非常自然:把spring.ai.openai.base-url指向 TaoToken 的 API 地址,api-key填你在控制台生成的 Key,模型名填通道支持的模型即可。

你需要先拿到两样东西。第一是 API Key,去控制台创建:https://taotoken.net/console?utm_source=taotoken_aicg_blog_end&utm_content=console&utm_campaign=rewrite 。第二是确认 API 基地址,统一用 https://taotoken.net/api ,注意这个地址后面不加 UTM 参数,配置里保持干净。

依赖方面,Chat Example 里如果用的是 dashscope starter,我们这次换成 OpenAI starter,因为 TaoToken 走 OpenAI 兼容协议。父 POM 里统一版本,子模块只声明依赖:

<properties> <spring-boot.version>3.3.0</spring-boot.version> <spring-ai.version>1.0.0-M2</spring-ai.version> </properties> <dependencyManagement> <dependencies> <dependency> <groupId>org.springframework.ai</groupId> <artifactId>spring-ai-bom</artifactId> <version>${spring-ai.version}</version> <type>pom</type> <scope>import</scope> </dependency> </dependencies> </dependencyManagement>

子模块pom.xml里加:

<dependency> <groupId>org.springframework.ai</groupId> <artifactId>spring-ai-openai-spring-boot-starter</artifactId> </dependency>

这里有个容易踩的点:Spring AI 的 OpenAI starter 默认 base-url 是官方地址,你不改它就会往默认地址发请求。所以下一步的 yml 配置是成败关键。

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

先给完整的application.yml,这是整个接入的核心。注意base-url结尾不要多加/v1,Spring AI 会自己拼路径,多写一层会变成/v1/v1/chat/completions直接 404。

server: port: 10000 spring: ai: openai: api-key: ${TAOTOKEN_API_KEY} base-url: https://taotoken.net/api chat: options: model: gpt-4o-mini temperature: 0.7 max-tokens: 2000

api-key用环境变量注入,别硬编码进仓库。本地调试时:

export TAOTOKEN_API_KEY="你的Key"

然后是 Java 配置类。Chat Example 里通常直接注入 ChatModel,但我们推荐再包一层 ChatClient,把系统提示和默认参数固化下来,Controller 里就干净了。

@Configuration public class ChatClientConfig { @Bean public ChatClient chatClient(ChatModel chatModel) { return ChatClient.builder(chatModel) .defaultSystem("你是一个专业的技术助手,用中文回答,代码示例要能直接运行。") .build(); } }

对应的 Controller 保持示例风格,同步和流式各一个端点:

@RestController @RequestMapping("/chat") public class ChatController { private final ChatClient chatClient; public ChatController(ChatClient chatClient) { this.chatClient = chatClient; } @GetMapping("/simple") public String simple(@RequestParam String message) { return chatClient.prompt() .user(message) .call() .content(); } @GetMapping(value = "/stream", produces = MediaType.TEXT_EVENT_STREAM_VALUE) public Flux<String> stream(@RequestParam String message) { return chatClient.prompt() .user(message) .stream() .content(); } }

流式端点必须带produces = MediaType.TEXT_EVENT_STREAM_VALUE,否则浏览器拿到的是普通文本,看不到逐字输出效果。这一点在 Chat Example 的原始代码里也是这么处理的。

4. 验证请求:本地启动与对话调用

启动前确认环境变量已生效,然后跑起来:

mvn clean package -DskipTests java -jar target/chat-example-*.jar

看到Started ChatExampleApplication就说明 ChatModel Bean 装配成功。如果启动阶段就报No qualifying bean of type ChatModel,八成是 starter 没引对或者 api-key 为空导致自动配置没触发。

先测同步接口:

curl "http://localhost:10000/chat/simple?message=用一句话解释什么是依赖注入"

正常返回类似:

"依赖注入是一种设计模式,对象的依赖由外部容器创建并注入,而不是对象自己 new 出来。"

再测流式,用-N关闭 curl 缓冲,才能看到逐块输出:

curl -N "http://localhost:10000/chat/stream?message=写一个Java冒泡排序"

你会看到data:开头的分块依次刷出来,最后以data: [DONE]结束。到这一步,ChatModel 到 ChatClient 的整条链路就通了。想直接在网页里对比不同模型的回答,可以用模型对话入口:https://taotoken.net/chat?utm_source=taotoken_aicg_blog_end&utm_content=model_chat&utm_campaign=rewrite 。

5. 本篇常见错排查

报 401 Unauthorized:Key 没读到或者填错。先echo $TAOTOKEN_API_KEY确认环境变量非空,再检查 yml 里是不是写成了${TAOTOKEN_API_KEY:}这种带默认空值的写法,那样会静默用空字符串。

报 404 Not Found:base-url 多写了/v1。正确写法是https://taotoken.net/api,Spring AI 内部会拼/v1/chat/completions。如果你手动加了/v1,路径就重复了。

流式接口返回一整块而不是逐字:检查produces是否漏了,以及有没有经过 Nginx 之类的反向代理把响应缓冲了。本地直连一般不会有这个问题。

中文乱码:在流式方法里加response.setCharacterEncoding("UTF-8"),或者确认请求头Accept带了text/event-stream。

模型名报错:spring.ai.openai.chat.options.model填的名字必须是通道支持的。先用模型对话页面确认某个模型可用,再写进配置,别凭记忆填。

启动慢或超时:检查网络到taotoken.net是否通畅,可以在启动日志里看 RestClient 的请求耗时。如果是公司网络限制,换一个网络环境再试。

6. 下一步:从骨架到长期编码

跑通这个骨架之后,你会发现 Chat Example 的价值不只是"能对话",而是它把 ChatModel / ChatClient 的边界划得很清楚。业务代码只依赖 ChatClient,模型切换、参数调整、系统提示都在配置层解决。如果你打算把它用在日常编码辅助或者 Agent 场景上,长期高频调用更适合走 Coding Plan:https://taotoken.net/coding-plan?utm_source=taotoken_aicg_blog_end&utm_content=coding_plan&utm_campaign=rewrite 。

接入细节和参数说明可以对照文档:https://taotoken.net/doc?utm_source=taotoken_aicg_blog_end&utm_content=doc&utm_campaign=rewrite 。Key 的创建和轮换在 API Keys 页面:https://taotoken.net/api-keys?utm_source=taotoken_aicg_blog_end&utm_content=api_keys&utm_campaign=rewrite 。如果你用的是 Claude Code 这类工具链,Anthropic 兼容入口在这里:https://taotoken.net/claude-code-anthropic?utm_source=taotoken_aicg_blog_end&utm_content=claudecode&utm_campaign=rewrite 。

最后留一个实操建议:把temperature和max-tokens做成可覆盖的请求参数,而不是写死在 yml 里。Chat Example 的ChatClient.prompt().options(...)支持运行时覆盖,这样同一个 Bean 既能处理需要确定性的代码生成,也能处理需要发散的文案场景,不用为每种用途各建一个 Client。

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

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

立即咨询