Lecture 5 · COMS W4995 Agentic Engineering

Agent tools in pictures

Chapter three

How a call happens

Follow one get_weather call from the definition you write, through the tokens the model sees, to the harness that runs it, and learn what each step does and does not guarantee.

After this chapter you can

  1. Trace one tool call from its definition, through the token stream, to dispatch and back to the model as a result.
  2. Say what constrained decoding and strict schemas guarantee, and name what they leave unchecked.
  3. Explain what call IDs do, and decide when two calls in one turn may run in parallel.
  4. Compare text, JSON and code as ways to write an action, and say what code costs you.
  5. Write an error message a model can act on, and judge a tool definition generated from code.

22 minutes to read12 sections10 quiz questions

Start reading →

You ask an assistant, “What’s the weather in Pittsburgh?” The model does not open a weather service. Instead, it writes a few dozen tokens that request: run get_weather for Pittsburgh. A program around the model handles the rest. In CMU’s example, the answer that comes back is “72°F · sunny”10. This chapter will follow that single call through every stage, slowly. The lecture skips this layer, but you will need it the first time a call fails and you have to find out where.

Section 3.1

The definition object

A tool definition for get_weather has three parts: a name the model writes in its call, a plain-language description the model reads to decide when to call, and a JSON Schema for the inputs that a program can check. Field What you write Who uses it name get_weather The label the model writes in its call description Get the current weather for a city. Use when the user asks about weather. The model reads this to decide when to call input_schema { "type": "object", "properties": { "city": { "type": "string" } }, "required": ["city"] } A program can check the arguments against it; the model reads it too All three are turned into text and placed in the model's context.
Three fields, three jobs. The name is what the model writes; the description is what it reads to decide; the schema is what a program can check.

We start with a small object you write. Anthropic’s API requires three fields: a name, a description, and an input_schema3. The name “Must match the regex ^[a-zA-Z0-9_-]{1,128}$”, so 1 to 128 letters, digits, underscores, or hyphens3. The description is “A detailed plaintext description of what the tool does, when it should be used, and how it behaves”3. The input schema is “A JSON Schema object defining the expected parameters for the tool”3. OpenAI launched the same idea in June 2023: developers “describe functions to the model via JSON Schema”4.

JSON Schema is a standard language. It defines the required shape of a piece of JSON. It “uses keywords to assert constraints on JSON instances”5. The validation vocabulary provides the keywords you will use most often: type (one of seven types, such as “object”, “string”, or “integer”), enum (the value must equal one item from a list), and required (these property names must be present)6. So the schema in the figure says: give me an object with a property city that is a string, and city is not optional.

A schema checks the shape of data. It cannot determine if a city name is correct. The model chooses between tools by reading their definitions. Claude “determines when to call a tool based on the user’s request and the tool’s description”2. OpenAI states that the definitions are “injected into the system message in a syntax the model has been trained on”, so they “count against the model’s context limit and are billed as input tokens”1. Every word in the definition is text the model reads and pays for on every turn.

Two design habits follow, both from OpenAI’s guide. Make bad calls impossible to write: “Use enums and object structure to prevent invalid states. For example, toggle_light(on: bool, off: bool) allows for invalid calls”1. And do not ask the model for what your code already knows: “Don’t make the model fill arguments you already know”1. Anthropic adds an optional input_examples field, and each example “must be valid according to the tool’s input_schema”, or the request fails with a 400 error3.

Check yourselfWhen the model decides whether to call get_weather, which part of the definition does it actually reason over?

It reasons over all of it, as text in its context1. But the description carries the ‘when’ and the ‘why’; Claude picks tools “based on the user’s request and the tool’s description”2. The schema shapes the arguments once the choice is made.

Section 3.2

Messages become tokens

An API receives a list of messages. Each message has a role, such as system, user, or assistant8. The model itself does not see this list directly. A language model performs one core function: it continues a sequence of tokens. “The list of role and content dictionaries that you pass to a chat model get converted to a token sequence, often with control tokens like <|user|> or <|assistant|>“8. The program that converts the message list into a token sequence is called the chat template. Models use different templates, sometimes even when they were fine-tuned from the same base model8.

It matters which one you use. With the wrong control tokens, Hugging Face warns, “these models would have drastically worse performance”8. The model learned its own markers in training.

Tools ride along the same way. CMU’s slide on tool definitions says it plainly: “The chat template renders it as text”9. Qwen2.5’s template, for example, tells the model that its function signatures are provided inside XML tools tags, and pastes every tool’s JSON between those tags in the system prompt7. It asks the model to answer with a JSON object inside tool_call tags, and it wraps each result that comes back in tool_response tags7.

So a tool call is not a special channel. It is more tokens. CMU puts the two possible continuations side by side: the model can continue with text (“The weather is sunny.”) or with a tool call, and “The model requests an action. The harness executes it.”10

Check yourselfWhere in the token stream are the tool definitions placed, and what is the cost of that placement?

Before the conversation, in the system part of the prompt; Qwen2.5 puts them inside a pair of tools tags7. Because they are ordinary input text, they use context and are billed as input tokens on every turn1.

