Purrx

Function calling: letting the model use your code

Declare tools, let Gemini decide when to call them, and understand why the model never executes anything itself. The building block every agent is made of.

11 min+30 XPHands-on

A model cannot check your inventory, query your database, or find out today's date. What it can do is recognize that a question needs one of those, and reply with a structured request asking you to do it. That is function calling, and it is how every you have heard of works underneath.

The full round trip

  1. 1
    Your app → Gemini

    Send the question and your tool list

    "How many copies of Dune?" + a description of getStock

  2. 2
    Gemini → your app

    Gemini replies with a function call, not text

    getStock({ title: "Dune" })

  3. 3
    Inside your app

    Your code checks the arguments and runs it

    → { count: 12 }

  4. 4
    Your app → Gemini

    Send the conversation plus the result

    functionResponse: { count: 12 }

  5. 5
    Gemini → your app

    Gemini writes the final answer

    "We have 12 copies of Dune in stock."

Two calls to Gemini. The model only asks for the function; your code is what runs it.

So it takes at least two API calls. The first decides what to do. The second turns the result into an answer.

Declaring a tool

tools go in config
const tools = [{
  functionDeclarations: [{
    name: "getStock",
    description: "Look up how many copies of a book are in stock.",
    parameters: {
      type: "OBJECT",
      properties: {
        title: { type: "STRING", description: "Exact book title" },
      },
      required: ["title"],
    },
  }],
}];

const response = await gemini.generateContent({
  contents: "How many copies of Dune do we have?",
  config: { tools },
});

The description fields aren't notes for you. The model reads them to decide when to use the tool. A vague description gets the tool called at the wrong moments, or never.

Under the hood — What does it mean for a model to "emit a function call"?

It sounds like a new ability. It isn't: the model is still doing exactly one thing, producing tokens.

Your tool declarations are turned into text and added to the prompt, and the model has been trained to produce a specific structured format when one of them fits. The API parses that output and presents it to you as a tidy functionCall object with name and args. Underneath, the model generated text that happened to conform to a shape.

This is worth remembering, because it explains how it fails: a model can "call" a tool that does not exist, invent an argument that was never in the schema, or fill a required field with a plausible guess. It is producing likely-looking output, not executing a typed interface — which is why validating the arguments is not optional.

Reading the reply

what comes back
{
  "candidates": [{
    "content": {
      "role": "model",
      "parts": [{
        "functionCall": {
          "name": "getStock",
          "args": { "title": "Dune" }
        }
      }]
    }
  }]
}

Note there is no text on that part. Code that assumes parts[0].text exists will hand you undefined the first time a tool fires — which is why you scan the parts for what you find rather than indexing blindly.

finding it safely
const parts = response.candidates?.[0]?.content?.parts ?? [];
const call = parts.find((part) => part.functionCall)?.functionCall;

if (call) {
  console.log(call.name);  // "getStock"
  console.log(call.args);  // { title: "Dune" }
}

Designing tools that behave

  • Few and distinct. With twelve overlapping tools, the model often guesses wrong. With five clearly different ones, it chooses well.
  • Narrow parameters. Use and required fields instead of free text. The model invents less when it has fewer choices.
  • Validate every argument. Arguments are model output. Treat them exactly as you would treat a request body from the internet.
  • Return errors as data. Sending back { error: "No such title" } lets the model recover and try something else. Throwing an exception just ends the conversation.

In this exercise you do the first half of the round trip: declare a tool and detect the call. After a lesson on what makes something an agent, you'll close the loop and send the result back.

Key takeaways

  • The model never runs your code. It asks for a function by name, and your code decides whether to run it.
  • Tool descriptions decide when a tool gets used, so write them clearly.
  • Treat tool arguments like untrusted input: validate them, and send errors back as data.

Sign in to run the exercise

Reading is free. Writing code here needs an account so we have somewhere to keep your Gemini key and the +30 XP you are about to earn.