
ollama部署Phi-4-mini-reasoning支持WebSocket长连接的实时推理服务搭建1. 引言为什么需要实时推理服务想象一下你正在开发一个智能客服系统用户输入问题后系统需要几秒钟甚至更长时间才能给出回复。这种延迟不仅影响用户体验还可能让用户失去耐心。传统的API调用方式每次请求都需要建立新的连接等待模型处理再返回结果这个过程本身就存在延迟。有没有一种方法能让AI模型的回复像聊天一样实时、流畅地呈现出来这就是WebSocket长连接的价值所在。通过WebSocket我们可以建立一个持久的双向通信通道让模型推理的结果像水流一样源源不断地“流式”返回给前端实现真正的实时交互。今天我们就来聊聊如何用ollama部署Phi-4-mini-reasoning这个轻量级推理模型并搭建一个支持WebSocket的实时推理服务。无论你是想做一个聊天机器人、代码助手还是需要实时分析文本的应用这套方案都能让你事半功倍。2. 认识我们的主角Phi-4-mini-reasoning在开始动手之前我们先简单了解一下Phi-4-mini-reasoning这个模型。2.1 模型特点Phi-4-mini-reasoning是Phi-4模型家族中的一员但它有几个特别吸引人的地方轻量级但能力强虽然模型体积相对较小但它专门在高质量、密集推理的数据上进行了训练和微调。这意味着它在数学推理、逻辑分析这类需要“动脑筋”的任务上表现突出。超长上下文支持128K令牌的上下文长度。简单来说它能记住并处理非常长的对话或文档内容不会因为内容太长而“失忆”。开源免费完全开源你可以自由使用、修改甚至商用没有额外的授权费用。2.2 适用场景这个模型特别适合哪些场景呢教育辅导解答数学题、物理题一步步推导解题过程代码助手分析代码逻辑解释复杂算法数据分析从文本中提取关键信息进行逻辑推理智能对话需要一定推理能力的聊天场景现在我们对模型有了基本了解接下来看看如何快速体验它。3. 快速体验在Web界面中试用Phi-4-mini-reasoning如果你只是想先试试这个模型的能力ollama提供了一个非常方便的Web界面。我们来看看怎么用。3.1 找到模型入口首先你需要确保已经安装并运行了ollama。打开你的ollama Web界面通常地址是http://localhost:11434。在界面上你会看到一个明显的模型展示区域点击进入模型管理页面。3.2 选择并加载模型在模型选择页面你可以看到所有可用的模型。在搜索框输入“phi-4-mini-reasoning”选择phi-4-mini-reasoning:latest这个版本。点击加载系统会自动下载并准备这个模型。3.3 开始对话模型加载完成后页面下方会出现一个输入框。你可以像聊天一样输入问题比如帮我解这个方程2x 5 15或者用Python写一个快速排序算法并解释每一步的原理模型会以流式的方式逐步显示回答你可以看到它“思考”和“输出”的过程。这个Web界面适合快速测试和体验但如果要集成到自己的应用中我们还需要更灵活的方案。4. 搭建WebSocket实时推理服务现在进入正题如何搭建一个支持WebSocket的推理服务让我们自己的应用也能享受实时交互的体验。4.1 环境准备首先确保你的系统已经安装了以下工具Python 3.8我们的服务端代码将用Python编写ollama模型运行环境基本的网络环境能够正常访问互联网下载模型安装必要的Python包pip install fastapi uvicorn websockets httpx4.2 服务端代码实现创建一个名为phi4_websocket_server.py的文件写入以下代码import asyncio import json from fastapi import FastAPI, WebSocket, WebSocketDisconnect from fastapi.middleware.cors import CORSMiddleware import httpx import logging # 设置日志 logging.basicConfig(levellogging.INFO) logger logging.getLogger(__name__) app FastAPI(titlePhi-4-mini-reasoning WebSocket服务) # 允许跨域请求 app.add_middleware( CORSMiddleware, allow_origins[*], # 生产环境请设置为具体的域名 allow_credentialsTrue, allow_methods[*], allow_headers[*], ) class ConnectionManager: 管理WebSocket连接 def __init__(self): self.active_connections [] async def connect(self, websocket: WebSocket): await websocket.accept() self.active_connections.append(websocket) logger.info(f新连接建立当前连接数{len(self.active_connections)}) def disconnect(self, websocket: WebSocket): if websocket in self.active_connections: self.active_connections.remove(websocket) logger.info(f连接关闭剩余连接数{len(self.active_connections)}) async def send_message(self, websocket: WebSocket, message: str): try: await websocket.send_text(message) except Exception as e: logger.error(f发送消息失败{e}) manager ConnectionManager() async def call_ollama_api(prompt: str, stream: bool True): 调用ollama的API支持流式响应 url http://localhost:11434/api/generate payload { model: phi-4-mini-reasoning:latest, prompt: prompt, stream: stream, options: { temperature: 0.7, top_p: 0.9, num_predict: 2048 # 最大生成长度 } } async with httpx.AsyncClient(timeout30.0) as client: try: if stream: # 流式响应 async with client.stream(POST, url, jsonpayload) as response: async for chunk in response.aiter_lines(): if chunk: try: data json.loads(chunk) if response in data: yield data[response] if data.get(done, False): break except json.JSONDecodeError: continue else: # 非流式响应 response await client.post(url, jsonpayload) data response.json() yield data.get(response, ) except Exception as e: logger.error(f调用ollama API失败{e}) yield f请求失败{str(e)} app.websocket(/ws/chat) async def websocket_chat(websocket: WebSocket): WebSocket聊天端点 await manager.connect(websocket) try: while True: # 接收客户端消息 data await websocket.receive_text() message_data json.loads(data) prompt message_data.get(prompt, ) if not prompt: await websocket.send_text(json.dumps({error: 请输入有效的问题})) continue logger.info(f收到问题{prompt[:50]}...) # 开始流式响应 full_response async for chunk in call_ollama_api(prompt): if chunk: full_response chunk # 实时发送每个片段 await websocket.send_text(json.dumps({ type: chunk, content: chunk, done: False })) # 发送完成信号 await websocket.send_text(json.dumps({ type: complete, content: , done: True, full_response: full_response })) except WebSocketDisconnect: manager.disconnect(websocket) except Exception as e: logger.error(fWebSocket处理异常{e}) await websocket.send_text(json.dumps({error: str(e)})) manager.disconnect(websocket) app.get(/health) async def health_check(): 健康检查端点 return {status: healthy, service: phi4-websocket-server} if __name__ __main__: import uvicorn uvicorn.run(app, host0.0.0.0, port8000)4.3 客户端示例代码服务端准备好了我们还需要一个客户端来测试。创建一个简单的HTML测试页面!DOCTYPE html html langzh-CN head meta charsetUTF-8 meta nameviewport contentwidthdevice-width, initial-scale1.0 titlePhi-4-mini-reasoning 实时聊天测试/title style body { font-family: Arial, sans-serif; max-width: 800px; margin: 0 auto; padding: 20px; background-color: #f5f5f5; } .chat-container { background: white; border-radius: 10px; box-shadow: 0 2px 10px rgba(0,0,0,0.1); overflow: hidden; } .chat-header { background: #4a6fa5; color: white; padding: 15px; text-align: center; } .chat-messages { height: 400px; overflow-y: auto; padding: 20px; } .message { margin-bottom: 15px; padding: 10px; border-radius: 8px; max-width: 80%; } .user-message { background: #e3f2fd; margin-left: auto; } .bot-message { background: #f5f5f5; margin-right: auto; } .input-area { display: flex; padding: 15px; border-top: 1px solid #eee; } #messageInput { flex: 1; padding: 10px; border: 1px solid #ddd; border-radius: 5px; font-size: 16px; } #sendButton { background: #4a6fa5; color: white; border: none; padding: 10px 20px; margin-left: 10px; border-radius: 5px; cursor: pointer; } .status { padding: 10px; text-align: center; color: #666; } .typing-indicator { display: none; color: #999; font-style: italic; } /style /head body div classchat-container div classchat-header h2Phi-4-mini-reasoning 实时聊天测试/h2 /div div classstatus idstatus 正在连接服务器... /div div classchat-messages idchatMessages !-- 消息会动态添加到这里 -- /div div classtyping-indicator idtypingIndicator Phi-4正在思考... /div div classinput-area input typetext idmessageInput placeholder输入你的问题... autocompleteoff button idsendButton发送/button /div /div script class Phi4ChatClient { constructor() { this.ws null; this.isConnected false; this.currentResponse ; this.currentMessageDiv null; this.init(); } init() { this.connectWebSocket(); this.setupEventListeners(); } connectWebSocket() { const wsUrl ws://localhost:8000/ws/chat; this.ws new WebSocket(wsUrl); this.ws.onopen () { console.log(WebSocket连接成功); this.isConnected true; this.updateStatus(连接成功可以开始聊天了, success); }; this.ws.onmessage (event) { try { const data JSON.parse(event.data); if (data.type chunk) { // 收到流式响应的一个片段 this.currentResponse data.content; this.updateBotMessage(this.currentResponse); } else if (data.type complete) { // 响应完成 this.hideTypingIndicator(); this.currentResponse ; this.currentMessageDiv null; } else if (data.error) { // 错误处理 this.addMessage(错误${data.error}, bot); this.hideTypingIndicator(); } } catch (error) { console.error(解析消息失败:, error); } }; this.ws.onerror (error) { console.error(WebSocket错误:, error); this.updateStatus(连接出错请检查服务器, error); }; this.ws.onclose () { console.log(WebSocket连接关闭); this.isConnected false; this.updateStatus(连接已断开正在重连..., warning); // 3秒后尝试重连 setTimeout(() this.connectWebSocket(), 3000); }; } setupEventListeners() { const input document.getElementById(messageInput); const sendButton document.getElementById(sendButton); // 发送按钮点击事件 sendButton.addEventListener(click, () { this.sendMessage(); }); // 输入框回车事件 input.addEventListener(keypress, (e) { if (e.key Enter) { this.sendMessage(); } }); } sendMessage() { const input document.getElementById(messageInput); const message input.value.trim(); if (!message) { return; } if (!this.isConnected) { this.updateStatus(请等待连接建立..., error); return; } // 添加用户消息到聊天窗口 this.addMessage(message, user); // 准备接收AI回复 this.currentResponse ; this.showTypingIndicator(); // 创建AI消息容器 this.currentMessageDiv this.addMessage(, bot); // 发送消息到服务器 this.ws.send(JSON.stringify({ prompt: message, timestamp: new Date().toISOString() })); // 清空输入框 input.value ; input.focus(); } addMessage(text, sender) { const messagesDiv document.getElementById(chatMessages); const messageDiv document.createElement(div); messageDiv.className message ${sender}-message; messageDiv.textContent text; messagesDiv.appendChild(messageDiv); messagesDiv.scrollTop messagesDiv.scrollHeight; return messageDiv; } updateBotMessage(text) { if (this.currentMessageDiv) { this.currentMessageDiv.textContent text; const messagesDiv document.getElementById(chatMessages); messagesDiv.scrollTop messagesDiv.scrollHeight; } } showTypingIndicator() { document.getElementById(typingIndicator).style.display block; } hideTypingIndicator() { document.getElementById(typingIndicator).style.display none; } updateStatus(text, type) { const statusDiv document.getElementById(status); statusDiv.textContent text; // 根据类型设置颜色 statusDiv.style.color type success ? green : type error ? red : type warning ? orange : #666; } } // 页面加载完成后初始化客户端 document.addEventListener(DOMContentLoaded, () { window.chatClient new Phi4ChatClient(); }); /script /body /html4.4 启动和测试现在让我们启动服务并进行测试启动ollama服务如果还没启动ollama serve拉取Phi-4-mini-reasoning模型ollama pull phi-4-mini-reasoning:latest启动WebSocket服务python phi4_websocket_server.py打开测试页面 将上面的HTML代码保存为test_client.html用浏览器打开。开始聊天 在输入框中提问比如“解释一下牛顿第二定律”你会看到模型实时、逐字地回复。5. 进阶配置与优化基础服务搭建好了但要让它在生产环境中稳定运行还需要一些优化。5.1 添加身份验证在生产环境中我们需要保护WebSocket端点。修改服务端代码添加简单的token验证from fastapi import WebSocket, WebSocketDisconnect, Query app.websocket(/ws/chat) async def websocket_chat( websocket: WebSocket, token: str Query(None) # 从查询参数获取token ): 带身份验证的WebSocket聊天端点 # 简单的token验证生产环境应该更复杂 valid_tokens [your_secret_token_here] # 应该从数据库或配置读取 if token not in valid_tokens: await websocket.close(code1008, reason未授权的访问) return await manager.connect(websocket) # ... 其余代码保持不变 ...客户端连接时需要带上tokenconst wsUrl ws://localhost:8000/ws/chat?tokenyour_secret_token_here;5.2 添加速率限制防止恶意用户过度使用服务from slowapi import Limiter, _rate_limit_exceeded_handler from slowapi.util import get_remote_address from slowapi.errors import RateLimitExceeded limiter Limiter(key_funcget_remote_address) app.state.limiter limiter app.add_exception_handler(RateLimitExceeded, _rate_limit_exceeded_handler) # 对WebSocket连接也添加限制 app.websocket(/ws/chat) limiter.limit(10/minute) # 每分钟最多10次连接 async def websocket_chat(websocket: WebSocket): # ... 代码 ...5.3 添加日志记录为了更好地监控服务运行状态import logging from logging.handlers import RotatingFileHandler # 配置日志 log_formatter logging.Formatter( %(asctime)s - %(name)s - %(levelname)s - %(message)s ) # 文件日志最多保留5个10MB的文件 file_handler RotatingFileHandler( phi4_websocket.log, maxBytes10*1024*1024, # 10MB backupCount5 ) file_handler.setFormatter(log_formatter) file_handler.setLevel(logging.INFO) # 控制台日志 console_handler logging.StreamHandler() console_handler.setFormatter(log_formatter) console_handler.setLevel(logging.INFO) # 获取logger并添加处理器 logger logging.getLogger(__name__) logger.setLevel(logging.INFO) logger.addHandler(file_handler) logger.addHandler(console_handler) # 在关键位置添加日志记录 async def call_ollama_api(prompt: str, stream: bool True): logger.info(f开始处理请求提示词长度{len(prompt)}) start_time asyncio.get_event_loop().time() try: # ... 原有代码 ... pass except Exception as e: logger.error(fAPI调用失败{e}, exc_infoTrue) raise finally: end_time asyncio.get_event_loop().time() logger.info(f请求处理完成耗时{end_time - start_time:.2f}秒)5.4 支持多模型切换如果将来需要支持多个模型可以这样扩展from enum import Enum from typing import Optional class ModelType(str, Enum): PHI4_MINI_REASONING phi-4-mini-reasoning:latest LLAMA3 llama3:latest MISTRAL mistral:latest async def call_ollama_api( prompt: str, model: ModelType ModelType.PHI4_MINI_REASONING, stream: bool True ): 支持多模型的API调用 payload { model: model.value, # 使用传入的模型 prompt: prompt, stream: stream, options: { temperature: 0.7, top_p: 0.9, num_predict: 2048 } } # ... 其余代码保持不变 ... # WebSocket端点也支持模型选择 app.websocket(/ws/chat) async def websocket_chat(websocket: WebSocket): await manager.connect(websocket) try: while True: data await websocket.receive_text() message_data json.loads(data) prompt message_data.get(prompt, ) model_name message_data.get(model, phi-4-mini-reasoning:latest) # 验证模型是否可用 try: model ModelType(model_name) except ValueError: await websocket.send_text(json.dumps({ error: f不支持的模型{model_name} })) continue # 使用指定的模型 async for chunk in call_ollama_api(prompt, model): # ... 发送响应 ... pass except WebSocketDisconnect: manager.disconnect(websocket)6. 实际应用场景这个WebSocket实时推理服务可以应用在很多实际场景中下面举几个例子6.1 智能客服系统传统的客服系统响应慢用户需要等待。使用我们的实时服务// 客服前端代码示例 class CustomerServiceChat { constructor() { this.ws new WebSocket(ws://your-domain.com/ws/chat); this.setupWebSocket(); } setupWebSocket() { this.ws.onmessage (event) { const data JSON.parse(event.data); if (data.type chunk) { // 实时显示AI回复 this.appendToChat(data.content); // 如果是关键信息可以触发其他操作 if (this.containsUrgentKeyword(data.content)) { this.notifyHumanAgent(); } } }; } // 用户发送问题 async sendUserQuestion(question) { this.ws.send(JSON.stringify({ prompt: 用户问题${question}\n请以客服身份专业、友好地回答, context: this.getConversationHistory() // 可以传入历史对话 })); } }6.2 编程助手程序员在写代码时可以实时获取帮助# 集成到代码编辑器的示例 class CodeAssistant: def __init__(self): self.websocket_url ws://localhost:8000/ws/chat self.connection None async def get_code_explanation(self, code_snippet): 获取代码解释 prompt f 请解释以下Python代码 {code_snippet} 请分点说明 1. 这段代码的功能是什么 2. 每行代码的作用 3. 有没有可以优化的地方 explanation async for chunk in self.stream_response(prompt): explanation chunk # 实时更新编辑器中的解释面板 self.update_explanation_panel(explanation) return explanation async def debug_code(self, error_message, code): 调试代码错误 prompt f 我遇到了这个错误{error_message} 相关代码 {code} 请帮我 1. 分析错误原因 2. 提供修复建议 3. 给出修复后的代码 return await self.get_complete_response(prompt)6.3 教育辅导应用学生可以实时获得学习帮助// 数学辅导应用 class MathTutor { constructor() { this.problemHistory []; } async solveMathProblem(problem) { const prompt 请解决这个数学问题${problem} 要求 1. 给出详细步骤 2. 解释每个步骤的原理 3. 最后给出答案 请用中文回答面向中学生水平。 ; // 通过WebSocket实时获取解答 const solution await this.streamFromWebSocket(prompt); // 记录问题历史用于后续个性化辅导 this.problemHistory.push({ problem, solution, timestamp: new Date() }); return solution; } async explainConcept(concept) { const prompt 请解释数学概念${concept} 要求 1. 用简单的语言定义 2. 给出2-3个例子 3. 说明在实际生活中的应用 面向初中生水平尽量生动有趣。 ; return await this.streamFromWebSocket(prompt); } }7. 总结通过今天的分享我们完成了一个完整的Phi-4-mini-reasoning实时推理服务搭建。让我们回顾一下关键点7.1 核心收获WebSocket的优势相比传统的HTTP请求WebSocket提供了真正的实时双向通信特别适合需要流式响应的AI应用。Phi-4-mini-reasoning的特点这个模型虽然在体积上比较轻量但在推理任务上表现优秀特别适合需要逻辑分析和分步思考的场景。完整的实现方案我们从服务端到客户端从基础功能到进阶优化提供了一个可以直接使用的完整方案。实际应用价值这个方案可以轻松集成到各种应用中无论是客服系统、编程助手还是教育应用都能显著提升用户体验。7.2 部署建议如果你准备在生产环境部署这个服务我建议使用反向代理用Nginx或Caddy作为反向代理处理SSL和负载均衡配置监控添加Prometheus和Grafana监控跟踪服务健康状态设置自动扩缩容根据请求量自动调整服务实例数量定期更新模型关注Phi-4-mini-reasoning的更新及时获取性能改进7.3 进一步探索这个基础框架还有很多可以扩展的地方添加对话历史让模型能记住之前的对话内容支持文件上传处理PDF、Word文档中的内容集成知识库结合向量数据库提供更准确的回答多语言支持让服务支持更多语言最重要的是现在你已经有了一个可以工作的基础。接下来就是根据你的具体需求在这个基础上添加更多功能。无论是做一个智能客服还是一个学习助手这个实时推理服务都能为你提供强大的AI能力支持。获取更多AI镜像想探索更多AI镜像和应用场景访问 CSDN星图镜像广场提供丰富的预置镜像覆盖大模型推理、图像生成、视频生成、模型微调等多个领域支持一键部署。