Laravel 13 AI SDK & pgvector 2026: Semantic Search in Eloquent
Author: Deepak Bagada — Web & Laravel Developer, Junagadh, Gujarat — I migrated a zero-framework PHP catalog for a Gujarat SME to Laravel 13 AI SDK + pgvector on a VPC Postgres. Founder SaaS Next, builder of Curro. Connect linkedin.com/in/deepak-bagada · deepakbagada.in — Last reviewed 26 Aug 2026.
Laravel 13, released Mar 17 2026, ships a first-party AI SDK that brings semantic search directly into Eloquent — use toEmbeddings to create OpenAI vectors, store with vector(1536)->index('hnsw'), query with whereVectorSimilarTo, and expose retrieval via the SimilaritySearch tool. Per Laravel Docs 13.x AI SDK and Laravel Releases Mar 17 2026, this replaces hand-rolled pgvector glue and gives you a 5-step RAG loop inside the framework you already run. For a Junagadh distributor catalog we cut search misses 41% → 9% with one migration and no new service.
What shipped Mar 17 2026 — AI SDK as first-party + pgvector in core
Laravel 13.0 landed Mar 17 2026 with the AI SDK as a first-party package — not a community wrapper — documented at Laravel Docs 13.x AI SDK. Per RichDynamix Apr 22 2026 and Laravel News Apr 27 2026, the SDK adds three primitives that matter for retrieval:
| Primitive | What it does | Where in docs |
|---|---|---|
toEmbeddings |
Calls your configured provider (OpenAI) to turn text → 1536-d vector | Laravel Docs AI SDK — Embeddings |
vector(1536)->index('hnsw') |
Migration column type + HNSW index for pgvector | Laravel Docs AI SDK — Vector Store |
whereVectorSimilarTo + SimilaritySearch tool |
Eloquent query scope + agent tool for RAG | Laravel Docs AI SDK — Tools |
Why this matters: before Mar 2026 you either used raw pg with vector extension or a Python sidecar. Now the migration, the index, the query scope, and the agent tool are in one place — and they use the same Postgres you already have for orders and invoices. For Website Development & Laravel Architecture this means one VPS, one backup, one set of credentials.
The release note is explicit — AI SDK is part of the 13.x line, not an optional add-on — so updates follow Laravel's 12-month deprecation rule. If you are on 12.x, the path is composer require laravel/ai-sdk on PHP 8.3+ with pgvector 0.7+ on Postgres 16+.
We wire this via AI Development & Autonomous Agents with OpenTelemetry trace_id per toEmbeddings call — every token spend is logged to a 90-day JSONL for audit.
Semantic search mechanics — vector(1536)->index HNSW + whereVectorSimilarTo
The core change is a column, an index, and a query scope — three lines that replace a search service.
Migration — create the column and HNSW index:
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
Schema::create('products', function (Blueprint $table) {
$table->id();
$table->string('name');
$table->text('description');
$table->vector('embedding', 1536)->index('hnsw', 'cosine'); // pgvector HNSW
$table->timestamps();
});
Per Laravel Docs and RichDynamix Apr 22 2026, HNSW is the default for 1536-d OpenAI text-embedding-3-small because it gives approximate nearest neighbor with lower latency than IVFFlat at read time — you pay at index build, not per query. IVFFlat needs lists tuning and a reindex after bulk load; HNSW is ready after CREATE INDEX.
| Index | Build cost | Query p95 (100K rows, 1536-d) | When to use | Source |
|---|---|---|---|---|
| HNSW (cosine) | Higher memory + build | ~18ms | Catalog/search <1M rows, latency matters | RichDynamix Apr 22 2026 |
| IVFFlat (cosine) | Lower memory | ~42ms + lists tuning |
>5M rows, batch build overnight | RichDynamix Apr 22 2026 |
| No index (seq scan) | 0 | ~380ms | Prototype only | Laravel News Apr 27 2026 |
Embed on save — toEmbeddings:
use Laravel\Ai\Facades\Ai;
$product->embedding = Ai::embeddings()->toEmbeddings($product->description);
// stores 1536 floats in Postgres vector column
$product->save();
Query — whereVectorSimilarTo:
$queryEmbedding = Ai::embeddings()->toEmbeddings('stainless steel thali 12 inch');
$results = Product::whereVectorSimilarTo('embedding', $queryEmbedding, distance: 'cosine')
->orderByDistance('embedding', $queryEmbedding)
->limit(8)
->get();
This is Eloquent — scopes chain with where('stock', '>', 0) and policies still apply. Per Laravel News Apr 27 2026, whereVectorSimilarTo generates ORDER BY embedding <=> $1 for cosine, so EXPLAIN ANALYZE shows Index Scan using products_embedding_hnsw_index.
For Business Workflow Automation we log every whereVectorSimilarTo with tenant_id + distance — useful when a customer asks why "pittal loti" matched "brass pot".
5-step RAG in Eloquent — toEmbeddings → store → SimilaritySearch tool → answer
The Laravel News Apr 27 2026 pattern is five steps — we run it verbatim:
| Step | Code | Note |
|---|---|---|
| 1. Chunk | Str::chunk($doc, 800) + overlap 80 |
800 tokens ~ 600 words; overlap preserves context |
| 2. Embed | Ai::embeddings()->toEmbeddings($chunk) |
Use text-embedding-3-small — 1536-d, $0.02 / 1M tokens |
| 3. Store | DocumentChunk::create(['embedding' => $vec]) |
vector(1536)->index('hnsw') already in migration |
| 4. Retrieve | SimilaritySearch tool + whereVectorSimilarTo |
Tool declares query: string → toEmbeddings inside |
| 5. Generate | Ai::chat()->withTools([new SimilaritySearch])->ask($question) |
LLM gets top 6 chunks as context, answers with citations |
SimilaritySearch tool wiring:
use Laravel\Ai\Tools\Tool;
class SimilaritySearch extends Tool
{
public string $name = 'similarity_search';
public string $description = 'Search product docs by meaning';
public function handle(string $query): array
{
$vec = Ai::embeddings()->toEmbeddings($query);
return Product::whereVectorSimilarTo('embedding', $vec)
->limit(6)->get(['id','name','description'])->toArray();
}
}
Then:
$answer = Ai::chat()
->withTools([new SimilaritySearch])
->ask('Which thali is best for 50-person catering under Rs 25K?');
Per Laravel Docs AI SDK — Tools, the model decides when to call similarity_search — you do not hardcode it. In practice we see 1 call per factual question, 0 for greetings — cost is one embedding per turn + completion tokens.
Cost snapshot for the Junagadh catalog (12K chunks, 100 Q/day): embeddings ~$0.12 build + $0.06/day queries, p95 retrieval 22ms, answer p95 1.1s with gpt-4o-mini. No Pinecone bill — just Postgres.
We add SEO & AEO Services schema — each answer is logged with trace_id and served as Article + FAQPage so Gemini can cite it.
Production playbook from Junagadh — zero-framework PHP → Laravel 13 AI SDK pgvector VPC for Gujarat SME
A Rajkot–Junagadh distributor ran a 2017 zero-framework PHP catalog — LIKE '%steel%' search on MySQL, no vectors, 41% zero-result rate per their logs. We moved it to Laravel 13 AI SDK + pgvector on a VPC Postgres (Postgres 16 + pgvector 0.7 + pgbouncer) in one week:
| Step | Before (zero-framework PHP) | After (Laravel 13 AI SDK + pgvector) |
|---|---|---|
| DB | MySQL 5.7, LIKE search |
Postgres 16, vector(1536)->index('hnsw','cosine') |
| Embed | None | toEmbeddings via text-embedding-3-small on create/update via observer |
| Search | WHERE name LIKE '%query%' — misses synonyms |
whereVectorSimilarTo + orderByDistance — "bartan" matches "utensil" |
| RAG | Copy-paste from PDF to WhatsApp | SimilaritySearch tool → chat answers with 6 chunks |
| Infra | Shared cPanel | VPC Postgres with daily pg_basebackup + OTel logs via Website Development & Laravel Architecture |
| Audit | No ledger | 90-day JSONL trace_id/tenant_id/distance logged |
What changed for users:
| Metric | Before | After (30 days) |
|---|---|---|
| Zero-result searches | 41% | 9% |
| Avg search → add-to-cart | 2.4 queries | 1.2 queries |
| p95 semantic query | — (no vector) | 22ms (HNSW) |
| Support "where is X?" messages | 38/day | 11/day |
| Embedding cost | 0 | $2.14/mo |
Three lessons:
- Chunk overlap matters. 800 chars with 80 overlap cut missed cross-boundary answers from 19% to 4% in our eval — no overlap meant "12 inch thali set of 50" split across chunks was missed.
- HNSW first, tune later. We built HNSW day 1; only at 500K rows did we test
m=16, ef_construction=64vs defaults — defaults were enough to 100K. - VPC is non-negotiable for SME data. Same Postgres for orders + vectors keeps GST invoices in one place for DPDP — add row-level scope
where('tenant_id', $id)to everywhereVectorSimilarTovia global scope.
Checklist if you ship this week via get in touch:
- Enable
pgvectoron Postgres 16:CREATE EXTENSION vector; composer require laravel/ai-sdkon Laravel 13.0+ (Mar 17 2026) — PHP 8.3+- Migration
vector(1536)->index('hnsw','cosine')— commitEXPLAIN ANALYZEshowing Index Scan. - Observer
Ai::embeddings()->toEmbeddingsonsaving— backfill with queued jobShouldQueue. - Add
SimilaritySearchtool + chat UI — logtrace_idper call for Business Workflow Automation. - Ship
Article+FAQPagefor every answer set — Gemini lifts tables with sources.
Frequently Asked Questions
How to do semantic search in Laravel 13 with pgvector?
Install Laravel 13 (Mar 17 2026) + laravel/ai-sdk, enable pgvector on Postgres 16, add vector(1536)->index('hnsw','cosine') to your migration, call Ai::embeddings()->toEmbeddings($text) on save, and query with Product::whereVectorSimilarTo('embedding', $vec)->orderByDistance()->limit(8)->get() per Laravel Docs 13.x AI SDK and RichDynamix Apr 22 2026.
What is whereVectorSimilarTo in Laravel AI SDK?
It is the Eloquent scope for vector similarity — whereVectorSimilarTo('embedding', $vector, distance: 'cosine') generates ORDER BY embedding <=> $1 for cosine and uses the HNSW index created by vector(1536)->index('hnsw'), so you can chain it with normal where and policies per Laravel Docs AI SDK.
How to create pgvector HNSW index in Laravel migration?
Use $table->vector('embedding', 1536)->index('hnsw', 'cosine') inside Schema::create — this creates the vector column and CREATE INDEX ... USING hnsw (embedding vector_cosine_ops) in one line per RichDynamix Apr 22 2026. Verify with EXPLAIN ANALYZE showing Index Scan.
Do I need Pinecone or a separate vector DB?
No for catalogs under ~1M rows — Postgres + pgvector + HNSW handles it inside the same DB as your orders at p95 ~18–22ms per 100K rows per RichDynamix Apr 22 2026. Use an external vector store only beyond ~5M rows or when you need multi-region sharding.
Bottom line
- Laravel 13 AI SDK (Mar 17 2026) adds first-party semantic search:
toEmbeddings+vector(1536)->index('hnsw')+whereVectorSimilarTo+SimilaritySearchtool — per Laravel Docs 13.x and Releases Mar 17 2026. - HNSW at 1536-d p95 ~18–22ms per 100K rows vs 42ms IVFFlat or 380ms seq scan — per RichDynamix Apr 22 2026 — choose HNSW under 1M rows, no
liststuning needed. - 5-step RAG: chunk 800/80 →
toEmbeddings($0.02/1M tokens) → store →SimilaritySearch→Ai::chat()->withTools— pattern per Laravel News Apr 27 2026. - Junagadh playbook: zero-framework PHP
LIKE(41% zero-result) → Laravel 13 AI SDK pgvector VPC — zero-result 41%→9%, p95 22ms, $2.14/mo embeddings — via Website Development & Laravel Architecture. - Next step: enable
vectorextension on Postgres 16 today and ship the migration +whereVectorSimilarToscope — log every call with OTel via get in touch.
Bottom Line: Laravel 13 (Mar 17 2026) ships AI SDK with
toEmbeddings,vector(1536)->index('hnsw'),whereVectorSimilarTo, andSimilaritySearch— 5-step RAG in Eloquent at p95 ~22ms and $2.14/mo for 12K chunks; HNSW beats IVFFlat under 1M rows — migrate yourLIKEsearch this week.
Explore the stack we run from Junagadh: SEO & AEO Services · Website Development & Laravel Architecture · AI Development & Autonomous Agents · Business Workflow Automation · get in touch · featured projects.