AgentSkillsCN

Ai Agent Orchestration Patterns

AI 代理编排模式

SKILL.md

AI Agent Orchestration Patterns

Patterns for building and orchestrating AI agents effectively.

When to Use

  • Building multi-agent systems
  • Designing agent architectures
  • Implementing agent communication patterns
  • Debugging agent workflows
  • Optimizing agent performance and costs

Core Architecture Patterns

Single Agent with Tools

Use when: Task is well-defined, tools are sufficient

code
User Request → Agent → [Tool Selection] → Tool Execution → Response

Implementation:

typescript
const agent = {
  systemPrompt: "You are a helpful assistant with access to tools.",
  tools: [searchTool, calculatorTool, fileTool],
  maxIterations: 10,
};

async function run(input: string) {
  let context = { messages: [{ role: 'user', content: input }] };

  while (!context.done && context.iterations < agent.maxIterations) {
    const response = await llm.chat(context.messages, agent.tools);

    if (response.toolCalls) {
      for (const call of response.toolCalls) {
        const result = await executeTool(call);
        context.messages.push({ role: 'tool', content: result });
      }
    } else {
      context.done = true;
      return response.content;
    }
    context.iterations++;
  }
}

Supervisor Pattern

Use when: Complex tasks requiring delegation to specialists

code
User Request → Supervisor Agent → [Routing Decision]
                                       ↓
              ┌──────────────────────────────────────┐
              ↓              ↓              ↓        ↓
         Researcher    Coder         Writer    Reviewer
              ↓              ↓              ↓        ↓
              └──────────────────────────────────────┘
                                       ↓
                              Supervisor Synthesis
                                       ↓
                                   Response

Key decisions:

  • Supervisor chooses which agent(s) to invoke
  • Can be parallel (all at once) or sequential (one at a time)
  • Supervisor synthesizes results

Pipeline Pattern

Use when: Task has clear sequential stages

code
Input → Stage 1 → Stage 2 → Stage 3 → Output
        (Plan)    (Execute)  (Review)

Example - Code Generation Pipeline:

typescript
const pipeline = [
  { name: 'planner', prompt: 'Create implementation plan' },
  { name: 'coder', prompt: 'Implement the plan' },
  { name: 'reviewer', prompt: 'Review for bugs and improvements' },
  { name: 'tester', prompt: 'Write tests for the implementation' },
];

async function runPipeline(input: string) {
  let context = input;
  for (const stage of pipeline) {
    context = await runAgent(stage.name, stage.prompt, context);
  }
  return context;
}

Parallel Execution Pattern

Use when: Independent subtasks can be processed simultaneously

typescript
async function parallelAgents(tasks: Task[]) {
  const results = await Promise.all(
    tasks.map(task => runAgent(task.agent, task.input))
  );
  return synthesize(results);
}

Reflection Pattern

Use when: Quality is critical, self-improvement needed

code
Initial Response → Critique Agent → Refined Response → [Iterate?]

Implementation:

typescript
async function reflectiveAgent(input: string, maxReflections = 3) {
  let response = await generateResponse(input);

  for (let i = 0; i < maxReflections; i++) {
    const critique = await critiqueResponse(response);

    if (critique.satisfactory) break;

    response = await improveResponse(response, critique.feedback);
  }

  return response;
}

Communication Patterns

Message Passing

Agents communicate through structured messages:

typescript
interface AgentMessage {
  from: string;
  to: string;
  type: 'request' | 'response' | 'update';
  content: unknown;
  metadata: {
    timestamp: number;
    traceId: string;
  };
}

Shared State

Agents share a common context:

typescript
interface SharedContext {
  goal: string;
  currentState: unknown;
  history: Message[];
  artifacts: Map<string, unknown>;
}

// Each agent reads/writes to shared context
async function agentStep(agent: Agent, context: SharedContext) {
  const result = await agent.run(context);
  context.history.push({ agent: agent.name, result });
  context.currentState = result.newState;
}

Event-Driven

Agents react to events:

typescript
const eventBus = new EventEmitter();

// Agent subscribes to relevant events
eventBus.on('code:generated', async (code) => {
  const review = await reviewerAgent.review(code);
  eventBus.emit('code:reviewed', review);
});

eventBus.on('code:reviewed', async (review) => {
  if (!review.approved) {
    eventBus.emit('code:needsRevision', review.feedback);
  }
});

Cost Optimization

Model Routing

Use appropriate models for each task:

typescript
const modelRouter = {
  planning: 'claude-3-opus',      // Complex reasoning
  coding: 'claude-3-sonnet',      // Balance of capability/cost
  formatting: 'claude-3-haiku',   // Simple tasks
  review: 'claude-3-sonnet',
};

async function routedCall(task: string, input: string) {
  const model = modelRouter[task] ?? 'claude-3-sonnet';
  return llm.chat(model, input);
}

Caching

Cache expensive operations:

typescript
const cache = new Map<string, unknown>();

async function cachedAgent(input: string) {
  const key = hash(input);
  if (cache.has(key)) return cache.get(key);

  const result = await runExpensiveAgent(input);
  cache.set(key, result);
  return result;
}

Early Termination

Stop when goal is achieved:

typescript
async function goalOrientedLoop(goal: string) {
  while (true) {
    const state = await getState();
    const evaluation = await evaluateGoal(goal, state);

    if (evaluation.achieved) return state;
    if (evaluation.impossible) throw new Error('Goal unreachable');

    await takeAction(evaluation.suggestedAction);
  }
}

Error Handling

Retry with Backoff

typescript
async function retryAgent(fn: () => Promise<T>, maxRetries = 3) {
  for (let i = 0; i < maxRetries; i++) {
    try {
      return await fn();
    } catch (error) {
      if (i === maxRetries - 1) throw error;
      await sleep(Math.pow(2, i) * 1000);
    }
  }
}

Fallback Agents

typescript
const agents = [primaryAgent, fallbackAgent1, fallbackAgent2];

async function resilientRun(input: string) {
  for (const agent of agents) {
    try {
      return await agent.run(input);
    } catch (error) {
      console.log(`${agent.name} failed, trying next`);
    }
  }
  throw new Error('All agents failed');
}

Debugging

Tracing

typescript
interface Trace {
  traceId: string;
  spans: Span[];
}

interface Span {
  name: string;
  startTime: number;
  endTime: number;
  input: unknown;
  output: unknown;
  metadata: Record<string, unknown>;
}

Logging

typescript
function instrumentAgent(agent: Agent) {
  return {
    ...agent,
    async run(input: string) {
      console.log(`[${agent.name}] Input:`, input);
      const start = Date.now();
      const result = await agent.run(input);
      console.log(`[${agent.name}] Output (${Date.now() - start}ms):`, result);
      return result;
    },
  };
}

Anti-Patterns

  1. Infinite loops - Always have max iterations
  2. No error handling - Agents fail; handle gracefully
  3. Tight coupling - Agents should be independent
  4. Over-orchestration - Sometimes one agent is enough
  5. Ignoring costs - Monitor token usage, use appropriate models