RAG für Statamic
Wir haben zwei Statamic-Addons entwickelt, die Retrieval-Augmented Generation (RAG) auf CMS-Inhalte legen:
In diesem Beitrag zeigen wir, wie beide Addons funktionieren, wie ihr sie konfiguriert und welchen Mehrwert sie für Redaktion und Besucher schaffen.
Klassische Keyword-Suche vergleicht Strings, keine Bedeutung. Ein Beispiel: Eine Besucherin sucht nach „Vertrag kündigen“, eure Seite spricht aber von „Abonnement beenden“, und die Keyword-Suche liefert nichts.
Ein Embedding ist eine numerische Vektor-Repräsentation eines Textstücks, platziert in einem Vektorraum, sodass semantisch ähnliche Texte nah beieinander liegen. Statt Wörter zu vergleichen, misst man den Abstand zwischen Vektoren, und damit werden die beiden Formulierungen zu Nachbarn.
Statamic-Entries sind kein flacher Text. Eine Seite kann ein Bard-Field sein, ein verschachtelter Replicator, ein Grid, Markdown oder ein einfaches Textfeld. Dazu kommt Präsentations-Rauschen wie HTML-Tags, CSS-Klassen, Alignment-Flags und IDs. Gibt man diese Rohdaten direkt in ein Embedding-Modell, sind die Ergebnisse entsprechend schlecht.
1. Ein Entry wird gespeichert. Statamic feuert das EntrySaved-Event.
2. Ein Listener prüft, ob die Collection des Entries konfiguriert ist, und dispatcht einen Queue-Job.
3. Eine Extraction-Pipeline durchläuft die konfigurierten Felder und wählt pro Field-Type einen passenden Extractor.
4. Jeder Extractor liefert ein oder mehrere ContentChunk-Objekte.
5. Die Chunks ersetzen die bisherigen Chunks des Entries in PostgreSQL.
6. Ein zweiter Queue-Job erzeugt die Embeddings.
Für jeden Field-Type gibt es einen eigenen Extractor, der weiß, wie er sinnvollen Text aus dem Feld herausholt:
Ein ContentChunk führt mit sich:
Weil jeder Chunk festhält, aus welchem Abschnitt welcher Entries er stammt, kann der Assistent genau den Block benennen, der eine Frage beantwortet hat, nicht nur die Seite.
Für Felder, die für regelbasiertes Parsing zu unregelmäßig sind, lässt sich ExtractFieldWithAi einsetzen. Die rohen Field-Daten gehen dabei an einen kleinen, günstigen LLM-Agenten. Dieser ist angewiesen, nur menschenlesbaren Text zu extrahieren und Tags, Klassen, Flags und technische Identifier zu ignorieren und niemals zu übersetzen oder zusammenzufassen. Über Structured Output (ein JSON-Schema) ist das Ergebnis immer ein sauberes Array von Chunks.
Die Funktion ist pro Feld oder pro Field-Type opt-in und kostet einen API-Call pro Entry.
Chunks werden in PostgreSQL mit der pgvector-Extension gespeichert. Die Vektoren liegen in derselben Datenbank. Ihr müsst also keinen separaten Vector-Store betreiben. Zwei Tabellen kommen zum Einsatz:
Nach der Extraktion verarbeitet GenerateEntryEmbeddingsJob die Chunk-Texte gebündelt über die Embeddings-API des Laravel AI SDK, schreibt die Vektoren zurück und setzt den Entry auf generated. Ist der Provider nicht erreichbar, versucht der Job es mit Backoff erneut und markiert den Entry andernfalls als failed.
Der gesamte Flow ist asynchron und event-getrieben: Eure Redaktion speichert wie gewohnt, das Embedding passiert im Hintergrund auf der Queue. Das Löschen eines Entries räumt seine Embeddings automatisch auf (konfigurierbar).
Standardmäßig wird nichts eingebettet. Ihr listet Collections und Felder explizit:
Das ist eine bewusste Sicherheitsentscheidung: Sie hält interne Notizen, admin-only Felder und sensible Daten vom AI-Provider fern. Weitere Optionen lassen sich konfigurieren, etwa die Embedding-Dimensionen (Standard 1536, muss zum Modell passen), ob Drafts übersprungen werden sowie Queue-Connection und -Name.
Das Control Panel ergänzt einen Bereich „AI Tools" mit Entry- und Chunk-Zählern plus Status pro Collection, abgesichert über eine Permission.
Diese Addons sind ein konkretes Beispiel dafür, wie wir AI in bestehende Projekte bringen. Wenn euch das Thema grundsätzlich interessiert – von RAG über Agenten bis zu produktiven AI-Workflows – findet ihr auf byte5.ai unsere Arbeit rund um AI.
Ein Chat im Control Panel, der Fragen zu euren Inhalten beantwortet.
1. Ein Nutzer sendet eine Nachricht. Sie wird gespeichert und UserMessageAddedToConversation dispatcht.
2. Ein Queue-Listener ruft den EntriesAssistantAgent auf.
3. Der Agent ruft immer zuerst ein Similarity-Search-Tool auf: eine pgvector-Nearest-Neighbour-Query, die die Top 25 Chunks zurückgibt.
4. Der Agent formuliert eine Markdown-Antwort, ausschließlich aus diesen Ergebnissen, ohne Zusätzliches zu erfinden. Wird nichts Relevantes gefunden, sagt er das.
5. Die Antwort wird als Assistant-Message gespeichert und ans Frontend gepusht.
Das ist der Kern von RAG: Das Modell antwortet nicht aus seinen Trainingsdaten, sondern ruft eure Inhalte ab und argumentiert darauf. Das hält Antworten fundiert, aktuell und spezifisch für eure Website und reduziert Halluzinationen.
Beide Addons sind Open Source und auf GitHub verfügbar: ai-entry-embeddings und ai-entries-assistant. Schaut rein, probiert sie aus, und wir freuen uns über Issues, Pull Requests und Feedback.
PHP-Entwickler
Marvin Vomberg
PHP-Entwickler Marvin ist Experte für Laravel und bereichert das Team mit seiner anpackenden Art und einer großen Leidenschaft für sein Fachgebiet.
Hilfe vom Laravel Premier Partner
Unsere Expert:innen für Laravel unterstützen dich.
Kontakt