Part 2: Start with a plain model call
Now the notebook has a working search(query) function over the course FAQ.
Before adding the tool, ask the model the course question directly.
from openai import OpenAI
openai_client = OpenAI()
response = openai_client.responses.create(
model="gpt-4o-mini",
input=[
{"role": "user", "content": "I just discovered the course. Can I join it?"}
],
)
response.output_text
The model can answer general questions from its training data, but it does not know which course we mean or what the FAQ says. That is the gap tool use fills.
Describe the tool
The Responses API needs a language-independent description of the
function. Python knows what search means, but the model only sees the
schema we send.
search_tool = {
"type": "function",
"name": "search",
"description": "Search the FAQ database",
"parameters": {
"type": "object",
"properties": {
"query": {
"type": "string",
"description": "Search query text to look up in the course FAQ."
}
},
"required": ["query"],
"additionalProperties": False
}
}
This schema is the contract between the model and your code. The model
can decide to call search, but your Python process is still responsible
for executing the function.
Let the model request a function call
Now create the conversation history and send the tool list with the request.
developer_prompt = """
You're a course teaching assistant.
You're given a question from a course student and your task is to answer it.
""".strip()
question = "I just discovered the course. Can I still join it?"
chat_messages = [
{"role": "developer", "content": developer_prompt},
{"role": "user", "content": question}
]
The developer message gives the assistant its job. The user message
is the student question.
response = openai_client.responses.create(
model="gpt-4o-mini",
input=chat_messages,
tools=[search_tool],
)
response.output
For this question, the response contains a function_call entry. In the
live session the model chose a query like join course now. That is not
the final answer yet. It is a request for your code to run the tool.
Execute the requested function
The function call contains JSON arguments. Parse them, call the Python function, and serialize the result so the model can read it on the next request.
import json
call = response.output[0]
args = json.loads(call.arguments)
results = search(**args)
result_json = json.dumps(results, indent=2)
The model also needs to see its own function-call request. Add the model output to the conversation history before adding the tool result.
chat_messages.extend(response.output)
chat_messages.append({
"type": "function_call_output",
"call_id": call.call_id,
"output": result_json,
})
The call_id links the tool output to the specific function call the
model requested.
Ask the model again
Call the Responses API a second time with the expanded history:
response = openai_client.responses.create(
model="gpt-4o-mini",
input=chat_messages,
tools=[search_tool],
)
response.output_text
This time the model has the original question, its own decision to call
search, and the FAQ results. It can now produce the course-specific
answer.
LLMs are stateless between API calls. The memory is the list you send as
input. If you leave out the previous messages, the model does not know
what happened in the earlier call.