FORAGER — KNOWLEDGE · v0.4.2.14

Retrieval: evidence first

Source: modules/anthill/docs/RAG.md, a path from the root of the product's repository — versioned with the code, rendered here at every release.

How the colony turns a question into evidence-backed context, and why it is not the usual thing.


1. What this is not

The default shape of retrieval-augmented generation is:

query -> embed -> nearest neighbours -> paste chunks into the prompt

What the model receives from that is text with no accountability. It cannot tell a signed decision from a hallway rumour that happened to be written down, and its only way to judge either is plausibility. If two documents disagree, similarity search returns both and says nothing; the model picks whichever it read first, or averages them into a sentence that is true of neither.

ANTHILL does not do this, and the reason is not quality — it is that a colony which acts on what it retrieves cannot afford a retrieval layer that launders provenance.


2. What it is instead

question
   |
   v
normalize                  collapse whitespace, bound length. Nothing else.
   |
   v
rank candidates            FORAGER's search backend (FTS5 today, vector later)
   |
   v
filter                     scope, confidentiality band, support floor, historical
   |
   v
fetch evidence             per candidate: the excerpts that support it
   |
   v
expand entities            who and what the statements are about
   |
   v
attach conflicts           scope-wide, then narrowed to the facts in hand
   |
   v
order + budget             deterministic sort, whole facts dropped from the tail
   |
   v
KnowledgeContext           labelled, sourced, conflict-aware

Evidence comes before assembly. An item whose evidence cannot be resolved has to be labelled, not dropped — and you cannot label what you have already flattened into a prompt.

Conflicts come before assembly for the same reason: a contested fact must never be rendered as settled, and the assembler needs to know it is contested before it writes it down.


3. Every statement is labelled

Four levels, carried from FORAGER, never inferred and never upgraded:

Level Means
DIRECT FACT The source states it. The excerpt says the thing.
SUPPORTED INFERENCE Inferred, and the evidence carries the inference.
UNCERTAIN INFERENCE Inferred with a gap the evidence does not close.
UNVERIFIED CLAIM Asserted somewhere, supported by nothing. Hedged and reported speech land here.

A level this build does not recognise renders as UNKNOWN SUPPORT. That happens when FORAGER is newer than ANTHILL, and the honest outcome is a statement a model treats carefully — not one silently promoted to DIRECT FACT because that was the first enum member.


4. What the model actually sees

KNOWLEDGE CONTEXT
Query: "Why was the Falcon launch date changed?"
Scope: project proj_ef42d498ae1e

CONFLICTS DETECTED — the sources disagree. Do not report either side as settled.

[CONFLICT-1] attribute_mismatch on falcon|launch_date — UNRESOLVED
  2 different values were stated for "launch_date" of falcon: 2026-03-03 vs 2026-04-10
  Competing statements: FACT-1, FACT-2
  Suggested (NOT APPLIED): Only this statement comes from a dated source (2026-02-18)

FACTS

[FACT-1] The launch date for Project Falcon is March 3, 2026.
  Support: DIRECT FACT   Confidence: 0.90   Status: DISPUTED   Effective: 2026-03-03
  Evidence:
    - 08-design-review.docx (Section: Falcon Design Review Notes > Schedule)
      "The launch date for Project Falcon is March 3, 2026."

[FACT-2] The launch date for Project Falcon has moved to April 10, 2026.
  Support: DIRECT FACT   Confidence: 0.90   Status: DISPUTED
  Evidence:
    - 02-schedule-update.eml (message body)
      "The launch date has moved to April 10 because the Northwind parts slipped."

RELATED ENTITIES
  Project Falcon (project) — also: Falcon

SOURCES
  02-schedule-update.eml
  08-design-review.docx

Conflicts are printed before the facts. A model that reads the statements first has already formed an answer; one that learns they are contested first reasons about the disagreement. The test TheRenderedContext_LeadsWithTheConflictAndNeverPresentsItAsSettled asserts the ordering, not just the presence.


