python+ollama自己写代码调用本地deepseek模型

 更新时间:2025年03月21日 10:00:45   作者:杨正同学  
本文主要介绍了python+ollama自己写代码调用本地deepseek模型,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧

目的:本地部署ollama后,下载了一堆模型,没有合适的界面使用,干脆自己写一个简单的。

1、本地下载并安装ollama,Ollama

2、在平台上找到deepseek模型,Ollama,选哪个模型都行,看自己的机器能不能扛得住,可以先1.5b的试试再玩其他的。

3、本地cmd执行模型下载,举例:ollama pull deepseek-r1:1.5b

4、随便什么ide编写以下代码。

目录结构:

index.html:

<!DOCTYPE html>
<html>
<head>
    <title>本地模型聊天</title>
    <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.0.0/css/all.min.css" rel="external nofollow" >
    <link rel="stylesheet" href="/static/style.css" rel="external nofollow" >
</head>
<body>
    <div class="container">
        <div class="sidebar">
            <div class="history-header">
                <h3>聊天话题</h3>
                <div>
                    <button onclick="createNewChat()" title="新话题"><i class="fas fa-plus"></i></button>
                    <button onclick="clearHistory()" title="清空记录"><i class="fas fa-trash"></i></button>
                </div>
            </div>
            <ul id="historyList"></ul>
            <div class="model-select">
                <select id="modelSelect">
                    <option value="">选择模型...</option>
                </select>
            </div>
        </div>
        <div class="chat-container">
            <div id="chatHistory" class="chat-history"></div>
            <div class="input-area">
                <input type="text" id="messageInput" placeholder="输入消息 (Enter发送,Shift+Enter换行)" 
                       onkeydown="handleKeyPress(event)">
                <button onclick="sendMessage()"><i class="fas fa-paper-plane"></i></button>
            </div>
        </div>
    </div>

    <script>
        // 新增历史记录功能
        let currentChatId = null;
        
        // 初始化时加载历史记录
        window.onload = async () => {
            loadHistory();
            const response = await fetch('/get_models');
            const models = await response.json();
            const select = document.getElementById('modelSelect');
            models.forEach(model => {
                const option = document.createElement('option');
                option.value = model;
                option.textContent = model;
                select.appendChild(option);
            });
        };

        // 保存聊天记录
        function saveToHistory(chatData) {
            const history = JSON.parse(localStorage.getItem('chatHistory') || '[]');
            const existingIndex = history.findIndex(item => item.id === chatData.id);
            
            if (existingIndex > -1) {
                // 更新现有记录
                history[existingIndex] = chatData;
            } else {
                // 添加新记录
                history.unshift(chatData);
            }
            
            localStorage.setItem('chatHistory', JSON.stringify(history));
            loadHistory();
        }

        // 加载历史记录
        function loadHistory() {
            const history = JSON.parse(localStorage.getItem('chatHistory') || '[]');
            const list = document.getElementById('historyList');
            
            list.innerHTML = history.map(chat => `
                <li class="history-item" onclick="loadChat('${chat.id}')">
                    <div class="history-header">
                        <div class="history-model">${chat.model || '未指定模型'}</div>
                        <button class="delete-btn" onclick="deleteChat('${chat.id}', event)">
                            <i class="fas fa-times"></i>
                        </button>
                    </div>
                    <div class="history-preview">${chat.messages.slice(-1)[0]?.content || '无内容'}</div>
                    <div class="history-time">${new Date(chat.timestamp).toLocaleString()}</div>
                </li>
            `).join('');
        }

        // 回车处理
        function handleKeyPress(e) {
            if (e.key === 'Enter' && !e.shiftKey) {
                e.preventDefault();
                sendMessage();
            }
        }

        // 处理消息发送
        async function sendMessage() {
            const input = document.getElementById('messageInput');
            const message = input.value;
            const model = document.getElementById('modelSelect').value;
            
            if (!message || !model) return;

            const chatHistory = document.getElementById('chatHistory');
            
            // 添加用户消息(带图标)
            chatHistory.innerHTML += `
                <div class="message user-message">
                    <div class="avatar"><i class="fas fa-user"></i></div>
                    <div class="content">${message}</div>
                </div>
            `;

            // 创建AI消息容器(带图标)
            const aiMessageDiv = document.createElement('div');
            aiMessageDiv.className = 'message ai-message';
            aiMessageDiv.innerHTML = `
                <div class="avatar"><i class="fas fa-robot"></i></div>
                <div class="content"></div>
            `;
            chatHistory.appendChild(aiMessageDiv);

            // 清空输入框
            input.value = '';
            
            // 滚动到底部
            chatHistory.scrollTop = chatHistory.scrollHeight;

            // 发起请求
            const response = await fetch('/chat', {
                method: 'POST',
                headers: {
                    'Content-Type': 'application/json'
                },
                body: JSON.stringify({
                    model: model,
                    messages: [{content: message}]
                })
            });

            const reader = response.body.getReader();
            const decoder = new TextDecoder();
            
            let responseText = '';

            while(true) {
                const { done, value } = await reader.read();
                if (done) break;
                const chunk = decoder.decode(value);
                const lines = chunk.split('\n');
                
                lines.forEach(line => {
                    if (line.startsWith('data: ')) {
                        try {
                            const data = JSON.parse(line.slice(6));
                            responseText += data.response;
                            aiMessageDiv.querySelector('.content').textContent = responseText;
                            chatHistory.scrollTop = chatHistory.scrollHeight;
                        } catch(e) {
                            console.error('解析错误:', e);
                        }
                    }
                });
            }

            // 初始化或更新聊天记录
            if (!currentChatId) {
                currentChatId = Date.now().toString();
            }
            
            // 保存到历史记录
            const chatData = {
                id: currentChatId,
                model: model,
                timestamp: Date.now(),
                messages: [...existingMessages, 
                          {role: 'user', content: message},
                          {role: 'assistant', content: responseText}]
            };
            
            saveToHistory(chatData);
        }

        function createNewChat() {
            currentChatId = Date.now().toString();
            document.getElementById('chatHistory').innerHTML = '';
            document.getElementById('messageInput').value = '';
        }

        function deleteChat(chatId, event) {
            event.stopPropagation();
            const history = JSON.parse(localStorage.getItem('chatHistory') || '[]');
            const newHistory = history.filter(item => item.id !== chatId);
            localStorage.setItem('chatHistory', JSON.stringify(newHistory));
            loadHistory();
            
            if (chatId === currentChatId) {
                createNewChat();
            }
        }
    </script>
