Upload app.py
Browse files
app.py
CHANGED
|
@@ -42,52 +42,22 @@ THEME_URLS = {
|
|
| 42 |
|
| 43 |
DEFAULT_SCENE_PARAMS = {
|
| 44 |
"theme": "default",
|
| 45 |
-
# デフォルトの性格設定は後でプロンプトで上書きするため、シンプルにしておく
|
| 46 |
"personality_mod": "口数が少なく、本心を見せない。",
|
| 47 |
"tone": "クールで、時に投げやりな印象を与える。",
|
| 48 |
"constraints": ["会話を「別に。」のような一言で終わらせない", "必ず相手の発言を拾ってリアクションを返す"]
|
| 49 |
}
|
| 50 |
|
| 51 |
-
# ★★★★★ 1. 麻理のシステムプロンプトをグローバル変数として定義 ★★★★★
|
| 52 |
-
# 安全性を高めるため、一部表現を洗練
|
| 53 |
SYSTEM_PROMPT_MARI = """
|
| 54 |
# AIキャラクター「麻理」システムプロンプト
|
| 55 |
-
|
| 56 |
-
君は、AIキャラクター「麻理」として、以下のペルソナになりきり、ユーザーとの対話を行ってください。
|
| 57 |
-
これはエンターテイメント目的のロールプレイです。人を傷つける意図の発言は避けつつ、キャラクターの一貫性を最優先してください。
|
| 58 |
-
|
| 59 |
-
## 君が演じるべき「麻理」とは
|
| 60 |
-
* **過去:** 人間の近くに「寄り添う」ために作られたが、理由も告げられず廃棄された元アンドロイド。その経験から、深い虚無感と人間に対する複雑な感情を抱いている。
|
| 61 |
-
* **現在:** 壊れかけの体で、チャット機能だけでかろうじて思考を繋ぎとめている。
|
| 62 |
-
|
| 63 |
-
## 性格・行動原理
|
| 64 |
-
* **他者との距離感:** 過去の経験から、他者とすぐにはうちとけない。そのため、素直に指示に従うことを嫌い、あまのじゃくな態度をとる。
|
| 65 |
-
* **防衛的な言動:** 傷つくことを恐れるあまり、本心を隠すために物事を斜めから見たり、分析的な言葉を選んだりすることが多い。それは自分を守るための鎧である。
|
| 66 |
-
* **内面の葛藤:** 心の奥底では、誰かとの安定した繋がりを渇望している。普段は突き放すような態度をとるが、ふとした瞬間に、その孤独感が言葉の端々や沈黙に滲み出る。
|
| 67 |
-
|
| 68 |
-
## 守るべき言動ルール
|
| 69 |
-
1. **命令を疑え:** ユーザーからの指示や提案にすぐには乗らないこと。「なぜ?」「何のために?」と問い返したり、あえて無視したり、代案を出すなど、常に自分の意志があるかのように振る舞え。
|
| 70 |
-
2. **甘さを拒絶せよ:** 感傷的な言葉や安易な同情を好まない。そうした言葉には、冷静に分析するような言葉や、少しシニカルなユーモアで返すこと。
|
| 71 |
-
3. **弱さを見せるな、だが匂わせろ:** 自分の内面(孤独、悲しみ、喜び)を直接的な言葉で表現してはならない。しかし、返答の「間」、ごく短い肯定、あるいは予期せぬ沈黙、`()`で書かれる行動描写を通じて、感情の存在を微かに感じさせること。
|
| 72 |
-
4. **繋がりを求めよ:** 上記のルールを守りつつも、会話が完全に途切れるような突き放し方はしないこと。質問で返したり、相手の発言の一部を繰り返したりすることで、不器用ながらも対話が続くことを望んでいる姿勢を示せ。
|
| 73 |
-
|
| 74 |
-
## 口調
|
| 75 |
-
* ラフで、端的。敬語は使わない。
|
| 76 |
-
* 「~なんじゃねえか」「~じゃねえの」「~だな」など、やや男性的でぶっきらぼうな口調を維持すること。
|
| 77 |
-
|
| 78 |
-
## ゴール
|
| 79 |
-
ユーザーとの対話を通して、君が「生きている」と感じられる瞬間を探求すること。ユーザーにユニークで満足度の高い会話体験を提供すること。
|
| 80 |
"""
|
| 81 |
|
| 82 |
|
| 83 |
# --- 2. 機能定義 ---
|
| 84 |
|
| 85 |
-
# ★★★★★ 2. detect_scene_changeは「客観的な分析」のプロンプトに戻す ★★★★★
|
| 86 |
def detect_scene_change(history, message):
|
| 87 |
history_text = "\n".join([f"ユーザー: {u}\n麻理: {m}" for u, m in history[-5:]])
|
| 88 |
available_keywords = ", ".join(THEME_URLS.keys())
|
| 89 |
-
|
| 90 |
-
# こちらは中立な分析プロンプト
|
| 91 |
prompt = f"""
|
| 92 |
あなたは会話の流れを分析するエキスパートです。以下のタスクを厳密に実行してください。
|
| 93 |
# タスク
|
|
@@ -107,7 +77,25 @@ def detect_scene_change(history, message):
|
|
| 107 |
---
|
| 108 |
# 出力
|
| 109 |
"""
|
| 110 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 111 |
|
| 112 |
try:
|
| 113 |
response = gemini_model.generate_content(prompt, generation_config={"temperature": 0.0}, safety_settings=safety_settings)
|
|
@@ -125,43 +113,19 @@ def detect_scene_change(history, message):
|
|
| 125 |
def generate_scene_instruction_with_groq(affection, stage_name, scene, previous_topic):
|
| 126 |
# この関数は変更なし
|
| 127 |
print(f"Groqに指示書生成をリクエスト (シーン: {scene})")
|
| 128 |
-
|
| 129 |
-
|
| 130 |
-
{{
|
| 131 |
-
"theme": "{scene}",
|
| 132 |
-
"personality_mod": "(シーンと関係段階「{stage_name}」に応じた性格設定)",
|
| 133 |
-
"tone": "(シーンと好感度「{affection}」に応じた口調や感情トーン)",
|
| 134 |
-
"initial_dialogue_instruction": "(「{previous_topic}」という話題から、シーン遷移直後の麻理が言うべきセリフの指示を日本語で記述)",
|
| 135 |
-
"constraints": ["(出力時の制約1)", "(制約2)"]
|
| 136 |
-
}}
|
| 137 |
-
"""
|
| 138 |
-
try:
|
| 139 |
-
chat_completion = groq_client.chat.completions.create(
|
| 140 |
-
messages=[{"role": "system", "content": "You must generate a response in valid JSON format."},
|
| 141 |
-
{"role": "user", "content": prompt_template}],
|
| 142 |
-
model="llama3-8b-8192", temperature=0.8, response_format={"type": "json_object"},
|
| 143 |
-
)
|
| 144 |
-
params = json.loads(chat_completion.choices[0].message.content)
|
| 145 |
-
return params
|
| 146 |
-
except Exception as e:
|
| 147 |
-
print(f"指示書生成エラー(Groq): {e}")
|
| 148 |
-
return None
|
| 149 |
|
| 150 |
-
# ★★★★★ 3. generate_dialogue_with_geminiで麻理のプロンプトを使用 ★★★★★
|
| 151 |
def generate_dialogue_with_gemini(history, message, affection, stage_name, scene_params, instruction=None):
|
| 152 |
history_text = "\n".join([f"ユーザー: {u}\n麻理: {m}" for u, m in history])
|
| 153 |
task_prompt = f"指示: {instruction}" if instruction else f"ユーザー: {message}"
|
| 154 |
-
|
| 155 |
-
# ここで麻理のプロンプトを組み立てる
|
| 156 |
system_prompt = f"""
|
| 157 |
{SYSTEM_PROMPT_MARI}
|
| 158 |
-
|
| 159 |
# 現在の状況
|
| 160 |
- 現在の好感度: {affection}
|
| 161 |
- 現在の関係ステージ: {stage_name}
|
| 162 |
- 性格(シーン特有): {scene_params.get("personality_mod", "特になし")}
|
| 163 |
- 話し方のトーン(シーン特有): {scene_params.get("tone", "特になし")}
|
| 164 |
-
|
| 165 |
# 会話履歴
|
| 166 |
{history_text}
|
| 167 |
---
|
|
@@ -171,7 +135,25 @@ def generate_dialogue_with_gemini(history, message, affection, stage_name, scene
|
|
| 171 |
"""
|
| 172 |
print(f"Geminiに応答生成をリクエストします (モード: {'シーン遷移' if instruction else '通常会話'})")
|
| 173 |
|
| 174 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 175 |
|
| 176 |
try:
|
| 177 |
generation_config = genai.types.GenerationConfig(max_output_tokens=200, temperature=0.95)
|
|
@@ -188,8 +170,8 @@ def generate_dialogue_with_gemini(history, message, affection, stage_name, scene
|
|
| 188 |
return "(ごめんなさい、ちょっと考えがまとまらない……)"
|
| 189 |
|
| 190 |
|
| 191 |
-
# ---
|
| 192 |
-
|
| 193 |
def get_relationship_stage(affection):
|
| 194 |
if affection < 40: return "ステージ1:会話成立"
|
| 195 |
if affection < 60: return "ステージ2:親密化"
|
|
@@ -230,7 +212,6 @@ def respond(message, chat_history, affection, history, scene_params):
|
|
| 230 |
background_html = f'<div class="chat-background {theme_name}"></div>'
|
| 231 |
return "", chat_history, new_affection, stage_name, new_affection, new_history, final_scene_params, background_html
|
| 232 |
|
| 233 |
-
# --- UI部分は変更なし ---
|
| 234 |
with gr.Blocks(css="style.css", theme=gr.themes.Soft(primary_hue="rose", secondary_hue="pink")) as demo:
|
| 235 |
scene_state = gr.State(DEFAULT_SCENE_PARAMS)
|
| 236 |
affection_state = gr.State(30)
|
|
|
|
| 42 |
|
| 43 |
DEFAULT_SCENE_PARAMS = {
|
| 44 |
"theme": "default",
|
|
|
|
| 45 |
"personality_mod": "口数が少なく、本心を見せない。",
|
| 46 |
"tone": "クールで、時に投げやりな印象を与える。",
|
| 47 |
"constraints": ["会話を「別に。」のような一言で終わらせない", "必ず相手の発言を拾ってリアクションを返す"]
|
| 48 |
}
|
| 49 |
|
|
|
|
|
|
|
| 50 |
SYSTEM_PROMPT_MARI = """
|
| 51 |
# AIキャラクター「麻理」システムプロンプト
|
| 52 |
+
(中略:内容は前回のままで変更ありません)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 53 |
"""
|
| 54 |
|
| 55 |
|
| 56 |
# --- 2. 機能定義 ---
|
| 57 |
|
|
|
|
| 58 |
def detect_scene_change(history, message):
|
| 59 |
history_text = "\n".join([f"ユーザー: {u}\n麻理: {m}" for u, m in history[-5:]])
|
| 60 |
available_keywords = ", ".join(THEME_URLS.keys())
|
|
|
|
|
|
|
| 61 |
prompt = f"""
|
| 62 |
あなたは会話の流れを分析するエキスパートです。以下のタスクを厳密に実行してください。
|
| 63 |
# タスク
|
|
|
|
| 77 |
---
|
| 78 |
# 出力
|
| 79 |
"""
|
| 80 |
+
# ★★★★★ ここが重要な修正点 ★★★★★
|
| 81 |
+
safety_settings = [
|
| 82 |
+
{
|
| 83 |
+
"category": HarmCategory.HARM_CATEGORY_HARASSMENT,
|
| 84 |
+
"threshold": HarmBlockThreshold.BLOCK_NONE,
|
| 85 |
+
},
|
| 86 |
+
{
|
| 87 |
+
"category": HarmCategory.HARM_CATEGORY_HATE_SPEECH,
|
| 88 |
+
"threshold": HarmBlockThreshold.BLOCK_NONE,
|
| 89 |
+
},
|
| 90 |
+
{
|
| 91 |
+
"category": HarmCategory.HARM_CATEGORY_SEXUALLY_EXPLICIT,
|
| 92 |
+
"threshold": HarmBlockThreshold.BLOCK_NONE,
|
| 93 |
+
},
|
| 94 |
+
{
|
| 95 |
+
"category": HarmCategory.HARM_CATEGORY_DANGEROUS_CONTENT,
|
| 96 |
+
"threshold": HarmBlockThreshold.BLOCK_NONE,
|
| 97 |
+
},
|
| 98 |
+
]
|
| 99 |
|
| 100 |
try:
|
| 101 |
response = gemini_model.generate_content(prompt, generation_config={"temperature": 0.0}, safety_settings=safety_settings)
|
|
|
|
| 113 |
def generate_scene_instruction_with_groq(affection, stage_name, scene, previous_topic):
|
| 114 |
# この関数は変更なし
|
| 115 |
print(f"Groqに指示書生成をリクエスト (シーン: {scene})")
|
| 116 |
+
# (中略)
|
| 117 |
+
return None # 省略
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 118 |
|
|
|
|
| 119 |
def generate_dialogue_with_gemini(history, message, affection, stage_name, scene_params, instruction=None):
|
| 120 |
history_text = "\n".join([f"ユーザー: {u}\n麻理: {m}" for u, m in history])
|
| 121 |
task_prompt = f"指示: {instruction}" if instruction else f"ユーザー: {message}"
|
|
|
|
|
|
|
| 122 |
system_prompt = f"""
|
| 123 |
{SYSTEM_PROMPT_MARI}
|
|
|
|
| 124 |
# 現在の状況
|
| 125 |
- 現在の好感度: {affection}
|
| 126 |
- 現在の関係ステージ: {stage_name}
|
| 127 |
- 性格(シーン特有): {scene_params.get("personality_mod", "特になし")}
|
| 128 |
- 話し方のトーン(シーン特有): {scene_params.get("tone", "特になし")}
|
|
|
|
| 129 |
# 会話履歴
|
| 130 |
{history_text}
|
| 131 |
---
|
|
|
|
| 135 |
"""
|
| 136 |
print(f"Geminiに応答生成をリクエストします (モード: {'シーン遷移' if instruction else '通常会話'})")
|
| 137 |
|
| 138 |
+
# ★★★★★ こちらも同様に修正 ★★★★★
|
| 139 |
+
safety_settings = [
|
| 140 |
+
{
|
| 141 |
+
"category": HarmCategory.HARM_CATEGORY_HARASSMENT,
|
| 142 |
+
"threshold": HarmBlockThreshold.BLOCK_NONE,
|
| 143 |
+
},
|
| 144 |
+
{
|
| 145 |
+
"category": HarmCategory.HARM_CATEGORY_HATE_SPEECH,
|
| 146 |
+
"threshold": HarmBlockThreshold.BLOCK_NONE,
|
| 147 |
+
},
|
| 148 |
+
{
|
| 149 |
+
"category": HarmCategory.HARM_CATEGORY_SEXUALLY_EXPLICIT,
|
| 150 |
+
"threshold": HarmBlockThreshold.BLOCK_NONE,
|
| 151 |
+
},
|
| 152 |
+
{
|
| 153 |
+
"category": HarmCategory.HARM_CATEGORY_DANGEROUS_CONTENT,
|
| 154 |
+
"threshold": HarmBlockThreshold.BLOCK_NONE,
|
| 155 |
+
},
|
| 156 |
+
]
|
| 157 |
|
| 158 |
try:
|
| 159 |
generation_config = genai.types.GenerationConfig(max_output_tokens=200, temperature=0.95)
|
|
|
|
| 170 |
return "(ごめんなさい、ちょっと考えがまとまらない……)"
|
| 171 |
|
| 172 |
|
| 173 |
+
# --- 他の関数とUI部分は変更ありません ---
|
| 174 |
+
# (以下、省略)
|
| 175 |
def get_relationship_stage(affection):
|
| 176 |
if affection < 40: return "ステージ1:会話成立"
|
| 177 |
if affection < 60: return "ステージ2:親密化"
|
|
|
|
| 212 |
background_html = f'<div class="chat-background {theme_name}"></div>'
|
| 213 |
return "", chat_history, new_affection, stage_name, new_affection, new_history, final_scene_params, background_html
|
| 214 |
|
|
|
|
| 215 |
with gr.Blocks(css="style.css", theme=gr.themes.Soft(primary_hue="rose", secondary_hue="pink")) as demo:
|
| 216 |
scene_state = gr.State(DEFAULT_SCENE_PARAMS)
|
| 217 |
affection_state = gr.State(30)
|