ARTICLE DETAIL

建站实战干货

来自一线的建站与推广经验沉淀,每一条都经过真实交付验证。

Agent Substrate:在Kubernetes之上为智能体构建新原语

2026/9/28 14:56:51 拓冰建站 浏览量
Agent Substrate:在Kubernetes之上为智能体构建新原语 最近云原生圈子里有个讨论值得所有做 AI 基础设施的工程师停下来想一想。Brendan Burns——Kubernetes 三位联合创始人之一——在一次对谈里聊到 Agent Substrate核心观点翻译成大白话就是K8s 把容器编排这件事做得很好了但 Agent智能体这种新工作负载需要在 K8s 之上再拥有一套自己的原语。这个说法乍一听有点反直觉。我们不是早就在 K8s 上跑各种 AI 应用了吗一个 Python 服务接上大模型 API不就是一个“AI 应用”为什么还要单独“造一层”看完相关讨论再结合我自己在 K8s 上跑 Agent 服务的实际经历我反而觉得这个方向是对的而且已经动手做这件事的团队大概率领先半步。这篇文章把我的思考完整整理出来讲四件事K8s 现有原语到底解决了什么问题、边界在哪Agent 服务与容器服务的本质差异到底有多大Agent Substrate 这层“新原语”最可能由哪些具体原语组成以及如果现在就要在 K8s 上落地这套思路实操路径和避坑经验是什么。文章适合正在做 AI 基础设施、准备把 LLM 应用或 Agent 服务迁到 K8s 上的工程师也适合正在评估“平台层要不要加一层”的架构师读完你至少能判断自己手里该补什么组件。1. 先说结论为什么 K8s 管得住容器却管不住 Agent1.1 原语这两个字到底意味着什么在聊 Agent Substrate 之前得先把“原语”这个概念对齐。原语就是构建复杂系统的标准化积木。K8s 里的 Pod、Service、Deployment、HPA就是容器世界的原语。它们定义了容器时代最基础的能力边界我能跑一个最小单元Pod、我能稳定暴露一个入口Service、我能声明式地管理多副本和滚动发布Deployment、我能按指标自动扩缩容HPA。当你只需要关心“几个副本在跑、流量怎么进、资源够不够”时K8s 就是最优解。Agent Substrate 要做的事情和 K8s 是同一类事但对象完全不同。它要给智能体定义一套标准化积木——一个 Agent 任务从开始到结束经历什么样的生命周期、它拥有哪些工具、它能访问什么数据、它消耗怎样成本这些都要变成可描述、可编排、可观测的基本单元。问题的核心在于容器和 Agent 是两种不同的“运行单元”。容器是确定性的执行环境而 Agent 是一个自主决策系统。给这两种东西设计原语难度其实不在一个数量级。1.2 容器和 Agent 的差异拿出来比一比拿现实场景做类比。容器像工厂流水线上的标准工件规格统一坏了就换一个新的完全不耽误生产。Agent 更像银行柜台里的客户经理他有完整的话术历史知道你这个客户之前办过什么业务会话记忆他要调后台系统查余额工具调用他得判断这笔业务该不该继续往下走推理决策。你绝不能在他服务到一半的时候直接换一个什么都不记得的新柜员接着办。这个类比点出了核心矛盾。K8s 的设计哲学是“基础设施可丢弃、可重建”——Pod 宕了就重新调度一个反正无状态服务不在乎。但对 Agent 来说运行过程本身就是核心资产推理轨迹、会话历史、工具返回结果全是这个 Agent 的价值所在。把这些丢掉用户被中断的服务就真的中断了。K8s 的调度器、控制器、健康检查全部建立在“容器”这个抽象上它们不知道什么叫“上下文窗口溢出”不知道什么叫“工具调用返回超时导致决策链中断”更不知道“这个 Agent 已经在这个会话上花了 1.8 万个 Token”。信息缺位能力自然缺位。2. K8s 原语体系复盘管住了什么又漏掉了什么2.1 四大核心原语与设计初衷要理解 Agent Substrate 为什么有必要先得把 K8s 原语吃透。K8s 能在容器编排领域站稳靠的是四个核心原语形成设计闭环。Pod 是最小的调度和运行单元解决“一组容器怎么在集群节点上排布”Service 是稳定的网络访问入口解决“后端 Pod 漂移之后前端访问地址不能跟着漂移”Deployment 是声明式的应用部署单元解决“期望副本数和实际副本数怎么持续收敛”顺带带来滚动发布和回滚能力HPA 是水平弹性伸缩原语解决“流量涨了怎么自动加副本、流量降了怎么自动缩容”。这套原语的闭环逻辑很清晰用 Deployment 描述期望的部署形态用 Pod 承载实际运行单元用 Service 固定访问语义用 HPA 联动负载做弹性伸缩。这个设计聪明在它把“跑服务”这件事的共性抽了出来所以任何无状态 Web 服务、批处理任务、定时任务都能直接套上去。我们日常见到的各类 K8s 发行版和云平台本质上都是在让这四件套更好用、更稳、更省心。2.2 扩展机制CRD 和 Operator为什么也不够可能有人会反驳K8s 不是有 CRD 和 Operator 吗想要什么能力自己扩展不就行了这个说法对了一半。CRD Operator 确实是 K8s 扩展能力的抓手社区里也有不少用自定义控制器管理 AI 工作负载的开源项目。但我要泼一盆冷水CRD 只是给了你“往 API Server 里加一种对象”的能力它没有替你回答“这个对象应该长什么样、字段怎么设计、语义怎么定义”。这就像一套法律体系只规定“你可以制定规则”却没说“你应该制定怎样的规则”。AgentRun、Session、TokenBudget 这些概念字段怎么设计、状态机怎么流转、控制器怎么协调这些语义层面的东西K8s 本身不会给你需要整个行业慢慢沉淀出一套约定俗成的标准。Burns 提 Agent Substrate本质上就是在呼吁大家先把这层语义定义出来。有了语义CRD 才能从“自己造的玩具对象”变成“有标准语义的一等原语”。2.3 一张表看懂 K8s 原语和 Agent 原语的差距维度K8s 现有原语Agent 需要的原语最小运行单元Pod进程 资源AgentRun意图 推理循环期望状态副本数达到 N、Pod Ready任务目标完成、会话可继续调度依据CPU/内存 requests模型上下文、工具可用性、会话亲和性故障处理重启容器、重新调度恢复会话、补偿副作用、幂等重试状态管理PVC 存文件会话记忆 推理历史 上下文摘要成本核算按资源用量计费按 Token 消耗 工具调用次数计费安全控制RBAC NetworkPolicy工具白名单 数据权限 人工审批点这张表是我认为整个讨论最核心的部分。右边那一列几乎每一项都没法直接映射到左边的原语上这就是“造新层”最实打实的理由。3. Agent 工作负载的三个“不匹配”看完就懂为什么需要新层3.1 运行模式不匹配长会话任务 vs 常驻进程我拿实际场景展开。一个真实的 Agent 应用长这样用户进来提问Agent 在一个“感知—推理—行动”的循环里工作先理解用户意图判断要不要查数据库决定调用哪个 API拿到返回结果再继续推理迭代若干轮直到给出最终答案。这个循环短则几秒慢了能持续几十分钟甚至跨天等待用户确认。传统容器负载大致分两类常驻服务Web 服务和批处理任务Spark Job。K8s 对这两类任务的抽象分别是 Deployment 和 Job。Agent 恰好卡在两者之间它每次会话是一个有中间状态的长任务但同时又有实时交互的诉求。你把 Agent 部署成 Deployment副本之间等权任何一个副本都能接任何请求——这在无状态 Web 服务里是正确的但对 Agent 是灾难。用户已经聊了五轮这五轮的状态都在副本 A 上流量一切到副本 B上下文就丢了用户只能从头再来。会话亲和性不是 K8s 原语的诉求但它是 Agent 原语的第一诉求。3.2 状态管理不匹配无状态十二要素 vs 有状态记忆系统云原生十二要素应用有一条铁律无状态一切配置走环境变量启动后不保留本地状态。这条铁律的背后是弹性伸缩的基础——副本随时可以杀掉重建。但 Agent 天生是有状态的。对话历史是短期记忆用户偏好是长期记忆推理过程中的中间步骤、工具调用记录都不能丢。一旦认真做产品立刻就会撞上麻烦这些状态放哪里放在容器本地磁盘副本一重启就消失放在外部数据库每个推理周期都要把几 MB 的上下文塞进模型请求成本和延迟都受不了放在 Redis又要解决淘汰策略、过期时间、上下文压缩问题。更麻烦的是“上下文窗口”这个概念——对话达到大模型上下文上限后要把早前内容抽象成摘要再塞回去这个“压缩记忆”的活由谁干K8s 的 PVC 只管磁盘读写完全不理解“上下文窗口”是一种需要专门管理的资源类型。没有专门的记忆原语每个做 Agent 的团队都会重复发明一套残缺的状态管理轮子而且大概率每个团队设计的都不一样。3.3 资源消耗不匹配确定型资源 vs 弹性不确定资源最后一个不匹配是运维同学最头疼的。K8s 的调度基于 requests/limits容器声明“我要 1 核 2G”调度器按这个装箱。传统容器的资源曲线是可以预测的峰值和均值差距不大。Agent 服务的资源消耗则极其剧烈模型推理阶段CPU 和内存密集占用等待外部 API 返回时容器几乎完全空闲长会话上下文积累内存可以几个 GB 地往上涨同一个请求因为输入 Token 长度不同、是否命中缓存延迟能差出几十倍。你用固定的 requests/limits 给 Agent 容器定资源不是设小了被频繁 OOM就是设大了让宿主机大片资源闲置。Agent 需要的是一套“按会话动态分配资源”的调度语义而不是“按静态规格装箱”的旧语义。这也解释了为什么 Agent 平台普遍需要独立于 K8s 调度器的“上层调度”——K8s 调度器只理解资源规格不理解“这个 Agent 下一步可能需要加载一个更大的模型、需要更多上下文空间”这种动态需求。4. Agent Substrate 的新原语清单从 AgentRun 到 TokenBudget4.1 生命周期原语从 Pod 到 AgentRun如果要在 K8s 之上为 Agent 造原语第一组肯定是生命周期类。K8s 里最小单元是 PodAgent 世界的等价物我认为应该叫 AgentRun——一个 AgentRun 代表一次完整的“感知—推理—行动”闭环可能是一次用户会话也可能是一个后台任务。AgentRun 需要定义自己的状态机Pending等待调度、Planning模型推理中、ToolCalling正在调用外部工具、Suspended等待外部异步事件或人工确认、Completed成功完成、Failed异常终止、Terminated被管理员停止。对比 Pod 的 Pending/Running/Succeeded/FailedAgentRun 的状态要丰富得多。关键区别在 SuspendedAgent 会等待人工审批、等待外部回调这时候“进程活着”不等于“任务推进了”K8s 的 ReadinessProbe 完全无法表达这种中间态。Agent 原语必须能描述“执行到哪一步、卡在什么事件上、需要什么才能继续”。4.2 工具层原语ToolBinding 与 ToolGatewayAgent 区别于普通 AI 应用的本质特征是主动调用工具。查数据库、发邮件、调支付接口、读内部系统都是常见操作。但问题在于这些外部系统的协议、鉴权、限流、数据结构各不相同让每个 Agent 直接对接所有系统不只是代码复杂度爆炸的问题更是安全审计的灾难。没有一个统一出口你根本没法回答“哪个 Agent 在什么时间调了哪个工具、传了什么参数”。所以第二个候选原语是 ToolBinding把“一个可被 Agent 调用的外部能力”定义为一等对象。它包含工具名、OpenAPI schema、鉴权策略、超时配置、限流阈值、审计开关。Agent 运行时不直接碰外部系统而是经过 ToolGateway 统一代理——网关统一做鉴权、注入密钥、限流、记录审计再把结果回传给 Agent。这个设计在理念上和 K8s 的 Sidecar 模式一脉相承把安全、可观测性这些横切关注点从业务逻辑里抽出来放到基础设施层统一管理。4.3 会话与记忆原语Session 和 MemoryStore第三个候选原语是 Session表示一个具体 Agent 会话的上下文容器。里面装着当前对话状态、已调用工具列表、累计 Token 用量、上下文摘要、临时工作变量。我把 Session 看作 AgentRun 的“业务侧镜像”AgentRun 管生命周期Session 管上下文数据前者是控制面后者是数据面。Session 必须持久化底层 Pod 重启后Agent 逻辑从 Session 恢复就好像什么都没发生过。配套要有一个 MemoryStore 抽象解决状态介质问题。别让 Agent 业务逻辑直接读写 PostgreSQL、Redis、向量数据库而是让运行时通过统一记忆接口存取短期记忆进缓存中期记忆进会话库长期记忆进向量库存储层由 Substrate 自己选。这个抽象的价值在于它把“Agent 的状态模型”和“存储实现”解耦了——以后你想换更快的缓存组件、更好的向量数据库改的是 Substrate 内部Agent 业务代码完全不用动。4.4 成本与策略原语TokenBudget 和 Policy最后这组原语最容易被忽略但也最要命——成本控制。容器世界里你看 CPU、内存的占用率Agent 世界里你盯着 Token 消耗。一个失控的 Agent 循环可能以分钟为单位烧掉几天的预算。TokenBudget 应该是写进 AgentRun 声明里的强制字段单次会话 Token 上限、单位时间工具调用次数上限、超出后的自动降级策略比如禁用工具、换更小的模型、以及超限时的兜底动作终止任务并保留上下文快照。Policy 原语管权限这个 Agent 能不能访问某个工具、能不能读某张表、哪些操作需要人工审批。K8s 的 RBAC 管 API 级别的权限Agent 的 Policy 管的是“业务行为级别”的权限。有了 TokenBudget 和 Policy 这两组原语Agent 才真正具备在企业生产环境里安全运营的基础。5. 在 K8s 上落地 Agent SubstrateCRD、KEDA 与最小实现步骤5.1 用 CRD 把 Agent 变成 K8s 一等公民如果你看完上面的分析决定现在就开始动手那最务实的路径就是利用 K8s 自己的扩展机制定义 AgentRun CRD再写一个 Controller 协调它的状态。不要一上来就自研一套全新的编排引擎那是重复造轮子。我把跑通的实施流程整理成步骤你可以直接参考定义 AgentRun CRD字段包含目标模型、系统提示词、允许工具列表、TokenBudget、超时时间、会话持久化要求写一个 Agent Controller用 kubebuilder 或 operator-sdkwatch 新建的 AgentRunController 拿到一个新建的 AgentRun 后把它翻译成底层 K8s 工作负载——一般是一个 Deployment 加一个 PVC或者一个 JobAgent 容器启动后先从 Session 存储恢复上下文然后进入感知—推理—行动循环循环中的关键状态变更通过 SDK 回写到 AgentRun 的 status 字段Controller 根据最新 status 决定后续动作任务该继续、该重启并恢复、该扩容还是该终止回收。这条路径最大的好处是K8s 生态原有的调度、日志、监控、网络策略、Secret 管理能力全部保留你只是在上面叠了一层 Agent 语义。这就是标准的 Operator 模式社区已经开始有开源项目往这个方向探索但语义标准远没有成熟。现在动手做定义的团队有机会把经验沉淀成标准。5.2 一个 AgentRun CRD 实例长什么样说多了抽象直接上配置。下面是我在测试环境里实际用过的简化版 AgentRun 定义你可以照着扩展。重点看字段语义不用纠结具体的 API 版本apiVersion: substrate.example.com/v1alpha1 kind: AgentRun metadata: name: order-refund-agent-001 namespace: customer-service spec: model: endpointRef: internal-llm-gateway # 指向统一的模型接入服务 name: fast-chat-model # 模型别名 temperature: 0.2 systemPrompt: | 你是订单退款处理助手。你可以查询订单、检查退款条件、发起退款申请。 所有退款申请必须经过人工审批确认。 tools: - order-query - refund-check - refund-apply session: storageClass: fast-ssd persist: true maxContextWindow: 128000 budget: maxTokensPerRun: 20000 maxToolCallsPerMinute: 20 onExceed: degrade-to-readonly # 超限后降级为只读禁用退款工具 policy: requireHumanApproval: tools: [refund-apply] timeoutSeconds: 3600 status: phase: ToolCalling currentTool: refund-apply sessionRef: refund-session-001 tokenUsed: 13400 toolCallCount: 12几个字段的设计考量值得展开。budget.maxTokensPerRun为什么是强制字段因为如果不强制总有 Agent 定义会漏掉它然后某个失控的循环悄悄烧掉大量钱。把它放进 CRD 的 OpenAPI 校验里没有预算的 AgentRun 直接拒绝创建这才是“强制约束”落到了实处。policy.requireHumanApproval是生产环境的核心安全要求——退款、发消息、改配置这类高风险工具必须卡一个人工审批点Agent 不能自行完成。status.phase由 Controller 持续更新你可以直接用kubectl get agentrun查看所有 Agent 任务当前跑到哪一步这在排障时极其好用。5.3 事件驱动与弹性伸缩KEDA 是关键零件Agent 服务的流量模式普遍是突发式的早上密集来单下午几乎没人用。为了高峰期把 Agent 容器 7x24 全量开着成本上完全不可接受。这时候 KEDAKubernetes Event-driven Autoscaling就能派上用场。KEDA 监听外部事件源——消息队列堆积长度、HTTP 请求指标、数据库指标——动态决定副本数量。但我必须提醒一个关键问题Agent 的会话亲和性会让 KEDA 的朴素扩容失效。会话压力大时KEDA 自动加副本但假如同一个会话的连续消息被负载均衡到了两个不同副本上下文就断了用户不得不从头开始。所以实际架构里一定要在会话网关层按 SessionID 做一致性哈希路由保证同一个会话始终落到同一个 Agent 副本。扩容解决的是“新会话进不来”的问题已有会话的连续性要靠路由策略保证这两者必须同时设计缺一个都会出线上事故。5.4 最小可用 Agent Substrate 的组件清单如果你要做的是最小可用闭环我建议直接按这个清单采购组件不必自己写全部东西组件用途实现建议AgentRun CRD ControllerAgent 执行单元语义kubebuilder 自研会话存储上下文持久化PostgreSQL Redis工具网关统一鉴权、限流、审计自研轻量代理或 Envoy 扩展模型接入层统一不同 LLM 的 APIEnvoy / 自研路由代理伸缩器按会话积压弹性伸缩KEDA 自定义指标审计日志记录决策链与工具调用结构化日志 对象存储我实际跑通的最小组合是K8s Contour入口网关 KEDA PostgreSQL会话存储 一个自研 AgentRuntime 容器镜像 一个工具网关部署。这个组合的核心代码量不大但解决了 Agent 上生产的绝大部分问题。还是要强调一句不要把时间和精力花在自研一套“Agent 编排引擎”上K8s 已经给了你最难的底层部分你缺的只是对 Agent 语义的薄薄一层封装。6. 避坑实录Agent 上生产最常见的四个事故现场6.1 Agent 崩溃恢复不是简单“重启容器”我踩过最大的坑是想当然地认为“容器崩溃重启就自愈了”。对无状态服务这确实成立但 Agent 崩溃时的状况完全不同推理可能已经产生了副作用——工具调了、数据库改了、消息发出去了——然后进程才死掉。重启以后就算上下文恢复好了但系统不知道“之前到底执行到哪一步”Agent 就会重复执行导致重复扣款、重复发消息。对策有两个必须同时做。第一每次工具调用前先落一条“意图记录”到 Session 存储写清楚“将调用哪个工具、参数是什么”Agent 恢复后先查这些记录判断这次调用是否已经发生过。第二所有关键工具接口必须支持幂等付款、发消息这类操作请求里要带幂等键外部系统按幂等键去重。这两个工程纪律决定了你的 Agent 平台是可恢复的还是事故连环套。6.2 工具调用的超时陷阱不要相信外部 API 给你的默认超时。Agent 调工具时超时设计比普通 API 调用复杂得多因为模型在等待工具返回时这个会话的 Token 已经消耗了一遍——超时太短导致工具调用失败模型就白推理一轮还得重新规划、重新调用超时太长用户那边等得就是天荒地老。我最终采用三层超时设计连接超时 5 秒、响应超时 30 秒、整个工具调用周期上限 2 分钟。更关键的是超时结果本身要作为一条特殊的“工具返回”喂给模型让模型带着“刚才那个工具超时了”这个信息自主决定是换一个工具重试、还是换一种方案、或者直接向用户说明情况。把超时当成工具调用的一种正常返回而不是异常崩溃Agent 的行为会稳定得多。6.3 Token 成本失控比资源失控更可怕如果说容器资源失控只是浪费 CPU 和内存Agent 的 Token 失控就是直接烧钱。我在灰度环境吃过一次大亏测试一个会自主多轮调用工具的 Agent结果一个配置缺陷触发了循环调用半小时烧掉了平时一整天的 Token 预算。从那次之后我把 TokenBudget 做成了 AgentRun CRD 的强制校验字段。没有明确预算上限的 Agent 定义直接拒绝创建运行中每 100 次工具调用做一次预算检查超限就强制终局并保留完整上下文快照供事后分析。成本控制一定要前置到创建阶段不能等出事以后靠监控告警补救。6.4 安全边界让 Agent 有权限但不能滥权最后讲安全。K8s 层面给 Agent 的 ServiceAccount 当然要做最小权限但业务权限边界才是真正的大头。我的建议是所有 Agent 触达外部系统的通道都必须经过工具网关的白名单校验。网关检查调用方 AgentID、目标工具是否有绑定关系、请求参数是否符合 schema、调用频率是否超限。这样即便模型被提示词注入攻击利用攻击面也被锁在这个 Agent 已经授权的工具集里不会扩散到整个集群或整个业务系统。密钥管理还有个细节不要把数据库账号、云厂商密钥直接配置在 Agent 容器里。密钥只放在 K8s Secret 中由工具网关在内部调用时动态注入到请求上下文Agent 代码永远接触不到明文密钥。这一条看着小但生产事故往往就出在这种细节上——某个 Agent 的日志里把密钥打了个明文的例子我见过不止一次。这轮关于 Agent Substrate 的讨论其实点破的是大家已经隐约感觉到、但还没系统表达出来的事K8s 是完美的底座但底座之上确实需要为 Agent 这种新物种补充一套自己的原语。每个人都能感觉到 Pod、Service 和 Agent 之间隔着一层但说不清差在哪。Burns 把它挑明了我觉得这是近期最值得关注的基础设施方向之一。我个人的实操体会是现阶段不要急着做一个全封闭的“Agent 编排平台”先把 CRD、Operator、KEDA、工具网关这四样组合好再补上会话存储和预算控制就已经能覆盖 Agent 上生产 80% 的需求。剩下 20% 的语义标准化工作会随着社区对 AgentRun、Session、TokenBudget 这些概念的讨论逐步收敛。你手里这套基于 K8s 的实现不会白费因为云原生底座没有变你只是提前把 Agent 的语义对齐了——这种提前量才是架构师真正值钱的地方。