Files
breakpilot-compliance/ai-compliance-sdk/internal/iace/tech_file_generator.go
Benjamin Admin 6d2de9b897
All checks were successful
CI/CD / go-lint (push) Has been skipped
CI/CD / python-lint (push) Has been skipped
CI/CD / nodejs-lint (push) Has been skipped
CI/CD / test-go-ai-compliance (push) Successful in 36s
CI/CD / test-python-backend-compliance (push) Successful in 33s
CI/CD / test-python-document-crawler (push) Successful in 24s
CI/CD / test-python-dsms-gateway (push) Successful in 21s
CI/CD / validate-canonical-controls (push) Successful in 13s
CI/CD / Deploy (push) Successful in 2s
feat(iace): complete CE risk assessment — LLM tech-file generation, multi-format export, TipTap editor
Phase 1: Fix completeness gates G23 (require verified/rejected mitigations) and G09 (audit trail check)
Phase 2: LLM-based tech-file section generation with 19 German prompts and RAG enrichment
Phase 3: Multi-format document export (PDF/Excel/DOCX/Markdown/JSON)
Phase 4: Company profile → IACE data flow with auto component/classification creation
Phase 5: TipTap WYSIWYG editor replacing textarea for tech-file sections
Phase 6: User journey tests, developer portal API reference, updated documentation

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-16 12:50:53 +01:00

680 lines
30 KiB
Go

