作为国产AI模型的佼佼者,DeepSeek凭借流畅的中文理解能力和行业解决方案,正在成为开发者心中的"新宠"。其开放的API接口,让普通程序员也能轻松搭建智能对话系统。

注意需要替换对应API

一、Python调用DeepSeek API代码

# 安装 OpenAI SDK:pip install openai
from openai import OpenAI
# 创建 API 客户端
client = OpenAI(api_key="sk-xxxxxxxxxxxxxxx", base_url="https://api.deepseek.com")

# 调用 deepseek-chat 模型
response = client.chat.completions.create(
    model="deepseek-chat",
    messages=[
        {"role": "system", "content": "You are a helpful assistant."},
        {"role": "user", "content": "你好?"},
    ],
    stream=False  # 设置为 True 可启用流式输出
)

# 输出响应内容
print(response.choices[0].message.content)

二、DeepSeek+tkinter(一)

import tkinter as tk
from tkinter import scrolledtext, messagebox
import threading
from openai import OpenAI

class DeepSeekChatGUI:
    def __init__(self, root):
        self.root = root
        self.root.title("DeepSeek Chat 客户端")
        self.root.geometry("800x600")
        self.root.configure(bg="#f0f0f0")
        
        # 创建API客户端
        self.client = OpenAI(api_key="sk-xxxxxxxxxx", base_url="https://api.deepseek.com")
        
        self.setup_ui()
        
    def setup_ui(self):
        # 标题
        title_label = tk.Label(self.root, text="DeepSeek Chat", font=("Arial", 16, "bold"), bg="#f0f0f0")
        title_label.pack(pady=10)
        
        # 聊天历史显示区域
        self.chat_history = scrolledtext.ScrolledText(self.root, wrap=tk.WORD, width=70, height=20, font=("Arial", 10))
        self.chat_history.pack(padx=20, pady=10, fill=tk.BOTH, expand=True)
        self.chat_history.config(state=tk.DISABLED)
        
        # 输入区域框架
        input_frame = tk.Frame(self.root, bg="#f0f0f0")
        input_frame.pack(padx=20, pady=10, fill=tk.X)
        
        # 用户输入框
        self.user_input = scrolledtext.ScrolledText(input_frame, wrap=tk.WORD, width=50, height=3, font=("Arial", 10))
        self.user_input.pack(side=tk.LEFT, padx=(0, 10), fill=tk.X, expand=True)
        
        # 发送按钮
        send_button = tk.Button(input_frame, text="发送", command=self.send_message, bg="#4CAF50", fg="white", 
                               font=("Arial", 10, "bold"), width=10, height=2)
        send_button.pack(side=tk.RIGHT)
        
        # 绑定回车键发送消息
        self.user_input.bind("<Control-Return>", lambda event: self.send_message())
        
        # 添加使用提示
        tip_label = tk.Label(self.root, text="提示: 使用 Ctrl+Enter 快速发送消息", 
                            font=("Arial", 9), fg="gray", bg="#f0f0f0")
        tip_label.pack(pady=5)
        
    def send_message(self):
        user_message = self.user_input.get("1.0", tk.END).strip()
        if not user_message:
            messagebox.showwarning("输入错误", "请输入消息内容")
            return
            
        # 禁用输入框和按钮
        self.user_input.config(state=tk.DISABLED)
        
        # 显示用户消息
        self.update_chat_history("用户", user_message)
        
        # 清空输入框
        self.user_input.delete("1.0", tk.END)
        
        # 在新线程中调用API,避免界面冻结
        threading.Thread(target=self.call_deepseek_api, args=(user_message,), daemon=True).start()
        
    def call_deepseek_api(self, user_message):
        try:
            # 显示"正在思考..."提示
            self.root.after(0, self.show_thinking)
            
            # 调用API
            response = self.client.chat.completions.create(
                model="deepseek-chat",
                messages=[
                    {"role": "system", "content": "You are a helpful assistant."},
                    {"role": "user", "content": user_message},
                ],
                stream=False
            )
            
            # 获取回复内容
            assistant_reply = response.choices[0].message.content
            
            # 更新聊天历史
            self.root.after(0, self.update_chat_history, "助手", assistant_reply)
            
        except Exception as e:
            error_msg = f"API调用出错: {str(e)}"
            self.root.after(0, self.update_chat_history, "系统", error_msg)
            
        finally:
            # 重新启用输入框
            self.root.after(0, self.enable_input)
    
    def show_thinking(self):
        self.update_chat_history("助手", "正在思考...")
    
    def update_chat_history(self, sender, message):
        self.chat_history.config(state=tk.NORMAL)
        
        # 添加发送者标签
        if sender == "用户":
            self.chat_history.insert(tk.END, "您: ", "user")
        elif sender == "助手":
            self.chat_history.insert(tk.END, "DeepSeek: ", "assistant")
        else:
            self.chat_history.insert(tk.END, "系统: ", "system")
        
        # 添加消息内容
        self.chat_history.insert(tk.END, message + "\n\n")
        
        # 配置标签样式
        self.chat_history.tag_config("user", foreground="blue")
        self.chat_history.tag_config("assistant", foreground="green")
        self.chat_history.tag_config("system", foreground="red")
        
        # 滚动到底部
        self.chat_history.see(tk.END)
        self.chat_history.config(state=tk.DISABLED)
    
    def enable_input(self):
        self.user_input.config(state=tk.NORMAL)
        self.user_input.focus()

if __name__ == "__main__":
    root = tk.Tk()
    app = DeepSeekChatGUI(root)
    root.mainloop()

对应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 Chat 客户端</title>
    <style>
        * {
            margin: 0;
            padding: 0;
            box-sizing: border-box;
            font-family: 'Arial', 'Microsoft YaHei', sans-serif;
        }

        body {
            background-color: #f5f7fa;
            color: #333;
            min-height: 100vh;
            display: flex;
            flex-direction: column;
        }

        /* 标题区域 */
        .header {
            background-color: #2c3e50;
            color: white;
            padding: 1rem 2rem;
            text-align: center;
            box-shadow: 0 2px 4px rgba(0,0,0,0.1);
        }

        .header h1 {
            font-size: 1.8rem;
            font-weight: 600;
        }

        /* 聊天容器 */
        .chat-container {
            flex: 1;
            max-width: 1000px;
            width: 100%;
            margin: 1.5rem auto;
            padding: 0 1rem;
            display: flex;
            flex-direction: column;
            gap: 1rem;
        }

        /* 聊天历史区域 */
        .chat-history {
            flex: 1;
            background-color: white;
            border-radius: 10px;
            padding: 1.5rem;
            overflow-y: auto;
            box-shadow: 0 2px 8px rgba(0,0,0,0.08);
            min-height: 400px;
            max-height: 600px;
        }

        /* 滚动条样式优化 */
        .chat-history::-webkit-scrollbar {
            width: 8px;
        }

        .chat-history::-webkit-scrollbar-track {
            background: #f1f1f1;
            border-radius: 4px;
        }

        .chat-history::-webkit-scrollbar-thumb {
            background: #bbb;
            border-radius: 4px;
        }

        .chat-history::-webkit-scrollbar-thumb:hover {
            background: #999;
        }

        /* 消息气泡样式 */
        .message {
            margin-bottom: 1.2rem;
            padding: 0.8rem 1.2rem;
            border-radius: 8px;
            max-width: 80%;
            line-height: 1.5;
        }

        .message-user {
            background-color: #e3f2fd;
            color: #1976d2;
            margin-left: auto;
        }

        .message-assistant {
            background-color: #e8f5e9;
            color: #2e7d32;
            margin-right: auto;
        }

        .message-system {
            background-color: #ffebee;
            color: #c62828;
            margin: 0 auto;
            text-align: center;
            max-width: 60%;
        }

        /* 消息发送者标签 */
        .message-sender {
            font-weight: 600;
            margin-bottom: 0.3rem;
            display: block;
        }

        /* 输入区域 */
        .input-container {
            display: flex;
            gap: 1rem;
            align-items: flex-end;
        }

        #user-input {
            flex: 1;
            border: 1px solid #ddd;
            border-radius: 8px;
            padding: 1rem;
            min-height: 80px;
            resize: none;
            font-size: 1rem;
            transition: border-color 0.3s;
        }

        #user-input:focus {
            outline: none;
            border-color: #2196f3;
            box-shadow: 0 0 0 2px rgba(33, 150, 243, 0.1);
        }

        #send-btn {
            padding: 0.8rem 2rem;
            background-color: #4CAF50;
            color: white;
            border: none;
            border-radius: 8px;
            font-size: 1rem;
            font-weight: 600;
            cursor: pointer;
            transition: background-color 0.3s;
            height: 50px;
        }

        #send-btn:hover {
            background-color: #43a047;
        }

        #send-btn:disabled {
            background-color: #a5d6a7;
            cursor: not-allowed;
        }

        /* 提示信息 */
        .tip {
            text-align: center;
            color: #666;
            font-size: 0.9rem;
            margin-bottom: 1rem;
        }

        /* 响应式调整 */
        @media (max-width: 768px) {
            .input-container {
                flex-direction: column;
                align-items: stretch;
            }

            #send-btn {
                height: auto;
                padding: 1rem;
            }

            .message {
                max-width: 90%;
            }
        }
    </style>
