Spring AI是Spring官方推出的AI框架,为Java开发者提供了原生的AI开发体验。本文将带你从零开始,掌握Spring AI的核心概念和Spring Boot集成技巧,打造属于自己的AI应用。

Spring AI简介

Spring AI是Spring官方推出的AI框架,专门为Java和Spring开发者设计,提供了一套简洁、直观且符合Spring生态的AI开发API。它集成了多种主流的AI模型和服务,让开发者能够轻松构建AI驱动的应用。

为什么选择Spring AI?

  1. Spring生态完美集成:无缝集成Spring Boot、Spring Cloud等
  2. 官方维护:Spring官方项目,质量保证
  3. 简洁的API:直观的编程模型,学习成本低
  4. 企业级支持:完整的生产环境特性支持
  5. 多模型支持:支持OpenAI、Azure OpenAI、Ollama等多种模型
  6. 工具集成:内置丰富的工具和增强器
  7. 社区活跃:Spring社区强大的支持

核心组件详解

1. ChatClient(聊天客户端)

ChatClient是Spring AI的核心组件,提供简洁的聊天交互API。

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
@Configuration
public class AiConfig {

@Bean
public ChatClient chatClient(ChatClient.Builder builder) {
return builder
.defaultAdvisors(new SimpleLoggerAdvisor())
.build();
}
}

// 使用示例
@Service
public class ChatService {

private final ChatClient chatClient;

public ChatService(ChatClient chatClient) {
this.chatClient = chatClient;
}

public String chat(String message) {
return chatClient.prompt()
.user(message)
.call()
.content();
}

public String chatWithSystem(String systemMessage, String userMessage) {
return chatClient.prompt()
.system(systemMessage)
.user(userMessage)
.call()
.content();
}
}

2. Prompt Templates(提示词模板)

Spring AI提供了丰富的提示词模板支持。

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
@Service
public class PromptService {

public String generatePrompt(String role, String question, String language) {
return """
你是一个专业的%s,请回答以下问题:
%s

请用%s回答。
""".formatted(role, question, language);
}

// 使用PromptTemplate
public String chatWithTemplate(String role, String question) {
PromptTemplate template = new PromptTemplate("""
你是一个专业的{role},请详细解释:
{question}

请提供具体示例和最佳实践。
""");

return chatClient.prompt(template)
.user(Map.of("role", role, "question", question))
.call()
.content();
}
}

3. Advisors(增强器)

Advisor是Spring AI中用于增强AI响应的机制。

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
@Configuration
public class AdvisorConfig {

// 日志增强器
@Bean
public Advisor loggingAdvisor() {
return new SimpleLoggerAdvisor();
}

// 自定义增强器
@Bean
public Advisor customAdvisor() {
return new DefaultPromptAugmentor() {
@Override
public AdvisedRequest adviseRequest(AdvisedRequest request, Map<String, Object> context) {
// 在请求前添加自定义逻辑
String enhancedUserMessage = "请详细回答以下问题,并提供示例:\n" + request.user().text();
return AdvisedRequest.from(request)
.withUser(enhancedUserMessage)
.build();
}
};
}

// 速率限制增强器
@Bean
public Advisor rateLimitAdvisor() {
return new RateLimitChatMemoryAdvisor(Duration.ofMinutes(10), 50);
}
}

4. Function Callbacks(函数调用)

Spring AI支持工具调用,让AI能够执行特定任务。

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
// 定义工具类
public class SearchTools {

@Tool(description = "搜索互联网信息")
public String searchWeb(String query) {
// 这里实现搜索逻辑
return "搜索 '" + query + "' 的结果...";
}

@Tool(description = "计算数学表达式")
public double calculate(String expression) {
try {
// 使用JavaScript引擎计算
ScriptEngineManager manager = new ScriptEngineManager();
ScriptEngine engine = manager.getEngineByName("JavaScript");
return ((Number) engine.eval(expression)).doubleValue();
} catch (Exception e) {
return 0.0;
}
}

@Tool(description = "获取当前时间")
public String getCurrentTime() {
return LocalDateTime.now().format(DateTimeFormatter.ISO_LOCAL_DATE_TIME);
}
}

// 配置函数调用
@Configuration
public class FunctionConfig {

@Bean
public FunctionCallbackContext functionCallbackContext() {
FunctionCallbackContext context = new FunctionCallbackContext();

// 注册工具
context.addCallback("searchWeb", new FunctionCallback() {
@Override
public String call(String input) {
return new SearchTools().searchWeb(input);
}
});

return context;
}
}

// 在ChatClient中使用
@Service
public class FunctionCallService {

public String chatWithTools(String message) {
return chatClient.prompt()
.user(message)
.functions("searchWeb", "calculate", "getCurrentTime")
.call()
.content();
}
}

