1. 项目概述:Spring AI与Function Call集成实战
作为一名长期深耕Java生态的开发者,最近在探索如何将大语言模型的能力深度整合到企业应用中。Spring AI作为Spring生态的新成员,为Java开发者提供了与主流AI模型交互的标准化方式。本文将重点分享如何通过Spring AI实现Function Call功能,让大语言模型具备调用本地方法的能力。
这个技术方案特别适合需要将AI能力与企业现有系统对接的场景。比如在客服系统中,当用户询问"我的订单状态如何?"时,模型可以自动调用订单查询接口返回实时数据。相比传统的关键词匹配方案,这种基于语义理解的智能路由更加灵活可靠。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 核心概念解析
2.1 Spring AI架构设计
Spring AI采用了典型的Spring生态设计哲学:
- 统一的编程模型:通过ChatClient接口抽象不同AI提供商的差异
- 自动配置:通过starter包简化集成过程
- 类型安全:POJO映射确保输入输出可维护性
- 扩展性:支持自定义函数调用和模型参数调优
其核心优势在于:
- 屏蔽了不同AI提供商API的差异
- 内置了重试、限流等企业级特性
- 与Spring生态无缝集成(如Spring Security、Spring Data)
2.2 Function Call工作机制
Function Call的本质是让大语言模型具备"工具使用"能力。其工作流程可分为三个阶段:
- 意图识别阶段:模型分析用户输入,判断是否需要调用外部工具
- 参数生成阶段:模型根据函数描述生成符合规范的JSON参数
- 结果整合阶段:将函数执行结果重新注入模型上下文,生成最终回复
这种机制突破了纯文本交互的限制,使模型可以:
- 查询实时数据(天气、股价等)
- 操作系统资源(创建工单、发送邮件等)
- 执行复杂计算(数据分析、公式求解等)
3. 环境准备与项目搭建
3.1 开发环境要求
推荐使用以下环境配置:
- JDK 17+(建议使用Amazon Corretto或Zulu发行版)
- Maven 3.8+(配置阿里云镜像加速依赖下载)
- IntelliJ IDEA(社区版即可,需安装Lombok插件)
注意:Spring AI 0.8.x版本与Spring Boot 3.2.x存在版本强依赖关系,混合使用其他版本可能导致兼容性问题。
3.2 项目初始化
使用Spring Initializr创建项目时需特别注意:
xml复制<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>3.2.3</version>
</parent>
核心依赖配置:
xml复制<dependencies>
<!-- Spring AI OpenAI集成 -->
<dependency>
<groupId>org.springframework.ai</groupId>
<artifactId>spring-ai-openai-spring-boot-starter</artifactId>
<version>0.8.0</version>
</dependency>
<!-- 工具类库 -->
<dependency>
<groupId>cn.hutool</groupId>
<artifactId>hutool-core</artifactId>
<version>5.8.24</version>
</dependency>
<!-- 开发辅助 -->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
</dependencies>
3.3 配置文件关键参数
application.yml中需要配置的关键参数:
yaml复制spring:
ai:
openai:
api-key: ${OPENAI_API_KEY} # 建议通过环境变量注入
chat:
options:
model: gpt-3.5-turbo-0613 # 必须使用支持function calling的模型
temperature: 0.4 # 控制输出随机性
max_tokens: 2000 # 限制响应长度
参数选择建议:
- 温度值(temperature):业务场景建议0.3-0.7,创意场景可提高到0.9
- 最大令牌数(max_tokens):根据业务需求调整,避免截断重要信息
4. 核心实现详解
4.1 函数定义最佳实践
WeatherService类实现要点:
java复制public class WeatherService implements Function<Request, Response> {
public record Request(
@JsonProperty(required = true, value = "location")
@JsonPropertyDescription("城市和省份,例如:广州")
String location) {}
public record Response(String weather, String advice) {}
@Override
public Response apply(Request request) {
String weather = fetchFromDatabase(request.location()); // 模拟数据库查询
String advice = generateAdvice(weather); // 生成穿衣建议
return new Response(weather, advice);
}
}
关键设计原则:
- 使用Java Record定义DTO,保持不可变性
- 通过Jackson注解增强元数据描述
- 函数实现应保持无状态和幂等性
- 添加业务逻辑扩展点(如生成穿衣建议)
4.2 函数注册机制
FunctionConfig类的核心作用:
java复制@Configuration
public class FunctionConfig {
@Bean
@Description("获取指定城市的当前天气状况和穿衣建议") // 增强函数描述
public FunctionCallback weatherFunction() {
return FunctionCallbackWrapper.builder(new WeatherService())
.withName("getCurrentWeather") // 函数标识符
.withDescription("查询实时天气数据") // 模型可见描述
.withResponseConverter(response -> {
// 可添加响应转换逻辑
return response;
})
.build();
}
}
注册时的注意事项:
- 函数名称需符合JavaScript命名规范(驼峰式)
- 描述信息应准确反映功能边界
- 可添加响应转换器处理特殊数据结构
4.3 控制器实现技巧
OpenaiTestController的优化实现:
java复制@RestController
@RequestMapping("/api/weather")
public class WeatherController {
@Autowired
private ChatClient chatClient;
@GetMapping
public String queryWeather(@RequestParam String city) {
SystemMessage systemMsg = new SystemMessage("""
你是一位天气助手,专门回答与天气相关的问题。
当用户询问天气时,你需要调用getCurrentWeather函数获取数据。
回答时应包含温度、天气状况和穿衣建议。""");
UserMessage userMsg = new UserMessage(city + "的天气怎么样?");
ChatOptions options = OpenAiChatOptions.builder()
.withFunction("getCurrentWeather")
.withTemperature(0.3)
.build();
Prompt prompt = new Prompt(List.of(systemMsg, userMsg), options);
return chatClient.call(prompt)
.getResults()
.stream()
.map(Generation::getOutput)
.map(AssistantMessage::getContent)
.collect(Collectors.joining());
}
}
工程化建议:
- 使用清晰的系统提示引导模型行为
- 将聊天选项提取为可配置参数
- 添加输入参数校验和异常处理
- 考虑添加缓存机制减少函数调用次数
5. 进阶应用与问题排查
5.1 多函数组合调用
实际业务中经常需要组合多个函数:
java复制ChatOptions options = OpenAiChatOptions.builder()
.withFunctions("getWeather", "getTraffic", "suggestAttractions")
.build();
模型会根据用户意图自动选择调用哪些函数。例如询问"周末去广州玩天气和交通怎么样?"可能同时触发天气和交通查询。
5.2 常见问题解决方案
问题1:函数未被调用
- 检查模型是否支持function calling(gpt-3.5-turbo-0613及以上)
- 验证函数描述是否清晰完整
- 确保系统提示中引导模型使用函数
问题2:参数解析失败
- 检查DTO类JsonProperty注解配置
- 验证参数是否标记为required
- 使用@JsonPropertyDescription增强参数描述
问题3:响应超时
- 调整max_tokens参数
- 为函数实现添加超时控制
- 考虑异步处理模式
5.3 性能优化技巧
- 批量处理:对多个相关查询合并函数调用
- 缓存策略:对时效性不高的数据添加缓存层
- 流式响应:虽然不支持函数调用,但可以用于最终结果输出
- 连接池配置:调整Spring WebClient的连接参数
6. 扩展应用场景
6.1 企业知识库问答
java复制public class KnowledgeBaseService implements Function<QueryRequest, QueryResponse> {
public record QueryRequest(String question, String department) {}
@Override
public QueryResponse apply(QueryRequest request) {
// 连接企业知识库系统
// 根据部门过滤结果
return new QueryResponse(searchResults);
}
}
6.2 智能表单填写
java复制public class FormService implements Function<FormRequest, FormResponse> {
public record FormRequest(String formType, Map<String, Object> context) {}
@Override
public FormResponse apply(FormRequest request) {
// 根据表单类型预填字段
// 结合上下文自动补全信息
return new FormResponse(prefilledForm);
}
}
6.3 数据分析报告生成
java复制public class AnalyticsService implements Function<ReportRequest, ReportResponse> {
public record ReportRequest(LocalDate start, LocalDate end, String metrics) {}
@Override
public ReportResponse apply(ReportRequest request) {
// 查询数据仓库
// 生成可视化图表
return new ReportResponse(analysisResult);
}
}
在实际项目中,我们通过这种模式将CRM系统的客户数据分析效率提升了60%,关键是通过自然语言交互降低了业务人员的使用门槛。
