一.SAA
1.1 SAA为什么出现
随着人工智能(AI)技术的迅猛发展,越来越多的开发者开始将目光投向AI应用的开发。然而,目前市场上多数AI框架和工具如LangChain、PyTorch等主要支持Python,而Java开发者常常面临工具缺乏和学习门较高的问题,但是不用担心,谁让Java/Spring群体强大呢?
所以任何一个框架/XXX云服务器,想要大面积推广,就应该不会忘记庞大的Spring社区和Java程序员,所以传统的微服务和大模型要联系在一起,需要借助中间件Langchain4J,SpringAI或者SpringAI Alibaba

1.2 什么是SSA
对于Java开发者来说,我们需要一款AI应用开发框架来简化AI应用开发。
在这样的背景下,Spring官方开源了Spring AI框架,用来简化 Spring开发者开发智能体应用的过程。随后阿里巴巴开源了 Spring AI Alibaba,它基于Spring AI,同时与阿里云百炼大模型服务、通义系列大模型做了深度集成与最佳实践。基于 Spring AI Alibaba,Java开发者可以非常方便的开发 AI智能体应用。
所以Spring AI Alibaba是一款以Spring AI为基础,深度集成百炼平台,支持ChatBot,工具流,多智能体应用开发模式的AI框架。
二.入门案例
1.确定 Spring AI Alibaba 与 Spring Al、SpringBoot版本的兼容关系
Spring AI Alibaba使用四位版本号的版本管理方式,前三位版本号与Spring AI主版本对应,SpringAI Alibaba社区在前三位主版本基础上持续迭代第四位版本号。需注意Spring AI Alibaba需要与Spring AI版本对应

