Trail content
8 minEnglish narration, Portuguese subtitlesPodcast (RSS)
Chapters
  1. 0:00Opening
  2. 0:45The idea
  3. 1:05How it works
  4. 1:59The four questions
  5. 3:27A challenge
  6. 4:15The common mistakes
  7. 5:28Try it
  8. 6:16In production
  9. 7:03Wrap-up
Transcript

Opening

Friday, five in the afternoon. Someone on the product team opens the settings panel and edits the ticket classifier prompt.

They swap the word urgent for high priority, to sound a bit more polite. Nobody runs a test. After all, it's just text.

On Monday, the urgent queue is empty. The code that read the model's output was looking for the word urgent. Half of the critical tickets spent the whole weekend in the regular queue.

The prompt was an interface. Nobody treated it like one. In this lesson, you'll see what it means to treat it that way, and the four questions that turn a prompt from a draft into a contract.

The idea

A production prompt is an API contract, between your code and the model. It has the same parts as any interface you already version and test. You'd never let someone rename a field in a public API from a settings panel on a Friday afternoon. The prompt deserves the same respect.

How it works

Let's go through the contract. The calling code knows exactly what it sends, and what it expects back.

The input has a defined shape. Which variables, what types, and where they come from.

The prompt has a name, a version and an owner. Changing the prompt is changing the interface.

The output follows a schema, with fixed fields, not free text that someone has to interpret.

The code validates that output before using it, just like it would with any external API response.

And a set of test cases, the eval, runs against every new version of the prompt.

That Friday afternoon broke steps four and six at the same time. The word in the output was part of the contract. And nobody had a test that failed when it changed.

The four questions

Before any prompt goes to production, answer four questions.

Input. What goes in, and from where? For the classifier: the ticket title and body, as plain text, with personal data already removed.

Output. What format comes out? A priority field that can only be urgent or normal, and a reason field with a short explanation.

Version. How do I know which one ran? Ticket classifier, version seven, recorded on every trace.

Test. How do I know it didn't get worse? Two hundred labeled tickets, run on every change.

If any answer is I don't know, the prompt is still a draft. Even if it's already in production.

And on input, also write down what must not go in. Support tickets at a Brazilian bank often carry a national ID number, a payment key, or pieces of a receipt. That's personal data under Brazil's data protection law. The contract says your code masks it before the call. And the eval has a case proving sensitive data doesn't leak to the provider.

That goes for the test cases too. A private repository doesn't cancel the law. If a real ID number sits in a test case, it goes to the provider on every eval run. Mask the fixture as well.

A challenge

Here's one for you. On Monday, the urgent queue is empty. Friday's trace says ticket classifier, version seven. But the settings panel doesn't record any version. What does that already tell you?

It tells you the change went around the contract. The trace says version seven, but the text that actually ran isn't version seven from the repository. You can't match what ran to the versioned file. So the trace can't help you investigate.

And an eval alone wouldn't save you either. It would test the file in the repository, while production runs something else. The next step is to take the prompt out of the panel, version the file, and reject any output the schema doesn't know.

The common mistakes

The most common trap is exactly that settings panel. It looks harmless, because the text is right there, visible. In practice, it's a deploy with no diff, no review, and no eval.

And the person who edited it on Friday wasn't being reckless. The path the product offered was the wrong one. Fix the path, not the person.

The other extreme breaks too. Three copies of the same prompt, in three services, each one almost the same. A bug gets fixed in one, and the other two stay stale. That smell is called scattered prompt strings. The cure is one versioned template, reused by every service.

And there's a quieter one. Say the schema asks for priority and reason. In four percent of cases, the model returns only the priority. And the code quietly fills in a default reason and moves on. That's the same fragility as searching the text for the word urgent. Incomplete output should fail validation, and go to a retry or a review queue. If the field is really optional, say so in the schema.

Try it

Now take one of your own prompts and write its contract, in five minutes, in a file right next to the prompt. A name, a version, an owner.

What goes in, and what must stay out.

If you couldn't fill in the output without hesitating, your code probably parses free text. That's a smell called free text handoff.

And if there's nothing to put next to eval, that's untested prompt.

Then compare the contract with the code that consumes the output. If the code looks for a Portuguese word, and the contract promises an English value, you just found Friday's bug before it happens. And the fix isn't translating the schema. It's reading only the validated field, and deleting the word search.

In production

So what changes in practice? The prompt lives in the repository, in its own file. Not scattered as strings across the code.

Prompt changes go through review, like any other code change.

Output is never read by searching the text for a word. It's read from a field.

And every call records the prompt version on the trace, so you know what ran when something goes wrong.

And a small change still follows the ritual. Say product wants a summary of five sentences instead of three. You bump the version, run the eval, and ship only if the length check still passes. The goal isn't a prompt that never changes. It's a prompt you can change with a test, and a way back.

Wrap-up

So here's the idea to take with you. Treating the prompt as an interface doesn't make the model deterministic. It makes the rest of your system ready for when it isn't. And it means your team stops being afraid to touch it.

In the next lesson, we'll see what happens when that interface grows and nobody prunes it: prompt bloat, where every incident left a rule behind. See you there.

Fundamental

The prompt as an interface

Prompt como interface

The prompt as an interface is an AI engineering fundamental for people who work with LLMs. Treat the prompt like an API contract: input, output, version, and test.

