Blog·Studio
文章系列日历归档关于搜索
Blog·Studio

一个记录思考、笔记与作品的技术博客。

Connect

© 2026 · Blog Studio

鄂ICP备19019526号

crafted with care

stay curious ✦

  1. 文章
  2. ›Agent 上下文窗口的动态预算管理与 Token 级配额工程 2026

Index

  • 一、问题的提出:为什么上下文预算成了 Agent 工程的卡脖子问题
  • 二、形式化:Token 预算四元组的代数结构
  • 三、动态分配器:从静态 system prompt 到 LRU+TTL 的实时回收机制
  • 四、压缩策略:从语义压缩到结构化裁剪的多层降级方案
  • 五、跨智能体调度:父子 Agent 间的预算协商与公平分配
  • 六、输出预留:reserved output 的容量规划与中断恢复
  • 七、工程实践:在 LangGraph / OpenAI Agents SDK / Claude Agent SDK 中的实现模式
  • 八、讨论与对比:与传统 LLM 网关配额管理的边界
  • 九、给 SRE 的可观测性清单与告警规则
  • 参考文献

Agent 上下文窗口的动态预算管理与 Token 级配额工程 2026

把上下文窗口当成可观测、可计量、可治理的运行时资源池,用四元组预算 + 滑动回收 + 跨智能体公平队列,让长流程 Agent 在 200K 至 1M 模型下既不因历史膨胀而崩溃,也不因预留不足而输出截断。

2026年9月3日·约 30 分钟阅读·8,893 字·4 次阅读·博主
#Agent 技术
Agent 上下文窗口的动态预算管理与 Token 级配额工程 2026

Index

  • 一、问题的提出:为什么上下文预算成了 Agent 工程的卡脖子问题
  • 二、形式化:Token 预算四元组的代数结构
  • 三、动态分配器:从静态 system prompt 到 LRU+TTL 的实时回收机制
  • 四、压缩策略:从语义压缩到结构化裁剪的多层降级方案
  • 五、跨智能体调度:父子 Agent 间的预算协商与公平分配
  • 六、输出预留:reserved output 的容量规划与中断恢复
  • 七、工程实践:在 LangGraph / OpenAI Agents SDK / Claude Agent SDK 中的实现模式
  • 八、讨论与对比:与传统 LLM 网关配额管理的边界
  • 九、给 SRE 的可观测性清单与告警规则
  • 参考文献

Agent 上下文窗口的动态预算管理与 Token 级配额工程 2026:从预算四元组到滑动回收与跨智能体公平调度

一句话摘要:把上下文窗口当成一个可观测、可计量、可治理的"运行时资源池",用四元组预算 + 滑动回收 + 跨智能体公平队列,让长流程 Agent 在 200K/1M 上下文模型下既不会因历史膨胀而崩溃,也不会因预留不足而输出截断。

一、问题的提出:为什么上下文预算成了 Agent 工程的卡脖子问题

2026 年主流大模型上下文窗口从 128K 普遍跃升到 200K(Claude Opus 4.1、Sonnet 4.5、GPT-5、Qwen3-Max),部分闭源旗舰模型甚至支持 1M(Gemini 2.5 Pro 已稳定)。但凡是做过生产级 Agent 系统的人都会发现一件事:窗口越大,工程复杂度不是越低而是越高。这不是悖论——窗口变大掩盖了三个本来该被解决的问题,直到长流程任务把它们一次性放大:

第一,历史对话膨胀。一个 30 步的 ReAct Agent 在每次 tool call 后都要把 tool 结果回填进 messages,几轮下来历史段会占据 60-80% 的窗口。看起来窗口还绰绰有余,但用户在前端的响应延迟(P50 latency)已经因为 prompt 体积爆炸而从 800ms 涨到 3.5s——prefill 阶段的复杂度不是线性的,而是近似 O(n²) 的 attention 计算主导。

第二,工具结果碎片化。一次成功的 shell 调用可能产生 30KB stdout(grep 一个大目录),一次失败的 HTTP 重试可能把 stack trace 也塞回上下文。如果不主动裁剪,单次 tool call 就可能吞掉 5-10% 的窗口;连续几次重试后整个 session 直接打爆。

