arborist/docs/modules/qa.md
russell@unturf.com 326badf6d8
docs: README label refresh + per-module reference + Graphviz diagrams
Three things in one commit because they're tightly coupled (README
points at the diagrams; diagrams index in modules/index.md points
back at README; module pages embed the diagrams).

(1) README — label refresh:
    - Quickstart label changed from STRICT/HYBRID/UNGROUNDED to the
      four-rung ladder POINTER-LINKED → ANCHOR-WARRANTED →
      EVIDENCE-WARRANTED → UNGROUNDED with -PARTIAL suffix on HYBRID.
    - Verifier section spells out both layers (schema trichotomy +
      display ladder), the seven hard checks of claim_lattice, and
      the five anchor classes of warrant.
    - Architecture tree updated: concepts/ package added, qa/
      sub-modules expanded (warrant, evidence, parse_claims, dag),
      verify.py described as quote/span/entity/paraphrase + claim_lattice.
    - Concept overlay description updated for corpus-derived layer
      (concept_relations table, link_reciprocity extractor, 1.6%
      tax cite).
    - Test count: 326+ → 641+.

(2) docs/diagrams/ — Graphviz dot sources:
    - aborist-modules.dot — top-level package graph (substrate /
      storage / sources / retrieval / qa / mesh / cli)
    - query-pipeline.dot — question → cache → retrieval → LLM →
      verify → render → cache write, with phase budgets
    - ingest-pipeline.dot — source doc → canonicalize → chunk →
      Merkle → upsert (+ optional distill)
    - verifier-ladder.dot — (audit_mode, violations) → display rung
      decision tree
    Existing mesh-*.dot kept as-is. Makefile `make docs` target
    extended to also emit .svg alongside the existing .png so the
    diagrams render in markdown viewers.

(3) docs/modules/ — per-module reference pages:
    - index.md (links to every diagram + every module page)
    - merkle.md, document.md, store.md, ingest.md, evict.md,
      sources.md, search.md, concepts.md, qa.md, distill.md,
      wikitext.md
    Each page is a one-screenful concise reference: what the
    module is for, public API, key invariants, embedded diagrams
    where useful, link to source. Mesh stays at the existing
    docs/mesh.md + docs/mesh-deploy.md (already comprehensive).

Tests: 641 passed (no code change).
2026-05-01 23:19:01 -04:00

6.9 KiB
Raw Blame History

aborist.qa

The Q&A pipeline. Question → cache → retrieval → LLM → verify → render → cache write. Lives in 9 sub-modules; this page is the map.

query pipeline

Sub-modules

qa.client — LLM transport

ChatClient ABC with three concrete implementations:

  • StubClient — deterministic test fixture; returns canned responses keyed on the input. Used in unit tests to avoid network.
  • OpenAICompatibleClient — talks to any OpenAI-shape /v1/chat/completions endpoint (Hermes-3 on vLLM by default). Includes HTTP retry layer (3× exponential backoff on 5xx).
  • (Future) AnthropicClient — Claude API direct.

aborist/qa/client.py

qa.keys — the 8-dim cache_key

cache_key = sha256(
    source_root | question_hash | model_profile_hash |
    conversation_hash | governance_policy_hash |
    schema_version | canonicalization_version | chunking_version
)

Two question-hash modes (strict vs equivalence_class) live here. Bumping any of these eight dimensions invalidates prior records on lookup. The verifier_policy_hash (v9.9 9th dim) is also implemented here.

aborist/qa/keys.py

qa.runnerask() for single-doc Q&A

The simplest entry point. Take one document, ask one question, get back an answer + audit_mode + cache record. Used by the CLI for focused queries against one URI.

aborist/qa/runner.py

qa.queryquery() for multi-source RAG

The main retrieval entry point. Walks shards, runs FTS5 BM25 with AND→OR fallback (with synonym-pool injection in OR mode), filters by title relevance with 4 accept paths, reranks by body coverage + title boost + source role + title purity, assembles a 60 KB context budget, calls the LLM, runs the verifier, persists to providence_cache.

aborist/qa/query.py

qa.verify — the layered verifier

