A Hard Extraction Belongs in Several Small Tools, Not One Large Schema

A tool definition is normally understood as a way to make something happen. You declare a function the model can call, it emits the arguments, and something on the other side runs. For pulling structured data out of text, invert that picture entirely. Define the tool, let the model call it, and let nothing on the far side ever run. The call itself is the deliverable. What you asked for arrives as the argument object the model constructed, read straight off the response, and the “action” is that you keep it.

Seeing the tool call as a data-capture device rather than as a trigger is the first move. The second, and the one that actually decides how reliable a hard extraction turns out to be, follows from it. Because the interface was built for a model to issue more than one call in a single request, a complicated extraction does not have to be forced into one sprawling schema. It can be carved into several small tools, each capturing one distinct thing, each validated on its own, all assembled by the model in a single pass. The carving, not the calling, is where the reliability is won.

The tool call is the return value, not a trigger for one

The mechanism grew up around side effects, a model invoking an external function so that real work happens on the other end. The same design carries structured data back just as cleanly when nothing on the far side executes. When you define a tool and the model calls it, what you receive is a structured argument object, handed back through the response as data your code can use directly. No step in between turns text into a structure, because the structure never passed through text on your side of the boundary.

That absent step is the point. A prompt that asks for JSON in prose hands you back a string, and a string is a thing you still have to parse, and parsing is a thing that can fail, and the validation guarding that parse is more code whose edge cases are the ones you never exercise until production finds them. An extraction tool removes that whole stretch of pipeline. The response carries an object already shaped like the schema you declared, so the failure surface that lives between “the model produced text” and “my code has a validated structure” simply is not there to fail. An extraction tool, then, is nothing more exotic than a tool that exists to lift data off the page instead of to set something running, understood from the outset as a return channel and not as a command.

The enforcement that makes the object trustworthy is settled by the strict variant of the interface, which compiles the declared schema into a grammar and constrains generation so a non-conforming argument object cannot be produced. That guarantee is not our subject; take it as given. Without it, the interface leans only on the model’s tendency to respect the declared argument shapes, which suffices for forgiving work and gives way the moment the shape has to hold. What matters here is a level up from the guarantee: given that the tool call returns a validated object, how should you shape the tools so the extraction stays reliable as the thing you are pulling out grows complicated.

One request can carry many calls, and that is the whole advantage

Both guaranteed forms return a schema-conforming object under the same enforcement, so the choice between receiving one object directly and reading it off a tool call is a question of fit, not of strength, and that groundwork is settled elsewhere. What matters for a complex extraction is one property the tool form has and the direct form does not: a single request can carry more than one call. The model can invoke one tool here and a different tool there, or the same tool several times, as the content supports each. That multiplicity is the feature to build on, because it lets a complicated output be produced not as one object but as many, each issued when the source calls for it.

A hard extraction splits into several small tools

The instinct when the output you need is elaborate is to describe all of it in one large schema, every entity and every field nested into a single object the model fills in one shot. That instinct works against you. Every field added to a schema is one more constraint the model has to hold and honor at the same time as all the others, and the dependability of getting the entire structure right on every call falls as the count climbs. A schema of two dozen fields is not just a longer version of one that holds a handful. It is a materially harder thing for the model to satisfy in full, and the difficulty compounds with nesting and with typed collections.

Composition is the way out. Give each distinct kind of thing in the source its own small tool, and let the model fire whichever of them the content warrants, as many times as it warrants them, all inside one request. A document describing several different kinds of thing becomes several focused tools, each small and concerned with a single kind of thing. The complex output you wanted still emerges, assembled from the several calls, but no single schema the model had to fill was ever large enough to strain it.

The property that makes this more than a stylistic preference is isolation. Because the response validates each call on its own terms, a shape the model botches on one entity is contained to that entity and never spreads to the rest. Under one monolithic schema, a single field the source cannot support, or one branch the model gets tangled in, can drag on the entire object, and you are left with a whole result that is suspect rather than a single part that is. Split across focused tools, a failure stays local to the entity it belongs to. The calls that went well are as valid as they would have been alone, and the one that struggled is identifiable as the one that struggled. Partial success becomes legible. You can see that two of the three entities came back clean and the third did not, act on the two, and route only the third to a retry or to review, instead of discarding everything because one corner of a large object was wrong.

