RAG Context Bundle and Session Briefings

Adapted from docs/admin/RagContextBundle.md in the Wikantik repository; current as of release 2.4.56.

Wikantik delivers retrieval to AI agents as RAG-as-a-Service: the wiki does the assembly work a RAG pipeline normally pushes onto the caller — ranking, de-duplication, section selection, and citation — and returns a ready-to-ground context bundle. It never synthesizes the answer itself. That boundary is a deliberate architectural decision, recorded in ADR-0001 (docs/adr/0001-rag-returns-context-bundle-not-synthesized-answer.md): the wiki assembles context with citations, and the calling model does the reasoning, so every claim an agent makes traces back to a page and version you can open.

This document is the operator/integrator reference for the two agent-facing surfaces built on that principle — the context bundle and the session briefing — plus the version-pinned citation layer that keeps their groundings honest. For the retrieval quality levers underneath (the chunker, contextual embeddings, fusion weights), see HybridRetrieval.

Both surfaces exist as a REST endpoint (for pipelines) and a mirrored MCP tool (for agents), returning the same structure.

CapabilityRESTMCP (/knowledge-mcp)
Context bundleGET /api/bundleassemble_bundle
Session briefingGET /api/briefingget_briefing
Stale-citation curationGET /admin/drift/citationslist_stale_citations

The context bundle

GET /api/bundle?q=… (and the assemble_bundle MCP tool) returns the top-ranked, de-duplicated sections across the whole corpus — section-level, not whole pages, and not a list of links to fetch yourself. Candidates are drawn from a global pool of content chunks, so a highly relevant section is never dropped just because its page ranked outside the top-N. Each returned section carries a version-pinned citation (source page + the exact version it came from), and the response carries a coverage signal.

Query parameters (/api/bundle)

ParameterRequiredDefaultMeaning
qyes—The natural-language query. Missing/blank → 400 with {"error":"q (query) parameter required"}.
modenohybridRetrieval strategy: hybrid (BM25 + dense, RRF-fused), dense (vector-only), lexical (BM25-only). Case-insensitive; an unknown value returns a clear error listing the valid modes.
debugno—debug=rankings returns the raw dense + BM25 chunk rankings (id + score) instead of a bundle, for tuning (used by bin/eval/sweep-bm25-fusion.py). Requires the chunk-hybrid source (wikantik.bundle.bm25.enabled).
kno500Only read with debug=rankings: how many ranked chunks to return. It does not change how many sections a normal bundle holds.

The assemble_bundle MCP tool takes the same query and mode; the no-mode path is fully backward compatible with the original assemble(query) contract.

The coverage signal

Every bundle response carries a coverage block so an agent can tell how well-grounded an answer will be before it composes one:

FieldMeaning
sectionCountNumber of sections in the bundle.
distinctPageCountNumber of distinct source pages represented.
topSimilarityThe best section's true dense cosine similarity (not a rank proxy).
confidencestrong / partial / weak, derived from topSimilarity against the thresholds below; unknown when no dense similarity is available (topSimilarity is -1.0).

Confidence is a routing tool, not just telemetry. A strong bundle means answer from it; a partial or weak bundle is the agent's cue to widen the search, ask a clarifying question, or fall back to a structured query (sparql_query, the ontology, list_clusters) — the MCP tool descriptions route count/enumeration questions there rather than at the prose bundle. Coverage is recounted after the ACL view-gate: if access filtering thins a strong bundle to fewer than 3 viewable sections, it is downgraded to partial, so the signal reflects what the caller can actually see.

Default thresholds (both configurable — see the config table): topSimilarity ≥ 0.55 → strong, ≥ 0.40 → partial, otherwise weak. These are provisional defaults validated against production cosine distributions.

Retrieval source

By default the bundle sources candidates from a global chunk-level hybrid (dense + BM25, RRF-fused) rather than a page-gated hybrid — it beats page-gating because it does not drop sections whose page ranks outside the top-N. The bundle has its own fusion config (wikantik.bundle.bm25.*), independent of the main search fusion. Set wikantik.bundle.dense.enabled=false to fall back to the page-gated retrieval source.

Op gotcha: only the pgvector dense backend (wikantik.search.dense.backend) reads through to the database, so its rows are queryable as soon as they commit. inmemory and lucene-hnsw build a separate index that is refreshed per page save and after a successful bootstrap embedding run. If a bootstrap ends in FAILED, the dense index stays stale and the bundle degrades to BM25-only until the next successful run or a restart. A topSimilarity of -1.0 with confidence: unknown in the coverage block is the sign.


Session briefings

Retrieval mid-task is reactive — the agent already has to know what to ask. A session briefing front-loads the context an agent should have before it starts: the conventions, runbooks, and design decisions for the area it is about to work in. A coding agent (Claude Code, or any compatible assistant) requests one at session start and injects the result as opening context.

