GPT-5 Freeform Function Calling Enabling AI Agents To Write Code
OpenAI’s GPT-5 introduces a set of developer features that deepen the integration between applications and the model.
Some Background
The are a few things that I find hard not more people are talking about…for instance, for a while now I have been carrying on about how inaccurate AI Agents are…considering various benchmarks.
And that a separation between the observation/planning stage and the action stage is required.
Of late, there has been a downward trend in the use of the world AI Agents, and an upward trend (increased use) of the phrase Agentic Workflows.
In my mind, Agentic Workflows leverages the autonomous nature of AI Agents to create a plan or sequence of events to solve a problem.
But then this plan is curated, saved and re-used, or optimised and curated by humans.
So another thing I discovered this week, is that OpenAI introduced freeform function calling…and with all the hype around GPT-5, why is no one speaking about?
Because the way I see this (and I’m happy to be wrong), is freeform function calling takes us closer to AI Agents that can write code to fulfil a task or a user request.
Considering the HuggingFace table lower down in the article, this allows AI Agents to have level 5 characteristics…
Back to GPT-5
While this creates some dependency on GPT-5’s unique capabilities, it also unlocks powerful new ways for AI to interact with tools, execute tasks and even write code.
Among these, Free-Form Function Calling stands out as a game-changer.
Why Functions Matter
Taking a step back to consider functions…
Demystifying Large Language Model Function Calling
Thanks for reading Cobus Greyling on LLMs, NLU, NLP, chatbots & voicebots! Subscribe for free to receive new posts and support my work.
Functions in AI models aren’t just code — they’re compact apps that you define for the model.
Each function has
a name,
a clear purpose, and
a set of inputs.
Traditional structured function calls required Language Models to output JSON that is passed to an external system (API) , adding friction and limiting flexibility.
The Biggest Misconception About Function Calling
When using the OpenAI API with function calling, the model itself does not run the functions.
Instead, it generates parameters for potential function calls.
Your application then decides how to handle these parameters, maintaining full control over whether to call the suggested function or take another action.
Language Model Autonomy & Function-Driven Decisions
In AI language models, the introduction of functions adds a new layer of autonomy.
The function calling capability allows the model to independently determine whether a function call is needed to handle a particular task or if it should respond directly.
By doing so, the model dynamically selects the most suitable response strategy based on the context, enhancing both its adaptability and effectiveness.
This decision-making power introduces a more nuanced autonomy, enabling the model to switch seamlessly between execution and conversation.
Free-Form Function Calling
GPT-5 can now emit raw, executable content/code directly — Python scripts, Bash commands, SQL queries, configuration files, and more.
This means an AI Agent can immediately feed the output into a runtime or executor without translation, cutting the thinking to doing loop dramatically.
The result?
Fewer components, less glue code and faster, more adaptable workflows.
Considering the table below which defines five levels of Agentic implementation, Free-Form Function Calling takes us closer to level 5 where the AI Agent can be fully autonomous, writing code which can be executed to achieve the task.
Flexibility for Unplanned Workflows
With free-form outputs, GPT-5 can:
Pass partial results between function calls without a rigid schema.
Mix tool calls with natural language reasoning mid-process.
Switch strategies or tools on the fly if it discovers a better approach.
How It Works
Define capabilities as functions
Each function represents a discrete action the agent can take. Examples:
generate_code
→ writes Python or JavaScript.run_code
→ executes code in a sandbox.query_database
→ runs SQL queries.send_notification
→ sends an email or Slack message.
AI call functions in any sequence
Unlike rigid structured calls, GPT-5 doesn’t have to stick to one tool at a time or a single JSON output format.
It can dynamically choose the order and combination of functions to achieve its goal:
Call
query_database
to fetch data.Call
generate_code
to process that data.Call
run_code
to execute the generated script.Call
send_notification
to report results.
Why Free-Form Function Calling?
Previously, function calls forced strict adherence to JSON schemas.
Any change to a function or tool often required updating both the prompt and the receiving code. GPT-5’s free-form approach removes this friction:
Less developer overhead — no need to define and maintain schemas for every function.
More natural workflows — GPT-5 produces ready-to-run commands, queries, or scripts in a single step.
Easier integration with legacy systems — the output is native to the tool, whether that’s SQL, shell commands, or HTML.
Free-Form Function Calling gives GPT-5 a natural programming voice…
It can speak directly in the “language” of any tool, without packaging or formatting constraints. For AI agents, this is revolutionary — it transforms GPT-5 from a model that suggests code to one that can generate, orchestrate, and execute it almost seamlessly.
What’s the Use?
If the whole idea was for function calling to be the specific preparation of data for an interface, why then go with free form?
Generating code to run somewhere is actually one of the clearest, easiest-to-grasp examples of why Free-Form Function Calling matters.
Before GPT-5: Constrained (Structured) Function Calling
The model could call “functions,” but those calls had to match a predefined JSON schema.
Example: You might define a function like run_sql_query(query: string)
.
The model would return:
{ "name": "run_sql_query", "arguments": { "query": "SELECT * FROM users;" } }
GPT-5: Free-Form Function Calling
The model can skip the JSON and just output the thing itself in the right syntax.
Example prompt: “Write a Bash script that deletes all .tmp
files in /var/log
.”
Output:
#!/bin/bash
find /var/log -type f -name "*.tmp" -delete
Working Notebook Example
The Python code below you can paste into a Colab Notebook and run…it shows in parallel the difference between constrained / structured function calling…
And freeform function calling…
!pip install --quiet openai
import os
from openai import OpenAI
os.environ["OPENAI_API_KEY"] = "<Your API Key>"
client = OpenAI()
# -------------------------------
# 1. Constrained (Structured) Function Calling
# -------------------------------
import json
response_constrained = client.responses.create(
model="gpt-5-mini",
input="Write an SQL query that selects all users who signed up in the last 30 days.",
tools=[{
"type": "function",
"name": "generate_sql",
"description": "Generates an SQL query",
"parameters": {
"type": "object",
"properties": {
"query": {"type": "string", "description": "The SQL query text"}
},
"required": ["query"]
}
}],
tool_choice="required" # valid for Responses API
)
print("=== Constrained Function Calling Output ===")
for item in response_constrained.output:
if item.type == "function_call":
print("Function name:", item.name)
print("Arguments string:", item.arguments)
try:
arguments_dict = json.loads(item.arguments)
print("Arguments dict:", arguments_dict)
query = arguments_dict.get("query")
print("\nExtracted SQL query:\n", query)
except json.JSONDecodeError:
print("Error decoding JSON from arguments string.")
Output…
=== Constrained Function Calling Output ===
Function name: generate_sql
Arguments string: {"query":"SELECT * FROM users WHERE created_at >= NOW() - INTERVAL '30 days';"}
Arguments dict: {'query': "SELECT * FROM users WHERE created_at >= NOW() - INTERVAL '30 days';"}
Extracted SQL query:
SELECT * FROM users WHERE created_at >= NOW() - INTERVAL '30 days';
# -------------------------------
# 2. Free-Form Function Calling
# -------------------------------
from openai import OpenAI
client = OpenAI()
response_freeform = client.responses.create(
model="gpt-5-mini",
input=[
{
"role": "user",
"content": [
{
"type": "input_text",
"text": "Write only the SQL query that selects all users who signed up in the last 30 days. No explanations."
}
]
}
]
)
print("\n=== Free-Form Function Calling Output ===")
for item in response_freeform.output:
if item.type == "message":
for content in item.content:
if content.type == "output_text":
print(content.text)
Output…
=== Free-Form Function Calling Output ===
SELECT * FROM users WHERE created_at >= CURRENT_TIMESTAMP - INTERVAL '30 days';
AI Agent That Generate Code
The code below is the simples example of an AI Agent that writes and executes the code…
from openai import OpenAI
client = OpenAI()
def ai_agent_task(task_description):
response = client.responses.create(
model="gpt-5-mini",
tools=[
{
"type": "function",
"name": "write_python_code",
"description": "Writes Python code for the requested task. Outputs runnable code only.",
"parameters": {
"type": "object",
"properties": {
"instruction": {"type": "string", "description": "The Python task to perform."}
},
"required": ["instruction"]
},
"strict": False
}
],
input=[
{
"role": "user",
"content": [
{
"type": "input_text",
"text": (
f"Call the tool `write_python_code` to write Python code that {task_description}. "
"Output only runnable code, no explanations."
)
}
]
}
]
)
# Extract the tool output
code_to_run = ""
for item in response.output:
if item.type == "tool" and item.name == "write_python_code":
code_to_run = item.arguments.strip()
elif item.type == "message":
for content in item.content:
if content.type == "output_text":
code_to_run = content.text
print("=== AI-Generated Code ===")
print(code_to_run)
print("\n=== Execution Result ===")
if code_to_run:
exec(code_to_run, globals())
else:
print("No code generated.")
# Run example
ai_agent_task("prints the first 10 Fibonacci numbers")
And the output…
=== AI-Generated Code ===
n = 10
a, b = 0, 1
for _ in range(n):
print(a)
a, b = b, a + b
=== Execution Result ===
0
1
1
2
3
5
8
13
21
34
Chief Evangelist @ Kore.ai | I’m passionate about exploring the intersection of AI and language. Language Models, AI Agents, Agentic Apps, Dev Frameworks & Data-Driven Tools shaping tomorrow.
GPT-5 New Params and Tools | OpenAI Cookbook
We're introducing new developer controls in the GPT-5 series that give you greater control over model responses-from…
GPT-5 New Params and Tools | OpenAI Cookbook
We're introducing new developer controls in the GPT-5 series that give you greater control over model responses-from…
When to Use Functions, a Multi-Tool AI Agent, or Multiple Agents
Thanks for reading Cobus Greyling on LLMs, NLU, NLP, chatbots & voicebots! Subscribe for free to receive new posts and support my work.
Demystifying Large Language Model Function Calling
Thanks for reading Cobus Greyling on LLMs, NLU, NLP, chatbots & voicebots! Subscribe for free to receive new posts and support my work.