</head>
<body>
    <!-- 标题区域 -->
    <header class="header">
        <h1>DeepSeek Chat 客户端</h1>
    </header>

    <!-- 主聊天容器 -->
    <main class="chat-container">
        <!-- 聊天历史显示区 -->
        <div class="chat-history" id="chat-history"></div>

        <!-- 使用提示 -->
        <div class="tip">提示: 使用 Ctrl+Enter 快速发送消息 | 输入内容后点击发送按钮也可提交</div>

        <!-- 输入和发送区域 -->
        <div class="input-container">
            <textarea 
                id="user-input" 
                placeholder="请输入您的问题或消息..."
                autocomplete="off"
                autocorrect="off"
                autocapitalize="off"
                spellcheck="false"
            ></textarea>
            <button id="send-btn" onclick="sendMessage()">发送</button>
        </div>
    </main>

    <script>
        // 配置信息(请替换为您自己的 API 密钥)
        const API_CONFIG = {
            apiKey: "sk-xxxxxxxxxx", // 替换为您的 DeepSeek API 密钥
            baseUrl: "https://api.deepseek.com/v1/chat/completions", // DeepSeek API 端点
            model: "deepseek-chat" // 使用的模型
        };

        // DOM 元素
        const chatHistoryEl = document.getElementById('chat-history');
        const userInputEl = document.getElementById('user-input');
        const sendBtnEl = document.getElementById('send-btn');

        // 初始化:绑定 Ctrl+Enter 事件
        userInputEl.addEventListener('keydown', (e) => {
            if (e.ctrlKey && e.key === 'Enter') {
                e.preventDefault(); // 阻止默认换行行为
                sendMessage();
            }
        });

        /**
         * 发送消息函数
         */
        async function sendMessage() {
            // 1. 获取并验证用户输入
            const userMessage = userInputEl.value.trim();
            if (!userMessage) {
                alert('请输入消息内容后再发送!');
                return;
            }

            // 2. 禁用输入和按钮,防止重复发送
            disableInput(true);

            // 3. 显示用户消息
            addMessageToHistory('user', userMessage);

            // 4. 清空输入框
            userInputEl.value = '';

            // 5. 显示"正在思考"提示
            const thinkingMsgId = addMessageToHistory('assistant', '正在思考...', true);

            try {
                // 6. 调用 DeepSeek API
                const response = await fetch(API_CONFIG.baseUrl, {
                    method: 'POST',
                    headers: {
                        'Content-Type': 'application/json',
                        'Authorization': `Bearer ${API_CONFIG.apiKey}`
                    },
                    body: JSON.stringify({
                        model: API_CONFIG.model,
                        messages: [
                            { role: 'system', content: 'You are a helpful assistant.' },
                            { role: 'user', content: userMessage }
                        ],
                        stream: false // 非流式响应(如需流式可改为 true 并处理)
                    })
                });

                // 7. 处理 API 响应
                if (!response.ok) {
                    const errorData = await response.json().catch(() => ({}));
                    throw new Error(errorData.error?.message || `API 请求失败 (状态码: ${response.status})`);
                }

                const data = await response.json();
                const assistantReply = data.choices[0]?.message?.content || '未获取到有效回复';

                // 8. 替换"正在思考"为实际回复
                updateMessageInHistory(thinkingMsgId, assistantReply);

            } catch (error) {
                // 9. 处理错误
                updateMessageInHistory(thinkingMsgId, `API调用出错: ${error.message}`, 'system');

            } finally {
                // 10. 重新启用输入和按钮
                disableInput(false);
                // 聚焦输入框,方便继续输入
                userInputEl.focus();
            }
        }

        /**
         * 添加消息到聊天历史
         * @param {string} sender 发送者类型: user/assistant/system
         * @param {string} content 消息内容
         * @param {boolean} isTemporary 是否为临时消息(如"正在思考")
         * @returns {string} 消息元素的 ID(用于后续更新)
         */
        function addMessageToHistory(sender, content, isTemporary = false) {
            // 生成唯一 ID(用于临时消息更新)
            const msgId = isTemporary ? `temp-${Date.now()}` : `msg-${Date.now()}`;

            // 确定消息类型和发送者名称
            let senderClass, senderName;
            switch (sender) {
                case 'user':
                    senderClass = 'message-user';
                    senderName = '您';
                    break;
                case 'assistant':
                    senderClass = 'message-assistant';
                    senderName = 'DeepSeek';
                    break;
                case 'system':
                    senderClass = 'message-system';
                    senderName = '系统';
                    break;
                default:
                    senderClass = 'message-system';
                    senderName = '未知';
            }

            // 创建消息元素
            const messageEl = document.createElement('div');
            messageEl.id = msgId;
            messageEl.className = `message ${senderClass}`;
            messageEl.innerHTML = `
                <span class="message-sender">${senderName}:</span>
                <span class="message-content">${formatMessageContent(content)}</span>
            `;

            // 添加到历史区域并滚动到底部
            chatHistoryEl.appendChild(messageEl);
            scrollToBottom();

            return msgId;
        }

        /**
         * 更新历史中的消息(用于替换临时消息)
         * @param {string} msgId 要更新的消息 ID
         * @param {string} newContent 新的消息内容
         * @param {string} newSender 可选:新的发送者类型(如错误时改为 system)
         */
        function updateMessageInHistory(msgId, newContent, newSender = null) {
            const messageEl = document.getElementById(msgId);
            if (!messageEl) return;

            // 更新内容
            const contentEl = messageEl.querySelector('.message-content');
            contentEl.textContent = newContent;

            // 如果需要更改发送者类型(如错误场景)
            if (newSender && newSender !== getSenderTypeFromClass(messageEl.className)) {
                // 移除旧类,添加新类
                messageEl.classList.remove(
                    'message-user', 'message-assistant', 'message-system'
                );
                messageEl.classList.add(`message-${newSender}`);

                // 更新发送者名称
                const senderEl = messageEl.querySelector('.message-sender');
                senderEl.textContent = newSender === 'system' ? '系统:' : 'DeepSeek:';
            }

            scrollToBottom();
        }

        /**
         * 禁用/启用输入框和发送按钮
         * @param {boolean} isDisabled 是否禁用
         */
        function disableInput(isDisabled) {
            userInputEl.disabled = isDisabled;
            sendBtnEl.disabled = isDisabled;
        }

        /**
         * 滚动聊天历史到底部
         */
        function scrollToBottom() {
            chatHistoryEl.scrollTop = chatHistoryEl.scrollHeight;
        }

        /**
         * 从消息元素的类名中获取发送者类型
         * @param {string} className 元素的类名字符串
         * @returns {string} 发送者类型: user/assistant/system
         */
        function getSenderTypeFromClass(className) {
            if (className.includes('message-user')) return 'user';
            if (className.includes('message-assistant')) return 'assistant';
            if (className.includes('message-system')) return 'system';
            return '';
        }

        /**
         * 格式化消息内容(处理换行等)
         * @param {string} content 原始消息内容
         * @returns {string} 格式化后的内容
         */
        function formatMessageContent(content) {
            // 替换 \n 为实际换行(避免 HTML 中不显示换行)
            return content.replace(/\n/g, '\n');
        }
    </script>