package iace
import (
"context"
"fmt"
"strings"
"github.com/breakpilot/ai-compliance-sdk/internal/llm"
"github.com/breakpilot/ai-compliance-sdk/internal/ucca"
"github.com/google/uuid"
)
// ============================================================================
// TechFileGenerator — LLM-based generation of technical file sections
// ============================================================================
// TechFileGenerator generates technical file section content using LLM and RAG.
type TechFileGenerator struct {
llmRegistry *llm.ProviderRegistry
ragClient *ucca.LegalRAGClient
store *Store
}
// NewTechFileGenerator creates a new TechFileGenerator.
func NewTechFileGenerator(registry *llm.ProviderRegistry, ragClient *ucca.LegalRAGClient, store *Store) *TechFileGenerator {
return &TechFileGenerator{
llmRegistry: registry,
ragClient: ragClient,
store: store,
}
}
// SectionGenerationContext holds all project data needed for LLM section generation.
type SectionGenerationContext struct {
Project *Project
Components []Component
Hazards []Hazard
Assessments map[uuid.UUID][]RiskAssessment // keyed by hazardID
Mitigations map[uuid.UUID][]Mitigation // keyed by hazardID
Classifications []RegulatoryClassification
Evidence []Evidence
RAGContext string // aggregated text from RAG search
}
// ============================================================================
// Section type constants
// ============================================================================
const (
SectionRiskAssessmentReport = "risk_assessment_report"
SectionHazardLogCombined = "hazard_log_combined"
SectionGeneralDescription = "general_description"
SectionEssentialRequirements = "essential_requirements"
SectionDesignSpecifications = "design_specifications"
SectionTestReports = "test_reports"
SectionStandardsApplied = "standards_applied"
SectionDeclarationConformity = "declaration_of_conformity"
SectionAIIntendedPurpose = "ai_intended_purpose"
SectionAIModelDescription = "ai_model_description"
SectionAIRiskManagement = "ai_risk_management"
SectionAIHumanOversight = "ai_human_oversight"
SectionComponentList = "component_list"
SectionClassificationReport = "classification_report"
SectionMitigationReport = "mitigation_report"
SectionVerificationReport = "verification_report"
SectionEvidenceIndex = "evidence_index"
SectionInstructionsForUse = "instructions_for_use"
SectionMonitoringPlan = "monitoring_plan"
)
// ============================================================================
// System prompts (German — CE compliance context)
// ============================================================================
var sectionSystemPrompts = map[string]string{
SectionRiskAssessmentReport: `Du bist CE-Experte fuer Maschinen- und KI-Sicherheit. Erstelle eine strukturierte Zusammenfassung der Risikobeurteilung gemaess ISO 12100 und EN ISO 13849. Gliederung: 1) Methodik, 2) Risikoueberblick (Anzahl Gefaehrdungen nach Risikostufe), 3) Kritische Risiken, 4) Akzeptanzbewertung, 5) Empfehlungen. Verwende Fachterminologie und beziehe dich auf die konkreten Projektdaten.`,
SectionHazardLogCombined: `Erstelle ein tabellarisches Gefaehrdungsprotokoll (Hazard Log) fuer die technische Dokumentation. Jede Gefaehrdung soll enthalten: ID, Bezeichnung, Kategorie, Lebenszyklusphase, Szenario, Schwere, Eintrittswahrscheinlichkeit, Risikolevel, Massnahmen und Status. Formatiere als strukturierte Tabelle in Markdown.`,
SectionGeneralDescription: `Erstelle eine allgemeine Maschinenbeschreibung fuer die technische Dokumentation gemaess EU-Maschinenverordnung 2023/1230 Anhang IV. Beschreibe: 1) Bestimmungsgemaesse Verwendung, 2) Aufbau und Funktion, 3) Systemkomponenten, 4) Betriebsbedingungen, 5) Schnittstellen. Verwende die bereitgestellten Projektdaten.`,
SectionEssentialRequirements: `Beschreibe die anwendbaren grundlegenden Anforderungen (Essential Health and Safety Requirements — EHSR) gemaess EU-Maschinenverordnung 2023/1230 Anhang III. Ordne jede Anforderung den relevanten Gefaehrdungen und Massnahmen zu. Beruecksichtige auch AI Act und CRA Anforderungen falls KI-Komponenten vorhanden sind.`,
SectionDesignSpecifications: `Erstelle eine Uebersicht der Konstruktionsdaten und Spezifikationen fuer die technische Dokumentation. Enthalten sein sollen: 1) Systemarchitektur, 2) Komponentenliste mit Sicherheitsrelevanz, 3) Software-/Firmware-Versionen, 4) Schnittstellenbeschreibungen, 5) Sicherheitsfunktionen. Beziehe dich auf die konkreten Komponenten.`,
SectionTestReports: `Erstelle eine Zusammenfassung der Pruefberichte und Verifikationsergebnisse. Gliederung: 1) Durchgefuehrte Pruefungen, 2) Pruefmethoden (Test, Analyse, Inspektion), 3) Ergebnisse pro Massnahme, 4) Offene Punkte, 5) Gesamtbewertung. Referenziere die konkreten Mitigationsmassnahmen und deren Verifikationsstatus.`,
SectionStandardsApplied: `Liste die angewandten harmonisierten Normen und technischen Spezifikationen auf. Ordne jede Norm den relevanten Anforderungen und Gefaehrdungskategorien zu. Beruecksichtige: ISO 12100, ISO 13849, IEC 62443, ISO/IEC 27001, sowie branchenspezifische Normen. Erklaere die Vermutungswirkung (Presumption of Conformity).`,
SectionDeclarationConformity: `Erstelle eine EU-Konformitaetserklaerung nach EU-Maschinenverordnung 2023/1230 Anhang IV. Enthalten sein muessen: 1) Hersteller-Angaben, 2) Produktidentifikation, 3) Angewandte Richtlinien und Verordnungen, 4) Angewandte Normen, 5) Bevollmaechtigter, 6) Ort, Datum, Unterschrift. Formales Dokument-Layout.`,
SectionAIIntendedPurpose: `Beschreibe den bestimmungsgemaessen Zweck des KI-Systems gemaess AI Act Art. 13 (Transparenzpflichten). Enthalten sein sollen: 1) Zweckbestimmung, 2) Einsatzbereich und -grenzen, 3) Zielgruppe, 4) Vorhersehbarer Fehlgebrauch, 5) Leistungskennzahlen, 6) Einschraenkungen und bekannte Risiken.`,
SectionAIModelDescription: `Beschreibe das KI-Modell, die Trainingsdaten und die Architektur gemaess AI Act Anhang IV. Enthalten: 1) Modelltyp und Architektur, 2) Trainingsdaten (Herkunft, Umfang, Qualitaet), 3) Validierungsmethodik, 4) Leistungsmetriken, 5) Bekannte Verzerrungen (Bias), 6) Energie-/Ressourcenverbrauch.`,
SectionAIRiskManagement: `Erstelle eine Beschreibung des KI-Risikomanagementsystems gemaess AI Act Art. 9. Gliederung: 1) Risikomanagement-Prozess, 2) Identifizierte Risiken fuer Gesundheit/Sicherheit/Grundrechte, 3) Risikomindernde Massnahmen, 4) Restrisiken, 5) Ueberwachungs- und Aktualisierungsverfahren.`,
SectionAIHumanOversight: `Beschreibe die Massnahmen zur menschlichen Aufsicht (Human Oversight) gemaess AI Act Art. 14. Enthalten: 1) Aufsichtskonzept, 2) Rollen und Verantwortlichkeiten, 3) Eingriffsmoglichkeiten, 4) Uebersteuern/Abschalten, 5) Schulungsanforderungen, 6) Informationspflichten an Nutzer.`,
SectionComponentList: `Erstelle eine detaillierte Komponentenliste fuer die technische Dokumentation. Pro Komponente: Name, Typ, Version, Beschreibung, Sicherheitsrelevanz, Vernetzungsstatus. Kennzeichne sicherheitsrelevante und vernetzte Komponenten besonders. Gruppiere nach Komponententyp.`,
SectionClassificationReport: `Erstelle einen Klassifizierungsbericht, der die regulatorische Einordnung des Produkts zusammenfasst. Pro Verordnung (MVO, AI Act, CRA, NIS2): Klassifizierungsergebnis, Risikoklasse, Begruendung, daraus resultierende Anforderungen. Bewerte die Gesamtkonformitaetslage.`,
SectionMitigationReport: `Erstelle einen Massnahmenbericht (Mitigation Report) fuer die technische Dokumentation. Gliederung nach 3-Stufen-Methode: 1) Inhaerent sichere Konstruktion (Design), 2) Technische Schutzmassnahmen (Protective), 3) Benutzerinformation (Information). Pro Massnahme: Status, Verifikation, zugeordnete Gefaehrdung.`,
SectionVerificationReport: `Erstelle einen Verifikationsbericht ueber alle durchgefuehrten Pruef- und Nachweisverfahren. Enthalten: 1) Verifikationsplan-Uebersicht, 2) Durchgefuehrte Pruefungen nach Methode, 3) Ergebnisse und Bewertung, 4) Offene Verifikationen, 5) Gesamtstatus der Konformitaetsnachweise.`,
SectionEvidenceIndex: `Erstelle ein Nachweisverzeichnis (Evidence Index) fuer die technische Dokumentation. Liste alle vorhandenen Nachweisdokumente auf: Dateiname, Beschreibung, zugeordnete Massnahme, Dokumenttyp. Identifiziere fehlende Nachweise und empfehle Ergaenzungen.`,
SectionInstructionsForUse: `Erstelle eine Gliederung fuer die Betriebsanleitung gemaess EU-Maschinenverordnung 2023/1230 Anhang III Abschnitt 1.7.4. Enthalten: 1) Bestimmungsgemaesse Verwendung, 2) Inbetriebnahme, 3) Sicherer Betrieb, 4) Wartung, 5) Restrisiken und Warnhinweise, 6) Ausserbetriebnahme. Beruecksichtige identifizierte Gefaehrdungen.`,
SectionMonitoringPlan: `Erstelle einen Post-Market-Monitoring-Plan fuer das Produkt. Enthalten: 1) Ueberwachungsziele, 2) Datenquellen (Kundenfeedback, Vorfaelle, Updates), 3) Ueberwachungsintervalle, 4) Eskalationsverfahren, 5) Dokumentationspflichten, 6) Verantwortlichkeiten. Beruecksichtige AI Act Art. 72 (Post-Market Monitoring) falls KI-Komponenten vorhanden.`,
}
// ============================================================================
// RAG query mapping
// ============================================================================
func buildRAGQuery(sectionType string) string {
ragQueries := map[string]string{
SectionRiskAssessmentReport: "Risikobeurteilung ISO 12100 Risikobewertung Maschine Gefaehrdungsanalyse",
SectionHazardLogCombined: "Gefaehrdungsprotokoll Hazard Log Risikoanalyse Gefaehrdungsidentifikation",
SectionGeneralDescription: "Maschinenbeschreibung technische Dokumentation bestimmungsgemaesse Verwendung",
SectionEssentialRequirements: "grundlegende Anforderungen EHSR Maschinenverordnung Anhang III Sicherheitsanforderungen",
SectionDesignSpecifications: "Konstruktionsdaten Spezifikationen Systemarchitektur technische Dokumentation",
SectionTestReports: "Pruefberichte Verifikation Validierung Konformitaetsbewertung Testberichte",
SectionStandardsApplied: "harmonisierte Normen ISO 12100 ISO 13849 IEC 62443 Vermutungswirkung",
SectionDeclarationConformity: "EU-Konformitaetserklaerung Maschinenverordnung 2023/1230 Anhang IV CE-Kennzeichnung",
SectionAIIntendedPurpose: "bestimmungsgemaesser Zweck KI-System AI Act Art. 13 Transparenz Intended Purpose",
SectionAIModelDescription: "KI-Modell Trainingsdaten Architektur AI Act Anhang IV technische Dokumentation",
SectionAIRiskManagement: "KI-Risikomanagementsystem AI Act Art. 9 Risikomanagement kuenstliche Intelligenz",
SectionAIHumanOversight: "menschliche Aufsicht Human Oversight AI Act Art. 14 Kontrolle KI-System",
SectionComponentList: "Komponentenliste Systemkomponenten sicherheitsrelevante Bauteile technische Dokumentation",
SectionClassificationReport: "regulatorische Klassifizierung Risikoklasse AI Act CRA Maschinenverordnung",
SectionMitigationReport: "Risikomindernde Massnahmen 3-Stufen-Methode ISO 12100 Schutzmassnahmen",
SectionVerificationReport: "Verifikation Validierung Pruefnachweis Konformitaetsbewertung Pruefprotokoll",
SectionEvidenceIndex: "Nachweisdokumente Evidence Konformitaetsnachweis Dokumentenindex",
SectionInstructionsForUse: "Betriebsanleitung Benutzerinformation Maschinenverordnung Abschnitt 1.7.4 Sicherheitshinweise",
SectionMonitoringPlan: "Post-Market-Monitoring Ueberwachungsplan AI Act Art. 72 Marktbeobachtung",
}
if q, ok := ragQueries[sectionType]; ok {
return q
}
return "CE-Konformitaet technische Dokumentation Maschinenverordnung AI Act"
}
// ============================================================================
// BuildSectionContext — loads all project data + RAG context
// ============================================================================
// BuildSectionContext loads project data and RAG context for a given section type.
func (g *TechFileGenerator) BuildSectionContext(ctx context.Context, projectID uuid.UUID, sectionType string) (*SectionGenerationContext, error) {
// Load project
project, err := g.store.GetProject(ctx, projectID)
if err != nil {
return nil, fmt.Errorf("load project: %w", err)
}
if project == nil {
return nil, fmt.Errorf("project %s not found", projectID)
}
// Load components
components, err := g.store.ListComponents(ctx, projectID)
if err != nil {
return nil, fmt.Errorf("load components: %w", err)
}
// Load hazards
hazards, err := g.store.ListHazards(ctx, projectID)
if err != nil {
return nil, fmt.Errorf("load hazards: %w", err)
}
// Load assessments and mitigations per hazard
assessments := make(map[uuid.UUID][]RiskAssessment)
mitigations := make(map[uuid.UUID][]Mitigation)
for _, h := range hazards {
a, err := g.store.ListAssessments(ctx, h.ID)
if err != nil {
return nil, fmt.Errorf("load assessments for hazard %s: %w", h.ID, err)
}
assessments[h.ID] = a
m, err := g.store.ListMitigations(ctx, h.ID)
if err != nil {
return nil, fmt.Errorf("load mitigations for hazard %s: %w", h.ID, err)
}
mitigations[h.ID] = m
}
// Load classifications
classifications, err := g.store.GetClassifications(ctx, projectID)
if err != nil {
return nil, fmt.Errorf("load classifications: %w", err)
}
// Load evidence
evidence, err := g.store.ListEvidence(ctx, projectID)
if err != nil {
return nil, fmt.Errorf("load evidence: %w", err)
}
// Perform RAG search for section-specific context
ragContext := ""
if g.ragClient != nil {
ragQuery := buildRAGQuery(sectionType)
results, ragErr := g.ragClient.SearchCollection(ctx, "bp_iace_libraries", ragQuery, nil, 5)
if ragErr == nil && len(results) > 0 {
var ragParts []string
for _, r := range results {
entry := fmt.Sprintf("[%s] %s", r.RegulationShort, truncateForPrompt(r.Text, 400))
ragParts = append(ragParts, entry)
}
ragContext = strings.Join(ragParts, "\n\n")
}
// RAG failure is non-fatal — we proceed without context
}
return &SectionGenerationContext{
Project: project,
Components: components,
Hazards: hazards,
Assessments: assessments,
Mitigations: mitigations,
Classifications: classifications,
Evidence: evidence,
RAGContext: ragContext,
}, nil
}
// ============================================================================
// GenerateSection — main entry point
// ============================================================================
// GenerateSection generates the content for a technical file section using LLM.
// If LLM is unavailable, returns an enhanced placeholder with project data.
func (g *TechFileGenerator) GenerateSection(ctx context.Context, projectID uuid.UUID, sectionType string) (string, error) {
sctx, err := g.BuildSectionContext(ctx, projectID, sectionType)
if err != nil {
return "", fmt.Errorf("build section context: %w", err)
}
// Build prompts
systemPrompt := getSystemPrompt(sectionType)
userPrompt := buildUserPrompt(sctx, sectionType)
// Attempt LLM generation
resp, err := g.llmRegistry.Chat(ctx, &llm.ChatRequest{
Messages: []llm.Message{
{Role: "system", Content: systemPrompt},
{Role: "user", Content: userPrompt},
},
Temperature: 0.15,
MaxTokens: 4096,
})
if err != nil {
// LLM unavailable — return structured fallback with real project data
return buildFallbackContent(sctx, sectionType), nil
}
return resp.Message.Content, nil
}
// ============================================================================
// Prompt builders
// ============================================================================
func getSystemPrompt(sectionType string) string {
if prompt, ok := sectionSystemPrompts[sectionType]; ok {
return prompt
}
return "Du bist CE-Experte fuer technische Dokumentation. Erstelle den angeforderten Abschnitt der technischen Dokumentation basierend auf den bereitgestellten Projektdaten. Schreibe auf Deutsch, verwende Fachterminologie und beziehe dich auf die konkreten Daten."
}
func buildUserPrompt(sctx *SectionGenerationContext, sectionType string) string {
var b strings.Builder
if sctx == nil || sctx.Project == nil {
b.WriteString("## Maschine / System\n\n- Keine Projektdaten vorhanden.\n\n")
return b.String()
}
// Machine info — always included
b.WriteString("## Maschine / System\n\n")
b.WriteString(fmt.Sprintf("- **Name:** %s\n", sctx.Project.MachineName))
b.WriteString(fmt.Sprintf("- **Typ:** %s\n", sctx.Project.MachineType))
b.WriteString(fmt.Sprintf("- **Hersteller:** %s\n", sctx.Project.Manufacturer))
if sctx.Project.Description != "" {
b.WriteString(fmt.Sprintf("- **Beschreibung:** %s\n", sctx.Project.Description))
}
if sctx.Project.CEMarkingTarget != "" {
b.WriteString(fmt.Sprintf("- **CE-Kennzeichnungsziel:** %s\n", sctx.Project.CEMarkingTarget))
}
if sctx.Project.NarrativeText != "" {
b.WriteString(fmt.Sprintf("\n**Projektbeschreibung:** %s\n", truncateForPrompt(sctx.Project.NarrativeText, 500)))
}
b.WriteString("\n")
// Components — for most section types
if len(sctx.Components) > 0 && needsComponents(sectionType) {
b.WriteString("## Komponenten\n\n")
for i, c := range sctx.Components {
if i >= 20 {
b.WriteString(fmt.Sprintf("... und %d weitere Komponenten\n", len(sctx.Components)-20))
break
}
safety := ""
if c.IsSafetyRelevant {
safety = " [SICHERHEITSRELEVANT]"
}
networked := ""
if c.IsNetworked {
networked = " [VERNETZT]"
}
b.WriteString(fmt.Sprintf("- %s (Typ: %s)%s%s", c.Name, string(c.ComponentType), safety, networked))
if c.Description != "" {
b.WriteString(fmt.Sprintf(" — %s", truncateForPrompt(c.Description, 100)))
}
b.WriteString("\n")
}
b.WriteString("\n")
}
// Hazards + assessments — for risk-related sections
if len(sctx.Hazards) > 0 && needsHazards(sectionType) {
b.WriteString("## Gefaehrdungen und Risikobewertungen\n\n")
for i, h := range sctx.Hazards {
if i >= 30 {
b.WriteString(fmt.Sprintf("... und %d weitere Gefaehrdungen\n", len(sctx.Hazards)-30))
break
}
b.WriteString(fmt.Sprintf("### %s\n", h.Name))
b.WriteString(fmt.Sprintf("- Kategorie: %s", h.Category))
if h.SubCategory != "" {
b.WriteString(fmt.Sprintf(" / %s", h.SubCategory))
}
b.WriteString("\n")
if h.LifecyclePhase != "" {
b.WriteString(fmt.Sprintf("- Lebenszyklusphase: %s\n", h.LifecyclePhase))
}
if h.Scenario != "" {
b.WriteString(fmt.Sprintf("- Szenario: %s\n", truncateForPrompt(h.Scenario, 150)))
}
if h.PossibleHarm != "" {
b.WriteString(fmt.Sprintf("- Moeglicher Schaden: %s\n", h.PossibleHarm))
}
if h.AffectedPerson != "" {
b.WriteString(fmt.Sprintf("- Betroffene Person: %s\n", h.AffectedPerson))
}
b.WriteString(fmt.Sprintf("- Status: %s\n", string(h.Status)))
// Latest assessment
if assessments, ok := sctx.Assessments[h.ID]; ok && len(assessments) > 0 {
a := assessments[len(assessments)-1] // latest
b.WriteString(fmt.Sprintf("- Bewertung: S=%d E=%d P=%d → Risiko=%.1f (%s) %s\n",
a.Severity, a.Exposure, a.Probability,
a.ResidualRisk, string(a.RiskLevel),
acceptableLabel(a.IsAcceptable)))
}
b.WriteString("\n")
}
}
// Mitigations — for mitigation/verification sections
if needsMitigations(sectionType) {
designMeasures, protectiveMeasures, infoMeasures := groupMitigations(sctx)
if len(designMeasures)+len(protectiveMeasures)+len(infoMeasures) > 0 {
b.WriteString("## Risikomindernde Massnahmen (3-Stufen-Methode)\n\n")
writeMitigationGroup(&b, "Stufe 1: Inhaerent sichere Konstruktion (Design)", designMeasures)
writeMitigationGroup(&b, "Stufe 2: Technische Schutzmassnahmen (Protective)", protectiveMeasures)
writeMitigationGroup(&b, "Stufe 3: Benutzerinformation (Information)", infoMeasures)
}
}
// Classifications — for classification/standards sections
if len(sctx.Classifications) > 0 && needsClassifications(sectionType) {
b.WriteString("## Regulatorische Klassifizierungen\n\n")
for _, c := range sctx.Classifications {
b.WriteString(fmt.Sprintf("- **%s:** %s (Risiko: %s)\n",
string(c.Regulation), c.ClassificationResult, string(c.RiskLevel)))
if c.Reasoning != "" {
b.WriteString(fmt.Sprintf(" Begruendung: %s\n", truncateForPrompt(c.Reasoning, 200)))
}
}
b.WriteString("\n")
}
// Evidence — for evidence/verification sections
if len(sctx.Evidence) > 0 && needsEvidence(sectionType) {
b.WriteString("## Vorhandene Nachweise\n\n")
for i, e := range sctx.Evidence {
if i >= 30 {
b.WriteString(fmt.Sprintf("... und %d weitere Nachweise\n", len(sctx.Evidence)-30))
break
}
b.WriteString(fmt.Sprintf("- %s", e.FileName))
if e.Description != "" {
b.WriteString(fmt.Sprintf(" — %s", truncateForPrompt(e.Description, 100)))
}
b.WriteString("\n")
}
b.WriteString("\n")
}
// RAG context — if available
if sctx.RAGContext != "" {
b.WriteString("## Relevante Rechtsgrundlagen (RAG)\n\n")
b.WriteString(sctx.RAGContext)
b.WriteString("\n\n")
}
// Instruction
b.WriteString("---\n\n")
b.WriteString("Erstelle den Abschnitt basierend auf den obigen Daten. Schreibe auf Deutsch, verwende Markdown-Formatierung und beziehe dich auf die konkreten Projektdaten.\n")
return b.String()
}
// ============================================================================
// Section type → data requirements
// ============================================================================
func needsComponents(sectionType string) bool {
switch sectionType {
case SectionGeneralDescription, SectionDesignSpecifications, SectionComponentList,
SectionEssentialRequirements, SectionAIModelDescription, SectionAIIntendedPurpose,
SectionClassificationReport, SectionInstructionsForUse:
return true
}
return false
}
func needsHazards(sectionType string) bool {
switch sectionType {
case SectionRiskAssessmentReport, SectionHazardLogCombined, SectionEssentialRequirements,
SectionMitigationReport, SectionVerificationReport, SectionTestReports,
SectionAIRiskManagement, SectionInstructionsForUse, SectionMonitoringPlan:
return true
}
return false
}
func needsMitigations(sectionType string) bool {
switch sectionType {
case SectionRiskAssessmentReport, SectionMitigationReport, SectionVerificationReport,
SectionTestReports, SectionEssentialRequirements, SectionAIRiskManagement,
SectionAIHumanOversight, SectionInstructionsForUse:
return true
}
return false
}
func needsClassifications(sectionType string) bool {
switch sectionType {
case SectionClassificationReport, SectionEssentialRequirements, SectionStandardsApplied,
SectionDeclarationConformity, SectionAIIntendedPurpose, SectionAIRiskManagement,
SectionGeneralDescription:
return true
}
return false
}
func needsEvidence(sectionType string) bool {
switch sectionType {
case SectionEvidenceIndex, SectionVerificationReport, SectionTestReports,
SectionMitigationReport:
return true
}
return false
}
// ============================================================================
// Mitigation grouping helper
// ============================================================================
func groupMitigations(sctx *SectionGenerationContext) (design, protective, info []Mitigation) {
for _, mits := range sctx.Mitigations {
for _, m := range mits {
switch m.ReductionType {
case ReductionTypeDesign:
design = append(design, m)
case ReductionTypeProtective:
protective = append(protective, m)
case ReductionTypeInformation:
info = append(info, m)
}
}
}
return
}
func writeMitigationGroup(b *strings.Builder, title string, measures []Mitigation) {
if len(measures) == 0 {
return
}
b.WriteString(fmt.Sprintf("### %s\n\n", title))
for i, m := range measures {
if i >= 20 {
b.WriteString(fmt.Sprintf("... und %d weitere Massnahmen\n", len(measures)-20))
break
}
b.WriteString(fmt.Sprintf("- **%s** [%s]", m.Name, string(m.Status)))
if m.VerificationMethod != "" {
b.WriteString(fmt.Sprintf(" — Verifikation: %s", string(m.VerificationMethod)))
if m.VerificationResult != "" {
b.WriteString(fmt.Sprintf(" (%s)", m.VerificationResult))
}
}
b.WriteString("\n")
if m.Description != "" {
b.WriteString(fmt.Sprintf(" %s\n", truncateForPrompt(m.Description, 150)))
}
}
b.WriteString("\n")
}
// ============================================================================
// Fallback content (when LLM is unavailable)
// ============================================================================
func buildFallbackContent(sctx *SectionGenerationContext, sectionType string) string {
var b strings.Builder
b.WriteString("[Automatisch generiert — LLM nicht verfuegbar]\n\n")
sectionTitle := sectionDisplayName(sectionType)
b.WriteString(fmt.Sprintf("# %s\n\n", sectionTitle))
b.WriteString(fmt.Sprintf("**Maschine:** %s (%s)\n", sctx.Project.MachineName, sctx.Project.MachineType))
b.WriteString(fmt.Sprintf("**Hersteller:** %s\n", sctx.Project.Manufacturer))
if sctx.Project.Description != "" {
b.WriteString(fmt.Sprintf("**Beschreibung:** %s\n", sctx.Project.Description))
}
b.WriteString("\n")
// Section-specific data summaries
switch sectionType {
case SectionComponentList, SectionGeneralDescription, SectionDesignSpecifications:
if len(sctx.Components) > 0 {
b.WriteString("## Komponenten\n\n")
b.WriteString(fmt.Sprintf("Anzahl: %d\n\n", len(sctx.Components)))
for _, c := range sctx.Components {
safety := ""
if c.IsSafetyRelevant {
safety = " [SICHERHEITSRELEVANT]"
}
b.WriteString(fmt.Sprintf("- %s (Typ: %s)%s\n", c.Name, string(c.ComponentType), safety))
}
b.WriteString("\n")
}
case SectionRiskAssessmentReport, SectionHazardLogCombined:
b.WriteString("## Risikoueberblick\n\n")
b.WriteString(fmt.Sprintf("Anzahl Gefaehrdungen: %d\n\n", len(sctx.Hazards)))
riskCounts := countRiskLevels(sctx)
for level, count := range riskCounts {
b.WriteString(fmt.Sprintf("- %s: %d\n", level, count))
}
b.WriteString("\n")
for _, h := range sctx.Hazards {
b.WriteString(fmt.Sprintf("- **%s** (%s) — Status: %s\n", h.Name, h.Category, string(h.Status)))
}
b.WriteString("\n")
case SectionMitigationReport:
design, protective, info := groupMitigations(sctx)
total := len(design) + len(protective) + len(info)
b.WriteString("## Massnahmenueberblick\n\n")
b.WriteString(fmt.Sprintf("Gesamt: %d Massnahmen\n", total))
b.WriteString(fmt.Sprintf("- Design: %d\n- Schutzmassnahmen: %d\n- Benutzerinformation: %d\n\n", len(design), len(protective), len(info)))
writeFallbackMitigationList(&b, "Design", design)
writeFallbackMitigationList(&b, "Schutzmassnahmen", protective)
writeFallbackMitigationList(&b, "Benutzerinformation", info)
case SectionClassificationReport:
if len(sctx.Classifications) > 0 {
b.WriteString("## Klassifizierungen\n\n")
for _, c := range sctx.Classifications {
b.WriteString(fmt.Sprintf("- **%s:** %s (Risiko: %s)\n",
string(c.Regulation), c.ClassificationResult, string(c.RiskLevel)))
}
b.WriteString("\n")
}
case SectionEvidenceIndex:
b.WriteString("## Nachweisverzeichnis\n\n")
b.WriteString(fmt.Sprintf("Anzahl Nachweise: %d\n\n", len(sctx.Evidence)))
for _, e := range sctx.Evidence {
desc := e.Description
if desc == "" {
desc = "(keine Beschreibung)"
}
b.WriteString(fmt.Sprintf("- %s — %s\n", e.FileName, desc))
}
b.WriteString("\n")
default:
// Generic fallback data summary
b.WriteString(fmt.Sprintf("- Komponenten: %d\n", len(sctx.Components)))
b.WriteString(fmt.Sprintf("- Gefaehrdungen: %d\n", len(sctx.Hazards)))
b.WriteString(fmt.Sprintf("- Klassifizierungen: %d\n", len(sctx.Classifications)))
b.WriteString(fmt.Sprintf("- Nachweise: %d\n", len(sctx.Evidence)))
b.WriteString("\n")
}
b.WriteString("---\n")
b.WriteString("*Dieser Abschnitt wurde ohne LLM-Unterstuetzung erstellt und enthaelt nur eine Datenuebersicht. Bitte erneut generieren, wenn der LLM-Service verfuegbar ist.*\n")
return b.String()
}
func writeFallbackMitigationList(b *strings.Builder, title string, measures []Mitigation) {
if len(measures) == 0 {
return
}
b.WriteString(fmt.Sprintf("### %s\n\n", title))
for _, m := range measures {
b.WriteString(fmt.Sprintf("- %s [%s]\n", m.Name, string(m.Status)))
}
b.WriteString("\n")
}
// ============================================================================
// Utility helpers
// ============================================================================
func countRiskLevels(sctx *SectionGenerationContext) map[string]int {
counts := make(map[string]int)
for _, h := range sctx.Hazards {
if assessments, ok := sctx.Assessments[h.ID]; ok && len(assessments) > 0 {
latest := assessments[len(assessments)-1]
counts[string(latest.RiskLevel)]++
}
}
return counts
}
func acceptableLabel(isAcceptable bool) string {
if isAcceptable {
return "[AKZEPTABEL]"
}
return "[NICHT AKZEPTABEL]"
}
func sectionDisplayName(sectionType string) string {
names := map[string]string{
SectionRiskAssessmentReport: "Zusammenfassung der Risikobeurteilung",
SectionHazardLogCombined: "Gefaehrdungsprotokoll (Hazard Log)",
SectionGeneralDescription: "Allgemeine Maschinenbeschreibung",
SectionEssentialRequirements: "Grundlegende Anforderungen (EHSR)",
SectionDesignSpecifications: "Konstruktionsdaten und Spezifikationen",
SectionTestReports: "Pruefberichte",
SectionStandardsApplied: "Angewandte Normen",
SectionDeclarationConformity: "EU-Konformitaetserklaerung",
SectionAIIntendedPurpose: "Bestimmungsgemaesser Zweck (KI)",
SectionAIModelDescription: "KI-Modellbeschreibung",
SectionAIRiskManagement: "KI-Risikomanagementsystem",
SectionAIHumanOversight: "Menschliche Aufsicht (Human Oversight)",
SectionComponentList: "Komponentenliste",
SectionClassificationReport: "Klassifizierungsbericht",
SectionMitigationReport: "Massnahmenbericht",
SectionVerificationReport: "Verifikationsbericht",
SectionEvidenceIndex: "Nachweisverzeichnis",
SectionInstructionsForUse: "Betriebsanleitung (Gliederung)",
SectionMonitoringPlan: "Post-Market-Monitoring-Plan",
}
if name, ok := names[sectionType]; ok {
return name
}
return sectionType
}
func truncateForPrompt(text string, maxLen int) string {
if len(text) <= maxLen {
return text
}
return text[:maxLen] + "..."
}