探索 進階 ChatBot 結合 DeepSeek

探索 進階 ChatBot 結合 DeepSeek

前言

透過這篇文章,在先前 簡單聊天機器人 文章中開發的聊天機器人。雖然後續可以新增或調整對話內容,但還是固定對話規則型回覆。對應無法匹配的意圖。讓我們結合 LLM ,使聊天更靈活自然,語氣更像真人 。另外,考量效能成本(LLM 只在必要時呼叫),不是所有對話都丟給 LLM。

DeepSeek 方案 (需要申請 API Key)

相關參看: https://www.deepseek.com/

安裝相關模型

pip install python-dotenv

python-dotenv 是一個專門用於從 .env 檔案載入環境變數到 Python 程式中的函式庫

實作代碼

環境變數(.env

DEEPSEEK_API_KEY=你的api_key

deepseek_client.py

import os import requests from dotenv import load_dotenv load_dotenv() DEEPSEEK_API_KEY = os.getenv("DEEPSEEK_API_KEY") def call_deepseek(user_input: str) -> str: if not DEEPSEEK_API_KEY: return "API key 未設定" url = "https://api.deepseek.com/chat/completions" headers = { "Authorization": f"Bearer {DEEPSEEK_API_KEY}", "Content-Type": "application/json" } data = { "model": "deepseek-chat", "messages": [{"role": "user", "content": user_input}], "max_tokens": 200, # 日常聊天:200-300 tokens "temperature": 0.7, # 控制AI輸出的隨機性和創造性,中溫(0.5-0.7) "stream": False # 一次性返回完整回复 } try: response = requests.post(url, headers=headers, json=data, timeout=30) if response.status_code == 200: result = response.json() reply = result["choices"][0]["message"]["content"] return reply elif response.status_code == 402: return "AI 服務額度不足" # 可能要去充值 elif response.status_code == 429: return "請求過於頻繁,請稍後再試" elif response.status_code == 401: return "API key 無效" else: print(f"[API Error] {response.status_code}: {response.text}") return f"AI 錯誤暫時無法使用,錯誤代碼:{response.status_code}" except requests.exceptions.Timeout: return "請求超時" except requests.exceptions.ConnectionError: return "網路連線失敗,請檢查網路" except Exception as e: print(f"[ERROR] 請求異常: {e}") return "AI 錯誤暫時無法使用"

註: 參考

簡答、摘要: 50-100 tokens
普通對話: 200-300 tokens

temperature:
0.0 完全確定,每次都一樣
0.1-0.3 非常穩定,稍有變化
0.5-0.7 平衡,自然對話 聊天機器人
0.8-1.0 有創意,多樣性

simple_chatbot.py

import os import requests import spacy import json import random from deepseek_client import call_deepseek from dotenv import load_dotenv load_dotenv() DEEPSEEK_API_KEY = os.getenv("DEEPSEEK_API_KEY") # 取得 目前 Python 檔案所在的資料夾路徑, 相對路徑 BASE_DIR = os.path.dirname(os.path.abspath(__file__)) RESPONSES_PATH = os.path.join(BASE_DIR, "responses.json") # 載入中文模型 nlp = spacy.load("zh_core_web_sm") # 讀取回覆資料 with open(RESPONSES_PATH, "r", encoding="utf-8") as f: responses = json.load(f) def get_response(intent): return random.choice(responses.get(intent, responses["default"])) def respond_to_user(user_input): doc = nlp(user_input) text = user_input.lower() if any(word in text for word in ["你好", "哈囉", "嗨", "hello", "hi"]): return get_response("greeting") if any(word in text for word in ["你好吗", "你好嗎", "過得"]): return get_response("how_are_you") if any(word in text for word in ["你是誰", "你的名字"]): return get_response("name") if any(word in text for word in ["幫助", "能做什麼"]): return get_response("help") if any(word in text for word in ["笑話", "講個笑話", "逗我笑", "好笑的"]): return get_response("jokes") if any(word in text for word in ["謝謝", "感謝"]): return get_response("thanks") if any(word in text for word in ["再見", "掰掰", "bye"]): return get_response("goodbye") # 使用 NER(人名/地點) for ent in doc.ents: if ent.label_ == "PERSON": return f"你提到了 {ent.text},他是你的朋友嗎?" if ent.label_ == "GPE": return f"{ent.text} 聽起來是個地方" # ---------- LLM fallback ---------- return call_deepseek(user_input) if __name__ == "__main__": print("ChatBot 已啟動,輸入「bye」結束對話") while True: user_input = input("你:") if not user_input.strip(): continue response = respond_to_user(user_input) print("ChatBot:", response) if user_input in ["bye","再見", "掰掰"]: break

測試