← 全部示例
// 生产控制

成本分层流水线

把同一条四阶段 runTasks() 流水线跑两次;在一条流水线内给每个智能体分配不同模型;通过 onTrace 采集各模型的 token 用量;按各 provider 的模型定价估算美元成本。

01 运行
OMA APIOpenMultiAgent
生产控制559 行

在仓库的克隆里运行这个文件:

terminal
npx tsx packages/core/examples/patterns/cost-tiered-pipeline.ts
前置条件
  • Set one of:
  • - ANTHROPIC_API_KEY
  • - OPENAI_API_KEY
展开完整同步源码 · 559 行

完整示例,从固定的 Framework commit 同步。

patterns/cost-tiered-pipeline.ts
/**
* 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 const
 
type PricedModel = keyof typeof PRICING
type 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: ProviderConfig
classifier: ProviderConfig
drafter: ProviderConfig
reviewer: ProviderConfig
}
type ScenarioResult = {
label: string
assignments: PipelineAssignments
agentResults: AgentRunSummary
usageByModel: UsageByModel
totalTokenUsage: TokenUsage
costUsd: number
elapsedMs: number
finalBrief: 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 constraints
Write 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. Audience
2. Risks
3. Differentiators
4. Open Questions
Write 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 angle
Write 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 the
recommendation 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: UsageByModel
handleTrace: (event: TraceEvent) => void
} {
const usageByModel: UsageByModel = {}
 
function handleTrace(event: TraceEvent): void {
if (event.type !== 'llm_call' || !isPricedModel(event.model)) return
usageByModel[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 sum
const 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: string
tieredLabel: string
endpointLabel: string
flagship: ProviderConfig
mid: ProviderConfig
cheap: ProviderConfig
} {
switch (provider) {
case 'openai': {
const apiKey = process.env.OPENAI_API_KEY
if (!apiKey) {
throw new Error('LLM_PROVIDER=openai requires OPENAI_API_KEY.')
}
 
const baseURL = process.env.OPENAI_BASE_URL
 
return {
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_KEY
if (!apiKey) {
throw new Error('LLM_PROVIDER=anthropic requires ANTHROPIC_API_KEY.')
}
 
const baseURL = process.env.ANTHROPIC_BASE_URL
 
return {
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)) continue
const costUsd =
(usage.input_tokens / 1_000_000) * PRICING[model].input +
(usage.output_tokens / 1_000_000) * PRICING[model].output
 
console.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: string
description: string
assignee: string
dependsOn?: 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() - startedAt
 
if (!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.')
在 GitHub 查看 / 编辑
// 企业服务

要把它用到生产环境?

open-multi-agent 采用 MIT 许可、可自行免费运行。当你需要在期限内交付、集成,或获得支持时,元定义科技(YuanASI)提供商业交付与支持。

// 直接联系

把 Open Multi-Agent 用进真实业务

联系框架作者本人,帮你梳理 AI 落地目标、让 AI 真正与业务结合

可提供的工程服务
S-01

AI Agent 定制开发

业务梳理、Agent 设计、Prompt 评估、生产部署、私有化与持续支持。

S-02

多智能体系统集成

多 Agent 架构编排、RAG、CRM / ERP / API 对接、性能与稳定性调优。

S-03

企业 AI 咨询

AI 场景评估、技术选型、POC、ROI 估算与落地路线规划。