Laravel RAG Pipeline: pgvector &amp; OpenAI Embeddings | Mohamed Said        [  ![Mohamed Said](https://cdn.msaied.com/01KT78WE565VEMM3PSNQAAB0MH.png)   Mohamed Said Laravel Backend Engineer  ](https://www.msaied.com) [ Home ](https://www.msaied.com) [ Projects ](https://www.msaied.com/projects) [ Articles  ](https://www.msaied.com/articles) [ Certificates ](https://www.msaied.com/certificates) [ Contact ](https://www.msaied.com#contact-section) 

       [  ](https://github.com/EG-Mohamed)       

 [ Home ](https://www.msaied.com) [ Projects ](https://www.msaied.com/projects) [ Articles ](https://www.msaied.com/articles) [ Certificates ](https://www.msaied.com/certificates) [ Contact ](https://www.msaied.com#contact-section) 

  [ home ](https://www.msaied.com)    [ articles ](https://www.msaied.com/articles)    RAG Pipelines in Laravel: Chunking, Embedding, and Retrieval with pgvector        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: Chunking, Embedding, and Retrieval with pgvector](https://cdn.msaied.com/215/e037e13535aa77822f879ee829ec3f68.png)

  #laravel   #ai   #pgvector   #rag   #embeddings  

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

     16 Jun 2026      3 min read    ![Mohamed Said](https://cdn.msaied.com/01KT78WE565VEMM3PSNQAAB0MJ.jpg)  Mohamed Said  

       Table of contents

1. [  01   RAG Pipelines in Laravel Without the Magic Box  ](#rag-pipelines-in-laravel-without-the-magic-box)
2. [  02   1. Schema: Storing Vectors in PostgreSQL  ](#1-schema-storing-vectors-in-postgresql)
3. [  03   2. Chunking Strategy  ](#2-chunking-strategy)
4. [  04   3. Embedding via a Queued Job  ](#3-embedding-via-a-queued-job)
5. [  05   4. Retrieval: Cosine Similarity Query  ](#4-retrieval-cosine-similarity-query)
6. [  06   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.

 Found this useful?

          [  ](https://twitter.com/intent/tweet?url=https%3A%2F%2Fwww.msaied.com%2Farticles%2Frag-pipelines-in-laravel-chunking-embedding-and-retrieval-with-pgvector&text=RAG+Pipelines+in+Laravel%3A+Chunking%2C+Embedding%2C+and+Retrieval+with+pgvector) [  ](https://www.linkedin.com/sharing/share-offsite/?url=https%3A%2F%2Fwww.msaied.com%2Farticles%2Frag-pipelines-in-laravel-chunking-embedding-and-retrieval-with-pgvector) 

 Frequently Asked Questions 
----------------------------

  3 questions  

     Q01  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. 

      Q02  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. 

      Q03  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. 

  Continue reading

 More Articles 
---------------

 [ View all    ](https://www.msaied.com/articles) 

 [ ![Laravel Telescope Alternatives: Building a Lightweight Request Inspector with Middleware](https://cdn.msaied.com/216/9b6d240010b80483f072902dafcd216c.png) laravel middleware debugging 

### Laravel Telescope Alternatives: Building a Lightweight Request Inspector with Middleware

Telescope is powerful but heavy for production. Learn how to build a focused, low-overhead request inspector u...

  ![Mohamed Said](https://cdn.msaied.com/01KT78WE565VEMM3PSNQAAB0MJ.jpg)  Mohamed Said 

 16 Jun 2026     1 min read  

  Read    

 ](https://www.msaied.com/articles/laravel-telescope-alternatives-building-a-lightweight-request-inspector-with-middleware) [ ![Laravel Pest: Architecture Tests, Mutation Testing, and Type Coverage in CI](https://cdn.msaied.com/214/0d4822fa8ee1765d0689e387dd849d92.png) laravel pest testing 

### Laravel Pest: Architecture Tests, Mutation Testing, and Type Coverage in CI

Go beyond feature tests. Learn how to enforce architectural rules, catch logic gaps with mutation testing, and...

  ![Mohamed Said](https://cdn.msaied.com/01KT78WE565VEMM3PSNQAAB0MJ.jpg)  Mohamed Said 

 16 Jun 2026     4 min read  

  Read    

 ](https://www.msaied.com/articles/laravel-pest-architecture-tests-mutation-testing-and-type-coverage-in-ci) [ ![Laravel Pennant: Feature Flags with Scope, Storage, and Custom Drivers](https://cdn.msaied.com/212/ab98aa676ce445275d736755a046b360.png) laravel feature-flags pennant 

### Laravel Pennant: Feature Flags with Scope, Storage, and Custom Drivers

Laravel Pennant ships with more power than most teams use. Learn how to scope flags to tenants, swap storage d...

  ![Mohamed Said](https://cdn.msaied.com/01KT78WE565VEMM3PSNQAAB0MJ.jpg)  Mohamed Said 

 16 Jun 2026     4 min read  

  Read    

 ](https://www.msaied.com/articles/laravel-pennant-feature-flags-with-scope-storage-and-custom-drivers) 

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

Explore

- [Home](https://www.msaied.com)
- [Projects](https://www.msaied.com/projects)
- [Articles](https://www.msaied.com/articles)
- [Certificates](https://www.msaied.com/certificates)
- [Contact](https://www.msaied.com#contact-section)

Connect

- [   hello@msaied.com ](mailto:hello@msaied.com)
- [   +20 109 461 9204 ](tel:+201094619204)

© 2026 Mohamed Said. All rights reserved.

 [  ](https://github.com/EG-Mohamed) [  ](https://www.linkedin.com/in/msaiedm/) [  ](https://wa.me/201094619204) [  ](mailto:hello@msaied.com) [  ](https://drive.google.com/file/u/0/d/1MF20IPRJyzfy32mhEutjL5EpSls0w2Q8/view)
