Kimi API Tool Calling: Functions and Structured Workflows

Kimi API tool calling lets a model request actions from functions that your application exposes. You define each tool with JSON Schema, send the definitions through the tools parameter, inspect the returned tool_calls, validate and execute the requested functions in your backend, and return every result with the matching tool_call_id. Kimi does not execute your code, access your database or approve an operation by itself. Your application remains responsible for authentication, authorization, validation, side effects and error handling. Use tools and tool_calls; the deprecated functions and function_call parameters are not supported.

Tool-choice support and reasoning behavior differ between models. Check the current Kimi Model Parameter Reference before changing the model orforcing a tool path.

Tool calling is a protocol, not remote code execution:
User request → model proposes a function and arguments → your backend validates and executes it → your backend returns a Tool message → the model produces the final answer.

Kimi Tool Calling at a Glance

ComponentPurposeWho controls it?
toolsDeclares the functions available during the requestYour application
function.nameProvides a stable identifier for the toolYour application
function.descriptionExplains when the model should select the toolYour application
function.parametersDefines accepted arguments using JSON SchemaYour application
tool_choiceAllows, forbids orrequires tool selectionYour request
finish_reason="tool_calls"Indicates that the response requests tool executionKimi API
message.tool_callsContains one ormore requested function callsKimi model
function.argumentsSerialized JSON containing the proposed argumentsKimi model; your backend must validate it
tool_call.idIdentifies one requested tool callKimi API
role="tool"Returns a function result to the modelYour application
reasoning_contentPreserves reasoning continuity in supported thinking modelsKimi API; your application must preserve it when required
response_formatConstrains the model’s final answer formatYour request

Complete the basic account and SDK setup first with the Kimi API Quickstart. The examples below use the Global Kimi Open Platform, https://api.moonshot.ai/v1, and the current flagship Kimi K3 model.

What Kimi Tool Calling Is—and What It Is Not

Tool calling gives the model a controlled vocabulary of operations. A tool may represent:

  • A database lookup.
  • A private search service.
  • A calculator.
  • A CRM action.
  • A file generator.
  • A payment-status query.
  • A business workflow.
  • An external API.

The model can decide that a tool is useful and generate arguments. It cannot automatically:

  • Import orcall your Python function.
  • Connect to a private database.
  • Confirm that the requesting user has permission.
  • Know whether an operation is safe oridempotent.
  • Guarantee that generated arguments are valid.
  • Commit a payment, refund ordeletion without your code.

The official Kimi Tool Calls guide describes the same separation: Kimi produces the call information, while the surrounding application executes the operation and returns its result.

Function Calling, Tool Calling and Structured Output

These terms solve different problems.

FeatureUse it forWhat it returns
tools / tool_callsRequesting data oractions from external functionsFunction name and serialized JSON arguments
Legacy functions / function_callOld OpenAI-style function protocolDeprecated and unsupported by Kimi
JSON ModeMaking the final answer valid parsable JSONA valid JSON object, but not necessarily an exact schema
Structured OutputConstraining the final answer to a JSON Schemamessage.content matching the declared schema
Tool strictTightening the arguments generated for a functionMore constrained Tool Call arguments

Use Tool Calling when something outside the model must happen. Use Structured Output when the final response must fit a machine-readable contract. A production workflow often uses both: tools collect orchange data, then response_format constrains the final answer.

The Kimi Tool-Calling State Machine

Official Kimi API tool-calling workflow from tool definition to final response
Official Kimi API documentation illustrating the tool-calling workflow from request to tool result. Screenshot captured September 8, 2026.
  1. Declare tools. Describe each function and its arguments using JSON Schema.
  2. Send the request. Include the conversation and the relevant tool definitions.
  3. Inspect the response. When finish_reason is tool_calls, do not treat the response as the final answer.
  4. Preserve the Assistant message. Append the complete returned message to the conversation.
  5. Validate every call. Check the function name, parse the JSON arguments and validate them against the schema.
  6. Authorize the operation. Confirm that the user and request may perform the action.
  7. Execute the function. Call the approved backend implementation.
  8. Return a Tool message. Include the matching tool_call_id and serialize the result as a string.
  9. Call Kimi again. Allow another tool round orreceive the final answer.
  10. Stop safely. End on a final response, an explicit error, a timeout ora maximum number of rounds.

