Multi-agent Helpers
Factory functions that return pre-configured FlowBuilder instances implementing common multi-agent orchestration topologies.
Import
import {
supervisorCrew,
sequentialCrew,
hierarchicalCrew,
roundRobinDebate,
swarm,
handoffTo,
} from "flowneer/presets/agent";supervisorCrew
A supervisor runs first to plan/set up context, workers execute in parallel, then the supervisor runs again to aggregate results.
const flow = supervisorCrew<BlogState>(
// 1. Supervisor: plan
async (s) => {
s.outline = await planContent(s.topic);
s.sections = {};
},
// 2. Workers: write sections in parallel
[
async (s) => {
s.sections.intro = await writeSection("Introduction", s.topic);
},
async (s) => {
s.sections.body = await writeSection("Main Body", s.topic);
},
async (s) => {
s.sections.outro = await writeSection("Conclusion", s.topic);
},
],
// 3. Post step: aggregate
{
post: async (s) => {
s.draft = [s.sections.intro, s.sections.body, s.sections.outro].join(
"\n\n",
);
},
// Optional: use reducer for safe parallel writes
reducer: (shared, drafts) => {
shared.sections = Object.assign({}, ...drafts.map((d) => d.sections));
},
},
);
await flow.run({ topic: "TypeScript", outline: [], sections: {}, draft: "" });Signature
function supervisorCrew<S, P>(
supervisor: NodeFn<S, P>,
workers: NodeFn<S, P>[],
options?: {
post?: NodeFn<S, P>;
reducer?: (shared: S, drafts: S[]) => void;
},
): FlowBuilder<S, P>;sequentialCrew
A strict pipeline: each step runs in order and passes its results to the next via shared state.
const flow = sequentialCrew<ResearchState>([
async (s) => {
s.research = await research(s.query);
},
async (s) => {
s.draft = await writeDraft(s.research);
},
async (s) => {
s.final = await editDraft(s.draft);
},
]);Signature
function sequentialCrew<S, P>(steps: NodeFn<S, P>[]): FlowBuilder<S, P>;hierarchicalCrew
A top-level manager delegates to sub-team flows (each is its own FlowBuilder). Teams run sequentially after the manager, then an optional aggregation step runs.
const researchTeam = supervisorCrew(researchSupervisor, researchWorkers);
const writingTeam = sequentialCrew([drafter, editor, seoOptimizer]);
const flow = hierarchicalCrew<State>(
async (s) => {
s.plan = planTasks(s.input);
}, // manager
[researchTeam, writingTeam], // sub-teams
async (s) => {
s.output = mergeResults(s);
}, // aggregation
);Signature
function hierarchicalCrew<S, P>(
manager: NodeFn<S, P>,
teams: FlowBuilder<S, P>[],
aggregate?: NodeFn<S, P>,
): FlowBuilder<S, P>;roundRobinDebate
Each agent runs in sequence, repeated rounds times. Agents append their perspectives to shared state — producing a multi-turn collaborative output.
const flow = roundRobinDebate<DebateState>(
[
async (s) => {
s.debate.push({ agent: "optimist", text: await optimist(s) });
},
async (s) => {
s.debate.push({ agent: "critic", text: await critic(s) });
},
async (s) => {
s.debate.push({ agent: "synthesiser", text: await synth(s) });
},
],
3, // 3 full rounds → 9 total turns
);
await flow.run({ topic: "AI safety", debate: [] });The current round is tracked in shared.__debateRound.
Signature
function roundRobinDebate<S, P>(
agents: NodeFn<S, P>[],
rounds: number,
): FlowBuilder<S, P>;swarm
Decentralized peer-to-peer handoff: any agent can pass control to any other agent at runtime. No central manager. Agents call handoffTo(shared, targetName, reason?) inside their fn to request a handoff; the loop repeats until an agent finishes without handing off or maxHandoffs is reached.
See swarm.md for the full guide.
const flow = swarm([triageAgent, billingAgent, supportAgent], {
defaultAgent: "triage",
maxHandoffs: 5,
});
await flow.run({ messages: [{ role: "user", content: "I need a refund" }] });Signature
function swarm<S extends SwarmState, P>(
agents: SwarmAgent<S, P>[],
options: SwarmOptions<S>,
): FlowBuilder<S, P>;Composition
All pattern functions return a FlowBuilder, so they can be extended with additional plugins:
const flow = supervisorCrew(supervisor, workers, { post })
.withTiming()
.withCostTracker();And nested inside larger flows:
const mainFlow = new FlowBuilder<State>()
.startWith(initialize)
.then(async (s, p) => {
await researchFlow.run(s, p); // inline sub-flow
})
.then(finalize);