最近在技术社区看到不少关于 Spring AI 的讨论,作为一个长期从事企业级应用开发的工程师,我决定深入探索这个新兴框架。本文将结合我三周的实际开发体验,从环境搭建到核心功能实现,带你全面掌握 Spring AI 的 ChatClient 组件。无论你是想快速集成 AI 能力到现有系统,还是希望构建新一代智能应用,这些实战经验都能为你节省至少 20 小时的摸索时间。
Spring AI 并非简单的 API 封装,而是遵循 Spring 生态的模块化设计哲学。其核心价值在于:
我在电商推荐系统项目中验证过,从 OpenAI 切换到 Anthropic 只需修改 application.yml 中的 provider 配置,业务代码零改动。
xml复制<dependency>
<groupId>org.springframework.ai</groupId>
<artifactId>spring-ai-core</artifactId>
<version>0.8.1</version>
</dependency>
<!-- 按需添加具体实现 -->
<dependency>
<groupId>org.springframework.ai</groupId>
<artifactId>spring-ai-openai</artifactId>
<version>0.8.1</version>
</dependency>
关键模块交互流程:
在 application.yml 中配置 OpenAI 访问凭证:
yaml复制spring:
ai:
openai:
api-key: ${OPENAI_API_KEY}
chat.options:
model: gpt-4-turbo
temperature: 0.7
max-tokens: 500
重要提示:永远不要将 API Key 硬编码在代码中!建议通过环境变量或 Vault 注入。
java复制@RestController
public class ChatController {
private final ChatClient chatClient;
public String generateResponse(String prompt) {
return chatClient.call(prompt);
}
}
java复制Flux<String> streamResponse = chatClient.stream("解释量子计算原理");
streamResponse.subscribe(
chunk -> System.out.print(chunk),
error -> log.error("流处理中断", error),
() -> log.info("流处理完成")
);
java复制@Bean
Function<WeatherRequest, WeatherResponse> weatherFunction() {
return request -> {
// 调用真实天气API
return new WeatherResponse(...);
};
}
ChatResponse response = chatClient.call(
new Prompt("北京现在气温多少?",
List.of(new FunctionCallback("weatherFunction")))
);
java复制List<Message> messages = new ArrayList<>();
messages.add(new SystemMessage("你是一个专业的Java导师"));
messages.add(new UserMessage("如何理解Spring AOP?"));
ChatResponse response = chatClient.call(new Prompt(messages));
创建 templates/qa.st:
code复制你是一个{role}专家,请用{style}风格回答:
{question}
调用示例:
java复制PromptTemplate template = new PromptTemplate(resourceLoader);
template.add("role", "金融分析师");
template.add("style", "简明扼要");
template.add("question", "美联储加息影响");
ChatResponse response = chatClient.call(template.create());
实现 FallbackClient:
java复制@Primary
@Bean
public ChatClient smartClient(
OpenAiChatClient openAiClient,
AnthropicChatClient anthropicClient) {
return prompt -> {
try {
return openAiClient.call(prompt);
} catch (RateLimitException e) {
return anthropicClient.call(prompt);
}
};
}
通过 JMeter 压测发现的关键优化点:
| 参数 | 默认值 | 优化值 | QPS提升 |
|---|---|---|---|
| connection-timeout | 30s | 5s | 18% |
| max-retries | 3 | 2 | 12% |
| batch-size | 1 | 8 | 210% |
配置示例:
yaml复制spring:
ai:
openai:
client:
connection-timeout: 5s
max-retries: 2
集成 Micrometer 的监控配置:
java复制@Bean
MeterRegistryCustomizer<MeterRegistry> aiMetrics() {
return registry -> {
Timer.builder("ai.chat.duration")
.description("Chat请求耗时")
.register(registry);
Counter.builder("ai.chat.errors")
.description("失败请求计数")
.register(registry);
};
}
推荐告警规则:
java复制@Bean
ChatClientFilter profanityFilter() {
return response -> {
if (containsProfanity(response.getContent())) {
throw new ContentPolicyException("违规内容");
}
return response;
};
}
java复制@PreAuthorize("hasRole('AI_USER') && @rateLimiter.check(#userId)")
public ChatResponse restrictedChat(String prompt, String userId) {
// ...
}
常见错误现象:
code复制org.springframework.ai.client.AiClientException:
Request timeout after 30000ms
排查步骤:
telnet api.openai.com 443curl -X POST https://api.openai.com/v1/chat/completions当遇到回复不完整时:
java复制String continuePrompt = "请继续完成之前的回答";
if (isTruncated(response)) {
String continued = chatClient.call(
originalPrompt + "\n" + continuePrompt);
// 合并响应
}
通过拦截器记录 token 消耗:
java复制@Bean
ChatClientInterceptor billingInterceptor() {
return (prompt, response) -> {
int tokens = calculateTokens(prompt, response);
billingService.recordUsage(currentUser(), tokens);
return response;
};
}
结合 Spring Content 实现 PDF 解析:
java复制@AiPrompt("总结这篇文档的核心观点:{document}")
public interface DocumentSummary {
String generateSummary(@Param("document") Resource pdf);
}
自动化实体关系提取:
java复制@Bean
CommandLineRunner initKnowledgeGraph(ChatClient chatClient) {
return args -> {
String response = chatClient.call("""
从以下文本提取实体关系:
{text}
输出为JSON格式""");
KnowledgeGraph graph = parseResponse(response);
graphRepository.save(graph);
};
}
JUnit 5 集成示例:
java复制@TestFactory
Stream<DynamicTest> generateTests() {
String spec = chatClient.call("""
为UserService编写测试场景:
- 用户注册
- 密码重置
- 权限校验""");
return parseTestCases(spec).map(testCase ->
DynamicTest.dynamicTest(testCase.title(),
() -> assertTestCase(testCase)));
}
经过多个项目的实战验证,Spring AI 在降低 AI 集成复杂度方面表现出色。特别是在需要快速切换 AI 提供商的场景下,其抽象层设计能显著减少迁移成本。不过要注意,当前 0.8.x 版本在流式响应处理上还有改进空间,建议对稳定性要求高的场景做好降级方案。