前言
透過這篇文章,在先前 簡單聊天機器人 文章中開發的聊天機器人。雖然後續可以新增或調整對話內容,但還是固定對話規則型回覆。對應無法匹配的意圖。讓我們結合 LLM ,使聊天更靈活自然,語氣更像真人 。另外,考量效能成本(LLM 只在必要時呼叫),不是所有對話都丟給 LLM。
DeepSeek方案 (需要申請 API Key)
相關成本費用參看: https://api-docs.deepseek.com/zh-cn/quick_start/pricing/
安裝相關模型
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()
# 取得 目前 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(user_input):
return random.choice(responses.get(user_input, 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
測試
留言