Bỏ qua đến nội dung chính

From Chaos to Choreography: Multi-Agent Orchestration Patterns That Actually Work — Sandipan Bhaumik

TL;DR

  • Xây dựng hệ thống AI đa tác nhân thực chất là thách thức của hệ thống phân tán, nơi độ phức tạp tăng theo cấp số nhân chứ không phải tuyến tính, thường dẫn đến các vấn đề như điều kiện tranh chấp và dữ liệu cũ nếu không được thiết kế đúng.
  • Việc lựa chọn mô hình điều phối phù hợp giữa choreography (phi tập trung, dựa trên sự kiện) và orchestration (tập trung, có bộ điều phối) là rất quan trọng để quản lý các phụ thuộc và đảm bảo tính ổn định của hệ thống.
  • Để ngăn chặn race condition và đảm bảo khả năng kiểm tra, cần áp dụng immutable state snapshots with versioning cùng với data contract rõ ràng giữa các tác nhân, tạo ra lịch sử trạng thái minh bạch và dễ gỡ lỗi.

Choreography — event bus

event

event

event

Event bus

Agent A

Agent B

Agent C

Orchestration — central coordinator

Coordinator

Agent A

Agent B

Agent C

Điểm chính

  • Nhận diện hệ thống đa tác nhân là hệ thống phân tán: Hãy tiếp cận ngay từ đầu như một hệ thống phân tán, chuẩn bị cho sự gia tăng độ phức tạp theo cấp số nhân, các điểm lỗi tiềm ẩn và thách thức đồng bộ hóa.
  • Chọn mô hình điều phối một cách chiến lược: Sử dụng orchestration cho các quy trình làm việc phức tạp, ổn định, yêu cầu kiểm soát tập trung, gỡ lỗi dễ dàng và khả năng phục hồi lỗi mạnh mẽ. Chọn choreography cho các hệ thống tự trị cao, dựa trên sự kiện nếu có khả năng quan sát mạnh mẽ.
  • Triển khai quản lý trạng thái bất biến: Áp dụng immutable state snapshots with versioning trong đó mỗi tác nhân tạo ra một phiên bản trạng thái mới, không thể sửa đổi, được ghi vào append-only log, nhằm loại bỏ race conditionlost update.
  • Thực thi hợp đồng dữ liệu giữa các tác nhân: Định nghĩa và xác thực schema của dữ liệu được trao đổi giữa các tác nhân tại mỗi handoff để đảm bảo tính tương thích và ngăn ngừa lỗi do đầu vào/đầu ra không khớp.
  • Ưu tiên khả năng quan sát và phục hồi lỗi: Thiết kế hệ thống với bulletproof observability để theo dõi luồng sự kiện (đặc biệt trong choreography) và xây dựng các cơ chế phục hồi lỗi, thử lại và khả năng khôi phục/quay lại (dễ dàng hơn với orchestration).
  • Tránh trạng thái có thể thay đổi được chia sẻ trực tiếp: Nếu bắt buộc phải cập nhật cơ sở dữ liệu trực tiếp, hãy sử dụng rõ ràng các tính năng của cơ sở dữ liệu như transaction logs, serialized isolation levelsexplicit locks (select for update) để giảm thiểu race condition, vì cài đặt mặc định thường không đủ.
  • Tận dụng các mô hình lai cho nhu cầu cụ thể: Đối với các kịch bản yêu cầu cả quy trình làm việc phức tạp và tính tự trị cao của tác nhân, hãy xem xét mô hình lai như choreography kết hợp với saga patterns để bù đắp giao dịch.

Từ vựng

  • multi-agent AI systems — hệ thống AI đa tác nhân
  • race condition — điều kiện tranh chấp
  • choreography — (kiểu điều phối) choreography
  • orchestration — (kiểu điều phối) orchestration
  • state management — quản lý trạng thái
  • immutable state snapshots with versioning — ảnh chụp trạng thái bất biến với phiên bản
  • data contract — hợp đồng dữ liệu
  • observability — khả năng quan sát
  • shared mutable state — trạng thái có thể thay đổi được chia sẻ
  • append-only log — nhật ký chỉ thêm

Nội dung chi tiết

Giới thiệu và Vấn đề Phát sinh trong Hệ thống Đa Tác nhân

Xin chào mọi người, tôi là Sandy. Tôi đã có 18 năm kinh nghiệm xây dựng các hệ thống dữ liệu, trong đó phần lớn thời gian tập trung vào việc xây dựng và mở rộng các hệ thống dữ liệu phân tán trên đám mây. Tôi đã thực hiện công việc này cho các hệ thống đa người thuê (multi-tenant systems) của các công ty phần mềm và SaaS, sau đó là mở rộng nền tảng dữ liệu và AI trong các ngành được quản lý chặt chẽ như dịch vụ tài chính và y tế. Tôi đã học được rất nhiều về các hệ thống phân tán cấp độ sản phẩm (production-grade distributed systems) trong thời gian làm việc tại AWS và hiện tại là Databricks.