Section 3.3

Same call, four wire formats

The same get_weather call for Pittsburgh written in four model formats: Qwen 2.5 puts JSON inside tool_call tags, Qwen 3.8 nests function and parameter tags, Mistral Small 4 uses a TOOL_CALLS control token with a call ID, and DeepSeek V3.2 uses a DSML invoke block. Qwen 2.5 · JSON inside tags <tool_call> {"name": "get_weather", "arguments": {"city": "Pittsburgh"}} </tool_call> Qwen 3.8 · nested tags <tool_call> <function=get_weather> <parameter=city> Pittsburgh </parameter> </function> </tool_call> Mistral Small 4 · control token [TOOL_CALLS] [{"name": "get_weather", "arguments": {"city": "Pittsburgh"}, "id": "abc123xyz"}] DeepSeek V3.2 · DSML block <|DSML|function_calls> <|DSML|invoke name="get_weather"> <|DSML|parameter name="city" string="true"> Pittsburgh </|DSML|parameter> </|DSML|invoke> </|DSML|function_calls> One schema, four serializations. Whoever serves the model needs the parser that matches it.
The same call to get_weather for Pittsburgh, as four model families write it.

Take the call get_weather with city Pittsburgh and ask four open models to write it. You get four different strings. Qwen2.5 writes a flat JSON object inside tool_call tags7. Qwen3.8 nests a function= tag and one parameter= tag per argument inside tool_call, and its template warns that “Function calls MUST follow the specified format”11. Mistral’s tokenizer has dedicated control tokens such as TOOL_CALLS and BEGIN_TOOL_RESULTS12; CMU’s example of a Mistral Small 4 call even carries its call ID inside the output10. DeepSeek V3.2 uses a block it calls DSML, where each parameter has a string attribute “set to ‘true’ for string type parameters and ‘false’ for other types”13.

CMU summarizes it in one line: “One schema; model-specific serialization and parsing.”10

Notice that two versions from the same family, Qwen2.5 and Qwen3.8, disagree7,11. Someone has to convert the model’s string back into a function name and arguments. If you call a hosted API, the vendor does this and gives you the tool name and its arguments in JSON format. If you serve an open model yourself, that task is yours. Mistral’s model card shows the serving command with --tool-call-parser mistral14, and DeepSeek’s parsing code says it “will not attempt to correct malformed output that may be generated by the model”13.

Check yourselfYou serve an open model with vLLM, an open-source serving engine. You upgrade from Qwen2.5 to Qwen3.8 but keep the parser you configured for Qwen2.5. What breaks?

The parser expects JSON inside tool_call tags7, but the new model writes nested function and parameter tags11. The calls no longer parse, so the harness either sees an error or mistakes the call for plain text.

Section 3.4

IDs, parallel calls and dependencies

Before and after: three tool calls made one per turn take about 6.4 seconds, because the model generates four times and each tool waits its turn; made in parallel in one turn they take about 2.8 seconds, two model passes plus the slowest tool. Serial: one call per turn 6.4 s Model Weather Model Calendar Model Flights Model Parallel: three independent calls in one turn 2.8 s Model Weather Calendar Flights Model 0 s 2 s 4 s 6 s CMU's worked example: model passes of 0.8 s; tools of 1.2, 0.9 and 1.1 s. Parallel = 2 × 0.8 + 1.2.
Before and after. Three independent calls, one per turn, then all in one turn.

Suppose the user asks for the weather, today’s calendar and a flight status. CMU works the arithmetic. One call per turn means four model passes of 0.8 s plus three tools of 1.2, 0.9 and 1.1 s, about 6.4 seconds10. If the model asks for all three in one turn, the harness runs them together: “Two generation phases plus the slowest tool: 2 × 0.8 + max(1.2, 0.9, 1.1) ≈ 2.8 seconds”10.

Running three calls at once creates a bookkeeping problem. The results can come back in any order. How does the model know which “72°F” belongs to which request? A call ID. In OpenAI’s Chat Completions API, the assistant message holds an array of tool_calls, each with an id, and each result is a message with role tool and a tool_call_id, the “Tool call that this message is responding to”15. Anthropic’s tool_use block carries an id the same way3. In CMU’s words, “tool_call_id matches results to calls, including parallel calls.”10

Parallel is not always allowed. CMU’s rule is short: “Parallelize only independent, safely concurrent calls.”10 Its counterexample is a refund: find the customer ID, use it to fetch orders, then refund the selected order10. Each step needs the output of the one before, so they must run in order. Independence is about data, and also about side effects. Two calls that each write the same database row do not depend on each other’s output, but running them at once means the final value depends on which finished last.

Check yourselfIn a single turn, the model emits two calls that both update the same customer’s address. Should the harness run these in parallel?

No. They are not “safely concurrent”10: the result depends on timing. Run them one after the other, or better, ask why the model wants two writes to one record.

Section 3.5

Inside the harness: dispatch

