import gradio as gr from llama_cpp import Llama # --- 1. SETUP & DATEN --- # Das lokale Modell laden (dein angegebener Pfad) llm = Llama.from_pretrained( repo_id="simonper/Llama-3.2-1B-bnb-4bit_finetome-100k_gguf_4bit", filename="Llama-3.2-1B.Q4_K_M.gguf", n_ctx=4096, # Context window n_threads=8, # CPU threads (anpassbar) n_gpu_layers=0, # 0 für CPU, höher setzen wenn du GPU hast ) # Die Datenbank der Rätsel (Szenario = Öffentlich, Lösung = Versteckt) RIDDLES = { "Der tote Mann": { "scenario": "Ein Mann liegt tot auf einer Wiese. Neben ihm liegt ein ungeöffnetes Paket. Es sind keine Fußspuren zu sehen. Wie ist er gestorben?", "solution": "Der Mann ist aus einem Flugzeug gesprungen. Das Paket war sein Fallschirm, der sich nicht geöffnet hat." }, "Der Barman": { "scenario": "Ein Mann geht in eine Bar und bestellt ein Glas Wasser. Der Barkeeper zieht eine Waffe und richtet sie auf ihn. Der Mann sagt 'Danke' und geht. Warum?", "solution": "Der Mann hatte Schluckauf. Der Schreck durch die Waffe hat ihn geheilt, weshalb er dankbar war." }, "Die Hütte": { "scenario": "Zwei Männer sind in einer Hütte im Wald. Einer ist tot. Die Hütte ist nicht abgebrannt, aber der tote Mann ist verkohlt. Was ist passiert?", "solution": "Die Hütte ist die Kabine eines abgestürzten Flugzeugs. Der Mann starb beim Absturzfeuer." }, "Der Koffer": { "scenario": "Eine Frau öffnet ihren Koffer und findet einen toten Mann darin. Sie wird nicht verhaftet und hat keine Angst. Warum?", "solution": "Der 'Koffer' ist eigentlich ein Sarg. Die Frau ist auf einer Beerdigung." } } # --- 2. HELPER FUNCTIONS --- def build_prompt(system_message: str, history: list[dict], user_message: str) -> str: """ Konstruiert den Prompt für das Llama Modell. Da wir Llama-3 nutzen, ist ein Format wichtig, das System-Instruktionen klar trennt. """ lines = [] # System Prompt Injection if system_message: lines.append(f"System: {system_message}\n") # History aufbauen for turn in history: role = turn["role"] content = turn["content"] if role == "user": lines.append(f"User: {content}") elif role == "assistant": lines.append(f"Assistant: {content}") # Aktuelle Nachricht lines.append(f"User: {user_message}") lines.append("Assistant:") return "\n".join(lines) def respond( message, history: list[dict[str, str]], system_message_dummy, # Wir ignorieren den Input vom Slider und bauen unseren eigenen max_tokens, temperature, top_p, selected_riddle, # Das kommt vom Dropdown ): # 1. Das aktuelle Rätsel laden current_game = RIDDLES[selected_riddle] # 2. Den "Game Master" System Prompt bauen (Hier passiert das Context Learning) # Wir injizieren die Lösung direkt in den Kontext des Modells game_master_prompt = ( f"Du bist der Spielleiter eines Laterale-Denk-Rätsels (Black Stories). " f"AKTUELLES SZENARIO: '{current_game['scenario']}' " f"VERSTECKTE LÖSUNG (Der User kennt diese NICHT!): '{current_game['solution']}' " f"\nREGELN FÜR DICH:\n" f"1. Analysiere die Frage des Users logisch in Bezug auf die versteckte Lösung.\n" f"2. Antworte AUSSCHLIESSLICH mit: 'Ja', 'Nein', oder 'Irrelevant'.\n" f"3. Wenn der User die Lösung errät, sage: 'KORREKT! Du hast es gelöst: [Erklärung]'.\n" f"4. Gib KEINE Hinweise." ) # 3. Prompt zusammenbauen prompt = build_prompt(game_master_prompt, history, message) # 4. Llama.cpp aufrufen output = llm( prompt, max_tokens=int(max_tokens), temperature=float(temperature), top_p=float(top_p), stop=["User:", "System:"], echo=False # Wichtig: Den Prompt nicht wiederholen ) reply = output["choices"][0]["text"].strip() return reply # --- 3. UI AUFBAU (GRADIO BLOCKS) --- with gr.Blocks(theme=gr.themes.Soft()) as demo: gr.Markdown("# 🕵️ Der Ja/Nein Detektiv (Llama GGUF Edition)") gr.Markdown( "**Anleitung:** Ich denke an eine seltsame Situation. " "Stelle mir Ja/Nein-Fragen, um herauszufinden, was passiert ist!" ) # Das Dropdown Menü für die Rätsel-Auswahl with gr.Row(): riddle_select = gr.Dropdown( choices=list(RIDDLES.keys()), value="Der tote Mann", label="Wähle einen Fall", interactive=True ) # Textfeld, das das Szenario anzeigt scenario_display = gr.Textbox( label="Das Szenario (Dein Hinweis)", value=RIDDLES["Der tote Mann"]["scenario"], interactive=False ) # Logik: Wenn Dropdown geändert wird, Textbox aktualisieren def update_scenario(choice): return RIDDLES[choice]["scenario"] riddle_select.change(fn=update_scenario, inputs=riddle_select, outputs=scenario_display) # Chat Interface chatbot = gr.ChatInterface( respond, type="messages", additional_inputs=[ gr.Textbox(value="", visible=False), # Dummy für System Message (wir nutzen den generierten) gr.Slider(minimum=1, maximum=512, value=100, step=1, label="Max new tokens"), gr.Slider(minimum=0.1, maximum=2.0, value=0.2, step=0.1, label="Temperature (Niedrig für Logik)"), gr.Slider(minimum=0.1, maximum=1.0, value=0.95, step=0.05, label="Top-p"), riddle_select # Das ausgewählte Rätsel wird an die respond-Funktion übergeben ], ) if __name__ == "__main__": demo.launch()