442 lines
No EOL
17 KiB
Python
442 lines
No EOL
17 KiB
Python
"""
|
|
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 |