Ship production AI

AI Engineering Bootcamp

maven.com/tailabs/ai-engineering-bootcamp

Lightning Lesson · 25 min

The Agent Harness

Why prompts fail in production, and what to build instead

Same model. Same prompt. Different outcome. The difference is the system around the call.

Dr. Aki Wijesundara

Dr. Aki Wijesundara

Co-Founder, TAI Labs

Courses we offer

Two paths from TAI Labs. Each card shows exactly what we cover.

The demo works. Production doesn't.

In testing

Same model. Same prompt. Every check you wrote passes.

Week one

Same model. Same prompt. The first real user breaks it.

If you have shipped an agent that worked in testing and failed in production, we will map that failure to a layer later.

What actually changed

Not the model. Not the prompt.

Reality has

  • Inputs you did not imagine
  • Users who change their mind mid-task
  • Systems that time out
  • Consequences when the agent is wrong

A prompt cannot handle these

  • No memory
  • No control flow
  • No ability to stop itself
  • A request, made once, with no enforcement

The harness

The system around the model call.

01

Workflow control

What runs, in what order, and when it stops

02

Context

What the model can see at this step

03

Permissions

What it is allowed to touch

04

Evaluation

How you know the output is good

05

State

What survives between steps and between runs

Almost every agent failure lives in one of these five, not in the prompt.

When does this stop?

An agent that cannot stop itself is not autonomous, it is unsupervised. The common failure is not an infinite loop. It is the agent that declares success on step two because nothing was checking.

What does the model see right now?

Most teams treat context as an append-only log. Treat it as a query: “What does this step need to know?” every single time.

What can it touch?

Classic failure: a tool kept write access because it was convenient in development and nobody removed it. Permissions failures are rare and expensive. Everything else is common and cheap.

How do I know this output is good?

Three tiers, cheapest first:

01 · Schema

Does it parse? Are required fields present? Are types right?

02 · Rules

Values in range? Referenced IDs exist? Does the arithmetic hold?

03 · Judgement

LLM or human check, only for what the first two cannot catch.

The dangerous failure is the plausible wrong answer: it passes casual review and reaches the user. Schema and rules are free and catch most of it. Reach for judgement last: slow, costly, and itself unreliable.

What survives?

State is what makes attempt two different from attempt one. Without it, a retry is the same failing call again, at three times the cost. When an agent asks the same question twice, that is usually a state problem in your system, not a memory problem in the model.

How they interact

The layers are not independent. This is where most designs go wrong.

Four out of five is not eighty percent of a harness. The missing layer is usually where the incident comes from.

Where everyone starts

def agent(user_input):
    result = model(user_input)
    return result

Failure modes hiding in one line:

Malformed JSONEvaluation
TimeoutsWorkflow control
Invented fieldsEvaluation
Forgets two steps agoState
Forbidden tool callPermissions

Five failures. Five layers. That is not a coincidence.

Move the guarantee into code

from pydantic import BaseModel

class Result(BaseModel):
    action: str
    target_id: str
    confidence: float

raw = model(user_input)
parsed = Result.model_validate_json(raw)   # throws on malformed

You just moved “always return valid JSON” out of the prompt and into code that enforces it. The prompt asks. The schema guarantees. That distinction is the whole session.

Why attempt two can succeed

for attempt in range(MAX_ATTEMPTS):
    raw = model(build_context(state))
    try:
        parsed = Result.model_validate_json(raw)
        state = state.update(parsed)
        break
    except ValidationError as e:
        state.log_failure(str(e))          # error goes back in
else:
    return escalate(state)

The loop is not the interesting part. The interesting part is state.log_failure feeding into build_context: the only reason attempt two can succeed where attempt one failed.

Allowlists are per step

ALLOWED = {"search", "read_record"}        # note: no writes

result = model(
    context=build_context(state),
    tools=[t for t in TOOLS if t.name in ALLOWED],
)

if state.steps > MAX_STEPS or state.is_complete():
    return finalize(state)

The allowlist is a per-step decision. A planning step and an execution step should not have the same tools available.

Side by side

~3 lines

def agent(user_input):
    return model(user_input)

~30 lines

Schema validation · state · per-step context · tool allowlist · step budget · escalate path

The model is identical. The prompt is nearly identical. One of these survives a real user, and the difference is roughly thirty lines that have nothing to do with AI.

Order matters: schema first (nearly free), then rules, then anything that needs another model call.

Harness or prompt?

Prompt

What good output looks like.

Harness

What happens when the output is not good.

Apply it

What you wrote in the promptWhere it actually belongs
“Always return valid JSON”Schema validation
“Never delete anything”Tool allowlist
“Remember what the user said earlier”State
“Do not make things up”Retrieval + grounding check
“Stop when you have enough information”Termination condition
“Be concise, use British spelling”Stays in the prompt

If you are writing “always” or “never” in a system prompt, you are describing a control, not an instruction. Every “never” is a guarantee you are hoping for instead of building.

This is why failing prompts keep growing: each incident adds a sentence, the sentences compete, and past a point they degrade each other. The harness scales the other way: it is code, and code composes.

Map your failure

What you sawLayer that brokeFirst thing to add
Agent looped foreverWorkflow controlStep budget
Answered from stale dataContextRebuild context per step
Deleted the wrong recordPermissionsPer-step allowlist
Shipped a plausible wrong answerEvaluationRules tier, not just schema
Forgot the user mid-taskStatePersist confirmed facts
Retried and failed identicallyState + contextFeed the error back in

What we did not cover

Ready to go deeper?

Same two programmes. Scan a QR, then take the next step.

Next

Prompt tuning has a ceiling.
The harness does not.

AI Engineering Bootcamp: build and ship production AI agents

maven.com/tailabs/ai-engineering-bootcamp

Dr. Aki Wijesundara

Dr. Aki Wijesundara

TAI Labs

1 / 22