Appearance
06. 缓存、Batch、Flex 与后台任务编排
版本:
v1.2最后更新:
2026-07-09适用对象:已经开始处理真实流量,正在拆同步 / 异步 / 批处理任务,或者在关注 prompt caching、background、batch、flex、priority 和 conversation state 的团队
很多团队刚进入生产期时,最容易把所有任务都塞进一条同步请求里。
表面上这样最简单,但一放量之后,常见问题会一起出现:
- 端到端延迟越来越长
- 长任务容易超时
- 相同前缀反复付费
- 低优先级任务挤占高价值流量
- 会话状态和后台任务混在一起,恢复困难
按 2026-07-09 可访问的 OpenAI Prompt caching、Background mode、Batch、Flex processing、Priority processing、Conversation state、Webhooks、Production best practices 官方资料来看,更稳的思路通常不是“一个模式跑天下”,而是:
- 先按任务价值和时效要求分层
- 再决定同步、后台、批量、低优先级和缓存策略
0.1 真正要治理的不是“用哪个模式”,而是“哪条运行 lane 承接哪类任务”
OpenAI 当前 Background mode、Batch、Flex processing、Priority processing、Conversation state、Production best practices 放在一起看,最值得先建立的一个共识是:
- 这些能力不是零散开关
- 它们更像不同的运行 lane
也就是说,一个成熟系统真正要回答的通常不是:
- “我们要不要用 batch”
而是:
- “这类任务应该走同步 lane、后台 lane、离线批处理 lane,还是低优先级节流 lane”
一旦 lane 分错,后面会出现的往往不是一点小抖动,而是:
- 关键在线流量被低优先级任务挤爆
- 长任务把连接撑死
- 低价值任务花掉高价值 lane 的预算
1. 先把五类运行模式分开
1.1 同步请求
适合:
- 用户正在等待结果
- 首屏体验直接受影响
- 结果必须立即返回
1.2 后台任务
适合:
- 单次任务很长
- 用户不需要一直保持连接
- 允许排队、轮询和后取结果
1.3 批处理任务
适合:
- 请求很多,但不要求实时
- 可统一排队
- 追求更低单位成本
1.4 低优先级弹性任务
适合:
- 对时延不敏感
- 更关心成本
- 可容忍资源波动
1.5 缓存命中型请求
适合:
- 大量共享稳定前缀
- 前几段上下文高度重复
- 想同时降成本和降延迟
2. Prompt caching 真正该怎么理解
OpenAI 当前 Prompt caching 明确说明:
- 这是自动生效的
- 可以显著降低输入成本和延迟
2.1 它最适合哪类系统
- system prompt 很长
- 公共规则很稳定
- tool schema 重复出现
- 多个请求共享相同前缀
2.2 更稳的 prompt 组织方式
OpenAI 当前关于缓存的资料都在强调一个要点:
- 静态内容尽量放前面
- 动态内容尽量放后面
2.3 最容易踩的坑
- 把动态字段插到前缀中间。
- 每次都重排工具说明顺序。
- 相同任务却没有稳定的
prompt_cache_key设计。
2.4 prompt caching 更像“前缀资产治理”,不只是省一点钱
很多团队会把 prompt caching 理解成一个纯成本优化开关。
但更长期有价值的视角通常是:
- 哪些前缀值得被平台长期稳定化
例如:
- system / developer instructions
- 工具 schema
- 公共 policy
- 稳定工作流骨架
这带来的不只是成本收益,还会影响:
- 前缀是否易于版本管理
- release bundle 是否容易冻结
- route bundle 是否能稳定复用
所以 prompt caching 往往反过来要求团队把 prompt 资产治理得更干净,而不是更随意。
2.5 缓存命中也要按 route / tenant / task bucket 看
如果只看全局命中率,很多问题会被掩盖:
- 某条高频主路径命中很好
- 某条高价值路径却因为前缀抖动几乎不命中
更稳的做法通常是按这些维度拆开看:
- route bundle
- task bucket
- tenant tier
- prompt version
这样你才能知道:
- 是缓存策略没设计好
- 还是某一类路径的 prompt 本来就不适合吃缓存
2.6 prompt_cache_key、保留时长和溢出率都要进运行设计
很多团队知道“有缓存”,但不知道真正应该治理哪些变量。
OpenAI 当前 Prompt caching 文档里,几个特别值得写进运行手册的点是:
- 缓存通常依赖 prompt 初始前缀的哈希,常见是前
256tokens 的前缀路由心智 - prompt 至少到
1024tokens 以上才真正具备缓存收益面 - 可以通过
prompt_cache_key影响路由和命中稳定性 - 相同前缀与同一
prompt_cache_key如果速率太高,官方文档提到大约在15 RPM量级可能出现 overflow,导致部分请求被分流到别的机器,命中率反而下降
这意味着缓存设计不该只看“有没有命中”,还要同时看:
- 同一路由是否用了稳定的
prompt_cache_key - 某个热点前缀是否被单 key 打爆
- prefix 版本切换时是否做了灰度,不要所有流量瞬时切前缀
同样要注意 retention policy:
in_memory更偏短时命中,官方文档给出的典型心智是空闲5到10分钟,最长到1小时24hextended retention 更适合长周期重复任务,但也意味着你需要更认真地评估数据保留与区域处理要求
所以对生产团队来说,真正该治理的不是“开没开缓存”,而是:
- 哪些 bundle 值得稳定成共享前缀
- 哪些流量该共用 key
- 哪些流量需要拆 key 防热点溢出
- 哪些模型和项目该使用哪种 retention policy
3. Background mode 适合什么,不适合什么
OpenAI Background mode 当前最值得注意的几个点非常直接:
- 用
background=true让长任务异步运行 - 响应对象可以轮询状态
- 支持取消
- 也支持背景流式
3.1 它最适合的场景
- 长推理
- 长输出
- 多步工具编排
- 用户不需要卡在连接上等待
3.2 它不该被当成什么
它不等于:
- 批处理
- 实时聊天
- 纯离线大规模补算
它更像:
- 单任务级别的异步执行模式
3.3 运行设计里最该补的几件事
- 任务状态机。
- 轮询频率。
- 取消策略。
- 失败后怎么重试或转人工。
3.4 一个很容易忽视的边界
OpenAI 当前 Background mode 资料明确提到:
- 它为了轮询会保留响应数据一段时间
这意味着后台任务模式不仅是运行问题,也会影响数据治理设计。
3.5 background job 最好有明确状态机
很多团队做后台任务时,只会记录:
- pending
- done
- failed
但对真实 AI 长任务来说,这通常不够。
更稳的状态机往往至少包括:
- queued
- running
- waiting_for_tool
- waiting_for_review
- cancelled
- failed_retryable
- failed_terminal
- completed
这样一来,值班、回放和人工接管时才能真正回答:
- 它现在卡在哪一段
- 是该重试、该恢复,还是该转人工
3.6 长任务最好显式设计 checkpoint / resume point
很多 background 任务最大的坑不是:
- 一次失败了
而是:
- 失败后只能从头再跑
如果任务里包含:
- 长推理
- 多步工具调用
- 审批等待
- 多轮状态恢复
那么更稳的做法通常是为关键节点打 checkpoint,至少明确:
- 哪一步之前的结果可复用
- 哪一步之后必须重新算
- 哪些外部副作用不能重放
否则重试策略很容易把系统从“可恢复”变成“重复执行事故”。
3.7 Background mode 最好和 webhook、stream cursor、ZDR 边界一起设计
OpenAI 当前 Background mode 与 Webhooks 文档合起来看,有三个非常容易被忽略的运行点:
background=true适合把长任务从同步连接里拿开,但官方文档明确说明,它为了轮询会暂存响应数据,大约10分钟,因此不兼容ZDR保证。- 如果同时启用
background=true和stream=true,你可以拿到流式事件并记录sequence_number,也就是更适合恢复的 stream cursor。 - 如果不想全靠轮询,官方
Webhooks文档已经给出response.completed这一类事件入口,更适合做后台完成通知。
因此更稳的 background lane 通常不会只做“丢个任务然后轮询”,而是会同时补三层:
- 控制面:任务状态机、取消接口、checkpoint、人工接管入口
- 事件面:webhook 订阅、签名校验、重复投递去重、事件重放窗口
- 客户端面:轮询退避、stream cursor 恢复、任务详情页
特别要注意的一点是,OpenAI 当前 webhook 文档明确说明:
- webhook 端点要尽快回
2xx - 如果不成功会重试,最长可持续到
72小时的指数退避交付
这意味着 webhook handler 最好只做:
- 验签
- 记录事件
- 投递到你自己的 worker 队列
而不要在入口上直接做重计算或复杂数据库事务。
4. Batch 真正解决的是“批量异步且更便宜”
OpenAI 当前 Batch 明确说明:
- 适合异步请求组
- 有更高的独立限额池
- 成本更低
- 有明确的 24 小时交付心智
4.1 最适合放进 Batch 的任务
- 批量标注
- 离线评测
- 数据 enrich
- 夜间批量摘要
- 大量历史文档抽取
4.2 不适合放进 Batch 的任务
- 用户在等
- 结果要秒级返回
- 一次失败就需要即时人工接管
4.3 为什么它和 Background mode 不能混着想
二者都不是同步请求,但差别很大:
- Background mode 更像“单个长任务异步跑”
- Batch 更像“一大批请求成组离线跑”
4.4 Batch lane 最值得先单独隔离哪些流量
更适合优先放进 batch lane 的通常是:
- 离线评测
- 历史数据补算
- 规则抽取回填
- 全量归档摘要
- 夜间大规模 enrich
这类流量如果混进主在线 lane,最常见的后果是:
- 抢限额
- 抬高成本曲线
- 干扰核心用户时延
所以 batch 的价值往往不只是更便宜,而是:
- 帮你把“本就不该在线跑的事”彻底从在线面拿开
4.5 Batch 不是“大请求列表”,它有自己的输入契约和失败语义
OpenAI 当前 Batch 官方文档已经把几个很关键的输入约束写得很清楚:
- 输入文件是
.jsonl - 每一行都要带唯一的
custom_id - 每个输入文件只能对应单一模型
- 当前 completion window 只有
24h - 单个 batch 最多
50,000个请求,输入文件最大200MB
这些限制的工程含义非常直接:
custom_id不是装饰字段,它是你回收结果、定位失败和做增量重放的主键- 单模型输入意味着“按模型拆 batch”通常应该在作业装配阶段就完成
24hcompletion window 说明 batch 更像“当天回收的离线作业”,不该被拿来承诺细粒度实时 SLA
更重要的是,Batch 有自己独立的失败语义。
OpenAI 当前文档明确说明:
- Batch 使用和同步请求不同的独立 rate limit pool
- 过期 batch 会进入
expired - 未完成的请求会被取消
- 已完成部分仍然会产出 output file,并按已完成请求计费
- 过期或错误请求会写进 error file
所以 batch lane 最值得先落下来的不是“怎么发请求”,而是这几个回收动作:
- 用
custom_id回填原始任务表。 - 对 output file 和 error file 做双文件回收。
- 只对失败或过期子集做增量重投,而不是整批重跑。
- 把 batch 结果与同步 / background lane 的产物对象统一到同一个 artifact schema。
5. Flex processing 适合放在哪一层
OpenAI Flex processing 当前明确说明:
- 它更便宜
- 但更慢
- 也可能出现资源不可用
所以它更适合:
- 非生产主路径
- 非严格 SLA
- 成本敏感的异步任务
5.1 更适合的场景
- 离线评测
- 数据改写
- 大规模清洗
- 回放补算
5.2 不适合的场景
- 用户正等待页面返回
- 线上关键业务
- 严格交付时限任务
5.3 Flex 最适合做“成本兜底 lane”,而不是默认主路径
很多团队第一次看到更便宜,会想把更多任务往 flex 放。
但更稳的用法通常是:
- 把 flex 当成成本敏感型 lane
- 而不是主生产默认 lane
更典型的承载对象是:
- 回放补算
- 离线指标重算
- 大量低优先级改写
- 非紧急批量评测
也就是说,flex 解决的不是“所有任务都更省”,而是:
- 某些可等待任务有没有更便宜的承载面
5.4 Flex 最好提前写好 fallback ladder,而不是 429 了再想办法
OpenAI 当前 Flex processing 文档里,有几个很实用但经常被漏掉的运行提醒:
- Flex 可能更慢,所以官方示例直接把 SDK timeout 提到了
15分钟 - SDK 会自动重试一部分
408 - Flex 可能返回
429 Resource Unavailable - 这类资源不可用错误不会计费
这意味着 flex lane 不适合只写成一句:
- “低优先级任务走 flex”
更稳的设计通常会显式给出 fallback ladder:
- 先走
service_tier=flex - 如果命中
429 Resource Unavailable,进入指数退避重试 - 超过重试阈值后,改投 background standard lane
- 如果任务已经过了业务最晚完成时刻,直接取消或转人工
同时最好明确每个任务 bucket 的:
- 最大等待时长
- 最大重试次数
- 是否允许升级到 standard
- 是否允许升级到 priority
否则 flex 很容易从“成本兜底 lane”演变成“没人知道什么时候能结束的黑箱队列”。
6. Priority processing 适合什么
OpenAI Priority processing 当前强调:
- 更低且更稳定的延迟
- 更适合高价值、面向用户、流量相对稳定的场景
6.1 更适合的场景
- 关键在线问答
- 重要客户工作台
- 需要稳定时延的核心流程
6.2 不适合的场景
- 数据补算
- 测试洪峰
- 评测回放
- 高度突发的批量任务
6.3 Priority lane 最好有明确配额和资格门槛
如果把 priority 当成“谁都能走的快车道”,结果通常会很糟:
- 成本抬升
- 高价值任务反而失去稳定低延迟
更稳的做法通常会明确:
- 哪些产品入口能走 priority
- 哪些租户能走 priority
- 哪些 bucket 允许升级进 priority
- 进入 priority 后的观察指标和回退条件
这样它才更像:
- 保关键时延目标的生产 lane
而不是:
- 新功能一着急就乱开的加速按钮
6.4 Priority 还要注意共享限额和 ramp rate 降级
OpenAI 当前 Priority processing 文档明确提到两件很关键的事:
- Priority 和 Standard 在同一模型上共享 rate limit 账户
- 如果流量 ramp 太快,部分 priority 请求可能被降级回 standard,并在响应里体现为默认 tier
官方文档给出的当前心智是:
- 如果已经来到至少
1M TPM - 并且
15分钟内增幅超过50%
就可能触发 ramp rate 保护。
这件事对生产治理的含义很大,因为它说明:
- priority 不是独立无限资源池
- 它更适合稳定高价值流量,而不是突发型迁移流量
因此更稳的上线方式通常是:
- 用 feature flag 按小时级逐步切流,而不是一次性全量改路由
- 避免把 ETL、eval replay 这类高度突发任务放进 priority
- 单独监控
service_tier实际落点,不只看请求时传了什么参数
另外,OpenAI 官方也明确说明:
- cache discount 仍适用于 priority 请求
这意味着真正成熟的策略不是“要低延迟就不用管缓存”,而是:
- 高价值同步 lane 也要继续吃前缀缓存收益
7. Conversation state 为什么会影响编排
OpenAI Conversation state 当前把多轮状态管理拿出来单讲,本身就说明:
- 长会话
- 多轮工具
- 上下文恢复
不是简单把历史消息继续拼上去就能稳住。
7.1 更适合显式管理状态的场景
- 长对话
- 多页面连续任务
- 后台任务恢复
- 需要精确控制上下文范围的系统
7.2 更适合注意的边界
- 哪些状态放模型上下文
- 哪些状态只放运行时
- 哪些状态必须持久化
- 哪些状态只在本轮有效
7.3 状态分层最好和运行 lane 一起设计
很多团队把 conversation state 单独看成“会话问题”,但真实系统里它和 lane 设计是绑在一起的。
例如:
- 同步 lane 更适合轻状态、短上下文
- background lane 往往需要 resumable state
- batch lane 通常更适合无会话或弱会话设计
如果状态分层没设计清楚,就很容易出现:
- 在线请求带了太多历史状态
- 后台任务恢复时拿不到关键上下文
- 批处理任务错误复用了会话态
7.4 previous_response_id、conversation 和 store=false 不是一回事
OpenAI 当前 Conversation state 与 Migrate to the Responses API 文档放在一起看,最值得团队统一的一个心智是:
- “状态延续”至少有三种不同级别
第一种是:
previous_response_id
它适合把一轮接一轮的响应串起来,形成线程式上下文延续。对于很多短会话、多轮问答、单页面连续交互,这已经足够。
第二种是:
conversation
OpenAI 当前文档明确说明,Conversations API 会把 conversation 作为可长期存在的对象,能跨 session、device、job 继续使用;其中 items 可以包含 message、tool call、tool output 等。它更适合:
- 多设备续聊
- 多页面连续任务
- 后台任务恢复后继续上下文
- 需要把工具轨迹也当会话资产保留的系统
第三种是:
store=false下的手动状态管理
如果你出于数据保留或合规原因不想默认保存 response,就要自己决定:
- 传
previous_response_id还是手动回放 item - 是否需要带上
reasoning.encrypted_content - 哪些状态放你自己的数据库,哪些只在本轮使用
OpenAI 当前文档还明确指出:
- 普通 response 默认保存
30天 - 但挂到 conversation 里的 items 不受
30天 TTL 约束
所以 conversation state 设计绝不是“要不要记历史消息”这么简单,而是要一起回答:
- 这个状态是短期线程态、长期会话态,还是合规受控态
- 这个状态是否允许跨设备、跨任务、跨后台作业恢复
- 这个状态是否需要脱离 OpenAI 对象,落到你自己的 durable store
8. 更稳的任务编排心智:先分价值,再分模式
8.1 高价值 + 强时效
更适合:
- 同步
- 流式
- 必要时 priority
8.2 高价值 + 弱时效
更适合:
- background
- 明确状态机
- 可取消与恢复
8.3 低价值 + 大批量
更适合:
- batch
- flex
- 夜间补算
8.4 高频 + 前缀稳定
更适合:
- prompt caching
- 固定前缀治理
8.5 高风险 + 可等待
更适合:
- background
- 明确审批等待状态
- 可恢复 checkpoint
- 必要时人工接管
8.6 低价值 + 可中断
更适合:
- flex
- batch
- 非高优先级 lane
这类区分很关键,因为很多团队把“异步”当成一种模式,但真实系统里:
- 高价值异步
- 低价值异步
其实应该走完全不同的控制面
9. 生产里最值得先绑定的几个指标
9.1 同步链路
- p95 延迟
- 首包时间
- 工具等待时间
- 超时率
9.2 后台链路
- queued 时间
- in_progress 时间
- 取消率
- 最终失败率
9.3 批处理链路
- 批次完成率
- 单请求均摊成本
- 24 小时内交付率
9.4 缓存链路
- cached tokens
- 命中率
- 命中后时延变化
9.5 状态链路
- 会话恢复成功率
- 背景任务续跑成功率
- 人工接管率
9.6 lane 治理指标
- 各 lane 请求占比
- lane 间升级 / 回退比例
- priority lane 占用率
- flex / batch 对主生产 lane 的隔离效果
- 因 incident mode 被强制改道的比例
这些指标很重要,因为很多系统的问题不是某一条请求坏了,而是:
- 某类任务整体走错 lane 了
10. 最容易踩的坑
- 把所有任务都做成同步请求。
- 该批量的任务没批量,该后台的任务没后台。
- 以为用了缓存就不用管 prompt 组织。
- 把低优先级离线任务挤进主生产流量。
- 长任务有状态,却没有恢复和取消模型。
- 没有 checkpoint,长任务一失败就只能整条重跑。
- 没有 lane 配额,高价值和低价值任务抢同一条资源池。
- 把 flex 当默认主路径,而不是成本敏感补充 lane。
- conversation state 和执行 lane 脱节,恢复时状态拿不回来。
11. 推荐搭配阅读
- 04-成本、延迟与异步运行策略
- [05-可观测性、Tracing与Trace Grading](./05-可观测性、Tracing与Trace Grading)
- 推理加速与成本优化专题
- 可观测性与tracing专题
- 安全治理
12. 重点官方资源
以下入口在 2026-07-09 检查时可访问:
- OpenAI Prompt caching
- OpenAI Background mode
- OpenAI Batch
- OpenAI Flex processing
- OpenAI Priority processing
- OpenAI Conversation state
- OpenAI Webhooks
- OpenAI Production best practices
- OpenAI Migrate to the Responses API
- OpenAI Rate limits