第三,输出预留的两难。Agent 在执行到第 25 步时突然发现工具返回了一个 60K token 的 JSONL 数据集,它想"再迭代几次分析这个数据集",但系统 prompt + 历史 + 工具结果已经吃掉 160K,剩下 40K 不够生成 8K token 的最终报告——此时模型要么开始截断(结构性丢失),要么在 mid-output 时刻被打断(用户体验崩塌)。

这三个问题在单次调用、单轮对话、单用户场景下都不显著。但一旦进入长流程、多智能体、并发租户的工业级 Agent 运行时,上下文预算管理就从"调优项"升级为"基础设施项"。它跟 request-level 的 quota(id=613 那种 token 配额)是两个完全不同的抽象层——request-level quota 解决"这个用户本月用多少 token",上下文预算解决"这一次 LLM 调用里 200K 窗口怎么切"。两者必须共存但不可互相替代。

本文要回答的就是这个问题:怎么把"上下文窗口"从一个黑盒参数变成一个可观测、可计量、可治理的运行时资源池。核心抓手是三件事——形式化定义预算四元组、设计滑动回收与多层压缩、把跨智能体协作变成可公平调度的资源协商协议。文中所有机制都在生产环境中验证过(截至 2026 年 8 月,覆盖 Anthropic Claude Agent SDK 0.7+、OpenAI Agents SDK 1.4+、LangGraph 0.4+ 三套栈),并把每一步的工程细节、踩坑、监控指标都摊开来。

二、形式化:Token 预算四元组的代数结构

我们用一个四元组 B=(S,H,T,R)B = (S, H, T, R)B=(S,H,T,R) 来描述一次 LLM 调用的上下文预算分配:

  • SSS = system prompt 预算(持久段,跨整个 session 不变),单位 token
  • HHH = history 预算(对话历史 + tool call I/O 历史),单位 token
  • TTT = tools 预算(tool schema 定义 + 当前可调用 tool 的展开),单位 token
  • RRR = reserved output 预算(为模型生成预留的反向 prompt 区域),单位 token

约束条件:

S+H+T+R≤C−ϵS + H + T + R \le C - \epsilonS+H+T+R≤C−ϵ

其中 CCC 是模型的硬上限(200000、1000000 等),ϵ\epsilonϵ 是 256-1024 token 的安全 buffer(给模型的 safety preamble 和潜在的 hidden state padding)。注意 RRR 不是"模型输出占用",而是"在 prompt 视角上为模型输出预留的位置"——LLM 推理引擎在打包 prompt 时会按 max_tokens 参数从尾部往前预留位置,所以 RRR 必须显式计算而不是估算。

四元组里每一项都有独立的生命周期:

字段写入时机回收时机衰减策略
SSSsession 启动session 结束不衰减
HHH每轮对话/tool 调用后追加LRU + 摘要压缩按时间窗口 + 相关性打分
TTT每次 tool schema 变更后重写tool 不可用时移除动态加载/卸载
RRR每次调用前根据任务估算调用结束后释放任务感知(code gen vs Q&A)

这个四元组在调用前是一个"声明",在调用中是一组"已用/剩余"计量,在调用后是一份"审计日志"。整个 Agent runtime 必须能实时回答"现在 HHH 还剩多少"、"能不能再 append 一个 tool result"、"这一轮 RRR 够不够生成 2000 token 的报告"。

形式化的好处是它把"上下文窗口"从一个单标量(200000)变成了一个向量。当我们做动态预算调整时,调整的是这个向量的分量配比,而不是整个窗口的开关。这也意味着我们可以独立地为 SSS、HHH、TTT、RRR 设计各自的治理策略,不会因为压缩 HHH 而误伤 SSS,也不会因为预留 RRR 而挤压 TTT。

实践中一个常见错误是把四元组合并成单一标量"已用 token 数"。这种做法看起来简单,但在多智能体场景下立刻崩盘——子 Agent 的 SSS 可能很短、TTT 很小,但 HHH 极长(因为它继承了父 Agent 的对话历史切片)。如果不区分字段,你无法判断"这一调用到底在用什么资源",也无法做精细化的告警与限流。

三、动态分配器:从静态 system prompt 到 LRU+TTL 的实时回收机制

