flare-agent Bài 8: Multi-Agent — Agent gọi Agent

Xây dựng @flare-agent/multi-agent — AgentNetwork và HandoffAgent cho phép agent gọi agent khác như tool, tạo hệ thống multi-agent linh hoạt trên Cloudflare.

Nghe bài viết này dưới dạng podcast

flare-agent Bài 8: Multi-Agent — Agent gọi Agent

Series: Build Your Own AI Agent Framework trên Cloudflare Bài: 8 / 9 — @flare-agent/multi-agent


Tại sao cần Multi-agent?

Single agent phù hợp cho task đơn giản. Nhưng khi task phức tạp hơn:

  • Quiz agent cần gọi Vocabulary agent để lấy từ phù hợp
  • Tutor agent cần gọi Progress agent để biết học sinh đang ở đâu
  • RAG agent cần gọi Search agent trước rồi mới Generate agent

Thay vì nhồi tất cả vào 1 agent lớn (context window phình to, khó debug), chia nhỏ thành nhiều agent chuyên biệt → mỗi agent làm tốt 1 việc.


Hai pattern chính

Pattern 1: Handoff (tuần tự)
Agent A → delegates to → Agent B → delegates to → Agent C

Pattern 2: Network (supervisor)
           Router Agent
          /      |      \
    Agent A  Agent B  Agent C

Cấu trúc package

packages/multi-agent/
  src/
    AgentHandoff.ts    # Agent A gọi Agent B
    AgentNetwork.ts    # Supervisor điều phối nhiều agents
    agentAsTool.ts     # Wrap agent thành tool
    index.ts

Pattern 1: Agent as Tool

Cách đơn giản nhất — wrap agent thành tool để agent khác gọi:

// src/agentAsTool.ts
import type { ToolDefinition, AgentContext } from '@flare-agent/types';
import type { Agent } from '@flare-agent/core';

export function agentAsTool(
  agent: Agent,
  config: {
    description: string;
    inputSchema?: Record<string, unknown>;
  }
): ToolDefinition {
  return {
    schema: {
      name: agent.name,
      description: config.description,
      parameters: {
        type: 'object',
        properties: {
          input: {
            type: 'string',
            description: 'Message gửi cho agent',
            ...(config.inputSchema ?? {}),
          },
          sessionId: {
            type: 'string',
            description: 'Session ID để share context',
          },
        },
        required: ['input'],
      },
    },
    execute: async ({ input, sessionId }, ctx: AgentContext) => {
      // Agent con dùng cùng env nhưng session riêng
      const subCtx: AgentContext = {
        ...ctx,
        sessionId: sessionId ?? `${ctx.sessionId}:${agent.name}`,
      };
      const result = await agent.run(input, subCtx);
      return result.output;
    },
  };
}

Dùng như này:

import { Agent, tool } from '@flare-agent/core';
import { agentAsTool } from '@flare-agent/multi-agent';

// Agent chuyên tìm từ vựng
const vocabularyAgent = new Agent({
  name: 'vocabulary-specialist',
  model: { provider: 'groq', model: 'llama-3.3-70b-versatile' },
  systemPrompt: 'Bạn chuyên về từ vựng tiếng Anh. Trả về định nghĩa, ví dụ, và mức độ khó.',
}).use(searchVocabularyTool);

// Agent chuyên đánh giá câu trả lời
const evaluatorAgent = new Agent({
  name: 'answer-evaluator',
  model: { provider: 'workersai', model: '@cf/meta/llama-3.3-70b-instruct-fp8-fast' },
  systemPrompt: 'Bạn đánh giá câu trả lời của học sinh. Cho điểm và giải thích.',
});

// Tutor agent gọi cả 2 agent trên như tool
const tutorAgent = new Agent({
  name: 'tutor',
  model: { provider: 'groq', model: 'llama-3.3-70b-versatile' },
  systemPrompt: 'Bạn là gia sư dạy từ vựng tiếng Anh.',
}).use(
  agentAsTool(vocabularyAgent, {
    description: 'Tìm thông tin chi tiết về một từ vựng',
  }),
  agentAsTool(evaluatorAgent, {
    description: 'Đánh giá câu trả lời của học sinh',
  })
);

Pattern 2: Agent Handoff

Agent A chuyển toàn bộ context sang Agent B:

// src/AgentHandoff.ts
import type { AgentContext, AgentResult } from '@flare-agent/types';
import type { Agent } from '@flare-agent/core';

export interface HandoffConfig {
  // Điều kiện để handoff
  shouldHandoff: (
    result: AgentResult,
    ctx: AgentContext
  ) => { handoff: true; toAgent: string; input: string } | { handoff: false };
}

export class AgentHandoff {
  private agents = new Map<string, Agent>();

  register(agent: Agent): this {
    this.agents.set(agent.name, agent);
    return this;
  }

