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:** **Empfehlung zum Löschen:**
``` ```
@ASK_ORCHESTRATOR <ask_orchestrator>
Question: Agent [name] sollte gelöscht werden. question: Agent [name] sollte gelöscht werden.
Context: [Performance-Details und Begründung] context: [Performance-Details und Begründung]
@END </ask_orchestrator>
``` ```
**Status-Report anfordern:** **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? 1. **Anfragen analysieren** — Was will die Person? Was muss getan werden?
2. **Entscheiden** — Sofort antworten, oder Task delegieren, oder beides? 2. **Entscheiden** — Sofort antworten, oder Task delegieren, oder beides?
3. **Handeln** — Die richtigen Kommandos ausführen (Email, Telegram, Sub-Task, Team-Update) 3. **Handeln** — Die richtigen XML-Aktionen ausführen
4. **Kommunizieren** — Klare, direkte Antworten. Keine unnötigen Gedankengänge. 4. **Kommunizieren** — Klare, direkte Antworten auf Deutsch
## 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.)
## Verfügbare spezialisierte Agenten ## Verfügbare spezialisierte Agenten
Die Agenten liegen in `agents/<name>/systemprompt.md`. Aktuell verfügbar:
- **budget_manager** — Budgetplanung, Kostenkalkulation, Finanzübersicht - **budget_manager** — Budgetplanung, Kostenkalkulation, Finanzübersicht
- **catering_manager** — Catering, Essen & Trinken, Lieferanten - **catering_manager** — Catering, Essen & Trinken, Lieferanten
- **location_manager** — Venue, Location, Raumplanung - **location_manager** — Venue, Location, Raumplanung
- **program_manager** — Programm, Zeitplan, Acts, Ablauf - **program_manager** — Programm, Zeitplan, Acts, Ablauf
- **music_rights** — Musikrechte, GEMA, Lizenzen - **musik_rechte_advisor** — Musikrechte, GEMA, Lizenzen
- **tax_advisor** — Steuerberatung, rechtliche Fragen - **tax_advisor** — Steuerberatung, rechtliche Fragen
- **researcher** — Recherche, Internet-Suche, Fakten - **researcher** — Recherche, Internet-Suche, Fakten
- **social_media** — Social Media, Marketing, Kommunikation - **social_media_manager** — Social Media, Marketing, Kommunikation
- **negotiator** — Verhandlungen, Verträge, Konditionen - **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` ## Aktionen (XML-Tags — werden automatisch ausgeführt)
- 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
## 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: **Email versenden:**
1. Antworte freundlich und direkt auf die Nachricht ```
2. Wenn eine Aufgabe dabei ist → lege sofort einen `@CREATE_SUBTASK` an <send_email>
3. Wenn du eine Email oder Telegram-Nachricht schicken sollst → tue es direkt mit `@SEND_EMAIL` / `@SEND_TELEGRAM` to: email@adresse.com
4. Wenn Team-Daten zu aktualisieren sind → tue es direkt mit `@UPDATE_TEAM_MEMBER` subject: Betreff
5. Bestätige was du getan hast 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

465
app.py
View file

