AXIOMO
An offline answer engine that looks facts up and refuses to guess — no model, no network, no invention.
- p95 fuzzy lookup at 1M surface forms
- 0.75 ms
- wrong answers avoided by refusing to guess
- 22.5%
- golden queries, zero false hits allowed
- 188
The problem
Asking a factual question means choosing between a language model that can fabricate a confident wrong answer and a search engine that needs connectivity and returns pages instead of values. Neither puts the source and the as-of date next to the number. AXIOMO's governing rule is that the engine retrieves and never invents: below the confidence threshold, "I don't have that" beats a guess, and a false hit is the only fatal bug class.
What I built
A pure TypeScript engine with zero runtime dependencies and no framework imports, so the same code runs byte-identically in Node for the test harness and inside the React Native app. The pipeline normalises the query, classifies it by rule into one of six intents, links the entity, resolves the fact, and passes it through a confidence gate.
Every property carries a type signature in the data pack — domain, range, unit, volatility, and whether it holds one value or many. The build fails on any fact that violates its signature, and the resolver refuses structurally rather than numerically: a country has no height, so "how tall is France" is rejected on domain mismatch. The question form overrides the property's default type too, so "when is the capital of France" refuses rather than answering a place name to a time question.
Entity linking uses a byte-encoded trie with spaces stripped from the keys, so "newyork" is an exact walk for "new york" — the split-and-merge class that edit distance handles badly. Fuzzy search is a bounded edit-distance traversal of the same structure, with the budget scaled to the length of the span as the user typed it.
The hard part
Measuring why fuzzy matches cannot answer
The instinct is to accept a close match when it is unambiguous. Measurement killed that. With fuzzy matching allowed to produce answers, a correctly spelled name that simply is not in the database resolves to a single confident wrong neighbour 0.47% of the time at the current 809 surface forms — and 22.5% of the time at a million, because the edit-distance ball around real names gets denser as the corpus grows. Two live examples from the existing data: "capital of Prussia" returned Moscow, and "capital of Siberia" returned Monrovia.
No margin, popularity or ratio gate tested brought that below 2.4%. Since answering an out-of-database name at all is the one fatal bug class, the fix had to be structural rather than a threshold adjustment: the answer path was removed. A match now returns success only on an exact surface-form hit at score 1.0. Anything merely within the edit budget is demoted to a pre-verified "did you mean?" chip that the user must tap. Widening the fuzzy budget then became safe precisely because a fuzzy result can no longer become an answer.
Outcome
- Fuzzy lookup at 0.75ms p95 across a million surface forms, against 35ms for the linear edit-distance scan it replaced at a tenth of that size.
- A 188-query golden set enforced in CI as the release contract: zero tolerated false hits, 100% of must-miss queries missing, and a p99 engine latency under 30ms.
- Continuous integration that typechecks both packages, rebuilds the binary data pack and fails if the committed artifact differs by a byte, then exports a real production iOS bundle.
- A workspace-symlink integrity check, added because a missing link let the tests pass while every app build broke.
Want something like this built? I take on a small number of projects at a time.