A Tool Loop can request several functions in one round and several rounds in one conversation. The loop—not a single API request—is what turns tool calling into a structured Agent workflow.

Design a Clear Tool Contract

Kimi API tool contract anatomy explaining the function definition, model tool call, JSON arguments, tool call ID and matching tool result

The model selects tools from names, descriptions and schemas. A clear contract reduces ambiguous selection and invalid arguments.

TOOLS = [
    {
        "type": "function",
        "function": {
            "name": "lookup_order",
            "description": (
                "Read the current status of one demo order. "
                "Use this tool only when the user asks about an existing order."
            ),
            "strict": True,
            "parameters": {
                "type": "object",
                "properties": {
                    "order_id": {
                        "type": "string",
                        "description": "Order ID, for example DEMO-100",
                    }
                },
                "required": ["order_id"],
                "additionalProperties": False,
            },
        },
    },
    {
        "type": "function",
        "function": {
            "name": "get_refund_policy",
            "description": (
                "Read the refund policy for a supported market. "
                "This is a read-only tool and does not create a refund."
            ),
            "strict": True,
            "parameters": {
                "type": "object",
                "properties": {
                    "market": {
                        "type": "string",
                        "enum": ["demo-market"],
                        "description": "Market whose refund policy is required",
                    }
                },
                "required": ["market"],
                "additionalProperties": False,
            },
        },
    },
]

Tool-schema rules that improve reliability

  • Use a short, stable andunique function name.
  • State when the function should—and should not—be used.
  • Keep each tool focused on one responsibility.
  • Mark mandatory arguments in required.
  • Use enum for closed sets.
  • Set additionalProperties to false when extra fields are not allowed.
  • Avoid two tools with nearly identical descriptions.
  • Do not hide argument constraints only inside the system prompt.
  • Validate again in your backend even when strict is enabled.

Send a Raw Tool Request with cURL

The following request asks K3 to inspect an order. It does not execute the function; it asks the model to generate a Tool Call.

curl https://api.moonshot.ai/v1/chat/completions \
  -H "Authorization: Bearer $MOONSHOT_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "model": "kimi-k3",
    "reasoning_effort": "low",
    "messages": [
      {
        "role": "user",
        "content": "Check the status of order DEMO-100."
      }
    ],
    "tools": [
      {
        "type": "function",
        "function": {
          "name": "lookup_order",
          "description": "Read the current status of one demo order.",
          "strict": true,
          "parameters": {
            "type": "object",
            "properties": {
              "order_id": {
                "type": "string",
                "description": "Order ID, for example DEMO-100"
              }
            },
            "required": ["order_id"],
            "additionalProperties": false
          }
        }
      }
    ],
    "tool_choice": "auto",
    "max_completion_tokens": 800
  }'

When the model selects the function, the response normally contains finish_reason="tool_calls". Its message.content may be empty, although Kimi can occasionally include a short explanation of the intended call.

Read a tool_calls Response

{
  "choices": [
    {
      "finish_reason": "tool_calls",
      "message": {
        "role": "assistant",
        "content": "",
        "reasoning_content": "...",
        "tool_calls": [
          {
            "id": "call_example_001",
            "type": "function",
            "function": {
              "name": "lookup_order",
              "arguments": "{\"order_id\":\"DEMO-100\"}"
            }
          }
        ]
      }
    }
  ]
}

Important details:

  • function.arguments is a serialized JSON string. Parse it before validation.
  • The function name is a proposal from the model. Match it against an explicit allowlist.
  • The arguments are untrusted input. Validate types, values and business rules.
  • The id identifies this exact call and must appear in its result message.
  • The complete Assistant message belongs in the conversation before the Tool result.
  • The response may include several Tool Calls.

