What Is Function Calling?

When a model decides to use a tool, it doesn't simply write text — it outputs a structured tool_call object containing the function name and arguments in JSON. The host application validates, executes, and returns the result.

This structured format is reliable: the model can't hallucinate argument names, argument types are validated, and the execution environment stays in full control.

JSON Schema Tool Definition

Every tool is described by a JSON Schema object. The LLM uses this schema to understand what the function does and what arguments it accepts.

{
  "name": "get_weather",
  "description": "Get current weather for a given location.",
  "parameters": {
    "type": "object",
    "properties": {
      "location": {
        "type": "string",
        "description": "City and country, e.g. 'Berlin, Germany'"
      },
      "units": {
        "type": "string",
        "enum": ["celsius", "fahrenheit"],
        "default": "celsius"
      }
    },
    "required": ["location"]
  }
}

OpenClaw auto-generates this schema from Python type hints when you use the @tool decorator.

OpenAI vs Anthropic Differences

FeatureOpenAIAnthropic (Claude)
Tool definition formattools[].functiontools[].input_schema
Tool result injectionrole: "tool"role: "user" with tool_result
Parallel callsSupported nativelySupported natively
Forced tool usagetool_choice: "required"tool_choice: {type:"any"}

OpenClaw normalises provider differences — you define tools once using the OpenClaw schema format and the framework translates to the correct provider API format.

Parallel Function Calls

Modern LLMs can request multiple tool calls in a single response. OpenClaw executes them concurrently using asyncio.gather:

from openclaw import Agent, tool

@tool(description="Get current weather for a city")
def get_weather(city: str) -> str:
    # ... call weather API
    return f"Weather in {city}: 22°C, partly cloudy"

@tool(description="Get current UTC time")
def get_time() -> str:
    from datetime import datetime, timezone
    return datetime.now(timezone.utc).isoformat()

agent = Agent(model="gpt-4o", tools=[get_weather, get_time])
result = agent.run(
    "What's the weather in Tokyo and London right now, "
    "and what's the current UTC time?"
)
# Both tools are called in parallel in one step

Structured Output Mode

Force the model to return a specific JSON format using response_format. Useful for extracting structured data from unstructured text:

from openclaw import Agent
from pydantic import BaseModel

class Article(BaseModel):
    title: str
    summary: str
    tags: list[str]
    sentiment: str  # positive | neutral | negative

agent = Agent(model="gpt-4o", response_format=Article)
result = agent.run("Analyse this article: ...")
article = result.parsed  # Typed Pydantic object
print(article.title, article.sentiment)

Error Handling

OpenClaw catches common function calling errors automatically:

  • Missing required field — Re-prompt the model with validation error details
  • Wrong argument type — Coerce if safe, otherwise re-prompt
  • Tool execution error — Return error message as tool result; let the model decide next step
  • Hallucinated tool name — Reject and re-prompt with available tool list
Key takeaways:
  • Function calling outputs structured JSON, not free text — this is what makes tool use reliable.
  • Write clear, specific tool descriptions; the model uses them to decide when and how to call.
  • OpenClaw abstracts provider differences — define tools once, run on any model.
  • Parallel calls reduce latency for tasks that don't depend on each other.
  • Use structured output mode (response_format) to extract typed data from any text.

Writing Effective Tool Schemas

The quality of your tool schema description directly influences how reliably the LLM calls your tool. Follow these guidelines:

  • Be specific about types and constraints. Use type: integer, minimum: 0, enum: ["a","b"] rather than generic strings. The model respects JSON Schema constraints.
  • Describe the preconditions. Your description should mention when to use the tool. E.g., “Use this tool when the user asks about current weather; do not use for historical data.”
  • Use illustrative parameter names. user_email is clearer than email; max_results is clearer than n.
  • Mark required vs optional. Required parameters prevent the model from omitting critical arguments; optional parameters should have clear defaults.
@tool(description="Search company knowledge base. Use for internal policy questions.")
def search_kb(
    query: str,            # The search query
    top_k: int = 5,        # Number of results to return (1-20)
    domain: str = "all",   # Filter: 'hr', 'legal', 'engineering', or 'all'
) -> list[dict]: ...