The harness handles a call in six steps: parse the arguments, look up the tool by name, validate the arguments, check permission, execute, and return an observation with the call ID. Each step has its own failure, and every failure also goes back to the model as a message. The happy path Parse Look up Validate Permission Execute Observe Arguments are not valid JSON Unknown name: list the tools that exist Missing or wrong-typed argument A rule or hook refuses a valid call The tool itself fails or times out Every outcome returns to the model as a message tied to the call ID
Six steps from the model’s string to a result, as this book draws them. The orange box, permission, is a separate step from validation.

Stanford’s slide names four stages of a call: Select and Arguments, where the model picks a tool and fills its fields, then Validate and Execute18. After the model returns its string, the harness owns the rest. OpenHands, an open-source agent platform19, shows the harness’s part in its code. Its dispatcher parses the argument string using parse_tool_call_arguments, normalizes the tool name, and finds it in a tools_map16. CMU’s walkthrough of this same code explains that it then validates with action_from_arguments(args), runs executor(action), and returns an ObservationEvent stamped with the call ID10. The OpenHands documentation describes the middle step as an “Execution Lifecycle - Validate inputs, execute logic, wrap outputs”20.

Why validate at all, if the API already returned JSON? Because nothing guarantees it is the JSON you asked for. OpenAI’s own API reference says the model “does not always generate valid JSON, and may hallucinate parameters not defined by your function schema. Validate the arguments in your code before calling your function.”15

Validation is not the same as permission. A call to delete a branch can be perfectly valid, but you might never want it to run. In OpenHands, permission is a separate step. A hook can block an action, and the model then receives a UserRejectObservation with rejection_source="hook"16. The codebase is careful about what it does not know. Its ToolAnnotations defaults to destructiveHint true, and idempotentHint defaults to false21. The defaults are the cautious ones: unless a tool’s author says otherwise, its hints say it may destroy data and is not safe to repeat.

One more job sits inside dispatch: the error message. Every failure in the figure goes back to the model as text, so write that text for its reader. Anthropic advises: “prompt-engineer your error responses to clearly communicate specific and actionable improvements, rather than opaque error codes or tracebacks”17. Compare two replies to a call that omitted the city. KeyError: 'city' tells the model that something broke. “Missing required argument city: give a city name such as Pittsburgh” tells it what to do next. OpenHands follows this pattern when a name is unknown; it responds with “Tool ‘{tool_name}’ not found. Available: {available}”16.

Errors are also a signal to you. Anthropic again: “lots of tool errors for invalid parameters might suggest tools could use clearer descriptions or better examples”17. When it launched Claude’s web search tool, Claude kept appending “2025” to its queries, and the fix was a better description17.

Check yourselfYour harness receives a call to get_wether, which is a misspelling. Write the error message you would return.

Name the problem and provide a solution. OpenHands returns Tool '{tool_name}' not found. Available: {available}, with the correct names filled in16. A bare status code or a traceback is exactly what Anthropic warns against: “opaque error codes or tracebacks”17.

Section 3.6

Making output parse

Token masking in four steps: the model scores eight candidate tokens, the grammar sets the forbidden ones to minus infinity, softmax turns the rest into probabilities, and the sampler picks token 3, the most likely allowed one. token 0 token 1 token 2 token 3 token 4 token 5 token 6 token 7 Logits 3 −2 4 5 1.1 3.2 −5 −1 Masked 3 −∞ −∞ 5 −∞ 3.2 −∞ −1 Probability 0.1 0 0 0.76 0 0.13 0 0.001 The grammar allows tokens 0, 3, 5 and 7 here; the rest get −∞. Softmax: forbidden tokens now have probability 0. The sampler draws from what is left: here, token 3. Numbers from CMU's Lecture 2 slide on token masking.
Token masking. The grammar removes forbidden tokens before the sampler ever sees them.

Here is a failure the harness cannot fix: the model writes {"name":"get_weather", "arguments":{"city":"Pittsburgh"} and stops. One closing brace is missing. CMU’s slide: “The harness cannot parse it.”10 Stanford frames the goal behind every fix for this: “Require a shared contract of how these system components will communicate.”22 A program downstream needs output it can read.

You can ask the model in the prompt to format its output correctly. Or you can make it impossible for the model to produce incorrect output. This second method is constrained decoding. At each step, the model assigns a score, called a logit, to every token in its vocabulary. Before the model samples a token, the decoder checks each candidate token against a grammar. It sets the score of any token that would break the grammar to minus infinity10. After softmax, the step that turns scores into probabilities, those tokens have a probability of zero. In CMU’s example, the model considers eight candidate tokens. Four of them survive the grammar check. The sampler then picks token 3, which is the most likely survivor10. The model cannot write output with a missing brace because, at the end, only tokens that correctly close the object remain.

Why is this hard to do fast? JSON nests. To know whether ] is allowed, you must know whether you are inside an array, inside an object inside an array, and so on, to any depth. A finite list of states is not enough; you need a stack. CMU: “The stack remembers arbitrarily deep nesting.”10 A grammar engine with a stack is a pushdown automaton. And a single token can span several grammar symbols, or end in the middle of one, so the check runs on the token’s bytes, below tokenization10.

