智能体服务流量增长前要补哪些防线
智能体服务的压力不只来自并发请求。一次任务可能反复调用模型、搜索、数据库和外部工具;其中某一步变慢或失败,模型还可能尝试换一种说法再做一遍。流量上涨前,最先要补的不是更激进的并发参数,而是让每个任务有可预期的资源边界和退出路径。
这些限制应当按租户、用户和单次任务分层设置。只给单个任务设上限,攻击者仍能并发创建大量任务;只做全局限流,又会让少量异常请求拖累正常用户。具体阈值取决于模型、业务价值和供应商配额,不能照抄某个固定数字。上线前应通过压测和真实流量样本校准,并给运营人员留出临时收紧或放宽的受控入口。
任务开始前就要记账
一个任务至少应带有请求 ID、归属主体、开始时间、最大步骤数、截止时间和费用或 token 预算。预算判断要发生在调用前,也要在拿到实际用量后更新;仅靠模型返回后的统计,无法阻止一次超过限额的请求。
下面的例子只演示状态管理。它不负责估算真实费用,也不能替代网关限流或账户配额。
from dataclasses import dataclass, field from time import monotonic class BudgetExceeded(RuntimeError): pass @dataclass class TaskBudget: max_steps: int max_tokens: int deadline: float steps: int = 0 tokens: int = 0 recent_calls: list[str] = field(default_factory=list) def reserve(self, estimated_tokens: int) -> None: if monotonic() >= self.deadline: raise BudgetExceeded("任务已超时") if self.steps + 1 > self.max_steps: raise BudgetExceeded("执行步数已达上限") if self.tokens + estimated_tokens > self.max_tokens: raise BudgetExceeded("预算不足,未发起模型调用") self.steps += 1 def settle(self, actual_tokens: int) -> None: self.tokens += actual_tokens if self.tokens > self.max_tokens: raise BudgetExceeded("实际消耗超出预算")真实系统还要处理估算和实际用量不一致、供应商迟到的计费信息、任务恢复后的重复扣减。账本记录必须具有幂等键,不能只放在进程内存里。
不要只用“连续三次相同调用”判断循环
连续相同的工具调用很可疑,却不是完整的循环定义。带分页的读取、暂时性网络错误后的重试,都可能出现相同形状的调用;反过来,模型也可能不断换参数绕开简单规则。更可靠的做法是同时观察步骤数、相同失败原因、状态是否有进展、同类工具的调用频率和任务总时长。
当规则触发时,先停止继续消费资源,保存当前轨迹和去敏后的错误信息,再把任务标为需要人工查看或可重试。不要把完整提示词、用户附件或工具凭证直接塞进告警日志。对有副作用的工具,重试还应使用幂等键或确认步骤,避免一次失败判断导致重复付款、重复发信或重复写入。
外部工具的退避必须受全局调度约束
收到 429 或暂时性 5xx 后立即重试,通常只会加重对方的压力。调用方应限制每次请求的尝试次数,使用退避与随机抖动,并尊重服务端提供的等待提示。与此同时,按供应商、租户和工具类型做并发与速率控制,避免一批任务同时醒来后再次拥塞。
并非所有失败都适合重试。参数错误、权限拒绝和内容校验失败应该直接返回给上层;对写操作,只有确认幂等时才考虑自动重试。工具的超时要小于任务的整体截止时间,否则最后一步会占满工作线程,却已经没有时间交付结果。
多模态处理要和请求入口隔开
图片解码、视频抽帧和大文件解析可能占用大量 CPU、内存或磁盘。把这类工作直接放在异步事件循环里,会让其他请求的心跳和流式输出一起变慢。更合理的安排是:入口先校验文件大小、类型和配额,把可接受的任务投递到有容量上限的工作队列;CPU 密集型处理放到独立 worker;结果通过任务状态或事件流回传。
队列本身也要有背压策略。积压超过阈值时,是拒绝新任务、延后低优先级任务还是只保留某些租户,都应是公开的产品决策,而不是让 worker 无限堆积后被操作系统杀掉。
指标要帮助定位具体问题
建议至少按模型和工具维度观察:任务完成率、取消率、步骤分布、预算拒绝数、工具错误类别、队列等待时间,以及端到端耗时。P99 延迟有用,但单独看它无法说明问题在模型、文件处理还是外部 API。仪表盘还应区分版本与租户,否则一次新提示词发布造成的变化很难被发现。
防线的目的不是让智能体永远不出错,而是让出错的代价有限、路径可追踪、恢复方式明确。先把预算、取消、限流和队列治理做好,再扩大流量,系统才有足够的回旋余地。