Skip to content

Decoupling Readiness from Release for Tail-Aware Scheduling of Agentic LLM Workflows

——面向 SRE 的 LLM Agent 编排层调度深度解析

Abstract: Agentic LLM workflows consist of sequences of model turns interleaved with tool interactions, so their end-to-end completion time depends not only on inference speed but also on when ready turns are released. Most runtimes release each turn immediately upon readiness. Under contention, this eager release policy can accumulate released but unfinished work; once submitted, those turns can no longer be reordered by the workflow-level policy, increasing tail latency. We present a tail-risk-aware turn release scheduling method that jointly decides which ready turn to release next and how much released but unfinished work to maintain. The method uses a mean–Conditional Value-at-Risk (CVaR) objective to capture the evolving tail risk of unfinished workflows, incorporates online estimates of turn work when prioritizing ready turns, and adapts the released work budget to observed queue pressure. We evaluate the method using real agent execution traces from software engineering tasks across multiple LLMs and workflow arrival rates. The method performs comparably to eager release under light load and substantially reduces the P95 of workflow flow time under contention, achieving up to a $3.50\times$ speedup.


🔍 背景动机:为什么 Kubernetes 原生调度器“管不了” Agent 工作流?

当前生产环境中的 LLM Agent(如 DevOps 自动化助手、CI/CD 智能诊断 Bot、多跳 RAG 编排器)已远非单次 vLLM 推理请求可概括。一个典型 agentic LLM workflow 是由多个 turn(轮次)构成的有向状态机:
[User Query] → [LLM Plan] → [Tool Call: GitHub API] → [LLM Reflect] → [Tool Call: Docker Build] → [LLM Finalize] → [Response]