The engines differ in how they avoid checking the entire vocabulary at every step:

  • XGrammar divides “the vocabulary into context-independent tokens that can be prechecked and context-dependent tokens that need to be interpreted during runtime”23. For Llama 3.1 with a JSON grammar, only 1,134 of about 128,000 tokens were context-dependent, and a further trick cut them to 12023.
  • SGLang builds “a compressed finite-state machine”, so that when only one path is possible it can decode several tokens at once24. Guidance does the same in spirit: tokens the grammar already determines are inserted without asking the model25.
  • LMQL compiles constraints into token masks26. Guidance notes that “A JSON schema is actually a context free grammar”25.
  • vLLM exposes five modes, choice, regex, json, grammar and structural_tag, on top of XGrammar or Guidance27.

Treat the speed numbers as best-case scenarios, because each paper states this. XGrammar reports “up to 100x speedup over existing solutions”23. SGLang reports “up to 6.4× higher throughput” for its whole runtime, across tasks from JSON decoding to multi-turn chat, not for constrained decoding alone24. LMQL reports “an up to 80% reduction of inference cost”26.

A related idea operates at a higher level. In DSPy, a signature is a “natural-language typed declaration of a function”: you declare the types for inputs and outputs, and the framework manages “structured formatting and parsing logic”28.

Check yourselfWhy can’t a fixed list of allowed next characters enforce JSON?

A fixed list cannot enforce JSON because what can come next depends on how deeply you are nested. Nesting has no fixed limit. The checker needs a stack, which is what a pushdown automaton adds10.

Section 3.7

Form is not truth

Four gates a tool call must pass: valid, meaning it parses and fits the schema; correct, meaning the values are right for the task; safe, meaning the call is allowed; and done, meaning the task actually finished. Constrained decoding and strict mode only guarantee the first gate. Valid Parses and fits the schema? Fails like "units": "K" Checked by constrained decoding, strict mode Correct Right tool, right values? Fails like "city": "Philly" asked for Pittsburgh Checked by your tests and evaluation Safe Allowed to run here, now? Fails like a well-formed delete on the wrong system Checked by the harness: rules, hooks, credentials Done Did the task finish? Fails like no tool called; stopped halfway Checked by evaluating the outcome Constraints guarantee only this gate. The other three need other checks.
Four gates. Constraints guarantee the first one only.

Constrained decoding solves a real problem, but it is easy to think it solves more. CMU lists what constraints can enforce: syntax (valid JSON), shape (the expected fields), types (city is a string), and values (units is C or F)10. The slide then adds a sentence to remember: “Constraints ensure form—not truth, permissions, tool choice, or task success.”10

Walk the four gates in the figure with one example. A call with city Pgh and units K fails the first gate because K is not in the enum10. If you change the units to F, it becomes valid. But if the user asked about Pittsburgh and the model wrote another city, the call is valid but wrong. A valid, correct call to delete production data can still be one the harness should refuse. And a run where every call was valid and correct can still end before the job is done.

The benchmarks show this gap clearly. In MCP-Bench, five strong models (o3, gpt-5, gpt-oss-120b, qwen3-235b-a22b-2507, and gpt-4o) were “all surpassing 98% in schema compliance and valid tool naming”29. The authors concluded: “while basic execution has largely converged, planning and reasoning capabilities remain the key differentiators among models”29. MCP-Atlas, a different benchmark of 1,000 tasks on 36 real MCP servers, broke down why runs failed30. Averaged across the models it tested, the largest share of failures was “No tools called” at 36.0%, then “Partial task completion” at 25.8%; “Incorrect tool parameters” was 14.2%30. Its public leaderboard’s top pass rate is 83.6%, after an April 2026 rescoring31. These are separate benchmarks with separate methods, so read them side by side, not as one combined number. Together, they say the same thing: getting the format right is mostly solved; deciding to act and finishing the task is not.

Check yourselfA call passes strict schema validation. What do you still not know?

You do not know whether the model picked the right tool, whether the values are right for this user, whether the harness should allow it, and whether the task will finish. Constraints “ensure form—not truth, permissions, tool choice, or task success”10.

Section 3.8

Vendor switches: strict mode and tool_choice

This section provides reference material. Vendors often change these switches, so check the current documentation before you rely on any detail here.

Strict mode is the hosted switch for the same goal as constrained decoding: arguments that reliably fit your schema. OpenAI: “Setting strict to true will ensure function calls reliably adhere to the function schema, instead of being best effort.”1 Two details catch people. A strict request whose schema does not meet the requirements “will be rejected with details about the missing constraints”1. And the default differs by API: Responses requests try to normalize a schema into strict mode, while “Chat Completions requests remain non-strict by default”1. Gemini’s equivalent is a tool_choice mode called validated: “Model ensures function schema adherence”33.

tool_choice determines whether the model can, must, or must not call a tool. Gemini documents four values: auto (the model decides), any (it must call a function), none (it may not), and validated33. Forcing a tool call has side effects. On Anthropic’s API, setting any or tool makes the API prefill the assistant message, so the model “will not emit a natural language response or explanation before tool_use content blocks”3. Anthropic’s newest models go further. On Claude Fable 5.1 and Mythos 5.1, “tool_choice types any and tool aren’t supported and return a 400 error”32. Opus 5.5 and Sonnet 5.5 return the same 400, and the Opus 5.5 entry advises: “use auto with strict tool use”32.