</body>
</html>

三、DeepSeek+tkinter(二)

import tkinter as tk
from tkinter import ttk, scrolledtext, messagebox
from openai import OpenAI
import threading

class DeepSeekChatGUI:
    def __init__(self, root):
        self.root = root
        self.root.title("DeepSeek Chat Assistant")
        self.root.geometry("800x600")
        self.root.configure(bg="#f0f0f0")
        
        # 创建API客户端
        self.client = OpenAI(api_key="sk-xxxxxxxxxx", base_url="https://api.deepseek.com")
        
        self.setup_ui()
        
    def setup_ui(self):
        # 主框架
        main_frame = ttk.Frame(self.root, padding="10")
        main_frame.grid(row=0, column=0, sticky=(tk.W, tk.E, tk.N, tk.S))
        
        # 配置行列权重
        self.root.columnconfigure(0, weight=1)
        self.root.rowconfigure(0, weight=1)
        main_frame.columnconfigure(1, weight=1)
        main_frame.rowconfigure(1, weight=1)
        
        # API密钥标签和输入框
        ttk.Label(main_frame, text="API密钥:").grid(row=0, column=0, sticky=tk.W, pady=5)
        self.api_key_var = tk.StringVar(value="sk-66c4e140194643b0b0b079f7d11395c8")
        api_key_entry = ttk.Entry(main_frame, textvariable=self.api_key_var, width=50, show="*")
        api_key_entry.grid(row=0, column=1, sticky=(tk.W, tk.E), pady=5)
        
        # 模型选择
        ttk.Label(main_frame, text="模型:").grid(row=0, column=2, sticky=tk.W, padx=(10, 0), pady=5)
        self.model_var = tk.StringVar(value="deepseek-chat")
        model_combo = ttk.Combobox(main_frame, textvariable=self.model_var, 
                                  values=["deepseek-chat", "deepseek-coder"], width=15)
        model_combo.grid(row=0, column=3, sticky=tk.W, pady=5)
        
        # 对话历史显示区域
        ttk.Label(main_frame, text="对话历史:").grid(row=1, column=0, sticky=tk.NW, pady=5)
        self.conversation_text = scrolledtext.ScrolledText(main_frame, width=80, height=20, wrap=tk.WORD)
        self.conversation_text.grid(row=1, column=1, columnspan=3, sticky=(tk.W, tk.E, tk.N, tk.S), pady=5)
        self.conversation_text.config(state=tk.DISABLED)
        
        # 用户输入区域
        ttk.Label(main_frame, text="你的消息:").grid(row=2, column=0, sticky=tk.NW, pady=5)
        self.user_input = scrolledtext.ScrolledText(main_frame, width=80, height=4, wrap=tk.WORD)
        self.user_input.grid(row=2, column=1, columnspan=3, sticky=(tk.W, tk.E), pady=5)
        
        # 按钮框架
        button_frame = ttk.Frame(main_frame)
        button_frame.grid(row=3, column=1, columnspan=3, sticky=tk.E, pady=10)
        
        # 发送按钮
        self.send_button = ttk.Button(button_frame, text="发送", command=self.send_message)
        self.send_button.pack(side=tk.RIGHT, padx=5)
        
        # 清除按钮
        clear_button = ttk.Button(button_frame, text="清除对话", command=self.clear_conversation)
        clear_button.pack(side=tk.RIGHT, padx=5)
        
        # 流式输出选项
        self.stream_var = tk.BooleanVar(value=False)
        stream_check = ttk.Checkbutton(button_frame, text="流式输出", variable=self.stream_var)
        stream_check.pack(side=tk.RIGHT, padx=5)
        
        # 绑定回车键发送消息
        self.user_input.bind("<Control-Return>", lambda event: self.send_message())
        
        # 保存对话历史
        self.conversation_history = [
            {"role": "system", "content": "You are a helpful assistant."}
        ]
        
    def send_message(self):
        user_message = self.user_input.get("1.0", tk.END).strip()
        if not user_message:
            messagebox.showwarning("输入错误", "请输入消息内容")
            return
            
        # 禁用发送按钮防止重复点击
        self.send_button.config(state=tk.DISABLED)
        
        # 添加用户消息到对话历史
        self.conversation_history.append({"role": "user", "content": user_message})
        
        # 显示用户消息
        self.display_message("用户", user_message)
        
        # 清空输入框
        self.user_input.delete("1.0", tk.END)
        
        # 在新线程中调用API
        threading.Thread(target=self.call_api, daemon=True).start()
        
    def call_api(self):
        try:
            # 更新API密钥
            self.client.api_key = self.api_key_var.get()
            
            # 调用API
            response = self.client.chat.completions.create(
                model=self.model_var.get(),
                messages=self.conversation_history,
                stream=self.stream_var.get()
            )
            
            if self.stream_var.get():
                # 流式输出处理
                assistant_message = ""
                for chunk in response:
                    if chunk.choices[0].delta.content:
                        content = chunk.choices[0].delta.content
                        assistant_message += content
                        # 实时更新显示(需要在主线程中执行)
                        self.root.after(0, self.update_stream_display, content)
                
                # 添加完整的助手消息到历史
                self.conversation_history.append({"role": "assistant", "content": assistant_message})
            else:
                # 非流式输出
                assistant_message = response.choices[0].message.content
                self.conversation_history.append({"role": "assistant", "content": assistant_message})
                # 在主线程中更新UI
                self.root.after(0, self.display_message, "助手", assistant_message)
                
        except Exception as e:
            error_msg = f"API调用错误: {str(e)}"
            self.root.after(0, self.display_message, "系统", error_msg, is_error=True)
        finally:
            # 重新启用发送按钮
            self.root.after(0, lambda: self.send_button.config(state=tk.NORMAL))
            
    def update_stream_display(self, content):
        # 更新流式输出显示
        self.conversation_text.config(state=tk.NORMAL)
        self.conversation_text.insert(tk.END, content)
        self.conversation_text.see(tk.END)
        self.conversation_text.config(state=tk.DISABLED)
        
    def display_message(self, sender, message, is_error=False):
        self.conversation_text.config(state=tk.NORMAL)
        
        # 添加发送者标签
        if is_error:
            self.conversation_text.insert(tk.END, f"{sender}: ", "error")
        else:
            self.conversation_text.insert(tk.END, f"{sender}: ", "sender")
        
        # 添加消息内容
        self.conversation_text.insert(tk.END, f"{message}\n\n")
        
        # 自动滚动到底部
        self.conversation_text.see(tk.END)
        self.conversation_text.config(state=tk.DISABLED)
        
        # 配置标签样式
        self.conversation_text.tag_configure("sender", foreground="blue")
        self.conversation_text.tag_configure("error", foreground="red")
        
    def clear_conversation(self):
        # 清除对话历史和显示
        self.conversation_history = [
            {"role": "system", "content": "You are a helpful assistant."}
        ]
        self.conversation_text.config(state=tk.NORMAL)
        self.conversation_text.delete("1.0", tk.END)
        self.conversation_text.config(state=tk.DISABLED)