Complete Python Tool Loop with Structured Output

This example uses fictional local data. It demonstrates:

  • Two read-only tools.
  • A function allowlist.
  • JSON Schema validation.
  • Structured Tool errors.
  • Matching Tool Call IDs.
  • A maximum round limit.
  • K3 Preserved Thinking.
  • A strict final JSON Schema.

Install the dependencies:

python -m pip install --upgrade openai jsonschema

Create tool_loop.py:

from __future__ import annotations

import json
import os
from typing import Any, Callable

from jsonschema import Draft202012Validator, ValidationError
from openai import OpenAI


BASE_URL = "https://api.moonshot.ai/v1"
MODEL = "kimi-k3"
MAX_TOOL_ROUNDS = 6


# Fictional local data used only by this example.
ORDERS = {
    "DEMO-100": {
        "order_id": "DEMO-100",
        "status": "delayed",
        "market": "demo-market",
        "days_late": 4,
    }
}

REFUND_POLICIES = {
    "demo-market": {
        "market": "demo-market",
        "eligible_after_days_late": 3,
        "requires_manual_review": True,
    }
}


TOOLS: list[dict[str, Any]] = [
    {
        "type": "function",
        "function": {
            "name": "lookup_order",
            "description": (
                "Read the status of one existing demo order. "
                "Use this only for order-status questions."
            ),
            "strict": True,
            "parameters": {
                "type": "object",
                "properties": {
                    "order_id": {
                        "type": "string",
                        "description": "Order ID, for example DEMO-100",
                    }
                },
                "required": ["order_id"],
                "additionalProperties": False,
            },
        },
    },
    {
        "type": "function",
        "function": {
            "name": "get_refund_policy",
            "description": (
                "Read the refund policy for a supported market. "
                "This tool is read-only and never submits a refund."
            ),
            "strict": True,
            "parameters": {
                "type": "object",
                "properties": {
                    "market": {
                        "type": "string",
                        "enum": ["demo-market"],
                        "description": "Market whose policy is required",
                    }
                },
                "required": ["market"],
                "additionalProperties": False,
            },
        },
    },
]


FINAL_RESPONSE_FORMAT: dict[str, Any] = {
    "type": "json_schema",
    "json_schema": {
        "name": "order_assessment",
        "strict": True,
        "schema": {
            "type": "object",
            "properties": {
                "summary": {"type": "string"},
                "order_status": {"type": "string"},
                "refund_eligible": {"type": "boolean"},
                "next_action": {"type": "string"},
                "evidence": {
                    "type": "array",
                    "items": {"type": "string"},
                },
            },
            "required": [
                "summary",
                "order_status",
                "refund_eligible",
                "next_action",
                "evidence",
            ],
            "additionalProperties": False,
        },
    },
}


def lookup_order(order_id: str) -> dict[str, Any]:
    order = ORDERS.get(order_id)
    if order is None:
        return {
            "found": False,
            "order_id": order_id,
        }
    return {
        "found": True,
        **order,
    }


def get_refund_policy(market: str) -> dict[str, Any]:
    policy = REFUND_POLICIES.get(market)
    if policy is None:
        return {
            "found": False,
            "market": market,
        }
    return {
        "found": True,
        **policy,
    }


TOOL_FUNCTIONS: dict[str, Callable[..., dict[str, Any]]] = {
    "lookup_order": lookup_order,
    "get_refund_policy": get_refund_policy,
}

TOOL_SCHEMAS = {
    tool["function"]["name"]: tool["function"]["parameters"]
    for tool in TOOLS
}


