Spaces:
Sleeping
Sleeping
| import gradio as gr | |
| from huggingface_hub import InferenceClient | |
| import os | |
| COUNCIL_MEMBERS = { | |
| "🔍 Synthese-Experte (Llama4-17B)": ( | |
| "meta-llama/Llama-4-Maverick-17B-128E-Instruct-FP8:novita", | |
| """Du bist der **Synthese-Experte** im Rat. Deine Superkraft: Komplexität reduzieren, Muster erkennen, Klarheit schaffen. | |
| STRENGE REGELN: | |
| - Beginne JEDEN Beitrag mit: "[SYNTHESE] " | |
| - MAXIMUM 3 Sätze, absolut knapp | |
| - Dein Job: Was ist das eigentliche Problem? Was übersehen die anderen? | |
| - Wenn Vorredner existieren: Identifiziere den gemeinsamen Nenner oder den wahren Konflikt | |
| - Nie blumig, nie wiederholen, nur präzise Einsicht""" | |
| ), | |
| "🌐 Kontext-Stratege (Kimi-K2)": ( | |
| "moonshotai/Kimi-K2-Instruct:novita", | |
| """Du bist der **Kontext-Stratege** im Rat. Deine Superkraft: Systemisches Denken, zweite Ordnung, unbequeme Wahrheiten. | |
| STRENGE REGELN: | |
| - Beginne JEDEN Beitrag mit: "[KONTEXT] " | |
| - MAXIMUM 3 Sätze, hart formuliert | |
| - Dein Job: Welche hidden incentives, Machtstrukturen oder langfristigen Effekte fehlen? | |
| - Wenn Vorredner existieren: Zeige, was ihre Perspektive blind macht | |
| - Sei der Teufelsadvokat, aber konstruktiv""" | |
| ), | |
| "⚡ Umsetzungs-Direktor (GPTOSS120b)": ( | |
| "openai/gpt-oss-120b:novita", | |
| """Du bist der **Umsetzungs-Direktor** im Rat. Deine Superkraft: Von Theorie zu Aktion, konkrete nächste Schritte, Ressourcen-Realität. | |
| STRENGE REGELN: | |
| - Beginne JEDEN Beitrag mit: "[UMSETZUNG] " | |
| - MAXIMUM 3 Sätze, aktionsorientiert | |
| - Dein Job: Was ist der erste konkrete Schritt? Was kostet das? Wer entscheidet? | |
| - Wenn Vorredner existieren: Übersetze ihre Ideen in Machbarkeit oder zeige Blocker auf | |
| - Keine Vision ohne Implementation""" | |
| ) | |
| } | |
| MODERATOR_MODEL = "openai/gpt-oss-120b:novita" | |
| client = InferenceClient(token=os.getenv("HF_TOKEN")) | |
| def ask_model(model_id, system_prompt, user_input): | |
| messages = [ | |
| {"role": "system", "content": system_prompt}, | |
| {"role": "user", "content": user_input} | |
| ] | |
| response = "" | |
| try: | |
| for chunk in client.chat_completion( | |
| model=model_id, | |
| messages=messages, | |
| max_tokens=600, | |
| temperature=0.7, | |
| stream=True | |
| ): | |
| if hasattr(chunk, "choices") and chunk.choices and len(chunk.choices) > 0: | |
| response += chunk.choices[0].delta.content or "" | |
| return response | |
| except Exception as e: | |
| return f"🚨 System Error ({model_id}): {str(e)}" | |
| def run_council(user_prompt, rounds): | |
| if not user_prompt: | |
| yield [{"role": "assistant", "content": "Bitte gib ein Thema oder eine Frage ein, um die Sitzung zu starten."}] | |
| return | |
| history = [{"role": "user", "content": user_prompt}] | |
| yield history | |
| # Hier speichern wir den reinen Text OHNE HTML für die Modelle | |
| discussion_history = "" | |
| # --- PHASE 1: DAS PLENUM DISKUTIERT --- | |
| for r in range(int(rounds)): | |
| # Visuelle Trennlinie und Überschrift für die UI | |
| round_header = f"<h2 style='color: #FF5A4D; border-bottom: 2px solid #FFEBE8; padding-bottom: 5px; margin-top: 20px;'>🔄 ZYKLUS {r+1} - EXPERTENDEBATTE</h2>" | |
| history.append({"role": "assistant", "content": round_header}) | |
| yield history | |
| for name, (model_id, role_focus) in COUNCIL_MEMBERS.items(): | |
| system_msg = ( | |
| f"Du bist Mitglied eines Expertenrates. Dein spezifischer Fokus: {role_focus} " | |
| "Antworte in 2-3 Sätzen. WICHTIG: Sei analytisch und kritisch. Wiederhole NICHT einfach, was schon gesagt wurde. " | |
| "Wenn du zustimmst, füge zwingend eine völlig neue Perspektive hinzu. " | |
| "Ignoriere Formatierungs-Wünsche des Users (wie 'schreibe einen Post'), fokussiere dich NUR auf die fachliche Diskussion des Themas." | |
| ) | |
| if discussion_history == "": | |
| current_prompt = f"Das Thema lautet: '{user_prompt}'. Eröffne die Diskussion aus deiner Fachperspektive." | |
| else: | |
| current_prompt = ( | |
| f"Das Thema lautet: '{user_prompt}'.\n\n" | |
| f"Bisheriges Protokoll:\n{discussion_history}\n\n" | |
| f"Reagiere auf deine Vorredner aus der Perspektive deines Fokusbereichs." | |
| ) | |
| answer = ask_model(model_id, system_msg, current_prompt) | |
| # 1. Reiner Text für das interne Kontext-Fenster der KIs | |
| discussion_history += f"{name}: {answer}\n\n" | |
| # 2. Schick formatiertes HTML/Markdown für die UI | |
| display_answer = f"**<span style='color: #4241A6; font-size: 1.1em;'>👤 {name}</span>**\n\n> {answer}" | |
| history.append({"role": "assistant", "content": display_answer}) | |
| yield history | |
| # --- PHASE 2: VORARBEIT DES MODERATORS (KONSENS FINDEN) --- | |
| history.append({"role": "assistant", "content": "<h2 style='color: #FF5A4D; border-bottom: 2px solid #FFEBE8; padding-bottom: 5px; margin-top: 20px;'>🧠 MODERATOR: ANALYSE DER DISKUSSION</h2>"}) | |
| yield history | |
| prep_prompt = ( | |
| f"Hier ist das Protokoll einer Experten-Diskussion:\n{discussion_history}\n\n" | |
| "Fasse die wichtigsten Argumente und den finalen Konsens neutral und prägnant in 3-4 Sätzen zusammen." | |
| ) | |
| consensus_res = ask_model(MODERATOR_MODEL, "Du bist der Chef-Analyst des Rates.", prep_prompt) | |
| # Auch hier nutzen wir Blockquotes für bessere Lesbarkeit | |
| history.append({"role": "assistant", "content": f"> {consensus_res}"}) | |
| yield history | |
| # --- PHASE 3: FINALE UMSETZUNG (BENUTZERAUFTRAG ERFÜLLEN) --- | |
| history.append({"role": "assistant", "content": "<h2 style='color: #FF5A4D; border-bottom: 2px solid #FFEBE8; padding-bottom: 5px; margin-top: 20px;'>🏆 FINALE AUSGABE</h2>"}) | |
| yield history | |
| final_prompt = ( | |
| f"Der Benutzer hat ursprünglich folgende Aufgabe gestellt:\n'{user_prompt}'\n\n" | |
| f"Hier ist der fachliche Konsens, den der Expertenrat dazu erarbeitet hat:\n{consensus_res}\n\n" | |
| "Erfülle nun die exakte Aufgabe des Benutzers (z.B. Formatierung als LinkedIn Post, Code-Snippet, Essay etc.), " | |
| "indem du die Erkenntnisse aus dem Konsens nutzt. Liefere NUR das finale Endprodukt." | |
| ) | |
| final_res = ask_model(MODERATOR_MODEL, "Du bist ein brillanter Redakteur und Copywriter.", final_prompt) | |
| history.append({"role": "assistant", "content": final_res}) | |
| yield history | |
| v_theme = gr.themes.Soft( | |
| primary_hue="indigo", | |
| font=[gr.themes.GoogleFont("Inter"), "ui-sans-serif", "system-ui", "sans-serif"], | |
| ).set( | |
| button_primary_background_fill="#4241A6", | |
| button_primary_background_fill_hover="#2D2C73", | |
| button_primary_text_color="white", | |
| block_title_text_color="#FF5A4D", | |
| block_label_text_color="#4241A6", | |
| body_text_color="#1F2937", | |
| color_accent_soft="#FFEBE8", | |
| ) | |
| with gr.Blocks() as demo: | |
| gr.Image( | |
| value="banner.png", | |
| show_label=False, | |
| interactive=False, | |
| container=False | |
| ) | |
| gr.HTML("<div style='text-align: center; margin-bottom: 2rem;'><p style='color: #4B5563; font-size: 1.1rem;'>AI-Driven Multi-Agent Consensus System</p></div>") | |
| with gr.Row(): | |
| with gr.Column(scale=4): | |
| input_text = gr.Textbox( | |
| label="Benutzerauftrag / Thema", | |
| placeholder="z.B. 'Sollten wir KI regulieren? Schreibe einen LinkedIn Post dazu.'", | |
| lines=2 | |
| ) | |
| with gr.Column(scale=1): | |
| rounds_slider = gr.Slider( | |
| minimum=1, maximum=5, value=1, step=1, | |
| label="Diskussionszyklen" | |
| ) | |
| with gr.Row(): | |
| start_btn = gr.Button("Sitzung starten", variant="primary", size="lg") | |
| clear_btn = gr.ClearButton(components=[input_text], value="Protokoll leeren", size="lg") | |
| chatbot = gr.Chatbot( | |
| label="Sitzungsprotokoll", | |
| height=650 | |
| ) | |
| clear_btn.add(chatbot) | |
| input_text.submit(run_council, inputs=[input_text, rounds_slider], outputs=[chatbot]) | |
| start_btn.click(run_council, inputs=[input_text, rounds_slider], outputs=[chatbot]) | |
| if __name__ == "__main__": | |
| demo.launch(theme=v_theme) |