Use models for ambiguity and code for consequence
Place probabilistic reasoning behind a typed boundary, then let deterministic code decide what the system stores, ranks, and shows.
Language models are good at interpreting evidence whose meaning is not fully captured by rules. They are much less suitable for deciding whether a record should be persisted, which user may see it, or whether an alert should fire.
That distinction gives an applied-AI system a useful dividing line: the model proposes meaning, while ordinary code owns consequence. The result is easier to test because probabilistic inference ends at a typed interface.
Define a proposal, not an action
This section turns model output into a candidate that has no authority on its own. A candidate describes an interpretation and the evidence behind it; it cannot write data or trigger a user-facing effect.
The boundary can be expressed with a small proposal contract:
export type EvidenceFact = {
key: string;
value: string | number | boolean;
observedAt: string;
};
export type InsightCandidate = {
kind: 'change' | 'pattern' | 'anomaly';
summary: string;
confidence: number;
evidenceKeys: string[];
};
export interface InsightEngine {
propose(facts: EvidenceFact[]): Promise<InsightCandidate[]>;
}
The verb propose matters. It tells callers that the result still requires validation and policy. The model receives facts rather than database rows so it cannot infer behavior from accidental schema details.
The contract also keeps evidence references separate from prose. A model can write a plausible sentence, but downstream code can reject that sentence when its evidenceKeys do not exist in the supplied input.
Validate before applying policy
This section creates the deterministic boundary around the model call. Parsing checks shape, evidence validation checks support, and policy decides whether a supported proposal has an effect.
Validation should reject unknown fields, unbounded text, unsupported categories, out-of-range confidence, and evidence keys that were not supplied. A valid object is still only a well-formed proposal; it is not proof that the interpretation is correct.
That distinction matters in incident analysis. Schema failures indicate a broken integration contract. Unsupported evidence indicates a grounding failure. A well-formed but unhelpful interpretation indicates a model-quality problem. Combining them into one “generation failed” metric removes the information needed to improve the system.
Make consequence a deterministic function
This section applies product policy without another model call. The same candidate and context should produce the same decision during a request, a test, or a historical replay.
Useful outcomes are more expressive than pass or fail. A supported candidate may be surfaced, retained only for continuity, or discarded with a reason. Ranking, visibility limits, permissions, and side effects are then applied under a named policy version.
Thresholds and priorities are product choices, not universal constants. They should be justified with evaluation evidence and reviewed when the model or evidence distribution changes. They should still be applied by code: the model interprets ambiguity but does not quietly redefine the product’s tolerance for error.
Test the seam, not the model’s eloquence
This section tests the behavior the application actually depends on. A fake inference boundary makes invalid output, missing evidence, and policy branches reproducible without spending tokens or depending on model variance.
Test that extra fields are rejected, unknown evidence cannot cross the boundary, and every policy outcome records a reason. Replay the same proposal under the same policy and require an identical decision. Then evaluate interpretation quality separately on an offline dataset with ambiguous and adversarial examples.
This division keeps application safety in ordinary unit tests while model quality uses probabilistic evaluation. A fluent model response cannot make a storage or visibility test flaky because those consequences no longer belong to the model.
The most dependable model boundary is narrow: evidence goes in and proposals come out. Storage, visibility, ordering, permissions, and side effects remain code paths that can be replayed and proven.