feat: Team-Member Lernsystem & Automatische Vorstellung
Team-Member Updates: - Korrigierte Rollen und Verantwortlichkeiten: * Eric Fischer: Lead Programming & Tech * Georg Tschare: CEO & Kundenkommunikation * Piotr Dyderski: SysAdmin, TechLead, 3D Artist & R&D Neue Funktionen: - update_team_member() - Aktualisiert Team-Member-Daten per Email/Name - is_known_team_member() - Prüft ob Email in Team-DB existiert - @UPDATE_TEAM_MEMBER Kommando für Orchestrator - @ADD_TEAM_MEMBER Kommando für Orchestrator Intelligentes Lernsystem: - Erkennt neue Email-Absender automatisch - Leitet unbekannte Absender automatisch an Orchestrator - Orchestrator bittet um Vorstellung (Name, Rolle, Aufgaben) - Informationen werden in Team-DB gespeichert - Verbessert zukünftige Koordination Email-Workflow für neue Absender: 1. Email von unbekanntem @diversityball.at kommt an 2. System erkennt: "Nicht in Team-DB" 3. Orchestrator übernimmt automatisch 4. Fragt freundlich nach Vorstellung 5. Beantwortet auch die eigentliche Anfrage 6. Speichert Infos mit @ADD_TEAM_MEMBER Vorteile: ✓ Orchestrator baut automatisch Wissensdatenbank auf ✓ Bessere Koordination durch bekannte Verantwortlichkeiten ✓ Keine manuellen Team-Member Einträge nötig ✓ Freundlicher, professioneller Erstkontakt ✓ Lernfähiges System das besser wird Context-Weiterleitung: - extra_context aus Tasks wird jetzt durchgereicht - Kombiniert Email-Kontext + Team-Member-Status - Agents haben volle Informationen über Absender
This commit is contained in:
parent
5b0cf6e640
commit
632a6c253a
1 changed files with 124 additions and 12 deletions
132
app.py
132
app.py
|
|
@ -515,6 +515,23 @@ TelegramID: [Telegram-ID des Team-Members]
|
||||||
Message: [Nachricht]
|
Message: [Nachricht]
|
||||||
@END
|
@END
|
||||||
|
|
||||||
|
**Team-Member Informationen aktualisieren:**
|
||||||
|
@UPDATE_TEAM_MEMBER
|
||||||
|
Identifier: [Email oder Name des Team-Members]
|
||||||
|
Role: [Neue Rolle] (optional)
|
||||||
|
Responsibilities: [Neue Verantwortlichkeiten] (optional)
|
||||||
|
TelegramID: [Telegram-ID] (optional)
|
||||||
|
Phone: [Telefon] (optional)
|
||||||
|
@END
|
||||||
|
|
||||||
|
**Neuen Team-Member hinzufügen:**
|
||||||
|
@ADD_TEAM_MEMBER
|
||||||
|
Name: [Vollständiger Name]
|
||||||
|
Role: [Rolle/Position]
|
||||||
|
Responsibilities: [Verantwortlichkeiten]
|
||||||
|
Email: [Email-Adresse]
|
||||||
|
@END
|
||||||
|
|
||||||
Der Orchestrator kümmert sich um die Zuweisung und Kommunikation!
|
Der Orchestrator kümmert sich um die Zuweisung und Kommunikation!
|
||||||
|
|
||||||
## Wichtig:
|
## Wichtig:
|
||||||
|
|
@ -746,6 +763,47 @@ def add_team_member(name, role, responsibilities='', email='', telegram_id=None,
|
||||||
return member_id
|
return member_id
|
||||||
|
|
||||||
|
|
||||||
|
def update_team_member(identifier, **kwargs):
|
||||||
|
"""Aktualisiert ein Team-Mitglied (per Email oder Name)."""
|
||||||
|
con = sqlite3.connect(EMAIL_JOURNAL_DB)
|
||||||
|
|
||||||
|
# Finde Member per Email oder Name
|
||||||
|
member = con.execute(
|
||||||
|
"SELECT id FROM team_members WHERE email = ? OR name = ? LIMIT 1",
|
||||||
|
(identifier, identifier)
|
||||||
|
).fetchone()
|
||||||
|
|
||||||
|
if not member:
|
||||||
|
con.close()
|
||||||
|
logging.warning(f"[DB] Team-Member '{identifier}' nicht gefunden für Update")
|
||||||
|
return False
|
||||||
|
|
||||||
|
member_id = member[0]
|
||||||
|
|
||||||
|
# Baue UPDATE query
|
||||||
|
updates = []
|
||||||
|
params = []
|
||||||
|
|
||||||
|
for key, value in kwargs.items():
|
||||||
|
if key in ['name', 'role', 'responsibilities', 'email', 'telegram_id', 'phone', 'active']:
|
||||||
|
updates.append(f"{key} = ?")
|
||||||
|
params.append(value)
|
||||||
|
|
||||||
|
if not updates:
|
||||||
|
con.close()
|
||||||
|
return False
|
||||||
|
|
||||||
|
params.append(member_id)
|
||||||
|
query = f"UPDATE team_members SET {', '.join(updates)} WHERE id = ?"
|
||||||
|
|
||||||
|
con.execute(query, params)
|
||||||
|
con.commit()
|
||||||
|
con.close()
|
||||||
|
|
||||||
|
logging.info(f"[DB] Team-Member '{identifier}' aktualisiert")
|
||||||
|
return True
|
||||||
|
|
||||||
|
|
||||||
def get_team_member_summary():
|
def get_team_member_summary():
|
||||||
"""Erstellt eine Text-Zusammenfassung aller Team-Members für den Orchestrator."""
|
"""Erstellt eine Text-Zusammenfassung aller Team-Members für den Orchestrator."""
|
||||||
members = get_team_members(active_only=True)
|
members = get_team_members(active_only=True)
|
||||||
|
|
@ -754,6 +812,8 @@ def get_team_member_summary():
|
||||||
return "Keine Team-Mitglieder definiert."
|
return "Keine Team-Mitglieder definiert."
|
||||||
|
|
||||||
summary = "## Reale Team-Mitglieder:\n\n"
|
summary = "## Reale Team-Mitglieder:\n\n"
|
||||||
|
summary += "Du kannst Team-Member-Informationen jederzeit mit @UPDATE_TEAM_MEMBER aktualisieren!\n\n"
|
||||||
|
|
||||||
for member in members:
|
for member in members:
|
||||||
summary += f"**{member['name']}** ({member['role']})\n"
|
summary += f"**{member['name']}** ({member['role']})\n"
|
||||||
if member['responsibilities']:
|
if member['responsibilities']:
|
||||||
|
|
@ -1172,6 +1232,22 @@ def is_whitelisted(sender_address):
|
||||||
return False
|
return False
|
||||||
|
|
||||||
|
|
||||||
|
def is_known_team_member(sender_address):
|
||||||
|
"""Prüft ob Absender ein bekanntes Team-Mitglied ist."""
|
||||||
|
match = re.search(r'<([^>]+)>', sender_address)
|
||||||
|
addr = match.group(1).lower() if match else sender_address.lower().strip()
|
||||||
|
|
||||||
|
# Prüfe ob Email in Team-Members DB
|
||||||
|
con = sqlite3.connect(EMAIL_JOURNAL_DB)
|
||||||
|
result = con.execute(
|
||||||
|
"SELECT name, role FROM team_members WHERE LOWER(email) = ? AND active = 1",
|
||||||
|
(addr,)
|
||||||
|
).fetchone()
|
||||||
|
con.close()
|
||||||
|
|
||||||
|
return result # Returns (name, role) or None
|
||||||
|
|
||||||
|
|
||||||
def decode_email_header_value(value):
|
def decode_email_header_value(value):
|
||||||
"""Dekodiert Email-Header (z.B. encoded Subject/From)."""
|
"""Dekodiert Email-Header (z.B. encoded Subject/From)."""
|
||||||
if not value:
|
if not value:
|
||||||
|
|
@ -1487,7 +1563,37 @@ def poll_emails():
|
||||||
addr_match = re.search(r'<([^>]+)>', sender)
|
addr_match = re.search(r'<([^>]+)>', sender)
|
||||||
sender_email = addr_match.group(1) if addr_match else sender.strip()
|
sender_email = addr_match.group(1) if addr_match else sender.strip()
|
||||||
|
|
||||||
# Agent bestimmen
|
# Prüfe ob Absender bekannt ist (in Team-Members DB)
|
||||||
|
known_member = is_known_team_member(sender)
|
||||||
|
|
||||||
|
# Wenn unbekannt: Bitte um Vorstellung
|
||||||
|
extra_context = ""
|
||||||
|
if not known_member:
|
||||||
|
extra_context = f"""
|
||||||
|
|
||||||
|
## ⚠️ WICHTIG - Neuer Absender:
|
||||||
|
Diese Email kommt von **{sender}** - diese Person ist noch nicht in der Team-Datenbank!
|
||||||
|
|
||||||
|
Bitte in deiner Antwort:
|
||||||
|
1. Freundlich begrüßen
|
||||||
|
2. Um Vorstellung bitten: Name, Rolle, Verantwortlichkeiten
|
||||||
|
3. Fragen was sie im Projekt macht
|
||||||
|
4. Dann ihre eigentliche Anfrage beantworten
|
||||||
|
|
||||||
|
Der Orchestrator wird die Informationen dann mit @ADD_TEAM_MEMBER oder @UPDATE_TEAM_MEMBER in die Datenbank eintragen.
|
||||||
|
"""
|
||||||
|
else:
|
||||||
|
extra_context = f"""
|
||||||
|
|
||||||
|
## Team-Member erkannt:
|
||||||
|
Diese Email kommt von **{known_member[0]}** ({known_member[1]})
|
||||||
|
"""
|
||||||
|
|
||||||
|
# Agent bestimmen - bei unbekannten Sendern immer Orchestrator
|
||||||
|
if not known_member:
|
||||||
|
agent_key = 'orchestrator'
|
||||||
|
logger.info("[EmailPoller] Unbekannter Absender → Orchestrator übernimmt")
|
||||||
|
else:
|
||||||
full_prompt_for_routing = f"{subject} {body}"
|
full_prompt_for_routing = f"{subject} {body}"
|
||||||
agent_key = delegate_to_agent(full_prompt_for_routing)
|
agent_key = delegate_to_agent(full_prompt_for_routing)
|
||||||
|
|
||||||
|
|
@ -1511,6 +1617,7 @@ def poll_emails():
|
||||||
'original_body': body,
|
'original_body': body,
|
||||||
'message_id': message_id, # für Journal-Update durch TaskWorker
|
'message_id': message_id, # für Journal-Update durch TaskWorker
|
||||||
'imap_uid': email_id.decode(),
|
'imap_uid': email_id.decode(),
|
||||||
|
'extra_context': extra_context, # Für unbekannte Absender
|
||||||
}
|
}
|
||||||
tasks.append(task)
|
tasks.append(task)
|
||||||
with task_queue_lock:
|
with task_queue_lock:
|
||||||
|
|
@ -1570,14 +1677,19 @@ Bitte bearbeite diese Anfrage vollständig:
|
||||||
3. Formuliere eine vollständige, hilfreiche Antwort
|
3. Formuliere eine vollständige, hilfreiche Antwort
|
||||||
4. Die Antwort wird automatisch an {sender_email} zurückgeschickt"""
|
4. Die Antwort wird automatisch an {sender_email} zurückgeschickt"""
|
||||||
|
|
||||||
extra_context = f"""
|
email_context = f"""
|
||||||
## Email-Kontext:
|
## Email-Kontext:
|
||||||
- Diese Anfrage kommt per Email von: {sender_email}
|
- Diese Anfrage kommt per Email von: {sender_email}
|
||||||
- Die Antwort wird automatisch per Email zurückgeschickt
|
- Die Antwort wird automatisch per Email zurückgeschickt
|
||||||
- Formuliere die Antwort daher als Email-Text (freundlich, professionell)
|
- Formuliere die Antwort daher als Email-Text (freundlich, professionell)
|
||||||
- Wenn gebeten wird, eine Email an jemanden zu schicken: gib die Adresse in der Form "An: adresse@example.com" oder "To: adresse@example.com" an"""
|
- Wenn gebeten wird, eine Email an jemanden zu schicken: gib die Adresse in der Form "An: adresse@example.com" oder "To: adresse@example.com" an"""
|
||||||
|
|
||||||
agent_response = execute_agent_task(agent_key, full_prompt, extra_context=extra_context)
|
# Kombiniere Email-Kontext mit Task-Kontext (z.B. unbekannter Absender)
|
||||||
|
combined_context = email_context
|
||||||
|
if task.get('extra_context'):
|
||||||
|
combined_context += "\n\n" + task['extra_context']
|
||||||
|
|
||||||
|
agent_response = execute_agent_task(agent_key, full_prompt, extra_context=combined_context)
|
||||||
|
|
||||||
# Zusätzliche Empfänger aus der Agenten-Antwort extrahieren
|
# Zusätzliche Empfänger aus der Agenten-Antwort extrahieren
|
||||||
extra_recipients = re.findall(
|
extra_recipients = re.findall(
|
||||||
|
|
@ -2762,24 +2874,24 @@ def init_default_team_members():
|
||||||
if len(existing) == 0:
|
if len(existing) == 0:
|
||||||
add_team_member(
|
add_team_member(
|
||||||
name="Eric Fischer",
|
name="Eric Fischer",
|
||||||
role="Projektleiter & Event-Koordinator",
|
role="Lead Programming & Tech",
|
||||||
responsibilities="Gesamtkoordination, Budget-Verwaltung, Location-Management",
|
responsibilities="Technische Entwicklung, Programmierung, System-Architektur",
|
||||||
email="eric.fischer@signtime.media",
|
email="eric.fischer@signtime.media",
|
||||||
telegram_id=None, # Wird bei Bedarf ergänzt
|
telegram_id=None,
|
||||||
phone=""
|
phone=""
|
||||||
)
|
)
|
||||||
add_team_member(
|
add_team_member(
|
||||||
name="Georg Tschare",
|
name="Georg Tschare",
|
||||||
role="Content & Marketing",
|
role="CEO & Kundenkommunikation",
|
||||||
responsibilities="Marketing, Social Media, Presse, Kommunikation",
|
responsibilities="Geschäftsführung, Kundenkontakt, Kommunikation, Strategie",
|
||||||
email="georg.tschare@signtime.media",
|
email="georg.tschare@signtime.media",
|
||||||
telegram_id=None,
|
telegram_id=None,
|
||||||
phone=""
|
phone=""
|
||||||
)
|
)
|
||||||
add_team_member(
|
add_team_member(
|
||||||
name="Piotr Dyderski",
|
name="Piotr Dyderski",
|
||||||
role="System-Administrator & Tech-Lead",
|
role="SysAdmin, TechLead, 3D Artist & R&D",
|
||||||
responsibilities="Technische Infrastruktur, AI-Agenten, Automatisierung",
|
responsibilities="System-Administration, Technische Leitung, 3D-Visualisierung, AI-Agenten, Research & Development",
|
||||||
email="p.dyderski@live.at",
|
email="p.dyderski@live.at",
|
||||||
telegram_id=None,
|
telegram_id=None,
|
||||||
phone=""
|
phone=""
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue