Skip to content

Context Selection

Context Control Center selects a limited set of published Context Items for each AI request. Selection is deterministic and rule-based: the module does not ask an AI model to decide which top-level items are relevant.

This guide explains the selection pipeline and how scope subscriptions affect ranking. For the behavior of each built-in scope, see Context Scope.

Selection at a glance

Context Items move through the following stages:

  1. Remove unpublished, inaccessible, and Never include items.
  2. Load published, accessible Always include overrides.
  3. Apply hard context filters to the remaining candidates.
  4. Collect Global items up to the global item limit.
  5. Collect exact-match items such as Specific Entities matches.
  6. Score candidates against scope subscriptions.
  7. In Relevant mode and above, also collect items positively matching a scope that declares situational matching — Languages, Site Sections, Entity Types, and Taxonomy Terms by default; see Situational-match. Situational-match classification acts as a fallback if a candidate leaves the final scope-scored group.
  8. In Broad mode, keep any remaining published item passing hard filters as a broad-fill group.
  9. Merge the groups in priority order.
  10. Add required and selected conditional subcontext children.
  11. Render the selected content within the token limit.

The merge order is:

  1. Global
  2. Always-included
  3. Exact-match, such as Specific Entities matches
  4. Scope-scored
  5. Situational-match (Relevant and above): scopes declaring situational matching — Languages, Site Sections, Entity Types, or Taxonomy Terms by default — positively matching the request context
  6. Broad-fill (Broad only): remaining published items ordered by Priority

The merge order is fixed, but Never include takes precedence over Always include. Global items remain subject to the global item limit; rendered output remains subject to the token limit. No scope match, subscription, or inclusion priority guarantees that complete content reaches the final prompt.

Hard filters and subscription scoring

Scopes can affect selection in two independent ways.

Hard context filters

A hard filter asks whether a Context Item applies to the request context. The built-in Languages, Site Sections, Entity Types, Taxonomy Terms, Specific Entities, and Roles scopes can act as hard filters. If a hard filter rejects an item, a separate subscription match does not bring it back.

Each enabled scope returns one of three results:

  • Match: the item is valid for the request context.
  • No match: the item is removed.
  • Neutral: the scope does not restrict the item or the detected value cannot be determined.

An item must avoid a No match result from every enabled scope. One matching scope does not override another scope that rejects the item.

Always include overrides skip this hard-filter pass. A role-gated item that an agent always includes is injected regardless of the acting user's roles. Publication, view access, Never include, and the token limit still apply.

Use Cases and Context Tags do not detect the request context. They never remove an item during hard filtering. Roles never auto-includes an item: a matching role is treated as neutral so the item remains a candidate without entering the exact-match or situational-match groups.

Situational-match

A Match result from Languages, Site Sections, Entity Types, or Taxonomy Terms means more than passing the hard filter: in Relevant mode and above, that positive match is enough to include the item on its own, even if no consumer subscribes to that scope. Situational matching is a scope plugin capability (supportsSituationalMatch()), so custom scopes can opt in; those four built-ins declare it by default. See Relevant (shipped default) below.

Subscription scoring

After hard filtering, subscribable scopes compare the Context Item's values with the values requested by the consumer, such as an agent. More overlap produces a higher score.

Only scopes with non-empty subscriptions participate in the score:

  • If the item has no values for a subscribed scope, it receives no credit for that scope. An empty scope no longer earns automatic credit; it simply does not match that dimension.
  • If all subscribed values occur on the item, it receives full credit.
  • If some subscribed values occur on the item, it receives proportional credit.
  • If none occur, it receives no credit for that scope.

An item with no credit from any subscribed scope receives a total score of zero and is excluded. A mismatch in one scope does not necessarily exclude an item: credit from another subscribed scope can keep its total score above zero.

For example, suppose an agent subscribes to the Working with Text use case and the Brand tag. An item scoped to Working with Canvas does not match the use case. If that item has no Tags scope, it receives no credit for Tags either, so it needs credit from a different subscribed scope to remain a candidate.

