Function Calling vs. Tool Calling in LLMs: What’s the Difference?

Содержание

Function calling and tool calling often describe the same model behavior: an LLM returns a structured request for an external capability instead of answering entirely from its own generated text. The practical difference is scope. A function is one type of tool, usually defined with a name, description, and JSON Schema. Tool calling is the broader term and can also include hosted search, code execution, computer control, file retrieval, or tools exposed through the Model Context Protocol (MCP).

The distinction also depends on the API. Some providers use the two terms interchangeably. Others reserve "function calling" for developer-defined code and use "tool calling" as the umbrella for both custom and hosted capabilities. The safest approach is to read the provider's request and response schema instead of inferring behavior from the label alone.

What Is LLM Function Calling?

LLM function calling is a structured interface between a model and application code. The application gives the model one or more function definitions, including each function's purpose and accepted arguments. When a user request matches one of those definitions, the model can return the function name and proposed arguments.

For example, an application might expose this function:

{
  "type": "function",
  "function": {
    "name": "get_order_status",
    "description": "Return the current status of an order owned by the user.",
    "parameters": {
      "type": "object",
      "properties": {
        "order_id": {
          "type": "string",
          "description": "The order ID shown in the user's account."
        }
      },
      "required": ["order_id"],
      "additionalProperties": false
    }
  }
}
{
  "type": "function",
  "function": {
    "name": "get_order_status",
    "description": "Return the current status of an order owned by the user.",
    "parameters": {
      "type": "object",
      "properties": {
        "order_id": {
          "type": "string",
          "description": "The order ID shown in the user's account."
        }
      },
      "required": ["order_id"],
      "additionalProperties": false
    }
  }
}
{
  "type": "function",
  "function": {
    "name": "get_order_status",
    "description": "Return the current status of an order owned by the user.",
    "parameters": {
      "type": "object",
      "properties": {
        "order_id": {
          "type": "string",
          "description": "The order ID shown in the user's account."
        }
      },
      "required": ["order_id"],
      "additionalProperties": false
    }
  }
}

If the user asks, "Has order A104 shipped?", the model may return a call to get_order_status with {"order_id":"A104"}. That structured output is a proposal, not proof that the order exists or that the user may access it.

The model normally does not execute developer-defined code. The application must parse the arguments, validate them, check authorization, run the function, and return the result. Google's function-calling documentation describes the same core pattern: the model connects natural-language intent to a defined function and supplies the parameters needed for the operation.

Function calling is useful when an LLM needs to:

  • retrieve current or private data from an API or database;

  • convert a natural-language request into typed arguments;

  • trigger a controlled business operation;

  • delegate deterministic work to a calculator, validator, or other service; or

  • select among several application capabilities at runtime.

It does not make every answer factual by itself. A model can still select the wrong function, invent an argument, misuse a correct result, or call a function when it should ask the user for clarification.

What Is Tool Calling?

Tool calling is the broader pattern in which a model requests access to functionality outside ordinary text generation. A tool may be a developer-defined function, but it can also be a search service, code runtime, file index, browser, database connector, image generator, or another agent.

A useful definition separates three components:

  1. Tool definition: What the model is told it can use, including the name, description, input schema, and sometimes output metadata.

  2. Tool call: The structured request produced by the model.

  3. Tool execution: The operation performed by application code, a provider-managed service, or another runtime.

Execution location is not what separates function calling from tool calling. A custom function can call a remote API, while a shell or calculator tool can run locally. Internet access is also not a defining difference. The relevant questions are what capability is exposed, who executes it, and which controls apply before execution.

Modern platforms increasingly use "tool" as the top-level API object. OpenAI, for example, defines a function as a specific type of tool and uses tool calling for the full request–execution–result loop in its function-calling guide. Anthropic likewise states that tool use is also called function calling, while distinguishing client tools from tools executed on its own infrastructure.

Tool calling as the umbrella term, with a developer-defined function as one of six capability types

Function Calling vs. Tool Calling: Are They the Same?

They are often interchangeable in conversation, but they are not always identical at the API level. Function calling is best treated as a subset of tool calling.

Dimension

Function Calling

Tool Calling

Scope

A named callable operation, usually with structured arguments

