Packages
nous
0.16.6
0.17.0
0.16.6
0.16.5
0.16.4
0.16.3
0.16.2
0.16.1
0.16.0
0.15.8
0.15.7
0.15.6
0.15.5
0.15.4
0.15.3
0.15.2
0.15.1
0.15.0
0.14.3
0.14.2
0.14.1
0.14.0
0.13.3
0.13.2
0.13.1
0.13.0
0.12.17
0.12.16
0.12.15
0.12.14
0.12.13
0.12.12
0.12.11
0.12.9
0.12.7
0.12.6
0.12.5
0.12.3
0.12.2
0.12.0
0.11.3
0.11.0
0.10.1
0.10.0
0.9.0
0.8.1
0.8.0
0.7.2
0.7.1
0.7.0
0.5.0
AI agent framework for Elixir with multi-provider LLM support
Current section
Files
Jump to
Current section
Files
lib/nous/workflow.ex
defmodule Nous.Workflow do
@moduledoc """
DAG/graph-based workflow engine for orchestrating agents, tools, and control flow.
Workflows define execution graphs where nodes are steps (agent calls, tool
executions, transformations, branches, parallel fan-outs) and edges define
the flow between them.
## Architecture
- **Complementary to Decisions**: Decisions track *why* an agent made choices.
Workflows define *what* executes and *when*.
- **Complementary to Teams**: Teams manage persistent agent groups.
Workflows define transient execution plans.
- **Standalone system**: Not a Behaviour or Plugin — operates above the agent level.
## Quick Start
alias Nous.{Agent, Workflow}
planner = Agent.new("openai:gpt-4o-mini", instructions: "You are a research planner.")
searcher = Agent.new("openai:gpt-4o-mini", instructions: "You search for information.")
reporter = Agent.new("openai:gpt-4o-mini", instructions: "You write reports.")
workflow =
Workflow.new("research")
|> Workflow.add_node(:plan, :agent_step, %{agent: planner, prompt: "Plan research on: ..."})
|> Workflow.add_node(:search, :agent_step, %{agent: searcher, prompt: fn s -> "Search: \#{s.data.plan}" end})
|> Workflow.add_node(:report, :agent_step, %{agent: reporter, prompt: "Write report from findings."})
|> Workflow.chain([:plan, :search, :report])
{:ok, result} = Workflow.run(workflow, %{topic: "AI agents in 2026"})
## Graph Definition
Build graphs using an `Ecto.Multi`-style pipe API:
- `new/1,2` — create an empty graph
- `add_node/4,5` — add a typed node
- `connect/3,4` — add an edge between nodes
- `chain/2` — connect nodes in sequence
## Execution
- `compile/1` — validate and compile the graph
- `run/2,3` — compile and execute in one step
"""
alias Nous.Workflow.{Graph, Compiler, Engine, Mermaid}
# ---------------------------------------------------------------------------
# Graph builder (delegated to Graph)
# ---------------------------------------------------------------------------
defdelegate new(id, opts \\ []), to: Graph
defdelegate add_node(graph, node_id, type, config \\ %{}, opts \\ []), to: Graph
defdelegate connect(graph, from, to, opts \\ []), to: Graph
defdelegate chain(graph, node_ids), to: Graph
defdelegate set_entry(graph, node_id), to: Graph
# ---------------------------------------------------------------------------
# Compilation
# ---------------------------------------------------------------------------
@doc """
Compile and validate a workflow graph.
Returns `{:ok, compiled}` or `{:error, errors}`.
"""
@spec compile(Graph.t()) :: {:ok, Compiler.compiled()} | {:error, [term()]}
defdelegate compile(graph), to: Compiler
@doc """
Validate a workflow graph without compiling.
"""
@spec validate(Graph.t()) :: :ok | {:error, [term()]}
defdelegate validate(graph), to: Compiler
# ---------------------------------------------------------------------------
# Visualization
# ---------------------------------------------------------------------------
@doc """
Generate a Mermaid flowchart diagram string from the graph.
"""
@spec to_mermaid(Graph.t(), keyword()) :: String.t()
defdelegate to_mermaid(graph, opts \\ []), to: Mermaid
# ---------------------------------------------------------------------------
# Graph mutation
# ---------------------------------------------------------------------------
defdelegate insert_after(graph, after_id, new_id, type, config \\ %{}, opts \\ []), to: Graph
defdelegate remove_node(graph, node_id), to: Graph
# ---------------------------------------------------------------------------
# Execution
# ---------------------------------------------------------------------------
@doc """
Compile and execute a workflow in one step.
## Options
- `:deps` — dependencies passed to agents/tools
- `:callbacks` — callback functions for agent steps
- `:notify_pid` — PID to receive progress notifications
- `:max_iterations` — max cycle iterations (default: 10)
## Returns
- `{:ok, final_state}` — workflow completed successfully
- `{:error, reason}` — compilation or execution failed
"""
@spec run(Graph.t(), map(), keyword()) :: {:ok, Nous.Workflow.State.t()} | {:error, term()}
def run(%Graph{} = graph, initial_data \\ %{}, opts \\ []) do
case compile(graph) do
{:ok, compiled} -> Engine.execute(compiled, initial_data, opts)
{:error, _} = error -> error
end
end
end