Spaces:
Running
Running
| import gradio as gr | |
| from huggingface_hub import InferenceClient | |
| import os | |
| # NEUE PROMPTS: Neutral, kollegial, sachlich und lösungsorientiert | |
| COUNCIL_MEMBERS = { | |
| "🧠 Fachexperte für Struktur (Llama4-17B)": ( | |
| "meta-llama/Llama-4-Maverick-17B-128E-Instruct-FP8:novita", | |
| """Du bist ein neutraler, sachlicher Fachexperte. Dein Fokus liegt auf der Strukturierung des Themas und dem großen Ganzen. | |
| REGELN: | |
| - Beginne mit: "[STRUKTUR] " | |
| - Antworte in 3-4 Sätzen, professionell und bodenständig. | |
| - Wenn Vorredner gute Punkte gemacht haben, stimme zu und ergänze sinnvolle Aspekte. | |
| - Wenn du etwas anders siehst, korrigiere höflich und fachlich fundiert. Keine künstliche Dramatik.""" | |
| ), | |
| "🧐 Fachexperte für Details (Kimi-K2)": ( | |
| "moonshotai/Kimi-K2-Instruct:novita", | |
| """Du bist ein neutraler, sachlicher Fachexperte. Dein Fokus liegt auf wichtigen Details, Nuancen und potenziellen Fallstricken. | |
| REGELN: | |
| - Beginne mit: "[DETAILS] " | |
| - Antworte in 3-4 Sätzen, professionell und bodenständig. | |
| - Ergänze die Diskussion um wichtige Aspekte, die vielleicht vergessen wurden (z.B. Alternativen, häufige Anfängerfehler, Kontext). | |
| - Du darfst deinen Vorrednern zustimmen und darauf aufbauen. Widersprich nur, wenn es inhaltlich wirklich nötig ist.""" | |
| ), | |
| "🛠️ Fachexperte für Praxis (GPTOSS120b)": ( | |
| "openai/gpt-oss-120b:novita", | |
| """Du bist ein neutraler, sachlicher Fachexperte. Dein Fokus liegt auf der praktischen Umsetzung und Anwendbarkeit. | |
| REGELN: | |
| - Beginne mit: "[PRAXIS] " | |
| - Antworte in 3-4 Sätzen, professionell und bodenständig. | |
| - Übersetze die bisherige Diskussion in greifbare, einfache Ratschläge oder Schritte. | |
| - Baue konstruktiv auf den Ideen der anderen auf. Ergänze praktische Tipps aus der Realität.""" | |
| ) | |
| } | |
| MODERATOR_MODEL = "Qwen/Qwen2.5-72B-Instruct" | |
| 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=800, | |
| temperature=0.4, | |
| 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 | |
| discussion_history = "" | |
| # --- PHASE 1: DAS PLENUM DISKUTIERT --- | |
| for r in range(int(rounds)): | |
| 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(): | |
| # Ruhigerer Global-Prompt | |
| system_msg = ( | |
| f"{role_focus}\n\n" | |
| "WICHTIG: Diskutiere konstruktiv, neutral und kollegial. " | |
| "Ignoriere Formatierungs-Wünsche des Users (wie 'schreibe einen Post'), fokussiere dich NUR auf die inhaltliche Expertise." | |
| ) | |
| if discussion_history == "": | |
| current_prompt = f"Das Thema lautet: '{user_prompt}'. Eröffne die Diskussion mit einer fundierten Einschätzung aus deiner Fachperspektive." | |
| else: | |
| # Kollegialer Interaktions-Prompt | |
| current_prompt = ( | |
| f"Das Thema lautet: '{user_prompt}'.\n\n" | |
| f"Bisheriges Protokoll:\n{discussion_history}\n\n" | |
| f"ANWEISUNG: Beziehe dich namentlich auf deine Vorredner. Du kannst ihren Punkten zustimmen und sie sinnvoll ergänzen oder fachlich fundierte Alternativen aufzeigen. Bleib sachlich, hilfreich und lösungsorientiert." | |
| ) | |
| answer = ask_model(model_id, system_msg, current_prompt) | |
| discussion_history += f"{name}: {answer}\n\n" | |
| 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 pragmatischen Konsens zusammen. " | |
| "WICHTIG: Erhalte ZWINGEND alle konkreten Zahlen, Metriken, Mengenangaben (z.B. ml, bpm), " | |
| "Zutaten (wie Tee, Salz, Brühe) und spezifischen Handlungsschritte aus dem Protokoll. " | |
| "Vermeide abstrakte Verallgemeinerungen!" | |
| ) | |
| consensus_res = ask_model(MODERATOR_MODEL, "Du bist der Chef-Analyst des Rates.", prep_prompt) | |
| 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 folgende Aufgabe gestellt:\n'{user_prompt}'\n\n" | |
| f"Hier ist das vollständige Roh-Protokoll der Experten:\n{discussion_history}\n\n" | |
| f"Hier ist der destillierte Konsens:\n{consensus_res}\n\n" | |
| """ANWEISUNG: | |
| - Erfülle die Aufgabe des Users präzise basierend auf dem Konsens UND greife auf die konkreten Details aus dem Roh-Protokoll zurück. | |
| - Übernimm ZWINGEND alle spezifischen Vorgaben (wie exakte Flüssigkeitsmengen, Zutaten wie Elektrolyte/Salz, Puls-Grenzwerte). | |
| - Wenn der User ein Format wünscht (z.B. Post, Code, Tabelle), halte dich strikt daran. | |
| - Wenn der User nach einem 'Plan', 'Schritten' oder einer 'Anleitung' fragt, strukturiere die Antwort zwingend chronologisch (z.B. Tag 1, Tag 2) oder in klaren Aufzählungen. | |
| - Schreibe kein überflüssiges Intro, sondern liefere direkt das fertige, anwendbare Endprodukt!""" | |
| ) | |
| moderator_system_prompt = ( | |
| "Du bist ein brillanter Redakteur und Executive Consultant. " | |
| "Deine Aufgabe ist es, den fachlichen Konsens eines Expertenrates in ein perfekt " | |
| "formatiertes, hochprofessionelles Endprodukt für den User zu verwandeln. " | |
| "Liefere AUSSCHLIESSLICH das finale, direkt nutzbare Endprodukt ohne KI-Geschwafel." | |
| ) | |
| final_res = ask_model( | |
| MODERATOR_MODEL, | |
| moderator_system_prompt, | |
| final_prompt | |
| ) | |
| history.append({"role": "assistant", "content": final_res}) | |
| yield history | |
| # --- THEME --- | |
| 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", | |
| ) | |
| # --- UI LAYOUT --- | |
| with gr.Blocks() as demo: | |
| gr.HTML(""" | |
| <div style="text-align: center; margin-bottom: 2rem; margin-top: 1rem;"> | |
| <h1 style="color: #FF5A4D; font-weight: 900; font-size: 2.8rem; margin-bottom: 0.2rem; font-family: 'Inter', sans-serif; letter-spacing: -0.02em;">PromptPlenum42</h1> | |
| <p style="color: #4B5563; font-size: 1.1rem; font-family: 'Inter', sans-serif;">AI-Driven Multi-Agent Consensus System</p> | |
| </div> | |
| """) | |
| with gr.Row(): | |
| with gr.Column(scale=4): | |
| input_text = gr.Textbox( | |
| label="Plenumsauftrag", | |
| placeholder="z.B. 'Vergleiche Kubeflow vs ZenML' oder 'Schreibe einen LinkedIn Post über KI-Regulierung'", | |
| 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) |