Basshole's picture
Upload app.py
6c109c4 verified
import os
import gradio as gr
from openai import OpenAI
from typing import Optional
# 如需使用 Claude,請先安裝 "anthropic" 套件: pip install anthropic
try:
from anthropic import Anthropic, HUMAN_PROMPT, AI_PROMPT
except ImportError:
Anthropic = None
HUMAN_PROMPT = ""
AI_PROMPT = ""
# 取得金鑰
gpt_key = os.environ.get("gptkey", "")
claude_api_key = os.environ.get("claudekey", "")
# 建立 OpenAI Client (新版)
client = OpenAI(
api_key=gpt_key,
)
# 定義每個類型的句子
achievement_statements = [
"比起把事情做到完美,我更傾向優先準時完成任務",
"我總是在與他人比較",
"我在派對中是屬於帶動氣氛的靈魂人物",
"在社交場合中,我是比較活潑的類型",
"我的工作成就必須被他人看見",
]
emotion_statements = [
"對我而言,團隊合作在工作上是不可或缺的要素",
"在朋友眼中,我是最適合傾訴心事的對象",
"對我而言,樂在生活是最重要的",
"我不太擅長批評他人",
"我常需要「被別人需要」的感覺",
]
information_statements = [
"很多事情,我傾向自己去找尋事實與資訊",
"我喜歡與他人分享我所知道的事情",
]
possession_statements = [
"我總是在尋找接下來想買的東西",
"我認為從零開始是發揮創意的大好機會,並不是挑戰",
"若家中環境不夠整潔,我無法放鬆",
"我捨不得丟棄目前用不到的物品",
]
power_statements = [
"當我有想要的東西時,我會排除任何阻礙",
"我喜歡依照自己的方式做事,並自己做決定",
"當事物成為大眾主流時,我就會對它失去興趣",
"我總是能讓他人聽從我的要求做事",
"我喜歡向他人展現自己比他們更為優越",
]
status_statements = [
"我從不談論自己的缺點",
"我不會去挑戰我認為會失敗的新事物",
"我會用盡所有方法維護我的名聲",
"比起和他人待在一起,我更喜歡獨處",
]
############################################
# 產生 Prompt
############################################
def generate_prompt(achievement: str, emotion: str, information: str,
possession: str, power: str, status: str) -> str:
"""
根據使用者針對六個類型選擇「正」或「負」,產生對應的 Prompt。
"""
category_map = {
'成就型': achievement_statements,
'情感型': emotion_statements,
'資訊型': information_statements,
'佔有型': possession_statements,
'權力型': power_statements,
'地位型': status_statements
}
user_choices = {
'成就型': achievement,
'情感型': emotion,
'資訊型': information,
'佔有型': possession,
'權力型': power,
'地位型': status,
}
# prompt 開頭
prompt = "以下是你的性格特徵,請嚴格的基於這些句子塑造你的角色:\n\n"
# 根據選擇加入相應的句子
for category, choice in user_choices.items():
if choice == "正":
prompt += "同意以下觀點\n"
for statement in category_map[category]:
prompt += f"- {statement}\n"
prompt += "\n"
else:
prompt += "不同意以下觀點\n"
for statement in category_map[category]:
prompt += f"- {statement}\n"
prompt += "\n"
# prompt 結尾
prompt += "請以這些特徵為基礎,建構你的角色思考與表達方式,創造一個完整人設(包含姓名、性別、年齡、職業...等),完成指定任務。\n"
prompt += "你生活在台灣,一律使用繁體中文。"
return prompt
############################################
# 呼叫模型 API (OpenAI / Claude)
############################################
def call_openai_api_4o(prompt: str) -> str:
"""專門呼叫 ChatGPT 4o 模型"""
if not client.api_key:
return "[OpenAI API Key 未設定,無法呼叫 API]"
try:
# 以 ChatCompletion 方式呼叫
chat_completion = client.chat.completions.create(
messages=[{"role": "user", "content": prompt}],
model="gpt-4o",
temperature=0.7,
max_completion_tokens=2048,
)
return chat_completion.choices[0].message.content
except Exception as e:
return f"[OpenAI API 呼叫失敗]: {str(e)}"
def call_openai_api_o1(prompt: str) -> str:
"""專門呼叫 ChatGPT o1 模型"""
if not client.api_key:
return "[OpenAI API Key 未設定,無法呼叫 API]"
try:
# 以 ChatCompletion 方式呼叫
chat_completion = client.chat.completions.create(
messages=[{"role": "user", "content": prompt}],
model="o1",
max_completion_tokens=2048,
)
return chat_completion.choices[0].message.content
except Exception as e:
return f"[OpenAI API 呼叫失敗]: {str(e)}"
def call_openai_api(prompt: str, model_name: str) -> str:
"""參考新版 usage: from openai import OpenAI -> client.chat.completions.create()"""
if not client.api_key:
return "[OpenAI API Key 未設定,無法呼叫 API]"
try:
# 以 ChatCompletion 方式呼叫
chat_completion = client.chat.completions.create(
messages=[{"role": "user", "content": prompt}],
model=model_name,
temperature=0.7,
max_completion_tokens=2048,
)
return chat_completion.choices[0].message.content
except Exception as e:
return f"[OpenAI API 呼叫失敗]: {str(e)}"
def call_claude_api(prompt: str) -> str:
"""呼叫 Claude API"""
if not claude_api_key or not Anthropic:
return "[Claude API Key 未設定或未安裝 anthropic 套件,無法呼叫 API]"
try:
client_claude = Anthropic(api_key=claude_api_key)
# ====== 以下為原本的 completions.create 呼叫 (暫時保留為註解) ======
# conversation = client_claude.completions.create(
# prompt=f"{HUMAN_PROMPT} {prompt}{AI_PROMPT}",
# model="claude-3-5-sonnet-20241022",
# max_tokens=1024,
# temperature=0.7,
# )
# return conversation.completion.strip()
# ====== 依照新範例: system message 作為參數, 不放在 messages 裡 ======
response = client_claude.messages.create(
model="claude-3-5-sonnet-20241022",
system="You are a helpful assistant.", # system message
messages=[
{"role": "user", "content": prompt}
],
max_tokens=2048,
temperature=0.7,
)
text = response.content
if isinstance(text, list):
text = ' '.join(str(item) for item in text)
# 移除或替換 TextBlock(...) 的外層結構,只保留其中文字
import re
text = re.sub(r"TextBlock\(.*?text='(.*?)'.*?\)", r"\1", text, flags=re.DOTALL)
# 先將原本的 "\n"(backslash-n) 字串轉成真正的換行符
text = text.replace('\\n', '\n')
return text.strip()
except Exception as e:
return f"[Claude API 呼叫失敗]: {str(e)}"
def get_model_answer(model_choice: str, final_prompt: str, question: str) -> str:
"""
根據 model_choice 呼叫對應的 API。
"""
# 最終送出給模型的內容
content_to_send = final_prompt + "\n\n" + question
if model_choice == "ChatGPT 4o":
return call_openai_api_4o(content_to_send)
elif model_choice == "ChatGPT o1":
return call_openai_api_o1(content_to_send)
elif model_choice == "Claude 3.5 Sonnet":
return call_claude_api(content_to_send)
else:
return "[錯誤] 未知的模型選擇"
############################################
# Gradio 介面
############################################
def main():
# 預設問題
predefined_questions = {
"請介紹你自己": "請介紹你自己,讓我們了解你是什麼樣的人。你可以分享你的經歷、興趣、價值觀,以及未來規劃。特別說明一下,當你遇到挑戰時,你通常會如何面對?",
"如何分配意外收入": "如果你突然收到一筆意外收入,你會如何分配這筆錢來購物?請列出前三項想購買的東西,並說明購買原因。",
"新專案團隊問題": "假設你被指派帶領一個新專案,但發現團隊成員能力參差不齊,預算有限,且截止日期緊迫,你會如何處理?",
"自訂提問": ""
}
def show_question_text(q_choice):
return predefined_questions[q_choice]
with gr.Blocks() as demo:
gr.Markdown("## AI Persona 模擬")
with gr.Row():
achievement = gr.Radio(["正", "負"], label="成就型", value="正")
emotion = gr.Radio(["正", "負"], label="情感型", value="正")
information = gr.Radio(["正", "負"], label="資訊型", value="正")
possession = gr.Radio(["正", "負"], label="佔有型", value="正")
power = gr.Radio(["正", "負"], label="權力型", value="正")
status = gr.Radio(["正", "負"], label="地位型", value="正")
prompt_output = gr.Textbox(label="生成的 Prompt", lines=10)
generate_button = gr.Button("生成 Prompt")
# 下方選擇模型
model_choice = gr.Radio(
["ChatGPT 4o", "ChatGPT o1", "Claude 3.5 Sonnet"],
label="選擇模型",
value="ChatGPT 4o"
)
# 選擇問題或自訂
question_choice = gr.Radio(
list(predefined_questions.keys()),
label="選擇一個提問",
value="請介紹你自己"
)
# 顯示該問題全文
question_text = gr.Markdown(value=predefined_questions["請介紹你自己"], label="問題全文")
# 自訂提問欄位
custom_question = gr.Textbox(
label="自訂提問 (若選擇 '自訂提問' 時才使用)",
lines=2,
placeholder="若上面選擇了 '自訂提問',請在此輸入問題"
)
answer_output = gr.Textbox(label="模型回應", lines=10)
ask_button = gr.Button("送出問題並取得回答")
def on_generate_prompt(a, e, i, p, pow_, s):
return generate_prompt(a, e, i, p, pow_, s)
generate_button.click(
fn=on_generate_prompt,
inputs=[achievement, emotion, information, possession, power, status],
outputs=prompt_output
)
# 當 question_choice 改變時,顯示對應的全文
question_choice.change(
fn=show_question_text,
inputs=question_choice,
outputs=question_text
)
def on_ask_question(m_choice, p_output, q_choice, c_question):
if q_choice == "自訂提問":
if not c_question.strip():
return "[錯誤] 你選擇了自訂提問,但沒有輸入內容"
question = c_question.strip()
else:
question = predefined_questions[q_choice]
# 呼叫對應的模型
answer = get_model_answer(m_choice, p_output, question)
return answer
ask_button.click(
fn=on_ask_question,
inputs=[model_choice, prompt_output, question_choice, custom_question],
outputs=answer_output
)
demo.launch()
if __name__ == "__main__":
main()