
cua-sandbox Fleet Builder API 遷移實戰用不可變流式 Builder 重構沙箱艦隊請求構建【免費下載鏈接】cuaScale computer-use 2.0 with open-source drivers, cross-OS fleets, and benchmarks for training, evaluation, and data generation.項目地址: https://gitcode.com/GitHub_Trending/cua/cua本文基于 docs/superpowers/plans/2026-08-07-sandbox-fleet-builder-api.md 實現計劃展開結合當前倉庫中已落地的源碼與測試完整還原一次「從直接構造 Fleet 記錄到生成式 Builder API」的 SDK 內部遷移如何用 AST 契約測試鎖定重構邊界、如何在cua_sandbox頂層再導出 7 個 Builder、如何遷移傳輸層與測試夾具并最終通過打包、發行與代碼質量驗證。讀者讀完可掌握一套可復制的「先立契約、再動代碼、TDD 驗證、打包兜底」的 SDK 演進方法論以及 cua-fleet Builder API 的完整用法。背景與目標為什么需要 Builder APIcua-sandbox 是 CUA 項目的 Python 沙箱 SDK位于 libs/python/cua-sandbox負責向調用方提供臨時/持久化沙箱計算環境的能力。它通過底層生成式 UniFFI Python 綁定fleet_sdk由cua-fleet發行包提供與 Fleet 云端交互構建兩類核心請求Template 請求CreateTemplateRequest描述虛擬機模板——容器磁盤鏡像、CPU/內存、探針、服務端口、固件等Pool 請求CreatePoolRequest描述熱池warm pool規格——副本數、模板引用、自動擴縮容、TTL 等。在遷移之前沙箱 SDK 與測試通過「直接調用 Fleet 記錄類的構造函數」來構造這些請求圖。遷移的目標非常明確將 Python 沙箱 SDK 及其測試從直接構造 builder-enabled Fleet 記錄遷移到生成的 Fleet builder API同時保留所有既有公開沙箱 API。計劃給出了清晰的架構決策見原文檔 Architecture 段從cua_sandbox再導出生成的 builder 伴生類在既有邊界處用不可變流式 Builderimmutable fluent builders構造 Fleet template 與 pool 請求圖未提供生成 builder 的 Fleet 記錄繼續使用構造函數用一個聚焦的 AST 契約測試防止 builder-enabled 記錄重新回歸直接構造。全局約束遷移的紅線原計劃列出六條全局約束是任何實現都不可逾越的邊界也是理解后續每個任務設計意圖的鑰匙保留cua_sandbox全部既有公開名稱、簽名與返回的 Fleet 記錄類型——遷移是內部的對外 API 完全不變增加 builder 導出但不刪除 legacy 記錄導出或構造函數兼容性——兩類用法并存將沙箱 SDK 鎖定到 builder-enabled 的cua-fleet發行版——計劃寫作時指向0.1.7當前倉庫 pyproject.toml 已演進為cua-fleet0.1.17遷移cua_sandbox包與tests下所有對 builder-enabled Fleet 記錄的直接調用CreateClaimRequest、ClaimSpec、HttpHeader、HttpRequest、HttpResponse、CyclopsConfiguration、CyclopsCredentials以及 FleetSandbox的構造函數調用保持不變——因為生成的 SDK 沒有為它們提供 builder不編輯 libs/fleet/sdk-bindings 下的生成文件也不在用戶明確要求前創建提交。其中第 5 條尤其關鍵它劃定了「哪些必須遷移、哪些保持原樣」的精確范圍避免了為追求形式統一而過度重構。Task 1用 AST 契約測試鎖定 Builder 使用契約遷移最容易發生的回歸是后續開發者圖省事又寫回CreatePoolRequest(...)之類的直接構造。因此計劃的第一步不是改代碼而是先寫一個會失敗RED的源碼契約測試把不允許直接構造 builder-enabled 記錄固化成一條不可違背的規則。七個 builder-enabled 記錄契約測試將以下七個 Fleet 記錄類列為「必須走 Builder」名單見 test_fleet_builder_usage.py 的BUILDER_ENABLED_RECORDS記錄類對應 Builder請求圖中的角色CreatePoolRequestCreatePoolRequestBuilder熱池創建請求頂層CreateTemplateRequestCreateTemplateRequestBuilder模板創建請求頂層OsGymSandboxTemplateSpecOsGymSandboxTemplateSpecBuilder模板規格包裹 vmTemplateOsGymSandboxWarmPoolSpecOsGymSandboxWarmPoolSpecBuilder熱池規格副本、模板引用、擴縮容SandboxServiceSandboxServiceBuilder暴露的服務端口SandboxTemplateRefSandboxTemplateRefBuilder熱池對模板的引用VmTemplateVmTemplateBuilder虛擬機模板本體計劃中的首個契約測試版本很直接遍歷cua_sandbox/與tests/下所有*.py解析 AST凡是ast.Call且函數名落在名單內即記為違規from __future__ import annotations import ast from pathlib import Path PACKAGE_ROOT Path(__file__).parents[1] BUILDER_ENABLED_RECORDS { CreatePoolRequest, CreateTemplateRequest, OsGymSandboxTemplateSpec, OsGymSandboxWarmPoolSpec, SandboxService, SandboxTemplateRef, VmTemplate, } def test_builder_enabled_fleet_records_use_generated_builders() - None: violations: list[str] [] source_roots (PACKAGE_ROOT / cua_sandbox, PACKAGE_ROOT / tests) for source_root in source_roots: for path in source_root.rglob(*.py): tree ast.parse(path.read_text(), filenamestr(path)) for node in ast.walk(tree): if not isinstance(node, ast.Call) or not isinstance(node.func, ast.Name): continue if node.func.id in BUILDER_ENABLED_RECORDS: relative_path path.relative_to(PACKAGE_ROOT) violations.append(f{relative_path}:{node.lineno}: {node.func.id}) assert violations [], Direct Fleet record constructors remain:\n \n.join(violations)按計劃執行uv run --project libs/python/cua-sandbox pytest libs/python/cua-sandbox/tests/test_fleet_builder_usage.py -q預期結果是FAIL并列出cua_sandbox/transport/fleet_cloud.py與tests/test_pool.py中的直接構造調用——這正為后續 Task 3、Task 4 的遷移提供了明確的待辦清單。從計劃到落地的演進綁定作用域解析當前倉庫中的 test_fleet_builder_usage.py 把這份契約測試實現得遠比計劃中的初版嚴謹。計劃的 Interfaces 要求檢測器必須能解析三類導入形態from ... import ... as ...別名導入如from fleet_sdk import VmTemplate as FleetVmimport fleet_sdk as ...模塊別名下的屬性調用如fleet.VmTemplate()import cua_sandbox as ...屬性調用如cua.SandboxService()。落地版實現了一個完整的_BindingScope作用域鏈 _BuilderRecordCallVisitor通過維護模塊/函數/類的詞法作用域能夠正確處理直接導入、別名導入、模塊屬性調用的識別對應測試test_finds_direct_builder_record_imports、test_finds_aliased_builder_record_imports、test_finds_builder_record_module_attributes本地同名類/函數/賦值對 Fleet 名稱的遮蔽shadowing——被遮蔽后不應誤報test_ignores_unrelated_local_builder_record_name、test_ignores_direct_import_after_local_shadowing函數內導入不泄漏到兄弟或外層作用域test_function_imports_do_not_leak_to_sibling_or_outer_scopes模塊別名被重綁定后不再當作 Fleet 模塊test_ignores_module_attribute_after_alias_rebinding嵌套作用域可解析外層未遮蔽的 Fleet 綁定test_nested_scope_resolves_unshadowed_outer_fleet_binding。這些聚焦的 snippet 測試每個導入形態一個保證了契約檢測器本身的行為可被單獨驗證是測試的測試也防止契約測試因誤報/漏報而在未來被繞過。Task 2通過 cua_sandbox 導出 Fleet Builders契約測試鎖定了必須用 Builder接下來要讓 Builder 真正可用。Task 2 的核心是在cua_sandbox的公開命名空間再導出七個 Builder讓外部使用者只需要from cua_sandbox import CreatePoolRequestBuilder即可無需感知底層fleet_sdk的存在。先寫一個失敗的公開導出測試計劃先在 tests/test_pool.py 的from cua_sandbox import (...)導入列表中加入全部七個 Builder 名并新增test_public_pool_schema_exports_generated_builders()——這個測試同時也演示了 Builder API 的完整拼裝鏈路def test_public_pool_schema_exports_generated_builders() - None: service SandboxServiceBuilder().name(server).target_port(8000).build() vm_template ( VmTemplateBuilder() .container_disk_image(registry.example/workspace:latest) .services([service]) .build() ) template_request ( CreateTemplateRequestBuilder() .namespace(default) .name(workspace) .spec(OsGymSandboxTemplateSpecBuilder().vm_template(vm_template).build()) .build() ) pool_request ( CreatePoolRequestBuilder() .namespace(default) .spec( OsGymSandboxWarmPoolSpecBuilder() .replicas(1) .sandbox_template_ref(SandboxTemplateRefBuilder().name(workspace).build()) .build() ) .build() ) assert template_request.spec.vm_template.services [service] assert pool_request.spec.sandbox_template_ref.name workspace此時運行該測試會得到collection ERROR——因為cua_sandbox尚未導出這些名字驗證了 RED 階段。鎖定 builder-enabled 的 Fleet 發行版隨后在pyproject.toml中把cua-fleet0.0.10升級為 builder-enabled 版本計劃為0.1.7并運行uv lock --project libs/python/cua-sandbox --upgrade-package cua-fleet預期uv.lock解析出目標版本。計劃還專門要求處理兩個配套測試test_fleet_sdk_packaging.py移除其中與 Fleet 版本無關的cua-sandbox項目版本硬編碼斷言避免每次發版都要附帶改動隨后把版本斷言更新為目標版本、把期望 registry 更新為https://wheels.cua.ai/simple先觀察 RED 再改到 GREENtest_fleet_sdk_distribution.py先運行觀察 RED再把期望的發行版本從0.0.7改為目標版本當前倉庫 test_fleet_sdk_distribution.py 斷言cua_fleet_distribution.version 0.1.16并校驗fleet_sdk/__init__.py確實來自該發行包重跑至 GREEN。這條路徑保證「鎖版本」不止是改一行依賴聲明還通過打包與發行測試驗證了依賴解析的真實狀態。再導出全部七個 Builder在 cua_sandbox/init.py 的既有from fleet_sdk import (...)塊中加入七個 Builder并在__all__中與對應記錄名相鄰添加字符串CreatePoolRequestBuilder CreateTemplateRequestBuilder OsGymSandboxTemplateSpecBuilder OsGymSandboxWarmPoolSpecBuilder SandboxServiceBuilder SandboxTemplateRefBuilder VmTemplateBuilder當前倉庫正是這樣實現的——__all__中每個*Record后面緊跟著對應的*RecordBuilder公開命名空間同時保留記錄類型與 Builder。WarmPoolAutoscalingBuilder也隨之上線供熱池自動擴縮容場景使用。修改后重跑導出測試預期GREEN。Task 3用 Builder 構建 Fleet Cloud 請求契約與導出就緒后進入核心遷移改造 cua_sandbox/transport/fleet_cloud.py 中_FleetCloudTransport的_template_request()與_pool_request()兩個內部方法要求請求記錄值完全不變。替換 builder-enabled 導入保留用于注解的請求記錄類型CreateTemplateRequest、CreatePoolRequest其余 builder-enabled 記錄導入替換為七個 Builder以及Firmware、PreservedJson、ServiceProtocol、WarmPoolAutoscaling等輔助值。流式構建服務記錄_template_request()先把端口表展開為SandboxService列表當前實現見 fleet_cloud.pyservices [ SandboxServiceBuilder() .name(name) .target_port(port) .protocol(ServiceProtocol.TCP) .build() for name, port in service_ports.items() ]注意默認端口表的組裝邏輯顯式傳入services時以server端口為基準合并其余命名服務未傳時則從鏡像暴露端口self._image._ports生成port-port命名服務見 fleet_cloud.py。測試 test_fleet_cloud_transport.py 驗證了expose(3000)后請求中服務列表為[(server, 8000), (port-3000, 3000)]。構建 VM、模板規格與模板請求vm_template_builder ( VmTemplateBuilder() .container_disk_image(self._image._registry) .image_pull_secret(ecr-credentials) .probes( PreservedJson.from_json( json.dumps({readinessProbe: {tcpSocket: {port: 8000}}}) ) ) .services(services) ) if self._cpu is not None: vm_template_builder vm_template_builder.cpu_cores(self._cpu) if self._memory_mb is not None: vm_template_builder vm_template_builder.memory(f{self._memory_mb}Mi) template_spec OsGymSandboxTemplateSpecBuilder().vm_template(vm_template_builder.build()).build() return ( CreateTemplateRequestBuilder() .namespace(self._name) .name(self._name) .spec(template_spec) .build() )計劃明確了一條重要約定當既有值為None時省略可選 setter——生成式記錄對這些字段仍會保持None因此省略與顯式置空結果等價但代碼更干凈。當前倉庫實現在此基礎上又補充了兩處真實業務邏輯見 fleet_cloud.pyECR 拉取憑證按需附加僅當鏡像來自賬號私有 ECR 時_needs_ecr_pull_secret判斷.dkr.ecr.與.amazonaws.com后綴才.image_pull_secret(ecr-credentials)。注釋解釋了原因網關的準入策略會把「附帶了拉取憑證」解讀為需要執行 ECR 白名單對公有鏡像反而會導致拒絕拉取Windows 鏡像強制 UEFI 固件Windows 客戶機磁盤只按 UEFI 構建見registry/qemu_builder.py而 Fleet 模式默認固件是 BIOS因此os_type windows時追加.firmware(Firmware.EFI)否則 Windows 鏡像在 SeaBIOS 下永遠無法通過就緒探針。測試test_windows_image_boots_uefi與test_linux_image_leaves_firmware_at_the_schema_defaulttest_fleet_cloud_transport.py精確鎖定了這一分支。構建熱池規格與請求_pool_request()的遷移同樣直觀當前實現見 fleet_cloud.pydef _pool_request(self) - CreatePoolRequest: template_ref SandboxTemplateRefBuilder().name(self._name).build() pool_spec ( OsGymSandboxWarmPoolSpecBuilder() .replicas(self._replicas) .sandbox_template_ref(template_ref) .build() ) return CreatePoolRequestBuilder().namespace(self._name).spec(pool_spec).build()當前版本進一步支持了自動擴縮容與創建 TTL 的條件追加autoscaling、ttl_seconds_after_created并有對應的傳輸層測試覆蓋test_pool_request_carries_the_requested_autoscaling、test_pool_request_carries_the_requested_creation_ttl、test_pool_request_leaves_creation_ttl_unset_by_default以及test_transport_rejects_invalid_creation_ttl校驗 TTL 必須是0 ~ 2^32-1的整數拒絕-1、True、3600、1.5、2**32。驗證請求字段不變運行聚焦測試確認遷移無行為變化uv run --project libs/python/cua-sandbox pytest \ libs/python/cua-sandbox/tests/test_fleet_cloud_client.py \ libs/python/cua-sandbox/tests/test_fleet_cloud_transport.py -q預期 PASS。例如test_registry_image_becomes_typed_template_request斷言遷移后cpu_cores 4、memory 8192Mi、鏡像與服務列表完全符合預期——這正是請求記錄值不變的機器證明。Task 4將沙箱測試夾具遷移到 Builder傳輸層遷移完成后輪到測試自身把 tests/test_pool.py 中的pool_request()、template_request()夾具以及自定義 VM 夾具全部改為 Builder 構造同時保持每個斷言所依賴的記錄身份與取值不變。轉換pool_request()夾具def pool_request( *, name: str foo, template_name: str | None None, replicas: int 1, ) - CreatePoolRequest: template_ref SandboxTemplateRefBuilder().name(template_name or name).build() spec ( OsGymSandboxWarmPoolSpecBuilder() .replicas(replicas) .sandbox_template_ref(template_ref) .build() ) return CreatePoolRequestBuilder().namespace(name).spec(spec).build()轉換template_request()夾具def template_request( *, name: str foo, image: str example:latest, services: dict[str, int] | None None, vm_template: VmTemplate | None None, ) - CreateTemplateRequest: if vm_template is None: built_services [ SandboxServiceBuilder() .name(service_name) .target_port(port) .protocol(ServiceProtocol.TCP) .build() for service_name, port in (services or {server: 8000}).items() ] vm_template ( VmTemplateBuilder() .container_disk_image(image) .image_pull_secret(ecr-credentials) .services(built_services) .build() ) spec OsGymSandboxTemplateSpecBuilder().vm_template(vm_template).build() return CreateTemplateRequestBuilder().namespace(name).name(name).spec(spec).build()轉換自定義 VM 夾具對于需要完整自定義 VM 的場景當前實現見 test_pool.py對應計劃中的自定義 VM fixtureservice ( SandboxServiceBuilder() .name(server) .target_port(8000) .protocol(ServiceProtocol.TCP) .build() ) vm_template ( VmTemplateBuilder() .container_disk_image(registry.example/workspace:latest) .runtime(RuntimeKind.KUBEVIRT) .image_pull_secret(workspace-pull) .cpu_cores(10) .memory(20Gi) .firmware(Firmware.EFI) .services([service]) .build() ) request template_request(vm_templatevm_template)這里展示了 Builder 相比構造函數的顯著優勢RuntimeKind.KUBEVIRT、Firmware.EFI等枚舉值以具名 setter 傳入可讀性遠高于位置參數堆疊且每個 setter 返回值仍是 Builder 本身支持鏈式調用。驗證契約 池測試全綠uv run --project libs/python/cua-sandbox pytest \ libs/python/cua-sandbox/tests/test_fleet_builder_usage.py \ libs/python/cua-sandbox/tests/test_pool.py -q預期 PASS且契約測試不再報告任何直接構造。測試如test_template_reconcile_preserves_named_services服務列表[(server, 8000), (mcp, 3000)]、test_reconcile_preserves_replicas_and_template_referencereplicas 2、模板引用為workspace等從夾具遷移前后行為一致的角度完成了最終確認。Task 5驗證打包與代碼質量遷移完成后需要從「包能不能裝、裝完能不能用、代碼干不干凈」三個維度做最終驗收。發行與打包測試uv run --project libs/python/cua-sandbox pytest \ libs/python/cua-sandbox/tests/test_fleet_sdk_distribution.py \ libs/python/cua-sandbox/tests/test_fleet_sdk_packaging.py -qtest_fleet_sdk_packaging.py 的斷言非常嚴格值得展開pyproject.toml聲明了精確版本的cua-fleet且不直接依賴cua-train訓練棧與運行時沙箱解耦cua-fleet不在tool.uv.sources中即不走本地路徑源碼而是從https://wheels.cua.ai/simple私有索引解析發布包uv.lock中的cua-fleet版本與 registry source 與聲明一致包不會把 checkout 里的綁定復制進 wheel無hatch_build.py、無 hatch build hooksfleet_cloud.py、fleet.py、cyclops_http_client.py三個運行時文件都是直接from fleet_sdk import ...沒有sys.path注入或ctypes.CDLL加載本地庫的旁路。聚焦沙箱 Fleet 套件uv run --project libs/python/cua-sandbox pytest \ libs/python/cua-sandbox/tests/test_fleet_builder_usage.py \ libs/python/cua-sandbox/tests/test_pool.py \ libs/python/cua-sandbox/tests/test_fleet_cloud_client.py \ libs/python/cua-sandbox/tests/test_fleet_cloud_transport.py \ libs/python/cua-sandbox/tests/test_fleet_transport.py -qRuff 代碼質量uv run --project libs/python/cua-sandbox --extra dev ruff check \ libs/python/cua-sandbox/cua_sandbox/__init__.py \ libs/python/cua-sandbox/cua_sandbox/transport/fleet_cloud.py \ libs/python/cua-sandbox/tests/test_fleet_builder_usage.py \ libs/python/cua-sandbox/tests/test_pool.py發行版冒煙測試與最終 diff 審查計劃要求在libs/python/cua-sandbox的臨時副本中執行uvx --from pdm2.20.1 pdm lock uvx --from pdm2.20.1 pdm build隨后在干凈虛擬環境中用默認 PyPI 索引安裝構建出的 wheel驗證fleet_sdk.CreatePoolRequestBuilder可以成功導入——這證明了最終用戶不依賴私有索引也能解析到公開的cua-fleet發行版并拿到 Builder API。最后審查 diffgit diff --check git diff -- \ libs/python/cua-sandbox/cua_sandbox/__init__.py \ libs/python/cua-sandbox/cua_sandbox/transport/fleet_cloud.py \ libs/python/cua-sandbox/tests/test_fleet_builder_usage.py \ libs/python/cua-sandbox/tests/test_pool.py預期git diff --check退出碼為 0且 diff 只包含四類改動builder 導出、builder 構造、源碼契約、夾具遷移——沒有夾帶任何無關變更。遷移全景回顧這份實現計劃展示了一條可復用的 SDK 演進路徑其核心方法論可歸納為四點契約先行任何重構開始前先用 AST 契約測試把不允許的寫法變成可執行的紅線讓遷移范圍有據可查、未來回歸有門可擋。當前倉庫的 test_fleet_builder_usage.py 甚至實現了完整的作用域鏈解析來對抗別名、遮蔽等真實代碼形態。公開面不變cua_sandbox.__init__同時保留記錄類型與 Builder對外 API 零破壞CreateClaimRequest等無 Builder 的記錄繼續走構造函數。行為由測試鎖定傳輸層測試test_fleet_cloud_transport.py在遷移前后逐一斷言請求字段取值鏡像、CPU、內存、服務列表、固件、副本數、TTL確保用 Builder 重構但不改變語義不是口號而是機器驗證。打包兜底發行測試、打包測試與 PDM 冒煙測試共同保證「版本鎖得住、wheel 裝得上、Builder 導得出」。對于任何希望在生成式 SDK 綁定之上構建業務庫的團隊這套「先鎖契約 → 再導出 → 遷移調用點 → 遷移夾具 → 打包驗收」的流程都是一份可以直接套用的工程模板。【免費下載鏈接】cuaScale computer-use 2.0 with open-source drivers, cross-OS fleets, and benchmarks for training, evaluation, and data generation.項目地址: https://gitcode.com/GitHub_Trending/cua/cua創作聲明:本文部分內容由AI輔助生成(AIGC),僅供參考