Spring AI + ZhiPu AI 实战:5分钟搞定智能对话机器人(附完整代码)
从零到一:基于Spring AI与智谱AI构建企业级智能对话服务实战指南
在当今这个技术快速迭代的时代,将大型语言模型(LLM)的能力无缝集成到现有应用系统中,已成为众多开发者面临的核心挑战。面对复杂的API调用、上下文管理、流式响应和函数调用等需求,一个成熟、高效的开发框架能显著降低集成门槛,加速产品落地。Spring AI正是为此而生的利器,它并非一个独立的AI模型,而是一个旨在简化AI应用开发的Spring生态扩展。它提供了一套标准化的抽象接口,让开发者能以熟悉的Spring方式,轻松对接包括智谱AI在内的多种主流大模型服务,将重心从繁琐的底层通信转移到业务逻辑的创新上。
本文面向有一定Spring Boot开发经验,希望快速、稳健地将智能对话能力嵌入到Java应用中的工程师。我们将摒弃枯燥的理论罗列和API文档复述,直接切入实战,通过一个完整的项目案例,手把手带你完成从环境搭建、服务集成、功能增强到生产级部署的全过程。你会发现,借助Spring AI的“约定大于配置”理念和智谱AI强大的模型能力,在5分钟内启动一个对话机器人原型只是起点,构建一个健壮、可扩展的企业级智能服务才是我们的目标。
1. 项目初始化与环境配置
在开始编码之前,我们需要一个干净的项目基础。Spring AI目前仍处于快速发展的里程碑(Milestone)阶段,因此依赖管理是第一步,也是避免后续版本冲突的关键。
1.1 创建Spring Boot项目与依赖管理
推荐使用 Spring Initializr 生成项目骨架。选择 Gradle (Groovy) 或 Maven 作为构建工具,语言为 Java,Spring Boot版本建议选择 3.2.x 或更高。除了基础的 Spring Web 依赖,我们还需要手动添加Spring AI的物料清单(BOM)和智谱AI的Starter。
对于Maven项目,在你的 pom.xml 中,首先添加Spring AI的仓库和BOM:
<project>
...
<repositories>
<repository>
<id>spring-milestones</id>
<name>Spring Milestones</name>
<url>https://repo.spring.io/milestone</url>
<snapshots>
<enabled>false</enabled>
</snapshots>
</repository>
</repositories>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.ai</groupId>
<artifactId>spring-ai-bom</artifactId>
<version>0.8.1</version> <!-- 请使用最新稳定版本 -->
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
...
</project>
然后,在 <dependencies> 部分添加核心依赖:
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!-- Spring AI 智谱AI集成 -->
<dependency>
<groupId>org.springframework.ai</groupId>
<artifactId>spring-ai-zhipuai-spring-boot-starter</artifactId>
</dependency>
<!-- 开发工具,可选但推荐 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<scope>runtime</scope>
<optional>true</optional>
</dependency>
</dependencies>
对于Gradle项目,在 build.gradle 中进行类似配置:
repositories {
mavenCentral()
maven { url 'https://repo.spring.io/milestone' }
}
dependencyManagement {
imports {
mavenBom "org.springframework.ai:spring-ai-bom:0.8.1"
}
}
dependencies {
implementation 'org.springframework.boot:spring-boot-starter-web'
implementation 'org.springframework.ai:spring-ai-zhipuai-spring-boot-starter'
developmentOnly 'org.springframework.boot:spring-boot-devtools'
}
注意:Spring AI版本更新较快,请务必访问其官方GitHub仓库或文档,确认当前最新的稳定版本号进行替换。
1.2 获取并安全配置智谱AI API Key
一切服务调用的前提是身份认证。你需要前往智谱AI开放平台注册账号并创建API Key。
- 访问智谱AI开放平台官网,完成注册与实名认证。
- 进入控制台,在“API密钥”管理页面,创建一个新的密钥。
- 至关重要的一步:安全地管理这个密钥。 绝对不要将它硬编码在源码中或提交到版本控制系统(如Git)。
Spring Boot提供了多种优雅的配置方式,优先级从高到低如下:
- 操作系统环境变量:最安全,适用于生产环境。
# Linux/macOS export SPRING_AI_ZHIPUAI_API_KEY=your_api_key_here # Windows (PowerShell) $env:SPRING_AI_ZHIPUAI_API_KEY="your_api_key_here" - 应用启动参数:
java -jar your-app.jar --spring.ai.zhipuai.api-key=your_api_key_here - 配置文件(
application.yml或application.properties):适用于本地开发,但务必确保配置文件不被提交。
或者使用# application.yml 示例 spring: ai: zhipuai: api-key: ${ZHIPU_API_KEY:} # 推荐:引用环境变量,本地可设默认值 chat: options: model: glm-3-turbo temperature: 0.7application.properties:spring.ai.zhipuai.api-key=${ZHIPU_API_KEY:} spring.ai.zhipuai.chat.options.model=glm-3-turbo spring.ai.zhipuai.chat.options.temperature=0.7
在本地开发时,我习惯在 ~/.bashrc 或 ~/.zshrc 中设置环境变量,并在IDE的运行配置里注入。对于团队协作,可以使用 .env 文件配合 spring-dotenv 这样的库,但记得将 .env 加入 .gitignore。
2. 核心对话服务构建与接口设计
环境就绪后,我们开始编写业务代码。Spring AI的自动配置(Auto-Configuration)会为我们创建好 ZhiPuAiChatModel 的Bean,直接注入使用即可。
2.1 注入ChatModel与基础对话接口
首先,创建一个简单的REST控制器来暴露对话接口。这里我们会实现一个同步响应和一个流式响应的端点,以满足不同场景的需求。
import org.springframework.ai.chat.ChatResponse;
import org.springframework.ai.chat.messages.UserMessage;
import org.springframework.ai.chat.prompt.Prompt;
import org.springframework.ai.zhipuai.ZhiPuAiChatModel;
import org.springframework.web.bind.annotation.*;
import reactor.core.publisher.Flux;
@RestController
@RequestMapping("/api/chat")
public class ChatController {
private final ZhiPuAiChatModel chatModel;
// 构造器注入,推荐方式
public ChatController(ZhiPuAiChatModel chatModel) {
this.chatModel = chatModel;
}
/**
* 同步对话接口
* @param message 用户输入
* @return 完整的AI回复
*/
@GetMapping("/completion")
public ChatCompletionResponse generate(@RequestParam String message) {
// 调用ChatModel,传入用户消息
ChatResponse response = chatModel.call(message);
String content = response.getResult().getOutput().getContent();
return new ChatCompletionResponse(content);
}
/**
* 流式对话接口 (Server-Sent Events)
* @param message 用户输入
* @return 流式的文本块
*/
@GetMapping(value = "/stream", produces = "text/event-stream")
public Flux<String> generateStream(@RequestParam String message) {
Prompt prompt = new Prompt(new UserMessage(message));
return chatModel.stream(prompt)
.map(chunk -> chunk.getResult().getOutput().getContent())
.filter(text -> text != null && !text.isEmpty());
}
// 简单的响应DTO
public record ChatCompletionResponse(String reply) {}
}
启动应用,访问 http://localhost:8080/api/chat/completion?message=你好,请介绍一下你自己,你应该能立刻收到智谱AI模型的回复。流式接口则可以通过前端使用 EventSource 来连接,体验逐词输出的效果。
2.2 深入理解与配置ChatOptions
基础的调用已经实现,但一个健壮的服务需要更精细的控制。ZhiPuAiChatOptions 封装了所有与模型行为相关的参数。我们可以在配置文件进行全局默认设置,也可以在运行时针对每次请求进行动态覆盖。
全局配置(application.yml)示例:
spring:
ai:
zhipuai:
api-key: ${ZHIPU_API_KEY:}
chat:
options:
model: glm-4 # 使用GLM-4模型
temperature: 0.3 # 降低随机性,使输出更确定、专业
max-tokens: 1024 # 限制单次回复的最大长度
top-p: 0.9 # 核采样参数,与temperature二选一调整
frequency-penalty: 0.5 # 降低重复用词的概率
presence-penalty: 0.3 # 鼓励谈论新话题
运行时动态选项覆盖:
有时,我们需要根据用户请求的上下文调整参数。例如,在创意写作场景提高 temperature,在代码生成场景降低它。
@PostMapping("/advanced")
public ChatCompletionResponse generateWithOptions(@RequestBody AdvancedChatRequest request) {
// 构建动态选项
ZhiPuAiChatOptions runtimeOptions = ZhiPuAiChatOptions.builder()
.withTemperature(request.creative() ? 0.9f : 0.2f) // 根据创意标志调整
.withMaxTokens(request.maxLength())
.withModel("glm-3-turbo") // 临时切换模型
.build();
// 将动态选项注入Prompt
Prompt prompt = new Prompt(
new UserMessage(request.message()),
runtimeOptions // 这里传入的选项会覆盖全局配置
);
ChatResponse response = chatModel.call(prompt);
return new ChatCompletionResponse(response.getResult().getOutput().getContent());
}
public record AdvancedChatRequest(String message, boolean creative, int maxLength) {}
通过这种方式,我们可以为不同的功能模块(如客服、内容生成、代码助手)定义不同的参数模板,实现灵活的模型行为管理。
3. 实现高级功能:函数调用(Function Calling)
函数调用是大模型与外部世界交互的桥梁,也是构建智能助理类应用的核心。Spring AI极大地简化了这一过程的实现。其核心思想是:你定义好Java函数(工具),并描述其功能,模型在对话中会根据需要决定是否调用以及传入什么参数,然后将执行结果返回给模型,由模型组织最终的自然语言回复给用户。
3.1 定义工具函数
假设我们要为聊天机器人添加一个查询天气的能力。首先,我们定义一个服务类作为“工具”。
import com.fasterxml.jackson.annotation.JsonClassDescription;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.annotation.JsonPropertyDescription;
// 请求参数记录
@JsonClassDescription("根据城市名称查询该城市的实时天气信息") // 关键:用于生成函数描述
public record WeatherRequest(
@JsonProperty(required = true)
@JsonPropertyDescription("城市的名称,例如:北京、上海、San Francisco")
String location,
@JsonPropertyDescription("温度单位,C代表摄氏度,F代表华氏度")
Unit unit
) {
public enum Unit { C, F }
}
// 响应记录
public record WeatherResponse(double temperature, WeatherRequest.Unit unit, String condition) {}
// 工具函数实现
@Service
public class WeatherService implements Function<WeatherRequest, WeatherResponse> {
// 模拟一个第三方天气API的调用
@Override
public WeatherResponse apply(WeatherRequest request) {
// 这里应该是真实的HTTP调用,例如调用和风天气、OpenWeatherMap等API
// 为了演示,我们返回模拟数据
double temp = switch (request.location().toLowerCase()) {
case "北京" -> 22.5;
case "上海" -> 25.0;
case "旧金山", "san francisco" -> 18.0;
default -> 20.0 + (Math.random() * 10); // 随机温度
};
String condition = switch ((int) (Math.random() * 4)) {
case 0 -> "晴天";
case 1 -> "多云";
case 2 -> "小雨";
default -> "阴天";
};
return new WeatherResponse(temp, request.unit(), condition);
}
}
注意 @JsonClassDescription 和 @JsonPropertyDescription 注解,它们为模型提供了关于函数功能和参数含义的清晰描述,是函数能否被正确调用的关键。
3.2 注册函数并启用调用
接下来,我们需要将这个工具函数注册到Spring上下文中,并让 ChatModel 知道它的存在。
方法一:通过 @Bean 定义自动注册(推荐)
在配置类中,将我们的 WeatherService 声明为一个Bean。Spring AI会自动扫描并包装它。
@Configuration
public class FunctionConfig {
@Bean
@Description("获取指定城市的当前天气情况") // 另一种提供函数描述的方式
public Function<WeatherRequest, WeatherResponse> weatherFunction(WeatherService weatherService) {
return weatherService; // 直接返回服务实例
}
}
方法二:在请求中动态注册(更灵活)
对于需要根据上下文动态选择工具的场景,可以在每次请求的 Prompt 中指定。
@PostMapping("/chat-with-weather")
public Flux<String> chatWithFunction(@RequestBody UserQuery query) {
// 1. 创建函数回调包装器
FunctionCallback weatherCallback = FunctionCallbackWrapper.builder()
.withName("getCurrentWeather") // 模型调用时使用的函数名
.withDescription("获取指定城市的当前天气情况")
.withResponseConverter((response) ->
String.format("温度 %.1f°%s, 天气状况: %s",
response.temperature(),
response.unit().name(),
response.condition())
) // 将响应对象转换为模型可读的文本
.withFunction(new WeatherService()) // 实际的函数
.build();
// 2. 构建包含函数调用的选项
ZhiPuAiChatOptions options = ZhiPuAiChatOptions.builder()
.withFunctionCallbacks(List.of(weatherCallback)) // 注册回调
.build();
// 3. 创建Prompt并调用
Prompt prompt = new Prompt(new UserMessage(query.message()), options);
return chatModel.stream(prompt)
.map(chunk -> chunk.getResult().getOutput().getContent());
}
现在,当你向 /chat-with-weather 发送消息“北京今天天气怎么样?”时,模型会识别出查询天气的意图,自动调用 getCurrentWeather 函数,并将 location 参数设为“北京”。函数执行后返回的天气文本会被模型接收,并最终生成类似“北京今天温度22.5°C,天气晴朗,适合外出。”的自然语言回复。整个过程对前端用户是完全透明的,他们感知到的只是一个更智能、能获取实时信息的对话机器人。
4. 生产环境考量与最佳实践
让服务在本地运行起来只是第一步,要将其部署到生产环境,还需要考虑稳定性、可观测性和性能。
4.1 配置重试与连接管理
网络波动和API限流是线上服务的常客。Spring AI内置了基于Spring Retry的重试机制,我们可以通过配置来增强服务的韧性。
spring:
ai:
retry:
max-attempts: 3 # 最大重试次数(含首次调用)
backoff:
initial-interval: 1000ms # 首次重试间隔
multiplier: 2.0 # 间隔乘数(指数退避)
max-interval: 10s # 最大重试间隔
on-client-errors: false # 4xx错误不重试(如认证失败、参数错误)
exclude-on-http-codes: 429, 401 # 针对特定HTTP状态码不重试(限流、未授权)
同时,配置HTTP客户端连接池和超时设置也至关重要,这通常通过底层的 RestClient 或 WebClient 配置实现。虽然Spring AI Starter可能提供了默认配置,但在高并发场景下需要调整。
@Configuration
public class RestClientConfig {
@Bean
public RestClient.Builder restClientBuilder() {
return RestClient.builder()
.requestFactory(new HttpComponentsClientHttpRequestFactory())
.requestInterceptor((request, body, execution) -> {
// 可以在这里添加统一的请求头,如User-Agent
request.getHeaders().set("X-Custom-Source", "spring-ai-app");
return execution.execute(request, body);
});
}
}
// 注意:Spring AI内部使用的客户端配置方式可能因版本而异,需查阅对应文档。
4.2 日志、监控与异常处理
清晰的日志是调试和监控的基石。确保为 org.springframework.ai 包设置 DEBUG 或 TRACE 级别日志,以查看详细的请求和响应信息(注意生产环境慎用,避免日志泛滥)。
logging:
level:
org.springframework.ai: DEBUG
org.springframework.ai.zhipuai: DEBUG
全局异常处理能提供友好的错误信息,避免将内部细节暴露给客户端。
@RestControllerAdvice
public class GlobalExceptionHandler {
@ExceptionHandler(AiClientException.class) // Spring AI定义的客户端异常
public ResponseEntity<ErrorResponse> handleAiClientException(AiClientException e) {
// 记录错误日志
log.error("AI服务调用失败: {}", e.getMessage(), e);
// 返回结构化的错误信息
return ResponseEntity.status(HttpStatus.BAD_GATEWAY)
.body(new ErrorResponse("AI_SERVICE_UNAVAILABLE", "智能服务暂时不可用,请稍后重试"));
}
@ExceptionHandler(Exception.class)
public ResponseEntity<ErrorResponse> handleGenericException(Exception e) {
log.error("系统内部错误: ", e);
return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR)
.body(new ErrorResponse("INTERNAL_ERROR", "系统内部错误"));
}
public record ErrorResponse(String code, String message) {}
}
4.3 性能优化与成本控制
使用大模型API会产生费用,优化调用是控制成本的关键。
- 缓存策略:对于常见、结果变化不频繁的查询(如“什么是Spring Boot?”),可以在应用层或使用Redis等缓存中间件缓存模型的回复。注意设置合理的TTL(生存时间)。
- 上下文长度管理:智谱AI模型有上下文窗口限制。在构建多轮对话系统时,需要设计策略来维护或摘要历史消息,避免无限增长导致API调用失败或成本激增。可以考虑只保留最近N轮对话,或使用更小的模型对长历史进行摘要。
- 异步与非阻塞:对于流式响应或耗时较长的复杂推理,务必使用
chatModel.stream()并配合Spring WebFlux(如本例中的Flux)实现非阻塞响应,避免占用宝贵的Servlet容器线程。 - 参数调优:如前所述,合理设置
max-tokens可以防止生成过长内容,temperature和top-p的调整也能影响生成质量和可预测性。
走到这里,你已经拥有了一个功能完整、具备生产潜力的智能对话服务后端。从项目初始化、依赖注入、基础对话到高级的函数调用,我们一步步拆解了集成过程中的关键环节。Spring AI的抽象层让我们几乎不用关心HTTP请求的细节,而智谱AI强大的模型能力则为应用提供了坚实的智能内核。
在实际部署时,记得将API密钥等敏感信息移入环境变量或专业的密钥管理服务(如HashiCorp Vault、AWS Secrets Manager)。对于更复杂的场景,如多模型路由、A/B测试、复杂的对话状态管理,你可以考虑在 ChatModel 之上再封装一层服务层,实现更灵活的业务逻辑。技术的价值在于解决实际问题,现在,你可以基于这个坚实的起点,去构建真正改变用户体验的智能应用了。
DAMO开发者矩阵,由阿里巴巴达摩院和中国互联网协会联合发起,致力于探讨最前沿的技术趋势与应用成果,搭建高质量的交流与分享平台,推动技术创新与产业应用链接,围绕“人工智能与新型计算”构建开放共享的开发者生态。
更多推荐



所有评论(0)