🇬🇧 Read the English version of this article on tanhdev.com

Trả lời nhanh (Answer-first): Dapr Workflows điều phối hiệu quả các tác vụ AI Agent kéo dài trong Go bằng cách cung cấp trạng thái bền vững (durable state), cơ chế thử lại (retries) và bù trừ tác vụ (Saga compensation) rõ ràng. Giữ các lệnh gọi LLM và công cụ bên ngoài trong các activity idempotent — thay vì trong orchestrator — để khi replay, hệ thống tái cấu trúc lại trạng thái quyết định mà không lặp lại các tác dụng phụ (side effects) đã hoàn tất.

Khi kiến trúc multi-agent phát triển vượt khỏi các API wrapper đơn lẻ, các khối lượng công việc doanh nghiệp đang dịch chuyển mạnh mẽ sang các workflow tự trị kéo dài. Tiếp nối chuỗi bài viết Kiến trúc Hệ thống Agentic, cùng các phân tích gần đây về Modular Monolith cho AI AgentZero-Trust AI Swarm, các đội ngũ platform hiện phải đối mặt với bài toán Độ bền bỉ trong thực thi (Execution Durability): một tác vụ có thể kết hợp suy luận nhiều bước, gọi công cụ bên ngoài và phê duyệt từ con người kéo dài trong nhiều phút.

Bài viết Tech Radar này tập trung vào ranh giới production giữa một yêu cầu agent và một business workflow bền vững. Để tham khảo hướng dẫn triển khai Go hoàn chỉnh cho các activity bù trừ, xem bài viết Dapr Workflow Saga Tutorial. Để so sánh các workflow engine và ràng buộc tính định hình (determinism), xem Hướng dẫn Temporal Orchestrated Saga.

1. Vấn đề Thực thi Dang dở của Agent (Dangling Agent Execution)

Một yêu cầu HTTP hoặc gRPC đồng bộ là một mô hình sở hữu kém cho một tác vụ kéo dài nhiều phút. Client có thể mất kết nối, ingress timeout có thể hết hạn, hoặc pod có thể bị tái khởi động (evict) trong khi các công cụ bên ngoài vẫn đang chạy. Platform bắt buộc phải phân biệt giữa một kết nối client bị đứt và vòng đời của câu lệnh nghiệp vụ bên dưới.

Các kịch bản thất bại phổ biến bao gồm:

  • Độ trễ không thể dự đoán (Unpredictable latency): Các bước duyệt web, truy xuất dữ liệu, sinh mã, xác thực và phê duyệt có thể mất hàng phút thay vì hàng miligiây như một API request thông thường.
  • Tác dụng phụ bị trùng lặp (Duplicate side effects): Caller thử lại sau khi hết thời gian chờ (timeout) trong khi lệnh gọi công cụ ban đầu vẫn đang chạy, dẫn đến nguy cơ tạo 2 ticket, 2 tài nguyên cloud hoặc 2 giao dịch thanh toán.
  • Mất trạng thái trong bộ nhớ (Lost in-memory state): Pod tái khởi động sẽ xóa sạch context cục bộ của Goroutine trừ khi tiến trình và đầu ra được lưu trữ bền vững bên ngoài process.

[!IMPORTANT] Một durable workflow tự nó không làm cho agent an toàn tuyệt đối. Ủy quyền (Authorization), xác thực đầu vào, hạn ngạch thử lại (retry budget), danh sách công cụ cho phép (allowlist) và kiểm toán log (audit logging) vẫn là các trách nhiệm độc lập.

2. Dapr Workflows Tự Động Lưu Trữ Bền Vững Những Gì?

Dapr Workflows sử dụng mô hình điều phối bền vững dựa trên khả năng phát lại (replay-based durable orchestration). Ứng dụng sẽ host một workflow worker, trong khi các dịch vụ Dapr runtime và thành phần Dapr state-store được cấu hình sẽ lưu trữ lịch sử điều phối và phối hợp thực thi. Dapr hỗ trợ cấu hình state store với PostgreSQL hoặc Redis; mã nguồn ứng dụng không sử dụng GORM làm cơ chế lưu trữ bền vững cho workflow.

Khi phục hồi sự cố, runtime sẽ phát lại (replay) lịch sử điều phối để dựng lại trạng thái quyết định của orchestrator. Một activity đã hoàn thành sẽ trả về kết quả đã ghi lại thay vì được gọi lại một lần nữa. Một activity chưa hoàn thành có thể được thử lại, do đó các lệnh gọi LLM và công cụ bên ngoài vẫn đòi hỏi tính idempotent ở tầng ứng dụng và cơ chế xử lý kết quả bền vững.

Đây cũng là lý do mã nguồn orchestrator phải duy trì tính định hình (deterministic). Hãy đưa toàn bộ I/O mạng, lệnh gọi model, ghi database và các lệnh không định hình vào bên trong các activity. Giữ orchestrator tập trung duy nhất vào logic ra quyết định theo thứ tự, chờ đợi, thử lại và bù trừ.

Dapr Actors giải quyết một bài toán khác: điều phối single-key cho một entity hoặc trạng thái của một agent. Sử dụng Actor khi một định danh cần thay đổi trạng thái tuần tự; sử dụng Workflow khi một quy trình trải dài trên nhiều bước, bộ đếm thời gian, thử lại, phê duyệt và bù trừ. Chúng có thể kết hợp với nhau, nhưng cái này không phải là chi tiết triển khai của cái kia.

