In this exercise, you'll research and discover how to allow an LLM to invoke your own Python functions.
Do some quick research:
- What is tool calling
- How to implement tool calling with the responses API
Starter code — two hard-coded functions:
from openai import OpenAI
client = OpenAI()
def get_weather(city: str) -> str:
fake_data = {"Barcelona": "22°C, sunny", "Berlin": "15°C, rainy"}
return fake_data.get(city, "Unknown city")
def get_capital(country: str) -> str:
fake_data = {"Spain": "Madrid", "Germany": "Berlin"}
return fake_data.get(country, "Unknown country")Note: make sure you have openai installed and your OPENAI_API_KEY set as an environment variable.
Give the model access to get_weather as a tool. Ask it: "What's the weather in Barcelona?"
Hints:
- Tools are described with a JSON schema (name, description, parameters) passed in the
toolsargument ofclient.responses.create(). - The model's response won't contain the answer directly — it will contain a function call (name + arguments) that you need to detect, run yourself, and return.
Add get_capital as a second tool. Ask a question that requires the model to pick the right one, e.g. "What's the capital of Germany?"
Hint: the model decides which tool to call (or none) — you don't tell it which one to use.
After running the function, send its output back to the model (as a function_call_output) so it can give a final natural-language answer to the user, instead of just returning raw data.
Hint: you need the call_id from the model's function call to link your result back to it.
Bonus 1 — Web search tool
Research the Responses API's built-in web_search tool (no function you need to write — OpenAI hosts it). Add it alongside your custom tools and ask a question that requires up-to-date info from the web.
Bonus 2 — Multi-tool loop Ask a question that requires calling both tools (e.g. "What's the weather in the capital of Germany?"). Write a loop that keeps handling function calls automatically until the model returns a final text answer.
Iteration 2
tools = [{
"type": "function",
"name": "get_weather",
"description": "Get current weather for a city",
"parameters": {
"type": "object",
"properties": {"city": {"type": "string"}},
"required": ["city"]
}
}]
response = client.responses.create(
model="gpt-5.4-nano",
input="What's the weather in Barcelona?",
tools=tools
)
print(response.output) # contains a function_call itemIteration 3
tools = [
{
"type": "function",
"name": "get_weather",
"description": "Get current weather for a city",
"parameters": {
"type": "object",
"properties": {"city": {"type": "string"}},
"required": ["city"]
}
},
{
"type": "function",
"name": "get_capital",
"description": "Get the capital city of a country",
"parameters": {
"type": "object",
"properties": {"country": {"type": "string"}},
"required": ["country"]
}
}
]
response = client.responses.create(
model="gpt-5.4-nano",
input="What's the capital of Germany?",
tools=tools
)
print(response.output)Iteration 4
response = client.responses.create(
model="gpt-5.4-nano",
input="What's the weather in Barcelona?",
tools=tools
)
call = response.output[0] # the function_call item
args = json.loads(call.arguments)
result = get_weather(args["city"])
followup = client.responses.create(
model="gpt-5.4-nano",
previous_response_id=response.id,
input=[{
"type": "function_call_output",
"call_id": call.call_id,
"output": result
}]
)
print(followup.output_text)Bonus 1 — Web search tool
response = client.responses.create(
model="gpt-5.4-nano",
input="What's the latest news about the Mars rover?",
tools=tools + [{"type": "web_search"}]
)
print(response.output_text)Bonus 2 — Multi-tool loop
import json
available_functions = {
"get_weather": get_weather,
"get_capital": get_capital
}
response = client.responses.create(
model="gpt-5.4-nano",
input="What's the weather in the capital of Germany?",
tools=tools
)
while any(item.type == "function_call" for item in response.output):
outputs = []
for item in response.output:
if item.type == "function_call":
args = json.loads(item.arguments)
fn = available_functions[item.name]
result = fn(**args)
outputs.append({
"type": "function_call_output",
"call_id": item.call_id,
"output": result
})
response = client.responses.create(
model="gpt-5.4-nano",
previous_response_id=response.id,
input=outputs,
tools=tools
)
print(response.output_text)