ollama调用Phi-4-mini-reasoning代码实例异步流式响应前端实时渲染Demo1. 项目概述与学习目标今天我们来做一个实用的技术Demo如何使用ollama调用Phi-4-mini-reasoning模型实现异步流式响应并在前端实时渲染结果。这个项目特别适合想要了解大模型实时交互开发的开发者。通过本教程你将学会如何通过API调用ollama部署的Phi-4-mini-reasoning模型实现异步流式响应的技术方案前端实时渲染流式数据的方法构建一个完整的问答演示应用不需要深厚的大模型背景只要有一些基础的编程知识就能跟着完成这个项目。2. 环境准备与项目搭建2.1 基础环境要求在开始之前确保你的开发环境满足以下要求Node.js 16.0 或更高版本已安装并运行ollama且部署了Phi-4-mini-reasoning模型现代浏览器Chrome、Firefox、Safari等2.2 创建项目目录结构首先创建一个新的项目文件夹然后建立以下目录结构phi4-mini-demo/ ├── backend/ # 后端代码 │ ├── server.js │ └── package.json ├── frontend/ # 前端代码 │ ├── index.html │ ├── style.css │ └── app.js └── README.md2.3 初始化后端项目进入backend目录初始化Node.js项目cd backend npm init -y npm install express cors node-fetch2这里我们使用Express作为后端框架cors处理跨域请求node-fetch用于调用ollama的API。3. 后端API实现3.1 创建Express服务器在后端的server.js文件中我们设置一个简单的Express服务器const express require(express); const cors require(cors); const fetch require(node-fetch); const app express(); const PORT 3001; // 中间件设置 app.use(cors()); app.use(express.json()); // Ollama API配置 const OLLAMA_HOST http://localhost:11434; const MODEL_NAME phi-4-mini-reasoning:latest; // 健康检查端点 app.get(/health, (req, res) { res.json({ status: OK, model: MODEL_NAME }); }); app.listen(PORT, () { console.log(后端服务器运行在 http://localhost:${PORT}); console.log(已配置模型: ${MODEL_NAME}); });3.2 实现流式API端点这是核心部分我们创建一个处理流式响应的API端点// 流式问答端点 app.post(/api/chat/stream, async (req, res) { const { message } req.body; if (!message) { return res.status(400).json({ error: 请输入消息内容 }); } try { // 设置SSE响应头 res.setHeader(Content-Type, text/event-stream); res.setHeader(Cache-Control, no-cache); res.setHeader(Connection, keep-alive); res.setHeader(Access-Control-Allow-Origin, *); // 调用Ollama的流式API const response await fetch(${OLLAMA_HOST}/api/generate, { method: POST, headers: { Content-Type: application/json, }, body: JSON.stringify({ model: MODEL_NAME, prompt: message, stream: true, }), }); if (!response.ok) { throw new Error(Ollama API错误: ${response.statusText}); } // 创建可读流 const reader response.body.getReader(); const decoder new TextDecoder(); // 流式传输数据 while (true) { const { done, value } await reader.read(); if (done) { res.write(data: [DONE]\n\n); res.end(); break; } const chunk decoder.decode(value); const lines chunk.split(\n).filter(line line.trim() ! ); for (const line of lines) { try { const data JSON.parse(line); if (data.response) { // 发送SSE格式的数据 res.write(data: ${JSON.stringify({ content: data.response })}\n\n); } } catch (e) { console.log(解析JSON失败:, line); } } } } catch (error) { console.error(API调用错误:, error); if (!res.headersSent) { res.status(500).json({ error: 内部服务器错误 }); } } });4. 前端界面开发4.1 HTML结构设计在前端的index.html中我们创建一个简洁的聊天界面!DOCTYPE html html langzh-CN head meta charsetUTF-8 meta nameviewport contentwidthdevice-width, initial-scale1.0 titlePhi-4-mini-reasoning 演示/title link relstylesheet hrefstyle.css /head body div classcontainer header h1Phi-4-mini-reasoning 智能问答/h1 p基于ollama的流式响应演示/p /header div classchat-container div idchat-messages classmessages/div div classinput-area textarea idmessage-input placeholder请输入您的问题... rows3 /textarea button idsend-button发送/button button idclear-button清空对话/button /div div classstatus idstatus就绪/div /div /div script srcapp.js/script /body /html4.2 CSS样式设计在style.css中添加一些基本的样式* { box-sizing: border-box; margin: 0; padding: 0; } body { font-family: Segoe UI, Tahoma, Geneva, Verdana, sans-serif; line-height: 1.6; color: #333; background-color: #f5f5f5; } .container { max-width: 800px; margin: 0 auto; padding: 20px; } header { text-align: center; margin-bottom: 30px; } header h1 { color: #2c3e50; margin-bottom: 10px; } .chat-container { background: white; border-radius: 10px; box-shadow: 0 2px 10px rgba(0, 0, 0, 0.1); overflow: hidden; } .messages { height: 400px; overflow-y: auto; padding: 20px; background: #fafafa; } .message { margin-bottom: 15px; padding: 12px; border-radius: 8px; max-width: 80%; } .user-message { background: #007bff; color: white; margin-left: auto; } .ai-message { background: #e9ecef; color: #333; } .input-area { padding: 20px; border-top: 1px solid #eee; display: flex; flex-direction: column; gap: 10px; } textarea { width: 100%; padding: 12px; border: 1px solid #ddd; border-radius: 5px; resize: vertical; font-family: inherit; } button { padding: 10px 15px; border: none; border-radius: 5px; cursor: pointer; font-weight: bold; } #send-button { background: #28a745; color: white; } #clear-button { background: #dc3545; color: white; } .status { padding: 10px 20px; background: #f8f9fa; text-align: center; color: #6c757d; font-style: italic; }4.3 JavaScript前端逻辑在app.js中实现前端的交互逻辑class ChatApp { constructor() { this.apiBase http://localhost:3001; this.messagesContainer document.getElementById(chat-messages); this.messageInput document.getElementById(message-input); this.sendButton document.getElementById(send-button); this.clearButton document.getElementById(clear-button); this.statusElement document.getElementById(status); this.setupEventListeners(); } setupEventListeners() { this.sendButton.addEventListener(click, () this.sendMessage()); this.clearButton.addEventListener(click, () this.clearChat()); this.messageInput.addEventListener(keypress, (e) { if (e.key Enter !e.shiftKey) { e.preventDefault(); this.sendMessage(); } }); } async sendMessage() { const message this.messageInput.value.trim(); if (!message) return; // 添加用户消息到界面 this.addMessage(user, message); this.messageInput.value ; this.updateStatus(模型思考中...); try { const response await fetch(${this.apiBase}/api/chat/stream, { method: POST, headers: { Content-Type: application/json, }, body: JSON.stringify({ message }), }); if (!response.ok) { throw new Error(HTTP错误: ${response.status}); } // 创建AI消息元素 const aiMessageElement this.createMessageElement(ai, ); this.messagesContainer.appendChild(aiMessageElement); // 读取流式响应 const reader response.body.getReader(); const decoder new TextDecoder(); let accumulatedText ; while (true) { const { done, value } await reader.read(); if (done) break; const chunk decoder.decode(value); const lines chunk.split(\n).filter(line line.trim() ! ); for (const line of lines) { if (line.startsWith(data: )) { const data line.slice(6); if (data [DONE]) { this.updateStatus(就绪); break; } try { const parsed JSON.parse(data); if (parsed.content) { accumulatedText parsed.content; aiMessageElement.textContent accumulatedText; // 自动滚动到底部 this.messagesContainer.scrollTop this.messagesContainer.scrollHeight; } } catch (e) { console.log(解析数据失败:, data); } } } } } catch (error) { console.error(发送消息失败:, error); this.updateStatus(错误: error.message); this.addMessage(ai, 抱歉发生了错误。请检查ollama服务是否运行正常。); } } addMessage(role, content) { const messageElement this.createMessageElement(role, content); this.messagesContainer.appendChild(messageElement); this.messagesContainer.scrollTop this.messagesContainer.scrollHeight; } createMessageElement(role, content) { const messageDiv document.createElement(div); messageDiv.className message ${role}-message; messageDiv.textContent content; return messageDiv; } clearChat() { this.messagesContainer.innerHTML ; this.updateStatus(对话已清空); } updateStatus(message) { this.statusElement.textContent message; } } // 初始化应用 document.addEventListener(DOMContentLoaded, () { new ChatApp(); });5. 运行与测试5.1 启动后端服务在backend目录下运行node server.js如果一切正常你会看到输出提示服务器已启动。5.2 打开前端页面由于我们使用了现代JavaScript模块你需要通过HTTP服务器来打开前端页面而不是直接双击HTML文件。你可以使用以下方式使用VS Code的Live Server插件或者使用Python内置服务器cd frontend python -m http.server 3000然后在浏览器中访问 http://localhost:30005.3 测试流式响应在输入框中输入问题比如请解释一下量子计算的基本原理然后点击发送。你应该能够看到文字逐个字地显示出来就像真正的流式响应一样。6. 常见问题与解决方案6.1 连接失败问题如果遇到连接错误检查以下几点Ollama服务是否运行确保ollama正在运行并且Phi-4-mini-reasoning模型已正确加载端口是否正确默认ollama运行在11434端口我们的后端运行在3001端口跨域问题我们已经配置了CORS但如果仍有问题检查浏览器控制台错误信息6.2 流式响应不工作如果流式响应没有按预期工作检查浏览器是否支持SSEServer-Sent Events查看后端控制台是否有错误输出确认ollama的API响应格式是否正确6.3 性能优化建议对于生产环境你可能需要考虑添加请求超时处理实现连接重试机制添加速率限制防止滥用使用WebSocket替代SSE以获得更好的性能7. 总结通过这个项目我们成功实现了一个完整的ollama Phi-4-mini-reasoning流式调用演示。你学会了如何通过API调用ollama模型实现服务器端的流式响应处理在前端实时渲染流式数据构建一个交互式的问答界面这个Demo可以作为你开发更复杂AI应用的基础。你可以在此基础上添加更多功能比如对话历史管理、多模型支持、用户认证等。获取更多AI镜像想探索更多AI镜像和应用场景访问 CSDN星图镜像广场提供丰富的预置镜像覆盖大模型推理、图像生成、视频生成、模型微调等多个领域支持一键部署。