Chapter 6 · the model chooses, your code executes
Ask a model for 26.2 miles in kilometers.
It answers with confidence — and often the wrong number. Weights approximate arithmetic; they don't do it.
The model never runs anything.
convert with a JSON SchemaTOOLS = [{
"type": "function",
"name": "convert",
"description": "Convert a length between units exactly.",
"parameters": {...}, # JSON Schema, enums included
}]
The model reads the description to decide when to call.
response = client.responses.create(
model="gpt-5.4-nano", input=QUESTION, tools=TOOLS)
call = next(i for i in response.output
if i.type == "function_call")
result = convert(**json.loads(call.arguments))
final = client.responses.create(
model="gpt-5.4-nano",
previous_response_id=response.id,
input=[{"type": "function_call_output",
"call_id": call.call_id,
"output": json.dumps({"result": result})}],
tools=TOOLS)response = client.models.generate_content(
model="gemini-3.1-flash-lite",
contents=QUESTION, config=CONFIG)
fc = response.function_calls[0] # args already a dict
result = convert(**fc.args)
# replay: question + model's call turn + function_response
No previous_response_id — you reassemble the conversation.
datetime countsEach one is a docker compose up web app.
The model chooses, your code executes — keep that line sharp.
Next week: put a while around this loop and add more tools.