aillmjson-schemaagents

Function Calling & Tool Use: Writing JSON Schemas for AI Agents

·10 min read·AI & JSON

What function calling really is

"Function calling" (OpenAI) and "tool use" (Anthropic) are the same idea: you describe a set of functions to the model using JSON Schema, and instead of answering in prose, the model responds with a structured request to call one of them — including a JSON object of arguments that conforms to your schema.

Your code then runs the real function and (optionally) feeds the result back so the model can continue. The model never executes anything itself; it only decides *which* function to call and *with what arguments*.

This makes JSON Schema the contract between the model and your code. A good schema produces reliable calls; a vague one produces hallucinated or malformed arguments.

Anatomy of a tool definition

json
{
  "name": "get_weather",
  "description": "Get the current weather for a city. Use when the user asks about weather conditions.",
  "input_schema": {
    "type": "object",
    "properties": {
      "city": {
        "type": "string",
        "description": "City name, e.g. 'Ahmedabad' or 'Tokyo'"
      },
      "unit": {
        "type": "string",
        "enum": ["celsius", "fahrenheit"],
        "description": "Temperature unit. Default celsius."
      }
    },
    "required": ["city"]
  }
}

Three parts do the heavy lifting:

  • `name` — short, verb-based, machine-friendly.
  • `description` — the most underrated field. Describe *what the function does and when to use it*. The model reads this to decide whether to call the tool.
  • `input_schema` — the JSON Schema for arguments, with per-property descriptions.

Writing schemas the model can satisfy

  • Describe every property. A description on each field dramatically reduces wrong arguments.
  • Use enums for closed sets. "status": {"enum": ["open", "closed"]} prevents the model from inventing values.
  • Mark only what you truly need as `required`. Over-requiring forces the model to guess.
  • Constrain formats. Use "format": "date", "format": "email", or pattern so arguments arrive parseable.
  • Avoid free-form objects. "type": "object" with no properties invites arbitrary, unpredictable shapes.

Example: a multi-tool agent

python
tools = [
  {
    "name": "search_orders",
    "description": "Search a customer's orders by status or date range.",
    "input_schema": {
      "type": "object",
      "properties": {
        "customer_id": {"type": "string"},
        "status": {"type": "string", "enum": ["pending", "shipped", "delivered", "cancelled"]},
        "since": {"type": "string", "format": "date"}
      },
      "required": ["customer_id"]
    }
  },
  {
    "name": "issue_refund",
    "description": "Issue a refund for a specific order. Only call after confirming with the user.",
    "input_schema": {
      "type": "object",
      "properties": {
        "order_id": {"type": "string"},
        "amount": {"type": "number"},
        "reason": {"type": "string"}
      },
      "required": ["order_id", "amount", "reason"]
    }
  }
]

Notice how issue_refund uses its description to add a guardrail ("Only call after confirming"). Descriptions are policy, not just documentation.

The call loop

  1. Send the user message plus your tool definitions.
  2. The model returns a tool call with JSON arguments.
  3. Validate the arguments against the schema (never trust them blindly).
  4. Execute the real function.
  5. Send the result back to the model as a tool result.
  6. The model produces a final answer or another tool call.

Common mistakes

  • No descriptions. The model guesses intent and picks the wrong tool.
  • Loose types. amount as a string leads to "50" vs 50 bugs.
  • Giant schemas. Twenty tools with thirty fields each overwhelm the model. Group and trim.
  • Skipping validation. Always re-validate arguments server-side before acting, especially for destructive operations.

Frequently asked questions

Yes. OpenAI calls it parameters, Anthropic calls it input_schema — both are JSON Schema for the function arguments.

Technically many, but accuracy drops as the list grows. Keep the active set focused; route to sub-agents if you have dozens of tools.

Absolutely — and it usually should. Returning structured JSON from your function makes it easy for the model to use the result in the next turn.

Try JSON Schema Generator

Generate a JSON Schema for your tool arguments from an example object.