3. Điều Phối Tool Idempotent & Cơ Chế Bù Trừ Explicit (Saga Compensation)

Các lệnh gọi công cụ của Agent là các tác dụng phụ phân tán (distributed side effects), không phải là hàm thuần túy (pure functions). Một agent khởi tạo hạ tầng, thay đổi bản ghi CRM hoặc thực hiện thanh toán phải đảm bảo câu lệnh nghiệp vụ đó an toàn dưới các lượt thử lại.

Hãy tạo một idempotency key do ứng dụng quản lý từ workflow instance ID, business command ID và tên activity. Lưu trữ key đó cùng với tác dụng phụ hạ nguồn và trả về kết quả trước đó khi câu lệnh trùng lặp được gửi lại. Đừng giả định rằng token InstanceID + StepID do framework cung cấp sẽ tự động bảo vệ mọi dependency.

Cơ chế bù trừ (Compensation) cũng là một logic nghiệp vụ rõ ràng (explicit business logic). Nếu khởi tạo tài nguyên thành công nhưng deployment thất bại, hãy gọi activity hủy tài nguyên theo thứ tự ngược lại; nếu bù trừ thất bại, hãy cảnh báo một sự cố vận hành có thể xử lý được thay vì thử lại vô tận một cách im lặng.

if err := ctx.CallActivity(DeployService, workflow.ActivityInput(input)).Await(&result); err != nil {
    var compensationResult DeprovisionResult
    compensationErr := ctx.CallActivity(
        DeprovisionDatabase,
        workflow.ActivityInput(provisioned),
    ).Await(&compensationResult)
    if compensationErr != nil {
        return nil, fmt.Errorf("deploy failed: %v; compensation failed: %w", err, compensationErr)
    }
    return nil, err
}

Đoạn code trên thể hiện ranh giới sở hữu: orchestrator quyết định kịch bản bù trừ nào là bắt buộc, trong khi mỗi activity sở hữu lệnh gọi API idempotent và bản ghi nghiệp vụ bền vững của nó.

4. Ranh Giới Service Go Cho Các Tác Vụ Agent Kéo Dài

Đối với một service Go, hãy cung cấp một API tiếp nhận lệnh đã được xác thực và trả về một workflow identifier thay vì giữ mở kết nối HTTP. Client sau đó có thể truy vấn endpoint trạng thái hoặc nhận Server-Sent Events (SSE) trong khi workflow tiếp tục xử lý.

Sự phân tách kiến trúc sạch sẽ như sau:

Tầng (Layer)Trách nhiệm (Responsibility)
apiXác thực caller, kiểm tra câu lệnh, khởi chạy workflow và trả về 202 Accepted kèm workflow ID.
bizĐịnh nghĩa quy tắc quyết định workflow, interface activity, chính sách thử lại và thứ tự bù trừ.
dataTriển khai các adapter activity idempotent cho LLM provider, database và công cụ bên ngoài.
Dapr runtimeĐiều phối thực thi workflow và lịch sử bền vững thông qua các component đã cấu hình.

Đối với các đội ngũ sử dụng Kratos và Wire, hãy giữ Dapr client và các adapter activity ở ranh giới data/infrastructure; inject các business interface vào tầng transport. Điều này tránh việc nén chặt các HTTP handler vào chi tiết sidecar và giữ cho hành vi activity dễ dàng unit test.

5. Khung Quyết Định: Workflow, Actor hay Engine Chuyên Dụng?

Yêu cầuLựa chọn khởi đầu tốt nhấtLý do
Phản hồi chat hoàn tất trong vài giâyHTTP/gRPC requestKhông cần tiến trình lưu trữ bền vững.
Tác vụ agent nhiều bước có chờ đợi và thử lạiDapr WorkflowQuy trình cần lưu trữ lịch sử điều phối bền vững.
Điều phối trạng thái cho một agent, giỏ hàng hoặc entity keyDapr ActorMột định danh cần thay đổi trạng thái tuần tự.
Workflow phức tạp跨domain với team platform chuyên biệtĐánh giá Temporal hoặc workflow engine khácYêu cầu vận hành và hệ sinh thái có thể đòi hỏi một engine chuyên dụng.

Hãy lựa chọn cơ chế bền vững nhỏ nhất phù hợp với kịch bản thất bại của nghiệp vụ. Tính bền vững của Workflow không thể thay thế mô hình phân quyền, và một Actor không thể tự động bù trừ cho một quy trình đa dịch vụ.

FAQ

Dapr Workflow có tự động ngăn chặn việc thực thi công cụ trùng lặp không?

Kết quả activity đã hoàn thành có thể được phát lại từ lịch sử workflow, nhưng một activity chưa hoàn thành có thể bị thử lại. Hãy sử dụng idempotency key do ứng dụng sở hữu, lưu trữ kết quả hạ nguồn và đảm bảo mọi tác dụng phụ bên ngoài an toàn với việc phân phối trùng lặp.

Có nên chuyển mọi yêu cầu AI thành workflow không?

Không. Sử dụng request thông thường cho các tương tác đọc dữ liệu nhanh. Chỉ khởi chạy workflow khi tác vụ có trạng thái nghiệp vụ bền vững, thời gian chờ dài, thử lại, phê duyệt từ con người hoặc các hành động bù trừ cần sống sót qua sự cố ngắt kết nối client hoặc pod restart.

Tài liệu tham khảo