HiNet.

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

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]


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 / evictttl + 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]

  1. Taxonomy granularity — the single biggest quality knob. Calibrate @v<n> depth + TAU_SNAP/TAU_REL/K_MAX on the dev's own query stream (E2) before trusting hit-rates. Too coarse = wrong quorum reused; too fine = cache never warms.
  2. 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.
  3. Weighted vs unweighted sets — should k encode topic weights/dominant-topic, or is membership enough? (MVP: membership only; weights live in v.)
  4. 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 k by membrane.
  5. 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.

Next: Quorumz map integration →  ·  All documentation →