ARTICLE DETAIL

资讯详情

深耕网站视觉设计与运营推广的一线实战洞察。

【Agent工程】(12)—— 限流与排队策略

【Agent工程】(12)—— 限流与排队策略 【Agent工程】12—— 限流与排队策略文章目录【Agent工程】12—— 限流与排队策略1. 并发峰值先打爆的是下游额度1.1 限流与重试、降级的边界1.2 为什么不能只靠供应商侧限流2. 三层限流对象2.1 令牌桶与滑动窗口的选型2.2 与 allowed_tools 投影一致3. 排队有界队列吸收峰值3.1 队列必须有界3.2 优先级4. 贯穿示例工单助手的配额4.1 与第 10 篇重试的叠加规则4.2 子 Agent 并行时的令牌归属4.3 配额怎么定初值5. 运行时落点网关顺序5.1 轨迹字段5.2 告警阈值建议6. 可运行示意令牌桶与有界排队6.1 令牌桶6.2 有界队列决策6.3 多键同时限流时的获取顺序7. 与验收、观测的衔接7.1 对外错误码8. 四个常见误区8.1 排查清单9. 适用边界9.1 迁移步骤9.2 与费用配额的关系10. 术语速查11. 小结与下一篇摘要第 11 篇管住了单任务上下文膨胀多任务并发时仍可能瞬时打满模型额度与工单 API。本篇在运行时网关增加限流与排队按模型、工具、租户分层配额用令牌桶或滑动窗口控速并用有界队列吸收峰值。贯穿示例继续用工单助手。适合读完 【Agent工程】11—— 上下文预算与截断策略、准备把下游额度当成共享资源调度的工程师。读完可以独立完成为模型与写工具配置 RPM 配额并实现超限时的排队或拒绝策略。1. 并发峰值先打爆的是下游额度单任务跑通后常见上线形态是多个工单同时升级、子 Agent 并行只读、重试与人工补发叠在同一分钟。没有限流时模型侧出现 RPM每分钟请求数或 TPM每分钟 token 数耗尽工具侧出现 429再叠加第 10 篇若配置不当的盲重试会形成重试风暴。现象常见根因模型调用大面积失败全局无 RPM / TPM 上限工单 API 429写工具与只读探测共用一个桶延迟抖动剧烈无队列全部同步硬撞小租户被大租户挤死只有全局配额无租户隔离工程结论限流是运行时门禁的一部分与权限、幂等、上下文预算同级。它保护的是共享下游不是「让模型少说话」的提示词技巧。前文见 【Agent工程】9—— 委托策略与失败降级、【Agent工程】10—— 失败重试与幂等边界、【Agent工程】11—— 上下文预算与截断策略。本篇补「多请求如何排队通过」。1.1 限流与重试、降级的边界机制触发点目标限流 / 排队调用前控制通过速率幂等重试调用失败后在安全前提下再试on_failure 降级重试耗尽改变任务终态限流拒绝或排队超时不应自动当成「可幂等重试的 timeout」。对写工具超限更稳妥的是入队等待、或返回可审计的rate_limited再由第 9 篇策略决定 degrade而不是立刻再打一发。1.2 为什么不能只靠供应商侧限流模型供应商与工单 SaaS 往往自带 429。只依赖对方限流的问题是错误形态不统一编排层难以映射到 degrade多租户共用一个 API Key 时供应商限流无法做公平隔离发现 429 时请求已出站成本与审计噪音已经产生自建网关限流的目标是把大多数超限挡在出站前并把决策写成轨迹。供应商 429 仍应处理但应是兜底而不是唯一手段。2. 三层限流对象先定「限谁」再定算法。层对象典型配额说明模型推理端点RPM、TPM所有 Agent 共享工具每个 tool_idRPM读写分开更稳租户 / 卡片tenant_id 或 card_id并发数、排队长度防止互相挤占工单助手建议默认{rate_limit:{model:{rpm:60,tpm:80000},tools:{get_ticket:{rpm:120},update_ticket_priority:{rpm:30},notify_oncall:{rpm:10}},tenant:{max_inflight:5,queue_max:50,queue_timeout_sec:30}}}只读可以略高写操作与 notify 应收紧。notify 还与第 6 篇 pending 配合真正发送次数本来就少RPM 低是正常的。2.1 令牌桶与滑动窗口的选型算法特点适用令牌桶允许短突发长期速率受控模型调用、工具 SDK滑动窗口计数实现简单突发更硬租户级粗粒度漏桶出速更平滑对下游极敏感的写路径Agent 场景优先令牌桶计划、摘要、工具结果回写往往会「一阵一阵」来完全禁止突发会把队列拖得很长。突发上限burst建议不超过配额的 1.52 倍并在轨迹里记录borrowed_burst。2.2 与 allowed_tools 投影一致第 11 篇提到工具 schema 应按任务卡投影。限流键也应只用当前allowed_tools中的 tool_id避免为未授权工具预扣令牌。未注册或未授权的调用在权限门禁就应失败到不了限流层。3. 排队有界队列吸收峰值限流不是只有「立刻拒绝」。高峰时更常见的是拿不到令牌就入队有令牌再执行。流程请求携带priority、card_id、tool_id或kindmodel若令牌充足 → 扣减并执行若不足且队列未满 → 入队等待唤醒若队列已满或等待超过queue_timeout_sec→ 返回rate_limited/queue_timeout轨迹记录等待时长、出队原因、是否借了 burst3.1 队列必须有界无界队列会把内存与尾延迟一起拖垮。建议同时约束参数含义queue_max最大等待任务数queue_timeout_sec单请求最长等待max_inflight同租户同时执行数超时出队时客户端看到的应是明确错误码而不是一直挂起直到上游 HTTP 超时。队列实现上可以用内存队列单实例或 Redis List / Stream多实例。无论哪种出队执行与令牌扣减要在同一临界区逻辑里完成避免「出队了但令牌已被别人拿走」造成瞬时超配。3.2 优先级建议优先级从高到低P0人工已 approve 的 pending 执行补发通知P1主任务写操作改 priorityP2主任务只读与模型推理P3后台探测、评测流量P0 可以插队但必须有插队比例上限例如每分钟 P0 不超过总执行的 20%避免运维手动补发把在线任务全部堵住。优先级与配额示例见下一节图示。4. 贯穿示例工单助手的配额调用建议配额理由模型推理60 RPM全局共享含子 Agentget_ticket120 RPM只读允许更高update_ticket_priority30 RPM写操作配合幂等notify_oncall10 RPM低频防连发场景早高峰 20 张工单同时升级。只读 get_ticket 大部分在令牌桶内直接通过模型推理进入队列按租户 max_inflight5 平滑放出update_priority 若撞到 30 RPM排队而不是立刻 429 打到工单系统notify 走 pending真正发送很少若审批员集中点击靠 P0 notify RPM 双约束4.1 与第 10 篇重试的叠加规则情况处理网关已限流不视为可自动重试的 timeout下游返回 429可按 retry_policyon_429 有限退避且仍受令牌桶约束幂等写排队后执行两次提议靠 request_id dedupe不靠「多排队几次」限流层出站的每一次真实 SDK 调用都要走幂等键排队成功不等于可以绕过 dedupe。4.2 子 Agent 并行时的令牌归属第 9 篇 parallel 只读子任务会放大 get_ticket 类调用。令牌归属建议子调用计入主租户配额避免子 Agent「另开一桶」绕过并行度受max_inflight二次约束评测流量使用独立 tenant_id避免冲掉生产配额并行只读看似「便宜」在限流视角下同样消耗 get_ticket 与模型令牌。能串行合并的探测不要无谓并行否则早高峰会把只读桶先打空写操作只能长时间排队。4.3 配额怎么定初值没有历史数据时可用下面的粗算取供应商文档中的 RPM/TPM留 20%30% 余量给控制面与人工操作写工具配额取只读的 1/31/5notify 类按「每小时真实发送上限」反推 RPM宁低勿高上线第一周以 waited_ms 与 reject 率为准调参而不是凭感觉把 queue_max 调到极大若 waited_ms 长期接近超时说明配额低于真实需求应提额或削峰而不是把超时一味加长到用户不可接受。5. 运行时落点网关顺序与既有门禁串联时推荐顺序权限 / scope → 幂等 dedupe写 → 限流取令牌或入队 → 上下文预算裁剪 → 调用模型或 SDK → 写轨迹含 rate_limit 字段限流放在上下文裁剪之前拿不到执行权就不必先做昂贵的摘要与组装。若业务上「入队前先估算 token」有成本需求可以把 TPM 检查与裁剪绑定但 RPM 检查仍应尽早。5.1 轨迹字段字段含义rate_limit_key命中的限额键waited_ms排队等待毫秒limited是否曾因无令牌等待或拒绝decisionallow / queue / rejectqueue_depth入队时队列深度成本与稳定性看板应同时看拒绝率、平均 waited_ms、P99 队列深度、下游 429 次数。下游 429 下降而 waited_ms 上升通常说明限流在生效。5.2 告警阈值建议指标告警示意reject 率持续 5%配额不足或流量异常P99 waited_ms 队列超时的 50%正在逼近超时拒绝下游 429 未归零存在旁路或配额仍高于下游单租户占满全局 RPM隔离策略失效告警应指向「调配额 / 查旁路 / 削峰」而不是只重启 Worker。6. 可运行示意令牌桶与有界排队6.1 令牌桶from__future__importannotationsimporttimefromdataclassesimportdataclassdataclassclassTokenBucket:rate_per_sec:floatcapacity:floattokens:float0.0updated_at:float0.0def__post_init__(self)-None:self.tokensself.capacity self.updated_attime.monotonic()def_refill(self)-None:nowtime.monotonic()elapsednow-self.updated_at self.tokensmin(self.capacity,self.tokenselapsed*self.rate_per_sec)self.updated_atnowdeftry_acquire(self,n:float1.0)-bool:self._refill()ifself.tokensn:self.tokens-nreturnTruereturnFalseif__name____main__:# 30 RPM ≈ 0.5 token/sburst10bucketTokenBucket(rate_per_sec0.5,capacity10)oksum(1for_inrange(12)ifbucket.try_acquire())assertok10assertbucket.try_acquire()isFalseprint(acquired,ok,remaining,round(bucket.tokens,2))6.2 有界队列决策from__future__importannotationsfromdataclassesimportdataclassfromtypingimportAnydataclassclassQueuePolicy:queue_max:int50queue_timeout_sec:float30.0defadmit(bucket:Any,queue_depth:int,waited_sec:float,policy:QueuePolicy,)-str:返回 allow / queue / reject。ifbucket.try_acquire():returnallowifqueue_depthpolicy.queue_max:returnrejectifwaited_secpolicy.queue_timeout_sec:returnrejectreturnqueueif__name____main__:classStub:deftry_acquire(self)-bool:returnFalsepolicyQueuePolicy(queue_max2,queue_timeout_sec5)assertadmit(Stub(),queue_depth1,waited_sec0,policypolicy)queueassertadmit(Stub(),queue_depth2,waited_sec0,policypolicy)rejectassertadmit(Stub(),queue_depth0,waited_sec6,policypolicy)rejectprint(queue policy ok)生产环境令牌桶应放在 Redis 等共享存储上多实例网关才能看到同一配额示例仅说明决策分支。6.3 多键同时限流时的获取顺序一次写工具调用可能同时需要租户 inflight、工具 RPM、模型 RPM若该步还要再推理。建议按从严到松或固定全局顺序获取避免死锁例如tenant max_inflighttool_id RPMmodel RPM任一步失败则释放已获取的令牌或根本不扣减采用「预检 执行时再扣」两阶段。轨迹里写清卡在哪一层排障才快。7. 与验收、观测的衔接层级本篇补充运行时配置rate_limit 与 context_budget 并列网关取令牌 / 入队 / 拒绝第 9 篇rate_limited 可映射到 degrade第 10 篇429 重试不得绕过令牌桶第 3 篇验收可断言 notify 发送速率上限建议评测用例用例 ID注入期望L011 分钟内 100 次模型调用通过数 ≈ RPM其余排队或拒绝L02写工具突发超过配额不出现下游 429 风暴L03队列打满返回 reject轨迹 decisionrejectL04P0 pending 与 P3 探测同时到达P0 先执行且受插队比例约束L05租户 A 打满租户 B 仍有自己的 max_inflight7.1 对外错误码给编排层的错误应稳定码含义RATE_LIMITED无令牌且不愿/不能再等QUEUE_TIMEOUT等待超过上限QUEUE_FULL队列已满不要把它们都映射成笼统的timeout否则第 10 篇重试策略会误判。对同步 HTTP 入口拒绝时应返回 429 或 503并带Retry-After秒数可取令牌预计恢复时间。对内部 Agent Worker则返回结构化错误对象供编排状态机消费。8. 四个常见误区误区典型表现更稳妥的做法只限模型不限工具SDK 仍打爆工单 API按 tool_id 分层429 后无限重试与限流目标相反有限退避 仍扣令牌全局一锅粥配额大租户挤死小租户tenant 隔离队列无限变长内存与尾延迟爆炸queue_max timeout还有一种隐蔽问题限流只做在边缘网关Agent Worker 内的子调用直连 SDK形成旁路。所有模型与工具出口应强制经同一限流组件开发环境可以用更高配额但不要提供「无门禁客户端」。8.1 排查清单是否同时配置了模型 RPM/TPM 与工具 RPM多实例是否共享同一令牌存储429 重试是否仍走限流队列是否有长度与等待超时轨迹是否含 waited_ms / decision评测流量是否与生产租户隔离9. 适用边界本篇方法适合多任务并发的生产 Agent下游有明确 RPM/TPM 或 API 配额需要把峰值变成可控延迟而不是瞬时失败风暴本篇不覆盖单请求超时取消与协作中断——下一运行时篇展开跨地域多活的全局配额一致性——需专门的配额服务计费套餐谈判与供应商侧提额——商务流程不是网关逻辑若长期排队深度很高说明配额低于真实业务需求应提额或削峰减少并行子 Agent、合并只读而不是只把 queue_max 调到极大。9.1 迁移步骤盘点模型与各 tool_id 的供应商限额配置分层 rate_limit写工具默认更严网关接入令牌桶 有界队列轨迹与看板增加 waited_ms、reject 率、下游 429压测 L01L05再与第 9、10 篇策略联调9.2 与费用配额的关系限流管的是速率与并发费用配额管的是金额或日调用总量。两者常同时存在日额度耗尽应拒绝新任务分钟级 RPM 触顶应排队。不要用费用日配额冒充限流否则高峰一分钟就能把整天预算打光。10. 术语速查术语含义RPM每分钟请求数上限TPM每分钟 token 数上限令牌桶按速率补充令牌、允许有限突发的限流算法有界队列有最大长度与等待超时的等待队列max_inflight同租户同时执行中的请求数上限rate_limited因限流未能执行的可审计结果11. 小结与下一篇运行时单元在上下文预算之后补上共享下游的调度分层限流模型、工具、租户分别设配额有界排队吸收峰值超时与满队列明确拒绝优先级可控pending 补发可插队但有比例上限与重试分账限流错误不要误走盲重试下一篇继续运行时超时、取消与协作中断避免任务卡在半空中挂死。系列导航上一篇【Agent工程】11—— 上下文预算与截断策略下一篇【Agent工程】13—— 超时取消与协作中断撰写中
返回列表