9월 20일 무렵 Google이 GitHub google 조직에 AX라는 저장소를 올렸다. Go로 짠 에이전트 오케스트레이터이고, Apache 2.0이며, 며칠 만에 스타 9,500개를 넘었다. README 첫 문장은 “클러스터에서 수십억 개의 자율 에이전트 워크로드를 돌리는 고처리량 선언형 오케스트레이터”다. Hacker News 글타래는 661점까지 올라갔는데, 반응은 둘로 갈렸다. 유휴 상태로 모델 응답이나 사람 승인을 기다리는 에이전트의 비용을 드디어 누가 다룬다는 쪽과, 마케팅은 “쉽다”인데 시작하려면 Kubernetes 클러스터와 컨테이너 레지스트리와 또 다른 베타 프로젝트가 필요하다는 쪽이다.
오케스트레이터라는 말부터 짚고 가자. 오케스트레이터는 프로그램을 만드는 도구가 아니다. 이미 만든 프로그램을 어느 머신에서 언제 몇 개 띄울지 정하고, 죽으면 다시 살리고, CPU와 메모리를 나눠 주고, 필요 없어지면 거두는 소프트웨어다. 컨테이너에 그 일을 하는 것이 Kubernetes다. AX는 같은 일을 에이전트에 한다. LangGraph나 CrewAI 같은 에이전트 프레임워크가 에이전트 안에서 모델과 도구와 메모리를 어떻게 엮을지 정한다면, 오케스트레이터는 그렇게 만든 에이전트를 밖에서 실행 단위로 다룬다. 어떤 저장소를 받아 놓고, 어디로 나가게 허용하고, 놀고 있으면 내리고, 부르면 다시 올린다. 이 글에서 “프레임워크가 아니다”라는 말은 그 구분을 가리킨다.
2차 기사도 많이 나왔는데 주의할 점이 있다. dev.to와 몇몇 블로그에 올라온 소개 글은 AX를 “DAG 그래프 엔진”, “protobuf 채널로 노드끼리 통신”, “LangChain보다 27% 빠름”, “LLaMA-3.2와 Claude-3 지원”이라고 설명한다. 저장소 어디에도 없는 내용이다. 그래프 엔진도 없고 LangChain과 비교한 벤치마크도 없다. 이 글은 그런 기사 대신 저장소의 설계 문서, docs/ 아래 문서 일곱 개, Go 코드, 그리고 HN에서 공동 제작자가 직접 남긴 답글을 읽고 썼다. 클러스터에 배포해 돌려 보지는 않았다. Agent Substrate가 필요한데, 그 프로젝트 자체가 “프로덕션 준비 안 됨”이라고 써 둔 상태라 코드와 문서를 읽는 범위로 한정했다.
샌드박스 런타임을 하나의 API로 통일하는 문제는 #58 OpenSandbox에서 다뤘고, 에이전트가 샌드박스 밖으로 나가는 경로는 #76에서 다뤘다. 이번 글은 그 위층, 샌드박스 수천 개를 누가 언제 띄우고 내리느냐를 본다.
사람들이 AX에서 기대한 것
HN 글타래와 공동 제작자 rakyll의 답글을 보면 AX가 답하려는 수요는 네 가지로 정리된다.
유휴 시간의 비용. 에이전트는 모델 API 응답을 기다리거나, 사람의 승인을 기다리거나, 다음 턴이 올 때까지 아무것도 안 하는 시간이 길다. 컨테이너 하나를 그 시간 내내 붙들고 있으면 CPU와 메모리를 그냥 태운다. HN에서 인프라 쪽 사람들이 AX를 칭찬한 대목이 정확히 여기다. 한 사람은 실제 쓰임새를 “평가, 강화학습, 학습 데이터 수집처럼 한꺼번에 몰리는 큰 fleet”이라고 짚었다. 매일 돌리는 제품보다 그쪽이 먼저다.
격리. kstenerud라는 사용자는 포렌식 작업에서 입출력을 제한한 샌드박스 안에서만 에이전트를 돌려야 한다고 썼다. 신뢰할 수 없는 코드를 실행하는 에이전트를 gVisor나 microVM 안에 넣고, 나가는 트래픽은 허용 목록으로만 열고 싶다는 요구다.
반복되는 환경 세팅. docs/concepts.md의 Workspace 설명이 이 수요를 그대로 적어 놨다. 에이전트가 첫 유용한 행동을 하기 전에 저장소를 올바른 리비전으로 받고, 호출해도 되는 도구를 꽂고, 스킬을 가져와야 하는데, “같은 환경이 필요한 모든 태스크가 그 세팅을 반복하고, 모든 에이전트 프레임워크가 그걸 다시 만든다”는 문장이다.
감사 가능한 스택. rakyll은 AX의 쓰임을 세 가지로 요약했다. 고객의 컴퓨트에 에이전트 앱을 넣어 주되 데이터 레지던시를 어기지 않는 것, 지루한 인프라 세팅을 없애는 것, 컴플라이언스를 위해 투명하고 감사 가능한 스택을 주는 것. 그리고 못 박았다. “AX는 에이전트 프레임워크가 아니다. 잡 오케스트레이션 층이다.”
이 네 가지를 놓고 코드가 각각 어디까지 왔는지 보면 AX의 위치가 분명해진다.
저장소 안에 실제로 있는 것
Go 파일은 32개, 1만 4천 줄이다. 생성된 protobuf 코드가 포함된 숫자라 손으로 쓴 코드는 그보다 훨씬 적다. 바이너리는 네 개다.
| 바이너리 | 역할 |
|---|---|
ax |
개발자 CLI. apply, get, describe, watch, delete에 suspend, resume, ssh가 붙는다. |
ax-server |
상태 없는 gRPC API. 매니페스트를 검증하고 Redis에 저장한 뒤 이벤트를 발행한다. |
ax-controller |
Redis Streams를 소비해 Agent Substrate에 액터를 만들고, egress 정책을 적용하고, 태스크를 원하는 상태로 몰고 간다. 레플리카를 늘려 확장한다. |
ax-task-runner |
태스크 컨테이너 안에서 PID 1로 뜬다. 워크스페이스를 준비하고, 메타데이터 서버를 띄우고, 에이전트 명령을 실행한다. |
Kubernetes를 닮았다고 하면서 왜 CRD를 안 썼는지는 DESIGN.md가 설명한다. 수명이 짧은 태스크 수백만 개를 CRD로 저장하면 etcd가 버티지 못한다. 저장 한도가 한 자릿수 GB이고 쓰기 속도가 병목이라, 상태는 Redis에 두고 API 서버와 컨트롤러 사이의 작업 큐는 Redis Streams로 만들었다. “수십억”이라는 숫자는 이 선택의 근거로 등장한다. 실제로 그 규모를 돌렸다는 자료는 저장소에 없다.
리소스는 네 종류다. 예제 하나가 네 개를 한 파일에 담고 있다.
apiVersion: ax.io/v1alpha1
kind: Task
metadata:
name: task123
spec:
image: "gcr.io/ax-substrate/ate-images/ax-task-runner@sha256:..."
resources:
requests: { cpu: "500m", memory: "1Gi" }
limits: { cpu: "2", memory: "4Gi" }
workspaces:
- name: default-workspace
goal: "Install dependencies and run the test suite"
gateway:
name: default-gateway
debug: true
---
kind: Workspace
spec:
git:
- repo: "https://github.com/chalk/chalk.git"
branch: "main"
mcp:
servers:
- name: git-tools
endpoint: "http://git-mcp.default.svc.cluster.local:8080"
skills:
path: "/.agents/skills"
---
kind: Gateway
spec:
egress:
allowlist:
hosts:
- host: "*"
port: 443
---
kind: Model
spec:
provider: google
model: gemini-3.8-flash
secretKey:
name: gemini-api-secret
key: GEMINI_API_KEY
Task는 컨테이너 이미지, 명령, 자원 한도, 환경 변수, Gateway 참조, Workspace 참조를 가진다. 설계 의도가 재미있는데, 에이전트 하나의 전체 수명을 모델링하지 않겠다고 문서에 써 뒀다. 에이전트는 계획하고 위임하고 재시도하고 일을 나누는데, AX는 그 모양을 따라가는 대신 “만들고 격리하고 중단하고 버리는 비용이 싼 원시 단위 하나”만 주고, 나머지는 에이전트가 태스크를 여러 개 만들어 조합하라는 것이다. 태스크 하나가 작업 전체일 수도 있고, 에이전트가 문제를 쪼개며 만든 큰 트리의 뿌리일 수도 있다.
Workspace는 git 저장소, MCP 서버, 스킬 경로를 선언한다. 한 번 선언하고 여러 Task에서 바인딩하면 러너가 각 샌드박스 안에 그대로 만들어 준다. goal이 붙으면 첫 부팅 때 그 목표를 에이전트에게 넘겨 툴체인 설치 같은 마무리 세팅을 시킨다.
Gateway는 태스크가 노출할 리스너와 나가는 트래픽의 허용 목록이다. 컨트롤러가 이 목록을 Agent Substrate의 egress 정책으로 바꿔 액터에 적용한다. 예제의 허용 목록은 *의 443 포트라 사실상 다 열려 있고, 주석에 “프로덕션에서는 조여라”라고 써 있다.
Model은 이름이 헷갈리는데, 문서가 먼저 말한다. “Model은 모델이 아니다.” 어느 프로바이더의 어느 모델을 어떤 파라미터로 부를지와 API 키가 든 Kubernetes 시크릿의 참조를 묶은 설정 리소스다. README 표를 보면 “플랫폼 자체가 쓰는 LLM”이라고 돼 있다. 태스크 안의 사용자 에이전트가 쓸 모델과는 별개로, AX가 워크스페이스 목표를 계획할 때 부르는 모델이다. 사용자 에이전트는 spec.env로 자기 키를 받아 알아서 부른다.
중단과 재개가 이 프로젝트의 중심이다
유휴 비용 문제를 AX가 어떻게 푸는지는 컨트롤러와 Substrate의 관계를 보면 된다. AX는 샌드박스를 직접 만들지 않는다. 컨트롤러가 gRPC로 Agent Substrate에 “액터를 만들어라”, “egress 정책을 걸어라”, “액터를 중단해라”, “재개해라”를 요청할 뿐이다.
Agent Substrate는 5월 21일 Google Cloud 블로그에서 Agent Sandbox와 함께 소개된 별도 프로젝트다. Agent Sandbox는 kubernetes-sigs 아래의 CRD로 gVisor 격리와 기본 거부 네트워크 정책을 제공하고 GKE에서 정식 출시됐다. Agent Substrate는 그 위에 얹는 얇은 제어 평면으로, 많은 수의 액터(에이전트)를 적은 수의 준비된 워커(파드)에 매핑한다. 에이전트가 대부분의 시간을 놀고 있다는 점을 이용해 워커 하나에 여러 액터를 다중화하고, 놀고 있는 액터는 체크포인트를 떠서 워커에서 내리고, 요청이 오면 500ms 안에 다시 올린다는 것이 README의 주장이다. 초당 500회 이상의 중단·재개, 클러스터당 초당 300개 샌드박스 할당 같은 숫자도 Google이 낸 것이다. README에는 “공식 지원 Google 제품이 아님”과 “프로덕션 준비 안 됨”이 같이 적혀 있다.
AX 쪽에서 중단과 재개는 세 군데에 걸쳐 있다.
첫째, ax suspend task를 치면 Task의 spec.suspend가 켜지고 컨트롤러가 SuspendActor를 부른다. 워크스페이스는 체크포인트되고 샌드박스는 사라진다. ax resume이 반대 방향이다. 컨트롤러 코드에서 확인되는 경로는 이 수동 경로뿐이다.
둘째, 네트워크 경로에서 자동으로 깨운다. 태스크는 자기 Service나 Ingress를 갖지 않는다. 모든 요청은 Substrate의 atenet-router를 거치는데, 라우터는 ate-target-actor 헤더 하나를 읽어 액터가 어느 워커에 있는지 찾고, 중단돼 있으면 먼저 재개한 뒤 요청을 넘긴다. 잠든 에이전트에 요청을 보내면 알아서 깨어난다는 뜻이다.
셋째, 러너가 재개를 견디게 만들어져 있다. 재개는 컨테이너를 다시 시작하는데, 그때 git 저장소를 다시 클론하면 에이전트가 쌓아 둔 상태를 덮어쓴다. 그래서 러너는 워크스페이스 세팅이 끝났다는 마커 파일을 내구 볼륨 아래 /ax에 남기고, 이후 부팅에서는 세팅을 건너뛴다. 중단 신호가 오면 명령의 프로세스 그룹에 SIGTERM을 보내고 10초 기다린 뒤 남은 것을 죽인다.
여기서 지금 없는 것이 하나 있다. 유휴 감지다. 로드맵 2번 항목이 “프로세스 실행, I/O, 네트워크, 활성 gRPC/SSH 세션을 계속 관찰해 노는 태스크를 찾아내고 자동으로 SuspendActor를 걸어 밀도를 높인다”고 적고 있다. 즉 HN에서 칭찬받은 “유휴 에이전트 비용 절감”은 지금 시점에서는 누군가가 ax suspend를 불러야 일어난다. 에이전트가 모델 응답을 기다리는 30초 동안 자동으로 내려갔다 올라오는 그림은 아직 로드맵이다.
러너 계약: 에이전트 프레임워크를 안 정한 방식
AX가 프레임워크를 정하지 않는다는 말은 러너 문서에서 구체적으로 확인된다. 컨트롤러는 spec.command를 컨테이너 엔트리포인트로 실행하지 않는다. 항상 /usr/local/bin/ax-task-runner를 고정 명령으로 띄우고, Task와 Workspace 전체를 AX_TASK_YAML, AX_WORKSPACES_YAML 환경 변수로 넘긴다. 러너가 그걸 파싱해 워크스페이스를 만들고, 80번 포트에 /healthz와 /readyz를 열고, 명령을 자식 프로세스로 띄우고, 명령이 끝난 뒤에도 PID 1로 살아 있어야 한다.
이 계약만 지키면 러너를 어떤 언어로든 새로 써도 된다. 문서는 세 단계를 제시한다. 기본 이미지 위에 도구만 얹기, runner Go 패키지를 임포트해 명령 종료 훅을 걸기, 처음부터 다시 쓰기. 컨트롤러는 이미지와 환경이 다른 태스크마다 별도의 액터 템플릿을 만들어서, 같은 atespace 안에서 러너가 다른 태스크들이 나란히 돈다.
기본 러너 이미지는 Python 3.12에 git, curl, openssh-client, 그리고 Antigravity 에이전트가 들어 있다. Workspace의 goal을 처리하는 것이 이 Antigravity다. internal/workspace/setup.go를 보면 /usr/local/bin/antigravity_bootstrap.py를 실행하고, GEMINI_API_KEY가 없으면 안 돌며, 기본 10분 제한이 있다. 로드맵 3번은 이 부분을 “내장 부트스트랩과 코딩 에이전트 하네스를 분리해 사용자가 자기 런타임을 꽂을 수 있게” 바꾸겠다고 한다. 지금은 목표 기반 세팅을 쓰려면 Gemini 키가 필수다.
Model 리소스도 같은 상태다. docs/manifests.md는 provider: anthropic과 claude-opus-5 예제를 싣고 있는데, internal/model/client.go의 Generate는 프로바이더가 비어 있거나 google일 때만 Gemini API를 부르고 나머지는 unsupported provider 오류를 낸다. 문서가 코드보다 앞서 있다. 다만 앞서 말했듯 이 Model은 AX 자신이 쓰는 것이라, 태스크 안의 에이전트가 Claude를 부르는 데는 지장이 없다.
종료를 모른다
잡 오케스트레이터로 볼 때 가장 눈에 띄는 공백은 태스크 종료 처리다. status.phase는 Running, Suspended, Failed, Terminating이다. Succeeded나 Completed가 없다. 러너 문서에 이유가 있다. 러너는 명령이 끝나도 PID 1로 남아 메타데이터 서버를 계속 돌리고 종료 코드는 로그에만 남기며, “제어 평면은 현재 명령의 종료 상태를 컨테이너에서 읽어 오지 않는다.”
그러니까 ax apply로 태스크 천 개를 던지고 ax get tasks로 몇 개가 끝났는지 볼 수는 없다. 에이전트가 일을 마쳤는지는 러너 패키지의 OnCommandExit 훅에서 웹훅을 쏘거나 결과물을 올리는 식으로 사용자가 직접 밖으로 알려야 한다. 로드맵 1번의 Task 항목에 “토큰·타임아웃 예산과 승인 정책”이 들어 있는데, 이것도 지금은 없다. README가 “아무도 안 보면 루프를 돌며 돈을 태울 수 있다”고 쓴 문제를 예산으로 막는 기능은 아직 스펙 단계다.
신원과 게이트웨이
HN에서 가장 기술적인 반론은 hhh라는 사용자가 냈다. 워커 하나에 태스크 여러 개를 다중화하면 “Kubernetes 파드 신원이 단일 워크로드에서 온 것이라고 더는 믿을 수 없다”는 것이다. 파드 신원으로 클라우드 IAM에 붙는 흔한 구성에서, 같은 파드 위의 다른 에이전트가 그 권한을 같이 쓰게 된다. 감사 로그에도 태스크가 아니라 파드가 남는다.
Googler인 ahmedtd가 답했다. Agent Substrate가 egress 게이트웨이를 통해 SPIFFE와 OIDC 신원 주입을 지원할 예정이고 “몇 주 안에 랜딩”한다고 했다. AX 로드맵 4번에도 모든 Task 액터와 Gateway에 SPIFFE ID와 X.509-SVID를 발급하겠다는 항목이 있다. 즉 태스크 단위 신원은 두 프로젝트 모두에서 로드맵이다.
Gateway 쪽도 반쯤 돼 있다. 허용 목록을 Substrate egress 정책으로 바꾸는 코드는 있지만, Gateway를 수정했을 때 이미 도는 태스크들에 전파하는 지속 조정은 로드맵 4번의 첫 항목이다. 지금은 태스크를 만들 때 한 번 적용된다고 보는 게 안전하다. #76에서 본 것처럼 에이전트가 밖으로 나가는 경로는 허용 목록에 넣어 둔 사내 서비스를 통해서도 열리므로, 허용 목록에 무엇을 넣느냐가 격리 강도를 정한다. 예제의 *:443은 출발점일 뿐이다.
ax ssh는 편리하지만 조건이 있다. Task에 debug: true가 있어야 러너가 게스트 서비스(임의 프로세스 실행과 파일 읽기·쓰기)를 gRPC로 열고, 그게 없으면 ax ssh가 연결을 거부한다. 기본값은 꺼짐이고, 문서는 켜는 순간 샌드박스 안에서 임의 실행이 가능해진다고 경고한다. 예제 파일들은 전부 debug: true다.
어떤 팀이 지금 써볼 만한가
여기까지 놓고 보면 AX는 두 가지 조건을 만족하는 팀에 맞다. 이미 Kubernetes를 운영하고 있고, 에이전트를 한꺼번에 수백 개 이상 띄우는 작업이 있어야 한다. HN에서 carlm42가 “인프라가 이미 있으면 쉽다”고 한 말이 정확하다. 평가 파이프라인, 강화학습 롤아웃, 대량 코드 마이그레이션처럼 저장소 하나에 태스크 수백 개를 뿌리는 작업이 첫 후보다. 그 경우 Workspace 한 번 선언으로 클론과 MCP 세팅을 통일하고, Gateway로 나가는 곳을 모델 API와 git 호스트로 제한하는 것만으로도 얻는 게 있다.
시작하려면 준비물이 있다.
- Kubernetes 클러스터와 클러스터가 pull할 수 있는 컨테이너 레지스트리
ko(Go 이미지를 Dockerfile 없이 빌드해 올리는 도구)- Agent Substrate가 배포된
ate-system네임스페이스. Substrate 자체는 GKE 밖의 Kubernetes에서도 돌고, kind 클러스터 안내도 있다. goal기반 워크스페이스 세팅을 쓰려면 Gemini API 키- 태스크 종료를 알 방법. 러너 훅으로 웹훅을 쏘거나 결과를 버킷에 올리는 코드를 직접 넣어야 한다.
혼자 또는 작은 팀이 코딩 에이전트 몇 개를 격리해 돌리고 싶은 경우라면 AX보다 가벼운 선택지가 있다. Google 자신이 4월에 공개한 Scion은 Claude Code, Gemini CLI, Codex, OpenCode를 컨테이너 하나씩에 담아 Docker, Podman, Apple Container, Kubernetes, Cloud Run에서 돌린다. 기존 하네스를 그대로 쓰고 클러스터가 필요 없다. HN에서 jauntywundrkind가 AX보다 Scion을 선호한 이유가 이것이다. 두 프로젝트는 서로를 언급하지 않는다. 샌드박스 API만 필요하다면 #58의 OpenSandbox나 Agent Sandbox CRD를 단독으로 쓰는 편이 층이 하나 적다.
도입을 미루고 지켜본다면 볼 시그널은 세 개다.
- Agent Substrate에 SPIFFE/OIDC 신원 주입이 실제로 들어왔는가. 이게 없으면 다중화된 워커에서 클라우드 권한을 태스크별로 나눌 수 없다.
- AX 컨트롤러에 유휴 감지와 자동 중단이 들어왔는가. 이게 없으면 비용 절감 서사는 수동이다.
- 태스크 종료 상태와 예산 정책이 스펙에 들어왔는가. 이게 있어야 잡 오케스트레이터로 부를 수 있다.
정리
AX는 Go로 만든 얇은 제어 평면이다. 매니페스트 네 종류를 Redis에 저장하고, 컨트롤러가 그것을 Agent Substrate의 액터로 바꾸고, 태스크 컨테이너 안의 러너가 워크스페이스를 준비해 에이전트 명령을 띄운다. 에이전트가 어떻게 생각하고 도구를 부르는지는 전혀 관여하지 않는다. 사람들이 기대한 네 가지 중 격리와 환경 세팅은 지금 코드에 있고, 유휴 비용 절감은 수동 중단·재개까지만 있으며, 감사 가능한 신원은 로드맵이다. “수십억”은 etcd 대신 Redis를 고른 이유를 설명하는 숫자이지 측정값이 아니다.
두 번째 시사점은 Google이 에이전트 인프라를 어떻게 쌓고 있는지다. Agent Sandbox(격리 CRD) 위에 Agent Substrate(액터 다중화와 중단·재개) 위에 AX(선언형 태스크와 워크스페이스)가 올라가고, 옆에 Scion(기존 하네스를 컨테이너로 돌리는 도구)이 따로 있다. 다 오픈소스이고 다 “공식 제품 아님” 또는 “안정 전”이다. 어느 층을 골라 쓸지는 이미 갖고 있는 인프라와 한 번에 띄울 에이전트 수로 정하면 된다.