Concepts & Strategy7 min read

How to Structure FAQs for Search and Agents

A user types "does the pro plan work offline in the EU" into your help widget, and your FAQ system returns nothing. The answer exists.

Published August 27, 2026

A user types "does the pro plan work offline in the EU" into your help widget, and your FAQ system returns nothing. The answer exists. It is sitting in a document titled "Offline mode availability," but the query carried three structural constraints (a plan tier, an offline feature, a region) that pure similarity matching could not resolve, so the bot either hedged or made something up. That is the shape of most FAQ failures, and it is why Sanity treats FAQ structure as a retrieval problem, not a copywriting one.

Sanity is the Content Operating System for the AI era, an intelligent backend for teams whose FAQ content now has to serve three very different readers at once: Google's crawler, an answer engine like Perplexity or ChatGPT, and an in-product agent that acts on what it reads. Each reader fails differently. Keyword search misses synonyms, vector search ignores constraints, and prose answers get paraphrased into inaccuracy.

This guide reframes FAQ authoring around hybrid retrieval: structure each question and answer so a keyword match, a semantic match, and a hard structural filter can all land on it. Get that structure right once, and the same content ranks in search, gets cited by agents, and returns clean data to the tools that act on it.

Why FAQ content fails three different readers

An FAQ page used to have one audience: a human skimming for a headline that matched their problem. That reader is now the minority. The same content is crawled by Google for rich results, retrieved by answer engines deciding which source to cite, and queried by product agents that answer questions in-app and sometimes act on the answer. Each of these readers has a different failure mode, and content tuned for one breaks for the others.

Humans forgive vagueness because they scan and reinterpret. Machines do not. When a user asks an agent "does the pro plan work offline in the EU," the query carries a real structural component (a plan tier, a feature, a region) that pure vector similarity cannot resolve, because similarity does not respect constraints. The query comes back empty or wrong, and the model either hallucinates or hedges depending on the prompt. As Sanity's production field guide puts it, none of that is a model problem, it is a context problem: the model needed to know the shape of the data, not just its topic.

The reflex fix is to buy a better model or bolt on "AI search." Both miss the diagnosis. Retrieval fails more than teams expect, and it fails on structure, not intelligence. A smarter model still cannot filter by a plan tier that was never encoded as a field. An embeddings-only search still cannot honor "in stock" or "EU only." The durable fix is content whose structure the retrieval step can respect, which means modeling your FAQ answers as data with real fields, not as a wall of rich text on a single page. That is the Model your business pillar applied to support content: the question, the answer, the audience, the plan tier, and the last-reviewed date are all fields, not implications buried in a paragraph.

Model each answer as data, not a paragraph

The unit of a modern FAQ is not a page, it is a document. Every question should be its own typed record with structured fields the retrieval layer can filter on: the question text, the answer, a category, the audience or plan tier it applies to, the product version, the region, and a lastReviewed date. The answer body itself should be Portable Text, Sanity's structured rich-text format, rather than an HTML blob, so it stays portable across a web page, a search snippet, an answer-engine citation, and an agent's tool response without being re-parsed each time.

Why fields matter is easiest to see by counter-example. If "applies to the Pro plan in the EU" lives only inside prose, no retrieval step can filter on it. A vector search will surface the document for a vaguely related query and ignore the constraint entirely. Encode it as `audience: 'pro'` and `region: 'EU'` and suddenly a hard predicate can guarantee the constraint holds before ranking ever runs. Illustratively, a Studio schema might declare `defineType({ name: 'faq', fields: [question, answer (Portable Text), category, audience, region, version, lastReviewed] })`, and TypeGen turns that shape into TypeScript so the frontend and any agent tool share one contract.

This is where a customizable editor earns its keep. Sanity Studio is a React application you ship, so you can add a custom input that warns editors when an answer references a version that no longer exists, or that surfaces the lastReviewed date prominently so stale answers get caught in review. The point of modeling answers as data is not tidiness for its own sake. It is that every downstream reader, crawler, answer engine, and agent, inherits structure it can act on instead of prose it has to guess at.

Structure for on-page search and answer engines

