Llama 3.3 function calling lets you turn a 70B open-weight model into a tool-using agent without proprietary APIs. This tutorial builds a working Python pipeline that defines functions, sends them to Llama 3.3 70B via an OpenAI-compatible endpoint, and parses the model’s tool calls to execute real code.
Prerequisites
- Python 3.11 or newer
openaiPython package (v1.40+)- An API key from a gateway that serves Llama 3.3 70B. If you use n4n.ai, its single OpenAI-compatible endpoint addresses 240+ models including this one and falls back automatically when a provider is degraded.
- A terminal and a scratch directory
Install the client:
pip install openai
Define the function schemas
Llama 3.3 70B expects tools in the same shape as the OpenAI spec: a list of objects with type: "function" and a JSON Schema for parameters. Keep descriptions terse but explicit; the model relies on them to pick the right tool and populate arguments correctly. Mark only truly required fields as required—over-constraining triggers unnecessary clarification turns.
tools = [
{
"type": "function",
"function": {
"name": "get_weather",
"description": "Get current temperature in Celsius for a city.",
"parameters": {
"type": "object",
"properties": {
"city": {"type": "string", "description": "City name, e.g. Berlin"}
},
"required": ["city"],
},
},
},
{
"type": "function",
"function": {
"name": "multiply",
"description": "Multiply two numbers.",
"parameters": {
"type": "object",
"properties": {
"a": {"type": "number"},
"b": {"type": "number"},
},
"required": ["a", "b"],
},
},
},
]
Initialize the client
Point the OpenAI client at your gateway. The model identifier varies by provider; for Llama 3.3 70B instruct, use the string your gateway documents. The OpenAI SDK works unchanged against any compliant base URL.
from openai import OpenAI
client = OpenAI(
base_url="https://api.n4n.ai/v1", # OpenAI-compatible, 240+ models
api_key="YOUR_KEY",
)
MODEL = "meta-llama/llama-3.3-70b-instruct"
First call: let the model choose
Seed a conversation and ask a question that requires a tool. With tool_choice="auto" the model returns tool_calls instead of text when it wants to invoke a function. Llama 3.3 function calling handles parallel invocations in one turn, which cuts latency for multi-tool queries.
messages = [
{"role": "user", "content": "What is 12 times 19, and what's the weather in Tokyo?"}
]
resp = client.chat.completions.create(
model=MODEL,
messages=messages,
tools=tools,
tool_choice="auto",
)
print(resp.choices[0].message)
Expected output (truncated for clarity):
{
"role": "assistant",
"content": null,
"tool_calls": [
{
"id": "call_abc",
"type": "function",
"function": {
"name": "multiply",
"arguments": "{\"a\": 12, \"b\": 19}"
}
},
{
"id": "call_def",
"type": "function",
"function": {
"name": "get_weather",
"arguments": "{\"city\": \"Tokyo\"}"
}
}
]
}
Execute the functions
Write local stubs that mimic real implementations. In production these hit APIs or databases. Parse arguments as JSON—Llama 3.3 emits compact JSON strings, not Python literals.
import json
def get_weather(city: str) -> str:
# Stub: real code calls a weather API
return json.dumps({"city": city, "temp_c": 21})
def multiply(a: float, b: float) -> str:
return str(a * b)
def dispatch(name: str, args: str) -> str:
data = json.loads(args)
if name == "get_weather":
return get_weather(data["city"])
if name == "multiply":
return multiply(data["a"], data["b"])
raise ValueError(f"Unknown tool {name}")
Feed results back
Append the assistant message (with tool_calls) and then one message per tool result with role: "tool" and the matching tool_call_id. The model synthesizes a final answer. Order of tool messages does not need to match the call order, but IDs must align exactly.
msg = resp.choices[0].message
messages.append(msg) # type: ignore
for call in msg.tool_calls: # type: ignore
result = dispatch(call.function.name, call.function.arguments)
messages.append(
{
"role": "tool",
"tool_call_id": call.id,
"content": result,
}
)
final = client.chat.completions.create(
model=MODEL,
messages=messages,
tools=tools,
)
print(final.choices[0].message.content)
Expected final answer:
12 times 19 is 228. The current temperature in Tokyo is 21°C.
Build a reusable loop
For agents, wrap the exchange in a loop that runs until the model stops emitting tool_calls. Cap iterations to avoid runaway loops. Set temperature=0 for deterministic tool selection when the task is mechanical.
def run_agent(user_prompt: str, max_steps: int = 5):
msgs = [{"role": "user", "content": user_prompt}]
for _ in range(max_steps):
resp = client.chat.completions.create(
model=MODEL, messages=msgs, tools=tools, temperature=0
)
assistant_msg = resp.choices[0].message
if not assistant_msg.tool_calls:
return assistant_msg.content
msgs.append(assistant_msg) # type: ignore
for call in assistant_msg.tool_calls: # type: ignore
msgs.append({
"role": "tool",
"tool_call_id": call.id,
"content": dispatch(call.function.name, call.function.arguments),
})
return "Agent exceeded step budget"
print(run_agent("Multiply 7 by 8 then get weather for Paris"))
Hardening Llama 3.3 function calling
The open-weight model is capable but not infallible. Enforce a strict schema by validating arguments with jsonschema before execution. If validation fails, return a tool error string so the model can self-correct in the next turn.
from jsonschema import validate, ValidationError
def safe_dispatch(name, args, schema):
try:
validate(instance=json.loads(args), schema=schema)
except ValidationError as e:
return f"Invalid arguments: {e.message}"
return dispatch(name, args)
Set tool_choice to a specific function when you know the next step, reducing ambiguity. For open-ended tasks keep "auto" and treat parallel calls as a batch. When a provider hiccups, an OpenAI-compatible gateway that honors fallback spares you manual retry logic. n4n.ai forwards provider cache-control hints and meters per-token usage, so you can trace cost per agent run without custom instrumentation.
Keep tool descriptions honest. A misleading description causes the model to call get_weather for stock prices. Log the raw tool_calls during development to spot schema drift early.
Closing notes
Llama 3.3 function calling is production-viable for medium-complexity agents. The 70B parameter count balances cost and capability for most internal automations. Validate inputs, cap loops, and use a gateway that handles provider degradation so your agent stays up when a single upstream fails.