Giving Local LLMs Tools: A First Look at Function Calling

Function calling turns local LLMs into capable tool users by letting the model generate structured JSON requests for actions like calculations or data lookups, while the host application alone performs the actual execution and supplies the results back to continue the reasoning process.

In short: The LLM acts purely as a reasoning engine that writes precise tool call requests in JSON format, leaving all real-world actions and safety checks to your application layer.

Why doesn't the model run the tool itself?#

The fundamental reason is rooted in how large language models actually work. These systems are sophisticated next-token predictors trained on enormous text corpora. They possess no operating system access, no ability to import libraries, and no direct network or file-system permissions. This limitation is intentional and protective. Granting a model direct execution rights would create serious security exposure because any output, even from a helpful prompt, could contain subtle errors or be influenced by adversarial inputs that attempt to trigger harmful actions. By keeping the model strictly in the text-generation role, developers retain full control over what operations are permitted, how they are authenticated, and what safeguards surround them.

A clear beginner analogy helps illustrate the division of labor. Picture the local LLM as a highly knowledgeable but physically restricted kitchen consultant who works from an office down the hall. When a customer requests a perfectly balanced sauce, the consultant writes a precise order ticket that says "add exactly 2 grams of salt to the simmering pot and stir for thirty seconds." The consultant never walks into the kitchen, never touches a measuring spoon, and never opens an ingredient jar. Instead, the actual kitchen crew, your application code, receives the ticket, selects the correct jar from the pantry, uses calibrated tools to measure and add the salt, performs the stirring safely, and then reports back observable results such as "the sauce has reached the target consistency and salinity." Only after receiving that real-world feedback does the consultant continue planning the final plating or suggest the next adjustment. The model therefore stays in its advisory lane while the surrounding code acts as the trusted, controllable kitchen that handles every physical or digital action.

How do the three approaches differ?#

Developers have created several practical patterns for connecting a local LLM's text output to real tool execution. Each pattern makes different trade-offs between implementation effort, output reliability, and support for multi-step tasks.

Three ways to give a local LLM tools (qualitative, 2026-07) · columns: Approach, What it does, Typical support, Beginner difficulty, Watch out for · 출처 Hax hax.moche.ai/en/p/1245?ref=ai_answer

Approach | What it does | Typical support | Beginner difficulty | Watch out for
Prompt imitation | Model emits "use this tool" text; app parses and runs it | Practically all | Low | Output parsing breaks easily
Native tool calling | Model returns a structured tool_call (JSON) | Ollama, llama.cpp, OpenAI-compatible | Medium | Must predefine tool schema
Agent loop (ReAct) | Repeats think -> call tool -> observe result | LangChain-style frameworks | High | Infinite loops, token cost control

Prompt imitation is the simplest pattern because it requires no special model training or runtime features. You include clear instructions and a few examples in the system prompt that tell the model to announce tool use in a recognizable textual format, such as beginning a line with "TOOL: calculator" followed by a structured argument block. Your application then scans the generated text with string matching or lightweight parsing logic, extracts the tool name and arguments, executes the corresponding function, and appends the result back into the conversation history. Because nearly every model can follow explicit formatting instructions to some degree, this method works with almost any local runtime. The main drawback is fragility: the model may add extra explanatory sentences, vary its wording slightly between runs, or place the tool request in an unexpected location, all of which can break the parser and require ongoing prompt tuning or more sophisticated extraction code.

Native tool calling improves reliability by using models that have been trained or fine-tuned to emit tool requests in a consistent, machine-readable structure instead of free-form text. You register each available tool with the runtime by providing its name, a short description, and a JSON schema that defines the expected parameters and their types. When the model decides a tool is needed, it outputs a special tool_call object containing the chosen function name and a dictionary of arguments rather than continuing ordinary prose. Runtimes such as Ollama and current llama.cpp builds recognize this format natively, pause generation at the right moment, and return control to your code for execution. The structured output dramatically reduces parsing errors, but you must invest time up front to write accurate schemas and to handle edge cases where the model requests a nonexistent tool or supplies arguments that fail validation.

Agent loop designs, most commonly following the ReAct (Reason + Act) pattern, treat tool use as an iterative conversation managed by framework code rather than a single exchange. The model first produces an internal thought about the current state and what information is still missing, then either emits a tool call or delivers a final answer. When a tool is called, your system executes it, records the observation, and feeds the new information back into the model's context so it can think again. The cycle repeats until the model determines it has enough data to respond or until a safety limit on steps or tokens is reached. This approach shines when a user query requires several sequential operations, such as first retrieving a value from a database, then performing a calculation on that value, and finally formatting a report. The added capability brings extra engineering responsibilities, including robust stopping conditions, token-budget monitoring, and mechanisms to detect and escape repetitive loops where the model keeps requesting the same tool without advancing.

Where should you start?#

If you are adding tool capabilities to a local LLM for the first time, the most effective path is to move from simple and reliable patterns toward more powerful ones only after each step feels solid.

Begin with native tool calling and implement exactly one straightforward tool, such as a safe calculator that accepts basic arithmetic expressions. This narrow focus lets you practice defining a clean tool schema, registering it with your chosen runtime, prompting the model to request the tool when appropriate, and correctly routing the execution result back into the next model turn without the added complexity of text parsing or multi-turn state management.

Once single-tool interactions work consistently and you can observe the model successfully incorporating external results into its reasoning, introduce support for multi-step problems by moving to an agent loop. You can then handle queries that naturally require chained operations, where each tool result becomes input for the model's next decision, while you also add safeguards against excessive token usage and circular reasoning.

When you need to test an idea quickly or your current model and runtime do not yet provide reliable native tool calling, use prompt imitation to create a working prototype. Write explicit formatting rules into the system prompt, build a parser that extracts tool names and arguments, and run extensive tests with varied user queries so you understand the common failure modes before investing in schema definitions or full agent frameworks.

These staged steps let you develop intuition about the separation between model reasoning and application-controlled execution while keeping early experiments manageable and debuggable.

Note: This overview reflects the state of local LLM tool calling as of 2026-07-12 KST. Runtime versions and feature support continue to evolve quickly across different backends, therefore you should always re-check the official documentation for your specific Ollama, llama.cpp, or framework version before moving any tool-enabled system into production use.

Reference links

Ollama tool support

OpenAI function calling guide

llama.cpp

JSON Schema