
1. 从“ax”这个标题说起一个被低估的运行时编排命题第一次看到“ax”这个标题很多人会一头雾水。它不像“Kubernetes 集群搭建”那样直白也不像“Agentic RAG 实战”那样自带场景。但把热搜词摊开来看线索就清楚了ax、agentic、orchestration、runtime、Kubernetes这几个词放在一起指向的其实是一个很具体的技术命题——在 Kubernetes 之上为 agentic 类工作负载构建一套轻量、可编排、可观测的运行时层。我之所以对这个方向感兴趣是因为过去一年里agentic 应用从“单次对话”快速演进到“多步推理 工具调用 状态保持”的形态。一个典型的 agent 任务可能包含解析用户意图、检索知识库、调用外部 API、生成中间结果、校验输出、再决定下一步。这套流程如果跑在本地脚本里调试方便但无法规模化如果直接塞进 Kubernetes 的普通 Deployment又会遇到状态管理、任务编排、资源隔离等一系列问题。ax 这个标题背后本质上是在问agentic 工作负载到底该怎么在 Kubernetes 上跑得稳、跑得省、跑得清楚这篇文章适合三类人看。第一类是做平台工程的手里有 K8s 集群想搞清楚怎么给 AI 类负载做运行时抽象第二类是做 agent 应用开发的想知道自己的多步任务怎么从“本地能跑”变成“线上可靠”第三类是对 orchestration 和 runtime 概念感兴趣、想找一个具体切入点理解它们区别的人。我会尽量把每个设计决策背后的“为什么”讲透而不是只丢一堆 YAML。需要先说明一点ax 并不是某个官方标准项目的名字它更像是一个代号代表“agent execution”这一类运行时抽象。所以下文里我讨论的是一套符合这个命题的通用架构思路结合我在实际集群里踩过的坑给出可复现的方案。你完全可以把这里的 ax 替换成你自己项目里的运行时层名字。2. 为什么 agentic 负载不能直接套用普通微服务那套2.1 普通微服务和 agentic 任务的本质差异普通微服务的请求模型是“短生命周期、无状态、可水平复制”。一个 HTTP 请求进来处理几百毫秒返回结果容器继续等下一个请求。Kubernetes 的 Deployment、Service、HPA 就是为这种模型设计的非常成熟。agentic 任务不一样。它的生命周期可能是几十秒到几分钟中间有多个步骤每一步的输入依赖上一步的输出还可能调用外部工具、读写临时状态。更麻烦的是同一个 agent 任务的不同步骤资源需求差异巨大检索阶段吃内存和网络推理阶段吃 GPU校验阶段几乎不吃资源。如果把它当成一个普通 Pod 跑要么资源按峰值预留导致浪费要么按均值预留导致 OOM。我在早期项目里试过最朴素的做法把整个 agent 流程写成一个 Python 脚本打成镜像用 Job 跑。结果第一个问题就是超时——Kubernetes Job 默认的 activeDeadlineSeconds 和 backoffLimit 在长任务上很难调。第二个问题是状态丢失——Pod 重启后中间结果全没了只能从头再来。第三个问题是可观测性差——一个任务跑了三分钟你根本不知道它卡在哪一步。2.2 orchestration 和 runtime 的职责边界这里必须把两个概念分清楚否则架构会乱。orchestration编排解决的是“任务之间怎么串、怎么并行、怎么重试、怎么依赖”的问题。它关心的是 DAG、状态机、调度策略。Kubernetes 本身提供了 Job、CronJob、以及通过 Operator 模式扩展的编排能力但它不负责“一个 agent 内部的多步推理怎么组织”。runtime运行时解决的是“单个 agent 任务在执行时需要哪些能力支撑”的问题。比如工具调用的沙箱、上下文的管理、中间状态的持久化、token 的计量、执行轨迹的采集。runtime 是贴着 agent 执行引擎的orchestration 是贴着集群调度的。ax 这个命题的价值就在于它试图在 Kubernetes 之上把这两层清晰地叠起来。底层用 K8s 做资源调度和隔离中间用 orchestration 层做任务图管理上层用 runtime 层做 agent 执行支撑。三层各司其职才不会出现“一个 YAML 里塞了所有逻辑”的混乱。2.3 选 Kubernetes 作为底座的理由和代价为什么是 Kubernetes而不是直接上 Serverless 或者自己写调度器理由很实际K8s 提供了成熟的资源隔离cgroups、namespace、网络模型Service、NetworkPolicy、存储抽象PV、PVC、以及丰富的生态Operator、CRD、HPA。对于需要跑 GPU 任务、需要多租户隔离、需要和现有基础设施复用的场景K8s 几乎是默认选择。代价也很明显。K8s 的抽象层次高调试成本大。一个 Pod 起不来可能是镜像问题、资源问题、调度问题、网络问题、权限问题。agentic 负载又特别依赖外部服务模型服务、向量库、工具 API任何一个环节抖动都会导致任务失败。所以 ax 这套运行时层必须把“失败可诊断”作为一等公民来设计而不是事后补日志。3. ax 运行时的核心架构拆解3.1 三层架构调度层、编排层、执行层我实际落地时采用的架构是这样的调度层直接复用 Kubernetes。每个 agent 任务对应一个自定义资源CRD比如叫 AgentTask。这个 CRD 里描述了任务的目标、输入、超时、资源需求。一个 Operator 监听这个 CRD负责把它翻译成实际的 Pod 或 Job。编排层是一个轻量的 DAG 引擎跑在 Operator 内部或者作为独立服务。它把 AgentTask 拆成多个 Step每个 Step 是一个可独立执行的单元。Step 之间有依赖关系编排层负责按拓扑顺序触发处理失败重试和超时。执行层是真正跑 agent 逻辑的地方。每个 Step 对应一个 PodPod 里跑的是 runtime 容器。runtime 容器负责加载 agent 定义、注入工具、管理上下文、上报执行轨迹。这样分层的好处是调度层的问题用 K8s 原生工具排查编排层的问题看 DAG 状态执行层的问题看 runtime 日志。不会出现“一个错误日志里混了三种层次的问题”。3.2 为什么用 CRD Operator 而不是直接写 Job有人会问直接用 Job 不就行了为什么要搞 CRD 和 Operator我的经验是当任务数量超过几十个、任务之间有依赖、需要动态调整资源时裸 Job 的管理成本会急剧上升。CRD 的好处是声明式。你描述“我要一个 agent 任务目标是 X超时 5 分钟需要 1 张 GPU”Operator 负责把它变成实际资源。任务状态、重试次数、当前步骤都记录在 CRD 的 status 里用 kubectl 就能看到全貌。这比翻一堆 Job 的日志高效得多。Operator 的实现语言我选的是 Go因为 client-go 生态最成熟。核心逻辑就是一个 reconcile 循环读取 AgentTask 的 spec对比当前集群状态决定下一步动作。这个模式看起来很笨但极其可靠因为它是幂等的——无论 reconcile 被触发多少次结果都一致。3.3 runtime 容器里到底装了什么runtime 容器是整个 ax 的执行核心。它不是一个简单的 Python 环境而是包含了几层能力agent 定义加载器从 ConfigMap 或挂载卷读取 agent 的配置包括系统提示、可用工具列表、最大步数。工具调用沙箱每个工具调用在一个受限的子进程或独立容器里执行避免工具代码影响主进程。上下文管理器维护对话历史、中间结果、token 计数超过阈值时触发压缩或截断。轨迹上报器把每一步的输入、输出、耗时、token 消耗上报到可观测性后端。健康检查端点暴露 liveness 和 readiness让 K8s 知道容器是否正常工作。这里有个关键设计runtime 容器本身是无状态的所有状态都通过外部存储比如 Redis 或对象存储持久化。这样 Pod 重启后可以恢复执行而不是从头再来。我踩过的坑是早期把状态放在容器本地文件系统结果节点驱逐后任务全丢教训很深刻。4. 实操从零搭一个最小可用的 ax 运行时4.1 环境准备与前置检查假设你已经有一个可用的 K8s 集群版本 1.26 以上。先做几项检查这些是我每次上新集群必做的kubectl version --short kubectl get nodes -o wide kubectl get sc kubectl auth can-i create crd第一项确认客户端和服务端版本匹配。第二项看节点资源和架构如果有 GPU 节点要确认标签。第三项看 StorageClassagent 任务如果需要持久化中间结果得有可用的存储。第四项确认你有创建 CRD 的权限很多托管集群默认不给。如果集群里还没装 Operator 开发框架可以用 kubebuilder 快速初始化kubebuilder init --domain example.com --repo github.com/yourorg/ax-operator kubebuilder create api --group ax --version v1alpha1 --kind AgentTask这两条命令会生成 CRD 定义、控制器骨架和 Makefile。我建议先用默认配置跑通再按需裁剪。4.2 AgentTask CRD 的字段设计CRD 的 spec 设计直接决定了运行时的表达能力。我最终收敛到这几个字段apiVersion: ax.example.com/v1alpha1 kind: AgentTask metadata: name: demo-task spec: goal: 检索最近一周的销售数据并生成摘要 agentRef: sales-summarizer timeoutSeconds: 300 maxRetries: 2 resources: requests: cpu: 500m memory: 1Gi limits: cpu: 2 memory: 4Gi steps: - name: retrieve tool: vector-search timeoutSeconds: 60 - name: summarize tool: llm-invoke dependsOn: [retrieve] timeoutSeconds: 180goal 是任务的自然语言目标agentRef 指向预定义的 agent 配置。steps 是可选的如果不填runtime 会根据 goal 自动规划。timeoutSeconds 和 maxRetries 控制失败行为。resources 直接透传给 Pod。这里有个设计取舍为什么把 steps 做成可选因为有些 agent 是自主规划的你不需要预先指定步骤有些是固定流程的预先指定更可控。两种模式都支持灵活性更高。4.3 Operator 的 reconcile 逻辑Operator 的核心是一个 reconcile 函数。伪代码逻辑如下func (r *AgentTaskReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Result, error) { var task axv1alpha1.AgentTask if err : r.Get(ctx, req.NamespacedName, task); err ! nil { return ctrl.Result{}, client.IgnoreNotFound(err) } if task.Status.Phase { task.Status.Phase Pending r.Status().Update(ctx, task) return ctrl.Result{Requeue: true}, nil } if task.Status.Phase Pending { pod : buildRuntimePod(task) r.Create(ctx, pod) task.Status.Phase Running r.Status().Update(ctx, task) } if task.Status.Phase Running { // 检查 Pod 状态更新 Phase } return ctrl.Result{}, nil }这段逻辑看起来简单但有几个细节要注意。第一每次 Update status 后要 Requeue否则状态不会继续推进。第二创建 Pod 前要检查是否已存在避免重复创建。第三Pod 失败时要根据 maxRetries 决定是重试还是标记失败。我实测下来reconcile 的幂等性是最容易出问题的地方。有一次因为没检查 Pod 是否已存在导致每次 reconcile 都创建一个新 Pod几分钟内集群里堆了几百个 Pod。后来加了 ownerReference 和存在性检查才解决。4.4 runtime 容器的启动流程runtime 容器启动后按这个顺序执行读取环境变量获取 AgentTask 的名称、命名空间、goal。从 ConfigMap 加载 agent 定义。初始化上下文管理器和轨迹上报器。如果 spec 里有 steps按 DAG 执行否则进入自主规划模式。每完成一步更新 AgentTask 的 status。全部完成后把最终结果写入 status 或指定的存储。这里的关键是第 5 步。runtime 要能通过 K8s API 更新 CRD 的 status所以需要给 Pod 配置合适的 ServiceAccount 和 RBAC。我建议单独建一个 Role只允许 update 自己命名空间下的 AgentTask status遵循最小权限原则。5. 编排层的关键设计DAG、重试与超时5.1 DAG 的表示与执行顺序编排层用 DAG 表示步骤依赖。每个 Step 有 name、tool、dependsOn、timeoutSeconds。执行时先做拓扑排序得到可并行执行的批次。比如三个步骤A 无依赖B 依赖 AC 依赖 A。拓扑排序后第一批是 A第二批是 B 和 C 并行。这样能最大化利用集群资源。实现上我用的是简单的 Kahn 算法维护入度表和邻接表。每执行完一批更新入度把入度为零的节点加入下一批。这个算法足够简单不需要引入复杂的图计算库。5.2 重试策略指数退避还是固定间隔重试策略直接影响任务的成功率和资源消耗。我的经验是分场景工具调用失败用指数退避初始 1 秒倍数 2最大 30 秒。因为工具服务可能是临时抖动退避能避开拥塞。模型推理失败用固定间隔 5 秒最多重试 2 次。因为模型服务失败通常是容量问题快速重试没意义。状态写入失败立即重试最多 3 次。因为这是内部依赖失败通常是瞬时问题。这些策略我做成可配置的放在 agent 定义里。默认用指数退避特殊场景覆盖。5.3 超时的分层控制超时要做三层任务级、步骤级、工具调用级。任务级超时由 AgentTask 的 timeoutSeconds 控制Operator 在 reconcile 时检查是否超时超时则删除 Pod 并标记失败。步骤级超时由编排层控制每个 Step 执行前记录开始时间超过 timeoutSeconds 则跳过并标记失败。工具调用级超时由 runtime 控制每次调用工具时设置 deadline。三层超时的意义是任务级防止无限挂起步骤级防止单步卡死工具级防止外部调用拖垮整个流程。我见过只设任务级超时的系统结果一个工具调用卡了 4 分钟整个任务超时但日志里看不出是哪一步的问题。6. 可观测性让 agent 执行过程不再黑盒6.1 轨迹数据的结构设计agent 执行轨迹是排查问题的核心。我设计的轨迹数据结构包含字段说明示例taskId任务唯一标识demo-task-abc123stepName步骤名retrievestartTime开始时间2026-01-15T10:00:00ZendTime结束时间2026-01-15T10:00:12Zinput输入摘要query: 最近一周销售output输出摘要12 条记录tokenUsagetoken 消耗prompt: 500, completion: 200status状态success / failed / timeouterror错误信息工具返回 503这些数据上报到后端后可以按 taskId 聚合还原整个执行链路。我用的是 OpenTelemetry 的 trace 模型每个 Step 是一个 span任务是一个 trace。这样能直接复用现有的可观测性工具。6.2 日志采集的注意事项runtime 容器的日志要结构化用 JSON 格式包含 taskId、stepName、level、message。这样采集到日志系统后可以按字段过滤。有个坑要注意agent 的中间输出可能很长直接打日志会撑爆存储。我的做法是只打摘要完整输出存到对象存储日志里放一个引用 ID。需要看详情时再根据 ID 去取。6.3 关键指标与告警至少要监控这几个指标任务成功率按 agent 类型分组低于 95% 告警。任务 P99 耗时超过阈值告警说明有任务卡住。步骤失败率按工具分组某个工具失败率突增说明外部依赖有问题。token 消耗速率异常增长可能是 agent 陷入循环。Pod 重启次数频繁重启说明资源不足或代码有 bug。这些指标用 Prometheus 采集Grafana 展示。告警规则我建议从宽到严先观察一周再收紧避免误报疲劳。7. 常见问题与排查技巧实录7.1 任务一直 Pending 不执行最常见的原因是 Operator 没正常工作或者 RBAC 权限不足。排查顺序kubectl get pods -n ax-system看 Operator 是否运行。kubectl logs -n ax-system deploy/ax-operator看有没有报错。kubectl describe agenttask demo-task看 status 和 events。检查 ServiceAccount 是否有创建 Pod 的权限。我遇到过一次是 Operator 的 leader election 卡住导致两个副本都在等没人干活。删掉 lease 后恢复。7.2 Pod 启动后立即退出通常是 runtime 容器启动参数有问题。看kubectl logs的最后几行常见错误包括ConfigMap 不存在、agent 定义格式错误、依赖的服务地址不通。有个隐蔽的坑是镜像的 entrypoint 和 K8s 的 command 冲突。如果 Dockerfile 里定义了 ENTRYPOINTK8s 的 command 会覆盖它但 args 会追加。我建议 entrypoint 用 exec 形式command 留空只通过 args 传参。7.3 工具调用超时但任务没失败这说明超时控制没生效。检查 runtime 里工具调用的 deadline 是否设置以及编排层是否检查了步骤超时。我见过的情况是工具调用用了同步 HTTP 请求但没设 timeout结果一直等。解决方法是所有外部调用都必须设 timeout并且用 context 传递取消信号。Go 里用 context.WithTimeoutPython 里用 asyncio.wait_for 或 requests 的 timeout 参数。7.4 任务成功但结果不对这类问题最难排查因为流程没报错。通常是 agent 的提示词或工具返回格式有问题。我的做法是在轨迹里记录每步的完整输入输出然后逐步回放。有个实用技巧在 runtime 里加一个 debug 模式开启后把每步的完整上下文 dump 到文件。复现问题时用 debug 模式跑一次就能看到 agent 到底“看到”了什么。7.5 常见问题速查表现象可能原因排查命令解决方向任务 PendingOperator 异常 / RBAC 不足kubectl logs operator修复 Operator / 补权限Pod 立即退出配置错误 / 依赖不通kubectl logs pod检查 ConfigMap 和网络工具超时未设 deadline看 runtime 日志加 context timeout结果错误提示词 / 数据问题开 debug 模式回放调整提示词或数据任务卡住死循环 / 外部阻塞看轨迹耗时分布加最大步数限制OOM资源限制过低kubectl describe pod调高 memory limit8. 资源管理与成本控制的实际经验8.1 GPU 资源的申请与释放agentic 任务里最贵的是 GPU。我的做法是只有推理步骤才申请 GPU检索和校验步骤用 CPU。这样通过 steps 的资源声明让 K8s 调度器把不同步骤放到合适的节点。但这里有个问题如果每个 Step 是一个独立 PodGPU 的申请和释放会有延迟。我实测下来GPU Pod 从创建到就绪平均 30 秒如果推理步骤只跑 10 秒调度开销比计算还大。解决方案是把多个推理步骤合并到一个 Pod 里或者用 GPU 共享方案比如 MIG 或时间片共享。我倾向于前者因为实现简单代价是灵活性降低。8.2 任务队列与并发控制不加控制的话大量任务同时提交会打爆集群。我在 Operator 里加了一个简单的队列AgentTask 创建后先进入 Pending 队列Operator 按并发上限逐个激活。并发上限按资源类型分别设置CPU 任务最多 20 个并发GPU 任务最多 4 个。这个数字根据集群规模调整原则是不超过节点可承载量的 80%。8.3 成本归因与优化每个 AgentTask 的 token 消耗和 GPU 时长都要记录按团队或项目归因。这样能看出哪个 agent 最贵针对性优化。优化手段包括压缩提示词、缓存常见查询结果、用小模型做初筛、限制最大步数。我做过一个优化把检索步骤的结果缓存 5 分钟重复查询直接命中token 消耗降了 40%。9. 从单集群到多集群扩展性考虑9.1 多集群调度的触发条件单集群跑一段时间后会遇到几个信号资源利用率长期超过 70%、任务排队时间超过 1 分钟、需要跨地域部署降低延迟。这时候就该考虑多集群了。多集群调度有两种模式集中式一个控制面管多个集群和联邦式每个集群独立通过上层协调。我倾向于联邦式因为故障隔离更好一个集群挂了不影响其他。9.2 状态同步的挑战多集群下最大的挑战是状态同步。AgentTask 的 status 要能在集群间可见否则用户不知道任务跑在哪。我的做法是用一个中心化的状态存储比如 etcd 或 PostgreSQL每个集群的 Operator 把状态写进去查询时从中心存储读。这里要注意一致性问题。我用的是最终一致允许短暂的状态延迟因为 agent 任务本身耗时就长几秒的延迟可以接受。9.3 跨集群的故障转移当一个集群不可用时未完成的任务要能转移到其他集群。实现上需要任务定义可移植、中间状态可恢复、结果存储共享。中间状态可恢复是关键。如果任务跑到第 3 步集群挂了转移到新集群后要从第 3 步继续而不是从第 1 步重来。这要求每步完成后都把状态持久化到共享存储。10. 我踩过的几个印象深刻的坑第一个坑是 CRD 的 status 更新频率。早期我每完成一个 token 就更新一次 status结果 K8s API Server 被打爆整个集群的 watch 都变慢。后来改成每完成一个 Step 更新一次问题解决。教训是CRD status 不是高频写入的地方要控制更新频率。第二个坑是 Pod 的优雅终止。agent 任务跑到一半如果 Pod 被驱逐默认的 30 秒终止期不够用。我遇到过任务正在写结果时被 kill导致数据不一致。解决方案是给 runtime 加 SIGTERM 处理收到信号后先保存状态再退出同时把 terminationGracePeriodSeconds 调到 120 秒。第三个坑是镜像拉取策略。默认的 IfNotPresent 在多节点集群里会导致版本不一致有的节点用旧镜像。改成 Always 后虽然拉取频繁但至少保证一致。配合镜像仓库的缓存实际开销可以接受。第四个坑是网络策略。agent 任务需要访问外部服务但默认的 NetworkPolicy 可能禁止出站。我调试了半天才发现是网络策略问题。建议在 runtime 的文档里明确列出需要的出站规则。11. 后续可以扩展的方向这套 ax 运行时跑通后有几个方向值得继续做。一是把 agent 定义做成可版本化的支持灰度发布和回滚。二是加入 A/B 测试能力同一个 goal 用不同 agent 配置跑对比效果。三是把轨迹数据用于自动优化比如识别出哪些步骤最耗时自动调整提示词。还有一个方向是和其他编排系统集成。比如 Karmada 这类多集群编排项目可以把 ax 的 AgentTask 作为一类工作负载接入复用它的调度和故障转移能力。这样不用自己造多集群的轮子。我个人在实际操作中的体会是agentic 运行时的核心难点不在“能不能跑”而在“跑得清楚、跑得省、出问题能查”。Kubernetes 提供了很好的底座但它不理解 agent 的语义。ax 这层运行时的价值就是把这层语义补上让 agent 任务在集群里像普通工作负载一样可管理。如果你也在做类似的事建议先把可观测性和状态持久化做扎实这两块是后面所有优化的基础。