Function Calling and Tool Use with LLMs

Khimananda Oli 8 min read Virtualization
Function Calling and Tool Use with LLMs

By Khimananda Oli | Last reviewed: August 2026

Large language models cannot natively check server status, query a database, or trigger a deployment pipeline. Function calling and tool use with LLMs solves this limitation by allowing the model to output structured JSON instructions that your application executes deterministically. Instead of hallucinating API responses, the model acts as an intent router, selecting the correct tool from a defined schema while your backend handles the actual execution and returns verified results. This pattern transforms chat interfaces into reliable automation agents capable of interacting with live infrastructure.

How does function calling and tool use with LLMs actually work?

The mechanism relies on separating reasoning from execution. When you send a prompt along with a list of available tools (defined via JSON Schema), the model evaluates whether a tool is needed. If so, it enters a special generation mode constrained by the schema, producing valid JSON rather than prose. Crucially, the model never runs the code itself; it only predicts the correct function name and arguments based on its training and the provided context.

User / AppLLM RouterExternal Tool1. Prompt + Schema3. Execute Func4. Return Result5. Final Answer2. Gen JSON Call
The five-step handshake of function calling and tool use with LLMs ensures deterministic execution outside the model context window.

Your application then validates this JSON against the schema before execution. This validation step is non-negotiable in production environments. I have seen models occasionally invent parameters that do not exist or pass strings where integers are expected. By treating the LLM output as untrusted input—similar to handling user-submitted form data—you prevent injection attacks and runtime errors. Once the tool returns a result, you append it to the conversation history as a "tool response" message, allowing the model to synthesize a natural language answer grounded in factual data. For teams exploring broader automation strategies, understanding this loop is foundational to automating DevOps tasks with AI assistants effectively.

How do you define robust tool schemas for production?

The quality of your tool definitions directly determines agent reliability. Vague descriptions lead to vague invocations. In practice, you must write tool descriptions as if documenting an API for a junior developer: explicit, constrained, and example-driven. Modern providers support strict structured outputs that guarantee 100% schema adherence, eliminating parsing failures entirely.

Crafting descriptive function signatures

Do not rely solely on parameter names. Use the description field to specify units, formats, and edge cases. If a parameter accepts an enum, list every valid value. If a date is required, specify ISO 8601 format explicitly. Here is a production-grade Python definition for checking Kubernetes pod health:

<script type="application/json">
{
  "name": "get_pod_status",
  "description": "Retrieves current status and restart count for a specific Kubernetes pod. Use this ONLY when debugging crash loops or readiness failures.",
  "parameters": {
    "type": "object",
    "properties": {
      "namespace": {
        "type": "string",
        "description": "Kubernetes namespace. Default: 'production'."
      },
      "pod_name": {
        "type": "string",
        "description": "Exact pod name including hash suffix (e.g., 'api-server-7d4b8c-x9z')."
      }
    },
    "required": ["pod_name"],
    "additionalProperties": false
  }
}
</script>

Note the additionalProperties: false constraint. This prevents the model from hallucinating extra parameters like cluster_id when none exists. When building agents that generate infrastructure code through these tools, applying similar rigor to schema design prevents costly misconfigurations, a topic covered deeply in our guide on generating IaC with AI guardrails.

Handling parallel and sequential tool calls

Modern APIs support parallel function calling, where the model requests multiple independent tools in a single turn. Your execution layer must handle this asynchronously. However, be cautious: parallel calls increase token usage and complexity. For dependent operations (e.g., "get instance ID" then "terminate instance"), force sequential execution by returning intermediate results and letting the model decide the next step. Never chain destructive actions in a single parallel batch without human confirmation gates.

What security guardrails prevent dangerous tool execution?

