From 27c0103736d91616e8ec66042c738a9a21e73a37 Mon Sep 17 00:00:00 2001 From: JackChen Date: Fri, 3 Apr 2026 14:23:22 +0800 Subject: [PATCH] docs: add examples for structured output and task retry --- README.md | 2 + examples/10-structured-output.ts | 73 +++++++++++++++++ examples/11-task-retry.ts | 132 +++++++++++++++++++++++++++++++ 3 files changed, 207 insertions(+) create mode 100644 examples/10-structured-output.ts create mode 100644 examples/11-task-retry.ts diff --git a/README.md b/README.md index 6fee192..b8ca34f 100644 --- a/README.md +++ b/README.md @@ -125,6 +125,8 @@ npx tsx examples/01-single-agent.ts | [07 — Fan-Out / Aggregate](examples/07-fan-out-aggregate.ts) | `runParallel()` MapReduce — 3 analysts in parallel, then synthesize | | [08 — Gemma 4 Local](examples/08-gemma4-local.ts) | Pure-local Gemma 4 agent team with tool-calling — zero API cost | | [09 — Gemma 4 Auto-Orchestration](examples/09-gemma4-auto-orchestration.ts) | `runTeam()` with Gemma 4 as coordinator — auto task decomposition, fully local | +| [10 — Structured Output](examples/10-structured-output.ts) | `outputSchema` (Zod) on AgentConfig — validated JSON via `result.structured` | +| [11 — Task Retry](examples/11-task-retry.ts) | `maxRetries` / `retryDelayMs` / `retryBackoff` with `task_retry` progress events | ## Architecture diff --git a/examples/10-structured-output.ts b/examples/10-structured-output.ts new file mode 100644 index 0000000..ef20872 --- /dev/null +++ b/examples/10-structured-output.ts @@ -0,0 +1,73 @@ +/** + * Example 10 — Structured Output + * + * Demonstrates `outputSchema` on AgentConfig. The agent's response is + * automatically parsed as JSON and validated against a Zod schema. + * On validation failure, the framework retries once with error feedback. + * + * The validated result is available via `result.structured`. + * + * Run: + * npx tsx examples/10-structured-output.ts + * + * Prerequisites: + * ANTHROPIC_API_KEY env var must be set. + */ + +import { z } from 'zod' +import { OpenMultiAgent } from '../src/index.js' +import type { AgentConfig } from '../src/types.js' + +// --------------------------------------------------------------------------- +// Define a Zod schema for the expected output +// --------------------------------------------------------------------------- + +const ReviewAnalysis = z.object({ + summary: z.string().describe('One-sentence summary of the review'), + sentiment: z.enum(['positive', 'negative', 'neutral']), + confidence: z.number().min(0).max(1).describe('How confident the analysis is'), + keyTopics: z.array(z.string()).describe('Main topics mentioned in the review'), +}) + +type ReviewAnalysis = z.infer + +// --------------------------------------------------------------------------- +// Agent with outputSchema +// --------------------------------------------------------------------------- + +const analyst: AgentConfig = { + name: 'analyst', + model: 'claude-sonnet-4-6', + systemPrompt: 'You are a product review analyst. Analyze the given review and extract structured insights.', + outputSchema: ReviewAnalysis, +} + +// --------------------------------------------------------------------------- +// Run +// --------------------------------------------------------------------------- + +const orchestrator = new OpenMultiAgent({ defaultModel: 'claude-sonnet-4-6' }) + +const reviews = [ + 'This keyboard is amazing! The mechanical switches feel incredible and the RGB lighting is stunning. Build quality is top-notch. Only downside is the price.', + 'Terrible experience. The product arrived broken, customer support was unhelpful, and the return process took 3 weeks.', + 'It works fine. Nothing special, nothing bad. Does what it says on the box.', +] + +console.log('Analyzing product reviews with structured output...\n') + +for (const review of reviews) { + const result = await orchestrator.runAgent(analyst, `Analyze this review: "${review}"`) + + if (result.structured) { + const data = result.structured as ReviewAnalysis + console.log(`Sentiment: ${data.sentiment} (confidence: ${data.confidence})`) + console.log(`Summary: ${data.summary}`) + console.log(`Topics: ${data.keyTopics.join(', ')}`) + } else { + console.log(`Validation failed. Raw output: ${result.output.slice(0, 100)}`) + } + + console.log(`Tokens: ${result.tokenUsage.input_tokens} in / ${result.tokenUsage.output_tokens} out`) + console.log('---') +} diff --git a/examples/11-task-retry.ts b/examples/11-task-retry.ts new file mode 100644 index 0000000..eafd274 --- /dev/null +++ b/examples/11-task-retry.ts @@ -0,0 +1,132 @@ +/** + * Example 11 — Task Retry with Exponential Backoff + * + * Demonstrates `maxRetries`, `retryDelayMs`, and `retryBackoff` on task config. + * When a task fails, the framework automatically retries with exponential + * backoff. The `onProgress` callback receives `task_retry` events so you can + * log retry attempts in real time. + * + * Scenario: a two-step pipeline where the first task (data fetch) is configured + * to retry on failure, and the second task (analysis) depends on it. + * + * Run: + * npx tsx examples/11-task-retry.ts + * + * Prerequisites: + * ANTHROPIC_API_KEY env var must be set. + */ + +import { OpenMultiAgent } from '../src/index.js' +import type { AgentConfig, OrchestratorEvent } from '../src/types.js' + +// --------------------------------------------------------------------------- +// Agents +// --------------------------------------------------------------------------- + +const fetcher: AgentConfig = { + name: 'fetcher', + model: 'claude-sonnet-4-6', + systemPrompt: `You are a data-fetching agent. When given a topic, produce a short +JSON summary with 3-5 key facts. Output ONLY valid JSON, no markdown fences. +Example: {"topic":"...", "facts":["fact1","fact2","fact3"]}`, + maxTurns: 2, +} + +const analyst: AgentConfig = { + name: 'analyst', + model: 'claude-sonnet-4-6', + systemPrompt: `You are a data analyst. Read the fetched data from shared memory +and produce a brief analysis (3-4 sentences) highlighting trends or insights.`, + maxTurns: 2, +} + +// --------------------------------------------------------------------------- +// Progress handler — watch for task_retry events +// --------------------------------------------------------------------------- + +function handleProgress(event: OrchestratorEvent): void { + const ts = new Date().toISOString().slice(11, 23) + + switch (event.type) { + case 'task_start': + console.log(`[${ts}] TASK START "${event.task}" (agent: ${event.agent})`) + break + case 'task_complete': + console.log(`[${ts}] TASK DONE "${event.task}"`) + break + case 'task_retry': { + const d = event.data as { attempt: number; maxAttempts: number; error: string; nextDelayMs: number } + console.log(`[${ts}] TASK RETRY "${event.task}" — attempt ${d.attempt}/${d.maxAttempts}, next in ${d.nextDelayMs}ms`) + console.log(` error: ${d.error.slice(0, 120)}`) + break + } + case 'error': + console.log(`[${ts}] ERROR "${event.task}" agent=${event.agent}`) + break + } +} + +// --------------------------------------------------------------------------- +// Orchestrator + team +// --------------------------------------------------------------------------- + +const orchestrator = new OpenMultiAgent({ + defaultModel: 'claude-sonnet-4-6', + onProgress: handleProgress, +}) + +const team = orchestrator.createTeam('retry-demo', { + name: 'retry-demo', + agents: [fetcher, analyst], + sharedMemory: true, +}) + +// --------------------------------------------------------------------------- +// Tasks — fetcher has retry config, analyst depends on it +// --------------------------------------------------------------------------- + +const tasks = [ + { + title: 'Fetch data', + description: 'Fetch key facts about the adoption of TypeScript in open-source projects as of 2024. Output a JSON object with a "topic" and "facts" array.', + assignee: 'fetcher', + // Retry config: up to 2 retries, 500ms base delay, 2x backoff (500ms, 1000ms) + maxRetries: 2, + retryDelayMs: 500, + retryBackoff: 2, + }, + { + title: 'Analyze data', + description: 'Read the fetched data from shared memory and produce a 3-4 sentence analysis of TypeScript adoption trends.', + assignee: 'analyst', + dependsOn: ['Fetch data'], + // No retry — if analysis fails, just report the error + }, +] + +// --------------------------------------------------------------------------- +// Run +// --------------------------------------------------------------------------- + +console.log('Task Retry Example') +console.log('='.repeat(60)) +console.log('Pipeline: fetch (with retry) → analyze') +console.log(`Retry config: maxRetries=2, delay=500ms, backoff=2x`) +console.log('='.repeat(60)) +console.log() + +const result = await orchestrator.runTasks(team, tasks) + +// --------------------------------------------------------------------------- +// Summary +// --------------------------------------------------------------------------- + +console.log('\n' + '='.repeat(60)) +console.log(`Overall success: ${result.success}`) +console.log(`Tokens — input: ${result.totalTokenUsage.input_tokens}, output: ${result.totalTokenUsage.output_tokens}`) + +for (const [name, r] of result.agentResults) { + const icon = r.success ? 'OK ' : 'FAIL' + console.log(` [${icon}] ${name}`) + console.log(` ${r.output.slice(0, 200)}`) +}