Forcing can also create a loop. OpenAI’s Agents SDK release of March 2025 listed “Reset tool use behavior when tool_choice is set” among its key changes, with a fix titled “Fix potential infinite tool call loop by resetting tool_choice after …”34. The likely mechanism is simple: if every turn must call a tool, the model can never stop and answer.

Check yourselfYou are porting lab code that sets tool_choice to any from an older Claude model to Claude Opus 5.5. What happens, and what do you change?

The request fails with a 400 error32. Switch to auto and enforce the argument shape with strict tool use, as the release notes advise32.

Section 3.9

Text, JSON or code as the action

Three ways to write the same action: as text, a tool name plus one input string; as JSON, a name plus typed arguments; and as code, a short program that can loop over many cities and call the tool many times in one action, which also means it needs a sandbox. Text get_weather Pittsburgh JSON {"name": "get_weather", "arguments": {"city": "Pittsburgh"}} Code t = {c: get_weather(c) for c in cities} best = max(t, key=t.get) A name and one string; one tool per turn Typed arguments a program can check Loops and variables; many calls in one action; harder to check, so it needs a sandbox
The same intent as a string, as a JSON call, and as a short program.

So far, every call we have made has used a JSON object. This is one of three ways to format calls. Early agents used plain text. LangChain’s 2023 post describes this: a model “would generate two strings: 1. A tool name 2. An input string for the chosen tool”, which “confined the agent to one tool per turn”35. JSON then added typed arguments. The third option is to let the model write code.

CodeAct, a 2024 paper, proposed “executable Python code to consolidate LLM agents’ actions into a unified action space”36. It compared text, JSON, and code across 17 models. On API-Bank, code was the best format for 8 of the 17, JSON for 5, and text for 436. On the authors’ own benchmark, M3ToolEval, code had “a higher task success rate (12 out of 17 evaluated LLMs)”36. The best model, gpt-4-1106-preview, gained “a 20.7% absolute improvement compared to the next best action format (text) while requiring 2.1 fewer interaction turns on average”36. CMU’s slide shows a subset of eight models where code wins for all eight10; the full picture is the 8 of 17 and 12 of 17 mentioned above. All of these are models from 2022 to 2024, several generations before today’s.

Why might code be helpful? Look at the figure. If you need the weather for twenty cities and then the warmest, JSON means twenty calls. Even sent in parallel, all twenty results come back into the model’s context before it can compare them. With code, it is one short loop that also does the comparison10. Code also provides error messages for free: CodeAct lets the model use “automated feedback (e.g., error messages) implemented in most software to improve task-solving by self-debugging”36. This idea is older than CodeAct. In 2022, Code as Policies had models write robot policy code that processes “perception outputs” and calls “control primitive APIs”37.

The price is control. CMU calls code “a high-power tool”: expressive, but “Validation is less precise than for a narrow typed function”, and it can touch files, network and processes10. Its prescription: “Use sandboxing, resource limits, permissions, and audit logs.”10 A JSON call is easy to check against a schema. A program can do anything its sandbox allows.

Check yourselfImagine a task that needs the weather in 20 cities, then the warmest one. Which action format does it in the fewest actions, and what must you add before you allow it?

Code: one loop that fetches all twenty and picks the warmest, in one action. Before you allow it, add a sandbox with resource limits, permissions, and audit logs, because code is harder to constrain than a typed call10.

Section 3.10

Definitions written from code

Most libraries generate the schema from your function. Hugging Face’s agents course shows the trick. A @tool decorator reads the function’s signature and docstring and produces “Tool Name: calculator, Description: Multiply two integers., Arguments: a: int, b: int, Outputs: int”39. All it needs is “that the tool implementation uses type hints, docstrings, and sensible function names”39. LangChain’s StructuredTool.from_function “infers the args_schema from the function’s signature”35. OpenHands auto-generates “LLM-compatible tool descriptions from Pydantic schemas”20. FastAPI generates an OpenAPI schema for your whole web API, using JSON Schema for the data40.

Schema generation copies what you wrote. A one-line docstring becomes a one-line description. This description is what the model reads to make its choice. FastMCP provides a clear warning. It can convert any OpenAPI spec into tools for an agent. By default, it converts “every endpoint” into a tool38. Its documentation states: “in practice LLMs achieve significantly better performance with well-designed and curated MCP servers than with auto-converted OpenAPI servers”38. It recommends generating servers this way for “bootstrapping and prototyping, not for mirroring your API to LLM clients”38.

Check yourselfYou auto-convert a 40-endpoint internal API into 40 tools. Name two problems before you even run it.

The model now chooses among 40 tools, double OpenAI’s soft suggestion of “fewer than 20 functions available at the start of a turn”1. And each description is only as good as the docstring or OpenAPI summary it came from; FastMCP itself says curated servers do “significantly better”38.

Section 3.11

How models learned to call tools

We will skip training in this lecture, so this section is short. This work comes from 2023 and 2024.