def execute_tool(name: str, arguments: dict[str, Any]) -> dict[str, Any]:
    """Validate and execute an allowlisted local tool."""

    function = TOOL_FUNCTIONS.get(name)
    schema = TOOL_SCHEMAS.get(name)

    if function is None or schema is None:
        return {
            "ok": False,
            "error": {
                "type": "unknown_tool",
                "message": f"Tool {name!r} is not available.",
            },
        }

    try:
        Draft202012Validator(schema).validate(arguments)
    except ValidationError as exc:
        return {
            "ok": False,
            "error": {
                "type": "invalid_arguments",
                "message": exc.message,
            },
        }

    try:
        result = function(**arguments)
    except Exception as exc:
        # Log the complete exception privately. Return a safe public error.
        return {
            "ok": False,
            "error": {
                "type": "tool_execution_failed",
                "message": "The tool could not complete the request.",
            },
        }

    return {
        "ok": True,
        "data": result,
    }


def run_workflow(question: str) -> dict[str, Any]:
    api_key = os.environ.get("MOONSHOT_API_KEY")
    if not api_key:
        raise RuntimeError("MOONSHOT_API_KEY is not set.")

    client = OpenAI(
        api_key=api_key,
        base_url=BASE_URL,
        timeout=60.0,
        max_retries=2,
    )

    messages: list[Any] = [
        {
            "role": "system",
            "content": (
                "You are an order-support assistant. "
                "Use the available read-only tools for current order data. "
                "Do not claim that a refund has been submitted. "
                "The final response must follow the requested JSON Schema."
            ),
        },
        {
            "role": "user",
            "content": question,
        },
    ]

    for round_number in range(1, MAX_TOOL_ROUNDS + 1):
        completion = client.chat.completions.create(
            model=MODEL,
            messages=messages,
            tools=TOOLS,
            tool_choice="auto",
            reasoning_effort="low",
            response_format=FINAL_RESPONSE_FORMAT,
            max_completion_tokens=1200,
        )

        choice = completion.choices[0]
        assistant_message = choice.message

        # Preserve the complete SDK message, including reasoning_content
        # and tool_calls. Do not rebuild it from selected fields.
        messages.append(assistant_message)

        if choice.finish_reason == "tool_calls":
            tool_calls = assistant_message.tool_calls or []
            if not tool_calls:
                raise RuntimeError(
                    "finish_reason was tool_calls but no calls were returned."
                )

            for tool_call in tool_calls:
                name = tool_call.function.name

                try:
                    arguments = json.loads(tool_call.function.arguments)
                except json.JSONDecodeError as exc:
                    result = {
                        "ok": False,
                        "error": {
                            "type": "invalid_json_arguments",
                            "message": str(exc),
                        },
                    }
                else:
                    result = execute_tool(name, arguments)

                # Every tool_call must receive one matching role=tool message.
                messages.append(
                    {
                        "role": "tool",
                        "tool_call_id": tool_call.id,
                        "name": name,
                        "content": json.dumps(
                            result,
                            ensure_ascii=False,
                        ),
                    }
                )

            continue

        if choice.finish_reason == "length":
            raise RuntimeError(
                "The response was truncated. Increase the output limit "
                "or shorten the tool results."
            )

        if choice.finish_reason != "stop":
            raise RuntimeError(
                f"Unexpected finish_reason: {choice.finish_reason}"
            )

        if not assistant_message.content:
            raise RuntimeError("Kimi returned no final content.")

        # response_format=json_schema constrains only message.content.
        return json.loads(assistant_message.content)

    raise RuntimeError(
        f"Maximum tool rounds reached: {MAX_TOOL_ROUNDS}"
    )


if __name__ == "__main__":
    result = run_workflow(
        "Order DEMO-100 in demo-market is late. "
        "Check its status, read the refund policy and recommend the next step."
    )
    print(json.dumps(result, indent=2, ensure_ascii=False))

The final result follows a predictable contract:

{
  "summary": "The demo order is delayed beyond the policy threshold.",
  "order_status": "delayed",
  "refund_eligible": true,
  "next_action": "Send the case for manual review; no refund has been submitted.",
  "evidence": [
    "The order is four days late.",
    "The demo policy becomes eligible after three days.",
    "The policy requires manual review."
  ]
}

This code is a learning example, not an authorization system. A real order workflow must derive the authenticated customer identity from the server session rather than trust an order ID supplied by the prompt.

