ap.Abhishek Patel

Search

Search notes, essays, projects, the library and the AI course. Use arrow keys to choose and Enter to open.

Lesson 4 of 6 28 min

The three gulfs: comprehension, specification, generalisation

Diagnose any LLM failure as a gap of comprehension, specification or generalisation, and apply the cheapest matching fix in the right order.

Words in this lessonThree gulfs · Gulf of comprehension · Gulf of specification · Gulf of generalisation · Criteria drift · Error analysis
Three gulfs
A model of the three gaps that make LLM apps hard: comprehension, specification and generalisation. Like the three misunderstandings between a shop owner, her customers and a new trainee.
Gulf of comprehension
The gap between you and what is really happening in your app's inputs and outputs. Like a founder who assumes customers mostly order cakes, when half the calls are about deliveries.
Gulf of specification
The gap between what you want the model to do and what you actually told it. Like a trainee who gives full refunds because nobody mentioned the new store-credit rule.
Gulf of generalisation
The gap between clear instructions and the model applying them reliably to every kind of real input. Like a well-briefed trainee who still gets flustered when a customer asks about two orders at once.
Criteria drift
The way your standards for "good output" change as you look at more outputs. Like a baker who only decides how sweet the icing should be after tasting a few batches.
Error analysis
Reading many real outputs of your AI app to discover how it actually fails, before measuring anything. Like a shop owner standing in a struggling store for a day before deciding what to change.
On this page
  1. Arjun's answer: a bigger model
  2. Three people who have to understand each other
  3. Opening the hood: the three gulfs
  4. Gulf 1: comprehension (you and your data)
  5. Gulf 2: specification (you and your pipeline)
  6. Gulf 3: generalisation (your pipeline and your data)
  7. Following one failure across all three gulfs
  8. The real code
  9. Putting the gulfs to work
  10. Where the model gets fuzzy
  11. Try it yourself
  12. Common mistakes
  13. Recap
  14. Go deeper

Three weeks into the eval work, the dashboard was finally useful. Allergy handling was at 99%. Refunds were at 97%. Then on Monday the “status matches tool” row dropped from 89% to 74%, and Arjun spent the morning finding out why. A local food blogger had posted about Crumb’s new Marathi-language WhatsApp line, and traffic in Marathi had tripled overnight. The bot was mixing up order numbers in messages like “maza order 4471 ani 4475 donhi late aahet, ek cancel kara” (both my orders 4471 and 4475 are late, cancel one).

At Friday’s review Meera asked the question that had clearly been bothering her. “Every week you fix something and every week something new breaks. Last week allergies. This week Marathi. Is this ever going to end? And when it breaks, how do I know whether the problem is the AI, or us?”

Important

The idea in one sentence: LLM apps fail across three gaps, between you and your data (comprehension), between what you want and what you actually told the model (specification), and between clear instructions and the model’s behaviour on new inputs (generalisation), and naming the gap tells you which fix to reach for.

Arjun’s answer: a bigger model

Arjun had an answer ready. “I think we’ve outgrown the model. If I switch to the largest model available and write one really thorough prompt covering every case, most of this goes away.”

He had already tried it on the eval suite over the weekend. Same 200 test conversations, bigger model, a new 4,100-token system prompt that tried to list every rule he could think of:

Eval Old setup Bigger model + mega prompt
Status matches tool 74% 79%
Refund within policy (new store-credit rules) 68% 69%
Correct language in reply 81% 82%
Cost per conversation ₹0.40 ₹1.10

Small gains, nearly triple the cost. Refunds barely moved at all.

Priya looked at the refund row. “Did the mega prompt mention the new store-credit rule Meera introduced last week?”

Arjun checked. It did not. Nobody had told him about it.

“So no model, however large, could have followed it,” Priya said. “And the 200 test conversations. How many are in Marathi?”

“Eleven.”

“Out of how many Marathi messages per day now?”

“About 180.”

“You’re treating every failure as one kind of problem and buying one kind of fix. There are three kinds.” She went to the whiteboard.

Three people who have to understand each other

Priya drew three circles in a triangle and labelled them You, Your data, Your pipeline.

“Meera, imagine you hire a trainee to answer the WhatsApp line. Three people are involved. Who are they?”

Meera thought. “Me, because I know what good service looks like. The customers, sending whatever they send. And the trainee.”

“Now, what can go wrong between you and the customers?”

“I don’t actually know what they’re sending. I don’t read 600 messages a day. I might think everyone wants cakes, when half of them are asking about delivery.”

“Between you and the trainee?”

