optimize py control service
This commit is contained in:
410
README.md
410
README.md
@@ -1,19 +1,20 @@
|
||||
# mitho AI Agent – Developer README
|
||||
Enterprise Hybrid RAG System (Symfony + NDJSON + FAISS)
|
||||
Enterprise Hybrid RAG System (Symfony + NDJSON + FAISS + Persistent Vector Service)
|
||||
|
||||
Stand: Februar 2026
|
||||
Status: Produktiv stabil – Job-basierte Ingest-Architektur vollständig integriert
|
||||
Status: Produktiv stabil – Job-basierte Ingest-Architektur + Persistenter Vector-Service integriert
|
||||
|
||||
---
|
||||
|
||||
# 1. Systemüberblick
|
||||
|
||||
Dieses System implementiert eine deterministische, governance-stabile
|
||||
Dieses System implementiert eine deterministische, governance-stabile
|
||||
Hybrid-RAG-Architektur mit:
|
||||
|
||||
- Symfony (PHP Backend)
|
||||
- NDJSON als Single Source of Truth
|
||||
- FAISS als Vektorindex (immer Full Rebuild)
|
||||
- Persistenter Python Vector-Service (FastAPI + Uvicorn)
|
||||
- Hybrid Retrieval (Keyword + Vektor)
|
||||
- Versioniertes Dokumentmodell
|
||||
- Job-basierte Ingest-Pipeline
|
||||
@@ -21,28 +22,34 @@ Hybrid-RAG-Architektur mit:
|
||||
- SSE-Streaming im Frontend
|
||||
|
||||
Grundprinzip:
|
||||
Keine inkrementellen Vektor-Updates.
|
||||
FAISS wird immer vollständig aus index.ndjson neu gebaut.
|
||||
|
||||
- Keine inkrementellen Vektor-Updates
|
||||
- FAISS wird immer vollständig aus `index.ndjson` neu gebaut
|
||||
- Retrieval läuft über einen persistenten Service (kein Python-Spawn pro Anfrage)
|
||||
|
||||
---
|
||||
|
||||
# 2. Architekturprinzipien
|
||||
|
||||
Determinismus:
|
||||
- Gleiche Dokumente + gleiche Konfiguration → identisches index.ndjson
|
||||
- Gleiches index.ndjson → identisches FAISS
|
||||
## 2.1 Determinismus
|
||||
|
||||
- Gleiche Dokumente + gleiche Konfiguration → identisches `index.ndjson`
|
||||
- Gleiches `index.ndjson` → identisches FAISS
|
||||
- Gleiche Query → identisches Retrieval-Ergebnis
|
||||
|
||||
Governance:
|
||||
## 2.2 Governance
|
||||
|
||||
- Eine aktive Version pro Dokument
|
||||
- Keine impliziten Index-Änderungen
|
||||
- Strukturänderungen erzwingen Global Reindex
|
||||
- Keine Selbstmodifikation durch KI
|
||||
|
||||
Skalierbarkeit:
|
||||
## 2.3 Skalierbarkeit
|
||||
|
||||
- NDJSON (streamingfähig)
|
||||
- Keine RAM-basierte JSON-Arrays
|
||||
- Kein RAM-basiertes JSON-Array
|
||||
- Zielgröße > 200k Chunks
|
||||
- FAISS Full Rebuild ist deterministisch
|
||||
|
||||
---
|
||||
|
||||
@@ -54,39 +61,44 @@ Single Source of Truth.
|
||||
|
||||
- 1 JSON-Objekt pro Zeile
|
||||
- Streaming-Append
|
||||
- Deterministische Compaction by document_id
|
||||
- Deterministische Compaction by `document_id`
|
||||
|
||||
Beispielstruktur:
|
||||
|
||||
```json
|
||||
{
|
||||
"chunk_id": "uuid",
|
||||
"document_id": "uuid",
|
||||
"document_version_id": "uuid",
|
||||
"text": "...",
|
||||
"meta": {...}
|
||||
"chunk_id": "uuid",
|
||||
"document_id": "uuid",
|
||||
"document_version_id": "uuid",
|
||||
"text": "...",
|
||||
"meta": { ... }
|
||||
}
|
||||
```
|
||||
|
||||
Keine JSON-Array-Datei.
|
||||
Keine Mutation einzelner Chunks.
|
||||
Nur Append + deterministische Entfernung per document_id.
|
||||
Regeln:
|
||||
|
||||
- Keine JSON-Array-Datei
|
||||
- Keine Mutation einzelner Chunks
|
||||
- Nur Append + deterministische Entfernung per `document_id`
|
||||
|
||||
---
|
||||
|
||||
## 3.2 index_meta.json
|
||||
|
||||
Enthält Strukturparameter:
|
||||
Strukturparameter:
|
||||
|
||||
- index_version
|
||||
- embedding_model
|
||||
- embedding_dimension
|
||||
- chunk_size
|
||||
- chunk_overlap
|
||||
- scoring_version
|
||||
- index_format
|
||||
- vector_backend
|
||||
- `index_version`
|
||||
- `embedding_model`
|
||||
- `embedding_dimension`
|
||||
- `chunk_size`
|
||||
- `chunk_overlap`
|
||||
- `scoring_version`
|
||||
- `index_format`
|
||||
- `vector_backend`
|
||||
|
||||
Wenn einer dieser Werte sich ändert:
|
||||
→ Global Reindex zwingend erforderlich.
|
||||
|
||||
→ **Global Reindex zwingend erforderlich**
|
||||
|
||||
---
|
||||
|
||||
@@ -94,31 +106,117 @@ Wenn einer dieser Werte sich ändert:
|
||||
|
||||
Dateien:
|
||||
|
||||
- vector.index
|
||||
- vector_meta.json (Chunk-ID Mapping)
|
||||
- `vector.index`
|
||||
- `vector.index.meta.json`
|
||||
- `vector_tags.index`
|
||||
- `vector_tags.index.meta.json`
|
||||
|
||||
FAISS wird IMMER vollständig aus index.ndjson gebaut.
|
||||
Keine Partial Updates.
|
||||
FAISS wird IMMER vollständig aus `index.ndjson` gebaut.
|
||||
|
||||
Keine Partial Updates.
|
||||
Kein inkrementelles Vector-Append.
|
||||
|
||||
---
|
||||
|
||||
# 4. Dokument- & Versionsmodell
|
||||
# 4. Persistenter Vector-Service
|
||||
|
||||
Document
|
||||
→ enthält mehrere DocumentVersion
|
||||
Retrieval läuft nicht mehr über:
|
||||
|
||||
- Symfony Process
|
||||
- `exec()`
|
||||
- `python vector_search.py` pro Anfrage
|
||||
|
||||
Sondern über:
|
||||
|
||||
**FastAPI + Uvicorn (persistent im RAM)**
|
||||
|
||||
## 4.1 Eigenschaften
|
||||
|
||||
Beim Start lädt der Service:
|
||||
|
||||
- Embedding-Modell
|
||||
- Chunk-Index
|
||||
- Tag-Index
|
||||
- ID-Mappings
|
||||
|
||||
Diese bleiben dauerhaft im RAM.
|
||||
|
||||
Kein Modell-Reload pro Anfrage.
|
||||
Kein Disk-Reload pro Anfrage.
|
||||
Kein Python-Spawn pro Anfrage.
|
||||
|
||||
## 4.2 Endpoints
|
||||
|
||||
- `GET /health`
|
||||
- `POST /search-chunks`
|
||||
- `POST /search-tags`
|
||||
- `POST /reload`
|
||||
|
||||
## 4.3 Reload-Mechanismus
|
||||
|
||||
Nach Global Reindex:
|
||||
|
||||
```bash
|
||||
curl -X POST http://127.0.0.1:8090/reload
|
||||
```
|
||||
|
||||
Lädt:
|
||||
|
||||
- Chunk-Index neu
|
||||
- Tag-Index neu
|
||||
- Modell nur wenn `embedding_model` geändert wurde
|
||||
|
||||
Kein Neustart nötig.
|
||||
Keine Downtime.
|
||||
|
||||
---
|
||||
|
||||
# 5. Score-Gates (Routing-Sicherheit)
|
||||
|
||||
## 5.1 Tag-Gate
|
||||
|
||||
Tags steuern Routing.
|
||||
|
||||
Empfohlener Mindestscore:
|
||||
|
||||
`MIN_SCORE ≈ 0.70`
|
||||
|
||||
Schützt vor:
|
||||
|
||||
- zufälligen semantischen Treffern
|
||||
- falschem Dokumentrouting
|
||||
|
||||
## 5.2 Chunk-Gate
|
||||
|
||||
Chunks sind Kontext.
|
||||
|
||||
Weicher Gate:
|
||||
|
||||
`MIN_SCORE ≈ 0.50`
|
||||
|
||||
Optional: relativer Score zum besten Treffer.
|
||||
|
||||
---
|
||||
|
||||
# 6. Dokument- & Versionsmodell
|
||||
|
||||
Document
|
||||
→ enthält mehrere `DocumentVersion`
|
||||
→ genau eine Version ist aktiv
|
||||
|
||||
Regel:
|
||||
|
||||
Es darf immer nur eine aktive Version pro Dokument existieren.
|
||||
|
||||
Beim Aktivieren einer Version:
|
||||
|
||||
- Alle anderen Versionen werden inaktiv
|
||||
- IngestStatus → PENDING
|
||||
- `IngestStatus → PENDING`
|
||||
- Re-Ingest via Job
|
||||
|
||||
---
|
||||
|
||||
# 5. Ingest-Architektur (vollständig Job-basiert)
|
||||
# 7. Ingest-Architektur (vollständig Job-basiert)
|
||||
|
||||
Ingest läuft NIEMALS synchron im HTTP-Request.
|
||||
|
||||
@@ -126,126 +224,44 @@ Jede Mutation am Index läuft über:
|
||||
|
||||
IngestJob → CLI Runner → IngestOrchestrator → IngestFlow
|
||||
|
||||
---
|
||||
## 7.1 Job-Typen
|
||||
|
||||
## 5.1 Job-Typen
|
||||
- `DOCUMENT_VERSION_ACTIVATE`
|
||||
- `DOCUMENT`
|
||||
- `GLOBAL_REINDEX`
|
||||
|
||||
DOCUMENT_VERSION_ACTIVATE
|
||||
- Wird genutzt für:
|
||||
- Version aktivieren
|
||||
- Neue Datei hochladen (Auto-Ingest)
|
||||
## 7.2 Job-Status
|
||||
|
||||
DOCUMENT
|
||||
- Manuelles Ingest einer Version
|
||||
- `QUEUED`
|
||||
- `RUNNING`
|
||||
- `COMPLETED`
|
||||
- `FAILED`
|
||||
- `ABORTED`
|
||||
|
||||
GLOBAL_REINDEX
|
||||
- Strukturänderungen
|
||||
|
||||
---
|
||||
|
||||
## 5.2 Job-Status
|
||||
|
||||
- QUEUED
|
||||
- RUNNING
|
||||
- COMPLETED
|
||||
- FAILED
|
||||
- ABORTED
|
||||
|
||||
Jobs werden über CLI ausgeführt:
|
||||
CLI-Ausführung:
|
||||
|
||||
```bash
|
||||
php bin/console mto:agent:ingest:run <jobId>
|
||||
|
||||
Start erfolgt asynchron per exec() aus dem Controller.
|
||||
|
||||
---
|
||||
|
||||
# 6. Admin-Flows (aktueller Stand)
|
||||
|
||||
## 6.1 Neue Datei hochladen (NEU: Auto-Ingest)
|
||||
|
||||
Beim Upload:
|
||||
|
||||
1. Datei speichern
|
||||
2. Document + Version 1 erzeugen
|
||||
3. Version 1 aktiv setzen
|
||||
4. IngestJob vom Typ DOCUMENT_VERSION_ACTIVATE anlegen
|
||||
5. Job asynchron starten
|
||||
6. Redirect auf Job-Detailseite
|
||||
|
||||
Ergebnis:
|
||||
Neue Dokumente werden automatisch indexiert.
|
||||
|
||||
---
|
||||
|
||||
## 6.2 Version aktivieren
|
||||
|
||||
1. DB-Status anpassen
|
||||
2. IngestStatus → PENDING
|
||||
3. DOCUMENT_VERSION_ACTIVATE Job erzeugen
|
||||
4. Async Runner starten
|
||||
5. Redirect zur Job-Seite
|
||||
|
||||
---
|
||||
|
||||
## 6.3 Manuelles Ingest
|
||||
|
||||
1. DOCUMENT Job erzeugen
|
||||
2. Async Runner starten
|
||||
3. Redirect zur Job-Seite
|
||||
|
||||
---
|
||||
|
||||
## 6.4 Reset
|
||||
|
||||
Reset löscht:
|
||||
|
||||
- index.ndjson
|
||||
- vector.index
|
||||
- vector_meta.json
|
||||
- Upload-Verzeichnis
|
||||
- Tabellen:
|
||||
- document
|
||||
- document_version
|
||||
- ingest_job
|
||||
|
||||
Nur möglich, wenn exec() aktiv ist.
|
||||
|
||||
---
|
||||
|
||||
# 7. Ingest-Flow Details
|
||||
|
||||
Local Ingest (ein Dokument):
|
||||
|
||||
1. Extract
|
||||
2. Normalize
|
||||
3. Chunk deterministisch
|
||||
4. Entferne alte Chunks per document_id
|
||||
5. Append neue Chunks
|
||||
6. Full FAISS Rebuild
|
||||
|
||||
Global Reindex:
|
||||
|
||||
1. Alle aktiven Versionen neu verarbeiten
|
||||
2. Komplettes index.ndjson neu schreiben
|
||||
3. FAISS neu bauen
|
||||
4. index_version++
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
# 8. Hybrid Retrieval
|
||||
|
||||
Ablauf:
|
||||
Flow:
|
||||
|
||||
User Query
|
||||
→ Keyword Retrieval
|
||||
→ FAISS Vector Retrieval
|
||||
→ Score Fusion
|
||||
→ NDJSON Chunk Lookup
|
||||
→ Context Builder
|
||||
→ LLM
|
||||
User Query
|
||||
→ Keyword Retrieval
|
||||
→ Tag Vector Search
|
||||
→ Dokumentfilter
|
||||
→ Chunk Vector Retrieval
|
||||
→ Score Fusion
|
||||
→ NDJSON Lookup
|
||||
→ Context Builder
|
||||
→ LLM
|
||||
→ SSE Streaming
|
||||
|
||||
Keyword ist Primärsignal.
|
||||
Keyword bleibt Primärsignal.
|
||||
Vector ergänzt Semantik.
|
||||
|
||||
---
|
||||
@@ -262,38 +278,101 @@ Keine gleichzeitigen Mutationen erlaubt.
|
||||
|
||||
---
|
||||
|
||||
# 10. CLI Commands
|
||||
# 10. Vector Control (Production Safe)
|
||||
|
||||
mto:agent:ingest:run <jobId>
|
||||
mto:agent:vector:ingest
|
||||
mto:agent:vector:search
|
||||
Ein zentrales Kommando steuert:
|
||||
|
||||
- Dependency-Check
|
||||
- Auto-Install (opt-in)
|
||||
- Service Start
|
||||
- Service Stop
|
||||
- Reload
|
||||
- Status
|
||||
- Health-Check
|
||||
- PID-Management
|
||||
|
||||
## Command
|
||||
|
||||
`mto:agent:vector:control`
|
||||
|
||||
## Beispiele
|
||||
|
||||
Status:
|
||||
|
||||
```bash
|
||||
bin/console mto:agent:vector:control
|
||||
```
|
||||
|
||||
Install + Start:
|
||||
|
||||
```bash
|
||||
bin/console mto:agent:vector:control --install --start
|
||||
```
|
||||
|
||||
Stop:
|
||||
|
||||
```bash
|
||||
bin/console mto:agent:vector:control --stop
|
||||
```
|
||||
|
||||
Reload:
|
||||
|
||||
```bash
|
||||
bin/console mto:agent:vector:control --reload
|
||||
```
|
||||
|
||||
## Production-Safety
|
||||
|
||||
- PID-File unter `var/run/vector_service.pid`
|
||||
- SIGTERM Stop mit Timeout
|
||||
- Optional SIGKILL (`--force`)
|
||||
- Health-Check mit Retry-Mechanismus
|
||||
- Kein automatisches Install ohne Flag
|
||||
|
||||
---
|
||||
|
||||
# 11. CLI Commands
|
||||
|
||||
- `mto:agent:ingest:run <jobId>`
|
||||
- `mto:agent:vector:control`
|
||||
- `mto:agent:test`
|
||||
- `mto:agent:chat`
|
||||
|
||||
Alle Commands unter:
|
||||
mto:agent:*
|
||||
|
||||
`mto:agent:*`
|
||||
|
||||
---
|
||||
|
||||
# 11. Failure Modes
|
||||
# 12. Failure Modes
|
||||
|
||||
- Vector index fehlt → vector ingest ausführen
|
||||
- index_meta mismatch → Global Reindex
|
||||
- exec deaktiviert → Async-Start schlägt fehl
|
||||
- Lock aktiv → Parallel-Ingest blockiert
|
||||
Vector Service nicht erreichbar
|
||||
→ `vector:control --start`
|
||||
|
||||
Reload Endpoint fehlt
|
||||
→ falsche Service-Version
|
||||
|
||||
index_meta mismatch
|
||||
→ Global Reindex
|
||||
|
||||
Lock aktiv
|
||||
→ Parallel-Ingest blockiert
|
||||
|
||||
---
|
||||
|
||||
# 12. Non-Goals
|
||||
# 13. Non-Goals
|
||||
|
||||
- Kein Online-Learning
|
||||
- Keine inkrementellen FAISS Updates
|
||||
- Keine selbstverändernden Prompts
|
||||
- Kein Auto-Merging von Chunks
|
||||
- Kein Vector-Append im Runtime
|
||||
|
||||
Strukturänderungen → explizit + reindex.
|
||||
Strukturänderungen → explizit + Reindex.
|
||||
|
||||
---
|
||||
|
||||
# 13. Zusammenfassung
|
||||
# 14. Zusammenfassung
|
||||
|
||||
Dieses System ist:
|
||||
|
||||
@@ -304,11 +383,14 @@ Dieses System ist:
|
||||
- enterprise-ready
|
||||
- job-basiert
|
||||
- versionssicher
|
||||
- persistent im Retrieval
|
||||
- ohne Spawn-Overhead
|
||||
- reload-fähig ohne Downtime
|
||||
|
||||
Wichtige Neuerung:
|
||||
Neue Dokumente lösen jetzt automatisch einen IngestJob aus
|
||||
(exakt derselbe Mechanismus wie bei Version-Aktivierung).
|
||||
Wichtige Neuerungen:
|
||||
|
||||
Kein HTTP-Ingest mehr.
|
||||
Keine Inline-Rebuilds.
|
||||
Alles läuft über das Job-System.
|
||||
- Persistenter Vector-Service ersetzt CLI-Spawn
|
||||
- Score-Gates verhindern falsches Routing
|
||||
- Reload-Endpoint vermeidet Neustarts
|
||||
- Production-Safe Control Command integriert
|
||||
- Vollständige Trennung von Ingest und Runtime
|
||||
Reference in New Issue
Block a user