Why Validate Tool Arguments Twice?

JSON Schema helps the model generate suitable arguments, but your backend must still treat them as untrusted input.

  • The model can select an unavailable function name.
  • A provider orSDK can return malformed JSON.
  • A valid string can still contain a forbidden identifier.
  • A schema-valid operation may be unauthorized for the current user.
  • A value can violate a business rule not represented in the schema.
  • A tool result can contain unsafe oroversized external content.

Use multiple layers:

  1. Function-name allowlist.
  2. JSON decoding.
  3. JSON Schema validation.
  4. Authentication.
  5. Authorization.
  6. Business-rule validation.
  7. Timeout andresource limits.
  8. Safe error response.
  9. Audit logging for consequential actions.

The official Kimi K3 Agent guide recommends using required, enum and additionalProperties in the schema and returning structured errors so the model can correct invalid arguments.

Return Every Tool Result Correctly

The required conversation order is:

system
user
assistant  ← contains tool_calls
tool       ← result for tool_call_id A
tool       ← result for tool_call_id B
assistant  ← next calls or final answer

For every returned Tool Call:

  • Create exactly one corresponding role="tool" message.
  • Use the original tool_call.id.
  • Return the Tool Result as a string.
  • Keep the Assistant message containing the original tool_calls.
  • Do not reorder results in a way that loses their IDs.

A missing Assistant message, missing Tool Result oran incorrect ID can trigger tool_call_id not found. The safest approach is to append choice.message directly rather than manually reconstructing it.

Preserve reasoning_content During Tool Calls

Kimi thinking models can return reasoning_content separately from the final answer. During multi-turn Tool Calling, preserve the complete Assistant message.

ModelThinking behaviorTool-loop requirement
Kimi K3Always reasons with Preserved ThinkingAppend the complete Assistant message, including reasoning_content
Kimi K2.7 CodeThinking and Preserved Thinking are always onKeep the complete Assistant message in every Tool round
Kimi K2.6Thinking can be enabled ordisabledPreserve cross-turn reasoning when thinking.keep="all" is enabled

Copying only content and tool_calls can remove reasoning state required by later steps. Preserved reasoning also consumes context and tokens, so monitor long-running workflows.

Control Tool Selection with tool_choice

ValueBehaviorTypical use
autoThe model decides whether a tool is neededNormal assistant andAgent workflows
noneForbids Tool Calls for the turnFinal text-only orstructured synthesis
requiredRequires at least one Tool CallMandatory retrieval ordatabase lookup on K3
Specific function objectForces one named functionOnly when compatible with the model’s thinking configuration

Model-specific restrictions

  • Kimi K3: supports auto, none and required.
  • Kimi K2.6: supports auto and none; required returns an error.
  • Kimi K2.7 Code: supports auto and none; required returns an error.
  • Forcing a specific named function is incompatible with thinking enabled.
  • K3 always reasons, so use required with a narrow Tool list rather than forcing a specific function object.

For mandatory retrieval on K3:

completion = client.chat.completions.create(
    model="kimi-k3",
    messages=messages,
    tools=tools,
    tool_choice="required",
    reasoning_effort="low",
)

After the required retrieval turn, switch back to auto to allow the model to decide whether more tools are needed.

Handle Parallel Tool Calls

Kimi can return several Tool Calls in one response. Examples include:

  • Checking several independent account records.
  • Looking up prices from several sources.
  • Retrieving a policy and an order status in parallel.
  • Running several independent calculations.

Parallel execution is appropriate when:

  • The calls have no dependency on one another.
  • The tools are read-only oridempotent.
  • Rate limits allow concurrency.
  • Each result remains associated with its original ID.

Do not execute calls concurrently when one depends on the result of another orwhen their combined side effects could conflict. Even when you run calls in parallel, append a matching Tool message for every returned ID before asking Kimi to continue.

JSON Mode vs Strict Structured Output

JSON Mode

response_format={"type": "json_object"}

