跳到正文

运行态:Pod 池与任务编排 ​

一句话:Agent 运行态要同时满足“随时有环境”和“不按用户独占资源”,答案是把执行环境做成按类型划分的 Pod 池,用会话粘性、检查点和幂等把无状态 Pod 拼成连续任务;代价是调度、回收和恢复逻辑显著变复杂。

为什么需要 ​

最直觉的做法是一用户一 Pod:用户进来就分配执行环境,会话结束再销毁。小规模没问题,规模一上来就撞墙:一万个用户意味着上万个管理 Pod 加执行 Pod,其中大部分时间在等输入,GPU 和内存被长期占住;K8s 要维护的 Pod 数、探针和网络端点随用户数线性增长,控制面先扛不住;用户空闲时资源不能复用,峰值一过就大面积浪费,扩容又慢。根因是把“会话”与“执行环境”绑定:会话是长期存在的逻辑实体,执行环境是短期的计算资源,两者的生命周期不该一致。

核心机制 ​

Pod 池:按执行类型而不是按用户划分 ​

这张图只要抓住一条环和一条分叉:环是 Manager 拆任务 → 队列 → Executor 执行 → 写回共享状态 → 解锁 Manager 继续判断,所有子任务都在这个环上跑;分叉是 Manager 与 Executor 的差别——前者粘会话、持有上下文,所以只能复用不能乱调度,后者无状态,杀掉重建不影响正确性。共享状态只有一处,任何“结果放在 Pod 本地”的实现都会让环断在 Pod 被回收的那一刻。

池按“执行类型”划分,因为不同类型资源需求差异很大:管理 Pod 只做任务分解和聚合,不需要 GPU;分析 Pod 可能需要 GPU;代码 Pod 需要沙箱和临时文件系统。按用户划分会让每种资源都被平均分配,按类型划分才能让每类 Pod 的副本数和资源配额独立调整。

四种池状态 ​

状态含义资源策略
冷池最小副本常驻,随时可分配保底容量,避免冷启动
暖池已分配给会话,正在执行按会话粘性优先复用
热池刚执行完,短时间内保留连接与缓存优先分配给同一会话,命中前缀缓存
回收空闲超过阈值,回到冷池或销毁先缩到最小,再按空闲时长销毁

热池是效果最直接的一层:Skill 已加载、MCP 连接还在、前缀缓存还热,同一会话的追加消息命中热池可以省掉几秒启动和大量重复 prefill。代价是内存被短期占住,回收阈值要在命中率和资源利用率之间调。

生产级 11 状态状态机 ​

核心原则是程序是决策者,LLM 只在 THINK 做选择。LLM 在 THINK 只能输出三选一:PlanUpdate(改计划)、PhaseAdvance(进入下一阶段)、TOOL_ACTION(请求调工具);状态转换、预算检查、循环检测、权限校验都由程序判定。裸 ReAct 把“什么时候停、下一步做什么”全交给模型,模型没有“任务完成”的可靠概念;状态机把控制权收回程序。

状态谁触发做什么出口
INIT任务创建加载任务、身份、预算、DeadlineANALYZE
ANALYZEINIT解析输入、识别附件/权限/边界THINK
THINK上一步完成LLM 三选一决策;程序查预算与指纹SELECT_TOOL / COMPRESS / COMPLETE / INTERRUPTED
SELECT_TOOLTHINK 选工具权限、白名单、沙箱、循环检测EXECUTE / ERROR
EXECUTESELECT_TOOL执行工具,带幂等键与超时OBSERVE / ERROR
OBSERVEEXECUTE 成功校验结果、写回共享状态THINK / COMPRESS / CHECKPOINT
COMPRESS上下文超阈值摘要旧轮次,保留近期与钉住事实THINK
CHECKPOINT步骤边界或 OBSERVE持久化任务图、已完成结果、预算余量THINK
INTERRUPTEDTHINK/SELECT_TOOL 需人工暂停、建审批单、释放执行资源THINK / ERROR
ERROR工具失败/校验失败分类错误;可重试则退避,否则终止THINK / COMPLETE
COMPLETETHINK 判定完成聚合结果、流式返回、落审计结束

状态机的价值不在状态数量,而在每个状态都有明确的进入条件和出口:COMPRESS 解决上下文爆炸,CHECKPOINT 解决长任务可恢复,INTERRUPTED 解决人工介入,ERROR 解决工具失败。程序侧再叠五道闸门:结构化 Plan、五重预算、调用指纹(相同 prompt+工具参数+上下文快照重复出现即判死循环)、策略中心二次校验、全局 Deadline 向下传播;任一超限先降级,仍不行才终止。

状态机的骨架只有一条环、两条旁路、一个出口。先看这张骨架图,再看下面的完整状态图:

