Evaluating Generative AI: From Vague Impressions to Reproducible Tests

Create a small golden dataset and evaluation harness for groundedness, citations, retrieval, task success, regression, and uncertainty.

evaluation and release gates This tutorial treats the concern as an application contract rather than a model feature. The contract states what enters the system, what may change, what evidence is recorded, and what happens when the expected path fails.

A dependable implementation separates facts, interpretations, permissions, and operations. A model response is an interpretation. A retrieved record is evidence. A policy decision is an authorization event. A metric is an observation about a test set. Keeping those categories distinct makes reviews and debugging possible.

The smallest useful experiment is deterministic. It uses a tiny in-memory dataset, a standard-library function, and an assertion about the intended behavior. It does not claim production performance. Its purpose is to make one mechanism visible so that a learner can change an input, rerun the test, and inspect the consequence.

Learning objectives

You will build a local demonstration, inspect its output, and translate its control into a real application. You will also identify a failure mode, define a boundary, and write an operational rule that a person or automated test can verify.

Prerequisites

You should know basic Python and understand the earlier articles on tool use, retrieval, and bounded agents. No paid API, cloud account, or model download is required. The example directory is content/ship-to-production/evaluating-generative-ai.examples.

Why this concept matters

AI systems fail at their boundaries. Data can be stale, a citation can be missing, a tool argument can be unsafe, an evaluator can reward style instead of correctness, or a dependency can time out. Adding a larger model does not automatically repair those failures. The application needs a clear contract and evidence about whether it is being followed.

Start by naming the decision the system must support. For memory, it may be whether a prior fact is still relevant. For evaluation, it may be whether a release preserves grounded answers. For security, it may be whether untrusted text can influence a privileged action. For production operations, it may be whether a request can complete within its budget. The decision determines the data and control.

Mental model

Use a pipeline of input, policy, computation, validation, evidence, and response. Each stage has an owner. Input is not trusted merely because it came from an internal screen. A schema checks structure but not permission. A passing metric is not proof outside its test distribution. A trace is useful only if it excludes secrets and identifies the versions involved.

When a result is wrong, classify the cause before editing a prompt. Did the data omit the needed fact? Did retrieval select the wrong record? Did a policy allow an unsafe action? Did output validation accept an incomplete object? Did the dependency fail? Different causes require different fixes and different tests.

Toy problem / implementation

The local program implements evaluation and release gates with a small list of records. It applies one explicit control before returning a summary. The code is deliberately plain so the boundary is visible. The test names the expected result instead of checking only that the program exits successfully.

Read the data definition first, then the control function, then the test. This order mirrors maintainable production work: establish the contract, implement the narrow behavior, and make the failure observable. If you later use a framework, preserve this shape inside the framework rather than hiding the important decision in a callback.

Run it yourself

Run the following command from the repository root:

bash content/ship-to-production/evaluating-generative-ai.examples/verify.sh

The command runs a unit test and prints a small summary. It uses Python 3.11 and the standard library on the tested environment. It makes no network requests. The output is an educational fixture, not a benchmark or a claim about model quality.

For reproducibility, record the input, source version, configuration, and result together. Model-based experiments also need the model identifier, sampling settings, prompt version, retrieval snapshot, and evaluation-set version. Otherwise a later change cannot be attributed to the right cause.

evaluating-generative-ai controlinputcontrolevidenceAccessible diagram: each stage has an explicit owner and observable outcome.
The control boundary and evidence path.

The conceptual visual shows the input, the controlled stage, and the inspectable result. A good diagram identifies the trust boundary rather than adding decorative system boxes. Readers should be able to point to where authorization, retention, evaluation, or timeout behavior is applied.

Real-world application

In a real application, convert the toy rule into a written contract. Define the owner of every field, its purpose, permitted values, retention period, and deletion behavior. If a request affects a person, account, payment, permission, or production service, include an approval boundary unless a documented risk assessment supports automation.

Keep untrusted content separate from system policy. A document, log line, user message, or retrieved passage may contain instruction-like text. It remains data. The application should decide which data can be quoted, summarized, retrieved, or passed to a tool. It should never let the content silently rewrite authorization.

evaluating-generative-ai lifecycleinputcontrolevidenceAccessible diagram: each stage has an explicit owner and observable outcome.
Lifecycle stages should have explicit owners.

The application visual maps the lifecycle from request to control to evidence. It is a design aid, not a claim that every deployment needs the same components. Add queues, caches, model routers, or extra databases only when a measured constraint justifies them.

A production owner also needs a boring operational answer. What happens when data is missing? What happens when the model is unavailable? What happens when a user asks for deletion? Who reviews a suspicious trace? How is a changed prompt or index versioned? If these answers are absent, the feature is not ready merely because its happy path works.

evaluating-generative-ai evidenceinputcontrolevidenceAccessible diagram: each stage has an explicit owner and observable outcome.
Operational evidence makes failures explainable.

