# Building Local RAG Workflows in Laravel with NativeRAG

> Source: <https://dev.to/hamdyelbatal122/building-local-rag-workflows-in-laravel-with-nativerag-3gfd>
> Published: 2026-10-12 16:12:31+00:00

AI features are becoming normal in web applications, but a lot of teams still hesitate when the first step requires sending private application data to an external API.

That is the problem NativeRAG tries to solve for Laravel developers.

NativeRAG is a Laravel package for building small to medium retrieval-augmented generation workflows using local models through Ollama or LM Studio. It gives you chat completions, embeddings, vector search, streaming responses, model indexing, and conversation persistence without needing a separate vector database to get started.

The goal is simple: keep the Laravel developer experience familiar while making local AI workflows easier to wire into a real application.

Retrieval-augmented generation usually has a few moving parts:

In many setups, those pieces quickly turn into multiple services. That can be the right architecture at scale, but it is not always what you want on day one.

Sometimes you just need to index Eloquent models, search relevant chunks, and ask a local model a question using that context.

NativeRAG keeps those pieces inside Laravel as much as possible.

The package currently supports:

`Embeddable` trait
It is not trying to replace a dedicated vector database for every use case. Instead, it gives Laravel applications a practical starting point for local RAG features.

Install the package with Composer:

```
composer require hamzi/nativerag
```

Then publish the configuration and migrations:

```
php artisan vendor:publish --tag="nativerag-config"
php artisan vendor:publish --tag="nativerag-migrations"
php artisan migrate
```

The package stores embeddings and conversations in your application database.

You can use Ollama as the default driver:

```
NATIVE_RAG_DRIVER=ollama

OLLAMA_BASE_URL=http://localhost:11434
OLLAMA_CHAT_MODEL=llama3
OLLAMA_EMBEDDING_MODEL=nomic-embed-text
```

Or LM Studio:

```
NATIVE_RAG_DRIVER=lmstudio

LMSTUDIO_BASE_URL=http://localhost:1234
LMSTUDIO_CHAT_MODEL=meta-llama-3-8b-instruct
LMSTUDIO_EMBEDDING_MODEL=nomic-embed-text
```

You can also tune chunking and search behavior:

```
NATIVE_RAG_CHUNK_SIZE=1000
NATIVE_RAG_CHUNK_OVERLAP=200
NATIVE_RAG_MIN_SCORE=0.35
```

The simplest use case is sending messages to a local chat model:

``` php
use Hamzi\NativeRag\Facades\NativeRag;

$response = NativeRag::chat([
    [
        'role' => 'system',
        'content' => 'You are a concise Laravel assistant.',
    ],
    [
        'role' => 'user',
        'content' => 'Explain service containers in simple terms.',
    ],
]);

echo $response->content;
```

This keeps the API close to common chat message formats while still using Laravel's facade and manager patterns under the hood.

One of the most useful parts of the package is the `Embeddable` trait.

You can make a model searchable by implementing `EmbeddableContract` and returning the text that should be embedded:

``` php
namespace App\Models;

use Hamzi\NativeRag\Contracts\EmbeddableContract;
use Hamzi\NativeRag\Traits\Embeddable;
use Illuminate\Database\Eloquent\Model;

class Article extends Model implements EmbeddableContract
{
    use Embeddable;

    public function toEmbeddableString(): string
    {
        return "Title: {$this->title}\n\nContent: {$this->content}";
    }
}
```

When the model is saved, NativeRAG can chunk the text, generate embeddings, and store them in the embeddings table.

This is especially useful for documentation, knowledge bases, product content, internal notes, support articles, and other text-heavy records.

Once content is indexed, you can search using either a plain text query or a precomputed embedding vector:

``` php
use Hamzi\NativeRag\Facades\NativeRag;

$results = NativeRag::search(
    query: 'How do I run database migrations?',
    limit: 5,
    minScore: 0.4,
);

foreach ($results as $chunk) {
    echo $chunk->chunk_content;
}
```

For many Laravel applications, this is enough to start building contextual AI features without introducing more infrastructure.

A common flow looks like this:

Example:

``` php
use Hamzi\NativeRag\Facades\NativeRag;
use Hamzi\NativeRag\Services\PromptCompiler;

$question = 'How can users reset their password?';

$chunks = NativeRag::search($question, limit: 4);

$context = $chunks
    ->pluck('chunk_content')
    ->implode("\n\n---\n\n");

$prompt = app(PromptCompiler::class)->buildRagPrompt(
    systemInstruction: 'Answer using only the provided context.',
    context: $context,
    query: $question,
);

$response = NativeRag::chat([
    ['role' => 'user', 'content' => $prompt],
]);

echo $response->content;
```

This pattern is intentionally straightforward. You can replace the prompt, filtering, scoring threshold, or context formatting based on your application's needs.

For UI experiences, waiting for a full response can feel slow. NativeRAG supports streaming through server-sent events:

``` php
use Hamzi\NativeRag\Facades\NativeRag;
use Illuminate\Support\Facades\Route;

Route::post('/ai/stream', function () {
    return NativeRag::stream([
        [
            'role' => 'user',
            'content' => request('message'),
        ],
    ]);
});
```

That makes it easier to build chat interfaces with Livewire, Alpine.js, Vue, React, or any frontend that can consume an event stream.

NativeRAG also includes conversation models for persistent chat history:

``` php
use Hamzi\NativeRag\Models\NativeRagConversation;

$conversation = NativeRagConversation::create([
    'name' => 'Support session',
]);

$conversation->addSystemMessage('You are a helpful support assistant.');

$reply = $conversation->ask('How do I publish the config file?');

echo $reply->content;
```

The conversation stores user and assistant messages, then sends the current history to the selected driver.

History can be pruned by message count or an approximate token budget. System messages can also be preserved so the assistant keeps its instructions.

Embedding generation can take time, especially when indexing larger records. NativeRAG can dispatch embedding sync to Laravel queues:

```
NATIVE_RAG_QUEUE_INDEXING=true
NATIVE_RAG_QUEUE_CONNECTION=redis
NATIVE_RAG_QUEUE_NAME=default
```

That lets normal application requests stay responsive while workers handle chunking and embedding generation in the background.

Running models locally gives you more control over where prompts, documents, and embeddings live. That is useful for internal tools and privacy-sensitive applications.

But local RAG also has tradeoffs:

The package gives you the Laravel plumbing. You still need to choose good models, test outputs, and decide what quality bar makes sense for your product.

NativeRAG is a good fit when you want to:

It may not be the right fit if you already need distributed vector indexing, advanced ranking pipelines, or large-scale retrieval over millions of chunks.

What I like about this approach is that it keeps the first version boring in the best way.

You install a Laravel package, publish config and migrations, add a trait to a model, and start searching your own data. The architecture can evolve later, but the early developer experience stays simple.

For many teams, that is exactly the right place to start.

Repository:

```
https://github.com/hamdyelbatal122/nativerag
```

Package:

```
composer require hamzi/nativerag
```


