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"

🔄 ZYKLUS {r+1} - EXPERTENDEBATTE

" 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, aber progressiver Interaktions-Prompt current_prompt = ( f"Das Thema lautet: '{user_prompt}'.\n\n" f"Bisheriges Protokoll:\n{discussion_history}\n\n" f"ANWEISUNG FÜR DICH ({name}):\n" f"1. Analysiere das Protokoll. Erkenne, was du und die anderen bereits gesagt haben.\n" f"2. ABSOLUTES VERBOT: Fasse die Vorredner NICHT zusammen. Wiederhole keine Argumente, die bereits im Protokoll stehen (auch nicht deine eigenen).\n" f"3. Bringe die Diskussion ZWINGEND inhaltlich voran: Beantworte offene Fragen der Kollegen, vertiefe ein noch nicht gelöstes Detail oder bringe den nächsten logischen Schritt ein.\n" f"4. Verzichte auf lange Höflichkeitsfloskeln. Komm direkt zum Punkt." ) answer = ask_model(model_id, system_msg, current_prompt) discussion_history += f"{name}: {answer}\n\n" display_answer = f"**👤 {name}**\n\n> {answer}" history.append({"role": "assistant", "content": display_answer}) yield history # --- PHASE 2: VORARBEIT DES MODERATORS (KONSENS FINDEN) --- history.append({"role": "assistant", "content": "

🧠 MODERATOR: ANALYSE DER DISKUSSION

"}) 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": "

🏆 FINALE AUSGABE

"}) 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("""

PromptPlenum42

AI-Driven Multi-Agent Consensus System

""") 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)