Understanding Function Calling: How AI Agents Safely Execute Tools

This article explains why AI agents need function calling, describes the structured tool‑call protocol between LLMs and runtimes, shows how to define and secure function tools, and provides best‑practice code and checklists for production deployments.

inShocking
inShocking
inShocking
Understanding Function Calling: How AI Agents Safely Execute Tools

Why Agents Need Function Calling

Without external tools an LLM can only answer from its static context and training data, which fails for real‑time information such as weather, order status, or internal knowledge bases. Early agents tried to parse free‑text like get_weather(city="beijing"), but this approach suffered from unstable output formats, type mismatches, ambiguous intent in multi‑tool scenarios, lack of error handling, and security risks.

Component Responsibilities

LLM : understand intent, select a tool, generate parameters, and interpret tool results. Must not bypass permission checks.

Agent Runtime : maintain conversation state, orchestrate call loops, validate parameters, enforce authentication, handle retries, and record audit logs. Must never trust model output unconditionally.

Tool : perform a single, well‑defined business capability and return a structured result. Must not decide user permissions.

Security / Policy : decide whether the caller, tenant, resource, and operation are allowed.

Full Tool‑Call Loop (OpenAI Specification)

Application sends the user request and the list of available tools to the model.

The model returns zero, one, or multiple function_call objects.

The application validates the arguments and executes the corresponding code.

The result (including a call_id) is sent back to the model.

The model uses the tool result to generate a final answer or to initiate another tool call.

The diagram below visualises this closed‑loop process.

Function Calling Tool Call Sequence
Function Calling Tool Call Sequence

Defining a Function Tool

Below is a strict‑mode weather tool definition in Python:

tools = [
  {
    "type": "function",
    "name": "get_current_weather",
    "description": (
      "Get the current weather for a city. "
      "Use this tool when the user asks about current weather, temperature, humidity, or wind. "
      "Do not use it for historical weather."
    ),
    "parameters": {
      "type": "object",
      "properties": {
        "location": {
          "type": "string",
          "description": "City and country, for example: Beijing, China"
        },
        "unit": {
          "type": "string",
          "enum": ["celsius", "fahrenheit"],
          "description": "Temperature unit"
        }
      },
      "required": ["location", "unit"],
      "additionalProperties": false
    },
    "strict": true
  }
]

Field Explanations

type

: must be function for custom tools; other tool types are also supported. name: a stable identifier using a verb and business object (e.g., get_current_weather). description: when to use, when not to use, and what the tool does. parameters: JSON‑Schema describing input; aim to make illegal states impossible. strict: when true, the model must produce arguments that exactly match the schema.

Strict Mode Guarantees and Limits

With strict: true, the model’s arguments reliably follow the declared schema, but strict mode does not guarantee business validity, correct tenancy, or side‑effect‑free execution. For example, {"location": "beijing1", "unit": "celsius"} conforms to the schema yet may refer to a non‑existent city.

Important: strict only constrains structure; authentication, business rules, and trust must be enforced by the runtime.

Controlling Tool Selection (tool_choice)

"auto"

: default, model may call zero, one, or many tools. "required": at least one tool must be called. "none": prohibit any tool calls.

Specify a function name to force that tool. allowed_tools: restrict the model to a whitelist of tools.

If parallel execution is not supported, set parallel_tool_calls=False in the request.

Production‑Level Validation & Security Layers

Parsing : ensure the arguments are valid JSON; catch parsing errors.

Schema : enforce type, required fields, enums, and disallow extra properties (use strict).

Business Rules : verify that values such as city, order ID, or amount are permissible.

Identity & Tenant : confirm who is invoking the tool and under which tenant.

Permission : apply RBAC/ABAC checks before execution.

Human Approval : require manual confirmation for high‑risk actions.

Execution Control : enforce timeouts, rate limits, concurrency limits, and sandboxing.

Audit : log caller, timestamp, parameters, and outcomes.

High‑Risk Tool Mitigation & Prompt Injection

