Volley / Templates / LLM tool use

AI agents template

LLM tool calling sequence diagram

Function calling from your app's side. You send a tool definition, the model replies with a tool_use block, your app runs the tool, and a tool_result goes back to get the final answer.

Numbered sequence diagram of LLM tool use across User, App, Claude API and Weather API: POST /v1/messages with a get_weather tool, a tool_use response, the app calling the weather API, a second POST /v1/messages with the tool_result, and a final answer with stop_reason end_turn.

Click any arrow in the editor to see its request and response as a table or the raw body. Change the hosts, ids and fields to match your integration, then share a link or embed it in your README.

Step by step

  1. Your app sends the question with a tool. POST /v1/messages with model, max_tokens, messages and a tools list where each tool has a name, description and input_schema.
  2. The model asks for the tool. The response has stop_reason: "tool_use" and a tool_use block with an id, the tool name and its input.
  3. Your app runs the tool, here a call to a weather API.
  4. Your app sends the result back. A second POST /v1/messages with the full conversation, the assistant turn, and a user turn holding a tool_result block with the matching tool_use_id.
  5. The model answers in text with stop_reason: "end_turn".

Easy to get wrong

  • The API is stateless. Each request carries the whole conversation, including the assistant's tool_use turn.
  • The model can ask for several tools in one response. Return a tool_result for every tool_use block.
  • Loop on stop_reason. The answer is not final until it is end_turn.

The source

The whole diagram is this plain text. Copy it into your repo, or open it in the editor.

llm-tool-use.volley
title: LLM tool use with the Claude Messages API
autonumber
order: User, App, Claude API, Weather API

User -> App: What's the weather in San Francisco?

App -> Claude API: POST /v1/messages
in: Request with a tool
```http
POST /v1/messages HTTP/1.1
Host: api.anthropic.com
Content-Type: application/json
x-api-key: <redacted>
anthropic-version: 2023-06-01

{
  "model": "claude-sonnet-5",
  "max_tokens": 1024,
  "tools": [
    {
      "name": "get_weather",
      "description": "Get the current weather for a city",
      "input_schema": {
        "type": "object",
        "properties": {
          "location": { "type": "string", "description": "City and state, like San Francisco, CA" },
          "unit": { "type": "string", "enum": ["celsius", "fahrenheit"] }
        },
        "required": ["location"]
      }
    }
  ],
  "messages": [{ "role": "user", "content": "What's the weather in San Francisco?" }]
}
```

Claude API --> App: 200 OK
out: Tool use
```json
{
  "id": "msg_01Aq9w938a90dw8q",
  "type": "message",
  "role": "assistant",
  "model": "claude-sonnet-5",
  "content": [
    { "type": "text", "text": "I'll check the current weather in San Francisco." },
    { "type": "tool_use", "id": "toolu_01A09q90qw90lq917835lq9", "name": "get_weather", "input": { "location": "San Francisco, CA", "unit": "fahrenheit" } }
  ],
  "stop_reason": "tool_use"
}
```
note: The model does not call the tool. It returns a `tool_use` block, and your app runs it.

=: {fas-cloud-sun} Your app runs the tool

App -> Weather API: GET /v1/current?city=San+Francisco&units=imperial
Weather API --> App: 200 OK
out: Weather
```json
{ "city": "San Francisco", "temp_f": 61, "conditions": "Fog", "wind_mph": 12 }
```

App -> Claude API: POST /v1/messages
in: Request with the tool result
```json
{
  "model": "claude-sonnet-5",
  "max_tokens": 1024,
  "tools": [{ "name": "get_weather", "description": "Get the current weather for a city", "input_schema": { "type": "object", "properties": { "location": { "type": "string" } }, "required": ["location"] } }],
  "messages": [
    { "role": "user", "content": "What's the weather in San Francisco?" },
    {
      "role": "assistant",
      "content": [
        { "type": "text", "text": "I'll check the current weather in San Francisco." },
        { "type": "tool_use", "id": "toolu_01A09q90qw90lq917835lq9", "name": "get_weather", "input": { "location": "San Francisco, CA", "unit": "fahrenheit" } }
      ]
    },
    {
      "role": "user",
      "content": [
        { "type": "tool_result", "tool_use_id": "toolu_01A09q90qw90lq917835lq9", "content": "61°F, fog, wind 12 mph" }
      ]
    }
  ]
}
```
note: Send the whole conversation again, including the assistant turn with its `tool_use` block. The `tool_result` goes in the next user message and its `tool_use_id` must match.

Claude API --> App: 200 OK
out: Final answer
```json
{
  "id": "msg_01Bx3k7Lm2Np4Qr5",
  "type": "message",
  "role": "assistant",
  "content": [{ "type": "text", "text": "It's 61°F and foggy in San Francisco, with wind around 12 mph." }],
  "stop_reason": "end_turn"
}
```

App --> User: It's 61°F and foggy, with wind around 12 mph.
note: Loop while `stop_reason` is `tool_use`. Stop when it is `end_turn`.

Related

Make this LLM tool use diagram yours.

Open it in the editor, change it to match your integration and share the link. Free, with no account.