完整状态图是把这条环展开成 11 个状态后的样子:主线仍是 THINK → 调工具 → 观察 → 回 THINK,多出来的是压缩、存档、等人、错误四类旁路以及它们的回边。可以直接当参照表用。

看这张图要抓三件事。第一,模型可选的出口只有三个:从 THINK 出发的边只有 SELECT_TOOL、COMPRESS、COMPLETE(加需人工时的 INTERRUPTED),其余状态全部由程序进入——这是“程序是决策者”在图上的形状。第二,COMPRESS 和 CHECKPOINT 是旁路而不是主线:它们做完必须回到 THINK,因为它们改变的是上下文和持久化状态,不改变任务本身。第三,ERROR 有两条出边而不是一条:可重试的退避后回 THINK,判定完成的直接进 COMPLETE(例如“下游返回已存在”,结果已达成就不必重跑)。把这三条画在一张图上,状态机才不是十一个名词,而是控制权的分配方案。

HITL 中断与 Checkpoint 的关系 ​

INTERRUPTED 不是“卡住”,而是一次显式的状态转换:在不可逆操作(删除、发布、支付)之前进入,先把任务图、已完成结果、预算余量和审批上下文写成 Checkpoint,再挂起并释放 Pod。恢复时从 Checkpoint 重建,重新校验权限与预算(人工等待期间权限可能已变),只重放未提交步骤;已经产生的外部副作用不能重放,只能查询或补偿。Checkpoint 是步骤边界的周期性持久化,INTERRUPTED 是其中一种“等人”的持久化原因;两者共用同一份状态,但 HITL 还多一条审批记录和超时策略——审批挂起也要有超时,否则任务会无限期占着预算。

Manager 与 Executor 的职责切分 ​

角色做什么不做什么
Manager接收消息、分解任务、分配子任务、聚合结果不加载业务 Skill,不直接连 MCP
Executor加载 Skill、连接工具、执行步骤、校验输出不做全局规划,不持有会话状态

Manager 不加载 Skill 是刻意的:它只负责编排,工具权限越少,被注入后能造成的破坏越小。Executor 无状态也是刻意的:它可以在任意节点重建,结果写在共享存储里,Pod 回收不影响会话连续性。

一次任务的流转 ​

  1. 消息进入会话管理,按 session_id 找到粘性的 Manager Pod;Pod 已被回收则从池中重建并回放历史。
  2. Manager 读工作流、会话历史、用户权限和预算,进入 ANALYZE/THINK,产出带依赖的子任务图;分发器按类型分配 Executor,依赖未满足的任务留在队列,同一层无依赖任务并行。
  3. Executor 加载 Skill、解析 MCP 依赖、读共享上下文,按 SELECT_TOOL → EXECUTE → OBSERVE 执行;模型调用走统一网关,人工检查创建审批单并进入 INTERRUPTED。
  4. 输出先校验,再用幂等键写入共享结果;子任务完成事件解锁下游,全部完成后 Manager 聚合、流式返回;Pod 进入热池,空闲超时后回收,任务状态和记忆保留。

图中的关键不是步骤数量,而是三个“外部化”:会话状态在共享存储,任务依赖在队列,执行结果在幂等存储。三者都外置后,Pod 才是真正可替换的。

追加消息发给谁 ​

用户追问时,不能简单新建任务,否则会丢掉正在进行的上下文。路由顺序是:会话仍绑定原 Manager 且 Pod 存活,直接投递并延续原任务图;Pod 已回收但任务未结束,从 Checkpoint 重建 Manager、恢复任务图后投递;原任务已结束,判断追问是补充还是新任务,同一任务复用历史,新任务新建子会话;无法判断时向用户确认,或在只读模式下先检索历史再回答。判断“同一任务还是新任务”不能只靠模型自由发挥,通常用规则加模型打分:是否引用上文实体、是否要求修改已有结果、是否改变目标。误判为新任务会丢上下文,误判为旧任务会把无关请求塞进旧预算。

恢复与幂等 ​

失败点恢复方式必须保证
Executor 崩溃从最近 Checkpoint 重跑当前步骤工具调用幂等,重跑不重复扣款/建单
Manager 崩溃重建后回放任务图与已完成结果任务状态已持久化,不能只存在内存
消息重复投递消费端按消息 ID 去重队列至少一次投递下的幂等消费
下游长时间无响应超时后标记失败并解锁或终止不能让整个任务图永久阻塞
回收时仍有未完成步骤拒绝回收或先落 Checkpoint回收不能丢正在执行的中间态

实战场景 ​

场景一:一用户一 Pod 撑出来的账单与控制面 ​