Giving an LLM access to production tools introduces significant risk. The model can be tricked via prompt injection into calling functions with malicious parameters. Security must be enforced at the application layer, never trusted to the model's "judgment." Treat every tool invocation as potentially hostile.

  • Least-privilege credentials: Tool execution should use scoped API keys with read-only access by default. Write operations require separate, audited credentials.
  • Input sanitization: Validate all LLM-generated parameters against allowlists. Reject SQL-like strings in filename parameters. Escape shell arguments rigorously.
  • Rate limiting and budgets: Implement per-session caps on tool calls. A runaway agent querying a paid API can exhaust budgets in minutes.
  • Audit logging: Log every tool call with timestamp, input parameters, output, and latency. This is essential for SOC 2 compliance and incident forensics.
  • Human-in-the-loop: For destructive operations (delete, deploy, modify IAM), require explicit user confirmation after showing the exact parameters the model intends to use.
LLM Output(Untrusted JSON)Guardrail Layer• Schema Validation• Allowlist Check• Rate Limiter• Audit Logger• Human Approval GateSafe Execution(Scoped Creds)
Defense-in-depth guardrails ensure function calling and tool use with LLMs remains safe even under adversarial prompts.

In my experience helping teams achieve ISO 27001 certification, automated evidence collection via LLM agents only passes audit when these guardrails are demonstrably enforced and logged. The model is a component, not a trust boundary. For deeper monitoring strategies, refer to our article on LLMOps monitoring and guardrails.

How do you handle errors and retries in tool workflows?

Tools fail. Networks timeout. APIs return 429s. Your agent must handle these gracefully without entering infinite retry loops or exposing raw stack traces to users. The key is feeding structured error messages back to the model so it can self-correct or explain the failure.

  1. Catch exceptions locally: Never let tool errors crash the agent loop. Wrap executions in try/catch blocks.
  2. Return semantic errors: Instead of "Error: 500", return {"error": "database_timeout", "message": "Query exceeded 30s limit. Try narrowing date range."}.
  3. Limit retries: Implement exponential backoff with max attempts (typically 3). After exhaustion, return the final error to the model.
  4. Context window management: Failed tool calls consume tokens. Prune old failed attempts from history before sending to the model to avoid context overflow.
  5. Fallback tools: Define secondary tools for critical paths. If primary search fails, offer a cached lookup alternative.

This error-handling discipline separates demo-grade chatbots from production systems. When integrating with observability platforms, structured error returns enable the model to correlate failures with metrics, enhancing AI-powered log analysis capabilities significantly.

Function calling vs structured outputs vs RAG: which to choose?

Engineers often conflate these patterns. Each serves distinct purposes, and choosing incorrectly leads to over-engineering or unreliable behavior. Understanding the trade-offs prevents wasted effort.

PatternBest ForLatencyComplexityRisk Profile
Function CallingExecuting actions, querying live data, multi-step workflowsMedium-High (round trips)High (schema + execution layer)High (requires guardrails)
Structured OutputsParsing documents, extracting entities, classificationLow (single pass)Low (schema only)Low (no side effects)
RAGAnswering questions from static knowledge basesMedium (retrieval + gen)Medium (indexing + retrieval)Medium (hallucination risk)

Use function calling when the answer depends on current state or requires side effects. Use structured outputs when you need guaranteed JSON format without execution. Use RAG when answering questions from large, static corpora. Many production systems combine all three: RAG retrieves policy docs, structured outputs parse them, and function calling enforces compliance checks.

Start: User RequestRequires Live Data / Action?YesNoFunction CallingExecute + Return ResultStatic Knowledge?YesNoRAG RetrievalStructured Output
Decision framework for selecting function calling and tool use with LLMs versus alternative patterns based on data freshness and action requirements.

Implementing function calling and tool use with LLMs in production

Moving from prototype to production requires operational discipline. Start with a minimal tool set—three to five well-defined functions beat twenty vague ones. Monitor token consumption per tool call; complex schemas inflate costs. Implement caching for idempotent read operations to reduce latency and API spend. Version your tool schemas alongside application code; breaking changes require coordinated model prompt updates.