JSON Mode guarantees a parsable JSON object. You still describe the desired fields andtypes in the prompt, and the exact shape can vary.

Structured Output with JSON Schema

response_format={
    "type": "json_schema",
    "json_schema": {
        "name": "task_result",
        "strict": True,
        "schema": {
            "type": "object",
            "properties": {
                "status": {
                    "type": "string",
                    "enum": ["completed", "needs_review", "failed"],
                },
                "summary": {"type": "string"},
            },
            "required": ["status", "summary"],
            "additionalProperties": False,
        },
    },
}

Structured Output constrains the final message.content to the declared schema. Parse that field—not reasoning_content.

A clean workflow is:

  1. Use Tool Calls to obtain orchange external state.
  2. Return the Tool Results.
  3. Use response_format to constrain the final synthesis.
  4. Parse and validate the final response before passing it downstream.

See the official Kimi JSON Mode guide and the K3 Structured Output example for the current request format.

Load Large Tool Inventories Dynamically

Sending dozens orhundreds of Tool Schemas on every request has two costs:

  • The schemas consume context tokens.
  • Overlapping descriptions can reduce selection accuracy.

K3 supports a tool-retrieval pattern:

  1. Declare one search_tools function and a few universal tools.
  2. Use tool_choice="required" on the first turn.
  3. Search your private Tool Catalog.
  4. Return candidate tool names and summaries.
  5. Inject the matching full Tool Schemas on demand.
  6. Switch tool_choice back to auto.
  7. Remove unused definitions from later requests when appropriate.

The official K3 Tool Calling Best Practices recommends this approach because it reduces token use and makes the model less likely to select the wrong tool.

Custom Tools vs Official Kimi Tools

Tool typeExecution locationExamples
Custom ToolYour backend orprivate infrastructureCRM lookup, internal search, invoice creation, private database
Official Kimi ToolKimi Formula APIWeb search, fetch, code runner, Excel, date andconversion tools

Official Tools use the standard Function Tool protocol but add a Formula execution step:

  1. Fetch the official Tool declarations from the Formula API.
  2. Pass those declarations to Chat Completions.
  3. Receive normal Function-type tool_calls.
  4. Execute the requested Formula Fiber.
  5. Return its result as a matching Tool message.
  6. Continue the conversation.

Availability, rate limits andfees can change. Check the current Official Kimi Tools documentation before production use. For K3 web search, follow the current Formula API route rather than copying a legacy search-tool example.

Streaming Tool Calls

When stream=True, a Tool Call can arrive across several SSE chunks. The fields may be fragmented:

  • The call ID may arrive first.
  • The function name may arrive separately.
  • The JSON arguments can be split across many deltas.
  • Several Tool Calls can be interleaved by index.

Do not execute a function as soon as the first argument fragment arrives. Accumulate the deltas by Tool Call index, concatenate function.arguments, wait for the turn to complete, then parse andvalidate the full JSON.

tool_calls_by_index = {}

for chunk in stream:
    delta = chunk.choices[0].delta

    for call_delta in delta.tool_calls or []:
        item = tool_calls_by_index.setdefault(
            call_delta.index,
            {
                "id": "",
                "name": "",
                "arguments": "",
            },
        )

        if call_delta.id:
            item["id"] = call_delta.id

        if call_delta.function:
            if call_delta.function.name:
                item["name"] = call_delta.function.name
            if call_delta.function.arguments:
                item["arguments"] += call_delta.function.arguments

Streaming improves perceived latency, but it makes message assembly anderror handling more complex. Begin with a non-streaming Tool Loop, then add streaming after the protocol is covered by tests.

Protect Tools That Change State

Kimi API tool calling security guardrails covering argument validation, authorization, idempotency, timeouts, audit logs and maximum tool rounds

Read-only lookup tools andaction tools should not share the same security policy.