The evidence visual emphasizes that a final sentence is not enough. Store the smallest useful record: identifiers, versions, timestamps, policy outcome, and result status. Redact secrets and personal data. Evidence should make a failure explainable without becoming a second data-leak surface.

Failure modes and debugging

A common failure is confusing valid shape with valid meaning. A parsed object may still contain an unauthorized target. A relevant memory may be obsolete. A citation may exist but not support the sentence. A security detector may catch one phrase and miss a paraphrase. A fast response may still violate policy.

Debug with a sanitized replay fixture. Compare inputs, configuration, selected records, policy decisions, timings, and outputs. Do not debug only from the final natural-language response. For privacy-sensitive systems, redaction belongs in the recorder and should have its own tests.

Retries need classification. A transient, idempotent operation may be retried within a deadline. An invalid argument, policy denial, deletion request, or irreversible mutation should not be retried automatically. A failure response should state what is known and what remains unknown.

Security, privacy, cost, and operations

Collect only what the declared purpose needs. Apply authorization before retrieval. Keep secrets outside model-visible context. Use least privilege, timeouts, rate limits, and bounded budgets. Redact traces and set retention. Measure model latency, tool latency, storage, token use, and human interventions separately so an optimization does not hide a new risk.

Strengths

The approach is inspectable, portable, and inexpensive to run in CI. It gives a learner a concrete control before introducing a framework abstraction. It also supports a useful conversation between engineering, editorial, security, and operations because each claim can be tied to a test, source, or owner.

Limitations and when not to use this approach

A toy program cannot establish production performance, legal compliance, or complete security. Standards and papers provide evidence and guidance, not a guarantee for a particular deployment. Current provider behavior, pricing, and regulatory obligations must be checked for the actual environment.

Do not retain state that is needed only for one request. Do not add an evaluator merely to produce another number. Do not call a guardrail effective because it catches one fixture. Do not ship an AI feature without an owner for incidents, updates, access reviews, and deletion requests.

Exercises

Add a boundary input and write the expected result before changing the code. Add timestamps and tenant identifiers, then test expiration and isolation. Add an incomplete dependency response and assert an honest fallback. Write one normal, one boundary, and one adversarial case, and identify which checks are deterministic, model-dependent, or human-reviewed.

Final notes

Applied AI becomes credible when invisible decisions are visible. Name the data, the boundary, the evidence, the failure response, and the owner. Then measure whether the system helps users under the constraints that matter.

Next in this path

Continue to Securing AI Applications to extend this control into the next production concern.

+Additional practice should be explicit about assumptions. Create a fixture for the normal path, a boundary case, and an adversarial or incomplete case. Store expected outcomes beside the fixture and review them whenever the product changes. A test that cannot explain why it failed is a signal that the contract is underspecified.

For a team handoff, write a short runbook identifying the owner, dashboard, first safe mitigation, escalation path, and rollback or deletion action. This is how a technically correct prototype becomes a service another person can operate. Use version identifiers in every report: prompts, model adapters, indexes, policies, schemas, and code can each change behavior.

State what the experiment cannot tell you. A tiny dataset cannot measure broad user quality. A local detector cannot prove resistance to every attack. A latency fixture cannot predict a provider’s tail latency. Honest limits make the next measurement easier to design and keep the article useful after surrounding tools change.

+A useful review asks four questions. First, is the behavior correct for the stated use case? Second, can a learner reproduce the result without hidden services? Third, can an operator tell what happened from the evidence that is retained? Fourth, can the owner change or remove the behavior safely? These questions work across memory, evaluation, security, and production architecture because they focus on responsibility rather than a particular framework.

Keep the interface narrow. A function that accepts an identifier should not also accept an arbitrary URL. A memory lookup should return source and age, not only a persuasive sentence. An evaluator should expose individual case results instead of reporting only an average. A security control should reject or quarantine a case with a reason. An API should return a stable error shape when a dependency times out. Narrow interfaces make the next test obvious.

The examples in this path intentionally favor boring data structures. That is a feature for teaching. A framework can provide retries, tracing, memory, or structured outputs, but it can also hide who owns a decision. Once the local mechanism is understood, replace one piece at a time and keep the same tests around the boundary. If a framework changes the behavior, the test should make that change visible.

Before release, run the example from a clean environment and inspect the generated output. Check that paths resolve, visual descriptions are meaningful, and dates identify when research and code were last verified. Then record an update path. Technical content becomes less trustworthy when it silently ages, especially where model behavior, security guidance, service limits, or operational costs can change.

This approach also improves communication with non-specialists. A product owner can review the purpose and retention rule. A security reviewer can review the trust boundary and permissions. An operator can review the timeout and fallback. A learner can run the fixture and understand the mechanism. No one needs to accept a vague claim that the system is intelligent, safe, or production-ready without evidence.

Sources

The research dossier is available at research/dossiers/evaluating-generative-ai.md. Code tested on 2026-08-05 with Python 3.11 and the standard library.

Discover more from Applied AI Tutorials

Subscribe now to keep reading and get access to the full archive.

Continue reading