基于Spring Boot实现OpenAI Assistants UI助手调用外部API的方法
基于UI创建的OpenAI助手实现Spring Boot函数调用方案
核心逻辑梳理
OpenAI助手不会直接调用你的外部API,全程通过Spring Boot中间件中转完成:
- 用户发送消息到你的Spring Boot服务
- 服务向OpenAI线程添加用户消息并触发助手运行
- 助手判断需要调用函数时,返回
requires_action状态,附带要调用的函数名和参数 - 你的Spring Boot服务执行对应函数(调用外部API)并获取结果
- 服务把函数结果提交给助手,让助手继续运行
- 助手基于函数结果生成最终回复,服务获取后返回给用户
步骤1:确认UI中函数定义的正确性
- 函数名称必须和Spring Boot中实现的函数名完全一致(大小写敏感)
- 参数必须严格遵循JSON Schema规范,示例如下(以天气查询函数为例):
{ "name": "get_current_weather", "description": "获取指定城市的当前天气信息", "parameters": { "type": "object", "properties": { "city": { "type": "string", "description": "城市名称,例如北京、上海" } }, "required": ["city"] } }
- 注意:UI里的函数不需要定义任何端点,端点是Spring Boot服务内部处理的逻辑,OpenAI只负责传递调用指令
步骤2:修改Spring Boot的运行处理逻辑
你之前的流程是创建运行后直接等待完成,现在需要加入状态判断和函数调用处理逻辑:
关键代码示例(基于OpenAI Java SDK)
import com.theokanning.openai.OpenAiService; import com.theokanning.openai.assistants.*; import java.util.List; import java.util.Map; import java.util.concurrent.TimeUnit; public class AssistantFunctionService { private final OpenAiService openAiService; // 函数映射:将UI中定义的函数名绑定到具体实现 private final Map<String, FunctionExecutor> functionMap = Map.of( "get_current_weather", this::invokeWeatherApi ); public AssistantFunctionService(String openAiApiKey) { this.openAiService = new OpenAiService(openAiApiKey, 60, TimeUnit.SECONDS); } public String handleUserQuery(String threadId, String userInput) { // 1. 向线程添加用户消息 openAiService.createMessage(threadId, MessageCreateRequest.builder() .role(Role.USER) .content(userInput) .build()); // 2. 创建助手运行实例 RunCreateRequest runReq = RunCreateRequest.builder() .assistantId("你的UI创建的助手ID") .build(); Run currentRun = openAiService.createRun(threadId, runReq); // 3. 轮询运行状态,处理函数调用或等待完成 while (true) { currentRun = openAiService.getRun(threadId, currentRun.getId()); if (RunStatus.COMPLETED.equals(currentRun.getStatus())) { break; } else if (RunStatus.REQUIRES_ACTION.equals(currentRun.getStatus())) { // 处理函数调用请求 currentRun = processFunctionCall(threadId, currentRun); } // 设置合理轮询间隔,避免API限流或无意义等待 try { Thread.sleep(1200); } catch (InterruptedException e) { Thread.currentThread().interrupt(); throw new RuntimeException("运行状态轮询被中断", e); } } // 4. 获取助手最终回复 List<Message> messages = openAiService.listMessages(threadId).getData(); return messages.get(0).getContent().get(0).getText().getValue(); } private Run processFunctionCall(String threadId, Run run) { RequiredAction action = run.getRequiredAction(); List<ToolCall> toolCalls = action.getSubmitToolOutputs().getToolCalls(); // 生成工具输出列表 List<ToolOutput> toolOutputs = toolCalls.stream().map(call -> { String funcName = call.getFunction().getName(); FunctionExecutor executor = functionMap.get(funcName); if (executor == null) { throw new RuntimeException("未找到对应函数实现:" + funcName); } // 解析OpenAI传递的函数参数 Map<String, Object> params = call.getFunction().getParameters(); // 执行函数(调用外部API) String apiResult = executor.execute(params); // 封装工具输出 return ToolOutput.builder() .toolCallId(call.getId()) .output(apiResult) .build(); }).toList(); // 提交工具输出,让助手继续运行 return openAiService.submitToolOutputs(threadId, run.getId(), SubmitToolOutputsRequest.builder() .toolOutputs(toolOutputs) .build()); } // 具体函数实现:调用外部天气API private String invokeWeatherApi(Map<String, Object> params) { String city = (String) params.get("city"); // 这里替换为实际的外部API调用逻辑,用RestTemplate或WebClient实现 // 示例返回模拟数据 return String.format("{\"city\":\"%s\",\"temperature\":\"26℃\",\"weather\":\"多云\"}", city); } // 函数执行器接口 @FunctionalInterface private interface FunctionExecutor { String execute(Map<String, Object> parameters); } }
步骤3:常见问题排查
- 助手无响应/卡顿:
- 检查是否正确处理了
REQUIRES_ACTION状态,未处理的话运行会一直停滞在该状态 - 核对函数参数定义:UI中的参数结构和代码中解析的参数必须完全匹配
- 调整轮询间隔:间隔太短易触发API限流,太长会导致等待超时,建议1-2秒
- 检查是否正确处理了
- 助手不触发函数调用:
- 检查函数描述是否清晰:需要明确说明触发场景,比如“当用户询问城市天气时调用此函数”
- 确认用户输入符合函数触发条件,比如用户说“上海今天天气怎么样”才会触发天气查询函数
- 关于端点的误解:
- OpenAI助手不需要访问你的Spring Boot端点,所有函数调用都是中间件中转完成的:助手只传递调用指令,中间件负责执行并返回结果,无需在UI函数定义中填写任何端点信息
内容的提问来源于stack exchange,提问作者Mikhail Saveliev
相关产品推荐
相关产品推荐