if __name__ == "__main__":
    root = tk.Tk()
    app = DeepSeekChatGUI(root)
    root.mainloop()

对应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 Chat Assistant</title>
    <style>
        * {
            margin: 0;
            padding: 0;
            box-sizing: border-box;
            font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
        }

        body {
            background-color: #f0f0f0;
            padding: 20px;
            min-height: 100vh;
        }

        .container {
            max-width: 1200px;
            margin: 0 auto;
            background-color: #ffffff;
            border-radius: 12px;
            box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1);
            padding: 20px;
            display: flex;
            flex-direction: column;
            gap: 20px;
        }

        /* 顶部配置区样式 */
        .config-section {
            display: flex;
            flex-wrap: wrap;
            gap: 15px;
            align-items: center;
            padding-bottom: 15px;
            border-bottom: 1px solid #eee;
        }

        .config-group {
            display: flex;
            flex-direction: column;
            gap: 5px;
            flex: 1;
            min-width: 250px;
        }

        .config-group label {
            font-weight: 500;
            color: #333;
            font-size: 14px;
        }

        .config-group input,
        .config-group select {
            padding: 10px 12px;
            border: 1px solid #ddd;
            border-radius: 6px;
            font-size: 14px;
            transition: border-color 0.3s;
        }

        .config-group input:focus,
        .config-group select:focus {
            outline: none;
            border-color: #4285f4;
            box-shadow: 0 0 0 2px rgba(66, 133, 244, 0.1);
        }

        /* 对话历史区样式 */
        .conversation-section {
            display: flex;
            flex-direction: column;
            gap: 8px;
            flex: 1;
            min-height: 400px;
        }

        .conversation-section label {
            font-weight: 500;
            color: #333;
            font-size: 14px;
        }

        #conversation-history {
            flex: 1;
            padding: 15px;
            border: 1px solid #ddd;
            border-radius: 6px;
            background-color: #fafafa;
            overflow-y: auto;
            white-space: pre-wrap;
            line-height: 1.6;
            font-size: 15px;
        }

        .sender-tag {
            font-weight: 600;
            color: #4285f4;
        }

        .error-tag {
            font-weight: 600;
            color: #ea4335;
        }

        /* 用户输入区样式 */
        .input-section {
            display: flex;
            flex-direction: column;
            gap: 8px;
        }

        .input-section label {
            font-weight: 500;
            color: #333;
            font-size: 14px;
        }

        #user-input {
            padding: 15px;
            border: 1px solid #ddd;
            border-radius: 6px;
            resize: none;
            min-height: 120px;
            font-size: 15px;
            line-height: 1.6;
            transition: border-color 0.3s;
        }

        #user-input:focus {
            outline: none;
            border-color: #4285f4;
            box-shadow: 0 0 0 2px rgba(66, 133, 244, 0.1);
        }

        /* 按钮区样式 */
        .button-section {
            display: flex;
            justify-content: flex-end;
            align-items: center;
            gap: 12px;
            padding-top: 10px;
        }

        .stream-option {
            display: flex;
            align-items: center;
            gap: 5px;
            font-size: 14px;
            color: #555;
        }

        button {
            padding: 10px 20px;
            border: none;
            border-radius: 6px;
            font-size: 14px;
            font-weight: 500;
            cursor: pointer;
            transition: all 0.3s;
        }

        #send-btn {
            background-color: #4285f4;
            color: white;
        }

        #send-btn:disabled {
            background-color: #a8c7fa;
            cursor: not-allowed;
        }

        #clear-btn {
            background-color: #f8f9fa;
            color: #333;
            border: 1px solid #ddd;
        }

        #clear-btn:hover {
            background-color: #f1f3f4;
        }

        /* 响应式调整 */
        @media (max-width: 768px) {
            .config-section {
                flex-direction: column;
                align-items: stretch;
            }

            .button-section {
                flex-wrap: wrap;
                justify-content: space-between;
            }
        }
    </style>