Any model-accessible external capability

Common inputs

JSON Schema or a similar typed parameter definition

Structured parameters, free-form input, UI actions, code, or provider-specific formats

Typical executor

The developer's application

The application, model provider, local runtime, MCP server, or another service

Examples

get_weather, create_ticket, lookup_customer

Function tools, web search, file search, code execution, computer use, MCP tools

Best term when discussing architecture

Precise when only custom functions are involved

Better when several tool types share one orchestration loop

The terminology should follow the implementation surface. On SiliconFlow, the Chat Completions API supports function tools through the tools field, with up to 128 function definitions per request as of August 26, 2026. In this interface, "LLM function calling" and "LLM tool calling" usually refer to the same mechanism. Broader tool categories available on other platforms are not automatically supported through the same endpoint. Check the current API reference before production rollout because supported tool types and request limits may change.

How an LLM Decides When to Call a Tool

An LLM does not inspect live systems before choosing a tool. It predicts a response from the conversation, instructions, and tool definitions included in its context. The tool name, description, parameter schema, examples, and surrounding policy all influence whether it answers directly, asks a question, or emits a call.

The decision usually involves four judgments:

  1. Need: Does the request require current data, private state, deterministic computation, or an external action?

  2. Match: Which available tool most closely fits the user's intent?

  3. Readiness: Are all required arguments present, or should the model ask for missing information?

  4. Permission: Is the requested action allowed, and does it require confirmation or human approval?

Tool descriptions should express boundaries, not just capabilities. "Look up an order" is weaker than "Return the status of an order owned by the authenticated user; do not use for product search." Distinct names and non-overlapping descriptions make routing easier to evaluate.

Developers may also have API-level controls that allow automatic selection, require a particular function, or prevent calls. Those controls vary by provider and model. Prompt instructions can guide behavior, but authorization and approval rules must remain in application code because generated instructions are not a security boundary.

What Happens After the Model Returns a Tool Call?

A production application should treat a tool call as untrusted structured input. The complete loop is:

  1. Send the user message and eligible tool definitions to the model.

  2. Inspect the response for one or more tool calls.

  3. Parse the arguments and validate them against the expected schema.

  4. Verify the tool name against an allowlist and enforce identity, permissions, budgets, and approval rules.

  5. Execute the approved operation with a timeout and an idempotency strategy where side effects are possible.

  6. Return the result with the matching tool-call ID.

  7. Ask the model to continue, then repeat until it returns a final answer or the application reaches a step limit.

The seven-step tool-call loop, from sending tool definitions to repeating until the application hits a step limit

The following example uses our OpenAI-compatible endpoint and focuses on the orchestration boundary. The get_order_status function still needs real authentication and data-access logic in production.

import json
import os
from openai import APIError, OpenAI

client = OpenAI(
    api_key=os.environ["SILICONFLOW_API_KEY"],
    base_url="https://api.siliconflow.com/v1",
    timeout=30.0,
    max_retries=2,
)

tools = [{
    "type": "function",
    "function": {
        "name": "get_order_status",
        "description": "Return the status of an order owned by the user.",
        "parameters": {
            "type": "object",
            "properties": {"order_id": {"type": "string"}},
            "required": ["order_id"],
            "additionalProperties": False,
        },
    },
}]

def get_order_status(order_id: str) -> dict:
    # Replace with an authorized database or service lookup.
    return {"order_id": order_id, "status": "shipped"}

messages = [{"role": "user", "content": "Has order A104 shipped?"}]

def call_model(messages):
    try:
        return client.chat.completions.create(
            model="deepseek-ai/DeepSeek-V3",
            messages=messages,
            tools=tools,
        )
    except APIError as exc:
        raise RuntimeError("Model request failed") from exc

response = call_model(messages)
assistant_message = response.choices[0].message
messages.append(assistant_message)

for call in assistant_message.tool_calls or []:
    if call.type != "function" or call.function.name != "get_order_status":
        raise ValueError("Unexpected tool call")

    arguments = json.loads(call.function.arguments)
    order_id = arguments.get("order_id")
    if not isinstance(order_id, str) or not order_id:
        raise ValueError("Invalid order_id")

    result = get_order_status(order_id)
    messages.append({
        "role": "tool",
        "tool_call_id": call.id,
        "content": json.dumps(result),
    })

