Spaces:
Sleeping
Sleeping
| 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() | |