Toolformer, in February 2023, taught a model to call tools on its own. The model added possible API calls to text. A call was kept only if it helped by the paper’s test: “An API call is helpful to M if providing it with both the input and the output of this call makes it easier for the model to predict future tokens, compared to not receiving the API call at all, or receiving only its input”42. At run time, decoding pauses when it hits a special token. The API runs, and then decoding continues with the result inserted42.

Gorilla (May 2023) fine-tuned a LLaMA-based model to write API calls and reported that it “surpasses the performance of GPT-4 on writing API calls”43. It also gave a useful definition: a hallucination is “an API call that is not a sub-tree of any API in the database – invoking an entirely imagined tool”, which is different from calling a real API incorrectly43. ToolLLM (July 2023) scaled the data up, collecting “16,464 real-world RESTful APIs spanning 49 categories from RapidAPI Hub”44.

Live APIs created a measurement problem. StableToolBench re-ran ToolBench, which calls real online APIs, and found “a notable decline in the performance of all methods over time”41. The status of the live APIs kept changing, which affected the results41. StableToolBench’s fix was a virtual API server, which allowed for reproducible results41.

Check yourselfWhy did scores on a benchmark built on live public APIs drop over time, even though no model got worse?

The APIs themselves changed or became unavailable. This meant the same method faced a different environment41. A tool benchmark measures both the model and the tools together.

Before you go on

Close the book

  1. Without looking, trace the get_weather call. Start from the definition you write and follow it to the “72°F” the model reads. Name each stage. For each stage, describe one way the call can fail there.
  2. A teammate says, “We turned on strict mode, so tool calls are reliable now.” Write a two-sentence reply. Explain what strict mode guarantees and what it leaves to you.
  3. Pick a tool from your own project. Write the error message it would show for a missing argument. Then, decide if two calls to this tool could safely run in parallel in the same turn.

Chapter three · quiz

Test yourself

Most questions have more than one right answer, so select every option that is true. Check each question, then open “Answer and why” for the reasoning.

Question 1A user asks about the weather and the model calls get_weather. Which of these happen before the model writes the call?Select all that apply.
Answer and why
  • RightThe tool’s name, description and schema are rendered as text into the model’s input. The chat template renders the definition as text9; OpenAI injects it into the system message1.
  • RightThe chat template turns the message list into one token sequence. Roles and content are converted to a token sequence with control tokens8.
  • WrongThe harness checks whether the user has permission for get_weather. Permission is checked after the model writes a call, during dispatch16.
  • WrongThe model runs a weather lookup to decide whether a call is worth it. The model never runs a tool; it only writes the request33.
  • RightThe definitions are counted as input tokens. Callable function definitions count against the context limit and are billed as input tokens1.
Question 2You move an open-weights agent from Qwen2.5 to Qwen3.8 on your own vLLM server. Which statements are true?Select all that apply.
Answer and why
  • RightThe two models write tool calls in different formats. Qwen2.5 writes JSON in tool_call tags7; Qwen3.8 nests function and parameter tags11.
  • WrongThe JSON Schema of your tools must be rewritten for the new model. One schema serves both; only serialization and parsing are model-specific10.
  • RightThe server’s tool-call parser must match the model. Serving engines need a matching parser, for example –tool-call-parser mistral for Mistral14.
  • RightUsing the wrong control tokens can badly hurt the model’s performance. Hugging Face warns of drastically worse performance with the wrong control tokens8.
  • WrongA hosted API user would have to write this parser too. A hosted API returns calls already parsed, as structured tool_calls objects15; parsing raw model output is the job of whoever serves the model14.
Question 3In one turn the model asks for three calls. Which pairs can safely run in parallel?Select all that apply.
Answer and why
  • RightWeather for Pittsburgh and today’s calendar. Independent reads; CMU lists them as safe to parallelize10.
  • WrongFind the customer ID, and fetch that customer’s orders. The second needs the first one’s output, so they must be sequenced10.
  • RightLook up a flight status and read today’s calendar. Neither depends on the other or changes shared state10.
  • WrongTwo updates to the same customer’s address. CMU’s rule is to parallelize only safely concurrent calls10; two writes to one record race, and the final value depends on which finishes last.
  • WrongFetch orders, and refund the order selected from them. The refund depends on the fetch10.
Question 4Why does each tool result carry a tool_call_id?Select all that apply.
Answer and why
  • RightSo results that come back in any order can be matched to their calls. tool_call_id matches results to calls, including parallel calls10.
  • WrongSo the vendor can bill each tool separately. The ID names the tool call a message responds to15; it is for matching, not billing.
  • RightSo the model knows which request a given result answers. The tool message names the tool call it is responding to15.
  • WrongSo the harness can skip validating the arguments. Arguments must still be validated; the model may produce invalid JSON or invented parameters15.
Question 5Your server uses constrained decoding with the tool’s JSON Schema, where units is an enum of C and F. Which outputs can no longer happen?Select all that apply.
Answer and why
  • RightA call missing its closing brace. Tokens that break the grammar are masked out before sampling10.
  • Rightunits set to K. Values are part of what constraints enforce: units is C or F10.
  • Wrongcity set to the wrong city for this question. Constraints ensure form, not truth10.
  • WrongA call to get_weather when the user wanted the calendar. Tool choice is outside what constraints guarantee10.
  • WrongNo tool call at all when one was needed. Constraints shape a call once it is written; in MCP-Atlas, calling no tool was the largest failure category30.