</head>
<body>
    <div class="container">
        <!-- 顶部配置区:API密钥 + 模型选择 -->
        <div class="config-section">
            <div class="config-group">
                <label for="api-key">API密钥</label>
                <input 
                    type="password" 
                    id="api-key" 
                    value="sk-xxxxxxxxxx" 
                    placeholder="请输入DeepSeek API密钥"
                >
            </div>
            <div class="config-group">
                <label for="model-select">模型选择</label>
                <select id="model-select">
                    <option value="deepseek-chat" selected>deepseek-chat</option>
                    <option value="deepseek-coder">deepseek-coder</option>
                </select>
            </div>
        </div>

        <!-- 对话历史显示区 -->
        <div class="conversation-section">
            <label>对话历史</label>
            <div id="conversation-history"></div>
        </div>

        <!-- 用户输入区 -->
        <div class="input-section">
            <label>你的消息(按 Ctrl+Enter 发送)</label>
            <textarea id="user-input" placeholder="请输入你的问题或需求..."></textarea>
        </div>

        <!-- 按钮区:流式输出选项 + 清除按钮 + 发送按钮 -->
        <div class="button-section">
            <div class="stream-option">
                <input type="checkbox" id="stream-mode">
                <label for="stream-mode">流式输出</label>
            </div>
            <button id="clear-btn">清除对话</button>
            <button id="send-btn">发送</button>
        </div>
    </div>

    <script>
        // 全局状态管理
        const state = {
            conversationHistory: [
                { role: "system", content: "You are a helpful assistant." }
            ],
            isSending: false // 防止重复发送
        };

        // DOM 元素获取
        const apiKeyInput = document.getElementById('api-key');
        const modelSelect = document.getElementById('model-select');
        const conversationHistoryEl = document.getElementById('conversation-history');
        const userInputEl = document.getElementById('user-input');
        const sendBtn = document.getElementById('send-btn');
        const clearBtn = document.getElementById('clear-btn');
        const streamModeEl = document.getElementById('stream-mode');

        // 初始化事件监听
        function initEventListeners() {
            // 发送按钮点击事件
            sendBtn.addEventListener('click', sendMessage);
            
            // 清除对话按钮点击事件
            clearBtn.addEventListener('click', clearConversation);
            
            // Ctrl+Enter 发送消息
            userInputEl.addEventListener('keydown', (e) => {
                if (e.ctrlKey && e.key === 'Enter') {
                    e.preventDefault();
                    sendMessage();
                }
            });
        }

        // 发送消息处理
        async function sendMessage() {
            const userMessage = userInputEl.value.trim();
            // 输入验证
            if (!userMessage) {
                alert('请输入消息内容');
                return;
            }
            // 防止重复发送
            if (state.isSending) return;

            // 更新状态和UI
            state.isSending = true;
            sendBtn.disabled = true;
            sendBtn.textContent = '发送中...';
            
            // 添加用户消息到历史
            state.conversationHistory.push({ role: 'user', content: userMessage });
            // 显示用户消息
            displayMessage('用户', userMessage);
            // 清空输入框
            userInputEl.value = '';

            try {
                await callDeepSeekAPI(userMessage);
            } catch (error) {
                displayMessage('系统', `API调用错误: ${error.message}`, true);
            } finally {
                // 恢复状态和UI
                state.isSending = false;
                sendBtn.disabled = false;
                sendBtn.textContent = '发送';
            }
        }

        // 调用DeepSeek API
        async function callDeepSeekAPI(userMessage) {
            const apiKey = apiKeyInput.value.trim();
            const model = modelSelect.value;
            const streamMode = streamModeEl.checked;
            const apiUrl = 'https://api.deepseek.com/v1/chat/completions';

            // 请求配置
            const requestOptions = {
                method: 'POST',
                headers: {
                    'Authorization': `Bearer ${apiKey}`,
                    'Content-Type': 'application/json'
                },
                body: JSON.stringify({
                    model,
                    messages: state.conversationHistory,
                    stream: streamMode,
                    temperature: 0.7 // 默认温度值,可调整
                })
            };

            const response = await fetch(apiUrl, requestOptions);
            
            // 处理响应错误
            if (!response.ok) {
                const errorData = await response.json().catch(() => ({}));
                throw new Error(errorData.error?.message || `HTTP错误: ${response.status}`);
            }

            // 处理流式响应
            if (streamMode) {
                await handleStreamResponse(response);
            } else {
                // 处理非流式响应
                const responseData = await response.json();
                const assistantMessage = responseData.choices[0].message.content;
                // 添加到历史
                state.conversationHistory.push({ role: 'assistant', content: assistantMessage });
                // 显示助手消息
                displayMessage('助手', assistantMessage);
            }
        }

        // 处理流式响应
        async function handleStreamResponse(response) {
            if (!response.body) {
                throw new Error('流式响应不支持');
            }

            const reader = response.body.getReader();
            const decoder = new TextDecoder('utf-8');
            let assistantMessage = '';
            let streamComplete = false;

            // 显示助手消息前缀(流式输出时先占位)
            const assistantPrefixEl = document.createElement('div');
            assistantPrefixEl.innerHTML = `<span class="sender-tag">助手:</span>`;
            conversationHistoryEl.appendChild(assistantPrefixEl);
            scrollToBottom();

            try {
                while (!streamComplete) {
                    const { done, value } = await reader.read();
                    streamComplete = done;

                    if (value) {
                        // 解析流式数据(逐行处理)
                        const chunkText = decoder.decode(value);
                        const lines = chunkText.split('\n').filter(line => line.trim() !== '');

                        for (const line of lines) {
                            if (line.startsWith('data: ')) {
                                const data = line.substring(6);
                                if (data === '[DONE]') continue;

                                try {
                                    const parsed = JSON.parse(data);
                                    const content = parsed.choices[0].delta.content;
                                    if (content) {
                                        assistantMessage += content;
                                        // 更新界面显示
                                        assistantPrefixEl.innerHTML = `<span class="sender-tag">助手:</span>${assistantMessage}`;
                                        scrollToBottom();
                                    }
                                } catch (parseError) {
                                    console.error('流式数据解析错误:', parseError);
                                }
                            }
                        }
                    }
                }

                // 流式结束后添加完整消息到历史
                state.conversationHistory.push({ role: 'assistant', content: assistantMessage });
            } finally {
                reader.releaseLock();
            }
        }

        // 显示消息到对话区域
        function displayMessage(sender, message, isError = false) {
            const messageEl = document.createElement('div');
            const tagClass = isError ? 'error-tag' : 'sender-tag';
            
            // 消息HTML结构(支持换行显示)
            messageEl.innerHTML = `
                <div style="margin-bottom: 12px;">
                    <span class="${tagClass}">${sender}:</span>
                    <span>${message.replace(/\n/g, '<br>')}</span>
                </div>
            `;
            
            conversationHistoryEl.appendChild(messageEl);
            scrollToBottom();
        }

        // 清除对话历史
        function clearConversation() {
            // 重置对话历史(保留系统提示)
            state.conversationHistory = [
                { role: "system", content: "You are a helpful assistant." }
            ];
            // 清空显示区域
            conversationHistoryEl.innerHTML = '';
        }

        // 滚动到对话底部
        function scrollToBottom() {
            conversationHistoryEl.scrollTop = conversationHistoryEl.scrollHeight;
        }

        // 页面加载完成后初始化
        document.addEventListener('DOMContentLoaded', () => {
            initEventListeners();
        });
    </script>
</body>
</html>

四、DeepSeek+tkinter(三)

import tkinter as tk
from tkinter import ttk, scrolledtext, messagebox
from openai import OpenAI
import threading
from datetime import datetime

