Mở rộng quy mô các Agent được quản lý: Tách biệt bộ não khỏi đôi tay
Bắt đầu với Claude Managed Agents bằng cách làm theo tài liệu hướng dẫn của chúng tôi.
Một chủ đề thường xuyên trên Blog Kỹ thuật là cách xây dựng các agent hiệu quả và thiết kế harness cho các tác vụ chạy dài. Một điểm chung xuyên suốt các công việc này là harness mã hóa các giả định về những gì Claude không thể tự làm. Tuy nhiên, những giả định đó cần được đặt câu hỏi thường xuyên vì chúng có thể trở nên lỗi thời khi các mô hình được cải thiện.
Chỉ lấy một ví dụ, trong công việc trước đây, chúng tôi phát hiện rằng Claude Sonnet 4.5 có xu hướng kết thúc tác vụ sớm khi cảm thấy giới hạn ngữ cảnh đang đến gần—một hành vi đôi khi được gọi là "lo lắng về ngữ cảnh." Chúng tôi đã giải quyết vấn đề này bằng cách thêm các thiết lập lại ngữ cảnh vào harness. Nhưng khi sử dụng cùng một harness trên Claude Opus 4.5, chúng tôi thấy rằng hành vi đó đã biến mất. Các thiết lập lại trở thành gánh nặng vô ích.
Chúng tôi kỳ vọng harness sẽ tiếp tục phát triển. Vì vậy, chúng tôi đã xây dựng Managed Agents: một dịch vụ lưu trữ trên Claude Platform chạy các agent tầm xa thay mặt bạn thông qua một tập hợp nhỏ các giao diện được thiết kế để tồn tại lâu hơn bất kỳ triển khai cụ thể nào—bao gồm cả những gì chúng tôi đang chạy hôm nay.
Xây dựng Managed Agents đồng nghĩa với việc giải quyết một vấn đề cũ trong điện toán: làm thế nào để thiết kế một hệ thống cho "các chương trình chưa từng được nghĩ tới." Hàng thập kỷ trước, các hệ điều hành đã giải quyết vấn đề này bằng cách ảo hóa phần cứng thành các trừu tượng hóa—tiến trình, tệp tin—đủ tổng quát cho các chương trình chưa tồn tại. Các trừu tượng hóa tồn tại lâu hơn phần cứng. Lệnh `read()` không quan tâm nó đang truy cập một đĩa từ những năm 1970 hay một SSD hiện đại. Các trừu tượng hóa ở trên vẫn ổn định trong khi các triển khai bên dưới thay đổi tự do.
Managed Agents tuân theo cùng một mô hình. Chúng tôi đã ảo hóa các thành phần của một agent: một phiên (nhật ký chỉ ghi thêm của mọi thứ đã xảy ra), một harness (vòng lặp gọi Claude và định tuyến các lệnh gọi công cụ của Claude đến cơ sở hạ tầng liên quan), và một sandbox (môi trường thực thi nơi Claude có thể chạy mã và chỉnh sửa tệp tin). Điều này cho phép triển khai của từng thành phần được hoán đổi mà không ảnh hưởng đến các thành phần khác. Chúng tôi có quan điểm rõ ràng về hình dạng của các giao diện này, chứ không phải về những gì chạy phía sau chúng.
Chúng tôi bắt đầu bằng cách đặt tất cả các thành phần agent vào một container duy nhất, điều đó có nghĩa là phiên, harness agent và sandbox đều chia sẻ một môi trường. Có những lợi ích cho cách tiếp cận này, bao gồm việc chỉnh sửa tệp tin là các lệnh gọi hệ thống trực tiếp và không có ranh giới dịch vụ nào cần thiết kế.
Nhưng bằng cách gắn kết mọi thứ vào một container, chúng tôi gặp phải một vấn đề cơ sở hạ tầng cũ: chúng tôi đã áp dụng một "thú cưng." Trong phép ẩn dụ thú cưng-vs-gia súc, thú cưng là một cá thể có tên, được chăm sóc thủ công mà bạn không thể để mất, trong khi gia súc có thể thay thế lẫn nhau. Trong trường hợp của chúng tôi, máy chủ trở thành thú cưng đó; nếu một container thất bại, phiên sẽ bị mất. Nếu một container không phản hồi, chúng tôi phải chăm sóc nó để phục hồi.
Chăm sóc container đồng nghĩa với việc gỡ lỗi các phiên bị kẹt không phản hồi. Cửa sổ duy nhất của chúng tôi là luồng sự kiện WebSocket, nhưng điều đó không thể cho chúng tôi biết lỗi phát sinh ở đâu, điều đó có nghĩa là một lỗi trong harness, một gói tin bị rớt trong luồng sự kiện, hoặc một container ngoại tuyến đều hiện ra...
Để tìm ra vấn đề, một kỹ sư phải mở shell bên trong container, nhưng vì container đó thường chứa dữ liệu người dùng, cách tiếp cận này về cơ bản đồng nghĩa với việc chúng tôi thiếu khả năng gỡ lỗi.
Vấn đề thứ hai là harness giả định rằng bất cứ thứ gì Claude làm việc đều nằm trong cùng container với nó. Khi khách hàng yêu cầu chúng tôi kết nối Claude với virtual private cloud của họ, họ phải kết nối mạng của họ với mạng của chúng tôi, hoặc chạy harness trong môi trường của riêng họ. Một giả định được tích hợp sẵn trong harness đã trở thành vấn đề khi chúng tôi muốn kết nối nó với cơ sở hạ tầng khác.
Giải pháp chúng tôi đưa ra là tách rời những gì chúng tôi coi là "bộ não" (Claude và harness của nó) khỏi cả "đôi tay" (sandbox và các công cụ thực hiện hành động) và "phiên" (nhật ký các sự kiện trong phiên). Mỗi thành phần trở thành một giao diện đưa ra ít giả định về các thành phần khác, và mỗi thành phần có thể gặp lỗi hoặc được thay thế độc lập.
Harness rời khỏi container. Việc tách rời bộ não khỏi đôi tay có nghĩa là harness không còn nằm trong container nữa. Nó gọi container giống như cách nó gọi bất kỳ công cụ nào khác: `execute(name, input) → string`. Container trở nên dễ thay thế. Nếu container chết, harness bắt lỗi như một lỗi gọi công cụ và chuyển nó lại cho Claude. Nếu Claude quyết định thử lại, một container mới có thể được khởi tạo lại bằng một công thức tiêu chuẩn: `provision({resources})`. Chúng tôi không còn phải chăm sóc các container bị lỗi để phục hồi chúng nữa.
Phục hồi sau lỗi harness. Harness cũng trở nên dễ thay thế. Vì nhật ký phiên nằm bên ngoài harness, không có gì trong harness cần tồn tại sau sự cố. Khi một harness gặp lỗi, một harness mới có thể được khởi động lại bằng `wake(sessionId)`, sử dụng `getSession(id)` để lấy lại nhật ký sự kiện, và tiếp tục từ sự kiện cuối cùng. Trong vòng lặp agent, harness ghi vào phiên bằng `emitEvent(id, event)` để duy trì một bản ghi bền vững về các sự kiện.
Ranh giới bảo mật. Trong thiết kế kết hợp, bất kỳ mã không đáng tin cậy nào do Claude tạo ra đều chạy trong cùng container với thông tin xác thực—vì vậy một cuộc tấn công chèn prompt chỉ cần thuyết phục Claude đọc môi trường của chính nó. Một khi kẻ tấn công có được những token đó, chúng có thể tạo ra các phiên mới, không bị hạn chế và ủy thác công việc cho chúng. Phạm vi hẹp là một biện pháp giảm thiểu rõ ràng, nhưng điều này mã hóa một giả định về những gì Claude không thể làm với một token bị giới hạn—và Claude ngày càng thông minh hơn. Sửa chữa cấu trúc là đảm bảo rằng các token không bao giờ có thể truy cập được từ sandbox nơi mã do Claude tạo ra chạy.
Chúng tôi đã sử dụng hai mẫu để đảm bảo điều này. Xác thực có thể được gói gọn với một tài nguyên hoặc được lưu giữ trong một kho lưu trữ bên ngoài sandbox. Đối với Git, chúng tôi sử dụng token truy cập của mỗi kho lưu trữ để clone repo trong quá trình khởi tạo sandbox và kết nối nó vào remote git cục bộ. Các thao tác `git push` và `pull` hoạt động từ bên trong sandbox mà agent không bao giờ xử lý token. Đối với các công cụ tùy chỉnh, chúng tôi hỗ trợ MCP và lưu trữ token OAuth trong một kho lưu trữ an toàn. Claude gọi các công cụ MCP thông qua một proxy chuyên dụng; proxy này nhận một token liên kết với
phiên. Proxy sau đó có thể lấy thông tin xác thực tương ứng từ kho lưu trữ và thực hiện cuộc gọi đến dịch vụ bên ngoài. Harness không bao giờ biết đến bất kỳ thông tin xác thực nào.
Các tác vụ dài hạn thường vượt quá độ dài cửa sổ ngữ cảnh của Claude, và các cách tiêu chuẩn để giải quyết vấn đề này đều liên quan đến các quyết định không thể đảo ngược về việc giữ lại nội dung gì. Chúng tôi đã khám phá các kỹ thuật này trong các nghiên cứu trước đây về kỹ thuật ngữ cảnh. Ví dụ, compaction cho phép Claude lưu một bản tóm tắt cửa sổ ngữ cảnh của nó, và công cụ bộ nhớ cho phép Claude ghi ngữ cảnh vào các tệp, cho phép học tập qua các phiên. Điều này có thể kết hợp với việc cắt tỉa ngữ cảnh, loại bỏ có chọn lọc các token như kết quả công cụ cũ hoặc các khối suy nghĩ.
Nhưng các quyết định không thể đảo ngược để giữ lại hoặc loại bỏ ngữ cảnh có thể dẫn đến thất bại. Rất khó để biết token nào sẽ cần trong các lượt tương lai. Nếu các thông điệp được biến đổi bởi một bước compaction, harness sẽ loại bỏ các thông điệp đã được nén khỏi cửa sổ ngữ cảnh của Claude, và chúng chỉ có thể khôi phục nếu được lưu trữ. Các nghiên cứu trước đây đã khám phá các cách giải quyết vấn đề này bằng cách lưu trữ ngữ cảnh dưới dạng một đối tượng tồn tại bên ngoài cửa sổ ngữ cảnh. Ví dụ, ngữ cảnh có thể là một đối tượng trong REPL mà LLM truy cập theo chương trình bằng cách viết mã để lọc hoặc cắt lát.
Trong Managed Agents, phiên cung cấp lợi ích tương tự, đóng vai trò như một đối tượng ngữ cảnh tồn tại bên ngoài cửa sổ ngữ cảnh của Claude. Nhưng thay vì được lưu trữ trong sandbox hoặc REPL, ngữ cảnh được lưu trữ bền vững trong nhật ký phiên. Giao diện, getEvents(), cho phép bộ não truy vấn ngữ cảnh bằng cách chọn các lát cắt vị trí của luồng sự kiện. Giao diện có thể được sử dụng linh hoạt, cho phép bộ não tiếp tục từ nơi nó dừng đọc lần cuối, tua lại một vài sự kiện trước một thời điểm cụ thể để xem diễn biến, hoặc đọc lại ngữ cảnh trước một hành động cụ thể.
Bất kỳ sự kiện nào được lấy về cũng có thể được biến đổi trong harness trước khi được chuyển đến cửa sổ ngữ cảnh của Claude. Những biến đổi này có thể là bất cứ thứ gì mà harness mã hóa, bao gồm