现象:客服场景上线三个月,日活会话 1.2 万,凌晨非高峰仍常驻 1.1 万个执行 Pod,其中 8 千多个申请了 GPU;监控显示这批 Pod 的 GPU 利用率整夜低于 3%(经验值,需校准),月账单里约 6.3 万 GPU 卡时属于“占住但没执行”。连带代价是控制面:watch 事件量与探针重试随 Pod 数线性上涨,节点心跳延迟升高,分配等待时间 P99 从 800ms 涨到 6.4s。用户投诉集中在“追问一句要等好几秒”。

排查:分层排除,先定层再定因。模型层——网关分别记录推理调用耗时,模型侧 P99 环比没变,排除。工具层——MCP 成功率与延迟分布平稳,排除。调度层——把“消息进来到 Pod 就绪”单独打点,这段占端到端延迟七成以上且与池水位正相关;而池水位按会话数走、不按并发任务数走,上限被空闲会话撑住。定位到运行态:Pod 数 = 会话数,长期存在的会话和短期计算资源被绑死。

对症做法:按执行类型切池,只有真需要 GPU 的类型才带 GPU;Manager 只做分解与聚合、不加载 Skill,副本数按会话粘性的并发峰值定,analysis 是唯一保留 GPU 的池。热池留住 Skill 加载状态、MCP 连接与前缀缓存,追加消息命中后省掉几秒启动和大量重复 prefill——上下文累计重发使 token 成本随轮次近似二次增长,少重发一轮就少一层成本,这比省启动时间更值钱。

yaml
pools:
  manager:  {min: 2, max: 8,  idle_recycle: 300s, gpu: 0}
  research: {min: 3, max: 16, idle_recycle: 120s, gpu: 0}
  analysis: {min: 1, max: 6,  idle_recycle: 180s, gpu: 1}
  code:     {min: 2, max: 10, idle_recycle: 90s,  gpu: 0}

复盘要点:四条曲线定成败——Pod 数是否跟并发任务数走、GPU 利用率与池水位是否同向、分配等待 P99 是否由队列深度提前触发扩容、热池命中率是否撑得起它占的内存;命中率上不去时热池只是占资源,idle_recycle 往下压。

场景二:热池回收撞在未提交步骤上 ​

现象:夜间空闲回收上线后,任务出现“结果只剩一半”。一个 14 步的分析任务停在 OBSERVE 之后、CHECKPOINT 之前时被当空闲回收,Pod 内中间结果随容器销毁;恢复时从上一个 Checkpoint 重跑,把已写入共享结果库的步骤又执行一次,下游系统重复建单。一周 23 起(经验值,需校准),重试带来的重复 token 成本约为正常任务的 2.3 倍(经验值,需校准),投诉话术统一是“上午的结果下午不对了”。

排查:仍然是分层。模型层——同 prompt 同工具参数重放,THINK 的三选一输出稳定,不是决策抖动。工具层——该 MCP 确实不支持幂等,但它是放大器不是触发点。调度层——查各状态停留时长,该 Pod 在 OBSERVE→THINK 区间停留 47s,而回收判据只写了一句“距上次请求超过 idle_recycle”。口径错了:回收看的是“有没有请求”,不是“有没有未提交状态”。

对症做法:回收判据改成状态机出口判断。

text
can_recycle(pod):
    if pod.state in (EXECUTE, OBSERVE):                  # 有未提交中间态
        return False, wait_next_tick
    if pod.state == THINK and pending_side_effect(pod):  # 结果已产生未落库
        return False, wait_next_tick
    if pod.state == INTERRUPTED and idle > interrupt_timeout:   # 600s
        return escalate_to_owner, keep_state              # 超时报审批人,不删状态
    checkpoint(pod)                                       # 步骤边界先落 Checkpoint
    return True, back_to_hot_or_cold

写回一律带 ${session_id}:${task_id}:${step_id},checkpoint: after_each_step 保证可切断点是稠密的;timeout: 120s 到了先标记失败并解锁下游,不能让任务图永久阻塞。

状态能否回收不这样做会
EXECUTE / OBSERVE否中间态随 Pod 销毁
THINK 且有待落库副作用否重跑重复扣款建单
CHECKPOINT 之后是恢复只能从头重放
INTERRUPTED 等待中释放 Pod 但保留状态预算被审批等待长期占住

复盘要点:回收只看步骤边界,Checkpoint 与回收必须在同一把锁上;外部副作用一旦提交就只能查询或补偿,重放不算恢复。

场景三:追加消息误判把无关请求塞进旧预算 ​

现象:一个跑到第 15 步的长任务(max_steps: 20),用户在同一个会话里发了句“另外帮我看下昨天那个项目进度”,路由判定为同一任务的补充,投给仍存活的 Manager 并延续原任务图与原预算。结果新请求继承了旧上下文与工具白名单,检索范围被带偏,返回混着两个目标的答案;它继续消耗旧预算触发 COMPRESS,把原任务的关键事实摘要掉,最终人工接管。当月跨任务串味 41 起(经验值,需校准),错误率上升 6.2%,集中在追加消息多的会话。

