Condukt.Tool behaviour (Condukt v0.16.5)

Copy Markdown View Source

Behaviour for defining tools that agents can use.

Tools are functions that agents can call to interact with the world: reading files, running commands, making HTTP requests, etc.

Defining a Tool

defmodule MyApp.Tools.Weather do
  use Condukt.Tool

  @impl true
  def name, do: "get_weather"

  @impl true
  def description do
    "Gets the current weather for a location"
  end

  @impl true
  def parameters do
    %{
      type: "object",
      properties: %{
        location: %{
          type: "string",
          description: "City name, e.g. 'San Francisco, CA'"
        }
      },
      required: ["location"]
    }
  end

  @impl true
  def call(%{"location" => location}, _context) do
    case WeatherAPI.get(location) do
      {:ok, data} -> {:ok, format_weather(data)}
      {:error, reason} -> {:error, reason}
    end
  end
end

Tool Context

The call/2 function receives a context map with:

  • :agent - The agent PID
  • :agent_module - The agent module for the session
  • :sandbox - The active Condukt.Sandbox struct (use this for any filesystem or process-execution work)
  • :cwd - Project working directory (kept for tools that need to refer to the host project root for non-sandbox concerns; tools that read/write files or run commands should go through :sandbox)
  • :secrets - Resolved session secrets. Built-in command tools expose these as environment variables. Custom trusted tools can use Condukt.Secrets.env/1 when they need the same values.
  • :opts - Options passed when adding the tool to the agent

Sandbox-aware tools

If your tool reads or writes files, or runs subprocesses, route through Condukt.Sandbox.read/2, Condukt.Sandbox.write/3, Condukt.Sandbox.exec/3, etc. Direct File.* or MuonTrap.cmd/3 calls bypass the sandbox and break the consumer's ability to swap one in (e.g. an in-memory virtual sandbox). Tools that touch unrelated systems (HTTP APIs, databases, in-process state) have nothing to sandbox and are unaffected.

Parameterized Tools

Tools can be parameterized when added to an agent:

defmodule MyApp.Tools.Database do
  use Condukt.Tool

  @impl true
  def name(opts), do: "query_#{opts[:table]}"

  @impl true
  def description(opts) do
    "Query the #{opts[:table]} table"
  end

  @impl true
  def call(args, context) do
    table = context.opts[:table]
    Repo.all(from r in table, where: ^build_where(args))
  end
end

# In agent:
def tools do
  [
    {MyApp.Tools.Database, table: "users"},
    {MyApp.Tools.Database, table: "orders"}
  ]
end

Summary

Callbacks

Executes the tool with the given arguments.

Returns a description of what the tool does.

Returns the tool name as it will appear to the LLM.

Returns the JSON Schema for the tool's parameters.

Functions

Executes a tool by name with arguments.

Gets the tool name for a tool spec.

Builds a tool specification for the LLM provider.

Callbacks

call(args, context)

@callback call(args :: map(), context :: map()) :: {:ok, term()} | {:error, term()}

Executes the tool with the given arguments.

description()

@callback description() :: String.t()

Returns a description of what the tool does.

description(opts)

(optional)
@callback description(opts :: keyword()) :: String.t()

name()

@callback name() :: String.t()

Returns the tool name as it will appear to the LLM.

name(opts)

(optional)
@callback name(opts :: keyword()) :: String.t()

parameters()

@callback parameters() :: map()

Returns the JSON Schema for the tool's parameters.

parameters(opts)

(optional)
@callback parameters(opts :: keyword()) :: map()

Functions

execute(module, args, context)

Executes a tool by name with arguments.

name(module)

Gets the tool name for a tool spec.

to_spec(inline)

Builds a tool specification for the LLM provider.