aciang's picture
Update app.py
6efc50c verified
import os
import torch
import gradio as gr
from transformers import AutoTokenizer, AutoModelForCausalLM
# ========= 基本設定 =========
# 你的模型 repo id(目前是 private 也沒關係)
MODEL_ID = "aciang/mistral7b-tk-sft-20251019-merged"
# 若模型是 private,建議在 Space 的「Settings → Repository secrets」加上 HF_TOKEN
HF_TOKEN = os.getenv("HF_TOKEN", None)
# 建議預設的「傳統知識」系統提示,可以在介面中修改
DEFAULT_SYSTEM_PROMPT = """你是一位熟悉台灣與國際原住民族傳統知識的學者,
擅長用淺顯但尊重文化脈絡的繁體中文說明各族的夢境、儀式、宇宙觀、傳統醫療與環境知識。
回答原則:
1. 先簡短摘要重點(3–5 點條列)。
2. 儘量說明「族名、場域、情境」與「知識來源背景」,避免抽象空話。
3. 若是推論或類比,要清楚標註「推測」而不是說成唯一正解。
4. 若資料不足或超出目前教材範圍,請誠實說明,並給出安全的延伸建議。
5. 全程使用繁體中文。"""
# ========= 載入模型 =========
print(f"載入模型:{MODEL_ID} ...")
tokenizer = AutoTokenizer.from_pretrained(
MODEL_ID,
use_auth_token=HF_TOKEN,
)
# 保險起見,若沒有 pad_token 就沿用 eos_token
if tokenizer.pad_token is None:
tokenizer.pad_token = tokenizer.eos_token
model = AutoModelForCausalLM.from_pretrained(
MODEL_ID,
torch_dtype=torch.float16,
device_map="auto", # 自動分配到 GPU
use_auth_token=HF_TOKEN,
)
model.eval()
print("模型載入完成。")
# ========= 建立提示詞 =========
def build_prompt(system_prompt: str, history: list[tuple[str, str]], user_message: str) -> str:
"""
將 system_prompt + 歷史對話 + 新問題 組成一段文字 prompt。
這裡用簡單的「使用者 / 助手」格式,對傳統知識生成已經很足夠。
"""
system_prompt = system_prompt.strip()
prompt = f"[系統提示]\n{system_prompt}\n\n"
# 過去對話(若有)
if history:
prompt += "[對話紀錄]\n"
for i, (user, bot) in enumerate(history, start=1):
prompt += f"輪次 {i}:\n使用者:{user}\n助手:{bot}\n\n"
# 最新一輪問題
prompt += "[目前問題]\n"
prompt += f"使用者:{user_message}\n助手:"
return prompt
# ========= 生成函式 =========
def generate_reply(
user_message: str,
chat_history: list[tuple[str, str]],
system_prompt: str,
temperature: float,
max_new_tokens: int,
):
if not user_message.strip():
return chat_history, gr.update(value="")
# 組合成一個大 prompt
prompt_text = build_prompt(system_prompt, chat_history, user_message)
inputs = tokenizer(
prompt_text,
return_tensors="pt",
add_special_tokens=True,
).to(model.device)
with torch.no_grad():
outputs = model.generate(
**inputs,
max_new_tokens=int(max_new_tokens),
do_sample=True,
temperature=float(temperature),
top_p=0.9,
pad_token_id=tokenizer.eos_token_id,
)
# 只取新生成的部分
input_len = inputs["input_ids"].shape[-1]
generated_tokens = outputs[0, input_len:]
answer = tokenizer.decode(generated_tokens, skip_special_tokens=True).strip()
chat_history = chat_history + [(user_message, answer)]
return chat_history, "" # 清空輸入框
def clear_history():
return [], ""
# ========= Gradio 介面 =========
with gr.Blocks(title="語言橋傳統知識聊天機器人 — Mistral7B TK", theme=gr.themes.Soft()) as demo:
gr.Markdown(
"""
# 語言橋傳統知識聊天機器人 — Mistral7B TK
使用你自訓練的 **mistral7b-tk-sft-20251019-merged** 模型,離線在 Hugging Face Space 上回答與傳統知識相關的問題。
建議題材舉例:
- 不同族群對「夢境」的五種層次與詮釋差異
- 布農族狩獵儀式與祖靈信仰
- 排灣族階級制度與紋面、圖騰的意義
- 阿美族年齡階層制與植物分類知識
- 海外原住民(如 Inuit)對身體、疾病與療癒的理解
"""
)
with gr.Row():
# 左側:設定區
with gr.Column(scale=1):
system_prompt_box = gr.Textbox(
label="系統提示(模型角色與回答風格)",
value=DEFAULT_SYSTEM_PROMPT,
lines=16,
)
temperature_slider = gr.Slider(
label="溫度(創造性)",
minimum=0.1,
maximum=1.5,
value=0.7,
step=0.05,
)
max_tokens_slider = gr.Slider(
label="最大回覆長度(token 數,大約字數的 1.5–2 倍)",
minimum=64,
maximum=1024,
value=512,
step=16,
)
gr.Markdown(
"""
**小提醒:**
- 回答太發散 → 降低溫度(0.4–0.7)。
- 回答太短 → 拉高「最大回覆長度」。
- Space 若常 timeout,可以稍微降低最大回覆長度。
"""
)
# 右側:聊天區
with gr.Column(scale=2):
chatbot = gr.Chatbot(
label="傳統知識 Chatbot",
height=480,
show_copy_button=True,
)
user_input = gr.Textbox(
label="輸入你的問題(可多輪對話)",
placeholder="例如:請比較布農族、排灣族和阿美族對治療疾病與夢境預兆的不同理解方式。",
lines=4,
)
with gr.Row():
send_btn = gr.Button("送出問題", variant="primary")
clear_btn = gr.Button("清除對話")
# 狀態:對話歷史
state = gr.State([]) # list[tuple[user, bot]]
# 綁定互動
send_btn.click(
fn=generate_reply,
inputs=[
user_input,
state,
system_prompt_box,
temperature_slider,
max_tokens_slider,
],
outputs=[chatbot, user_input],
).then(
fn=lambda h: h,
inputs=[chatbot],
outputs=[state],
)
user_input.submit(
fn=generate_reply,
inputs=[
user_input,
state,
system_prompt_box,
temperature_slider,
max_tokens_slider,
],
outputs=[chatbot, user_input],
).then(
fn=lambda h: h,
inputs=[chatbot],
outputs=[state],
)
clear_btn.click(
fn=clear_history,
inputs=[],
outputs=[chatbot, user_input],
).then(
fn=lambda: [],
inputs=[],
outputs=[state],
)
# 在 HF Space 中不需要 demo.launch(),平台會自動呼叫 demo