class DeepSeekAssistant:
    def __init__(self, root):
        self.root = root
        self.root.title("DeepSeek 智能助手")
        self.root.geometry("800x600")
        self.root.configure(bg="#f0f0f0")
        
        # API 配置
        self.api_key = "sk-xxxxxxxxxx"
        self.base_url = "https://api.deepseek.com"
        self.model = "deepseek-chat"
        self.stream = False
        
        # 对话历史
        self.messages = []
        
        self.setup_ui()
        
    def setup_ui(self):
        # 主框架
        main_frame = ttk.Frame(self.root, padding="10")
        main_frame.grid(row=0, column=0, sticky=(tk.W, tk.E, tk.N, tk.S))
        
        # 配置列权重
        self.root.columnconfigure(0, weight=1)
        self.root.rowconfigure(0, weight=1)
        main_frame.columnconfigure(1, weight=1)
        main_frame.rowconfigure(1, weight=1)
        
        # 设置区域
        settings_frame = ttk.LabelFrame(main_frame, text="设置", padding="5")
        settings_frame.grid(row=0, column=0, columnspan=2, sticky=(tk.W, tk.E), pady=(0, 10))
        
        # API Key 输入
        ttk.Label(settings_frame, text="API Key:").grid(row=0, column=0, sticky=tk.W, padx=(0, 5))
        self.api_key_var = tk.StringVar(value=self.api_key)
        api_key_entry = ttk.Entry(settings_frame, textvariable=self.api_key_var, width=50, show="*")
        api_key_entry.grid(row=0, column=1, sticky=(tk.W, tk.E), padx=(0, 10))
        
        # Base URL 输入
        ttk.Label(settings_frame, text="Base URL:").grid(row=1, column=0, sticky=tk.W, padx=(0, 5))
        self.base_url_var = tk.StringVar(value=self.base_url)
        base_url_entry = ttk.Entry(settings_frame, textvariable=self.base_url_var, width=50)
        base_url_entry.grid(row=1, column=1, sticky=(tk.W, tk.E), padx=(0, 10))
        
        # 模型选择
        ttk.Label(settings_frame, text="模型:").grid(row=0, column=2, sticky=tk.W, padx=(10, 5))
        self.model_var = tk.StringVar(value=self.model)
        model_combo = ttk.Combobox(settings_frame, textvariable=self.model_var, 
                                  values=["deepseek-chat", "deepseek-coder"], width=15)
        model_combo.grid(row=0, column=3, sticky=tk.W, padx=(0, 10))
        
        # 流式输出选项
        self.stream_var = tk.BooleanVar(value=self.stream)
        stream_check = ttk.Checkbutton(settings_frame, text="启用流式输出", variable=self.stream_var)
        stream_check.grid(row=1, column=2, columnspan=2, sticky=tk.W, padx=(10, 0))
        
        # 保存设置按钮
        save_btn = ttk.Button(settings_frame, text="保存设置", command=self.save_settings)
        save_btn.grid(row=0, column=4, rowspan=2, padx=(10, 0))
        
        # 聊天显示区域
        chat_frame = ttk.LabelFrame(main_frame, text="对话", padding="5")
        chat_frame.grid(row=1, column=0, columnspan=2, sticky=(tk.W, tk.E, tk.N, tk.S), pady=(0, 10))
        chat_frame.columnconfigure(0, weight=1)
        chat_frame.rowconfigure(0, weight=1)
        
        self.chat_display = scrolledtext.ScrolledText(chat_frame, wrap=tk.WORD, width=70, height=20)
        self.chat_display.grid(row=0, column=0, sticky=(tk.W, tk.E, tk.N, tk.S))
        self.chat_display.config(state=tk.DISABLED)
        
        # 输入区域
        input_frame = ttk.Frame(main_frame)
        input_frame.grid(row=2, column=0, columnspan=2, sticky=(tk.W, tk.E), pady=(0, 10))
        input_frame.columnconfigure(0, weight=1)
        
        ttk.Label(input_frame, text="输入消息:").grid(row=0, column=0, sticky=tk.W, pady=(0, 5))
        
        self.input_var = tk.StringVar()
        input_entry = ttk.Entry(input_frame, textvariable=self.input_var, width=70)
        input_entry.grid(row=1, column=0, sticky=(tk.W, tk.E))
        input_entry.bind('<Return>', lambda event: self.send_message())
        
        # 按钮区域
        button_frame = ttk.Frame(main_frame)
        button_frame.grid(row=3, column=0, columnspan=2, sticky=(tk.W, tk.E))
        
        send_btn = ttk.Button(button_frame, text="发送", command=self.send_message)
        send_btn.pack(side=tk.LEFT, padx=(0, 10))
        
        clear_btn = ttk.Button(button_frame, text="清空对话", command=self.clear_chat)
        clear_btn.pack(side=tk.LEFT)
        
        # 状态栏
        self.status_var = tk.StringVar(value="就绪")
        status_bar = ttk.Label(self.root, textvariable=self.status_var, relief=tk.SUNKEN, anchor=tk.W)
        status_bar.grid(row=1, column=0, sticky=(tk.W, tk.E))
        
    def save_settings(self):
        self.api_key = self.api_key_var.get()
        self.base_url = self.base_url_var.get()
        self.model = self.model_var.get()
        self.stream = self.stream_var.get()
        self.status_var.set("设置已保存")
        
    def send_message(self):
        message = self.input_var.get().strip()
        if not message:
            return
            
        self.input_var.set("")  # 清空输入框
        self.add_message("user", message)
        
        # 在新线程中调用API,避免界面冻结
        thread = threading.Thread(target=self.call_api, args=(message,))
        thread.daemon = True
        thread.start()
        
    def call_api(self, message):
        self.status_var.set("正在生成响应...")
        
        try:
            client = OpenAI(api_key=self.api_key, base_url=self.base_url)
            
            # 构建消息
            messages = [{"role": "system", "content": "You are a helpful assistant."}]
            for msg in self.messages:
                messages.append({"role": msg["role"], "content": msg["content"]})
            
            if self.stream:
                # 流式响应
                stream = client.chat.completions.create(
                    model=self.model,
                    messages=messages,
                    stream=True
                )
                
                full_response = ""
                for chunk in stream:
                    if chunk.choices[0].delta.content is not None:
                        chunk_content = chunk.choices[0].delta.content
                        full_response += chunk_content
                        # 实时更新显示
                        self.root.after(0, self.update_stream_display, chunk_content)
                
                # 添加完整响应到历史
                self.add_message("assistant", full_response)
            else:
                # 非流式响应
                response = client.chat.completions.create(
                    model=self.model,
                    messages=messages,
                    stream=False
                )
                
                assistant_response = response.choices[0].message.content
                self.add_message("assistant", assistant_response)
                
            self.status_var.set("响应完成")
            
        except Exception as e:
            error_msg = f"API调用出错: {str(e)}"
            self.root.after(0, lambda: messagebox.showerror("错误", error_msg))
            self.status_var.set("错误发生")
            
    def add_message(self, role, content):
        timestamp = datetime.now().strftime("%H:%M:%S")
        self.messages.append({"role": role, "content": content, "time": timestamp})
        
        # 更新显示
        self.update_chat_display()
        
    def update_chat_display(self):
        self.chat_display.config(state=tk.NORMAL)
        self.chat_display.delete(1.0, tk.END)
        
        for msg in self.messages:
            role_icon = "👤" if msg["role"] == "user" else "🤖"
            role_name = "您" if msg["role"] == "user" else "助手"
            self.chat_display.insert(tk.END, f"{role_icon} {role_name} ({msg['time']}):\n")
            self.chat_display.insert(tk.END, f"{msg['content']}\n\n")
            
        self.chat_display.see(tk.END)
        self.chat_display.config(state=tk.DISABLED)
        
    def update_stream_display(self, chunk):
        self.chat_display.config(state=tk.NORMAL)
        self.chat_display.insert(tk.END, chunk)
        self.chat_display.see(tk.END)
        self.chat_display.config(state=tk.DISABLED)
        
    def clear_chat(self):
        self.messages = []
        self.update_chat_display()
        self.status_var.set("对话已清空")

if __name__ == "__main__":
    root = tk.Tk()
    app = DeepSeekAssistant(root)
    root.mainloop()

