Current section

22 Versions

Jump to

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 [![Coverage Status](https://coveralls.io/repos/github/agentjido/jido/badge.svg?branch=main)](https://coveralls.io/github/agentjido/jido?branch=main)
11 11 [![Apache 2 License](https://img.shields.io/hexpm/l/jido)](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…