Powered by AppSignal & Oban Pro

Jidoka: Imported Agents Deep Dive

15_imported_agents_deep_dive.livemd

Jidoka: Imported Agents Deep Dive

Run in Livebook

Imported agents are constrained JSON/YAML specs. Executable capabilities resolve through explicit registries supplied by the application.

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()

Build A Registry

defmodule LivebookDemo.Imported.Tools.AddNumbers do
  use Jidoka.Tool,
    name: "imported_add_numbers",
    description: "Adds two integers.",
    schema: Zoi.object(%{a: Zoi.integer(), b: Zoi.integer()})

  @impl true
  def run(%{a: a, b: b}, _context), do: {:ok, %{sum: a + b}}
end

defmodule LivebookDemo.Imported.Tools.MultiplyNumbers do
  use Jidoka.Tool,
    name: "imported_multiply_numbers",
    description: "Multiplies two integers.",
    schema: Zoi.object(%{a: Zoi.integer(), b: Zoi.integer()})

  @impl true
  def run(%{a: a, b: b}, _context), do: {:ok, %{product: a * b}}
end

defmodule LivebookDemo.Imported.MathPlugin do
  use Jidoka.Plugin,
    description: "Adds multiplication to imported agents.",
    tools: [LivebookDemo.Imported.Tools.MultiplyNumbers]
end

defmodule LivebookDemo.Imported.Guardrails.SafePrompt do
  use Jidoka.Guardrail, name: "imported_safe_prompt"

  @impl true
  def call(%Jidoka.Guardrails.Input{message: message}) do
    if String.contains?(String.downcase(message), "secret") do
      {:error, :unsafe_prompt}
    else
      :ok
    end
  end
end

defmodule LivebookDemo.Imported.SupportCharacter do
  use Jido.Character,
    defaults: %{
      name: "Imported Support Advisor",
      identity: %{role: "Support specialist"},
      voice: %{tone: :professional},
      instructions: ["Use the imported support persona."]
    }
end

Import From JSON

spec = %{
  "agent" => %{
    "id" => "livebook_imported_deep_dive",
    "description" => "Imported math assistant",
    "context" => %{"tenant" => "demo"}
  },
  "defaults" => %{
    "model" => "fast",
    "instructions" => "Use available tools when they help. Keep answers short.",
    "character" => "support_advisor"
  },
  "capabilities" => %{
    "tools" => ["imported_add_numbers"],
    "plugins" => ["math_plugin"]
  },
  "lifecycle" => %{
    "guardrails" => %{
      "input" => ["imported_safe_prompt"]
    }
  }
}

{:ok, imported_agent} =
  Jidoka.import_agent(spec,
    available_tools: [LivebookDemo.Imported.Tools.AddNumbers],
    available_plugins: [LivebookDemo.Imported.MathPlugin],
    available_guardrails: [LivebookDemo.Imported.Guardrails.SafePrompt],
    available_characters: %{"support_advisor" => LivebookDemo.Imported.SupportCharacter}
  )

{:ok, definition} = Jidoka.inspect_agent(imported_agent)

Map.take(definition, [:kind, :id, :description, :context, :tool_names, :guardrails])

Encode the normalized imported agent back to YAML.

{:ok, yaml} = Jidoka.encode_agent(imported_agent, format: :yaml)
yaml

Unknown capabilities fail at import time.

bad_spec = put_in(spec, ["capabilities", "tools"], ["missing_tool"])

{:error, reason} =
  Jidoka.import_agent(bad_spec,
    available_tools: [LivebookDemo.Imported.Tools.AddNumbers]
  )

reason

Optional Provider Turn

{:ok, pid} =
  Jidoka.Kino.start_or_reuse("livebook-imported-deep-dive-agent", fn ->
    Jidoka.start_agent(imported_agent, id: "livebook-imported-deep-dive-agent")
  end)

Jidoka.Kino.chat("Imported deep-dive chat", fn ->
  Jidoka.chat(pid, "Use imported_add_numbers to add 9 and 33. Reply with only the sum.")
end)