Documentation / Query classification & quorum cache
HiNet — Query Classification & Quorum Cache
Status: [NEW spec, MVP-buildable core] · companion to Routing & Aggregation (deepens §2.4 Quorum Memory) and Node Identity & Registry (§4.5 taxonomy).
This spec defines the routing hot path: how a natural-language query is turned into a small, stable topic-set, and how that topic-set keys a cache of standing quorums so the common case answers without re-routing the whole network. It is the concrete form of the cache key left abstract in Routing §2.4 (StandingQuorum.signature).
The load-bearing property, stated up front:
The same set of topics — regardless of order or phrasing — resolves to the same quorum. Two differently-worded queries that touch
{thermal-design, materials-science}hit one cached quorum; a genuinely new topic-set mints a new one. Routing depends on which domains a question spans, not on its words — so we canonicalize to a set and cache on that.
0. Vocabulary (reused + added)
| term | meaning | source |
|---|---|---|
| topic / domain-id | a canonical, governed competence label tax:<domain>@v<n> |
Identity §4.5.2, Routing §2.5.6 |
| TopicSet | the unordered set of domain-ids a query spans, after classification + relevance filtering | [NEW] |
set-key k |
H(sorted(topic_ids)) — order-invariant exact cache key |
[NEW] |
topic-vector v |
pooled centroid of the TopicSet's members — the semantic ANN key for near-matches | [NEW] |
| StandingQuorum | a remembered, reusable assembly (membership + mode, possibly pre-merged) | Routing §2.4.1 |
| QuorumCache | {set-key → StandingQuorum} + an ANN index over topic-vectors |
[NEW] (concretizes §2.4) |
1. Pipeline overview [MVP]
query ──▶ ① classify ──▶ ② snap to taxonomy ──▶ ③ relevance filter ──▶ TopicSet
│
┌───────────────────────────────┤
▼ set-key k = H(sorted(ids)) ▼ topic-vector v = pool(centroids)
④ EXACT cache lookup ⑤ SEMANTIC near lookup (ANN on v)
│hit │near-hit (cos ≥ 1−radius)
▼ ▼
reuse StandingQuorum warm-start + grow/shrink to fit TopicSet
│ │
└────────────┬───────────────────┘
▼ full miss
⑥ COLD route (Routing §2.2) → assemble → INSERT new entry {k, v → quorum}
▼
answer + signed receipts → ⑦ reinforce/decay (§2.4.3)
Every stage after ① is cheap, deterministic, and local to the asker's node. ① is one small model pass. The whole point is that stages ④–⑤ replace the expensive discover() + right-size + N-way fan-out (§2.2) on the hot path.
2. Classification — query → TopicSet [MVP]
2.1 Extract candidate topics [MVP]
Reuse the already-built local agent pass (/v1/agent, the §2.2 decompose()): one cheap generation that emits the query's facets as short keyword phrases (T=1 for a simple ask, more for a cross-domain one). No new model. Output: raw_topics = ["thermal fatigue in turbine blades", "nickel superalloy selection", ...].
In parallel, embed the whole query q = e5.embed(query) (the same multilingual-e5 space the registry indexes in) — used for the fallback path and to weight relevance.
2.2 Snap to the governed taxonomy [MVP]
Free-text keywords don't compare across nodes — "thermal fatigue" vs "heat cycling" must resolve to the same domain or the cache fractures. So each raw_topic is snapped to a canonical domain-id by e5-kNN against the embedded taxonomy table (competence_taxonomy.centroid, Identity §4.5.2):
def snap(raw_topic):
v = e5.embed(raw_topic)
cand = taxonomy.ann(v, k=3) # nearest governed domains
best = cand[0]
if cos(v, best.centroid) >= TAU_SNAP: return best.domain_id # confident → canonical id
return propose_new_domain(raw_topic, v) # below threshold → candidate new taxonomy node (§6, owner/governance-gated)
Snapping is what makes the set-key stable and cross-node comparable. A query never invents a private label on the hot path; it either matches a governed domain or flags a proposal (handled off-path).
2.3 Relevance filter → the TopicSet [MVP]
Drop incidental topics so the set stays tight (or the cache never hits): keep a domain only if its facet is materially part of the query.
TopicSet = { d for (d, facet_vec) in snapped
if cos(facet_vec, q) >= TAU_REL # actually central to the query
and weight(facet) >= W_MIN } # not a throwaway aside
# cap |TopicSet| ≤ K_MAX (default 4): beyond that, route cold — over-broad queries shouldn't share a cache slot
TopicSet is now a small unordered set of canonical domain-ids — the routing-relevant essence of the query, phrasing and order discarded.
3. The cache key — order-invariant by construction [MVP]
Two keys are derived from the one TopicSet; they serve the two lookup modes.
ids = sorted(TopicSet) # canonical order → order-invariance is structural, not learned
k = blake3("|".join(ids)).hex() # EXACT set-key: identical topic-sets collide by design
v = normalize(sum(taxonomy[d].centroid * rel[d] for d in ids)) # SEMANTIC topic-vector for near-match ANN
k(exact). Same set of domains → same string → same slot.{A,B}and{B,A}are identical;{A,B}and{A,B,C}are not (handled by the semantic path + grow/shrink, §4.2). This is the "same topics regardless of order → same quorum" guarantee — it is a property of the key derivation, not a heuristic.v(semantic). A single vector summarizing the set, so near-miss sets (a subset, a superset, a synonymous domain that snapped slightly differently) can still warm-start instead of cold-routing.
Granularity is the one real tuning knob (§6): the taxonomy version @v<n> fixes how fine the domains are. Coarser taxonomy → higher hit rate, blunter quorums; finer → sharper quorums, colder cache. The set-key is only as stable as the taxonomy it snaps to — which is why the taxonomy is governed + versioned (Identity §4.5.2) and re-basing is deterministic (Routing §2.5.6).
4. Lookup + fill [MVP]
4.1 The three outcomes
def route_via_cache(query, ctx):
ts = classify(query) # §2
k, v = keys(ts)
sq = cache.get_exact(k) # ④ EXACT
if sq and fresh(sq):
return serve(sq, query, ctx) # identical topic-set → reuse verbatim (hot/warm, §2.4.2)
sq = cache.ann(v, radius=R) # ⑤ SEMANTIC near-match
if sq and cos(v, sq.topic_vec) >= 1 - R and fresh(sq):
members = fit(sq.members, ts, query) # grow/shrink to THIS set (§4.2)
ans = serve_with(members, query, ctx)
cache.put(k, v, standing_from(members, ts)) # record the exact-key variant so next identical query is a hot hit
return ans
plan = right_size(discover(q, ctx.membrane), query) # ⑥ COLD (Routing §2.2)
ans = aggregate(plan, query, ctx)
cache.put(k, v, standing_from(plan.members, ts)) # mint a new StandingQuorum keyed by this TopicSet
return ans
4.2 Subset / superset via grow-shrink (why near-match is safe) [MVP]
A cached quorum for {A,B,C} answering a {A,B} query shrinks (drop the member that only covered C); a {A,B} quorum facing {A,B,D} grows (add one specialist for D). This is exactly Routing §2.4.3 grow/shrink/decay — the cache is not brittle to set variation; it warm-starts from the nearest set and adjusts by a small delta, then records the exact-key entry so the next identical query is a pure hot hit. Cache convergence: popular topic-sets settle into stable exact-key quorums; the long tail rides the semantic path.
4.3 What "serve" means (tier ladder) [reuse §2.4.2]
- hot —
sq.warm_merge ≠ nulland membership unchanged → one forward pass on the pre-merged model. - warm — reuse membership, committee-aggregate (skip discovery).
- cold — full route; the only path that touches the global index.
5. Maintenance — the cache stays live, not stale [MVP → LATER]
Delegates to Routing §2.4.3 (this spec adds nothing new, just the keying):
- reinforce — a served quorum's realized quality (signed QueryReceipt acceptance + ewma_score) adjusts its standing; only down, or up within tier caps (never grant trust a proof didn't earn — §2.5.4).
- decay / evict — ttl + ewma decay with disuse; LRU-by-value eviction; a topic-set that stops recurring lets its entry lapse.
- split / merge — a bimodal exact-key slot (the same TopicSet routed to two genuinely different good quorums, e.g. by membrane) splits; near-duplicate topic-vectors merge. Mirrors §2.4.3.
- taxonomy re-base — when the governed taxonomy versions (@v<n> → @v<n+1>), affected set-keys re-hash under the deterministic re-basing rule (§2.5.6); the cache migrates rather than silently mis-hitting.
Admission gate (reuse): a StandingQuorum may only cache members whose speciality for those domains is a fresh, sufficiently-proven competence attestation (Routing §2.5.4) — the cache cannot memorize a self-declared liar.
6. Worked example
query: "how do I stop my turbine blades from cracking after thousands of heat cycles?"
① decompose → ["thermal fatigue turbine blades", "heat-cycle cracking", "blade material choice"]
② snap → tax:thermal-fatigue@v2, tax:thermal-fatigue@v2, tax:materials-science/superalloys@v1
③ relevance→ TopicSet = { tax:thermal-fatigue@v2, tax:materials-science/superalloys@v1 } (deduped, 2 topics)
k = blake3("tax:materials-science/superalloys@v1|tax:thermal-fatigue@v2") # sorted → order-invariant
v = pooled centroid
④ exact miss (first time) → ⑤ semantic miss → ⑥ COLD route → quorum = { eLiCore-mecheng, iCore-metallurgist }
cache.put(k, v, quorum)
—— later, a differently-worded query ——
query: "my jet-engine blades keep fatiguing under repeated thermal loading — material fix?"
① decompose → [...] ② snap → same two domain-ids ③ → SAME TopicSet → SAME k
④ EXACT HIT → serve the standing quorum (warm/hot). No re-discovery. ✔ same set of topics → same quorum.
7. Reuses vs adds
Reuses [EXISTS]: the /v1/agent decompose pass + e5 embedder (RAG); the governed competence_taxonomy + snap-by-kNN (Identity §4.5.2, Routing §2.5.6); StandingQuorum + hot/warm/cold tiers + grow/shrink/decay/split-merge + reinforce (Routing §2.4); the proven-competence admission gate + receipts (Routing §2.5). Adds [NEW]: the explicit TopicSet extraction + relevance filter; the order-invariant set-key k and the semantic topic-vector v as the two-mode StandingQuorum key; the route_via_cache hot path; the exact-then-semantic-then-cold lookup with exact-key backfill after a near-hit.
8. Open decisions [OPEN]
- Taxonomy granularity — the single biggest quality knob. Calibrate
@v<n>depth +TAU_SNAP/TAU_REL/K_MAXon the dev's own query stream (E2) before trusting hit-rates. Too coarse = wrong quorum reused; too fine = cache never warms. - Set-key vs pure-semantic — is the exact set-key worth it over semantic-only ANN? (It buys determinism + zero-drift for identical intents + a cheap first probe; cost is taxonomy dependence.) Measure hit-rate + mis-hit-rate both ways.
- Weighted vs unweighted sets — should
kencode topic weights/dominant-topic, or is membership enough? (MVP: membership only; weights live inv.) - Membrane-scoped keys — an iCorp's internal cache is separate from the public one (a set-key can resolve to different quorums inside vs outside a membrane, §2.3). Namespace
kby membrane. - Per-node vs shared cache — node-local hot cache (zero-hop) vs the registry's global standing-quorum index (§2.4.4): consistency + staleness policy.