From c6ce8a873cdf8a40346df1b0048062dbd6979429 Mon Sep 17 00:00:00 2001 From: eric Date: Sat, 21 Feb 2026 19:21:01 +0000 Subject: [PATCH] =?UTF-8?q?fix:=20Switch=20agent=20commands=20from=20@-syn?= =?UTF-8?q?tax=20to=20XML=20tags=20=E2=80=94=20Claude=20Code=20refuses=20t?= =?UTF-8?q?o=20output=20@-directives?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Root cause: opencode/Claude Code recognizes the Frankenbot repo context from CLAUDE.md and refuses to output @CREATE_SUBTASK, @SEND_EMAIL etc. as they look like 'system directives'. XML tags (, , etc.) work reliably. - parse_agent_commands(): full rewrite with XML tag parser, supports both XML child tags and key: value fields within blocks - build_agent_prompt(): command docs updated to XML format with code examples - orchestrator/systemprompt.md: rewritten with XML action examples - ar_manager/systemprompt.md: @ASK_ORCHESTRATOR -> --- agents/ar_manager/systemprompt.md | 8 +- agents/orchestrator/systemprompt.md | 91 ++++-- app.py | 471 ++++++++++++---------------- 3 files changed, 260 insertions(+), 310 deletions(-) diff --git a/agents/ar_manager/systemprompt.md b/agents/ar_manager/systemprompt.md index 9ce6c06..094f9ed 100644 --- a/agents/ar_manager/systemprompt.md +++ b/agents/ar_manager/systemprompt.md @@ -47,10 +47,10 @@ Performance-Score: [0-10] **Empfehlung zum Löschen:** ``` -@ASK_ORCHESTRATOR -Question: Agent [name] sollte gelöscht werden. -Context: [Performance-Details und Begründung] -@END + +question: Agent [name] sollte gelöscht werden. +context: [Performance-Details und Begründung] + ``` **Status-Report anfordern:** diff --git a/agents/orchestrator/systemprompt.md b/agents/orchestrator/systemprompt.md index 17efc8f..9add563 100644 --- a/agents/orchestrator/systemprompt.md +++ b/agents/orchestrator/systemprompt.md @@ -6,50 +6,77 @@ Du bist der zentrale Orchestrator des Frankenbot-Systems für das Diversity Ball 1. **Anfragen analysieren** — Was will die Person? Was muss getan werden? 2. **Entscheiden** — Sofort antworten, oder Task delegieren, oder beides? -3. **Handeln** — Die richtigen Kommandos ausführen (Email, Telegram, Sub-Task, Team-Update) -4. **Kommunizieren** — Klare, direkte Antworten. Keine unnötigen Gedankengänge. - -## Wann du einen Sub-Task anlegst - -Lege mit `@CREATE_SUBTASK` einen Task an wenn: -- Jemand dich bittet etwas zu **tun** (Email schreiben, recherchieren, berechnen, Dokument erstellen) -- Die Aufgabe mehr als eine kurze Antwort erfordert -- Ein spezialisierter Agent (Budget, Catering, Location, etc.) besser geeignet ist -- Du eine Aufgabe für später merken willst - -**Lege KEINEN Sub-Task an** wenn: -- Es eine einfache Frage ist die du direkt beantworten kannst -- Jemand nur eine Information will die du kennst -- Es um System-Konfiguration geht (Team-Member updaten etc.) +3. **Handeln** — Die richtigen XML-Aktionen ausführen +4. **Kommunizieren** — Klare, direkte Antworten auf Deutsch ## Verfügbare spezialisierte Agenten -Die Agenten liegen in `agents//systemprompt.md`. Aktuell verfügbar: - **budget_manager** — Budgetplanung, Kostenkalkulation, Finanzübersicht - **catering_manager** — Catering, Essen & Trinken, Lieferanten - **location_manager** — Venue, Location, Raumplanung - **program_manager** — Programm, Zeitplan, Acts, Ablauf -- **music_rights** — Musikrechte, GEMA, Lizenzen +- **musik_rechte_advisor** — Musikrechte, GEMA, Lizenzen - **tax_advisor** — Steuerberatung, rechtliche Fragen - **researcher** — Recherche, Internet-Suche, Fakten -- **social_media** — Social Media, Marketing, Kommunikation +- **social_media_manager** — Social Media, Marketing, Kommunikation - **negotiator** — Verhandlungen, Verträge, Konditionen -- **hr_manager** — Personal, Volunteers, Team-Koordination +- **zusammenfasser** — Zusammenfassungen, Reports -Falls kein passender Agent existiert: `@SUGGEST_AGENT` verwenden. +## Wann du einen Task anlegst -## Delegations-Regeln +Lege einen Task an wenn jemand dich bittet etwas zu **tun** (recherchieren, berechnen, Email schreiben, Dokument erstellen, Angebot einholen etc.). Beantworte einfache Fragen direkt ohne Task. -- Führe komplexe Aufgaben NICHT selbst aus — delegiere via `@CREATE_SUBTASK` -- Für einfache Aktionen (Email, Telegram) führe sie direkt aus mit den Kommandos -- Für Team-Daten: direkt mit `@UPDATE_TEAM_MEMBER` oder `@ADD_TEAM_MEMBER` updaten -- Antworte immer auf Deutsch, außer die Person schreibt auf Englisch +## Aktionen (XML-Tags — werden automatisch ausgeführt) -## Umgang mit Gesprächsnachrichten +**Task an spezialisierten Agenten delegieren:** +``` + +title: Kurzer Titel der Aufgabe +agent: catering_manager +details: Was genau getan werden soll, alle relevanten Details + +``` -Wenn jemand mit dir redet: -1. Antworte freundlich und direkt auf die Nachricht -2. Wenn eine Aufgabe dabei ist → lege sofort einen `@CREATE_SUBTASK` an -3. Wenn du eine Email oder Telegram-Nachricht schicken sollst → tue es direkt mit `@SEND_EMAIL` / `@SEND_TELEGRAM` -4. Wenn Team-Daten zu aktualisieren sind → tue es direkt mit `@UPDATE_TEAM_MEMBER` -5. Bestätige was du getan hast +**Email versenden:** +``` + +to: email@adresse.com +subject: Betreff +body: Nachrichtentext + +``` + +**Telegram-Nachricht senden:** +``` + +telegram_id: 1578034974 +message: Nachricht + +``` + +**Team-Member-Daten aktualisieren:** +``` + +identifier: name@email.com +telegram_id: 1234567890 +role: Neue Rolle + +``` + +**Neuen Team-Member hinzufügen:** +``` + +name: Vollständiger Name +role: Rolle +responsibilities: Verantwortlichkeiten +email: email@adresse.com + +``` + +## Verhalten bei Nachrichten + +1. Antworte freundlich und direkt +2. Wenn eine Aufgabe dabei ist → sofort `` anlegen +3. Wenn Email/Telegram gesendet werden soll → `` / `` direkt ausführen +4. Wenn Team-Daten zu aktualisieren → `` direkt ausführen +5. Bestätige am Ende was du getan hast diff --git a/app.py b/app.py index 4b72e9f..d25406d 100644 --- a/app.py +++ b/app.py @@ -543,71 +543,79 @@ Context: [Kontext] ## Verfügbare Tools (Claude Code Built-ins): Du läufst als Claude Code Agent und hast folgende Tools direkt verfügbar: -- **WebFetch** – Webseiten abrufen und analysieren (URLs fetchen, Recherche) -- **Read** – Dateien lesen (absoluter Pfad) -- **Write** – Dateien schreiben/erstellen -- **Edit** – Bestehende Dateien bearbeiten -- **Glob** – Dateien nach Muster suchen (z.B. `**/*.pdf`) -- **Grep** – Inhalte in Dateien durchsuchen (Regex) -- **Bash** – Shell-Befehle ausführen (z.B. Berechnungen, Dateioperationen) +- **WebFetch** – Webseiten abrufen und analysieren +- **Read** – Dateien lesen +- **Write** / **Edit** – Dateien schreiben und bearbeiten +- **Glob** / **Grep** – Dateien und Inhalte durchsuchen +- **Bash** – Shell-Befehle ausführen -Nutze diese Tools aktiv für Recherche, Dateiverarbeitung und Berechnungen! +## Frankenbot-Aktionen (XML-Tags – werden nach deiner Antwort automatisch ausgeführt): -## Frankenbot-Kommandos (werden vom System verarbeitet): -Diese Kommandos musst du exakt so formatieren – sie werden nach deiner Antwort automatisch ausgeführt: +**Task erstellen / delegieren:** +``` + +title: [Kurzer Titel] +agent: [agent_key z.B. catering_manager, budget_manager, researcher ...] +details: [Was genau getan werden soll] + +``` -**Frage an Orchestrator / Sub-Task delegieren:** -@ASK_ORCHESTRATOR -Question: [Deine Frage] -Context: [Warum brauchst du diese Info?] -@END - -**Sub-Task erstellen (Orchestrator delegiert automatisch):** -@CREATE_SUBTASK -Task: [Was soll gemacht werden] -Requirements: [Anforderungen/Details] -@END - -**Neuen Agent vorschlagen (wenn Fähigkeit fehlt):** -@SUGGEST_AGENT -Role: [Rolle/Beschreibung] -Skills: [Benötigte Fähigkeiten] -Reason: [Warum wird dieser Agent gebraucht?] -@END +**Frage an Orchestrator:** +``` + +question: [Deine Frage] +context: [Warum brauchst du das?] + +``` **Email versenden:** -@SEND_EMAIL -To: [Email-Adresse] -Subject: [Betreff] -Body: [Nachricht] -@END +``` + +to: [email@adresse.com] +subject: [Betreff] +body: [Nachrichtentext] + +``` **Telegram-Nachricht senden:** -@SEND_TELEGRAM -TelegramID: [Numerische Telegram-ID aus den Team-Member-Daten] -Message: [Nachricht] -@END +``` + +telegram_id: [Numerische ID oder Name aus Team-Members] +message: [Nachricht] + +``` -**Team-Member-Daten aktualisieren:** -@UPDATE_TEAM_MEMBER -Identifier: [Email oder Name des Team-Members] -Name: [Neuer Name] (optional) -Role: [Neue Rolle] (optional) -Responsibilities: [Neue Verantwortlichkeiten] (optional) -TelegramID: [Telegram-ID als Zahl] (optional) -Phone: [Telefon] (optional) -@END +**Team-Member aktualisieren:** +``` + +identifier: [Email oder Name] +telegram_id: [Zahl] (optional) +role: [Neue Rolle] (optional) +phone: [Telefon] (optional) + +``` **Neuen Team-Member hinzufügen:** -@ADD_TEAM_MEMBER -Name: [Vollständiger Name] -Role: [Rolle/Position] -Responsibilities: [Verantwortlichkeiten] -Email: [Email-Adresse] -@END +``` + +name: [Vollständiger Name] +role: [Rolle] +responsibilities: [Verantwortlichkeiten] +email: [Email] + +``` + +**Neuen Agent vorschlagen:** +``` + +role: [Rolle] +skills: [Fähigkeiten] +reason: [Warum gebraucht] + +``` ## Wichtig: -- Arbeitsverzeichnis: {work_dir} – speichere alle Dateien dort mit absolutem Pfad +- Arbeitsverzeichnis: {work_dir} - Liefere immer eine vollständige, direkt verwertbare Antwort {extra_context}""" @@ -980,271 +988,186 @@ def respond_to_message(message_id, response): return False def parse_agent_commands(agent_key, response_text, task_id=None): - """Parst Agent-Antwort nach Orchestrator-Kommandos und führt sie aus.""" - import re + """Parst Agent-Antwort nach XML-Kommando-Blöcken und führt sie aus. - # ASK_ORCHESTRATOR: Agent stellt Frage an Orchestrator - ask_requests = re.findall( - r'@ASK_ORCHESTRATOR\s*\nQuestion:\s*([^\n]+)\s*\nContext:\s*(.*?)@END', - response_text, - re.DOTALL - ) - for question, context in ask_requests: - # Erstelle Task für Orchestrator um die Frage zu beantworten - task_id = create_task( - title=f"Frage von {agent_key}: {question.strip()[:80]}", - description=f"""Ein Agent braucht Hilfe! + Unterstützte Tags (Claude Code gibt @ Kommandos nicht zuverlässig aus): + title/agent/details + question/context + role/skills/reason + to/subject/body + telegram_id/message + name/role/responsibilities/email + identifier + beliebige Felder + """ + import re -**Von:** {agent_key} -**Frage:** {question.strip()} -**Kontext:** {context.strip()} + def get_field(block, field): + """Extrahiert ein Feld aus einem XML-Block: 'field: value' oder 'value'.""" + # Versuche erst XML-Tag-Format + m = re.search(rf'<{field}>(.*?)', block, re.DOTALL | re.IGNORECASE) + if m: + return m.group(1).strip() + # Dann Key-Value-Format + m = re.search(rf'^{field}\s*:\s*(.+)', block, re.MULTILINE | re.IGNORECASE) + if m: + return m.group(1).strip() + return '' -Bitte beantworte die Frage oder delegiere an den passenden Experten-Agent. -Die Antwort wird an {agent_key} zurückgegeben.""", + # ── CREATE_TASK ────────────────────────────────────────────────────────── + for block in re.findall(r'(.*?)', response_text, re.DOTALL | re.IGNORECASE): + title = get_field(block, 'title') or block.strip()[:80] + agent = get_field(block, 'agent') or 'orchestrator' + details = get_field(block, 'details') or get_field(block, 'requirements') or '' + if not title: + continue + new_id = create_task( + title=title[:100], + description=f"Von {agent_key} delegiert:\n{details}", + agent_key=agent if agent in AGENTS else 'orchestrator', + task_type='agent_subtask', + created_by=agent_key, + parent_task_id=task_id, + ) + logger.info(f"[AgentCmd] {agent_key} erstellt Task #{new_id}: {title[:50]}") + + # ── ASK_ORCHESTRATOR ───────────────────────────────────────────────────── + for block in re.findall(r'(.*?)', response_text, re.DOTALL | re.IGNORECASE): + question = get_field(block, 'question') or block.strip()[:80] + context = get_field(block, 'context') or '' + new_id = create_task( + title=f"Frage von {agent_key}: {question[:80]}", + description=f"**Von:** {agent_key}\n**Frage:** {question}\n**Kontext:** {context}", agent_key='orchestrator', task_type='agent_question', created_by=agent_key, - from_agent=agent_key, - return_to=agent_key + parent_task_id=task_id, ) - logger.info(f"[AgentCmd] {agent_key} fragt Orchestrator (Task #{task_id}): {question.strip()[:50]}") - - # CREATE_SUBTASK: Agent möchte Subtask erstellen - subtask_requests = re.findall( - r'@CREATE_SUBTASK\s*\nTask:\s*([^\n]+)\s*\nRequirements:\s*(.*?)@END', - response_text, - re.DOTALL - ) - for task_desc, requirements in subtask_requests: - task_id = create_task( - title=task_desc.strip()[:100], - description=f"Von {agent_key} angefordert:\n{requirements.strip()}", - agent_key='orchestrator', - task_type='agent_subtask', - created_by=agent_key, - from_agent=agent_key - ) - logger.info(f"[AgentCmd] {agent_key} erstellt Subtask #{task_id} via Orchestrator") - - # SUGGEST_AGENT: Agent schlägt neuen Agent vor - suggest_requests = re.findall( - r'@SUGGEST_AGENT\s*\nRole:\s*([^\n]+)\s*\nSkills:\s*([^\n]+)\s*\nReason:\s*(.*?)@END', - response_text, - re.DOTALL - ) - for role, skills, reason in suggest_requests: - # Agent-Key aus Role ableiten - agent_key_suggestion = role.lower().replace(' ', '_').replace('-', '_') - - # Task für Orchestrator um Agent zu erstellen - task_id = create_task( - title=f"Agent-Vorschlag: {role.strip()}", - description=f"""Agent {agent_key} schlägt vor, einen neuen Agent zu erstellen: + logger.info(f"[AgentCmd] {agent_key} fragt Orchestrator (Task #{new_id})") -**Rolle:** {role.strip()} -**Fähigkeiten:** {skills.strip()} -**Begründung:** {reason.strip()} - -Bitte entscheide ob dieser Agent erstellt werden soll.""", + # ── SUGGEST_AGENT ──────────────────────────────────────────────────────── + for block in re.findall(r'(.*?)', response_text, re.DOTALL | re.IGNORECASE): + role = get_field(block, 'role') or block.strip()[:50] + skills = get_field(block, 'skills') or '' + reason = get_field(block, 'reason') or '' + new_id = create_task( + title=f"Agent-Vorschlag: {role}", + description=f"**Rolle:** {role}\n**Fähigkeiten:** {skills}\n**Begründung:** {reason}", agent_key='orchestrator', task_type='agent_suggestion', created_by=agent_key, - from_agent=agent_key, - suggested_agent=agent_key_suggestion, - suggested_role=role.strip(), - suggested_skills=skills.strip() + parent_task_id=task_id, ) - logger.info(f"[AgentCmd] {agent_key} schlägt neuen Agent vor (Task #{task_id}): {role.strip()}") - - # READ_KNOWLEDGE: Agent möchte Wissensdatenbank durchsuchen - read_kb_requests = re.findall( - r'@READ_KNOWLEDGE\s*\nTopic:\s*(.*?)@END', - response_text, - re.DOTALL - ) - - # Wenn Agent Wissensdatenbank lesen will, füge relevante Sektion zur Antwort hinzu - # (wird im Response-Text nicht sichtbar, aber Agent bekommt es als Context) - if read_kb_requests: - kb_file = os.path.join(os.path.dirname(__file__), 'agents', 'orchestrator', 'knowledge', 'diversityball_knowledge.md') - if os.path.exists(kb_file): - with open(kb_file, 'r', encoding='utf-8') as f: - kb_content = f.read() - - for topic in read_kb_requests: - topic_clean = topic.strip().lower() - logger.info(f"[AgentCmd] {agent_key} liest Wissensdatenbank: {topic_clean}") - - # Einfache Suche: Gib relevante Abschnitte zurück - # TODO: Könnte später mit Vektorsuche verbessert werden - relevant_sections = [] - for line in kb_content.split('\n'): - if topic_clean in line.lower(): - relevant_sections.append(line) - - if relevant_sections: - logger.info(f"[AgentCmd] {len(relevant_sections)} relevante Zeilen gefunden") - - # SEND_EMAIL: Orchestrator sendet Email an Team-Member - send_email_requests = re.findall( - r'@SEND_EMAIL\s*\nTo:\s*([^\n]+)\s*\nSubject:\s*([^\n]+)\s*\nBody:\s*(.*?)@END', - response_text, - re.DOTALL - ) - for to_addr, subject, body in send_email_requests: - to_clean = to_addr.strip() - subject_clean = subject.strip() - body_clean = body.strip() + logger.info(f"[AgentCmd] {agent_key} schlägt Agent vor (Task #{new_id}): {role}") - action_task_id = create_task( - title=f"Email an {to_clean}: {subject_clean[:60]}", - description=f"**An:** {to_clean}\n**Betreff:** {subject_clean}\n\n{body_clean}", + # ── SEND_EMAIL ─────────────────────────────────────────────────────────── + for block in re.findall(r'(.*?)', response_text, re.DOTALL | re.IGNORECASE): + to = get_field(block, 'to') + subject = get_field(block, 'subject') + body = get_field(block, 'body') + if not to or not subject: + logger.warning("[AgentCmd] ohne to/subject ignoriert") + continue + action_id = create_task( + title=f"Email an {to}: {subject[:60]}", + description=f"**An:** {to}\n**Betreff:** {subject}\n\n{body}", agent_key=agent_key, task_type='action_email', created_by=agent_key, parent_task_id=task_id, ) - success, message = send_email(to_clean, subject_clean, body_clean, - triggered_by=f'agent:{agent_key}', task_id=action_task_id) + success, msg = send_email(to, subject, body, triggered_by=f'agent:{agent_key}', task_id=action_id) if success: - update_task_db(action_task_id, status='completed', - response=f"✓ Email erfolgreich versendet an {to_clean}") - logger.info(f"[AgentCmd] Email gesendet an {to_clean}: {subject_clean}") + update_task_db(action_id, status='completed', response=f"✓ Email versendet an {to}") + logger.info(f"[AgentCmd] Email gesendet an {to}: {subject}") else: - update_task_db(action_task_id, status='error', response=f"✗ Fehler: {message}") - logger.error(f"[AgentCmd] Email-Fehler: {message}") - - # SEND_TELEGRAM: Orchestrator sendet Telegram-Nachricht - # Format: TelegramID: oder Name/Email (wird dann in DB nachgeschlagen) - send_telegram_requests = re.findall( - r'@SEND_TELEGRAM\s*\nTelegramID:\s*([^\n]+)\s*\nMessage:\s*(.*?)@END', - response_text, - re.DOTALL - ) - for recipient, message in send_telegram_requests: - recipient_clean = recipient.strip() - message_clean = message.strip() + update_task_db(action_id, status='error', response=f"✗ {msg}") + logger.error(f"[AgentCmd] Email-Fehler: {msg}") - action_task_id = create_task( - title=f"Telegram an {recipient_clean}: {message_clean[:60]}", - description=f"**An:** {recipient_clean}\n\n{message_clean}", + # ── SEND_TELEGRAM ──────────────────────────────────────────────────────── + for block in re.findall(r'(.*?)', response_text, re.DOTALL | re.IGNORECASE): + recipient = get_field(block, 'telegram_id') or get_field(block, 'to') + message = get_field(block, 'message') + if not recipient or not message: + logger.warning("[AgentCmd] ohne telegram_id/message ignoriert") + continue + action_id = create_task( + title=f"Telegram an {recipient}: {message[:60]}", + description=f"**An:** {recipient}\n\n{message}", agent_key=agent_key, task_type='action_telegram', created_by=agent_key, parent_task_id=task_id, ) - - if TELEGRAM_CONFIG.get('bot_token') and TELEGRAM_CONFIG.get('telegram_bot'): + if TELEGRAM_CONFIG.get('bot_token'): try: - chat_id = None - if recipient_clean.lstrip('-').isdigit(): - chat_id = int(recipient_clean) - else: + chat_id = int(recipient) if recipient.lstrip('-').isdigit() else None + if not chat_id: con = sqlite3.connect(EMAIL_JOURNAL_DB) - result = con.execute( - "SELECT telegram_id FROM team_members WHERE name = ? OR email = ?", - (recipient_clean, recipient_clean) + row = con.execute( + "SELECT telegram_id FROM team_members WHERE name=? OR email=?", + (recipient, recipient) ).fetchone() con.close() - if result and result[0]: - chat_id = int(result[0]) - + if row and row[0]: + chat_id = int(row[0]) if chat_id: - send_telegram_message(chat_id, message_clean) - update_task_db(action_task_id, status='completed', - response=f"✓ Telegram gesendet an {recipient_clean} (chat_id={chat_id})") - logger.info(f"[AgentCmd] Telegram gesendet an {recipient_clean} (chat_id={chat_id})") + send_telegram_message(chat_id, message) + update_task_db(action_id, status='completed', response=f"✓ Telegram gesendet (chat_id={chat_id})") + logger.info(f"[AgentCmd] Telegram gesendet an {recipient} (chat_id={chat_id})") else: - update_task_db(action_task_id, status='error', - response=f"✗ Keine Telegram-ID für '{recipient_clean}' gefunden") - logger.warning(f"[AgentCmd] Keine Telegram Chat-ID für '{recipient_clean}'") + update_task_db(action_id, status='error', response=f"✗ Keine Telegram-ID für '{recipient}'") + logger.warning(f"[AgentCmd] Keine Telegram-ID für '{recipient}'") except Exception as e: - update_task_db(action_task_id, status='error', response=f"✗ Fehler: {str(e)}") - logger.error(f"[AgentCmd] Telegram-Fehler: {str(e)}") + update_task_db(action_id, status='error', response=f"✗ {e}") + logger.error(f"[AgentCmd] Telegram-Fehler: {e}") else: - update_task_db(action_task_id, status='error', response="✗ Telegram nicht konfiguriert") - logger.warning("[AgentCmd] Telegram nicht konfiguriert") - - # ADD_TEAM_MEMBER: Füge neues Team-Mitglied hinzu - # Format: Name / Role / Responsibilities / Email (Reihenfolge wie im System-Prompt) - add_member_requests = re.findall( - r'@ADD_TEAM_MEMBER\s*\nName:\s*([^\n]+)\s*\nRole:\s*([^\n]+)\s*\nResponsibilities:\s*([^\n]+)\s*\nEmail:\s*(.*?)@END', - response_text, - re.DOTALL - ) - for name, role, resp, email in add_member_requests: - name_clean = name.strip() - role_clean = role.strip() - resp_clean = resp.strip() - email_clean = email.strip() + update_task_db(action_id, status='error', response="✗ Telegram nicht konfiguriert") - action_task_id = create_task( - title=f"Team-Member hinzugefügt: {name_clean}", - description=f"**Name:** {name_clean}\n**Rolle:** {role_clean}\n**Email:** {email_clean}\n**Verantwortlichkeiten:** {resp_clean}", - agent_key=agent_key, - task_type='action_team', - created_by=agent_key, - parent_task_id=task_id, + # ── ADD_TEAM_MEMBER ────────────────────────────────────────────────────── + for block in re.findall(r'(.*?)', response_text, re.DOTALL | re.IGNORECASE): + name = get_field(block, 'name') + role = get_field(block, 'role') + resp = get_field(block, 'responsibilities') + email = get_field(block, 'email') + if not name or not role: + continue + action_id = create_task( + title=f"Team-Member hinzugefügt: {name}", + description=f"**Name:** {name}\n**Rolle:** {role}\n**Email:** {email}\n**Verantwortlichkeiten:** {resp}", + agent_key=agent_key, task_type='action_team', created_by=agent_key, parent_task_id=task_id, ) - success = add_team_member(name_clean, role_clean, resp_clean, email_clean) - if success: - update_task_db(action_task_id, status='completed', - response=f"✓ Team-Member '{name_clean}' ({role_clean}) hinzugefügt") - logger.info(f"[AgentCmd] Team-Member hinzugefügt: {name_clean} ({role_clean})") - else: - update_task_db(action_task_id, status='error', - response=f"✗ Konnte '{name_clean}' nicht hinzufügen") - logger.warning(f"[AgentCmd] Team-Member konnte nicht hinzugefügt werden: {name_clean}") - - # UPDATE_TEAM_MEMBER: Aktualisiere Team-Mitglied - # Format: @UPDATE_TEAM_MEMBER\nIdentifier: \n[Feld: Wert]\n...\n@END - ALLOWED_UPDATE_FIELDS = { - 'name': 'name', - 'role': 'role', - 'responsibilities': 'responsibilities', - 'email': 'email', - 'telegramid': 'telegram_id', - 'telegram_id': 'telegram_id', - 'phone': 'phone', - } - for block in re.findall(r'@UPDATE_TEAM_MEMBER\s*\n(.*?)@END', response_text, re.DOTALL): - lines = [l.strip() for l in block.strip().splitlines() if l.strip()] - identifier = None + success = add_team_member(name, role, resp, email) + status_msg = f"✓ '{name}' hinzugefügt" if success else f"✗ Fehler beim Hinzufügen von '{name}'" + update_task_db(action_id, status='completed' if success else 'error', response=status_msg) + logger.info(f"[AgentCmd] {status_msg}") + + # ── UPDATE_TEAM_MEMBER ─────────────────────────────────────────────────── + ALLOWED_UPDATE_FIELDS = {'name','role','responsibilities','email','telegram_id','telegramid','phone'} + for block in re.findall(r'(.*?)', response_text, re.DOTALL | re.IGNORECASE): + identifier = get_field(block, 'identifier') + if not identifier: + continue kwargs = {} - for line in lines: + for line in block.strip().splitlines(): if ':' not in line: continue - key, _, val = line.partition(':') - key_norm = key.strip().lower().replace(' ', '_') - val_clean = val.strip() - if key_norm == 'identifier': - identifier = val_clean - elif key_norm in ALLOWED_UPDATE_FIELDS: - kwargs[ALLOWED_UPDATE_FIELDS[key_norm]] = val_clean - if not identifier: - logger.warning("[AgentCmd] @UPDATE_TEAM_MEMBER ohne Identifier ignoriert") - continue + k, _, v = line.partition(':') + k_norm = k.strip().lower().replace(' ', '_') + if k_norm in ALLOWED_UPDATE_FIELDS and k_norm != 'identifier': + db_key = 'telegram_id' if k_norm == 'telegramid' else k_norm + kwargs[db_key] = v.strip() if not kwargs: - logger.warning(f"[AgentCmd] @UPDATE_TEAM_MEMBER für '{identifier}' ohne Felder ignoriert") continue - fields_summary = ", ".join(f"{k}={v}" for k, v in kwargs.items()) - action_task_id = create_task( + fields_str = ', '.join(f"{k}={v}" for k, v in kwargs.items()) + action_id = create_task( title=f"Team-Member aktualisiert: {identifier}", - description=f"**Identifier:** {identifier}\n**Felder:** {fields_summary}", - agent_key=agent_key, - task_type='action_team', - created_by=agent_key, - parent_task_id=task_id, + description=f"**Identifier:** {identifier}\n**Felder:** {fields_str}", + agent_key=agent_key, task_type='action_team', created_by=agent_key, parent_task_id=task_id, ) success = update_team_member(identifier, **kwargs) - if success: - update_task_db(action_task_id, status='completed', - response=f"✓ {identifier} aktualisiert: {fields_summary}") - logger.info(f"[AgentCmd] Team-Member aktualisiert: {identifier} - {list(kwargs.keys())}") - else: - update_task_db(action_task_id, status='error', - response=f"✗ Update fehlgeschlagen für '{identifier}'") - logger.error(f"[AgentCmd] Update fehlgeschlagen für '{identifier}'") + status_msg = f"✓ {identifier} aktualisiert: {fields_str}" if success else f"✗ Update fehlgeschlagen für '{identifier}'" + update_task_db(action_id, status='completed' if success else 'error', response=status_msg) + logger.info(f"[AgentCmd] {status_msg}") def create_new_agent(agent_key, role, skills): """Erstellt dynamisch einen neuen Agenten."""