File size: 9,878 Bytes
3b36e9a
 
 
 
fb6ba49
3b36e9a
fb6ba49
9cb7762
fb6ba49
 
 
 
 
 
9cb7762
fb6ba49
9cb7762
fb6ba49
 
 
 
 
 
9cb7762
fb6ba49
9cb7762
fb6ba49
 
 
 
 
 
9cb7762
3b36e9a
 
fb6ba49
f991c72
 
3b1ed46
3b36e9a
 
 
 
 
 
 
 
2c9e8e8
3b1ed46
2c9e8e8
 
1401dd9
 
2c9e8e8
 
3b1ed46
 
 
2c9e8e8
 
098fd29
3b36e9a
 
098fd29
 
 
 
5f1bcef
 
 
7b5221f
3b36e9a
b5db64c
3b36e9a
6d8772b
5f1bcef
2c9e8e8
3b36e9a
fdf4ef2
0e14d1d
fb6ba49
1034608
0e14d1d
fb6ba49
 
1034608
7b5221f
 
fb6ba49
7b5221f
7a28ec5
7b5221f
b5db64c
 
7a28ec5
 
 
 
 
7b5221f
7a28ec5
 
7b5221f
 
3b36e9a
6d8772b
7b5221f
6d8772b
 
1401dd9
3b36e9a
1401dd9
 
 
 
 
 
b540bda
 
 
 
1401dd9
 
 
 
 
 
 
 
 
 
 
 
b540bda
 
1401dd9
b540bda
 
9903ff9
b540bda
 
9903ff9
 
 
 
 
 
b540bda
1401dd9
 
 
 
9903ff9
1401dd9
 
 
 
 
b540bda
0555995
 
 
098fd29
0555995
098fd29
 
0555995
098fd29
 
0555995
098fd29
 
1401dd9
dd90011
 
17907d4
 
 
 
 
 
f0bbb7e
3b36e9a
098fd29
 
17907d4
e6394dc
098fd29
 
 
 
3931f6a
098fd29
 
 
 
 
 
6a3a98b
098fd29
 
0555995
098fd29
 
 
3b36e9a
098fd29
3b36e9a
 
5f1bcef
0555995
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
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, 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"**<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)