final_response = call_model(messages) if assistant_message.tool_calls else response
print(final_response.choices[0].message.content)
import json
import os
from openai import APIError, OpenAI

client = OpenAI(
    api_key=os.environ["SILICONFLOW_API_KEY"],
    base_url="https://api.siliconflow.com/v1",
    timeout=30.0,
    max_retries=2,
)

tools = [{
    "type": "function",
    "function": {
        "name": "get_order_status",
        "description": "Return the status of an order owned by the user.",
        "parameters": {
            "type": "object",
            "properties": {"order_id": {"type": "string"}},
            "required": ["order_id"],
            "additionalProperties": False,
        },
    },
}]

def get_order_status(order_id: str) -> dict:
    # Replace with an authorized database or service lookup.
    return {"order_id": order_id, "status": "shipped"}

messages = [{"role": "user", "content": "Has order A104 shipped?"}]

def call_model(messages):
    try:
        return client.chat.completions.create(
            model="deepseek-ai/DeepSeek-V3",
            messages=messages,
            tools=tools,
        )
    except APIError as exc:
        raise RuntimeError("Model request failed") from exc

response = call_model(messages)
assistant_message = response.choices[0].message
messages.append(assistant_message)

for call in assistant_message.tool_calls or []:
    if call.type != "function" or call.function.name != "get_order_status":
        raise ValueError("Unexpected tool call")

    arguments = json.loads(call.function.arguments)
    order_id = arguments.get("order_id")
    if not isinstance(order_id, str) or not order_id:
        raise ValueError("Invalid order_id")

    result = get_order_status(order_id)
    messages.append({
        "role": "tool",
        "tool_call_id": call.id,
        "content": json.dumps(result),
    })

final_response = call_model(messages) if assistant_message.tool_calls else response
print(final_response.choices[0].message.content)
import json
import os
from openai import APIError, OpenAI

client = OpenAI(
    api_key=os.environ["SILICONFLOW_API_KEY"],
    base_url="https://api.siliconflow.com/v1",
    timeout=30.0,
    max_retries=2,
)

tools = [{
    "type": "function",
    "function": {
        "name": "get_order_status",
        "description": "Return the status of an order owned by the user.",
        "parameters": {
            "type": "object",
            "properties": {"order_id": {"type": "string"}},
            "required": ["order_id"],
            "additionalProperties": False,
        },
    },
}]

def get_order_status(order_id: str) -> dict:
    # Replace with an authorized database or service lookup.
    return {"order_id": order_id, "status": "shipped"}

messages = [{"role": "user", "content": "Has order A104 shipped?"}]

def call_model(messages):
    try:
        return client.chat.completions.create(
            model="deepseek-ai/DeepSeek-V3",
            messages=messages,
            tools=tools,
        )
    except APIError as exc:
        raise RuntimeError("Model request failed") from exc

response = call_model(messages)
assistant_message = response.choices[0].message
messages.append(assistant_message)

for call in assistant_message.tool_calls or []:
    if call.type != "function" or call.function.name != "get_order_status":
        raise ValueError("Unexpected tool call")

    arguments = json.loads(call.function.arguments)
    order_id = arguments.get("order_id")
    if not isinstance(order_id, str) or not order_id:
        raise ValueError("Invalid order_id")

    result = get_order_status(order_id)
    messages.append({
        "role": "tool",
        "tool_call_id": call.id,
        "content": json.dumps(result),
    })

final_response = call_model(messages) if assistant_message.tool_calls else response
print(final_response.choices[0].message.content)

The 30-second timeout and two retries are example client settings rather than platform limits. Set them according to the operation's latency budget and retry safety, especially when a function can create an external side effect.

To run this workflow on SiliconFlow, define the available functions through the OpenAI-compatible tools structure and select a model that currently supports function calling. Check model support again before changing the model ID because API availability and model capabilities can change independently.

Some reasoning models carry additional state across tool steps. When using DeepSeek V3.2 or GLM-4.7 with Interleaved Thinking, preserve and replay every reasoning_content segment exactly as returned, including segments generated after tool results. Dropping or editing that field can break the multi-step sequence. This requirement is model-specific and should not be assumed for every model.