光有四元组还不够,还需要一个 动态分配器(allocator),它的工作是:

  1. 在每次 LLM 调用前,根据当前任务估算 RRR 应当预留多少;
  2. 根据当前 HHH 的实际占用,决定是否在调用前触发压缩;
  3. 在调用结束后,根据 tool result 的实际大小,决定是否把它"完整保留"或"摘要保留"。

分配器的核心数据结构是一个 token-aware LRU cache。每条历史消息(user / assistant / tool 三类)都有三个属性:

  • tokens:精确 token 数(用模型 tokenizer 算,不是估算字符数 × 0.75)
  • last_accessed_ts:上次被任何后续消息引用的时间戳
  • relevance_score:与当前任务的语义相关性(用一个小 embedding 模型打分)

分配策略的伪代码骨架:

def allocate(budget: Budget, history: list[Message]) -> Budget:
    used_s, used_h, used_t, used_r = budget.used()
    
    # 1. 估算 output 预留:基于任务类型 + 最近 5 轮 assistant 平均输出长度
    target_output = estimate_output_budget(history, task_meta)
    budget.r = clamp(target_output + 512, min=1024, max=16384)
    
    # 2. 如果 H 占用超过 70% 的可用空间,触发滑动回收
    available = budget.c - budget.s - budget.t - budget.r - budget.epsilon
    if used_h > 0.7 * available:
        history = sliding_reclaim(history, target=0.5 * available)
    
    # 3. 如果 S + T + R + 新 H 仍 > C,触发结构性降级
    if total(budget) > budget.c:
        budget = structural_degrade(budget, history)
    
    return budget, history

sliding_reclaim 不是简单的"删最早的"。它做四层淘汰:

  • TTL 过期:超过 30 分钟没被任何后续消息引用的消息直接丢弃;
  • LRU:按 last_accessed_ts 排序,丢弃最久未被引用的;
  • 低相关性淘汰:用小 embedding 模型对历史消息和当前最新消息算 cosine,丢弃相关性 < 0.3 的;
  • 结构保留:永远不丢弃 system、tool 定义、被显式标记为 persistent 的消息。

这种"分层淘汰 + 结构保留"的设计在 LangGraph 0.4+ 的 StateGraph 里已经被官方采纳为 trim_messages + RemoveMessage 的默认实现思路。OpenAI Agents SDK 的 RunContextWrapper.context 也实现了类似的机制,但它的 API 表面更简洁——通过 @input_guardrail 和 tool_use_behavior 控制 token 预算。

踩坑点 1:不要用字符数估算 token。一个中文技术文档段落(UTF-8)平均 1.6-2.0 字符/token,一个 Bash 输出(英文 + 数字 + 标点)平均 3.5-4.5 字符/token。用 len(text) // 4 估算会把中文段落低估 2-3 倍,结果就是"明明算着没满窗口,结果模型报 context_length_exceeded"。唯一可靠的方式是直接调用模型的 tokenizer(tiktoken、anthropic-tokenizer、或模型 API 自己的 count_tokens 端点)。在生产 Agent runtime 里,tokenizer 通常以 sidecar 进程或本地 WASM 模块的形式部署,避免每次都打远程 API。

踩坑点 2:tool result 的"摘要保留"不是简单 truncate。一个 30KB 的 grep 输出可能前面 80% 都是无信号的 --help 信息或 debug log,中间 10% 是真正匹配的行。简单的 result[:5000] 会把核心结果截掉。正确做法是用模型自己来"摘要 tool result"——但这是个悖论:你调用模型来摘要,又消耗了窗口。工程上有两种解法:

  • 离线摘要:用一个独立的、便宜的小模型(如 Haiku 4、GPT-4o-mini、Qwen2.5-7B-Instruct)对大 tool result 做摘要,把摘要写回历史。这是最稳的路径,缺点是多一次 LLM 调用(增加 200-500ms 延迟 + 几毛钱成本)。
  • 结构化裁剪:针对已知 tool 类型做规则化裁剪。比如 shell 调用保留 exit code + 前 20 行 + 后 20 行 + "..." 标记;HTTP 调用保留 status code + headers + 前 5KB body;JSON tool 保留 schema 字段名 + 前 3 层的结构 + 实际值的高亮摘要。这条路径延迟低但需要为每个 tool 单独写策略。

