AI对话应用接口开发全解析:同步接口+SSE流式+智能体+前端对接

AI对话应用接口开发全解析:同步接口+SSE流式+智能体+前端对接

AI对话应用接口开发

我们平时开发的大多数都是同步接口,也就是扥后端处理完再返回。但是对于AI应用,特别是响应时间较长的应用,可能会让用户失去耐心等待,因此推荐使用SSE技术实现实时流式输出,类似打字机效果,大幅度提升用户体验

开发AI对话同步接口

接下来我们先开发AI同步接口,对比学习。首先我们编写一个与"科泰旅游大师"对话的接口,使用常规同步的方式获得对话结果。

在controller包中新建ChatWithAIController,如下图所示:

编写同步接口:

 @RestController @RequestMapping("/ai") public class ChatWithAIController { // 注入TravelApp实例 @Resource private TravelApp travelApp; /* * 前端可以通过此方法获得一个ID * */ @GetMapping("/chat/new") public String newChat(){ return UUID.randomUUID().toString(); } // 与AI聊天(同步) @GetMapping("/chat/travel/sync") public String chat(String chatId,String message){ return travelApp.chat(chatId,message); } } 

通过/chat/new接口,让前端利用UUID生成会话ID,避免A用户能够通过猜测会话ID获取用户B的信息

同步接口代码也非常简单,直接使用注入的TravelApp实例对用户的问题进行回复

测试同步接口

启动springBoot项目,浏览器打开Swagger页面测试

浏览器打开http://localhost:8080/api/doc.html

获取到ChatId后测试/api/ai/chat/travel/sync时会出现很长的时间进度提示

开发AI对话SSE流式接口

Server-Sent Events(SSE,服务器推送事件)

SSE 是 HTML5 定义的基于 HTTP 的单向实时数据推送技术,客户端通过EventSource API 发起长连接,服务器以文本流持续推送事件,适配实时通知、行情更新等场景。

  1. 核心特性
    • 单向通信:仅服务器向客户端推送数据,客户端反馈需另发 HTTP 请求。
    • HTTP 原生支持:基于 HTTP/1.1 分块传输编码,无需协议升级,易穿透代理与防火墙。
    • 自动重连:浏览器内置重连机制,默认 3 秒重试,可通过retry字段自定义。
    • 轻量协议:文本流格式,支持dataeventidretry等字段,开销低。
  2. 应用场景:实时通知、股票行情、系统日志流、进度更新、IoT 状态推送等单向推送场景。

让TravelApp应用支持流式输出

要让接口支持流式输出,首先我们的TravelApp也不能使用同步的call()+chatResponse()方法返回String类型的全部内容

需要使用到Flux响应式对象进行异步操作。在TravelApp类增加一个StreamChat方法,(SpringAI系列的文章都是围绕一个项目写没有的内容在前面几期)

 /* * 与AI大模型流式对话接口 * @param chatId 会话ID * @param message 用户输入 * @return AI模型返回结果 * */ public Flux<String> streamChat(String chatId, String message){ FunctionCallback[] mcpTools = toolCallbackProvider.getToolCallbacks(); Flux<String> result = chatClient.prompt() .user(message) .advisors(spec -> spec .param(CHAT_MEMORY_CONVERSATION_ID_KEY,chatId) .param(CHAT_MEMORY_RETRIEVE_SIZE_KEY,10) ) .tools(aiTools) .tools(McpToolCallbackProxy.proxyAll(mcpTools)) .toolContext(Map.of("chatId",chatId)) .stream().content(); // log.info("chatResponse: {}",chatResponse); return result; }

实现SSE流式接口

实现流式接口一共有三种方式

1.直接返回Flux对象,但是必须添加SSE对应的MediaType

修改ChatWithAIController,增加方法chatWithTravelAppSEE_Flux:

 /* * SSE实现方式一:使用Flux * 必须设置produces为MediaType.TEXT_EVENT_STREAM_VALUE * */ @GetMapping(value = "/chat/travel/sse/flux",produces = MediaType.TEXT_EVENT_STREAM_VALUE) public Flux<String> chatWithTravelAppSEE_Flux(String chatId, String message){ return travelApp.streamChat(chatId,message); }

2.返回Flux对象,并设置泛型为ServerSentEvent

这种方式可省略MediaType的设置,但是要对String进行转化

 /* * SSE实现方式二:使用ServerSentEvent泛型 * */ @GetMapping("/chat/travel/sse/generic") public Flux<ServerSentEvent<String>> chatWithTravelAppSEE_Generic(String chatId, String message){ return travelApp.streamChat(chatId,message) .map(result -> ServerSentEvent.builder(result).build()); }

3.使用SseEmiter类型,通过send方法持续向SseEmiter对象发送消息,这种方式代码会稍微大一些,但是通用性和可控制性更强

 // 实现方式三使用SseEmitter @GetMapping("/chat/travel/sse/emitter") public SseEmitter chatWithTravelAppSEE_Emitter(String chatId, String message){ // 创建SseEmitter对象,设置超时时间为5分钟 SseEmitter emitter = new SseEmitter(300000L); // 订阅流 travelApp.streamChat(chatId,message).subscribe( result -> { try { // 发送数据 emitter.send(result); } catch (Exception e) { emitter.completeWithError(e); } }, // 错误处理 error -> emitter.completeWithError(error), () -> emitter.complete() ); return emitter; }

AI智能体应用接口开发

改造智能体支持流式运行

那么首先类似于TravelApp增加流式对话StreamChat(),我们也需要改造智能体BaseAgent增加支持流式运行的streamRun()方法:

 /* * 增加流式输出的运行方法 * @param userPrompt 用户输入 * @return SseEmitter * */ public SseEmitter streamRun(String userPrompt){ SseEmitter emitter = new SseEmitter(300000L); //使用异步线程处理,避免线程阻塞 CompletableFuture.runAsync(() -> { try{ if (this.state != AgentState.IDLE){ emitter.send("当前Agent正在运行中,请勿重复运行!"); emitter.complete(); return; } if (StrUtil.isBlank(userPrompt)){ emitter.send("请输入用户提示词!"); emitter.complete(); return; } // 添加用户输入 chatMemory.add(chatId,new UserMessage(userPrompt)); this.state = AgentState.RUNNING; try{ for (int i = 0; i < maxSteps && state != AgentState.FINISHED; i++) { currentStep = i + 1; log.info("开始执行第{}步...",currentStep); String result = "Step " + currentStep + ": " + step(); emitter.send(result); } if(currentStep >= maxSteps){ state = AgentState.FINISHED; emitter.send("已执行最大步骤数,任务结束!,最大步数为"+maxSteps); } // 正常完成 emitter.complete(); } catch (IOException e) { state = AgentState.ERROR; try{ emitter.send("执行出错,请检查!"); emitter.complete(); } catch (IOException ex) { throw new RuntimeException(ex); } }finally { cleanResources(); } } catch (IOException e) { emitter.completeWithError(e); } }); // 设置超时回调 emitter.onTimeout(() -> { this.state = AgentState.ERROR; this.cleanResources(); log.warn("SSE连接超时,会话ID: {}", chatId); }); // 设置完成回调 emitter.onCompletion(() -> { if(state == AgentState.RUNNING){ this.state = AgentState.FINISHED; } this.cleanResources(); log.debug("SSE连接完成,会话ID: {}", chatId); }); return emitter; }

上述代码看上去很复杂,但是大部分都是在原有run方法上改造,将结果信息交给SseEmitter通过send方法推送

添加智能体异步接口

有了异步运行的方法,我们就可以添加智能体接口了,修改ChatWithAIController
式调用 ktTravelManus 智能体的接口,

 @Resource private ToolCallback[] toolCallbacks; @Resource private ChatModel dashscopeChatModel; /* * 流式调用智能体 * @param message * @return * */ @GetMapping("/chat/manus/sse") public SseEmitter chatWithManus(String message){ TravelManus travelManus = new TravelManus(toolCallbacks,dashscopeChatModel); travelManus.setMaxSteps(20); return travelManus.streamRun(message); }

为了保证每次请求都是一个新的TravelManus实例,我们通过自己new 来创建,那么构造函数必要的工具列表aiTools 和dashscopeChatModel对象就需要注入进来
当然我们也可以直接注入 TravelManus,然后 Controller 添加@Scope 注解值为request,这样每次请求都会创建一个新的控制器,由于KtTravelManus 的@Scope是prototype,也会注入一个新的KtTravelManus 实例。

测试智能体接口

使用ApiPost测试

重启SpringBoott项目在,ApiPost中新增如下接口测试

接口链接:http://localhost:8080/api/ai/chat/manus/sse

参考参数:message=我想在长沙旅游三天预算3000,帮我规划旅程,并安排附近公里的酒店,并生成PDF

前端项目的生成和对接

由于我们这个项目不需要复杂的前端页面,我们可以使用AI编程工具来快速生成前端代码,极大提高开发效率

创建前端项目

(写这个项目需要具备前端的一些基础知识)在vscode打开终端,输入npm create vite@latest使用vite去创建一个最新的vue版本的项目

接着我们编写提示词给AI编程工具

参考提示词

你是一位专业的前端开发,请帮我根据下列信息来生成对应的前端项目代码。#需求
1、提供主页:可以切换不同应用(考虑页面样式美观,操作流畅)
2、页面1:AI旅游大师应用。页面风格为聊天室,仿Deepseek等AI聊天界面,首次运行创建新会话,每次创建新会话调用后端newChat接口获得新的聊天ID,老的会话可以通过列表保留(仅界面支持,后端暂不支持返回会话列表)。聊天室内通过SSE的方式调用chatwithTravelAppsSE Flux接口,实时显示对话内容。
3、页面2:AI旅游智能体应用。页面风格同页面1,但不保留历史会话,调用chathithManus接口,实时显示对话内容,按Step x:的格式分割气泡展示步骤(x为步骤数字值)
###技术选型
1.Vue3+ ElementPlus布局
2.Axios请求库
## 后端接口信息
接口地址前缀:http://localhost:8080/api
# SpringBoot 后端接口代码
与AI聊天的控制器
@RestController
@RequestMapping("/ai")
public class ChatWithAIController {
    // 注入TravelApp实例
    @Resource
    private TravelApp travelApp;

    @Resource
    private ToolCallback[] aiTools;

    @Resource
    private ChatModel dashscopeChatModel;

    /*
    * 流式调用智能体
    * @param message
    * @return
    * */
    @GetMapping("/chat/manus/sse")
    public SseEmitter chatWithManus(String message){
        TravelManus travelManus = new TravelManus(aiTools,dashscopeChatModel);
        travelManus.setMaxSteps(20);
        return travelManus.streamRun(message);
    }

    /*
    * 前端可以通过此方法获得一个ID
    * */
    @GetMapping("/chat/new")
    public String newChat(){
        return UUID.randomUUID().toString();
    }

    /*
     * SSE实现方式一:使用Flux
     * 必须设置produces为MediaType.TEXT_EVENT_STREAM_VALUE
     * */
    @GetMapping(value = "/chat/travel/sse/flux",produces = MediaType.TEXT_EVENT_STREAM_VALUE)
    public Flux<String> chatWithTravelAppSEE_Flux(String chatId, String message){
        return travelApp.streamChat(chatId,message);
    }

然后把提示词发送给AI后他会根据我们提示词生成一个页面

页面如果觉得不好看可以按照想法去修改

但是在我们运行过程中会有一个错误返回,会话创建失败,然后我们端口控制台可以看到是一个跨域错误

使用CorsConfig配置跨域

在我们后端项目的config包中新建CorsConfig

 @Configuration public class CorsConfig implements WebMvcConfigurer { @Override public void addCorsMappings(CorsRegistry registry) { registry.addMapping("/**") .allowCredentials(true) .allowedOriginPatterns("*") .allowedMethods("GET", "POST", "PUT", "DELETE", "OPTIONS") .allowedHeaders("*") .allowCredentials(true) .exposedHeaders("*"); } }

配置完成后我们再次重启后端项目,测试是否可以正常运行了

如下图所示,会话创建成功,并且给出了正确的回复

优化项目展示最终结果

优化工具提示词

        由于工具的使用由AI决定,如果它胡乱使用就可能会出现一些异常,例如原本有图片搜索工具,它却选择去使用“网页爬取工具”去爬取百度图片搜索结果,导致读取的页面数据得到大量乱码(可能百度做了技术防护)。

        另外还有一个问题,就是我们仅仅告知了最大执行步骤,并未告知大模型目前在第几步,大模型由于上下文限制,可能会忘记自己现在做到第几步了,我们也可以在提示词中再次提示它。
        我们修改一下 KtTravelManus 智能体的 NEXT_STEP_PROMPT 提示词,把这两个问题一起解决一些,看下情况是否能够改善。

根据用户需求,主动选择最合适的工具或工具组合。对于复杂的任务, 你可以将问题分解开来,然后分步骤使用不同的工具来解决它。你应该尽量在 %d 个步骤内完成任务,否则任务将可能提前结束, 当前已经是第 %d 步。在使用每种工具之后,要清晰地说明执行结果,并提出下一步的建议。如果您想在任何时刻终止这种交互, 请使用 “终止” 工具 / 函数调用重要的选择工具规则: 搜索和爬取1、图片搜索只能使用 searchImage 工具,不允许通过 searchWeb 和 scrapeWebContent 工具 2、如果需要搜索信息,请使用 searchWeb 工具,不允许使用 scrapeWebContent工具 3、如果需要爬取信息,请使用 scrapeWebContent 工具,不允许使用 searchWeb 工具 4、如果需要地理位置信息,请使用高德地图相关工具 5、如果需要图文并茂的 PDF,先下载图片,并使用 Markdown 格式引用相对路径的图片地址,再传递此Markdown 内容,使用 generatePdf 工具生成 6、如果用户要求提供文件,在最终响应时提供生成文件的下载链接(A 标签)

智能体注册全部工具

        如果想要给智能体用所有工具,那么我们就需要合并本地工具和 MCP工具提供给KtTravelManus 的构造函数。
修改 ChatWithAIController

 // 合并工具 private ToolCallback[] mergeAiTools() { // 注意到进行代理,避免MCP工具出现ToolContext问题 FunctionCallback[] mcpFunctionCallbacks = McpToolCallbackProxy.proxyAll(toolCallbackProvider.getToolCallbacks()); // 转化为ToolCallBack数组 ToolCallback[] mcpToolCallbacks = Arrays.stream(mcpFunctionCallbacks) .map(tool -> (ToolCallback)tool).toArray(ToolCallback[]::new); // 合并本地工具和MCP工具 ToolCallback[] allTools = ArrayUtil.addAll(aiTools,mcpToolCallbacks); return allTools; }

让最终结果展示

因为我们只发送了步骤信息,最终结果信息并没有发送,所以我们需要改造BaseAgent,在最终完成状态时,发送最后一次的AI消息。

修改BaseAgent代码

对应代码

 // 完成之前获取最后一次有效的AI响应并发送 if(state == AgentState.FINISHED){ String finalAiResponse = getLastValidAiResponse(); // 发送最终响应给前端 if(StrUtil.isNotBlank(finalAiResponse)){ emitter.send("\n=== 智能体最终响应 ==="); emitter.send(finalAiResponse); emitter.send("\n=== 智能体最终响应结束 ==="); } }
 /** * @return 最后一次 AI 的有效响应文本,如果没有则返回空字符串 */ protected String getLastValidAiResponse() { // 获取所有消息 List<Message> messageList = chatMemory.get(this.chatId, Integer.MAX_VALUE); // 如果消息为空,则最后的响应也为空 if (messageList == null || messageList.isEmpty()) { return ""; } // 从后往前遍历消息列表,查找最后一个助手消息 for (int i = messageList.size() - 1; i >= 0; i--) { Message message = messageList.get(i); // 获取最后一条助手消息 if (message instanceof AssistantMessage) { AssistantMessage assistantMessage = (AssistantMessage) message; String content = assistantMessage.getText(); // 返回非空的助手消息内容 if (StrUtil.isNotBlank(content)) { log.info("Final response: {}", content); return content; } } } return "智能体未返回最终响应"; }

注意后端数据发送逻辑

  • 先通过getLastValidAiResponse()方法获取到最后一次的助理消息
  • 然后通过emitter.send("\n=== 智能体最终响应===");发送了响应开始标识,这需要前端代码的支持。
  • 然后发送响应内容 emitter.send(finalAiResponse);
  • 最后发送结束符号emitter.send("=== 响应结束===");

Read more

AI 行业周新闻报告(截至 2026年3月21日)

AI 行业周新闻报告(截至 2026年3月21日) 统计范围: 2026年3月16日—2026年3月21日(北京时间,截至生成时) 一周总览 过去一周,AI 产业的重心进一步从“模型能力竞赛”转向“代理式 AI(Agentic AI)与推理基础设施竞赛”。OpenAI 把政府与开发者生态同时向前推进,微软加快 Copilot 与 Foundry 体系整合,Google 则在个性化 AI 与 AGI 评测框架上同步落子;而 Nvidia 在 GTC 期间把“推理时代”与 OpenClaw/OpenClaw 类技术推到聚光灯中央,标志行业叙事已从单纯训练能力,转向可持续、可规模化的推理与执行系统。 与此同时,基础设施侧的紧张感继续升温。Nvidia H200

【人工智能之深度学习】8. 轻量化网络设计:MobileNet V2倒残差结构全解析与部署实战

【人工智能之深度学习】8. 轻量化网络设计:MobileNet V2倒残差结构全解析与部署实战

摘要:随着移动端与嵌入式设备对AI能力的需求激增,轻量化神经网络成为研究热点。MobileNet V2作为轻量化网络的里程碑之作,通过创新的倒残差结构和线性瓶颈理论,在保持较高准确率的同时大幅降低了模型参数量与计算量。本文从数学原理出发,系统拆解倒残差结构的设计逻辑,对比标准残差与倒残差的核心差异;深入解析线性瓶颈理论如何解决ReLU在低维特征空间的信息丢失问题;基于PyTorch实现完整的MobileNet V2模型,并详细讲解每一层的设计细节;通过实战案例演示模型训练、量化压缩、跨设备部署的全流程,包括在iPhone、Jetson Nano、树莓派等设备上的性能对比;最后结合工业质检场景,展示如何将MobileNet V2应用于实时缺陷检测任务。本文适合深度学习工程师、移动端AI开发者及研究人员,为轻量化网络的设计与部署提供从理论到实践的完整指南。 AI领域优质专栏欢迎订阅! 【DeepSeek深度应用】 【机器视觉:C# + HALCON】 【人工智能之深度学习】 【AI 赋能:Python 人工智能应用实战】 【AI工程化落地与YOLOv8/v9实战】 文章目

字节跳动 AI 原生 IDE Trae 安装与上手图文教程

字节跳动 AI 原生 IDE Trae 安装与上手图文教程

文章目录 * 一、 什么是 Trae? * 国际版与国内版区别 * 二、 下载与环境准备 * 第一步:访问官网下载 * 第二步:系统安装 * 第三步:首次启动与初始化配置 * 三、 核心功能上手实战 * 四、 进阶技巧:如何切换满血大模型 * 五、 总结 一、 什么是 Trae? 简单来说,Trae 是字节跳动近期推出的一款 AI 原生集成开发环境 (IDE)。你可以把它看作是国内打磨极佳的 Cursor 或 Windsurf 替代品。它从底层架构开始就围绕 AI 能力构建,不仅能自动补全代码,还能直接听懂你的大白话,帮你从零开始写项目、修 Bug、甚至一键部署后端服务。 核心亮点: * 完全免费:目前处于免费阶段,对于动辄几十美元一个月的 AI 开发工具来说,性价比拉满。

OpenClaw 最新保姆级飞书对接指南教程 搭建属于你的 AI 助手

OpenClaw 最新保姆级飞书对接指南教程 搭建属于你的 AI 助手

OpenClaw 最新保姆级飞书对接指南教程 搭建属于你的 AI 助手 OpenClaw 是一款开源的本地 AI 助手,本篇 OpenClaw 安装教程将手把手教你在 Linux 系统下部署最新版 OpenClaw,并完成飞书机器人对接。OpenClaw 支持在你自己的服务器上运行,通过飞书、WhatsApp、Telegram 等聊天工具交互。与云端 SaaS 服务不同,OpenClaw 让你完全掌控数据隐私,可以执行系统命令、浏览网页、管理文件,甚至编写代码——是你的专属开源 AI 助手。 注意:本教程在 Linux 系统下进行 OpenClaw 是什么? OpenClaw(原名 Clawdbot,后更名为 Moltbot,现正式命名为 OpenClaw)是一个运行在你本地环境的高权限 AI 智能体。