Spring AI Alibaba 入门开发1-环境搭建、ollama部署、Chat和ChatModel、流式输出
目录一、准备工作二、HelloWorld2.1 项目结构2.2 创建父工程2.3 创建子模块——ssa-01-HelloWorld三、微服务调用本地大模型-本章可跳过3.1 准备工作系统与硬件要求本地部署大模型配置推荐列表3.2 开发调用本地大模型四、ChatModel和ChatClient4.1 ChatModel4.2 ChatClient4.3 使用ChatClient调用大模型4.4 两者对比五、同时使用多个模型实现流式输出5.1 流式输出介绍5.2 SSE(Server-Sent Events)服务器发送事件5.3 多模型共存开发5.4 Flux介绍5.5 新增前端交互一、准备工作1、到阿里百炼平台申请API KEY、然后获取base_url、模型code。具体请自行百度。2、我的相关配置JDK 21maven 建议3.9.5SpringBoot : 3.5.5spring-ai: 1.0.0SpringAIAlibaba:1.0.2二、HelloWorld2.1 项目结构2.2 创建父工程只需修改父工程pom.xml即可代码如下properties project.build.sourceEncodingUTF-8/project.build.sourceEncoding project.reporting.outputEncodingUTF-8/project.reporting.outputEncoding maven.compiler.source21/maven.compiler.source maven.compiler.target21/maven.compiler.target java.version21/java.version spring-boot.version3.5.5/spring-boot.version spring-ai.version1.0.0/spring-ai.version SpringAIAlibaba.version1.0.0.2/SpringAIAlibaba.version /properties dependencyManagement dependencies !-- Spring Boot -- dependency groupIdorg.springframework.boot/groupId artifactIdspring-boot-dependencies/artifactId version${spring-boot.version}/version typepom/type scopeimport/scope /dependency !-- Spring AI Alibaba -- dependency groupIdcom.alibaba.cloud.ai/groupId artifactIdspring-ai-alibaba-bom/artifactId version${SpringAIAlibaba.version}/version typepom/type scopeimport/scope /dependency !-- Spring AI -- dependency groupIdorg.springframework.ai/groupId artifactIdspring-ai-bom/artifactId version${spring-ai.version}/version typepom/type scopeimport/scope /dependency /dependencies /dependencyManagement build plugins plugin groupIdorg.springframework.boot/groupId artifactIdspring-boot-maven-plugin/artifactId version${spring-boot.version}/version /plugin /plugins /build repositories repository idspring-milestones/id nameSpring Milestones/name urlhttps://repo.spring.io/milestone/url snapshots enabledfalse/enabled /snapshots /repository /repositories2.3 创建子模块——ssa-01-HelloWorld1、pom.xmlproperties maven.compiler.source21/maven.compiler.source maven.compiler.target21/maven.compiler.target project.build.sourceEncodingUTF-8/project.build.sourceEncoding /properties dependencies dependency groupIdorg.springframework.boot/groupId artifactIdspring-boot-starter-web/artifactId /dependency dependency groupIdcom.alibaba.cloud.ai/groupId artifactIdspring-ai-alibaba-starter-dashscope/artifactId /dependency !--lombok-- dependency groupIdorg.projectlombok/groupId artifactIdlombok/artifactId optionaltrue/optional /dependency !--hutool-- dependency groupIdcn.hutool/groupId artifactIdhutool-all/artifactId version5.8.22/version /dependency dependency groupIdorg.springframework.boot/groupId artifactIdspring-boot-starter-test/artifactId scopetest/scope /dependency /dependencies build plugins plugin groupIdorg.springframework.boot/groupId artifactIdspring-boot-maven-plugin/artifactId /plugin plugin groupIdorg.apache.maven.plugins/groupId artifactIdmaven-compiler-plugin/artifactId version3.11.0/version configuration compilerArgs arg-parameters/arg /compilerArgs source21/source target21/target /configuration /plugin /plugins /build repositories repository idspring-milestones/id nameSpring Milestones/name urlhttps://repo.spring.io/milestone/url snapshots enabledfalse/enabled /snapshots /repository /repositories2、配置文件application.propertiesserver.port8888 #解决中文乱码 server.servlet.encoding.charsetUTF-8 server.servlet.encoding.enabledtrue server.servlet.encoding.forcetrue spring.application.namessa-01-HelloWorld #Sprig AI Alibaba 相关配置 spring.ai.dashscope.api-key${qianwen-api} spring.ai.dashscope.base-urlhttps://ws-6tq01jo6mtthlhoq.cn-beijing.maas.aliyuncs.com/compatible-mode/v1 spring.ai.dashscope.chat.options.modelqwen-max注意配置api-key时不能使用明文需要先配置环境变量然后在properties中使用${环境变量}3、创建配置类有两种方式获取api-key一种是读取application.properties的内容一种是读取环境变量的值。具体实现如下Configuration public class LLMConfig { //方式1读取配置文件信息 Value(${spring.ai.dashscope.api-key}) private String apiKey; Bean public DashScopeApi dashScopeApi() { return DashScopeApi.builder().apiKey(apiKey).build(); } //方式2读取环境变量 // Bean // public DashScopeApi dashScopeApi2() { // return DashScopeApi.builder().apiKey(System.getenv(qianwen-api)).build(); // } }4、对话模型-Chat Model对话模型Chat Model接收一系列消息Message作为输入与模型 LLM 服务进行交互并接收返回的聊天消息Chat Message作为输出。相比于普通的程序输入模型的输入与输出消息Message不止支持纯字符文本还支持包括语音、图片、视频等作为输入输出。同时在 Spring AI Alibaba 中消息中还支持包含不同的角色帮助底层模型区分来自模型、用户和系统指令等的不同消息。Spring AI Alibaba 复用了 Spring AI 抽象的 Model API并与通义系列大模型服务进行适配如通义千问、通义万相等目前支持纯文本聊天、文生图、文生语音、语音转文本等。以下是框架定义的几个核心 APIChatModel文本聊天交互模型支持纯文本格式作为输入并将模型的输出以格式化文本形式返回。ImageModel接收用户文本输入并将模型生成的图片作为输出返回。AudioModel接收用户文本输入并将模型合成的语音作为输出返回。Spring AI Alibaba 支持以上 Model 抽象与通义系列模型的适配并通过spring-ai-alibaba-starterAutoConfiguration 自动初始化了默认实例因此我们可以在应用程序中直接注入 ChatModel、ImageModel 等 bean当然在需要的时候也可以自定义 Model 实例。5、创建controller使用两种方式与大模型进行交互一种是.call()此方式是一次响应所有的内容一种是.stream()文字逐个返回。RestController public class ChatHelloWorldController { Resource private ChatModel chatModel; GetMapping(/hello/call) public String doCall(RequestParam(value msg , defaultValue 高欢) String msg) { String result chatModel.call(msg); return result; } GetMapping(/hello/stream) public FluxString doStream(RequestParam(value msg , defaultValue 宇文泰) String msg) { return chatModel.stream(msg); } }三、微服务调用本地大模型-本章可跳过3.1 准备工作1、本地安装Ollama。注意默认安装C盘建议更换其他位置。安装方法请百度。系统与硬件要求项目最低要求特别说明操作系统Windows 10 22H2 或更新版本家庭版/专业版均可版本过旧可能导致兼容性问题建议更新。处理器 (CPU)4核及以上推荐Intel i7或AMD Ryzen 5以上若没有独立显卡将完全依赖CPU进行推理速度会慢很多。内存 (RAM)16GB 或更高运行大模型时内存是保证系统不崩溃的关键。显卡 (GPU)强烈推荐NVIDIA或AMD独立显卡Ollama支持NVIDIA、AMD Radeon GPU加速。NVIDIA显卡驱动551.61 或更新版本这是官方文档的要求驱动太旧可能无法识别显卡。AMD显卡驱动安装最新版AMD Radeon驱动或支持ROCm/Vulkan的驱动部分RDNA2架构显卡如RX 6000系列可能推荐使用Vulkan加速。存储空间安装程序本身约需4GB另需为模型文件预留几十到上百GB模型文件体积很大强烈建议安装在SSD上以提升加载速度。本地部署大模型配置推荐列表推荐级别模型系列具体推荐模型 (Ollama命令)模型大小 (量化后)显存占用预期速度 (tokens/秒)适用场景 首选推荐Qwen3 (4B)ollama run qwen3:4b-q4_K_M~2.9 GB~3.2 GB30 - 50日常聊天、问答、编程辅助、文本总结体验流畅 次选推荐Qwen2.5 (3B)ollama run qwen2.5:3b-q4_K_M~2.0 GB~2.2 GB40 - 60轻量级任务响应极快适合资源敏感场景 进阶挑战DeepSeek-R1 (7B)ollama run deepseek-r1:7b-q4_K_M~4.4 GB~4.6 GB (略超显存)8 - 15复杂推理、代码生成需要部分CPUGPU混合运行速度较慢确认好模型后可以使用pull命令拉去使用run命令运行具体与docker类似。3.2 开发调用本地大模型1、新建子模块项目结构如下2、修改pom.xml文件dependencies dependency groupIdorg.springframework.boot/groupId artifactIdspring-boot-starter-web/artifactId /dependency !--spring-ai-alibaba dashscope-- dependency groupIdcom.alibaba.cloud.ai/groupId artifactIdspring-ai-alibaba-starter-dashscope/artifactId /dependency !--ollama-- dependency groupIdorg.springframework.ai/groupId artifactIdspring-ai-starter-model-ollama/artifactId version1.0.0/version /dependency !--lombok-- dependency groupIdorg.projectlombok/groupId artifactIdlombok/artifactId optionaltrue/optional /dependency !--hutool-- dependency groupIdcn.hutool/groupId artifactIdhutool-all/artifactId version5.8.22/version /dependency dependency groupIdorg.springframework.boot/groupId artifactIdspring-boot-starter-test/artifactId scopetest/scope /dependency dependency groupIdorg.springframework.boot/groupId artifactIdspring-boot-starter-actuator/artifactId /dependency /dependencies build plugins plugin groupIdorg.springframework.boot/groupId artifactIdspring-boot-maven-plugin/artifactId /plugin plugin groupIdorg.apache.maven.plugins/groupId artifactIdmaven-compiler-plugin/artifactId version3.11.0/version configuration compilerArgs arg-parameters/arg /compilerArgs source21/source target21/target /configuration /plugin /plugins /build repositories repository idspring-milestones/id nameSpring Milestones/name urlhttps://repo.spring.io/milestone/url snapshots enabledfalse/enabled /snapshots /repository /repositories3、application.propertiesserver.port8888 #解决中文乱码 server.servlet.encoding.charsetUTF-8 server.servlet.encoding.enabledtrue server.servlet.encoding.forcetrue spring.application.namessa-02-ollama #Sprig AI Alibaba 相关配置 spring.ai.dashscope.api-key${qianwen-api} spring.ai.ollama.base-urlhttp://localhost:11434 spring.ai.ollama.chat.options.modelqwen3:4b4、新建controllerRestController public class OllamaController { Resource(name ollamaChatModel) private ChatModel chatModel; GetMapping(/ollama) private FluxString ollama(RequestParam(value msg ,defaultValue 高欢) String msg){ return chatModel.stream(msg); } }5、新建启动类public class SSA_02_OllamaApplication { public static void main(String[] args) { SpringApplication.run(SSA_02_OllamaApplication.class, args); } }6、运行如下四、ChatModel和ChatClient4.1 ChatModel对话模型Chat Model接收一系列消息Message作为输入与模型 LLM 服务进行交互并接收返回的聊天消息Chat Message作为输出。相比于普通的程序输入模型的输入与输出消息Message不止支持纯字符文本还支持包括语音、图片、视频等作为输入输出。同时在 Spring AI Alibaba 中消息中还支持包含不同的角色帮助底层模型区分来自模型、用户和系统指令等的不同消息。Spring AI Alibaba 复用了 Spring AI 抽象的 Model API并与通义系列大模型服务进行适配如通义千问、通义万相等目前支持纯文本聊天、文生图、文生语音、语音转文本等。以下是框架定义的几个核心 APIChatModel文本聊天交互模型支持纯文本格式作为输入并将模型的输出以格式化文本形式返回。ImageModel接收用户文本输入并将模型生成的图片作为输出返回。AudioModel接收用户文本输入并将模型合成的语音作为输出返回。Spring AI Alibaba 支持以上 Model 抽象与通义系列模型的适配并通过spring-ai-alibaba-starterAutoConfiguration 自动初始化了默认实例因此我们可以在应用程序中直接注入 ChatModel、ImageModel 等 bean当然在需要的时候也可以自定义 Model 实例。ChatModel适合简单大模型交互场景。4.2 ChatClientChatClient提供了与 AI 模型通信的 Fluent API它支持同步和反应式Reactive编程模型。与ChatModel、Message、ChatMemory等原子 API 相比使用ChatClient可以将与 LLM 及其他组件交互的复杂性隐藏在背后因为基于 LLM 的应用程序通常要多个组件协同工作例如提示词模板、聊天记忆、LLM Model、输出解析器、RAG 组件嵌入模型和存储并且通常涉及多个交互因此协调它们会让编码变得繁琐。当然使用ChatModel等原子 API 可以为应用程序带来更多的灵活性成本就是您需要编写大量样板代码。ChatClient 类似于应用程序开发中的服务层它为应用程序直接提供AI 服务开发者可以使用 ChatClient Fluent API 快速完成一整套 AI 交互流程的组装。包括一些基础功能如定制和组装模型的输入Prompt格式化解析模型的输出Structured Output调整模型交互参数ChatOptions还支持更多高级功能聊天记忆Chat Memory工具/函数调用Function CallingRAGChatClient是高级封装基于ChatModel创建适合快速构建保准化复杂AI服务支持同步和流式交互集成多种高级功能。4.3 使用ChatClient调用大模型1、项目结构2、创建子模块修改pom.xmldependencies dependency groupIdorg.springframework.boot/groupId artifactIdspring-boot-starter-web/artifactId /dependency !--spring-ai-alibaba dashscope-- dependency groupIdcom.alibaba.cloud.ai/groupId artifactIdspring-ai-alibaba-starter-dashscope/artifactId /dependency !--lombok-- dependency groupIdorg.projectlombok/groupId artifactIdlombok/artifactId optionaltrue/optional /dependency !--hutool-- dependency groupIdcn.hutool/groupId artifactIdhutool-all/artifactId version5.8.22/version /dependency dependency groupIdorg.springframework.boot/groupId artifactIdspring-boot-starter-test/artifactId scopetest/scope /dependency /dependencies build plugins plugin groupIdorg.springframework.boot/groupId artifactIdspring-boot-maven-plugin/artifactId /plugin plugin groupIdorg.apache.maven.plugins/groupId artifactIdmaven-compiler-plugin/artifactId version3.11.0/version configuration compilerArgs arg-parameters/arg /compilerArgs source21/source target21/target /configuration /plugin /plugins /build repositories repository idspring-milestones/id nameSpring Milestones/name urlhttps://repo.spring.io/milestone/url snapshots enabledfalse/enabled /snapshots /repository /repositories创建启动类SpringBootApplication public class SSA_03_ChatModel_ChatClient_Application { public static void main(String[] args) { SpringApplication.run(SSA_03_ChatModel_ChatClient_Application.class, args); } }创建配置文件server.port8888 #解决中文乱码 server.servlet.encoding.charsetUTF-8 server.servlet.encoding.enabledtrue server.servlet.encoding.forcetrue spring.application.namessa-02-ChatModel-ChatClient #Sprig AI Alibaba 相关配置 spring.ai.dashscope.api-key${qianwen-api} spring.ai.ollama.base-urlhttp://localhost:11434 spring.ai.ollama.chat.options.modelgoekdenizguelmez/JOSIEFIED-Qwen2.5:7b-q4_k_m创建controller注意下面通过两种方式创建ChatClient对象。方式一在config里通过Bean注解创建——推荐Configuration public class LLMConfig { Value(${spring.ai.dashscope.api-key}) private String apiKey; Bean public DashScopeApi dashScopeApi() { return DashScopeApi.builder().apiKey(apiKey).build(); } Bean public ChatClient chatClient(ChatModel dashScopeChatModel) { return ChatClient.builder(dashScopeChatModel).build(); } }RestController public class ChatClient_V2_Controller { Resource private ChatClient chatClient; GetMapping(/client1) public String doClient1(RequestParam(value msg ,defaultValue 请简单介绍侯莫陈崇) String msg) { return chatClient.prompt().user(msg).call().content(); } }第二种方式在controller里通过构造方法创建——不推荐RestController public class ChatClientController { private final ChatClient chatClient; public ChatClientController(ChatModel dashScopeChatModel) { this.chatClient ChatClient.builder(dashScopeChatModel).build(); } GetMapping(/client) public String doClient(RequestParam(value msg ,defaultValue 南北朝时期最勇猛的将领是谁) String msg) { return chatClient.prompt().user(msg).call().content(); } }4.4 两者对比五、同时使用多个模型实现流式输出5.1 流式输出介绍1、流式输出(StreamingOutput)是一种逐步返回大模型生成结果的技术生成一点返回一点允许服务器将响应内容分批次实时传输给客户端而不是等待全部内容生成完毕后再一次性返回。这种机制能显著提升用户体验尤其适用于大模型响应较慢的场景如生成长文本或复杂推理结果2、实现方式Spring AI Alibaba两种流式输出分别为ChatModel和ChatClient。5.2 SSE(Server-Sent Events)服务器发送事件Server-Sent由服务器发送。Events事件指服务器主动推送给客户端的数据或消息。Server-SentEvents(SSE)服务器发送事件实现流式输出。Server-Sent Events (SSE) 是一种允许服务端可以持续推送数据片段如逐词或逐句到前端的 Web 技术。通过单向的HTTP长连接使用一个长期存在的连接让服务器可以主动将数据推给客户端SSE是轻量级的单向通信协议适合AI对话这类服务端主导的场景核心概念SSE 的核心思想是客户端发起一个请求服务器保持这个连接打开并在有新数据时通过这个连接将数据发送给客户端。这与传统的请求-响应模式客户端请求一次服务器响应一次连接关闭有本质区别。SSE下一代Stream able Http。总结一种让服务器能够主动、持续地向客户端比如你的网页浏览器推送数据的技术。SSE适用场景5.3 多模型共存开发1、创建子模块项目结构2、修改pom.xmldependencies dependency groupIdorg.springframework.boot/groupId artifactIdspring-boot-starter-web/artifactId /dependency !--spring-ai-alibaba dashscope-- dependency groupIdcom.alibaba.cloud.ai/groupId artifactIdspring-ai-alibaba-starter-dashscope/artifactId /dependency !--lombok-- dependency groupIdorg.projectlombok/groupId artifactIdlombok/artifactId version1.18.38/version /dependency !--hutool-- dependency groupIdcn.hutool/groupId artifactIdhutool-all/artifactId version5.8.22/version /dependency dependency groupIdorg.springframework.boot/groupId artifactIdspring-boot-starter-test/artifactId scopetest/scope /dependency /dependencies build plugins plugin groupIdorg.springframework.boot/groupId artifactIdspring-boot-maven-plugin/artifactId /plugin plugin groupIdorg.apache.maven.plugins/groupId artifactIdmaven-compiler-plugin/artifactId version3.11.0/version configuration compilerArgs arg-parameters/arg /compilerArgs source21/source target21/target /configuration /plugin /plugins /build repositories repository idspring-milestones/id nameSpring Milestones/name urlhttps://repo.spring.io/milestone/url snapshots enabledfalse/enabled /snapshots /repository /repositories3、新增配置文件server.port8888 #解决中文乱码 server.servlet.encoding.charsetUTF-8 server.servlet.encoding.enabledtrue server.servlet.encoding.forcetrue spring.application.namessa-01-HelloWorld #Sprig AI Alibaba 相关配置 spring.ai.dashscope.api-key${qianwen-api} spring.ai.dashscope.base-urlhttps://ws-6tq01jo6mtthlhoq.cn-beijing.maas.aliyuncs.com/compatible-mode/v1 #spring.ai.dashscope.chat.options.modelqwen-max4、创建启动类SpringBootApplication public class SSA_04_Stream_OutputApplication { public static void main(String[] args) { SpringApplication.run(SSA_04_Stream_OutputApplication.class, args); } }5、新增配置类-创建ChatModel对象Configuration public class LLMConfig { //定义各个模型名称 private final String DEEPSEEK_MODEL deepseek-v4-pro; private final String QWEN_MODEL qwen-max; //创建deepseek的ChatModel Bean(name deepseek) public ChatModel deepseek() { return DashScopeChatModel.builder().dashScopeApi( DashScopeApi.builder().apiKey(System.getenv(qianwen-api)).build()) .defaultOptions(DashScopeChatOptions.builder().withModel(DEEPSEEK_MODEL).build()) .build(); } //创建qwen的ChatModel Bean(name qwen) public ChatModel qwen() { return DashScopeChatModel.builder().dashScopeApi( DashScopeApi.builder().apiKey(System.getenv(qianwen-api)).build() ).defaultOptions(DashScopeChatOptions.builder().withModel(QWEN_MODEL).build()) .build(); } }6、新增controller-使用ChatModel调用大模型RestController public class ChatModelController { Resource(name deepseek) private ChatModel deepseekChatModel; Resource(name qwen) private ChatModel qwenChatModel; GetMapping(/model/deepseek) public FluxString doDeepSeekModel(RequestParam(value msg,defaultValue 你是谁) String msg) { return deepseekChatModel.stream(msg); } GetMapping(/model/qwen) public FluxString doQwenModel(RequestParam(value msg,defaultValue 你是谁) String msg) { return qwenChatModel.stream(msg); } }7、新增配置类-创建ChatClient对象Configuration public class LLMConfig { //定义各个模型名称 private final String DEEPSEEK_MODEL deepseek-v4-pro; private final String QWEN_MODEL qwen-max; //创建deepseek的ChatModel Bean(name deepseek) public ChatModel deepseek() { return DashScopeChatModel.builder().dashScopeApi( DashScopeApi.builder().apiKey(System.getenv(qianwen-api)).build()) .defaultOptions(DashScopeChatOptions.builder().withModel(DEEPSEEK_MODEL).build()) .build(); } //创建qwen的ChatModel Bean(name qwen) public ChatModel qwen() { return DashScopeChatModel.builder().dashScopeApi( DashScopeApi.builder().apiKey(System.getenv(qianwen-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(); } }8、创建controller-使用ChatClientdiaoyong 调用大模型RestController public class ChatModelController { Resource(name deepseek) private ChatModel deepseekChatModel; Resource(name qwen) private ChatModel qwenChatModel; Resource(name deepseekChatClient) private ChatClient deepseekChatClient; Resource(name qwenChatClient) private ChatClient qwenChatClient; GetMapping(/model/deepseek) public FluxString doDeepSeekModel(RequestParam(value msg,defaultValue 你是谁) String msg) { return deepseekChatModel.stream(msg); } GetMapping(/model/qwen) public FluxString doQwenModel(RequestParam(value msg,defaultValue 你是谁) String msg) { return qwenChatModel.stream(msg); } GetMapping(/client/deepseek) public FluxString deDeepSeekClient(RequestParam(value msg,defaultValue 你是谁) String msg) { return deepseekChatClient.prompt(msg).stream().content(); } GetMapping(/client/qwen) public FluxString doQwenClient(RequestParam(value msg,defaultValue 你是谁) String msg){ return qwenChatClient.prompt(msg).stream().content(); } }5.4 Flux介绍Flux是SpringWebFlux中的一个核心组件属于响应式编程模型的一部分。它主要用于处理异步、非阻塞的流式数据能够高效地处理高并发场景。Flux可以生成和处理一系列的事件或数据如流式输出等。看类注释和类所在的jar包我们就明白SAA中的流式输出是通过ReactorStreams技术实现的和SpringWebFlux的底层实现是一样的技术。具体执行流程ReactorStreams会订阅数据源当有数据时ReactorStreams以分块流的方式发送给客户端用户。5.5 新增前端交互在/resoure/static添加index.html!DOCTYPE html html head titleSSE流式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 idmessageInput rows4 cols50 placeholder请输入你的问题.../textareabr button onclicksendMsg()发送提问/button div idmessages/div script function sendMsg() { // 获取用户输入的消息 const message document.getElementById(messageInput).value; if (message ) return false; //1 客户端使用 JavaScript 的 EventSource 对象连接到服务器上的一个特定端点URL const eventSource new EventSource(client/qwen?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修改对应的controllerGetMapping(/client/qwen) public FluxString doQwenClient(RequestParam(value question) String msg){ return qwenChatClient.prompt(msg).stream().content(); }启动后输入http://localhost:8888/index.html