5. Rules the assembler enforces

Every fact has evidence, or says it does not.

  Evidence: NONE — this statement is UNRESOLVED. Its supporting text could not be
            located. Do not rely on it without checking the source yourself.

KnowledgeContext.FactsWithoutProvenance() must return empty. A non-empty result is a defect in the assembler, and the tests assert it stays empty for every fixture including the deliberately broken ones.

Conflicts are presented, never resolved. FORAGER's suggested resolution is carried and marked NOT APPLIED. Presenting prefer ki_3943b4 to a reasoning layer invites compliance; presenting "only this statement comes from a dated source" invites judgement, which is the point.

Truncation is declared. Whole facts are dropped from the tail — never truncated mid-statement, because a half-quoted excerpt is a misquotation and this pipeline's entire claim is that the quotes are real. When it happens the context says so, so absence is not read as non-existence.

Absence is not silence. An empty result says the base was searched and had nothing, and that this is not permission to assume an answer. An unavailable base says something different again — see §7.

The render is deterministic. Facts sort by support, then confidence, then id. Same knowledge base, same query, same bytes.


6. Iterative retrieval

Agents are not required to get everything in one call, and should not try.

"Determine why the authentication deployment failed last month."

  knowledge_retrieve  "authentication deployment failure"   -> incident INC-1842, a procedure
  knowledge_entity    "INC-1842"                            -> the people and services involved
  knowledge_retrieve  "authentication integration test"     -> the procedure's verification step
  knowledge_evidence  ki_...                                -> the exact text of the step
                                                            -> answer, with citations

Each call is cheap, scoped, cached briefly, and charged against the mission's budget. The knowledge_search tool exists for exactly this: a candidate list without evidence expansion, so an agent can decide what is worth the full retrieval.


7. Retrieval strategies

The Anthill-facing API is deliberately backend-agnostic. Today FORAGER answers with:

  • FTS5 — stemming, prefix matching, bm25 ranking. The normal case.
  • LIKE fallback — case-insensitive substring with field weighting, on a Node build whose SQLite lacks FTS5. Same response shape; thinner results.

Which one answered is reported in Metadata.Backend and shown in the console, because a thin result set means something different under each.

Vector search is not implemented, and that is a decision. FORAGER's SearchBackend seam is where it belongs and it does not exist there yet. When it lands, nothing in this document's API changes — the provider, the tools, the context and the console all keep working, because ranking is FORAGER's job and the boundary carries results rather than scores. Forcing a vector database in now would add a dependency for retrieval quality FORAGER cannot currently deliver.

The architecture supports, at the seam rather than in this repository: BM25/FTS, vector, hybrid, knowledge-graph expansion, entity-aware and temporal retrieval.


8. Temporal questions

Status survives the whole pipeline, so history stays answerable:

Question How
What is the latest state? default retrieval — superseded and archived are excluded
What was the procedure in March? include_historical: true, read effective_date
What changed between March and August? historical retrieval; compare the superseding chain
What did we believe before the incident? historical retrieval bounded by effective date

A superseded fact renders as Status: SUPERSEDED with Superseded by: FACT-n. It was true of its time, and saying so is different from hiding it.


9. When it cannot answer

The failure text is part of the safety design, not a message:

Knowledge retrieval is unavailable: the knowledge service did not respond within 5000ms.
The mission can continue without organizational knowledge, but evidence-backed context
could not be retrieved. Do NOT substitute recalled, assumed, or generally-known facts for
it, and do not present anything from this attempt as sourced.

Without that last sentence, a model told only that retrieval failed will commonly proceed to answer from training as though it had retrieved something — confidently, and with no way for a reader to tell. KnowledgeFailure distinguishes disabled, unavailable, timeout, unauthorized, scope-unresolved and not-found, and each renders its own lead sentence.