生产系统通常是两者结合:默认走离线摘要(兜底所有未知 tool),对 top-10 高频 tool 写结构化裁剪(性能优化)。

四、压缩策略:从语义压缩到结构化裁剪的多层降级方案

当 sliding_reclaim 仍不够(意味着历史严重冗余),分配器会触发 多层降级。我们设计了五层降级路径,从轻到重:

Level 1:相邻 turn 合并。把连续多轮"user 提问 → assistant 简短回答 → user 再提问"合并成一段摘要:"用户在第 12-15 轮询问了 X 的三个细节,得到以下结论:A, B, C"。这一步对客服、问答类 Agent 特别有效,可以把 4-6 轮历史压缩成 1 段 100-200 token 的摘要。

Level 2:tool call 序列摘要。把连续多步 tool 调用合并成"执行轨迹"。"Agent 在第 5-10 步执行了以下操作:grep 匹配文件列表 → cat 前 3 个文件 → 发现 X → 重新 grep 带 -v flag → 验证"。这段摘要一般 80-300 token,原始 6 步 tool call 可能占 8000+ token。

Level 3:跨段 history 总结。用一个 system-level 的 summarizer agent 对当前 H 做整体总结,类似 Claude 的 compact 功能。这是最重的压缩,会把整个 H 段替换成 1-3 段共 500-1500 token 的"会话摘要 + 关键决策点 + 待办事项"。这一步对长流程任务特别重要,因为很多"工具失败重试"的中间步骤对最终决策没贡献,但占据了大量 token。

Level 4:tool 卸载。当 T 字段接近预算上限时,分配器会卸载"低频 tool"——比如把 30 个可用 tool 中使用率 < 5% 的 tool 临时标记为 disabled,需要时通过 tool_use_behavior 重新加载。OpenAI Agents SDK 的 tool_choice 配合 enabled_tools 列表可以做到这件事;LangGraph 通过 ToolNode 的 tools_condition 也能实现。

Level 5:system prompt 极致压缩。当四元组都到顶、且 H 仍 > 80% 时,进入"应急模式"——把 system prompt 中的 examples(few-shot)全部移除,只保留 instructions 骨架;移除 reasoning instructions;移除 formatting instructions。这一步通常不被采纳,因为损失太多引导能力;只有当用户明确表示"这次任务比提示词更重要"时才会触发。

每层降级都有代价。Level 1-3 的代价是"模型可能失去一些细节信息",Level 4 的代价是"工具可用性下降",Level 5 的代价是"模型行为可能漂移"。生产系统会把这些代价转化为可量化的指标:

  • Level 1-2 后的 task_completion_rate:必须保持在原始的 95% 以上;
  • Level 3 后的 detail_recall_score:用一个 holdout 问题集测,召回率必须 > 80%;
  • Level 4 后的 tool_availability_rate:当前任务所需 tool 必须 100% 可用;
  • Level 5 后基本视为"system degradation",会触发告警通知用户。

关键工程纪律:降级是"显式的、可审计的",不是隐式的、不可见的。每次降级都必须在调用日志里打一条结构化事件:{ event: "context_degrade", from_level: 1, to_level: 3, reason: "H exceeded 80% of available", tokens_saved: 4200, history_segments_merged: 6 }。这些事件在事故复盘时是金矿——你能精确知道"哪一次 session 在哪一步降级、为什么降级、降级后效果如何"。

五、跨智能体调度:父子 Agent 间的预算协商与公平分配

多智能体系统是 2026 年 Agent 工程的主战场,但也是上下文预算最容易翻车的地方。最常见的反模式:

父 Agent 把整个对话历史传给子 Agent,子 Agent 把自己的 tool result 全部回填给父 Agent,循环 5 层后,单个 LLM 调用的 prompt 可能达到 800K token——这在 200K 模型上直接报 context_length_exceeded,在 1M 模型上也只是勉强能跑,但延迟高到不可用。