“I might not tell her something. Like the new store-credit rule. Or I tell her, but vaguely, and she understands something different.”

“And between the trainee and the customers?”

“Even if I explain everything perfectly, she might handle a weird message badly. Someone writes in half Marathi, half English, about two orders at once, and she panics.”

Priya pointed at each line in turn. “That’s it. Three gaps. Each causes different failures. Each has a different fix. If you mix them up, you spend weeks fixing the wrong thing, like buying a smarter trainee when the problem is that nobody told her the rule.”

Where the analogy breaks: a human trainee asks questions when she is confused, and learns from yesterday’s mistakes. The model does neither. It fills every gap with its best guess, confidently, and forgets everything between conversations unless you put it back in the prompt.

Opening the hood: the three gulfs

This model comes from the research of Shreya Shankar and colleagues on how people build LLM pipelines. A pipeline here means your whole app: prompts, retrieval, tool calls and the code that glues them together. They call the gaps gulfs, wide gaps that are hard to cross.

flowchart LR
  Y["You (the developer)"] -->|"Gulf of Specification"| P["Your LLM pipeline"]
  Y -->|"Gulf of Comprehension"| D["Your data (real inputs and outputs)"]
  P -->|"Gulf of Generalisation"| D

Gulf 1: comprehension (you and your data)

The gulf of comprehension is the gap between you and what is actually happening in your app. You cannot personally read every message and every reply, so you form beliefs about your traffic, and those beliefs are often wrong.

Crumb example. Arjun’s test set had 11 Marathi conversations out of 200, about 5%. Real traffic was now about 30% Marathi or mixed Marathi-English. The eval suite was measuring a bot for customers Crumb used to have.

Symptoms: surprises in production; “users would never ask that”; a test set made of questions you wrote yourself; metrics that look healthy while complaints rise.

How to cross it: look at the data. Error analysis, from the first lesson in this module: read traces, write notes, bucket and count. Use filters and summaries to see more than you could read alone, for example “share of messages by language, per week”. This gulf never closes for good, because traffic keeps changing.

Gulf 2: specification (you and your pipeline)

The gulf of specification is the gap between what you mean and what you actually told the model. You know Crumb’s refund policy, house style and allergy rules. The model knows only what is in its prompt and context.

Crumb example. The store-credit rule existed in Meera’s head and in a WhatsApp message to store managers. It never reached the prompt. The bot was not being stupid. It was filling a gap with something reasonable: full refunds.

A quick test for specification gaps: give your prompt to two colleagues and describe a tricky situation. If they disagree about what the bot should do, the prompt is under-specified. The model will not do better than two humans reading the same words.

Symptoms: the model does something reasonable but not what you wanted; the “right” answer lives only in someone’s head or in a document the prompt does not include.

How to cross it: write it down. Policies, formats, edge cases, examples, what to do when information is missing. And expect your own standards to sharpen as you look at outputs. Meera did not know she had an opinion about emoji in replies until she saw one with six of them. Researchers call this criteria drift: you discover what you want by looking at what you get.

Note

Pause and think. The bot’s replies to complaints run to 150 words. The system prompt says nothing about length. Meera says that is too long. Which gulf is this, and should Arjun build an eval for it?

Show the answer

Specification. Nobody told the model how long replies should be, so it chose. Arjun should add a length rule to the prompt first and read a few fresh replies. Only if replies stay too long despite a clear rule does it become a generalisation problem worth an eval. Building an eval for a rule you never gave the model measures your own omission.

Gulf 3: generalisation (your pipeline and your data)

The gulf of generalisation is what remains when your instructions are clear, but the model still does not apply them reliably across all the messy inputs real people send.

Crumb example. With the store-credit rule in the prompt, refunds were handled correctly 97% of the time. The remaining 3% were long, emotional messages about two orders, where the model applied the late-delivery rule to the wrong one. The instruction was clear. The model did not carry it over to that shape of input.

Symptoms: the rule is in the prompt; it works on your examples; it still fails on a slice of real inputs, often a slice that shares some feature (long, mixed-language, multiple items).

How to cross it: this is where evals earn their keep. Measure the failure with a binary eval or an aligned judge, then try fixes and watch the pass rate move:

  • Break a big task into smaller steps (identify the orders first, then apply the policy to each).
  • Improve retrieval so the right facts are in context (Module 09).
  • Add examples of the tricky input shapes to the prompt.
  • Validate outputs in code and retry when they fail.
  • Try a stronger model, or fine-tune one, when cheaper fixes stall (Module 33).

Following one failure across all three gulfs

Priya picked one real message and walked it across the triangle with Arjun.

