LLM Function Calling: The Complete Guide for Building AI Tools
Function calling (tool use) is the technology that turned LLMs from chatbots into agents. Here's the complete guide.
What Is Function Calling?
Function calling lets an LLM decide when to call external tools and format the arguments correctly. Instead of generating text about searching the web, it generates a structured call that actually searches.
How It Works
User: "What's the weather in Tokyo?"
LLM thinks: I need to call get_weather(city="Tokyo")
↓
Tool call: get_weather(city="Tokyo")
↓
Result: { "temp": 22, "condition": "Sunny" }
↓
LLM: "The weather in Tokyo is 22°C and sunny."
The key insight: the LLM doesn't execute the function — it tells your code what to execute.
Basic Implementation
from openai import OpenAI
client = OpenAI()
# Define your tools
tools = [
{
"type": "function",
"function": {
"name": "get_weather",
"description": "Get current weather for a city",
"parameters": {
"type": "object",
"properties": {
"city": { "type": "string", "description": "City name" },
"unit": { "type": "string", "enum": ["celsius", "fahrenheit"] }
},
"required": ["city"]
}
}
},
{
"type": "function",
"function": {
"name": "run_python",
"description": "Execute Python code and return output",
"parameters": {
"type": "object",
"properties": {
"code": { "type": "string", "description": "Python code to execute" }
},
"required": ["code"]
}
}
}
]
def run_agent(user_message):
messages = [{"role": "user", "content": user_message}]
while True:
response = client.chat.completions.create(
model="gpt-4",
messages=messages,
tools=tools
)
msg = response.choices[0].message
messages.append(msg.to_dict())
# No tool calls = agent is done
if not msg.tool_calls:
return msg.content
# Execute each tool call
for tool_call in msg.tool_calls:
result = execute_tool(tool_call.function.name,
json.loads(tool_call.function.arguments))
messages.append({
"role": "tool",
"tool_call_id": tool_call.id,
"content": json.dumps(result)
})
def execute_tool(name, args):
if name == "get_weather":
return {"temp": 22, "condition": "Sunny"} # Your API call here
elif name == "run_python":
try:
exec_locals = {}
exec(args["code"], {}, exec_locals)
return {"output": str(exec_locals)}
except Exception as e:
return {"error": str(e)}
The ReAct Pattern
The most effective agent pattern combines Reasoning + Acting:
- Observe: What's the current state?
- Think: What should I do next?
- Act: Call a tool or respond
- Repeat until done
def react_agent(task, max_steps=10):
messages = [{"role": "user", "content": task}]
for step in range(max_steps):
# LLM reasons about next action
response = client.chat.completions.create(
model="gpt-4",
messages=messages,
tools=tools
)
choice = response.choices[0]
if choice.finish_reason == "tool_calls":
# Execute tool calls and feed results back
for tc in choice.message.tool_calls:
result = execute_tool(tc.function.name,
json.loads(tc.function.arguments))
messages.append({"role": "tool",
"tool_call_id": tc.id,
"content": json.dumps(result)})
else:
# Agent has a final answer
return choice.message.content
return "Agent exceeded max steps without completing"
Best Practices
| Practice | Why | How |
|---|---|---|
| Limit tool count | Reduces confusion | 3-7 tools per agent |
| Clear descriptions | LLM picks right tool | One-sentence purpose + parameter docs |
| Validate inputs | Prevent injection | Pydantic/schemas on all tool args |
| Handle errors | Agents will call wrong tools | Return descriptive error messages |
| Set max iterations | Prevent infinite loops | 10-20 steps max |
| Log all calls | Debug + audit trail | Store tool name, args, result |
Advanced: Multi-Agent Tool Use
For complex tasks, decompose into specialist agents:
# Router agent picks which specialist to use
tools = [
{"type": "function", "function": {"name": "ask_research_agent", ...}},
{"type": "function", "function": {"name": "ask_coding_agent", ...}},
{"type": "function", "function": {"name": "ask_writing_agent", ...}},
]
Each specialist has its own system prompt and tool set. The router just decides who to delegate to.
Key Takeaways
- Function calling = agents. Without tools, LLMs are just fancy chatbots
- ReAct pattern is the foundation of all modern agent frameworks
- Tool design is UX design — clear names, descriptions, and schemas
- Error handling is critical — agents will call tools wrong
- Log everything — you'll need it for debugging
Building AI agents? I write about this regularly. Follow for more, and check out my work on GitHub.
Top comments (0)