Five strategies run in sequence; first to find evidence classifies:

  1. quote"..."-wrapped claims tested verbatim
  2. span — bullet/sentence units substring-tested
  3. entity — multi-word proper nouns with proximity gating
  4. paraphrase — token coverage on prose-shaped spans (≥85%)
  5. claim_lattice — pointer-line [E1,E2] or JSON; runs seven deterministic hard checks:
    1. parser succeeded
    2. evidence_id resolves
    3. source_role allowed
    4. claim text non-empty
    5. citation coverage threshold
    6. pointer count cap (trim-and-verify)
    7. anchor-class warrant (see qa.warrant)

The classifier output rolls up into the v9.8 trichotomy audit_mode ∈ {STRICT, HYBRID, UNGROUNDED}. Display layer (in cli.py) maps (audit_mode, violations) → four-rung ladder.

verifier ladder

aborist/qa/verify.py

qa.warrant — anchor-class warrant

Five lexical anchor classes the verifier composes:

  • Proper-noun — relation-question shape; at least one Title-Case anchor must appear in some cited span
  • Date — claim has a 4-digit year + month name; ALL components required in some cited span
  • Entity-list — entity-list-shape question; ≥1 named entity must anchor (demote-don't-reject)
  • Count — count-shape question; count token must appear in word OR digit form (digit↔word equivalence)
  • Cause — why-shape question; ≥1 cause anchor (proper noun OR ≥5-char common noun outside stopword pool)

The warrant layer earns proof-path entry by staying lexical — no NLI, no embeddings. Substring tests over already-canonicalized spans. See docs/concept-relations-design.md (sibling section) for the relationship to retrieval-time synonym expansion.

aborist/qa/warrant.py

qa.evidence — EvidenceObject + spotlight

Builds the runtime evidence map for claim-lattice modes. Each chunk becomes one EvidenceObject carrying TWO ids:

  • pointer_id — short prompt-facing tag (E1, E2, …)
  • evidence_id — content-addressed E######## (sha256-derived)

The model sees only pointer_ids in the prompt; the runtime maps to evidence_id for the cache & run-DAG (run-stable identity).

The spotlight excerpt picks the load-bearing slice via density rank — find ALL match positions for ALL claim content tokens, pick the position with maximum distinct-token cluster within ±half-window. Replaces the older first-match-of-longest-token approach which lost the load-bearing slice on noisy chunks.

aborist/qa/evidence.py

qa.parse_claims — pointer-line parser

Walks lines of the model output, pulls every [E\d+] and [E\d+,E\d+,…] bracket payload, returns (claim_text, pointer_ids[]) per line. Lines without a tag get parse_status='NO_EVIDENCE_POINTER' & count toward the denominator so unsourced prose can't smuggle past the verifier.

aborist/qa/parse_claims.py

qa.dag — per-run Merkle DAG

Commits each provenance step independently as a stage hash. Two shapes:

  • 7-stage (quote mode): question / retrieval / context / prompt / answer / verify / final_label
  • 9-stage (claim-lattice / CTI): question / retrieval / evidence_map / prompt / raw_answer / parsed_claim_lattice / verify / render / final_label

The run_dag_root is persisted alongside every providence record; run_dag_blob carries the full {root, nodes} JSON so an auditor can recompute & verify any step.

aborist/qa/dag.py

qa.inspect — read-only sidecar

Pulls source chunks for a given cache_key & classifies each unverified span: verbatim_in_base / verbatim_in_raw_only / trailing_artifact / paraphrase / partial_paraphrase / no_overlap. Also includes the deflection-detection sidecar (subject-anchor heuristic for adversarial-premise topic shift).

Sidecars never write to providence_cache or audit_events — they're diagnostic only. That invariant is what keeps audit_mode a binary classification rather than a soft score.

aborist/qa/inspect.py

qa.concepts — backwards-compat shim

Delegates to aborist.concepts (the corpus-derived synonym/rivalry layer). Pre-2026-05-01 the data lived as hand-curated frozensets in this file; now it's a per-shard SQLite table. The shim preserves the legacy public API (synonym_expand, rivalry_excluded, has_compare_phrasing) so call sites in qa/query.py didn't have to change.

aborist/qa/concepts.pyaborist/concepts/

Source papers

  • Whitepaper §13.8 covers the layered verifier in depth
  • Whitepaper §13.9 covers claim-lattice / CTI mode
  • docs/cti-architecture.md is the architecture reference
  • docs/seven-point-program.md enumerates the seven hard checks
  • docs/concept-relations-design.md covers the synonym layer