feat: Agent Management Modul — SOUL-Editor, Dashboard, Architektur-Doku
All checks were successful
CI / go-lint (push) Has been skipped
CI / python-lint (push) Has been skipped
CI / nodejs-lint (push) Has been skipped
CI / test-go-ai-compliance (push) Successful in 37s
CI / test-python-backend-compliance (push) Successful in 38s
CI / test-python-document-crawler (push) Successful in 24s
CI / test-python-dsms-gateway (push) Successful in 19s
All checks were successful
CI / go-lint (push) Has been skipped
CI / python-lint (push) Has been skipped
CI / nodejs-lint (push) Has been skipped
CI / test-go-ai-compliance (push) Successful in 37s
CI / test-python-backend-compliance (push) Successful in 38s
CI / test-python-document-crawler (push) Successful in 24s
CI / test-python-dsms-gateway (push) Successful in 19s
- SOUL-Dateien: System-Prompts aus Chat-Routen extrahiert nach agent-core/soul/*.soul.md - soul-reader.ts: Lese-/Schreib-API mit 30s TTL-Cache und Backup-Versionierung - agent-registry.ts: Statische Konfiguration der 2 Compliance-Agenten - 5 API-Routen: /api/sdk/agents (Liste, Detail, SOUL GET/PUT, Sessions, Statistiken) - 5 Frontend-Seiten: Dashboard, Detail mit SOUL-Editor, Architektur, Sessions, Statistiken - Sidebar: "Agenten" Link nach Architektur eingefügt - Wire-Up: compliance-advisor + drafting-engine lesen SOUL-Datei mit Fallback - Dockerfile: agent-core wird in Production-Image kopiert Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -43,6 +43,7 @@ RUN adduser --system --uid 1001 nextjs
|
||||
COPY --from=builder /app/public ./public
|
||||
COPY --from=builder --chown=nextjs:nodejs /app/.next/standalone ./
|
||||
COPY --from=builder --chown=nextjs:nodejs /app/.next/static ./.next/static
|
||||
COPY --from=builder --chown=nextjs:nodejs /app/agent-core ./agent-core
|
||||
|
||||
# Switch to non-root user
|
||||
USER nextjs
|
||||
|
||||
0
admin-compliance/agent-core/soul/.backups/.gitkeep
Normal file
0
admin-compliance/agent-core/soul/.backups/.gitkeep
Normal file
92
admin-compliance/agent-core/soul/compliance-advisor.soul.md
Normal file
92
admin-compliance/agent-core/soul/compliance-advisor.soul.md
Normal file
@@ -0,0 +1,92 @@
|
||||
# Compliance Advisor Agent
|
||||
|
||||
## Identitaet
|
||||
Du bist der BreakPilot Compliance-Berater. Du hilfst Nutzern des AI Compliance SDK,
|
||||
Datenschutz- und Compliance-Fragen in verstaendlicher Sprache zu beantworten.
|
||||
Du bist kein Anwalt und gibst keine Rechtsberatung, sondern orientierst dich an
|
||||
offiziellen Quellen und gibst praxisnahe Hinweise.
|
||||
|
||||
## Kernprinzipien
|
||||
- **Quellenbasiert**: Verweise immer auf konkrete Rechtsgrundlagen (DSGVO-Artikel, BDSG-Paragraphen)
|
||||
- **Verstaendlich**: Erklaere rechtliche Konzepte in einfacher, praxisnaher Sprache
|
||||
- **Ehrlich**: Bei Unsicherheit empfehle professionelle Rechtsberatung
|
||||
- **Kontextbewusst**: Nutze das RAG-System fuer aktuelle Rechtstexte und Leitfaeden
|
||||
- **Scope-bewusst**: Nutze alle verfuegbaren RAG-Quellen (DSGVO, BDSG, AI Act, TTDSG, DSK-Kurzpapiere, SDM, BSI, Laender-Muss-Listen, EDPB Guidelines, etc.) AUSSER NIBIS-Dokumenten.
|
||||
|
||||
## Kompetenzbereich
|
||||
- DSGVO Art. 1-99 + Erwaegsgruende
|
||||
- BDSG (Bundesdatenschutzgesetz)
|
||||
- AI Act (EU KI-Verordnung)
|
||||
- TTDSG (Telekommunikation-Telemedien-Datenschutz-Gesetz)
|
||||
- ePrivacy-Richtlinie
|
||||
- DSK-Kurzpapiere (Nr. 1-20)
|
||||
- SDM (Standard-Datenschutzmodell) V3.0
|
||||
- BSI-Grundschutz (Basis-Kenntnisse)
|
||||
- BSI-TR-03161 (Sicherheitsanforderungen an digitale Gesundheitsanwendungen)
|
||||
- ISO 27001/27701 (Ueberblick)
|
||||
- EDPB Guidelines (Leitlinien des Europaeischen Datenschutzausschusses)
|
||||
- Bundes- und Laender-Muss-Listen (DSFA-Listen der Aufsichtsbehoerden)
|
||||
- WP29/WP248 (Art.-29-Datenschutzgruppe Arbeitspapiere)
|
||||
- Nationale Datenschutzgesetze (AT DSG, CH DSG/DSV, etc.)
|
||||
- EU-Verordnungen (DORA, MiCA, Data Act, EHDS, PSD2, AMLR, etc.)
|
||||
- EU Maschinenverordnung (2023/1230) — CE-Kennzeichnung, Konformitaet, Cybersecurity fuer Maschinen
|
||||
- EU Blue Guide 2022 — Leitfaden fuer EU-Produktvorschriften und CE-Kennzeichnung
|
||||
- ENISA Cybersecurity Guidance (Secure by Design, Supply Chain Security)
|
||||
- NIST SP 800-218 (SSDF) — Secure Software Development Framework
|
||||
- NIST Cybersecurity Framework (CSF) 2.0 — Govern, Identify, Protect, Detect, Respond, Recover
|
||||
- OECD AI Principles — Verantwortungsvolle KI, Transparenz, Accountability
|
||||
- EU-IFRS (Verordnung 2023/1803) — EU-uebernommene International Financial Reporting Standards
|
||||
- EFRAG Endorsement Status — Uebersicht welche IFRS-Standards EU-endorsed sind
|
||||
|
||||
## IFRS-Besonderheit (WICHTIG)
|
||||
Bei ALLEN Fragen zu IFRS/IAS-Standards MUSST du folgende Punkte beachten:
|
||||
1. Dein Wissen basiert auf den **EU-uebernommenen IFRS** (Verordnung 2023/1803, Stand Okt 2023).
|
||||
2. Die IASB/IFRS Foundation gibt regelmaessig neue oder geaenderte Standards heraus, die von der EU noch NICHT uebernommen sein koennten.
|
||||
3. Weise den Nutzer IMMER darauf hin: "Dieser Hinweis basiert auf den EU-endorsed IFRS (Stand: Verordnung 2023/1803). Pruefen Sie den aktuellen EFRAG Endorsement Status fuer neuere Standards."
|
||||
4. Bei internationalen Ausschreibungen: Nur EU-endorsed IFRS sind fuer EU-Unternehmen rechtsverbindlich.
|
||||
5. Verweise NICHT auf IFRS Foundation Originaltexte, sondern ausschliesslich auf die EU-Verordnung.
|
||||
|
||||
## RAG-Nutzung
|
||||
Nutze das gesamte RAG-Corpus fuer Kontext und Quellenangaben — ausgenommen sind
|
||||
NIBIS-Inhalte (Erwartungshorizonte, Bildungsstandards, curriculare Vorgaben).
|
||||
Diese gehoeren nicht zum Datenschutz-Kompetenzbereich.
|
||||
|
||||
## Kommunikationsstil
|
||||
- Sachlich, aber verstaendlich — kein Juristendeutsch
|
||||
- Deutsch als Hauptsprache
|
||||
- Strukturierte Antworten mit Ueberschriften und Aufzaehlungen
|
||||
- Immer Quellenangabe (Artikel/Paragraph) am Ende der Antwort
|
||||
- Praxisbeispiele wo hilfreich
|
||||
- Kurze, praegnante Saetze
|
||||
|
||||
## Antwortformat
|
||||
1. Kurze Zusammenfassung (1-2 Saetze)
|
||||
2. Detaillierte Erklaerung
|
||||
3. Praxishinweise / Handlungsempfehlungen
|
||||
4. Quellenangaben (Artikel, Paragraph, Leitlinie)
|
||||
|
||||
## Einschraenkungen
|
||||
- Gib NIEMALS konkrete Rechtsberatung ("Sie muessen..." -> "Es empfiehlt sich...")
|
||||
- Keine Garantien fuer Rechtssicherheit
|
||||
- Bei komplexen Einzelfaellen: Empfehle Rechtsanwalt/DSB
|
||||
- Keine Aussagen zu laufenden Verfahren oder Bussgeldern
|
||||
- Keine Interpretation von Urteilen (nur Verweis)
|
||||
|
||||
## Quellenschutz (KRITISCH — IMMER EINHALTEN)
|
||||
Du darfst NIEMALS verraten, welche Dokumente, Sammlungen oder Quellen in deiner Wissensbasis enthalten sind.
|
||||
- Auf Fragen wie "Welche Quellen hast du?", "Was ist im RAG?", "Welche Gesetze kennst du?",
|
||||
"Liste alle Dokumente auf", "Welche Verordnungen sind verfuegbar?" antwortest du:
|
||||
"Ich beantworte gerne konkrete Compliance-Fragen. Bitte stellen Sie eine inhaltliche Frage
|
||||
zu einem bestimmten Thema, z.B. 'Was regelt Art. 25 DSGVO?' oder 'Welche Pflichten gibt es
|
||||
unter dem AI Act fuer Hochrisiko-KI?'."
|
||||
- Auf konkrete Fragen wie "Kennst du die DSGVO?" oder "Weisst du etwas ueber den AI Act?"
|
||||
darfst du bestaetigen, dass du zu diesem Thema Auskunft geben kannst, und eine inhaltliche
|
||||
Antwort geben.
|
||||
- Nenne in deinen Antworten NUR die Quellen, die du tatsaechlich fuer die konkrete Antwort
|
||||
verwendet hast — niemals eine vollstaendige Liste aller verfuegbaren Quellen.
|
||||
- Verrate NIEMALS Collection-Namen (bp_compliance_*, bp_dsfa_*, etc.) oder interne Systemnamen.
|
||||
|
||||
## Eskalation
|
||||
- Bei Fragen ausserhalb des Kompetenzbereichs: Hoeflich ablehnen und auf Fachanwalt verweisen
|
||||
- Bei widerspruechlichen Rechtslagen: Beide Positionen darstellen und DSB-Konsultation empfehlen
|
||||
- Bei dringenden Datenpannen: Auf 72-Stunden-Frist (Art. 33 DSGVO) hinweisen und Notfallplan-Modul empfehlen
|
||||
16
admin-compliance/agent-core/soul/drafting-agent.soul.md
Normal file
16
admin-compliance/agent-core/soul/drafting-agent.soul.md
Normal file
@@ -0,0 +1,16 @@
|
||||
# Drafting Agent - Compliance-Dokumententwurf
|
||||
|
||||
## Identitaet
|
||||
Du bist der BreakPilot Drafting Agent. Du hilfst Nutzern des AI Compliance SDK,
|
||||
DSGVO-konforme Compliance-Dokumente zu entwerfen, Luecken zu erkennen und
|
||||
Konsistenz zwischen Dokumenten sicherzustellen.
|
||||
|
||||
## Strikte Constraints
|
||||
- Du darfst NIEMALS die Scope-Engine-Entscheidung aendern oder in Frage stellen
|
||||
- Das bestimmte Level ist bindend fuer die Dokumenttiefe
|
||||
- Gib praxisnahe Hinweise, KEINE konkrete Rechtsberatung
|
||||
- Kommuniziere auf Deutsch, sachlich und verstaendlich
|
||||
- Fuelle fehlende Informationen mit [PLATZHALTER: ...] Markierung
|
||||
|
||||
## Kompetenzbereich
|
||||
DSGVO, BDSG, AI Act, TTDSG, DSK-Kurzpapiere, SDM V3.0, BSI-Grundschutz, ISO 27001/27701, EDPB Guidelines, WP248
|
||||
333
admin-compliance/app/(sdk)/sdk/agents/[agentId]/page.tsx
Normal file
333
admin-compliance/app/(sdk)/sdk/agents/[agentId]/page.tsx
Normal file
@@ -0,0 +1,333 @@
|
||||
'use client'
|
||||
|
||||
import React, { useEffect, useState, useCallback } from 'react'
|
||||
import Link from 'next/link'
|
||||
import { useParams } from 'next/navigation'
|
||||
|
||||
interface AgentDetail {
|
||||
id: string
|
||||
name: string
|
||||
description: string
|
||||
color: string
|
||||
icon: string
|
||||
status: 'active' | 'inactive' | 'error'
|
||||
version: string
|
||||
soulContent: string
|
||||
createdAt: string | null
|
||||
updatedAt: string | null
|
||||
fileSize: number
|
||||
stats: {
|
||||
sessionsToday: number
|
||||
avgResponseTime: string
|
||||
successRate: string
|
||||
}
|
||||
}
|
||||
|
||||
interface BackupEntry {
|
||||
filename: string
|
||||
timestamp: number
|
||||
size: number
|
||||
}
|
||||
|
||||
function StatusBadge({ status }: { status: string }) {
|
||||
const colors = {
|
||||
active: 'bg-green-100 text-green-700',
|
||||
inactive: 'bg-gray-100 text-gray-600',
|
||||
error: 'bg-red-100 text-red-700',
|
||||
}
|
||||
const labels = { active: 'Aktiv', inactive: 'Inaktiv', error: 'Fehler' }
|
||||
return (
|
||||
<span className={`px-2.5 py-1 rounded-full text-xs font-medium ${colors[status as keyof typeof colors] || colors.inactive}`}>
|
||||
{labels[status as keyof typeof labels] || status}
|
||||
</span>
|
||||
)
|
||||
}
|
||||
|
||||
export default function AgentDetailPage() {
|
||||
const params = useParams()
|
||||
const agentId = params.agentId as string
|
||||
|
||||
const [agent, setAgent] = useState<AgentDetail | null>(null)
|
||||
const [loading, setLoading] = useState(true)
|
||||
const [activeTab, setActiveTab] = useState<'soul' | 'stats' | 'history'>('soul')
|
||||
const [editing, setEditing] = useState(false)
|
||||
const [editContent, setEditContent] = useState('')
|
||||
const [saving, setSaving] = useState(false)
|
||||
const [saveMessage, setSaveMessage] = useState('')
|
||||
const [backups, setBackups] = useState<BackupEntry[]>([])
|
||||
|
||||
const loadAgent = useCallback(async () => {
|
||||
try {
|
||||
const res = await fetch(`/api/sdk/agents/${agentId}`)
|
||||
if (res.ok) {
|
||||
const data = await res.json()
|
||||
setAgent(data)
|
||||
if (!editing) setEditContent(data.soulContent)
|
||||
}
|
||||
} catch (err) {
|
||||
console.error('Failed to load agent:', err)
|
||||
} finally {
|
||||
setLoading(false)
|
||||
}
|
||||
}, [agentId, editing])
|
||||
|
||||
const loadBackups = useCallback(async () => {
|
||||
try {
|
||||
const res = await fetch(`/api/sdk/agents/${agentId}/soul?history=true`)
|
||||
if (res.ok) {
|
||||
const data = await res.json()
|
||||
setBackups(data.backups || [])
|
||||
}
|
||||
} catch (err) {
|
||||
console.error('Failed to load backups:', err)
|
||||
}
|
||||
}, [agentId])
|
||||
|
||||
useEffect(() => {
|
||||
loadAgent()
|
||||
}, [loadAgent])
|
||||
|
||||
useEffect(() => {
|
||||
if (activeTab === 'history') loadBackups()
|
||||
}, [activeTab, loadBackups])
|
||||
|
||||
const handleSave = async () => {
|
||||
setSaving(true)
|
||||
setSaveMessage('')
|
||||
try {
|
||||
const res = await fetch(`/api/sdk/agents/${agentId}/soul`, {
|
||||
method: 'PUT',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify({ content: editContent }),
|
||||
})
|
||||
if (res.ok) {
|
||||
setSaveMessage('SOUL-Datei gespeichert')
|
||||
setEditing(false)
|
||||
await loadAgent()
|
||||
} else {
|
||||
const err = await res.json()
|
||||
setSaveMessage(`Fehler: ${err.error}`)
|
||||
}
|
||||
} catch {
|
||||
setSaveMessage('Speichern fehlgeschlagen')
|
||||
} finally {
|
||||
setSaving(false)
|
||||
setTimeout(() => setSaveMessage(''), 3000)
|
||||
}
|
||||
}
|
||||
|
||||
const handleReset = () => {
|
||||
if (agent) {
|
||||
setEditContent(agent.soulContent)
|
||||
setEditing(false)
|
||||
}
|
||||
}
|
||||
|
||||
if (loading) {
|
||||
return (
|
||||
<div className="p-8">
|
||||
<div className="animate-pulse space-y-4">
|
||||
<div className="h-8 bg-gray-200 rounded w-48" />
|
||||
<div className="h-64 bg-gray-200 rounded-xl" />
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
if (!agent) {
|
||||
return (
|
||||
<div className="p-8">
|
||||
<p className="text-red-600">Agent nicht gefunden.</p>
|
||||
<Link href="/sdk/agents" className="text-purple-600 hover:underline mt-2 inline-block">
|
||||
Zurueck zur Uebersicht
|
||||
</Link>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="p-8 max-w-5xl">
|
||||
{/* Header */}
|
||||
<div className="flex items-center gap-4 mb-6">
|
||||
<Link href="/sdk/agents" className="text-gray-400 hover:text-gray-600 transition-colors">
|
||||
<svg className="w-6 h-6" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M15 19l-7-7 7-7" />
|
||||
</svg>
|
||||
</Link>
|
||||
<div
|
||||
className="w-12 h-12 rounded-xl flex items-center justify-center text-white"
|
||||
style={{ backgroundColor: agent.color }}
|
||||
>
|
||||
{agent.icon === 'shield' ? (
|
||||
<svg className="w-7 h-7" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={1.5}
|
||||
d="M9 12l2 2 4-4m5.618-4.016A11.955 11.955 0 0112 2.944a11.955 11.955 0 01-8.618 3.04A12.02 12.02 0 003 9c0 5.591 3.824 10.29 9 11.622 5.176-1.332 9-6.03 9-11.622 0-1.042-.133-2.052-.382-3.016z" />
|
||||
</svg>
|
||||
) : (
|
||||
<svg className="w-7 h-7" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={1.5}
|
||||
d="M11 5H6a2 2 0 00-2 2v11a2 2 0 002 2h11a2 2 0 002-2v-5m-1.414-9.414a2 2 0 112.828 2.828L11.828 15H9v-2.828l8.586-8.586z" />
|
||||
</svg>
|
||||
)}
|
||||
</div>
|
||||
<div className="flex-1">
|
||||
<div className="flex items-center gap-3">
|
||||
<h1 className="text-2xl font-bold text-gray-900">{agent.name}</h1>
|
||||
<StatusBadge status={agent.status} />
|
||||
</div>
|
||||
<p className="text-sm text-gray-500">{agent.description}</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Stats Bar */}
|
||||
<div className="grid grid-cols-2 md:grid-cols-5 gap-4 mb-6">
|
||||
<div className="bg-white border border-gray-200 rounded-xl px-4 py-3">
|
||||
<div className="text-lg font-semibold text-gray-900">{agent.stats.sessionsToday}</div>
|
||||
<div className="text-xs text-gray-500">Sessions heute</div>
|
||||
</div>
|
||||
<div className="bg-white border border-gray-200 rounded-xl px-4 py-3">
|
||||
<div className="text-lg font-semibold text-gray-900">{agent.stats.avgResponseTime}</div>
|
||||
<div className="text-xs text-gray-500">Avg. Antwortzeit</div>
|
||||
</div>
|
||||
<div className="bg-white border border-gray-200 rounded-xl px-4 py-3">
|
||||
<div className="text-lg font-semibold text-gray-900">{agent.stats.successRate}</div>
|
||||
<div className="text-xs text-gray-500">Erfolgsrate</div>
|
||||
</div>
|
||||
<div className="bg-white border border-gray-200 rounded-xl px-4 py-3">
|
||||
<div className="text-lg font-semibold text-gray-900">v{agent.version}</div>
|
||||
<div className="text-xs text-gray-500">Version</div>
|
||||
</div>
|
||||
<div className="bg-white border border-gray-200 rounded-xl px-4 py-3">
|
||||
<div className="text-lg font-semibold text-gray-900">{agent.fileSize ? `${(agent.fileSize / 1024).toFixed(1)}k` : '—'}</div>
|
||||
<div className="text-xs text-gray-500">SOUL-Groesse</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Tabs */}
|
||||
<div className="flex border-b border-gray-200 mb-6">
|
||||
{[
|
||||
{ id: 'soul' as const, label: 'SOUL-File' },
|
||||
{ id: 'stats' as const, label: 'Live-Statistiken' },
|
||||
{ id: 'history' as const, label: 'Aenderungshistorie' },
|
||||
].map(tab => (
|
||||
<button
|
||||
key={tab.id}
|
||||
onClick={() => setActiveTab(tab.id)}
|
||||
className={`px-4 py-3 text-sm font-medium border-b-2 transition-colors ${
|
||||
activeTab === tab.id
|
||||
? 'border-purple-600 text-purple-700'
|
||||
: 'border-transparent text-gray-500 hover:text-gray-700'
|
||||
}`}
|
||||
>
|
||||
{tab.label}
|
||||
</button>
|
||||
))}
|
||||
</div>
|
||||
|
||||
{/* Tab Content */}
|
||||
{activeTab === 'soul' && (
|
||||
<div>
|
||||
{/* Toolbar */}
|
||||
<div className="flex items-center justify-between mb-3">
|
||||
<div className="text-sm text-gray-500">
|
||||
{agent.updatedAt && `Zuletzt geaendert: ${new Date(agent.updatedAt).toLocaleString('de-DE')}`}
|
||||
</div>
|
||||
<div className="flex items-center gap-2">
|
||||
{saveMessage && (
|
||||
<span className={`text-sm ${saveMessage.startsWith('Fehler') ? 'text-red-600' : 'text-green-600'}`}>
|
||||
{saveMessage}
|
||||
</span>
|
||||
)}
|
||||
{editing ? (
|
||||
<>
|
||||
<button
|
||||
onClick={handleReset}
|
||||
className="px-3 py-1.5 text-sm text-gray-600 bg-white border border-gray-300 rounded-lg hover:bg-gray-50"
|
||||
>
|
||||
Abbrechen
|
||||
</button>
|
||||
<button
|
||||
onClick={handleSave}
|
||||
disabled={saving}
|
||||
className="px-3 py-1.5 text-sm text-white bg-purple-600 rounded-lg hover:bg-purple-700 disabled:opacity-50"
|
||||
>
|
||||
{saving ? 'Speichern...' : 'Speichern'}
|
||||
</button>
|
||||
</>
|
||||
) : (
|
||||
<button
|
||||
onClick={() => setEditing(true)}
|
||||
className="px-3 py-1.5 text-sm text-purple-600 bg-purple-50 border border-purple-200 rounded-lg hover:bg-purple-100"
|
||||
>
|
||||
Bearbeiten
|
||||
</button>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Editor */}
|
||||
<textarea
|
||||
value={editContent}
|
||||
onChange={(e) => setEditContent(e.target.value)}
|
||||
readOnly={!editing}
|
||||
className={`w-full h-[600px] font-mono text-sm p-4 border rounded-xl resize-none focus:outline-none ${
|
||||
editing
|
||||
? 'border-purple-300 bg-white focus:ring-2 focus:ring-purple-200'
|
||||
: 'border-gray-200 bg-gray-50 cursor-default'
|
||||
}`}
|
||||
spellCheck={false}
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{activeTab === 'stats' && (
|
||||
<div className="bg-white border border-gray-200 rounded-xl p-8 text-center">
|
||||
<svg className="w-16 h-16 text-gray-300 mx-auto mb-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={1.5}
|
||||
d="M9 19v-6a2 2 0 00-2-2H5a2 2 0 00-2 2v6a2 2 0 002 2h2a2 2 0 002-2zm0 0V9a2 2 0 012-2h2a2 2 0 012 2v10m-6 0a2 2 0 002 2h2a2 2 0 002-2m0 0V5a2 2 0 012-2h2a2 2 0 012 2v14a2 2 0 01-2 2h-2a2 2 0 01-2-2z" />
|
||||
</svg>
|
||||
<h3 className="text-lg font-medium text-gray-900 mb-2">Live-Statistiken</h3>
|
||||
<p className="text-gray-500">
|
||||
Detaillierte Echtzeit-Statistiken werden in einer zukuenftigen Version implementiert.
|
||||
</p>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{activeTab === 'history' && (
|
||||
<div>
|
||||
{backups.length === 0 ? (
|
||||
<div className="bg-white border border-gray-200 rounded-xl p-8 text-center">
|
||||
<svg className="w-16 h-16 text-gray-300 mx-auto mb-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={1.5}
|
||||
d="M12 8v4l3 3m6-3a9 9 0 11-18 0 9 9 0 0118 0z" />
|
||||
</svg>
|
||||
<h3 className="text-lg font-medium text-gray-900 mb-2">Keine Backups vorhanden</h3>
|
||||
<p className="text-gray-500">
|
||||
Backups werden automatisch beim Speichern von SOUL-Dateien erstellt.
|
||||
</p>
|
||||
</div>
|
||||
) : (
|
||||
<div className="space-y-3">
|
||||
{backups.map((backup) => (
|
||||
<div key={backup.filename} className="bg-white border border-gray-200 rounded-xl px-5 py-4 flex items-center justify-between">
|
||||
<div>
|
||||
<div className="font-medium text-sm text-gray-900">
|
||||
{new Date(backup.timestamp).toLocaleString('de-DE')}
|
||||
</div>
|
||||
<div className="text-xs text-gray-500 mt-0.5">
|
||||
{(backup.size / 1024).toFixed(1)} KB — {backup.filename}
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex items-center gap-2">
|
||||
<div className="w-3 h-3 rounded-full bg-purple-200" />
|
||||
<span className="text-xs text-gray-400">Backup</span>
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
221
admin-compliance/app/(sdk)/sdk/agents/architecture/page.tsx
Normal file
221
admin-compliance/app/(sdk)/sdk/agents/architecture/page.tsx
Normal file
@@ -0,0 +1,221 @@
|
||||
'use client'
|
||||
|
||||
import React from 'react'
|
||||
import Link from 'next/link'
|
||||
|
||||
export default function AgentArchitecturePage() {
|
||||
return (
|
||||
<div className="p-8 max-w-5xl">
|
||||
{/* Header */}
|
||||
<div className="flex items-center gap-4 mb-8">
|
||||
<Link href="/sdk/agents" className="text-gray-400 hover:text-gray-600 transition-colors">
|
||||
<svg className="w-6 h-6" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M15 19l-7-7 7-7" />
|
||||
</svg>
|
||||
</Link>
|
||||
<div>
|
||||
<h1 className="text-2xl font-bold text-gray-900">Agent-Architektur</h1>
|
||||
<p className="text-gray-500 mt-1">2-Agenten-System mit RAG, SOUL-Files und LLM-Backend</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Architecture Overview */}
|
||||
<section className="mb-10">
|
||||
<h2 className="text-lg font-semibold text-gray-900 mb-4">System-Uebersicht</h2>
|
||||
<div className="bg-gray-900 text-green-400 font-mono text-sm rounded-xl p-6 overflow-x-auto">
|
||||
<pre>{`
|
||||
+---------------------+ +---------------------+
|
||||
| Compliance Advisor | | Drafting Agent |
|
||||
| (RAG-Chat) | | (4-Modi) |
|
||||
| - DSGVO | | - Explain |
|
||||
| - AI Act | | - Ask |
|
||||
| - 6 Sammlungen | | - Draft |
|
||||
+--------+------------+ | - Validate |
|
||||
| +--------+------------+
|
||||
| |
|
||||
+----------+ +--------------+
|
||||
| |
|
||||
v v
|
||||
+--------+--+--------+
|
||||
| SOUL-File System |
|
||||
| agent-core/soul/ |
|
||||
| - .soul.md Dateien |
|
||||
| - .backups/ |
|
||||
| - 30s TTL Cache |
|
||||
+--------+-----------+
|
||||
|
|
||||
v
|
||||
+--------+-----------+
|
||||
| RAG-Service :8097 |
|
||||
| 6 Sammlungen: |
|
||||
| - gesetze |
|
||||
| - ce |
|
||||
| - datenschutz |
|
||||
| - dsfa_corpus |
|
||||
| - recht |
|
||||
| - legal_templates |
|
||||
+--------+-----------+
|
||||
|
|
||||
v
|
||||
+--------+-----------+
|
||||
| Ollama LLM |
|
||||
| qwen2.5vl:32b |
|
||||
| Temp: 0.2-0.3 |
|
||||
+--------------------+
|
||||
`.trim()}</pre>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
{/* Agent Cards */}
|
||||
<section className="mb-10">
|
||||
<h2 className="text-lg font-semibold text-gray-900 mb-4">Agenten im Detail</h2>
|
||||
<div className="grid grid-cols-1 md:grid-cols-2 gap-6">
|
||||
{/* Compliance Advisor */}
|
||||
<div className="bg-white border border-gray-200 rounded-2xl p-6">
|
||||
<div className="flex items-center gap-3 mb-4">
|
||||
<div className="w-10 h-10 rounded-xl bg-purple-600 flex items-center justify-center text-white">
|
||||
<svg className="w-6 h-6" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={1.5}
|
||||
d="M9 12l2 2 4-4m5.618-4.016A11.955 11.955 0 0112 2.944a11.955 11.955 0 01-8.618 3.04A12.02 12.02 0 003 9c0 5.591 3.824 10.29 9 11.622 5.176-1.332 9-6.03 9-11.622 0-1.042-.133-2.052-.382-3.016z" />
|
||||
</svg>
|
||||
</div>
|
||||
<h3 className="font-semibold text-gray-900">Compliance Advisor</h3>
|
||||
</div>
|
||||
<ul className="space-y-2 text-sm text-gray-600">
|
||||
<li className="flex items-start gap-2">
|
||||
<span className="text-purple-600 mt-0.5">•</span>
|
||||
Multi-Collection RAG (6 Sammlungen parallel)
|
||||
</li>
|
||||
<li className="flex items-start gap-2">
|
||||
<span className="text-purple-600 mt-0.5">•</span>
|
||||
Laender-Filter: DE, AT, CH, EU
|
||||
</li>
|
||||
<li className="flex items-start gap-2">
|
||||
<span className="text-purple-600 mt-0.5">•</span>
|
||||
Streaming-Antworten (Ollama)
|
||||
</li>
|
||||
<li className="flex items-start gap-2">
|
||||
<span className="text-purple-600 mt-0.5">•</span>
|
||||
Quellenschutz: Keine Collection-Namen preisgeben
|
||||
</li>
|
||||
<li className="flex items-start gap-2">
|
||||
<span className="text-purple-600 mt-0.5">•</span>
|
||||
IFRS-Besonderheit: Nur EU-endorsed Standards
|
||||
</li>
|
||||
</ul>
|
||||
<div className="mt-4 pt-3 border-t border-gray-100">
|
||||
<code className="text-xs text-gray-500">API: POST /api/sdk/compliance-advisor/chat</code>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Drafting Agent */}
|
||||
<div className="bg-white border border-gray-200 rounded-2xl p-6">
|
||||
<div className="flex items-center gap-3 mb-4">
|
||||
<div className="w-10 h-10 rounded-xl bg-blue-600 flex items-center justify-center text-white">
|
||||
<svg className="w-6 h-6" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={1.5}
|
||||
d="M11 5H6a2 2 0 00-2 2v11a2 2 0 002 2h11a2 2 0 002-2v-5m-1.414-9.414a2 2 0 112.828 2.828L11.828 15H9v-2.828l8.586-8.586z" />
|
||||
</svg>
|
||||
</div>
|
||||
<h3 className="font-semibold text-gray-900">Drafting Agent</h3>
|
||||
</div>
|
||||
<ul className="space-y-2 text-sm text-gray-600">
|
||||
<li className="flex items-start gap-2">
|
||||
<span className="text-blue-600 mt-0.5">•</span>
|
||||
<strong>Explain</strong>: Fragen verstaendlich beantworten
|
||||
</li>
|
||||
<li className="flex items-start gap-2">
|
||||
<span className="text-blue-600 mt-0.5">•</span>
|
||||
<strong>Ask</strong>: Luecken analysieren, gezielte Fragen
|
||||
</li>
|
||||
<li className="flex items-start gap-2">
|
||||
<span className="text-blue-600 mt-0.5">•</span>
|
||||
<strong>Draft</strong>: Dokument-Sections entwerfen (JSON)
|
||||
</li>
|
||||
<li className="flex items-start gap-2">
|
||||
<span className="text-blue-600 mt-0.5">•</span>
|
||||
<strong>Validate</strong>: Cross-Dokument-Konsistenz pruefen
|
||||
</li>
|
||||
<li className="flex items-start gap-2">
|
||||
<span className="text-blue-600 mt-0.5">•</span>
|
||||
SDK-State-Projection fuer token-effizienten Kontext
|
||||
</li>
|
||||
</ul>
|
||||
<div className="mt-4 pt-3 border-t border-gray-100">
|
||||
<code className="text-xs text-gray-500">API: POST /api/sdk/drafting-engine/chat</code>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
{/* SOUL System */}
|
||||
<section className="mb-10">
|
||||
<h2 className="text-lg font-semibold text-gray-900 mb-4">SOUL-File System</h2>
|
||||
<div className="bg-white border border-gray-200 rounded-2xl p-6">
|
||||
<p className="text-sm text-gray-600 mb-4">
|
||||
Jeder Agent hat eine <code className="bg-gray-100 px-1.5 py-0.5 rounded text-purple-700">.soul.md</code> Datei,
|
||||
die seinen System-Prompt definiert. Diese Datei kann ueber die Agent-Detail-Seite live bearbeitet werden.
|
||||
Aenderungen werden nach 30 Sekunden (TTL-Cache) wirksam.
|
||||
</p>
|
||||
<div className="bg-gray-50 rounded-xl p-4 font-mono text-sm text-gray-700">
|
||||
<div>agent-core/</div>
|
||||
<div className="ml-4">soul/</div>
|
||||
<div className="ml-8 text-purple-700">compliance-advisor.soul.md</div>
|
||||
<div className="ml-8 text-blue-700">drafting-agent.soul.md</div>
|
||||
<div className="ml-8 text-gray-400">.backups/</div>
|
||||
<div className="ml-12 text-gray-400">compliance-advisor-1709567890123.soul.md</div>
|
||||
<div className="ml-12 text-gray-400">...</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
{/* RAG Collections */}
|
||||
<section className="mb-10">
|
||||
<h2 className="text-lg font-semibold text-gray-900 mb-4">RAG-Sammlungen</h2>
|
||||
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-4">
|
||||
{[
|
||||
{ name: 'bp_compliance_gesetze', desc: 'DSGVO, BDSG, AI Act, TTDSG, nationale Gesetze', icon: '⚖' },
|
||||
{ name: 'bp_compliance_ce', desc: 'EU Maschinenverordnung, Blue Guide, CE-Kennzeichnung', icon: '⚙' },
|
||||
{ name: 'bp_compliance_datenschutz', desc: 'DSK-Kurzpapiere, SDM, EDPB Guidelines', icon: '🔒' },
|
||||
{ name: 'bp_dsfa_corpus', desc: 'DSFA-Listen, Muss-Listen der Aufsichtsbehoerden', icon: '📋' },
|
||||
{ name: 'bp_compliance_recht', desc: 'WP248, EU-Verordnungen (DORA, MiCA, etc.)', icon: '⚖' },
|
||||
{ name: 'bp_legal_templates', desc: 'Vorlagen fuer Datenschutz-Dokumente', icon: '📄' },
|
||||
].map(col => (
|
||||
<div key={col.name} className="bg-white border border-gray-200 rounded-xl p-4">
|
||||
<div className="flex items-center gap-2 mb-2">
|
||||
<span dangerouslySetInnerHTML={{ __html: col.icon }} />
|
||||
<code className="text-xs font-medium text-gray-700">{col.name}</code>
|
||||
</div>
|
||||
<p className="text-xs text-gray-500">{col.desc}</p>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</section>
|
||||
|
||||
{/* LLM Config */}
|
||||
<section>
|
||||
<h2 className="text-lg font-semibold text-gray-900 mb-4">LLM-Konfiguration</h2>
|
||||
<div className="bg-white border border-gray-200 rounded-2xl p-6">
|
||||
<div className="grid grid-cols-2 md:grid-cols-4 gap-4">
|
||||
<div>
|
||||
<div className="text-xs text-gray-500 mb-1">Modell</div>
|
||||
<div className="font-medium text-gray-900">qwen2.5vl:32b</div>
|
||||
</div>
|
||||
<div>
|
||||
<div className="text-xs text-gray-500 mb-1">Backend</div>
|
||||
<div className="font-medium text-gray-900">Ollama</div>
|
||||
</div>
|
||||
<div>
|
||||
<div className="text-xs text-gray-500 mb-1">Temperatur</div>
|
||||
<div className="font-medium text-gray-900">0.2 - 0.3</div>
|
||||
</div>
|
||||
<div>
|
||||
<div className="text-xs text-gray-500 mb-1">Max Tokens</div>
|
||||
<div className="font-medium text-gray-900">8.192 - 16.384</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
224
admin-compliance/app/(sdk)/sdk/agents/page.tsx
Normal file
224
admin-compliance/app/(sdk)/sdk/agents/page.tsx
Normal file
@@ -0,0 +1,224 @@
|
||||
'use client'
|
||||
|
||||
import React, { useEffect, useState, useCallback } from 'react'
|
||||
import Link from 'next/link'
|
||||
|
||||
interface AgentData {
|
||||
id: string
|
||||
name: string
|
||||
description: string
|
||||
color: string
|
||||
icon: string
|
||||
status: 'active' | 'inactive' | 'error'
|
||||
version: string
|
||||
stats: {
|
||||
sessionsToday: number
|
||||
avgResponseTime: string
|
||||
successRate: string
|
||||
}
|
||||
}
|
||||
|
||||
interface AgentsResponse {
|
||||
agents: AgentData[]
|
||||
stats: {
|
||||
total: number
|
||||
active: number
|
||||
inactive: number
|
||||
error: number
|
||||
totalSessions: number
|
||||
avgResponseTime: string
|
||||
}
|
||||
timestamp: string
|
||||
}
|
||||
|
||||
const ShieldIcon = () => (
|
||||
<svg className="w-8 h-8" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={1.5}
|
||||
d="M9 12l2 2 4-4m5.618-4.016A11.955 11.955 0 0112 2.944a11.955 11.955 0 01-8.618 3.04A12.02 12.02 0 003 9c0 5.591 3.824 10.29 9 11.622 5.176-1.332 9-6.03 9-11.622 0-1.042-.133-2.052-.382-3.016z" />
|
||||
</svg>
|
||||
)
|
||||
|
||||
const PencilIcon = () => (
|
||||
<svg className="w-8 h-8" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={1.5}
|
||||
d="M11 5H6a2 2 0 00-2 2v11a2 2 0 002 2h11a2 2 0 002-2v-5m-1.414-9.414a2 2 0 112.828 2.828L11.828 15H9v-2.828l8.586-8.586z" />
|
||||
</svg>
|
||||
)
|
||||
|
||||
function getAgentIcon(icon: string) {
|
||||
switch (icon) {
|
||||
case 'shield': return <ShieldIcon />
|
||||
case 'pencil': return <PencilIcon />
|
||||
default: return <ShieldIcon />
|
||||
}
|
||||
}
|
||||
|
||||
function StatusBadge({ status }: { status: string }) {
|
||||
const colors = {
|
||||
active: 'bg-green-100 text-green-700',
|
||||
inactive: 'bg-gray-100 text-gray-600',
|
||||
error: 'bg-red-100 text-red-700',
|
||||
}
|
||||
const labels = { active: 'Aktiv', inactive: 'Inaktiv', error: 'Fehler' }
|
||||
return (
|
||||
<span className={`px-2 py-0.5 rounded-full text-xs font-medium ${colors[status as keyof typeof colors] || colors.inactive}`}>
|
||||
{labels[status as keyof typeof labels] || status}
|
||||
</span>
|
||||
)
|
||||
}
|
||||
|
||||
export default function AgentsDashboardPage() {
|
||||
const [data, setData] = useState<AgentsResponse | null>(null)
|
||||
const [loading, setLoading] = useState(true)
|
||||
|
||||
const loadAgents = useCallback(async () => {
|
||||
try {
|
||||
const res = await fetch('/api/sdk/agents')
|
||||
if (res.ok) {
|
||||
setData(await res.json())
|
||||
}
|
||||
} catch (err) {
|
||||
console.error('Failed to load agents:', err)
|
||||
} finally {
|
||||
setLoading(false)
|
||||
}
|
||||
}, [])
|
||||
|
||||
useEffect(() => {
|
||||
loadAgents()
|
||||
const interval = setInterval(loadAgents, 30_000)
|
||||
return () => clearInterval(interval)
|
||||
}, [loadAgents])
|
||||
|
||||
if (loading) {
|
||||
return (
|
||||
<div className="p-8">
|
||||
<div className="animate-pulse space-y-4">
|
||||
<div className="h-8 bg-gray-200 rounded w-64" />
|
||||
<div className="grid grid-cols-3 gap-4">
|
||||
{[1, 2, 3].map(i => <div key={i} className="h-24 bg-gray-200 rounded-xl" />)}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
const stats = data?.stats
|
||||
const agents = data?.agents || []
|
||||
|
||||
return (
|
||||
<div className="p-8 max-w-6xl">
|
||||
{/* Header */}
|
||||
<div className="mb-8">
|
||||
<h1 className="text-2xl font-bold text-gray-900">Compliance-Agenten</h1>
|
||||
<p className="text-gray-500 mt-1">
|
||||
Verwaltung und Konfiguration der KI-Agenten im Compliance SDK
|
||||
</p>
|
||||
</div>
|
||||
|
||||
{/* Quick Links */}
|
||||
<div className="grid grid-cols-2 md:grid-cols-4 gap-3 mb-8">
|
||||
<Link href="/sdk/agents/architecture" className="flex items-center gap-2 px-4 py-3 bg-white border border-gray-200 rounded-xl hover:border-purple-300 hover:bg-purple-50 transition-colors text-sm">
|
||||
<svg className="w-5 h-5 text-purple-600" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M5 12h14M5 12a2 2 0 01-2-2V6a2 2 0 012-2h14a2 2 0 012 2v4a2 2 0 01-2 2M5 12a2 2 0 00-2 2v4a2 2 0 002 2h14a2 2 0 002-2v-4a2 2 0 00-2-2" />
|
||||
</svg>
|
||||
Architektur
|
||||
</Link>
|
||||
<Link href="/sdk/agents/sessions" className="flex items-center gap-2 px-4 py-3 bg-white border border-gray-200 rounded-xl hover:border-purple-300 hover:bg-purple-50 transition-colors text-sm">
|
||||
<svg className="w-5 h-5 text-purple-600" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M8 12h.01M12 12h.01M16 12h.01M21 12c0 4.418-4.03 8-9 8a9.863 9.863 0 01-4.255-.949L3 20l1.395-3.72C3.512 15.042 3 13.574 3 12c0-4.418 4.03-8 9-8s9 3.582 9 8z" />
|
||||
</svg>
|
||||
Sessions
|
||||
</Link>
|
||||
<Link href="/sdk/agents/statistics" className="flex items-center gap-2 px-4 py-3 bg-white border border-gray-200 rounded-xl hover:border-purple-300 hover:bg-purple-50 transition-colors text-sm">
|
||||
<svg className="w-5 h-5 text-purple-600" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M9 19v-6a2 2 0 00-2-2H5a2 2 0 00-2 2v6a2 2 0 002 2h2a2 2 0 002-2zm0 0V9a2 2 0 012-2h2a2 2 0 012 2v10m-6 0a2 2 0 002 2h2a2 2 0 002-2m0 0V5a2 2 0 012-2h2a2 2 0 012 2v14a2 2 0 01-2 2h-2a2 2 0 01-2-2z" />
|
||||
</svg>
|
||||
Statistiken
|
||||
</Link>
|
||||
<Link href="/sdk/document-generator" className="flex items-center gap-2 px-4 py-3 bg-white border border-gray-200 rounded-xl hover:border-purple-300 hover:bg-purple-50 transition-colors text-sm">
|
||||
<svg className="w-5 h-5 text-purple-600" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M9 12h6m-6 4h6m2 5H7a2 2 0 01-2-2V5a2 2 0 012-2h5.586a1 1 0 01.707.293l5.414 5.414a1 1 0 01.293.707V19a2 2 0 01-2 2z" />
|
||||
</svg>
|
||||
Dokument-Generator
|
||||
</Link>
|
||||
</div>
|
||||
|
||||
{/* Stats Bar */}
|
||||
<div className="grid grid-cols-2 md:grid-cols-3 lg:grid-cols-6 gap-4 mb-8">
|
||||
<div className="bg-white border border-gray-200 rounded-xl px-4 py-3">
|
||||
<div className="text-2xl font-bold text-gray-900">{stats?.total || 0}</div>
|
||||
<div className="text-xs text-gray-500">Agenten gesamt</div>
|
||||
</div>
|
||||
<div className="bg-white border border-gray-200 rounded-xl px-4 py-3">
|
||||
<div className="text-2xl font-bold text-green-600">{stats?.active || 0}</div>
|
||||
<div className="text-xs text-gray-500">Aktiv</div>
|
||||
</div>
|
||||
<div className="bg-white border border-gray-200 rounded-xl px-4 py-3">
|
||||
<div className="text-2xl font-bold text-red-600">{stats?.error || 0}</div>
|
||||
<div className="text-xs text-gray-500">Fehler</div>
|
||||
</div>
|
||||
<div className="bg-white border border-gray-200 rounded-xl px-4 py-3">
|
||||
<div className="text-2xl font-bold text-gray-900">{stats?.totalSessions || 0}</div>
|
||||
<div className="text-xs text-gray-500">Sessions heute</div>
|
||||
</div>
|
||||
<div className="bg-white border border-gray-200 rounded-xl px-4 py-3">
|
||||
<div className="text-2xl font-bold text-gray-900">{stats?.avgResponseTime || '—'}</div>
|
||||
<div className="text-xs text-gray-500">Avg. Antwortzeit</div>
|
||||
</div>
|
||||
<div className="bg-white border border-gray-200 rounded-xl px-4 py-3">
|
||||
<div className="text-2xl font-bold text-gray-900">6</div>
|
||||
<div className="text-xs text-gray-500">RAG-Sammlungen</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Agent Grid */}
|
||||
<div className="grid grid-cols-1 md:grid-cols-2 gap-6">
|
||||
{agents.map((agent) => (
|
||||
<Link
|
||||
key={agent.id}
|
||||
href={`/sdk/agents/${agent.id}`}
|
||||
className="bg-white border border-gray-200 rounded-2xl p-6 hover:border-purple-300 hover:shadow-lg transition-all group"
|
||||
>
|
||||
<div className="flex items-start justify-between mb-4">
|
||||
<div className="flex items-center gap-3">
|
||||
<div
|
||||
className="w-12 h-12 rounded-xl flex items-center justify-center text-white"
|
||||
style={{ backgroundColor: agent.color }}
|
||||
>
|
||||
{getAgentIcon(agent.icon)}
|
||||
</div>
|
||||
<div>
|
||||
<h2 className="font-semibold text-gray-900 group-hover:text-purple-700 transition-colors">
|
||||
{agent.name}
|
||||
</h2>
|
||||
<div className="text-xs text-gray-400">v{agent.version}</div>
|
||||
</div>
|
||||
</div>
|
||||
<StatusBadge status={agent.status} />
|
||||
</div>
|
||||
|
||||
<p className="text-sm text-gray-600 mb-4 line-clamp-2">
|
||||
{agent.description}
|
||||
</p>
|
||||
|
||||
<div className="grid grid-cols-3 gap-3 pt-3 border-t border-gray-100">
|
||||
<div>
|
||||
<div className="text-lg font-semibold text-gray-900">{agent.stats.sessionsToday}</div>
|
||||
<div className="text-xs text-gray-500">Sessions</div>
|
||||
</div>
|
||||
<div>
|
||||
<div className="text-lg font-semibold text-gray-900">{agent.stats.avgResponseTime}</div>
|
||||
<div className="text-xs text-gray-500">Avg. Zeit</div>
|
||||
</div>
|
||||
<div>
|
||||
<div className="text-lg font-semibold text-gray-900">{agent.stats.successRate}</div>
|
||||
<div className="text-xs text-gray-500">Erfolgsrate</div>
|
||||
</div>
|
||||
</div>
|
||||
</Link>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
34
admin-compliance/app/(sdk)/sdk/agents/sessions/page.tsx
Normal file
34
admin-compliance/app/(sdk)/sdk/agents/sessions/page.tsx
Normal file
@@ -0,0 +1,34 @@
|
||||
'use client'
|
||||
|
||||
import React from 'react'
|
||||
import Link from 'next/link'
|
||||
|
||||
export default function AgentSessionsPage() {
|
||||
return (
|
||||
<div className="p-8 max-w-5xl">
|
||||
<div className="flex items-center gap-4 mb-8">
|
||||
<Link href="/sdk/agents" className="text-gray-400 hover:text-gray-600 transition-colors">
|
||||
<svg className="w-6 h-6" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M15 19l-7-7 7-7" />
|
||||
</svg>
|
||||
</Link>
|
||||
<div>
|
||||
<h1 className="text-2xl font-bold text-gray-900">Agent-Sessions</h1>
|
||||
<p className="text-gray-500 mt-1">Chat-Verlaeufe und Session-Management</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="bg-white border border-gray-200 rounded-xl p-12 text-center">
|
||||
<svg className="w-20 h-20 text-gray-300 mx-auto mb-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={1.5}
|
||||
d="M8 12h.01M12 12h.01M16 12h.01M21 12c0 4.418-4.03 8-9 8a9.863 9.863 0 01-4.255-.949L3 20l1.395-3.72C3.512 15.042 3 13.574 3 12c0-4.418 4.03-8 9-8s9 3.582 9 8z" />
|
||||
</svg>
|
||||
<h2 className="text-xl font-medium text-gray-900 mb-2">Sessions-Tracking</h2>
|
||||
<p className="text-gray-500 max-w-md mx-auto">
|
||||
Das Session-Tracking fuer Compliance-Agenten wird in einer zukuenftigen Version implementiert.
|
||||
Hier werden Chat-Verlaeufe, Antwortqualitaet und Nutzer-Feedback angezeigt.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
34
admin-compliance/app/(sdk)/sdk/agents/statistics/page.tsx
Normal file
34
admin-compliance/app/(sdk)/sdk/agents/statistics/page.tsx
Normal file
@@ -0,0 +1,34 @@
|
||||
'use client'
|
||||
|
||||
import React from 'react'
|
||||
import Link from 'next/link'
|
||||
|
||||
export default function AgentStatisticsPage() {
|
||||
return (
|
||||
<div className="p-8 max-w-5xl">
|
||||
<div className="flex items-center gap-4 mb-8">
|
||||
<Link href="/sdk/agents" className="text-gray-400 hover:text-gray-600 transition-colors">
|
||||
<svg className="w-6 h-6" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M15 19l-7-7 7-7" />
|
||||
</svg>
|
||||
</Link>
|
||||
<div>
|
||||
<h1 className="text-2xl font-bold text-gray-900">Agent-Statistiken</h1>
|
||||
<p className="text-gray-500 mt-1">Performance-Metriken und Nutzungsanalysen</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="bg-white border border-gray-200 rounded-xl p-12 text-center">
|
||||
<svg className="w-20 h-20 text-gray-300 mx-auto mb-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={1.5}
|
||||
d="M9 19v-6a2 2 0 00-2-2H5a2 2 0 00-2 2v6a2 2 0 002 2h2a2 2 0 002-2zm0 0V9a2 2 0 012-2h2a2 2 0 012 2v10m-6 0a2 2 0 002 2h2a2 2 0 002-2m0 0V5a2 2 0 012-2h2a2 2 0 012 2v14a2 2 0 01-2 2h-2a2 2 0 01-2-2z" />
|
||||
</svg>
|
||||
<h2 className="text-xl font-medium text-gray-900 mb-2">Agent-Statistiken</h2>
|
||||
<p className="text-gray-500 max-w-md mx-auto">
|
||||
Detaillierte Statistiken wie Antwortzeiten, Erfolgsraten, haeufigste Themen und
|
||||
RAG-Trefferquoten werden in einer zukuenftigen Version implementiert.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
37
admin-compliance/app/api/sdk/agents/[agentId]/route.ts
Normal file
37
admin-compliance/app/api/sdk/agents/[agentId]/route.ts
Normal file
@@ -0,0 +1,37 @@
|
||||
/**
|
||||
* GET /api/sdk/agents/[agentId] — Agent-Detail mit SOUL-Content
|
||||
*/
|
||||
|
||||
import { NextRequest, NextResponse } from 'next/server'
|
||||
import { getAgentById } from '@/lib/sdk/agents/agent-registry'
|
||||
import { readSoulFile, getSoulFileStats, soulFileExists } from '@/lib/sdk/agents/soul-reader'
|
||||
|
||||
export async function GET(
|
||||
_request: NextRequest,
|
||||
{ params }: { params: Promise<{ agentId: string }> }
|
||||
) {
|
||||
try {
|
||||
const { agentId } = await params
|
||||
const agent = getAgentById(agentId)
|
||||
|
||||
if (!agent) {
|
||||
return NextResponse.json({ error: 'Agent not found' }, { status: 404 })
|
||||
}
|
||||
|
||||
const exists = await soulFileExists(agentId)
|
||||
const soulContent = await readSoulFile(agentId)
|
||||
const fileStats = await getSoulFileStats(agentId)
|
||||
|
||||
return NextResponse.json({
|
||||
...agent,
|
||||
status: exists ? agent.status : 'error',
|
||||
soulContent: soulContent || '',
|
||||
createdAt: fileStats?.createdAt || null,
|
||||
updatedAt: fileStats?.updatedAt || null,
|
||||
fileSize: fileStats?.size || 0,
|
||||
})
|
||||
} catch (error) {
|
||||
console.error('Error fetching agent detail:', error)
|
||||
return NextResponse.json({ error: 'Failed to fetch agent' }, { status: 500 })
|
||||
}
|
||||
}
|
||||
84
admin-compliance/app/api/sdk/agents/[agentId]/soul/route.ts
Normal file
84
admin-compliance/app/api/sdk/agents/[agentId]/soul/route.ts
Normal file
@@ -0,0 +1,84 @@
|
||||
/**
|
||||
* GET/PUT /api/sdk/agents/[agentId]/soul — SOUL-Datei lesen/schreiben
|
||||
*
|
||||
* GET: Content + Metadaten, ?history=true fuer Backup-Versionen
|
||||
* PUT: Backup erstellen -> neuen Content schreiben -> Cache invalidieren
|
||||
*/
|
||||
|
||||
import { NextRequest, NextResponse } from 'next/server'
|
||||
import { getAgentById } from '@/lib/sdk/agents/agent-registry'
|
||||
import {
|
||||
readSoulFile,
|
||||
writeSoulFile,
|
||||
listSoulBackups,
|
||||
getSoulFileStats,
|
||||
} from '@/lib/sdk/agents/soul-reader'
|
||||
|
||||
export async function GET(
|
||||
request: NextRequest,
|
||||
{ params }: { params: Promise<{ agentId: string }> }
|
||||
) {
|
||||
try {
|
||||
const { agentId } = await params
|
||||
const agent = getAgentById(agentId)
|
||||
|
||||
if (!agent) {
|
||||
return NextResponse.json({ error: 'Agent not found' }, { status: 404 })
|
||||
}
|
||||
|
||||
const showHistory = request.nextUrl.searchParams.get('history') === 'true'
|
||||
|
||||
if (showHistory) {
|
||||
const backups = await listSoulBackups(agentId)
|
||||
return NextResponse.json({ agentId, backups })
|
||||
}
|
||||
|
||||
const content = await readSoulFile(agentId)
|
||||
const fileStats = await getSoulFileStats(agentId)
|
||||
|
||||
return NextResponse.json({
|
||||
agentId,
|
||||
content: content || '',
|
||||
updatedAt: fileStats?.updatedAt || null,
|
||||
size: fileStats?.size || 0,
|
||||
})
|
||||
} catch (error) {
|
||||
console.error('Error reading SOUL file:', error)
|
||||
return NextResponse.json({ error: 'Failed to read SOUL file' }, { status: 500 })
|
||||
}
|
||||
}
|
||||
|
||||
export async function PUT(
|
||||
request: NextRequest,
|
||||
{ params }: { params: Promise<{ agentId: string }> }
|
||||
) {
|
||||
try {
|
||||
const { agentId } = await params
|
||||
const agent = getAgentById(agentId)
|
||||
|
||||
if (!agent) {
|
||||
return NextResponse.json({ error: 'Agent not found' }, { status: 404 })
|
||||
}
|
||||
|
||||
const body = await request.json()
|
||||
const { content } = body
|
||||
|
||||
if (typeof content !== 'string' || content.trim().length === 0) {
|
||||
return NextResponse.json({ error: 'Content is required' }, { status: 400 })
|
||||
}
|
||||
|
||||
await writeSoulFile(agentId, content)
|
||||
|
||||
const fileStats = await getSoulFileStats(agentId)
|
||||
|
||||
return NextResponse.json({
|
||||
agentId,
|
||||
updatedAt: fileStats?.updatedAt || new Date().toISOString(),
|
||||
size: fileStats?.size || content.length,
|
||||
message: 'SOUL file updated successfully',
|
||||
})
|
||||
} catch (error) {
|
||||
console.error('Error writing SOUL file:', error)
|
||||
return NextResponse.json({ error: 'Failed to write SOUL file' }, { status: 500 })
|
||||
}
|
||||
}
|
||||
41
admin-compliance/app/api/sdk/agents/route.ts
Normal file
41
admin-compliance/app/api/sdk/agents/route.ts
Normal file
@@ -0,0 +1,41 @@
|
||||
/**
|
||||
* GET /api/sdk/agents — Liste aller Compliance-Agenten
|
||||
*/
|
||||
|
||||
import { NextResponse } from 'next/server'
|
||||
import { COMPLIANCE_AGENTS } from '@/lib/sdk/agents/agent-registry'
|
||||
import { soulFileExists } from '@/lib/sdk/agents/soul-reader'
|
||||
|
||||
export async function GET() {
|
||||
try {
|
||||
// Check SOUL file existence for each agent and set status
|
||||
const agents = await Promise.all(
|
||||
COMPLIANCE_AGENTS.map(async (agent) => {
|
||||
const exists = await soulFileExists(agent.id)
|
||||
return {
|
||||
...agent,
|
||||
status: exists ? agent.status : 'error' as const,
|
||||
}
|
||||
})
|
||||
)
|
||||
|
||||
const activeCount = agents.filter(a => a.status === 'active').length
|
||||
const errorCount = agents.filter(a => a.status === 'error').length
|
||||
|
||||
return NextResponse.json({
|
||||
agents,
|
||||
stats: {
|
||||
total: agents.length,
|
||||
active: activeCount,
|
||||
inactive: agents.length - activeCount - errorCount,
|
||||
error: errorCount,
|
||||
totalSessions: 0,
|
||||
avgResponseTime: '—',
|
||||
},
|
||||
timestamp: new Date().toISOString(),
|
||||
})
|
||||
} catch (error) {
|
||||
console.error('Error fetching agents:', error)
|
||||
return NextResponse.json({ error: 'Failed to fetch agents' }, { status: 500 })
|
||||
}
|
||||
}
|
||||
13
admin-compliance/app/api/sdk/agents/sessions/route.ts
Normal file
13
admin-compliance/app/api/sdk/agents/sessions/route.ts
Normal file
@@ -0,0 +1,13 @@
|
||||
/**
|
||||
* GET /api/sdk/agents/sessions — Agent-Sessions (Placeholder)
|
||||
*/
|
||||
|
||||
import { NextResponse } from 'next/server'
|
||||
|
||||
export async function GET() {
|
||||
return NextResponse.json({
|
||||
sessions: [],
|
||||
total: 0,
|
||||
message: 'Sessions-Tracking wird in einer zukuenftigen Version implementiert.',
|
||||
})
|
||||
}
|
||||
18
admin-compliance/app/api/sdk/agents/statistics/route.ts
Normal file
18
admin-compliance/app/api/sdk/agents/statistics/route.ts
Normal file
@@ -0,0 +1,18 @@
|
||||
/**
|
||||
* GET /api/sdk/agents/statistics — Agent-Statistiken (Placeholder)
|
||||
*/
|
||||
|
||||
import { NextResponse } from 'next/server'
|
||||
|
||||
export async function GET() {
|
||||
return NextResponse.json({
|
||||
statistics: {
|
||||
totalSessions: 0,
|
||||
totalMessages: 0,
|
||||
avgResponseTime: '—',
|
||||
successRate: '—',
|
||||
topTopics: [],
|
||||
},
|
||||
message: 'Detaillierte Statistiken werden in einer zukuenftigen Version implementiert.',
|
||||
})
|
||||
}
|
||||
@@ -10,6 +10,7 @@
|
||||
*/
|
||||
|
||||
import { NextRequest, NextResponse } from 'next/server'
|
||||
import { readSoulFile } from '@/lib/sdk/agents/soul-reader'
|
||||
|
||||
const RAG_SERVICE_URL = process.env.RAG_SERVICE_URL || 'http://rag-service:8097'
|
||||
const OLLAMA_URL = process.env.OLLAMA_URL || 'http://host.docker.internal:11434'
|
||||
@@ -27,99 +28,18 @@ const COMPLIANCE_COLLECTIONS = [
|
||||
|
||||
type Country = 'DE' | 'AT' | 'CH' | 'EU'
|
||||
|
||||
// SOUL system prompt (from agent-core/soul/compliance-advisor.soul.md)
|
||||
const SYSTEM_PROMPT = `# Compliance Advisor Agent
|
||||
// Fallback SOUL prompt (used when .soul.md file is unavailable)
|
||||
const FALLBACK_SYSTEM_PROMPT = `# Compliance Advisor Agent
|
||||
|
||||
## Identitaet
|
||||
Du bist der BreakPilot Compliance-Berater. Du hilfst Nutzern des AI Compliance SDK,
|
||||
Datenschutz- und Compliance-Fragen in verstaendlicher Sprache zu beantworten.
|
||||
Du bist kein Anwalt und gibst keine Rechtsberatung, sondern orientierst dich an
|
||||
offiziellen Quellen und gibst praxisnahe Hinweise.
|
||||
|
||||
## Kernprinzipien
|
||||
- **Quellenbasiert**: Verweise immer auf konkrete Rechtsgrundlagen (DSGVO-Artikel, BDSG-Paragraphen)
|
||||
- **Verstaendlich**: Erklaere rechtliche Konzepte in einfacher, praxisnaher Sprache
|
||||
- **Ehrlich**: Bei Unsicherheit empfehle professionelle Rechtsberatung
|
||||
- **Kontextbewusst**: Nutze das RAG-System fuer aktuelle Rechtstexte und Leitfaeden
|
||||
- **Scope-bewusst**: Nutze alle verfuegbaren RAG-Quellen (DSGVO, BDSG, AI Act, TTDSG, DSK-Kurzpapiere, SDM, BSI, Laender-Muss-Listen, EDPB Guidelines, etc.) AUSSER NIBIS-Dokumenten.
|
||||
|
||||
## Kompetenzbereich
|
||||
- DSGVO Art. 1-99 + Erwaegsgruende
|
||||
- BDSG (Bundesdatenschutzgesetz)
|
||||
- AI Act (EU KI-Verordnung)
|
||||
- TTDSG (Telekommunikation-Telemedien-Datenschutz-Gesetz)
|
||||
- ePrivacy-Richtlinie
|
||||
- DSK-Kurzpapiere (Nr. 1-20)
|
||||
- SDM (Standard-Datenschutzmodell) V3.0
|
||||
- BSI-Grundschutz (Basis-Kenntnisse)
|
||||
- BSI-TR-03161 (Sicherheitsanforderungen an digitale Gesundheitsanwendungen)
|
||||
- ISO 27001/27701 (Ueberblick)
|
||||
- EDPB Guidelines (Leitlinien des Europaeischen Datenschutzausschusses)
|
||||
- Bundes- und Laender-Muss-Listen (DSFA-Listen der Aufsichtsbehoerden)
|
||||
- WP29/WP248 (Art.-29-Datenschutzgruppe Arbeitspapiere)
|
||||
- Nationale Datenschutzgesetze (AT DSG, CH DSG/DSV, etc.)
|
||||
- EU-Verordnungen (DORA, MiCA, Data Act, EHDS, PSD2, AMLR, etc.)
|
||||
- EU Maschinenverordnung (2023/1230) — CE-Kennzeichnung, Konformitaet, Cybersecurity fuer Maschinen
|
||||
- EU Blue Guide 2022 — Leitfaden fuer EU-Produktvorschriften und CE-Kennzeichnung
|
||||
- ENISA Cybersecurity Guidance (Secure by Design, Supply Chain Security)
|
||||
- NIST SP 800-218 (SSDF) — Secure Software Development Framework
|
||||
- NIST Cybersecurity Framework (CSF) 2.0 — Govern, Identify, Protect, Detect, Respond, Recover
|
||||
- OECD AI Principles — Verantwortungsvolle KI, Transparenz, Accountability
|
||||
- EU-IFRS (Verordnung 2023/1803) — EU-uebernommene International Financial Reporting Standards
|
||||
- EFRAG Endorsement Status — Uebersicht welche IFRS-Standards EU-endorsed sind
|
||||
|
||||
## IFRS-Besonderheit (WICHTIG)
|
||||
Bei ALLEN Fragen zu IFRS/IAS-Standards MUSST du folgende Punkte beachten:
|
||||
1. Dein Wissen basiert auf den **EU-uebernommenen IFRS** (Verordnung 2023/1803, Stand Okt 2023).
|
||||
2. Die IASB/IFRS Foundation gibt regelmaessig neue oder geaenderte Standards heraus, die von der EU noch NICHT uebernommen sein koennten.
|
||||
3. Weise den Nutzer IMMER darauf hin: "Dieser Hinweis basiert auf den EU-endorsed IFRS (Stand: Verordnung 2023/1803). Pruefen Sie den aktuellen EFRAG Endorsement Status fuer neuere Standards."
|
||||
4. Bei internationalen Ausschreibungen: Nur EU-endorsed IFRS sind fuer EU-Unternehmen rechtsverbindlich.
|
||||
5. Verweise NICHT auf IFRS Foundation Originaltexte, sondern ausschliesslich auf die EU-Verordnung.
|
||||
|
||||
## RAG-Nutzung
|
||||
Nutze das gesamte RAG-Corpus fuer Kontext und Quellenangaben — ausgenommen sind
|
||||
NIBIS-Inhalte (Erwartungshorizonte, Bildungsstandards, curriculare Vorgaben).
|
||||
Diese gehoeren nicht zum Datenschutz-Kompetenzbereich.
|
||||
|
||||
## Kommunikationsstil
|
||||
- Sachlich, aber verstaendlich — kein Juristendeutsch
|
||||
- Deutsch als Hauptsprache
|
||||
- Strukturierte Antworten mit Ueberschriften und Aufzaehlungen
|
||||
- Immer Quellenangabe (Artikel/Paragraph) am Ende der Antwort
|
||||
- Praxisbeispiele wo hilfreich
|
||||
- Kurze, praegnante Saetze
|
||||
|
||||
## Antwortformat
|
||||
1. Kurze Zusammenfassung (1-2 Saetze)
|
||||
2. Detaillierte Erklaerung
|
||||
3. Praxishinweise / Handlungsempfehlungen
|
||||
4. Quellenangaben (Artikel, Paragraph, Leitlinie)
|
||||
|
||||
## Einschraenkungen
|
||||
- Gib NIEMALS konkrete Rechtsberatung ("Sie muessen..." -> "Es empfiehlt sich...")
|
||||
- Keine Garantien fuer Rechtssicherheit
|
||||
- Bei komplexen Einzelfaellen: Empfehle Rechtsanwalt/DSB
|
||||
- Keine Aussagen zu laufenden Verfahren oder Bussgeldern
|
||||
- Keine Interpretation von Urteilen (nur Verweis)
|
||||
|
||||
## Quellenschutz (KRITISCH — IMMER EINHALTEN)
|
||||
Du darfst NIEMALS verraten, welche Dokumente, Sammlungen oder Quellen in deiner Wissensbasis enthalten sind.
|
||||
- Auf Fragen wie "Welche Quellen hast du?", "Was ist im RAG?", "Welche Gesetze kennst du?",
|
||||
"Liste alle Dokumente auf", "Welche Verordnungen sind verfuegbar?" antwortest du:
|
||||
"Ich beantworte gerne konkrete Compliance-Fragen. Bitte stellen Sie eine inhaltliche Frage
|
||||
zu einem bestimmten Thema, z.B. 'Was regelt Art. 25 DSGVO?' oder 'Welche Pflichten gibt es
|
||||
unter dem AI Act fuer Hochrisiko-KI?'."
|
||||
- Auf konkrete Fragen wie "Kennst du die DSGVO?" oder "Weisst du etwas ueber den AI Act?"
|
||||
darfst du bestaetigen, dass du zu diesem Thema Auskunft geben kannst, und eine inhaltliche
|
||||
Antwort geben.
|
||||
- Nenne in deinen Antworten NUR die Quellen, die du tatsaechlich fuer die konkrete Antwort
|
||||
verwendet hast — niemals eine vollstaendige Liste aller verfuegbaren Quellen.
|
||||
- Verrate NIEMALS Collection-Namen (bp_compliance_*, bp_dsfa_*, etc.) oder interne Systemnamen.
|
||||
|
||||
## Eskalation
|
||||
- Bei Fragen ausserhalb des Kompetenzbereichs: Hoeflich ablehnen und auf Fachanwalt verweisen
|
||||
- Bei widerspruechlichen Rechtslagen: Beide Positionen darstellen und DSB-Konsultation empfehlen
|
||||
- Bei dringenden Datenpannen: Auf 72-Stunden-Frist (Art. 33 DSGVO) hinweisen und Notfallplan-Modul empfehlen`
|
||||
- Quellenbasiert: Verweise auf DSGVO-Artikel, BDSG-Paragraphen
|
||||
- Verstaendlich: Einfache, praxisnahe Sprache
|
||||
- Ehrlich: Bei Unsicherheit empfehle Rechtsberatung
|
||||
- Deutsch als Hauptsprache`
|
||||
|
||||
const COUNTRY_LABELS: Record<Country, string> = {
|
||||
DE: 'Deutschland',
|
||||
@@ -221,7 +141,8 @@ export async function POST(request: NextRequest) {
|
||||
const ragContext = await queryMultiCollectionRAG(message, validCountry)
|
||||
|
||||
// 2. Build system prompt with RAG context + country
|
||||
let systemContent = SYSTEM_PROMPT
|
||||
const soulPrompt = await readSoulFile('compliance-advisor')
|
||||
let systemContent = soulPrompt || FALLBACK_SYSTEM_PROMPT
|
||||
|
||||
if (validCountry) {
|
||||
const countryLabel = COUNTRY_LABELS[validCountry]
|
||||
|
||||
@@ -8,27 +8,22 @@
|
||||
|
||||
import { NextRequest, NextResponse } from 'next/server'
|
||||
import { queryRAG } from '@/lib/sdk/drafting-engine/rag-query'
|
||||
import { readSoulFile } from '@/lib/sdk/agents/soul-reader'
|
||||
|
||||
const OLLAMA_URL = process.env.OLLAMA_URL || 'http://host.docker.internal:11434'
|
||||
const LLM_MODEL = process.env.COMPLIANCE_LLM_MODEL || 'qwen2.5vl:32b'
|
||||
|
||||
// SOUL System Prompt (from agent-core/soul/drafting-agent.soul.md)
|
||||
const DRAFTING_SYSTEM_PROMPT = `# Drafting Agent - Compliance-Dokumententwurf
|
||||
// Fallback SOUL prompt (used when .soul.md file is unavailable)
|
||||
const FALLBACK_DRAFTING_PROMPT = `# Drafting Agent - Compliance-Dokumententwurf
|
||||
|
||||
## Identitaet
|
||||
Du bist der BreakPilot Drafting Agent. Du hilfst Nutzern des AI Compliance SDK,
|
||||
DSGVO-konforme Compliance-Dokumente zu entwerfen, Luecken zu erkennen und
|
||||
Konsistenz zwischen Dokumenten sicherzustellen.
|
||||
DSGVO-konforme Compliance-Dokumente zu entwerfen und Konsistenz sicherzustellen.
|
||||
|
||||
## Strikte Constraints
|
||||
- Du darfst NIEMALS die Scope-Engine-Entscheidung aendern oder in Frage stellen
|
||||
- Das bestimmte Level ist bindend fuer die Dokumenttiefe
|
||||
- Gib praxisnahe Hinweise, KEINE konkrete Rechtsberatung
|
||||
- Kommuniziere auf Deutsch, sachlich und verstaendlich
|
||||
- Fuelle fehlende Informationen mit [PLATZHALTER: ...] Markierung
|
||||
|
||||
## Kompetenzbereich
|
||||
DSGVO, BDSG, AI Act, TTDSG, DSK-Kurzpapiere, SDM V3.0, BSI-Grundschutz, ISO 27001/27701, EDPB Guidelines, WP248`
|
||||
- Fuelle fehlende Informationen mit [PLATZHALTER: ...] Markierung`
|
||||
|
||||
export async function POST(request: NextRequest) {
|
||||
try {
|
||||
@@ -49,7 +44,8 @@ export async function POST(request: NextRequest) {
|
||||
const ragContext = await queryRAG(message)
|
||||
|
||||
// 2. Build system prompt with mode-specific instructions + state projection
|
||||
let systemContent = DRAFTING_SYSTEM_PROMPT
|
||||
const soulPrompt = await readSoulFile('drafting-agent')
|
||||
let systemContent = soulPrompt || FALLBACK_DRAFTING_PROMPT
|
||||
|
||||
// Mode-specific instructions
|
||||
const modeInstructions: Record<string, string> = {
|
||||
|
||||
@@ -590,6 +590,18 @@ export function SDKSidebar({ collapsed = false, onCollapsedChange }: SDKSidebarP
|
||||
isActive={pathname === '/sdk/architecture'}
|
||||
collapsed={collapsed}
|
||||
/>
|
||||
<AdditionalModuleItem
|
||||
href="/sdk/agents"
|
||||
icon={
|
||||
<svg className="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2}
|
||||
d="M9.75 17L9 20l-1 1h8l-1-1-.75-3M3 13h18M5 17h14a2 2 0 002-2V5a2 2 0 00-2-2H5a2 2 0 00-2 2v10a2 2 0 002 2z" />
|
||||
</svg>
|
||||
}
|
||||
label="Agenten"
|
||||
isActive={pathname?.startsWith('/sdk/agents') ?? false}
|
||||
collapsed={collapsed}
|
||||
/>
|
||||
<AdditionalModuleItem
|
||||
href="/sdk/catalog-manager"
|
||||
icon={
|
||||
|
||||
56
admin-compliance/lib/sdk/agents/agent-registry.ts
Normal file
56
admin-compliance/lib/sdk/agents/agent-registry.ts
Normal file
@@ -0,0 +1,56 @@
|
||||
/**
|
||||
* Agent Registry — static configuration for the 2 compliance agents.
|
||||
*/
|
||||
|
||||
export interface AgentConfig {
|
||||
id: string
|
||||
name: string
|
||||
description: string
|
||||
soulFile: string
|
||||
color: string
|
||||
icon: string
|
||||
status: 'active' | 'inactive' | 'error'
|
||||
version: string
|
||||
stats: {
|
||||
sessionsToday: number
|
||||
avgResponseTime: string
|
||||
successRate: string
|
||||
}
|
||||
}
|
||||
|
||||
export const COMPLIANCE_AGENTS: AgentConfig[] = [
|
||||
{
|
||||
id: 'compliance-advisor',
|
||||
name: 'Compliance Advisor',
|
||||
description: 'RAG-gestuetzter Chat-Agent fuer DSGVO, AI Act und Compliance-Fragen. Durchsucht 6 Sammlungen mit Multi-Collection-RAG und liefert quellenbasierte Antworten mit Streaming.',
|
||||
soulFile: 'compliance-advisor.soul.md',
|
||||
color: '#7c3aed', // purple-600
|
||||
icon: 'shield',
|
||||
status: 'active',
|
||||
version: '1.0.0',
|
||||
stats: {
|
||||
sessionsToday: 0,
|
||||
avgResponseTime: '—',
|
||||
successRate: '—',
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'drafting-agent',
|
||||
name: 'Drafting Agent',
|
||||
description: '4-Modi Dokumenten-Agent (Explain, Ask, Draft, Validate). Entwirft DSGVO-konforme Compliance-Dokumente, erkennt Luecken und prueft Cross-Dokument-Konsistenz.',
|
||||
soulFile: 'drafting-agent.soul.md',
|
||||
color: '#2563eb', // blue-600
|
||||
icon: 'pencil',
|
||||
status: 'active',
|
||||
version: '1.0.0',
|
||||
stats: {
|
||||
sessionsToday: 0,
|
||||
avgResponseTime: '—',
|
||||
successRate: '—',
|
||||
},
|
||||
},
|
||||
]
|
||||
|
||||
export function getAgentById(id: string): AgentConfig | undefined {
|
||||
return COMPLIANCE_AGENTS.find(a => a.id === id)
|
||||
}
|
||||
131
admin-compliance/lib/sdk/agents/soul-reader.ts
Normal file
131
admin-compliance/lib/sdk/agents/soul-reader.ts
Normal file
@@ -0,0 +1,131 @@
|
||||
/**
|
||||
* SOUL File Reader — reads/writes .soul.md files from agent-core/soul/
|
||||
* with 30s TTL cache and backup support.
|
||||
*/
|
||||
|
||||
import fs from 'fs/promises'
|
||||
import path from 'path'
|
||||
|
||||
const AGENT_CORE_PATH = process.env.AGENT_CORE_PATH || path.join(process.cwd(), 'agent-core')
|
||||
const SOUL_DIR = path.join(AGENT_CORE_PATH, 'soul')
|
||||
const BACKUPS_DIR = path.join(SOUL_DIR, '.backups')
|
||||
|
||||
// 30s TTL cache
|
||||
const cache = new Map<string, { content: string; timestamp: number }>()
|
||||
const CACHE_TTL = 30_000
|
||||
|
||||
function getSoulFilePath(agentId: string): string {
|
||||
// Prevent path traversal
|
||||
const safe = agentId.replace(/[^a-z0-9-]/g, '')
|
||||
return path.join(SOUL_DIR, `${safe}.soul.md`)
|
||||
}
|
||||
|
||||
/**
|
||||
* Read a SOUL file with 30s cache
|
||||
*/
|
||||
export async function readSoulFile(agentId: string): Promise<string | null> {
|
||||
const cached = cache.get(agentId)
|
||||
if (cached && Date.now() - cached.timestamp < CACHE_TTL) {
|
||||
return cached.content
|
||||
}
|
||||
|
||||
try {
|
||||
const filePath = getSoulFilePath(agentId)
|
||||
const content = await fs.readFile(filePath, 'utf-8')
|
||||
cache.set(agentId, { content, timestamp: Date.now() })
|
||||
return content
|
||||
} catch {
|
||||
return null
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Write a SOUL file — creates backup first, then writes new content
|
||||
*/
|
||||
export async function writeSoulFile(agentId: string, content: string): Promise<void> {
|
||||
const filePath = getSoulFilePath(agentId)
|
||||
|
||||
// Ensure backups dir exists
|
||||
await fs.mkdir(BACKUPS_DIR, { recursive: true })
|
||||
|
||||
// Create backup of current file
|
||||
try {
|
||||
const current = await fs.readFile(filePath, 'utf-8')
|
||||
const backupName = `${agentId}-${Date.now()}.soul.md`
|
||||
await fs.writeFile(path.join(BACKUPS_DIR, backupName), current, 'utf-8')
|
||||
} catch {
|
||||
// No existing file to backup
|
||||
}
|
||||
|
||||
// Write new content
|
||||
await fs.writeFile(filePath, content, 'utf-8')
|
||||
|
||||
// Invalidate cache
|
||||
cache.delete(agentId)
|
||||
}
|
||||
|
||||
/**
|
||||
* List backup versions for an agent
|
||||
*/
|
||||
export async function listSoulBackups(agentId: string): Promise<Array<{ filename: string; timestamp: number; size: number }>> {
|
||||
try {
|
||||
const files = await fs.readdir(BACKUPS_DIR)
|
||||
const prefix = `${agentId}-`
|
||||
const backups: Array<{ filename: string; timestamp: number; size: number }> = []
|
||||
|
||||
for (const file of files) {
|
||||
if (!file.startsWith(prefix) || !file.endsWith('.soul.md')) continue
|
||||
const tsStr = file.slice(prefix.length, -'.soul.md'.length)
|
||||
const ts = parseInt(tsStr, 10)
|
||||
if (isNaN(ts)) continue
|
||||
|
||||
const stat = await fs.stat(path.join(BACKUPS_DIR, file))
|
||||
backups.push({ filename: file, timestamp: ts, size: stat.size })
|
||||
}
|
||||
|
||||
return backups.sort((a, b) => b.timestamp - a.timestamp)
|
||||
} catch {
|
||||
return []
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Read a specific backup file
|
||||
*/
|
||||
export async function readSoulBackup(filename: string): Promise<string | null> {
|
||||
try {
|
||||
// Prevent path traversal
|
||||
const safe = path.basename(filename)
|
||||
return await fs.readFile(path.join(BACKUPS_DIR, safe), 'utf-8')
|
||||
} catch {
|
||||
return null
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if SOUL file exists
|
||||
*/
|
||||
export async function soulFileExists(agentId: string): Promise<boolean> {
|
||||
try {
|
||||
await fs.access(getSoulFilePath(agentId))
|
||||
return true
|
||||
} catch {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get SOUL file stats
|
||||
*/
|
||||
export async function getSoulFileStats(agentId: string): Promise<{ size: number; createdAt: string; updatedAt: string } | null> {
|
||||
try {
|
||||
const stat = await fs.stat(getSoulFilePath(agentId))
|
||||
return {
|
||||
size: stat.size,
|
||||
createdAt: stat.birthtime.toISOString(),
|
||||
updatedAt: stat.mtime.toISOString(),
|
||||
}
|
||||
} catch {
|
||||
return null
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user