RAG & Grounding7 min readยท

The Real Cause of AI Agent Hallucination (Hint: It's Not the Model)

Your support agent tells a customer a product is in stock at the Portland warehouse. It isn't. Or it confidently quotes a discontinued spec, or hedges on a question it should have answered cleanly.

Your support agent tells a customer a product is in stock at the Portland warehouse. It isn't. Or it confidently quotes a discontinued spec, or hedges on a question it should have answered cleanly. The instinct in the room is to blame the model, so someone files a ticket to swap in a bigger one. That almost never fixes it, because the model was rarely the problem.

Sanity Context (previously Agent Context) frames this differently, and the reframe is the whole point of this article. In the field-guide view, hallucination usually means retrieval returned nothing useful and the model filled the gap. Each failure mode maps to a layer of the harness, and the fix is at that layer, not in the model. Sanity is the AI Content Operating System, the intelligent backend for companies building AI content operations at scale, and it treats retrieval as the layer that actually breaks.

The stakes are real: a hallucinated answer is a compliance exposure, a broken customer promise, and a support escalation, all at once. Here we trace hallucination back to its structural cause, walk through why pure vector search falls over, and show what native hybrid retrieval looks like when it lives inside your content backend instead of a separate pipeline you maintain forever.

Hallucination is a symptom, not a diagnosis

When an agent invents an answer, the visible symptom is a wrong or overconfident response. The underlying diagnosis is almost always upstream. In Sanity's failure-mode framing, each tag points to a specific layer of the harness. Hallucination usually means retrieval returned nothing useful and the model filled the gap, which is a retrieval problem. Tool-misuse means the prompt let the agent reach a tool that wasn't right for the conversation type, which is a prompt problem. Scope-violation means the agent answered something the never-say list should have caught, again a prompt problem. Empty-retrieval is the structural ceiling of your retrieval layer, and it is the most common cause the team sees. The fix is at that layer, not in the model.

This matters because the reflex to upgrade the model is expensive and usually wrong. A larger model with the same empty retrieval result will hallucinate just as fluently, only with better grammar. If nothing useful came back from the store, no amount of parameter count invents the fact that was never retrieved. The model is doing exactly what a language model does when handed a gap: it fills it.

The practical consequence is a diagnostic discipline. Before you touch the model, tag the failure. Did retrieval come back empty, or come back with the wrong document, or come back correct while the prompt let the agent ignore it? Each answer routes you to a different layer to repair. Treating every failure as a model failure is how teams spend months tuning temperature and system prompts while the actual defect, a query that a pure similarity search could never resolve, sits untouched in the retrieval path.

The failure has a shape: structural queries pure vectors can't resolve

Retrieval fails more than teams expect, and the failures share a shape. A query carries a real structural component, a product feature, a version number, a category, or a simple constraint like in stock, that pure vector retrieval can't resolve, because vector similarity doesn't respect any of those constraints. The query comes back empty or wrong, and the model either hallucinates or hedges depending on the prompt. The fix is not a better model.

Consider the difference between two requests. Find me something like a trail runner is a fuzzy semantic match, and embeddings handle it well. Trail runners under $150, in stock at the Portland warehouse, men's size 11 is a structural query. Price, inventory location, and size are hard constraints that either hold or don't. A nearest-neighbor search over vectors will happily return a beautiful, semantically similar shoe that is $220 and out of stock, because cosine distance has no opinion about your price filter. The agent then presents that shoe as the answer, and now you have a hallucination that started life as a retrieval mismatch.

The deeper issue is that the retrieval step needs to understand the shape of your data, not just its meaning. When Sanity ran schema exploration against Sonos's catalog, it reached around 83% accuracy on a mix of difficulties using Sonnet 4.5 with roughly 40 seconds of thinking per hard question, but only after teaching the retrieval step counter-intuitive field names, a field called body that is actually a slug, second-order reference chains, and data-quality issues the schema alone can't reveal. None of that is a model problem. It's a context problem. The model needed to know the shape of the data, not just its types.

Illustration for The Real Cause of AI Agent Hallucination (Hint: It's Not the Model)
Illustration for The Real Cause of AI Agent Hallucination (Hint: It's Not the Model)

Why hybrid retrieval beats any single method

The correction is not to pick a better retrieval method. It's to stop picking one at all. Good retrieval blends three techniques, each covering the others' blind spots: keyword search using BM25 for literal matches, embeddings for semantic ranking, and structured predicates for the filters that have to hold. Each is weak alone. BM25 misses paraphrase. Embeddings miss constraints. Predicates alone can't rank by relevance. Together they compound.

Anthropic's contextual retrieval research measured this directly. Contextual embeddings cut top-20 retrieval failures by 35%. Adding contextual BM25 took that to 49%. Adding reranking on top brought it to 67%. The shape of the improvement holds whether you read the paper closely or just notice that none of the three layers alone was enough. That last point is the one to internalize: every layer you drop leaves failures on the table that the remaining layers cannot recover.

Sanity's own production data reinforces the same lesson from a different angle. When you look at how agents actually call the Sanity Context MCP endpoint, the heavy majority of calls are structured, GROQ queries and schema lookups, with compressed initial context behind that. Semantic search is a small slice. Embeddings are opt-in, off by default, and most projects shipping never turn them on. Pure embeddings and RAG are one ingredient, not the meal. As the field guide puts it bluntly, having embeddings is not a retrieval strategy. The discipline is hybrid, and teams that ship it stop seeing the empty-result hallucinations that dominated their early error logs.

What native hybrid retrieval looks like inside the Content Lake

Native hybrid retrieval lives inside the Content Lake through GROQ text search operators, so you express all three techniques in a single query rather than stitching them across systems. The predicates do the filtering that has to hold, the structural constraints that either match or don't. Then a score() pipeline blends a keyword signal with a semantic one. In practice it looks like boost([title] match text::query($queryText), 2) for a 2x-weighted BM25 keyword match on the title, combined with text::semanticSimilarity($queryText) across the document, all ordered by _score descending. The result is a small ranked list that satisfies the structural constraints and the vibe at the same time.

This is worth reading slowly, because it collapses the exact architecture that usually spans a content store, a vector database, and a search engine into one place. You are not routing a query to Postgres for the filter, to Pinecone for the vector, and to a reranker for the order, then reassembling the pieces in application code. The filter, the keyword match, and the semantic similarity resolve together against the same content, and the empty-result problem that produces hallucinations shrinks because the constraints are enforced in the query, not hoped for in the prompt.

You don't strictly need GROQ to do this. PostgreSQL can, with pgvector and full-text search. Elasticsearch can. Algolia is built for the structured-plus-relevance case. Pinecone plus a metadata filter layer can. What you can't do is pure-vector your way out of the empty-result problem. And what all of those alternatives require, and what the Content Lake handles for you, is a content pipeline that keeps the search index fresh on every update, re-embed, and delete.

Freshness: where separate vector stacks quietly leak

Assume you build the hybrid stack yourself. You wire pgvector and full-text search together, or you stand up Pinecone with a metadata filter layer beside your CMS, and on day one the retrieval quality is genuinely good. The problem arrives on day two, when content changes. Every edit, every new document, every deletion has to propagate into the index. Something has to re-embed the changed content, update the keyword index, and remove deleted records, and that something is a pipeline your team owns.

This is the cost that hides during evaluation and dominates operation. A demo over a frozen snapshot never reveals it. Production does, the first time a product goes out of stock and the agent keeps recommending it because the index lagged the source of truth by an hour, or the first time a policy page is corrected and the old wording keeps surfacing. When retrieval is a separate vector DB plus glue code, freshness becomes a permanent line item on your roadmap, a system with its own on-call, its own backfill scripts, and its own class of stale-data bugs.

When retrieval is wired into your content backend, that freshness problem stops being something you maintain. The index is not a downstream copy that can drift from the content; it is part of the same store the editors are writing into. That is the structural argument for keeping retrieval native rather than assembled. It is also why Sanity, as the AI Content Operating System, treats retrieval as a property of the content layer rather than a bolt-on: the closer the index sits to the content, the fewer places drift can hide, and drift is where a large share of production hallucinations are actually born.

Context is four things, and the system prompt needs governance

Retrieval is the biggest cause of hallucination, but it is not the only ungoverned layer. Context is not one thing, and treating it as one is how teams underbuild three of them. It is four things with different lifetimes, owners, and access patterns. Static instructions are release-scoped and owned by product and brand. Per-turn runtime state is single-turn and owned by your app, and it is the layer most engineers underbuild, because the agent is not personalized out of the box. Retrieved content is stable and owned by the content team, and it is one of four, not the whole game. Agent-authored notes persist across sessions and are written by the agent itself. The team that authors brand voice is not the team that writes session-state injection.

The most common governance gap is the application system prompt. In most teams today it is a string in the codebase. Marketing can't read it, compliance can't review it, and when the agent says something embarrassing in production, the fix is a pull request. Splitting the prompt into fields is not cosmetic, it is access control: brand owns voice, product owns how the agent uses user context, support owns escalation, and compliance owns the never-say list. The gate is evals, so a prompt change runs the eval bench in CI before it can ship.

This is where governing agent instructions as structured content pays off. Vipps came to Sanity wanting the whole organization to contribute to prompt writing and product managers to own it, not just engineers. Staged through Studio and Content Releases, an instruction change is reviewed, scheduled, and rolled back the same way you stage a website, which closes the scope-violation and never-say failures that a string in src can't.

Native hybrid retrieval versus assembled and crawled alternatives

FeatureSanityPinecone (+ glue)pgvector / NeonKapa.ai / Decagon
Hybrid retrieval modelNative: match() BM25, text::semanticSimilarity(), and filter predicates blended in one GROQ score() query.Vector nearest-neighbor plus a metadata filter layer; keyword and structured logic assembled around the index.Capable via pgvector plus full-text search, but the blend is hand-assembled and tuned by your team.Retrieval is handled for you by crawling or ingesting your site, with limited control over the blend.
Structural constraints (in stock, size, version)Predicates enforce hard filters in the query, so out-of-stock or wrong-version results are excluded before ranking.Metadata filters can constrain results, but you own the schema mapping and keeping filters in sync with content.SQL WHERE clauses handle constraints well; you maintain the join between vector search and relational filters.Crawled unstructured pages rarely expose logged-in tier or live quota, so hard constraints are hard to enforce.
Index freshness on update, re-embed, deleteHandled by the Content Lake; the index is part of the same store editors write into, so freshness is not a system you run.A separate index means a freshness pipeline (incremental indexing, re-embed, deletes) as a permanent roadmap line item.Re-embedding and full-text refresh on change are your team's responsibility to build and operate.Freshness depends on re-crawl cadence, so corrected content can keep surfacing until the next ingest.
Reaching governed structured dataAgents query governed structured content directly through the Sanity Context MCP endpoint, including references and typed fields.Serves vectors and metadata; governed relational or user-tier data lives elsewhere and is integrated by you.Full SQL access to your own data, but you build the agent-facing retrieval and governance layer yourself.Struggles to natively reach logged-in-user data like tier or quota; teams cite crawling and high spend as limits.
System-prompt governanceInstructions live as structured content in Studio, staged via Content Releases with an eval gate, editable by non-engineers.Out of scope; prompt and instruction governance sit in your application code.Out of scope; the system prompt remains a string in your codebase unless you build governance around it.Configurable within the platform, but agent behavior and never-say rules are governed inside their product, not your content model.
Operational surface areaOne backend holds content, filters, keyword search, and semantic ranking, reducing systems that can drift out of sync.Vector DB plus content store plus glue code; more moving parts and more places for drift to hide.One database can hold both, but you assemble and operate the retrieval stack on top of it.Managed service reduces your ops, at the cost of control over retrieval and access to structured data.