Deep Seek R1本地化部署:用python代码调用模型
·

市场存在特别多关于大模型本地部署的教程、视频、案例卖课的同样存在很多。各种工具花样百出。
在ollma的基础上,我们使用python创建窗口用来调用,可以进行切换模型,如果由新下载的模型直接刷新就好。
# local_llm_gui_stream.py
import tkinter as tk
from tkinter import ttk, scrolledtext, messagebox, filedialog
import openai
import threading
import queue
import json
from datetime import datetime
import platform
class LLMGUI:
def __init__(self, root):
self.root = root
self.setup_system_font()
self.setup_ui_config()
root.title("一个黑客创业者 - 本地大模型交互工具 v2.2")
root.geometry("1200x800")
root.configure(bg=self.background_color)
# 初始化配置
self.base_url = "http://localhost:11434/v1"
self.current_model = "qwen2:7b"
self.history = []
self.response_queue = queue.Queue()
# 创建界面组件
self.create_widgets()
self.setup_styles()
# 初始化客户端
self.client = openai.OpenAI(
base_url=self.base_url,
api_key="sk-no-key-required"
)
# 启动队列处理器
self.process_queue()
self.load_models()
def setup_system_font(self):
"""设置跨平台系统字体"""
system = platform.system()
if system == 'Windows':
self.font_family = '微软雅黑'
elif system == 'Darwin':
self.font_family = 'PingFang SC'
else:
self.font_family = 'Noto Sans'
self.base_font = (self.font_family, 11)
self.title_font = (self.font_family, 12, 'bold')
self.status_font = (self.font_family, 9)
def setup_ui_config(self):
"""初始化UI全局配置"""
self.primary_color = '#2C3E50'
self.secondary_color = '#3498DB'
self.background_color = '#F5F5F5'
self.text_color = '#333333'
self.history_bg = '#FFFFFF'
def setup_styles(self):
"""配置UI样式"""
style = ttk.Style()
style.theme_use('clam')
# 通用样式
style.configure('.',
background=self.background_color,
font=self.base_font)
# 按钮样式
style.configure('TButton',
borderwidth=0,
relief='flat',
background='#ECF0F1',
foreground=self.text_color)
style.map('TButton',
background=[('active', self.secondary_color), ('!active', '#ECF0F1')],
foreground=[('active', 'white'), ('!active', self.text_color)])
# 标签框样式
style.configure('TLabelframe',
borderwidth=0,
background=self.background_color)
style.configure('TLabelframe.Label',
background=self.background_color,
foreground=self.primary_color,
font=self.title_font)
# 状态栏样式
style.configure('Status.TLabel',
background='#E0E0E0',
foreground=self.text_color,
font=self.status_font,
padding=5)
def create_widgets(self):
"""创建界面组件"""
# 主框架
main_frame = ttk.Frame(self.root)
main_frame.pack(fill=tk.BOTH, expand=True, padx=20, pady=20)
# 控制面板
control_frame = ttk.LabelFrame(main_frame, text="模型设置")
control_frame.pack(fill=tk.X, pady=10, ipadx=10, ipady=5)
# 模型选择
model_row = ttk.Frame(control_frame)
model_row.pack(fill=tk.X, pady=5)
ttk.Label(model_row, text="当前模型:").pack(side=tk.LEFT, padx=5)
self.model_combobox = ttk.Combobox(model_row, width=35)
self.model_combobox.pack(side=tk.LEFT, padx=5)
ttk.Button(model_row, text="刷新列表", command=self.load_models).pack(side=tk.LEFT)
# 参数设置
param_row = ttk.Frame(control_frame)
param_row.pack(fill=tk.X, pady=5)
ttk.Label(param_row, text="Temperature:").pack(side=tk.LEFT, padx=5)
self.temp_slider = ttk.Scale(param_row, from_=0.0, to=1.0, value=0.7)
self.temp_slider.pack(side=tk.LEFT, padx=5, fill=tk.X, expand=True)
self.temp_value = ttk.Label(param_row, text="0.7")
self.temp_value.pack(side=tk.LEFT, padx=5)
self.temp_slider.configure(command=lambda v: self.temp_value.config(text=f"{float(v):.1f}"))
# 对话区域
dialog_frame = ttk.LabelFrame(main_frame, text="对话")
dialog_frame.pack(fill=tk.BOTH, expand=True, pady=10)
# 历史记录
self.history_text = scrolledtext.ScrolledText(
dialog_frame,
wrap=tk.WORD,
font=self.base_font,
padx=15,
pady=15,
bg=self.history_bg,
borderwidth=1,
relief='solid'
)
self.history_text.pack(fill=tk.BOTH, expand=True)
self.history_text.tag_config('user', foreground='#2980B9', spacing2=5)
self.history_text.tag_config('assistant', foreground='#27AE60', spacing2=5)
self.history_text.configure(state='disabled')
# 输入区域
input_frame = ttk.Frame(dialog_frame)
input_frame.pack(fill=tk.X, pady=10)
self.input_text = scrolledtext.ScrolledText(
input_frame,
height=3,
wrap=tk.WORD,
font=self.base_font,
padx=10,
pady=10,
bg='white',
borderwidth=1,
relief='solid'
)
self.input_text.pack(fill=tk.X, expand=True)
self.input_text.bind("<Control-Return>", lambda e: self.start_query())
# 按钮面板
btn_frame = ttk.Frame(input_frame)
btn_frame.pack(pady=5)
self.send_btn = ttk.Button(btn_frame, text="发送 (Ctrl+Enter)", command=self.start_query)
self.send_btn.pack(side=tk.LEFT, padx=5)
ttk.Button(btn_frame, text="清空历史", command=self.clear_history).pack(side=tk.LEFT, padx=5)
ttk.Button(btn_frame, text="保存记录", command=self.save_chat_dialog).pack(side=tk.LEFT, padx=5)
# 状态栏
self.status = ttk.Label(main_frame, text="就绪", style='Status.TLabel')
self.status.pack(fill=tk.X)
def save_chat_dialog(self):
"""保存聊天记录对话框"""
top = tk.Toplevel(self.root)
top.title("保存选项")
top.geometry("300x150")
top.configure(bg=self.background_color)
ttk.Label(top, text="选择保存格式:").pack(pady=10)
btn_frame = ttk.Frame(top)
btn_frame.pack(pady=5)
ttk.Button(btn_frame, text="文本文件 (.txt)",
command=lambda: [self.save_chat('txt'), top.destroy()]).pack(side=tk.LEFT, padx=5)
ttk.Button(btn_frame, text="JSON文件 (.json)",
command=lambda: [self.save_chat('json'), top.destroy()]).pack(side=tk.LEFT, padx=5)
def save_chat(self, format='txt'):
"""保存聊天记录到文件"""
file_path = filedialog.asksaveasfilename(
defaultextension=f".{format}",
filetypes=[(f"{format.upper()}文件", f"*.{format}")],
title="保存聊天记录"
)
if not file_path:
return
try:
if format == 'txt':
self._save_as_txt(file_path)
elif format == 'json':
self._save_as_json(file_path)
self.update_status(f"聊天记录已保存至: {file_path}")
except Exception as e:
messagebox.showerror("保存错误", f"保存失败: {str(e)}")
def _save_as_txt(self, path):
"""保存为文本格式"""
with open(path, 'w', encoding='utf-8') as f:
f.write(f"=== 对话记录 ({datetime.now().strftime('%Y-%m-%d %H:%M')}) ===\n\n")
for msg in self.history:
role = msg['role'].capitalize()
content = msg['content'].strip()
f.write(f"[{role}]\n{content}\n\n")
def _save_as_json(self, path):
"""保存为JSON格式"""
with open(path, 'w', encoding='utf-8') as f:
json.dump({
"metadata": {
"save_time": datetime.now().isoformat(),
"model": self.current_model,
"temperature": float(self.temp_slider.get())
},
"history": self.history
}, f, ensure_ascii=False, indent=2)
def load_models(self):
"""加载可用模型列表"""
try:
models = [m.id for m in self.client.models.list().data]
self.model_combobox['values'] = models
if models:
self.current_model = models[0]
self.model_combobox.set(models[0])
self.update_status(f"已加载 {len(models)} 个模型")
except Exception as e:
messagebox.showerror("错误", f"无法获取模型列表: {str(e)}")
self.model_combobox['values'] = ['qwen2:7b', 'llama3.1']
def process_queue(self):
"""处理响应队列"""
try:
while True:
msg = self.response_queue.get_nowait()
if msg == 'EOS':
self.finalize_response()
elif isinstance(msg, dict):
self.append_stream(msg['content'])
elif isinstance(msg, Exception):
self.show_error(str(msg))
except queue.Empty:
pass
finally:
self.root.after(100, self.process_queue)
def start_query(self):
"""启动查询线程"""
prompt = self.input_text.get("1.0", tk.END).strip()
if not prompt:
return
self.send_btn.configure(state='disabled')
self.input_text.delete("1.0", tk.END)
self.update_status("生成中...")
self.append_history("user", prompt)
thread = threading.Thread(target=self.stream_query, args=(prompt,))
thread.daemon = True
thread.start()
def stream_query(self, prompt):
"""流式查询处理"""
try:
messages = self.history + [{"role": "user", "content": prompt}]
response = self.client.chat.completions.create(
model=self.current_model,
messages=messages,
temperature=float(self.temp_slider.get()),
max_tokens=2048,
stream=True
)
full_response = ""
for chunk in response:
content = chunk.choices[0].delta.content or ""
full_response += content
self.response_queue.put({'content': content})
self.history.append({"role": "assistant", "content": full_response})
self.response_queue.put('EOS')
except Exception as e:
self.response_queue.put(e)
def append_stream(self, content):
"""追加流式内容"""
self.history_text.configure(state='normal')
self.history_text.insert(tk.END, content, 'assistant')
self.history_text.see(tk.END)
self.history_text.configure(state='disabled')
def append_history(self, role, content):
"""添加历史记录"""
self.history_text.configure(state='normal')
self.history_text.insert(tk.END, f"\n[{role.capitalize()}] ", role)
self.history_text.insert(tk.END, f"{content}\n")
self.history_text.see(tk.END)
self.history_text.configure(state='disabled')
def finalize_response(self):
"""完成响应处理"""
self.send_btn.configure(state='normal')
self.update_status("就绪")
def show_error(self, message):
"""显示错误信息"""
self.history_text.configure(state='normal')
self.history_text.insert(tk.END, f"\n[系统] 错误: {message}\n", 'error')
self.history_text.see(tk.END)
self.history_text.configure(state='disabled')
self.send_btn.configure(state='normal')
self.update_status("错误发生")
def clear_history(self):
"""清空历史记录"""
self.history = []
self.history_text.configure(state='normal')
self.history_text.delete("1.0", tk.END)
self.history_text.configure(state='disabled')
self.update_status("历史已清空")
def update_status(self, message):
"""更新状态栏"""
self.status.config(text=f"状态: {message}")
if __name__ == "__main__":
root = tk.Tk()
app = LLMGUI(root)
root.mainloop()
更多推荐
所有评论(0)