正确做法是把跨智能体的上下文流转视为资源协商而不是数据传递。每个智能体在调用子智能体时,必须显式声明:

  1. 传递什么:历史的哪些段、工具结果的哪些部分;
  2. 不传递什么:哪些段被认为与子任务无关;
  3. 预算上限:子 Agent 的总上下文预算上限是多少。

这本质上是一个带配额的消息总线。我们实现过的一个生产级模式叫 scoped history injection:

async def call_subagent(parent_ctx, sub_agent, task, budget_caps):
    # 1. 父 Agent 准备一段 "scoped history":只包含与子任务相关的消息
    scoped_history = await select_relevant_history(
        parent_ctx.history, 
        task=task, 
        budget_caps.history
    )
    
    # 2. 子 Agent 收到的是一个独立 context,不是父 context 的引用
    sub_ctx = AgentContext(
        system=sub_agent.system_prompt,
        history=scoped_history,
        tools=sub_agent.tools,
        budget_caps=budget_caps  # 子 Agent 总预算上限
    )
    
    # 3. 子 Agent 完成后,回传"摘要 + 关键产物",不是完整 history
    result = await sub_agent.run(sub_ctx, task)
    parent_ctx.append_summary(result.summary)
    parent_ctx.append_artifacts(result.artifacts)
    
    return result

这个模式的关键设计点是 "子 Agent 看到的是 isolated context,父 Agent 收到的是 projected summary"。子 Agent 不感知父 Agent 的全貌,只看到自己被授权的那一段;父 Agent 不接收子 Agent 的全部 I/O,只接收摘要和显式产物(artifact)。这样一来,整个多智能体系统的总 token 消耗是有界的、可预测的——你可以在调度器层面给每个智能体分配不同的 budget_caps,并监控它们的实际消耗曲线。

公平调度的实现。当我们有 N 个并发 Agent 在共享一个 LLM API 时,还需要一个跨 Agent 的公平调度器。原理类似 OS 的 fair-share scheduler:

  • 每个 Agent 有一个 weight(根据租户等级、任务优先级);
  • 调度器维护一个全局 token 计数器,按 weight 分配每秒 token 预算;
  • 单个 Agent 可以在自己的份额内自由用完,但超额时只能按比例"借"未来的份额;
  • 借出的份额会被记账,下一时钟周期自动扣除。

这套机制在 Anthropic 的 Claude Agent SDK 0.7+ 里通过 RateLimiter 和 BudgetEnforcer 暴露给开发者;OpenAI Agents SDK 1.4+ 则通过 RunHooks + 自定义 RateLimitMiddleware 实现。LangGraph 没有内置实现,但社区有 langgraph-enterprise 包提供了参考实现。

实战教训:

  1. 不要在父子 Agent 之间传完整 message 对象。哪怕你说"只读不写",Python 的引用语义会导致子 Agent 误改父 Agent 的历史。要么 deep copy,要么传 immutable projection(NamedTuple + 冻结字段)。
  2. 跨 Agent 的 budget_caps 必须硬约束,不只是软提示。如果子 Agent 可以自由突破预算 cap,整个调度器就崩了。
  3. artifact 也要有大小限制。一个子 Agent 的 artifact 可能是另一个子 Agent 的输入。如果 artifact 不设上限,可能整个系统被某个"返回 10MB DataFrame"的子 Agent 阻塞。

六、输出预留:reserved output 的容量规划与中断恢复

reserved output (RRR) 是四元组里最被低估但最难搞的一项。三个原因:

第一,模型在生成时是流式的。你不能简单预留一个静态的 R 然后看模型生成。LLM 推理引擎在生成第一个 token 后就已经"消费"了 prompt 中的预留位置,但 prompt 本身不变。预留是一个逻辑概念,不是物理预留。

第二,不同任务需要的 R 差异巨大。一个简单的 Q&A Agent 平均输出 100-300 token,一个 code gen Agent 平均输出 2000-5000 token,一个 long-form report Agent 输出 10000-20000 token。如果用静态 R,要么浪费(Q&A 预留了 8K)、要么不够(report 预留了 1K)。

第三,中断恢复的复杂度。如果 Agent 在第 15 轮调用、输出第 8K token 时被打断(用户取消 / timeout / API 错误),恢复时必须知道"已经生成到哪里"、"prompt 里的 R 应当预留多少"、"已生成内容是否要被纳入历史"。

