""" Dokumentations-Agent für die Erstellung von Dokumentation, Berichten und strukturierten Inhalten. Verwendet einen strukturierten mehrstufigen Prozess zur Erstellung hochwertiger Dokumentation. Angepasst für das refaktorisierte Core-Modul. """ import logging import json import re import traceback from typing import List, Dict, Any, Optional, Tuple, Union from datetime import datetime import uuid from modules.agentservice_base import BaseAgent from connectors.connector_aichat_openai import ChatService from modules.agentservice_utils import WorkflowUtils, MessageUtils, LoggingUtils logger = logging.getLogger(__name__) class DocumentationAgent(BaseAgent): """Agent für die Erstellung von Dokumentation und strukturierten Inhalten""" def __init__(self): """Initialisiert den Dokumentations-Agenten""" super().__init__() self.id = "documentation" self.name = "Dokumentation" self.type = "documentation" self.description = "Erstellt Dokumentation und strukturierte Inhalte" self.capabilities = "report_generation,documentation,content_structuring,technical_writing,knowledge_organization" 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 """ self.result_format = "FormattedDocument" # Chat-Service initialisieren self.chat_service = None # Utility-Klassen initialisieren self.message_utils = MessageUtils() def get_agent_info(self) -> Dict[str, Any]: """Get agent information for agent registry""" return { "id": self.id, "type": self.type, "name": self.name, "description": self.description, "capabilities": self.capabilities, "result_format": self.result_format } 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 sowenige Kapitel wie nötig, die das Thema umfassend behandeln. Schreibe in Prosa und nur als Liste, wenn auch angebracht. """ 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], workflow: Dict[str, Any], context: Dict[str, Any] = None, log_func=None) -> Dict[str, Any]: """ Verarbeitet eine Nachricht und erstellt Dokumentation in einem strukturierten Prozess. Args: message: Die zu verarbeitende Nachricht workflow: Der aktuelle Workflow context: Zusätzlicher Kontext log_func: Funktion für Workflow-Logging Returns: Die generierte Dokumentation """ # Initialize logging workflow_id = workflow.get("id", "unknown") logging_utils = LoggingUtils(workflow_id, log_func) logging_utils.info(f"DocumentationAgent startet Dokumentationserstellung", "agents") # Create response message response = self.message_utils.create_message(workflow_id, role="assistant") response["agent_type"] = self.type response["agent_name"] = self.name response["parent_message_id"] = message.get("id") 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) logging_utils.info(f"Dokumenttyp erkannt: {document_type}", "agents") # Schritt 1: Titel generieren title = await self.generate_title(task, document_type) logging_utils.info(f"Titel generiert: {title}", "agents") # Schritt 2: Zusammenfassung generieren summary = await self.generate_summary(task, document_type, title) logging_utils.info("Zusammenfassung generiert", "agents") # Schritt 3: Inhaltsverzeichnis mit Prompts generieren toc_with_prompts = await self.generate_toc_with_prompts(task, document_type, title, summary) logging_utils.info(f"Inhaltsverzeichnis mit {len(toc_with_prompts)} Kapiteln generiert", "agents") # Schritt 4: Kapitelinhalte in einer Schleife generieren chapter_contents = {} for chapter_title, chapter_prompt in toc_with_prompts.items(): logging_utils.info(f"Generiere Inhalt für Kapitel: {chapter_title}", "agents") 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) logging_utils.info(f"Dokument fertiggestellt mit {len(final_document)} Zeichen", "agents") # Set the content in the response response["content"] = final_document # Finalize the message self.message_utils.finalize_message(response) response["result_format"] = self.result_format # Chat-Service schließen await self.chat_service.close() self.chat_service = None return response except Exception as e: error_msg = f"Fehler bei der Dokumentationserstellung: {str(e)}" logging_utils.error(error_msg, "error") # Chat-Service schließen bei Fehler if self.chat_service: try: await self.chat_service.close() except: pass self.chat_service = None # Create error response response["content"] = f"## Fehler bei der Dokumentationserstellung\n\n{error_msg}\n\n```\n{traceback.format_exc()}\n```" self.message_utils.finalize_message(response) return response # 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