AI 服务化是指将原本只能本地运行的 AI 能力转化为可远程调用的接口服务,使更多人能够便捷地访问 AI 能力。通过本节学习,你将掌握如何将 AI 智能体转变为可供他人调用的服务
具体内容包括:
在开始之前,先给大家提个醒,Spring AI 版本更新飞快,有些代码的写法随时可能失效,尽量以 官方文档 为准。
我们平时开发的大多数接口都是同步接口,也就是等后端处理完再返回。但是对于 AI 应用,特别是响应时间较长的对话类应用,可能会让用户失去耐心等待,因此推荐使用 SSE(Server-Sent Events)技术实现实时流式输出,类似打字机效果,大幅提升用户体验。
接下来我们会同时提供同步接口(一次性完整返回)和基于 SSE 的流式输出接口。
开发
1、支持流式调用
首先,我们需要为 InterviewAPP 添加流式调用方法,通过 stream 方法就可以返回 Flux 响应式对象了:
public Flux
doChatByStream(String message, String chatId) {
GPT plus 代充 只需 145return chatClient .prompt() .user(message) .advisors(spec -> spec.param(CHAT_MEMORY_CONVERSATION_ID_KEY, chatId) .param(CHAT_MEMORY_RETRIEVE_SIZE_KEY, 10)) .stream() .content();
}
💡 建议不要直接使用 ChatResponse 作为返回类型,因为这会导致返回内容膨胀,影响传输效率。所以上述代码中我们使用 content 方法,只返回 AI 输出的文本信息。
2、开发同步接口
在 controller 包下新建 AiController,将所有的接口都写在这个文件内。
先编写一个同步接口:
@RestController@RequestMapping("/ai")public class AiController {
GPT plus 代充 只需 145@Resourceprivate InterviewAPP interviewAPP; @Resourceprivate ToolCallback[] allTools; @Resourceprivate ChatModel dashscopeChatModel; @GetMapping("/interview_app/chat/sync")public String doChatWithInterviewAPPSync(String message, String chatId) { return interviewAPP.doChat(message, chatId); }
}
3、开发 SSE 流式接口
然后编写基于 SSE 的流式输出接口,有几种常见的实现方式:
1) 返回 Flux 响应式对象,并且添加 SSE 对应的 MediaType:
@GetMapping(value = "/interview_app/chat/sse", produces = MediaType.TEXT_EVENT_STREAM_VALUE) public Flux
doChatWithInterviewAPPSSE(String message, String chatId) {
GPT plus 代充 只需 145return interviewAPP.doChatByStream(message, chatId);
}
2)返回 Flux 对象,并且设置泛型为 ServerSentEvent。使用这种方式可以省略 MediaType:
@GetMapping(value = "/interview_app/chat/sse")public Flux
> doChatWithInterviewAPPSSE(String message, String chatId) {
GPT plus 代充 只需 145return interviewAPP.doChatByStream(message, chatId) .map(chunk -> ServerSentEvent.
builder() .data(chunk) .build());
}
3)使用 SSEEmiter,通过 send 方法持续向 SseEmitter 发送消息(有点像 IO 操作):
@GetMapping("/interview_app/chat/sse/emitter")public SseEmitter doChatWithInterviewAPPSseEmitter(String message, String chatId) {
GPT plus 代充 只需 145// 创建一个超时时间较长的 SseEmitterSseEmitter emitter = new SseEmitter(L); // 3分钟超时// 获取 Flux 数据流并直接订阅 interviewAPP.doChatByStream(message, chatId) .subscribe( // 处理每条消息 chunk -> { try { emitter.send(chunk); } catch (IOException e) { emitter.completeWithError(e); } }, // 处理错误 emitter::completeWithError, // 处理完成 emitter::complete ); // 返回emitterreturn emitter;
}
测试接口
开发完成后,我们可以通过 Swagger 接口文档来测试接口功能、验证会话上下文是否正常工作。但是,浏览器控制台可能无法实时查看 SSE 返回的内容,这时我们不妨使用 CURL 工具进行测试。
一般 Linux 和 Mac 系统自带了 CURL 工具,打开终端,输入下列命令:
控制台会持续不断地输出文本片段
💡 在浏览器 F12 控制台中,可以直接选中网络请求来复制 CURL 命令,非常便于测试
当然,如果你无法使用 CURL,也可以使用 IDEA 自带的 HTTP Client 工具进行测试。点击接口旁边的绿豆就能自动生成测试代码:
由于智能体执行过程通常包含多个步骤,执行时间较长,使用同步方法会导致用户体验不佳。因此,我们采用 SSE 技术将智能体的推理过程实时分步输出给用户。
开发
1)首先在 BaseAgent 类中添加流式输出方法:
- 运行代理(流式输出) *
- @param userPrompt 用户提示词
- @return SseEmitter实例 */public SseEmitter runStream(String userPrompt)
if (StringUtil.isBlank(userPrompt)) { emitter.send("错误:不能使用空提示词运行代理"); emitter.complete(); return; } // 更改状态 state = AgentState.RUNNING; // 记录消息上下文 messageList.add(new UserMessage(userPrompt)); try { for (int i = 0; i < maxSteps && state != AgentState.FINISHED; i++) { int stepNumber = i + 1; currentStep = stepNumber; log.info("Executing step " + stepNumber + "/" + maxSteps); // 单步执行String stepResult = step(); String result = "Step " + stepNumber + ": " + stepResult; // 发送每一步的结果 emitter.send(result); } // 检查是否超出步骤限制if (currentStep >= maxSteps) { state = AgentState.FINISHED; emitter.send("执行结束: 达到最大步骤 (" + maxSteps + ")"); } // 正常完成 emitter.complete(); } catch (Exception e) catch (Exception ex) { emitter.completeWithError(ex); } } finally { // 清理资源this.cleanup(); } } catch (Exception e) { emitter.completeWithError(e); }});
// 设置超时和完成回调 emitter.onTimeout(() -> {
GPT plus 代充 只需 145
this.state = AgentState.ERROR; this.cleanup(); log.warn("SSE connection timed out");});
emitter.onCompletion(() ->
this.cleanup(); log.info("SSE connection completed");});
return emitter; }
上述代码虽然看着很复杂,但是大部分都是在原有 run 方法的基础上进行改造,补充给 SseEmitter 推送消息的代码。
注意,上述代码中使用
CompletableFuture.runAsync()实现非阻塞式异步执行,否则会长时间占用 Web 服务器线程池资源。2)在
AiController中编写新的接口,注意每次对话都要创建一个新的实例:GPT plus 代充 只需 145
@Resourceprivate ToolCallback[] allTools;
@Resourceprivate ChatModel dashscopeChatModel;
/
- 流式调用 Manus 超级智能体 *
- @param message
- @return */@GetMapping("/manus/chat")public SseEmitter doChatWithManus(String message) { YuManus yuManus = new YuManus(allTools, dashscopeChatModel); return yuManus.runStream(message); }
测试接口
跟前面一样,使用 CURL 工具进行测试,效果如图:
后端支持跨域
为了让前端项目能够顺利调用后端接口,我们需要在后端配置跨域支持。在 config 包下创建跨域配置类,代码如下:
/ - 全局跨域配置 */@Configurationpublic class CorsConfig implements WebMvcConfigurer {
@Overridepublic void addCorsMappings(CorsRegistry registry) {
GPT plus 代充 只需 145
// 覆盖所有请求 registry.addMapping("/") // 允许发送 Cookie .allowCredentials(true) // 放行哪些域名(必须用 patterns,否则 * 会和 allowCredentials 冲突) .allowedOriginPatterns("*") .allowedMethods("GET", "POST", "PUT", "DELETE", "OPTIONS") .allowedHeaders("*") .exposedHeaders("*");} }
注意,如果
.allowedOrigins("*")与.allowCredentials(true)同时配置会导致冲突,因为出于安全考虑,跨域请求不能同时允许所有域名访问和发送认证信息(比如 Cookie)。
版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容,请联系我们,一经查实,本站将立刻删除。
如需转载请保留出处:https://51itzy.com/kjqy/243354.html