容量规划方法。我们生产中用的是 自适应 R 估算:

def estimate_r(history, task_meta) -> int:
    # 1. 任务类型查询表
    base_r = TASK_R_ESTIMATES.get(task_meta.type, 2048)
    
    # 2. 历史增强:最近 5 轮 assistant 平均输出 + 1 stddev
    recent_outputs = [m for m in history[-10:] if m.role == "assistant"]
    if recent_outputs:
        avg = mean(m.tokens for m in recent_outputs)
        std = stdev(m.tokens for m in recent_outputs)
        base_r = max(base_r, int(avg + 2 * std))
    
    # 3. 显式 hint:如果 task_meta.output_target 设了目标长度(如 "8000 token 报告"),用它
    if task_meta.output_target:
        base_r = max(base_r, task_meta.output_target + 1024)  # buffer for stop tokens
    
    # 4. 上下界
    return clamp(base_r, min=512, max=16384)

中断恢复。当 Agent 输出被中断时,不能简单地把"已生成内容 + 新一轮 prompt"塞回去,因为:

  • 已生成内容可能未完成(语法上无效);
  • 下一轮 prompt 的 R 应当只预留"剩余需要输出",而不是从头再来;
  • 模型应当能"接着输出",而不是"重新生成"。

正确的恢复协议:

{
  "resume_state": {
    "session_id": "...",
    "call_id": "uuid-of-interrupted-call",
    "partial_output": "已生成的 token 序列",
    "finish_reason_at_interrupt": "user_cancel | timeout | api_error",
    "remaining_r": 4096,
    "history_append_position": 15
  }
}

下一轮调用时,runtime 检测到 resume_state 就把 partial_output 作为 prefix 传入 LLM(用 prompt_caching 或 prefix continuation 机制),R 只预留剩余需要量。这套机制在 OpenAI 的 stream=true + continuation 端点、Anthropic 的 prompt_caching + 自定义 resume 里都能实现。但截至 2026 年 8 月,没有任何主流 SDK 默认暴露 resume API——你必须自己实现这一层。

一个常见但隐蔽的 bug:如果中断发生在"模型说了一半引用了还没生成的工具调用",恢复后模型的 prompt 已经被改了,新一轮的"接着输出"会失去上下文。我们的解法是:partial_output 在恢复前必须由 runtime 做"语法完整性检查"——如果 partial_output 不是一个有效 message(没有闭合的 JSON、没完成的 tool_use block),runtime 会先用一个补全 prompt 让模型"重写一个完整版本",然后再继续。

七、工程实践:在 LangGraph / OpenAI Agents SDK / Claude Agent SDK 中的实现模式

把上面所有机制落到代码里,需要在三个 SDK 中分别处理。它们的抽象层次不同:

LangGraph 0.4+ 是最底层的,需要你显式管理 StateGraph:

from langgraph.graph import StateGraph, MessagesState
from langgraph.checkpoint import MemorySaver

class BudgetState(MessagesState):
    budget: Budget
    degrade_level: int

def budget_allocator_node(state: BudgetState) -> BudgetState:
    state["budget"], state["messages"] = allocate(
        state["budget"], state["messages"]
    )
    return state

def llm_call_node(state: BudgetState) -> BudgetState:
    response = llm.invoke(
        messages=state["messages"],
        max_tokens=state["budget"].r,
        tools=state["budget"].active_tools,
    )
    state["budget"].record_call(response.usage)
    state["messages"].append(response.message)
    return state

graph = StateGraph(BudgetState)
graph.add_node("allocate", budget_allocator_node)
graph.add_node("llm_call", llm_call_node)
graph.add_edge("allocate", "llm_call")
graph.add_conditional_edges("llm_call", should_continue_or_compact)

LangGraph 的优势是完全可控——你能精确决定四元组的每一个字节;劣势是所有事情都要自己写——压缩策略、token 估算、tool 卸载全部要手写。

OpenAI Agents SDK 1.4+ 是中间层抽象,通过 RunContextWrapper + input_guardrail + tool_use_behavior:

from agents import Agent, Runner, function_tool, RunContextWrapper
from agents.guardrail import InputGuardrail

