Using the Knowledge Base
A practical guide to the case-corpus knowledge base: how staff feed resolved escalations into it, how Tier 2 uses it automatically, and how to query or extend it. For the design rationale and data-model decisions, see the V1.1 Knowledge Base PRD — this document is the "how," that one is the "why."
1. Overview
The knowledge base is a small, structured corpus of resolved escalations. Every time a staff engineer resolves an escalation and opts it in, a row goes into kb_entries: the bugcheck/exception code, faulting module, failure bucket, a short root-cause tag, and the engineer's own resolution note — never a transcript. From then on, every Tier 2 run automatically checks that corpus for structurally similar prior cases and, if it finds any, includes them as extra context for the model.
There is no separate "turn the KB on" step and nothing to configure to start using it — it's live in both directions today. The only thing that changes its usefulness over time is corpus size: right after launch, few (if any) escalations have been opted in yet, so most Tier 2 runs will see no prior cases. That's expected — see §7.
2. For Staff: Opting a Resolution In
When you resolve an escalation in the staff queue, the resolve form has a checkbox: "Feed this resolution into the knowledge base." Checking it is the only action required — there's no separate review or approval step.
| Field | What it's for |
|---|---|
| Root cause tag | A short, free-text label (e.g. driver-bug, hardware-fault, corrupt-heap). This is what shows up alongside your resolution note in a future Tier 2's grounding context and in the queue UI — keep it short and consistent with how you'd tag similar cases, since there's no fixed taxonomy to pick from. |
| Resolution | Your normal resolution write-up. This exact text becomes the KB entry's resolution_note — write it as something useful to a future engineer (or model) seeing a similar case cold, not just as a closing note for this one customer. |
| Feed into knowledge base (checkbox) | Opts this resolution's structured signals and your note into the shared corpus. It does not share the customer's dump, transcript, or any other case detail — see §3. |
Once resolved, your root cause tag shows up next to the resolution in the escalation detail view and in the copy-to-clipboard markdown export — it's a real column now, not text buried inside the resolution.
3. What Gets Stored — and What Never Does
kb_entries column | Source |
|---|---|
bugcheck_code / exception_code | Parsed by Tier 1 (transcript_signals.parse_signals), recovered from the stored finding at resolve time — never re-parsed from a transcript |
faulting_module | Tier 2's conclusion when Tier 2 ran, else Tier 1's — normalized (lowercased, extension stripped) |
failure_bucket_id | Tier 1's !analyze -v bucket ID |
dump_type | user_mode or kernel_mode, from the dump itself |
root_cause_tag, resolution_note | Exactly what the engineer typed on the resolve form |
source_escalation_id | Traceability back to the escalation — nullable, and SET NULL rather than cascaded if that escalation is ever pruned |
4. How Retrieval Works
Every Tier 2 run calls find_similar_cases() (triage/kb_lookup.py) automatically — nothing to trigger, nothing to configure per job. The match logic, in order:
- Look for entries with the same dump type, the same bugcheck/exception code, and the same normalized faulting module as this job.
- If nothing matches on code+module, fall back to entries sharing the same failure bucket ID alone.
- If neither matches, or the lookup itself fails for any reason (a DB hiccup, say), Tier 2 proceeds exactly as it would with an empty corpus — no error, no retry, no delay.
Matches (up to 5, most recent first) are rendered as plain-text grounding — module, root cause tag, and resolution note — and handed to the model alongside the transcript, explicitly framed as context, not authority: the model is told to verify against this job's own evidence rather than assume the same root cause applies. This is deliberate: a superficially similar bugcheck/module pair can still have an unrelated cause, and the prompt framing exists so the model doesn't anchor on a misleading prior case.
5. For Engineers: Code Map & Querying Directly
| File | What's there |
|---|---|
app/models/kb_entry.py | The KBEntry ORM model / kb_entries table definition |
app/routers/escalations_staff.py | resolve_escalation — the write path; builds a KBEntry from the job's stored Tier 1/2 findings on kb_opt_in |
app/triage/kb_lookup.py | find_similar_cases() (the query) and format_kb_context() (the prompt rendering) — the read path |
app/orchestrator/tier2_runner.py | Calls both of the above and passes the result through structured_report's context parameter |
alembic/versions/0013_kb_entries.py | The schema migration, if you need the exact column types/indexes |
Querying the corpus directly
For ad-hoc exploration (e.g. sanity-checking corpus size or coverage before relying on it for anything), query kb_entries like any other table. A couple of starting points:
# How many opted-in cases exist so far, by dump type
SELECT dump_type, count(*) FROM kb_entries GROUP BY dump_type;
# Every case tagged for a given root cause
SELECT root_cause_tag, faulting_module, resolution_note
FROM kb_entries
WHERE root_cause_tag = 'driver-bug'
ORDER BY created_at DESC;
From Python, use find_similar_cases() directly rather than hand-rolling the query — it already encodes the code+module / failure-bucket fallback logic described in §4:
from app.triage.kb_lookup import find_similar_cases, format_kb_context
matches = await find_similar_cases(
session,
dump_type=dump_type,
bugcheck_code=parsed.bugcheck_code,
exception_code=parsed.exception_code,
faulting_module=normalize_module(parsed.faulting_module),
failure_bucket_id=parsed.failure_bucket_id,
)
print(format_kb_context(matches))
6. Retention & Lifecycle
kb_entries rows are not touched by the dump/transcript retention sweep — there's no foreign key from a dump or transcript into the KB, only a nullable, SET NULL-on-delete pointer back to the source escalation. A KB entry outlives the dump it came from by design: once an engineer opts a resolution in, it stays part of the corpus regardless of what later happens to the original dump, transcript, or even the escalation record itself.
7. Current Limitations
- Small corpus. The KB only contains what's been opted in since Phase 2 shipped — early on, expect most Tier 2 runs to find no prior cases. This is expected, not a bug; see the PRD's rollout phasing.
- No embeddings/semantic matching. Two cases with the same underlying cause but a different bugcheck code or module naming won't match. Revisit only if real usage shows structured matching falling short — not before there's data to justify it.
- No browse UI. The corpus is consumed by Tier 2's prompt construction only; querying it directly (§5) is currently the only way for a human to look at it.
- No correction/retraction mechanism. If a resolution later turns out to be wrong, there's no built-in way to fix or remove its KB entry yet — flagged as a follow-up in the PRD once the corpus is large enough for this to matter.