Skip to content

Commit 997cbc9

Browse files
committed
docs(agency): add graph strategy docs, README, and example
- AGENCY_API.md: new ### graph section with dependsOn, auto-detection, topo sort tiers, concurrent execution, cycle detection, full example - README.md: add graph row to strategy table + 9-line code example - examples/agency-graph.mjs: runnable 4-agent research team DAG with both generate() and streaming examples
1 parent a550131 commit 997cbc9

3 files changed

Lines changed: 194 additions & 0 deletions

File tree

README.md

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -186,6 +186,22 @@ provider. All five strategies are available out of the box:
186186
| `debate` | Agents argue and refine a shared answer over multiple rounds |
187187
| `review-loop` | One agent drafts, another reviews and requests revisions |
188188
| `hierarchical` | A coordinator dispatches sub-tasks to specialist agents at runtime |
189+
| `graph` | Explicit dependency DAG via `dependsOn`; tiers run concurrently |
190+
191+
**Graph strategy** — declare dependencies between agents and let the orchestrator
192+
handle topological ordering and concurrent execution:
193+
194+
```typescript
195+
const team = agency({
196+
agents: {
197+
researcher: { instructions: 'Research the topic.' },
198+
writer: { instructions: 'Write an article.', dependsOn: ['researcher'] },
199+
illustrator: { instructions: 'Describe illustrations.', dependsOn: ['researcher'] },
200+
reviewer: { instructions: 'Review everything.', dependsOn: ['writer', 'illustrator'] },
201+
},
202+
strategy: 'graph', // auto-detected when any agent has dependsOn
203+
});
204+
```
189205

190206
Add guardrails, HITL, resource controls, observability, `listen()` for voice
191207
transport, `connect()` for channel adapters, RAG context injection, and real