OperationRecommended control
Read public informationInput validation, rate limit andtimeout
Read private account dataAuthenticated identity andobject-level authorization
Create a draftPreview andexplicit user confirmation
Send an email ormessageRecipient allowlist, preview andconfirmation
Create a payment orrefundStep-up authorization, idempotency key andtransaction log
Delete oroverwrite dataHuman approval, backup androllback
Fetch an arbitrary URLSSRF protection, protocol andhost allowlist
Run codeSandbox, resource limits andnetwork restrictions

Production controls

  • Never derive user identity from the model’s arguments.
  • Authorize the current user against the requested object.
  • Require confirmation before consequential actions.
  • Use idempotency keys for retried write operations.
  • Separate read tools from write tools.
  • Do not expose shell, SQL orHTTP clients without strict boundaries.
  • Limit output sizes returned to the model.
  • Remove secrets andunnecessary personal data from Tool Results.
  • Log tool name, call ID, result status andlatency.
  • Set per-tool timeouts andmaximum Tool rounds.

Recommended Production Architecture

  1. API Gateway: authenticates the application request andapplies rate limits.
  2. Conversation Service: builds the Kimi message history.
  3. Policy Layer: decides which tools the user may access.
  4. Tool Registry: stores function definitions, schemas andversions.
  5. Model Orchestrator: calls Kimi andmanages the Tool Loop.
  6. Validator: parses andchecks every argument.
  7. Executor: runs allowlisted tools with timeouts andsandboxing.
  8. Approval Layer: pauses consequential actions for confirmation.
  9. Audit Log: records actions without exposing secrets.
  10. Response Validator: checks the final Structured Output.

A Tool Loop is one building block of an Agent, not the complete Agent product. The hosted Kimi Agent adds planning, tools, workspace, progress handling anddeliverables around the model. Developers building their own version must implement those layers themselves.

Common Kimi Tool Calling Errors

ProblemLikely causeFix
functions is rejectedThe integration uses the deprecated Function Calling protocolMigrate to tools and tool_calls
tool_call_id not foundThe Assistant Tool Call message is missing orreconstructed incorrectlyAppend choice.message as-is before Tool messages
Tool-result count mismatchNot every Tool Call received a resultReturn exactly one matching Tool message for every call
Invalid JSON argumentsArguments are incomplete ormalformedParse safely, return a structured Tool error andallow correction
Unexpected argument fieldsThe schema allows extra propertiesUse additionalProperties:false andbackend validation
Wrong tool repeatedly selectedDescriptions overlap or too many tools were declaredNarrow descriptions anduse Dynamic Tool Loading
required returns an errorThe request uses K2.6 orK2.7 CodeUse K3 orswitch to auto
Specific Tool Choice returns 400Thinking is enabledDo not force a named function while thinking is enabled
Later Tool round loses contextreasoning_content was droppedPreserve the complete Assistant message
finish_reason="length"Reasoning, Tool Results orfinal output exceeded the capIncrease the limit carefully orshorten Tool Results
Infinite Tool LoopAmbiguous tools orresults never satisfy the modelSet maximum rounds andreturn explicit completion status
High token useLarge schemas are sent every turnLoad only relevant tools andkeep stable prefixes

For HTTP, authentication, quota andserver failures outside the Tool protocol, use the Kimi API Errors and Troubleshooting Guide.

Tool Calling Testing Checklist

  • Unit-test every Tool Function without the model.
  • Validate example arguments against every schema.
  • Test the no-tool path with tool_choice="none".
  • Test automatic selection with auto.
  • Test mandatory retrieval on K3 with required.
  • Test several Tool Calls in one response.
  • Test a Tool Result error.
  • Test unknown function names.
  • Test malformed JSON arguments.
  • Test unauthorized object access.
  • Test duplicate write requests andidempotency.
  • Test timeout andnetwork failure.
  • Test finish_reason="length".
  • Test the maximum Tool-round limit.
  • Verify that the complete Assistant message is preserved.
  • Validate the final Structured Output.
  • Confirm that logs do not contain API keys orconfidential Tool Results.

What to Build Next