排查:模型层——固定上下文重放,模型决策一致,不是方差。工具层——参数由上下文推出、白名单沿用旧任务,工具没被误用但用错了场景。运行态——查 session_id 到 task_id 的映射,发现两条无关的用户消息共享同一个 task_id:问题在投递之前的路由,不在池也不在模型。

对症做法:规则加模型打分,两者都不单独定结论;得分高于阈值沿用原 task_id 与延续预算,低于阈值新建子会话,中间区间向用户确认或在只读模式下先检索历史再回答,阈值起点取 0.65 与 0.35(经验值,需校准)。不这样做会:误判为新任务丢上下文,误判为旧任务把无关请求塞进旧预算——后者在五重预算上表现为单任务 token 陡增,是调用指纹之外的第二类信号,要单独告警。

信号判定说明
显式指代上文实体计入同任务“刚才那个”“上面的表格”
要求修改已有结果计入同任务只读检索不算修改
业务域或数据级别变化直接判新任务跨域必须新预算新白名单
原工具白名单覆盖不到直接判新任务不能靠扩旧任务权限绕过
Pod 已回收或距上轮超时降同任务权重从 Checkpoint 重建代价要计入

复盘要点:预算作用域必须与 task_id 一致;路由判定要有可解释的信号和阈值,不能只让模型自由发挥;把“单会话 token 增速明显快于轮次增速”当作串味的直接指标,因为二次增长本就该有形状,变形说明预算里混了别的任务。

最小上手 ​

先用一个 Manager 池加一个 Executor 池跑通,不要一开始做四类池:

yaml
pools:
  manager: {min: 2, max: 6, idle_recycle: 300s, gpu: 0}
  executor: {min: 2, max: 10, idle_recycle: 180s, gpu: 0}
task:
  max_steps: 20
  checkpoint: after_each_step
  idempotency_key: "${session_id}:${task_id}:${step_id}"
  interrupt_timeout: 600s
  timeout: 120s

验收看五件事:杀掉 Executor 后任务能继续;重复投递不会重复写结果;追加消息能命中同一会话;池满时请求排队而不是失败;空闲后副本能缩回最小值。

生产动作 ​

  • 池容量按类型分别定:用真实任务分布估算各类型的并发峰值,不要所有池用同一个副本数。
  • 回收看状态:只在步骤边界回收,执行中的 Pod 先落 Checkpoint;回收前检查是否有未提交结果。
  • 预热:冷启动慢的镜像和模型连接提前预热,扩容按队列深度提前触发,不等超时。
  • 隔离:不同租户、不同数据级别的任务尽量不共享同一个执行 Pod;至少做到工作目录和凭证隔离。
  • 可观测与压测:记录池水位、分配等待时间、热池命中率、回收率、Checkpoint 恢复次数、各状态停留时长和失败原因;重点压“池耗尽 + 大量追加消息 + 下游超时”的组合,而不是只压单任务吞吐。

边界 ​

  • 池化不适合强隔离场景:同一 Pod 复用给不同租户会带来数据残留风险,强合规场景要按租户分池或每次新建。
  • 热池有内存成本:命中率不够高时,热池只是占着资源;要用命中率和回收率判断是否保留。
  • Checkpoint 不是免费的:每步持久化会增加延迟和存储,步骤很细时要合并 Checkpoint。
  • 幂等需要业务配合,小规模不必池化:平台只能传幂等键,真正保证不重复写需要下游系统支持,外部副作用只能补偿不能靠重放;并发很低时按需创建 Pod 更简单,池化反而增加调度复杂度。

讲清楚 ​

运行态的核心是把会话和执行环境解耦:会话是长期的逻辑实体,Pod 是短期的计算资源。做法是按执行类型建池,用冷池保底、热池提命中、回收控成本;Manager 只编排不碰工具,Executor 只执行不持有状态;会话、任务依赖和结果都外置到共享存储,Pod 才能随时替换。循环用 11 状态机而不是裸 ReAct:程序决定状态转换和预算,LLM 只在 THINK 做三选一决策;人工介入进 INTERRUPTED,先落 Checkpoint 再挂起,恢复时重新校验权限和预算,已提交的副作用不重放。追加消息靠会话粘性和 Checkpoint 延续,恢复靠幂等键和步骤边界。池化带来复杂度和隔离风险,强合规或低并发场景要慎重。

适用:2026 年企业级 Agent 运行态实践 | 初稿 2026-10

把 AI 工程讲到既懂也会用