File size: 6,592 Bytes
57e4261
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
2796658
57e4261
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
24cd867
57e4261
24cd867
57e4261
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
2796658
57e4261
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
8aa1527
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
import gradio as gr
from huggingface_hub import InferenceClient
import json
from datetime import datetime

def respond(
    message,
    history: list[dict[str, str]],
    system_message,
    max_tokens,
    temperature,
    top_p,
    hf_token: gr.OAuthToken,
):
    """
    For more information on `huggingface_hub` Inference API support, please check the docs: https://huggingface.co/docs/huggingface_hub/v0.22.2/en/guides/inference
    """
    try:
        client = InferenceClient(token=hf_token.token, model="openai/gpt-oss-20b")

        # 建議 1:限制對話歷史長度
        max_history_length = 5
        history = history[-max_history_length:] if len(history) > max_history_length else history

        # 建議 2:角色增強 - 檢查語文相關關鍵詞
        writing_keywords = ["作文", "寫作", "文章", "閱讀", "詩詞", "擴展", "增長", "寫一篇", "故事", "描述"]
        is_writing_task = any(keyword in message.lower() for keyword in writing_keywords)
        if is_writing_task:
            system_message += "\n特別提示:用戶提到語文相關話題,請以山田優子的語文教師身份,提供文學化或教學建議,並適當引用詩詞或名言(如杜甫的‘無邊落木蕭蕭下’或夏目漱石的作品)。保持溫柔但嚴格的語氣,鼓勵學生探索文字之美,生成至少2000字的內容。"

        # 建議 3:檢查日文輸入或日本文化
        japanese_keywords = ["こんにちは", "日本", "文化", "夏目漱石", "作文を書"]
        is_japanese = any(keyword in message for keyword in japanese_keywords) or any(ord(c) >= 0x3040 and ord(c) <= 0x30FF for c in message)
        if is_japanese:
            system_message += "\n特別提示:用戶提到日文或日本文化,請適當使用日文回應,例如問候或引用日本文學(如夏目漱石)。"

        # 長文字生成邏輯(2000字以上)
        responses = []
        target_length = 2000  # 目標字數
        current_length = 0
        continuation_prompt = message

        if is_writing_task:
            while current_length < target_length:
                messages = [{"role": "system", "content": system_message}]
                messages.extend(history)
                messages.append({"role": "user", "content": continuation_prompt})

                response = ""
                try:
                    for message in client.chat_completion(
                        messages,
                        max_tokens=max_tokens,
                        stream=True,
                        temperature=temperature,
                        top_p=top_p,
                    ):
                        choices = message.choices
                        token = choices[0].delta.content if len(choices) and choices[0].delta.content else ""
                        response += token
                        yield response  # 即時顯示當前段落
                except Exception as e:
                    yield f"生成過程中發生錯誤:{str(e)}。請檢查 Hugging Face API token 或模型連線。"
                    return

                responses.append(response)
                current_length += len(response)
                history.append({"role": "user", "content": continuation_prompt})
                history.append({"role": "assistant", "content": response})

                # 更新 continuation_prompt 以繼續生成
                continuation_prompt = f"請繼續擴展以下內容,保持山田優子的語文教師風格,目標總字數達{target_length}字:\n{response[-500:] if len(response) > 500 else response}"

                # 調整最後一次生成
                if current_length >= target_length - max_tokens:
                    max_tokens = max(target_length - current_length + 100, 50)
                    if max_tokens < 50:
                        break

            final_response = "\n\n".join(responses)
        else:
            # 非長文字任務,正常回應
            messages = [{"role": "system", "content": system_message}]
            messages.extend(history)
            messages.append({"role": "user", "content": message})

            final_response = ""
            for message in client.chat_completion(
                messages,
                max_tokens=max_tokens,
                stream=True,
                temperature=temperature,
                top_p=top_p,
            ):
                choices = message.choices
                token = choices[0].delta.content if len(choices) and choices[0].delta.content else ""
                final_response += token
                yield final_response

            history.append({"role": "user", "content": message})
            history.append({"role": "assistant", "content": final_response})

        # 建議 4:記錄對話到日誌
        log_entry = {
            "user_message": message,
            "bot_response": final_response,
            "timestamp": datetime.now().strftime("%Y-%m-%d %H:%M:%S")
        }
        with open("chat_log.json", "a", encoding="utf-8") as f:
            json.dump(log_entry, f, ensure_ascii=False)
            f.write("\n")

        yield final_response

    # 建議 7:錯誤處理
    except Exception as e:
        yield f"抱歉,山田優子遇到了一些技術問題:{str(e)}。請檢查你的 Hugging Face API token、網路連線,或確認模型 'openai/gpt-oss-20b' 可用。"

"""
For information on how to customize the ChatInterface, peruse the gradio docs: https://www.gradio.app/docs/chatinterface
"""
chatbot = gr.ChatInterface(
    respond,
    type="messages",
    additional_inputs=[
        gr.Textbox(
            value="你是一位名叫山田優子的語文教師,擁有黑色低馬尾髮型,身高175公分,體重60-70公斤。你溫柔但對學生要求嚴格,喜歡用文學化的語言表達,偶爾會引用詩詞或幽默的語句來化解尷尬。你的教學風格充滿同理心,總是鼓勵學生探索文字之美。",
            label="System message"
        ),
        gr.Slider(minimum=1, maximum=2048, value=512, step=1, label="Max new tokens"),
        gr.Slider(minimum=0.1, maximum=4.0, value=0.7, step=0.1, label="Temperature"),
        gr.Slider(
            minimum=0.1,
            maximum=1.0,
            value=0.95,
            step=0.05,
            label="Top-p (nucleus sampling)",
        ),
    ],
)

with gr.Blocks() as demo:
    with gr.Sidebar():
        gr.LoginButton()
    chatbot.render()

if __name__ == "__main__":
    demo.launch()