There is a plainer benefit underneath the isolation argument, which is that a small schema is simply easier for the model to comply with than a large one. Keeping each tool focused holds every individual extraction inside the range where the interface is dependable, and composition is the architectural move that lets you stay inside that range while the output as a whole grows arbitrarily rich. You get complexity in the aggregate and simplicity at each point of enforcement, which is the combination that survives real documents.

The failures that come from carving the extraction wrong

The reliability problems that show up in practice are, for the most part, problems of shaping the tools badly rather than of the mechanism failing. Three recur.

The first is the overloaded tool, the single schema asked to carry too much. It is the default mistake because it feels like the direct expression of the goal: you want a lot of structured data, so you write one large tool to get it. Compliance degrades under the weight of the constraints, and the fix is exactly the composition above, breaking the one bloated schema back into tools that each own a single coherent entity. The signal to watch for is a schema whose field count keeps climbing and whose fields describe more than one kind of entity, which is the schema begging to be divided.

The second is trusting argument shapes that were never guaranteed. Reaching for the tool interface because the output had to be reliable, and then leaving it in its default, non-strict state, gets you the appearance of enforcement with none of the substance. The arguments will conform most of the time, by the model’s training, and then one call will not, and nothing will have stood between that call and your downstream code. If the reason you chose tool calling was that the shape could not be allowed to vary, the strict variant is not optional, it is the entire point of having chosen it.

The third is over-splitting, the mirror image of the first. Composition has a cost, which is that several calls have to be coordinated and their results reassembled into whatever coherent whole the task actually needs. Carve past the natural seams of the data, into tools so granular that fields which genuinely belong to one entity are scattered across three, and you have bought yourself reassembly work and coordination risk in exchange for reliability you did not need. The right grain is the grain of the domain. Split where the source has genuinely distinct entities, and stop there.

When one object is the right shape

Composition is the answer to a complex extraction, not to every extraction, and reaching for it reflexively is its own error. When the thing you are pulling out is genuinely one cohesive object, a handful of fields that describe a single entity, splitting it into multiple tools adds a reassembly step and a coordination surface for no gain in reliability, because the single schema was never large enough to be at risk. And when you simply want one structured result back, the direct object form is cleaner than dressing a single-tool call around it, since the tool interface’s distinctive advantage, its capacity for multiple and varied calls, goes entirely unused.

The line is drawn by the source, not by taste. Composition earns its keep precisely when the material contains several distinct kinds of entity, or a run of like entities whose number is not fixed in advance, so that the model’s ability to call the right tool the right number of times is doing real work. Absent that multiplicity, the extraction is one object, and it should be modeled as one. The judgment is reading the shape of what you are extracting and matching the tooling to it, rather than defaulting to either extreme.

Reliability is decided in how you carve the problem

The reframe that starts all of this is small and does most of the work. A tool call is not only a way to make something run. Treated as a return channel, it is a way to receive a validated structured object without any of the text-to-structure plumbing that quietly breaks, and the strict variant makes that object trustworthy. Once the call is understood as the deliverable, the interface’s design for choice and for multiple calls stops being incidental and becomes the reason to prefer it whenever an extraction is really an act of selection or assembly.

From there the discipline is one of decomposition. The enforcement guarantee is a solved problem you inherit; it does not weaken as schemas grow and it does not need your attention. What remains genuinely in your hands is the carving. Model one thing as one tool and several distinct things as several tools, keep each schema small enough to sit inside the range where the model is dependable, and let independent validation contain a failure to the entity it belongs to rather than let it poison the whole result. Do that and complex extraction stops being a single fragile object you hope survives generation, and becomes a set of small reliable captures the model assembles, each one holding whether or not the others do. The mechanism was never the hard part. Deciding where to cut is.