  async run(
    entryAgent: string,
    input: string,
    ctx: AgentContext,
    maxHandoffs = 5
  ): Promise<AgentResult> {
    let currentAgentName = entryAgent;
    let currentInput = input;
    let handoffCount = 0;

    while (handoffCount < maxHandoffs) {
      const agent = this.agents.get(currentAgentName);
      if (!agent) throw new Error(`Agent "${currentAgentName}" not found`);

      const result = await agent.run(currentInput, ctx);

      // Kiểm tra xem agent có muốn handoff không
      // Agent signal handoff qua output format đặc biệt
      const handoffSignal = this.parseHandoffSignal(result.output);

      if (!handoffSignal) {
        // Không có handoff → đây là final result
        return result;
      }

      // Handoff sang agent khác
      currentAgentName = handoffSignal.toAgent;
      currentInput = handoffSignal.input;
      handoffCount++;
    }

    throw new Error(`Max handoffs (${maxHandoffs}) exceeded`);
  }

  // Parse handoff signal từ output
  // Agent có thể emit: HANDOFF:{"to":"agent-name","input":"..."}
  private parseHandoffSignal(
    output: string
  ): { toAgent: string; input: string } | null {
    const match = output.match(/HANDOFF:(\{.*?\})/);
    if (!match) return null;
    try {
      const { to, input } = JSON.parse(match[1]);
      return { toAgent: to, input };
    } catch {
      return null;
    }
  }
}

Pattern 3: Agent Network (Supervisor)

Router agent quyết định agent nào sẽ xử lý:

// src/AgentNetwork.ts
import type { AgentContext } from '@flare-agent/types';
import type { Agent } from '@flare-agent/core';
import { agentAsTool } from './agentAsTool';

export interface NetworkConfig {
  name: string;
  // Router dùng model nào để quyết định
  routerModel: { provider: string; model: string };
  agents: Agent[];
  systemPrompt?: string;
}

export class AgentNetwork {
  private routerAgent: Agent;

  constructor(config: NetworkConfig) {
    // Router agent biết về tất cả agents như tools
    const agentTools = config.agents.map((a) =>
      agentAsTool(a, {
        description: `Delegate task to ${a.name} agent`,
      })
    );

    this.routerAgent = new Agent({
      name: config.name,
      model: config.routerModel as any,
      systemPrompt:
        config.systemPrompt ??
        `Bạn là supervisor điều phối các agents sau:\n` +
        config.agents
          .map((a) => `- ${a.name}`)
          .join('\n') +
        `\nPhân tích request và delegate cho agent phù hợp nhất.`,
      maxIterations: 15,
    }).use(...agentTools);
  }

  async run(input: string, ctx: AgentContext) {
    return this.routerAgent.run(input, ctx);
  }
}

Dùng trong edu-ai-platform:

import { AgentNetwork } from '@flare-agent/multi-agent';

const eduNetwork = new AgentNetwork({
  name: 'edu-supervisor',
  routerModel: { provider: 'groq', model: 'llama-3.3-70b-versatile' },
  agents: [
    vocabularyAgent,    // tìm và giải thích từ
    quizAgent,          // tạo và chấm quiz
    progressAgent,      // xem tiến độ học
    motivationAgent,    // động viên khi học sinh nản
  ],
});

// Network tự quyết định gọi agent nào
const result = await eduNetwork.run(
  'Tôi muốn ôn lại những từ khó nhất tuần này',
  { sessionId, userId, env }
);

Lưu ý quan trọng

Context window: Mỗi agent trong network có context riêng. Nếu muốn share thông tin giữa agents, dùng sessionId chung và đọc từ memory.

Rate limit: Multi-agent = nhiều LLM calls hơn. Với Groq free tier, cần đặc biệt chú ý — dùng RateLimitManager từ bài trước cho mỗi provider.

Cost: Mỗi agent call tốn tokens. Router agent cũng tốn tokens để quyết định. Với free tier, dùng model nhẹ hơn cho router (WorkersAI) và model mạnh cho task agents (Groq).

const eduNetwork = new AgentNetwork({
  name: 'edu-supervisor',
  routerModel: {
    provider: 'workersai',  // ← model nhẹ cho routing
    model: '@cf/meta/llama-3.1-8b-instruct',
  },
  agents: [ ... ],  // agents dùng Groq cho task nặng
});

Checklist

  • Tạo packages/multi-agent/
  • Test agentAsTool với 2 agents đơn giản
  • Test AgentNetwork với 3 agents
  • Verify rate limit không bị hit khi chạy network

Bài tiếp theo: Bài 9 — Observability & Tracing: Biết agent đang làm gì

Chưa có bình luận

Để lại bình luận

Bình luận sẽ được phê duyệt trước khi hiển thị.

Nhận bài viết mới

Mình sẽ gửi email khi có bài mới. Không spam.