Element
05/13
Level
Beginner
Read
4 min
Updated
On this page 5
  1. 01The situation
  2. 02The concept
  3. 03Try it
  4. 04What this changes
  5. 05Check your understanding
  6. ··Quick answers

The situation

Friday, 5 p.m. Someone on the product team edits the ticket classifier prompt straight in the settings panel: they swap “urgent” for “high priority” to sound more polite. Nobody runs a test, because “it’s just text”.

On Monday the urgent queue is empty. The code reading the output was looking for the word urgent. Half of the critical tickets spent the whole weekend in the regular queue.

The prompt was an interface. Nobody treated it like one.

The concept

A production prompt is an API contract between your code and the model. It has the same parts as any interface you already version and test:

flowchart LR
  C["1. Calling code"]:::tool --> IN(["2. Typed input"]):::io
  IN --> P(["3. Versioned prompt"]):::llm
  P --> OUT(["4. Output with a schema"]):::io
  OUT --> V["5. Validation"]:::tool
  V --> C
  E[("6. Eval")]:::store -.->|"tests"| P
  1. The calling code knows exactly what it sends and what it expects back.
  2. The input has a defined shape: which variables, what types, where they come from.
  3. The prompt has a name, a version, and an owner. Changing the prompt is changing the interface.
  4. The output follows a schema, not free text that someone has to interpret.
  5. The code validates the output before using it, as it would with any external API response.
  6. A set of test cases runs against every prompt version.

That Friday at 5 p.m. broke item 4 and item 6 at the same time. The word in the output was part of the contract, and nobody had a test that failed when it changed.

Treat the change the way you would treat a missing field in an API JSON. Nobody edits the contract in the customer panel at 5 p.m. and waits until Monday. The prompt deserves the same ritual: diff, review, eval, version on the trace. If the trace does not say which text ran, Monday becomes guesswork.

The four questions of a contract

Before a prompt goes to production, answer:

Question Example answer
Input: what goes in, and from where? Ticket title and body, plain text, personal data already removed
Output: what format comes out? priority: urgent or normal; reason: string
Version: how do I know which one ran? ticket-classifier@v7, recorded on every trace
Test: how do I know it did not get worse? 200 labeled tickets, run on every change

If any answer is “I don’t know”, the prompt is still a draft, even if it is in production.

On input, also write down what does not go in. Bank and marketplace tickets in Brazil often carry a CPF (national ID), a Pix key (instant-payment address), and snippets of a receipt. That is personal data under Brazil’s LGPD. The contract says whether code masks it first, and the eval includes a case where sensitive data must not leak to the provider.

What changes in practice

  • The prompt lives in the repository, in its own file, not scattered as strings across the code.
  • Prompt changes go through review, like any code change.
  • Output is never read with if "urgent" in answer. It is read from a schema field.
  • Every call records the prompt version on the trace, so you know what ran when something went wrong.

The most common trap is the settings panel. It looks harmless because the text is visible. In practice it is a deploy with no diff, no review, and no eval. The person who edited at 5 p.m. was not being reckless; the path the product offered was the wrong one.

The other extreme breaks too. Three copies of the same prompt in three services, each “almost the same”. A bug gets fixed in one and the other two stay stale. That is the Scattered Prompt Strings smell, next to Unversioned Prompt.

Check +20 XP on first tryOn Monday the urgent queue is empty. Friday's trace shows version ticket-classifier@v7. The settings panel does not record a version. What does that already tell you?

Try it

Take one of your prompts and write its contract in 5 minutes, in a file next to the prompt:

name: ticket-classifier
version: 7
owner: support-team
input:
  title: string
  body: string        # no personal data
output:
  priority: urgent | normal
  reason: string
eval: evals/ticket-classifier.jsonl   # labeled cases

If you could not fill in output without hesitating, your code probably parses free text. That is the Free-Text Handoff smell. If there is nothing to put under eval, it is the Untested Prompt smell.

Compare the contract with the code that consumes the output. If the code looks for a Portuguese word and the contract promises an English enum, you just found Friday’s bug before it happens.

Check +20 XP on first tryThe contract says priority: urgent | normal. The code still does if 'urgente' in text. Which change comes first?

What this changes

  • Output becomes data: Structured Output is the tool for that.
  • Changes become measured experiments: What an eval is shows how to build the cases.
  • The prompt gets a home of its own: the Prompt Template Registry pattern organizes name, version, and variables.
  • The team stops being afraid to touch it, because every change has a test and a way back.

Treating the prompt as an interface does not make the model deterministic. It makes the rest of the system ready for when it is not.

Check your understanding

Check +20 XP on first tryProduct wants to change 'summarize in 3 sentences' to 'summarize in 5 sentences' in a billing-email prompt. The eval has 80 cases with a length rubric. Which sequence matches the contract?

Check +20 XP on first tryA bank support bot sends the ticket body to the model. One eval case still contains a real CPF (Brazilian national ID). What does the input contract require now?

Check +20 XP on first tryTwo services classify the same ticket with 'almost identical' prompts, one in each repo. One got the 'urgent' fix. The other did not. Which smell is in front?

Check +20 XP on first tryThe schema asks for priority and reason. In 4% of cases the model returns only priority. The code fills a default for reason and continues. Is the contract whole?

Quick answers

What is The prompt as an interface?

The prompt as an interface is an AI engineering fundamental for people who work with LLMs. Treat the prompt like an API contract: input, output, version, and test.

Go further

Relations

Part of the tracks 1
Search