GET /api/briefing (and the get_briefing MCP tool) assembles a budgeted, de-duplicated, injection-ready briefing from any combination of pinned pages, clusters, and the task prompt. GET /api/briefing returns structured JSON by default and injection-ready Markdown with format=md; the get_briefing MCP tool always returns Markdown.

Query parameters (/api/briefing)

ParameterMeaning
pinsCSV of page names to pin into the briefing verbatim.
clustersCSV of clusters to draw representative context from.
promptThe task prompt — used to retrieve additionally-relevant sections.
budgetToken budget for the assembled briefing (the assembler dedupes and trims to fit). A non-integer value returns 400. Default 6000 (wikantik.briefing.default_budget), capped at 24000 (wikantik.briefing.max_budget).
scope_modeprefer (default: the briefing may widen beyond the pins/clusters) or strict (stay within them). Any other value returns 400.
formatjson (default — structured) or md (injection-ready Markdown, text/markdown). Any other value returns 400. Session hooks pass format=md.

The get_briefing MCP tool takes pins, clusters, prompt, budget and scope_mode (no format) and returns Markdown.

At least one of pins, clusters or prompt is required, otherwise the endpoint returns 400. Input is capped at 25 pins and 10 clusters (200 members per cluster); excess is truncated with a logged warning.

Each assembled briefing is recorded in the briefing_log table (migration V044; toggle with wikantik.briefing.log.enabled, default true) for telemetry — what was requested, how large the result was, whether the budget bound. Portable client shims that wire the briefing into an agent runtime (Claude Code, Antigravity) live under clients/ in the repository.


Version-pinned citations & self-healing

Grounding is only worth something if it stays true. An author (or a curator agent) marks a claim's source inline in the page body with cite:// markup; at save time that becomes a first-class citation edge in the citations table — version-pinned and span-hashed to the exact passage it grounds. These are the same version pins that make a bundle section citable.

When a cited page is later edited, Wikantik does not blanket-invalidate everything pointing at it. It grades staleness at the span level — did the cited passage actually change, or just something elsewhere on the page? — and surfaces what genuinely drifted:

This is the self-healing half of RAG-as-a-Service: the corpus tells you when a grounding has gone out of date instead of letting it rot silently. Full rationale in ADR-0005 (docs/adr/0005-persisted-citation-edges-and-stale-citation-curation.md). wikantik.citations.enabled gates the citation subsystem (document ingestion is unrelated and always on).


Access control

Bundles and briefings are assembled from the same retrieval candidates as the rest of the platform, so the same page-level ACLs apply — a section from a restricted page never appears in a bundle or briefing for a caller whose session cannot view it (and, as above, coverage is recounted after that gate). The REST endpoints enforce the view ACL through the candidate retrieval, the same posture as /api/pages. Both /api/bundle and /api/search sit in the expensive tier of the public-surface rate limiter (see Wikantik Operations, section 1.5 — default 3 req/s per client


Configuration reference

All keys live in ini/wikantik.properties; override in wikantik-custom.properties (bare-metal) or via the container's property injection. Defaults shown.

KeyDefaultPurpose
wikantik.bundle.dense.enabledtrueUse the global dense-chunk source (vs. the page-gated fallback).
wikantik.bundle.dense.top_k300Chunk candidate pool size pulled before fusion/selection.
wikantik.bundle.sections_per_page20Max sections contributed per source page.
wikantik.bundle.bm25.enabledtrueEnable the chunk-level BM25 arm of the bundle's own hybrid fusion.
wikantik.bundle.bm25.bm25_weight / .dense_weight0.5 / 1.5Fusion weights for the bundle's BM25 ↔ dense RRF (independent of main search). Deliberately dense-tilted 3:1 — tuned 2026-06-18 as half the page-level BM25 weight, which kept the similarity gain without the boundary@5 dip the reused page weights (bm25=1.0) caused.
wikantik.bundle.bm25.rrf_k20RRF constant for the bundle fusion.
wikantik.bundle.coverage.strong_similarity0.55topSimilarity at/above which coverage is strong.
wikantik.bundle.coverage.partial_similarity0.40topSimilarity at/above which coverage is partial (else weak).
wikantik.bundle.reranker.enabledfalseLLM listwise reranker. Default off — measured to reorder without improving recall (a bad relevance judge under shuffled input).
wikantik.briefing.enabledtrueKill switch for the briefing service; when false it is not wired (code default; not declared in wikantik.properties).
wikantik.briefing.default_budget / .max_budget6000 / 24000Default and maximum token budget for /api/briefing (code defaults; not declared in wikantik.properties).
wikantik.briefing.log.enabledtrueRecord briefing requests to briefing_log.
wikantik.citations.enabledtrueGate the cite:// citation-edge subsystem (unrelated to document ingestion).

The public-surface rate-limit knobs (WIKANTIK_RATELIMIT_*) that guard /api/bundle are documented in the "Performance / search-backend tuning" section of Wikantik on Docker and section 1.5 of Wikantik Operations.

See also


Cluster Hub: Wikantik Platform Development Hub: Architecture, MCP Protocols, and Agent Briefings (wikantik-development)