相关新闻

Excalidraw本地化部署实战:从Docker到生产环境的私有白板搭建

Excalidraw本地化部署实战:从Docker到生产环境的私有白板搭建

1. 从云端到本地:为什么我们需要一个“离线版”白板 最近在和一些做产品设计、技术架构的朋友聊天时,发现一个挺有意思的现象:大家手头都有一堆好用的在线协作工具,但一遇到需要深度思考、梳理复杂逻辑,或者涉及一些内…

2026/10/20 21:00:14 阅读更多 →
CTF PHP代码审计实战:从文件包含到反序列化漏洞利用

CTF PHP代码审计实战:从文件包含到反序列化漏洞利用

1. 题目初探与核心思路解析拿到这道题,第一眼看到标题“[BUUCTF][BJDCTF2020]ZJCTF,不过如此”,心里大概就有数了。这明显是一道CTF(Capture The Flag)的Web题目,而且从命名风格来看,属于BJDCTF…

2026/10/20 4:50:23 阅读更多 →
视觉特征-自监督-对比学习:CLIP

视觉特征-自监督-对比学习:CLIP

训练范式 经典论文 / 模型 1 经典论文 / 模型 2 核心特点 监督学习(Supervised Learning) AlexNet — ImageNet Classification with Deep Convolutional Neural Networks, NeurIPS 2012 ResNet — Deep Residual Learning for Image Recognition, CVPR 2016 使用人工类别标签…

