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] + "..." }