Question 6In MCP-Bench, five strong models passed 98% schema compliance. What can you conclude?Select all that apply.
Answer and why
  • RightWriting well-formed calls is close to solved for strong models. The authors say basic execution has largely converged29.
  • WrongThose models complete about 98% of tasks. Schema compliance is not task success; planning and reasoning remain the differentiators29.
  • RightWhat separates the models is planning and reasoning, not writing well-formed calls. The MCP-Bench authors conclude that basic execution has largely converged and planning and reasoning remain the key differentiators29.
  • WrongYou can drop argument validation in your harness. OpenAI still tells you to validate arguments in your code before calling the function15.
  • WrongMCP-Bench and MCP-Atlas numbers can be added into one score. They are separate benchmarks; MCP-Atlas’s leaderboard was also rescored in April 202631.
Question 7A call arrives with the city argument missing. Which replies help the model recover?Select all that apply.
Answer and why
  • RightMissing required argument city: give a city name such as Pittsburgh. Specific and actionable, as Anthropic advises17.
  • WrongKeyError: ‘city’ with a full traceback. Anthropic warns against opaque error codes or tracebacks17.
  • WrongError 400. An opaque code gives the model nothing to act on17.
  • WrongSilently calling the tool with a default city. The model never learns its call was incomplete, and the answer may be for the wrong place; validation exists to catch this15.
Question 8What did CodeAct report about writing actions as code?Select all that apply.
Answer and why
  • RightCode was the best format for 12 of 17 models on M3ToolEval. CodeAct had a higher task success rate for 12 of 17 evaluated models36.
  • WrongCode won for every model on every benchmark. On API-Bank it was best for 8 of 17; CMU’s 8 of 8 is a displayed subset10,36.
  • RightCode let the best model finish in fewer turns. gpt-4-1106-preview needed 2.1 fewer turns on average36.
  • RightThe models tested are several generations older than today’s. The 17 models include gpt-4-1106-preview, claude-2 and text-davinci-00310,36.
  • WrongCode needs no extra controls because it is more expressive. CMU pairs code with sandboxing, resource limits, permissions and audit logs10.
Question 9Your agent must compare the weather in 30 cities and report the warmest. Which action format fits best, and with what condition?Select one.
Answer and why
  • WrongText: a tool name and one input string per turn. Text tools were confined to one tool per turn35.
  • WrongJSON calls, one city per call. It works, and parallel calls cut the waiting, but all thirty results still pass through the model before it can compare them10.
  • RightCode, run in a sandbox with limits and audit logs. A loop handles all cities in one action; code is high-power, so it needs a sandbox10.
  • WrongCode, run directly on the server for speed. Code can touch files, network and processes; CMU says to sandbox it10.
Question 10You generate tool definitions from code. Which statements are true?Select all that apply.
Answer and why
  • RightA decorator can build the name, description and arguments from the signature and docstring. Hugging Face’s @tool reads type hints and the docstring39.
  • RightThe generated description is only as good as the docstring. The decorator copies the docstring into the description39.
  • WrongAuto-converting every endpoint of a large API works as well as a curated server. FastMCP says curated servers perform significantly better38.
  • WrongGeneration removes the need to validate arguments at run time. The model can still invent parameters; validate before calling15.
  • RightFastAPI can produce an OpenAPI schema that tools can be generated from. FastAPI generates an OpenAPI schema using JSON Schema40; FastMCP converts OpenAPI specs into tools38.
  • WrongConverting a 40-endpoint API one-to-one keeps you under OpenAI’s suggested tool count. OpenAI’s soft suggestion is fewer than 20 functions at the start of a turn1.