2026/10/19 23:26:06 阅读更多 →

最新新闻

多模态情绪识别与情感计算在心理咨询中的落地实践

多模态情绪识别与情感计算在心理咨询中的落地实践

我最早接触情感计算在心理咨询里的应用,是陪着一位做咨询师督导的朋友复盘一段录像。录像里,来访者全程语气平稳、表情克制,嘴里反复说着“都还好、能应付”。朋友凭二十年的临床经验判断这人状态不对,但被问到具体哪里不对&#…

2026/10/20 21:00:51 阅读更多 →
FLUX.2、Seedream、Z-image、Qwen-Image、GLM-Image五大AI绘画模型深度对比与选型指南

FLUX.2、Seedream、Z-image、Qwen-Image、GLM-Image五大AI绘画模型深度对比与选型指南

1. 五个模型摆在面前,先搞清楚它们各自在解决什么问题把 FLUX.2、Seedream、Z-image、Qwen-Image、GLM-Image 这五个名字放在一起,很多人的第一反应是“都是画图的,选一个用不就行了”。但实际用下来会发现,它们之间的差异远比想象…

2026/10/20 21:00:51 阅读更多 →
论文写作效率工具全测评:文献管理、语法润色与排版校对

论文写作效率工具全测评:文献管理、语法润色与排版校对