Relative scope influence

Subscribed scopes do not all contribute equally. The current relative influence, from greatest to least, is:

  1. Use Cases
  2. Context Tags
  3. Languages
  4. Site Sections
  5. Entity Types
  6. Taxonomy Terms

Global, Roles, and Specific Entities do not participate in subscription scoring.

These values affect ranking only when the consumer subscribes to the corresponding scopes. They do not change hard-filter behavior.

The numeric calculation is an internal implementation detail. Developers working on scope plugins can find the current formula in Scope scoring.

Selection mode: a nested ladder

The selection mode is a strictly nested ladder: each mode is a superset of the one below it, and hard filters apply in every mode.

The site-wide default mode is configured on the General settings page (Default context selection mode); it ships as Relevant. It applies to every agent and request-factory consumer that does not set its own mode, and each agent can override it on its agent settings form under Advanced. Callers that construct AiContextRequest directly must pass the desired mode.

Minimal

The strictest mode. It considers:

  • Global items
  • Always-include overrides
  • Exact-match items (e.g., Specific Entities matches)
  • Strict scope subscription matches

It does not add situational or unscoped items merely because a scope happens to match the request context; only an explicit subscription earns a scored candidate a place here.

Relevant (shipped default)

Everything Minimal includes, plus items positively matched against the request context by scopes declaring supportsSituationalMatch() — Languages, Site Sections, Entity Types, and Taxonomy Terms by default (a Match result from Situational-match), even when no consumer subscribes to that scope. An unscoped ("generic") item is not added by this alone; it needs a subscription match or Broad mode.

Broad

Everything Relevant includes, plus any remaining published item that passes hard filters, ordered by Priority, to fill leftover token budget. Broad supplements the scope-scored and exact-match groups; it never displaces them. The token budget still applies, and the selector keeps only the highest-priority candidates when a very large catalog exceeds its internal candidate bound.

Relevant and Broad always load the full published catalog. On sites with more than 50 published items, Drupal logs one warning per consumer per request. That is expected. Use Minimal to prefilter, or follow #3586420 for a Relevant prefilter.

Inclusion overrides

Always include

Always-include items bypass scope subscription scoring and hard context filters. They must still be published, accessible to the current user, and within the final token limit. Always include gives an item priority; it does not guarantee that the complete item reaches the prompt. Do not use it as the only delivery control for compliance-critical instructions.

Never include

Never-include items are removed before categorization. This override also wins when the same item appears under Always include.

Limits

Two module-wide limits shape the final result:

  • Max global context items limits the Global group.
  • Max tokens limits the rendered text.

The selector also applies an internal, non-configurable bound of 100 candidates per selection group (scope-scored, broad-fill, and exact-match) as a performance safeguard on very large catalogs. It keeps the highest-scored (with Priority breaking score ties) or highest-priority candidates, and realistic token budgets are exhausted well before this bound is reached.

When candidates have equal subscription scores, the selector orders them by Priority first, then by shorter content length. The same priority ordering applies within global, always-included, and exact-match groups. Shorter content is preferred when priority ties so more complete items are likely to fit within the token budget. The highest item ID is the final, deterministic tie-break.

Subcontext children are resolved after top-level selection. Required or conditionally selected children can increase the final selected item count, but their rendered content remains subject to the token limit. Required children may be shortened or omitted during rendering when the token budget is exhausted.

Translation happens separately

Language scope values determine whether an item is eligible and how it scores. They do not determine which entity translation is rendered. After loading candidates, the selector chooses the Context Item translation for the detected content language when one exists and otherwise uses the original translation.

See Multilingual Support for the language-detection and fallback rules.

Page and entity context

Hard filters for Specific Entities, Entity Types, Taxonomy Terms, and Site Sections need a request entity or page path. CCC can read that from the Drupal route, from agent token contexts, or from explicit tool parameters. Chatbots and AI Assistants often have none of those unless the integration supplies them. See Page and entity context.