Trong hai năm qua, tôi đã triển khai các hệ thống AI đa tác nhân (multi-agent AI systems) vào sản xuất và tôi đã chứng kiến những kỹ sư xuất sắc lặp đi lặp lại những sai lầm tương tự. Họ nghĩ rằng việc thêm nhiều tác nhân (agent) cũng giống như thêm nhiều tính năng. Không phải vậy. Đây là việc xây dựng một hệ thống phân tán và hôm nay tôi sẽ chỉ cho bạn những mô hình (patterns) thực sự hiệu quả khi bạn thực hiện quá trình chuyển đổi này. Đây là những bài học tôi đã đúc rút được từ thực tế và hôm nay tôi muốn chia sẻ với các bạn.

Những gì chúng ta sẽ đề cập hôm nay bao gồm: Đầu tiên là vấn đề. Tôi sẽ chia sẻ một câu chuyện thực tế cơ bản về tình trạng race condition (điều kiện tranh chấp) và lý do tại sao sự phức tạp bùng nổ khi bạn chuyển từ một tác nhân lên năm tác nhân. Tôi sẽ nói về các mô hình điều phối (coordination patterns), cụ thể là choreographyorchestration, để điều phối các tác nhân. Chúng ta sẽ thảo luận về quản lý trạng thái (state management), phục hồi lỗi (failure recovery) và cách chúng ta có thể thiết kế để chống lỗi trong các hệ thống sản xuất. Sau đó, tôi sẽ chia sẻ một kiến trúc cấp độ sản phẩm (production-grade architecture) trông như thế nào một cách đơn giản nhất có thể. Tôi cũng sẽ trình bày một ví dụ về cách chúng tôi xây dựng điều này trên Databricks.

Vậy hãy cùng tìm hiểu sâu hơn. Bạn thấy đấy, một tác nhân hoạt động rất tốt. Bạn có LLM, một số lời nhắc (prompts), có thể là một pipeline tạo sinh có tăng cường truy xuất (retrieval augmented generation pipeline), và có thể một số lời gọi công cụ (tool calls). Bản demo trông tuyệt vời, lãnh đạo rất thích, bạn cảm thấy vui vẻ và nhóm của bạn cũng vậy. Và sau đó, bộ phận sản phẩm quay lại với một yêu cầu thay đổi mọi thứ: họ muốn thêm năm tác nhân nữa. Và đây là điều sẽ xảy ra: bạn nghĩ, "Ồ, tôi biết cách xây dựng các tác nhân, tôi sẽ thêm năm cái nữa." Ngoại trừ việc bây giờ bạn có các vấn đề về điều phối. Tác nhân A tạo ra dữ liệu mà tác nhân B cần; tác nhân C đang đợi cả tác nhân A và tác nhân B; tác nhân D vừa cập nhật trạng thái chia sẻ mà tác nhân B đang đọc; và tác nhân E vừa gặp sự cố và làm sập toàn bộ workflow. Đây không còn là một vấn đề AI nữa. Đây là một vấn đề của hệ thống phân tán, và hầu hết các bạn không đăng ký để trở thành kỹ sư hệ thống phân tán.

Vấn đề Thực tế: Race Condition và Độ phức tạp Tăng vọt

Hãy để tôi kể cho bạn nghe về một triển khai sản xuất đã đi sai hướng rất nhiều. Chúng tôi đã xây dựng một hệ thống ra quyết định tín dụng (credit decisioning system) cho một công ty dịch vụ tài chính. Tác nhân đầu tiên, tính toán điểm tín dụng (credit score calculation), hoạt động hoàn hảo. Nó hoạt động rất tốt trong các bản demo, và hai tuần trong sản xuất không gặp vấn đề gì. Sau đó, chúng tôi đã thêm bốn tác nhân nữa: xác minh thu nhập (income verification), đánh giá rủi ro (risk assessment), phát hiện gian lận (fraud detection) và phê duyệt cuối cùng (final approval). Chúng tôi đã triển khai cả năm tác nhân trong ba ngày.

Chúng tôi bắt đầu thấy những trường hợp phê duyệt kỳ lạ. 20% các quyết định có xếp hạng rủi ro không chính xác; những khách hàng đáng lẽ phải bị gắn cờ lại được phê duyệt. Nhóm kinh doanh hoảng loạn. Chúng tôi mất hai ngày để tìm ra điều gì đang xảy ra. Tác nhân tính điểm tín dụng đã tính ra điểm 750 và ghi vào cơ sở dữ liệu. Tác nhân đánh giá rủi ro, mặt khác, đọc từ cơ sở dữ liệu 500 mili giây sau đó và nhận được điểm 680 cho cùng một khách hàng. Tại sao lại xảy ra điều đó? Bởi vì chúng tôi có một lớp cache cho hồ sơ khách hàng. Việc ghi vào PostgreSQL thành công nhưng cache không được invalidate. Tác nhân rủi ro đọc từ cache và nhận dữ liệu cũ (stale data). Nó đã sử dụng điểm sai và đưa ra quyết định sai.

Đây là một vấn đề kinh điển của hệ thống phân tán. Chúng tôi có một lớp cache giữa các tác nhân và cơ sở dữ liệu. Việc cache invalidation thất bại và tác nhân đang đọc các giá trị cũ. Race condition không nằm ở cơ sở dữ liệu; nó nằm ở kiến trúc. Nhiều tác nhân chia sẻ cache mà không có sự điều phối về việc cache invalidation. Việc này đã khiến chúng tôi mất khá nhiều thời gian để tìm ra mô hình (pattern), gây ra sự chậm trễ trong việc triển khai và dẫn đến các quyết định sai lầm.

