484 lines
11 KiB
Markdown
484 lines
11 KiB
Markdown
# README.md
|
|
|
|
# RetrieX
|
|
|
|
> Hinweis: Das System wird fachlich als **RetrieX** bezeichnet.
|
|
> Im Repository existieren aus historischen Gründen noch einzelne Bezeichner wie `RAG`, `rag.zip` oder `RAG_SYSTEM_OVERVIEW.md`.
|
|
|
|
## Überblick
|
|
|
|
RetrieX ist ein dokumentenbasiertes Assistenzsystem auf Basis von Retrieval-Augmented Generation.
|
|
|
|
Das System beantwortet Nutzeranfragen nicht rein frei, sondern kombiniert:
|
|
|
|
- versionierte Wissensdokumente
|
|
- deterministische Ingest- und Indexierungslogik
|
|
- hybrides Retrieval mit Routing
|
|
- optionale Shopware-Live-Produktsuche
|
|
- LLM-basierte Antwortformulierung
|
|
- SSE-Streaming für die Browserausgabe
|
|
|
|
Der aktuelle Stand ist kein generischer Chatbot, sondern eine kontrollierte Wissens- und Antwortpipeline.
|
|
|
|
---
|
|
|
|
## Technologiestack
|
|
|
|
### Backend
|
|
|
|
- PHP 8.2
|
|
- Symfony 7.4
|
|
- Doctrine ORM / Doctrine Migrations
|
|
- Symfony Messenger
|
|
- Symfony HttpClient
|
|
- Twig
|
|
|
|
### Python-Layer
|
|
|
|
- SentenceTransformers
|
|
- FAISS
|
|
- FastAPI
|
|
- uvicorn
|
|
|
|
### Zusätzliche Libraries
|
|
|
|
- `fivefilters/readability.php` für URL-Inhaltsauswertung
|
|
- `smalot/pdfparser` für PDF-Textgewinnung
|
|
- `league/commonmark` für Markdown-Rendering
|
|
|
|
---
|
|
|
|
## Kernidee des Systems
|
|
|
|
RetrieX trennt klar zwischen:
|
|
|
|
1. **Primärquellen**
|
|
Dokumente, Dokumentversionen, aktive System-Prompts, Modellkonfigurationen, optionale Shopdaten
|
|
|
|
2. **Index- und Retrieval-Ebene**
|
|
`index.ndjson`, `index_meta.json`, `index_runtime.json`, `vector.index`, `tags.ndjson`, `vector_tags.index`
|
|
|
|
3. **Orchestrierung**
|
|
Anfrageannahme, Kontextaufbau, URL-Analyse, Retrieval, Commerce-Erkennung, Prompt-Aufbau, Streaming
|
|
|
|
4. **Ausgabe**
|
|
Token-Streaming an das Frontend über Server-Sent Events
|
|
|
|
---
|
|
|
|
## Aktuell unterstützte Dokumentformate
|
|
|
|
Der verifizierte aktuelle Loader unterstützt:
|
|
|
|
- PDF
|
|
- TXT
|
|
- MD
|
|
|
|
Wichtig:
|
|
Im aktuellen Code-Stand ist **kein produktiver DOCX-Loader** in der eigentlichen Ingest-Pipeline sichtbar. Die README sollte deshalb bewusst nur die real verifizierten Formate nennen.
|
|
|
|
---
|
|
|
|
## Laufzeitfluss einer Anfrage
|
|
|
|
Der zentrale Browser-Endpunkt ist:
|
|
|
|
- `POST /ask-sse`
|
|
|
|
Die Anfrage läuft im aktuellen Stand vereinfacht so:
|
|
|
|
1. `AskSseController` nimmt die Anfrage entgegen
|
|
2. `ClientIdResolver` bestimmt eine stabile Client-ID
|
|
3. Antwort wird als **SSE-Stream** geöffnet
|
|
4. `AgentRunner` orchestriert den kompletten Ablauf
|
|
5. Optional wird URL-Inhalt aus dem Prompt extrahiert
|
|
6. `RetrieverInterface` bzw. `NdjsonHybridRetriever` holt Wissenskontext
|
|
7. Optional wird Commerce-Intent erkannt und Shopware durchsucht
|
|
8. `PromptBuilder` baut den finalen LLM-Prompt
|
|
9. `OllamaClient` streamt die Modellantwort
|
|
10. Die Antwort wird chunkweise ins Frontend gesendet
|
|
11. Der Turn wird in der Gesprächshistorie persistiert
|
|
|
|
---
|
|
|
|
## Gesprächskontext
|
|
|
|
`ContextService` verwaltet die Konversationshistorie dateibasiert.
|
|
|
|
Eigenschaften:
|
|
|
|
- Nutzerhistorie wird pro Client-ID in einer Textdatei gespeichert
|
|
- abgeschlossene Turns werden append-only geschrieben
|
|
- regulärer Kontext und Full-Context sind getrennt vorgesehen
|
|
- der aktuelle SSE-Flow ruft `AgentRunner->run(..., true)` auf und nutzt damit **vollen Kontext**
|
|
|
|
Standardlogik im aktuellen Stand:
|
|
|
|
- normale Historie: letzte 20 Zeilen
|
|
- voller Kontext: letzte 500 Zeilen
|
|
|
|
---
|
|
|
|
## URL-Auswertung
|
|
|
|
Wenn im Prompt eine URL vorkommt, extrahiert `UrlAnalyzer` den Inhalt der **ersten** URL.
|
|
|
|
Aktuelle Eigenschaften:
|
|
|
|
- Timeout-basiertes Laden
|
|
- `Readability`-basierte Extraktion des lesbaren Inhalts
|
|
- HTML wird entfernt
|
|
- Whitespace wird normalisiert
|
|
- Ausgabe wird auf maximal 5000 Zeichen begrenzt
|
|
|
|
Der extrahierte Text wird als zusätzlicher unterstützender Wissensblock in den finalen Prompt aufgenommen.
|
|
|
|
---
|
|
|
|
## Ingest- und Indexarchitektur
|
|
|
|
### Zielbild
|
|
|
|
RetrieX nutzt eine deterministische Ingest-Architektur mit NDJSON als operativer Wissensbasis und FAISS als Vektorindex.
|
|
|
|
### Zentrale Dateien
|
|
|
|
- `var/knowledge/index.ndjson`
|
|
- `var/knowledge/index_meta.json`
|
|
- `var/knowledge/index_runtime.json`
|
|
- `var/knowledge/vector.index`
|
|
- `var/knowledge/vector.index.meta.json`
|
|
- `var/knowledge/tags.ndjson`
|
|
- `var/knowledge/vector_tags.index`
|
|
- `var/knowledge/vector_tags.index.meta.json`
|
|
|
|
### Bedeutung
|
|
|
|
#### `index.ndjson`
|
|
|
|
Operative Chunk-Basis des Systems:
|
|
|
|
- eine JSON-Zeile pro Chunk
|
|
- streamingfähig
|
|
- geeignet für append und full rewrite
|
|
- Grundlage für den Vector-Rebuild
|
|
|
|
#### `index_meta.json`
|
|
|
|
Struktur-Metadaten des Index, u. a.:
|
|
|
|
- `index_version`
|
|
- `created_at`
|
|
- `chunk_size`
|
|
- `chunk_overlap`
|
|
- `embedding_model`
|
|
- `embedding_dimension`
|
|
- `scoring_version`
|
|
- weitere Strukturfelder aus der aktiven Index-Konfiguration
|
|
|
|
#### `index_runtime.json`
|
|
|
|
Laufzeitdaten, u. a.:
|
|
|
|
- `chunk_count`
|
|
- `last_rebuild_at`
|
|
- optionale Commit-Marker wie `last_tags_rebuild_at`
|
|
|
|
---
|
|
|
|
## Dokument-Lifecycle
|
|
|
|
Der aktuelle Dokumentfluss ist technisch so aufgebaut:
|
|
|
|
1. Dokumentversion wird aktiviert oder ingestiert
|
|
2. Guardrails prüfen die Strukturverträglichkeit
|
|
3. alte Chunks des Dokuments werden entfernt
|
|
4. neue Chunks werden streamingfähig geschrieben
|
|
5. der komplette Vektorindex wird neu gebaut
|
|
6. Runtime-Stats werden atomar aktualisiert
|
|
7. Status der Version wird auf `INDEXED` gesetzt
|
|
|
|
Wichtige Eigenschaft:
|
|
|
|
- Pro Dokument gibt es fachlich eine aktive Version
|
|
- Chunks sind abgeleitete Artefakte, keine Primärdaten
|
|
|
|
---
|
|
|
|
## Ingest-Services im aktuellen Stand
|
|
|
|
### `GuardrailValidator`
|
|
|
|
Prüft über `IndexMetaManager`, ob der aktuelle Index strukturell zur aktiven Konfiguration passt.
|
|
|
|
Wenn sich relevante Strukturparameter geändert haben, wird lokaler Ingest blockiert.
|
|
|
|
### `ChunkWriteService`
|
|
|
|
Kapselt die Schreibzugriffe auf den Chunk-Bestand:
|
|
|
|
- Gesamtanzahl zählen
|
|
- Chunks nach `document_id` kompaktieren
|
|
- neue Chunks anhängen
|
|
- vollständigen Rewrite durchführen
|
|
|
|
### `VectorRebuildService`
|
|
|
|
Führt den vollständigen FAISS-Rebuild aus und aktualisiert anschließend die Runtime-Metadaten.
|
|
|
|
### `IngestFlow`
|
|
|
|
Orchestriert:
|
|
|
|
- Dokument-Ingest
|
|
- Global Reindex
|
|
- Dokumentlöschung inklusive Rebuild
|
|
|
|
---
|
|
|
|
## Guardrails
|
|
|
|
RetrieX schützt sich gegen strukturellen Drift.
|
|
|
|
Lokaler Ingest darf nicht weiterlaufen, wenn sich z. B. diese Strukturparameter geändert haben:
|
|
|
|
- Embedding-Modell
|
|
- Embedding-Dimension
|
|
- Chunk-Größe
|
|
- Chunk-Overlap
|
|
- Scoring-Version
|
|
- Index-Strukturkonfiguration
|
|
|
|
Dann ist ein **Global Reindex** erforderlich.
|
|
|
|
---
|
|
|
|
## Retrieval im aktuellen Stand
|
|
|
|
Der aktive Retriever ist:
|
|
|
|
- `App\Knowledge\Retrieval\NdjsonHybridRetriever`
|
|
|
|
Wichtig:
|
|
„Hybrid“ bedeutet hier im verifizierten Code nicht einfach „Keyword + Vektor“, sondern eine orchestrierte Kombination aus mehreren Retrieval-Schritten.
|
|
|
|
### Aktuelle Retrieval-Pipeline
|
|
|
|
1. Query Cleaning
|
|
2. Query Enrichment
|
|
3. Intent-Erkennung
|
|
4. Katalog-/Listenrouting
|
|
5. Tag-Routing auf Kandidatendokumente
|
|
6. globale Vektorsuche
|
|
7. optionale gescopte Vektorsuche auf Kandidatendokumente
|
|
8. Fusion der Treffer
|
|
9. Selektion der finalen Chunk-IDs
|
|
10. Laden der finalen Chunk-Texte aus NDJSON
|
|
|
|
### Verwendete Bausteine
|
|
|
|
- `QueryCleaner`
|
|
- `QueryEnricher`
|
|
- `IntentLite`
|
|
- `SalesIntentLite`
|
|
- `CatalogIntentLite`
|
|
- `IntentRouteResolver`
|
|
- `TagRoutingService`
|
|
- `VectorSearchClient`
|
|
- `NdjsonChunkLookup`
|
|
|
|
### Besondere Logiken
|
|
|
|
- Listenfragen werden gesondert erkannt
|
|
- Kataloganfragen können direkt einen Katalogblock statt regulärer Chunks liefern
|
|
- globale und gescopte Treffer werden gefused
|
|
- Chunk-Selektion ist dokument- und abstandsbegrenzt
|
|
|
|
---
|
|
|
|
## Vektor-Layer
|
|
|
|
### Komponenten
|
|
|
|
- `python/vector/vector_ingest.py`
|
|
- `python/vector/vector_search.py`
|
|
- `python/vector/vector_service.py`
|
|
|
|
### Betriebsmodus
|
|
|
|
Der produktive Zugriff erfolgt primär über den HTTP-basierten Vector-Service.
|
|
|
|
Service-URL im aktuellen Default:
|
|
|
|
- `http://127.0.0.1:8090`
|
|
|
|
### Aufgaben des Vector-Service
|
|
|
|
- Embedding-Modell laden
|
|
- FAISS-Hauptindex laden
|
|
- Tag-Index laden
|
|
- Suchanfragen für Chunks beantworten
|
|
- Suchanfragen für Tags beantworten
|
|
- Reloads auf neue Indexstände ermöglichen
|
|
|
|
### Wichtige Eigenschaft
|
|
|
|
Der Python-Service hält Modell und Indizes persistent im RAM, um wiederholte Suchanfragen deutlich schneller zu bedienen.
|
|
|
|
---
|
|
|
|
## Prompt-Aufbau
|
|
|
|
`PromptBuilder` setzt den finalen LLM-Prompt im aktuellen Stand aus diesen Blöcken zusammen:
|
|
|
|
1. **SYSTEM**
|
|
Aktiver System-Prompt aus der Datenbank
|
|
|
|
2. **CONVERSATION CONTEXT (authoritative)**
|
|
Gesprächshistorie des Nutzers
|
|
|
|
3. **LIVE SHOP RESULTS (authoritative for products)**
|
|
Shopware-Treffer, falls Commerce aktiv ist
|
|
|
|
4. **RETRIEVED KNOWLEDGE (supporting)**
|
|
Wissens-Chunks aus dem Retriever
|
|
|
|
5. **CONTENT FROM URL (supporting)**
|
|
Optional extrahierter Webinhalt
|
|
|
|
6. **USER QUESTION**
|
|
Aktuelle Nutzerfrage
|
|
|
|
Wichtig:
|
|
Für Produktfragen behandelt das System Shopdaten explizit als **führende Quelle**.
|
|
|
|
---
|
|
|
|
## Commerce- und Shopware-Integration
|
|
|
|
RetrieX besitzt im aktuellen Stand eine optionale Shopware-Store-API-Integration.
|
|
|
|
### Aktivierung
|
|
|
|
Die Shop-Suche wird nur genutzt, wenn `CommerceIntentLite` eine passende Anfrage erkennt.
|
|
|
|
Mögliche Zustände:
|
|
|
|
- `none`
|
|
- `product_search`
|
|
- `advisory_product_search`
|
|
|
|
### Signale für Commerce
|
|
|
|
Der Intent-Detektor reagiert u. a. auf:
|
|
|
|
- Such- und Produktbegriffe
|
|
- SKU-/Zahlenmuster
|
|
- Preisangaben
|
|
- Größenangaben
|
|
- Farbangaben
|
|
- beratende Formulierungen wie „passt“, „besser“, „empfiehl“
|
|
|
|
### Aktueller Shop-Flow
|
|
|
|
1. Commerce-Intent erkennen
|
|
2. LLM erzeugt zunächst eine kurze Shop-Suchphrase
|
|
3. `ShopSearchService` ruft `CommerceQueryParser` auf
|
|
4. `ShopwareCriteriaBuilder` baut Suchkriterien
|
|
5. `StoreApiClient` ruft `/store-api/search` auf
|
|
6. Ergebnisse werden zu `ShopProductResult` gemappt
|
|
7. Ergebnisse fließen in den Prompt ein
|
|
|
|
### Wichtige Anmerkung
|
|
|
|
Der aktuelle Code kombiniert also:
|
|
|
|
- **heuristische Intent-Erkennung**
|
|
- **LLM-unterstützte Kurzsuchphrase**
|
|
- **deterministische Nachverarbeitung im Parser**
|
|
- **Store-API-Suche**
|
|
|
|
### Aktuelle Shop-Parameter
|
|
|
|
In `services.yaml` sind u. a. konfiguriert:
|
|
|
|
- `mto.commerce.enabled`
|
|
- `mto.commerce.max_shop_results`
|
|
- `mto.commerce.shop_timeout`
|
|
- `mto.commerce.store_api_base_url`
|
|
- `mto.commerce.sales_channel_access_key`
|
|
|
|
---
|
|
|
|
## Antwort-Streaming
|
|
|
|
Die Browserausgabe erfolgt per **Server-Sent Events**.
|
|
|
|
### Eigenschaften des aktuellen SSE-Flows
|
|
|
|
- Output Buffer werden aktiv geleert
|
|
- Cookies werden vor Streamstart weitergereicht
|
|
- Chunks werden direkt als SSE `data:`-Zeilen übertragen
|
|
- Zeilenumbrüche bleiben erhalten
|
|
- am Ende wird ein `done`-Event gesendet
|
|
|
|
Das ist im aktuellen System die bevorzugte Streaming-Variante für den Browser.
|
|
|
|
---
|
|
|
|
## Projektstruktur
|
|
|
|
Die zentralen Verzeichnisse im aktuellen Stand sind:
|
|
|
|
- `src/`
|
|
- `config/`
|
|
- `templates/`
|
|
- `migrations/`
|
|
- `python/`
|
|
- `public/`
|
|
- `var/`
|
|
- `bin/`
|
|
|
|
### Wichtige Bereiche in `src/`
|
|
|
|
- `Agent/`
|
|
- `Commerce/`
|
|
- `Context/`
|
|
- `Controller/`
|
|
- `Index/`
|
|
- `Ingest/`
|
|
- `Intent/`
|
|
- `Knowledge/`
|
|
- `Routing/`
|
|
- `Shopware/`
|
|
- `Tag/`
|
|
- `Vector/`
|
|
|
|
---
|
|
|
|
## Wichtige Commands
|
|
|
|
Verifizierte relevante Commands im aktuellen Stand:
|
|
|
|
- `mto:agent:chat`
|
|
- `mto:agent:ingest:run`
|
|
- `mto:agent:ingest:version`
|
|
- `mto:agent:vector:control`
|
|
- `mto:agent:vector:rebuild`
|
|
- `mto:agent:vector:health`
|
|
- `mto:agent:tags:export`
|
|
- `mto:agent:tags:rebuild`
|
|
- `mto:agent:tags:job:run`
|
|
- `mto:agent:tag:health`
|
|
- `mto:agent:system:rebuild`
|
|
- `mto:agent:test:shop-search`
|
|
- `mto:agent:test-vector`
|
|
- `mto:agent:user:create`
|
|
|
|
### Besonders wichtig
|
|
|
|
#### Vector-Service steuern
|
|
|
|
```bash
|
|
bin/console mto:agent:vector:control --status
|
|
bin/console mto:agent:vector:control --install --start --reload |