2. 接入通义模型:
阿里云百炼平台入口官网
大模型调用三件套:
- 获得API-key
- 获得模型名
- 获得baseUrl开发地址
3.IDEA工具中创建project夫工程
通过bom统一版本管理
XML
<properties>
<!--引入-->
<spring-ai.version>1.0.0</spring-ai.version>
<spring-ai-alibaba.version>1.0.0.2</spring-ai-alibaba.version>
<spring-boot.version>3.5.5</spring-boot.version>
</properties>
<dependencyManagement>
<dependencies>
<!-- Spring Boot -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-dependencies</artifactId>
<version>${spring-boot.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
<!-- Spring AI Alibaba -->
<dependency>
<groupId>com.alibaba.cloud.ai</groupId>
<artifactId>spring-ai-alibaba-bom</artifactId>
<version>${spring-ai-alibaba.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
<!-- Spring AI -->
<dependency>
<groupId>org.springframework.ai</groupId>
<artifactId>spring-ai-bom</artifactId>
<version>${spring-ai.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<version>${spring-boot.version}</version>
</plugin>
</plugins>
</build>
4.开发五步骤:
-
建module---->建一个SAA-01HelloWorld
-
改POM
XML<dependencies> <!-- 引入 springai alibaba DashScope 模型适配的 Starter--> <!--对于开发者来说,DashScope 的意义在于:它把所有大模型的 API 接口标准化了--> <dependency> <groupId>com.alibaba.cloud.ai</groupId> <artifactId>spring-ai-alibaba-starter-dashscope</artifactId> </dependency> </dependencies> -
写yml
bash#防止乱码的配置 server.servlet.encoding.enabled=true server.servlet.encoding.force=true server.servlet.encoding.charset=UTF-8 spring.application.name=SAA-01HelloWorld # ====SpringAIAlibaba Config============= spring.ai.dashscope.api-key=密钥 spring.ai.dashscope.base-url= spring.ai.dashscope.chat.options.model=名字 -
主启动
-
业务类
对话模型Chat Model
对话模型(Chat Model)接收一系列消息(Message)作为输入,与模型LLM服务进行交互,并接收返回的聊天消息(Chat Message)作为输出。相比于普通的程序输入,模型的输入与输出消息不止支持纯字符文本,还支持包括语音、图片、视频等为输入输出。同时,在SpringAIAlibaba中,消息中还支持包含不同的角色,帮助底层模型区分来自模型、用户和系统指令等的不同消息。
Spring AI Alibaba复用了Spring AI抽象的 Model API,并与通义系列大模型服务进行适配(如通义千问、通义万相等),目前支持纯文本聊天、文生图、文生语音、语音转文本等。以下是框架定义的几个核心API:
- ChatModel,文本聊天交互模型,支持纯文本格式作为输入,并将模型的输出以格式化文本形式返回。
- ImageModel,接收用户文本输入,并将模型生成的图片作为输出返回。
- AudioModel,接收用户文本输入,并将模型合成的语音作为输出返回。
| 模式 | 方法 | 表现 | 适用场景 |
|---|---|---|---|
| 同步 (Sync) | chatModel.call() |
等 AI 全部写完后,一次性返回。 | 后端逻辑处理、短文本生成。 |
| 流式 (Stream) | chatModel.stream() |
边生成边返回(打字机效果),基于 Flux。 |
聊天机器人、长文创作(用户体验好)。 |
java
package com.alibaba.cloud.ai.example.helloworld.controller;
import com.alibaba.cloud.ai.dashscope.api.DashScopeApi;
import jakarta.annotation.Resource;
import org.springframework.ai.chat.client.ChatClient;
import org.springframework.ai.chat.messages.UserMessage;
import org.springframework.ai.chat.model.ChatModel;
import org.springframework.boot.context.properties.bind.DefaultValue;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import reactor.core.publisher.Flux;
@RestController
public class ChatHelloController
{
@Resource // 对话模型,调用阿里云百炼平台
private ChatModel chatModel;
/**
* 通用调用
* @param msg
* @return
*/
@GetMapping(value = "/hello/dochat")
public String doChat(@RequestParam(name = "msg",defaultValue="你是谁") String msg)
{
String result = chatModel.call(msg);
return result;
}
/**
* 流式返回调用
* @param msg
* @return
*/
@GetMapping(value = "/hello/streamchat")
public Flux<String> stream(@RequestParam(name = "msg",defaultValue="你是谁") String msg)
{
return chatModel.stream(msg);
}
}
访问:localhost:8001/hello/dochat

注意: 如果结果出不来的,请注意一下模型别用成多模态的模型,Qwen3.5-plus就是多模态的
当我不想用qwen了,想用deepseek这些该怎么办呢?
此时只需要改名称即可,百炼提供了各大模型,如下所示:

效果:

上述可能出现密钥暴露的情况,所以我们的ApiKey可以配置到环境变量

改yml:
spring.ai.dashscope.api-key=${aliQwen-api}
新建配置类:SaaLLMConfig
有两种方式,方式一:
java
@Configuration
public class SaaLLMConfig
{
/**
* 方式1:${}
* 持有yml文件配置:spring.ai.dashscope.api-key=${aliQwen-api}
*/
@Value("${spring.ai.dashscope.api-key}")
private String apiKey;
@Bean
public DashScopeApi dashScopeApi()
{
return DashScopeApi.builder().apiKey(apiKey).build();
}
}
方式二:
java
@Configuration
public class SaaLLMConfig
{
/**
* 方式2:System.getenv("环境变量")
* 持有yml文件配置:spring.ai.dashscope.api-key=${aliQwen-api}
* @return
*/
@Bean
public DashScopeApi dashScopeApi()
{
return DashScopeApi.builder()
.apiKey(System.getenv("aliQwen-api"))
.build();
}
}
三.Ollama私有化部署和对接本地大模型
3.1 Ollama之入门理论
Ollama作为一个工具是专门用来做本地化部署。旨在简化在Docker容器中部署和管理大型语言模型(LLM)的过程。它帮助用户快速在本地运行大模型,通过简单的安装指令,用户可以执行一条命令就在本地运行开源大型语言模型,如Llama 2.
Ollama极大地简化了在Docker容器内部署和管理LLM的过程,它优化了设置和配置细节,包括GPU使用情况,并将模型权重、配置和数据捆绑到一个包中,定义成Modelfile。此外,Ollama还提供了多种大型语言模型的开源仓库,用户可以通过简单的命令行操作来下载和运行这些模型。
总的来说,Ollama是一个用于在本地高效运行大型语言模型的工具。
Docker Hub玩镜像,Ollama Hub玩模型
3.2 Ollama安装
1.可以进入Ollama的官网下载 Ollama

2.自定义安装路径:因为ollama在拉取大模型的时候占用的空间可能比较大,所以需要我们安装在C盘以外的盘;

- 在文件路径上输入cmd回车打开命令窗口
- 然后再其输入:OllamaSetup.exe /DIR=D:\develop\ollama

3.手动创建大模型的存储路径
-
进行 "环境变量" 配置:在 "系统变量" 中点击 "新建":
-
变量名 :
OLLAMA_MODELS -
变量值 :D:\develop\ollama\models。
-

- 创建完环境变量,把Ollama停止,然后进入C盘-->用户-->你自己的电脑名称-->.ollama-->复制整个models到刚刚上面新建的存储目录下。复制完成后要删除C盘目录下的models文件夹。
- 重启Ollama,打开CMD输入。

之后每次想要用模型的时候,就输入 ollama run qwen3:1.7b(模型名)

3.3 Ollama下载模型命令
在官网Ollama中搜索想要的模型

首先在下载之前需要知道b代表什么意思:

在此我演示一个占用较小空间的演示:点进入后copy


安装成功之后,此时我输入:

3.4 微服务调用ollama本地模型
下面我们通过一个编码案例来介绍如何在微服务中调用ollama本地模型
步骤还是和之前一样
1.建module
2.改pom--->在其基础上改动新建
XML
<!--ollama-->
<dependency>
<groupId>org.springframework.ai</groupId>
<artifactId>spring-ai-starter-model-ollama</artifactId>
<version>1.0.0</version>
</dependency>
3.改properties
bash
server.port=8002
server.servlet.encoding.enabled=true
server.servlet.encoding.force=true
server.servlet.encoding.charset=UTF-8
spring.application.name=SAA-02Ollama
# ====SpringAIAlibaba Config=============
spring.ai.dashscope.api-key=自己的key
spring.ai.ollama.base-url=http://localhost:11434
spring.ai.ollama.chat.model=qwen3:1.7b
4.主启动
4.业务类

正常启动,业务类会有两个chatmodel(一个allama一个dashscope),所以需要我们指定使用哪一个
java
@Resource(name = "ollamaChatModel")
private ChatModel chatModel;

四.ChatClient和ChatModel
4.1 简介
通过之前的实战案例,相信大家对ChatModel 已经不陌生了,对话模型(ChatModel)是底层接口,直接与具体大语言模型交互,提供call0和stream0方法,适合简单大模型交互场景。那什么是ChatClient呢?

由上可知,ChatClient是高级封装,基于ChatModel构建,适合快速构建标准化复杂AI服务,支持同步和流式交互,集成多种高级功能。
| 特性 | ChatModel | ChatClient |
|---|---|---|
| 定位 | 底层适配器(Low-level) | 高层 API 封装(High-level) |
| 关注点 | 模型的连接与推理参数 | 开发者体验与业务逻辑 |
| 状态感 | 通常是无状态的(点对点) | 可以通过 Advisor 轻松实现有状态(记住对话) |
| 灵活性 | 决定了你用哪个"脑子" | 决定了你如何与"脑子"沟通 |
类比于java中的知识点
- ChatModel
数据库驱动 (JDBC Driver)
- ChatClient
JdbcTemplate / MyBatis
下述是二者对比:

4.2 编码案例
和第一个module一样,建module,改pom,写yml,主启动,然后写业务类
业务类第一版:--->先只注入chatmodel
java
package com.alibaba.ai.controller;
import jakarta.annotation.Resource;
import org.springframework.ai.chat.model.ChatModel;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class ChatModelController
{
@Resource //阿里云百炼
private ChatModel dashScopeChatModel;
@GetMapping("/chatmodel/dochat")
public String doChat(@RequestParam(name = "msg",defaultValue = "你是谁") String msg)
{
String result = dashScopeChatModel.call(msg);
System.out.println("响应:" + result);
return result;
}
}
但是此时如果我像chatmodel一样注入chatclient,重启微服务会报错,由此可知道ChatClient只支持手动注入,不支持自动注入

那么如何解决ChatClient无法自动注入呢?由此引出了第二版:
业务类第二版:--->先只注入chatclient
使用自动配置的构造注入 ChatClient.Builder
在快速开始示例中,就是使用的 Spring Boot 自动装配默认生成的 ChatClient.Builder 的 bean,把它注入到您自己的类中。
java
package com.alibaba.ai.controller;
import org.springframework.ai.chat.client.ChatClient;
import org.springframework.ai.chat.model.ChatModel;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class ChatClientController
{
private final ChatClient dashScopechatClient;
/**
* 使用自动配置的 ChatClient.Builder
* @param dashscopeChatModel
*/
public ChatClientController(ChatModel dashscopeChatModel)
{
this.dashScopechatClient = ChatClient.builder(dashscopeChatModel).build();
}
/**
* http://localhost:8003/chatclient/dochat
* @param msg
* @return
*/
@GetMapping("/chatclient/dochat")
public String doChat(@RequestParam(name = "msg",defaultValue = "2加4等于几") String msg)
{
String result = dashScopechatClient.prompt().user(msg).call().content();
System.out.println("响应:" + result);
return result;
}
}
由上述代码可知ChatClient不支持自动注入,依赖ChatModel对象接口。

但是在实际业务中,chatmodel和chatclient同时存在,由此引出业务的第三版:
业务类第三版:--->chatmodel和chatclient混合使用
- 修改配置类SaaLLMConfig
java
@Configuration
public class SaaLLMConfig
{
/**
* 知识出处:
* https://java2ai.com/docs/1.0.0.2/tutorials/basics/chat-client/?spm=5176.29160081.0.0.2856aa5cmUTyXC#%E5%88%9B%E5%BB%BA-chatclient
* @param dashscopeChatModel
* @return
*/
@Bean
public ChatClient chatClient(ChatModel dashscopeChatModel)
{
return ChatClient.builder(dashscopeChatModel).build();
}
}
- 新建ChatClientControllerV2
java
@RestController
public class ChatClientControllerV2
{
/**
* chatModel + ChatClient 混合使用
*/
@Resource
private ChatModel chatModel;
@Resource
private ChatClient dashScopechatClientv2;
/**
* http://localhost:8003/chatclientv2/dochat
* @param msg
* @return
*/
@GetMapping("/chatclientv2/dochat")
public String doChat(@RequestParam(name = "msg",defaultValue = "你是谁") String msg)
{
String result = dashScopechatClientv2.prompt().user(msg).call().content();
System.out.println("ChatClient响应:" + result);
return result;
}
/**
* http://localhost:8003/chatmodelv2/dochat
* @param msg
* @return
*/
@GetMapping("/chatmodelv2/dochat")
public String doChat2(@RequestParam(name = "msg",defaultValue = "你是谁") String msg)
{
String result = chatModel.call(msg);
System.out.println("ChatModel响应:" + result);
return result;
}
}

那如果我想要同时存在多种大模型产品在系统中共存使用,怎么办呢?可以看下一章节
五.SSE服务器发送事件
在本章节,我们将会讲到Server-SentEvents(SSE)实现Stream流式输出和多模型共存
5.1 流式输出(StreamingOutput)
是一种逐步返回大模型生成结果的技术,生成一点返回一点,允许服务器将响应内容分批次实时传输给客户端,而不是等待全部内容生成完毕后再一次性返回。
这种机制能显著提升用户体验,尤其适用于大模型响应较慢的场景(如生成长文本或复杂推理结果。

前置知识:Springboot3响应式编程
响应式编程是能实现一个全异步式的非阻塞式框架。
在响应式世界里,数据不再是简单的
String或List,而是像流水一样。Spring 使用了Project Reactor 库,它有两个核心的"包装盒":
Mono :代表 0 到 1 个元素的流。比如:获取一个用户的详情。
Flux :代表 0 到 N 个元素的流。比如:获取一个列表,或者实时刷新的股票行情。
响应式编程其核心:
数据流:数据源头
变化传播: 数据操作(中间操作)
异步编程模式: 底层控制异步
subscribe
5.2 SSE概念
SSE即Server-Sent Events,翻译过来就是服务器发送事件;它由两部分组成:Server-Sent:由服务器发送。Events:事件,指服务器主动推送给客户端的数据或消息。
Server-SentEvents(SSE)是一种允许服务端可以持续推送数据片段(如逐词或逐句)到前端的Web技术。通过单向的HTTP长连接,使用一个长期存在的连接,让服务器可以主动将数据"推"给客户端,SSE是轻量级的单向通信协议,适合AI对话这类服务端主导的场景。
SSE的核心思想是:客户端发起一个请求,服务器保持这个连接打开并在有新数据时,通过这个连接将数据发送给客户端。这与传统的请求-响应模式(客户端请求一次,服务器响应一次,连接关闭)有本质区别。SSE下一代(StreamableHttp)

总之,SSE是一种让服务器能主动,持续地向客户端(比如你的网页浏览器)推送数据的技术。
5.3 编码案例
接下来,我们将实现一个案例,要求同时存在多种大模型产品在系统中共存使用
前面的步骤还是一致,新建子模块Module,改pom,写yml,主启动

先看第一版:
java
package com.ai.alibaba.config;
import com.alibaba.cloud.ai.dashscope.api.DashScopeApi;
import com.alibaba.cloud.ai.dashscope.chat.DashScopeChatModel;
import com.alibaba.cloud.ai.dashscope.chat.DashScopeChatOptions;
import org.springframework.ai.chat.model.ChatModel;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class SaaLLMConfig {
// 模板名称常量定义,一套系统多模型共存
private final String DEEPSEEK_MODEL="deepseek-v3";
private final String QWEN_MODEL = "qwen3-max";
@Bean(name = "deepSeek")
public ChatModel deepSeek(){
return DashScopeChatModel.builder()
.dashScopeApi(DashScopeApi.builder().apiKey(System.getenv("aliQwen-api")).build())
.defaultOptions(DashScopeChatOptions.builder().withModel(DEEPSEEK_MODEL).build())
.build();
}
@Bean(name = "qwen")
public ChatModel qwen(){
return DashScopeChatModel.builder()
.dashScopeApi(DashScopeApi.builder().apiKey(System.getenv("aliQwen-api")).build())
.defaultOptions(DashScopeChatOptions.builder().withModel(QWEN_MODEL).build())
.build();
}
}
java
package com.ai.alibaba.controller;
import jakarta.annotation.Resource;
import org.springframework.ai.chat.client.ChatClient;
import org.springframework.ai.chat.model.ChatModel;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import reactor.core.publisher.Flux;
@RestController
public class StreamOutputController
{
//V1 通过ChatModel实现stream实现流式输出
@Resource(name = "deepSeek")
private ChatModel deepseekChatModel;
@Resource(name = "qwen")
private ChatModel qwenChatModel;
@GetMapping(value = "/stream/chatflux1")
public Flux<String> chatflux(@RequestParam(name = "question",defaultValue = "你是谁") String question)
{
return deepseekChatModel.stream(question);
}
@GetMapping(value = "/stream/chatflux2")
public Flux<String> chatflux2(@RequestParam(name = "question",defaultValue = "你是谁") String question)
{
return qwenChatModel.stream(question);
}
}
结果为:


看第二版:
java
package com.ai.alibaba.config;
import com.alibaba.cloud.ai.dashscope.api.DashScopeApi;
import com.alibaba.cloud.ai.dashscope.chat.DashScopeChatModel;
import com.alibaba.cloud.ai.dashscope.chat.DashScopeChatOptions;
import org.springframework.ai.chat.client.ChatClient;
import org.springframework.ai.chat.model.ChatModel;
import org.springframework.ai.chat.prompt.ChatOptions;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class SaaLLMConfig {
// 模板名称常量定义,一套系统多模型共存
private final String DEEPSEEK_MODEL="deepseek-v3";
private final String QWEN_MODEL = "qwen3-max";
@Bean(name = "deepSeek")
public ChatModel deepSeek(){
return DashScopeChatModel.builder()
.dashScopeApi(DashScopeApi.builder().apiKey(System.getenv("aliQwen-api")).build())
.defaultOptions(DashScopeChatOptions.builder().withModel(DEEPSEEK_MODEL).build())
.build();
}
@Bean(name = "qwen")
public ChatModel qwen(){
return DashScopeChatModel.builder()
.dashScopeApi(DashScopeApi.builder().apiKey(System.getenv("aliQwen-api")).build())
.defaultOptions(DashScopeChatOptions.builder().withModel(QWEN_MODEL).build())
.build();
}
@Bean(name = "deepseekChatClient")
public ChatClient deepseekChatClient(@Qualifier("deepSeek") ChatModel deepSeek)
{
return ChatClient.builder(deepSeek)
.defaultOptions(ChatOptions.builder()
.model(DEEPSEEK_MODEL)
.build())
.build();
}
@Bean(name = "qwenChatClient")
public ChatClient qwenChatClient(@Qualifier("qwen") ChatModel qwen)
{
return ChatClient.builder(qwen)
.defaultOptions(ChatOptions.builder()
.model(QWEN_MODEL)
.build())
.build();
}
}
java
//V2 通过ChatClient实现stream实现流式输出
@Resource(name = "deepseekChatClient")
private ChatClient deepseekChatClient;
@Resource(name = "qwenChatClient")
private ChatClient qwenChatClient;
.........
@GetMapping(value = "/stream/chatflux3")
public Flux<String> chatflux3(@RequestParam(name = "question",defaultValue = "你是谁") String question)
{
return deepseekChatClient.prompt(question).stream().content();
}
@GetMapping(value = "/stream/chatflux4")
public Flux<String> chatflux4(@RequestParam(name = "question",defaultValue = "你是谁") String question)
{
return qwenChatClient.prompt(question).stream().content();
}

5.4 SSE前端页面
前端效果:

Flux是SpringWebFlux中的一个核心组件,属于响应式编程模型的一部分。它主要用于处理异步、非阻塞的流式数据,能够高效地处理高并发场景。Flux可以生成和处理一系列的事件或数据如流式输出等。
看类注释和类所在的jar包我们就明白:
SAA中的流式输出是通过ReactorStreams技术实现的和SpringWebFlux的底层实现是一样的技术。具体执行流程:
ReactorStreams会订阅数据源,当有数据时,ReactorStreams以分块流的方式发送给客户端用户。
前端代码:
html
<!DOCTYPE html>
<html>
<head>
<title>SSE流式chat</title>
<style>
body {
font-family: Arial, sans-serif;
background-color: #f4f4f4;
margin: 0;
padding: 20px;
}
#messageInput {
width: 90%;
padding: 10px;
font-size: 16px;
border: 1px solid #ccc;
border-radius: 4px;
margin-bottom: 10px;
}
button {
padding: 10px 20px;
font-size: 16px;
background-color: #007bff;
color: white;
border: none;
border-radius: 4px;
cursor: pointer;
}
button:hover {
background-color: #0056b3;
}
#messages {
margin-top: 20px;
padding: 15px;
background-color: #f9f9f9;
border: 1px solid #ddd;
border-radius: 8px;
max-height: 300px;
overflow-y: auto;
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
}
#messages div {
padding: 8px 0;
border-bottom: 1px solid #eee;
font-size: 14px;
color: #333;
}
#messages div:last-child {
border-bottom: none;
}
</style>
</head>
<body>
<textarea id="messageInput" rows="4" cols="50" placeholder="请输入你的问题..."></textarea><br>
<button onclick="sendMsg()">发送提问</button>
<div id="messages"></div>
<script>
function sendMsg() {
// 获取用户输入的消息
const message = document.getElementById('messageInput').value;
if (message == "") return false;
//1 客户端使用 JavaScript 的 EventSource 对象连接到服务器上的一个特定端点(URL)
const eventSource = new EventSource('stream/chatflux2?question=' + message);
//2 监听消息事件
eventSource.onmessage = function (event) {
// 获取流式返回的数据
const data = event.data;
// 将接收到的数据展示到页面上
const messagesDiv = document.getElementById('messages');
messagesDiv.innerHTML += event.data;
};
//3 监听错误事件
eventSource.onerror = function (error) {
console.error('EventSource 发生错误:', error);
eventSource.close(); // 关闭连接
};
}
</script>
</body>
</html>
该代码应该放置到:


