Search by

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.

Maintainers

Package info

git.drupalcode.org/project/ai_recipe_answers.git

Type:drupal-recipe

pkg:composer/drupal/ai_recipe_answers

Transparency log

Statistics

Installs: 1

Dependents: 0

Suggesters: 0

1.0.x-dev 2026-09-07 21:45 UTC

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 the ai_recipe_content_search_vector dependency below, which aborts if none is set.
  • A reachable Postgres instance with the vector extension enabled. See ai_recipe_vdb_provider_postgres, which this recipe depends on.
  • The ai_search RagTool 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 on ai_recipe_vdb_provider_postgres for the Postgres/pgvector connection and on ai_recipe_content_search_vector for the actual vector index — search_api/ai_search, the search_index view-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 named content_vector) on the search_api_ai_search backend.
  • 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) and chunk_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 plain default tracker, 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:

    FieldTypeRole
    rendered_itemtextRendered HTML (anonymous, search_index view mode), main content
    titletextContextual content
    urlstringContextual content, absolute
  • Creates an AI Agent (content_search_agent) with the ai_search:rag_search tool, its index property force-set to content_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_above region 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_vector with setProperties (it's a config entity; simpleConfigUpdate is deprecated for entities as of Drupal 11.2) to replace field_settings and datasource_settings with fields and bundles specific to the site. Match ai_search.index.content_vector (a plain config object, not an entity, so simpleConfigUpdate is fine there) to the same indexing_options.
  • Agent instructions. Use setProperties on ai_agents.ai_agent.content_search_agent to replace label, description, and system_prompt.
  • Answer behaviour. Use simpleConfigUpdate on ai_answers.agents (a plain config object) to replace the single agents list entry, such as no_answer_message or reference_view_mode.
  • Block placement and copy. Use setProperties on the three block.block.ai_answers_*_content_above entities, for example 'settings.placeholder', 'settings.suggested_questions', and visibility to 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:

  1. Check Configuration › Search API › Content Vector. Status should show "Successfully connected" and the index at 100%.
  2. Visit any page. Question, Answer, and Sources blocks should render above the page body (no visibility restriction is set by default).
  3. Ask a question. You should get a cited answer ([1], [2], …) followed by a matching Sources list with working links.
  4. If you get the no-answer fallback with zero sources every time, check the ai_search RagTool patch (see Requirements) is actually applied. That's the most common cause.
  5. 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 answers permission by default. The blocks render for everyone (no visibility restriction), but the question and feedback endpoints reject every request until some role is granted use 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