“maza order 4471 ani 4475 donhi late aahet, ek cancel kara ani dusra lavkar pathva” (both my orders 4471 and 4475 are late; cancel one and send the other quickly)

The bot looked up order 4471, said it was out for delivery, and offered a refund on it. It never mentioned 4475. The customer wanted 4475 cancelled.

Comprehension first: had they seen messages like this? Arjun filtered last week’s traces for messages that mention two or more order numbers. There were 64 of them, 41 in Marathi or mixed language. The test set had zero. So the first fix was not to the bot at all: Arjun added 25 real two-order messages to the test set, with Meera writing down the correct handling for each.

Running the bot on those 25 gave the baseline: 9 of 25 handled correctly (36%).

Specification next: did the prompt say what to do with two orders? It did not. Nor did it say what to do when “cancel one” is ambiguous. Meera decided the rule on the spot: “If they mention more than one order, look up all of them. If it’s unclear which one they mean, ask. Never guess on a cancellation.” Arjun added that paragraph to the prompt.

Re-run on the same 25: 19 of 25 (76%). Ten conversations fixed by one paragraph. No new model, no new code.

Generalisation last: why do 6 still fail? Arjun read all six. The rule was clear, and the model still skipped the second order in long, emotional messages, or when the two order numbers were far apart in the text. This is the model failing to generalise a clear instruction. So he changed the pipeline: a first, small model call that only extracts order numbers into structured output, then code that looks up every order before the main reply is written.

Re-run: 24 of 25 (96%). The one remaining failure was a customer who typed “4471” and “4417” for the same order, a typo no rule would fix. It goes to a human.

Notice the order and the costs. Comprehension cost an hour of reading. Specification cost one paragraph. Generalisation cost a pipeline change and an extra model call. Doing them in the opposite order, starting with the expensive fix, would have hidden the cheap ones.

The real code

Here is the generalisation fix from the walkthrough, built in three steps.

Step 1: extract the order numbers with structured output. A narrow task with a typed answer is much easier for a model to get right than “read this, apply five policies and reply”. You used messages.parse with Pydantic in Module 06:

import anthropic
from dotenv import load_dotenv
from pydantic import BaseModel

load_dotenv()
client = anthropic.Anthropic()

class OrderMentions(BaseModel):
    order_ids: list[str]          # every order number the customer mentions
    wants_cancellation: bool
    which_to_cancel_is_clear: bool

def extract_orders(message: str) -> OrderMentions:
    response = client.messages.parse(
        model="claude-opus-5-5",
        max_tokens=500,
        system="Extract every order number in the customer's message. "
               "Messages may be in English, Marathi, Hindi or a mix.",
        messages=[{"role": "user", "content": message}],
        output_format=OrderMentions,
    )
    return response.parsed_output

m = extract_orders("maza order 4471 ani 4475 donhi late aahet, ek cancel kara")
print(m)

Expected output:

order_ids=['4471', '4475'] wants_cancellation=True which_to_cancel_is_clear=False

Step 2: let code, not the model, decide what to look up and when to ask. The model’s only job was reading. The rules live in plain Python where they cannot be forgotten:

def handle(message: str) -> str:
    mentions = extract_orders(message)
    orders = [lookup_order(oid) for oid in mentions.order_ids]   # your existing tool
    if mentions.wants_cancellation and not mentions.which_to_cancel_is_clear:
        ids = " and ".join(mentions.order_ids)
        return f"I can see orders {ids}. Which one would you like me to cancel?"
    return write_reply(message, orders)    # the main reply call, now given every order

Step 3: measure on the targeted test set, before and after. The test set is a JSONL file of the 25 two-order messages, each with Meera’s expected order IDs:

import json

with open("two_order_tests.jsonl") as f:
    tests = [json.loads(line) for line in f]

passed = sum(set(extract_orders(t["message"]).order_ids) == set(t["expected_ids"])
             for t in tests)
print(f"Order extraction: {passed}/{len(tests)} correct")

Expected output: Order extraction: 25/25 correct on Crumb’s set. The end-to-end eval, using the binary evals from earlier in this module, went from 19 to 24 of 25.

Note

Pause and think. Classify each of these into a gulf. (a) The prompt clearly says “never share another customer’s address”, yet 2 of 200 replies read out the wrong customer’s address. (b) Customers keep sending photos of damaged cakes, which the bot cannot see, and nobody on the team knew this happened. © Replies to corporate bulk orders use a casual tone; there is no tone guidance in the prompt.

Show the answer

