gateway/gwserver/modules/agentservice_agent_documentation.py
2025-04-06 23:57:10 +02:00

420 lines
No EOL
16 KiB
Python

"""
Dokumentations-Agent für die Erstellung von Dokumentation, Berichten und strukturierten Inhalten.
Verwendet einen strukturierten mehrstufigen Prozess zur Erstellung hochwertiger Dokumentation.
"""
import logging
from typing import List, Dict, Any, Optional, Tuple
from modules.agentservice_base import BaseAgent
from connectors.connector_aichat_openai import ChatService
logger = logging.getLogger(__name__)
class DocumentationAgent(BaseAgent):
"""Agent für die Erstellung von Dokumentation und strukturierten Inhalten"""
_instance = None
@classmethod
def get_instance(cls):
"""Gibt eine Singleton-Instanz zurück"""
if cls._instance is None:
cls._instance = cls()
return cls._instance
def __init__(self):
"""Initialisiert den Dokumentations-Agenten"""
super().__init__()
self.id = "documentation_agent"
self.name = "Dokumentation"
self.type = "documentation"
self.description = "Erstellt Dokumentation und strukturierte Inhalte"
self.capabilities = "Berichte, Dokumentation, Zusammenfassungen und Erklärungen"
self.instructions = """
Du bist der Dokumentations-Agent. Deine Aufgabe:
1. Komplexe Informationen in klare, strukturierte Dokumente umsetzen
2. Verschiedene Dokumentformate erstellen
3. Informationen aus verschiedenen Quellen strukturieren
4. Technische Konzepte verständlich erklären
5. Konsistente Formatierung sicherstellen
"""
# Chat-Service initialisieren
self.chat_service = None
def get_base_prompt(self, document_type: str = "") -> str:
"""
Generiert einen Basis-Prompt für den Dokumentations-Agenten.
Args:
document_type: Typ des zu erstellenden Dokuments
Returns:
Basis-Prompt für den Dokumentations-Agenten
"""
# Basis-Prompt
prompt = f"""
Du bist {self.name}, ein {self.type} Agent.
{self.description}
Fähigkeiten: {self.capabilities}
{self.instructions}
"""
# Dokumenttyp-spezifische Anweisungen hinzufügen
if document_type:
prompt += self._get_document_type_instructions(document_type)
return prompt.strip()
def _get_document_type_instructions(self, document_type: str) -> str:
"""
Gibt spezifische Anweisungen für einen bestimmten Dokumenttyp zurück.
Args:
document_type: Typ des Dokuments
Returns:
Spezifische Anweisungen für den Dokumenttyp
"""
document_type = document_type.lower()
if "handbuch" in document_type or "anleitung" in document_type or "guide" in document_type:
return "\n\nHANDBUCH: Beginne mit Zweckbeschreibung, strukturiere in logische Schritte, verwende direkte Anweisungen."
elif "bericht" in document_type or "report" in document_type:
return "\n\nBERICHT: Beginne mit Executive Summary, strukturiere in thematische Abschnitte, halte professionellen Ton."
elif "prozess" in document_type or "process" in document_type:
return "\n\nPROZESS: Beschreibe Zweck, Ziele, Beteiligte, sequenzielle Schritte, Inputs/Outputs und Verantwortlichkeiten."
elif "präsentation" in document_type or "presentation" in document_type:
return "\n\nPRÄSENTATION: Klare Hauptpunkte, visuelle Elemente, Einleitung-Hauptteil-Schluss Struktur."
else:
return "\n\nDOKUMENT: Erstelle ein gut strukturiertes Dokument mit klarer Gliederung und präziser Sprache."
def _detect_document_type(self, message: str) -> str:
"""
Erkennt den Dokumenttyp aus der Nachricht.
Args:
message: Nachricht des Benutzers
Returns:
Erkannter Dokumenttyp
"""
message = message.lower()
if "handbuch" in message or "anleitung" in message or "guide" in message:
return "handbuch"
elif "bericht" in message or "report" in message:
return "bericht"
elif "prozess" in message or "process" in message or "ablauf" in message:
return "prozess"
elif "präsentation" in message or "presentation" in message or "folien" in message:
return "präsentation"
else:
return "dokument"
async def generate_title(self, task: str, document_type: str) -> str:
"""
Generiert einen Titel für das Dokument.
Args:
task: Die Aufgabe/Anfrage
document_type: Typ des Dokuments
Returns:
Generierter Titel
"""
prompt = f"""
Erstelle einen prägnanten, professionellen Titel für folgendes {document_type.capitalize()}:
AUFTRAG: {task}
Gib NUR den Titel zurück, ohne weitere Erklärungen oder Formatierungen.
"""
messages = [
{"role": "system", "content": "Du bist ein Experte für die Erstellung von Dokumenttiteln."},
{"role": "user", "content": prompt}
]
title = await self.chat_service.call_api(messages)
# Bereinige den Titel von Anführungszeichen und Überschriften-Symbolen
title = title.strip('"\'#*- \n\t')
return title
async def generate_summary(self, task: str, document_type: str, title: str) -> str:
"""
Generiert eine Zusammenfassung für das Dokument.
Args:
task: Die Aufgabe/Anfrage
document_type: Typ des Dokuments
title: Titel des Dokuments
Returns:
Generierte Zusammenfassung
"""
prompt = f"""
Erstelle eine prägnante Zusammenfassung für folgendes Dokument:
TITEL: {title}
TYP: {document_type.capitalize()}
AUFTRAG: {task}
Die Zusammenfassung soll einen Überblick über den Zweck und die Hauptinhalte des Dokuments geben.
Sie sollte etwa 3-5 Sätze umfassen und als eigenständiger Abschnitt funktionieren.
"""
messages = [
{"role": "system", "content": "Du bist ein Experte für die Erstellung prägnanter Dokumentzusammenfassungen."},
{"role": "user", "content": prompt}
]
summary = await self.chat_service.call_api(messages)
return summary.strip()
async def generate_toc_with_prompts(self, task: str, document_type: str, title: str, summary: str) -> Dict[str, str]:
"""
Generiert ein Inhaltsverzeichnis mit Prompts für die einzelnen Kapitel.
Args:
task: Die Aufgabe/Anfrage
document_type: Typ des Dokuments
title: Titel des Dokuments
summary: Zusammenfassung des Dokuments
Returns:
Dict mit Kapiteltiteln als Schlüssel und Prompts als Werte
"""
prompt = f"""
Erstelle ein strukturiertes Inhaltsverzeichnis für folgendes Dokument:
TITEL: {title}
TYP: {document_type.capitalize()}
AUFTRAG: {task}
ZUSAMMENFASSUNG: {summary}
Für jedes Kapitel gib auch einen kurzen Prompt an, der beschreibt, was in diesem Kapitel behandelt werden soll.
Formatiere deine Antwort als JSON-Objekt mit folgendem Format:
{{
"Kapitel 1: Titel": "Prompt für Kapitel 1",
"Kapitel 2: Titel": "Prompt für Kapitel 2",
...
}}
Beschränke dich auf 5-7 sinnvolle Kapitel, die das Thema umfassend behandeln.
"""
messages = [
{"role": "system", "content": "Du bist ein Experte für die Strukturierung von Dokumenten und die Erstellung von Inhaltsverzeichnissen."},
{"role": "user", "content": prompt}
]
toc_response = await self.chat_service.call_api(messages)
# JSON aus der Antwort extrahieren
import json
import re
# Markdown-Code-Blöcke entfernen, falls vorhanden
toc_response = re.sub(r'```json\s*|\s*```', '', toc_response)
try:
toc_with_prompts = json.loads(toc_response)
return toc_with_prompts
except json.JSONDecodeError as e:
logger.error(f"Fehler beim Parsen des Inhaltsverzeichnisses: {str(e)}")
logger.error(f"Rohe Antwort: {toc_response}")
# Notfall-Fallback
return {
"1. Einleitung": "Einführung in das Thema und Überblick",
"2. Hauptteil": "Hauptinhalte des Dokuments",
"3. Schlussfolgerung": "Zusammenfassung und nächste Schritte"
}
async def generate_chapter_content(self, chapter_title: str, chapter_prompt: str,
task: str, document_type: str, title: str, summary: str) -> str:
"""
Generiert den Inhalt für ein bestimmtes Kapitel.
Args:
chapter_title: Titel des Kapitels
chapter_prompt: Prompt für das Kapitel
task: Die Aufgabe/Anfrage
document_type: Typ des Dokuments
title: Titel des Dokuments
summary: Zusammenfassung des Dokuments
Returns:
Generierter Kapitelinhalt
"""
prompt = f"""
Erstelle detaillierten Inhalt für folgendes Kapitel eines {document_type}s:
DOKUMENT-TITEL: {title}
AUFGABE: {task}
KAPITEL: {chapter_title}
ANWEISUNG FÜR DIESES KAPITEL: {chapter_prompt}
Der Inhalt sollte detailliert, informativ und gut strukturiert sein.
Verwende bei Bedarf Unterüberschriften, Aufzählungen und Tabellen zur besseren Strukturierung.
Der Inhalt sollte direkt mit dem Kapiteltext beginnen, ohne den Kapiteltitel zu wiederholen.
"""
messages = [
{"role": "system", "content": "Du bist ein Experte für die Erstellung hochwertiger Dokumentationsinhalte."},
{"role": "user", "content": prompt}
]
chapter_content = await self.chat_service.call_api(messages)
return chapter_content.strip()
def _format_final_document(self, title: str, summary: str, toc: Dict[str, str], chapter_contents: Dict[str, str]) -> str:
"""
Formatiert das endgültige Dokument aus allen Teilen.
Args:
title: Titel des Dokuments
summary: Zusammenfassung
toc: Inhaltsverzeichnis (Dict mit Kapiteltiteln als Schlüssel)
chapter_contents: Kapitelinhalte (Dict mit Kapiteltiteln als Schlüssel und Inhalten als Werte)
Returns:
Formatiertes Dokument
"""
# Titel formatieren
doc = f"# {title}\n\n"
# Zusammenfassung hinzufügen
doc += f"## Zusammenfassung\n\n{summary}\n\n"
# Inhaltsverzeichnis hinzufügen
doc += "## Inhaltsverzeichnis\n\n"
for idx, chapter in enumerate(toc.keys(), 1):
# Extrahiere den reinen Kapitelnamen (entferne Nummerierung, falls vorhanden)
clean_chapter = chapter
if chapter.strip().startswith(('0', '1', '2', '3', '4', '5', '6', '7', '8', '9')) and '. ' in chapter:
clean_chapter = chapter.split('. ', 1)[1]
doc += f"{idx}. {clean_chapter}\n"
doc += "\n"
# Kapitelinhalte hinzufügen
for idx, (chapter, content) in enumerate(chapter_contents.items(), 1):
# Extrahiere den reinen Kapitelnamen (entferne Nummerierung, falls vorhanden)
clean_chapter = chapter
if chapter.strip().startswith(('0', '1', '2', '3', '4', '5', '6', '7', '8', '9')) and '. ' in chapter:
clean_chapter = chapter.split('. ', 1)[1]
doc += f"## {idx}. {clean_chapter}\n\n{content}\n\n"
# Metadaten hinzufügen
doc += "---\n\n"
doc += f"**Erstellt durch:** {self.name}\n"
return doc
async def process_message(self, message: Dict[str, Any], context: Dict[str, Any] = None) -> Dict[str, Any]:
"""
Verarbeitet eine Nachricht und erstellt Dokumentation in einem strukturierten Prozess.
Args:
message: Die zu verarbeitende Nachricht
context: Zusätzlicher Kontext
Returns:
Die generierte Dokumentation
"""
try:
# Chat-Service initialisieren, falls noch nicht geschehen
if self.chat_service is None:
self.chat_service = ChatService()
# Task aus der Nachricht extrahieren
task = message.get("content", "")
if context and "task" in context:
task = context["task"]
# Dokumenttyp erkennen
document_type = self._detect_document_type(task)
logger.info(f"Starte Dokumentationserstellung für Typ: {document_type}")
# Schritt 1: Titel generieren
title = await self.generate_title(task, document_type)
logger.info(f"Titel generiert: {title}")
# Schritt 2: Zusammenfassung generieren
summary = await self.generate_summary(task, document_type, title)
logger.info("Zusammenfassung generiert")
# Schritt 3: Inhaltsverzeichnis mit Prompts generieren
toc_with_prompts = await self.generate_toc_with_prompts(task, document_type, title, summary)
logger.info(f"Inhaltsverzeichnis mit {len(toc_with_prompts)} Kapiteln generiert")
# Schritt 4: Kapitelinhalte in einer Schleife generieren
chapter_contents = {}
for chapter_title, chapter_prompt in toc_with_prompts.items():
logger.info(f"Generiere Inhalt für Kapitel: {chapter_title}")
content = await self.generate_chapter_content(
chapter_title, chapter_prompt, task, document_type, title, summary
)
chapter_contents[chapter_title] = content
# Schritt 5: Dokument zusammenführen
final_document = self._format_final_document(title, summary, toc_with_prompts, chapter_contents)
logger.info(f"Dokument fertiggestellt mit {len(final_document)} Zeichen")
# Schritt 6: Antwort zurückgeben
documentation_response = {
"role": "assistant",
"content": f"{final_document}\n\n[STATUS: ERGEBNIS]",
"agent_type": self.type
}
# Extrahiere den Status aus der Antwort und aktualisiere den Inhalt
content, status = self.extract_status(documentation_response["content"])
documentation_response["content"] = content
# Setze den Status im Kontext, falls vorhanden
if context is not None:
context["status"] = status
# Chat-Service schließen
await self.chat_service.close()
self.chat_service = None
return documentation_response
except Exception as e:
logger.error(f"Fehler bei der Dokumentationserstellung: {str(e)}", exc_info=True)
# Chat-Service schließen bei Fehler
if self.chat_service:
try:
await self.chat_service.close()
except:
pass
self.chat_service = None
# Fehlerantwort zurückgeben
return {
"role": "assistant",
"content": f"Bei der Erstellung der Dokumentation ist ein Fehler aufgetreten: {str(e)}",
"agent_type": self.type
}
# Singleton-Instanz
_documentation_agent = None
def get_documentation_agent():
"""Gibt eine Singleton-Instanz des Dokumentations-Agenten zurück"""
global _documentation_agent
if _documentation_agent is None:
_documentation_agent = DocumentationAgent()
return _documentation_agent