Orchestrate multi-agent pipelines
A common pattern is splitting work across specialised agents — a researcher gathers facts, a writer turns them into prose, an editor polishes the result. Each is a separate agent and session; the application chains them.
This is the simplest orchestration pattern: no shared state, no subagent spawning, just sequential calls.
Pipeline skeleton
Section titled “Pipeline skeleton”import asynciofrom everruns_sdk import Everruns
async def run_pipeline(client: Everruns, topic: str) -> str: researcher = await client.agents.create( name="Researcher", system_prompt="Research the given topic thoroughly. Write detailed notes.", capabilities=["web_fetch", "session_file_system"], )
research_session = await client.sessions.create(agent_id=researcher.id) await client.messages.create(research_session.id, f"Research {topic}")
research_output = await collect_final_text(client, research_session.id)
writer = await client.agents.create( name="Writer", system_prompt="Write clear, well-structured technical articles.", ) writer_session = await client.sessions.create(agent_id=writer.id) await client.messages.create( writer_session.id, f"Write a blog post based on this research:\n\n{research_output}", )
return await collect_final_text(client, writer_session.id)
async def collect_final_text(client: Everruns, session_id: str) -> str: final_text: str | None = None async for event in client.events.stream(session_id): if event.type == "output.message.completed": message = event.data.get("message", {}) final_text = "\n".join( p["text"] for p in message.get("content", []) if p.get("type") == "text" ) elif event.type == "turn.failed": raise RuntimeError(event.data.get("error", "turn failed")) elif event.type == "turn.cancelled": raise RuntimeError("turn cancelled") elif event.type == "turn.completed": break if not final_text: raise RuntimeError("turn completed without producing a final message") return final_textCleanup
Section titled “Cleanup”Each session and agent persists until you explicitly delete it. For ephemeral pipelines, clean up at the end:
for sid in (research_session.id, writer_session.id): await client.sessions.delete(sid)for aid in (researcher.id, writer.id): await client.agents.delete(aid)For pipelines you’ll re-run, don’t recreate the agents — create them once, store the IDs, and reuse them.
When to use subagents instead
Section titled “When to use subagents instead”If one agent needs to delegate to another during a turn, use the Sub Agents capability instead of an application-level pipeline. Subagents run inside the parent session and emit subagent.* events that the parent agent receives as tool results.
Pick application-level pipelines when:
- The stages are clearly separated and you want independent observability per stage.
- Stages run at different cadences (e.g., scheduled research → on-demand writeup).
- You want to reuse intermediate output across multiple downstream agents.
Pick subagents when:
- The parent agent decides at runtime which subagent to call.
- The work feels like a single user request, not a pipeline.