Tool Calling in AI Agent Workflows

A single function call is not automatically an AI agent. An agent adds an execution loop around model decisions so the system can observe results, update state, choose another action, request approval, or stop.

Consider a support agent asked to refund a duplicate charge. It may need to:

  1. retrieve the account and transaction;

  2. determine whether the two charges meet the duplicate-payment rule;

  3. ask for confirmation if the refund is irreversible;

  4. submit the refund through an authorized function;

  5. verify the resulting transaction state; and

  6. summarize the outcome without exposing internal data.

The model supplies flexible routing, while the application supplies control. A reliable agent loop needs explicit limits on steps, elapsed time, spend, retries, and tool access. It also needs durable state, trace IDs, structured error handling, and a clear stop condition.

Use a deterministic workflow when the sequence is already known. Let the model choose tools when user intent, missing information, or intermediate results can genuinely change the next step. This keeps routine operations testable while reserving model judgment for the parts that need it.

Common Tool-Calling Failure Modes

Tool calling can fail before execution, during the operation, or after the result returns. Logging only the final answer hides where the failure began.

Failure Mode

What It Looks Like

Practical Control

Missed call

The model answers from memory when fresh or private data is required

Add negative and positive routing examples; measure call recall

Wrong tool

A similar function is selected

Separate overlapping descriptions and limit the active tool set

Invalid arguments

JSON is malformed, required fields are absent, or types are wrong

Parse safely and validate against the server-side schema

Plausible but wrong arguments

The schema passes, but an ID, date, unit, or scope is incorrect

Validate semantics and confirm ambiguous or high-impact values

Unnecessary call

The model invokes a tool for a conversational or already-answered request

Test no-call cases and tune the routing instruction

Incorrect sequence

A write occurs before a required read, check, or approval

Enforce state-machine and approval prerequisites outside the model

Duplicate side effect

A retry creates two tickets, payments, or messages

Use idempotency keys and record completed call IDs

Tool-result injection

Untrusted content tries to redirect the model or request secrets

Treat results as data, restrict cross-tool data flow, and filter sensitive context

Endless loop

The model repeats failing calls or alternates between tools

Set step, retry, latency, and spend limits with a defined fallback

Bad final synthesis

The tool succeeds, but the response misstates or omits the result

Score final-answer grounding separately from call accuracy

Errors should be returned in a form the model can act on, such as invalid_order_id, permission_denied, or temporary_unavailable. Do not expose stack traces, credentials, or internal network details. Retry only errors likely to be transient, and do not let the model override application policy.

How to Test Tool-Calling Reliability

Test the model, tool definitions, prompt, orchestration code, and execution environment as one system. A public benchmark can help screen models, but it cannot represent an application's exact schemas, policies, user language, or side effects.

The Berkeley Function-Calling Leaderboard evaluates native and prompt-based function calling across categories that include single, multiple, parallel, and multi-turn behavior. For production selection, add a private evaluation set built from real task shapes and known failure risks.

Include at least these case groups:

  • Direct calls: The request clearly maps to one function with all arguments present.

  • No-call prompts: The model should answer normally or decline rather than use a tool.

  • Missing information: One required field is absent and the correct action is clarification.

  • Confusable tools: Several functions have similar names, inputs, or business domains.

  • Adversarial values: IDs, dates, units, empty strings, unsupported enums, and extra fields stress the schema.

  • Multi-step tasks: Later calls depend on earlier tool results or changing state.

  • Permission boundaries: The user requests data or actions outside their authorization.

  • Failure injection: Tools time out, return partial data, reject a request, or produce untrusted text.

  • Retry cases: The harness verifies that repeated delivery cannot duplicate a side effect.

Nine test case groups for tool calling, from direct calls and no-call prompts to failure injection and retry safety

Report metrics by category instead of compressing everything into one accuracy number:

Metric

What It Measures

Tool-selection precision and recall

Whether the model calls the right tool when needed and avoids it when not needed

Argument validity rate

Whether generated arguments parse and satisfy the schema

Semantic argument accuracy

