Adaptive research agent demonstrating dynamic workflow construction.
Unlike the Studio OrchestratorAgent which uses a fixed 5-node pipeline,
this agent dynamically selects its Phase 2 writing pipeline based on
Phase 1 research results:
- Phase 1 —
PlanQueries → SimulateSearch - Phase 2 (rich) —
BuildOutline → DraftArticle → EditAndAssemble - Phase 2 (thin) —
DraftArticle → EditAndAssemble
The agent uses the standard Jido.Runic.Strategy with runic.set_workflow
to hot-swap workflows between phases.
Summary
Functions
Returns the list of actions from all attached plugins.
Extract the final article markdown from run results.
Build the Phase 1 research workflow: PlanQueries → SimulateSearch.
Build the Phase 2 writing workflow, dynamically shaped by research results.
Returns the union of all capabilities from all mounted plugin instances.
Returns the agent's category.
Execute actions against the agent: (agent, action) -> {agent, directives}
Returns the agent's description.
Returns the agent's name.
Creates a new agent with optional initial state.
Returns the configuration for a specific plugin.
Returns the list of plugin instances attached to this agent.
Returns the expanded and validated plugin routes.
Returns the expanded plugin and agent schedules.
Returns the state slice for a specific plugin.
Returns the list of plugin modules attached to this agent (deduplicated).
Run the full adaptive research pipeline for a topic.
Returns the merged schema (base + plugin schemas).
Updates the agent's state by merging new attributes.
Returns all expanded route signal types from plugin routes.
Returns the execution strategy module for this agent.
Returns the strategy options for this agent.
Returns a stable, public view of the strategy's execution state.
Returns the agent's tags.
Validates the agent's state against its schema.
Returns the agent's version.
Functions
@spec actions() :: [module()]
Returns the list of actions from all attached plugins.
Extract the final article markdown from run results.
Build the Phase 1 research workflow: PlanQueries → SimulateSearch.
Build the Phase 2 writing workflow, dynamically shaped by research results.
When the research summary is rich (>= 500 chars), includes an outline step. Otherwise skips straight to drafting.
@spec capabilities() :: [atom()]
Returns the union of all capabilities from all mounted plugin instances.
Capabilities are atoms describing what the agent can do based on its mounted plugins.
Example
MyAgent.capabilities()
# => [:messaging, :channel_management, :chat, :embeddings]
@spec category() :: String.t() | nil
Returns the agent's category.
@spec cmd(Jido.Agent.t(), Jido.Agent.action()) :: Jido.Agent.cmd_result()
Execute actions against the agent: (agent, action) -> {agent, directives}
This is the core operation. Actions modify state and may perform required work; directives are runtime-owned external effects. Execution is delegated to the configured strategy (default: Direct).
Action Formats
MyAction- Action module with no params{MyAction, %{param: 1}}- Action with params{MyAction, %{param: 1}, %{context: data}}- Action with params and context{MyAction, %{param: 1}, %{}, [timeout: 1000]}- Action with opts%Instruction{}- Full instruction struct[...]- List of any of the above (processed in sequence)
Options
The optional third argument opts is a keyword list merged into all instructions:
:timeout- Maximum time (in ms) for each action to complete:max_retries- Maximum retry attempts on failure:backoff- Initial backoff time in ms (doubles with each retry)
Examples
{agent, directives} = Jido.Runic.Examples.Adaptive.AdaptiveResearcher.cmd(agent, MyAction)
{agent, directives} = Jido.Runic.Examples.Adaptive.AdaptiveResearcher.cmd(agent, {MyAction, %{value: 42}})
{agent, directives} = Jido.Runic.Examples.Adaptive.AdaptiveResearcher.cmd(agent, [Action1, Action2])
# With per-call options (merged into all instructions)
{agent, directives} = Jido.Runic.Examples.Adaptive.AdaptiveResearcher.cmd(agent, MyAction, timeout: 5000)
@spec cmd(Jido.Agent.t(), Jido.Agent.action(), keyword()) :: Jido.Agent.cmd_result()
@spec description() :: String.t() | nil
Returns the agent's description.
@spec name() :: String.t()
Returns the agent's name.
@spec new(keyword() | map()) :: Jido.Agent.t()
Creates a new agent with optional initial state.
The agent is fully initialized including strategy state. For the default Direct strategy, this is a no-op. For custom strategies, any state initialization is applied (but directives are only processed by AgentServer).
Examples
agent = Jido.Runic.Examples.Adaptive.AdaptiveResearcher.new()
agent = Jido.Runic.Examples.Adaptive.AdaptiveResearcher.new(id: "custom-id")
agent = Jido.Runic.Examples.Adaptive.AdaptiveResearcher.new(state: %{counter: 10})
Returns the configuration for a specific plugin.
Accepts either a module or a {module, as_alias} tuple for multi-instance plugins.
@spec plugin_instances() :: [Jido.Plugin.Instance.t()]
Returns the list of plugin instances attached to this agent.
Returns the expanded and validated plugin routes.
@spec plugin_schedules() :: [ Jido.Plugin.Schedules.schedule_spec() | Jido.Agent.Schedules.schedule_spec() ]
Returns the expanded plugin and agent schedules.
@spec plugin_state(Jido.Agent.t(), module() | {module(), atom()}) :: map() | nil
Returns the state slice for a specific plugin.
Accepts either a module or a {module, as_alias} tuple for multi-instance plugins.
@spec plugins() :: [module()]
Returns the list of plugin modules attached to this agent (deduplicated).
For multi-instance plugins, the module appears once regardless of how many instances are mounted.
Example
MyAgent.plugins()
# => [MyApp.SlackPlugin, MyApp.OpenAIPlugin]
Run the full adaptive research pipeline for a topic.
Phase 1 researches the topic, then Phase 2's shape is determined by how rich the research results are.
Options
:jido- Name of a running Jido instance (required):timeout- Timeout in ms for each phase (default: 120_000):debug- Enable debug event buffer (default: true)
Returns
A map with :topic, :productions, :phase_2_type, :status, and :pid.
@spec schema() :: Zoi.schema() | keyword()
Returns the merged schema (base + plugin schemas).
@spec set(Jido.Agent.t(), map() | keyword()) :: Jido.Agent.agent_result()
Updates the agent's state by merging new attributes.
Uses deep merge semantics - nested maps are merged recursively.
Examples
{:ok, agent} = Jido.Runic.Examples.Adaptive.AdaptiveResearcher.set(agent, %{status: :running})
{:ok, agent} = Jido.Runic.Examples.Adaptive.AdaptiveResearcher.set(agent, counter: 5)
@spec signal_types() :: [String.t()]
Returns all expanded route signal types from plugin routes.
These are the fully-prefixed signal types that the agent can handle.
Example
MyAgent.signal_types()
# => ["slack.post", "slack.channels.list", "openai.chat"]
@spec strategy() :: module()
Returns the execution strategy module for this agent.
@spec strategy_opts() :: keyword()
Returns the strategy options for this agent.
@spec strategy_snapshot(Jido.Agent.t()) :: Jido.Agent.Strategy.Snapshot.t()
Returns a stable, public view of the strategy's execution state.
Use this instead of inspecting agent.state.__strategy__ directly.
Returns a Jido.Agent.Strategy.Snapshot struct with:
status- Coarse execution statusdone?- Whether strategy reached terminal stateresult- Main output if anydetails- Additional strategy-specific metadata
@spec tags() :: [String.t()]
Returns the agent's tags.
@spec validate( Jido.Agent.t(), keyword() ) :: Jido.Agent.agent_result()
Validates the agent's state against its schema.
Options
:strict- When true, only schema-defined fields are kept (default: false)
Examples
{:ok, agent} = Jido.Runic.Examples.Adaptive.AdaptiveResearcher.validate(agent)
{:ok, agent} = Jido.Runic.Examples.Adaptive.AdaptiveResearcher.validate(agent, strict: true)
@spec vsn() :: String.t() | nil
Returns the agent's version.