從零開始部署DeepSeek:基于Ollama+Flask的本地化AI對話系統

從零開始部署DeepSeek:基于Ollama+Flask的本地化AI對話系統


一、部署背景與工具選型

在AI大模型遍地開花的2025年,DeepSeek R1憑借其出色的推理能力和開源特性成為開發者首選。本文將以零基礎視角,通過以下工具鏈實現本地化部署:

1.Ollama:輕量級模型管理工具,支持一鍵拉取、運行模型

Ollama 是一個功能強大的大語言模型管理端,專為下載、運行和調用大型語言模型(如DeepSeek)而設計。它提供了以下幾個核心功能:

  • 模型下載:支持從官方倉庫下載不同規模的模型。
  • 模型運行:通過API提供服務,讓用戶與模型進行交互。
  • 實時對話模擬:以流式方式展示模型回復。
2.DeepSeek : 平衡性能與資源消耗的中等規模模型

DeepSeek 是目標語言模型,可以根據您的硬件配置選擇不同的模型規模(如7B、13B等)。它具有以下特點:

  • 可擴展性:根據內存和計算資源調整模型大小。
  • 高效訓練:支持并行訓練以加速模型收斂。
3.Python+Flask:構建Web交互界面,支持流式響應

Flask 是一個輕量級的Web框架,廣泛應用于快速開發Web應用。結合Python,它為構建動態Web界面提供了靈活性:

  • API集成:通過Flask API調用Ollama服務。
  • 前端動態:使用JavaScript和HTML創建交互式對話框。
4.HTML+JavaScript:實現類ChatGPT的對話交互體驗

HTML 是構建Web界面的基礎語言,用于設計用戶友好的對話框,并將其嵌入到Flask應用中。您可以通過以下方式集成:

  • 響應式布局:使用CSS樣式表確保界面在不同設備上適配。
  • 動態內容展示:通過JavaScript更新頁面內容,模擬模型回復,需要模擬打字機方式。

**備注說明**: 不想自己編程的話,可使用chatbox直接接入ollama

二、環境準備與模型部署

1. 安裝Ollama

Windows/Mac用戶
訪問Ollama官網下載安裝包,雙擊完成安裝。 (github下載很慢,需要使用加速或從其他地方下載)

Linux用戶

curl -fsSL https://ollama.com/install.sh | sh
sudo systemctl start ollama
2. 下載DeepSeek模型

根據硬件配置選擇模型(顯存要求參考):

  • 基礎版(1.5B):適合筆記本(8GB內存)
  • 標準版(7B):推薦配置(16GB內存+8GB顯存)
  • 加強版(70B):需專業顯卡(如RTX 5090)
# 拉取7B模型
ollama run deepseek-r1:7b

上述指令會完成下載和運行兩個步驟(ollama支持斷線續傳功能,而且我發現速度慢下來的時候,手動停止然后再啟動接著下載模型的話,速度會恢復上來),成功運行后可通過命令行直接向deepseek提問,如下:

另外,也可以在命令行手動執行ollama相關指令:

  • 無參數啟動
C:\Users\arbboter\Desktop>ollama
Usage:ollama [flags]ollama [command]Available Commands:serve       Start ollamacreate      Create a model from a Modelfileshow        Show information for a modelrun         Run a modelpull        Pull a model from a registrypush        Push a model to a registrylist        List modelsps          List running modelscp          Copy a modelrm          Remove a modelhelp        Help about any commandFlags:-h, --help      help for ollama-v, --version   Show version informationUse "ollama [command] --help" for more information about a command.
命令功能示例
ollama serve啟動模型ollama serve
ollama pull下載模型ollama pull deepseek-r1:7b
ollama list查看本地模型ollama list
ollama rm刪除模型ollama rm deepseek-r1:1.5b
ollama cp復制模型ollama cp deepseek-r1:7b my-backup

3.配置ollama

