Current section
22 Versions
Jump to
Current section
22 Versions
Compare versions
25
files changed
+665
additions
-356
deletions
| @@ -1,6 +1,6 @@ | |
| 1 1 | # Jido (自動) |
| 2 2 | |
| 3 | - Jido is a foundational framework for building autonomous, distributed agent systems in Elixir. |
| 3 | + Jido is a toolkit for building autonomous, distributed agent systems in Elixir. |
| 4 4 | |
| 5 5 | The name "Jido" (自動) comes from the Japanese word meaning "automatic" or "automated", where 自 (ji) means "self" and 動 (dō) means "movement". |
| 6 6 | |
| @@ -10,73 +10,34 @@ The name "Jido" (自動) comes from the Japanese word meaning "automatic" or "au | |
| 10 10 | [](https://coveralls.io/github/agentjido/jido?branch=main) |
| 11 11 | [](https://opensource.org/licenses/Apache-2.0) |
| 12 12 | |
| 13 | - ## Quick Start |
| 14 | - |
| 15 | - ```elixir |
| 16 | - # First, define our Calculator agent with supported operations |
| 17 | - iex> defmodule CalculatorAgent do |
| 18 | - ...> use Jido.Agent, |
| 19 | - ...> name: "calculator", |
| 20 | - ...> actions: [Actions.Add, Actions.Subtract, Actions.Multiply, Actions.Divide] |
| 21 | - ...> # Omitting the router that maps the "add" signal to the Add Action |
| 22 | - ...> end |
| 23 | - {:module, CalculatorAgent, <<...>>, %{}} |
| 24 | - |
| 25 | - # Start the agent process |
| 26 | - iex> {:ok, pid} = CalculatorAgent.start_link() |
| 27 | - {:ok, #PID<0.123.0>} |
| 28 | - |
| 29 | - # Send a synchronous request to the agent |
| 30 | - iex> {:ok, result} = CalculatorAgent.call(pid, Signal.new(%{type: "add", data: %{a: 1, b: 2}})) |
| 31 | - {:ok, 3} |
| 32 | - |
| 33 | - # Send an asynchronous request to the agent |
| 34 | - iex> {:ok, request_id} = CalculatorAgent.cast(pid, Signal.new(%{type: "multiply", data: %{a: 2, b: 4}})) |
| 35 | - {:ok, "req_abc123"} |
| 36 | - |
| 37 | - # Receive the result of the asynchronous request |
| 38 | - iex> flush() |
| 39 | - {:jido_agent, "req_abc123", 8} |
| 40 | - :ok |
| 41 | - ``` |
| 42 | - |
| 43 | - This example barely scratches the surface of what Jido can do. For more examples, see the [Getting Started Guide](guides/getting-started.md) and [Jido Workbench](https://github.com/agentjido/jido_workbench) to play with our growing catalog of real-life examples. |
| 44 | - |
| 45 13 | ## Overview |
| 46 14 | |
| 47 | - Jido provides a robust foundation for building autonomous agents that can plan, execute, and adapt their behavior in distributed Elixir applications. Think of it as a toolkit for creating smart, composable workflows that can evolve and respond to their environment. |
| 15 | + Jido provides the foundation for building autonomous agents that can plan, execute, and adapt their behavior in distributed Elixir applications. Think of it as a toolkit for creating smart, composable workflows that can evolve and respond to their environment. |
| 48 16 | |
| 49 | - ## Are You Sure You Need an Agent? |
| 17 | + This package is geared towards Agent builders. It contains the basis building blocks for creating advanced agentic systems. This is why there's no AI baked into the core of this framework. |
| 50 18 | |
| 51 | - Agents are a hot topic right now, but they aren’t a silver bullet. In particular, Large Language Models (LLMs) are powerful yet slow and costly—if your application doesn’t require dynamic decision-making or complex planning, consider whether you really need an Agent at all. |
| 19 | + To see demo's and examples, check out our [Jido Workbench](https://github.com/agentjido/jido_workbench). It includes many examples of agents and workflows, including: |
| 52 20 | |
| 53 | - - **LLMs aren’t required for all tasks** — Avoid building them into your core logic unless necessary |
| 54 | - - **Agents as Dynamic ETL** — Agents dynamically direct data ingestion, transformation, and output based on: |
| 55 | - - LLMs (e.g., GPT) |
| 56 | - - Classical planning algorithms (A\*, Behavior Trees, etc.) |
| 57 | - - **Simplicity often wins** — If you don’t need these dynamic behaviors, you probably don’t need an Agent. This library is likely overkill compared to straightforward code. |
| 21 | + - Agents with Tools |
| 22 | + - ChatBots |
| 23 | + - Agents acting as a Team |
| 24 | + - Multi-modal input & output |
| 25 | + - ... and many more examples |
| 58 26 | |
| 59 | - ### Our Definition of an Agent |
| 27 | + Jido Workbench relies on the following packages to extend Jido's capabilities: |
| 60 28 | |
| 61 | - An Agent is a system where LLMs _or_ classical planning algorithms dynamically direct their own processes. Some great definitions from the community: |
| 62 | - |
| 63 | - - “Agents are Dynamic ETL processes directed by LLMs” — [YouTube](https://youtu.be/KY8n96Erp5Q?si=5Itt7QR11jgfWDTY&t=22) |
| 64 | - - “Agents are systems where LLMs dynamically direct their own processes” — [Anthropic Research](https://www.anthropic.com/research/building-effective-agents) |
| 65 | - - “AI Agents are programs where LLM outputs control the workflow” — [Hugging Face Blog](https://huggingface.co/blog/smolagents) |
| 66 | - |
| 67 | - If your application doesn’t involve dynamic workflows or data pipelines that change based on AI or planning algorithms, you can likely do more with less. |
| 68 | - |
| 69 | - > 💡 **NOTE**: This library intends to support both LLM planning and Classical AI planning (ie. [Behavior Trees](https://github.com/jschomay/elixir-behavior-tree) as a design principle via Actions. See [`jido_ai`](https://github.com/agentjido/jido_ai) for example LLM actions. |
| 70 | - |
| 71 | - _This space is evolving rapidly. Last updated 2025-01-01_ |
| 29 | + - [`jido_ai`](https://github.com/agentjido/jido_ai) package for the AI capabilities. |
| 30 | + - [`jido_chat`](https://github.com/agentjido/jido_chat) package for the chat capabilities. |
| 31 | + - [`jido_memory`](https://github.com/agentjido/jido_memory) package for the memory capabilities. |
| 72 32 | |
| 73 33 | ## Key Features |
| 74 34 | |
| 75 35 | - 🧩 **Composable Actions**: Build complex behaviors from simple, reusable actions |
| 76 | - - 🤖 **Autonomous Agents**: Self-directing entities that plan and execute workflows |
| 36 | + - 🤖 **Agent Data Structures**: Stateless agentic data structures for planning, execution, and monitoring |
| 37 | + - 🔥 **Agent GenServer**: Comprehensive GenServer implementation for agents, with support for dynamic capabilities |
| 77 38 | - 📡 **Real-time Sensors**: Event-driven data gathering and monitoring |
| 78 | - - 🔄 **Adaptive Learning**: Agents can modify their capabilities at runtime |
| 79 | - - 📊 **Built-in Telemetry**: Comprehensive observability and debugging |
| 39 | + - 📨 **Signal System**: Comprehensive signal system for communication between agents and other systems |
| 40 | + - 🧠 **Skills**: Skills are reusable, composable behavior groups that can be used by agents - think Agentic plugins |
| 80 41 | - ⚡ **Distributed by Design**: Built for multi-node Elixir clusters |
| 81 42 | - 🧪 **Testing Tools**: Rich helpers for unit and property-based testing |
| 82 43 | |
| @@ -87,7 +48,7 @@ Add Jido to your dependencies: | |
| 87 48 | ```elixir |
| 88 49 | def deps do |
| 89 50 | [ |
| 90 | - {:jido, "~> 1.0.0"} |
| 51 | + {:jido, "~> 1.1.0"} |
| 91 52 | ] |
| 92 53 | end |
| 93 54 | ``` |
| @@ -115,27 +76,12 @@ defmodule MyApp.Actions.FormatUser do | |
| 115 76 | }} |
| 116 77 | end |
| 117 78 | end |
| 79 | + |
| 80 | + # Execute a single Action via the Workflow system |
| 81 | + {:ok, result} = Jido.Workflow.run(FormatUser, %{name: "John Doe", email: "john@example.com"}) |
| 118 82 | ``` |
| 119 83 | |
| 120 | - [Learn more about Actions →](guides/actions.md) |
| 121 | - |
| 122 | - ### Workflows |
| 123 | - |
| 124 | - Workflows chain Actions together to accomplish complex tasks. Jido handles data flow and error handling between steps: |
| 125 | - |
| 126 | - ```elixir |
| 127 | - alias MyApp.Actions.{FormatUser, EnrichUserData, NotifyUser} |
| 128 | - |
| 129 | - {:ok, result} = Jido.Workflow.Chain.chain( |
| 130 | - [FormatUser, EnrichUserData, NotifyUser], |
| 131 | - %{ |
| 132 | - name: "John Doe ", |
| 133 | - email: "JOHN@EXAMPLE.COM" |
| 134 | - } |
| 135 | - ) |
| 136 | - ``` |
| 137 | - |
| 138 | - [Learn more about Workflows →](guides/actions.md#combining-actions-into-a-workflow) |
| 84 | + [Learn more about Actions →](guides/actions/overview.md) |
| 139 85 | |
| 140 86 | ### Agents |
| 141 87 | |
| @@ -155,16 +101,19 @@ defmodule MyApp.CalculatorAgent do | |
| 155 101 | value: [type: :float, default: 0.0], |
| 156 102 | operations: [type: {:list, :atom}, default: []] |
| 157 103 | ] |
| 104 | + end |
| 158 105 | |
| 159 | - def on_after_run(agent, result) do |
| 160 | - # Track which operations we've used |
| 161 | - ops = [result.action | agent.state.operations] |> Enum.uniq() |
| 162 | - {:ok, %{agent | state: %{agent.state | operations: ops}}} |
| 163 | - end |
| 164 | - end |
| 106 | + # Start the agent |
| 107 | + {:ok, pid} = MyApp.CalculatorAgent.start_link() |
| 108 | + |
| 109 | + # Synchronous call |
| 110 | + {:ok, result} = MyApp.CalculatorAgent.call(pid, Signal.new!(%{type: "add", data: %{a: 1, b: 2}})) |
| 111 | + |
| 112 | + # Asynchronous call |
| 113 | + {:ok, response_ref} = MyApp.CalculatorAgent.cast(pid, Signal.new!(%{type: "add", data: %{a: 1, b: 2}})) |
| 165 114 | ``` |
| 166 115 | |
| 167 | - [Learn more about Agents →](guides/agents.md) |
| 116 | + [Learn more about Agents →](guides/agents/overview.md) |
| 168 117 | |
| 169 118 | ### Sensors |
| 170 119 | |
| @@ -190,7 +139,7 @@ defmodule MyApp.Sensors.OperationCounter do | |
| 190 139 | end |
| 191 140 | ``` |
| 192 141 | |
| 193 | - [Learn more about Sensors →](guides/sensors.md) |
| 142 | + [Learn more about Sensors →](guides/sensors/overview.md) |
| 194 143 | |
| 195 144 | ## Running in Production |
| 196 145 | |
| @@ -201,32 +150,22 @@ Start your agents under supervision: | |
| 201 150 | children = [ |
| 202 151 | {Registry, keys: :unique, name: Jido.AgentRegistry}, |
| 203 152 | {Phoenix.PubSub, name: MyApp.PubSub}, |
| 204 | - {Jido.Agent.Supervisor, pubsub: MyApp.PubSub}, |
| 205 | - {Jido.Agent.Server, |
| 206 | - agent: MyApp.CalculatorAgent.new(), |
| 207 | - name: "calculator_1" |
| 208 | - } |
| 153 | + |
| 154 | + # Agents fit into your existing supervision tree |
| 155 | + # Specify an id to always uniquely identify the agent |
| 156 | + {MyApp.CalculatorAgent, id: "calculator_1"} |
| 209 157 | ] |
| 210 158 | |
| 211 159 | Supervisor.start_link(children, strategy: :one_for_one) |
| 212 160 | ``` |
| 213 161 | |
| 214 | - ## Example Use Cases |
| 215 | - |
| 216 | - - **Service Orchestration**: Coordinate complex workflows across multiple services |
| 217 | - - **Data Processing**: Build adaptive ETL pipelines that evolve with your data |
| 218 | - - **Business Automation**: Model complex business processes with autonomous agents |
| 219 | - - **System Monitoring**: Create smart monitoring agents that adapt to system behavior |
| 220 | - - **Transaction Management**: Handle multi-step transactions with built-in compensation |
| 221 | - - **Event Processing**: Process and react to event streams in real-time |
| 222 | - |
| 223 162 | ## Documentation |
| 224 163 | |
| 225 164 | - [📘 Getting Started Guide](guides/getting-started.md) |
| 226 | - - [🧩 Actions & Workflows](guides/actions.md) |
| 227 | - - [🤖 Building Agents](guides/agents.md) |
| 228 | - - [📡 Sensors & Monitoring](guides/sensors.md) |
| 229 | - - [🔄 Agent Directives](guides/directives.md) |
| 165 | + - [🧩 Actions & Workflows](guides/actions/overview.md) |
| 166 | + - [🤖 Building Agents](guides/agents/overview.md) |
| 167 | + - [📡 Sensors & Monitoring](guides/sensors/overview.md) |
| 168 | + - [🔄 Agent Directives](guides/agents/directives.md) |
| 230 169 | |
| 231 170 | ## Contributing |
| @@ -1,6 +1,8 @@ | |
| 1 | - {<<"links">>,[{<<"GitHub">>,<<"https://github.com/agentjido/jido">>}]}. |
| 1 | + {<<"links">>, |
| 2 | + [{<<"GitHub">>,<<"https://github.com/agentjido/jido">>}, |
| 3 | + {<<"Jido Workbench">>,<<"https://github.com/agentjido/jido_workbench">>}]}. |
| 2 4 | {<<"name">>,<<"jido">>}. |
| 3 | - {<<"version">>,<<"1.1.0-rc">>}. |
| 5 | + {<<"version">>,<<"1.1.0-rc.1">>}. |
| 4 6 | {<<"description">>, |
| 5 7 | <<"A foundational framework for building autonomous, distributed agent systems in Elixir">>}. |
| 6 8 | {<<"elixir">>,<<"~> 1.17">>}. |
| @@ -8,7 +10,7 @@ | |
| 8 10 | {<<"files">>, |
| 9 11 | [<<"lib">>,<<"lib/jido.ex">>,<<"lib/jido">>,<<"lib/jido/supervisor.ex">>, |
| 10 12 | <<"lib/jido/util.ex">>,<<"lib/jido/sensors">>, |
| 11 | - <<"lib/jido/sensors/cron_sensor.ex">>, |
| 13 | + <<"lib/jido/sensors/bus_sensor.ex">>,<<"lib/jido/sensors/cron_sensor.ex">>, |
| 12 14 | <<"lib/jido/sensors/heartbeat_sensor.ex">>,<<"lib/jido/action.ex">>, |
| 13 15 | <<"lib/jido/skill.ex">>,<<"lib/jido/runner">>, |
| 14 16 | <<"lib/jido/runner/chain.ex">>,<<"lib/jido/runner/simple.ex">>, |
| @@ -67,11 +69,6 @@ | |
| 67 69 | {<<"optional">>,false}, |
| 68 70 | {<<"requirement">>,<<"~> 1.0">>}, |
| 69 71 | {<<"repository">>,<<"hexpm">>}], |
| 70 | - [{<<"name">>,<<"elixir_uuid">>}, |
| 71 | - {<<"app">>,<<"elixir_uuid">>}, |
| 72 | - {<<"optional">>,false}, |
| 73 | - {<<"requirement">>,<<"~> 1.2">>}, |
| 74 | - {<<"repository">>,<<"hexpm">>}], |
| 75 72 | [{<<"name">>,<<"jason">>}, |
| 76 73 | {<<"app">>,<<"jason">>}, |
| 77 74 | {<<"optional">>,false}, |
| @@ -127,6 +124,11 @@ | |
| 127 124 | {<<"optional">>,false}, |
| 128 125 | {<<"requirement">>,<<"~> 3.5">>}, |
| 129 126 | {<<"repository">>,<<"hexpm">>}], |
| 127 | + [{<<"name">>,<<"uniq">>}, |
| 128 | + {<<"app">>,<<"uniq">>}, |
| 129 | + {<<"optional">>,false}, |
| 130 | + {<<"requirement">>,<<"~> 0.6.1">>}, |
| 131 | + {<<"repository">>,<<"hexpm">>}], |
| 130 132 | [{<<"name">>,<<"ex_dbug">>}, |
| 131 133 | {<<"app">>,<<"ex_dbug">>}, |
| 132 134 | {<<"optional">>,false}, |
| @@ -159,7 +159,8 @@ defmodule Jido.Actions.Basic do | |
| 159 159 | |
| 160 160 | @spec run(map(), map()) :: {:ok, map()} |
| 161 161 | def run(%{value: value} = params, _ctx) do |
| 162 | - IO.inspect(value) |
| 162 | + # credo:disable-for-next-line Credo.Check.Warning.IoInspect |
| 163 | + IO.inspect(value, label: "Inspect action output") |
| 163 164 | {:ok, params} |
| 164 165 | end |
| 165 166 | end |
| @@ -22,6 +22,7 @@ defmodule Jido.Actions.Directives do | |
| 22 22 | params: [type: :map, default: %{}] |
| 23 23 | ] |
| 24 24 | |
| 25 | + @spec run(map(), map()) :: {:ok, map(), Jido.Agent.Directive.Enqueue.t()} |
| 25 26 | def run(%{action: action} = input, context \\ %{}) do |
| 26 27 | params = Map.get(input, :params, %{}) |
| 27 28 | opts = Map.get(input, :opts, []) |
| @@ -46,6 +47,7 @@ defmodule Jido.Actions.Directives do | |
| 46 47 | action_module: [type: :atom, required: true] |
| 47 48 | ] |
| 48 49 | |
| 50 | + @spec run(map(), map()) :: {:ok, map(), Jido.Agent.Directive.RegisterAction.t()} |
| 49 51 | def run(%{action_module: action_module}, _context) do |
| 50 52 | directive = %Jido.Agent.Directive.RegisterAction{ |
| 51 53 | action_module: action_module |
| @@ -64,6 +66,9 @@ defmodule Jido.Actions.Directives do | |
| 64 66 | action_module: [type: :atom, required: true] |
| 65 67 | ] |
| 66 68 | |
| 69 | + @spec run(map(), map()) :: |
| 70 | + {:ok, map(), Jido.Agent.Directive.DeregisterAction.t()} |
| 71 | + | {:error, :cannot_deregister_self} |
| 67 72 | def run(%{action_module: action_module}, _context) do |
| 68 73 | # Prevent deregistering this module |
| 69 74 | if action_module == __MODULE__ do |
| @@ -88,8 +93,7 @@ defmodule Jido.Actions.Directives do | |
| 88 93 | args: [type: :any, required: true, doc: "Arguments to pass to the module"] |
| 89 94 | ] |
| 90 95 | |
| 91 | - @spec run(map(), map()) :: |
| 92 | - {:ok, map(), Jido.Agent.Directive.Spawn.t()} | {:error, term()} |
| 96 | + @spec run(map(), map()) :: {:ok, map(), Jido.Agent.Directive.Spawn.t()} | {:error, term()} |
| 93 97 | def run(%{module: module, args: args}, _ctx) do |
| 94 98 | directive = %Jido.Agent.Directive.Spawn{ |
| 95 99 | module: module, |
| @@ -109,8 +113,7 @@ defmodule Jido.Actions.Directives do | |
| 109 113 | pid: [type: :pid, required: true, doc: "PID of process to terminate"] |
| 110 114 | ] |
| 111 115 | |
| 112 | - @spec run(map(), map()) :: |
| 113 | - {:ok, map(), Jido.Agent.Directive.Kill.t()} | {:error, term()} |
| 116 | + @spec run(map(), map()) :: {:ok, map(), Jido.Agent.Directive.Kill.t()} | {:error, term()} |
| 114 117 | def run(%{pid: pid}, _ctx) do |
| 115 118 | directive = %Jido.Agent.Directive.Kill{pid: pid} |
| 116 119 | {:ok, %{}, directive} |
| @@ -100,6 +100,8 @@ defmodule Jido.Agent do | |
| 100 100 | Each Agent module defines its own struct type and behavior. Agent functions must be called |
| 101 101 | on matching agent structs: |
| 102 102 | |
| 103 | + |
| 104 | + ```elixir |
| 103 105 | # Correct usage: |
| 104 106 | agent = MyAgent.new() |
| 105 107 | {:ok, agent} = MyAgent.set(agent, attrs) |
| @@ -107,6 +109,7 @@ defmodule Jido.Agent do | |
| 107 109 | # Incorrect usage: |
| 108 110 | agent = MyAgent.new() |
| 109 111 | {:ok, agent} = OtherAgent.set(agent, attrs) # Will fail - type mismatch |
| 112 | + ``` |
| 110 113 | |
| 111 114 | ## Runner Architecture |
| 112 115 | |
| @@ -136,9 +139,14 @@ defmodule Jido.Agent do | |
| 136 139 | * `agent_result()` - `{:ok, t()} | {:error, term()}` |
| 137 140 | """ |
| 138 141 | use TypedStruct |
| 139 | - alias Jido.Error |
| 140 | - alias Jido.Instruction |
| 142 | + use Private |
| 143 | + use ExDbug, enabled: false |
| 144 | + |
| 145 | + alias Jido.{Error, Signal, Instruction} |
| 146 | + alias Jido.Agent.Directive |
| 141 147 | alias Jido.Agent.Server.Signal, as: ServerSignal |
| 148 | + alias Jido.Agent.Server.State, as: ServerState |
| 149 | + |
| 142 150 | require OK |
| 143 151 | |
| 144 152 | @type instruction :: Instruction.t() | module() | {module(), map()} |
| @@ -214,6 +222,7 @@ defmodule Jido.Agent do | |
| 214 222 | defmacro __using__(opts) do |
| 215 223 | escaped_schema = Macro.escape(@agent_compiletime_options_schema) |
| 216 224 | |
| 225 | + # credo:disable-for-next-line Credo.Check.Refactor.LongQuoteBlocks |
| 217 226 | quote location: :keep do |
| 218 227 | @behaviour Jido.Agent |
| 219 228 | @type t :: Jido.Agent.t() |
| @@ -610,12 +619,12 @@ defmodule Jido.Agent do | |
| 610 619 | |
| 611 620 | def set(%_{} = agent, _attrs, _opts) do |
| 612 621 | Error.validation_error( |
| 613 | - "Invalid agent type. Expected #{agent.__struct__}, got #{__MODULE__}" |
| 622 | + "Invalid agent type. Expected #{inspect(agent.__struct__)}, got #{inspect(__MODULE__)}" |
| 614 623 | ) |
| 615 624 | |> OK.failure() |
| 616 625 | end |
| 617 626 | |
| 618 | - def set(server, attrs, opts) do |
| 627 | + def set(server, attrs, opts) when not is_struct(server) do |
| 619 628 | with {:ok, pid} <- Jido.resolve_pid(server), |
| 620 629 | signal <- ServerSignal.cmd_signal(:set, server, attrs, opts) do |
| 621 630 | GenServer.call(pid, signal) |
Loading more files…