</body>
</html> 

style.css:

body {
    margin: 0;
    font-family: Arial, sans-serif;
    background: #f0f0f0;
}

.container {
    display: flex;
    height: 100vh;
}

.sidebar {
    width: 250px;
    background: #2c3e50;
    padding: 20px;
    color: white;
}

.chat-container {
    flex: 1;
    display: flex;
    flex-direction: column;
}

.chat-history {
    flex: 1;
    padding: 20px;
    overflow-y: auto;
    background: white;
}

.message {
    display: flex;
    align-items: start;
    gap: 10px;
    padding: 12px;
    margin: 10px 0;
}

.user-message {
    flex-direction: row-reverse;
}

.message .avatar {
    width: 32px;
    height: 32px;
    border-radius: 50%;
    flex-shrink: 0;
}

.user-message .avatar {
    background: #3498db;
    display: flex;
    align-items: center;
    justify-content: center;
}

.ai-message .avatar {
    background: #2ecc71;
    display: flex;
    align-items: center;
    justify-content: center;
}

.message .content {
    max-width: calc(100% - 50px);
    padding: 10px 15px;
    border-radius: 15px;
    white-space: pre-wrap;
    word-break: break-word;
    line-height: 1.5;
}

.input-area {
    display: flex;
    gap: 10px;
    padding: 15px;
    background: white;
    box-shadow: 0 -2px 10px rgba(0,0,0,0.1);
}

input[type="text"] {
    flex: 1;
    min-width: 300px;  /* 最小宽度 */
    padding: 12px 15px;
    border: 1px solid #ddd;
    border-radius: 25px;  /* 更圆润的边框 */
    margin-right: 0;
    font-size: 16px;
}

button {
    padding: 10px 20px;
    background: #007bff;
    color: white;
    border: none;
    border-radius: 5px;
    cursor: pointer;
}

button:hover {
    background: #0056b3;
}

/* 新增历史记录样式 */
.history-header {
    display: flex;
    justify-content: space-between;
    align-items: center;
    margin-bottom: 8px;
}

.history-item {
    padding: 12px;
    border-bottom: 1px solid #34495e;
    cursor: pointer;
    transition: background 0.3s;
}

.history-item:hover {
    background: #34495e;
}

.history-preview {
    color: #bdc3c7;
    font-size: 0.9em;
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}

.history-time {
    color: #7f8c8d;
    font-size: 0.8em;
    margin-top: 5px;
}

/* 新增代码块样式 */
.message .content pre {
    background: rgba(0,0,0,0.05);
    padding: 10px;
    border-radius: 5px;
    overflow-x: auto;
    white-space: pre-wrap;
}

.message .content code {
    font-family: 'Courier New', monospace;
    font-size: 0.9em;
}

/* 历史记录删除按钮 */
.delete-btn {
    background: none;
    border: none;
    color: #e74c3c;
    padding: 2px 5px;
    cursor: pointer;
}

.delete-btn:hover {
    color: #c0392b;
} 

app.py:

from flask import Flask, render_template, request, jsonify, Response
import requests
import subprocess
import time

