1. 从“ax”这个标题说起一个被低估的Agent执行调度内核第一次看到“ax”这个标题很多人会以为是某个命令行工具的缩写或者某个前端库的名字。但把热搜词摊开来看——ax调度、agent、kubernetes、workspace、gateway、agent开发、agent框架、agent架构、agent记忆、agent安全——这条线索就非常清楚了ax 是一套围绕 Agent 执行调度Agent Execution Scheduling构建的运行时内核它要解决的核心问题是当你有几十上百个 Agent 任务同时跑在 Kubernetes 集群里每个任务都有自己的 workspace、自己的 gateway 出口、自己的记忆存储你怎么让它们不打架、不卡死、不把集群资源吃干净。我接触这类系统大概是从两年前开始的。那时候团队在做一套自动化运维 Agent最初的想法很朴素写个 Python 脚本用 subprocess 拉起任务跑完就结束。结果任务一多问题全来了——有的 Agent 卡在“setting up workspace: loading packages...”这一步不动了有的报“agent execution terminated due to error”还有的 gateway 返回 502 bad gateway日志里写着“cc switch local proxy failed while handling”。这些问题单独看都是小毛病但放在一起就是系统性的调度缺陷。ax 要做的就是把这些零散的、容易出错的环节收敛成一套可观测、可控制、可复现的调度层。它不负责 Agent 的“智能”部分——那是模型和 prompt 的事——它负责的是 Agent 的“生存”部分怎么启动、怎么分配资源、怎么隔离环境、怎么管理生命周期、怎么处理失败。适合谁来读这篇内容如果你正在做 Agent 开发尤其是需要把 Agent 部署到 Kubernetes 上跑批量任务的场景那这篇内容会对你有直接帮助。如果你只是用现成的 Agent 工具做单次对话那可能暂时用不上但了解一下调度层的设计思路对理解 Agent 系统的整体架构也有好处。2. ax 调度内核的整体设计思路拆解2.1 为什么不能直接用 Kubernetes Job 跑 Agent很多人第一反应是Agent 任务不就是个 Job 吗Kubernetes 原生 Job 加个 CronJob 不就完了我一开始也是这么想的直到踩了几个坑才明白为什么不行。Kubernetes Job 的设计假设是任务是无状态的、短生命周期的、失败就重试的。但 Agent 任务不一样。一个 Agent 在执行过程中会积累上下文、会写 workspace 文件、会调用外部 gateway、会维护自己的记忆状态。如果你用 Job 跑Pod 一重启这些状态全丢了。更麻烦的是Agent 任务经常需要“暂停-恢复”——比如等一个外部 API 回调或者等用户确认——Job 没有原生的暂停语义你只能靠不断轮询或者挂起容器这两种方式都很别扭。ax 的做法是在 Job 之上加了一层Agent Runtime Controller。这一层不替代 Kubernetes而是把 Kubernetes 的原语Pod、PVC、Service、ConfigMap重新组合成适合 Agent 的抽象。具体来说ax 把每个 Agent 任务建模成一个AgentExecution自定义资源这个资源里包含了 workspace 规格、gateway 配置、记忆存储挂载、生命周期钩子等字段。Controller 监听这个资源的变化然后驱动底层 Kubernetes 资源去满足它。这样做的好处是Agent 的状态和 Kubernetes 的状态解耦了。Pod 可以重启但 AgentExecution 的状态还在workspace 可以迁移但记忆存储的引用不变gateway 可以切换但 Agent 不需要感知。2.2 ax 调度器的三层结构ax 的调度器我把它拆成三层来看这样理解起来比较清晰。第一层是准入控制层Admission Layer。这一层负责在 AgentExecution 创建之前做校验和预处理。比如检查 workspace 规格是否合法、gateway 配置是否完整、记忆存储的 PVC 是否存在。这一层还会做资源配额检查——如果集群里已经没有足够的 CPU 或内存直接拒绝创建而不是让 Pod 卡在 Pending 状态。我见过太多系统把配额检查放在调度之后结果就是一堆 Pod 排队等资源用户体验极差。第二层是调度决策层Scheduling Layer。这一层决定 AgentExecution 应该分配到哪个节点上。和 Kubernetes 默认调度器不同ax 的调度器会考虑 Agent 特有的因素比如 workspace 的本地性如果 workspace 数据在某个节点的 SSD 上优先调度到那个节点、gateway 的网络延迟如果 Agent 需要频繁调用外部 gateway优先调度到网络出口好的节点、记忆存储的访问模式如果 Agent 需要频繁读写记忆优先调度到有本地缓存的节点。第三层是执行控制层Execution Layer。这一层负责 Agent 的实际执行——启动容器、注入配置、挂载存储、监控状态、处理失败。这一层是 ax 和 Agent 代码交互最密切的地方。ax 在这里定义了一套Agent Harness 接口Agent 开发者只需要实现这个接口就能被 ax 调度。Harness 和 Agent 的区别我在后面会详细讲这里先记住一点Harness 是“壳”Agent 是“核”ax 调度的是壳壳里面跑的是核。2.3 为什么 gateway 配置是 ax 的关键设计点热搜词里“gateway配置”、“springcloud gateway”、“vercel ai gateway”、“502 bad gateway”反复出现说明 gateway 是这类系统里最容易出问题的地方。ax 把 gateway 设计成一个独立的抽象层而不是让 Agent 直接调用外部服务原因有几个。第一统一出口管理。Agent 可能需要调用多个外部服务——模型 API、工具 API、数据源 API——如果每个 Agent 自己管理这些连接配置会散落在各处很难统一管理。ax 的 gateway 层把这些连接收敛到一个地方Agent 只需要知道 gateway 的地址不需要知道后面具体连了什么。第二故障隔离。当外部服务返回 502 或者超时的时候gateway 层可以做重试、降级、熔断而不是让 Agent 直接面对错误。我见过很多 Agent 代码里直接写requests.get()一旦外部服务抖动Agent 就挂了。ax 的 gateway 层会把这类错误包装成 Agent 可以理解的异常并且根据配置决定是重试还是失败。第三可观测性。所有经过 gateway 的请求都可以被记录、被追踪、被分析。这对于调试 Agent 行为非常重要。当 Agent 表现异常的时候你可以通过 gateway 的日志看到它到底调用了什么、收到了什么、花了多长时间。ax 的 gateway 配置支持多种模式直连模式Agent 直接访问外部服务gateway 只做记录、代理模式Agent 访问 gatewaygateway 转发到外部服务、路由模式根据请求内容动态选择后端服务。具体用哪种模式取决于你的安全要求和性能要求。3. ax 核心细节解析与实操要点3.1 AgentExecution 资源的字段设计ax 的核心自定义资源是 AgentExecution它的字段设计直接决定了系统的能力边界。我根据实际使用经验把关键字段分成几组来说明。基础标识组metadata.name、metadata.namespace、spec.agentRef。其中agentRef指向一个 Agent 定义可以是镜像地址、可以是 Git 仓库地址、可以是预注册的 Agent 名称。这一组字段决定了“跑哪个 Agent”。Workspace 组spec.workspace.size、spec.workspace.storageClass、spec.workspace.mountPath、spec.workspace.initContainers。这一组字段决定了 Agent 的工作目录怎么创建、多大、用什么存储、初始化时跑什么。我踩过的一个坑是workspace 的 storageClass 如果选了网络存储比如 NFSAgent 启动时会非常慢因为要等网络挂载。后来改成 local-path 或者 SSD 的 storageClass启动时间从 30 秒降到了 3 秒。Gateway 组spec.gateway.mode、spec.gateway.endpoint、spec.gateway.timeout、spec.gateway.retryPolicy。这一组字段决定了 Agent 怎么访问外部服务。mode可以是direct、proxy、routeendpoint是 gateway 的地址timeout是请求超时时间retryPolicy定义了重试策略。我一般会把timeout设成 30 秒retryPolicy设成最多重试 2 次因为大部分外部服务的 P99 延迟都在 10 秒以内30 秒足够覆盖长尾请求。记忆组spec.memory.type、spec.memory.size、spec.memory.persistence。这一组字段决定了 Agent 的记忆怎么存储。type可以是ephemeral临时Pod 删除就丢、persistent持久化用 PVC、external外部存储比如 Redis 或数据库。persistence决定了是否在 Agent 重启后保留记忆。对于需要长期运行的 Agent我建议用persistent或者external否则每次重启都失忆体验很差。生命周期组spec.lifecycle.preStart、spec.lifecycle.postStart、spec.lifecycle.preStop、spec.lifecycle.timeout。这一组字段定义了 Agent 在不同阶段要执行的钩子。preStart可以用来做环境检查postStart可以用来做注册preStop可以用来做清理。timeout是整体超时时间超过这个时间 Agent 会被强制终止。我一般会把timeout设成 1 小时因为大部分 Agent 任务都在这个时间内完成超过 1 小时的基本都是卡死了。3.2 Workspace 初始化的常见问题与解决“setting up workspace: loading packages...卡住”这个热搜词我太熟悉了。Workspace 初始化卡住的原因通常有几个。第一个原因是包管理器锁冲突。如果多个 Agent 共享同一个 workspace 的包缓存目录pip 或者 npm 会争抢锁导致其中一个卡住。ax 的解法是给每个 AgentExecution 分配独立的包缓存目录通过环境变量PIP_CACHE_DIR和npm_config_cache指向 workspace 内的独立路径。这样虽然会多占一点磁盘但避免了锁冲突。第二个原因是网络问题。如果 workspace 初始化时需要从外部拉取包而网络不通或者很慢就会卡住。ax 的解法是在 gateway 层配置一个包镜像的代理或者预先在节点上缓存常用包。我一般会在节点上跑一个本地包镜像服务workspace 初始化时优先从本地拉拉不到再走外部。第三个原因是初始化脚本本身有 bug。比如脚本里写了一个死循环或者等待一个永远不会就绪的服务。ax 的解法是给 workspace 初始化设置超时默认 5 分钟超过就标记为失败并输出日志。这样至少不会无限卡住。实操中我建议在spec.workspace.initContainers里加一个健康检查容器定期检查 workspace 的关键文件是否存在、关键服务是否可达。如果检查失败就主动退出让 ax 重新调度。3.3 Gateway 配置的实操细节Gateway 配置是 ax 里最容易出错的部分我结合“502 bad gateway”、“cc switch local proxy failed”、“unexpected status 502 bad gateway: unknown error”这些热搜词讲几个实操要点。要点一区分 502 的来源。502 可能来自 gateway 本身也可能来自 gateway 后面的上游服务。ax 的 gateway 层会在响应头里加一个X-Gateway-Error-Source字段标明错误来源。如果是gateway说明 gateway 自己有问题比如配置错误、进程挂了如果是upstream说明上游服务有问题比如超时、拒绝连接。这个字段对排查问题非常有用。要点二配置合理的超时和重试。我见过很多配置把 timeout 设成 5 秒结果稍微慢一点的请求就 502。ax 的 gateway 支持分级超时连接超时、读取超时、整体超时。我一般会把连接超时设成 5 秒读取超时设成 30 秒整体超时设成 60 秒。重试策略用指数退避第一次重试等 1 秒第二次等 2 秒第三次等 4 秒最多重试 3 次。要点三处理 local proxy 失败。“cc switch local proxy failed while handling”这个错误通常出现在 gateway 用本地代理模式的时候。原因是本地代理进程挂了或者端口被占用。ax 的解法是给本地代理加一个守护进程定期检查代理是否存活不存活就重启。同时gateway 配置里要指定一个备用端口主端口不可用时自动切换。要点四监控 gateway 的 P99 延迟。502 很多时候是延迟累积的结果。如果 gateway 的 P99 延迟持续上升说明后面有瓶颈迟早会出 502。ax 的 gateway 层会暴露 Prometheus 指标包括请求数、错误数、延迟分布。我一般会设置告警P99 延迟超过 10 秒持续 5 分钟就发通知。3.4 Agent Harness 与 Agent 的区别“harness和agent区别”这个热搜词问到了点子上。在 ax 的语境里Harness 是 Agent 的运行外壳Agent 是具体的业务逻辑。打个比方Harness 是汽车底盘Agent 是发动机。底盘负责承载、供电、散热、通信发动机负责产生动力。你可以换发动机但底盘不变你也可以换底盘但发动机接口不变。具体来说Harness 负责这些事情启动和停止 Agent 进程、注入环境变量和配置、挂载 workspace 和记忆存储、管理 gateway 连接、收集日志和指标、处理信号和退出。Agent 负责这些事情实现具体的业务逻辑、调用模型和工具、维护自己的状态、输出结果。ax 定义了一套 Harness 接口Agent 开发者只需要实现这个接口就能被 ax 调度。接口的核心方法包括initialize()、execute()、pause()、resume()、terminate()、getStatus()。其中execute()是核心Agent 的主要逻辑都在这里。pause()和resume()是可选的如果 Agent 不需要暂停恢复可以不实现。我建议 Agent 开发者在实现 Harness 接口的时候把业务逻辑和 Harness 逻辑严格分开。业务逻辑放在独立的模块里Harness 逻辑只做适配。这样以后换调度系统的时候业务逻辑不用改。4. ax 在 Kubernetes 上的实操部署与核心环节实现4.1 环境准备与前置检查在 Kubernetes 上部署 ax 之前有几项前置检查必须做。我列了一个清单每次部署新集群的时候都会过一遍。检查项检查命令预期结果不通过的后果Kubernetes 版本kubectl version --short1.24低版本缺少某些 API存储类可用性kubectl get storageclass至少一个 defaultworkspace 无法创建网络插件kubectl get pods -n kube-systemCNI Pod 运行中Pod 之间无法通信DNS 服务kubectl run test --imagebusybox -- nslookup kubernetes解析成功gateway 域名解析失败资源配额kubectl describe quota -A有足够配额AgentExecution 被拒绝节点标签kubectl get nodes --show-labels有区分节点的标签调度器无法做亲和性调度其中存储类是最容易出问题的。我见过一个集群默认存储类是 NFS结果 workspace 初始化要等 30 秒以上。后来加了一个 local-path 的存储类把 ax 的 workspace 默认存储类改成 local-path启动时间立刻降到 3 秒以内。4.2 ax 控制器的安装与配置ax 控制器本身是一个 Kubernetes Operator用 Deployment 的方式部署。安装过程不复杂但配置项需要仔细调。apiVersion: apps/v1 kind: Deployment metadata: name: ax-controller namespace: ax-system spec: replicas: 2 selector: matchLabels: app: ax-controller template: metadata: labels: app: ax-controller spec: serviceAccountName: ax-controller containers: - name: controller image: ax/controller:latest args: - --leader-electtrue - --metrics-bind-address:8080 - --health-probe-bind-address:8081 - --workspace-default-storage-classlocal-path - --gateway-default-timeout30s - --gateway-default-retry2 - --agent-execution-concurrent50 env: - name: AX_LOG_LEVEL value: info - name: AX_GATEWAY_MODE value: proxy resources: requests: cpu: 500m memory: 512Mi limits: cpu: 2 memory: 2Gi几个关键参数说明--agent-execution-concurrent50表示同时处理的 AgentExecution 数量上限这个值要根据集群规模和 Agent 资源消耗来调。我一般会按“节点数 × 每节点可跑 Agent 数 × 0.8”来估算。--gateway-default-timeout30s是 gateway 的默认超时如果单个 Agent 需要更长的超时可以在 AgentExecution 里覆盖。--workspace-default-storage-classlocal-path是 workspace 的默认存储类建议用本地 SSD 而不是网络存储。安装完成后用kubectl get pods -n ax-system检查控制器是否运行。如果 Pod 一直 CrashLoopBackOff大概率是 RBAC 权限不够检查 ServiceAccount 是否绑定了正确的 ClusterRole。4.3 创建一个完整的 AgentExecution下面是一个完整的 AgentExecution 示例我加了详细注释你可以直接参考。apiVersion: ax.io/v1alpha1 kind: AgentExecution metadata: name: example-agent namespace: default spec: # Agent 定义指向一个预注册的 Agent agentRef: name:>apiVersion: apps/v1 kind: Deployment metadata: name: ax-gateway namespace: ax-system spec: replicas: 3 selector: matchLabels: app: ax-gateway template: metadata: labels: app: ax-gateway spec: containers: - name: gateway image: ax/gateway:latest ports: - containerPort: 8080 env: - name: AX_GATEWAY_UPSTREAM_TIMEOUT value: 30s - name: AX_GATEWAY_MAX_CONNECTIONS value: 1000 - name: AX_GATEWAY_LOG_LEVEL value: info resources: requests: cpu: 1 memory: 1Gi limits: cpu: 4 memory: 4Gi --- apiVersion: v1 kind: Service metadata: name: ax-gateway namespace: ax-system spec: selector: app: ax-gateway ports: - port: 8080 targetPort: 8080 type: ClusterIP路由配置通过 ConfigMap 注入支持固定链接地址转发。比如把所有/v1/responses的请求转发到某个模型服务把所有/v1/tools的请求转发到工具服务。apiVersion: v1 kind: ConfigMap metadata: name: ax-gateway-routes namespace: ax-system data: routes.yaml: | routes: - match: prefix: /v1/responses forward: service: model-service port: 8080 timeout: 60s - match: prefix: /v1/tools forward: service: tool-service port: 8080 timeout: 30s - match: prefix: /v1/data forward: service:>