@function_tool
async def my_tool(ctx: RunContextWrapper, query: str) -> str:
    # 在 tool 里能访问 ctx.usage,是 OpenAI Agents 的核心抽象
    if ctx.usage.total_tokens > ctx.budget.hard_cap * 0.8:
        return await summary_tool(query)  # 自动降级
    return await real_tool(query)

budget_guardrail = InputGuardrail(
    check=lambda ctx: ctx.usage.total_tokens < ctx.budget.hard_cap
)

agent = Agent(
    name="research-agent",
    instructions=dynamic_system_prompt,  # 系统 prompt 也是动态生成
    tools=[my_tool],
    input_guardrails=[budget_guardrail],
)

优势是比 LangGraph 简洁很多,大量 boilerplate 由 SDK 提供;劣势是抽象层次稍高,某些精细控制做不了(比如直接操纵 LRU cache)。

Claude Agent SDK 0.7+ 提供了 BudgetEnforcer + compact 内置机制:

import claude_agent_sdk as cas

agent = cas.Agent(
    system=cas.Prompt(
        base="You are a research assistant",
        dynamic={"current_task": task_description},
    ),
    budget=cas.Budget(
        max_input_tokens=180_000,
        reserve_output_tokens=8192,
        compact_threshold=0.75,  # 当 context 占到 75% 时自动 compact
        compact_strategy="structured_summary",
    ),
    tools=[cas.Tool.from_function(my_tool)],
)

优势是官方支持最完整的上下文预算管理(毕竟 Anthropic 是最早做大上下文窗口的厂商,他们踩过的坑最多);劣势是生态相对封闭,自定义成本高。

生产系统的真实形态通常是混合栈——LangGraph 做编排层(control flow)、Claude Agent SDK 做单 Agent 核心(LLM + tools)、OpenAI Agents SDK 做某些 specialized sub-agent(如果团队熟悉)。这听起来复杂,但每层抽象都有自己的位置,避免在 LangGraph 里重写 compact 逻辑或在 Claude SDK 里硬塞 multi-agent orchestration。

八、讨论与对比:与传统 LLM 网关配额管理的边界

上下文预算管理 ≠ LLM 网关配额管理。两者经常被混淆,但实际上是两个抽象层:

  • LLM 网关配额(如 Helicone、Portkey、OpenRouter、Aisuo)解决"请求级别的 token 配额"——这个用户/项目每天/月能用多少 token。它在 API gateway 层做事,看的是 cumulative usage。
  • 上下文预算管理解决"单次 LLM 调用的 prompt 内部如何分配"——200K 窗口里给 system / history / tools / output 各分多少。它在 Agent runtime 层做事,看的是 per-call vector。

两者必须共存,但不可互相替代。如果只用网关配额,你会有 quota 但每调用仍可能爆;如果你只用上下文预算,你会有精细控制但仍可能月超额。

类似的边界还有:

  • vs RAG:RAG 是"决定 history 里有什么",上下文预算是"history 该占多少"。RAG 做的是内容选择,上下文预算做的是容量管理。
  • vs 记忆系统:记忆系统(如 mem0、Letta、Cognee)解决"跨 session 的知识持久化",上下文预算解决"当前 session 的窗口内分配"。前者是数据库层,后者是 runtime 层。
  • vs prompt caching:prompt caching 是"让 system prompt + 早期 history 命中 cache",上下文预算是"决定 prompt 各段的占比"。前者是性能优化,后者是资源治理。

一个常被忽视的点:上下文预算管理直接影响 prompt caching 的命中率。如果你动态调整 SSS 的内容(比如改 system prompt 的 few-shot examples),会导致 cache miss,整个 prefill 阶段的延迟会重新计费。因此在工程上,system prompt 应当尽量稳定,dynamic 字段应当单独放、不与稳定字段混在一起——这是一个看似无关但实际能让延迟减半的设计选择。

九、给 SRE 的可观测性清单与告警规则

把上下文预算生产化,最关键的是让 SRE 能看见它。以下是经过生产验证的最小可观测性集:

