
1. 从“ax”这个标题说起一个被低估的调度原语第一次看到“ax”这个标题很多人会以为是某个命令行工具的缩写或者某个内部代号。但把热搜词摊开来看——ax、agentic、orchestration、runtime、Kubernetes——这几个词凑在一起指向的其实是一个非常具体的工程命题在 Kubernetes 之上为 agentic 工作负载做一层调度与运行时编排。我先把结论摆在前面ax 在这里不是一个孤立的工具名而是一类“agent 执行轴agent execution axis”的抽象。它要解决的问题是——当你的系统里不再只有无状态的 HTTP 服务而是有一堆会思考、会调工具、会互相委派的 agent 时Kubernetes 原生的 Deployment/Service 模型就不够用了。你需要一个中间层把“谁在什么时候、用什么资源、跑哪一段推理或工具调用”这件事管起来。为什么这个方向现在这么热因为 agentic 应用和传统微服务有一个本质区别传统微服务的调用图是静态的agent 的调用图是运行时才生成的。一个 agent 可能先调检索、再调代码执行、再调另一个 agent 做校验这条链路在部署时根本不存在。Kubernetes 擅长管理“声明式的长期状态”但不擅长管理“临时生成的、短生命周期的、带依赖关系的执行单元”。ax 这类调度层补的就是这个缺口。这篇文章适合谁看如果你正在把 LLM 应用从“单次问答”往“多步 agent 工作流”迁移或者你已经在 K8s 上跑推理服务、发现资源利用率一塌糊涂那这篇内容对你有直接参考价值。我会从设计思路、核心机制、实操落地、问题排查四个层面拆开讲尽量给到能直接抄的配置和参数。2. 整体设计思路为什么要在 K8s 之上再加一层2.1 agentic 负载和普通负载到底差在哪先把差异讲清楚不然选型就是拍脑袋。普通在线服务的特征是请求短、无状态、副本数固定、扩缩容看 QPS。而 agentic 负载的特征完全相反执行时间长且方差极大一次 agent 任务可能 2 秒结束也可能因为多轮工具调用跑 3 分钟。资源需求是阶段性的推理阶段吃 GPU工具调用阶段几乎不吃 GPU 但吃网络和 CPU等待人工确认阶段啥都不吃。有会话状态同一个 agent 的多轮交互需要粘在同一个上下文里不能随便被调度到别的 Pod。调用关系动态生成agent A 在运行时才决定要不要叫 agent BK8s 的 Service 发现机制对此无能为力。我踩过的一个坑早期直接把 agent 跑在一个 Deployment 里副本数设 4结果发现 GPU 利用率常年在 15% 以下——因为大部分时间 agent 在等工具返回或者等模型流式输出GPU 是空转的。这就是典型的“用管理无状态服务的思路管理有状态 agent”的后果。2.2 ax 这一层的核心职责划分基于上面的差异ax 调度层要承担四件事我按重要性排序执行单元的细粒度建模把一次 agent 任务拆成可独立调度的 step每个 step 有自己的资源画像。生命周期管理step 是短命的跑完就回收不能像 Pod 一样长期占着。依赖编排step 之间有先后和数据依赖需要 DAG 式的调度而不是简单的负载均衡。运行时隔离不同 agent 的代码执行、工具调用要隔离避免一个 agent 的失控影响全局。这里有个关键的设计取舍是把 agent 调度做成 K8s 的 CRD自定义资源还是做成 K8s 之上的独立调度器两种路线我都试过。CRD 路线的好处是复用 K8s 的 RBAC、etcd 存储、kubectl 生态运维成本低。坏处是 K8s 的调度周期默认调度器是秒级对 agent step 来说太慢而且 Pod 的创建销毁开销镜像拉取、CNI 配置对短任务来说是纯浪费。独立调度器路线类似 Volcano、Karmada 那种思路的好处是调度延迟可以做到毫秒级能自己实现 gang scheduling、bin packing 等策略。坏处是要自己解决高可用、状态持久化、和 K8s 的边界划分。我的实际选择是混合路线用 CRD 定义 agent 任务的声明式描述方便运维和审计但真正的 step 调度走一个轻量的 sidecar 调度器Pod 复用用 warm pool 预热避免频繁创建销毁。这个方案在实测中把单 step 的调度延迟从 800ms 压到了 40ms 左右。2.3 和 Karmada、Kubernetes 的关系定位热搜里出现了 Karmada 毕业的消息这里要澄清一下 ax 和它的关系。Karmada 解决的是多集群的编排问题——把工作负载分发到多个 K8s 集群。而 ax 解决的是单集群内 agent 执行单元的调度问题。两者是互补的不是替代关系。一个典型的组合是Karmada 负责把 agent 服务分发到多个集群比如按地域就近推理ax 负责在每个集群内部把 agent 的 step 调度到合适的节点。如果你只有单集群那 Karmada 这一层可以先不上直接用 ax 就够了。至于 Kubernetes 本身ax 是构建在它之上而不是替代它。节点管理、网络、存储、镜像分发这些还是交给 K8sax 只接管“agent 执行”这一层的调度决策。这个边界一定要划清楚否则你会陷入“重新造一个 K8s”的泥潭。3. 核心机制拆解runtime、调度与隔离3.1 runtime 这一层到底管什么热搜里 runtime 出现的频率极高从 webview2 runtime 到 container runtime 到 llama-server runtime说明大家对“运行时”这个概念既熟悉又模糊。在 ax 的语境下runtime 指的是agent step 的实际执行环境它要解决三个问题语言与依赖隔离agent 可能用 Python 写工具可能是 Node 的代码执行沙箱可能是独立的。runtime 要能按 step 类型加载不同的执行环境。资源配额每个 step 声明自己要多少 CPU、内存、GPUruntime 负责 enforce。生命周期step 开始、执行、产出、回收runtime 要能感知并上报状态。我推荐的做法是分层 runtime底层用 containerd 或 gVisor 做进程级隔离中间层用语言特定的 worker poolPython 用 multiprocessingNode 用 worker_threads上层用 ax 的调度协议对接。这样既保证了隔离性又避免了每个 step 都起一个容器的开销。这里有个参数要特别注意worker pool 的预热数量。设太小冷启动延迟高设太大内存浪费。我的经验公式是预热 worker 数 峰值 QPS × 平均 step 执行时间秒 × 1.3安全系数比如峰值 20 QPS平均 step 跑 1.5 秒那预热数就是 20 × 1.5 × 1.3 ≈ 39 个。实测这个公式在负载波动 ±30% 的情况下都能扛住。3.2 调度策略从 FIFO 到优先级抢占ax 的调度器核心是一个多队列优先级调度器。为什么不用简单的 FIFO因为 agent 任务的优先级差异极大——用户交互式的 agent 任务需要秒级响应后台批处理任务可以等几分钟。如果混在一个队列里一个大批处理任务就能把交互式任务堵死。我的队列设计是这样的队列名优先级典型任务抢占策略interactive高用户实时对话可抢占 batchstandard中常规 agent 工作流不可抢占 interactivebatch低离线数据处理可被任意抢占best-effort最低实验性任务随时可被抢占调度算法用的是加权公平队列 抢占。每个队列分配权重interactive 权重 10standard 权重 5batch 权重 2best-effort 权重 1。当资源不足时低优先级队列的任务会被挂起资源让给高优先级。这里有个坑抢占不能太激进。我一开始设置成“高优先级任务一到就立刻抢占”结果 batch 任务永远跑不完因为 interactive 任务源源不断。后来改成“抢占需要满足最小运行时间阈值默认 30 秒”让 batch 任务至少能跑完一个完整 step情况就好多了。3.3 隔离机制为什么不能只靠 namespace很多人觉得 K8s 的 namespace ResourceQuota 就够了但 agent 场景下不够。原因有三第一agent 会执行用户提供的代码。如果只是 namespace 隔离一个恶意代码可以 fork 炸弹把整个节点打挂。必须上 gVisor 或 Kata Containers 这种强隔离。第二agent 之间的数据要隔离。同一个 namespace 下的 Pod 默认网络互通agent A 能访问 agent B 的端口这在多租户场景下是灾难。第三资源超卖要可控。agent 的 GPU 需求是突发的如果按 request 严格分配利用率极低如果按 limit 超卖又容易 OOM。ax 的做法是分级超卖CPU 可以超卖 2 倍内存超卖 1.2 倍GPU 不超卖但支持时间片共享。GPU 时间片共享这块值得展开说。一张 A100 跑一个 agent 的推理利用率可能只有 20%。ax 的做法是把 GPU 切成多个时间片多个 agent step 轮流用。实现上可以用 MPSMulti-Process Service或者 MIGMulti-Instance GPU。MIG 隔离更彻底但切分粒度粗最小 1/7MPS 更灵活但隔离性弱。我的选择是对隔离要求高的用 MIG对利用率要求高的用 MPS。4. 实操落地从零搭一个 ax 调度层4.1 环境准备与依赖确认先把基础环境列清楚。我假设你已经有一个可用的 K8s 集群1.26 以上并且节点上装了 NVIDIA 驱动和 container toolkit。# 确认 K8s 版本 kubectl version --short # Client Version: v1.28.2 # Server Version: v1.26.0 # 确认节点 GPU 可用 kubectl get nodes -o json | jq .items[].status.allocatable[nvidia.com/gpu] # 确认 container runtime 正常 crictl info | jq .status.conditions如果这里报container runtime is not running先别急着往下走把 runtime 修好。常见原因是 containerd 的配置里SystemdCgroup没开或者 cgroup driver 和 kubelet 不一致。这个错误我在热搜里看到有人问确实是高频坑。# 检查 containerd 配置 cat /etc/containerd/config.toml | grep -A2 SystemdCgroup # 应该是 SystemdCgroup true # 检查 kubelet 的 cgroup driver cat /var/lib/kubelet/config.yaml | grep cgroupDriver # 应该是 cgroupDriver: systemd两者必须一致否则就会出现 runtime 起不来的问题。改完记得systemctl restart containerd kubelet。4.2 部署 ax 调度层ax 调度层我建议用 Helm 部署方便管理配置。核心组件有三个scheduler、runtime-agent、state-store。# values.yaml scheduler: replicas: 2 queueConfig: interactive: weight: 10 preemptible: false standard: weight: 5 preemptible: false batch: weight: 2 preemptible: true minRuntimeSeconds: 30 bestEffort: weight: 1 preemptible: true minRuntimeSeconds: 10 schedulingInterval: 100ms runtimeAgent: workerPool: python: minWorkers: 10 maxWorkers: 100 idleTimeout: 300s node: minWorkers: 5 maxWorkers: 50 idleTimeout: 300s isolation: default: gvisor highTrust: runc stateStore: type: etcd endpoints: - ax-etcd:2379部署命令helm install ax ./charts/ax -n ax-system --create-namespace -f values.yaml # 确认组件状态 kubectl get pods -n ax-system # NAME READY STATUS # ax-scheduler-xxx 1/1 Running # ax-runtime-agent-xxx 1/1 Running # ax-state-store-xxx 1/1 Running4.3 定义一个 agent 任务ax 用 CRD 来声明 agent 任务。下面是一个典型的多步 agent 工作流apiVersion: ax.io/v1alpha1 kind: AgentTask metadata: name: research-agent namespace: default spec: priority: standard sessionAffinity: true steps: - name: retrieve runtime: python image: ax/python-runtime:3.11 command: [python, -m, agent.retrieve] resources: cpu: 500m memory: 1Gi timeout: 30s - name: reason runtime: python image: ax/python-runtime:3.11 command: [python, -m, agent.reason] resources: cpu: 1 memory: 2Gi nvidia.com/gpu: 1 timeout: 120s dependsOn: [retrieve] - name: execute runtime: node image: ax/node-runtime:20 command: [node, agent/execute.js] resources: cpu: 500m memory: 512Mi timeout: 60s dependsOn: [reason] retryPolicy: maxRetries: 2 backoff: exponential这个 CRD 里几个关键字段值得说明sessionAffinity: true保证同一个 agent 任务的所有 step 尽量调度到同一个节点减少跨节点数据传输。dependsOn声明 step 之间的依赖ax 调度器会据此构建 DAG。retryPolicy失败重试策略指数退避避免雪崩。提交任务kubectl apply -f research-agent.yaml # 查看任务状态 kubectl get agenttask research-agent -o yaml # status: # phase: Running # steps: # - name: retrieve # phase: Succeeded # duration: 2.3s # - name: reason # phase: Running # startedAt: 2026-01-15T10:23:45Z4.4 参数计算资源配额怎么定资源配额定多少这是最容易被拍脑袋决定的环节。我给一套可复用的计算方法。CPU 配额agent step 的 CPU 需求分两段——推理前的预处理和推理后的后处理。预处理通常是 IO 密集CPU 需求低后处理如果是解析大 JSONCPU 需求高。我的经验值是CPU request 峰值 CPU 使用率 × 0.6 CPU limit 峰值 CPU 使用率 × 1.5留 40% 的 buffer 给突发limit 设 1.5 倍是为了防止单个 step 卡死影响其他 step。内存配额内存是最容易 OOM 的。agent 场景下内存峰值通常出现在“加载模型”或“解析大响应”时。建议Memory request 稳态内存 × 1.2 Memory limit 峰值内存 × 1.3注意 request 和 limit 的比值不要超过 1.5否则 K8s 的 QoS 等级会降到 Burstable节点内存压力大时容易被驱逐。GPU 配额GPU 不能按小数分除非用 MIG 或 MPS。如果一张卡要跑多个 agent用时间片共享resources: limits: nvidia.com/gpu: 1 annotations: ax.io/gpu-share: 4 # 4 个 agent 共享一张卡这个gpu-share注解会被 runtime-agent 识别通过 MPS 实现共享。实测 4 个 agent 共享一张 A100每个 agent 的推理延迟增加约 15%但整体吞吐提升 3.2 倍非常划算。5. 常见问题与排查技巧实录5.1 调度延迟高从 800ms 到 40ms 的优化过程这是我最开始遇到的问题。agent step 提交后平均要 800ms 才能开始执行。排查下来发现瓶颈在三处第一CRD 的 watch 机制有延迟。K8s 的 informer 默认 resync 周期是 10 分钟虽然 watch 是实时的但事件处理有队列积压。解决办法是给 ax scheduler 单独配置一个高优先级的 informerresync 周期调到 30 秒。第二Pod 创建开销大。每个 step 起一个 Pod镜像拉取 CNI 配置 容器启动加起来 500ms 起步。解决办法是用 warm pool——预先起一批空转的 Podstep 来了直接注入执行。第三调度决策本身慢。最初的调度算法是 O(n²) 的节点一多就慢。改成基于优先队列的 O(n log n) 后决策时间从 200ms 降到 5ms。优化后的效果优化项优化前优化后informer 延迟300ms20msPod 创建500ms15mswarm pool调度决策200ms5ms总计1000ms40ms5.2 runtime 找不到几个高频报错的处理热搜里有一堆 runtime 相关的报错我挑几个 agent 场景下最常见的说说。could not find the webview2 runtime这个通常出现在 agent 需要调用浏览器做网页操作时。webview2 是 Windows 上的组件Linux 环境下要用 Playwright 或 Puppeteer 自带的 Chromium。解决办法是在 runtime 镜像里预装FROM ax/python-runtime:3.11 RUN apt-get update apt-get install -y \ libnss3 libatk1.0-0 libatk-bridge2.0-0 \ libcups2 libdrm2 libxkbcommon0 libxcomposite1 \ libxdamage1 libxfixes3 libxrandr2 libgbm1 libasound2 RUN pip install playwright playwright install chromiumno LM runtime found for model format gguf这个说明你的推理 runtime 不支持 GGUF 格式。GGUF 是 llama.cpp 的格式如果你用的是 vLLM 或 TGI它们不支持。解决办法是换 runtime或者把模型转成 safetensors 格式。我一般建议 agent 场景用 vLLM safetensors因为 vLLM 的 PagedAttention 对多 agent 并发更友好。unable to locate the codex cli binary or required runtime components这是代码执行类 agent 的典型报错。runtime 镜像里缺了 CLI 工具。解决办法是在 runtime 的 Dockerfile 里显式安装并且把 PATH 配好。注意不要用latesttag要锁版本否则某天上游更新了 CLI 的接口你的 agent 就挂了。5.3 常见问题速查表现象可能原因排查命令解决方向step 一直 Pending资源不足或调度器挂了kubectl describe agenttask检查节点资源和 scheduler 日志step 频繁重启OOM 或超时kubectl logs --previous调大 memory limit 或 timeoutGPU 利用率低未开启共享或调度不均nvidia-smi ax 指标开启 MPS/MIG检查调度策略任务卡在某个 step依赖未满足或死锁kubectl get agenttask -o yaml检查 dependsOn 配置跨节点延迟高sessionAffinity 未生效检查 step 所在节点开启 sessionAffinity 或调大亲和权重5.4 几个只有踩过才知道的坑坑一不要给 agent step 设太短的 timeout。我一开始设 30 秒结果很多正常的推理任务被误杀。agent 的推理时间方差极大建议用 P99 值再加 50% buffer。如果确实需要快速失败用单独的 fast-fail 队列而不是全局调小 timeout。坑二warm pool 的 worker 要定期回收。我遇到过 worker 内存泄漏的问题——跑了几百个 step 后worker 的内存涨到 8G 不释放。后来加了maxTasksPerWorker: 50的配置跑满 50 个任务就重建 worker问题解决。坑三agent 的日志要单独收集。agent 的输出可能非常大比如把整个网页内容打出来如果混在容器标准输出里会把日志系统打爆。我的做法是让 agent 把详细日志写到挂载的 emptyDir只把摘要打到 stdout。坑四抢占要记录审计日志。被抢占的任务如果没记录用户会以为任务丢了。ax 的 state-store 里要保留完整的抢占事件包括谁抢了谁、什么时间、什么原因。6. 性能调优与扩展方向6.1 调度吞吐的瓶颈在哪当 agent 任务量上来后比如每秒几百个 step调度器本身会成为瓶颈。我实测下来单实例 ax scheduler 的吞吐上限在 2000 step/s 左右再高就会出现调度延迟。提升吞吐的几个手段分片调度按 agent 任务的 hash 分片到多个 scheduler 实例每个实例只管一部分任务。缺点是跨分片的依赖处理复杂。批量调度把多个 step 攒成一批一起调度减少锁竞争。实测批量大小 32 时吞吐提升 40%。异步状态更新state-store 的写入改成异步批量提交避免每个 step 都同步写 etcd。6.2 和 agentic RAG 的结合热搜里出现了 agentic RAG这其实是 ax 的一个典型应用场景。传统 RAG 是“检索一次生成一次”agentic RAG 是“检索、评估、再检索、再生成”的多轮过程。每一轮检索和评估都是一个 agent step正好用 ax 来编排。我的做法是把检索器、评估器、生成器都封装成独立的 step用 dependsOn 串起来。评估器如果判断检索质量不够就动态生成一个新的检索 step。这种动态 DAG 是 ax 相比静态工作流引擎的核心优势。6.3 后续可以扩展的方向ax 这一层搭好之后往上可以接的东西很多。比如接一个 agent 注册中心让 agent 之间能互相发现接一个成本追踪模块按 step 统计 token 消耗和 GPU 时间接一个 A/B 测试框架对比不同调度策略的效果。我个人最看好的方向是自适应调度——根据历史数据自动调整队列权重和资源配额。比如发现 interactive 队列的 P99 延迟超标了就自动提高它的权重。这个用强化学习或者简单的 PID 控制都能做效果比手工调参好得多。最后分享一个我在实际使用中的体会ax 这类调度层的价值不在于它用了多先进的算法而在于它把 agent 执行的“不确定性”收敛成了“可观测、可控制、可优化”的工程问题。没有这一层你的 agent 系统就是一堆黑盒有了这一层每个 step 的耗时、资源、失败原因都清清楚楚。这个从“不可知”到“可知”的转变才是它真正的意义。