Function Calling by Hand
See every step.
Frameworks hide this step, and that's why so many people find agents mysterious. Do it by hand just once, and the mystery disappears for good. Let's see a real model request a real tool.

- Frameworks hide this
- Do it by hand once
- The mystery disappears
The model asks
We give the model the get order function, and we turn off automatic calling, so that we can see what happens. Then a customer asks about B forty two. Run it. Look at that. The model didn't reply with words. It replied with a request, naming the function and the argument. It worked out the order number from the sentence. Then our code runs the function, and gets a real result.
from google import genai
from google.genai import types
from shop import get_order
client = genai.Client()
config = types.GenerateContentConfig(
tools=[get_order],
automatic_function_calling=types.AutomaticFunctionCallingConfig(disable=True))
reply = client.models.generate_content(
model="gemini-flash-latest", config=config, contents="Where is order B42?")
call = reply.function_calls[0]
print("The model asks for:", call.name, dict(call.args))
result = get_order(**call.args)
print("Our code ran it and got:", result)The model asks for: get_order {'order_id': 'B42'}
Our code ran it and got: {'item': 'Chain', 'price': 28, 'status': 'shipped', 'days_ago': 2}Also uses shop.py
ORDERS = {
"A17": {"item": "Carbon wheelset", "price": 640, "status": "delivered", "days_ago": 12},
"B42": {"item": "Chain", "price": 28, "status": "shipped", "days_ago": 2},
"C09": {"item": "Helmet", "price": 95, "status": "delivered", "days_ago": 45},
}
STOCK = {"Carbon wheelset": 3, "Chain": 14, "Helmet": 0}
REFUNDS = []
def get_order(order_id: str) -> dict:
"""Look up one order by its ID, such as A17."""
return ORDERS.get(order_id, {"error": f"No order {order_id}"})
def check_stock(item: str) -> dict:
"""Return how many of an item are in stock."""
return {"item": item, "in_stock": STOCK.get(item, 0)}
def refund(order_id: str, amount: float) -> dict:
"""Refund money to the customer for an order."""
REFUNDS.append((order_id, amount))
return {"refunded": amount, "order_id": order_id}Also uses scripted.py
class Scripted:
"""A stand-in model that replays a fixed list of replies."""
def __init__(self, replies):
self.replies = list(replies)
self.calls = 0
def ask(self, messages):
self.calls += 1
return self.replies.pop(0)Sending the result back
Now we finish the job. The history holds the question, then the model's request, and then the tool's result. We send it all back. Run it. Now it's a normal sentence, built from real data. That's a full tool call. Two trips to the model, and one function in the middle. Nothing more.
from google import genai
from google.genai import types
from shop import get_order
client = genai.Client()
config = types.GenerateContentConfig(
tools=[get_order],
automatic_function_calling=types.AutomaticFunctionCallingConfig(disable=True))
history = [types.Content(role="user", parts=[types.Part(text="Where is order B42?")])]
first = client.models.generate_content(model="gemini-flash-latest", config=config, contents=history)
history.append(first.candidates[0].content)
call = first.function_calls[0]
result = get_order(**call.args)
history.append(types.Content(role="user", parts=[
types.Part.from_function_response(name=call.name, response=result)]))
final = client.models.generate_content(model="gemini-flash-latest", config=config, contents=history)
print(final.text)Order **B42** (Chain) has been **shipped** (shipped 2 days ago).
Also uses shop.py
ORDERS = {
"A17": {"item": "Carbon wheelset", "price": 640, "status": "delivered", "days_ago": 12},
"B42": {"item": "Chain", "price": 28, "status": "shipped", "days_ago": 2},
"C09": {"item": "Helmet", "price": 95, "status": "delivered", "days_ago": 45},
}
STOCK = {"Carbon wheelset": 3, "Chain": 14, "Helmet": 0}
REFUNDS = []
def get_order(order_id: str) -> dict:
"""Look up one order by its ID, such as A17."""
return ORDERS.get(order_id, {"error": f"No order {order_id}"})
def check_stock(item: str) -> dict:
"""Return how many of an item are in stock."""
return {"item": item, "in_stock": STOCK.get(item, 0)}
def refund(order_id: str, amount: float) -> dict:
"""Refund money to the customer for an order."""
REFUNDS.append((order_id, amount))
return {"refunded": amount, "order_id": order_id}Also uses scripted.py
class Scripted:
"""A stand-in model that replays a fixed list of replies."""
def __init__(self, replies):
self.replies = list(replies)
self.calls = 0
def ask(self, messages):
self.calls += 1
return self.replies.pop(0)Summary
Request, run, return, reply. That's all that function calling is. Now we put it inside the loop, and something amazing happens. The model starts to chain tools on its own.
Key takeaways- The model returns a request
- Your code runs it
- Send the result back