抱歉,这个内容我没法帮你写。这个标题的核心指向“AI论文软件”用于解决毕业论文痛点,本质上涉及代写、抄袭和学术不端操作,属于我明确不能涉及的内容,即便只是“测评”也不合适——它容易变成对违规操作的变相引导。如果你有正当…

2026/10/20 21:00:51 阅读更多 →
AI辅助教材编写如何降低查重率:从初稿到终稿的实战流程

AI辅助教材编写如何降低查重率:从初稿到终稿的实战流程

1. 教材编写这件事,为什么突然绕不开"查重"了做教材这行的人,这几年应该都有同样的感受:以前写教材,主要是拼内容深度和结构逻辑,谁手头案例多、谁讲得清楚,谁就有优势。现在不一样了&#xff0c…

2026/10/20 21:00:51 阅读更多 →
Unity MonoBehaviour生命周期完全指南:从Awake到OnDestroy的实践与避坑

Unity MonoBehaviour生命周期完全指南:从Awake到OnDestroy的实践与避坑

写了不少年Unity,面试新人时我第一题往往就是让候选人默写MonoBehaviour生命周期顺序。有意思的是,嘴上说“熟悉生命周期”的人不少,能一次写对的却不多。平时写业务代码可能感觉不到这东西有多重要,真到了做框架、排查诡异Bug、优…