Next objectiveRelated page
Create a key andsend the first requestKimi API Quickstart
Understand OpenAI compatibilityOpenAI-Compatible Kimi API
Choose K3, K2.7 Code orK2.6Kimi API Models
Estimate Tool Loop token costKimi API Pricing
Diagnose API failuresKimi API Errors
Plan a broader developer workflowKimi AI for Developers

Frequently Asked Questions

What is Kimi Tool Calling?

Kimi Tool Calling is an API protocol that lets a model request one ormore external functions by returning their names andJSON arguments. Your application validates andexecutes the functions andreturns their results to the model.

Is Kimi Function Calling the same as Tool Calling?

The concepts are closely related, but the current Kimi API uses tools and tool_calls. The older functions and function_call parameters are deprecated andnot supported.

Does Kimi execute the function?

No. Kimi generates the proposed function name andarguments. Your backend executes the approved implementation andreturns the result.

How do I know that Kimi requested a tool?

Check whether finish_reason equals tool_calls, then inspect message.tool_calls.

Why are function arguments returned as a string?

function.arguments contains a serialized JSON object. Parse it with a JSON parser, then validate it against the function schema andbusiness rules.

Can Kimi call multiple tools at once?

Yes. The tool_calls array can contain several calls. Independent calls may be executed concurrently, but every call must receive a matching Tool Result message.

What is tool_call_id?

It is the identifier that links one Tool Result to the Tool Call that requested it. Copy the exact ID into the corresponding role="tool" message.

Why do I get tool_call_id not found?

The Assistant message containing the Tool Call may be missing from the conversation, orits Tool Calls may have been reconstructed incorrectly. Append the complete SDK message before returning Tool Results.

Does tool_choice required work with every Kimi model?

No. K3 supports required. K2.6 andK2.7 Code support auto and none but return an error for required.

Can I force one specific function?

The API supports a specific function Tool Choice, but it is incompatible with thinking enabled. Since K3 always reasons, use a narrow Tool list with required rather than forcing one named function.

Do I need to preserve reasoning_content?

Yes for K3 Tool Loops andfor models using Preserved Thinking. Append the complete Assistant message returned by the API instead of copying selected fields.

What is the difference between Tool Calling and JSON Mode?

Tool Calling requests external functions. JSON Mode constrains the final answer to valid JSON. Structured Output goes further by requiring the final answer to match a JSON Schema.

Can I use Structured Output with Tool Calling?

Yes. The Tool Loop can collect external data, while response_format constrains the final message.content returned after the Tool Results are available.

How many tools should I send?

Send only the tools relevant to the current task. For dozens orhundreds of functions, use a Tool Catalog andDynamic Tool Loading rather than putting every schema into every request.

How do I stop an infinite Tool Loop?

Set a maximum number of rounds, use precise tool descriptions, return explicit success anderror states, andstop on truncated orrepeatedly failing results.

Is Tool Calling safe for payments anddeletions?

Only when your application adds authentication, object-level authorization, explicit confirmation, idempotency, audit logs androllback controls. The model’s Tool Call is never sufficient authorization by itself.

Official Sources and Update Methodology

This guide prioritizes current first-party Kimi andMoonshot AI developer documentation.

The Tool Calls protocol, model-specific Tool Choice rules, Preserved Thinking requirements, Dynamic Tool Loading andStructured Output behavior were last checked on August 29, 2026. When current first-party documentation differs from this article, follow the live Kimi API reference.

Last verified: August 29, 2026.

Mohamed Hossam El-Din
Mohamed Hossam El-Din

Mohamed Hossam El-Din is a content editor at Thinkly for Digital Business, responsible for kimi-ai.free. He is a social work student at Helwan University, and he came to Kimi the way most of its users do: with long PDFs to read, research to gather, and reports to write. That is the angle he writes from — long-context work, document analysis, and research, tested on a live account before it is written about. He writes in English and Arabic. Every feature covered here was used first; error messages published on this site are errors that actually appeared. When a claim cannot be verified against Moonshot AI's official documentation, the article says so.

Articles: 49