Và đây là bài học chúng tôi đã học được: vấn đề tất nhiên không nằm ở mô hình (model). Vấn đề không nằm ở lời nhắc (prompts). Vấn đề là chúng tôi đã xây dựng một hệ thống phân tán mà không có tư duy hệ thống phân tán. Và đó là điều làm hỏng các dự án đa tác nhân, không phải AI kém mà là kiến trúc tồi.

Bây giờ tôi sẽ chỉ cho bạn kiến trúc hoạt động. Chúng ta cũng sẽ xem xét dữ liệu. Chúng ta sẽ xem xét một kiến trúc cấp độ sản phẩm (production-grade architecture), nhưng trước tiên, hãy hiểu tại sao sự phức tạp này lại bùng nổ nhanh chóng như vậy.

Khi bạn chuyển từ một hệ thống một tác nhân sang một hệ thống đa tác nhân, giả sử năm tác nhân, nó không chỉ khó hơn gấp năm lần mà còn phức tạp hơn gấp 25 lần. Độ phức tạp điều phối (coordination complexity) tăng theo cấp số nhân. Một tác nhân không có vấn đề điều phối nào. Hai tác nhân có ít nhất một kết nối. Năm tác nhân có ít nhất 10 kết nối và điều phối tiềm năng. Mỗi kết nối là một điểm lỗi (failure point), một race condition, một vấn đề đồng bộ hóa trạng thái (state synchronization problem). Bạn không chỉ xây dựng năm tác nhân; bạn đang xây dựng một vấn đề điều phối trên nhiều mối quan hệ và khả năng có nhiều chế độ lỗi (failure modes). Và đó là lý do tại sao sự phức tạp tăng lên rất, rất nhanh chóng.

Các Mô hình Điều phối: ChoreographyOrchestration

Bây giờ tôi sẽ chỉ cho bạn hai mô hình quan trọng. Mô hình đầu tiên là về cách điều phối nhiều tác nhân. Sau đó, chúng ta sẽ nói về cách bạn có thể quản lý trạng thái (state) và sau đó chúng ta sẽ nói về cách chúng ta có thể phục hồi và thiết kế để chống lại lỗi (design for failure). Những mô hình này đến từ nhiều năm làm việc với các hệ thống phân tán và tôi có thể áp dụng trực tiếp chúng vào hệ thống AI đa tác nhân. Khi bạn nắm vững những nguyên tắc cơ bản, rất khó để bỏ lỡ những mô hình này khi bạn xây dựng kiến trúc AI đa tác nhân.

Quyết định đầu tiên bạn cần đưa ra là về choreography hay orchestration. Đây là hai mô hình cơ bản để điều phối phân tán (distributed coordination). Choreography có nghĩa là các tác nhân điều phối thông qua các sự kiện (events); chúng được phân cấp (decentralized), tự trị (autonomous). Orchestration có nghĩa là một bộ điều phối trung tâm (central coordinator) quản lý workflow; đây là tập trung (centralized) và được kiểm soát. Hầu hết các nhóm chọn một cách bản năng và sau đó hối tiếc. Hãy để tôi chỉ cho bạn khi nào nên sử dụng từng loại.

Choreography

Hãy bắt đầu với choreography. Choreography là dựa trên sự kiện (event-driven). Tác nhân nghiên cứu (research agent) hoàn thành nghiên cứu và công bố một sự kiện research completed lên một message bus. Tác nhân B đăng ký message bus đó và lắng nghe sự kiện đó khi nó quan tâm. Tác nhân phân tích (analysis agent) đăng ký loại sự kiện đó, nhận nó, thực hiện phân tích và công bố sự kiện analysis ready. Sau đó, tác nhân báo cáo (report agent) nhận sự kiện analysis ready đó và tạo báo cáo.

Ở đây không có bộ điều phối trung tâm. Mỗi tác nhân tự trị, lắng nghe các sự kiện mà nó quan tâm, và công bố khi nó hoàn thành. Đây là vẻ đẹp của choreography: các tác nhân được kết nối lỏng lẻo (loosely coupled), dễ dàng thêm các tác nhân mới và khiến chúng đăng ký các sự kiện mà chúng quan tâm. Điều này thúc đẩy tính tự trị cao và mở rộng rất tốt.

Tuy nhiên, cơn ác mộng của choreography là gỡ lỗi (debugging). Khi có sự cố, bạn đóng vai thám tử mà không có manh mối thực sự: tác nhân nào không công bố? Sự kiện có được tiêu thụ không? Sự kiện có được tiêu thụ hai lần không? Bạn cần khả năng quan sát bulletproof observability để choreography hoạt động. Ngay cả với việc truyền tải sự kiện (event propagation), bạn cần những đảm bảo mạnh mẽ về việc phân phối các sự kiện này. Nếu không có điều này, việc gỡ lỗi thực sự khó khăn.

