Skip to content

Retrieval-Augmented Generation (RAG)

Retrieval-Augmented Generation (RAG) means letting the AI look things up in your content and use what it finds when it answers. You can combine RAG with Context Control Center (CCC) without changing this module — everything you need is already in the public API:

There are three ways to wire it up, from least to most code.

Pattern A — Agentic RAG via tools (no custom code)

Use CCC for what it does best: injecting instructions about retrieval rather than the retrieved content itself.

  1. Index your corpus with the AI module's ai_search (Search API plus a vector backend such as pgvector or Milvus).
  2. Give your agent a search tool. The AI ecosystem exposes Search API / RAG tools as function calls.
  3. Create curated context items, scoped to the relevant use cases or entity types, that tell the agent when and how to retrieve. For example: "When answering product questions, search the product_docs index first. Never answer pricing questions from memory. Cite the source node."

CCC governs the retrieval policy (scoped, moderated, revisioned — an editor can change retrieval behavior without touching code), and the agent performs the retrieval itself through tool calls. This is the most idiomatic fit for how the module works today, and it is pure site-building.

Pattern B — Deterministic injection via the event (one small subscriber)

Use this when you need retrieved content guaranteed in the prompt, rather than left to the agent's tool-calling judgement. Subscribe to the ai_context.selection.text_rendered event and fill the leftover token budget with retrieval results.

<?php

declare(strict_types=1);

namespace Drupal\example\EventSubscriber;

use Drupal\ai_context\Event\AiContextSelectionEvents;
use Drupal\ai_context\Event\AiContextSelectionTextRenderedEvent;
use Drupal\Core\Cache\CacheableMetadata;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;

final class ExampleRagSubscriber implements EventSubscriberInterface {

  public static function getSubscribedEvents(): array {
    return [
      AiContextSelectionEvents::TEXT_RENDERED => 'onTextRendered',
    ];
  }

  public function onTextRendered(AiContextSelectionTextRenderedEvent $event): void {
    // Only retrieve if there is meaningful budget left after curated items.
    $remaining = $event->getMaxTokens() - $event->getTokensUsed();
    if ($remaining < 500) {
      return;
    }

    // The retrieval query: the request's task.
    $query = $event->getRequest()->getTask();

    // Query your ai_search / Search API vector index (make sure it enforces
    // access processing). Trim chunks to fit the remaining budget; the token
    // estimator service is internal, so estimate yourself (chars / 4 is fine).
    $chunks = $this->ragIndex->search($query, limit: 3);
    if (!$chunks) {
      return;
    }

    $event->setRenderedText(
      $event->getRenderedText()
      . "\n\n## Retrieved reference material\n"
      . $this->formatWithSources($chunks)
    );

    // Retrieval results vary per task, so do not let them be cached as if they
    // were static context.
    $metadata = new CacheableMetadata();
    $metadata->setCacheMaxAge(0);
    $event->addCacheableDependency($metadata);
  }

}

Why this composition is better than a naive RAG bolt-on:

  • Curated context items always win the budget first — they are selected and rendered before your subscriber runs.
  • Retrieval only fills what is left over.
  • Everything arrives through CCC's single injection point per agent.
  • The event is documented public API, so this survives module updates.

Pattern C — Authoring-time RAG (semi-curated)

Instead of retrieving at prompt time, use automation (AI Automators, cron) to periodically summarize source documents into context items — scoped, moderated, and reviewed like any other item. You trade freshness for full editorial governance and cacheability. This suits slowly changing corpora such as policy documents.

Caveats for Patterns A and B

  • Access control: make sure the Search API index enforces access processing, because retrieved chunks bypass the agent's own entity access at render time (Pattern B especially).
  • Latency and caching: Pattern B adds an embedding and vector query to every selection and needs max-age 0 on the retrieved portion; Pattern A pushes that cost into agent tool calls instead.
  • Token estimation: the module's token estimator service is internal, so estimate in your subscriber (chars / 4) rather than relying on it.

Which pattern should I use?

Start with Pattern A — it needs zero code, and the "context items as retrieval policy" framing is powerful on its own. Reach for Pattern B when you need compliance-grade certainty that reference material is present in the prompt. Use Pattern C for slowly changing content where editorial review matters more than freshness.