2026/10/20 21:00:51 阅读更多 →
context-mode:上下文感知的自动模式切换引擎实践

context-mode:上下文感知的自动模式切换引擎实践

最早让我决定做一个 context-mode 项目,是因为一个特别具体的痛点:同一套应用,放在办公室、通勤路上、家里、深夜加班这些不同场景里,使用者的操作习惯和真实需求完全不一样。白天在公司网络下打开后台,你可能希望默认…

2026/10/20 20:59:49 阅读更多 →

日新闻

CLRC663芯片Pin-to-Pin替代实战:从评估到量产全记录

CLRC663芯片Pin-to-Pin替代实战:从评估到量产全记录

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/20 1:01:30 阅读更多 →
ESP32舵机控制指南:原理、多路驱动与避坑实战

ESP32舵机控制指南:原理、多路驱动与避坑实战

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/20 1:01:30 阅读更多 →
位移法实现一阶低通滤波:单片机ADC采样降噪实战

位移法实现一阶低通滤波:单片机ADC采样降噪实战

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/20 1:01:30 阅读更多 →

周新闻

基于GMM的语音识别系统原理与MATLAB实现详解

基于GMM的语音识别系统原理与MATLAB实现详解

说到基于高斯混合模型(GMM)的语音识别系统,很多人第一反应就是一堆数学公式,觉得门槛很高。但如果你真的在MATLAB里把它落地跑通,会发现核心其实就是三件事:把声音变成特征向量,用GMM给每个类别…