Vậy khi nào bạn nên sử dụng choreography? Bạn sử dụng choreography khi workflow của bạn tự nhiên dựa trên sự kiện, khi các tác nhân cần hoạt động độc lập, khi bạn thường xuyên thêm tác nhân và không muốn cập nhật một bộ điều phối trung tâm. Nhưng điều quan trọng là phải hiểu rằng điều này chỉ có thể thực hiện được nếu bạn có khả năng quan sát mạnh mẽ (strong observability). Nếu bạn không thể theo dõi các sự kiện xuyên suốt hệ thống của mình, choreography sẽ hủy hoại bạn. Tôi đã thấy các nhóm chọn choreography vì nó cảm thấy "đậm chất tác nhân" (more agentic), tự trị hơn, sau đó dành hàng tháng trời để khắc phục sự cố vì họ không thể gỡ lỗi các luồng sự kiện phân tán. Đừng mắc sai lầm đó.

Orchestration

Bây giờ hãy xem xét giải pháp thay thế: orchestration. Orchestration là tập trung. Bạn có một bộ điều phối workflow (workflow orchestrator) gọi trực tiếp từng tác nhân. Tác nhân A chạy trước, bộ điều phối gọi tác nhân A, chờ kết quả, nhận kết quả về. Sau đó, bộ điều phối gọi tác nhân B và C song song nếu chúng là các tác nhân cần chạy song song. Bộ điều phối quản lý sự song song, không phải các tác nhân B và C. B và C trả về kết quả cho bộ điều phối. Sau đó, bộ điều phối gọi tác nhân D với các kết quả kết hợp từ B và C. Mọi lời gọi đều thông qua bộ điều phối; các tác nhân không bao giờ gọi nhau.

Bộ điều phối là nguồn thông tin đáng tin cậy duy nhất (single source of truth). Nó biết toàn bộ đồ thị thực thi (execution graph), nó quản lý trạng thái (state), nó xử lý việc thử lại (retries), nó ghi nhật ký mọi bước. Các tác nhân là "ngu ngốc" (dumb); chúng chỉ nhận đầu vào, thực hiện công việc, và trả về đầu ra. Bộ điều phối thực hiện tất cả sự điều phối thông minh.

Trong Databricks, một cách để triển khai mô hình này là với LandGraph được tích hợp vào AI agent framework làm bộ điều phối. Nhưng bất kỳ workflow nào cung cấp cho bạn dữ liệu một cách trực tiếp và có cơ chế thử lại phù hợp đều có thể phù hợp với loại mô hình bộ điều phối này.

Bạn sử dụng orchestration khi bạn có các phụ thuộc phức tạp cần quản lý tập trung, khi bạn cần khôi phục (roll back) hoặc bù đắp cho các lỗi (compensate for failures), khi bạn muốn một dashboard hiển thị toàn bộ trạng thái hệ thống, khi workflow của bạn tương đối ổn định. Ví dụ, trong các dịch vụ tài chính, chúng tôi sử dụng orchestration gần như độc quyền. Tại sao? Bởi vì nó cung cấp khả năng gỡ lỗi dễ dàng và khả năng khôi phục, và điều đó quan trọng hơn tính tự trị trong các ngành này. Khi có sự cố với quyết định tín dụng, ví dụ, chúng tôi cần biết chính xác tác nhân nào đã thực hiện lời gọi đó, theo thứ tự nào và với dữ liệu nào. Orchestration cung cấp cho chúng tôi điều đó; choreography thì không.

Framework Ra quyết định

Vậy làm thế nào để bạn lựa chọn? Đây là framework quyết định của bạn để đánh giá độ phức tạp workflow (đơn giản đến phức tạp) và yêu cầu tự trị (thấp đến cao).

  • Workflow đơn giản, tự trị cao: Bạn chọn choreography.
  • Workflow phức tạp, khả năng chịu đựng tự trị thấp: Bạn chọn orchestration.
  • Góc phần tư thú vị là phía trên bên phải, nơi bạn cần workflow phức tạp nhưng các tác nhân cần tự trị. Đây là nơi bạn sử dụng các mô hình lai (hybrid patterns): choreography với saga patterns để bù đắp (compensation). Tôi sẽ nói về mô hình này sau trong phiên này. Các công cụ (tools) như Agent Breaks trên Databricks đang bắt đầu đóng gói các mô hình orchestration này cho các trường hợp sử dụng đa tác nhân phổ biến, vì vậy bạn không cần phải xây dựng lại chúng mỗi lần. Điều này giúp việc xây dựng các mô hình này thực sự dễ dàng trong môi trường sản xuất.

Bây giờ tôi sử dụng các số liệu (metrics) quyết định này mỗi lần để đưa ra quyết định với khách hàng dựa trên các trường hợp sử dụng của họ. Bạn nên chụp ảnh màn hình; tôi chắc chắn bạn sẽ tham khảo nó. Hãy để tôi chỉ cho bạn một orchestration sản xuất thực sự trông như thế nào ở cuối phiên này.

Quản lý Trạng thái: Tránh Race ConditionStale Reads

Được rồi, bây giờ chúng ta đã chọn một mô hình điều phối. Bây giờ hãy nói về điều thực sự gây hỏng khi bạn mở rộng quy mô: trạng thái (state). Làm thế nào để các tác nhân chia sẻ dữ liệu mà không có race condition, không có stale reads (đọc dữ liệu cũ) và không có các lỗi bí ẩn (mystery bugs)?

