File size: 12,046 Bytes
936f5c5
3c6e0bd
9a616b0
936f5c5
 
9a616b0
936f5c5
 
 
 
 
 
 
9a616b0
 
936f5c5
3c6e0bd
9a616b0
 
 
 
 
3c6e0bd
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
936f5c5
 
 
 
 
 
 
 
3c6e0bd
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
936f5c5
 
 
aed4040
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
f4dcfae
9a616b0
 
936f5c5
 
 
9a616b0
 
936f5c5
9a616b0
936f5c5
f4dcfae
936f5c5
9a616b0
936f5c5
 
 
 
 
 
 
 
 
9a616b0
37c8e37
 
 
 
affd82d
37c8e37
 
 
 
628e064
ea7bc39
2cdaa29
628e064
37c8e37
628e064
37c8e37
f4dcfae
936f5c5
 
8043d15
d0f9b8b
 
dcefb5c
 
 
 
f4dcfae
 
d0f9b8b
936f5c5
 
 
 
 
 
 
 
 
 
 
aed4040
936f5c5
aed4040
936f5c5
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
3c6e0bd
1354565
 
 
936f5c5
6c109c4
936f5c5
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1354565
 
 
 
 
936f5c5
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1354565
 
 
 
 
 
 
936f5c5
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
3c6e0bd
936f5c5
3c6e0bd
 
 
936f5c5
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
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
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()