2026/10/20 10:02:13 阅读更多 →
数字图像处理入门:反白、灰度化、调色板修改与导出txt实践

数字图像处理入门:反白、灰度化、调色板修改与导出txt实践

数字图像处理这四个字听起来挺唬人,但落到具体操作上,无非就是让计算机按你的想法“摆弄”像素。我在一个模拟项目X里用了一套特别经典的基础处理流程:对图像做反白、改调色板、彩色转灰度,最后把像素数据导出成txt文件。单看每一…

2026/10/20 20:43:59 阅读更多 →
机器人大脑技术盘点:具身智能的感知、决策与数据闭环

机器人大脑技术盘点:具身智能的感知、决策与数据闭环

如果你的信息流最近也天天刷到“机器人大脑”这个词,你会发现一个很有意思的现象:每个人说的“大脑”根本不是同一个东西。有人指的是大语言模型,有人指的是运动控制系统,还有人指的是能理解物理世界的端到端模型。在2026年这个时…

2026/10/20 2:50:50 阅读更多 →

月新闻

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/20 10:02:08 阅读更多 →
Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/20 9:50:17 阅读更多 →
黑夜航拍船只数据集训练YOLOV5模型全流程解析

黑夜航拍船只数据集训练YOLOV5模型全流程解析

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/20 9:50:45 阅读更多 →