Aller au contenu principal

E-05 Traitement de documents

Brique E: Données, RAG, contexte, mémoire. Version anglaise: E-05-document-processing

1. Rôle et responsabilités​

Convertit PDF, Office, images et scans en texte structuré (titres, tableaux) avec Docling et PaddleOCR.

  • Extraction de structure et tableaux
  • OCR des pages scannées
  • Découpage en chunks pour le RAG

2. Position dans l'architecture et interactions​

  • Appelants: Ingestion de knowledge base, Connector Hub.
  • Chemin d'exécution: Asynchrone (job).
  • Toute interaction passe par l'API Control Plane v1 (/v1) derrière la passerelle API (D-14).

3. Composant open source et partie développée​

  • Open source: Docling, PaddleOCR
  • Développé: Orchestration, chunking, classification à l'ingestion.
  • Intégration externe via un adaptateur eai/integrations/<system>.py avec repli in-process lorsque l'URL n'est pas configurée.

4. Modèle de données​

Tables (schéma eai, RLS tenant_id):

  • eai.kb_documents
  • eai.kb_chunks
  • eai.media

Colonnes communes : id uuid, tenant_id uuid NOT NULL, created_at, updated_at ; politique RLS current_setting('app.tenant_id', true)::uuid.

5. Contrat​

  • Contrat appliqué: multimodal-contract
  • Schéma JSON: contracts/v1/multimodal-contract.schema.json
  • Champs clés: document mime, pages, ocr
  • Validation : POST /v1/contracts/{name}/validate ; application d'un manifeste : POST /v1/manifests/apply.

6. Endpoints API​

  • POST /v1/knowledge-bases/{id}/documents
  • GET /v1/knowledge-bases/{id}/documents

Requête POST /v1/knowledge-bases/{id}/documents:

{
"media_id": "med_44",
"chunking": {
"strategy": "structure",
"max_tokens": 512,
"overlap": 64
},
"ocr": "auto"
}

Réponse:

{
"doc_id": "doc_210",
"status": "processing"
}

7. Exemples SDK​

Python:

from enterprise_ai import EnterpriseAI

client = EnterpriseAI(base_url="<control-plane-url>", api_key="eai_xxx_secret")
client.rag.ingest("kb_support", content_base64=pdf_base64, mime_type="application/pdf", title="contract.pdf")

TypeScript:

import { EnterpriseAI } from "@enterprise-ai/sdk";

const client = new EnterpriseAI({ baseUrl: "<control-plane-url>", apiKey: process.env.EAI_API_KEY });
await client.knowledgeBases.addDocuments("kb_support", { title: "Refund policy", content: "Refunds are paid within 14 days.", mime_type: "text/plain" });

Java:

EnterpriseAIClient client = EnterpriseAIClient.builder()
.baseUrl("<control-plane-url>")
.apiKey(System.getenv("EAI_API_KEY"))
.build();
client.knowledge().addDocument("kb_support", Map.of("title", "Refund policy", "content", "Refunds are processed within 14 days."));

Go:

c, _ := eai.NewClient(eai.WithBaseURL("<control-plane-url>"), eai.WithAPIKey(os.Getenv("EAI_API_KEY")))
c.Knowledge.AddDocument(ctx, "kb_support", map[string]any{"uri": "https://<intranet-host>/refunds.pdf", "title": "Refund policy"})

8. Configuration​

  • Variables d'environnement: EAI_DOCLING_URL, EAI_OCR_LANG, EAI_DATABASE_URL
  • Clés Helm: controlPlane.config, workers.items

9. Contrôles de sécurité​

  • Scan antivirus avant traitement (Media F)
  • Classification héritée par chaque chunk
  • Toutes les requêtes portent le tenant issu du credential ; RLS PostgreSQL
  • Chaque mutation et décision de politique écrit un événement d'audit chaîné

10. Observabilité​

  • Métriques Prometheus: eai_documents_processed_total{status}, eai_ocr_pages_total
  • Traces: spans OpenTelemetry avec conventions sémantiques GenAI (gen_ai.operation.name, gen_ai.agent.id, gen_ai.tool.name), attributs eai.tenant_id, eai.run_id, propagation traceparent.
  • Actions d'audit: knowledge.document.ingest

11. Unités FinOps​

  • Pages traitées, pages OCR, CPU/GPU.
  • Émis comme Usage Event (POST /v1/finops/usage) avec tenant, projet, agent et run.

12. Modes de défaillance et résilience​

  • Job relancé par doc_id, idempotent par sha256 du fichier
  • Erreurs normalisées (enveloppe error, message_i18n en/fr)

