← 全部示例
// 生产控制
成本分层流水线
把同一条四阶段 runTasks() 流水线跑两次;在一条流水线内给每个智能体分配不同模型;通过 onTrace 采集各模型的 token 用量;按各 provider 的模型定价估算美元成本。
01 运行
OMA API
OpenMultiAgent在仓库的克隆里运行这个文件:
npx tsx packages/core/examples/patterns/cost-tiered-pipeline.ts
前置条件
- Set one of:
- - ANTHROPIC_API_KEY
- - OPENAI_API_KEY
展开完整同步源码 · 559 行
完整示例,从固定的 Framework commit 同步。
/*** Cost-Tiered Pipeline** Demonstrates:* - Running the same four-stage `runTasks()` pipeline twice* - Assigning different models per agent within one pipeline* - Capturing token usage per model via `onTrace`* - Estimating USD cost from provider-specific model pricing** Run:* npx tsx packages/core/examples/patterns/cost-tiered-pipeline.ts** Prerequisites:* Set one of:* - ANTHROPIC_API_KEY* - OPENAI_API_KEY** Optional:* - LLM_PROVIDER=anthropic|openai* - ANTHROPIC_BASE_URL for Anthropic-compatible relays* - OPENAI_BASE_URL for OpenAI-compatible relays*/import { OpenMultiAgent } from '../../src/index.js'import type { AgentConfig, OrchestratorEvent, TokenUsage, TraceEvent } from '../../src/types.js'// ---------------------------------------------------------------------------// Pricing// ---------------------------------------------------------------------------const PRICING_AS_OF = '2026-04-24'const PRICING = {// Anthropic + OpenAI API pricing, as of 2026-04-24, USD per 1M tokens'claude-opus-4-7': { input: 5, output: 25 },'claude-sonnet-4-6': { input: 3, output: 15 },'claude-haiku-4-5': { input: 1, output: 5 },'gpt-5.5': { input: 5, output: 30 },'gpt-5.4': { input: 2.5, output: 15 },'gpt-5.4-mini': { input: 0.75, output: 4.5 },} as consttype PricedModel = keyof typeof PRICINGtype UsageByModel = Partial<Record<PricedModel, TokenUsage>>type ProviderId = 'anthropic' | 'openai'type ProviderConfig = Pick<AgentConfig, 'provider' | 'model' | 'apiKey' | 'baseURL'>type AgentRunSummary = Map<string, { success: boolean, tokenUsage: TokenUsage }>type PipelineAssignments = {researcher: ProviderConfigclassifier: ProviderConfigdrafter: ProviderConfigreviewer: ProviderConfig}type ScenarioResult = {label: stringassignments: PipelineAssignmentsagentResults: AgentRunSummaryusageByModel: UsageByModeltotalTokenUsage: TokenUsagecostUsd: numberelapsedMs: numberfinalBrief: string}// ---------------------------------------------------------------------------// Topic// ---------------------------------------------------------------------------const TOPIC = 'Launch a developer-focused AI code review assistant for 200-800 engineer software teams'// ---------------------------------------------------------------------------// Prompts// ---------------------------------------------------------------------------const RESEARCHER_PROMPT = `You are a product researcher preparing source material for a launch brief.Focus on:- target buyer and user personas- pain points in code review workflows- market landscape and competitive differentiators- adoption risks and rollout constraintsWrite structured markdown in 220-300 words. Be concrete and specific.`const CLASSIFIER_PROMPT = `You are a product strategist turning raw research into decision-ready structure.Organize the upstream research into four sections:1. Audience2. Risks3. Differentiators4. Open QuestionsWrite 180-250 words in markdown. Keep it crisp and managerial.`const DRAFTER_PROMPT = `You are a senior product marketer writing a launch brief.Use the prior task outputs from shared memory and produce a polished brief with:- executive summary- target audience- product positioning- go-to-market risks- recommended launch angleWrite 380-500 words in markdown.`const REVIEWER_PROMPT = `You are an executive reviewer. Rewrite the brief for clarity, internal consistency,and sharper prioritization. Keep the strongest claims, remove repetition, and make therecommendation decisive.Return the final brief in 350-450 words using markdown headings.`// ---------------------------------------------------------------------------// Helpers// ---------------------------------------------------------------------------function zeroUsage(): TokenUsage {return { input_tokens: 0, output_tokens: 0 }}function addUsage(a: TokenUsage, b: TokenUsage): TokenUsage {return {input_tokens: a.input_tokens + b.input_tokens,output_tokens: a.output_tokens + b.output_tokens,}}function isPricedModel(model: string): model is PricedModel {return model in PRICING}function createUsageCollector(): {usageByModel: UsageByModelhandleTrace: (event: TraceEvent) => void} {const usageByModel: UsageByModel = {}function handleTrace(event: TraceEvent): void {if (event.type !== 'llm_call' || !isPricedModel(event.model)) returnusageByModel[event.model] = addUsage(usageByModel[event.model] ?? zeroUsage(), event.tokens)}return { usageByModel, handleTrace }}function estimateCostUsd(usageByModel: UsageByModel): number {return Object.entries(usageByModel).reduce((sum, [model, usage]) => {if (!usage || !isPricedModel(model)) return sumconst pricing = PRICING[model]return sum + ((usage.input_tokens / 1_000_000) * pricing.input +(usage.output_tokens / 1_000_000) * pricing.output)}, 0)}function formatUsd(value: number): string {return `$${value.toFixed(4)}`}function formatSeconds(ms: number): string {return `${(ms / 1000).toFixed(2)}s`}function formatPercent(value: number): string {return `${(value * 100).toFixed(1)}%`}function inferProvider(): ProviderId {if (process.env.OPENAI_API_KEY) return 'openai'if (process.env.ANTHROPIC_API_KEY) return 'anthropic'throw new Error('Set OPENAI_API_KEY or ANTHROPIC_API_KEY to run this example.')}function getSelectedProvider(): ProviderId {const requested = process.env.LLM_PROVIDER?.trim().toLowerCase()if (!requested) return inferProvider()if (requested === 'anthropic' || requested === 'openai') {return requested}throw new Error(`Unsupported LLM_PROVIDER="${process.env.LLM_PROVIDER}". Use one of: anthropic, openai.`,)}function getProviderConfigs(provider: ProviderId): {baselineLabel: stringtieredLabel: stringendpointLabel: stringflagship: ProviderConfigmid: ProviderConfigcheap: ProviderConfig} {switch (provider) {case 'openai': {const apiKey = process.env.OPENAI_API_KEYif (!apiKey) {throw new Error('LLM_PROVIDER=openai requires OPENAI_API_KEY.')}const baseURL = process.env.OPENAI_BASE_URLreturn {baselineLabel: 'All GPT-5.5 baseline',tieredLabel: 'Tiered GPT mix',endpointLabel: baseURL ?? 'OpenAI official API',flagship: {provider: 'openai',model: 'gpt-5.5',apiKey,baseURL,},mid: {provider: 'openai',model: 'gpt-5.4',apiKey,baseURL,},cheap: {provider: 'openai',model: 'gpt-5.4-mini',apiKey,baseURL,},}}case 'anthropic':default: {const apiKey = process.env.ANTHROPIC_API_KEYif (!apiKey) {throw new Error('LLM_PROVIDER=anthropic requires ANTHROPIC_API_KEY.')}const baseURL = process.env.ANTHROPIC_BASE_URLreturn {baselineLabel: 'All Opus baseline',tieredLabel: 'Tiered Claude mix',endpointLabel: baseURL ?? 'Anthropic official API',flagship: {provider: 'anthropic',model: 'claude-opus-4-7',apiKey,baseURL,},mid: {provider: 'anthropic',model: 'claude-sonnet-4-6',apiKey,baseURL,},cheap: {provider: 'anthropic',model: 'claude-haiku-4-5',apiKey,baseURL,},}}}}function createProgressHandler(label: string): (event: OrchestratorEvent) => void {return (event: OrchestratorEvent): void => {if (event.type === 'task_start') {console.log(` [START] ${label} :: ${event.task ?? ''} → ${event.agent ?? ''}`)}if (event.type === 'task_complete') {console.log(` [DONE] ${label} :: ${event.task ?? ''}`)}}}function createAgents(assignments: PipelineAssignments): AgentConfig[] {return [{name: 'researcher',...assignments.researcher,systemPrompt: RESEARCHER_PROMPT,maxTurns: 2,temperature: 0.2,},{name: 'classifier',...assignments.classifier,systemPrompt: CLASSIFIER_PROMPT,maxTurns: 2,temperature: 0.2,},{name: 'drafter',...assignments.drafter,systemPrompt: DRAFTER_PROMPT,maxTurns: 2,temperature: 0.3,},{name: 'reviewer',...assignments.reviewer,systemPrompt: REVIEWER_PROMPT,maxTurns: 2,temperature: 0.2,},]}function describeAssignments(assignments: PipelineAssignments): string {return [`researcher=${assignments.researcher.model}`,`classifier=${assignments.classifier.model}`,`drafter=${assignments.drafter.model}`,`reviewer=${assignments.reviewer.model}`,].join(', ')}function printAgentTokenSummary(agentResults: AgentRunSummary): void {console.log('Agent Token Summary')console.log('-'.repeat(60))for (const [name, run] of agentResults) {const icon = run.success ? 'OK ' : 'FAIL'const tokens = `in:${run.tokenUsage.input_tokens} out:${run.tokenUsage.output_tokens}`console.log(` [${icon}] ${name.padEnd(12)} ${tokens}`)}console.log()}function printModelCostBreakdown(usageByModel: UsageByModel): void {console.log('Model Cost Breakdown')console.log('-'.repeat(60))console.log('| Model | Input | Output | USD cost |')console.log('|-------|------:|-------:|---------:|')for (const [model, usage] of Object.entries(usageByModel)) {if (!usage || !isPricedModel(model)) continueconst costUsd =(usage.input_tokens / 1_000_000) * PRICING[model].input +(usage.output_tokens / 1_000_000) * PRICING[model].outputconsole.log(`| ${model} | ${usage.input_tokens} | ${usage.output_tokens} | ${formatUsd(costUsd)} |`,)}console.log()}function printComparisonTable(runs: ScenarioResult[]): void {console.log('Comparison')console.log('-'.repeat(60))console.log('| Run | Input | Output | USD cost | Wall time |')console.log('|-----|------:|-------:|---------:|----------:|')for (const run of runs) {console.log(`| ${run.label} | ${run.totalTokenUsage.input_tokens} | ${run.totalTokenUsage.output_tokens} | ${formatUsd(run.costUsd)} | ${formatSeconds(run.elapsedMs)} |`,)}console.log()}function printFinalSummaryTable(runs: ScenarioResult[]): void {console.log('Final Summary')console.log('-'.repeat(60))console.log('| Run | Models | Input | Output | USD cost | Wall time |')console.log('|-----|--------|------:|-------:|---------:|----------:|')for (const run of runs) {console.log(`| ${run.label} | ${describeAssignments(run.assignments)} | ${run.totalTokenUsage.input_tokens} | ${run.totalTokenUsage.output_tokens} | ${formatUsd(run.costUsd)} | ${formatSeconds(run.elapsedMs)} |`,)}console.log()}function printScenarioDetails(run: ScenarioResult): void {console.log('='.repeat(60))console.log(run.label.toUpperCase())console.log('='.repeat(60))console.log(`Models: ${describeAssignments(run.assignments)}`)console.log(`Tokens — input: ${run.totalTokenUsage.input_tokens}, output: ${run.totalTokenUsage.output_tokens}`,)console.log(`Estimated USD cost — ${formatUsd(run.costUsd)}`)console.log(`Wall time — ${formatSeconds(run.elapsedMs)}`)console.log()printAgentTokenSummary(run.agentResults)printModelCostBreakdown(run.usageByModel)console.log('Final Brief')console.log('-'.repeat(60))console.log(run.finalBrief)console.log()}async function runScenario(label: string,assignments: PipelineAssignments,tasks: Array<{title: stringdescription: stringassignee: stringdependsOn?: string[]}>,): Promise<ScenarioResult> {const agents = createAgents(assignments)const usageCollector = createUsageCollector()const orchestrator = new OpenMultiAgent({defaultProvider: assignments.reviewer.provider,defaultModel: assignments.reviewer.model,defaultBaseURL: assignments.reviewer.baseURL,defaultApiKey: assignments.reviewer.apiKey,onProgress: createProgressHandler(label),onTrace: usageCollector.handleTrace,})const team = orchestrator.createTeam(`cost-tiered-pipeline-${label.toLowerCase().replaceAll(/[^a-z0-9]+/g, '-')}`, {name: `cost-tiered-pipeline-${label.toLowerCase().replaceAll(/[^a-z0-9]+/g, '-')}`,agents,sharedMemory: true,})console.log(`Run: ${label}`)console.log(`Models: ${describeAssignments(assignments)}`)console.log()const startedAt = performance.now()const result = await orchestrator.runTasks(team, tasks)const elapsedMs = performance.now() - startedAtif (!result.success) {console.error(`${label} failed.`)for (const [name, run] of result.agentResults) {if (!run.success) {console.error(` ${name}: ${run.output}`)}}process.exit(1)}const finalResult = result.agentResults.get('reviewer')if (!finalResult?.success) {console.error(`${label} did not produce a final reviewer output.`)process.exit(1)}return {label,assignments,agentResults: result.agentResults,usageByModel: usageCollector.usageByModel,totalTokenUsage: result.totalTokenUsage,costUsd: estimateCostUsd(usageCollector.usageByModel),elapsedMs,finalBrief: finalResult.output,}}// ---------------------------------------------------------------------------// Provider resolution// ---------------------------------------------------------------------------const selectedProvider = getSelectedProvider()const providerConfigs = getProviderConfigs(selectedProvider)// ---------------------------------------------------------------------------// Tasks — fixed four-stage pipeline// ---------------------------------------------------------------------------const tasks = [{title: 'Research launch context',description: `Research ${TOPIC}. Focus on buyer pain points, review bottlenecks, team adoption concerns, competitor positioning, and measurable product value.`,assignee: 'researcher',},{title: 'Classify findings',description: `Read the research from shared memory and classify it into audience, risks, differentiators, and open questions. Preserve concrete details and avoid generic filler.`,assignee: 'classifier',dependsOn: ['Research launch context'],},{title: 'Draft launch brief',description: `Using the structured findings, draft a substantial launch brief for leadership review. The brief should be decision-ready and exceed 300 words.`,assignee: 'drafter',dependsOn: ['Classify findings'],},{title: 'Review final brief',description: `Review the drafted brief, tighten the positioning, and return the final version leadership should read.`,assignee: 'reviewer',dependsOn: ['Draft launch brief'],},]// ---------------------------------------------------------------------------// Scenario assignments// ---------------------------------------------------------------------------const baselineAssignments: PipelineAssignments = {researcher: providerConfigs.flagship,classifier: providerConfigs.flagship,drafter: providerConfigs.flagship,reviewer: providerConfigs.flagship,}const tieredAssignments: PipelineAssignments = {researcher: providerConfigs.cheap,classifier: providerConfigs.cheap,drafter: providerConfigs.mid,reviewer: providerConfigs.flagship,}// ---------------------------------------------------------------------------// Run// ---------------------------------------------------------------------------console.log('Cost-Tiered Pipeline')console.log('='.repeat(60))console.log(`Provider: ${selectedProvider}`)console.log(`Endpoint: ${providerConfigs.endpointLabel}`)console.log(`Pricing as of: ${PRICING_AS_OF}`)console.log(`Topic: ${TOPIC}`)console.log('Pipeline: researcher -> classifier -> drafter -> reviewer')console.log()const baselineRun = await runScenario(providerConfigs.baselineLabel, baselineAssignments, tasks)console.log('-'.repeat(60))const tieredRun = await runScenario(providerConfigs.tieredLabel, tieredAssignments, tasks)// ---------------------------------------------------------------------------// Output// ---------------------------------------------------------------------------console.log('\n' + '='.repeat(60))printComparisonTable([baselineRun, tieredRun])const savings = 1 - (tieredRun.costUsd / baselineRun.costUsd)console.log(`Tiered savings vs baseline — ${formatPercent(savings)}`)console.log()printScenarioDetails(baselineRun)printScenarioDetails(tieredRun)console.log('='.repeat(60))printFinalSummaryTable([baselineRun, tieredRun])if (savings < 0.4) {console.error(`ASSERTION FAILED: tiered savings ${Math.round(savings * 100)}% < 40%`)process.exit(1)}console.log(`Savings assertion — OK (${formatPercent(savings)} >= 40.0%)`)console.log('\nDone.')
// 企业服务
要把它用到生产环境?
open-multi-agent 采用 MIT 许可、可自行免费运行。当你需要在期限内交付、集成,或获得支持时,元定义科技(YuanASI)提供商业交付与支持。