# 限制本地訪問(默認配置)
export OLLAMA_HOST=127.0.0.1# 允許局域網訪問
export OLLAMA_HOST=0.0.0.0# 啟用調試
export OLLAMA_DEBUG=1# 自定義模型路徑(可能需重啟機器生效)
export OLLAMA_MODELS=~\ollama\models

三、構建Web對話系統

備注說明:選型flask及代碼編寫基本是由deepseek完成,本人只負責提問和修繕
1. 項目結構
deepseek-web/
├── app.py          # Flask主程序
├── templates/
│   └── index.html  # 聊天界面
2. Flask后端實現(app.py)
# app.py
from flask import Flask, render_template, request, Response
import ollama
import logging
from datetime import datetimeapp = Flask(__name__)# 配置日志
logging.basicConfig(level=logging.INFO,format='%(asctime)s - %(levelname)s - %(message)s',handlers=[logging.FileHandler('chat.log'),logging.StreamHandler()]
)def chat_ollama(user_message, stream):host = 'http://192.168.1.10:11434'cli = ollama.Client(host=host)response = cli.chat(model='deepseek-r1:7b',messages=[{'role': 'user', 'content': user_message}],stream=stream,# options={'temperature': 0.7})return response@app.route('/')
def index():return render_template('index.html')@app.route('/api/chat', methods=['POST'])
def chat():"""流式聊天接口"""def generate(user_message):try:app.logger.info(f"流式處理開始: {user_message[:50]}...")stream = chat_ollama(user_message, True)for chunk in stream:content = chunk['message']['content']if content.startswith('<think>'):content = content.replace('<think>', '', 1)elif content.startswith('</think>'):content = content.replace('</think>', '\n', 1)app.logger.debug(f"發送數據塊: {content}")yield f"{content}"app.logger.info("流式處理完成")except Exception as e:app.logger.error(f"流式錯誤: {str(e)}")yield f"[ERROR] {str(e)}\n\n"return Response(generate(request.json['message']), mimetype='text/event-stream')if __name__ == '__main__':app.run(host='0.0.0.0', port=5001, debug=True)
3. 前端實現(index.html)
<!DOCTYPE html>
<html lang="zh-CN">
<head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>AI 對話助手</title><link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.9.0/styles/github.min.css"><script src="https://cdn.jsdelivr.net/npm/marked/marked.min.js"></script><script src="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.9.0/highlight.min.js"></script><style>:root {--primary-color: #10a37f;--bg-color: #f0f2f5;}body {margin: 0;padding: 20px;font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif;background-color: var(--bg-color);max-width: 800px;margin: 0 auto;}#chat-container {height: 70vh;overflow-y: auto;border: 1px solid #e0e0e0;border-radius: 8px;padding: 15px;background: white;margin-bottom: 20px;}.message {margin: 12px 0;display: flex;gap: 15px;}.user-message {justify-content: flex-end;}.message-content {max-width: 80%;padding: 12px 16px;border-radius: 8px;}.assistant-message .message-content {background: #f8f9fa;border: 1px solid #e0e0e0;}.user-message .message-content {background: var(--primary-color);color: white;}#input-container {display: flex;gap: 10px;}#user-input {flex: 1;padding: 12px;border: 1px solid #e0e0e0;border-radius: 8px;resize: none;min-height: 44px;}button {background: var(--primary-color);color: white;border: none;padding: 0 20px;border-radius: 8px;cursor: pointer;transition: opacity 0.2s;}button:disabled {opacity: 0.6;cursor: not-allowed;}.typing-indicator {display: inline-block;padding: 8px 12px;background: #f8f9fa;border-radius: 8px;border: 1px solid #e0e0e0;}.dot {display: inline-block;width: 6px;height: 6px;margin-right: 3px;background: #ccc;border-radius: 50%;animation: bounce 1.4s infinite;}@keyframes bounce {0%, 80%, 100% { transform: translateY(0) }40% { transform: translateY(-6px) }}/* markdown基礎樣式 */.markdown-content {line-height: 1.6;transition: opacity 0.3s;}.markdown-content:not(.markdown-rendered) {opacity: 0.5;}.markdown-content h1 { font-size: 2em; margin: 0.67em 0; }.markdown-content h2 { font-size: 1.5em; margin: 0.83em 0; }.markdown-content pre { background: #f5f5f5;padding: 1em;border-radius: 4px;overflow-x: auto;}</style>
</head>
<body><div id="chat-container"></div><div id="input-container"><textarea id="user-input" placeholder="輸入消息..." rows="1"></textarea><button id="send-btn" onclick="sendMessage()">發送</button></div><script>const chatContainer = document.getElementById('chat-container');const userInput = document.getElementById('user-input');const sendBtn = document.getElementById('send-btn');// 滾動到底部function scrollToBottom() {}function renderMarkdown(options = {}) {// 合并配置參數const config = {selector: options.selector || '.markdown-content',breaks: options.breaks ?? true,gfm: options.gfm ?? true,highlight: options.highlight || null};// 配置Markedmarked.setOptions({breaks: config.breaks,gfm: config.gfm,highlight: config.highlight});// 渲染處理器const render = () => {document.querySelectorAll(config.selector).forEach(container => {if (container.dataset.rendered) return;// 創建虛擬容器避免內容閃爍const virtualDiv = document.createElement('div');virtualDiv.style.display = 'none';virtualDiv.innerHTML = container.innerHTML.trim();// 執行Markdown轉換container.innerHTML = marked.parse(virtualDiv.innerHTML);container.dataset.rendered = true;// 添加加載動畫container.classList.add('markdown-rendered');});};// 自動執行渲染if (document.readyState === 'complete') {render();} else {document.addEventListener('DOMContentLoaded', render);}}// 添加用戶消息function addUserMessage(content) {const messageDiv = document.createElement('div');messageDiv.className = 'message user-message';messageDiv.innerHTML = `<div class="message-content">${content}</div>`;chatContainer.appendChild(messageDiv);}// 添加AI消息(流式)async function addAssistantMessageStream() {const messageDiv = document.createElement('div');messageDiv.className = 'message assistant-message';messageDiv.innerHTML = `<div class="message-content markdown-content"><div class="typing-indicator"><span class="dot"></span><span class="dot" style="animation-delay: 0.2s"></span><span class="dot" style="animation-delay: 0.4s"></span></div></div>`;chatContainer.appendChild(messageDiv);return messageDiv.querySelector('.message-content');}// 發送消息async function sendMessage() {const content = userInput.value.trim();if (!content) return;sendBtn.disabled = true;userInput.disabled = true;userInput.value = '';addUserMessage(content);const responseContainer = await addAssistantMessageStream();try {const response = await fetch('/api/chat', {method: 'POST',headers: {'Content-Type': 'application/json',// 如果需要認證// 'Authorization': 'Bearer YOUR_TOKEN'},body: JSON.stringify({ message: content })});if (!response.ok) throw new Error('請求失敗');await this.createStreamTypewriter(response, responseContainer, {});this.scrollToBottom();} catch (error) {responseContainer.innerHTML = '? 請求出錯: ' + error.message;} finally {sendBtn.disabled = false;userInput.disabled = false;userInput.focus();}}// 輸入框事件處理userInput.addEventListener('keydown', (e) => {if (e.key === 'Enter' && !e.shiftKey && !e.ctrlKey) {e.preventDefault();sendMessage();} else if (e.key === 'Enter' && (e.ctrlKey || e.metaKey)) {userInput.value += '\n';}});async function createStreamTypewriter(stream, container, options = {}) {const config = {baseSpeed: 50,maxSpeedup: 3,retryCount: 3,...options};this.reader = null;// 狀態控制let isDestroyed = false;let cursorVisible = true;let renderQueue = [];let retryCounter = 0;// DOM元素初始化const cursor = document.createElement('span');cursor.className = 'typewriter-cursor';cursor.textContent = '▌';container.append(cursor);// 光標動畫const cursorInterval = setInterval(() => {cursor.style.opacity = cursorVisible ? 1 : 0;cursorVisible = !cursorVisible;}, 600);// 核心渲染邏輯const renderEngine = () => {if (renderQueue.length === 0 || isDestroyed) return;// 動態調速算法const speed = Math.max(config.baseSpeed / config.maxSpeedup,config.baseSpeed - renderQueue.length * 2);const fragment = document.createDocumentFragment();while (renderQueue.length > 0) {const char = renderQueue.shift();fragment.append(document.createTextNode(char));}container.insertBefore(fragment, cursor);setTimeout(() => requestAnimationFrame(renderEngine), speed);};// 流數據處理const processStream = async () => {try {this.reader = stream.body.getReader();// Fetch模式處理while (!isDestroyed) {const { done, value } = await this.reader.read();if (done) break;renderQueue.push(...new TextDecoder().decode(value).split(''));if (!renderQueue.length) continue;requestAnimationFrame(renderEngine);}} catch (err) {if (retryCounter++ < config.retryCount && !isDestroyed) {processStream();} else {destroy();throw new Error('Stream connection failed');}} finally {container.innerHTML = marked.parse(container.textContent.replace('▌', ''));destroy();}};// 資源清理const destroy = () => {if (isDestroyed) return;isDestroyed = true;clearInterval(cursorInterval);cursor.remove();if (stream.cancel) stream.cancel();if (stream.close) stream.close();};// 啟動引擎processStream();return { destroy };};</script>
</body>
</html>

四、啟動與優化

1. 運行系統
flask run --port 5001

訪問 http://localhost:5001 即可開始對話 ,如下:
主頁面

2. 性能優化技巧
  • 量化加速:使用ollama run deepseek-r1:7b --quantize q4_0 減少顯存占用
  • GPU加速:在Ollama配置中啟用CUDA支持

五、安全注意事項

  1. 端口防護
export OLLAMA_HOST=127.0.0.1  # 禁止外部訪問
netstat -an | grep 11434      # 驗證監聽地址
  1. 防火墻規則
sudo ufw deny 11434/tcp      # 禁用Ollama外部端口

六、擴展應用場景

  1. 私有知識庫:接入LangChain處理本地文檔
  2. 自動化腳本:通過API實現代碼生成/自動Debug
  3. 硬件控制:結合HomeAssistant實現語音智能家居

完整代碼已開源(其實沒有):GitHub倉庫地址
通過本教程,您已掌握從模型部署到應用開發的全流程。本地化AI部署不僅降低成本,更保障了數據隱私,為開發者提供了真正的「AI自由」!

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/diannao/70222.shtml
繁體地址,請注明出處:http://hk.pswp.cn/diannao/70222.shtml
英文地址,請注明出處:http://en.pswp.cn/diannao/70222.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

圖論入門算法:拓撲排序(C++)

上文中我們了解了圖的遍歷(DFS/BFS), 本節我們來學習拓撲排序. 在圖論中, 拓撲排序(Topological Sorting)是對一個有向無環圖(Directed Acyclic Graph, DAG)的所有頂點進行排序的一種算法, 使得如果存在一條從頂點 u 到頂點 v 的有向邊 (u, v) , 那么在排序后的序列中, u 一定…

第1章大型互聯網公司的基礎架構——1.2 客戶端連接機房的技術1:DNS

客戶端啟動時要做的第一件事情就是通過互聯網與機房建立連接&#xff0c;然后用戶才可以在客戶端與后臺服務器進行網絡通信。目前在計算機網絡中應用較為廣泛的網絡通信協議是TCP/IP&#xff0c;它的通信基礎是IP地址&#xff0c;因為IP地址有如下兩個主要功能。 標識設備&…

全面解析鴻蒙(HarmonyOS)開發:從入門到實戰,構建萬物互聯新時代

文章目錄 引言 一、鴻蒙操作系統概述二、鴻蒙開發環境搭建三、鴻蒙核心開發技術1. **ArkUI框架**2. **分布式能力開發**3. **原子化服務與元服務** 四、實戰案例&#xff1a;構建分布式音樂播放器五、鴻蒙開發工具與調試技巧六、鴻蒙生態與未來展望結語 引言 隨著萬物互聯時代…

Android:播放Rtsp視頻流的兩種方式

一.SurfaceView Mediaplayer XML中添加SurfaceView: <SurfaceViewandroid:id"id/surface_view"android:layout_width"match_parent"android:layout_height"match_parent"/> Activity代碼&#xff1a; package com.android.rtsp;impor…

Next.js【詳解】CSS 樣式方案

全局樣式 Global CSS 默認已創建&#xff0c;即 src\app\globals.css&#xff0c;可根據需要修改 默認在全局布局中導入 src\app\layout.tsx import "./globals.css";組件樣式 CSS Modules 新建文件 src\app\test\styles.module.css .red {color: red;}導入目標頁面…

LVS相關原理

一、LVS集群的體系結構 1.1 LVS簡介 LVS 是 Linux Virtual Server 的簡稱&#xff0c;也就是 Linux 虛擬服務器 , 是一個由章文嵩博士發起的自由軟件項目&#xff0c;它的官方站點是 www.linuxvirtualserver.org 。現在 LVS 已經是 Linux標準內核的一部分&#xff0c;在Linux2…

【2025深度學習系列專欄大綱:深入探索與實踐深度學習】

第一部分:深度學習基礎篇 第1章:深度學習概覽 1.1 深度學習的歷史背景與發展軌跡 1.2 深度學習與機器學習、傳統人工智能的區別與聯系 1.3 深度學習的核心組件與概念解析 神經網絡基礎 激活函數的作用與類型 損失函數與優化算法的選擇 1.4 深度學習框架簡介與選擇建議 第2…

Java與C語言中取模運算符%的區別對比

博客主頁&#xff1a; [小????????] 本文專欄: Java 文章目錄 &#x1f4af;前言&#x1f4af;C語言中的取模運算符 %基本行為示例 注意事項示例&#xff1a;負數取模 &#x1f4af;Java中的取模運算符 %基本行為示例 對浮點數的支持示例&#xff1a;浮點數取模 符…

OpenCV機器學習(4)k-近鄰算法(k-Nearest Neighbors, KNN)cv::ml::KNearest類

操作系統&#xff1a;ubuntu22.04 OpenCV版本&#xff1a;OpenCV4.9 IDE:Visual Studio Code 編程語言&#xff1a;C11 算法描述 cv::ml::KNearest 是 OpenCV 機器學習模塊中的一部分&#xff0c;它提供了實現 k-近鄰算法&#xff08;k-Nearest Neighbors, KNN&#xff09;的…

過于依賴chatgpt編程會有哪些弊端?

過于依賴ChatGPT編程可能會帶來以下問題&#xff1a; 1. 基礎不扎實&#xff0c;容易“變菜” 以前遇到代碼還會琢磨哪里不懂、怎么改&#xff0c;現在直接復制粘貼&#xff0c;時間長了可能連基本的語法和邏輯都搞不清楚。就像考試總抄答案&#xff0c;真讓你自己寫的時候腦子…

紅隊視角出發的k8s敏感信息收集——Kubernetes API 擴展與未授權訪問

針對 Kubernetes 第三方組件與 Operator 的詳細攻擊視角分析&#xff0c;涵蓋 Service Mesh、Helm Releases 和 Database Operators 的潛在風險及利用方法。 攻擊鏈示例 1. 攻擊者通過未授權的 Tiller 服務部署惡意 Helm Chart → 2. 創建后門 Pod 并橫向移動至 Istio 控制平…

3D與2D機器視覺機械臂引導的區別

3D與2D機器視覺在機械臂引導中的主要區別如下&#xff1a; 數據維度 2D視覺&#xff1a;僅處理平面圖像&#xff0c;提供X、Y坐標信息&#xff0c;無法獲取深度&#xff08;Z軸&#xff09;數據。 3D視覺&#xff1a;處理三維空間數據&#xff0c;提供X、Y、Z坐標及物體的姿態…

日常開發中,使用JSON.stringify來實現深拷貝的坑

使用JSON.stringify的方式來實現深拷貝的弊端 弊端一&#xff1a;無法拷貝NaN、Infinity、undefined這類值 無法拷貝成功的原因&#xff1a; 對于JSON來說&#xff0c;它支持的數據類型只有null、string、number、boolean、Object、Array&#xff0c;所以對于它不支持的數據類…

AI大模型(如GPT、BERT等)可以通過自然語言處理(NLP)和機器學習技術,顯著提升測試效率

在軟件測試中,AI大模型(如GPT、BERT等)可以通過自然語言處理(NLP)和機器學習技術,顯著提升測試效率。以下是幾個具體的應用場景及對應的代碼實現示例: 1. 自動生成測試用例 AI大模型可以根據需求文檔或用戶故事自動生成測試用例。 代碼示例(使用 OpenAI GPT API): …

【Linux】Ubuntu Linux 系統——Node.js 開發環境

??大家好&#xff0c;我是練小杰&#xff0c;今天星期五了&#xff0c;同時也是2025年的情人節&#xff0c;今晚又是一個人的舉個爪子&#xff01;&#xff01; &#x1f642; 本文是有關Linux 操作系統中 Node.js 開發環境基礎知識&#xff0c;后續我將添加更多相關知識噢&a…

Dockerfile 編寫推薦

一、導讀 本文主要介紹在編寫 docker 鏡像的時候一些需要注意的事項和推薦的做法。 雖然 Dockerfile 簡化了鏡像構建的過程&#xff0c;并且把這個過程可以進行版本控制&#xff0c;但是不正當的 Dockerfile 使用也會導致很多問題。 docker 鏡像太大。如果你經常使用鏡像或者…

mysql 學習16 視圖,存儲過程,存儲函數,觸發器

視圖&#xff0c; 存儲過程&#xff0c; 存儲函數 觸發器

SpringBoot+Vue+數據可視化的動漫妝造服務平臺(程序+論文+講解+安裝+調試+售后等)

感興趣的可以先收藏起來&#xff0c;還有大家在畢設選題&#xff0c;項目以及論文編寫等相關問題都可以給我留言咨詢&#xff0c;我會一一回復&#xff0c;希望幫助更多的人。 系統介紹 在當今數字化高速發展的時代&#xff0c;動漫產業迎來了前所未有的繁榮&#xff0c;動漫…

rtsp rtmp 跟 http 區別

SDP 一SDP介紹 1. SDP的核心功能 會話描述&#xff1a;定義會話的名稱、創建者、時間范圍、連接地址等全局信息。媒體協商&#xff1a;明確媒體流的類型&#xff08;如音頻、視頻&#xff09;、傳輸協議&#xff08;如RTP/UDP&#xff09;、編碼格式&#xff08;如H.264、Op…

Containerd 簡介、安裝與使用指南

1. Containerd 簡介 Containerd 是一個開源的容器運行時&#xff0c;專注于管理容器的生命周期。它最初是 Docker 的一部分&#xff0c;后來被分離出來成為一個獨立的項目&#xff0c;并成為 Kubernetes 和其他容器平臺的底層運行時。Containerd 提供了容器的創建、啟動、停止…