經過前三天的基礎學習,今天我們要整合所學的知識,建立第一個功能完整的對話機器人!這個機器人將具備基本的對話能力、記憶功能,以及簡單的個性設定。
我們將建立一個名為「小助手」的對話機器人,具備以下功能:
首先建立專案結構:
chatbot/
├── main.py # 主程式
├── chatbot.py # 機器人核心類別
├── config.py # 設定檔
├── utils.py # 工具函數
└── conversation_log/ # 對話記錄目錄
import os
from dotenv import load_dotenv
load_dotenv()
# API 設定
GEMINI_API_KEY = os.getenv('GEMINI_API_KEY')
# 機器人設定
BOT_NAME = "小助手"
BOT_PERSONALITY = """
你是一個友善、專業的 AI 助手,名字叫做小助手。
你的特質:
- 樂於助人,總是以正面積極的態度回應
- 專業可靠,能提供準確的資訊和建議
- 有耐心,會詳細解釋複雜的概念
- 記住用戶的偏好和之前的對話內容
- 用繁體中文回應,語氣親切自然
"""
# 系統設定
MAX_HISTORY = 50 # 最多保留 50 輪對話
SAVE_CONVERSATION = True
import json
import os
from datetime import datetime
import re
def save_conversation(conversation_log):
"""儲存對話記錄到檔案"""
try:
# 確保目錄存在,如果不存在則創建
log_dir = "conversation_log"
if not os.path.exists(log_dir):
os.makedirs(log_dir)
# 生成檔案名稱
timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
filename = f"{log_dir}/conversation_{timestamp}.json"
# 儲存對話記錄
with open(filename, 'w', encoding='utf-8') as f:
json.dump(conversation_log, f, ensure_ascii=False, indent=2)
print(f"💾 對話記錄已儲存至: {filename}")
except Exception as e:
print(f"❌ 儲存對話記錄時發生錯誤: {str(e)}")
def format_response(response_text):
"""格式化機器人回應"""
# 移除多餘的空行
response_text = re.sub(r'\n\s*\n\s*\n', '\n\n', response_text)
# 確保回應不為空
if not response_text.strip():
return "抱歉,我沒有收到完整的回應。請再試一次。"
return response_text.strip()
def load_conversation(filename):
"""載入對話記錄"""
try:
with open(filename, 'r', encoding='utf-8') as f:
return json.load(f)
except FileNotFoundError:
print(f"❌ 找不到檔案: {filename}")
return []
except Exception as e:
print(f"❌ 載入對話記錄時發生錯誤: {str(e)}")
return []
def get_conversation_files():
"""取得所有對話記錄檔案"""
log_dir = "conversation_log"
if not os.path.exists(log_dir):
return []
files = []
for filename in os.listdir(log_dir):
if filename.startswith("conversation_") and filename.endswith(".json"):
filepath = os.path.join(log_dir, filename)
files.append(filepath)
return sorted(files, reverse=True) # 最新的檔案在前面
def clean_old_conversations(max_files=10):
"""清理舊的對話記錄,只保留最新的幾個檔案"""
files = get_conversation_files()
if len(files) > max_files:
for old_file in files[max_files:]:
try:
os.remove(old_file)
print(f"🗑️ 已刪除舊對話記錄: {old_file}")
except Exception as e:
print(f"❌ 刪除檔案時發生錯誤: {str(e)}")
import google.generativeai as genai
from datetime import datetime
import config
from utils import save_conversation, format_response
class SimpleChatBot:
def __init__(self):
# 初始化 Gemini
genai.configure(api_key=config.GEMINI_API_KEY)
self.model = genai.GenerativeModel('gemini-2.5-flash')
self.system_instruction = config.BOT_PERSONALITY
# 初始化對話狀態 - 在歷史中加入系統指令
initial_history = [
{
"role": "user",
"parts": [self.system_instruction]
},
{
"role": "model",
"parts": ["我理解了,我會按照這個人格設定來回應用戶的問題。"]
}
]
self.chat = self.model.start_chat(history=initial_history)
self.conversation_log = []
self.user_name = "朋友"
self.start_time = datetime.now()
print(f"🤖 {config.BOT_NAME}已啟動!輸入 '/help' 查看可用指令。")
def process_command(self, user_input):
"""處理特殊指令"""
if user_input.startswith('/'):
command = user_input[1:].lower()
if command == 'help':
return self._show_help()
elif command == 'clear':
return self._clear_history()
elif command.startswith('name '):
return self._set_name(command[5:])
elif command == 'stats':
return self._show_stats()
elif command == 'save':
return self._save_conversation()
elif command == 'quit' or command == 'exit':
return self._quit()
else:
return "❓ 未知指令,輸入 '/help' 查看可用指令。"
return None
def _show_help(self):
help_text = f"""
🔧 {config.BOT_NAME} 可用指令:
/help - 顯示此說明
/clear - 清除對話歷史
/name <名字> - 設定你的名字
/stats - 顯示對話統計
/save - 儲存對話記錄
/quit, /exit - 結束對話
"""
return help_text.strip()
def _clear_history(self):
# 重新初始化對話,保留系統指令
initial_history = [
{
"role": "user",
"parts": [self.system_instruction]
},
{
"role": "model",
"parts": ["我理解了,我會按照這個人格設定來回應用戶的問題。"]
}
]
self.chat = self.model.start_chat(history=initial_history)
self.conversation_log = []
return "🧹 對話歷史已清除!"
def _set_name(self, name):
self.user_name = name.strip()
return f"👋 很高興認識你,{self.user_name}!"
def _show_stats(self):
duration = datetime.now() - self.start_time
stats = f"""
📊 對話統計:
• 對話輪數:{len(self.conversation_log) // 2}
• 對話時間:{duration.seconds // 60} 分鐘
• 使用者名稱:{self.user_name}
• 開始時間:{self.start_time.strftime('%Y-%m-%d %H:%M:%S')}
"""
return stats.strip()
def _save_conversation(self):
if config.SAVE_CONVERSATION:
save_conversation(self.conversation_log)
return "💾 對話記錄已儲存!"
return "⚠️ 對話儲存功能未啟用。"
def _quit(self):
if config.SAVE_CONVERSATION:
save_conversation(self.conversation_log)
return "👋 再見!對話記錄已自動儲存。"
def chat_with_user(self, user_input):
"""處理使用者輸入"""
# 檢查是否為指令
command_response = self.process_command(user_input)
if command_response:
if '/quit' in user_input or '/exit' in user_input:
return command_response, True # True 表示要結束程式
return command_response, False
try:
# 記錄使用者輸入
self.conversation_log.append({
"role": "user",
"content": user_input,
"timestamp": datetime.now().isoformat()
})
# 發送訊息給 Gemini
response = self.chat.send_message(user_input)
bot_response = format_response(response.text)
# 記錄機器人回應
self.conversation_log.append({
"role": "assistant",
"content": bot_response,
"timestamp": datetime.now().isoformat()
})
# 維護歷史記錄長度
if len(self.conversation_log) > config.MAX_HISTORY * 2:
self.conversation_log = self.conversation_log[-config.MAX_HISTORY * 2:]
return bot_response, False
except Exception as e:
error_msg = f"❌ 抱歉,我遇到了一些技術問題:{str(e)}"
return error_msg, False
from chatbot import SimpleChatBot
import config
def main():
"""主程式入口"""
print(f"🚀 歡迎使用 {config.BOT_NAME}!")
print("💡 提示:輸入 '/help' 查看可用指令,'/quit' 結束對話。")
print("=" * 50)
# 初始化機器人
bot = SimpleChatBot()
# 主要對話迴圈
while True:
try:
# 獲取使用者輸入
user_input = input(f"\n{bot.user_name}:").strip()
# 跳過空白輸入
if not user_input:
continue
# 處理使用者輸入
response, should_quit = bot.chat_with_user(user_input)
# 顯示機器人回應
print(f"{config.BOT_NAME}:{response}")
# 檢查是否要結束程式
if should_quit:
break
except KeyboardInterrupt:
print(f"\n\n👋 {config.BOT_NAME}:再見!")
break
except Exception as e:
print(f"\n❌ 系統錯誤:{e}")
print("請重新啟動程式。")
break
if __name__ == "__main__":
main()
啟動機器人:
python main.py
對話範例:
朋友:你好!
小助手:你好!很高興見到你!我是小助手,隨時為你提供協助。有什麼我可以幫你的嗎?
朋友:/name 小明
小助手:👋 很高興認識你,小明!
小明:你能幫我解釋什麼是機器學習嗎?
小助手:當然可以!機器學習是人工智慧的一個分支...
小明:/stats
小助手:📊 對話統計:
• 對話輪數:2
• 對話時間:3 分鐘
• 使用者名稱:小明
• 開始時間:2024-01-15 14:30:25
今天我們成功建立了第一個功能完整的對話機器人!它不僅能進行自然對話,還具備記憶功能、指令處理和對話記錄等實用特性。
明天我們將引入 LangGraph,學習如何用圖形化的方式設計更複雜的 AI 工作流程。記得多和你的機器人聊天,測試各種功能!