Đây là điều hầu hết mọi người làm đầu tiên, và nó sai: shared mutable state (trạng thái có thể thay đổi được chia sẻ). Nhiều tác nhân ghi vào cùng một bản ghi cơ sở dữ liệu cùng một lúc. Tác nhân A đọc điểm tín dụng, tính toán giá trị, ghi lại. Tác nhân B làm điều tương tự vào cùng một thời điểm. Cả hai đọc 680. Tác nhân A ghi 750. Tác nhân B ghi 720. Ghi cuối cùng sẽ thắng (last write wins); bản cập nhật của tác nhân A biến mất – cập nhật bị mất (lost update).

Tôi hiểu rằng các cơ sở dữ liệu hiện đại có các biện pháp bảo vệ tại chỗ cho việc này: transaction logs, isolation levels được thiết lập. Nhưng bạn phải sử dụng chúng một cách chính xác: explicit transactions (giao dịch rõ ràng), bạn phải xây dựng cách ly tuần tự (serialized isolation), bạn phải đảm bảo rằng bạn chọn để cập nhật (select for update). Và nhiều khi, họ không sử dụng cách ly mặc định (default isolation), họ không sử dụng khóa rõ ràng (explicit locks), và họ vẫn đưa race condition vào sản xuất. Chúng tôi đã mắc lỗi đó, và kết quả là giá trị bị chậm trễ đối với doanh nghiệp. Chúng tôi chỉ cho rằng cơ sở dữ liệu sẽ xử lý các điều kiện này, nhưng chúng không làm vậy. Khi mọi thứ trở nên thực sự phức tạp, bạn phải xử lý chúng một cách rõ ràng trong mã.

Bây giờ, đây là những gì hoạt động: immutable state snapshots with versioning (ảnh chụp trạng thái bất biến với phiên bản). Tác nhân A tạo ra một phiên bản trạng thái, giả sử version one. Nó được niêm phong, nó bất biến. Không ai có thể sửa đổi nó. Trạng thái được lưu trữ trong cơ sở dữ liệu được điều phối dưới dạng một log chỉ thêm (append-only log). Đây là các thao tác chèn (insert operations), không phải bất kỳ cập nhật nào. Tác nhân A chuyển state version one cho tác nhân B. Tác nhân B xác thực schema, kiểm tra xem hợp đồng dữ liệu (data contract) có khớp với kỳ vọng của nó không. Nó xử lý, nó tạo ra trạng thái...

Trạng thái tác nhân bất biến và theo dõi phiên bản

Một phiên bản cũng bất biến. Agent B chèn phiên bản đó như một hàng mới; nó không cập nhật phiên bản một rồi chuyển cho Agent C. Tương tự, việc xác thực lược đồ, theo dõi phiên bản và đảm bảo tính nhận dạng bất biến tại mỗi lần chuyển giao giữa các tác nhân. Nếu Agent C thất bại, bạn có thể quay lại phiên bản trước đó. Nếu cần gỡ lỗi, bạn có thể phát lại quá trình tiến hóa trạng thái từ phiên bản một đến phiên bản hiện tại, và bạn có thể thấy chính xác những gì mỗi tác nhân đã nhận và tạo ra. Điều này loại bỏ các điều kiện tranh chấp (race conditions), không có sửa đổi đồng thời vào cùng một bản ghi. Mỗi tác nhân phụ thuộc vào một phiên bản mới thay vì cập nhật trạng thái chia sẻ. Tất nhiên, nếu bạn muốn lưu các ảnh chụp trạng thái (state snapshots), chúng có thể được ghi vào bất kỳ loại lưu trữ chỉ-ghi-thêm (append-only storage) nào để kiểm tra và phát lại (audit replay), nhưng chúng không bao giờ được chia sẻ để đọc.

Trạng thái bất biến trong mã và dòng dõi

Đây là cách nó trông trong mã. Lớp AgentState với frozen có nghĩa là bất biến. Thứ hai, nó có một số phiên bản, dữ liệu tải trọng (payload) và người tạo ra nó. Hàm handoff thực hiện ba điều:

  1. Xác thực lược đồ (schema): Đây là việc thực thi hợp đồng. Chúng tôi kiểm tra xem đầu ra của Agent A có khớp với hợp đồng đầu vào của Agent B hay không. Điều này rất quan trọng và chúng ta sẽ quay lại vấn đề này.
  2. Tăng phiên bản: Tạo một đối tượng trạng thái bất biến mới với phiên bản N+1.
  3. Thực thi tác nhân tiếp theo: Với trạng thái bất biến đó, tác nhân không thể sửa đổi trạng thái đầu vào, nó chỉ có thể tạo ra một trạng thái mới. Điều này ngăn chặn toàn bộ một loại lỗi và ngăn chặn các điều kiện tranh chấp (race conditions) trên trạng thái chia sẻ.

Không có cuộc đua trạng thái (state race), nó cung cấp một dòng dõi rõ ràng. Mọi trạng thái đều có một phiên bản và bạn biết ai đã tạo ra nó. Khi có sự cố, bạn có thể truy ngược lại qua quá trình tiến hóa trạng thái: phiên bản bảy tạo ra đầu ra nào? Hãy xem xét phiên bản sáu đã đi vào tác nhân, xem xét phiên bản năm trước đó. Bạn có thể tìm kiếm qua lịch sử trạng thái của mình để tìm ra nơi mọi thứ đã sai. Và điều này trở nên thực sự mạnh mẽ.