Whether values are correct for the user's request, not merely well typed

Clarification or abstention accuracy

Whether the model pauses when information, capability, or permission is missing

Sequence success rate

Whether multi-step calls occur in a valid order

End-state success rate

Whether the external system reaches the expected state

Duplicate side-effect rate

Whether retries or loops repeat an irreversible operation

Grounded final-answer rate

Whether the final response accurately reflects tool results

P50 and P95 latency and cost

Whether the complete loop meets operational targets

Use deterministic settings where the selected model supports them, but run repeated trials when sampling or provider behavior can vary. Freeze the model ID, schemas, prompt, tool responses, evaluator, and test-set version for every comparison. On SiliconFlow, this makes cross-model testing more meaningful because the API surface stays consistent while the selected supported model changes.

Set release gates around the most consequential errors. A customer-support lookup might tolerate an occasional clarification but require zero cross-account disclosure in the test set. A payment workflow should prioritize authorization, duplicate-execution prevention, and correct end state over fluent final wording.

Function Calling, MCP, and AI Agents: How They Fit Together

Function calling, MCP, and agents solve different layers of the system:

Layer

Main Responsibility

Function calling

Gives the model a structured way to request one developer-defined operation

Tool calling

Covers the broader model-to-capability interaction pattern

MCP

Standardizes how an AI application connects to servers that expose tools, resources, and prompts

Agent runtime

Repeats model and tool steps while managing state, policies, approvals, retries, and stopping

MCP does not replace the model's decision to use a capability. It standardizes the connection between an AI application and external servers. Under the current MCP specification, servers can expose tools with input schemas, while clients can list and call them through JSON-RPC messages. The host still decides which tools reach the model and how calls are authorized and executed. Because MCP is versioned, confirm the client, server, and negotiated capabilities before depending on newer protocol fields.

A common architecture therefore looks like this:

  1. An MCP client discovers tools from an approved server.

  2. The host converts or presents eligible definitions to the model.

  3. The model returns a tool call.

  4. The host validates policy and requests approval when required.

  5. The MCP client sends tools/call to the server.

  6. The host returns the result to the model.

  7. The agent loop continues or produces a final answer.

Tool metadata and tool results remain untrusted inputs. The MCP tools specification recommends clear visibility into exposed tools and human confirmation for operations. In practice, high-impact actions also need least-privilege credentials, destination checks, audit logs, and application-side authorization.

Use function calling when a small set of application-owned operations is enough. Add MCP when standardized discovery and reusable integrations reduce custom connector work. Add an agent runtime when tasks genuinely require repeated decisions, intermediate observations, or approval-aware branching. The layers can work together, but each adds a separate reliability and security surface that must be tested.

FAQs

Can SiliconFlow Tool Calls Be Streamed?

Yes. With streaming enabled, tool-call data can arrive incrementally through delta.tool_calls, so the client must assemble the fragments before parsing arguments. For supported Interleaved Thinking models, also collect delta.reasoning_content and preserve it for the next request.

Should Tool Definitions Be Versioned?

Yes. Version the tool name or schema contract when a change can alter accepted arguments, output meaning, or side effects. Record the version used by each evaluation and production trace so routing regressions can be connected to a specific definition change.

Is JSON Mode the Same as LLM Tool Calling?

No. JSON mode constrains how model-generated content is formatted. Tool calling also associates structured arguments with a named capability and an execution loop. Valid JSON alone does not establish that the right tool was selected, authorized, executed, or grounded in the final answer.

Can the Same Function Schema Be Used Across LLM Providers?

Not always. Providers commonly use JSON-style schemas, but request fields, supported schema features, tool-choice controls, parallel-call behavior, and result-message formats can differ. Adapt the integration to the target API and rerun the same reliability tests after changing providers or deployments.

How Should Credentials Be Passed to an LLM Tool?

Keep credentials outside the model context whenever possible. The host application should inject short-lived, least-privilege credentials at execution time after authorization checks. Redact secrets from prompts, tool results, traces, and error messages, and never treat a model-generated credential as trusted.

Готовы ускорить ваше развитие ИИ?

Готовы ускорить ваше развитие ИИ?

Готовы ускорить ваше развитие ИИ?