Appearance
Tool Use & Function Calling
A prompt that asks for a tool call is different from a prompt that asks for text. You're not just describing what you want the model to say, you're describing what it should do, in a format a parser can read. This lesson covers designing prompts that produce reliable tool calls: choosing the right function descriptions, handling errors, and structuring multi-tool prompts.

What you'll learn
- Tool-calling prompts describe available functions with JSON Schema, the model decides which to call and with what arguments
- Good function descriptions are the difference between a reliable tool call and a hallucinated one
- Always validate the model's output against the schema before executing the tool
The problem
You want your agent to call an API, query a database, or send an email. You describe the available functions in the prompt, and the model returns a structured call. But a vague function description produces hallucinated parameters. A missing required field crashes your parser. And when the model calls the wrong tool entirely, the user gets a confusing error.
Build it
Step 1: Define a tool with a clear schema
Provide the model with detailed schemas defining exactly how to interact with external tools. Strict typing and clear descriptions are critical for accurate function selection.
python
from anthropic import Anthropic
client = Anthropic()
response = client.messages.create(
model="claude-sonnet-4-6",
max_tokens=1024,
system="You are a helpful assistant with access to a weather API.",
messages=[{"role": "user", "content": "What's the weather in Tokyo?"}],
tools=[{
"name": "get_weather",
"description": "Get the current weather for a city. Returns temperature, conditions, and humidity.",
"input_schema": {
"type": "object",
"properties": {
"city": {
"type": "string",
"description": "The city name, e.g. 'Tokyo' or 'San Francisco'"
},
"units": {
"type": "string",
"enum": ["celsius", "fahrenheit"],
"description": "Temperature unit. Defaults to celsius."
}
},
"required": ["city"]
}
}]
)Step 2: Handle the tool call
Intercept the structured output when the model opts to use a tool. Execute the actual function locally and feed the results back into the conversation context.
python
if response.stop_reason == "tool_use":
tool_call = response.content[-1]
if tool_call.name == "get_weather":
args = tool_call.input
weather = fetch_weather(args["city"], args.get("units", "celsius"))
# Feed the result back to the model
messages.append({"role": "assistant", "content": response.content})
messages.append({
"role": "user",
"content": [{"type": "tool_result", "tool_use_id": tool_call.id, "content": str(weather)}]
})Step 3: Validate before executing
Enforce structural correctness using Pydantic before triggering any real actions. If the model hallucinates parameters, gracefully return the validation error so it can retry.
python
from pydantic import BaseModel, ValidationError
class WeatherArgs(BaseModel):
city: str
units: str = "celsius"
try:
args = WeatherArgs(**tool_call.input)
weather = fetch_weather(args.city, args.units)
except ValidationError as e:
# Model produced invalid arguments, feed the error back
error_msg = f"Invalid arguments: {e}. Please provide a valid city name."What goes wrong
| Mistake | How you notice it | The fix |
|---|---|---|
| Vague function description | Model hallucinates parameter values or calls the wrong tool | Make descriptions specific: "Get the current weather for a city" not "Get weather" |
| Missing required field | Parser crashes on KeyError | Validate with Pydantic before executing. Feed validation errors back to the model |
| Wrong tool selected | Model calls send_email when user asked for weather | Make tool descriptions distinct. If two tools overlap, add a when_to_use hint in the description |
| Too many tools | Model gets confused, calls wrong tool or none | Limit to 5-10 tools per prompt. Group related tools into separate prompts |
Confirm it worked
Test the complete tool invocation flow by verifying the model correctly identifies the required tool and populates the schema. This confirms the tool configuration is properly integrated.
python
# Test: model should call get_weather for Tokyo
response = client.messages.create(
model="claude-sonnet-4-6",
max_tokens=1024,
system="You are a helpful assistant.",
messages=[{"role": "user", "content": "What's the weather in Tokyo?"}],
tools=[weather_tool]
)
assert response.stop_reason == "tool_use"
assert response.content[-1].name == "get_weather"
assert response.content[-1].input["city"] == "Tokyo"