drupal / ai_recipe_answers
Generic setup for the AI Answers RAG search-and-cite Q&A feature: a vector search index over all site content, a retrieval agent, and placeable Question/Answer/Sources blocks.
Package info
git.drupalcode.org/project/ai_recipe_answers.git
Type:drupal-recipe
pkg:composer/drupal/ai_recipe_answers
Requires
- drupal/ai: ^1.4
- drupal/ai_agents: ^1.3
- drupal/ai_answers: ^1.0@beta
- drupal/ai_recipe_content_search_vector: 1.x-dev@dev
- drupal/ai_recipe_vdb_provider_postgres: ^1.0@dev
- drupal/ai_search: ^1.3@alpha
- drupal/ai_vdb_provider_postgres: ^1.0@alpha
- drupal/core: ^10.5 || ^11.2
- drupal/search_api: ^1.39
Requires (Dev)
None
Suggests
None
Provides
None
Conflicts
None
Replaces
None
This package is auto-updated.
Last update: 2026-09-07 16:16:16 UTC
README
Generic setup for the ai_answers RAG search-and-cite Q&A feature: a vector
search index over all site content, a retrieval agent restricted to that
index, and placeable Question/Answer/Sources blocks. Meant to be extended,
not used as-is on a real site. See "Extending this recipe" below.
Requirements
- A default embeddings provider configured at
/admin/config/ai/settings(Configuration › AI › AI settings › Default Providers). Required by theai_recipe_content_search_vectordependency below, which aborts if none is set. - A reachable Postgres instance with the
vectorextension enabled. Seeai_recipe_vdb_provider_postgres, which this recipe depends on. - The
ai_searchRagTool patch (issue #3584031). Without it,RagTool::execute()never populates its structured output, so AI Answers silently gets zero retrieval results on every question even though search itself works.
FYI: this recipe only indexes content that already lives in Drupal. If you
need to pull in pages from other websites first, see
crwlr, which crawls external
sites into structured Drupal content you can then index like anything
else.
Apply
composer require drupal/ai_recipe_answers
drush recipe:apply recipes/ai_recipe_answers
drush cache:rebuild
drush search-api:index content_vector
Not safe to re-apply on a site that already has it. This recipe
depends on ai_recipe_content_search_vector for the actual server/index,
and that recipe's setupVdbServerWithDefaults/setupVdbIndex actions are
raw creates with no existence check — the second apply fails on those,
not on anything in this recipe.
What it does
- Installs
ai_agents,ai_answers; depends onai_recipe_vdb_provider_postgresfor the Postgres/pgvector connection and onai_recipe_content_search_vectorfor the actual vector index —search_api/ai_search, thesearch_indexview-mode clone (so indexing can embed the same rendered HTML a visitor would see, without needing to know any site's specific field names), and a Search API server + index (both namedcontent_vector) on thesearch_api_ai_searchbackend. - Overrides two values on the server that don't fit an AI Answers RAG
workload:
chat_model(blank instead of a hardcoded model this site may not have) andchunk_size(300 tokens instead of 1000, which overflows smaller-context embeddings models like e5-large's ~512 token limit once contextual fields are appended to each chunk — reproduced as raw HTTP 500s during testing). Overrides the index's tracker to the AI Search Chunked Tracker (
ai_search_tracker), since the dependency's own default is core Search API's plaindefaulttracker, which doesn't do the per-chunk indexing bookkeeping AI Answers' RAG retrieval depends on. Field mapping (rendered_item/title/url) and bundle coverage (every node bundle) are already right as the dependency creates them:Field Type Role rendered_itemtext Rendered HTML (anonymous, search_indexview mode), main contenttitletext Contextual content urlstring Contextual content, absolute Creates an AI Agent (
content_search_agent) with theai_search:rag_searchtool, itsindexproperty force-set tocontent_vector. An LLM-pickable index is a prompt-injection vector, so this isn't left to the model's discretion. It's forced but not hidden from the tool schema too, which would be the safer default; see "Known limitations" for why.- Enables AI Answers for that agent: teaser reference view, feedback enabled, 1-hour conversation retention, all history sent on follow-ups.
- Places three blocks (Question → Answer, references rendered separately →
Sources) in the
content_aboveregion of the site's default theme, with no visibility restriction. They render on every page until restricted. The follow-up input is part of the Answer block's own template. It always renders right after the answer text and can't be moved below a separately-placed Sources block without a custom template override.
Extending this recipe
This recipe is deliberately unopinionated about what gets indexed, what the
agent sounds like, and where the Q&A widget appears. Those are exactly the
things a real site needs to decide. Extend it with a
recipes: [ai_recipe_answers] dependency and override via config actions
targeting the same config names it creates:
- Index field mapping. Override
search_api.index.content_vectorwithsetProperties(it's a config entity;simpleConfigUpdateis deprecated for entities as of Drupal 11.2) to replacefield_settingsanddatasource_settingswith fields and bundles specific to the site. Matchai_search.index.content_vector(a plain config object, not an entity, sosimpleConfigUpdateis fine there) to the sameindexing_options. - Agent instructions. Use
setPropertiesonai_agents.ai_agent.content_search_agentto replacelabel,description, andsystem_prompt. - Answer behaviour. Use
simpleConfigUpdateonai_answers.agents(a plain config object) to replace the singleagentslist entry, such asno_answer_messageorreference_view_mode. - Block placement and copy. Use
setPropertieson the threeblock.block.ai_answers_*_content_aboveentities, for example'settings.placeholder','settings.suggested_questions', andvisibilityto restrict them to a specific page.
Indexing content
After applying, trigger an initial full index:
drush search-api:index content_vector
New and updated nodes re-index automatically on save (index_directly).
To re-index everything (e.g. after changing chunk settings or an
extending recipe's field mapping):
drush search-api:reset-tracker content_vector
drush search-api:index content_vector
Cost note
Every node save triggers one or more embeddings API calls, one per
chunk produced from the indexed content. Every question asked through the
Answer block triggers a chat_with_tools call for generation, plus one
embeddings call to vectorize the question itself. Bulk re-indexing all
content, or heavy question traffic, incurs proportional API cost. Review
your provider's per-token pricing before a full re-index or opening this
up to real traffic.
Testing
After applying and indexing, verify it worked:
- Check Configuration › Search API › Content Vector. Status should show "Successfully connected" and the index at 100%.
- Visit any page. Question, Answer, and Sources blocks should render above the page body (no visibility restriction is set by default).
- Ask a question. You should get a cited answer (
[1],[2], …) followed by a matching Sources list with working links. - If you get the no-answer fallback with zero sources every time, check
the
ai_searchRagTool patch (see Requirements) is actually applied. That's the most common cause. - If asking a question shows "Could not reach the answer service." instead, that's a 403, not a connectivity problem. See "Known limitations" below.
Known limitations
- No role has the
use ai answerspermission by default. The blocks render for everyone (no visibility restriction), but the question and feedback endpoints reject every request until some role is granteduse ai answers. This is left to an extending recipe or the site itself rather than granted here, since deciding whether anonymous visitors can call an LLM-backed endpoint is a cost/abuse decision this generic recipe shouldn't make for every site that installs it.
Issue queue
Bugs and feature requests: https://www.drupal.org/project/issues/ai_recipe_answers