Powered by AppSignal & Oban Pro

Jidoka: Subagents

livebook/08_subagents.livemd

Jidoka: Subagents

Run in Livebook

Subagents are specialist agents exposed to a parent as tool-like capabilities. The parent stays in control of the turn; the specialist handles a bounded task.

Setup

Mix.install(
  [
    {:jidoka, git: "https://github.com/mikehostetler/jidoka.git", ref: "924a486f3c1b7e7a943cb3d5ceee0de65f158467"},
    {:kino, "~> 0.19.0"}
  ],
  config: [
    jidoka: [
      model_aliases: %{fast: "anthropic:claude-haiku-4-5"}
    ]
  ]
)
Jidoka.Kino.setup()

Define A Specialist

defmodule LivebookDemo.Subagents.ResearchSpecialist do
  defmodule Runtime do
    use Jido.Agent,
      name: "livebook_research_specialist_runtime",
      schema: Zoi.object(%{})
  end

  def name, do: "research_agent"
  def runtime_module, do: Runtime
  def start_link(opts \\ []), do: Jidoka.start_agent(Runtime, opts)

  def chat(_pid, message, opts \\ []) do
    context = Keyword.get(opts, :context, %{})

    if notify_pid = Map.get(context, :notify_pid, Map.get(context, "notify_pid")) do
      send(notify_pid, {:research_context, context})
    end

    tenant = Map.get(context, :tenant, Map.get(context, "tenant", "none"))
    depth = Map.get(context, Jidoka.Subagent.depth_key(), 0)

    {:ok, "research: #{message}; tenant=#{tenant}; depth=#{depth}"}
  end
end

defmodule LivebookDemo.Subagents.ManagerAgent do
  use Jidoka.Agent

  agent do
    id :livebook_subagent_manager
  end

  defaults do
    model :fast
    instructions "Delegate research tasks to the research specialist."
  end

  capabilities do
    subagent LivebookDemo.Subagents.ResearchSpecialist,
      as: "research_agent",
      description: "Ask the research specialist for a bounded research task.",
      result: :structured
  end
end

Inspect the generated subagent capability.

%{
  subagent_names: LivebookDemo.Subagents.ManagerAgent.subagent_names(),
  tool_names: LivebookDemo.Subagents.ManagerAgent.tool_names(),
  subagents: LivebookDemo.Subagents.ManagerAgent.subagents()
}

Call The Generated Tool Directly

This is the same model-visible surface the parent agent receives.

research_tool =
  Enum.find(LivebookDemo.Subagents.ManagerAgent.tools(), fn tool ->
    tool.name() == "research_agent"
  end)

{:ok, result} =
  research_tool.run(
    %{task: "Summarize the onboarding risk."},
    %{tenant: "acme", notify_pid: self(), secret: "not forwarded"}
  )

forwarded_context =
  receive do
    {:research_context, context} -> context
  after
    100 -> :no_context_message
  end

%{
  result: result,
  forwarded_context: forwarded_context
}

The subagent metadata is available when result: :structured is configured.

Map.take(result.subagent, [:name, :mode, :outcome, :context_keys, :result_preview])

Optional Provider Turn

{:ok, pid} =
  Jidoka.Kino.start_or_reuse("livebook-subagent-manager", fn ->
    LivebookDemo.Subagents.ManagerAgent.start_link(id: "livebook-subagent-manager")
  end)

Jidoka.Kino.chat("Subagent manager chat", fn ->
  LivebookDemo.Subagents.ManagerAgent.chat(
    pid,
    "Ask the research_agent to summarize why focused onboarding notebooks help.",
    context: %{tenant: "acme"}
  )
end)