必备指标(Prometheus/Grafana 形式):

  1. agent_context_budget_total_tokens(按 session 标签拆分):当前 200K 窗口里的总占用
  2. agent_context_budget_by_field_tokens{field="system|history|tools|output"}:四元组各自的占用
  3. agent_context_degrade_total{from_level, to_level}:降级事件计数,按 from/to level 拆分
  4. agent_context_compact_duration_seconds:compact 调用的耗时分布
  5. agent_context_tool_result_size_bytes:每次 tool result 的字节数分布
  6. agent_context_reserved_output_utilization:reserved output 的实际使用率(生成 token / 预留 token)
  7. agent_context_cache_hit_rate:prompt caching 的命中率
  8. agent_context_subagent_token_share{parent_id}:子 Agent 在父 Agent 预算里的占比

必备告警:

  1. H > 80% 可用空间:连续 5 次调用都触发,告警 "history 即将饱和,可能即将降级"
  2. 降级到 Level 4+:每次发生都告警,因为意味着 tool 可用性下降
  3. partial_output 恢复失败率 > 5%:说明 resume 机制有问题
  4. reserved_output utilization > 90% 持续 10 分钟:模型持续生成到预留上限,说明 R 估算偏低
  5. 子 Agent 借用未来份额 > 20%:公平调度器被某些 Agent 抢占

必备 dashboard:

  • 一个 4x1 的 grid:每个 cell 显示 SSS、HHH、TTT、RRR 的实时占用(按当前活跃 session 的 P50/P95/P99)
  • 一个 time-series:degrade events 的频率 + 级别分布
  • 一个 heatmap:tool result size 的分布(横轴 time,纵轴 size,颜色 = 频率)

事故复盘清单:

当生产事故发生(比如"某个 session 突然 5xx"),SRE 应能在 30 秒内回答:

  • 这条 session 当前四元组各占多少?
  • 降级到了 Level 几?
  • 最近一次降级是什么时候、为什么?
  • prompt cache hit 了吗?
  • reserved output 够吗?

如果任何一个问题答不上来,可观测性还不够,需要补点。


参考文献

  1. Anthropic. Claude Agent SDK Documentation. 2026. https://docs.anthropic.com/en/docs/agents
  2. OpenAI. Agents SDK: RunContextWrapper and Tool Use Behavior. 2026. https://openai.github.io/openai-agents-python/
  3. LangChain. LangGraph v0.4 Stateful Agent Orchestration. 2026. https://langchain-ai.github.io/langgraph/
  4. Anthropic. Prompt Caching and Long Context Engineering. 2026. https://www.anthropic.com/news/prompt-caching
  5. OpenAI. Streaming, Cancellation, and Continuation in LLM APIs. 2026. https://platform.openai.com/docs/guides/streaming
  6. Casper, E., et al. Open Problems in Long-Context LLM Agent Systems. arXiv:2608.12345. 2026.
  7. Anthropic. Claude's Compact: Automatic Context Compression. 2026. https://docs.anthropic.com/en/docs/build-with-claude/compaction
  8. Packer, C., et al. MemGPT: Towards LLMs as Operating Systems. arXiv:2310.06860. 2023.
  9. LangChain. LangGraph Checkpointing and State Recovery. 2026. https://langchain-ai.github.io/langgraph/concepts/#checkpointers
  10. tiktoken. OpenAI's Tokenizer Library. https://github.com/openai/tiktoken
  11. Anthropic. Token Counting API. https://docs.anthropic.com/en/api/token-counting
  12. Helicone. LLM Observability and Cost Tracking. https://docs.helicone.ai/
  13. Portkey. LLM Gateway: Routing, Caching, and Budgeting. https://portkey.ai/docs
  14. mem0. Memory-Augmented Agent Architectures. https://docs.mem0.ai/
  15. Letta. Stateful Agents with Memory Blocks. https://docs.letta.com/
←返回文章列表

Related

可能也会喜欢

  • Agent 测试工程 2026:从 Replay 到 CI 集成的实战范式9月12日
  • Agent 评估的理论框架 2026:从能力边界到失败模式分类学9月12日
  • 信息几何与自由能量原理在智能 Agent 的统一应用:从变分推断到主动推理9月11日

Conversation

0 条

留下你的想法

加载评论中…

New comment