Tool-use fine-tuning: teaching a model your internal APIs.
SEPTEMBER 2, 2026 · NUMERATA TEAM
Tool-use fine-tuning is training a model on examples of calling a
specific set of tools correctly, so that the conventions of those
tools end up in the weights instead of in the prompt. The training
examples are ordinary conversations that happen to contain calls: a
request, the structured call the model should emit, the result the
tool returned, and the reply that follows from it. Nothing exotic is
going on underneath. It is the same supervised fine-tuning machinery
used for everything else, pointed at the tokens that make up a call.
The reason it matters more than it sounds is narrow and specific.
A frontier model has read an enormous amount of public API surface.
It has never seen yours. And the gap between those two facts is
exactly the gap between a model that is impressive in a demo and one
that is useful against the systems your firm actually runs on.
What a tool call really is
Strip away the SDK and there is no special mechanism. The tools
available to the model are serialized into its context as JSON Schema
definitions, usually in a dedicated section of the chat template. The
model generates text. Some of that text, delimited by special tokens
or a fixed structure the template defines, is meant to be read as a
call rather than as prose. A parser in the serving layer pulls it
out, your code dispatches it, and the result comes back as another
message in the conversation, which the model then reads and continues
from.
Two consequences follow, and both drive everything else in this
post. First, "function calling" is a formatting contract, so a model
can be perfectly capable of the reasoning and still fail because it
emitted a shape your parser does not recognize. Second, choosing the
tool and filling its arguments is generation, which means it is
learnable the same way any other generation is.
The mechanism is generation plus a parser. Every failure below is a generation problem, which is why training can move it.
Why your APIs are the hard case
Public APIs are in the pretraining data. A capable open-weight
model has effectively read the Stripe reference, the GitHub REST
docs, a decade of Stack Overflow answers about both, and enough SQL
to be dangerous. Ask it to charge a card and it has priors. Ask it to
call positions.get_net_exposure against your book
taxonomy and it has nothing but the schema you handed it thirty
thousand tokens ago.
That produces a distinctive failure profile, and it is worth being
precise about it because each item responds to a different fix:
Selection error. Internal catalogues grow by
accretion, so they are full of endpoints that differ by one word.
get_positions, get_positions_v2,
get_positions_intraday, and
get_book_positions all exist, three are deprecated in
practice but not in the registry, and the schema descriptions do
not say which.
Argument fabrication. The model produces a
plausible field name from the public APIs it does know
(start_date) where yours wants
as_of, or invents an enum value that reads correctly
and does not exist.
Semantic error inside a valid call. This is the
expensive one, because the call passes schema validation and returns
a number. Your firm's "close" means the 21:00 UTC mark and not the
exchange close, your book_id is not the same
granularity as a desk, and none of that is in the JSON Schema.
Sequencing and protocol error. The system
expects a session token first, or requires the reconciliation job to
be checked before positions are read as final. The model has no way
to know an ordering that lives in a runbook.
Over-calling. The model calls a tool for
something already answered in the conversation, which costs latency
and, on a write endpoint, occasionally costs more than that.
Notice that only the second of those is a formatting problem. The
rest are knowledge about your organization, encoded nowhere a general
model could have found it.
Try the cheap fixes first
Fine-tuning is weeks. Improving what you put in the prompt is
afternoons, and it frequently closes most of the gap. Rewrite the
schema descriptions to say when to use each endpoint rather than what
it returns. Delete deprecated tools from the catalogue instead of
documenting them. Collapse near-duplicates behind one wrapper. Add two
or three worked examples per high-traffic tool. Then, if the catalogue
is large, retrieve a relevant subset of schemas per request instead of
sending all of them, which is what tool retrieval and the various
registry layers over MCP servers are for.
The limit of that approach is real, though, and shows up in two
places. The schema tokens are paid on every request, and a catalogue
of two hundred endpoints at a few hundred tokens each is a substantial
fixed cost per call. More importantly, selection accuracy degrades as
the list gets longer, because you have turned every request into a
multiple-choice question with two hundred options, most of which look
alike. Retrieval helps until the retriever's own mistakes become the
dominant error, which happens sooner than people expect when the
candidates are near-duplicates.
One honest counterweight before moving on: if you serve the model
yourself, a static tool catalogue sitting at the front of every prompt
is an ideal prefix-cache target, so the compute cost of those tokens
is largely recoverable even though the context-window cost is not.
Prompted schemas are cheaper than a naive token count suggests. What
caching cannot fix is the accuracy curve.
The three options are not exclusive. Most working systems retrieve a subset and fine-tune on it.
The three approaches, side by side
Worth being blunt about the trade, because the third column is not
strictly better than the first two.
Prompted schemas
Tool retrieval
Tool-use fine-tuning
What it changes
The context
Which context you build
The weights
Time to first result
An afternoon
Days
Weeks, mostly on data
Cost per request
Full catalogue, every call
A subset, plus a retrieval hop
A short list
Scales to 200+ tools
Poorly, selection degrades
Yes, up to retriever error
Yes, that is the point
Learns undocumented conventions
Only what you can write down
Same, it just selects
Yes, from real examples
Adding a new tool
Edit a schema file
Reindex
Regenerate data, retrain
Breaks when
The catalogue grows
Candidates look alike
The API changes underneath it
What one training example looks like
A tool-use training record is a conversation, stored in whatever
messages format your trainer reads, carrying the tool definitions
alongside it. The definitions belong in the record rather than in a
global config, because the model has to learn the mapping from a
schema it can see to a call it should emit, and freezing that
relationship into training config is how you end up with a model that
only works when the catalogue is exactly the one it trained on.
ONE RECORD, WITH THE MASK MARKED
{
"tools": [ /* the same JSON Schemas production will send */ ],
"messages": [
{"role": "system", "content": "..."}, // masked
{"role": "user", "content": "Net EURUSD exposure on the // masked
spot book at yesterday's close?"},
{"role": "assistant", "tool_calls": [{ // TRAINED ON
"id": "c1", "type": "function",
"function": {
"name": "positions.get_net_exposure",
"arguments": "{\"book_id\":\"FX-SPOT-1\",
\"symbol\":\"EURUSD\",
\"as_of\":\"2026-09-01T21:00:00Z\",
\"basis\":\"MARK_CLOSE\"}"}}]},
{"role": "tool", "tool_call_id": "c1", // masked
"content": "{\"net_usd\": -4182300.0, \"ccy\": \"USD\"}"},
{"role": "assistant", "content": "The spot book was short // TRAINED ON
4.18m USD equivalent..."}
]
}
The masking is the one piece that is easy to get wrong and
expensive to get wrong. Loss goes on the assistant turns only. The
system prompt, the user's request, and above all the tool results are
context the model must read and must never be trained to produce. Put
loss on the tool result tokens and you are explicitly teaching the
model to write convincing API responses, which is the precise habit
that turns a helpful assistant into one that reports a position it
never queried. Most trainers do the right thing by default for the
user and system roles and get the tool role wrong, so check it rather
than assume it.
Two things belong in the dataset that teams routinely leave out.
One is examples where the correct action is not to call
anything, because a model trained exclusively on successful calls
learns that a tool call is always the answer. The other is recovery:
a call that returned an error or an empty result, followed by the
corrected call. Without those, the first production error puts the
model somewhere its training never went.
Where the data comes from
Almost nobody has a labelled corpus of natural-language requests
paired with correct calls. You build one, and there are three
practical sources. The unusual luck of this problem is that you get a
grader nearly for free: a call either validates against the schema and
succeeds against a staging replica, or it does not, so you can filter
machine-generated data at scale without human review.
LOGS YOU ALREADY HAVE
Gateway logs, audit trails, and client SDK traces record calls that definitely worked, with real argument distributions. What they lack is the request that motivated each one, which has to be written back on, by hand or with a model.
SYNTHESIS FROM THE SPEC
Sample valid calls directly from the OpenAPI or JSON Schema definitions, execute them against a staging replica, and keep the ones that return. Cheap, unlimited, and covers endpoints nobody has used yet.
REJECTION SAMPLING
Let the current model attempt a set of real requests, keep only the attempts a validator accepts, and train on those. This is the loop that keeps paying after the first round, because each version generates better data than the last.
The synthesis path deserves a diagram, because the ordering is
counterintuitive. You do not start from questions and try to produce
calls. You start from calls, which you can generate correctly by
construction, and work backwards to the question that would have
prompted each one.
Backwards generation. The round-trip check is also your first evaluation set, so build it before you need it.
The chat template trap
This is the bug that eats the first week, and it is entirely
avoidable. The syntax of a tool call is not defined by the model, it
is defined by the model's chat template: the Jinja template shipped
alongside the tokenizer that turns a list of messages into a single
token sequence. Different model families use genuinely different
conventions, some wrapping calls in special tokens, others in tagged
blocks, others in bare JSON.
Your inference server has a matching parser on the other side.
Serving stacks expose it as an explicit choice of tool-call parser,
and that parser is looking for one specific shape. So if you fine-tune
the model to emit a format of your own devising, everything will look
fine in your training logs and then, in production, every call will
arrive as ordinary text in the content field with no tool call
detected. The rule is simple: render your training data through the
exact chat template you will serve with, and do not invent a format.
If you genuinely need new special tokens, they have to be added to the
tokenizer and their embeddings trained, which is a much larger change
than it appears.
Worth pairing with this: constrained decoding, where the serving
layer restricts generation to tokens that keep the output valid
against a grammar or JSON Schema, makes syntactically invalid calls
impossible by construction. It is not a substitute for fine-tuning,
because a call can be perfectly valid and completely wrong, but it is
a good complement. Guarantee the syntax mechanically and let training
spend its capacity on choosing the right tool and the right values.
How much training this actually takes
Less than people assume, because you are not teaching a new
capability. The model already knows how to call tools. You are
teaching it a specific vocabulary and a set of house conventions,
which is a comparatively shallow change, and that has three practical
consequences.
Parameter-efficient methods are usually enough.
LoRA at a modest rank moves this behavior reliably, trains in hours
rather than days, and produces an adapter you can swap or roll back
without touching the base weights. That last property matters more
here than in most fine-tuning work, for reasons the next section
makes clear.
The dataset is smaller than you fear. Hundreds
to a few thousand well-covered examples per tool family, with real
argument diversity, beats a much larger set that repeats the same
three shapes. Coverage of the awkward cases is worth more than
volume.
Smaller models punch above their weight here.
This is a narrow, well-specified job, and a mid-sized open-weight
model trained on your catalogue routinely outperforms a far larger
general model working from prompted schemas, at a fraction of the
serving cost. It is one of the clearer cases where owning a
small language
model beats renting a big one.
Two cautions on the recipe. Mix a slice of general instruction data
into the training mix, or you will trade broad competence for tool
accuracy and notice it only when someone asks the model an ordinary
question. And hold out a few tools entirely, never training on them,
so you can measure whether the model learned to read schemas in
general or merely memorized the ones it saw.
How to evaluate it
Validation loss is close to useless for this. It moves a little
while behavior moves a lot, and it cannot distinguish a call that is
wrong from a call that is right but phrased differently. Measure
outcomes instead, as a ladder of increasingly strict conditions, and
report every rung rather than a single headline number. The rung where
the number collapses tells you what to fix.
Six rungs, reported separately. Most tool-use fine-tunes clear the bottom two immediately and live or die on the middle three.
Break every rung down per tool, too. Aggregate accuracy hides the
pattern that actually matters, which is almost always that four
endpoints out of two hundred account for most of the errors, and they
are the four that look like each other.
Failure modes to plan for
Schema drift. This is the dominant one, and it has
no clever fix. A fine-tuned model has partly memorized the catalogue
it trained against, so when a field is renamed or a new required
argument appears, the model does not error, it produces confident,
well-formed, wrong calls. Internal APIs change on a release cadence
that has nothing to do with your training schedule. Version tool
definitions alongside checkpoints, keep the regeneration pipeline
runnable on demand rather than as a one-off, and treat the adapter as
a build artifact of a specific API version.
Over-triggering. Train mostly on successful calls
and you teach the model that calling is always correct. It starts
reaching for a tool when the answer is three messages up in the
conversation. Counter it in the data, with explicit examples where the
right move is to answer directly or to ask a clarifying question.
Data that encodes the past. Logs record what your
systems did, including through deprecated endpoints and workarounds
that were retired last quarter. Filter log-derived data against the
current specification before training on it, or you will faithfully
teach the model habits your platform team spent a year removing.
Real values in captured arguments. Calls carry
account identifiers, client names, position sizes, and file paths.
A dataset built from logs inherits every one of those, which makes it
as sensitive as the systems it was drawn from and subject to the same
controls. Treat it accordingly from the first extraction, not at
review time.
Grading yourself with your own generator. If your
evaluation set is synthesized by the same pipeline as your training
set, it shares the pipeline's blind spots and will report a number
that production does not reproduce. Keep a held-out set drawn from
real requests, however small.
Why this one is a self-hosted advantage
Most arguments for private AI infrastructure are about the data you
train on. This one is different, because the sensitive artifact is the
tool catalogue itself.
A complete set of your internal tool schemas is a machine-readable
map of how your firm is built: what systems exist, what they are
called, what they accept, which ones talk to which, and, from the
argument names alone, what your data model looks like. Add the
training examples and you have added live values from those systems
and the sequences in which your people actually use them. It is a
more concentrated description of the business than most documents that
go through legal review, and fine-tuning requires assembling it in one
place, in one file, in a format designed to be read by a machine.
There is a second, more mundane reason this work wants to live
inside your walls. Retraining is not a one-time event here. It is tied
to your platform release cadence, because that is what schema drift
is, and a loop you have to run every few weeks needs to be cheap,
fast, and yours. Owning the pipeline turns a schema change into a
short job that regenerates the affected slice of the dataset and
swaps a LoRA adapter. Renting it turns the same change into a
procurement conversation.
Where this fits at Numerata
P95 is the training layer this work
runs on: many small runs rather than one large one, grouped and
tracked so you can tell which change to the dataset moved which rung
of the ladder, with the regeneration and rejection-sampling loops as
ordinary jobs rather than scripts on somebody's laptop. It draws GPUs
from the pool Lupine manages and hands
finished adapters to NinetyFive to serve,
which is also where the tool-call parser lives, so the format you
trained against and the format you parse are configured in the same
place. All of it installs inside your own environment: private cloud,
on-premises, or fully air-gapped. The schemas, the call logs, and the
weights never leave.
If you are working through the neighbouring questions:
agentic
fine-tuning covers what happens once single calls are reliable and
you need the model to string them together over many turns,
synthetic data for
fine-tuning goes deeper on building the dataset itself when there
is no labelled corpus to start from, and
SFT vs.
RL covers how to choose a training objective once you have a grader. For the task set that grader runs
against, which is the only honest way to tell whether any of
this worked, see evaluating agents. For the ground floor, see
what fine-tuning is, and
for the constraint that shapes all of this,
air-gapped LLM
training.