Building a Chatbot with Function Calling: A Practical Walkthrough

Function calling turns a language model into something that can actually take action. A practical walkthrough of building a chatbot that uses tools reliably.

Beyond Plain Text Generation

Function calling (also called tool use) is what turns a language model from a text generator into something that can actually do things — look up real-time data, perform calculations, query a database, or trigger an action in another system. Understanding the mechanics well is essential for building any chatbot that needs to go beyond answering from its training data alone.

How Function Calling Actually Works

You describe available functions to the model — a name, a description, and a schema for its parameters — alongside the conversation. When the model determines a function would help answer the user’s request, instead of generating a plain text response, it generates a structured request to call that function with specific arguments. Your application code executes the actual function (the model never runs it directly), and the result is fed back into the conversation for the model to incorporate into its final response.

Designing Good Function Descriptions

The single highest-leverage thing you can do to improve function-calling reliability is writing clear, unambiguous function descriptions and parameter definitions. A function called get_data with a vague description invites the model to misuse it or miss it entirely when relevant; a function called get_current_weather with a description specifying it returns temperature and conditions for a named city gives the model exactly what it needs to decide correctly when to call it.

A Practical Walkthrough: Building a Support Chatbot

Consider a customer support chatbot that needs to check order status. You’d define a get_order_status function accepting an order ID, with a clear description of what it returns. When a user asks “where’s my order,” the model recognizes it needs this function, extracts or asks for the order ID, calls the function, and your backend returns real order data — the model then formats that data into a natural, helpful response rather than hallucinating a plausible-sounding but fabricated answer.

Handling Multi-Step Tool Use

More complex requests often require multiple function calls in sequence — checking a customer’s account, then looking up their most recent order, then checking that order’s shipping status. Modern models handle this reasonably well autonomously, calling functions, receiving results, and deciding whether another call is needed before generating a final response, effectively running a loop until it has enough information to answer.

Validating and Sandboxing Function Execution

Never trust that a model-generated function call is inherently safe to execute as-is. Validate arguments against expected types and ranges before executing any function, especially ones with real-world side effects like sending an email or modifying a database record. For consequential actions, consider requiring explicit user confirmation before execution, rather than letting the model trigger them autonomously based purely on its own judgment.

Error Handling Within the Conversation

When a function call fails — invalid input, a downstream service being down, a not-found result — return a clear error message as the function result rather than letting your application crash or silently fail. The model can then handle this gracefully in conversation, explaining the issue to the user or trying a corrected approach, which produces a far better user experience than an unhandled exception surfacing as a broken chat.

Streaming Responses with Function Calls

For a responsive user experience, stream the model’s text output as it’s generated, but be aware that function calls typically need to be received in full before execution — you can’t partially execute a function call as its arguments stream in. Design your UI to show an appropriate “thinking” or “looking that up” state during function execution, so the interaction doesn’t feel like it’s stalled during the necessarily synchronous function call and result round trip.

Testing Function-Calling Behavior

Build a test suite of representative user messages and verify the model calls the correct function with reasonable arguments — this is worth automating and running whenever you change function descriptions or switch model versions, since subtle wording changes in a function description can noticeably affect when and how a model chooses to invoke it.

Practical Recommendations

  • Write function descriptions as carefully as you’d write documentation for a human API consumer — ambiguity here directly translates to unreliable behavior.
  • Validate all function arguments server-side before execution, regardless of how confident the model seems in its output.
  • Require explicit confirmation for consequential, hard-to-reverse actions rather than fully autonomous execution.
  • Build an eval set specifically for function-calling accuracy, separate from your general response-quality evaluation.