使用 OpenVINO 本地部署 DeepSeek-R1 量化大模型(第二章:前端交互与后端服务)

使用 OpenVINO 本地部署 DeepSeek-R1 量化大模型(第二章:前端交互与后端服务)

一、前言

        基于上一章的环境准备和模型转换,本章专注于后端服务器的部署以及前端页面的启动。

使用 OpenVINO 本地部署 DeepSeek-R1 量化大模型(第一章:基础概念与模型转换)-ZEEKLOG博客https://blog.ZEEKLOG.net/plmm__/article/details/156192071?spm=1001.2014.3001.5502

        整个后端服务器是依赖于 OpenVINO 的,不过只要你的设备可以使用 OpenVINO,理论上就可以使用这个后端,如果你有 intel 的独立显卡,只需要把代码中 device 更改为对应的设备即可运行在 GPU 上。

self.pipeline = ov_genai.LLMPipeline(self.model_path, device)

二、前端交互界面

         网页预览:

        作者本人是第一次接触 html 源码,很多编程可能不规范,不过我的目的是部署,不是开发,这里也仅是给各位和我一样想快速部署且未接触过 html 的读者一个参考,也欢迎各位优秀的前端开发者提供建议和意见。

chat_interface.html

<!DOCTYPE html> <html lang="zh-CN"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>DeepSeek-R1 智能对话</title> <link rel="icon" type="image/svg+xml" href="logo.svg"> <style> * { margin: 0; padding: 0; box-sizing: border-box; } body { font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif; background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); min-height: 100vh; padding: 20px; } .chat-container { max-width: 900px; margin: 0 auto; background: white; border-radius: 15px; box-shadow: 0 10px 30px rgba(0,0,0,0.2); overflow: hidden; display: flex; flex-direction: column; height: 90vh; } .chat-header { background: #2d3748; color: white; padding: 25px; text-align: center; } .status-bar { background: #f7fafc; padding: 10px 20px; border-bottom: 1px solid #e2e8f0; display: flex; justify-content: space-between; align-items: center; font-size: 14px; } .status-indicator { padding: 4px 12px; border-radius: 12px; font-weight: bold; } .status-loading { background: #fed7d7; color: #c53030; } .status-ready { background: #c6f6d5; color: #276749; } .chat-messages { flex: 1; overflow-y: auto; padding: 20px; background: #f7fafc; } .message { margin-bottom: 15px; display: flex; align-items: flex-start; } .user-message { justify-content: flex-end; } /* 用户消息靠右 */ .bot-message { justify-content: flex-start; } /* 默认机器人靠左 */ .message-bubble { max-width: 70%; padding: 12px 18px; border-radius: 18px; line-height: 1.4; } .user-message .message-bubble { background: #4299e1; color: white; border-bottom-right-radius: 5px; } .bot-message .message-bubble { background: white; color: #2d3748; border: 1px solid #e2e8f0; border-bottom-left-radius: 5px; } .message-time { font-size: 12px; color: #718096; margin-top: 5px; } .typing-indicator { display: none; color: #718096; font-style: italic; margin: 10px 0; padding: 0 20px; } .chat-input-container { padding: 20px; background: white; border-top: 2px solid #e2e8f0; } .chat-input { display: flex; gap: 10px; } #messageInput { flex: 1; padding: 12px 15px; border: 1px solid #cbd5e0; border-radius: 25px; outline: none; font-size: 16px; } #messageInput:focus { border-color: #4299e1; } #sendButton { padding: 12px 25px; background: #4299e1; color: white; border: none; border-radius: 25px; cursor: pointer; font-size: 16px; transition: background 0.3s; } #sendButton:hover:not(:disabled) { background: #3182ce; } #sendButton:disabled { background: #a0aec0; cursor: not-allowed; } .system-message { text-align: center; color: #718096; font-size: 14px; margin: 10px 0; padding: 8px; background: #edf2f7; border-radius: 8px; } </style> </head> <body> <div> <div> <h1>🤖 DeepSeek-R1 智能对话</h1> <p>基于 OpenVINO 本地部署</p> </div> <div> <span>服务状态: <span>检测中...</span></span> <span>模型加载中</span> </div> <div> <div> 系统正在初始化,请稍候... </div> </div> <div> DeepSeek正在思考中... </div> <div> <div> <input type="text" placeholder="输入您的问题..." autocomplete="off" disabled> <button disabled>发送</button> </div> </div> </div> <script> const API_BASE = 'http://localhost:5000'; // DOM元素 const chatMessages = document.getElementById('chatMessages'); const messageInput = document.getElementById('messageInput'); const sendButton = document.getElementById('sendButton'); const typingIndicator = document.getElementById('typingIndicator'); const statusText = document.getElementById('statusText'); const modelStatus = document.getElementById('modelStatus'); // 状态检查函数,添加OpenVINO特定信息 async function checkServerStatus() { try { const response = await fetch(`${API_BASE}/health`); const data = await response.json(); if (data.status === 'ready') { statusText.textContent = 'OpenVINO服务就绪'; modelStatus.textContent = 'CPU加速就绪'; modelStatus.className = 'status-indicator status-ready'; messageInput.disabled = false; sendButton.disabled = false; messageInput.placeholder = "输入您的问题..."; // 更新欢迎消息 if (chatMessages.querySelector('.system-message')) { chatMessages.innerHTML = ''; addMessage('您好!我是基于OpenVINO GenAI优化的AI助手,现在运行在CPU上,为您提供高效的本地推理服务。', false); } return true; } else { statusText.textContent = 'OpenVINO模型加载中...'; modelStatus.textContent = '优化中'; modelStatus.className = 'status-indicator status-loading'; messageInput.placeholder = "OpenVINO模型优化加载中,请稍候..."; return false; } } catch (error) { statusText.textContent = 'OpenVINO服务未连接'; modelStatus.textContent = '离线'; modelStatus.className = 'status-indicator status-loading'; messageInput.placeholder = "无法连接到OpenVINO服务,请检查服务器状态"; return false; } } // 添加模型信息查询 async function getModelInfo() { try { const response = await fetch(`${API_BASE}/model_info`); const data = await response.json(); console.log('OpenVINO后端信息:', data); } catch (error) { console.error('获取模型信息失败:', error); } } function getCurrentTime() { return new Date().toLocaleTimeString('zh-CN', { hour: '2-digit', minute: '2-digit' }); } function addMessage(content, isUser = false) { // 动态创建一个 <div> 元素作为单条消息的外层容器 const messageDiv = document.createElement('div'); // 基础类 + 条件类 messageDiv.className = `message ${isUser ? 'user-message' : 'bot-message'}`; // 构建消息内容 HTML // 将文本中的换行符 \n 转为 HTML 换行 <br>,确保多行消息正常显示 messageDiv.innerHTML = ` <div> ${content.replace(/\n/g, '<br>')} <div>${getCurrentTime()}</div> </div> `; chatMessages.appendChild(messageDiv); // 自动滚动到底部 chatMessages.scrollTop = chatMessages.scrollHeight; } function showTypingIndicator() { typingIndicator.style.display = 'block'; chatMessages.scrollTop = chatMessages.scrollHeight; } function hideTypingIndicator() { typingIndicator.style.display = 'none'; } // async:标记一个函数为异步函数(该函数会自动返回 Promise) // await:暂停当前函数的执行,直到 await 后面的 Promise 被解决(resolved),然后返回其结果 // await 只能在 async 函数内部使用 // 调用带有async函数,调用者不会阻塞,只有该函数会阻塞,调用者不会等到函数执行结束,而是直接下一步 async function sendMessage() { const message = messageInput.value.trim(); if (!message) return; // 添加用户消息 addMessage(message, true); messageInput.value = ''; sendButton.disabled = true; // 显示打字指示器 showTypingIndicator(); try { // fetch:现代浏览器提供的网络请求 API const response = await fetch(`${API_BASE}/chat`, { method: 'POST', // Header:声明内容为 JSON,确保后端能正确解析 headers: { 'Content-Type': 'application/json', }, body: JSON.stringify({ message: message, max_tokens: 512 }) }); if (!response.ok) { throw new Error(`HTTP错误! 状态码: ${response.status}`); } const data = await response.json(); if (data.status === 'success') { hideTypingIndicator(); addMessage(data.response, false); } else { throw new Error(data.error || '未知错误'); } } catch (error) { hideTypingIndicator(); addMessage(`抱歉,出现了错误:${error.message}`, false); console.error('API调用错误:', error); } finally { sendButton.disabled = false; // messageInput.focus():自动聚焦回输入框,方便继续输入 messageInput.focus(); } } // 事件监听 sendButton.addEventListener('click', sendMessage); messageInput.addEventListener('keypress', (e) => { if (e.key === 'Enter') { sendMessage(); } }); // 初始化 async function init() { // 每5秒检查一次服务状态 const checkInterval = setInterval(async () => { const isReady = await checkServerStatus(); if (isReady) { clearInterval(checkInterval); } }, 5000); // 首次检查 await checkServerStatus(); } // 当 HTML 文档、所有图片、样式表、脚本等外部资源全部加载完成后, // 浏览器会触发 load 事件,此时 window.onload 指定的函数就会被调用 window.onload = init; </script> </body> </html>

        主要实现了几个简单控件,输入条,发送按钮和状态栏,需要后端提供三个接口:

const response = await fetch(`${API_BASE}/health`); const response = await fetch(`${API_BASE}/model_info`); const response = await fetch(`${API_BASE}/chat`

分别用于服务器状态检查,获取模型信息和对话。

网页图标

        网页图标是一个本地 svg 文件,由于不方便上传,这里给大家推荐一个快速生成图标的网站:

Favicon Generator for perfect icons on all browsershttps://realfavicongenerator.net/点击 Create a logo:

点击创建:

选择一个色系:

然后就可以个性化调整:

三、后端模型服务器

        OpenVINO 为生成式AI优化提供了推理引擎:OpenVINO GenAI。

        OpenVINO GenAI 提供简洁的 API 接口,支持Python和C++两种编程语言,安装容量不到200MB。

        以我本次使用的 DeepSeek-R1-0528-Qwen3-8B 为例,流水线使用 LLMPipeline。

        后端需要用到的包:

pip install Flask Flask-CORS openvino-genai

openvino_server.py

import openvino_genai as ov_genai from flask import Flask, request, jsonify from flask_cors import CORS import threading import logging from typing import Dict, Any from pathlib import Path class OpenVINOBackend: def __init__(self, model_path: str): self.model_path = model_path self.pipeline = None self.is_ready = False self.init_lock = threading.Lock() def initialize_model(self, device: str): """初始化OpenVINO模型管道""" with self.init_lock: if self.is_ready: return logger.info("正在加载OpenVINO模型...") try: self.pipeline = ov_genai.LLMPipeline(self.model_path, device) self.is_ready = True logger.info("OpenVINO模型加载完成!") except Exception as e: logger.error(f"模型加载失败: {str(e)}") self.is_ready = False def generate_response(self, message: str, max_tokens: int = 512, temperature: float = 0.7, do_sample: bool = True) -> Dict[str, Any]: """生成对话响应""" if not self.is_ready or self.pipeline is None: return {"status": "error", "error": "模型未就绪"} try: # 配置生成参数 config = ov_genai.GenerationConfig() config.max_new_tokens = max_tokens # 控制生成的最大token数量 config.temperature = temperature # 控制生成的随机性,值越高生成越随机(典型范围0.1-2.0) config.do_sample = do_sample # 控制是否使用采样策略,使用采样策略,生成更随机、创造性更强的文本 # 开始聊天会话(优化KV缓存) self.pipeline.start_chat() # 生成响应 response = self.pipeline.generate(message, config) # 结束聊天会话 self.pipeline.finish_chat() return { "status": "success", "response": response, "tokens_generated": len(response.split()) } except Exception as e: return {"status": "error", "error": str(e)} def create_app(backend: OpenVINOBackend) -> Flask: """创建Flask应用并绑定后端实例""" app = Flask(__name__) CORS(app) # 允许跨域请求 @app.route('/health', methods=['GET']) def health_check(): """健康检查端点""" return jsonify({ "status": "ready" if backend.is_ready else "loading", "backend": "openvino_genai", "device": "CPU" }) @app.route('/chat', methods=['POST']) def chat_endpoint(): """对话API端点""" data = request.json if not data or 'message' not in data: return jsonify({"status": "error", "error": "缺少message参数"}) message = data['message'] # 如果 max_tokens 不存在,返回默认值 512,限制范围(1~2048) max_tokens = min(max(int(data.get('max_tokens', 512)), 1), 2048) # 生成响应 result = backend.generate_response(message, max_tokens) return jsonify(result) @app.route('/model_info', methods=['GET']) def model_info(): """获取模型信息""" return jsonify({ "backend": "openvino_genai", "status": "ready" if backend.is_ready else "loading", "device": "CPU", "optimization": "int4_quantization" # 根据实际量化方式调整 }) return app def initialize_backend(backend: OpenVINOBackend, device: str='CPU'): """在后台线程中初始化模型""" def init_task(): backend.initialize_model(device) # 守护线程,不需要(也不能)显式回收守护线程 thread = threading.Thread(target=init_task, daemon=True) thread.start() def main(model_path: str = "./converted_ov_model"): """主函数,接收模型路径作为参数""" # 初始化日志 logging.basicConfig( level=logging.INFO, format='-- %(levelname)s - %(message)s', # format='-- %(levelname)s - %(asctime)s - %(name)s - %(message)s', force=True # 强制重新配置(Python 3.8+) ) global logger logger = logging.getLogger(__name__) logger.info("Starting OpenVINO GenAI server...") p = Path(model_path) if not p.exists(): logger.error(f"路径不存在:{model_path}") return if not p.is_dir: logger.error(f"请传入模型文件夹:{model_path}") return # 创建后端实例 backend = OpenVINOBackend(model_path) # 创建Flask应用 app = create_app(backend) # 启动时初始化后端 initialize_backend(backend, "CPU") # 启动Flask服务器 logger.info(f"启动OpenVINO GenAI服务器,模型路径: {model_path}...") app.run(host='0.0.0.0', port=5000, debug=False) if __name__ == '__main__': path = "./DeepSeek-R1-0528-Qwen3-8B_openvino_int4" main(path)

        为了启动方便,我将模型路径写进了 py 文件,读者可以根据自身情况改为从命令行传参。后端也是主要实现三个接口,以及模型的初始化和推理。

        为了适配长期运行环境,启用了 logger 和局域网服务器,并且关闭了 Flask 的 debug,防止Werkzeug 的自动重载机制(reloader),出现一些莫名的情况。

四、资源占用

        启动后端服务是不占用资源的,开始对话以后才会明显占用 CPU 和内存。

        可以看到,OpenVINO 优化以及 INT4 量化后的 DeepSeek-R1-0528-Qwen3-8B,内存占用约 10G,CPU 也在可接受范围,每次推理大约需要 40 秒。

        根据缩略图情况,启动后内存不会释放,直到后端服务器关闭。目前对话是单次输入输出,也就是每次对话只会有一次请求和回复,因此消息没有逐字生成的效果,未来有机会再继续优化,并且现在有字符上线限制,主要是因为我的设备性能有限,如果能将逐字显示完成,使用体验应该可以流畅很多。

Read more

告别996:GitHub Copilot将我的开发效率提升300%的实战记录

告别996:GitHub Copilot将我的开发效率提升300%的实战记录

👋 大家好,欢迎来到我的技术博客! 📚 在这里,我会分享学习笔记、实战经验与技术思考,力求用简单的方式讲清楚复杂的问题。 🎯 本文将围绕AI这个话题展开,希望能为你带来一些启发或实用的参考。 🌱 无论你是刚入门的新手,还是正在进阶的开发者,希望你都能有所收获! 文章目录 * 告别996:GitHub Copilot将我的开发效率提升300%的实战记录 * 引言:从疲惫到高效 * 什么是GitHub Copilot?🤖 * 效率提升300%的核心场景 * 1. 快速生成样板代码 * 2. 自动编写单元测试 * 3. 智能调试与注释 * 集成Copilot到工作流 * 步骤1:设置合理的期望 * 步骤2:结合IDE使用 * 步骤3:代码审查与调整 * 高级用法:超越代码生成 * 数据库查询优化 * API接口设计 * 正则表达式助手 * 数据支撑:效率提升分析 * 避坑指南:常见问题与解决 * 1. 可能生成过时或不安全代码

有图有真相 MATLAB实现基于ACO-DRL 蚁群算法(ACO)结合深度强化学习(DRL)进行无人机三维路径规划(代码已调试成功,可一键运行,每一行都有详细注释) 还请多多点一下关注 加油 谢谢 你

有图有真相 MATLAB实现基于ACO-DRL 蚁群算法(ACO)结合深度强化学习(DRL)进行无人机三维路径规划(代码已调试成功,可一键运行,每一行都有详细注释) 还请多多点一下关注 加油 谢谢 你

有图有真相 请注意所有代码结构内容都在这里了 这个只是有些汉字和字母做了替代 未替代内容可以详谈 请直接联系博主本人或者访问对应标题的完整文档下载页面 还请多多点一下关注 加油 谢谢 你的鼓励是我前行的动力 谢谢支持 加油 谢谢 有图有真相 代码已调试成功,可一键运行,每一行都有详细注释,运行结果详细见实际效果图 完整代码内容包括(模拟数据生成,数据处理,模型构建,模型训练,预测和评估) 含参数设置和停止窗口,可以自由设置参数,随时停止并保存,避免长时间循环。(轮次越她,预测越准确,输出评估图形也更加准确,但她时间也会增长,可以根据需求合理安排,具体详细情况可参考日志信息) 提供两份代码(运行结果一致,一份已加详细注释,一份为简洁代码) 目录 有图有真相 代码已调试成功,可一键运行,每一行都有详细注释,运行结果详细见实际效果图     1 完整代码内容包括(模拟数据生成,数据处理,模型构建,模型训练,预测和评估)

Matlab报错找不到编译器?5分钟搞定MinGW-w64 C/C++环境配置(附环境变量设置)

Matlab报错找不到编译器?5分钟搞定MinGW-w64 C/C++环境配置(附环境变量设置) 最近在尝试用Matlab调用一些C/C++写的算法库,或者想编译一个别人分享的.mex文件时,是不是经常在命令行里敲下 mex -setup 后,迎面而来的就是一个冰冷的报错窗口?"未找到支持的编译器或 SDK"——这句话对很多刚接触Matlab混合编程的朋友来说,简直像一盆冷水。别担心,这几乎是每个Matlab用户进阶路上的必经之坎。问题的核心,往往不在于Matlab本身,而在于你的电脑缺少一个它认可的“翻译官”:C/C++编译器。对于Windows用户,官方推荐且免费的解决方案就是MinGW-w64。这篇文章,就是为你准备的从报错到成功配置的完整路线图。我们不只告诉你步骤,更会解释每一步背后的逻辑,并附上那些容易踩坑的细节和验证方法,目标是让你一次配置,终身受益。 1. 理解问题根源:为什么Matlab需要单独的编译器? 在深入操作之前,花几分钟搞清楚“为什么”,能帮你避免未来很多“是什么”的困惑。Matlab本身是一个强大的解释型语言环境,

Home Assistant联动语音设置智能家居

Home Assistant 联动语音设置智能家居 你有没有过这样的场景:刚进家门,手里拎着 groceries,累得不想动,只想喊一声“开灯”?或者半夜醒来,懒得摸手机,只希望说一句“把空调调到 25 度”就能搞定一切? 💡 如果有,那你一定知道—— 语音控制才是智能家居的终极形态 。 而如果你已经用上了 Home Assistant(HA) ,那恭喜你,其实离“动口不动手”的智能生活,只差一步: 把 HA 和你的语音助手打通 。 别急着去翻文档、折腾 YAML 配置。咱们今天就来聊聊,怎么让 Google Assistant、Amazon Alexa、Apple Siri 这三大语音巨头,乖乖听你家 HA 的指挥,而且不依赖云端、不泄露隐私、