对应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 智能助手</title>
    <style>
        :root {
            --primary-color: #4a6cf7;
            --secondary-color: #6c757d;
            --success-color: #28a745;
            --danger-color: #dc3545;
            --dark-color: #212529;
            --light-color: #f8f9fa;
            --sidebar-bg: #2c3e50;
            --chat-user-bg: #4a6cf7;
            --chat-assistant-bg: #e9ecef;
            --chat-user-text: white;
            --chat-assistant-text: #212529;
        }

        * {
            margin: 0;
            padding: 0;
            box-sizing: border-box;
            font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
        }

        body {
            background-color: #f5f7fb;
            color: #333;
            height: 100vh;
            display: flex;
            flex-direction: column;
        }

        .header {
            background: linear-gradient(135deg, var(--primary-color), #3b5bdb);
            color: white;
            padding: 1rem 2rem;
            display: flex;
            justify-content: space-between;
            align-items: center;
            box-shadow: 0 2px 10px rgba(0, 0, 0, 0.1);
        }

        .logo {
            display: flex;
            align-items: center;
            gap: 10px;
        }

        .logo h1 {
            font-size: 1.5rem;
            font-weight: 600;
        }

        .logo-icon {
            font-size: 1.8rem;
        }

        .main-container {
            display: flex;
            flex: 1;
            overflow: hidden;
        }

        .sidebar {
            width: 300px;
            background-color: var(--sidebar-bg);
            color: white;
            padding: 1.5rem;
            display: flex;
            flex-direction: column;
            gap: 1.5rem;
            overflow-y: auto;
        }

        .settings-section {
            background-color: rgba(255, 255, 255, 0.1);
            padding: 1rem;
            border-radius: 8px;
        }

        .settings-section h3 {
            margin-bottom: 1rem;
            font-size: 1.1rem;
            display: flex;
            align-items: center;
            gap: 8px;
        }

        .form-group {
            margin-bottom: 1rem;
        }

        .form-group label {
            display: block;
            margin-bottom: 0.5rem;
            font-size: 0.9rem;
        }

        .form-control {
            width: 100%;
            padding: 0.5rem;
            border: none;
            border-radius: 4px;
            background-color: rgba(255, 255, 255, 0.9);
        }

        .checkbox-group {
            display: flex;
            align-items: center;
            gap: 0.5rem;
        }

        .btn {
            padding: 0.5rem 1rem;
            border: none;
            border-radius: 4px;
            cursor: pointer;
            font-weight: 500;
            transition: all 0.2s;
        }

        .btn-primary {
            background-color: var(--primary-color);
            color: white;
        }

        .btn-primary:hover {
            background-color: #3b5bdb;
        }

        .btn-danger {
            background-color: var(--danger-color);
            color: white;
        }

        .btn-danger:hover {
            background-color: #bd2130;
        }

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

        .chat-messages {
            flex: 1;
            padding: 1.5rem;
            overflow-y: auto;
            display: flex;
            flex-direction: column;
            gap: 1rem;
        }

        .message {
            max-width: 80%;
            padding: 1rem;
            border-radius: 12px;
            position: relative;
            animation: fadeIn 0.3s;
        }

        @keyframes fadeIn {
            from { opacity: 0; transform: translateY(10px); }
            to { opacity: 1; transform: translateY(0); }
        }

        .message-user {
            align-self: flex-end;
            background-color: var(--chat-user-bg);
            color: var(--chat-user-text);
            border-bottom-right-radius: 4px;
        }

        .message-assistant {
            align-self: flex-start;
            background-color: var(--chat-assistant-bg);
            color: var(--chat-assistant-text);
            border-bottom-left-radius: 4px;
        }

        .message-header {
            display: flex;
            justify-content: space-between;
            margin-bottom: 0.5rem;
            font-size: 0.8rem;
            opacity: 0.8;
        }

        .message-content {
            line-height: 1.5;
        }

        .input-area {
            padding: 1rem;
            border-top: 1px solid #e9ecef;
            background-color: white;
            display: flex;
            gap: 0.5rem;
        }

        .message-input {
            flex: 1;
            padding: 0.75rem;
            border: 1px solid #ddd;
            border-radius: 24px;
            resize: none;
            height: 50px;
            outline: none;
            transition: border-color 0.2s;
        }

        .message-input:focus {
            border-color: var(--primary-color);
        }

        .send-btn {
            background-color: var(--primary-color);
            color: white;
            border: none;
            border-radius: 50%;
            width: 50px;
            height: 50px;
            display: flex;
            align-items: center;
            justify-content: center;
            cursor: pointer;
            transition: background-color 0.2s;
        }

        .send-btn:hover {
            background-color: #3b5bdb;
        }

        .send-btn:disabled {
            background-color: var(--secondary-color);
            cursor: not-allowed;
        }

        .status-bar {
            padding: 0.5rem 1rem;
            background-color: #e9ecef;
            color: var(--secondary-color);
            font-size: 0.85rem;
            display: flex;
            justify-content: space-between;
        }

        .typing-indicator {
            display: flex;
            align-items: center;
            gap: 0.5rem;
            color: var(--primary-color);
        }

        .typing-dots {
            display: flex;
            gap: 3px;
        }

        .typing-dot {
            width: 6px;
            height: 6px;
            background-color: var(--primary-color);
            border-radius: 50%;
            animation: typingAnimation 1.4s infinite ease-in-out;
        }

        .typing-dot:nth-child(1) { animation-delay: 0s; }
        .typing-dot:nth-child(2) { animation-delay: 0.2s; }
        .typing-dot:nth-child(3) { animation-delay: 0.4s; }

        @keyframes typingAnimation {
            0%, 60%, 100% { transform: translateY(0); }
            30% { transform: translateY(-5px); }
        }

        @media (max-width: 768px) {
            .main-container {
                flex-direction: column;
            }
            
            .sidebar {
                width: 100%;
                max-height: 300px;
            }
            
            .message {
                max-width: 90%;
            }
        }
    </style>
</head>
<body>
    <div class="header">
        <div class="logo">
            <span class="logo-icon">🤖</span>
            <h1>DeepSeek 智能助手</h1>
        </div>
    </div>

    <div class="main-container">
        <div class="sidebar">
            <div class="settings-section">
                <h3>⚙️ API 设置</h3>
                <div class="form-group">
                    <label for="api-key">API Key</label>
                    <input type="password" id="api-key" class="form-control" value="sk-xxxxxxxxxx">
                </div>
                <div class="form-group">
                    <label for="base-url">Base URL</label>
                    <input type="text" id="base-url" class="form-control" value="https://api.deepseek.com">
                </div>
                <div class="form-group">
                    <label for="model">模型</label>
                    <select id="model" class="form-control">
                        <option value="deepseek-chat" selected>deepseek-chat</option>
                        <option value="deepseek-coder">deepseek-coder</option>
                    </select>
                </div>
                <div class="form-group">
                    <div class="checkbox-group">
                        <input type="checkbox" id="stream-mode">
                        <label for="stream-mode">启用流式输出</label>
                    </div>
                </div>
                <button id="save-settings" class="btn btn-primary">保存设置</button>
            </div>

            <div class="settings-section">
                <h3>🔄 对话管理</h3>
                <button id="clear-chat" class="btn btn-danger">清空对话历史</button>
            </div>
        </div>

        <div class="chat-container">
            <div class="chat-messages" id="chat-messages">
                <div class="message message-assistant">
                    <div class="message-header">
                        <span>🤖 助手</span>
                        <span>刚刚</span>
                    </div>
                    <div class="message-content">
                        您好!我是DeepSeek智能助手,有什么可以帮您的吗?
                    </div>
                </div>
            </div>

            <div class="status-bar">
                <div id="status">就绪</div>
                <div id="typing-indicator" class="typing-indicator" style="display: none;">
                    <span>正在输入</span>
                    <div class="typing-dots">
                        <div class="typing-dot"></div>
                        <div class="typing-dot"></div>
                        <div class="typing-dot"></div>
                    </div>
                </div>
            </div>

            <div class="input-area">
                <textarea id="message-input" class="message-input" placeholder="输入消息..." rows="1"></textarea>
                <button id="send-btn" class="send-btn">
                    <svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
                        <line x1="22" y1="2" x2="11" y2="13"></line>
                        <polygon points="22 2 15 22 11 13 2 9 22 2"></polygon>
                    </svg>
                </button>
            </div>
        </div>
    </div>

    <script>
        // 配置和状态
        const config = {
            apiKey: "sk-xxxxxxxxxx",
            baseUrl: "https://api.deepseek.com",
            model: "deepseek-chat",
            stream: false
        };

        let messages = [];
        let isGenerating = false;

        // DOM 元素
        const chatMessages = document.getElementById('chat-messages');
        const messageInput = document.getElementById('message-input');
        const sendBtn = document.getElementById('send-btn');
        const statusEl = document.getElementById('status');
        const typingIndicator = document.getElementById('typing-indicator');
        const apiKeyInput = document.getElementById('api-key');
        const baseUrlInput = document.getElementById('base-url');
        const modelSelect = document.getElementById('model');
        const streamCheckbox = document.getElementById('stream-mode');
        const saveSettingsBtn = document.getElementById('save-settings');
        const clearChatBtn = document.getElementById('clear-chat');

        // 初始化
        function init() {
            // 加载保存的设置
            const savedConfig = localStorage.getItem('deepseek-config');
            if (savedConfig) {
                const parsedConfig = JSON.parse(savedConfig);
                Object.assign(config, parsedConfig);
                
                // 更新UI
                apiKeyInput.value = config.apiKey;
                baseUrlInput.value = config.baseUrl;
                modelSelect.value = config.model;
                streamCheckbox.checked = config.stream;
            }

            // 事件监听
            sendBtn.addEventListener('click', sendMessage);
            messageInput.addEventListener('keydown', e => {
                if (e.key === 'Enter' && !e.shiftKey) {
                    e.preventDefault();
                    sendMessage();
                }
            });

            saveSettingsBtn.addEventListener('click', saveSettings);
            clearChatBtn.addEventListener('click', clearChat);

            // 自动调整文本区域高度
            messageInput.addEventListener('input', function() {
                this.style.height = 'auto';
                this.style.height = (this.scrollHeight) + 'px';
            });
        }

        // 保存设置
        function saveSettings() {
            config.apiKey = apiKeyInput.value;
            config.baseUrl = baseUrlInput.value;
            config.model = modelSelect.value;
            config.stream = streamCheckbox.checked;
            
            localStorage.setItem('deepseek-config', JSON.stringify(config));
            updateStatus('设置已保存');
            
            // 显示成功提示
            showTempMessage('设置已成功保存!', 'success');
        }

        // 清空聊天
        function clearChat() {
            if (confirm('确定要清空所有对话记录吗?')) {
                messages = [];
                chatMessages.innerHTML = '';
                
                // 添加欢迎消息
                addMessage('assistant', '您好!我是DeepSeek智能助手,有什么可以帮您的吗?');
                updateStatus('对话已清空');
            }
        }

        // 发送消息
        function sendMessage() {
            const message = messageInput.value.trim();
            if (!message || isGenerating) return;
            
            // 添加用户消息
            addMessage('user', message);
            messageInput.value = '';
            messageInput.style.height = 'auto';
            
            // 更新状态
            isGenerating = true;
            sendBtn.disabled = true;
            updateStatus('正在生成响应...');
            showTypingIndicator(true);
            
            // 调用API
            callAPI(message);
        }

        // 调用API
        async function callAPI(message) {
            try {
                // 构建请求消息
                const requestMessages = [
                    { role: "system", content: "You are a helpful assistant." },
                    ...messages.map(msg => ({ role: msg.role, content: msg.content }))
                ];
                
                const requestBody = {
                    model: config.model,
                    messages: requestMessages,
                    stream: config.stream
                };
                
                const response = await fetch(config.baseUrl + '/v1/chat/completions', {
                    method: 'POST',
                    headers: {
                        'Content-Type': 'application/json',
                        'Authorization': `Bearer ${config.apiKey}`
                    },
                    body: JSON.stringify(requestBody)
                });
                
                if (!response.ok) {
                    throw new Error(`API请求失败: ${response.status} ${response.statusText}`);
                }
                
                if (config.stream) {
                    // 流式响应处理
                    await processStreamResponse(response);
                } else {
                    // 非流式响应处理
                    await processJsonResponse(response);
                }
                
                updateStatus('响应完成');
                
            } catch (error) {
                console.error('API调用错误:', error);
                addMessage('assistant', `抱歉,发生了错误: ${error.message}`);
                updateStatus(`错误: ${error.message}`);
            } finally {
                isGenerating = false;
                sendBtn.disabled = false;
                showTypingIndicator(false);
            }
        }

        // 处理流式响应
        async function processStreamResponse(response) {
            const reader = response.body.getReader();
            const decoder = new TextDecoder();
            let assistantMessage = '';
            let messageElement = null;
            
            while (true) {
                const { done, value } = await reader.read();
                if (done) break;
                
                const chunk = decoder.decode(value);
                const lines = chunk.split('\n').filter(line => line.trim() !== '');
                
                for (const line of lines) {
                    if (line.startsWith('data: ')) {
                        const data = line.slice(6);
                        if (data === '[DONE]') {
                            // 流式响应结束
                            messages.push({ role: 'assistant', content: assistantMessage });
                            return;
                        }
                        
                        try {
                            const parsed = JSON.parse(data);
                            const content = parsed.choices[0]?.delta?.content || '';
                            
                            if (content) {
                                assistantMessage += content;
                                
                                if (!messageElement) {
                                    // 创建新消息元素
                                    messageElement = createMessageElement('assistant', assistantMessage);
                                    chatMessages.appendChild(messageElement);
                                    scrollToBottom();
                                } else {
                                    // 更新现有消息元素
                                    const contentElement = messageElement.querySelector('.message-content');
                                    contentElement.textContent = assistantMessage;
                                    scrollToBottom();
                                }
                            }
                        } catch (e) {
                            console.error('解析流数据错误:', e);
                        }
                    }
                }
            }
        }

        // 处理JSON响应
        async function processJsonResponse(response) {
            const data = await response.json();
            const assistantMessage = data.choices[0].message.content;
            addMessage('assistant', assistantMessage);
        }

        // 添加消息到聊天
        function addMessage(role, content) {
            const timestamp = new Date().toLocaleTimeString();
            messages.push({ role, content, timestamp });
            
            const messageElement = createMessageElement(role, content, timestamp);
            chatMessages.appendChild(messageElement);
            scrollToBottom();
        }

        // 创建消息元素
        function createMessageElement(role, content, timestamp = null) {
            if (!timestamp) {
                timestamp = new Date().toLocaleTimeString();
            }
            
            const messageDiv = document.createElement('div');
            messageDiv.className = `message message-${role}`;
            
            const headerDiv = document.createElement('div');
            headerDiv.className = 'message-header';
            
            const roleSpan = document.createElement('span');
            roleSpan.textContent = role === 'user' ? '👤 您' : '🤖 助手';
            
            const timeSpan = document.createElement('span');
            timeSpan.textContent = timestamp;
            
            headerDiv.appendChild(roleSpan);
            headerDiv.appendChild(timeSpan);
            
            const contentDiv = document.createElement('div');
            contentDiv.className = 'message-content';
            contentDiv.textContent = content;
            
            messageDiv.appendChild(headerDiv);
            messageDiv.appendChild(contentDiv);
            
            return messageDiv;
        }

        // 滚动到底部
        function scrollToBottom() {
            chatMessages.scrollTop = chatMessages.scrollHeight;
        }

        // 更新状态
        function updateStatus(text) {
            statusEl.textContent = text;
        }

        // 显示/隐藏输入指示器
        function showTypingIndicator(show) {
            typingIndicator.style.display = show ? 'flex' : 'none';
        }

        // 显示临时消息
        function showTempMessage(text, type = 'info') {
            const tempMsg = document.createElement('div');
            tempMsg.textContent = text;
            tempMsg.style.position = 'fixed';
            tempMsg.style.bottom = '20px';
            tempMsg.style.right = '20px';
            tempMsg.style.padding = '10px 20px';
            tempMsg.style.borderRadius = '4px';
            tempMsg.style.color = 'white';
            tempMsg.style.zIndex = '1000';
            tempMsg.style.boxShadow = '0 4px 12px rgba(0,0,0,0.15)';
            
            if (type === 'success') {
                tempMsg.style.backgroundColor = '#28a745';
            } else {
                tempMsg.style.backgroundColor = '#6c757d';
            }
            
            document.body.appendChild(tempMsg);
            
            setTimeout(() => {
                tempMsg.style.opacity = '0';
                tempMsg.style.transition = 'opacity 0.5s';
                setTimeout(() => document.body.removeChild(tempMsg), 500);
            }, 3000);
        }

        // 初始化应用
        init();
    </script>
</body>
</html>

Logo

码道开发者社区,聚焦华为云码道 CodeArts 代码智能体,沉淀 Agent、Skill、鸿蒙开发实战内容,供开发者查阅资料、交流技术、分享工程实践

更多推荐