5. Vector Store(向量存储)

Spring AI支持多种向量存储,用于实现RAG(检索增强生成)。

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
60
61
62
63
@Configuration
public class VectorStoreConfig {

@Bean
public VectorStore vectorStore(EmbeddingModel embeddingModel) {
// 使用内存向量存储(生产环境建议使用Redis、PostgreSQL等)
return new SimpleVectorStore(embeddingModel);
}

@Bean
public EmbeddingModel embeddingModel() {
return new OpenAiEmbeddingModel(OpenAiApi.builder()
.apiKey(System.getenv("OPENAI_API_KEY"))
.build());
}
}

@Service
public class RagService {

private final VectorStore vectorStore;
private final EmbeddingModel embeddingModel;
private final ChatClient chatClient;

public RagService(VectorStore vectorStore,
EmbeddingModel embeddingModel,
ChatClient chatClient) {
this.vectorStore = vectorStore;
this.embeddingModel = embeddingModel;
this.chatClient = chatClient;
}

// 文档入库
public void storeDocument(String content, String metadata) {
TextSegment segment = TextSegment.from(content);
Embedding embedding = embeddingModel.embed(segment).content();

Document document = new Document(segment, Map.of("source", metadata));
vectorStore.add(List.of(document));
}

// 检索增强问答
public String askWithContext(String question) {
// 检索相关文档
List<Document> relevantDocs = vectorStore.similaritySearch(
SearchRequest.builder()
.query(question)
.topK(3)
.build()
);

// 构建上下文
String context = relevantDocs.stream()
.map(doc -> doc.getText())
.collect(Collectors.joining("\n\n"));

return chatClient.prompt()
.system("基于以下文档内容回答问题:\n" + context)
.user(question)
.call()
.content();
}
}

实战案例:智能问答系统

项目结构

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
spring-ai-qa-system/
├── src/main/java/com/example/springai/
│ ├── config/ # 配置类
│ │ ├── AiConfig.java # AI配置
│ │ ├── VectorStoreConfig.java # 向量存储配置
│ │ └── AdvisorConfig.java # 增强器配置
│ ├── controller/ # 控制器
│ │ └── QaController.java # 问答接口
│ ├── service/ # 业务服务
│ │ ├── ChatService.java # 聊天服务
│ │ ├── RagService.java # RAG服务
│ │ └── PromptService.java # 提示词服务
│ ├── model/ # 数据模型
│ │ ├── ChatRequest.java # 请求模型
│ │ └── ChatResponse.java # 响应模型
│ └── tools/ # 工具类
│ └── SearchTools.java # 搜索工具
├── src/main/resources/
│ ├── application.yml # 应用配置
└── pom.xml # Maven依赖

Maven依赖配置

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
<!-- pom.xml -->
<dependencies>
<!-- Spring Boot -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>

<!-- Spring AI -->
<dependency>
<groupId>org.springframework.ai</groupId>
<artifactId>spring-ai-openai-spring-boot-starter</artifactId>
<version>1.0.0-M3</version>
</dependency>

<!-- 向量存储(可选) -->
<dependency>
<groupId>org.springframework.ai</groupId>
<artifactId>spring-ai-pgvector-store-spring-boot-starter</artifactId>
<version>1.0.0-M3</version>
</dependency>

<!-- 其他依赖 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-validation</artifactId>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
</dependencies>

核心服务实现

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
// ChatService.java
@Service
@Slf4j
public class ChatService {

private final ChatClient chatClient;
private final PromptService promptService;

public ChatService(ChatClient chatClient, PromptService promptService) {
this.chatClient = chatClient;
this.promptService = promptService;
}

public ChatResponse chat(ChatRequest request) {
try {
// 生成提示词
String prompt = promptService.generatePrompt(request);

// 调用AI
String response = chatClient.prompt()
.system(prompt)
.user(request.getMessage())
.call()
.content();

return ChatResponse.builder()
.response(response)
.timestamp(System.currentTimeMillis())
.success(true)
.build();

} catch (Exception e) {
log.error("聊天服务调用失败", e);
return ChatResponse.builder()
.success(false)
.errorMessage(e.getMessage())
.timestamp(System.currentTimeMillis())
.build();
}
}

// 流式响应
public Flux<String> streamChat(ChatRequest request) {
String prompt = promptService.generatePrompt(request);

return chatClient.prompt()
.system(prompt)
.user(request.getMessage())
.stream()
.content();
}
}