Hợp đồng dữ liệu giữa các tác nhân

Quản lý trạng thái chỉ là một nửa cuộc chiến; hợp đồng dữ liệu là nửa còn lại. Agent A không thể chỉ đơn thuần truyền dữ liệu cho Agent B và hy vọng nó hoạt động. Điều đó không hoạt động theo cách đó; chúng cần có một hợp đồng tại chỗ. Trong ví dụ này, tác nhân nghiên cứu (research agent) hứa sẽ xuất ra các phát hiện, điểm tin cậy (confident score), nguồn, dấu thời gian, v.v. Tác nhân chính sách (policy agent) khai báo rằng nó yêu cầu đầu ra của tác nhân nghiên cứu với loại 1. Và nó xác thực nếu độ tin cậy dưới 0.7, nó sẽ từ chối chuyển giao (handoff). Đây là hợp đồng. Nếu tác nhân nghiên cứu cố gắng chuyển giao dữ liệu chất lượng thấp, hợp đồng sẽ bắt được nó ngay tại ranh giới. Bạn sẽ phát hiện ra ngay lập tức, chứ không phải sau khi ba tác nhân khác đã xử lý và tạo ra một báo cáo vô nghĩa.

Trong mọi công việc với khách hàng của chúng tôi sử dụng Databricks, một cách để thực hiện là đăng ký các lược đồ đầu vào/đầu ra này trong Unity Catalog. Vì vậy, hợp đồng của mỗi tác nhân được theo dõi phiên bản và quản lý ở một nơi duy nhất.

Xử lý lỗi và phục hồi hệ thống

Chúng ta đã nói về các coordination pattern, chúng ta đã nói về quản lý trạng thái, bây giờ chúng ta hãy nói về một điều khác mà bạn cần ghi nhớ: đó là xử lý lỗi và phục hồi. Lý do điều này quan trọng là vì các tác nhân sẽ thất bại. Điều đó là không thể tránh khỏi. LLM sẽ hết thời gian chờ (time out), API sẽ giới hạn tỷ lệ yêu cầu của bạn (rate limit), tác nhân sẽ gặp sự cố trong luồng công việc. Điều gì sẽ xảy ra sau đó? Điều gì sẽ xảy ra sau đó là điều bạn cần lập kế hoạch và thiết kế trong hệ thống. Hãy nói về một vài pattern.

Mô hình Circuit Breaker

Hãy nói về pattern đầu tiên, đó là circuit breaker pattern. Điều này xuất phát trực tiếp từ các hệ thống phân tán (distributed system). Khi Agent A gọi Agent B, nó gói cuộc gọi đó trong một circuit breaker. Nếu Agent B thất bại liên tục, chẳng hạn năm lần liên tiếp, circuit breaker sẽ mở. Bây giờ thay vì chờ hết thời gian mỗi lần, về cơ bản bạn thất bại nhanh chóng (fail fast). Circuit mở, Agent B đã ngừng hoạt động. Bạn chỉ cần thử lại sau. Bạn không tấn công Agent B bằng các yêu cầu liên tục. Bạn đang bảo vệ hệ thống của mình.

Sau một khoảng thời gian chờ được xác định, ví dụ 60 giây, circuit chuyển sang trạng thái bán mở (half open). Sau đó, bạn kiểm tra lại Agent B bằng một yêu cầu. Nếu nó thành công, circuit đóng và hoạt động bình thường tiếp tục. Nếu nó thất bại, circuit mở lại và đặt lại bộ hẹn giờ. Điều này ngăn chặn các lỗi dây chuyền (cascading failures) trong hệ thống. Một tác nhân bị lỗi không làm sập toàn bộ luồng công việc của bạn. Bạn giảm thiểu chức năng một cách duyên dáng (gracefully degrade). Có thể bạn bỏ qua tác nhân đó và tiếp tục với chức năng giảm thiểu. Có thể bạn sử dụng kết quả được lưu trữ trong bộ nhớ đệm (cached results). Có thể bạn cảnh báo con người, nhưng bạn không làm sập toàn bộ luồng công việc. Circuit breakerspattern phục hồi lỗi quan trọng nhất đối với các hệ thống đa tác nhân (multi-agent systems). Mọi cuộc gọi tác nhân nên được gói gọn trong một circuit breaker. Chúng tôi thực thi các chính sách circuit breaker này tại lớp phục vụ (serving layer) trên Databricks thông qua model serving hoặc AI gateway.

Triển khai Circuit Breaker trong mã

Đây là cách nó trông trong mã. Bạn theo dõi số lần thất bại và bạn theo dõi trạng thái. Khi bạn gọi một tác nhân, bạn kiểm tra trạng thái trước. Nếu nó đang mở (open), bạn thất bại nhanh chóng (fail fast). Bạn thậm chí không cố gắng. Nếu nó đang đóng (closed), bạn thực hiện cuộc gọi. Nếu cuộc gọi thành công, bạn đặt lại số lần thất bại và giữ trạng thái đóng. Nếu nó thất bại, bạn tăng số lần thất bại lên. Nếu bạn đạt đến ngưỡng (threshold), bạn mở circuit. Sau khoảng thời gian chờ, bạn chuyển sang trạng thái bán mở (half open). Bạn thử một yêu cầu. Nếu nó thành công, bạn đóng circuit. Nếu nó thất bại, bạn mở nó lại. Đây là một pattern đơn giản, nhưng nó có tác động rất lớn. Trong Databricks, bạn có thể ghi lại mọi quá trình chuyển đổi mở/đóng trong MLflow. Bạn có thể thấy khi nào một tác nhân bắt đầu hoạt động không ổn định.

