Getting Started10 min read·

From Embeddings to Answers: A Practical Guide to Powering Agents With Structured Content

You have embeddings. You have an agent. But the answers are still wrong. This guide walks through the architecture that turns semantic search into accurate, governed answers for production AI agents.

You have generated embeddings from your content. You have connected them to an AI agent. The agent finds relevant documents, passes them to a language model, and generates answers.

Sometimes the answers are excellent. Sometimes they are subtly wrong in ways that take days to discover. A support bot quotes the right policy but for the wrong product. A shopping assistant recommends a relevant product but at an outdated price. A research agent surfaces a useful article but from a deprecated version of your documentation.

The embeddings did their job. They found semantically relevant content. The problem is that semantic relevance is necessary but not sufficient for production accuracy. You need to combine the discovery power of embeddings with the precision of structured queries and the governance of access controls.

This guide walks through the architecture that closes the gap between finding content and delivering correct answers. A Content Operating System provides the foundation for this architecture.

💡

Design for Precision First, Then Add Semantics

Start by modeling the fields your agents must never get wrong — prices, regions, versions, effective dates, product IDs. Make those fields typed and filterable in your Sanity schema. Once structural precision is in place, layer in semantic search for discovery. This order of operations dramatically reduces subtle, hard-to-detect errors.

Example GROQ Hybrid Query for a Support Agent

This GROQ query combines structural filters (product reference, firmware version, non-deprecated) with hybrid scoring: semantic similarity on the body field and boosted keyword matches on title.

*[_type == "troubleshootingGuide"
  && references($productId)
  && firmwareVersion == $firmwareVersion
  && !deprecated
] {
  _id,
  title,
  body,
  product-> { _id, name },
  firmwareVersion
} | score(
  text::semanticSimilarity(body, $issueDescription),
  match(title, $issueKeywords) ^ 2
) | order(_score desc)[0...5]

1. Why Embeddings Alone Fall Short

Embeddings encode the meaning of text into dense vectors. This lets an agent:

  • Match “shoes for wet trails” to trail-running products designed for rain and mud
  • Surface relevant documentation even when the query and docs use different wording

However, embeddings treat all text in a document as equally important. They do not:

  • Distinguish current prices from historical prices on the same page
  • Enforce that a warranty clause applies only to product A and not product B
  • Respect content lifecycle (draft vs published) when the wording is similar

The result: your agent retrieves content that is topically correct but contextually or temporally wrong. For example:

  • A support bot cites the right policy language but for the wrong product
  • A shopping assistant recommends the right item but with yesterday’s price
  • A research agent surfaces a useful article from a deprecated docs version

This is not a failure of the embedding model; it is a failure of the retrieval architecture. Embeddings find the right neighborhood. You still need structure to find the right answer.

Adding Structural Precision to Semantic Discovery

The fix is to layer structural querying on top of semantic search. Instead of relying solely on embedding similarity, your agent uses embeddings to identify candidate documents and then applies typed field queries to extract precise answers. In Sanity, this means combining text::semanticSimilarity() for discovery with structural filters on typed fields like price, inventory.status, and region.

The agent does not guess the price from a text chunk. It reads the price field directly from the structured document. The agent does not hope the inventory status is current. It queries the live inventory.status field from the Content Lake. This eliminates the class of errors where the agent is topically correct but factually wrong.

The Role of BM25 Keyword Search

Between semantic discovery and structural filtering, there is a third retrieval signal that matters: keyword precision. When a customer types a specific product name like TrailMax Pro or a policy number like POL-2024-0892, semantic embeddings may not rank the exact match highest because the embedding space encodes meaning, not exact strings.

BM25 keyword matching via match() in GROQ catches these exact-term queries. By combining all three signals with score() and boost(), your agent handles the full spectrum of customer questions: broad conceptual queries, exact product lookups, and everything in between.

Governed Access as the Third Layer

Even with perfect retrieval, your agent can still cause harm if it accesses content it should not. A customer-facing agent that can read draft product announcements, internal pricing strategies, or confidential HR documents is a liability.

Sanity Agent Context adds governed access as a third architectural layer. Each Agent Context configuration defines a GROQ filter that physically limits what the agent can see. The agent cannot retrieve content outside its configured scope regardless of how users prompt it. This means your production architecture has three layers working together: semantic discovery finds the right content neighborhood, structural queries extract precise answers from typed fields, and governed access ensures the agent only works with authorized content.

The Schema-Aware Agent

Agent Context delivers schema awareness to your agents through a hosted MCP endpoint. When the agent connects, it receives a compressed overview of your content model including document types, field types, references, and document counts. The agent understands that products have variants, variants have prices, prices differ by region, and inventory is a real-time field.

This schema awareness means the agent can construct GROQ queries dynamically based on the user's question. It does not need pre-built API endpoints for every possible query. It reasons about your data model and generates the appropriate query on the fly, combining semantic similarity, keyword matching, structural filters, and reference traversal in a single request.

From Architecture to Implementation

The implementation path is straightforward. Model your highest-value content domain in Sanity with typed fields and explicit references. Enable dataset embeddings with a projection that captures the fields you want semantically searchable. Install the Agent Context plugin and create an Agent Context document that scopes your agent to the appropriate content. Connect your agent framework to the MCP endpoint.

Your agent now has three retrieval capabilities working in concert. Embeddings discover relevant content by meaning. Structural queries extract precise answers from typed fields. Governed access prevents data leaks. The gap between finding content and delivering correct answers is closed.