RAG for Statamic
We've built two Statamic addons that layer Retrieval-Augmented Generation (RAG) onto CMS content:
In this post, we show how both addons work, how you configure them, and what value they create for editors and visitors.
Classic keyword search compares strings, not meaning. An example: a visitor searches for "cancel contract," but your site talks about "end subscription," and the keyword search returns nothing.
An embedding is a numerical vector representation of a piece of text, placed in a vector space so that semantically similar texts lie close together. Instead of comparing words, you measure the distance between vectors, and that turns the two phrasings into neighbors.
Statamic entries aren't flat text. A page might be a Bard field, a nested Replicator, a Grid, Markdown, or a simple text field. On top of that comes presentation noise like HTML tags, CSS classes, alignment flags, and IDs. Feed this raw data straight into an embedding model, and the results are correspondingly poor.
1. An entry is saved. Statamic fires the EntrySaved event.
2. A listener checks whether the entry's collection is configured and dispatches a queue job.
3. An extraction pipeline runs through the configured fields and picks a suitable extractor for each field type.
4. Each extractor returns one or more ContentChunk objects.
5. The chunks replace the entry's previous chunks in PostgreSQL.
6. A second queue job generates the embeddings.
For every field type there's a dedicated extractor that knows how to pull meaningful text out of the field:
A ContentChunk carries with it:
Because every chunk records which section of which entry it came from, the assistant can name the exact block that answered a question, not just the page.
For fields too irregular for rule-based parsing, you can use ExtractFieldWithAi. The raw field data is sent to a small, cheap LLM agent. It's instructed to extract only human-readable text, to ignore tags, classes, flags, and technical identifiers, and to never translate or summarize. Via structured output (a JSON schema), the result is always a clean array of chunks.
The feature is opt-in per field or per field type and costs one API call per entry.
Chunks are stored in PostgreSQL using the pgvector extension. The vectors live in the same database, so you don't have to run a separate vector store. Two tables are involved:
After extraction, GenerateEntryEmbeddingsJob processes the chunk texts in batches via the Laravel AI SDK's embeddings API, writes the vectors back, and sets the entry to generated. If the provider is unreachable, the job retries with backoff and otherwise marks the entry as failed.
The entire flow is asynchronous and event-driven: your editors save as usual, and the embedding happens in the background on the queue. Deleting an entry cleans up its embeddings automatically (configurable).
By default, nothing is embedded. You list collections and fields explicitly:
This is a deliberate security decision: it keeps internal notes, admin-only fields, and sensitive data away from the AI provider. Further options can be configured, such as the embedding dimensions (default 1536, which must match the model), whether drafts are skipped, and the queue connection and name.
The Control Panel adds an "AI Tools" section with entry and chunk counts plus status per collection, secured behind a permission.
These addons are a concrete example of how we bring AI into existing projects. If the topic interests you more broadly – from RAG to agents to productive AI workflows – you'll find our work around AI at byte5.ai.
A chat in the Control Panel that answers questions about your content.
1. A user sends a message. It's saved and UserMessageAddedToConversation is dispatched.
2. A queue listener calls the EntriesAssistantAgent.
3. The agent always calls a similarity-search tool first: a pgvector nearest-neighbor query that returns the top 25 chunks.
4. The agent composes a Markdown answer, exclusively from those results. No general knowledge, no internet, no making things up. If nothing relevant is found, it says so.
5. The answer is saved as an assistant message and pushed to the frontend.
That's the core of RAG: the model doesn't answer from its training data but retrieves your content and reasons over it. This keeps answers grounded, up to date, and specific to your website, and reduces hallucinations.
Both addons are open source and available on GitHub: ai-entry-embeddings and ai-entries-assistant. Take a look, give them a try, and we welcome issues, pull requests, and feedback.
PHP-Developer
Marvin Vomberg
Marvin, a PHP developer, is an expert in Laravel and enriches the team with his hands-on attitude and a great passion for his field.
Help from Premier tier Laravel Partner byte5
Our Laravel experts are here to support you.
Contact