(a) Generalisation, and a security problem. The rule is clear and still breaks. Measure it, and add a code guard so the bot cannot fetch other customers’ data in the first place (Module 24). (b) Comprehension. You did not know what your data looked like. Read traces and rebuild the test set. © Specification. Nobody stated the desired tone for corporate customers. Add it to the prompt before building any eval.

Putting the gulfs to work

The gulfs are a question you ask of every failure bucket from error analysis: which gap caused this? Here is how Priya and Arjun sorted Crumb’s current list:

Failure mode Gulf First fix
Test set has almost no Marathi Comprehension Rebuild test set from real traffic
Store-credit rule ignored Specification Add the rule to the prompt
Emoji overload Specification Add a style rule
Wrong order in two-order messages Generalisation Measure; split extraction into its own step
Reply contradicts order tool Generalisation Measure; add a code check comparing status

Where the model gets fuzzy

The three gulfs are a thinking tool, not a law, and they blur at the edges.

Specification and generalisation can look identical from outside. In both cases the model “didn’t do what I wanted”. The only way to tell them apart is to read your prompt honestly and ask: would a careful new employee, reading only this prompt, have known what to do? If not, it is specification. Engineers are bad at this test because they read their prompt with everything else they know in their heads. Ask a colleague.

“The model should know that.” A common belief is that a smart enough model will infer your policy. It will infer a policy, often a sensible one, and not necessarily yours. Crumb’s store-credit rule was a business decision, and no amount of intelligence could have guessed it.

Bigger models hide specification gaps, temporarily. A stronger model guesses better, so a missing rule fails less often. That feels like a fix. Then the model guesses differently after an update, and the failure returns. Write the rule down.

Fine-tuning does not fix missing instructions. Teams sometimes reach for fine-tuning (further training a model on your own examples) to fix behaviour that one sentence of prompt would fix. Cross specification first; it is cheaper by orders of magnitude.

Try it yourself

Take the failure buckets from your own error analysis (or Crumb’s table in the first lesson of this module). For each one:

  1. Name the gulf.
  2. Write the cheapest fix that addresses that gulf.
  3. For one generalisation failure, build a targeted test set of 15 to 25 real examples, measure the baseline pass rate, apply a fix, and measure again.

Expected result: at least one failure you thought needed an eval turns out to be a specification gap fixed by a sentence, and your generalisation fix shows a measured before-and-after, like Crumb’s 19 to 24 of 25.

Common mistakes

  • Building evals for missing instructions. If the prompt never said it, fix the prompt first. Evals are for failures that survive clear instructions.
  • Assuming you know your users. Test sets you write yourself reflect your imagination. Rebuild them from real traffic regularly.
  • Treating every failure as “the model is bad”. Swapping models will not fix a policy nobody wrote down.
  • Freezing your criteria. Standards sharpen as you read outputs. Update definitions, relabel, and re-check judges when they do.
  • Fixing in the wrong order. Expensive generalisation fixes (pipeline changes, fine-tuning) before cheap specification fixes waste time and hide easy wins.

Recap

  • Three gulfs separate you, your data and your pipeline.
  • Comprehension: you cannot see all your data, so read traces and keep the test set matched to real traffic.
  • Specification: the model knows only what you wrote, so state policies, formats and edge cases, and expect criteria to drift as you look.
  • Generalisation: clear instructions still fail on some inputs; this is where evals and deeper fixes pay off.
  • Work in that order: look, then say, then measure and engineer.

Meera photographed the whiteboard. Arjun walked her through it once more in plain words. “The Marathi thing was us not looking: our test set was from two months ago. The refund thing was us not saying: the rule never reached the bot. The two-orders thing was the AI genuinely struggling, so we measured it and changed how the pipeline works, from 36% to 96%. Bigger model wouldn’t have fixed the first two.”

“So it won’t ever end,” Meera said, “but each time, we’ll know what kind of problem it is.” “And which kind of fix to pay for,” Priya added.

Arjun was already looking at the next problem. “The handbook assistant for store staff is different, though. It’s RAG. When it’s wrong, I can’t tell whether it fetched the wrong page or misread the right one.” Priya smiled. “There are metrics built for exactly that question.”

Go deeper

Check your understanding

3 quick questions
1. The refund policy changed last week, but the new rule never reached the system prompt, and the bot keeps offering old-style refunds. Which gulf is this?
2. Your 200-case test set has 11 Marathi conversations, but 30% of real traffic is now Marathi or mixed. Which gulf does this show?
3. A rule is clearly in the prompt and works on your examples, but fails on long two-order messages 24% of the time. What is the best approach?