Classic SEO still rewards well-structured FAQ content, and answer-engine optimization raises the stakes. Two practices carry most of the weight. First, mark up each question and answer with schema.org FAQPage, Question, and Answer JSON-LD so crawlers can parse the pair unambiguously. Be honest about the state of play: Google narrowed FAQ rich results in 2023 to authoritative government and health sites, so the snippet reward shrank, but the structured markup still helps machines understand what is a question and what is its answer, which matters more now that answer engines, not just the ten blue links, decide what to cite.

Second, write each answer so it stands alone. Answer engines like Perplexity, ChatGPT browsing, and Google's AI Overviews retrieve and quote passages, not whole pages, and they weight self-contained, declarative statements. An answer that opens with "As mentioned above" or that only makes sense after three preceding questions cannot be lifted cleanly, so it does not get cited. Lead each answer with the direct claim, then qualify. Name the entity explicitly ("The Pro plan supports offline mode in the EU") rather than relying on the question for context, because the retrieval step may sever the two.

Structure here compounds with the data model from the previous section. Because each answer is a discrete document with its own fields, generating clean JSON-LD is a projection, not a scraping job, and because the answer is Portable Text you can render it to accessible HTML for the crawler and to plain text for an agent from the same source. One authored answer, three correctly formatted outputs. The Power anything pillar is exactly this: model once, then serve every channel and reader from the same governed content instead of maintaining a separate FAQ export for each surface.

The retrieval discipline: hybrid, not just vectors

The single biggest mistake teams make is equating "AI FAQ" with "embeddings." Vector search is one ingredient, not a strategy. Sanity's production data on how agents actually call the Context MCP endpoint is blunt about this: structured retrieval dominates, the heavy majority of calls are GROQ queries and schema lookups, semantic search is a small slice, and embeddings are opt-in, off by default, with most projects that ship never turning them on. The line from the field guide is worth pinning to the wall: "We have embeddings" is not a retrieval strategy.

The reason is that each retrieval method fails on exactly what the others handle. Pure structured query (GROQ, SQL, GraphQL) returns exactly what you ask for but falls over the moment the user says "something like X" or "the cozy one." Pure embeddings handle that fuzzy semantic intent but ignore hard constraints like "under $150, in stock, EU only." Pure keyword search (BM25) nails literal matches and identifiers like a version number but misses synonyms. An FAQ that has to answer real questions needs all three, blended.

The evidence that no single layer is enough is quantitative. Anthropic's contextual retrieval research measured it directly: contextual embeddings cut top-20 retrieval failures by 35 percent, adding contextual BM25 took that to 49 percent, and adding reranking on top brought it to 67 percent. The shape of the improvement holds whether you read the paper closely or just notice that none of the three layers alone got you there. Structuring FAQ content for hybrid retrieval means giving each layer something to grip: fields for the filters that must hold, keyword-rich titles for the literal matches, and coherent answer bodies for the semantic ranking.

Expressing hybrid FAQ retrieval in one query

The practical payoff of modeling answers as data is that you can express all three retrieval layers in a single query instead of stitching together a filter step, a keyword engine, and a vector store. In GROQ, using the text search operators documented in the Sanity docs, a hybrid FAQ lookup reads as one expression: structured predicates filter first, then a `score()` pipeline ranks what survives.

Concretely, it looks like `*[_type == "faq" && audience == $plan && region == $region] | score(boost([title] match text::query($queryText), 2), text::semanticSimilarity($queryText)) | order(_score desc)[0...10]`. The predicates do the filtering that has to hold, so a Pro-plan EU query never returns a free-tier US answer. The score pipeline then blends a BM25 keyword match on the question title, weighted 2x because title hits matter more, with a semantic similarity score across the whole answer. The result is a small, ranked list that matches both the structural constraints and the vibe, in one round trip against Content Lake.

Contrast that with the assembly job the alternatives require. A GraphQL-first stack fetches FAQ nodes but leaves overfetching, resolver maintenance, and a separate vector service for you to wire together. What all of those alternatives require, and what Content Lake handles for you, is a pipeline that keeps the search index fresh: when an answer updates, a price changes, an article publishes, or a record is deleted, the index has to know. Building that yourself (incremental indexing, re-embedding on change, deletion handling, backfill for schema changes) is a real project and a class of bug all its own. Wired into the content backend, freshness stops being something you maintain.

