# 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:config:validate`
- `mto:agent:config:audit-source`
- `mto:agent:config:dump-effective`
- `mto:agent:regression:test`
- `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

```


---

## Developer Policies / Governance

The YAML-only migration is treated as completed after Patch 11.0a, as long as these checks stay green:

```bash
php bin/console mto:agent:config:validate
php bin/console mto:agent:config:audit-source --details
php bin/console mto:agent:regression:test
```

From now on, developers must follow `DEVELOPER_POLICIES.md`.

Core rules:

- YAML under `config/retriex/` is the source of truth for configurable behavior.
- No new PHP-only semantic defaults, token lists, prompt texts, matching rules or business fallbacks.
- New configurable behavior must be added through YAML and wired explicitly.
- Regression-sensitive flows from the 1.4.2/1.5.0 baseline must remain protected.
- Strict YAML validation is deferred and must remain disabled by default when introduced later.