docs/AGENCY_API.md

Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -186,6 +186,86 @@ const team = agency({
186186
const { text } = await team.generate('Explain and demonstrate the quicksort algorithm.');
187187
```
188188

189+
### graph
190+
191+
Agents declare explicit dependencies via `dependsOn`. The orchestrator
192+
topologically sorts agents into tiers and runs each tier concurrently. Every
193+
agent receives the original user prompt plus the concatenated plain-text outputs
194+
of its direct dependencies.
195+
196+
**Auto-detection:** when _any_ agent in the roster has a `dependsOn` array, the
197+
strategy is automatically set to `'graph'` — you don't need to specify it
198+
explicitly (though doing so is fine).
199+
200+
**Cycle detection:** the orchestrator validates the dependency DAG at
201+
construction time and throws if it contains a cycle.
202+
203+
**Context passing:** each agent's prompt is assembled as:
204+
205+
```
206+
<original user prompt>
207+
208+
--- Output from <dependencyName> ---
209+
<plain text output>
210+
```
211+
212+
There is no expression language (no `$steps.<name>` references). Each agent
213+
simply receives plain text from its predecessors.
214+
215+
#### Agent config — `dependsOn`
216+
217+
| Option | Type | Default | Description |
218+
|---|---|---|---|
219+
| `dependsOn` | `string[]` | `[]` | Names of agents in the same agency that must complete before this agent runs. Agents with no `dependsOn` are roots and execute first. |
220+
221+
#### Full example — research team
222+
223+
```typescript
224+
const team = agency({
225+
model: 'openai:gpt-4o',
226+
agents: {
227+
// Tier 0 — no dependencies, runs first
228+
researcher: {
229+
instructions: 'Research the topic thoroughly. Provide facts, statistics, and sources.',
230+
},
231+
232+
// Tier 1 — both depend on researcher, run concurrently
233+
writer: {
234+
instructions: 'Write a polished article based on the research provided.',
235+
dependsOn: ['researcher'],
236+
},
237+
illustrator: {
238+
instructions: 'Describe 3 illustrations that would complement the article.',
239+
dependsOn: ['researcher'],
240+
},
241+
242+
// Tier 2 — depends on both writer and illustrator, runs last
243+
reviewer: {
244+
instructions: 'Review the article and illustrations for consistency and accuracy.',
245+
dependsOn: ['writer', 'illustrator'],
246+
},
247+
},
248+
strategy: 'graph', // optional — auto-detected from dependsOn
249+
});
250+
251+
const { text, agentCalls } = await team.generate('Write about the James Webb Space Telescope.');
252+
console.log(text);
253+
console.log(agentCalls.map(c => `${c.agent} (${c.durationMs}ms)`));
254+
// researcher (2100ms)
255+
// writer (1800ms) — ran concurrently with illustrator
256+
// illustrator (1200ms) — ran concurrently with writer
257+
// reviewer (1500ms)
258+
```
259+
260+
#### Streaming
261+
262+
```typescript
263+
const stream = team.stream('Write about the James Webb Space Telescope.');
264+
for await (const chunk of stream.textStream) {
265+
process.stdout.write(chunk);
266+
}
267+
```
268+
189269
---
190270

191271
## Adaptive Mode

examples/agency-graph.mjs

Lines changed: 98 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,98 @@
1+
#!/usr/bin/env node
2+
// Example: agency() with graph strategy — explicit agent dependencies
3+
//
4+
// The graph strategy topologically sorts agents into tiers based on their
5+
// `dependsOn` declarations and executes each tier concurrently. Every agent
6+
// receives the original prompt plus the plain-text outputs of its dependencies.
7+
//
8+
// DAG for this example:
9+
//
10+
// researcher (tier 0)
11+
// |
12+
// +---+---+
13+
// | |
14+
// writer illustrator (tier 1 — concurrent)
15+
// | |
16+
// +---+---+
17+
// |
18+
// reviewer (tier 2)
19+
//
20+
// Usage:
21+
// export OPENAI_API_KEY="sk-..."
22+
// node examples/agency-graph.mjs
23+
24+
import { agency } from '../dist/index.js';
25+
26+
const provider = process.env.AGENTOS_PROVIDER || 'openai';
27+
28+
async function main() {
29+
const team = agency({
30+
provider,
31+
agents: {
32+
// Tier 0 — no dependencies, runs first
33+
researcher: {
34+
instructions:
35+
'You are a meticulous researcher. Given a topic, gather key facts, ' +
36+
'statistics, and credible sources. Output a structured research brief.',
37+
},
38+
39+
// Tier 1 — both depend on researcher, run concurrently
40+
writer: {
41+
instructions:
42+
'You are a skilled technical writer. Using the research brief provided, ' +
43+
'write a clear, engaging article of roughly 300 words.',
44+
dependsOn: ['researcher'],
45+
},
46+
illustrator: {
47+
instructions:
48+
'You are a visual designer. Using the research brief provided, describe ' +
49+
'3 illustrations that would complement the article. For each, give a ' +
50+
'title and a one-sentence description.',
51+
dependsOn: ['researcher'],
52+
},
53+
54+
// Tier 2 — depends on both writer and illustrator, runs last
55+
reviewer: {
56+
instructions:
57+
'You are a senior editor. Review the article and the illustration ' +
58+
'descriptions for factual accuracy, consistency, and completeness. ' +
59+
'Output a final verdict with any suggested corrections.',
60+
dependsOn: ['writer', 'illustrator'],
61+
},
62+
},
63+
strategy: 'graph', // optional — auto-detected when any agent has dependsOn
64+
});
65+
66+
// --- Non-streaming run ------------------------------------------------
67+
console.log('=== agency() graph strategy — generate() ===\n');
68+
69+
const result = await team.generate(
70+
'Explain the significance of the James Webb Space Telescope.',
71+
);
72+
73+
console.log(result.text);
74+
console.log('\n--- Agent calls ---');
75+
for (const call of result.agentCalls) {
76+
console.log(` ${call.agent}${call.durationMs}ms`);
77+
}
78+
console.log(`\nTotal tokens: ${result.usage.totalTokens}`);
79+
80+
// --- Streaming run ----------------------------------------------------
81+
console.log('\n=== agency() graph strategy — stream() ===\n');
82+
83+
const stream = team.stream(
84+
'Summarise recent breakthroughs in quantum computing.',
85+
);
86+
87+
for await (const chunk of stream.textStream) {
88+
process.stdout.write(chunk);
89+
}
90+
process.stdout.write('\n');
91+
92+
await team.close();
93+
}
94+
95+
main().catch((error) => {
96+
console.error(error);
97+
process.exitCode = 1;
98+
});

0 commit comments

Comments
 (0)