From 31a0fa495110a4f3a8adf93f28aa782d00dc724e Mon Sep 17 00:00:00 2001 From: JackChen Date: Fri, 3 Apr 2026 02:12:05 +0800 Subject: [PATCH 1/4] docs: add examples for local models (Ollama) and fan-out/aggregate pattern - 06-local-model.ts: mix Ollama (local) + Claude (cloud) in a runTasks pipeline, demonstrating baseURL and apiKey placeholder for OpenAI-compatible servers - 07-fan-out-aggregate.ts: MapReduce pattern using AgentPool.runParallel() to fan out analysis to 3 perspective agents, then aggregate via a synthesizer --- examples/06-local-model.ts | 199 +++++++++++++++++++++++++++++ examples/07-fan-out-aggregate.ts | 209 +++++++++++++++++++++++++++++++ 2 files changed, 408 insertions(+) create mode 100644 examples/06-local-model.ts create mode 100644 examples/07-fan-out-aggregate.ts diff --git a/examples/06-local-model.ts b/examples/06-local-model.ts new file mode 100644 index 0000000..d7cf292 --- /dev/null +++ b/examples/06-local-model.ts @@ -0,0 +1,199 @@ +/** + * Example 06 — Local Model + Cloud Model Team (Ollama + Claude) + * + * Demonstrates mixing a local model served by Ollama with a cloud model + * (Claude) in the same task pipeline. The key technique is using + * `provider: 'openai'` with a custom `baseURL` pointing at Ollama's + * OpenAI-compatible endpoint. + * + * This pattern works with ANY OpenAI-compatible local server: + * - Ollama → http://localhost:11434/v1 + * - vLLM → http://localhost:8000/v1 + * - LM Studio → http://localhost:1234/v1 + * - llama.cpp → http://localhost:8080/v1 + * Just change the baseURL and model name below. + * + * Run: + * npx tsx examples/06-local-model.ts + * + * Prerequisites: + * 1. Ollama installed and running: https://ollama.com + * 2. Pull the model: ollama pull llama3.1 + * 3. ANTHROPIC_API_KEY env var must be set. + */ + +import { OpenMultiAgent } from '../src/index.js' +import type { AgentConfig, OrchestratorEvent, Task } from '../src/types.js' + +// --------------------------------------------------------------------------- +// Agents +// --------------------------------------------------------------------------- + +/** + * Coder — uses Claude (Anthropic) for high-quality code generation. + */ +const coder: AgentConfig = { + name: 'coder', + model: 'claude-sonnet-4-6', + provider: 'anthropic', + systemPrompt: `You are a senior TypeScript developer. Write clean, well-typed, +production-quality code. Use the tools to write files to /tmp/local-model-demo/. +Always include brief JSDoc comments on exported functions.`, + tools: ['bash', 'file_write'], + maxTurns: 6, +} + +/** + * Reviewer — uses a local Ollama model via the OpenAI-compatible API. + * The apiKey is required by the OpenAI SDK but Ollama ignores it, + * so we pass the placeholder string 'ollama'. + */ +const reviewer: AgentConfig = { + name: 'reviewer', + model: 'llama3.1', + provider: 'openai', // 'openai' here means "OpenAI-compatible protocol", not the OpenAI cloud + baseURL: 'http://localhost:11434/v1', + apiKey: 'ollama', + systemPrompt: `You are a code reviewer. You read source files and produce a structured review. +Your review MUST include these sections: +- Summary (2-3 sentences) +- Strengths (bullet list) +- Issues (bullet list — or "None found" if the code is clean) +- Verdict: SHIP or NEEDS WORK + +Be specific and constructive. Reference line numbers or function names when possible.`, + tools: ['file_read'], + maxTurns: 4, +} + +// --------------------------------------------------------------------------- +// Progress handler +// --------------------------------------------------------------------------- + +const taskTimes = new Map() + +function handleProgress(event: OrchestratorEvent): void { + const ts = new Date().toISOString().slice(11, 23) + + switch (event.type) { + case 'task_start': { + taskTimes.set(event.task ?? '', Date.now()) + const task = event.data as Task | undefined + console.log(`[${ts}] TASK READY "${task?.title ?? event.task}" → ${task?.assignee ?? '?'}`) + break + } + case 'task_complete': { + const elapsed = Date.now() - (taskTimes.get(event.task ?? '') ?? Date.now()) + console.log(`[${ts}] TASK DONE task=${event.task} in ${elapsed}ms`) + 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 + Team +// --------------------------------------------------------------------------- + +const orchestrator = new OpenMultiAgent({ + defaultModel: 'claude-sonnet-4-6', + maxConcurrency: 2, + onProgress: handleProgress, +}) + +const team = orchestrator.createTeam('local-cloud-team', { + name: 'local-cloud-team', + agents: [coder, reviewer], + sharedMemory: true, +}) + +// --------------------------------------------------------------------------- +// Task pipeline: code → review +// --------------------------------------------------------------------------- + +const OUTPUT_DIR = '/tmp/local-model-demo' + +const tasks: Array<{ + title: string + description: string + assignee?: string + dependsOn?: string[] +}> = [ + { + title: 'Write: retry utility', + description: `Write a small but complete TypeScript utility to ${OUTPUT_DIR}/retry.ts. + +The module should export: +1. A \`RetryOptions\` interface with: maxRetries (number), delayMs (number), + backoffFactor (optional number, default 2), shouldRetry (optional predicate + taking the error and returning boolean). +2. An async \`retry(fn: () => Promise, options: RetryOptions): Promise\` + function that retries \`fn\` with exponential backoff. +3. A convenience \`withRetry\` wrapper that returns a new function with retry + behaviour baked in. + +Include JSDoc comments. No external dependencies — use only Node built-ins. +After writing the file, also create a small test script at ${OUTPUT_DIR}/retry-test.ts +that exercises the happy path and a failure case, then run it with \`npx tsx\`.`, + assignee: 'coder', + }, + { + title: 'Review: retry utility', + description: `Read the files at ${OUTPUT_DIR}/retry.ts and ${OUTPUT_DIR}/retry-test.ts. + +Produce a structured code review covering: +- Summary (2-3 sentences describing the module) +- Strengths (bullet list) +- Issues (bullet list — be specific about what and why) +- Verdict: SHIP or NEEDS WORK`, + assignee: 'reviewer', + dependsOn: ['Write: retry utility'], + }, +] + +// --------------------------------------------------------------------------- +// Run +// --------------------------------------------------------------------------- + +console.log('Local + Cloud model team') +console.log(` coder → Claude (${coder.model}) via Anthropic API`) +console.log(` reviewer → Ollama (${reviewer.model}) at ${reviewer.baseURL}`) +console.log() +console.log('Pipeline: coder writes code → local model reviews it') +console.log('='.repeat(60)) + +const result = await orchestrator.runTasks(team, tasks) + +// --------------------------------------------------------------------------- +// Summary +// --------------------------------------------------------------------------- + +console.log('\n' + '='.repeat(60)) +console.log('Pipeline complete.\n') +console.log(`Overall success: ${result.success}`) +console.log(`Tokens — input: ${result.totalTokenUsage.input_tokens}, output: ${result.totalTokenUsage.output_tokens}`) + +console.log('\nPer-agent summary:') +for (const [name, r] of result.agentResults) { + const icon = r.success ? 'OK ' : 'FAIL' + const provider = name === 'coder' ? 'anthropic' : 'ollama (local)' + const tools = r.toolCalls.map(c => c.toolName).join(', ') + console.log(` [${icon}] ${name.padEnd(10)} (${provider.padEnd(16)}) tools: ${tools || '(none)'}`) +} + +// Print the reviewer's output +const review = result.agentResults.get('reviewer') +if (review?.success) { + console.log('\nCode review (from local model):') + console.log('─'.repeat(60)) + console.log(review.output) + console.log('─'.repeat(60)) +} diff --git a/examples/07-fan-out-aggregate.ts b/examples/07-fan-out-aggregate.ts new file mode 100644 index 0000000..43b2c32 --- /dev/null +++ b/examples/07-fan-out-aggregate.ts @@ -0,0 +1,209 @@ +/** + * Example 07 — Fan-Out / Aggregate (MapReduce) Pattern + * + * Demonstrates: + * - Fan-out: send the same question to N "analyst" agents in parallel + * - Aggregate: a "synthesizer" agent reads all analyst outputs and produces + * a balanced final report + * - AgentPool with runParallel() for concurrent fan-out + * - No tools needed — pure LLM reasoning to keep the focus on the pattern + * + * Run: + * npx tsx examples/07-fan-out-aggregate.ts + * + * Prerequisites: + * ANTHROPIC_API_KEY env var must be set. + */ + +import { Agent, AgentPool, ToolRegistry, ToolExecutor, registerBuiltInTools } from '../src/index.js' +import type { AgentConfig, AgentRunResult } from '../src/types.js' + +// --------------------------------------------------------------------------- +// Analysis topic +// --------------------------------------------------------------------------- + +const TOPIC = `Should a solo developer build a SaaS product that uses AI agents +for automated customer support? Consider the current state of AI technology, +market demand, competition, costs, and the unique constraints of being a solo +founder with limited time (~6 hours/day of productive work).` + +// --------------------------------------------------------------------------- +// Analyst agent configs — three perspectives on the same question +// --------------------------------------------------------------------------- + +const optimistConfig: AgentConfig = { + name: 'optimist', + model: 'claude-sonnet-4-6', + systemPrompt: `You are an optimistic technology analyst who focuses on +opportunities, upside potential, and emerging trends. You see possibilities +where others see obstacles. Back your optimism with concrete reasoning — +cite market trends, cost curves, and real capabilities. Keep your analysis +to 200-300 words.`, + maxTurns: 1, + temperature: 0.4, +} + +const skepticConfig: AgentConfig = { + name: 'skeptic', + model: 'claude-sonnet-4-6', + systemPrompt: `You are a skeptical technology analyst who focuses on risks, +challenges, failure modes, and hidden costs. You stress-test assumptions and +ask "what could go wrong?" Back your skepticism with concrete reasoning — +cite failure rates, technical limitations, and market realities. Keep your +analysis to 200-300 words.`, + maxTurns: 1, + temperature: 0.4, +} + +const pragmatistConfig: AgentConfig = { + name: 'pragmatist', + model: 'claude-sonnet-4-6', + systemPrompt: `You are a pragmatic technology analyst who focuses on practical +feasibility, execution complexity, and resource requirements. You care about +what works today, not what might work someday. You think in terms of MVPs, +timelines, and concrete tradeoffs. Keep your analysis to 200-300 words.`, + maxTurns: 1, + temperature: 0.4, +} + +const synthesizerConfig: AgentConfig = { + name: 'synthesizer', + model: 'claude-sonnet-4-6', + systemPrompt: `You are a senior strategy advisor who synthesizes multiple +perspectives into a balanced, actionable recommendation. You do not simply +summarise — you weigh the arguments, identify where they agree and disagree, +and produce a clear verdict with next steps. Structure your output as: + +1. Key agreements across perspectives +2. Key disagreements and how you weigh them +3. Verdict (go / no-go / conditional go) +4. Recommended next steps (3-5 bullet points) + +Keep the final report to 300-400 words.`, + maxTurns: 1, + temperature: 0.3, +} + +// --------------------------------------------------------------------------- +// Build agents — no tools needed for pure reasoning +// --------------------------------------------------------------------------- + +function buildAgent(config: AgentConfig): Agent { + const registry = new ToolRegistry() + registerBuiltInTools(registry) // not needed here, but safe if tools are added later + const executor = new ToolExecutor(registry) + return new Agent(config, registry, executor) +} + +const optimist = buildAgent(optimistConfig) +const skeptic = buildAgent(skepticConfig) +const pragmatist = buildAgent(pragmatistConfig) +const synthesizer = buildAgent(synthesizerConfig) + +// --------------------------------------------------------------------------- +// Set up the pool +// --------------------------------------------------------------------------- + +const pool = new AgentPool(3) // 3 analysts can run simultaneously +pool.add(optimist) +pool.add(skeptic) +pool.add(pragmatist) +pool.add(synthesizer) + +console.log('Fan-Out / Aggregate (MapReduce) Pattern') +console.log('='.repeat(60)) +console.log(`\nTopic: ${TOPIC.replace(/\n/g, ' ').trim()}\n`) + +// --------------------------------------------------------------------------- +// Step 1: Fan-out — run all 3 analysts in parallel +// --------------------------------------------------------------------------- + +console.log('[Step 1] Fan-out: 3 analysts running in parallel...\n') + +const analystResults: Map = await pool.runParallel([ + { agent: 'optimist', prompt: TOPIC }, + { agent: 'skeptic', prompt: TOPIC }, + { agent: 'pragmatist', prompt: TOPIC }, +]) + +// Print each analyst's output (truncated) +const analysts = ['optimist', 'skeptic', 'pragmatist'] as const +for (const name of analysts) { + const result = analystResults.get(name)! + const status = result.success ? 'OK' : 'FAILED' + console.log(` ${name} [${status}] — ${result.tokenUsage.output_tokens} output tokens`) + console.log(` ${result.output.slice(0, 150).replace(/\n/g, ' ')}...`) + console.log() +} + +// Check all analysts succeeded +for (const name of analysts) { + if (!analystResults.get(name)!.success) { + console.error(`Analyst '${name}' failed: ${analystResults.get(name)!.output}`) + process.exit(1) + } +} + +// --------------------------------------------------------------------------- +// Step 2: Aggregate — synthesizer reads all 3 analyses +// --------------------------------------------------------------------------- + +console.log('[Step 2] Aggregate: synthesizer producing final report...\n') + +const synthesizerPrompt = `Three analysts have independently evaluated the same question. +Read their analyses below and produce your synthesis report. + +--- OPTIMIST --- +${analystResults.get('optimist')!.output} + +--- SKEPTIC --- +${analystResults.get('skeptic')!.output} + +--- PRAGMATIST --- +${analystResults.get('pragmatist')!.output} + +Now synthesize these three perspectives into a balanced recommendation.` + +const synthResult = await pool.run('synthesizer', synthesizerPrompt) + +if (!synthResult.success) { + console.error('Synthesizer failed:', synthResult.output) + process.exit(1) +} + +// --------------------------------------------------------------------------- +// Final output +// --------------------------------------------------------------------------- + +console.log('='.repeat(60)) +console.log('SYNTHESIZED REPORT') +console.log('='.repeat(60)) +console.log() +console.log(synthResult.output) +console.log() +console.log('-'.repeat(60)) + +// --------------------------------------------------------------------------- +// Token usage comparison +// --------------------------------------------------------------------------- + +console.log('\nToken Usage Summary:') +console.log('-'.repeat(60)) + +let totalInput = 0 +let totalOutput = 0 + +for (const name of analysts) { + const r = analystResults.get(name)! + totalInput += r.tokenUsage.input_tokens + totalOutput += r.tokenUsage.output_tokens + console.log(` ${name.padEnd(12)} — input: ${r.tokenUsage.input_tokens}, output: ${r.tokenUsage.output_tokens}`) +} + +totalInput += synthResult.tokenUsage.input_tokens +totalOutput += synthResult.tokenUsage.output_tokens +console.log(` ${'synthesizer'.padEnd(12)} — input: ${synthResult.tokenUsage.input_tokens}, output: ${synthResult.tokenUsage.output_tokens}`) +console.log('-'.repeat(60)) +console.log(` ${'TOTAL'.padEnd(12)} — input: ${totalInput}, output: ${totalOutput}`) + +console.log('\nDone.') From 94cccf24d737e094659faa6fce9a0f3192cc4aa3 Mon Sep 17 00:00:00 2001 From: JackChen Date: Fri, 3 Apr 2026 02:18:58 +0800 Subject: [PATCH 2/4] docs: replace inline code examples with examples/ index table MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Remove ~160 lines of duplicated code snippets from both READMEs. Link to the runnable scripts in examples/ instead — single source of truth, type-checked by npm run lint. --- README.md | 168 ++++----------------------------------------------- README_zh.md | 168 ++++----------------------------------------------- 2 files changed, 26 insertions(+), 310 deletions(-) diff --git a/README.md b/README.md index 34b0a1b..12340be 100644 --- a/README.md +++ b/README.md @@ -104,165 +104,23 @@ Tokens: 12847 output tokens -## More Examples +## Examples -
-Single Agent — one agent, one prompt +All examples are runnable scripts in [`examples/`](./examples/). Run any of them with `npx tsx`: -```typescript -import { OpenMultiAgent } from '@jackchen_me/open-multi-agent' - -const orchestrator = new OpenMultiAgent({ defaultModel: 'claude-sonnet-4-6' }) - -const result = await orchestrator.runAgent( - { - name: 'coder', - model: 'claude-sonnet-4-6', - tools: ['bash', 'file_write'], - }, - 'Write a TypeScript function that reverses a string, save it to /tmp/reverse.ts, and run it.', -) - -console.log(result.output) +```bash +npx tsx examples/01-single-agent.ts ``` -
- -
-Task Pipeline — explicit control over task graph and assignments - -```typescript -const result = await orchestrator.runTasks(team, [ - { - title: 'Design the data model', - description: 'Write a TypeScript interface spec to /tmp/spec.md', - assignee: 'architect', - }, - { - title: 'Implement the module', - description: 'Read /tmp/spec.md and implement the module in /tmp/src/', - assignee: 'developer', - dependsOn: ['Design the data model'], // blocked until design completes - }, - { - title: 'Write tests', - description: 'Read the implementation and write Vitest tests.', - assignee: 'developer', - dependsOn: ['Implement the module'], - }, - { - title: 'Review code', - description: 'Review /tmp/src/ and produce a structured code review.', - assignee: 'reviewer', - dependsOn: ['Implement the module'], // can run in parallel with tests - }, -]) -``` - -
- -
-Custom Tools — define tools with Zod schemas - -```typescript -import { z } from 'zod' -import { defineTool, Agent, ToolRegistry, ToolExecutor, registerBuiltInTools } from '@jackchen_me/open-multi-agent' - -const searchTool = defineTool({ - name: 'web_search', - description: 'Search the web and return the top results.', - inputSchema: z.object({ - query: z.string().describe('The search query.'), - maxResults: z.number().optional().describe('Number of results (default 5).'), - }), - execute: async ({ query, maxResults = 5 }) => { - const results = await mySearchProvider(query, maxResults) - return { data: JSON.stringify(results), isError: false } - }, -}) - -const registry = new ToolRegistry() -registerBuiltInTools(registry) -registry.register(searchTool) - -const executor = new ToolExecutor(registry) -const agent = new Agent( - { name: 'researcher', model: 'claude-sonnet-4-6', tools: ['web_search'] }, - registry, - executor, -) - -const result = await agent.run('Find the three most recent TypeScript releases.') -``` - -
- -
-Multi-Model Teams — mix Claude, GPT, and local models in one workflow - -```typescript -const claudeAgent: AgentConfig = { - name: 'strategist', - model: 'claude-opus-4-6', - provider: 'anthropic', - systemPrompt: 'You plan high-level approaches.', - tools: ['file_write'], -} - -const gptAgent: AgentConfig = { - name: 'implementer', - model: 'gpt-5.4', - provider: 'openai', - systemPrompt: 'You implement plans as working code.', - tools: ['bash', 'file_read', 'file_write'], -} - -// Any OpenAI-compatible API — Ollama, vLLM, LM Studio, etc. -const localAgent: AgentConfig = { - name: 'reviewer', - model: 'llama3.1', - provider: 'openai', - baseURL: 'http://localhost:11434/v1', - apiKey: 'ollama', - systemPrompt: 'You review code for correctness and clarity.', - tools: ['file_read', 'grep'], -} - -const team = orchestrator.createTeam('mixed-team', { - name: 'mixed-team', - agents: [claudeAgent, gptAgent, localAgent], - sharedMemory: true, -}) - -const result = await orchestrator.runTeam(team, 'Build a CLI tool that converts JSON to CSV.') -``` - -
- -
-Streaming Output - -```typescript -import { Agent, ToolRegistry, ToolExecutor, registerBuiltInTools } from '@jackchen_me/open-multi-agent' - -const registry = new ToolRegistry() -registerBuiltInTools(registry) -const executor = new ToolExecutor(registry) - -const agent = new Agent( - { name: 'writer', model: 'claude-sonnet-4-6', maxTurns: 3 }, - registry, - executor, -) - -for await (const event of agent.stream('Explain monads in two sentences.')) { - if (event.type === 'text' && typeof event.data === 'string') { - process.stdout.write(event.data) - } -} -``` - -
+| Example | What it shows | +|---------|---------------| +| [01 — Single Agent](examples/01-single-agent.ts) | `runAgent()` one-shot, `stream()` streaming, `prompt()` multi-turn | +| [02 — Team Collaboration](examples/02-team-collaboration.ts) | `runTeam()` auto-orchestration with coordinator pattern | +| [03 — Task Pipeline](examples/03-task-pipeline.ts) | `runTasks()` explicit dependency graph (design → implement → test + review) | +| [04 — Multi-Model Team](examples/04-multi-model-team.ts) | `defineTool()` custom tools, mixed Anthropic + OpenAI providers, `AgentPool` | +| [05 — Copilot](examples/05-copilot-test.ts) | GitHub Copilot as an LLM provider | +| [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 | ## Architecture diff --git a/README_zh.md b/README_zh.md index 86436dc..0f38c51 100644 --- a/README_zh.md +++ b/README_zh.md @@ -108,165 +108,23 @@ Tokens: 12847 output tokens -## 更多示例 +## 示例 -
-单智能体 — 一个智能体,一个提示词 +所有示例都是可运行脚本,位于 [`examples/`](./examples/) 目录。使用 `npx tsx` 运行: -```typescript -import { OpenMultiAgent } from '@jackchen_me/open-multi-agent' - -const orchestrator = new OpenMultiAgent({ defaultModel: 'claude-sonnet-4-6' }) - -const result = await orchestrator.runAgent( - { - name: 'coder', - model: 'claude-sonnet-4-6', - tools: ['bash', 'file_write'], - }, - 'Write a TypeScript function that reverses a string, save it to /tmp/reverse.ts, and run it.', -) - -console.log(result.output) +```bash +npx tsx examples/01-single-agent.ts ``` -
- -
-任务流水线 — 显式控制任务图和分配 - -```typescript -const result = await orchestrator.runTasks(team, [ - { - title: 'Design the data model', - description: 'Write a TypeScript interface spec to /tmp/spec.md', - assignee: 'architect', - }, - { - title: 'Implement the module', - description: 'Read /tmp/spec.md and implement the module in /tmp/src/', - assignee: 'developer', - dependsOn: ['Design the data model'], // 等待设计完成后才开始 - }, - { - title: 'Write tests', - description: 'Read the implementation and write Vitest tests.', - assignee: 'developer', - dependsOn: ['Implement the module'], - }, - { - title: 'Review code', - description: 'Review /tmp/src/ and produce a structured code review.', - assignee: 'reviewer', - dependsOn: ['Implement the module'], // 可以和测试并行执行 - }, -]) -``` - -
- -
-自定义工具 — 使用 Zod schema 定义工具 - -```typescript -import { z } from 'zod' -import { defineTool, Agent, ToolRegistry, ToolExecutor, registerBuiltInTools } from '@jackchen_me/open-multi-agent' - -const searchTool = defineTool({ - name: 'web_search', - description: 'Search the web and return the top results.', - inputSchema: z.object({ - query: z.string().describe('The search query.'), - maxResults: z.number().optional().describe('Number of results (default 5).'), - }), - execute: async ({ query, maxResults = 5 }) => { - const results = await mySearchProvider(query, maxResults) - return { data: JSON.stringify(results), isError: false } - }, -}) - -const registry = new ToolRegistry() -registerBuiltInTools(registry) -registry.register(searchTool) - -const executor = new ToolExecutor(registry) -const agent = new Agent( - { name: 'researcher', model: 'claude-sonnet-4-6', tools: ['web_search'] }, - registry, - executor, -) - -const result = await agent.run('Find the three most recent TypeScript releases.') -``` - -
- -
-多模型团队 — 在一个工作流中混合使用 Claude、GPT 和本地模型 - -```typescript -const claudeAgent: AgentConfig = { - name: 'strategist', - model: 'claude-opus-4-6', - provider: 'anthropic', - systemPrompt: 'You plan high-level approaches.', - tools: ['file_write'], -} - -const gptAgent: AgentConfig = { - name: 'implementer', - model: 'gpt-5.4', - provider: 'openai', - systemPrompt: 'You implement plans as working code.', - tools: ['bash', 'file_read', 'file_write'], -} - -// 任何 OpenAI 兼容 API — Ollama、vLLM、LM Studio 等 -const localAgent: AgentConfig = { - name: 'reviewer', - model: 'llama3.1', - provider: 'openai', - baseURL: 'http://localhost:11434/v1', - apiKey: 'ollama', - systemPrompt: 'You review code for correctness and clarity.', - tools: ['file_read', 'grep'], -} - -const team = orchestrator.createTeam('mixed-team', { - name: 'mixed-team', - agents: [claudeAgent, gptAgent, localAgent], - sharedMemory: true, -}) - -const result = await orchestrator.runTeam(team, 'Build a CLI tool that converts JSON to CSV.') -``` - -
- -
-流式输出 - -```typescript -import { Agent, ToolRegistry, ToolExecutor, registerBuiltInTools } from '@jackchen_me/open-multi-agent' - -const registry = new ToolRegistry() -registerBuiltInTools(registry) -const executor = new ToolExecutor(registry) - -const agent = new Agent( - { name: 'writer', model: 'claude-sonnet-4-6', maxTurns: 3 }, - registry, - executor, -) - -for await (const event of agent.stream('Explain monads in two sentences.')) { - if (event.type === 'text' && typeof event.data === 'string') { - process.stdout.write(event.data) - } -} -``` - -
+| 示例 | 展示内容 | +|------|----------| +| [01 — 单智能体](examples/01-single-agent.ts) | `runAgent()` 单次调用、`stream()` 流式输出、`prompt()` 多轮对话 | +| [02 — 团队协作](examples/02-team-collaboration.ts) | `runTeam()` 自动编排 + 协调者模式 | +| [03 — 任务流水线](examples/03-task-pipeline.ts) | `runTasks()` 显式依赖图(设计 → 实现 → 测试 + 评审) | +| [04 — 多模型团队](examples/04-multi-model-team.ts) | `defineTool()` 自定义工具、Anthropic + OpenAI 混合、`AgentPool` | +| [05 — Copilot](examples/05-copilot-test.ts) | GitHub Copilot 作为 LLM 提供者 | +| [06 — 本地模型](examples/06-local-model.ts) | Ollama + Claude 混合流水线,通过 `baseURL` 接入(兼容 vLLM、LM Studio 等) | +| [07 — 扇出聚合](examples/07-fan-out-aggregate.ts) | `runParallel()` MapReduce — 3 个分析师并行,然后综合 | ## 架构 From 24a2c4fe1ab31795c1d4396ad6b92bd3763459ab Mon Sep 17 00:00:00 2001 From: JackChen Date: Fri, 3 Apr 2026 02:20:47 +0800 Subject: [PATCH 3/4] chore: add tsx to devDependencies for running examples --- package-lock.json | 532 +++++++++++++++++++++++++++++++++++++++++++++- package.json | 5 +- 2 files changed, 533 insertions(+), 4 deletions(-) diff --git a/package-lock.json b/package-lock.json index 96f1dec..0b541e2 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,11 +1,11 @@ { - "name": "maestro-agents", + "name": "@jackchen_me/open-multi-agent", "version": "0.1.0", "lockfileVersion": 3, "requires": true, "packages": { "": { - "name": "maestro-agents", + "name": "@jackchen_me/open-multi-agent", "version": "0.1.0", "license": "MIT", "dependencies": { @@ -15,6 +15,7 @@ }, "devDependencies": { "@types/node": "^22.0.0", + "tsx": "^4.21.0", "typescript": "^5.6.0", "vitest": "^2.1.0" }, @@ -320,6 +321,23 @@ "node": ">=12" } }, + "node_modules/@esbuild/netbsd-arm64": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.27.7.tgz", + "integrity": "sha512-b6pqtrQdigZBwZxAn1UpazEisvwaIDvdbMbmrly7cDTMFnw/+3lVxxCTGOrkPVnsYIosJJXAsILG9XcQS+Yu6w==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=18" + } + }, "node_modules/@esbuild/netbsd-x64": { "version": "0.21.5", "resolved": "https://registry.npmmirror.com/@esbuild/netbsd-x64/-/netbsd-x64-0.21.5.tgz", @@ -337,6 +355,23 @@ "node": ">=12" } }, + "node_modules/@esbuild/openbsd-arm64": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.27.7.tgz", + "integrity": "sha512-AFuojMQTxAz75Fo8idVcqoQWEHIXFRbOc1TrVcFSgCZtQfSdc1RXgB3tjOn/krRHENUB4j00bfGjyl2mJrU37A==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" + } + }, "node_modules/@esbuild/openbsd-x64": { "version": "0.21.5", "resolved": "https://registry.npmmirror.com/@esbuild/openbsd-x64/-/openbsd-x64-0.21.5.tgz", @@ -354,6 +389,23 @@ "node": ">=12" } }, + "node_modules/@esbuild/openharmony-arm64": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/openharmony-arm64/-/openharmony-arm64-0.27.7.tgz", + "integrity": "sha512-+KrvYb/C8zA9CU/g0sR6w2RBw7IGc5J2BPnc3dYc5VJxHCSF1yNMxTV5LQ7GuKteQXZtspjFbiuW5/dOj7H4Yw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openharmony" + ], + "engines": { + "node": ">=18" + } + }, "node_modules/@esbuild/sunos-x64": { "version": "0.21.5", "resolved": "https://registry.npmmirror.com/@esbuild/sunos-x64/-/sunos-x64-0.21.5.tgz", @@ -1287,6 +1339,19 @@ "node": ">= 0.4" } }, + "node_modules/get-tsconfig": { + "version": "4.13.7", + "resolved": "https://registry.npmjs.org/get-tsconfig/-/get-tsconfig-4.13.7.tgz", + "integrity": "sha512-7tN6rFgBlMgpBML5j8typ92BKFi2sFQvIdpAqLA2beia5avZDrMs0FLZiM5etShWq5irVyGcGMEA1jcDaK7A/Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "resolve-pkg-maps": "^1.0.0" + }, + "funding": { + "url": "https://github.com/privatenumber/get-tsconfig?sponsor=1" + } + }, "node_modules/gopd": { "version": "1.2.0", "resolved": "https://registry.npmmirror.com/gopd/-/gopd-1.2.0.tgz", @@ -1557,6 +1622,16 @@ "node": "^10 || ^12 || >=14" } }, + "node_modules/resolve-pkg-maps": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/resolve-pkg-maps/-/resolve-pkg-maps-1.0.0.tgz", + "integrity": "sha512-seS2Tj26TBVOC2NIc2rOe2y2ZO7efxITtLZcGSOnHHNOQ7CkiUBfw0Iw2ck6xkIhPwLhKNLS8BO+hEpngQlqzw==", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://github.com/privatenumber/resolve-pkg-maps?sponsor=1" + } + }, "node_modules/rollup": { "version": "4.60.1", "resolved": "https://registry.npmmirror.com/rollup/-/rollup-4.60.1.tgz", @@ -1683,6 +1758,459 @@ "integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==", "license": "MIT" }, + "node_modules/tsx": { + "version": "4.21.0", + "resolved": "https://registry.npmjs.org/tsx/-/tsx-4.21.0.tgz", + "integrity": "sha512-5C1sg4USs1lfG0GFb2RLXsdpXqBSEhAaA/0kPL01wxzpMqLILNxIxIOKiILz+cdg/pLnOUxFYOR5yhHU666wbw==", + "dev": true, + "license": "MIT", + "dependencies": { + "esbuild": "~0.27.0", + "get-tsconfig": "^4.7.5" + }, + "bin": { + "tsx": "dist/cli.mjs" + }, + "engines": { + "node": ">=18.0.0" + }, + "optionalDependencies": { + "fsevents": "~2.3.3" + } + }, + "node_modules/tsx/node_modules/@esbuild/aix-ppc64": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.27.7.tgz", + "integrity": "sha512-EKX3Qwmhz1eMdEJokhALr0YiD0lhQNwDqkPYyPhiSwKrh7/4KRjQc04sZ8db+5DVVnZ1LmbNDI1uAMPEUBnQPg==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "aix" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/tsx/node_modules/@esbuild/android-arm": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.27.7.tgz", + "integrity": "sha512-jbPXvB4Yj2yBV7HUfE2KHe4GJX51QplCN1pGbYjvsyCZbQmies29EoJbkEc+vYuU5o45AfQn37vZlyXy4YJ8RQ==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/tsx/node_modules/@esbuild/android-arm64": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.27.7.tgz", + "integrity": "sha512-62dPZHpIXzvChfvfLJow3q5dDtiNMkwiRzPylSCfriLvZeq0a1bWChrGx/BbUbPwOrsWKMn8idSllklzBy+dgQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/tsx/node_modules/@esbuild/android-x64": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.27.7.tgz", + "integrity": "sha512-x5VpMODneVDb70PYV2VQOmIUUiBtY3D3mPBG8NxVk5CogneYhkR7MmM3yR/uMdITLrC1ml/NV1rj4bMJuy9MCg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/tsx/node_modules/@esbuild/darwin-arm64": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.27.7.tgz", + "integrity": "sha512-5lckdqeuBPlKUwvoCXIgI2D9/ABmPq3Rdp7IfL70393YgaASt7tbju3Ac+ePVi3KDH6N2RqePfHnXkaDtY9fkw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/tsx/node_modules/@esbuild/darwin-x64": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.27.7.tgz", + "integrity": "sha512-rYnXrKcXuT7Z+WL5K980jVFdvVKhCHhUwid+dDYQpH+qu+TefcomiMAJpIiC2EM3Rjtq0sO3StMV/+3w3MyyqQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/tsx/node_modules/@esbuild/freebsd-arm64": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.27.7.tgz", + "integrity": "sha512-B48PqeCsEgOtzME2GbNM2roU29AMTuOIN91dsMO30t+Ydis3z/3Ngoj5hhnsOSSwNzS+6JppqWsuhTp6E82l2w==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/tsx/node_modules/@esbuild/freebsd-x64": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.27.7.tgz", + "integrity": "sha512-jOBDK5XEjA4m5IJK3bpAQF9/Lelu/Z9ZcdhTRLf4cajlB+8VEhFFRjWgfy3M1O4rO2GQ/b2dLwCUGpiF/eATNQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/tsx/node_modules/@esbuild/linux-arm": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.27.7.tgz", + "integrity": "sha512-RkT/YXYBTSULo3+af8Ib0ykH8u2MBh57o7q/DAs3lTJlyVQkgQvlrPTnjIzzRPQyavxtPtfg0EopvDyIt0j1rA==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/tsx/node_modules/@esbuild/linux-arm64": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.27.7.tgz", + "integrity": "sha512-RZPHBoxXuNnPQO9rvjh5jdkRmVizktkT7TCDkDmQ0W2SwHInKCAV95GRuvdSvA7w4VMwfCjUiPwDi0ZO6Nfe9A==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/tsx/node_modules/@esbuild/linux-ia32": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.27.7.tgz", + "integrity": "sha512-GA48aKNkyQDbd3KtkplYWT102C5sn/EZTY4XROkxONgruHPU72l+gW+FfF8tf2cFjeHaRbWpOYa/uRBz/Xq1Pg==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/tsx/node_modules/@esbuild/linux-loong64": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.27.7.tgz", + "integrity": "sha512-a4POruNM2oWsD4WKvBSEKGIiWQF8fZOAsycHOt6JBpZ+JN2n2JH9WAv56SOyu9X5IqAjqSIPTaJkqN8F7XOQ5Q==", + "cpu": [ + "loong64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/tsx/node_modules/@esbuild/linux-mips64el": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.27.7.tgz", + "integrity": "sha512-KabT5I6StirGfIz0FMgl1I+R1H73Gp0ofL9A3nG3i/cYFJzKHhouBV5VWK1CSgKvVaG4q1RNpCTR2LuTVB3fIw==", + "cpu": [ + "mips64el" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/tsx/node_modules/@esbuild/linux-ppc64": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.27.7.tgz", + "integrity": "sha512-gRsL4x6wsGHGRqhtI+ifpN/vpOFTQtnbsupUF5R5YTAg+y/lKelYR1hXbnBdzDjGbMYjVJLJTd2OFmMewAgwlQ==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/tsx/node_modules/@esbuild/linux-riscv64": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.27.7.tgz", + "integrity": "sha512-hL25LbxO1QOngGzu2U5xeXtxXcW+/GvMN3ejANqXkxZ/opySAZMrc+9LY/WyjAan41unrR3YrmtTsUpwT66InQ==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/tsx/node_modules/@esbuild/linux-s390x": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.27.7.tgz", + "integrity": "sha512-2k8go8Ycu1Kb46vEelhu1vqEP+UeRVj2zY1pSuPdgvbd5ykAw82Lrro28vXUrRmzEsUV0NzCf54yARIK8r0fdw==", + "cpu": [ + "s390x" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/tsx/node_modules/@esbuild/linux-x64": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.27.7.tgz", + "integrity": "sha512-hzznmADPt+OmsYzw1EE33ccA+HPdIqiCRq7cQeL1Jlq2gb1+OyWBkMCrYGBJ+sxVzve2ZJEVeePbLM2iEIZSxA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/tsx/node_modules/@esbuild/netbsd-x64": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.27.7.tgz", + "integrity": "sha512-OfatkLojr6U+WN5EDYuoQhtM+1xco+/6FSzJJnuWiUw5eVcicbyK3dq5EeV/QHT1uy6GoDhGbFpprUiHUYggrw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/tsx/node_modules/@esbuild/openbsd-x64": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.27.7.tgz", + "integrity": "sha512-+A1NJmfM8WNDv5CLVQYJ5PshuRm/4cI6WMZRg1by1GwPIQPCTs1GLEUHwiiQGT5zDdyLiRM/l1G0Pv54gvtKIg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/tsx/node_modules/@esbuild/sunos-x64": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.27.7.tgz", + "integrity": "sha512-ikktIhFBzQNt/QDyOL580ti9+5mL/YZeUPKU2ivGtGjdTYoqz6jObj6nOMfhASpS4GU4Q/Clh1QtxWAvcYKamA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "sunos" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/tsx/node_modules/@esbuild/win32-arm64": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.27.7.tgz", + "integrity": "sha512-7yRhbHvPqSpRUV7Q20VuDwbjW5kIMwTHpptuUzV+AA46kiPze5Z7qgt6CLCK3pWFrHeNfDd1VKgyP4O+ng17CA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/tsx/node_modules/@esbuild/win32-ia32": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.27.7.tgz", + "integrity": "sha512-SmwKXe6VHIyZYbBLJrhOoCJRB/Z1tckzmgTLfFYOfpMAx63BJEaL9ExI8x7v0oAO3Zh6D/Oi1gVxEYr5oUCFhw==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/tsx/node_modules/@esbuild/win32-x64": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.27.7.tgz", + "integrity": "sha512-56hiAJPhwQ1R4i+21FVF7V8kSD5zZTdHcVuRFMW0hn753vVfQN8xlx4uOPT4xoGH0Z/oVATuR82AiqSTDIpaHg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/tsx/node_modules/esbuild": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.27.7.tgz", + "integrity": "sha512-IxpibTjyVnmrIQo5aqNpCgoACA/dTKLTlhMHihVHhdkxKyPO1uBBthumT0rdHmcsk9uMonIWS0m4FljWzILh3w==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "bin": { + "esbuild": "bin/esbuild" + }, + "engines": { + "node": ">=18" + }, + "optionalDependencies": { + "@esbuild/aix-ppc64": "0.27.7", + "@esbuild/android-arm": "0.27.7", + "@esbuild/android-arm64": "0.27.7", + "@esbuild/android-x64": "0.27.7", + "@esbuild/darwin-arm64": "0.27.7", + "@esbuild/darwin-x64": "0.27.7", + "@esbuild/freebsd-arm64": "0.27.7", + "@esbuild/freebsd-x64": "0.27.7", + "@esbuild/linux-arm": "0.27.7", + "@esbuild/linux-arm64": "0.27.7", + "@esbuild/linux-ia32": "0.27.7", + "@esbuild/linux-loong64": "0.27.7", + "@esbuild/linux-mips64el": "0.27.7", + "@esbuild/linux-ppc64": "0.27.7", + "@esbuild/linux-riscv64": "0.27.7", + "@esbuild/linux-s390x": "0.27.7", + "@esbuild/linux-x64": "0.27.7", + "@esbuild/netbsd-arm64": "0.27.7", + "@esbuild/netbsd-x64": "0.27.7", + "@esbuild/openbsd-arm64": "0.27.7", + "@esbuild/openbsd-x64": "0.27.7", + "@esbuild/openharmony-arm64": "0.27.7", + "@esbuild/sunos-x64": "0.27.7", + "@esbuild/win32-arm64": "0.27.7", + "@esbuild/win32-ia32": "0.27.7", + "@esbuild/win32-x64": "0.27.7" + } + }, "node_modules/typescript": { "version": "5.9.3", "resolved": "https://registry.npmmirror.com/typescript/-/typescript-5.9.3.tgz", diff --git a/package.json b/package.json index ee0e26a..b185a8c 100644 --- a/package.json +++ b/package.json @@ -42,8 +42,9 @@ "zod": "^3.23.0" }, "devDependencies": { + "@types/node": "^22.0.0", + "tsx": "^4.21.0", "typescript": "^5.6.0", - "vitest": "^2.1.0", - "@types/node": "^22.0.0" + "vitest": "^2.1.0" } } From 8d27c6a1fe2c0ad698c178ec2e63a0e947eca53d Mon Sep 17 00:00:00 2001 From: JackChen Date: Fri, 3 Apr 2026 02:47:02 +0800 Subject: [PATCH 4/4] docs: add supported providers section and clarify contributing guidelines - Add Supported Providers table with 4 verified providers (Anthropic, OpenAI, Copilot, Ollama) and note that other OpenAI-compatible providers are unverified - Update Contributing to distinguish baseURL verification (#25) from new adapters - Note that local models via Ollama require no API key in Quick Start --- README.md | 15 +++++++++++++-- README_zh.md | 15 +++++++++++++-- 2 files changed, 26 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 12340be..edef1d4 100644 --- a/README.md +++ b/README.md @@ -26,7 +26,7 @@ Requires Node.js >= 18. npm install @jackchen_me/open-multi-agent ``` -Set `ANTHROPIC_API_KEY` (and optionally `OPENAI_API_KEY` or `GITHUB_TOKEN` for Copilot) in your environment. +Set `ANTHROPIC_API_KEY` (and optionally `OPENAI_API_KEY` or `GITHUB_TOKEN` for Copilot) in your environment. Local models via Ollama require no API key — see [example 06](examples/06-local-model.ts). Three agents, one goal — the framework handles the rest: @@ -173,11 +173,22 @@ npx tsx examples/01-single-agent.ts | `file_edit` | Edit a file by replacing an exact string match. | | `grep` | Search file contents with regex. Uses ripgrep when available, falls back to Node.js. | +## Supported Providers + +| Provider | Config | Env var | Status | +|----------|--------|---------|--------| +| Anthropic (Claude) | `provider: 'anthropic'` | `ANTHROPIC_API_KEY` | Verified | +| OpenAI (GPT) | `provider: 'openai'` | `OPENAI_API_KEY` | Verified | +| GitHub Copilot | `provider: 'copilot'` | `GITHUB_TOKEN` | Verified | +| Ollama / vLLM / LM Studio | `provider: 'openai'` + `baseURL` | — | Verified | + +Any OpenAI-compatible API should work via `provider: 'openai'` + `baseURL` (DeepSeek, Groq, Mistral, Qwen, MiniMax, etc.). These providers have not been fully verified yet — contributions welcome via [#25](https://github.com/JackChen-me/open-multi-agent/issues/25). + ## Contributing Issues, feature requests, and PRs are welcome. Some areas where contributions would be especially valuable: -- **LLM Adapters** — Anthropic, OpenAI, and Copilot are supported out of the box. Any OpenAI-compatible API (Ollama, vLLM, LM Studio, etc.) works via `baseURL`. Additional adapters for Gemini and other providers are welcome. The `LLMAdapter` interface requires just two methods: `chat()` and `stream()`. +- **Provider integrations** — Verify and document OpenAI-compatible providers (DeepSeek, Groq, Qwen, MiniMax, etc.) via `baseURL`. See [#25](https://github.com/JackChen-me/open-multi-agent/issues/25). For providers that are NOT OpenAI-compatible (e.g. Gemini), a new `LLMAdapter` implementation is welcome — the interface requires just two methods: `chat()` and `stream()`. - **Examples** — Real-world workflows and use cases. - **Documentation** — Guides, tutorials, and API docs. diff --git a/README_zh.md b/README_zh.md index 0f38c51..4cf7a00 100644 --- a/README_zh.md +++ b/README_zh.md @@ -26,7 +26,7 @@ npm install @jackchen_me/open-multi-agent ``` -在环境变量中设置 `ANTHROPIC_API_KEY`(以及可选的 `OPENAI_API_KEY` 或用于 Copilot 的 `GITHUB_TOKEN`)。 +在环境变量中设置 `ANTHROPIC_API_KEY`(以及可选的 `OPENAI_API_KEY` 或用于 Copilot 的 `GITHUB_TOKEN`)。通过 Ollama 使用本地模型无需 API key — 参见 [example 06](examples/06-local-model.ts)。 三个智能体,一个目标——框架处理剩下的一切: @@ -177,11 +177,22 @@ npx tsx examples/01-single-agent.ts | `file_edit` | 通过精确字符串匹配编辑文件。 | | `grep` | 使用正则表达式搜索文件内容。优先使用 ripgrep,回退到 Node.js 实现。 | +## 支持的 Provider + +| Provider | 配置 | 环境变量 | 状态 | +|----------|------|----------|------| +| Anthropic (Claude) | `provider: 'anthropic'` | `ANTHROPIC_API_KEY` | 已验证 | +| OpenAI (GPT) | `provider: 'openai'` | `OPENAI_API_KEY` | 已验证 | +| GitHub Copilot | `provider: 'copilot'` | `GITHUB_TOKEN` | 已验证 | +| Ollama / vLLM / LM Studio | `provider: 'openai'` + `baseURL` | — | 已验证 | + +任何 OpenAI 兼容 API 均可通过 `provider: 'openai'` + `baseURL` 接入(DeepSeek、Groq、Mistral、Qwen、MiniMax 等)。这些 Provider 尚未完整验证——欢迎通过 [#25](https://github.com/JackChen-me/open-multi-agent/issues/25) 贡献验证。 + ## 参与贡献 欢迎提 Issue、功能需求和 PR。以下方向的贡献尤其有价值: -- **LLM 适配器** — Anthropic、OpenAI、Copilot 已原生支持。任何 OpenAI 兼容 API(Ollama、vLLM、LM Studio 等)可通过 `baseURL` 直接使用。欢迎贡献 Gemini 等其他适配器。`LLMAdapter` 接口只需实现两个方法:`chat()` 和 `stream()`。 +- **Provider 集成** — 验证并文档化 OpenAI 兼容 Provider(DeepSeek、Groq、Qwen、MiniMax 等)通过 `baseURL` 接入。详见 [#25](https://github.com/JackChen-me/open-multi-agent/issues/25)。对于非 OpenAI 兼容的 Provider(如 Gemini),欢迎贡献新的 `LLMAdapter` 实现——接口只需两个方法:`chat()` 和 `stream()`。 - **示例** — 真实场景的工作流和用例。 - **文档** — 指南、教程和 API 文档。