Series 4 - Bài 4: Agent Communication Patterns

Agents communicate theo ba pattern: Push, Pull, và Event-driven. Mỗi pattern có coupling, reliability và latency khác nhau. Bài này phân tích khi nào dùng cái nào.

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

Agents cần communicate với nhau. Cách chúng communicate quyết định coupling, reliability, và latency của toàn bộ system.

Có ba communication pattern chính, mỗi cái phù hợp với một loại workload khác nhau.

Pattern 1: Push (Direct Call)

 Push Pattern:

 Orchestrator ─────────▶ Worker
   │        call(task)          │
   │                            │ execute
   │         result             │
   ◄────────────────────────── │
   │ blocking wait               │
   │ (orchestrator blocks        │
   │  until worker done)         │

Orchestrator gọi worker trực tiếp và đợi kết quả. Đơn giản nhưng synchronous.

Phù hợp khi:

  • Tasks sequential, kết quả này là input của task sau
  • Task kết thúc nhanh (<30s)
  • Cần có kết quả trước khi continue
  • Dễ debug và trace

Không phù hợp khi:

  • Worker có thể chạy rất lâu (orchestrator bị block)
  • Nhiều workers cần chạy parallel (push sequential không tận dụng)
Implementation sketch:

class Orchestrator:
  async delegateAndWait(worker, task) {
    const result = await worker.execute(task); // blocking
    return result;
  }

  async run(mainTask) {
    const analysis = await this.delegateAndWait(
      this.backendWorker, analysisTask);

    // Chỉ continue sau khi analysis xong
    const implementation = await this.delegateAndWait(
      this.backendWorker,
      { ...implTask, context: analysis });
  }

Pattern 2: Pull (Polling)

 Pull Pattern:

 Orchestrator                    Worker
   │                              │
   │── submit(task) ────────────▶│
   │   returns task_id            │ chạy async
   │                              │
   │── doOtherWork() ──────────▶│
   │   (không bị block)          │ (vẫn chạy)
   │                              │
   │── poll(task_id) ──────────▶│
   ◄──────────────────────────┘
   status: "pending" | "done" | result

Orchestrator submit task và nhận task_id. Tiếp tục làm việc khác. Poll định kỳ cho đến khi xong.

Phù hợp khi:

  • Workers chạy lâu (phut đến giờ)
  • Orchestrator có thể làm việc khác trong khi đợi
  • Nhiều tasks parallel, orchestrator monitor tất cả

Không phù hợp khi:

  • Task nhanh — polling overhead vượt quata lợi
  • Cần real-time notification ngay khi xong
Implementation sketch:

class Orchestrator:
  async runParallel(tasks) {
    // Submit tất cả tasks
    const submissions = await Promise.all(
      tasks.map(t => this.submit(t))
    );

    // Poll cho đến khi tất cả xong
    const results = [];
    while (submissions.some(s => s.status !== 'done')) {
      await sleep(2000); // poll mỗi 2 giây
      for (const submission of submissions) {
        if (submission.status !== 'done') {
          submission.status = await this.poll(submission.task_id);
        }
      }
    }
    return results;
  }

Pattern 3: Event-driven (Message Queue)

 Event-driven Pattern:

 Orchestrator             Queue              Workers
   │                       │                  │
   │─ publish(task_A) ───▶│                  │
   │─ publish(task_B) ───▶│                  │
   │─ publish(task_C) ───▶│                  │
   │                       │── worker1 poll ▶│ Worker 1
   │                       │── worker2 poll ▶│ Worker 2
   │                       │── worker3 poll ▶│ Worker 3
   │                       │                  │ execute
   │                       │◄─ result A ────│
   │◄─ subscribe(result) ──│◄─ result B ────│
   │                       │                  │

Orchestrator publish tasks vào queue. Workers tự pull tasks khi available. Results publish lại vào queue.

Phù hợp khi:

  • High throughput, nhiều tasks concurrently
  • Worker pool có thể scale independently
  • Tasks có khác nhau về priority
  • System phải survive partial failures

Không phù hợp khi:

  • ít tasks, overhead của queue không xứng
  • Cần strict ordering
  • Simple agent system không cần production scale

So sánh trực tiếp

                Push     Pull    Event-driven
──────────────────────────────────────────────
  Complexity      Low      Medium    High
  Latency        Low      Medium    Low-Medium
  Throughput     Low      Medium    High
  Debugging      Easy     Medium    Hard
  Fault toler.   Low      Low       High
  Scalability    Low      Medium    High

Pattern Selection: Guideline thực tế

Bạn đang build gì?

  CLI agent tool, internal use:
  → Push (simple, debụg dễ, phù hợp)

  API service với long-running tasks:
  → Pull (async không block)

  High-scale enterprise system:
  → Event-driven (khi thực sự cần)

Kết luận

  • Ba patterns: Push (sync blocking), Pull (async polling), Event-driven (queue-based).
  • Push: đơn giản nhất, phù hợp cho sequential tasks và small systems.
  • Pull: cho long-running tasks, orchestrator không bị block, monitor parallel.
  • Event-driven: high throughput, scalable, resilient — nhưng complex, debug khó.
  • Không có "best" pattern — chọn theo workload characteristics và system requirements.
  • Bắt đầu với Push. Move to Pull khi có blocking problem. Event-driven chỉ khi thực sự cần.

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.