An agent is a module that does use Condukt. Each agent runs as its own GenServer backed by Condukt.Session, which manages the conversation history, the tool loop, and any optional features (sessions, compaction, redaction).

The behaviour

use Condukt exposes the following optional callbacks, all with defaults:

CallbackDefaultPurpose
system_prompt/0nilStatic system prompt for the agent.
tools/0[]List of tool modules, {module, opts} tuples, or inline tools.
model/0"anthropic:claude-sonnet-4-20250514"ReqLLM provider:model identifier.
thinking_level/0:mediumOne of :off, :minimal, :low, :medium, :high.
init/1identityCalled with the keyword opts at startup.
handle_event/2no opReceives events as they happen during a run.

You only override what you need:

defmodule MyApp.ResearchAgent do
  use Condukt

  @impl true
  def system_prompt do
    "You are a careful research assistant. Always cite sources."
  end

  @impl true
  def tools do
    [Condukt.Tools.Read, Condukt.Tools.Bash]
  end

  @impl true
  def model, do: "anthropic:claude-sonnet-4-20250514"
end

Starting an agent

{:ok, agent} =
  MyApp.ResearchAgent.start_link(
    api_key: System.fetch_env!("ANTHROPIC_API_KEY"),
    cwd: "/path/to/project"
  )

Resolution order for configuration is:

  1. Options passed to start_link/1
  2. config :condukt, ...
  3. Module callback defaults

Common options

MyApp.ResearchAgent.start_link(
  api_key: "sk-ant-...",                        # Provider key
  model: "anthropic:claude-sonnet-4-20250514",  # ReqLLM model id
  base_url: "http://localhost:11434/v1",        # Override provider URL
  system_prompt: "You are helpful.",            # Static prompt
  thinking_level: :medium,                      # Thinking budget
  load_project_instructions: true,              # See Project Instructions guide
  cwd: "/path/to/project",                      # Tool working directory
  session_store: Condukt.SessionStore.Memory,   # See Sessions guide
  compactor: {Condukt.Compactor.Sliding, keep: 40}, # See Compaction guide
  redactor: Condukt.Redactors.Regex,            # See Redaction guide
  name: MyApp.ResearchAgent                     # GenServer name
)

Public API

For a running agent process, the Condukt module forwards these calls to Condukt.Session:

Condukt.run/2 also accepts a prompt as the first argument for anonymous workflows that do not need an agent module. See the Anonymous Workflows guide.

Handling events in the agent module

Override handle_event/2 to react to events without subscribing to the stream:

@impl true
def handle_event({:tool_call, name, _id, _args}, state) do
  Logger.info("Calling tool: #{name}")
  {:noreply, state}
end

def handle_event(_event, state), do: {:noreply, state}

This is the easiest way to add logging, metrics, or pubsub broadcasts.

Custom init/1

init/1 lets you build per session state when the agent starts. The return value is stored on the session and passed to handle_event/2:

@impl true
def init(opts) do
  {:ok, %{started_at: System.monotonic_time(), opts: opts}}
end