Mô hình bù trừ (Compensation Pattern)

Bây giờ, hãy nói về một pattern khác. Chúng tôi gọi nó là compensation pattern, còn được gọi là saga pattern. Mỗi tác nhân có hai phương thức: executecompensate. Execute thực hiện công việc, compensate hoàn tác nó. Trong đó, orchestrator theo dõi những tác nhân nào đã được thực thi. Nếu tác nhân thực thi thất bại, orchestrator sẽ đi ngược lại qua các tác nhân đã được thực thi và gọi compensate cho từng tác nhân. Tác nhân phân tích (analyst agent) bù trừ bằng cách xóa bản nháp khuyến nghị khỏi hệ thống mà nó đã ghi ban đầu. Sau đó, tác nhân nghiên cứu bù trừ bằng cách xóa bộ nhớ đệm (cache) đối với dữ liệu nghiên cứu mà nó đã thu thập trước đó. Vì vậy, bạn quay trở lại trạng thái ban đầu. Không có giao dịch một phần (partial transactions), không có luồng công việc bị kẹt (stuck workflows). Đây là một pattern hoàn tác (rollback pattern) đơn giản mà bạn có thể triển khai trong hệ thống đa tác nhân (multi-agent system). Compensation cung cấp cho bạn ngữ nghĩa giao dịch (transactional semantic) trên các tác nhân phân tán (distributed agents). Nó không hào nhoáng, nhưng đó là cách các hệ thống sản xuất xử lý các lỗi một phần. Mọi luồng công việc được điều phối (orchestrated workflow) đều cần loại compensation pattern này. Và bạn cần lập kế hoạch cho nó tùy thuộc vào những gì bạn đang làm với các luồng công việc của mình.

Triển khai Compensation Pattern trong mã

Đây là cách compensation trông trong mã. Mỗi tác nhân, như tôi đã đề cập trước đó, có hai phương thức: phương thức execute và phương thức compensate. Phương thức execute thực hiện công việc, phương thức compensate hoàn tác nó. Đó là hợp đồng. Mọi hoạt động phải có thể đảo ngược. Orchestrator theo dõi những tác nhân nào đã chạy thành công và sau đó giữ danh sách. Agent A thực thi được thêm vào. Agent B thực thi được thêm vào. Agent C thất bại. Bây giờ chúng ta đi ngược lại danh sách theo thứ tự đảo ngược. Agent B bù trừ đầu tiên, nó hoàn tác công việc mà nó đã làm. Agent A bù trừ tiếp theo. Nó hoàn tác công việc mà Agent A đã làm và nó quay trở lại trạng thái ban đầu. Đây là saga pattern từ các cơ sở dữ liệu phân tán. Các dịch vụ tài chính yêu cầu điều này.

Tổng quan kiến trúc sản phẩm

Bây giờ chúng ta đã đề cập đến các pattern khác nhau này, tôi muốn cho bạn thấy một kiến trúc sản phẩm sẽ trông như thế nào khi bạn kết hợp chúng lại với nhau. Bạn có orchestrator ở phía bên trái. Đó là bộ não của luồng công việc. Nó chứa workflow engine. Nó chứa state store lưu giữ các phiên bản từ 0 đến N. Và nó có thể xem xét lớp quan sát (observability layer). Nó xử lý dữ liệu quan sát. Mọi cuộc gọi đều đi đến orchestrator. Orchestrator gọi Agent A. Agent A trả về trạng thái phiên bản 1 cho orchestrator. Sau đó, orchestrator gọi Agent BC song song. Nếu chúng cần chạy song song, cả hai đều nhận trạng thái phiên bản 1 từ orchestrator. Chúng trả về kết quả. Orchestrator lưu trữ phiên bản 2 và 3. Cuối cùng, orchestrator gọi D với các kết quả kết hợp này. Các tác nhân không bao giờ gọi nhau. Tất cả sự phối hợp đều xảy ra thông qua orchestrator. Và đây là điều mang lại cho chúng ta khả năng kiểm soát, khả năng quan sát, khả năng hoàn tác (roll back). Điều này chạy 24/7 trên hàng tỷ giao dịch vì orchestrator là nguồn đáng tin cậy duy nhất (single source of truth).

Kiến trúc sản phẩm trên Databricks

Đây là một kiến trúc sản phẩm mà bạn có thể triển khai với Databricks Data Intelligence Platform. Tại lớp điều phối (orchestration layer), bạn có thể tích hợp Langgraph vào Mozike AI, Agent Framework. Nó xử lý việc điều phối đa tác nhân (multi-agent orchestration). Nó quản lý workflow graph và biết tác nhân nào cần gọi theo thứ tự nào. Mỗi tác nhân được triển khai dưới dạng Unity Catalog function. Nó có thể được viết bằng SQL hoặc Python. Hoặc nó có thể là một model được đăng ký trong Unity Catalog. Khi bạn đăng ký các tài sản này trong Unity Catalog, chúng có thể được khám phá tập trung trong tổ chức. Chúng có thể được quản lý ở một nơi và chúng có thể được theo dõi phiên bản, điều rất quan trọng trong việc vận hành luồng công việc này trong môi trường sản xuất.