Test extensively with edge cases. Models behave differently with empty arrays, null values, or unusually long parameter strings. Create evaluation datasets covering happy paths, malformed inputs, and permission denials. Automate these tests in CI to catch regressions before deployment. Remember that provider updates can shift model behavior; pin model versions and re-evaluate tools after upgrades.

Finally, measure business impact. Track successful tool invocations versus fallbacks. Monitor user satisfaction when tools fail. These metrics guide refinement far better than theoretical benchmarks. Function calling and tool use with LLMs is not a set-and-forget feature; it is a living interface between probabilistic reasoning and deterministic systems that demands continuous tuning.

If your team needs help designing secure, compliant tool architectures or evaluating whether function calling fits your use case, reach out to discuss your specific requirements. Building reliable AI agents requires engineering rigor, not just prompt magic.

Frequently Asked Questions

Function calling allows LLMs to output structured JSON matching a predefined schema instead of free text. This enables applications to reliably parse model responses and execute external code, APIs, or database queries based on user intent without fragile regex parsing.

Standard completions generate unstructured text for humans. Tool use forces the model to select specific functions and provide valid arguments defined in your system prompt, enabling deterministic programmatic execution rather than conversational guesswork.

OpenAI, Anthropic, Google Gemini, Mistral, and Cohere all offer native tool use APIs. Most open-weight models like Llama 3 and Qwen also support it via compatible inference servers like vLLM or Ollama with proper chat templates.

Yes, most providers accept JSON schemas describing function names, parameters, and descriptions. The model uses these definitions during inference to generate valid calls, though schema complexity limits vary significantly between different model families and versions.

Yes, tool definitions consume input tokens on every request. Complex schemas with many parameters can add hundreds of tokens. Optimize by using concise descriptions, removing unused tools dynamically, and caching system prompts where the provider supports it.

Always validate generated arguments against your schema server-side before execution. Use strict typing, required fields, and enum constraints in your tool definitions. Never trust raw model output directly in SQL queries, shell commands, or file system operations without sanitization.

Parallel calling lets models return multiple tool invocations in one response. Use it when tasks are independent, like fetching weather and calendar data simultaneously. Sequential dependencies still require separate round trips to maintain correct execution order and context.

Log the full tool call JSON, model response, and execution result separately. Implement structured error returns that feed back into the conversation so the model can self-correct. Monitor latency spikes indicating schema confusion or excessive retry loops.

Top open-weight models like Qwen 2.5 and Llama 3 now approach proprietary performance for standard tool use. However, complex multi-step reasoning and edge-case schema adherence still lag behind frontier models in demanding production environments requiring high reliability.

Treat all model-generated arguments as untrusted user input. Apply allowlists, sandbox execution environments, and rate limiting. Never expose sensitive tool descriptions that reveal internal architecture. Validate outputs independently of the LLM that generated them.

Loops occur when tools return ambiguous errors or the model misunderstands completion criteria. Set maximum iteration limits, provide clear success/failure signals in tool responses, and include explicit stop conditions in your system prompt to prevent runaway execution.

Use function calling for structured queries with known parameters like database lookups. Use RAG for semantic search across unstructured documents. Many production systems combine both, using tools to trigger retrievals and then synthesizing results conversationally.

Namespace functions with version prefixes and maintain backward compatibility during transitions. Deprecate old tools gradually by marking them optional in schemas while introducing replacements. Document breaking changes clearly and monitor usage metrics before removing legacy definitions entirely.

Yes, most providers stream tool call deltas incrementally. Your client must buffer partial JSON until complete before parsing. Handle incomplete chunks gracefully and implement timeout logic for stalled streams to avoid hanging application threads during long generations.

Use provider-specific evaluation SDKs or generic frameworks like Braintrust and LangSmith. Create golden datasets mapping user queries to expected tool calls and arguments. Automate regression testing on schema changes to catch performance degradation before deployment.