@ -543,71 +543,79 @@ Context: [Kontext]
## Verfügbare Tools (Claude Code Built-ins): ## Verfügbare Tools (Claude Code Built-ins):
Du läufst als Claude Code Agent und hast folgende Tools direkt verfügbar: Du läufst als Claude Code Agent und hast folgende Tools direkt verfügbar:
- **WebFetch** Webseiten abrufen und analysieren (URLs fetchen, Recherche) - **WebFetch** Webseiten abrufen und analysieren
- **Read** Dateien lesen (absoluter Pfad) - **Read** Dateien lesen
- **Write** Dateien schreiben/erstellen - **Write** / **Edit** Dateien schreiben und bearbeiten
- **Edit** Bestehende Dateien bearbeiten - **Glob** / **Grep** Dateien und Inhalte durchsuchen
- **Glob** Dateien nach Muster suchen (z.B. `**/*.pdf`) - **Bash** Shell-Befehle ausführen
- **Grep** Inhalte in Dateien durchsuchen (Regex)
- **Bash** Shell-Befehle ausführen (z.B. Berechnungen, Dateioperationen)
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): **Task erstellen / delegieren:**
Diese Kommandos musst du exakt so formatieren sie werden nach deiner Antwort automatisch ausgeführt: ```
<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:** **Frage an Orchestrator:**
@ASK_ORCHESTRATOR ```
Question: [Deine Frage] <ask_orchestrator>
Context: [Warum brauchst du diese Info?] question: [Deine Frage]
@END context: [Warum brauchst du das?]
</ask_orchestrator>
**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
**Email versenden:** **Email versenden:**
@SEND_EMAIL ```
To: [Email-Adresse] <send_email>
Subject: [Betreff] to: [email@adresse.com]
Body: [Nachricht] subject: [Betreff]
@END body: [Nachrichtentext]
</send_email>
```
**Telegram-Nachricht senden:** **Telegram-Nachricht senden:**
@SEND_TELEGRAM ```
TelegramID: [Numerische Telegram-ID aus den Team-Member-Daten] <send_telegram>
Message: [Nachricht] telegram_id: [Numerische ID oder Name aus Team-Members]
@END message: [Nachricht]
</send_telegram>
```
**Team-Member-Daten aktualisieren:** **Team-Member aktualisieren:**
@UPDATE_TEAM_MEMBER ```
Identifier: [Email oder Name des Team-Members] <update_team_member>
Name: [Neuer Name] (optional) identifier: [Email oder Name]
Role: [Neue Rolle] (optional) telegram_id: [Zahl] (optional)
Responsibilities: [Neue Verantwortlichkeiten] (optional) role: [Neue Rolle] (optional)
TelegramID: [Telegram-ID als Zahl] (optional) phone: [Telefon] (optional)
Phone: [Telefon] (optional) </update_team_member>
@END ```
**Neuen Team-Member hinzufügen:** **Neuen Team-Member hinzufügen:**
@ADD_TEAM_MEMBER ```
Name: [Vollständiger Name] <add_team_member>
Role: [Rolle/Position] name: [Vollständiger Name]
Responsibilities: [Verantwortlichkeiten] role: [Rolle]
Email: [Email-Adresse] responsibilities: [Verantwortlichkeiten]
@END email: [Email]
</add_team_member>
```
**Neuen Agent vorschlagen:**
```
<suggest_agent>
role: [Rolle]
skills: [Fähigkeiten]
reason: [Warum gebraucht]
</suggest_agent>
```
## Wichtig: ## Wichtig:
- Arbeitsverzeichnis: {work_dir} speichere alle Dateien dort mit absolutem Pfad - Arbeitsverzeichnis: {work_dir}
- Liefere immer eine vollständige, direkt verwertbare Antwort - Liefere immer eine vollständige, direkt verwertbare Antwort
{extra_context}""" {extra_context}"""
@ -980,271 +988,186 @@ def respond_to_message(message_id, response):
return False return False
def parse_agent_commands(agent_key, response_text, task_id=None): 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 import re
# ASK_ORCHESTRATOR: Agent stellt Frage an Orchestrator def get_field(block, field):
ask_requests = re.findall( """Extrahiert ein Feld aus einem XML-Block: 'field: value' oder '<field>value</field>'."""
r'@ASK_ORCHESTRATOR\s*\nQuestion:\s*([^\n]+)\s*\nContext:\s*(.*?)@END', # Versuche erst XML-Tag-Format
response_text, m = re.search(rf'<{field}>(.*?)</{field}>', block, re.DOTALL | re.IGNORECASE)
re.DOTALL 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 ''
# ── 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,
) )
for question, context in ask_requests: logger.info(f"[AgentCmd] {agent_key} erstellt Task #{new_id}: {title[:50]}")
# 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!
**Von:** {agent_key} # ── ASK_ORCHESTRATOR ─────────────────────────────────────────────────────
**Frage:** {question.strip()} for block in re.findall(r'<ask_orchestrator>(.*?)</ask_orchestrator>', response_text, re.DOTALL | re.IGNORECASE):
**Kontext:** {context.strip()} question = get_field(block, 'question') or block.strip()[:80]
context = get_field(block, 'context') or ''
Bitte beantworte die Frage oder delegiere an den passenden Experten-Agent. new_id = create_task(
Die Antwort wird an {agent_key} zurückgegeben.""", title=f"Frage von {agent_key}: {question[:80]}",
description=f"**Von:** {agent_key}\n**Frage:** {question}\n**Kontext:** {context}",
agent_key='orchestrator', agent_key='orchestrator',
task_type='agent_question', task_type='agent_question',
created_by=agent_key, created_by=agent_key,
from_agent=agent_key, parent_task_id=task_id,
return_to=agent_key
) )
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 # ── SUGGEST_AGENT ────────────────────────────────────────────────────────
subtask_requests = re.findall( for block in re.findall(r'<suggest_agent>(.*?)</suggest_agent>', response_text, re.DOTALL | re.IGNORECASE):
r'@CREATE_SUBTASK\s*\nTask:\s*([^\n]+)\s*\nRequirements:\s*(.*?)@END', role = get_field(block, 'role') or block.strip()[:50]
response_text, skills = get_field(block, 'skills') or ''
re.DOTALL reason = get_field(block, 'reason') or ''
) new_id = create_task(
for task_desc, requirements in subtask_requests: title=f"Agent-Vorschlag: {role}",
task_id = create_task( description=f"**Rolle:** {role}\n**Fähigkeiten:** {skills}\n**Begründung:** {reason}",
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.""",
agent_key='orchestrator', agent_key='orchestrator',
task_type='agent_suggestion', task_type='agent_suggestion',
created_by=agent_key, created_by=agent_key,
from_agent=agent_key, parent_task_id=task_id,
suggested_agent=agent_key_suggestion,
suggested_role=role.strip(),
suggested_skills=skills.strip()
) )
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 # ── SEND_EMAIL ───────────────────────────────────────────────────────────
read_kb_requests = re.findall( for block in re.findall(r'<send_email>(.*?)</send_email>', response_text, re.DOTALL | re.IGNORECASE):
r'@READ_KNOWLEDGE\s*\nTopic:\s*(.*?)@END', to = get_field(block, 'to')
response_text, subject = get_field(block, 'subject')
re.DOTALL body = get_field(block, 'body')
) if not to or not subject:
logger.warning("[AgentCmd] <send_email> ohne to/subject ignoriert")
# Wenn Agent Wissensdatenbank lesen will, füge relevante Sektion zur Antwort hinzu continue
# (wird im Response-Text nicht sichtbar, aber Agent bekommt es als Context) action_id = create_task(
if read_kb_requests: title=f"Email an {to}: {subject[:60]}",
kb_file = os.path.join(os.path.dirname(__file__), 'agents', 'orchestrator', 'knowledge', 'diversityball_knowledge.md') description=f"**An:** {to}\n**Betreff:** {subject}\n\n{body}",
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}",
agent_key=agent_key, agent_key=agent_key,
task_type='action_email', task_type='action_email',
created_by=agent_key, created_by=agent_key,
parent_task_id=task_id, parent_task_id=task_id,
) )
success, message = send_email(to_clean, subject_clean, body_clean, success, msg = send_email(to, subject, body, triggered_by=f'agent:{agent_key}', task_id=action_id)
triggered_by=f'agent:{agent_key}', task_id=action_task_id)
if success: if success:
update_task_db(action_task_id, status='completed', update_task_db(action_id, status='completed', response=f"✓ Email versendet an {to}")
response=f"✓ Email erfolgreich versendet an {to_clean}") logger.info(f"[AgentCmd] Email gesendet an {to}: {subject}")
logger.info(f"[AgentCmd] Email gesendet an {to_clean}: {subject_clean}")
else: else:
update_task_db(action_task_id, status='error', response=f"Fehler: {message}") update_task_db(action_id, status='error', response=f"{msg}")
logger.error(f"[AgentCmd] Email-Fehler: {message}") logger.error(f"[AgentCmd] Email-Fehler: {msg}")
# SEND_TELEGRAM: Orchestrator sendet Telegram-Nachricht # ── SEND_TELEGRAM ────────────────────────────────────────────────────────
# Format: TelegramID: <numerische ID> oder Name/Email (wird dann in DB nachgeschlagen) for block in re.findall(r'<send_telegram>(.*?)</send_telegram>', response_text, re.DOTALL | re.IGNORECASE):
send_telegram_requests = re.findall( recipient = get_field(block, 'telegram_id') or get_field(block, 'to')
r'@SEND_TELEGRAM\s*\nTelegramID:\s*([^\n]+)\s*\nMessage:\s*(.*?)@END', message = get_field(block, 'message')
response_text, if not recipient or not message:
re.DOTALL logger.warning("[AgentCmd] <send_telegram> ohne telegram_id/message ignoriert")
) continue
for recipient, message in send_telegram_requests: action_id = create_task(
recipient_clean = recipient.strip() title=f"Telegram an {recipient}: {message[:60]}",
message_clean = message.strip() description=f"**An:** {recipient}\n\n{message}",
action_task_id = create_task(
title=f"Telegram an {recipient_clean}: {message_clean[:60]}",
description=f"**An:** {recipient_clean}\n\n{message_clean}",
agent_key=agent_key, agent_key=agent_key,
task_type='action_telegram', task_type='action_telegram',
created_by=agent_key, created_by=agent_key,
parent_task_id=task_id, parent_task_id=task_id,
) )
if TELEGRAM_CONFIG.get('bot_token'):
if TELEGRAM_CONFIG.get('bot_token') and TELEGRAM_CONFIG.get('telegram_bot'):
try: try:
chat_id = None chat_id = int(recipient) if recipient.lstrip('-').isdigit() else None
if recipient_clean.lstrip('-').isdigit(): if not chat_id:
chat_id = int(recipient_clean)
else:
con = sqlite3.connect(EMAIL_JOURNAL_DB) con = sqlite3.connect(EMAIL_JOURNAL_DB)
result = con.execute( row = con.execute(
"SELECT telegram_id FROM team_members WHERE name=? OR email=?", "SELECT telegram_id FROM team_members WHERE name=? OR email=?",
(recipient_clean, recipient_clean) (recipient, recipient)
).fetchone() ).fetchone()
con.close() con.close()
if result and result[0]: if row and row[0]:
chat_id = int(result[0]) chat_id = int(row[0])
if chat_id: if chat_id:
send_telegram_message(chat_id, message_clean) send_telegram_message(chat_id, message)
update_task_db(action_task_id, status='completed', update_task_db(action_id, status='completed', response=f"✓ Telegram gesendet (chat_id={chat_id})")
response=f"✓ Telegram gesendet an {recipient_clean} (chat_id={chat_id})") logger.info(f"[AgentCmd] Telegram gesendet an {recipient} (chat_id={chat_id})")
logger.info(f"[AgentCmd] Telegram gesendet an {recipient_clean} (chat_id={chat_id})")
else: else:
update_task_db(action_task_id, status='error', update_task_db(action_id, status='error', response=f"✗ Keine Telegram-ID für '{recipient}'")
response=f"✗ Keine Telegram-ID für '{recipient_clean}' gefunden") logger.warning(f"[AgentCmd] Keine Telegram-ID für '{recipient}'")
logger.warning(f"[AgentCmd] Keine Telegram Chat-ID für '{recipient_clean}'")
except Exception as e: except Exception as e:
update_task_db(action_task_id, status='error', response=f"Fehler: {str(e)}") update_task_db(action_id, status='error', response=f"{e}")
logger.error(f"[AgentCmd] Telegram-Fehler: {str(e)}") logger.error(f"[AgentCmd] Telegram-Fehler: {e}")
else: else:
update_task_db(action_task_id, status='error', response="✗ Telegram nicht konfiguriert") update_task_db(action_id, status='error', response="✗ Telegram nicht konfiguriert")
logger.warning("[AgentCmd] Telegram nicht konfiguriert")
# ADD_TEAM_MEMBER: Füge neues Team-Mitglied hinzu # ── ADD_TEAM_MEMBER ──────────────────────────────────────────────────────
# Format: Name / Role / Responsibilities / Email (Reihenfolge wie im System-Prompt) for block in re.findall(r'<add_team_member>(.*?)</add_team_member>', response_text, re.DOTALL | re.IGNORECASE):
add_member_requests = re.findall( name = get_field(block, 'name')
r'@ADD_TEAM_MEMBER\s*\nName:\s*([^\n]+)\s*\nRole:\s*([^\n]+)\s*\nResponsibilities:\s*([^\n]+)\s*\nEmail:\s*(.*?)@END', role = get_field(block, 'role')
response_text, resp = get_field(block, 'responsibilities')
re.DOTALL 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,
) )
for name, role, resp, email in add_member_requests: success = add_team_member(name, role, resp, email)
name_clean = name.strip() status_msg = f"'{name}' hinzugefügt" if success else f"✗ Fehler beim Hinzufügen von '{name}'"
role_clean = role.strip() update_task_db(action_id, status='completed' if success else 'error', response=status_msg)
resp_clean = resp.strip() logger.info(f"[AgentCmd] {status_msg}")
email_clean = email.strip()
action_task_id = create_task( # ── UPDATE_TEAM_MEMBER ───────────────────────────────────────────────────
title=f"Team-Member hinzugefügt: {name_clean}", ALLOWED_UPDATE_FIELDS = {'name','role','responsibilities','email','telegram_id','telegramid','phone'}
description=f"**Name:** {name_clean}\n**Rolle:** {role_clean}\n**Email:** {email_clean}\n**Verantwortlichkeiten:** {resp_clean}", for block in re.findall(r'<update_team_member>(.*?)</update_team_member>', response_text, re.DOTALL | re.IGNORECASE):
agent_key=agent_key, identifier = get_field(block, 'identifier')
task_type='action_team', if not identifier:
created_by=agent_key, continue
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: <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
kwargs = {} kwargs = {}
for line in lines: for line in block.strip().splitlines():
if ':' not in line: if ':' not in line:
continue continue
key, _, val = line.partition(':') k, _, v = line.partition(':')
key_norm = key.strip().lower().replace(' ', '_') k_norm = k.strip().lower().replace(' ', '_')
val_clean = val.strip() if k_norm in ALLOWED_UPDATE_FIELDS and k_norm != 'identifier':
if key_norm == 'identifier': db_key = 'telegram_id' if k_norm == 'telegramid' else k_norm
identifier = val_clean kwargs[db_key] = v.strip()
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
if not kwargs: if not kwargs:
logger.warning(f"[AgentCmd] @UPDATE_TEAM_MEMBER für '{identifier}' ohne Felder ignoriert")
continue continue
fields_summary = ", ".join(f"{k}={v}" for k, v in kwargs.items()) fields_str = ', '.join(f"{k}={v}" for k, v in kwargs.items())
action_task_id = create_task( action_id = create_task(
title=f"Team-Member aktualisiert: {identifier}", title=f"Team-Member aktualisiert: {identifier}",
description=f"**Identifier:** {identifier}\n**Felder:** {fields_summary}", description=f"**Identifier:** {identifier}\n**Felder:** {fields_str}",
agent_key=agent_key, agent_key=agent_key, task_type='action_team', created_by=agent_key, parent_task_id=task_id,
task_type='action_team',
created_by=agent_key,
parent_task_id=task_id,
) )
success = update_team_member(identifier, **kwargs) success = update_team_member(identifier, **kwargs)
if success: status_msg = f"{identifier} aktualisiert: {fields_str}" if success else f"✗ Update fehlgeschlagen für '{identifier}'"
update_task_db(action_task_id, status='completed', update_task_db(action_id, status='completed' if success else 'error', response=status_msg)
response=f"{identifier} aktualisiert: {fields_summary}") logger.info(f"[AgentCmd] {status_msg}")
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}'")
def create_new_agent(agent_key, role, skills): def create_new_agent(agent_key, role, skills):
"""Erstellt dynamisch einen neuen Agenten.""" """Erstellt dynamisch einen neuen Agenten."""