Skip to content
Appmake
Kalle API docs

Tool use

Give Kalle functions it can call: look up an order, check a calendar, query your database. Kalle decides when a tool helps, asks you to run it, and uses the result in its answer.

Your code always runs the tool. Kalle never calls your systems directly.

1. Describe your tools

Each tool has a name, a description that tells Kalle when to use it, and an input_schema in JSON Schema that describes its arguments.

curl https://api.kalle.build/v1/messages \
  -H "Authorization: Bearer $KALLE_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "model": "kalle-turbo-max",
    "max_tokens": 1024,
    "tools": [
      {
        "name": "get_order_status",
        "description": "Look up the delivery status of an order by its order number.",
        "input_schema": {
          "type": "object",
          "properties": {
            "order_number": {
              "type": "string",
              "description": "The order number, for example A-1042."
            }
          },
          "required": ["order_number"]
        }
      }
    ],
    "messages": [{ "role": "user", "content": "Where is my order A-1042?" }]
  }'

2. Kalle asks for a tool call

When Kalle wants to use a tool, the response stops with stop_reason set to tool_use and contains a tool_use block:

JSON
{
  "id": "msg_01J8Z7",
  "type": "message",
  "role": "assistant",
  "model": "kalle-turbo-max",
  "content": [
    { "type": "text", "text": "Let me check that order for you." },
    {
      "type": "tool_use",
      "id": "call_7Ks2",
      "name": "get_order_status",
      "input": { "order_number": "A-1042" }
    }
  ],
  "stop_reason": "tool_use",
  "usage": { "input_tokens": 212, "output_tokens": 38 }
}

3. Send the result back

Run the function with input, then send the conversation again with two more messages: Kalle's message as it was, and a user message with a tool_result block that points to the call by its id.

JSON
{
  "model": "kalle-turbo-max",
  "max_tokens": 1024,
  "tools": ["...the same tools as before..."],
  "messages": [
    { "role": "user", "content": "Where is my order A-1042?" },
    {
      "role": "assistant",
      "content": [
        { "type": "text", "text": "Let me check that order for you." },
        {
          "type": "tool_use",
          "id": "call_7Ks2",
          "name": "get_order_status",
          "input": { "order_number": "A-1042" }
        }
      ]
    },
    {
      "role": "user",
      "content": [
        {
          "type": "tool_result",
          "tool_use_id": "call_7Ks2",
          "content": "Shipped on 3 November with PostNord. Expected delivery 6 November."
        }
      ]
    }
  ]
}

Kalle then answers in plain words, or calls another tool. Keep looping until stop_reason is no longer tool_use.

When a tool fails

Send the failure back instead of stopping. Set is_error to true and describe what went wrong, and Kalle will explain it or try another way:

JSON
{
  "type": "tool_result",
  "tool_use_id": "call_7Ks2",
  "is_error": true,
  "content": "No order found with the number A-1042."
}

Controlling tool calls

tool_choiceWhat Kalle does
autoThe default. Kalle calls a tool when it helps.
anyKalle must call one of the tools.
noneKalle answers without calling tools.
{ "type": "tool", "name": "get_order_status" }Kalle must call this exact tool.

Kalle can ask for several tool calls in one message. Run them, and send all the results back in one user message.

Writing good tools

  • Write the description for a new colleague: what the tool does, when to use it and what it returns.
  • Keep arguments few and specific. An order_number beats a free-text query.
  • Check every argument before you act on it, just like input from a form.
  • Ask your user before a tool does something that cannot be undone, such as a refund or a deleted record.