fix: Switch agent commands from @-syntax to XML tags — Claude Code refuses to output @-directives

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 (<create_task>, <send_email>, 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 -> <ask_orchestrator>
This commit is contained in:
eric 2026-02-21 19:21:01 +00:00
parent 54cc5a496f
commit c6ce8a873c
3 changed files with 260 additions and 310 deletions

View file

@ -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
<ask_orchestrator>
question: Agent [name] sollte gelöscht werden.
context: [Performance-Details und Begründung]
</ask_orchestrator>
```
**Status-Report anfordern:**

View file

@ -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/<name>/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:**
```
<create_task>
title: Kurzer Titel der Aufgabe
agent: catering_manager
details: Was genau getan werden soll, alle relevanten Details
</create_task>
```
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:**
```
<send_email>
to: email@adresse.com
subject: Betreff
body: Nachrichtentext
</send_email>
```
**Telegram-Nachricht senden:**
```
<send_telegram>
telegram_id: 1578034974
message: Nachricht
</send_telegram>
```
**Team-Member-Daten aktualisieren:**
```
<update_team_member>
identifier: name@email.com
telegram_id: 1234567890
role: Neue Rolle
</update_team_member>
```
**Neuen Team-Member hinzufügen:**
```
<add_team_member>
name: Vollständiger Name
role: Rolle
responsibilities: Verantwortlichkeiten
email: email@adresse.com
</add_team_member>
```
## Verhalten bei Nachrichten
1. Antworte freundlich und direkt
2. Wenn eine Aufgabe dabei ist → sofort `<create_task>` anlegen
3. Wenn Email/Telegram gesendet werden soll → `<send_email>` / `<send_telegram>` direkt ausführen
4. Wenn Team-Daten zu aktualisieren → `<update_team_member>` direkt ausführen
5. Bestätige am Ende was du getan hast

467
app.py
View file

@ -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:**
```
<create_task>
title: [Kurzer Titel]
agent: [agent_key z.B. catering_manager, budget_manager, researcher ...]
details: [Was genau getan werden soll]
</create_task>
```
**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:**
```
<ask_orchestrator>
question: [Deine Frage]
context: [Warum brauchst du das?]
</ask_orchestrator>
```
**Email versenden:**
@SEND_EMAIL
To: [Email-Adresse]
Subject: [Betreff]
Body: [Nachricht]
@END
```
<send_email>
to: [email@adresse.com]
subject: [Betreff]
body: [Nachrichtentext]
</send_email>
```
**Telegram-Nachricht senden:**
@SEND_TELEGRAM
TelegramID: [Numerische Telegram-ID aus den Team-Member-Daten]
Message: [Nachricht]
@END
```
<send_telegram>
telegram_id: [Numerische ID oder Name aus Team-Members]
message: [Nachricht]
</send_telegram>
```
**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:**
```
<update_team_member>
identifier: [Email oder Name]
telegram_id: [Zahl] (optional)
role: [Neue Rolle] (optional)
phone: [Telefon] (optional)
</update_team_member>
```
**Neuen Team-Member hinzufügen:**
@ADD_TEAM_MEMBER
Name: [Vollständiger Name]
Role: [Rolle/Position]
Responsibilities: [Verantwortlichkeiten]
Email: [Email-Adresse]
@END
```
<add_team_member>
name: [Vollständiger Name]
role: [Rolle]
responsibilities: [Verantwortlichkeiten]
email: [Email]
</add_team_member>
```
**Neuen Agent vorschlagen:**
```
<suggest_agent>
role: [Rolle]
skills: [Fähigkeiten]
reason: [Warum gebraucht]
</suggest_agent>
```
## 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."""
"""Parst Agent-Antwort nach XML-Kommando-Blöcken und führt sie aus.
Unterstützte Tags (Claude Code gibt @ Kommandos nicht zuverlässig aus):
<create_task>title/agent/details</create_task>
<ask_orchestrator>question/context</ask_orchestrator>
<suggest_agent>role/skills/reason</suggest_agent>
<send_email>to/subject/body</send_email>
<send_telegram>telegram_id/message</send_telegram>
<add_team_member>name/role/responsibilities/email</add_team_member>
<update_team_member>identifier + beliebige Felder</update_team_member>
"""
import re
# 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!
def get_field(block, field):
"""Extrahiert ein Feld aus einem XML-Block: 'field: value' oder '<field>value</field>'."""
# Versuche erst XML-Tag-Format
m = re.search(rf'<{field}>(.*?)</{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 ''
**Von:** {agent_key}
**Frage:** {question.strip()}
**Kontext:** {context.strip()}
# ── CREATE_TASK ──────────────────────────────────────────────────────────
for block in re.findall(r'<create_task>(.*?)</create_task>', 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]}")
Bitte beantworte die Frage oder delegiere an den passenden Experten-Agent.
Die Antwort wird an {agent_key} zurückgegeben.""",
# ── ASK_ORCHESTRATOR ─────────────────────────────────────────────────────
for block in re.findall(r'<ask_orchestrator>(.*?)</ask_orchestrator>', 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]}")
logger.info(f"[AgentCmd] {agent_key} fragt Orchestrator (Task #{new_id})")
# 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:
**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'<suggest_agent>(.*?)</suggest_agent>', 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()}")
logger.info(f"[AgentCmd] {agent_key} schlägt Agent vor (Task #{new_id}): {role}")
# 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()
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'<send_email>(.*?)</send_email>', 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] <send_email> 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}")
update_task_db(action_id, status='error', response=f"{msg}")
logger.error(f"[AgentCmd] Email-Fehler: {msg}")
# SEND_TELEGRAM: Orchestrator sendet Telegram-Nachricht
# Format: TelegramID: <numerische ID> 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()
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'<send_telegram>(.*?)</send_telegram>', 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] <send_telegram> 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")
update_task_db(action_id, status='error', response="✗ 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()
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'<add_team_member>(.*?)</add_team_member>', 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}")
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: Aktualisiere Team-Mitglied
# Format: @UPDATE_TEAM_MEMBER\nIdentifier: <email oder name>\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
# ── UPDATE_TEAM_MEMBER ───────────────────────────────────────────────────
ALLOWED_UPDATE_FIELDS = {'name','role','responsibilities','email','telegram_id','telegramid','phone'}
for block in re.findall(r'<update_team_member>(.*?)</update_team_member>', 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."""