sequenceDiagram autonumber participant Client as APEX / Client participant AKF as ApiKeyFilter participant CR as CheckResource participant COS as CheckOrchestrationService participant OLF as OrdsLoggingFilter participant OC as OrdsClient participant DPS as DocumentProcessingService participant ES as EvaluationService participant OCR_MDL as OllamaChatModel [ocr] participant MAIN_MDL as OllamaChatModel [main] participant ORDS as ORDS / Oracle DB participant OLLAMA as ollama.aquantico.de %% ─── Eingehende Anfrage ────────────────────────────────────────────────── Client->>AKF: POST /check/{projectId} (Header: X-API-KEY) AKF->>AKF: filter(ContainerRequestContext) Note over AKF: Prüft X-API-KEY gegen dc.api.key
→ 401 wenn ungültig, sonst weiter AKF->>CR: weiterleiten (Key gültig) CR->>CR: startCheck(projectId) CR-->>Client: 202 Accepted { message, project_id, hint } Note over CR,COS: CompletableFuture.runAsync() – Fire & Forget CR-)COS: process(projectId) %% ─── Alle ORDS-Aufrufe laufen durch OrdsLoggingFilter ─────────────────── Note over OLF,ORDS: OrdsLoggingFilter.filter() loggt jeden Request/Response
des REST-Clients (ClientRequestFilter + ClientResponseFilter) %% ─── Initialisierung ──────────────────────────────────────────────────── rect rgb(235, 245, 255) Note over COS,ORDS: Initialisierung COS->>OC: getProject(projectId) OC->>OLF: filter(ClientRequestContext) OLF->>ORDS: GET /api/dc/projects/{id} ORDS-->>OLF: OrdsProject (JSON) OLF->>OLF: filter(ClientRequestContext, ClientResponseContext) OLF-->>COS: OrdsProject Note over COS: Status-Prüfung: nur PENDING wird verarbeitet COS->>OC: startProject(projectId) OC->>ORDS: POST /api/dc/projects/{id}/start ORDS-->>COS: 200 OK | 409 Conflict COS->>OC: getDocuments(projectId) OC->>ORDS: GET /api/dc/projects/{id}/documents/ ORDS-->>COS: OrdsDocumentList COS->>OC: getQuestions(catalogId) OC->>ORDS: GET /api/dc/catalogs/{id}/questions/ ORDS-->>COS: OrdsQuestionList COS->>OC: deleteResults(projectId) OC->>ORDS: DELETE /api/dc/projects/{id}/results Note over ORDS: Alte Ergebnisse löschen → Re-Processing möglich end %% ─── Phase A: OCR + Übersetzung ───────────────────────────────────────── rect rgb(220, 255, 225) Note over COS,OLLAMA: Phase A – OCR + Übersetzung (pro Dokument) loop für jedes OrdsDocument COS->>OC: downloadFile(docId) OC->>ORDS: GET /api/dc/documents/{id}/file ORDS-->>COS: byte[] (BLOB) COS->>DPS: extractText(fileBytes, mimeType, filename) DPS->>DPS: resolveMimeType(mimeType, filename) alt MIME = application/pdf DPS->>DPS: extractFromPdf(fileBytes) DPS->>DPS: Loader.loadPDF(fileBytes) → PDDocument DPS->>DPS: new PDFRenderer(pdf) loop für jede PDF-Seite DPS->>DPS: renderer.renderImageWithDPI(i, 200f, RGB) DPS->>DPS: ImageIO.write(image, "PNG", baos) → Base64 DPS->>DPS: ocrPage(base64Image, pageNum, totalPages) DPS->>OCR_MDL: generate(List) Note over DPS,OCR_MDL: UserMessage: ImageContent(PNG) + TextContent(OCR-Prompt) OCR_MDL->>OLLAMA: POST /api/chat [Vision, X-API-KEY] OLLAMA-->>OCR_MDL: Markdown-Seitentext OCR_MDL-->>DPS: String (Seite i) end else MIME = application/vnd...wordprocessingml / msword DPS->>DPS: extractFromDocx(fileBytes) DPS->>DPS: new XWPFDocument(stream) loop für jedes Body-Element DPS->>DPS: paragraphToMarkdown(XWPFParagraph) DPS->>DPS: tableToMarkdown(XWPFTable) end else MIME = application/vnd.oasis.opendocument.text DPS->>DPS: extractFromOdt(fileBytes) DPS->>DPS: OdfTextDocument.loadDocument(stream) DPS->>DPS: getElementsByTagName("text:p") → Markdown else text/plain | text/markdown Note over DPS: direkt als UTF-8 String zurück end DPS-->>COS: originalText (Markdown) COS->>ES: translate(originalText) ES->>MAIN_MDL: generate(List) Note over ES,MAIN_MDL: SystemMessage: Übersetzer-Prompt
UserMessage: originalText MAIN_MDL->>OLLAMA: POST /api/chat [X-API-KEY] OLLAMA-->>MAIN_MDL: Übersetzung (Deutsch) MAIN_MDL-->>ES: Response ES-->>COS: translatedText COS->>OC: updateTexts(docId, TextsRequest(original, translated)) OC->>ORDS: PUT /api/dc/documents/{id}/texts COS->>COS: pushProgress(projectId, step, total) COS->>OC: updateProgress(projectId, ProgressRequest(pct)) OC->>ORDS: PUT /api/dc/projects/{id}/progress end end %% ─── Phase B: Fragen auswerten ─────────────────────────────────────────── rect rgb(255, 245, 220) Note over COS,OLLAMA: Phase B – Fragenauswertung (pro Dokument × Frage) loop für jedes OrdsDocument × jede OrdsQuestion COS->>ES: evaluate(question, originalText) ES->>ES: buildEvaluationSystem() ES->>ES: buildEvaluationUser(question, documentText) Note over ES: Prompt: Dokument + Frage + Schwellwert
+ Beispiele 0% / 100% ES->>MAIN_MDL: generate(List) MAIN_MDL->>OLLAMA: POST /api/chat [X-API-KEY] OLLAMA-->>MAIN_MDL: JSON { answer, score, result_type, the_comment } MAIN_MDL-->>ES: Response ES->>ES: parseEvaluationResult(rawText, question) ES->>ES: extractJson(text) Note over ES: Regex: ```json...``` oder erstes {...} im Text ES->>ES: objectMapper.readValue(json, EvaluationResult) ES->>ES: deriveResultType(score, threshold) [Fallback] ES-->>COS: EvaluationResult { answer, score, resultType, theComment } Note over COS: score < threshold?
result_handling=ABWEICHUNG → deviation=1
result_handling=HINWEIS → warning=1 COS->>OC: saveResult(projectId, ResultRequest) OC->>ORDS: POST /api/dc/projects/{id}/results → 201 Created COS->>COS: pushProgress(projectId, step, total) COS->>OC: updateProgress(projectId, ProgressRequest(pct)) OC->>ORDS: PUT /api/dc/projects/{id}/progress end end %% ─── Abschluss ─────────────────────────────────────────────────────────── COS->>OC: completeProject(projectId) OC->>ORDS: POST /api/dc/projects/{id}/complete Note over ORDS: status → COMPLETED, completed_at = SYSDATE