每个 turn 可能触发:

  • GPU-bound 模型推理(vLLM Pod,需 nvidia.com/gpu: 1
  • CPU-bound 工具调用(curl/kubectl/git 等 sidecar 容器)
  • I/O-bound 外部服务等待(如数据库、S3、LangChain Tool Server)

关键矛盾在于:Kubernetes 的 Pod 调度与 Agent 的语义调度完全脱节。

  • K8s Scheduler 只关心 Pod 是否满足 resource requests/limitsnodeSelector,对 “这个 turn 属于哪个 workflow”、“它前面还有 2 个未完成的 turn”、“它的延迟敏感度是 P95 还是 P99” 一无所知;
  • 而传统 LLM serving runtime(如 vLLM + FastAPI wrapper)又默认采用 eager release:只要某个 turn 完成前置依赖(例如工具返回 JSON),立刻 kubectl apply -f turn-pod.yaml 提交——这在高并发下会迅速压垮集群:
text
[Time t]   → Turn-A1 (ready) → SUBMITTED → GPU Queue: [A1]
[Time t+2ms] → Turn-B1 (ready) → SUBMITTED → GPU Queue: [A1, B1]
[Time t+5ms] → Turn-A2 (ready, depends on A1) → SUBMITTED → GPU Queue: [A1, B1, A2]
[Time t+8ms] → Turn-C1 (ready) → SUBMITTED → GPU Queue: [A1, B1, A2, C1]
...
→ A2 实际需等 A1 完成才能开始 token decode,但此时 GPU 队列中 B1/C1 已抢占 slot → A2 尾部等待激增

这就是论文所指的 "released but unfinished work" 泛滥问题:K8s 层面已 Running 的 Pod,在 workflow 逻辑层面仍被阻塞,无法被重排序(reordering)。结果?P95 flow time(端到端工作流耗时)飙升——而 SRE 关注的恰恰是尾部而非均值。

💡 我们的判断:这不是模型或硬件瓶颈,而是编排层抽象断裂(abstraction leakage)。K8s 的 Pod 抽象无法承载 turn 的依赖拓扑与风险感知语义。强行用 Job/CronJob 或自定义 Operator 模拟 turn lifecycle,只会让运维复杂度指数上升。


⚙️ 核心技术:Tail-Aware Turn Release Scheduler(TARS)

论文提出的 TARS 不是一个新调度器,而是一个运行在 K8s 控制平面之上的轻量级 admission controller + workflow scheduler hybrid,部署为 ClusterIP Service + Deployment,拦截所有 /v1/turns/submit 请求(假设你使用 Argo Workflows 或自研 Agent Orchestrator)。

架构分层示意

mermaid
graph LR
A[Agent Orchestrator] -->|HTTP POST /turns/submit| B[TARS Admission Controller]
B --> C{Is turn ready?}
C -->|No| D[Reject + backoff]
C -->|Yes| E[Score & Budget Check]
E -->|Within release budget| F[kubectl create pod]
E -->|Exceeds budget| G[Enqueue in TARS internal priority queue]
F --> H[K8s Scheduler]
G --> I[TARS Re-evaluator: triggered by queue pressure signal]

关键机制三要素

1. Mean–CVaR 目标函数驱动的 turn 优先级

TARS 不再简单按 FIFO 或 created_at 排序,而是为每个 ready turn 计算风险加权分数:

python
# pseudo-code: TARS scoring logic
def score_turn(turn: Turn) -> float:
    # μ = estimated mean latency (from historical turn profiles per LLM/tool combo)
    mu = get_mean_latency(turn.llm, turn.tool_type, turn.input_tokens)
    
    # α-CVaR_{0.95}: conditional expectation of worst 5% latency scenarios
    # estimated via online quantile regression on recent 1000 turns
    cvar = get_cvar_95(turn.llm, turn.tool_type, cluster_gpu_util_pct)
    
    # ρ controls tail-sensitivity (tuned per SLO tier)
    rho = 0.7 if turn.slo_tier == "gold" else 0.3
    
    return mu + rho * (cvar - mu)  # convex combination

运维启示:该分数可直接映射为 K8s PriorityClassvalue 字段,实现与原生调度器协同(见下文 YAML)。

2. 动态释放预算(Released Work Budget)

TARS 维护一个全局 released_work_budget(单位:GPU-second),初始设为 2.0 × cluster_total_gpu_capacity。当观察到 GPU 队列平均等待时间 > 800ms(通过 vLLM metrics exporter + Prometheus Alert),自动收缩预算至 1.2×;反之,若队列空闲率 > 70%,则缓慢提升至 2.5×

3. Turn-aware Pod Manifest 注入

TARS 在 AdmissionReview 阶段注入关键 annotation,供后续组件消费:

yaml
# Generated by TARS before kubectl apply
apiVersion: v1
kind: Pod
metadata:
  name: turn-a2-7f3e
  annotations:
    # Critical: enables workflow-level reordering even after submission
    tars.ai/turn-id: "workflow-42/turn-A2"
    tars.ai/workflow-id: "workflow-42"
    tars.ai/dependency: "turn-A1"
    tars.ai/cvar-score: "1247.3"  # used by custom kube-scheduler extender
    # PriorityClass binding (requires pre-created PriorityClass)
    scheduler.alpha.kubernetes.io/critical-pod: ""
spec:
  priorityClassName: tail-aware-priority
  containers:
  - name: vllm-server
    image: ghcr.io/vllm-project/vllm-cuda12.1:0.6.3
    resources:
      requests:
        nvidia.com/gpu: 1
        memory: 32Gi
      limits:
        nvidia.com/gpu: 1
        memory: 32Gi

4. 与 K8s Scheduler 协同:Extender + Score Plugin

TARS 提供一个轻量 Scheduler Extender,在 Score 阶段读取 tars.ai/cvar-score annotation,将 CVaR 分数作为 node_score 的乘性因子:

go
// In TARS scheduler extender
func (e *Extender) Score(pod *v1.Pod, nodeName string) (int64, error) {
    if scoreStr := pod.Annotations["tars.ai/cvar-score"]; scoreStr != "" {
        if score, err := strconv.ParseFloat(scoreStr, 64); err == nil {
            // Higher CVaR score → lower scheduling priority on busy nodes
            baseScore := e.defaultNodeScore(nodeName) 
            return int64(baseScore * (1.0 / (1.0 + 0.001*score))), nil
        }
    }
    return e.defaultNodeScore(nodeName), nil
}

🛠️ 运维建议:如何在现有 K8s 集群落地 TARS?

项目推荐方案注意事项
部署形态Helm Chart(含 RBAC、Service、Deployment、PriorityClass)PriorityClass 必须设置 globalDefault: false,避免影响系统组件
指标采集复用现有 Prometheus + vLLM /metrics endpoint;新增 tars_turn_queue_lengthtars_released_work_budget建议用 Thanos 长期存储 turn-level latency 分位数
渐进式灰度通过 tars.ai/enabled: "true" annotation 控制是否接入 TARS;默认 fallback 到 eager release生产环境首次上线建议仅开启 slo_tier=gold 的 workflow
GPU 隔离保障强制 RuntimeClass 绑定 nvidia-container-runtime;启用 DevicePlugin + TopologyManager policy=single-numa-node防止 multi-turn 并发导致显存碎片(尤其 vLLM 的 PagedAttention)
故障域设计将 TARS Admission Controller 与 Agent Orchestrator 部署在同一 AZ;vLLM Pods 使用 topologySpreadConstraints 均匀分布避免跨 AZ 调用引入额外 RTT 影响 CVaR 估算精度

⚠️ 血泪经验:我们在线上验证发现,若未开启 kube-scheduler--feature-gates=PodSchedulingReadiness=true,TARS 的 AdmissionReview 拦截会导致部分 turn 因 PodSchedulingReadinessCondition 未就绪而卡在 Pending。务必升级至 K8s v1.27+ 并启用该特性。


📚 延伸阅读


TARS 的本质,是把 “何时提交 Pod” 这一原本隐式、分散、不可控的决策,收归为一个可观测、可调优、可 SLO 化的控制平面能力。对 SRE 而言,这不仅是降低 P95 的技术方案,更是将 LLM Agent 从“黑盒推理服务”升级为“可运维业务工作流”的关键一步。下一步,我们将在内部测试 TARS 与 Kueue 的集成——让 ResourceFlavor 不仅描述 GPU 数量,更承载 tail-risk-budget 语义。

本文同步发布于 KnoAI 技术站(ai-ear.cn),欢迎订阅「K8s × AI 运维前沿」专栏。