2026/5/18 12:42:21
网站建设
项目流程
买机箱网站,可以购买网站空间的网站,怎么做网站推广图片,丰台网站建设推广之前的文章里已经教会了大家怎么在本地安装ollama以及运行模型。接下来要开始做真正的JAVA AI应用了#xff0c;大家准备好了吗#xff1f;
最近玩本地大模型的朋友越来越多#xff0c;但大多数人都是在命令行里和模型对话。说实话#xff0c;这种方式有点反人类
——体验远…之前的文章里已经教会了大家怎么在本地安装ollama以及运行模型。接下来要开始做真正的JAVA AI应用了大家准备好了吗最近玩本地大模型的朋友越来越多但大多数人都是在命令行里和模型对话。说实话这种方式有点反人类——体验远不如在网页里直接聊天舒服。那有没有办法既能用上本地的 Ollama 模型又能做一个像 ChatGPT 那样的网页回答还能一字一字“流”出来答案是可以而且很简单。今天我就分享一下如何用Spring AI 1.0.0-M8搭配Ollama快速搭建一个流式问答的 HTML 页面。为什么要搞一个流式页面命令行交互虽然轻量但回答出来是“一坨文字”不直观不方便做界面交互更别提加点样式给非技术朋友用劝退指数 100%。而流式问答的页面回答会像打字机一样逐字出现体验更自然网页界面简单易用谁都能上手后面可以随便扩展功能比如多轮对话、模型切换、知识库对接。一句话有了网页才是真正可用的产品形态。Ollama 作为本地模型引擎Ollama 的定位很清晰让本地大模型跑起来尽可能简单。安装后会在本地开一个服务默认端口 11434你只要ollama run qwen:14b模型就能下载并运行Spring AI 刚好可以无缝调用这个本地服务。所以这次我们不需要复杂的配置只要把 Spring Boot 和 Ollama“连起来”就能玩。Spring AI 1.0.0-M8 的作用Spring AI 相当于给大模型封了一层Java 接口我们不用管复杂的 HTTP 请求也不用自己拼 JSON直接用现成的客户端。最关键的是它支持流式输出。也就是说模型生成的内容不会一次性返回而是源源不断推过来你前端就能一字一字显示。这一点正好满足我们要的“网页打字机”体验。项目思路整个流程其实很清晰Ollama 本地服务→ 提供大模型 API。Spring Boot 应用→ 作为中间层请求模型并做流式输出。HTML 页面→ 前端通过EventSource订阅流式回答并实时渲染。换句话说Spring Boot 是“大脑”Ollama 是“引擎”HTML 是“界面”。实现效果浏览器里打开一个简单的聊天页面输入问题比如“给我写一首关于秋天的诗”回答会像 ChatGPT 一样一字一字流出来体验完全离线不花一分钱。-------------------------好了废话不多说上代码-----------------一、准备工作首先确保这一步没有的请到我之前的文章里学习。你已经安装好Ollama并能正常运行模型例如ollama run qwen:14b如果能在命令行和模型对话就说明 Ollama 环境没问题二、Spring Boot 项目依赖在pom.xml中加入 Spring AI 的 Ollama starter以下是我的配置大家可以复制参考用的是JDK17?xml version1.0 encodingUTF-8? project xmlnshttp://maven.apache.org/POM/4.0.0 xmlns:xsihttp://www.w3.org/2001/XMLSchema-instance xsi:schemaLocationhttp://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd modelVersion4.0.0/modelVersion groupIdcom.botongsoft/groupId artifactIdbotong-ai/artifactId version0.0.1-SNAPSHOT/version namebotong-ai/name descriptionSpring AI Ollama Demo/description properties java.version17/java.version spring-boot.version3.2.5/spring-boot.version spring-ai.version1.0.0-M8/spring-ai.version project.build.sourceEncodingUTF-8/project.build.sourceEncoding project.reporting.outputEncodingUTF-8/project.reporting.outputEncoding /properties dependencies !-- Spring Boot Web (可选: 如果只用 WebFlux 可删) -- dependency groupIdorg.springframework.boot/groupId artifactIdspring-boot-starter-web/artifactId /dependency !-- WebFlux: 必须用于流式输出 (SSE) -- dependency groupIdorg.springframework.boot/groupId artifactIdspring-boot-starter-webflux/artifactId /dependency !-- Spring AI Ollama -- dependency groupIdorg.springframework.ai/groupId artifactIdspring-ai-starter-model-ollama/artifactId version${spring-ai.version}/version /dependency !-- Lombok (可选) -- dependency groupIdorg.projectlombok/groupId artifactIdlombok/artifactId optionaltrue/optional /dependency !-- 测试 -- dependency groupIdorg.springframework.boot/groupId artifactIdspring-boot-starter-test/artifactId scopetest/scope /dependency /dependencies dependencyManagement dependencies !-- Spring Boot BOM -- dependency groupIdorg.springframework.boot/groupId artifactIdspring-boot-dependencies/artifactId version${spring-boot.version}/version typepom/type scopeimport/scope /dependency /dependencies /dependencyManagement build plugins !-- 编译插件 -- plugin groupIdorg.apache.maven.plugins/groupId artifactIdmaven-compiler-plugin/artifactId version3.8.1/version configuration source${java.version}/source target${java.version}/target encodingUTF-8/encoding compilerArgs arg-parameters/arg /compilerArgs /configuration /plugin !-- Spring Boot 打包插件 -- plugin groupIdorg.springframework.boot/groupId artifactIdspring-boot-maven-plugin/artifactId version${spring-boot.version}/version /plugin /plugins /build repositories !-- Spring Milestone 仓库: 用于 Spring AI M 版本 -- repository idspring-milestones/id nameSpring Milestone Repository/name urlhttps://repo.spring.io/milestone/url /repository /repositories /project三、配置 application.properties在配置文件里声明 Ollama 的地址和默认模型server.port9999 spring.ai.ollama.chat.modelqwen2.5:14b spring.ai.ollama.chat.options.num-predict4096 spring.ai.ollama.chat.options.num-ctx8192四、编写后端接口这里是关键部分我们要写一个支持流式输出 记忆的接口。import lombok.RequiredArgsConstructor; import org.springframework.ai.chat.memory.ChatMemory; import org.springframework.ai.chat.messages.Message; import org.springframework.ai.chat.messages.UserMessage; import org.springframework.ai.chat.model.ChatModel; import org.springframework.ai.chat.prompt.Prompt; import org.springframework.http.MediaType; import org.springframework.web.bind.annotation.*; import reactor.core.publisher.Flux; import java.util.List; import java.util.Map; RestController RequiredArgsConstructor RequestMapping(/ai) public class OllamaController { private final ChatModel chatModel; // 或 StreamingChatModel private final ChatMemory chatMemory; // 注入内存管理器 PostMapping(value /chat, consumes MediaType.APPLICATION_JSON_VALUE, produces MediaType.TEXT_EVENT_STREAM_VALUE) public FluxString chat(RequestBody MapString, String body) { String conversationId body.getOrDefault(conversationId, ChatMemory.DEFAULT_CONVERSATION_ID); String messageText body.getOrDefault(message, ); // 1. 构建用户消息 Message userMessage new UserMessage(messageText); // 2. 加入记忆 chatMemory.add(conversationId, userMessage); // 3. 获取上下文历史包括用户消息和 AI 回复 ListMessage history chatMemory.get(conversationId); // 4. 用历史构建 Prompt Prompt prompt new Prompt(history); // 5. 调用模型流式返回 return chatModel.stream(prompt) .map(resp - { String text resp.getResult().getOutput().getText(); // 记得把 AI 回复也加入记忆 chatMemory.add(conversationId, resp.getResult().getOutput()); return text; }); } }五、HTML 页面写一个简单的前端页面放到src/main/resources/static/index.html订阅流式回答!DOCTYPE html html langzh head meta charsetUTF-8 / titleMarkdown 渲染 SSE 示例/title !-- 你指定的版本 -- script srcknowledge/markdown.js/script style body { font-family: system-ui, -apple-system, Segoe UI, Roboto, Arial; margin: 20px; background:#f5f7fb; } h2 { margin: 0 0 10px; } .toolbar { display:flex; gap:10px; align-items:center; } #messageInput { flex:1; height:36px; padding:0 10px; border:1px solid #d0d7de; border-radius:8px; background:#fff; } #sendBtn { height:36px; padding:0 14px; border:none; border-radius:8px; background:#2263ff; color:#fff; font-weight:600; cursor:pointer; } #sendBtn:hover { filter:brightness(.95); } #output { margin-top:12px; background:#fff; border:1px solid #e5e7eb; border-radius:8px; padding:14px; height:520px; overflow:auto; } /* 一点常用的 Markdown 样式 */ #output h1 { font-size:22px; margin: .8em 0 .4em; } #output h2 { font-size:18px; margin: .8em 0 .4em; border-bottom:1px solid #eee; padding-bottom:4px; } #output p { margin: .5em 0; } #output ul { padding-left: 1.2em; } #output li { margin: .25em 0; } #output pre { background:#0b1220; color:#e5e7eb; padding:10px; border-radius:6px; overflow-x:auto; } #output code { background:#f6f8fa; border-radius:4px; padding:0 4px; } /style /head body h2Markdown 渲染回答/h2 div classtoolbar input idmessageInput typetext placeholder请输入你的问题服务器返回 Markdown 文本 / button idsendBtn发送/button /div div idoutput请输入问题并点击发送/div script const input document.getElementById(messageInput); const outputDiv document.getElementById(output); const sendBtn document.getElementById(sendBtn); marked.setOptions({ gfm: true, breaks: true, smartLists: true }); let markdownBuffer ; // 把流式拼起来后做“轻量归一化”解决 // 1) “markdown# 标题”前缀2) “##/###/1./-”不在行首3) “#”后没空格。 function normalizeMd(md) { if (!md) return ; // HTML 实体反转义 md md.replace(/ /g, ) .replace(//g, ) .replace(//g, ) .replace(//g, ); // 去掉开头的 markdown 或 md md.replace(/^(?:markdown)?\s*/i, ); // 去掉结尾的 md md.replace(/$/i, ); // 去掉开头的 markdown# md md.replace(/^(\s*)markdown\s*#/i, $1#); // 把行中间的标题/列表标记换到行首 md md.replace(/(?!^)(?!\n)\s*(#{1,6}\s)/g, \n$1); md md.replace(/(?!^)(?!\n)\s*((?:\d[.)]|[-*•·])\s)/g, \n$1); // # 后补空格 md md.replace(/(^|\n)(\s{0,3}#{1,6})([^\s#])/g, $1$2 $3); // 合并多余空行 md md.replace(/\n{3,}/g, \n\n); return md; } sendBtn.onclick async () { const message input.value.trim(); if (!message) return; outputDiv.textContent 连接中…; markdownBuffer ; const resp await fetch(/ai/chat, { method: POST, headers: { Content-Type: application/json, Accept: text/event-stream }, body: JSON.stringify({ message,conversationId:xxx1 }) }); if (!resp.ok || !resp.body) { outputDiv.textContent 连接失败; return; } outputDiv.innerHTML ; // 清空占位 const reader resp.body.getReader(); const decoder new TextDecoder(); let sseBuffer ; while (true) { const { value, done } await reader.read(); if (done) break; sseBuffer decoder.decode(value, { stream: true }); // 逐行解析 SSE每行形如 data: xxx let idx; while ((idx sseBuffer.indexOf(\n)) 0) { const line sseBuffer.slice(0, idx); sseBuffer sseBuffer.slice(idx 1); if (!line) continue; if (line.startsWith(data:)) { const chunk line.slice(5); // 不人为加 \n markdownBuffer chunk; // 实时渲染带归一化 const mdFixed normalizeMd(markdownBuffer); outputDiv.innerHTML marked.parse(mdFixed); outputDiv.scrollTop outputDiv.scrollHeight; } } } // 收流后再做一次最终归一化与渲染确保尾部语法也被吃到 const mdFinal normalizeMd(markdownBuffer); outputDiv.innerHTML marked.parse(mdFinal); }; /script /body /html六、运行与效果启动 Spring Boot 项目访问http://127.0.0.1:9999打开 HTML 页面先输入“我叫小王”再问“记住我叫什么吗” → 模型会回答“小王”。这就是多轮对话记忆的效果七、总结通过这一步我们完成了Spring AI 调用本地 Ollama流式输出回答会话记忆功能。这已经是一个雏形版的本地 ChatGPT。下一步你可以扩展多用户会话隔离“清空记忆”按钮对接知识库实现更强大的问答。想入门 AI 大模型却找不到清晰方向备考大厂 AI 岗还在四处搜集零散资料别再浪费时间啦2025 年AI 大模型全套学习资料已整理完毕从学习路线到面试真题从工具教程到行业报告一站式覆盖你的所有需求现在全部免费分享扫码免费领取全部内容一、学习必备100本大模型电子书26 份行业报告 600 套技术PPT帮你看透 AI 趋势想了解大模型的行业动态、商业落地案例大模型电子书这份资料帮你站在 “行业高度” 学 AI1. 100本大模型方向电子书2. 26 份行业研究报告覆盖多领域实践与趋势报告包含阿里、DeepSeek 等权威机构发布的核心内容涵盖职业趋势《AI 职业趋势报告》《中国 AI 人才粮仓模型解析》商业落地《生成式 AI 商业落地白皮书》《AI Agent 应用落地技术白皮书》领域细分《AGI 在金融领域的应用报告》《AI GC 实践案例集》行业监测《2024 年中国大模型季度监测报告》《2025 年中国技术市场发展趋势》。3. 600套技术大会 PPT听行业大咖讲实战PPT 整理自 2024-2025 年热门技术大会包含百度、腾讯、字节等企业的一线实践安全方向《端侧大模型的安全建设》《大模型驱动安全升级腾讯代码安全实践》产品与创新《大模型产品如何创新与创收》《AI 时代的新范式构建 AI 产品》多模态与 Agent《Step-Video 开源模型视频生成进展》《Agentic RAG 的现在与未来》工程落地《从原型到生产AgentOps 加速字节 AI 应用落地》《智能代码助手 CodeFuse 的架构设计》。二、求职必看大厂 AI 岗面试 “弹药库”300 真题 107 道面经直接抱走想冲字节、腾讯、阿里、蔚来等大厂 AI 岗这份面试资料帮你提前 “押题”拒绝临场慌1. 107 道大厂面经覆盖 Prompt、RAG、大模型应用工程师等热门岗位面经整理自 2021-2025 年真实面试场景包含 TPlink、字节、腾讯、蔚来、虾皮、中兴、科大讯飞、京东等企业的高频考题每道题都附带思路解析2. 102 道 AI 大模型真题直击大模型核心考点针对大模型专属考题从概念到实践全面覆盖帮你理清底层逻辑3. 97 道 LLMs 真题聚焦大型语言模型高频问题专门拆解 LLMs 的核心痛点与解决方案比如让很多人头疼的 “复读机问题”三、路线必明 AI 大模型学习路线图1 张图理清核心内容刚接触 AI 大模型不知道该从哪学起这份「AI大模型 学习路线图」直接帮你划重点不用再盲目摸索路线图涵盖 5 大核心板块从基础到进阶层层递进一步步带你从入门到进阶从理论到实战。L1阶段:启航篇丨极速破界AI新时代L1阶段了解大模型的基础知识以及大模型在各个行业的应用和分析学习理解大模型的核心原理、关键技术以及大模型应用场景。L2阶段攻坚篇丨RAG开发实战工坊L2阶段AI大模型RAG应用开发工程主要学习RAG检索增强生成包括Naive RAG、Advanced-RAG以及RAG性能评估还有GraphRAG在内的多个RAG热门项目的分析。L3阶段跃迁篇丨Agent智能体架构设计L3阶段大模型Agent应用架构进阶实现主要学习LangChain、 LIamaIndex框架也会学习到AutoGPT、 MetaGPT等多Agent系统打造Agent智能体。L4阶段精进篇丨模型微调与私有化部署L4阶段大模型的微调和私有化部署更加深入的探讨Transformer架构学习大模型的微调技术利用DeepSpeed、Lamam Factory等工具快速进行模型微调并通过Ollama、vLLM等推理部署框架实现模型的快速部署。L5阶段专题集丨特训篇 【录播课】四、资料领取全套内容免费抱走学 AI 不用再找第二份不管你是 0 基础想入门 AI 大模型还是有基础想冲刺大厂、了解行业趋势这份资料都能满足你现在只需按照提示操作就能免费领取扫码免费领取全部内容2025 年想抓住 AI 大模型的风口别犹豫这份免费资料就是你的 “起跑线”