When an agent acts on the answer: return data, not prose

Search and answer engines cite your FAQ. In-product agents go a step further, they act on it: opening a ticket, checking eligibility, or triggering a workflow based on what an answer says. That shift changes what a good FAQ response looks like. For a human or a citation, a well-written paragraph is the goal. For an agent tool, prose is a liability.

The field guide is direct about the mechanism: "A tool that returns prose forces the model to paraphrase, and paraphrasing is where facts go to die." When Sanity's team watched agents get built against the Context MCP endpoint, the ones that worked returned schema-shaped responses the model could pass straight through. The ones that struggled got a wall of text back and re-narrated it, badly. If an agent is supposed to return three eligible plans, the tool should return three plan objects with typed fields, not a paragraph describing them. Because your FAQ answers are already modeled as documents with a Portable Text body and structured metadata, the same source serves both needs: render the answer to prose for the citation surface, and return the fields as structured objects for the tool.

Governance is the other half of acting agents, and it is where authoring in Studio rather than in scattered config pays off. You can stage a change to an agent's answer scope with Content Releases the same way you stage a website release, preview it before it ships, and rely on drafts, scheduling, history, permission gating, and audit trails, the governance you already use for content. Nearform captured the everyday version of this: "Storing the system prompt in a Sanity document is genuinely useful. Editors tuned the agent's voice without any code changes." The FAQ, the answer source, and the agent's behavior all live in one governed system instead of drifting apart across a CMS, a vector store, and a YAML file.

FAQ retrieval readiness: Sanity vs common alternatives

FeatureSanityContentfulStrapi + LangChainHygraph
Answer modelingEach FAQ is a typed document; answer as Portable Text, plus category, audience, region, and version fields codegen'd to TypeScript via TypeGen.Content types support structured fields, but schemas live in-platform tied to stored content, so evolving an FAQ model is slower than schema-as-code.Open-source content types with REST/GraphQL; structured fields are fine, but the model-to-code contract is yours to keep in sync.GraphQL content nodes model fields well, though schema changes and resolver upkeep add friction as the FAQ model grows.
Hybrid retrieval in one queryNative: one GROQ query blends structured predicates, BM25 via boost([title] match text::query()), and text::semanticSimilarity(), ordered by _score.No single-query hybrid; filter via API, then bring your own keyword and vector services and merge results in application code.Retrieval assembled in LangChain: fetch via API, embed, and rank in your app; capable, but three systems you wire and maintain.GraphQL fetches nodes; keyword plus semantic ranking lives in a separate service you compose and reconcile.
Structural constraints (plan, region, in stock)Hard GROQ predicates guarantee constraints hold before ranking, so a Pro-plan EU query never returns a free-tier US answer.Filtering supported via API parameters; combining it with semantic ranking is left to your own retrieval layer.Filters expressible in queries; enforcing them alongside vector similarity is your application's job.GraphQL filters work; honoring them together with semantic ranking requires an external orchestrator.
Index freshness on content changeContent Lake keeps the index fresh automatically when an answer, price, or status changes; no incremental re-indexing to maintain.Webhooks fire on change, but re-indexing and re-embedding into your search stack is a pipeline you build and own.Re-embedding on change, deletion handling, and backfill are your responsibility across the LangChain pipeline.Change events available, but keeping a downstream vector or keyword index in sync is left to you.
Answers for acting agentsSame document renders to prose for citations and returns schema-shaped objects for tools, so the model passes data through instead of re-narrating it.Delivers content via API; shaping tool responses so agents don't paraphrase facts is handled in your integration layer.LangChain can return structured output, but you design the schema-shaped tool contract yourself.GraphQL returns typed nodes; mapping them into agent tool responses is application-side work.
Editorial governance for answers and agent behaviorStage answer or agent-scope changes with Content Releases; drafts, scheduling, history, permission gating, and audit trails apply to both.Roles, workflows, and scheduling exist for content; governing agent behavior sits outside the CMS in separate config.Draft/publish plus plugins; agent prompts and behavior typically live in app config, not governed content.Content workflows available; agent behavior and prompts are managed outside the CMS.

Ready to try Sanity?

See how Sanity can transform your enterprise content operations.