diff --git a/README.md b/README.md index a74723f..e1871d1 100644 --- a/README.md +++ b/README.md @@ -122,6 +122,7 @@ npx tsx examples/01-single-agent.ts | [06 — Local Model](examples/06-local-model.ts) | Ollama + Claude in one pipeline via `baseURL` (works with vLLM, LM Studio, etc.) | | [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 | ## Architecture diff --git a/README_zh.md b/README_zh.md index 76c5d2f..362d0c5 100644 --- a/README_zh.md +++ b/README_zh.md @@ -126,6 +126,7 @@ npx tsx examples/01-single-agent.ts | [06 — 本地模型](examples/06-local-model.ts) | Ollama + Claude 混合流水线,通过 `baseURL` 接入(兼容 vLLM、LM Studio 等) | | [07 — 扇出聚合](examples/07-fan-out-aggregate.ts) | `runParallel()` MapReduce — 3 个分析师并行,然后综合 | | [08 — Gemma 4 本地](examples/08-gemma4-local.ts) | 纯本地 Gemma 4 智能体团队 + tool-calling — 零 API 费用 | +| [09 — Gemma 4 自动编排](examples/09-gemma4-auto-orchestration.ts) | `runTeam()` 用 Gemma 4 当 coordinator — 自动任务拆解,完全本地 | ## 架构 diff --git a/examples/09-gemma4-auto-orchestration.ts b/examples/09-gemma4-auto-orchestration.ts new file mode 100644 index 0000000..185ede7 --- /dev/null +++ b/examples/09-gemma4-auto-orchestration.ts @@ -0,0 +1,162 @@ +/** + * Example 09 — Gemma 4 Auto-Orchestration (runTeam, 100% Local) + * + * Demonstrates the framework's key feature — automatic task decomposition — + * powered entirely by a local Gemma 4 model. No cloud API needed. + * + * What happens: + * 1. A Gemma 4 "coordinator" receives the goal + agent roster + * 2. It outputs a structured JSON task array (title, description, assignee, dependsOn) + * 3. The framework resolves dependencies, schedules tasks, and runs agents + * 4. The coordinator synthesises all task results into a final answer + * + * This is the hardest test for a local model — it must produce valid JSON + * for task decomposition AND do tool-calling for actual task execution. + * Gemma 4 e2b (5.1B params) handles both reliably. + * + * Run: + * no_proxy=localhost npx tsx examples/09-gemma4-auto-orchestration.ts + * + * Prerequisites: + * 1. Ollama >= 0.20.0 installed and running: https://ollama.com + * 2. Pull the model: ollama pull gemma4:e2b + * 3. No API keys needed! + * + * Note: The no_proxy=localhost prefix is needed if you have an HTTP proxy + * configured, since the OpenAI SDK would otherwise route Ollama requests + * through the proxy. + */ + +import { OpenMultiAgent } from '../src/index.js' +import type { AgentConfig, OrchestratorEvent, Task } from '../src/types.js' + +// --------------------------------------------------------------------------- +// Configuration +// --------------------------------------------------------------------------- + +// See available tags at https://ollama.com/library/gemma4 +const OLLAMA_MODEL = 'gemma4:e2b' // or 'gemma4:e4b', 'gemma4:26b' +const OLLAMA_BASE_URL = 'http://localhost:11434/v1' + +// --------------------------------------------------------------------------- +// Agents — the coordinator is created automatically by runTeam() +// --------------------------------------------------------------------------- + +const researcher: AgentConfig = { + name: 'researcher', + model: OLLAMA_MODEL, + provider: 'openai', + baseURL: OLLAMA_BASE_URL, + apiKey: 'ollama', + systemPrompt: `You are a system researcher. Use bash to run non-destructive, +read-only commands and report the results concisely.`, + tools: ['bash'], + maxTurns: 4, +} + +const writer: AgentConfig = { + name: 'writer', + model: OLLAMA_MODEL, + provider: 'openai', + baseURL: OLLAMA_BASE_URL, + apiKey: 'ollama', + systemPrompt: `You are a technical writer. Use file_write to create clear, +structured Markdown reports based on the information provided.`, + tools: ['file_write'], + maxTurns: 4, +} + +// --------------------------------------------------------------------------- +// Progress handler +// --------------------------------------------------------------------------- + +function handleProgress(event: OrchestratorEvent): void { + const ts = new Date().toISOString().slice(11, 23) + switch (event.type) { + case 'task_start': { + const task = event.data as Task | undefined + console.log(`[${ts}] TASK START "${task?.title ?? event.task}" → ${task?.assignee ?? '?'}`) + break + } + case 'task_complete': + console.log(`[${ts}] TASK DONE "${event.task}"`) + break + case 'agent_start': + console.log(`[${ts}] AGENT START ${event.agent}`) + break + case 'agent_complete': + console.log(`[${ts}] AGENT DONE ${event.agent}`) + break + case 'error': + console.error(`[${ts}] ERROR ${event.agent ?? ''} task=${event.task ?? '?'}`) + break + } +} + +// --------------------------------------------------------------------------- +// Orchestrator — defaultModel is used for the coordinator agent +// --------------------------------------------------------------------------- + +const orchestrator = new OpenMultiAgent({ + defaultModel: OLLAMA_MODEL, + defaultProvider: 'openai', + defaultBaseURL: OLLAMA_BASE_URL, + defaultApiKey: 'ollama', + maxConcurrency: 1, // local model serves one request at a time + onProgress: handleProgress, +}) + +const team = orchestrator.createTeam('gemma4-auto', { + name: 'gemma4-auto', + agents: [researcher, writer], + sharedMemory: true, +}) + +// --------------------------------------------------------------------------- +// Give a goal — the framework handles the rest +// --------------------------------------------------------------------------- + +const goal = `Check this machine's Node.js version, npm version, and OS info, +then write a short Markdown summary report to /tmp/gemma4-auto/report.md` + +console.log('Gemma 4 Auto-Orchestration — Zero API Cost') +console.log('='.repeat(60)) +console.log(` model → ${OLLAMA_MODEL} via Ollama (all agents + coordinator)`) +console.log(` researcher → bash`) +console.log(` writer → file_write`) +console.log(` coordinator → auto-created by runTeam()`) +console.log() +console.log(`Goal: ${goal.replace(/\n/g, ' ').trim()}`) +console.log('='.repeat(60)) + +const start = Date.now() +const result = await orchestrator.runTeam(team, goal) +const totalTime = Date.now() - start + +// --------------------------------------------------------------------------- +// Results +// --------------------------------------------------------------------------- + +console.log('\n' + '='.repeat(60)) +console.log('Pipeline complete.\n') +console.log(`Overall success: ${result.success}`) +console.log(`Total time: ${(totalTime / 1000).toFixed(1)}s`) +console.log(`Tokens — input: ${result.totalTokenUsage.input_tokens}, output: ${result.totalTokenUsage.output_tokens}`) + +console.log('\nPer-agent results:') +for (const [name, r] of result.agentResults) { + const icon = r.success ? 'OK ' : 'FAIL' + const tools = r.toolCalls.length > 0 ? r.toolCalls.map(c => c.toolName).join(', ') : '(none)' + console.log(` [${icon}] ${name.padEnd(24)} tools: ${tools}`) +} + +// Print the coordinator's final synthesis +const coordResult = result.agentResults.get('coordinator') +if (coordResult?.success) { + console.log('\nFinal synthesis (from local Gemma 4 coordinator):') + console.log('-'.repeat(60)) + console.log(coordResult.output) + console.log('-'.repeat(60)) +} + +console.log('\nAll processing done locally. $0 API cost.')