Chúng tôi công khai các tác nhân này thông qua Databricks model serving hoặc function serving. Và đó là nơi chúng tôi thực thi các chính sách theo kiểu circuit breaker như thử lại (retries) hoặc thời gian chờ (timeout) hoặc giới hạn tỷ lệ (rate limits) tại lớp phục vụ (serving layer), thường thông qua cấu hình AI gateway.

Lớp dữ liệu và công cụ hỗ trợ

Khi nói về lớp dữ liệu, Delta Lake lưu trữ mọi thứ. Nó không chỉ lưu trữ các phiên bản trạng thái từ tác nhân, mà còn lưu trữ dữ liệu khách hàng và tất cả dữ liệu bạn cần để các luồng công việc của bạn hoạt động. Nói về các ảnh chụp trạng thái (state snapshots), Delta Table là bất biến và có phiên bản. Đối với chúng tôi, các phiên bản trạng thái đó chỉ là các hàng trong Delta Table. Chúng tôi không bao giờ cập nhật chúng tại chỗ. Mỗi lần chạy tác nhân được liên kết với một phiên bản trạng thái thông qua MLflow traces, vì vậy chúng tôi có thể theo dõi sự tiến hóa khi có điều gì đó không ổn.

Bây giờ, tôi chỉ muốn đề cập đến Unity Catalog. Nó quản lý mọi thứ, kiểm soát truy cập (access control), dòng dõi (lineage), nhật ký kiểm toán (audit trail) cho cả dữ liệu và tác nhân. MLflow cung cấp cho chúng ta khả năng theo dõi và đánh giá từng tác nhân với LLM làm giám khảo và các metric cho mỗi cuộc gọi. Và như tôi đã đề cập trước đó, các tool như agent breaks là cách cấp cao hơn mà Databricks đóng gói các orchestration pattern này cho các trường hợp sử dụng đa tác nhân phổ biến. Vì vậy, bạn không cần phải xây dựng lại chúng mỗi lần.

Tóm tắt luồng công việc

Để tóm tắt luồng công việc này, bạn thấy Langgraph orchestrator gọi Agent A là một Unity Catalog function hoặc model. Nó nhận kết quả, ghi trạng thái phiên bản một vào Delta. Sau đó, nó gọi Agent B với trạng thái phiên bản một, ghi phiên bản hai, v.v. MLflow theo dõi mọi cuộc gọi, độ trễ (latency), đầu vào (inputs), đầu ra (outputs), lượng token sử dụng; một circuit breaker tại lớp phục vụ (serving layer) bảo vệ mỗi cuộc gọi. Nếu Agent C thất bại, Langgraph kích hoạt logic bù trừ (compensation logic) và đi ngược lại, gọi các hàm compensate cho các bước thành công trước đó. Những loại pattern này chạy trong môi trường sản xuất ngày này qua ngày khác.

Những suy nghĩ cuối cùng

Trước hết, sự hỗn loạn của tác nhân (agent chaos) là không thể tránh khỏi. Khi bạn mở rộng quy mô vượt quá một tác nhân, bạn sẽ gặp phải các vấn đề phối hợp (coordination problems), điều kiện tranh chấp (race conditions), lỗi dây chuyền (cascading failures). Điều đó được đảm bảo. Đường cong phức tạp không bao giờ nói dối. Agent choreography là một lựa chọn. Bạn có thể xây dựng hệ thống với các pattern phù hợp: orchestration, choreography, trạng thái bất biến (immutable state), circuit breakers, compensation patterns, data contracts. Hãy đảm bảo bạn hiểu những pattern này và áp dụng chúng vào kiến trúc sản phẩm của mình. Làm như vậy sẽ giúp bạn xây dựng các hệ thống, không phải demo.

Demo thì dễ. Bạn sử dụng một LLM để thể hiện điều gì đó thú vị. Mọi người đều có thể làm được điều đó. Những thứ này không hoạt động trong môi trường sản xuất. Trong môi trường sản xuất, bạn phải xây dựng hệ thống. Và hệ thống thì khó. Hệ thống là thứ tạo ra giá trị cho doanh nghiệp. Mọi thứ tôi đã trình bày hôm nay, choreography so với orchestration, trạng thái bất biến, circuit breakers, đây đều là những công việc cơ sở hạ tầng "không gợi cảm". Bạn không nhận được lời khen ngợi khi triển khai một circuit breaker. Nhưng bạn làm cho hệ thống của mình đáng tin cậy hơn. Chúng không bị lỗi lúc 2 giờ sáng. Đó là điều mọi người sẽ nhận thấy theo thời gian. Hãy trở thành một kỹ sư hệ thống. Các pattern ở đây, chúng hoạt động. Hãy áp dụng những pattern này vào kiến trúc sản phẩm của bạn.

Góp ý / Báo lỗiPhát hiện sai sót hoặc có ý tưởng cải thiện?