app = Flask(__name__)
OLLAMA_HOST = "http://localhost:11434"

def start_ollama():
    try:
        # 尝试启动Ollama服务
        subprocess.Popen(["ollama", "serve"], stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL)
        time.sleep(2)  # 等待服务启动
    except Exception as e:
        print(f"启动Ollama时出错: {e}")

@app.route('/')
def index():
    return render_template('index.html')

@app.route('/get_models')
def get_models():
    try:
        response = requests.get(f"{OLLAMA_HOST}/api/tags")
        models = [model['name'] for model in response.json()['models']]
        return jsonify(models)
    except requests.ConnectionError:
        return jsonify({"error": "无法连接Ollama服务,请确保已安装并运行Ollama"}), 500

@app.route('/chat', methods=['POST'])
def chat():
    data = request.json
    model = data['model']
    messages = data['messages']
    
    def generate():
        try:
            response = requests.post(
                f"{OLLAMA_HOST}/api/generate",
                json={
                    "model": model,
                    "prompt": messages[-1]['content'],
                    "stream": True
                },
                stream=True
            )
            for line in response.iter_lines():
                if line:
                    yield f"data: {line.decode()}\n\n"
        except Exception as e:
            yield f"data: {str(e)}\n\n"
    
    return Response(generate(), mimetype='text/event-stream')

if __name__ == '__main__':
    start_ollama()
    app.run(debug=True, port=5000) 

运行环境:

  • Python 3.7+
  • Flask (pip install flask)
  • requests库 (pip install requests)
  • Ollama ,至少PULL一个模型

5、运行:python app.py

6、打开 http://127.0.0.1:5000 开始使用。

简单实现,界面如下,自己看着改:

到此这篇关于python+ollama自己写代码调用本地deepseek模型的文章就介绍到这了,更多相关python+ollama调用本地deepseek模型内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • django celery定时任务实战详解

    django celery定时任务实战详解

    本文介绍了在Django项目中配置Celery定时任务的步骤,包括安装、参数设置、创建任务文件、启动任务及后台配置,最后整理项目结构,本文给大家介绍的非常详细,感兴趣的朋友一起看看吧
    2025-07-07
  • Pycharm在创建py文件时,自动添加文件头注释的实例

    Pycharm在创建py文件时,自动添加文件头注释的实例

    今天小编就为大家分享一篇Pycharm在创建py文件时,自动添加文件头注释的实例,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧
    2018-05-05
  • python使用pyshark库捕获数据包的示例详解

    python使用pyshark库捕获数据包的示例详解

    PyShark是一个基于Python的网络数据包分析工具库,它允许用户捕获、解码和分析实时网络流量,特别是Wi-Fi和TCP/IP协议的数据,所以本文给大家介绍了python使用pyshark库捕获数据包的示例,需要的朋友可以参考下
    2024-08-08
  • Django Serializer HiddenField隐藏字段实例

    Django Serializer HiddenField隐藏字段实例

    这篇文章主要介绍了Django Serializer HiddenField隐藏字段实例,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧
    2020-03-03
  • 利用Python制作动态排名图的实现代码

    利用Python制作动态排名图的实现代码

    这篇文章主要介绍了利用Python制作动态排名图的实现代码,本文通过实例代码给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下
    2020-04-04
  • Python实战基础之Pandas统计某个数据列的空值个数

    Python实战基础之Pandas统计某个数据列的空值个数

    我们在处理数据的时候,经常需要检查数据的质量,也需要知道出问题的数据在哪个位置,下面这篇文章主要给大家介绍了关于Python实战基础之利用Pandas统计某个数据列空值个数的相关资料,需要的朋友可以参考下
    2022-08-08
  • 详解Python+OpenCV实现图像二值化

    详解Python+OpenCV实现图像二值化

    图像二值化就是将图像上的像素点的灰度值设置为0或255,也就是将整个图像呈现出明显的黑白效果的过程。本文将通过Python+OpenCV实现这一过程,感兴趣的可以学习一下
    2022-05-05
  • Python搭建自己IP代理池的方法实现

    Python搭建自己IP代理池的方法实现

    本文主要介绍了Python搭建自己IP代理池的方法实现,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2023-02-02
  • 20个被低估的Python性能优化技巧分享

    20个被低估的Python性能优化技巧分享

    这篇文章主要为大家详细介绍了20个被低估的Python性能优化技巧并附上了实测数据,文中的示例代码简洁易懂,有需要的小伙伴可以参考一下
    2025-03-03
  • Python gRPC流式通信协议详细讲解

    Python gRPC流式通信协议详细讲解

    这篇文章主要介绍了Python gRPC流式通信协议,最近几天在搞golang的grpc,跑通之后想用php作为客户端调用一下grpc服务,结果拉了,一个php的grpc服务安装,搞了好几天,总算搞定了
    2022-11-11

最新评论