Sources

  1. OpenAI Function Calling Guide. OpenAI. developers.openai.com/api/docs/guides/function-calling ↗
  2. Tool use with Claude - Claude Platform Docs. Anthropic. platform.claude.com/docs/en/agents-and-tools/tool-use/overview ↗
  3. Define tools. Anthropic. platform.claude.com/docs/en/agents-and-tools/tool-use/implement-tool-use ↗
  4. Function calling and other API updates. OpenAI. openai.com/index/function-calling-and-other-api-updates ↗
  5. JSON Schema 2020-12. json-schema.org/draft/2020-12/json-schema-core.html ↗
  6. JSON Schema 2020-12 Validation. json-schema.org/draft/2020-12/json-schema-validation.html ↗
  7. Qwen2.5 Chat Template. huggingface.co/Qwen/Qwen2.5-7B-Instruct/blob/main/tokenizer_config.json ↗
  8. Hugging Face Chat Templates. huggingface.co/docs/transformers/chat_templating ↗
  9. CMU 11-768 Lecture 1: What Are Agents? And How Do They Work?. Daniel Fried, Graham Neubig. www.cmu-agents.com/slides/lecture-01-agents.pdf ↗
  10. Tool Use for Language Model Agents (Lecture 2 slides). CMU LTI / Graham Neubig. www.cmu-agents.com/slides/lecture-02-tool-use.pdf ↗
  11. Qwen3.8 Tool-Call Template. huggingface.co/Qwen/Qwen3.8-27B/blob/main/tokenizer_config.json ↗
  12. Mistral Tool-Call Tokenization Guide. github.com/mistralai/cookbook/blob/main/concept-deep-dive/tokenization/tool_calling.md ↗
  13. DeepSeek V3.2 Tool-Call Encoding. huggingface.co/deepseek-ai/DeepSeek-V3.2/blob/main/encoding/encoding_dsv32.py ↗
  14. Mistral Small 4 Model Card. huggingface.co/mistralai/Mistral-Small-4-119B-2603 ↗
  15. OpenAI Chat Completions API. platform.openai.com/docs/api-reference/chat/create ↗
  16. OpenHands Agent Dispatch Source. github.com/OpenHands/software-agent-sdk/blob/main/openhands-sdk/openhands/sdk/agent/agent.py ↗
  17. Writing effective tools for AI agents—using AI agents. Anthropic. www.anthropic.com/engineering/writing-tools-for-agents ↗
  18. Stanford CS329Z Lecture 1: Intro to Agentic Systems. Diyi Yang, Michael Ryan, John Yang. web.stanford.edu/class/cs329z/slides/lecture01.pdf ↗
  19. Wang et al. "OpenHands: An Open Platform for AI Software Developers." ICLR (2025).. arxiv.org/abs/2407.16741 ↗
  20. OpenHands Tool System Architecture. docs.openhands.dev/sdk/arch/tool-system ↗
  21. OpenHands ToolDefinition Source. github.com/OpenHands/software-agent-sdk/blob/main/openhands-sdk/openhands/sdk/tool/tool.py ↗
  22. Stanford CS329Z Lecture 2: LLMs for Builders. Diyi Yang, Michael Ryan, John Yang. web.stanford.edu/class/cs329z/slides/lecture02.pdf ↗
  23. XGrammar. arxiv.org/abs/2411.15100 ↗
  24. SGLang / RadixAttention. arxiv.org/abs/2312.07104 ↗
  25. guidance: a guidance language for controlling large language models. guidance-ai (GitHub). github.com/guidance-ai/guidance ↗
  26. Prompting Is Programming: A Query Language for LLMs (LMQL). Beurer-Kellner et al. (2022). arxiv.org/abs/2212.06094 ↗
  27. vLLM structured outputs. vLLM docs. docs.vllm.ai/en/latest/features/structured_outputs.html ↗
  28. Khattab et al. "DSPy: Compiling Declarative Language Model Calls into Self-Improving Pipelines." ICLR (2024).. arxiv.org/abs/2310.03714 ↗
  29. MCP-Bench: Benchmarking Tool-Using LLM Agents with Complex Real-World Tasks via MCP Servers. Wang et al. (Accenture). arxiv.org/abs/2508.20453 ↗
  30. MCP-Atlas: A Large-Scale Benchmark for Tool-Use Competency with Real MCP Servers. Scale AI. arxiv.org/abs/2602.00933 ↗
  31. Scale Labs Leaderboard: MCP Atlas. Scale AI. labs.scale.com/leaderboard/mcp_atlas ↗
  32. Claude Platform release notes (round-1 source, A29 URL). Anthropic. platform.claude.com/docs/en/release-notes/overview ↗
  33. Function calling with the Gemini API - Interactions API | Google AI for Developers. Google. ai.google.dev/gemini-api/docs/function-calling ↗
  34. Release v0.0.7 · openai/openai-agents-python. OpenAI (GitHub release). github.com/openai/openai-agents-python/releases/tag/v0.0.7 ↗
  35. Structured Tools. LangChain. www.langchain.com/blog/structured-tools ↗
  36. Executable Code Actions Elicit Better LLM Agents. Wang et al.. arxiv.org/abs/2402.01030 ↗
  37. Code as Policies. arxiv.org/abs/2209.07753 ↗
  38. FastMCP OpenAPI Integration. gofastmcp.com/integrations/openapi ↗
  39. What are Tools?. Hugging Face. huggingface.co/learn/agents-course/en/unit1/tools ↗
  40. FastAPI Generated OpenAPI. fastapi.tiangolo.com/tutorial/first-steps ↗
  41. StableToolBench: Towards Stable Large-Scale Benchmarking on Tool Learning of LLMs. Guo et al.. arxiv.org/abs/2403.07714 ↗
  42. Toolformer: Language Models Can Teach Themselves to Use Tools. arXiv (Schick et al.). arxiv.org/abs/2302.04761 ↗
  43. Gorilla: Large Language Model Connected with Massive APIs. arXiv (Patil et al., UC Berkeley). arxiv.org/abs/2305.15334 ↗
  44. ToolLLM: Facilitating Large Language Models to Master 16000+ Real-world APIs (abstract). arXiv (Qin et al.; ICLR 2024). arxiv.org/abs/2307.16789 ↗