Claudio.Agent (Claudio v0.5.0)

View Source

Stateless agent loop utility for tool-calling workflows.

Runs the tool-calling loop: send request → check for tool uses → execute handlers → append results → repeat until done.

This is a pure function — no GenServer, no process. State management (conversation history, persistence) is the caller's responsibility.

Example

alias Claudio.{Agent, Messages.Request, Tools}

# Define tools and handlers
weather_tool = Tools.define_tool("get_weather", "Get weather", %{
  "type" => "object",
  "properties" => %{"location" => %{"type" => "string"}},
  "required" => ["location"]
})

handlers = %{
  "get_weather" => fn %{"location" => loc} ->
    {:ok, "72°F and sunny in #{loc}"}
  end
}

# Build request
request = Request.new("claude-sonnet-4-5-20250929")
|> Request.add_message(:user, "What's the weather in SF?")
|> Request.add_tool(weather_tool)
|> Request.set_max_tokens(1024)

# Run the agent loop
{:ok, response, messages} = Agent.run(client, request, handlers)

# response is the final Response struct
# messages is the full conversation history (for continuing later)

Options

  • :max_turns — Maximum tool-calling iterations (default: 10)
  • :on_tool_call — Optional callback fn tool_use, result -> :ok end for logging/observability

Summary

Functions

Runs the agent loop until the model stops requesting tools or max_turns is reached.

Types

handler()

@type handler() :: (map() -> {:ok, String.t()} | {:error, String.t()})

handlers()

@type handlers() :: %{required(String.t()) => handler()}

run_result()

@type run_result() ::
  {:ok, Claudio.Messages.Response.t(), [map()]}
  | {:error, :max_turns_exceeded, Claudio.Messages.Response.t(), [map()]}
  | {:error, term()}

Functions

run(client, request, tool_handlers, opts \\ [])

Runs the agent loop until the model stops requesting tools or max_turns is reached.

max_turns limits tool-result round-trips, not API calls. With max_turns: 2, the model is called up to 3 times: the initial call plus 2 tool-result follow-ups. If the third call still requests tools, the loop stops with :max_turns_exceeded.

Returns {:ok, final_response, messages} on success, where messages is the full conversation history including all tool calls and results.

Returns {:error, :max_turns_exceeded, last_response, messages} if the loop doesn't converge within max_turns tool round-trips.

Returns {:error, reason} if the API call fails.