REST控制器

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
@RestController
@RequestMapping("/api/chat")
@RequiredArgsConstructor
@Slf4j
public class ChatController {

private final ChatService chatService;
private final RagService ragService;

@PostMapping("/simple")
public ResponseEntity<ChatResponse> simpleChat(@Valid @RequestBody ChatRequest request) {
log.info("收到简单聊天请求:{}", request.getMessage());
ChatResponse response = chatService.chat(request);
return ResponseEntity.ok(response);
}

@PostMapping("/rag")
public ResponseEntity<ChatResponse> ragChat(@Valid @RequestBody ChatRequest request) {
log.info("收到RAG聊天请求:{}", request.getMessage());

String answer = ragService.askWithContext(request.getMessage());

ChatResponse response = ChatResponse.builder()
.response(answer)
.timestamp(System.currentTimeMillis())
.success(true)
.build();

return ResponseEntity.ok(response);
}

@PostMapping(value = "/stream", produces = MediaType.TEXT_EVENT_STREAM_VALUE)
public Flux<String> streamChat(@Valid @RequestBody ChatRequest request) {
log.info("收到流式聊天请求:{}", request.getMessage());
return chatService.streamChat(request);
}

@PostMapping("/tools")
public ResponseEntity<ChatResponse> chatWithTools(@Valid @RequestBody ChatRequest request) {
log.info("收到工具聊天请求:{}", request.getMessage());

// 这里可以调用带有工具的聊天服务
ChatResponse response = chatService.chat(request);
return ResponseEntity.ok(response);
}
}

应用配置

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
# application.yml
spring:
application:
name: spring-ai-qa-system
ai:
openai:
api-key: ${OPENAI_API_KEY:your-api-key-here}
chat:
model: gpt-3.5-turbo
temperature: 0.3
max-tokens: 1000
embedding:
model: text-embedding-ada-002

server:
port: 8080

logging:
level:
org.springframework.ai: DEBUG
com.example.springai: INFO

高级功能扩展

1. 多模型支持

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
@Configuration
public class MultiModelConfig {

@Bean
@Qualifier("gpt4ChatClient")
public ChatClient gpt4ChatClient(ChatClient.Builder builder) {
return builder
.defaultOptions(ChatOptions.builder()
.model("gpt-4")
.temperature(0.1)
.build())
.build();
}

@Bean
@Qualifier("gpt35ChatClient")
public ChatClient gpt35ChatClient(ChatClient.Builder builder) {
return builder
.defaultOptions(ChatOptions.builder()
.model("gpt-3.5-turbo")
.temperature(0.7)
.build())
.build();
}
}

@Service
public class MultiModelService {

private final ChatClient gpt4Client;
private final ChatClient gpt35Client;

public MultiModelService(
@Qualifier("gpt4ChatClient") ChatClient gpt4Client,
@Qualifier("gpt35ChatClient") ChatClient gpt35Client) {
this.gpt4Client = gpt4Client;
this.gpt35Client = gpt35Client;
}

public String chatWithModel(String message, String model) {
ChatClient client = "gpt4".equals(model) ? gpt4Client : gpt35Client;
return client.prompt()
.user(message)
.call()
.content();
}
}

2. 自定义增强器

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
@Component
public class CustomLoggingAdvisor implements Advisor {

private static final Logger log = LoggerFactory.getLogger(CustomLoggingAdvisor.class);

@Override
public AdvisedRequest adviseRequest(AdvisedRequest request, Map<String, Object> context) {
log.info("AI请求 - 用户: {}, 系统: {}, 消息长度: {}",
request.user().text(),
request.system().text(),
request.user().text().length());

return request;
}

@Override
public ChatResponse adviseResponse(ChatResponse response, Map<String, Object> context) {
log.info("AI响应 - 成功: {}, 响应长度: {}, Token使用: {}",
response != null,
response != null ? response.getResult().getOutput().getContent().length() : 0,
response != null ? response.getMetadata().getUsage().getTotalTokens() : 0);

return response;
}
}

部署和优化

Docker部署

1
2
3
4
5
6
7
8
9
10
# Dockerfile
FROM openjdk:17-jdk-slim

WORKDIR /app

