Multi-Agent
Orchestration
for TypeScript
SocietyAI is a TypeScript library with zero runtime dependencies for building collaborative AI workflows — model-agnostic, graph-native, and production-ready.
npm install societyaiBuilt for reliability
No black magic — solid engineering foundations designed for complex production workflows.
Model Agnostic
Compatible with OpenAI, Anthropic, Mistral, or any local model. Implement one interface — that's it.
Graph Orchestration
Native DAG and cyclic graph support. Build sequential chains, parallel fans, and feedback loops.
Zero Runtime Dependencies
The core is pure TypeScript with no runtime dependencies. Optional peer deps (OpenTelemetry, MCP, Redis, Postgres) are strictly opt-in.
Fully Type-Safe
Autocomplete your entire workflow. Catch configuration errors at compile time, not runtime.
Fluent Builder API
Express agents, roles, and task graphs in a readable, chainable API — one line per concept.
Extensible by Design
13 built-in middlewares, custom tools, structured output validation, and a plugin-ready event system.
Everything you need to ship
From quick prototypes to production-grade multi-agent pipelines.
Multi-Agent System
Define roles, personalities, and system prompts for each agent. Agents can collaborate through structured message passing, iterate over multiple rounds, or debate to reach consensus.
Flexible Workflows
Sequential, parallel, collaborative, conditional, and human-in-the-loop tasks. Combine them in any order with explicit dependency declarations or implicit sequential wiring.
Memory & Context
Built-in short-term, long-term, and entity memory systems. Share data across tasks via typed context injection. Pluggable VectorProvider interface for RAG-compatible backends.
Worker Thread Isolation
Run CPU-intensive agents in isolated Worker Threads with executionMode: 'isolated'. Prevents event loop blocking for latency-sensitive applications.
Observability & Tracing
Full event-driven system with built-in structured logger. Optional OpenTelemetry integration (peer dep) for distributed tracing. Plug in your own observer for custom monitoring.
MCP Protocol
Optional Model Context Protocol integration (peer dep) for connecting external tools — filesystem, git, search engines, and more via the standardized MCP interface.
Tools & Function Calling
Define custom tools with JSON Schema parameter validation. The ReAct pattern is natively supported — agents reason, act, and observe in a structured loop.
Structured Validation
Validate agent outputs against JSON Schema definitions. The self-correcting validator automatically retries with feedback until the output matches the expected shape.
Persistence & Recovery
Save and restore execution state mid-workflow. Built-in file adapter included; optional Redis and PostgreSQL adapters available as peer dependencies.
From config to execution in 4 steps
The SocietyExecutor transforms your high-level configuration into an optimized DAG and runs it.
Define Roles & Agents
Each agent has a role (system prompt), an AI model adapter, and a unique ID. Roles are reusable across agents.
import { Society } from 'societyai';
// Define roles inline when creating agents
const society = Society.create()
.addAgent(a => a
.withId('writer')
.withRole(r => r
.withName('Technical Writer')
.withSystemPrompt('You are an expert in concise technical writing.'))
.withModel(model))
.addAgent(a => a
.withId('editor')
.withRole(r => r
.withName('Editor in Chief')
.withSystemPrompt('You correct style and verify clarity.'))
.withModel(model));Declare Tasks & Dependencies
Use Society.create() to wire agents into tasks. Use .dependsOn() to declare explicit ordering — no magic positional assumptions.
const society = Society.create()
.withId('blog-team')
.addAgent(a => a
.withId('writer').withRole(writerRole).withModel(model))
.addAgent(a => a
.withId('editor').withRole(reviewerRole).withModel(model))
.addTask(t => t
.withId('draft').withAgents(['writer']).sequential())
.addTask(t => t
.withId('review')
.dependsOn('draft') // explicit dependency edge
.withAgents(['editor']).sequential());Graph Transformation (DAG)
Behind the scenes, SocietyExecutor converts your task declarations into an optimized execution graph. Dependency edges, conditional branches, and resolver nodes are all wired automatically.
Nodes
Agents, parallel tasks, conditions, human gates
Edges
dependsOn() declarations + implicit sequential links
Validation
Unknown agents, duplicate IDs, broken references
Execute & Collect Results
Call .execute(input) to run the entire orchestration. The result contains the final output, per-task results, message history, and execution duration.
const result = await society.execute('Write about TypeScript...');
console.log(result.output); // final aggregated output
console.log(result.taskResults); // per-task results map
console.log(result.duration); // total execution time (ms)
console.log(result.success); // booleanSix orchestration modes
Mix and match within the same workflow to model any real-world collaboration pattern.
Sequential
.sequential()Agents execute one by one, each receiving the accumulated output of the previous step as context. Ideal for linear pipelines where order and data flow matter.
Best for
- Writing & editing pipelines
- Data processing chains
- Step-by-step analysis
Step-by-step execution
13 built-in middlewares
Composable, priority-ordered middleware chain. Wrap any model or agent with logging, caching, rate limiting, and more.
Observability
logging()timing()metrics(collector)Resilience
timeout(ms)retry({ maxAttempts })cache({ ttl })rateLimit()circuitBreaker()dedupe()fallback(value)Transform
validation()transformInput(fn)transformOutput(fn)import { Society, Middlewares, MiddlewareChain } from 'societyai';
// Build a reusable middleware chain
const chain = MiddlewareChain.create()
.use(Middlewares.logging())
.use(Middlewares.timeout(30_000))
.use(Middlewares.retry({ maxAttempts: 3 }))
.use(Middlewares.cache({ ttl: 60_000 }))
.use(Middlewares.circuitBreaker({ threshold: 5 }));
// Pass the chain directly to addMiddleware()
const result = await Society.create()
.addMiddleware(chain) // ← Middleware | MiddlewareChain
.addAgent(...)
.addTask(...)
.execute('Run workflow');Hierarchical societies
Wrap an entire society as a model using EngineAsModel. Compose teams inside teams — infinite nesting depth.
How it works
- 1.Build an inner society (sub-team)
- 2.Wrap it with
EngineAsModel - 3.Use it as a regular model in an outer society
Use Cases
- Manager delegating to sub-teams
- Nested multi-stage pipelines
- Reusable domain-specific societies
- Recursive problem decomposition
import { Society, GraphBuilder, NodeType, EngineAsModel } from 'societyai';
// 1. Build the inner team graph
const codeTeamEngine = GraphBuilder.create()
.addNode('start', NodeType.START)
.addNode('coder', NodeType.AGENT, { agentId: 'coder' })
.addNode('tester', NodeType.AGENT, { agentId: 'tester' })
.addNode('end', NodeType.END)
.addEdge('start', 'coder')
.addEdge('coder', 'tester')
.addEdge('tester', 'end')
.build();
// 2. Wrap the inner engine as a model
const codeTeamModel = new EngineAsModel({
engine: codeTeamEngine,
agents: [coderAgent, testerAgent],
name: 'code-team',
});
// 3. Use the entire sub-team as a single agent in an outer society
const result = await Society.create()
.addAgent(a => a
.withId('manager')
.withRole(r => r.withSystemPrompt('You delegate to the team.'))
.withModel(codeTeamModel) // ← sub-team as a model
)
.addTask(t => t.withId('delegate').withAgents(['manager']).sequential())
.execute('Build a REST API');Two levels of abstraction
Start high-level and drop down to the graph API only when you need full control.
High-Level API
For most use cases. Simple, fluent, fast to write.
Entry Point
Society.create()Building Blocks
Task + TaskExecutionTypeBest For
Standard workflows, rapid prototyping, production pipelines
Low-Level API
For complex cases requiring full graph control.
Entry Point
GraphBuilder.create()Building Blocks
GraphNode + 10 NodeTypesBest For
Cycles, custom transforms, complex aggregations, hierarchical nesting
Tip: Always start with the High-Level API. Switch to Low-Level only when you need cycles, custom node types, or nested graph composition.
A complete example
Writer → Editor workflow in under 40 lines.
import { Society } from 'societyai';
// SocietyAI is model-agnostic — bring your own adapter (OpenAI, Anthropic, etc.)
import { MyAIModel } from './my-model-adapter';
const model = new MyAIModel(process.env.OPENAI_API_KEY);
const result = await Society.create()
.withId('blog-team')
// ── Agents ──────────────────────────────────────────────────
.addAgent(agent => agent
.withId('writer')
.withRole(role => role
.withName('Technical Writer')
.withSystemPrompt('You are an expert in concise technical writing.')
)
.withModel(model)
)
.addAgent(agent => agent
.withId('editor')
.withRole(role => role
.withName('Editor in Chief')
.withSystemPrompt('You correct style and verify clarity.')
)
.withModel(model)
)
// ── Tasks ────────────────────────────────────────────────────
.addTask(task => task
.withId('draft')
.withAgents(['writer'])
.withInstructions('Write a paragraph about the benefits of TypeScript.')
.sequential()
)
.addTask(task => task
.withId('review')
.dependsOn('draft') // explicit dependency edge
.withAgents(['editor'])
.withInstructions('Review the draft, correct mistakes, and improve tone.')
.sequential()
)
.execute('Start Project');
console.log('Result:', result.output);Get started today
Explore the documentation, source code, and npm package.