Never expose low‑level dangerous tools such as execute_sql, run_shell, or unrestricted HTTP requests. Instead, expose constrained business‑level functions (e.g., get_order_status, cancel_order) that perform internal validation and have no arbitrary side effects.

All tool outputs must be treated as untrusted data; they must not be allowed to elevate subsequent instructions. High‑risk tools require separate authorization or manual approval, and secret keys or full permission contexts must never be injected into the model prompt.

Error Handling and Idempotency

Tools should return a structured result so the model can decide whether to retry:

{
  "ok": false,
  "error": {
    "code": "WEATHER_SERVICE_TIMEOUT",
    "message": "Weather service did not respond within 3 seconds",
    "retryable": true
  }
}

Read‑only queries may be auto‑retried a limited number of times.

Write operations should be retried only when an idempotency_key is provided.

Permission errors, validation failures, or non‑retryable codes must not be retried.

Prevent infinite model loops by capping the maximum number of tool‑call rounds.

Designing High‑Accuracy Tools

Clear Naming : use verb + object (e.g., get_current_weather).

Precise Description : state when to use, when not to use, and any boundaries.

Constrained Parameters : use enums and required fields; avoid free‑form strings.

Avoid Redundancy : do not let the model fill fields already known to the runtime (e.g., tenant_id, user_id).

Prevent Overlap : merge semantically similar tools or give them distinct signatures.

Limit Tool Count : keep the number of available functions low (ideally < 20) to reduce token usage and selection errors.

Tool Design Review Checklist

Tool name is stable and descriptive.

Description includes usage and non‑usage scenarios.

Parameters have explicit types, enums, and no unnecessary fields. strict is enabled and schema meets strict‑mode requirements.

No semantic overlap with other tools.

Runtime does not rely on the model to provide known identity/tenant fields.

Runtime Checklist

Support zero, one, or multiple tool calls per response.

Associate each call with a call_id for tracing.

Enforce maximum call rounds, per‑call timeout, and overall task timeout.

Distinguish retryable vs. non‑retryable errors.

Provide idempotency keys for write operations.

Return structured, sanitized error objects to the model.

Security Checklist

Re‑run schema and business validation on the server.

Perform identity, tenant, and resource‑level authorization based on a trusted session.

Require user confirmation or manual approval for high‑risk actions.

Never expose raw SQL, shell, file‑system, or unrestricted HTTP capabilities.

Treat all external content and tool outputs as untrusted.

Mask sensitive fields, logs, and error details.

Observability & Evaluation

Log tool selection, parameter summary, latency, result, and error codes.

Enable trace reconstruction of a full agent interaction.

Benchmark tool selection and parameter accuracy on real task sets.

Cover scenarios: no tool, single tool, multiple tools, permission denial, timeout.

Run regression tests after any schema or prompt change.

Common Misconceptions

LLM does not execute functions; it only generates a call request.

JSON arguments still require schema, business, and permission validation.

Enabling strict only enforces structure, not security.

A response may contain zero, one, or many tool calls.

More tools do not automatically increase agent capability; overlapping semantics hurt accuracy.

Longer descriptions are not inherently better; clarity and boundary definition matter.

Model cannot remember previous tool results unless the runtime feeds them back.

Permissions must be enforced by server code, not by prompt engineering.

Final Summary

Function Calling establishes a structured protocol that lets an LLM request external capabilities, while the runtime validates, authorizes, executes, and returns results. It solves the instability of free‑text tool protocols, but business correctness, security, retries, and observability remain responsibilities of the runtime. An agent’s reliability therefore depends on the tight integration of model intent, tool design, and runtime safeguards, not merely on the model’s raw abilities.

Original Source

Signed-in readers can open the original source through BestHub's protected redirect.

Sign in to view source
Republication Notice

This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactadmin@besthub.devand we will review it promptly.

PythonLLMsecurityFunction CallingAgent Runtime
inShocking
Written by

inShocking

Occasional sharing

0 followers
Reader feedback

How this landed with the community

Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.