13. Adoption autonome (client d'un client)​

  • enterprise_ai.rag + Docling.
  • SDK: enterprise_ai.privacy / dlp / knowledge_bases / memory / retention / lineage
  • Déploiement minimal : chart Helm avec controlPlane.enabled=true et PostgreSQL ; les composants externes désactivés utilisent leur repli in-process.

14. Tests d'acceptation​

  1. PDF scanné -> texte OCR non vide
  2. Accès inter-tenant refusé
  3. Erreur renvoyée en fr avec Accept-Language: fr

RAG gouverné (kit L4)​

Docling Serve (EAI_DOCLING_URL) et PaddleOCR (EAI_PADDLEOCR_URL, eai.integrations.ocr, POST /ocr) passent par eai.security.egress. Sans service, l'extraction intégrée (texte, HTML, PDF) et tesseract restent actifs.

Chaînes Docling et PaddleOCR vérifiées (DATA-010, DATA-011)​

Installation (environnements virtuels séparés, versions épinglées, sources officielles uniquement) :

bash tools/rag/setup-docling.sh # /tmp/toolchains/docling-venv + /tmp/toolchains/docling-models
bash tools/rag/setup-paddleocr.sh # /tmp/toolchains/paddleocr-venv + /tmp/toolchains/paddleocr-models
  • Docling 2.145.0 (MIT), docling-core 2.105.0 (MIT), docling-ibm-models 4.0.4 (MIT), torch 2.9.1+cpu (BSD-3-Clause, index officiel https://download.pytorch.org/whl/cpu), python-docx 1.2.0 (MIT).
  • paddlepaddle 3.3.1 CPU (Apache-2.0), paddleocr 3.7.0 (Apache-2.0), paddlex 3.7.2 (Apache-2.0), depuis PyPI.
  • Les scripts écrivent models.sha256 (sha256 de chaque fichier de poids) et affichent la licence de chaque carte de modèle.

Poids de modèles et licences :

ModèleSource officielleFichiersha256Licence
docling-layout-heronHugging Face docling-project/docling-layout-heronmodel.safetensors00333a43451945aaf89db8ca9c0a17e75d1537c17db60fdb91aa95f4c7929e0cApache-2.0
docling-layout-heron-onnxHugging Face docling-project/docling-layout-heron-onnxmodel.onnx59c81a3a2923042d85034ffc487f8f47e4854117e879aef89b2b9f728fb4922aApache-2.0
TableFormer accurateHugging Face docling-project/docling-modelstableformer_accurate.safetensors2a7d6c924b3cd12fb99a09280ca9c33a89c5d60b93253617d2e088c1a40374d9CDLA-Permissive-2.0
TableFormer fastHugging Face docling-project/docling-modelstableformer_fast.safetensors3119563aab5a7c96fda4d621119b63fd8806272b86c30936d15507616422f718CDLA-Permissive-2.0
PP-OCRv5_mobile_detHugging Face PaddlePaddle/PP-OCRv5_mobile_detinference.pdiparamsafa1820cb16c1fd0dad589d0f8b389139061c1ef6d68019685fd07be997dda5bApache-2.0
PP-OCRv5_mobile_recHugging Face PaddlePaddle/PP-OCRv5_mobile_recinference.pdiparams2460da90875937c94db97eba74ae3d9e5d4c4c57c42f1f41531c09a26bcc771aApache-2.0

Workers HTTP (127.0.0.1 uniquement, port libre choisi par l'appelant) :

HF_HUB_OFFLINE=1 /tmp/toolchains/docling-venv/bin/python tools/rag/docling_worker.py --port PORT --models /tmp/toolchains/docling-models
PADDLE_PDX_CACHE_HOME=/tmp/toolchains/paddleocr-models /tmp/toolchains/paddleocr-venv/bin/python tools/rag/paddleocr_worker.py --port PORT

docling_worker.py reprend le sous-ensemble POST /v1/convert/source de Docling Serve 1.x (sources de type file, réponse document.md_content), OCR Docling désactivé (les scans vont à PaddleOCR, ocr_applied: false est renvoyé). paddleocr_worker.py reprend POST /ocr de PaddleX serving ; oneDNN est désactivé (enable_mkldnn=False) car paddlepaddle 3.3.1 CPU lève NotImplementedError (ConvertPirAttribute2RuntimeAttribute) sur ces modèles. L'adaptateur ajoute l'extension du type MIME au nom de fichier envoyé à Docling, qui choisit le format d'entrée par l'extension.

Preuve : services/control-plane/tests/test_rag_docling_ocr_real.py démarre les deux workers et vérifie, via eai.integrations.docling et eai.integrations.ocr (donc eai.security.egress), un PDF de recette (texte et tableau), un DOCX avec tableau (cellules exactes), l'ingestion par l'API puis la requête, le refus d'une destination hors politique d'egress et l'OCR d'une image de texte rendue par Pillow. Toolchain absente : échec explicite, jamais d'ignorance.