Architecting Hallucination-Impossible AI Tools
AI tools hallucinate. We’ve all seen it. The chatbot that confidently invents a feature that doesn’t exist. The agent that reaches for a tool the model just made up. The LLM-routed support system that politely fabricates an answer with full confidence and a citation that’s also fabricated.
The reflex fix in 2026 is more guardrails. Validate the output, retry on failure, route uncertain cases to humans, fine-tune the model to hallucinate less. Layer enough mitigation on top and the failure rate drops to something tolerable.
That whole approach is downstream of the actual problem.
The actual problem is architectural. You put the model in a position where hallucination was an available output. If hallucination was on the menu, the model will eventually order it, and no amount of validation downstream will catch every case. The fix isn’t to validate harder. The fix is to design a system where the model cannot hallucinate, because the architecture doesn’t give it a path to.
That’s what I want to talk about.
The architectural move
The pattern I keep coming back to in production AI tooling is this: separate what’s deterministic from what’s inferential, and give the LLM only the closed-set selection problem.
Most “AI agent” architectures in 2026 do the opposite. They put the LLM at the decision-making layer. The model figures out what to do, picks tools, generates inputs, interprets outputs, wraps the result in natural language. The work the model is doing is open-ended at every step. Open-ended generation is exactly where hallucination lives.
The closed-set move flips this. The deterministic work runs in code that you wrote: the actual computation, the lookup against a known database, the diagnostic check, the API call to a real service. The model’s job is reduced to one thing: take this messy unstructured input and pick which of the N known options applies.
N is finite. N is enumerable. N is something you typed out. The model can pick “none” and escalate. The model can pick “ambiguous” and surface options for a human or a deterministic resolver. The one thing the model cannot do is invent option N+1, because option N+1 is not on the menu.
Hallucination, in this architecture, is structurally impossible. Not “very unlikely.” Impossible, in the same sense that you can’t withdraw money from an account that doesn’t exist if the system has no row to draw from.
What “closed-set selection” actually looks like
Imagine a diagnostic tool for some technical operations team. Tickets come in. Each ticket is a paragraph or two of free-form text from a customer or a colleague describing what’s broken. The job of the tool is to figure out which of the team’s diagnostic checks should run.
There are, let’s say, 14 diagnostic checks. Each one is deterministic code: it runs a real query, hits a real endpoint, executes a real test, returns a real signal. They’ve been written, tested, and validated. They produce reliable outputs.
The naive design is: give the LLM the ticket, give it tool-use access to all 14 diagnostics plus the ability to query any database it wants, and let it figure things out. This works most of the time. It also occasionally invokes a check with the wrong parameters, hallucinates a database table, paraphrases a real result into something subtly wrong, or fabricates a recommendation that sounds plausible. When it goes wrong, it goes wrong confidently, and the customer-facing output is worse than no automation at all.
The closed-set design is different. The LLM gets the ticket text and a description of the 14 diagnostics: what each one does, what kinds of tickets it’s appropriate for. The model’s job is exactly one thing: return one of fifteen tokens. Either one of the 14 check IDs, or a fifteenth that means “none of these match, escalate.” That’s it.
Once the model has selected, the actual work happens in code I wrote. The diagnostic runs. It produces a deterministic output. The output gets formatted by deterministic code. The result lands in the ticket without ever passing back through the LLM.
The model touched the system at exactly one point: the selection step. The selection step’s failure mode is bounded. The worst case is that the model picks the wrong diagnostic from the closed set, in which case the wrong-but-real check runs and produces a wrong-but-real signal that’s still useful. The model cannot fabricate a check, fabricate a result, or invent a recommendation. None of those outputs are reachable from where the model sits.
The 20-minute demo
I shipped a feature in 20 minutes recently that senior engineering leadership had told me was impossible at that timeline. They expected partial work. Maybe a sketch, maybe a stub, maybe a proof of concept. What landed was the feature, working, integrated, tested.
The reason isn’t that I’m faster than the team thought. It’s that the architecture was right.
When the system is built around closed-set selection, adding a new diagnostic is a matter of writing one thing: the deterministic code for the new check, plus a one-line registration in the closed set. The model doesn’t need retraining. The prompt doesn’t need re-tuning. There’s no “make sure the model knows about the new tool” step where you stare at the prompt and wonder if you’ve worded the new option in a way that’s distinguishable from option 7. The closed set is data; the model reads it at runtime; the new option just shows up.
20 minutes isn’t a benchmark. It’s the bound on how long it takes to write the new deterministic check, which is the only actual work. Everything else is structural. The architecture had already absorbed the cost of “what if we want to add more options later” and made that cost negligible.
This is what I mean when I say the architectural move pays for itself across waves of tooling. The model gets better. Fine. The closed-set selection gets more reliable. The architectural property is preserved by structure, not by accuracy. No path to hallucination. The system survives model upgrades, prompt revisions, even framework migrations. What it doesn’t survive is being rebuilt on a different architecture that re-introduces hallucination, but you wouldn’t do that, because by then you’ve seen what the trustworthy version feels like in production.
Why the industry hasn’t fully caught this
The dominant 2026 patterns in production AI tooling fall into three buckets, and all three accept hallucination as a force of nature.
Validate-and-retry. The LLM does whatever it does, and a downstream layer checks if the output is plausible. Sometimes structurally (does the JSON parse, does the SQL execute), sometimes semantically (does another LLM call agree with this one). If the output fails validation, retry, and route to humans on N consecutive failures. This is hallucination-mitigation by exhaustion. It works most of the time and fails confidently the rest of the time, which is the exact failure mode that erodes trust in production.
Fine-tune to reduce. Treat hallucination as a probabilistic property of the model. Train it on more examples of correct behavior, hope the failure rate drops. Better models do hallucinate less in absolute terms, but the architectural property is unchanged. Nothing structurally prevents fabrication. The model is still in a position where fabrication is reachable; you’re just betting it won’t reach.
Tool-use frameworks. Give the LLM tools it can call. The LLM picks the tool and generates the parameters. The framework executes the tool and feeds the result back. This almost gets to closed-set selection, except the parameters are still LLM-generated, and the choice of which tool to expose at any given moment is often LLM-mediated too. Hallucination just moved one layer down. The model can still invent a tool input that doesn’t make sense, fabricate a context that doesn’t apply, or chain tools in ways the system designer didn’t anticipate.
The closed-set move asks a different question than any of these. Not “how do we catch the model when it goes wrong” but “how do we make it impossible for the model to be wrong?” Shrink the inferential surface. The smaller the inferential surface, the smaller the hallucination surface, and at some point the inferential surface gets small enough that you can enumerate every possible model output by hand and confirm that none of them are wrong.
That’s the goal. That’s the architecture.
Where this fits, where it doesn’t
Closed-set selection is the right shape for a wider class of production AI work than most teams use it for:
- Diagnostics and routing. Pick the right check, the right escalation path, the right team to involve.
- Classification and intent. Pick which of N intents this user message expresses, where N is finite and well-defined.
- Triage. Pick the priority level, the SLA bucket, the assignee from a known team.
- Compliance gates. Pick whether this transaction matches a known-violating pattern from a closed taxonomy.
- Retrieval over closed corpora. Pick which of N indexed documents is relevant. The retrieval-augmented-generation step that follows is a separate architectural decision; the retrieval part is closed-set.
- Function dispatch. Pick which of N registered handlers this incoming event should hit.
It’s not the right shape for:
- Open-ended generation. Long-form writing, brainstorming, creative work, anything where the desired output doesn’t exist before the model produces it. Closed-set doesn’t apply because the set is the open universe of possible writings.
- Novel synthesis across unfamiliar domains. When the value of the LLM is connecting things you haven’t pre-enumerated.
- Conversational interfaces where the conversation is the product. Chatbot UX where the model’s natural-language response is itself the deliverable.
These are real categories. They benefit from LLMs. They also have to live with hallucination as a real failure mode, because the architecture doesn’t admit a closed-set-shaped reduction. That’s fine. Pick the right tool for the right job. The point of this article isn’t that closed-set selection is universal. It’s that it’s underused, and a lot of production AI tools that are currently lived with as “good but unreliable” could become “good and trustworthy” by switching architectures.
The methodological move
The question to ask up front, on any production AI system you’re designing:
What does the LLM need to be inferential about, and what’s deterministic?
Most projects answer this question implicitly, by reaching for an agent framework and letting the LLM be inferential about everything that isn’t an explicit guardrail. Flip the question. Make the LLM inferential about as little as possible. Move work into deterministic code wherever you can. If the inferential surface ends up being “pick one of these 14 known options,” you’ve reached closed-set selection. If it ends up being “generate this paragraph in a particular voice,” you’re in an open-ended-generation problem and you need different tools. That’s fine, but name it as that, and don’t pretend the validate-and-retry layer makes it trustworthy.
The architectural property to chase is no reachable hallucination, not low hallucination rate. They sound similar. They’re philosophically different. Low rate is a probability claim that the system doesn’t make a verifiable promise about. No reachable hallucination is a structural claim that you can audit by inspecting the code paths the model output flows through. Production teams trust the second one. They eventually stop trusting the first.
This is what makes closed-set selection the right architectural call across a multi-year arc, not just a tactical fix for the current model generation. Models will get better. Some hallucination types will get rarer. New hallucination types will appear. The architecture survives all of that, because the property it provides isn’t probabilistic. It’s structural.
Closing
Hallucination isn’t a tax to pay or a probability to reduce. It’s a design failure to engineer out. The closed-set selection move is one way to do that: give the LLM the smallest possible inferential job, embed it in deterministic infrastructure, and check whether the work the model is doing can be reduced to “pick from this finite list.”
If it can, you have a path to a system that is structurally trustworthy in a way that fine-tuning and guardrails can’t deliver.
If it can’t, you might be using an LLM where you don’t need one. Or you might be in genuinely open-ended-generation territory, in which case the architectural conversation is different and worth having on its own terms.
Either way, the question worth asking before you reach for an agent framework: what does the model need to actually be smart about? If the answer is “less than you think,” congratulations, you’re closer to a hallucination-impossible architecture than you knew.
Carlos Cortorreal is a software engineer working at the intersection of AI tooling, methodology, and production systems. More writing at writing.cortorreal.fun.