The production form of ReAct (Module 3): the model requests, your code executes.
You declare tools with JSON schemas. The model decides whether and how to call them and returns a structured call. You run it, return the result, the model continues.
1{
2 "name": "get_order_status",
3 "description": "Look up the current status of a customer's order. Use when the user asks where their order is or about delivery.",
4 "parameters": {
5 "type": "object",
6 "properties": {
7 "order_id": { "type": "string", "description": "The order ID, e.g. 'A1234'" }
8 },
9 "required": ["order_id"]
10 }
11}The model selects tools and fills arguments using the name + description + parameter descriptions. These are prompt engineering, not afterthoughts:
| Weak | Strong |
|---|---|
"name": "func1" | "name": "get_order_status" |
"description": "gets stuff" | "description": "Look up order status. Use when the user asks about delivery or where their order is." |
order_id: string | order_id: "The order ID, e.g. 'A1234'" (with example) |
1. Send user message + tool schemas
2. Model returns: tool_call(get_order_status, {order_id:"A1234"})
3. Your code executes the real function
4. Send the tool result back to the model
5. Model produces the final natural-language answer
search before answering factual questions").User: "Where's order A1234?" → model calls
get_order_status({order_id:"A1234"})→ your DB returns{status:"shipped", eta:"2026-05-20"}→ model: "Your order A1234 has shipped and should arrive around May 20, 2026."
Security note: Tool calling is an injection surface. Treat all model-produced arguments as untrusted input — validate and sanitise (deep dive: Module 5).