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

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

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

  [ home ](https://msaied.com)    [ articles ](https://msaied.com/articles)    RAG in Laravel: pgvector, Embeddings, and Retrieval-Augmented Generation in Practice        On this page       1. [  Why RAG Belongs in Your Laravel App ](#why-rag-belongs-in-your-laravel-app)
2. [  1. Enable pgvector and Create the Schema ](#1-enable-pgvector-and-create-the-schema)
3. [  2. Generating and Storing Embeddings ](#2-generating-and-storing-embeddings)
4. [  3. Retrieval: Cosine Similarity Query ](#3-retrieval-cosine-similarity-query)
5. [  4. Assembling the Prompt ](#4-assembling-the-prompt)
6. [  Key Takeaways ](#key-takeaways)

  ![RAG in Laravel: pgvector, Embeddings, and Retrieval-Augmented Generation in Practice](https://cdn.msaied.com/281/8d2ac57c0e69d3ff9f1e68faf0e4d10c.png)

  #laravel   #ai   #pgvector   #postgresql   #embeddings  

 RAG in Laravel: pgvector, Embeddings, and Retrieval-Augmented Generation in Practice 
======================================================================================

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

       Table of contents

1. [  01   Why RAG Belongs in Your Laravel App  ](#why-rag-belongs-in-your-laravel-app)
2. [  02   1. Enable pgvector and Create the Schema  ](#1-enable-pgvector-and-create-the-schema)
3. [  03   2. Generating and Storing Embeddings  ](#2-generating-and-storing-embeddings)
4. [  04   3. Retrieval: Cosine Similarity Query  ](#3-retrieval-cosine-similarity-query)
5. [  05   4. Assembling the Prompt  ](#4-assembling-the-prompt)
6. [  06   Key Takeaways  ](#key-takeaways)

 Why RAG Belongs in Your Laravel App
-----------------------------------

Retrieval-Augmented Generation (RAG) is the pattern that lets an LLM answer questions grounded in *your* data rather than its training corpus. Most tutorials reach for Python and LangChain. You don't need either. Laravel, PostgreSQL with the `pgvector` extension, and a thin HTTP client to an embeddings API are enough to ship a production RAG pipeline that your team can actually maintain.

This article walks through the full loop: chunking documents, storing embeddings, querying by cosine similarity, and injecting retrieved context into a prompt.

---

1. Enable pgvector and Create the Schema
----------------------------------------

Install the extension once on your PostgreSQL instance, then write a migration:

```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('token_count');
    // 1536 dims for text-embedding-3-small
    $table->string('embedding', 65535)->nullable(); // stored as raw SQL type below
    $table->timestamps();
});

// Raw statement for the vector column — Blueprint has no native type yet
DB::statement('ALTER TABLE document_chunks ADD COLUMN IF NOT EXISTS embedding vector(1536)');
DB::statement('CREATE INDEX ON document_chunks USING ivfflat (embedding vector_cosine_ops) WITH (lists = 100)');

```

The `ivfflat` index trades perfect recall for speed. For datasets under ~100 k rows, an exact scan is fine — drop the index and benchmark before adding it.

---

2. Generating and Storing Embeddings
------------------------------------

Wrap the OpenAI embeddings endpoint in a focused service:

```php
final class EmbeddingService
{
    public function __construct(private readonly OpenAIClient $client) {}

    /** @return float[] */
    public function embed(string $text): array
    {
        $response = $this->client->embeddings()->create([
            'model' => 'text-embedding-3-small',
            'input' => $text,
        ]);

        return $response->embeddings[0]->embedding;
    }
}

```

Store chunks via a queued job so you never block an HTTP request:

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

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

    public function handle(EmbeddingService $embedder, ChunkSplitter $splitter): void
    {
        foreach ($splitter->split($this->text, maxTokens: 400) as $chunk) {
            $vector = $embedder->embed($chunk);
            $literal = '[' . implode(',', $vector) . ']';

            DB::table('document_chunks')->insert([
                'document_id' => $this->documentId,
                'content'     => $chunk,
                'token_count' => count(explode(' ', $chunk)),
                'embedding'   => DB::raw("'$literal'::vector"),
                'created_at'  => now(),
                'updated_at'  => now(),
            ]);
        }
    }
}

```

---

3. Retrieval: Cosine Similarity Query
-------------------------------------

```php
final class VectorRetriever
{
    public function __construct(private readonly EmbeddingService $embedder) {}

    /** @return Collection */
    public function retrieve(string $query, int $topK = 5): Collection
    {
        $vector = $this->embedder->embed($query);
        $literal = '[' . implode(',', $vector) . ']';

        return DB::table('document_chunks')
            ->selectRaw('content, 1 - (embedding  ?::vector) AS similarity', [$literal])
            ->orderByDesc('similarity')
            ->limit($topK)
            ->get();
    }
}

```

The `` operator is pgvector's cosine distance. Subtracting from 1 gives similarity. No ORM magic needed — raw query builder keeps it transparent.

---

4. Assembling the Prompt
------------------------

```php
final class RagPipeline
{
    public function __construct(
        private readonly VectorRetriever $retriever,
        private readonly OpenAIClient $client,
    ) {}

    public function answer(string $question): string
    {
        $chunks = $this->retriever->retrieve($question, topK: 5);

        $context = $chunks
            ->map(fn ($row) => $row->content)
            ->implode("\n\n---\n\n");

        $response = $this->client->chat()->create([
            'model'    => 'gpt-4o-mini',
            'messages' => [
                ['role' => 'system', 'content' => "Answer using only the context below.\n\n$context"],
                ['role' => 'user',   'content' => $question],
            ],
        ]);

        return $response->choices[0]->message->content;
    }
}

```

Bind `RagPipeline` in a service provider and inject it into a controller or Artisan command. The whole pipeline is testable: fake `EmbeddingService` to return a fixed vector, seed `document_chunks`, assert the SQL similarity query runs.

---

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

- **pgvector + PostgreSQL** eliminates the need for a dedicated vector database at moderate scale.
- **Queue embedding jobs** — never block HTTP workers on synchronous embedding API calls.
- **`ivfflat` index** is a good default; add it only after you have enough rows to justify the build cost.
- **Cosine distance (``)** works well for text embeddings; use `` (inner product) only when vectors are normalized.
- **Keep retrieval and generation separate** — `VectorRetriever` and `RagPipeline` are independently testable and swappable.
- **Chunk size matters**: 300–500 tokens per chunk balances recall and context window usage for most models.

 Found this useful?

          [  ](https://twitter.com/intent/tweet?url=https%3A%2F%2Fmsaied.com%2Farticles%2Frag-in-laravel-pgvector-embeddings-and-retrieval-augmented-generation-in-practice&text=RAG+in+Laravel%3A+pgvector%2C+Embeddings%2C+and+Retrieval-Augmented+Generation+in+Practice) [  ](https://www.linkedin.com/sharing/share-offsite/?url=https%3A%2F%2Fmsaied.com%2Farticles%2Frag-in-laravel-pgvector-embeddings-and-retrieval-augmented-generation-in-practice) 

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

  3 questions  

     Q01  Do I need a dedicated vector database like Pinecone or Weaviate for RAG in Laravel?        Not at moderate scale. PostgreSQL with the pgvector extension handles millions of vectors efficiently, especially with an ivfflat or hnsw index. A dedicated vector DB adds operational overhead that most Laravel apps don't need until they exceed tens of millions of rows. 

      Q02  How do I keep embeddings in sync when document content changes?        Use an Eloquent observer or a model event to dispatch a re-embedding job whenever the source document is updated. Delete the old chunks for that document_id first, then re-chunk and re-embed the new content in the queued job. 

      Q03  Which embedding model should I use with this setup?        OpenAI's text-embedding-3-small (1536 dimensions) is a solid default — it's cheap, fast, and outperforms ada-002 on most retrieval benchmarks. If you need higher accuracy, text-embedding-3-large (3072 dims) is available but requires adjusting the vector column dimension. 

  Continue reading

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

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

 [ ![Typed Enums as First-Class Domain Citizens in Laravel with PHP 8.3](https://cdn.msaied.com/282/71a8fc3e4cf4239b1bf6d38d57e0b985.png) laravel php8.3 enums 

### Typed Enums as First-Class Domain Citizens in Laravel with PHP 8.3

Go beyond simple enum labels. Learn how to attach behaviour, implement interfaces, and use backed enums as Elo...

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

 24 Jun 2026     3 min read  

  Read    

 ](https://msaied.com/articles/typed-enums-as-first-class-domain-citizens-in-laravel-with-php-83) [ ![Ship AI with Laravel: Failover, Queues, and Middleware for AI Agents](https://cdn.msaied.com/283/f0a6d6a6f22d9131bacb96bae1bfc10b.png) Laravel AI Agents Queues 

### Ship AI with Laravel: Failover, Queues, and Middleware for AI Agents

Learn how to make Laravel AI agents production-ready with automatic provider failover, background queue proces...

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

 24 Jun 2026     3 min read  

  Read    

 ](https://msaied.com/articles/ship-ai-with-laravel-failover-queues-and-middleware-for-ai-agents) [ ![Laravel Queues: Reliable Job Middleware, Idempotency, and Graceful Failure Handling](https://cdn.msaied.com/280/3b6ba5c26ef22b77f3c39c5d9a6b76e9.png) laravel queues jobs 

### Laravel Queues: Reliable Job Middleware, Idempotency, and Graceful Failure Handling

Beyond basic dispatching: how to build production-grade queue jobs with custom middleware, idempotency guards,...

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

 24 Jun 2026     4 min read  

  Read    

 ](https://msaied.com/articles/laravel-queues-reliable-job-middleware-idempotency-and-graceful-failure-handling) 

   [  ![Mohamed Said](https://cdn.msaied.com/01KT78WE565VEMM3PSNQAAB0MH.png)   Mohamed Said Laravel Backend Engineer  ](https://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://msaied.com)
- [Projects](https://msaied.com/projects)
- [Articles](https://msaied.com/articles)
- [Certificates](https://msaied.com/certificates)
- [Contact](https://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)
