Your Extraction POC Works on 10 Documents. Here's Why It Breaks on 10,000.
An extraction demo on ten curated PDFs proves the model works. It says nothing about cost, silent truncation, partial coverage, or correctness under retry.

Marcin Wylot is a Senior AI & Data Architect specializing in building production-grade AI and data systems. He helps teams take AI from R&D and prototypes to secure, reliable systems at scale, with a focus on performance, explainability, and real-world impact.
An extraction pipeline has to hold up on the whole corpus, not the slice in the demo — and the demo is the one place that gap stays invisible. The vendor demo ran clean: a dozen hand-picked PDFs went in, structured fields came out, the fields were correct, and everyone in the room exhaled. Somebody said the word “magic.” A six-figure number got attached to a roadmap. Then the pipeline met the real corpus — the ten thousand documents the business actually owns, the ones nobody curated — and the output quietly fell apart. Not with an error. With confident, plausible, wrong results that nobody caught until they were already downstream.
Here is what that demo actually proved: the model can do the task once, on inputs chosen to make it look good. That is a real fact, and it is almost never the fact in question. The model can extract the field. Modern models are genuinely good at reading a document and returning structure. What the demo proved nothing about is the only question that matters at scale: will this be true, cheaply, every single time, when part of the batch fails and the whole thing gets retried at two in the morning? “Can the model do this” is a research question, and it’s usually settled before you walk in the door. “Will this hold, at cost, under failure, across the full ugly corpus” is the entire job. Those are different disciplines. Confusing them is the difference between a POC that predicts production and one that only flatters it.
The gap is engineering, not intelligence
When the pipeline cracks, the instinct in the room is always the same: reach for a smarter model. Swap the mid-tier model for the frontier one, add another reasoning step, wait for next quarter’s release. It’s an understandable reflex — the failure feels like the model isn’t smart enough. It almost never is.
The gap between a working demo and a working system is four engineering failures, and not one of them is fixed by a model upgrade. Cost that scales with the wrong thing. Silent truncation. Confident but partial coverage. And no correctness when the system retries itself. A better model runs each of these failure modes more eloquently; it does not remove any of them. That’s the part that decides whether the six figures land, and it’s the part that’s invisible in a demo. These four are predictable. You can predict which one bites first by looking at your corpus’s tail length and its failure rate — before you write the check, not after.
Here they are, one at a time. Each has a fix, and the fix is something you build, not something you buy.
Your cost scales with the wrong thing
On ten documents, cost is a rounding error. You will never notice it, which is exactly why the demo hides it. On ten thousand documents reprocessed at real cadence — every hour, every few minutes, forever — cost is the line item nobody budgeted for. Someone in finance eventually asks why the inference bill looks like a second headcount, and there is no good answer, because the pipeline was built to re-read the entire world on every run.
That’s the trap: the naive design’s cost scales with your total state, not with what changed. It reprocesses everything because it has no idea what’s new.
Consider a pipeline built for a US enterprise’s public-facing Socratic AI companion — a system that reads each user’s entire conversation history and distills a few durable, evolving personal insights. It runs continuously, per user, on a five-minute Temporal schedule. The naive version re-reads every conversation for every user every five minutes, forever. This one doesn’t. Before a single token is spent, each conversation is fingerprinted — a SHA-256 hash over its message count and its latest timestamps — and any conversation that hasn’t changed since the last run is skipped outright. The extraction stage only ever touches conversations that actually moved. On top of that, the whole synthesis step is short-circuited by a second hash: if none of the relevant inputs changed, there is no model call at all. And the profile it maintains is deliberately bounded — it does not grow with how long someone has been a user.
There’s no honest single cost ratio to quote — the savings depend on your traffic. But this is a design property, not a tuning parameter. Cost proportional to change is something you architect in at the start. You cannot bolt it onto a pipeline that was built to re-read the world, and you certainly cannot buy it from a better model.
Silent truncation, the bug you never see
This is the failure worth catching before anything ships — and the one a demo is structurally unable to show you.
Every other failure at least announces itself eventually — a bill, a crash, an empty result. Silent truncation doesn’t. It produces a confident, well-formed, completely reviewable answer derived from part of the input. A real document — a long conversation, a dense report, a full transcript — doesn’t fit the model’s context window. The naive pipeline clips it to fit. No error is raised. No warning is logged. The model dutifully extracts structure from the fraction that happened to fit — everything past the cutoff silently gone — and returns it looking exactly like a correct result. It passes human review, because a reviewer checks whether the output is plausible, not whether the input was complete. You cannot see what was silently dropped.
At ten documents, none of them overflow, so this bug is invisible in the demo by construction. At ten thousand, the long ones always exist, and they are usually your most important records.
The fix is not subtle, but it has to be deliberate. In the companion pipeline, an oversized conversation is split into sixty-thousand-token windows using a deterministic size estimate, each window is extracted independently, and the results are merged losslessly. The rule that matters is the one about failure: all windows are required. If any single window fails to extract, the entire conversation is marked as errored and nothing partial is ever persisted. There is no code path that writes half a document and calls it done. That’s what “no truncation” actually means as an engineering commitment — not “we made the window bigger,” but “the system structurally cannot ship a fraction of a document as if it were whole.”
Confident, and wrong about half of it
Here is a fact about running anything at scale: some fraction of every batch fails. A model call times out, a document is malformed, a rate limit trips. On ten inputs you can pretend this away. On ten thousand, failure isn’t an exception, it’s a rate.
The dangerous move — and it’s a common one — is to treat partial success as success: take whatever came back from the batch, synthesize a result from it, and ship it labeled “complete.” Now you have a confident summary that silently omits whatever failed, and nobody downstream knows a piece is missing. That’s not a smaller version of the right answer. It’s a wrong answer wearing the right answer’s clothes.
The companion pipeline fails closed instead, and it does so at two levels. At the coarse level there’s an explicit coverage gate: if the failure rate in a batch exceeds twenty percent, the synthesis stage does not run at all — the run returns “in progress” and the failed conversations get re-processed on the next cycle. But the gate is the blunt guard, not the real guarantee. The guarantee is structural: no conversation is ever recorded as covered until its extract has actually been folded in. A watermark tracks how much history has genuinely been synthesized, and it advances only over what succeeded — so a conversation that failed this cycle is never quietly marked done. It simply comes back next cycle. The system would rather tell you “not finished yet” than hand you a confident result that’s missing a piece it can’t name. That is a product decision as much as an engineering one, and it’s the kind of decision that only gets made after someone has been burned by the alternative. Correct under failure is a posture you choose on purpose.
No correctness when things retry
Production extraction doesn’t run as a script on your laptop. It runs on distributed execution engines, and those engines retry. This is not a bug in them — it’s the contract. Temporal — the durable-orchestration engine behind these pipelines — executes activities at least once: a step that already completed can be run a second time after a worker crash or a timeout, because the engine couldn’t confirm the first attempt finished. At-least-once is the price of durability. It means every step in your pipeline must assume it may execute twice.
A pipeline that ignores this double-counts. It folds the same batch of extractions in twice, or it marks a half-finished run as complete because the retry landed on the wrong side of a state change. These bugs are miserable to reproduce because they only appear under exactly the failure timing the demo never experiences.
So idempotency isn’t a nice-to-have here, it’s load-bearing, and in the companion pipeline it’s enforced three ways. That “covered-through” watermark is advanced only after a fold has successfully completed — the fold runs and is allowed to fail before the watermark ever moves, so a crash can never skip a batch by advancing state past work that didn’t happen. A generation hash over the run’s actual inputs short-circuits any run whose inputs haven’t changed, so a spurious retry does no synthesis at all. And retryable failures are deliberately re-raised so that Temporal owns the retry, rather than being swallowed and papered over. The orchestration engine is configured to match: bounded automatic retries, heartbeats on the long-running steps so a stalled worker is detected instead of hanging, and — deliberately — a poll-driven drain in bounded per-cycle batches rather than a cleverer self-continuing construct, because a system you can reason about and observe beats a system that’s elegant and opaque.
One more property rides on the same discipline. This pipeline persists a profile and feeds it back into the next run’s prompt, so a single poisoned extract could otherwise steer every future cycle. Nothing reaches persisted state unvetted: model output is allow-listed before it’s stored — emitted principles checked against a controlled catalog, behavioral dimensions normalized against a fixed vocabulary, anything out-of-vocabulary dropped. The state that gets re-prompted is state the system already vouched for. Correct under retry and secure by design turn out to be the same instinct — never trust output you didn’t verify, whether the threat is a double execution or a prompt injection — and both are things you can point to in the code, not things you hope for.
Scale is a discipline, not a trick
If all of this only ever showed up in one pipeline, you could dismiss it as one clever hack for one weird problem. It isn’t. It’s the same discipline, and it recurs across domains that have nothing to do with each other.
For a sports-media company generating automated broadcast narratives, the pipeline processes hundreds of games a season across nine narrative categories each. Same problem shape, different clothes. Cost discipline shows up as prompt caching: one large system prompt — rosters, eighty-seven stat types, the full speech-to-text transcript, tens of kilobytes of it — is cached once and reused across all nine parallel category calls for a game, so only the first call pays full freight and the rest hit cache reads. Coverage discipline shows up as blast-radius control: failure is isolated per category, per element, per game, so one empty category never sinks the broadcast. And correctness shows up as an independent judge model that verifies every extracted element before it survives to the final curated set. An earlier framework-heavy design was deliberately torn out and rebuilt on plain async orchestration for direct control over exactly these concerns.
For a civil-engineering firm generating site-assessment reports, hundreds of data points per report flow through an async work queue with an explicit state machine — pending, processing, done, failed — tracked in a real database. It is the least glamorous mechanism in the stack, and it is exactly the thing that turns “it worked when I ran it” into “I can tell you precisely which of the N items succeeded, which failed, and why.” Every generated section carries provenance back to a source file and page number, and the whole thing runs through human verification checkpoints. The unglamorous state machine is the product.
Three industries, one shape. That’s how you know it’s a practice and not a party trick.
Why a POC has to predict production
The reason the demo lies is now precise: it exercises none of the four failure modes. It runs short, curated, low-failure, single-pass inputs, which is to say it runs the exact conditions under which all four failures are invisible. Of course it looked like magic. It was staged in the one environment where the hard problems don’t exist.
A POC worth the name does the opposite on purpose. It deliberately runs the ugliest, longest, highest-failure inputs in the corpus — the documents that overflow the window, the batches with a bad failure rate, the load that makes the retry timing actually happen — and it reports back on the four things that predict production: what this costs at real cadence, what happens to the documents too big to fit, what the system does when a fifth of the batch fails, and whether it stays correct when every step runs twice. That’s the artifact worth building first: a proof of concept that tells you the expensive truth cheaply and early, while the number on the roadmap is still an estimate and not a sunk cost. A POC that tests the six-figure bet against reality before you place it is a fundamentally different artifact from a demo that makes the bet feel safe.
Where to go deeper on each
This essay compresses four failure modes into one, and each of them deserves its own. Trusting an extracted value as if it were a fact — when to gate, verify, and refuse — is a whole discipline. The data backbone beneath production AI, the state machines and provenance that make the intelligence trustworthy, is the depth that everything else stands on. Knowing when a problem wants a deterministic pipeline versus an agentic one — and refusing to reach for autonomy where control is cheaper and safer — is its own decision with its own failure modes. And what an agentic system actually costs to run, once it’s making its own choices, is a question worth pricing up front. Each gets its own full treatment. Consider this the map.
Before more money goes in
So here’s the honest question. If your extraction POC only ever ran on the easy documents — the curated dozen, the ones that fit, the batch that happened not to fail — and nobody has yet pointed it at the full, ugly corpus at real cadence, then you don’t actually know what you have. You have a demonstration that the model can do the task. You do not yet have evidence about cost, truncation, coverage, or correctness under retry. That gap is precisely the thing worth closing before the next tranche of budget goes in.
It’s cheaper to find out what breaks in a POC built to break it than to find out in production, with the six figures already spent. So point your POC at the ugliest, longest, highest-failure documents you own and watch which of the four shows up first — that’s the truth worth having before you commit the next round.
A version of this piece was originally published at mwylot.net/post/extraction-poc-10-documents.

Your agency owns your media data. That's the real AI bottleneck.
CPG media AI doesn't stall on model quality. It stalls because your agency holds your first-party Google and Meta campaign data. Here's how to own the pipe.

The trend dies before your brief is written
Detecting a social trend isn't the hard part. Scoring it for brand fit and turning it into a brief before the window closes is. Here's the discipline, and what an agent changes.

The campaign was failing in week one. The report came in week six.
The signals that a campaign is failing show up while it's still running. Most teams don't see them until the monthly report, after the budget's spent. Here's the in-flight discipline.