Spaces:
Sleeping
Sleeping
File size: 5,191 Bytes
59f470d c42fa70 789edd4 59f470d 5c4a2a6 5298262 97ef8d5 5298262 5c4a2a6 5298262 97ef8d5 f530aed 5c4a2a6 5298262 d5d3ba6 848228d d5d3ba6 f530aed 5298262 848228d 6a4fcaf d5d3ba6 789edd4 5298262 c42fa70 6a4fcaf 789edd4 c42fa70 52d1dce 5298262 52d1dce d5d3ba6 f530aed 5298262 6a4fcaf d5d3ba6 6a4fcaf c42fa70 59f470d 4f5e4fa 16452cd c42fa70 848228d 6a4fcaf 4f5e4fa 5c4a2a6 6a4fcaf f530aed 54d9979 f530aed d5d3ba6 f530aed 789edd4 97ef8d5 6a4fcaf 4f5e4fa 6a4fcaf 16452cd 5c4a2a6 5298262 f530aed a38aea5 16452cd 6a4fcaf 97ef8d5 6a4fcaf 16452cd 5c4a2a6 f530aed |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 |
import os
import re
import traceback
import numpy as np
import gradio as gr
from openai import OpenAI
# ======== 1. HF Secret 讀取與安全檢查 ========
secret_key = os.environ.get("OPENAIAPIKEY")
if not secret_key:
raise ValueError(
"⚠️ HF Space Secret 'OPENAIAPIKEY' 未設定或名稱錯誤!"
"請先在 HF Space Secrets 裡建立此 Secret 並重新部署。"
)
os.environ["OPENAI_API_KEY"] = secret_key
# ======== 2. 初始化 OpenAI SDK ========
client = OpenAI()
# ======== 3. 專業領域定義 ========
PROFESSIONS = {
"程式設計": "你是一位資深程式設計師,回答必須專業、詳細,附上程式範例與步驟。",
"行銷": "你是一位行銷專家,回答必須專業、詳細,提供可執行行銷策略與步驟。",
"法律": "你是一位律師,回答必須專業、法律依據明確、可操作建議清楚。",
"醫療": "你是一位醫師,回答必須專業、以健康與安全為前提,提供可操作建議。",
"財務": "你是一位財務顧問,回答必須專業、符合台灣會計與稅務法規,提供可執行建議。",
"設計": "你是一位設計師,回答必須專業、詳細,提供設計步驟與案例。"
}
# ======== 4. UTF-8 安全函數 ========
def safe_utf8(text):
if not isinstance(text, str):
text = str(text)
return text.encode("utf-8", errors="ignore").decode("utf-8")
# ======== 5. Embedding 初始化 ========
profession_embeddings = {}
def ensure_embeddings():
global profession_embeddings
if not profession_embeddings:
for field in PROFESSIONS.keys():
try:
emb = client.embeddings.create(
model="text-embedding-3-small",
input=safe_utf8(field)
)
profession_embeddings[field] = np.array(emb.data[0].embedding)
except Exception as e:
print(f"Embedding 初始化失敗 ({field}):", e)
profession_embeddings[field] = np.zeros(1536)
# ======== 6. 自動判斷職業 ========
def detect_profession(user_input: str) -> str:
ensure_embeddings()
try:
text_emb = client.embeddings.create(
model="text-embedding-3-small",
input=safe_utf8(user_input)
)
text_vector = np.array(text_emb.data[0].embedding)
except Exception as e:
print("Embedding 呼叫錯誤:", traceback.format_exc())
return "你是一個專業顧問,回答必須專業、詳細、可操作。"
scores = {}
for field, emb in profession_embeddings.items():
if np.linalg.norm(text_vector) == 0 or np.linalg.norm(emb) == 0:
scores[field] = -1
else:
scores[field] = np.dot(text_vector, emb) / (np.linalg.norm(text_vector)*np.linalg.norm(emb))
best_field = max(scores, key=scores.get)
return PROFESSIONS[best_field]
# ======== 7. AI Agent 回答 ========
def professional_agent(user_input, state):
user_input = safe_utf8(user_input)
if state.get("profession_prompt") is None:
profession_prompt = detect_profession(user_input)
state["profession_prompt"] = profession_prompt
question = re.sub(r"我是.*?(,|,)", "", user_input)
if not question.strip():
answer = f"✅ 已設定你的專業領域:\n{profession_prompt}\n請提出問題。"
state["chat_history"].append({"role":"user","content":user_input})
state["chat_history"].append({"role":"assistant","content":answer})
return state["chat_history"], state
else:
user_input = question
messages = [{"role": "system", "content": state["profession_prompt"]}]
for msg in state["chat_history"]:
messages.append({"role": msg["role"], "content": msg["content"]})
messages.append({"role": "user", "content": user_input})
try:
response = client.chat.completions.create(
model="gpt-4o-mini",
messages=messages,
temperature=0.2
)
answer = safe_utf8(response.choices[0].message.content)
except Exception as e:
tb = traceback.format_exc()
print(tb) # 後端完整 log
answer = f"⚠️ 發生錯誤,請查看後端 logs: {str(e)}"
# Append using dict 格式
state["chat_history"].append({"role":"user","content":user_input})
state["chat_history"].append({"role":"assistant","content":answer})
# 保留最近 10 條訊息
if len(state["chat_history"]) > 20:
state["chat_history"] = state["chat_history"][-20:]
return state["chat_history"], state
# ======== 8. Gradio 介面 ========
with gr.Blocks() as demo:
gr.Markdown("## 🧑💼AI 專業領域顧問")
gr.Markdown("第一次輸入可以同時輸入職業 + 問題,例如:我是會計師,我想知道台灣稅務分析")
chatbot = gr.Chatbot(type="messages")
msg = gr.Textbox(label="輸入訊息")
state = gr.State({"chat_history": [], "profession_prompt": None})
msg.submit(professional_agent, [msg, state], [chatbot, state])
demo.launch(server_name="0.0.0.0", server_port=7860)
|