Laravel RAG Pipeline: pgvector &amp; OpenAI Embeddings | Mohamed Said       [Skip to content](#main)  [ ![](https://cdn.msaied.com/01KT78WE565VEMM3PSNQAAB0MH.png) Mohamed SaidLaravel Backend Engineer ](https://www.msaied.com/public) - [Home](https://www.msaied.com/public)
- [Projects](https://www.msaied.com/public/projects)
- [Articles](https://www.msaied.com/public/articles)
- [Certificates](https://www.msaied.com/public/certificates)
- [About](https://www.msaied.com/public#about)

           [  Contact](https://www.msaied.com/public#contact) Menu 

Menu
----

Close 

 - [HomeStart here](https://www.msaied.com/public)
- [ProjectsCase studies](https://www.msaied.com/public/projects)
- [ArticlesEngineering notes](https://www.msaied.com/public/articles)
- [CertificatesCredentials](https://www.msaied.com/public/certificates)
- [AboutHow I work](https://www.msaied.com/public#about)
- [ContactGet in touch](https://www.msaied.com/public#contact)

  [Start a conversation](https://www.msaied.com/public#contact) [WhatsApp](https://wa.me/201094619204) [Email](mailto:hello@msaied.com) 

 1. [Home](https://www.msaied.com/public)
2. /
3. [Articles](https://www.msaied.com/public/articles)
4. /
5. RAG Pipelines in Laravel: Chunking, Embedding, and Retrieval with pgvector

 RAG Pipelines in Laravel: Chunking, Embedding, and Retrieval with pgvector
===========================================================================

 Build a production-ready Retrieval-Augmented Generation pipeline in Laravel using pgvector, OpenAI embeddings, and a clean chunking strategy — without reaching for a heavyweight framework.

 ![](https://cdn.msaied.com/01M22N44A70A5MC2S599JP0MPH.webp) [Mohamed Said](https://www.msaied.com/public#person) Published 16 Jun 2026 · Updated 16 Jun 2026 · 3 min read

ShareCopy linkCopied

 ![RAG Pipelines in Laravel: Chunking, Embedding, and Retrieval with pgvector](https://cdn.msaied.com/215/e037e13535aa77822f879ee829ec3f68.png) 

  On this page +1. [RAG Pipelines in Laravel Without the Magic Box](#rag-pipelines-in-laravel-without-the-magic-box)
2. [1. Schema: Storing Vectors in PostgreSQL](#1-schema-storing-vectors-in-postgresql)
3. [2. Chunking Strategy](#2-chunking-strategy)
4. [3. Embedding via a Queued Job](#3-embedding-via-a-queued-job)
5. [4. Retrieval: Cosine Similarity Query](#4-retrieval-cosine-similarity-query)
6. [Key Takeaways](#key-takeaways)

 RAG Pipelines in Laravel Without the Magic Box
----------------------------------------------

Retrieval-Augmented Generation (RAG) is the backbone of most production AI features: you embed your own documents, store the vectors, and retrieve the most relevant chunks before sending them to an LLM. Laravel has everything you need to build this cleanly — no Python microservice required.

This article focuses on the **ingestion side**: chunking, embedding, and storing. Retrieval and prompt assembly follow naturally once the foundation is solid.

---

1. Schema: Storing Vectors in PostgreSQL
----------------------------------------

Install the `pgvector` extension and add a `vector` column to your documents table.

```sql
CREATE EXTENSION IF NOT EXISTS vector;

```

```php
// database/migrations/xxxx_create_document_chunks_table.php
Schema::create('document_chunks', function (Blueprint $table) {
    $table->id();
    $table->foreignId('document_id')->constrained()->cascadeOnDelete();
    $table->text('content');
    $table->integer('chunk_index');
    $table->vector('embedding', 1536); // text-embedding-3-small dimensions
    $table->timestamps();
});

// Add an HNSW index for fast approximate nearest-neighbour search
DB::statement('CREATE INDEX ON document_chunks USING hnsw (embedding vector_cosine_ops)');

```

The `vector` column type requires the `pgvector/pgvector-php` package or raw DB statements. Keep dimensions consistent with your chosen model.

---

2. Chunking Strategy
--------------------

Naive line-splitting loses context at boundaries. A sliding-window approach with overlap preserves sentence continuity.

```php
final class TextChunker
{
    public function __construct(
        private readonly int $maxTokens = 400,
        private readonly int $overlapTokens = 50,
    ) {}

    /** @return list */
    public function chunk(string $text): array
    {
        // Rough token estimate: 1 token ≈ 4 characters for English
        $chunkSize = $this->maxTokens * 4;
        $overlap = $this->overlapTokens * 4;
        $chunks = [];
        $offset = 0;
        $length = strlen($text);

        while ($offset < $length) {
            $slice = substr($text, $offset, $chunkSize);

            // Break at the last sentence boundary within the slice
            if ($offset + $chunkSize < $length) {
                $boundary = strrpos($slice, '. ');
                if ($boundary !== false) {
                    $slice = substr($slice, 0, $boundary + 1);
                }
            }

            $chunks[] = trim($slice);
            $offset += strlen($slice) - $overlap;
        }

        return array_filter($chunks);
    }
}

```

Character-based estimation is imprecise but avoids a tokeniser dependency. For stricter control, use `tiktoken-php`.

---

3. Embedding via a Queued Job
-----------------------------

Embedding is I/O-bound and rate-limited — always do it asynchronously.

```php
final class EmbedDocumentChunks implements ShouldQueue
{
    use Dispatchable, Queueable;

    public int $tries = 3;
    public int $backoff = 10;

    public function __construct(private readonly int $documentId) {}

    public function handle(TextChunker $chunker, OpenAIClient $openai): void
    {
        $document = Document::findOrFail($this->documentId);
        $chunks = $chunker->chunk($document->body);

        // Batch embed: OpenAI accepts up to 2048 inputs per request
        $response = $openai->embeddings()->create([
            'model' => 'text-embedding-3-small',
            'input' => $chunks,
        ]);

        $rows = [];
        foreach ($response->embeddings as $i => $embedding) {
            $rows[] = [
                'document_id' => $document->id,
                'chunk_index' => $i,
                'content' => $chunks[$i],
                // Cast float[] to pgvector literal
                'embedding' => '[' . implode(',', $embedding->embedding) . ']',
                'created_at' => now(),
                'updated_at' => now(),
            ];
        }

        // Delete stale chunks before re-inserting
        DocumentChunk::where('document_id', $document->id)->delete();
        DocumentChunk::insert($rows);
    }
}

```

---

4. Retrieval: Cosine Similarity Query
-------------------------------------

```php
final class ChunkRetriever
{
    public function __construct(private readonly OpenAIClient $openai) {}

    /** @return Collection */
    public function retrieve(string $query, int $limit = 5): Collection
    {
        $vector = $this->embed($query);

        return DocumentChunk::query()
            ->orderByRaw('embedding  ?', [$vector])
            ->limit($limit)
            ->get();
    }

    private function embed(string $text): string
    {
        $response = $this->openai->embeddings()->create([
            'model' => 'text-embedding-3-small',
            'input' => $text,
        ]);

        return '[' . implode(',', $response->embeddings[0]->embedding) . ']';
    }
}

```

The `` operator is pgvector's cosine distance. Swap for `` (inner product) or `` (L2) depending on your model's recommendation.

---

Key Takeaways
-------------

- **Chunk with overlap** to avoid losing context at boundaries; sentence-boundary snapping improves coherence.
- **Batch embed** in a single API call per document to stay within rate limits and reduce latency.
- **Delete-then-insert** on re-ingestion keeps chunks consistent without complex upsert logic.
- **HNSW indexes** on the `embedding` column are essential for sub-millisecond retrieval at scale.
- Keep the chunker, embedder, and retriever as small, injectable classes — they're easy to unit-test and swap.

- [laravel](https://www.msaied.com/public/articles?search=laravel)
- [ai](https://www.msaied.com/public/articles?search=ai)
- [pgvector](https://www.msaied.com/public/articles?search=pgvector)
- [rag](https://www.msaied.com/public/articles?search=rag)
- [embeddings](https://www.msaied.com/public/articles?search=embeddings)

 Frequently asked questions 
---------------------------

  Which pgvector distance operator should I use with OpenAI embeddings?OpenAI recommends cosine similarity for their text-embedding models, so use the `&lt;=&gt;` operator in pgvector. Normalise vectors first if you want to use inner product (`&lt;#&gt;`) as an equivalent, faster alternative.

   How do I handle documents that are updated frequently without duplicating chunks?Delete all existing chunks for the document before inserting the new set, as shown in the job above. Wrap the delete and insert in a database transaction to avoid a window where the document has no chunks.

   Is it safe to store raw embedding floats as a string literal in Laravel?Yes, when using the pgvector extension the `\[f1,f2,...\]` string literal is cast to the `vector` type by PostgreSQL. Bind it as a plain string parameter in your query — no special driver support is needed.

   ![Mohamed Said](https://cdn.msaied.com/01M22N44A70A5MC2S599JP0MPH.webp)About the author
----------------

[Mohamed Said](https://www.msaied.com/public#person)Senior Backend Engineer specializing in Laravel, scalable SaaS platforms, APIs, and cloud infrastructure. I build secure, high-performance web applications that help businesses grow.

[About](https://www.msaied.com/public#about) [GitHub ↗](https://github.com/EG-Mohamed) [LinkedIn ↗](https://www.linkedin.com/in/msaiedm/) [WhatsApp ↗](https://wa.me/201094619204) [Email Address ↗](mailto:hello@msaied.com) [My CV ↗](https://drive.google.com/file/u/0/d/1MF20IPRJyzfy32mhEutjL5EpSls0w2Q8/view)  

   [Previous articleLaravel Pest: Architecture Tests, Mutation Testing, and Type Coverage in CI](https://www.msaied.com/public/articles/laravel-pest-architecture-tests-mutation-testing-and-type-coverage-in-ci) [Next articleLaravel Unofficial API Starter Kit from Laravel Daily: The Overview](https://www.msaied.com/public/articles/laravel-unofficial-api-starter-kit-from-laravel-daily-the-overview)  

   On this page
-------------

1. [RAG Pipelines in Laravel Without the Magic Box](#rag-pipelines-in-laravel-without-the-magic-box)
2. [1. Schema: Storing Vectors in PostgreSQL](#1-schema-storing-vectors-in-postgresql)
3. [2. Chunking Strategy](#2-chunking-strategy)
4. [3. Embedding via a Queued Job](#3-embedding-via-a-queued-job)
5. [4. Retrieval: Cosine Similarity Query](#4-retrieval-cosine-similarity-query)
6. [Key Takeaways](#key-takeaways)

 ###  Have a technical challenge?

 Tell me what you’re building. I reply within two working days.

[Start a conversation](https://www.msaied.com/public#contact) 

   Related articles
-----------------

 [ ![](https://cdn.msaied.com/740/cce86edc21eddcbdd2f2454fadaf9c70.png)  · 3 min read### The Pipeline Pattern in Laravel: Custom Pipelines Beyond Middleware

5 Oct 2026 ](https://www.msaied.com/public/articles/the-pipeline-pattern-in-laravel-custom-pipelines-beyond-middleware-1) [ ![](https://cdn.msaied.com/739/2d6897fdcdcf090613f96f72a64b8a78.png)  · 4 min read### MySQL Full-Text Search in Laravel: Indexes, Relevance Scoring, and Boolean Mode

4 Oct 2026 ](https://www.msaied.com/public/articles/mysql-full-text-search-in-laravel-indexes-relevance-scoring-and-boolean-mode) [ ![](https://cdn.msaied.com/738/073696a3fefe18bec825beec5ac658f5.png)  · 4 min read### Laravel Queue Rate-Limited Middleware: Throttling Jobs Without Losing Work

4 Oct 2026 ](https://www.msaied.com/public/articles/laravel-queue-rate-limited-middleware-throttling-jobs-without-losing-work) 

  Have a technical challenge?
----------------------------

Tell me what you’re building. I reply within two working days.

 [Discuss your project ↗](https://www.msaied.com/public#contact) 

  © 2026 Mohamed Said · Built with Laravel, meant to last.Senior Backend Engineer specializing in Laravel, scalable SaaS platforms, APIs, and cloud infrastructure. I build secure, high-performance web applications that help businesses grow.

 - [Home](https://www.msaied.com/public)
- [Articles](https://www.msaied.com/public/articles)
- [Certificates](https://www.msaied.com/public/certificates)
- [GitHub](https://github.com/EG-Mohamed)
- [LinkedIn](https://www.linkedin.com/in/msaiedm/)
- [WhatsApp](https://wa.me/201094619204)
- [Email Address](mailto:hello@msaied.com)
- [My CV](https://drive.google.com/file/u/0/d/1MF20IPRJyzfy32mhEutjL5EpSls0w2Q8/view)
- [Sitemap](https://www.msaied.com/public/sitemap.xml)
