Topology #9 — independent_analysis → iterate(critique, until: ...) → chair.
Members analyse independently, then critique each other's outputs iteratively
until a convergence callback returns true or max_iterations is reached.
The chair synthesizes the final iteration's outputs.
Options
:members— list of{id, module, opts}tuples (required):chair—{module, opts}tuple (required):until—(prev_round_result | nil, current_round_result -> boolean)(default: always false):max_iterations— positive integer, default3:as— module name for the created council module (optional)
Usage (module-form)
council =
CouncilEx.Councils.Consensus.new(
as: MyApp.ConsensusCouncil,
members: [
{:m1, MyApp.Members.Analyst, [provider: :openai, model: "gpt-4o-mini"]},
{:m2, MyApp.Members.Analyst, [provider: :openai, model: "gpt-4o-mini"]}
],
chair: {MyApp.Members.Synth, [provider: :openai, model: "gpt-4o"]},
max_iterations: 3
)Usage (dynamic / data-only form)
new_dynamic/1 returns a %CouncilEx.DynamicCouncil{} with the same
topology. The :until callback (when provided) MUST be a remote
function capture — &MyMod.converged?/2 — not an inline fn,
because dynamic councils are designed to be JSON-serializable.
Inline fn works at runtime but won't survive to_json/from_json.
Omit :until to rely solely on :max_iterations.
Summary
Functions
Look up the until/2 function registered for a council module.
Build the Consensus topology as a generated council module.
Options
:as— module name for the generated council (default: anonymous unique).:members(required) —[{id, module, opts}]member tuples.:chair(required) —{module, opts}chair tuple.:until— 2-arity(prev_round_result | nil, current_round_result -> boolean)convergence callback. Defaults to "always false" (run until:max_iterations).:max_iterations— positive integer, default3.
See the moduledoc for a complete example. Use new_dynamic/1 for the
data-only %CouncilEx.DynamicCouncil{} form.
@spec new_dynamic(keyword()) :: CouncilEx.DynamicCouncil.t()
Build the same Consensus topology as new/1 but as a data-only
%CouncilEx.DynamicCouncil{}.
Members and chair accept the same shapes as Specialist.new_dynamic/1.
The optional :until MUST be a remote function capture
(&MyMod.converged?/2); inline fn literals will work at runtime
but break JSON round-trips.