Function Calling with GPT and Claude: A Practical Guide
Build a Safe Tool-Calling Loop
Function calling lets a model decide when it needs your application to do work: look up an order, query a database, create a support ticket, or call an internal service. The model does not run the function itself. Instead, it returns structured arguments, your server validates and executes them, and then you send the result back to the model.
This pattern works well for both GPT and Claude. If you want one economical endpoint for experimentation and production traffic, 59API provides pay-as-you-go access to official-quality GPT and Claude models through https://api.59api.com. Its OpenAI SDK compatibility is especially useful when you want to keep one tool-calling integration while switching models based on cost, latency, or task complexity.
1. Define a Small, Strict Tool Schema
Start with one read-only tool. A weather lookup is simple enough to test but demonstrates the full flow. In Python, define the tool using the OpenAI-compatible tools format:
from openai import OpenAI
client = OpenAI(api_key="YOUR_59API_KEY", base_url="https://api.59api.com/v1")
tools = [{"type": "function", "function": {"name": "get_weather", "description": "Get the current weather for a city.", "parameters": {"type": "object", "properties": {"city": {"type": "string", "description": "City and country, for example Paris, FR"}, "unit": {"type": "string", "enum": ["celsius", "fahrenheit"]}}, "required": ["city"], "additionalProperties": false}}}]
The important details are the required list, the unit enum, and additionalProperties set to false. These constraints reduce malformed arguments and make your backend easier to secure. Do not expose a generic SQL, shell, filesystem, or HTTP-request tool to a model unless you add strict allowlists and authorization checks.
2. Ask the Model to Choose a Tool
Use a model ID available in your 59API dashboard. The same application structure can be used with a GPT model for broad tool use or a Claude Sonnet, Haiku, Opus, or Fable model when that model better fits your quality and budget requirements.
messages = [{"role": "user", "content": "Will I need an umbrella in Berlin today? Use Celsius."}]
response = client.chat.completions.create(model="YOUR_MODEL_ID", messages=messages, tools=tools, tool_choice="auto")
message = response.choices[0].message
With tool_choice set to auto, the model can answer directly or request a function. During testing, use tool_choice set to required if you specifically need to verify the tool path. In production, preserve the full assistant message in conversation history, including its tool_calls data.
3. Validate Arguments Before Running Code
Never trust tool arguments merely because they came from a model. Parse the JSON, verify expected fields, enforce limits, and check the current user’s permissions. This example uses a local function; a real implementation could call a weather provider after validating the city string.
import json
def get_weather(city, unit="celsius"):
allowed_cities = {"Berlin, DE", "Paris, FR", "London, GB"}
if city not in allowed_cities:
return {"error": "Unsupported city"}
return {"city": city, "temperature": 12, "unit": unit, "condition": "light rain"}
For every requested tool call, parse its arguments and execute only known functions. Also set timeouts on external requests, log a request ID, and redact secrets from logs. A useful rule is that tools may retrieve or propose actions, but irreversible actions such as refunds, deployments, or account changes should require explicit user confirmation.
4. Return Tool Results and Generate the Final Reply
The model needs the tool output before it can write a grounded answer. Append the assistant tool-call message, then append one tool result per call using the matching tool_call_id:
messages.append(message)
for call in message.tool_calls or []:
if call.function.name == "get_weather":
args = json.loads(call.function.arguments)
result = get_weather(**args)
messages.append({"role": "tool", "tool_call_id": call.id, "content": json.dumps(result)})
final = client.chat.completions.create(model="YOUR_MODEL_ID", messages=messages, tools=tools)
print(final.choices[0].message.content)
Claude’s native API calls the same idea tool_use and returns input objects rather than OpenAI-style function arguments. The server-side safety pattern is identical: define a JSON schema, inspect tool requests, execute validated code, return a tool result, and request the final response. When using an OpenAI-compatible 59API integration, you can keep the OpenAI tool format for supported Claude routing, which avoids maintaining separate application logic.
Practical Model and Cost Strategy
Use a lower-cost model such as Haiku for classification, extraction, and predictable lookups; reserve stronger models such as Sonnet, Opus, or capable GPT models for ambiguous requests and multi-step reasoning. Keep tool descriptions short and return compact JSON results to reduce tokens. 59API’s low-cost, pay-as-you-go relay makes this routing strategy practical without sacrificing native official-quality model access. If you are ready to test your first tool loop, sign up for 59API and point your existing OpenAI SDK configuration at its API endpoint.