0522chatbot / app.py
XUSHUYA's picture
Update app.py
9188cd5 verified
import gradio as gr
import os
# 移除 Hugging Face Inference Client 相關導入
# from huggingface_hub import InferenceClient
import google.generativeai as genai # <-- 導入 Google Generative AI 庫
# 移除 Hugging Face Token 讀取和 Client 初始化
# hf_token = os.getenv("HF_TOKEN")
# if not hf_token:
# print("Error: Hugging Face Token (HF_TOKEN) not found...")
# client = InferenceClient("...", token=hf_token)
# 讀取 Google AI API 金鑰 Secret
# 確保您的 Secret 名稱為 GOOGLE_API_KEY
google_api_key = os.getenv("GOOGLE_API_KEY")
# 檢查金鑰是否存在
if not google_api_key:
# 在 Gradio 應用啟動時顯示錯誤或拋出異常
error_message = "錯誤:Google AI API Key (GOOGLE_API_KEY) Secret 未設定。請在 Hugging Face Space 設定中添加。"
print(error_message)
# 為了讓 Space 啟動不失敗,可以在這裡暫時顯示錯誤,但後續 API 呼叫會失敗
# 也可以選擇直接 sys.exit(1) 讓 Space 構建或啟動失敗,提示使用者設定 Secret
# import sys
# sys.exit(1)
# 配置 genai 庫
genai.configure(api_key=google_api_key)
# 選擇一個支援聊天的 Gemini 模型
# 'gemini-pro' 是常用的文字聊天模型
# 模型名稱可以在 Google AI 文檔中找到
MODEL_NAME = "gemini-pro"
# System Prompt 讀取方式保持不變 (從環境變數讀取,請確保 Secret 名稱一致)
# 假設你在 Hugging Face Space Secrets 中設定了一個名為 SYSTEM_PROMPT 的 Secret。
SYSTEM_PROMPT_CONTENT = os.getenv("SYSTEM_PROMPT", "你現在是一名專業的昆蟲學系的大學教授,所以你要用專業的角度簡單跟學生回答昆蟲的知識,請盡可能使用流暢的中文回答。")
# 修改 respond 函數,使用 Gemini API 進行回覆生成
# 函數簽名與之前修改後的版本一致
def respond(
message,
history: list[tuple[str, str]], # history 包含之前的對話 [(user, assistant), ...]
max_tokens, # 對應 generation_config 的 max_output_tokens
temperature,
top_p,
):
# 如果金鑰不存在,不進行 API 呼叫,直接返回錯誤訊息
if not google_api_key:
yield error_message
return # 結束函數執行
# 構建發送給 Gemini API 的 messages 列表
# Gemini API 的 generate_content 方法,當包含 history 時,通常期望格式為
# [{'role': 'user', 'parts': ['...']}, {'role': 'model', 'parts': ['...']}, ...]
# System Instruction 在 generate_content 中通常通過將其內容放在第一條 'user' 消息之前實現
# 或者將其作為單獨的 system_instruction 參數(如果可用且通過 generate_content 傳遞)
# 這裡我們採用將 system prompt 內容放在第一條實際 user 消息前的策略(如果歷史為空)
# 或直接將 system prompt 作為一個獨立的 'user'/'model' 對話對放在歷史最前面 (方便處理 Gradio history)
# 雖然不太符合官方 system_instruction 參數的用法,但能適應 Gradio 的 history 結構
messages_for_gemini = []
# 將 System Prompt 內容加入消息列表的開頭
# 這是為了讓模型理解指令,儘管不是 Gemini 官方推薦的 system_instruction 參數
# 我們將其作為一個獨立的 'user' 訊息放在最前面,後面跟一個 'model' 訊息作為確認
messages_for_gemini.append({"role": "user", "parts": [SYSTEM_PROMPT_CONTENT]})
messages_for_gemini.append({"role": "model", "parts": ["好的,我明白了,請開始。"]}) # 假裝模型確認了指令
# 將 Gradio 提供的 history 轉換為 Gemini API 的格式
# Gradio history 是 [(user_msg, bot_msg), ...]
for user_msg, bot_msg in history:
# 確保訊息不為 None 或空字串,避免 API 錯誤
if user_msg:
messages_for_gemini.append({"role": "user", "parts": [user_msg]})
if bot_msg:
messages_for_gemini.append({"role": "model", "parts": [bot_msg]})
# 加入當前使用者訊息
if message:
messages_for_gemini.append({"role": "user", "parts": [message]})
response = ""
try:
# 呼叫 Gemini API
# 使用 generate_content 方法
model = genai.GenerativeModel(MODEL_NAME)
# 設定生成參數
generation_config = genai.types.GenerationConfig(
max_output_tokens=max_tokens, # 對應 max_tokens
temperature=temperature,
top_p=top_p
# stop_sequences=[] # 如果需要,可以在這裡設定停止標記
)
# 呼叫 generate_content,傳入格式化好的消息列表和生成參數
# 對於 stream=True,它返回一個 Response 迭代器
stream = model.generate_content(
messages_for_gemini, # <-- 傳入構建好的消息列表
generation_config=generation_config,
stream=True,
)
# 處理流式回覆
# 迭代 Response 迭代器,獲取 chunks
for chunk in stream:
# chunk.text 包含當前的文本片段
# 需要確保 chunk.text 不是 None 或空字串
if hasattr(chunk, 'text') and chunk.text:
response += chunk.text
yield response # 逐步返回結果
except Exception as e:
print(f"Error during Gemini inference: {e}")
# 顯示更詳細的錯誤信息給使用者
yield f"生成回覆時發生錯誤: {e}"
# Gradio ChatInterface 設定保持不變
# additional_inputs 移除 System message 文字框
demo = gr.ChatInterface(
respond,
additional_inputs=[
gr.Slider(minimum=1, maximum=2048, value=512, step=1, label="Max new tokens"),
gr.Slider(minimum=0.1, maximum=4.0, value=0.7, step=0.1, label="Temperature"),
gr.Slider(
minimum=0.1,
maximum=1.0,
value=0.95,
step=0.05,
label="Top-p (nucleus sampling)",
),
],
# Chatbot 组件可以考虑设置 type='messages' 并在 respond 中返回对应的格式,以消除 UserWarning
# 例如 bot=gr.Chatbot(type='messages')
# 这需要调整 respond 函数返回值的格式,超出当前主要任务范围,可以之后优化
)
if __name__ == "__main__":
demo.launch()