Agent 调度新范式:像管理 Pod 一样调度 AI Agent

发布时间:2026/9/28 16:44:19
Agent 调度新范式:像管理 Pod 一样调度 AI Agent 1. 从 Pod 到 Agent调度思路的范式迁移1.1 为什么 Agent 需要“被调度”做过 Kubernetes 的人都有一个肌肉记忆任何长期运行的服务最终都要抽象成一个 Pod交给调度器去决定它跑在哪台机器、什么时候重启、资源给多少。这套逻辑之所以成立是因为 Pod 是一个边界清晰、生命周期可管理、资源可量化的单元。Agent 这个东西刚出来的时候大家的关注点都在“它能不能完成任务”上很少有人去想“它该怎么被管理”。但一旦你手里有几十个甚至上百个 Agent 在跑问题就来了有的 Agent 在等大模型返回占着内存干烧钱有的 Agent 在跑工具调用CPU 打满有的 Agent 因为上游 API 限流卡死了却没人知道还有的 Agent 跑完一次任务就僵在那里既不退出也不释放资源。这些问题的本质和当年物理机时代跑服务遇到的问题一模一样——缺少一个统一的调度层来管理生命周期和资源分配。Google 开源 AX 这个项目核心思路就是把 Agent 当成 Pod 来调度用一套声明式的接口来定义 Agent 的期望状态让调度器去负责实际状态的收敛。这个思路的价值在于它把 Agent 从“一个脚本”升级成了“一个可运维的工作负载”。你可以像写 Deployment 一样写 Agent 的配置指定它需要多少 token 预算、允许多少次重试、超时时间是多少、失败后是重启还是标记为失败。调度器会根据这些声明决定什么时候启动 Agent、什么时候杀掉它、什么时候把它迁移到另一个执行环境。1.2 AX 的核心抽象Agent 即工作负载AX 的设计哲学可以用一句话概括Agent 是一等公民的工作负载不是附属在某个应用里的函数调用。在传统的 Agent 开发框架里Agent 通常是一个类或者一个函数你调用它它返回结果。这种模式在单机、单任务场景下没问题但一旦要并发跑多个 Agent你就得自己写线程池、自己管理超时、自己处理重试。AX 把这些东西全部下沉到调度层Agent 本身只需要关心“我要做什么”不需要关心“我什么时候被启动、被谁启动、资源不够了怎么办”。具体来说AX 定义了几个关键抽象AgentSpec描述 Agent 的期望状态包括镜像或者代码包、入口命令、环境变量、资源需求token 预算、CPU/内存限额、超时策略、重试策略。AgentInstanceAgentSpec 的一个运行实例有唯一的 ID有生命周期状态Pending、Running、Succeeded、Failed、Terminated。Scheduler负责把 AgentInstance 分配到具体的执行节点上考虑资源余量、亲和性、优先级。Executor实际运行 Agent 的载体可以是一个容器、一个进程、甚至一个远程的 serverless 函数。这套抽象和 Kubernetes 的 Pod/Node/Scheduler 几乎是一一对应的。如果你熟悉 K8s上手 AX 会非常快因为概念模型是相通的。如果你不熟悉 K8s也没关系你可以把 AgentSpec 理解成“给 Agent 写的一份说明书”调度器就是“按照说明书安排工作的工头”。1.3 和传统任务调度器的本质区别有人可能会问这不就是 Airflow、DolphinScheduler 那套东西吗把任务丢进去调度器负责按依赖关系执行。区别在于调度粒度和状态管理。传统任务调度器的调度单元是“任务”任务通常是一个脚本或者一个 SQL执行完就结束了状态很简单成功、失败、重跑。但 Agent 不一样Agent 是一个有内部状态的实体它可能在执行过程中产生中间结果、可能调用外部工具、可能因为大模型的不确定性需要人工介入。Agent 的“失败”也不是简单的非零退出码可能是“模型返回了不合规的内容”、“工具调用超时”、“token 预算耗尽”。AX 的调度层需要感知这些细粒度的状态并且根据状态做出决策。比如一个 Agent 因为 token 预算耗尽而失败调度器可以选择给它追加预算后重启而不是直接标记为失败。这种基于语义的调度决策是传统任务调度器做不到的。另一个区别是资源模型的差异。传统任务的资源主要是 CPU 和内存而 Agent 的核心资源是 token 预算和外部 API 的调用配额。AX 把 token 预算作为一等资源来管理调度器在分配 Agent 时会检查剩余预算是否足够不够就排队等待。这个设计非常贴合 Agent 的实际运行场景。2. AX 调度层的核心机制拆解2.1 AgentSpec 的字段设计与参数计算AgentSpec 是整个调度系统的输入它的字段设计直接决定了调度器能做多细粒度的决策。根据我对这类系统的理解一个完整的 AgentSpec 通常包含以下几类字段基础信息类name、namespace、labels、annotations。这些字段和 K8s 的 metadata 类似用于标识和分组。labels 特别重要因为调度器可以根据 labels 做亲和性调度比如“这个 Agent 必须和某个工具服务跑在同一台机器上”。执行体类image、command、args、env。这部分定义 Agent 实际怎么跑。image 可以是一个容器镜像也可以是一个代码包的 URL。env 里通常会放 API key、模型端点、工具配置等。资源类tokenBudget、cpuLimit、memoryLimit、timeoutSeconds。tokenBudget 是 AX 特有的字段表示这个 Agent 最多能消耗多少 token。调度器会根据这个字段做准入控制。策略类restartPolicy、maxRetries、backoffLimit、priorityClass。restartPolicy 可以是 Always、OnFailure、Never和 K8s 一致。maxRetries 控制最大重试次数backoffLimit 控制重试的退避策略。依赖类dependsOn、affinity、antiAffinity。dependsOn 用于声明 Agent 之间的依赖关系调度器会确保依赖满足后才启动。关于 tokenBudget 的计算这里有一个实操中常用的估算公式预估 token 消耗 (系统提示词长度 用户输入长度) × 轮次 工具调用返回长度 × 工具调用次数 输出长度举个例子一个客服场景的 Agent系统提示词 500 token用户输入平均 200 token平均对话 5 轮每轮可能调用 1 次工具工具返回 300 token输出 150 token。那么单次会话的 token 消耗大约是(500 200) × 5 300 × 5 150 × 5 3500 1500 750 5750 token如果你给这个 Agent 设置 tokenBudget 为 10000那么它大概能处理 1.7 次会话。实际配置时建议留 30% 的余量所以 tokenBudget 可以设为 8000 左右配合重试策略使用。注意tokenBudget 的设置不要过于精确因为大模型的输出长度是不确定的。留足余量比精确计算更重要否则 Agent 会在执行过程中频繁因为预算耗尽而中断。2.2 调度器的决策流程与优先级队列AX 调度器的核心工作流程可以拆解成四个阶段准入控制、优先级排序、节点筛选、绑定执行。准入控制阶段调度器会检查 AgentSpec 是否合法比如 tokenBudget 是否为正数、image 是否存在、依赖的 Agent 是否已经定义。如果校验不通过AgentInstance 会直接进入 Failed 状态并记录事件。优先级排序阶段调度器会根据 priorityClass 和提交时间对等待中的 AgentInstance 进行排序。优先级高的先调度同优先级的按 FIFO。这里有一个细节如果一个高优先级的 Agent 因为资源不足无法调度调度器可以选择抢占低优先级的 Agent把它们杀掉释放资源。这个行为和 K8s 的抢占机制一致。节点筛选阶段调度器会遍历所有可用的 Executor 节点根据资源余量、亲和性规则、污点容忍度进行过滤。过滤出来的节点再根据打分函数排序选得分最高的。打分函数通常考虑资源利用率是否均衡、是否已经有相同 Agent 的实例在跑亲和性加分、网络延迟是否低。绑定执行阶段调度器把 AgentInstance 绑定到选中的 Executor 上Executor 负责拉取镜像、启动进程、上报状态。整个流程中优先级队列的设计是关键。我见过一些团队自己实现的 Agent 调度系统用的是简单的 FIFO 队列结果一个跑批的 Agent 把队列堵死了后面交互式的 Agent 全部超时。AX 的优先级队列支持多级优先级并且支持队列配额可以给不同优先级的队列分配不同的资源比例避免低优先级任务饿死高优先级任务。2.3 生命周期管理与状态机设计AgentInstance 的状态机是调度器的核心数据结构。一个设计良好的状态机应该包含以下状态状态含义可转移到的状态Pending已创建等待调度Running、FailedRunning正在执行Succeeded、Failed、TerminatedSucceeded执行成功无Failed执行失败Pending重试时Terminated被主动终止无状态转移的触发条件需要明确定义。比如从 Running 到 Failed 的触发条件可以是进程退出码非零、超时、token 预算耗尽、健康检查失败。从 Failed 到 Pending 的触发条件是重试次数未超过 maxRetries且 backoff 时间已过。这里有一个容易踩坑的地方重试时的状态重置。如果一个 Agent 因为 token 预算耗尽而失败重试时应该重置预算还是追加预算AX 的做法是如果失败原因是预算耗尽重试时会追加预算追加量由 retryBudgetIncrement 字段指定如果是其他原因重试时保持原预算不变。这个设计很实用因为预算耗尽往往是因为任务比预期复杂追加预算比直接失败更合理。另一个细节是优雅终止。当调度器决定终止一个 Running 的 Agent 时比如被抢占应该先发送 SIGTERM等待一个 gracePeriod如果还没退出再发 SIGKILL。Agent 本身应该处理 SIGTERM保存中间状态以便下次重试时可以从断点继续。这个机制对于长运行的 Agent 特别重要。3. 实操从零搭建一个 AX 调度环境3.1 环境准备与依赖安装假设你已经在本地或者测试环境有了一套基础的容器运行时Docker 或者 containerd接下来需要安装 AX 的调度组件。根据我对这类系统的理解AX 通常包含三个核心组件ax-scheduler调度器、ax-executor执行器、ax-apiserverAPI 服务。安装方式一般有两种二进制安装和容器化安装。推荐容器化安装因为依赖管理更简单。以下是一个典型的 docker-compose 配置示例version: 3.8 services: ax-apiserver: image: axproject/ax-apiserver:latest ports: - 8080:8080 environment: - ETCD_ENDPOINTSetcd:2379 depends_on: - etcd ax-scheduler: image: axproject/ax-scheduler:latest environment: - APISERVER_ENDPOINThttp://ax-apiserver:8080 - SCHEDULER_INTERVAL5s depends_on: - ax-apiserver ax-executor: image: axproject/ax-executor:latest environment: - APISERVER_ENDPOINThttp://ax-apiserver:8080 - EXECUTOR_CAPACITY10 volumes: - /var/run/docker.sock:/var/run/docker.sock depends_on: - ax-apiserver etcd: image: quay.io/coreos/etcd:v3.5.0 command: etcd --advertise-client-urls http://0.0.0.0:2379 --listen-client-urls http://0.0.0.0:2379这个配置里etcd 用来存储 AgentSpec 和 AgentInstance 的状态apiserver 提供 REST APIscheduler 负责调度决策executor 负责实际运行 Agent。EXECUTOR_CAPACITY10 表示这个 executor 最多同时跑 10 个 Agent 实例。启动之后你可以用 curl 测试 apiserver 是否正常curl http://localhost:8080/healthz # 期望返回{status:ok}提示如果 executor 启动失败大概率是 docker.sock 的权限问题。确保运行 executor 的用户在 docker 组里或者把 docker.sock 的权限改成 666仅限测试环境。3.2 编写第一个 AgentSpec 并提交环境跑起来之后下一步是定义一个 AgentSpec 并提交给 apiserver。以下是一个最简单的 AgentSpec 示例定义了一个“天气查询 Agent”apiVersion: ax.io/v1 kind: AgentSpec metadata: name: weather-agent namespace: default labels: app: weather tier: interactive spec: image: axproject/weather-agent:1.0.0 command: [python, main.py] env: - name: MODEL_ENDPOINT value: https://api.example.com/v1/chat/completions - name: MODEL_API_KEY valueFrom: secretKeyRef: name: model-secret key: api-key tokenBudget: 8000 cpuLimit: 500m memoryLimit: 512Mi timeoutSeconds: 120 restartPolicy: OnFailure maxRetries: 3 priorityClass: interactive提交方式curl -X POST http://localhost:8080/apis/ax.io/v1/namespaces/default/agentspecs \ -H Content-Type: application/yaml \ --data-binary weather-agent.yaml提交成功后apiserver 会返回 AgentSpec 的详细信息包括 uid 和创建时间。此时调度器会感知到这个新的 AgentSpec但不会立即创建 AgentInstance因为 AgentSpec 只是模板需要创建一个 AgentInstance 才会真正触发调度。创建 AgentInstance 的方式有两种手动创建和自动创建。手动创建适合调试场景curl -X POST http://localhost:8080/apis/ax.io/v1/namespaces/default/agentinstances \ -H Content-Type: application/json \ -d { spec: { agentSpecName: weather-agent, input: { query: 北京今天天气怎么样 } } }自动创建适合生产场景通常通过一个 Controller 来监听某个事件源比如消息队列收到消息后自动创建 AgentInstance。3.3 观察调度过程与状态流转AgentInstance 创建之后你可以通过以下命令观察它的状态变化# 查看所有 AgentInstance curl http://localhost:8080/apis/ax.io/v1/namespaces/default/agentinstances # 查看特定 AgentInstance 的详情 curl http://localhost:8080/apis/ax.io/v1/namespaces/default/agentinstances/weather-agent-001 # 查看调度事件 curl http://localhost:8080/apis/ax.io/v1/namespaces/default/agentinstances/weather-agent-001/events一个典型的调度过程会经历以下事件序列Scheduled调度器选中了某个 executor绑定成功。Pullingexecutor 正在拉取镜像。Started容器启动成功Agent 开始执行。TokenBudgetUpdatedAgent 执行过程中上报 token 消耗预算余量更新。SucceededAgent 执行完成输出结果。如果中间出现问题你会看到Failed事件事件的 message 字段会说明失败原因比如token budget exhausted、timeout after 120s、container exited with code 1。我实测下来从提交 AgentInstance 到状态变成 Running延迟通常在 2-5 秒之间取决于镜像大小和网络速度。如果超过 30 秒还是 Pending大概率是资源不足可以用以下命令查看 executor 的资源余量curl http://localhost:8080/apis/ax.io/v1/executors返回结果里会显示每个 executor 的 capacity、allocated、available 三个字段。如果 available 都是 0说明需要扩容 executor 或者调低 Agent 的资源请求。4. 生产环境中的坑与排查手册4.1 常见问题速查表在实际跑 AX 的过程中我踩过不少坑这里整理成一张速查表方便你遇到问题时快速定位现象可能原因排查方法解决方案AgentInstance 一直 Pending资源不足查看 executor 的 available 字段扩容 executor 或降低资源请求Agent 启动后立即 Failed镜像拉取失败查看 events 里的 Pulling 事件检查镜像地址和网络Agent 执行到一半 Terminated被高优先级 Agent 抢占查看 events 里的 Preempted 事件提高 priorityClass 或错峰执行token 消耗远超预期提示词过长或工具调用过多查看 Agent 日志里的 token 统计优化提示词限制工具调用次数Agent 卡在 Running 不结束外部 API 调用超时查看 Agent 日志里的 HTTP 请求设置合理的 timeoutSeconds重试次数用完了还是失败根本性问题未解决查看每次重试的失败原因修复根因后再重试这张表里的每一条都是我实际遇到过的。其中最容易忽视的是“token 消耗远超预期”因为大模型的 token 计算方式和人类直觉不一样一个看起来很短的中文句子token 数可能是字符数的 1.5 到 2 倍。建议在 Agent 里加一个 token 统计的中间件每次调用模型后记录消耗方便后续优化。4.2 资源争抢与优先级配置的实战经验多 Agent 并发跑的时候资源争抢是必然的。我遇到过最典型的情况是一个跑批的 Agent 在凌晨启动占满了所有 executor 的槽位结果早上的交互式 Agent 全部 Pending用户投诉。解决这个问题的核心是优先级配置 队列配额。AX 的 priorityClass 支持三个级别interactive、batch、best-effort。interactive 用于用户直接触发的 Agentbatch 用于定时任务best-effort 用于可延迟的后台任务。配置队列配额的方式是在 scheduler 的配置文件里指定每个优先级的最大资源占比scheduler: queueQuotas: - priorityClass: interactive maxCPU: 4 maxMemory: 8Gi - priorityClass: batch maxCPU: 8 maxMemory: 16Gi - priorityClass: best-effort maxCPU: 2 maxMemory: 4Gi这样配置之后即使 batch 队列有大量任务排队也不会占用 interactive 队列的资源。interactive 队列的 Agent 永远有资源可用。另一个经验是设置合理的 backoffLimit。默认的重试退避是 10 秒、20 秒、40 秒指数增长。但如果失败原因是外部 API 限流退避时间太短会导致重试也失败。建议根据外部 API 的限流窗口来设置 backoffLimit比如限流窗口是 1 分钟那退避时间至少要是 60 秒起步。4.3 监控与告警的落地方法生产环境跑 AX没有监控就是裸奔。我建议至少监控以下四个指标调度延迟从 AgentInstance 创建到状态变成 Running 的时间。P99 超过 10 秒就要告警。失败率Failed 状态的 AgentInstance 占总数的比例。超过 5% 就要排查。token 消耗速率单位时间内消耗的 token 总量。突然飙升可能是提示词被注入了或者有死循环。executor 资源利用率CPU 和内存的 allocated/capacity 比值。持续超过 80% 就要考虑扩容。监控数据的采集方式有两种一种是 AX 自带的 metrics 接口通常是/metrics用 Prometheus 抓取另一种是在 Agent 里埋点上报自定义指标。推荐两种都做自带的指标看系统层面自定义指标看业务层面。告警规则可以用 Prometheus 的 alerting rules 来定义比如groups: - name: ax-alerts rules: - alert: HighSchedulingLatency expr: histogram_quantile(0.99, ax_scheduling_duration_seconds_bucket) 10 for: 5m labels: severity: warning annotations: summary: AX 调度延迟 P99 超过 10 秒这个规则的意思是如果调度延迟的 P99 在 5 分钟内持续超过 10 秒就触发告警。告警渠道可以接企微、钉钉或者邮件看团队习惯。注意告警阈值不要设得太敏感否则会被噪音淹没。建议先跑一周观察指标的基线再根据基线设置阈值。我一开始把失败率的阈值设成 1%结果每天告警几十次后来调到 5% 才合理。5. 从 AX 看 Agent 调度的未来演进5.1 多集群调度与跨区域协同单集群的调度解决的是“一台机器上怎么安排多个 Agent”的问题但生产环境往往是多集群的。比如你在华北有一个集群在华东有一个集群Agent 应该调度到哪个集群AX 的设计里executor 可以注册到多个 schedulerscheduler 之间通过一个全局的协调层来同步状态。这个协调层可以用 etcd 的 multi-region 模式也可以用专门的全局调度器。核心思路是本地调度器负责本地决策全局调度器负责跨区域协调。跨区域调度的决策因素比单集群复杂得多除了资源余量还要考虑数据亲和性Agent 需要的数据在哪个区域、网络延迟Agent 调用外部 API 的延迟、合规要求某些数据不能跨区域传输。AX 的 affinity 字段支持 region 级别的亲和性配置可以满足大部分场景。我个人的经验是跨区域调度不要追求全局最优因为全局最优的计算成本太高而且网络抖动会导致决策频繁变化。更实用的做法是本地优先 溢出调度Agent 优先调度到本地集群本地资源不足时才溢出到其他集群。这样既保证了大部分场景的低延迟又能在高峰期利用其他集群的闲置资源。5.2 和 Serverless 执行环境的结合AX 的 executor 目前主要是容器化的但容器有一个问题冷启动慢。如果一个 Agent 几分钟才跑一次每次都要拉镜像、启动容器开销很大。这时候 Serverless 执行环境就更合适。Serverless executor 的思路是Agent 的代码打包成一个函数提交给 Serverless 平台平台负责按需启动、按量计费。AX 的调度器只需要把 AgentInstance 绑定到 Serverless executor 上剩下的交给平台。这种结合方式特别适合低频、短时的 Agent 任务。比如一个每天跑一次的报表 Agent用容器跑的话容器启动的 10 秒钟比 Agent 实际执行的 5 秒钟还长。用 Serverless 的话启动时间可以压缩到毫秒级。但 Serverless 也有局限不支持长连接、有最大执行时间限制、冷启动时延不稳定。所以我的建议是混合部署高频、长时的 Agent 用容器低频、短时的 Agent 用 Serverless。AX 的调度器可以根据 AgentSpec 里的 executionMode 字段自动选择 executor 类型。5.3 调度策略的可编程化AX 目前的调度策略是内置的优先级队列、亲和性、抢占这些逻辑都是写死在调度器里的。但实际场景中不同团队的调度需求差异很大内置策略不可能覆盖所有情况。未来的演进方向是调度策略的可编程化允许用户用插件的方式扩展调度器。比如你可以写一个 Go 插件实现一个自定义的打分函数根据 Agent 的历史成功率来决定调度优先级。或者写一个 Python 脚本在准入控制阶段做自定义校验。这种可编程化的调度器本质上是一个调度框架而不是一个调度器。它提供扩展点用户根据自己的需求填充逻辑。K8s 的调度框架就是这么做的AX 大概率也会走这条路。对于普通用户来说可编程化意味着你可以把团队的领域知识编码到调度策略里。比如你知道某个 Agent 在周一早上特别忙就可以写一个策略在周一早上给它更高的优先级。这种灵活性是内置策略做不到的。5.4 我个人的一些实践体会最后分享几个我在实际使用中总结的小技巧不一定对所有人都适用但至少在我这里跑通了。第一个技巧是给 Agent 打标签要克制。一开始我给每个 Agent 打了十几个标签结果调度器的亲和性计算变得很慢。后来精简到三个核心标签app、tier、region调度性能明显提升。标签不是越多越好够用就行。第二个技巧是tokenBudget 要动态调整。固定预算要么不够用要么浪费。我现在的做法是根据 Agent 的历史 token 消耗分布取 P95 作为初始预算然后根据实际运行情况动态调整。AX 的 API 支持更新 AgentSpec 的 tokenBudget 字段不需要重启 Agent。第三个技巧是失败重试要区分错误类型。不是所有失败都值得重试。比如“模型返回内容不合规”这种失败重试大概率还是不合规应该直接标记为 Failed 并通知人工。而“外部 API 超时”这种失败重试往往能成功。AX 的 restartPolicy 支持按错误码配置可以把不可重试的错误码列出来避免无意义的重试。第四个技巧是监控要看趋势而不是看单点。单点的失败率升高可能是偶然但如果连续三个采集周期都在升高那就是趋势需要介入。我现在的告警规则都是基于趋势的比如“失败率连续 3 个周期上升”而不是“失败率超过 5%”。这样误报少了很多。这些经验都是在实际踩坑中积累的希望对你有帮助。AX 这个项目还在快速迭代中很多设计还在演进但“Agent 像 Pod 一样被调度”这个核心思路我认为是方向正确的。如果你正在做 Agent 相关的系统不妨关注一下这个项目或者借鉴它的设计思路自己实现一套轻量级的调度层。