COPY target/*.jar app.jar

EXPOSE 8080

CMD ["java", "-jar", "app.jar"]
1
2
3
4
5
6
7
8
9
10
# docker-compose.yml
version: '3.8'
services:
spring-ai-app:
build: .
ports:
- "8080:8080"
environment:
- OPENAI_API_KEY=${OPENAI_API_KEY}
restart: unless-stopped

性能优化策略

  1. 缓存机制

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    @Configuration
    public class CacheConfig {

    @Bean
    public CacheManager cacheManager() {
    CaffeineCacheManager cacheManager = new CaffeineCacheManager();
    cacheManager.setCaffeine(Caffeine.newBuilder()
    .maximumSize(1000)
    .expireAfterWrite(Duration.ofMinutes(30)));
    return cacheManager;
    }
    }

    @Service
    @Cacheable("ai-responses")
    public String cachedChat(String message) {
    return chatClient.prompt()
    .user(message)
    .call()
    .content();
    }
  2. 异步处理

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    @Service
    public class AsyncChatService {

    @Async
    public CompletableFuture<String> asyncChat(String message) {
    return CompletableFuture.supplyAsync(() ->
    chatClient.prompt()
    .user(message)
    .call()
    .content()
    );
    }

    public List<String> batchChat(List<String> messages) {
    return messages.stream()
    .map(message -> chatClient.prompt()
    .user(message)
    .call()
    .content())
    .collect(Collectors.toList());
    }
    }

最佳实践

1. 错误处理

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
@Service
public class ResilientChatService {

@Retryable(
value = {AiException.class},
maxAttempts = 3,
backoff = @Backoff(delay = 1000, multiplier = 2)
)
public String resilientChat(String message) {
try {
return chatClient.prompt()
.user(message)
.call()
.content();
} catch (AiException e) {
log.error("AI服务调用失败,重试中...", e);
throw e;
}
}

@Recover
public String recover(AiException e, String message) {
log.error("AI服务调用失败,使用降级方案", e);
return "抱歉,AI服务暂时不可用,请稍后再试。";
}
}

2. 监控和指标

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
@Configuration
public class MetricsConfig {

@Bean
public MeterRegistryCustomizer<MeterRegistry> metricsCustomizer() {
return registry -> {
registry.config()
.commonTags("application", "spring-ai-qa-system");
};
}
}

@Service
public class MonitoredChatService {

private final Counter chatRequests;
private final Counter chatErrors;
private final Timer chatDuration;

public MonitoredChatService(MeterRegistry registry) {
this.chatRequests = Counter.builder("ai.chat.requests")
.description("Number of chat requests")
.register(registry);

this.chatErrors = Counter.builder("ai.chat.errors")
.description("Number of chat errors")
.register(registry);

this.chatDuration = Timer.builder("ai.chat.duration")
.description("Chat request duration")
.register(registry);
}

public String monitoredChat(String message) {
chatRequests.increment();

return chatDuration.recordCallable(() -> {
try {
return chatClient.prompt()
.user(message)
.call()
.content();
} catch (Exception e) {
chatErrors.increment();
throw e;
}
});
}
}

3. 安全考虑

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
60
@Configuration
public class SecurityConfig {

@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
http
.csrf().disable()
.authorizeHttpRequests(authz -> authz
.requestMatchers("/api/chat/**").authenticated()
.anyRequest().permitAll()
)
.httpBasic();

return http.build();
}

@Bean
public RateLimiter rateLimiter() {
return RateLimiter.create(10.0); // 每秒10个请求
}
}

@Service
public class SecureChatService {

private final RateLimiter rateLimiter;

public SecureChatService(RateLimiter rateLimiter) {
this.rateLimiter = rateLimiter;
}

public String secureChat(String message, Authentication auth) {
// 速率限制
if (!rateLimiter.tryAcquire()) {
throw new RuntimeException("请求过于频繁,请稍后再试");
}

// 输入过滤
String sanitizedMessage = sanitizeInput(message);

// 调用AI
String response = chatClient.prompt()
.user(sanitizedMessage)
.call()
.content();

// 输出过滤
return sanitizeOutput(response);
}

private String sanitizeInput(String input) {
// 实现输入过滤逻辑
return input.trim();
}

private String sanitizeOutput(String output) {
// 实现输出过滤逻辑
return output.trim();
}
}

总结与注意事项

技术要点总结

  1. Spring AI是Spring官方推出的AI框架,提供了原生的Spring集成体验
  2. 核心组件包括ChatClient、Advisor、Function Callbacks、Vector Store等
  3. Spring Boot集成提供了完整的应用开发体验
  4. 多模型支持可以灵活选择不同的AI模型
  5. 企业级特性包括监控、安全、缓存等生产环境必需功能

使用注意事项

  • 合理管理API密钥,避免泄露
  • 注意速率限制和成本控制
  • 在生产环境中使用适当的缓存和监控
  • 定期更新Spring AI版本,获取最新功能
  • 遵守相关法律法规,确保内容合规

扩展阅读建议

  • 深入学习Spring AI官方文档
  • 了解Spring生态的其他AI相关项目
  • 学习Spring Boot最佳实践
  • 关注AI技术的最新发展趋势
  • 研究向量数据库和RAG技术

通过本文的介绍,你已经掌握了使用Spring AI构建LLM应用的基本技能。Spring AI以其简洁的API设计和强大的Spring生态集成,为Java开发者提供了一条快速构建AI应用的捷径。

参考资料

  1. Spring AI官方文档
  2. Spring AI GitHub仓库
  3. OpenAI API文档
  4. Spring Boot官方文档
  5. Spring WebFlux文档
  6. Resilience4j文档
  7. Micrometer文档
  8. Docker最佳实践
  9. Spring Security文档
  10. 向量数据库比较