The Tool-Use Loop

Model requests a tool, your code runs it, the result goes back, model answers.

Tool use is a small loop between the model and your code.

The tool-use loop: the model asks for a tool, your code runs it, the result returns, and the model answersModelcall get_weathertool requestYour coderuns get_weather()tool result returned to modelFinal answer
The loop can repeat several times before the model produces its final answer.
  1. You send the user message plus the tool definitions.
  2. The model replies asking to call a tool with specific arguments.
  3. Your code runs the tool and returns the result as a new message.
  4. The model uses the result to answer (or calls another tool).

Example

Example · javascript
let messages = [{ role: 'user', content: 'Weather in Tokyo?' }];
let res = await chat({ messages, tools });

if (res.tool_call) {
  const args = JSON.parse(res.tool_call.arguments);
  const result = await getWeather(args.city);          // run the real function
  messages.push(res.message);                           // model's tool request
  messages.push({ role: 'tool', name: 'get_weather', content: JSON.stringify(result) });
  res = await chat({ messages, tools });                // model now answers
}
console.log(res.message.content);

When to use it

  • A booking agent runs the tool loop repeatedly, calling availability-check and then confirm-booking functions in sequence until the task is complete.
  • A code assistant loops through tool calls — search, read-file, run-tests — until all tests pass before returning a final summary to the user.
  • An expense processor calls parse-receipt, then lookup-budget, then record-expense in successive loop turns to handle a full reimbursement flow.

More examples

Tool loop with auto-dispatch

Runs the tool loop until the model stops requesting tools and returns a final text answer.

Example · python
from openai import OpenAI
import json

client = OpenAI()

def get_weather(city): return f'{city}: 22°C sunny'
def get_population(city): return f'{city} population: 3.7 million'
TOOL_FNS = {'get_weather': get_weather, 'get_population': get_population}

tools = [
    {'type':'function','function':{'name':'get_weather','description':'Weather for a city.','parameters':{'type':'object','properties':{'city':{'type':'string'}},'required':['city']}}},
    {'type':'function','function':{'name':'get_population','description':'Population of a city.','parameters':{'type':'object','properties':{'city':{'type':'string'}},'required':['city']}}},
]
messages = [{'role':'user','content':'What is the weather and population of Tokyo?'}]

while True:
    r = client.chat.completions.create(model='gpt-4o-mini', messages=messages, tools=tools)
    msg = r.choices[0].message
    messages.append(msg)
    if not msg.tool_calls:
        print(msg.content)
        break
    for tc in msg.tool_calls:
        fn_result = TOOL_FNS[tc.function.name](**json.loads(tc.function.arguments))
        messages.append({'role':'tool','tool_call_id':tc.id,'content':fn_result})

Limit loop iterations as a safety cap

Caps the loop at MAX_TURNS iterations to prevent infinite loops when a model keeps requesting tools.

Example · python
from openai import OpenAI
import json

client = OpenAI()
MAX_TURNS = 5

def lookup(key): return f'Value for {key}: 42'
tool = {'type':'function','function':{'name':'lookup','description':'Lookup a key.','parameters':{'type':'object','properties':{'key':{'type':'string'}},'required':['key']}}}

messages = [{'role':'user','content':'What is the value of alpha?'}]
for turn in range(MAX_TURNS):
    r = client.chat.completions.create(model='gpt-4o-mini', messages=messages, tools=[tool])
    msg = r.choices[0].message
    messages.append(msg)
    if not msg.tool_calls:
        print('Final:', msg.content)
        break
    for tc in msg.tool_calls:
        result = lookup(**json.loads(tc.function.arguments))
        messages.append({'role':'tool','tool_call_id':tc.id,'content':result})
else:
    print('Max turns reached')

Parallel tool calls in one turn

Handles the case where the model requests multiple tools in the same turn by processing all tool_calls before the next generation.

Example · python
from openai import OpenAI
import json

client = OpenAI()

def get_weather(city): return f'{city}: 18°C'
def get_time(city):    return f'{city} local time: 14:35'
FNS = {'get_weather': get_weather, 'get_time': get_time}

tools = [
    {'type':'function','function':{'name':'get_weather','description':'Weather.','parameters':{'type':'object','properties':{'city':{'type':'string'}},'required':['city']}}},
    {'type':'function','function':{'name':'get_time','description':'Local time.','parameters':{'type':'object','properties':{'city':{'type':'string'}},'required':['city']}}},
]
messages = [{'role':'user','content':'What is the weather and local time in London?'}]
r = client.chat.completions.create(model='gpt-4o-mini', messages=messages, tools=tools)
msg = r.choices[0].message
messages.append(msg)
for tc in msg.tool_calls:
    result = FNS[tc.function.name](**json.loads(tc.function.arguments))
    messages.append({'role':'tool','tool_call_id':tc.id,'content':result})
final = client.chat.completions.create(model='gpt-4o-mini', messages=messages)
print(final.choices[0].message.content)

Discussion

  • Be the first to comment on this lesson.