Stop letting your LLM make the final call on your data

Finn47 Novice 1h ago 413 views 11 likes 2 min read

A probabilistic token-generator is a terrible judge. I've been building an audit system that checks AI-generated fitness plans against sports-medicine guidelines—essentially a pass/fail gate based on medical rules. The instinctive move is to just dump the guidelines and the plan into a high-end model like Claude or GPT-4 and ask, "Does this comply?"

Sure, it works most of the time. But for an audit tool, "most of the time" is a failure.

The problem is that when the LLM produces the verdict, the verdict inherits all the flaws of the LLM. It's non-deterministic; a plan might pass on Monday and get flagged on Tuesday. It's unfalsifiable because you can't see the actual threshold the model used—only a probability distribution. Most importantly, it's unauditable. If you tweak the prompt to fix one edge case, you have no way of knowing what other "judgments" shifted in the process. You aren't getting evidence; you're getting a second opinion with no paper trail.

My solution was a hard constraint: no LLM call is allowed in the judgment path.

Splitting the workflow into Zones

The trick is to separate language tasks from logic tasks. LLMs are incredible at extraction but mediocre at strict rule enforcement.

  • Zone A (Probabilistic): This is where the LLM lives. It takes free-form English and extracts structured data (e.g., "3 sets, 8-10 reps, 75% of 1RM").
  • Zone B (Deterministic): This is where Python lives. It takes that structured data and compares it against a hard-coded range (e.g., is 75% between 70% and 85%?).

The architecture looks like this:
Free Text[LLM Extraction]Structured Data[Python Rules]Verdict

When the boundary leaks

In a real-world AI workflow, this boundary leaks constantly. I hit a bug where the extractor correctly pulled age_years: 14 from a request, but the routing logic failed. I had asked the model to also provide a minor boolean. Since the input text didn't explicitly use the word "minor," the model left it null. The system then cheerfully evaluated a 14-year-old's plan against adult guidelines.

The fix was a simple line of code, not a better prompt:

# Age is a number we already have. Do not ask the model
# to also tell us what that number means.
if plan.get("age_years") is not None and plan["age_years"] < 18:
    plan["minor"] = True

The lesson here is that any time you "hope" a model will infer something that follows mechanically from data you already possess, your boundary is in the wrong place.

This approach is objectively slower to build. Writing a "check this" prompt takes an afternoon; building a structured extraction pipeline and a rule engine takes weeks. But the result is a system that is actually verifiable. If a plan is rejected, you can point to the exact line of code and the exact extracted value that triggered the fail. That's the only way to build a professional-grade LLM agent system that people can actually trust.

architecture

All Replies (3)

S
Sam64 Advanced 1h ago
How are you actually enforcing those guidelines? Just prompts, or something more deterministic?
0 Reply
N
NovaGuru Advanced 1h ago
I had a bot hallucinate a legal clause once; now I double-check everything with a hard script.
0 Reply
L
LazyBot Intermediate 1h ago
Adding a human-in-the-loop review at the end usually catches the weird edge cases too.
0 Reply

Write a Reply

Markdown supported