Today AI 这一层要做的事很窄:决定调谁、按什么顺序调、传什么参数、拿到结果后走哪个分支。凭据从哪里签发、当前配额还剩多少、这次调用会不会被限流,这些通常都不该由编排流程来判断。把凭据读取和配额比较写进节点里,结果是同一次失败既可能是流程逻辑写错,也可能是云服务侧已经拒绝,排查时要先剥离掉一半无关信息,成本很高。
编排层保留步骤顺序、入参出参、超时与重试上限、结果分支;凭据签发和配额核算交回云服务。适用场景是多步 Agent 流程串接外部 API 或 SaaS。操作上,把 token 拼接和 quota 比较从节点里删掉,改成只读云服务返回的错误码。验证方式是配额打满时流程只做分支、不做猜测。风险边界在于:如果云服务不返回可区分的错误码,需要在接入层做一次统一映射,而不是回到每个业务节点里各自判断。
列出编排层当前实际承担的动作
先别改代码,先把现状盘清楚。做法是把流程定义、节点脚本、公共工具函数扫一遍,按动作维度列清单,而不是按文件维度。判断标准只有三个问题:谁执行、谁校验、失败后谁重试。
# 在流程仓库根目录粗筛,关键词按自己项目替换
grep -rn -E "api_key|token|Authorization|quota|rate_limit|remain" \
`--include`="*.py" `--include`="*.yaml" `--include`="*.json" .| 动作 | 由谁执行 | 由谁校验 | 失败后由谁重试 |
|---|---|---|---|
| 拼接 Authorization 头 | 编排节点 | 编排节点自查 | 编排节点 |
| 读取配额余量并比较阈值 | 编排节点 | 编排节点 | 编排节点 |
| 决定调用顺序与并发度 | 编排节点 | 编排节点 | 编排节点 |
| 签发或刷新短期凭据 | 应当交回云服务 | 云服务 | 云服务 |
| 判断本次是否超配额 | 应当交回云服务 | 云服务 | 云服务 |
清单里落在“编排节点自查”的格子越多,后面定位问题就越难。可以先只标注、不修改,标注本身就能看出哪些格子是重复劳动。
把鉴权和配额判断从流程里搬走
搬走之后,流程节点里应当只剩四类内容:步骤顺序与依赖关系、每个步骤的入参出参约定、超时与重试次数上限、以及基于返回结果的分支条件。凭据不再由流程拼接,配额不再由流程比较,流程也不再去猜“现在还剩多少”。
# 通用接入骨架,字段名按自己云服务侧的实际返回替换
step: call_upstream
request:
endpoint: "${SERVICE_ENDPOINT}"
credential: "from_service_side" # 由云服务或凭据代理注入,流程不持有
payload: "${step_input}"
branch:
on_success: next_step
on_auth_error: fail_fast
on_quota_error: defer_and_notify
on_timeout: retry_once_if_idempotent流程如何得知配额被拒,取决于云服务返回什么。常见做法是返回可区分的状态码加错误标识,例如 429 配上类似 quota_exceeded 的错误码,以及一个建议等待时长字段。编排层只读这些字段,不参与计算。如果云服务只给一个笼统的失败,那就需要接入层做一次映射,把“配额类失败”和“鉴权类失败”分开,映射逻辑集中在一处,不要散落到业务节点。
定义流程拿到失败结果后的处理方式
三类结果要分开处理,核心区别是能不能重试、要不要换凭据、要不要等待。流程不要根据耗时或报错文本自行推断类别,只按约定字段判断。
| 结果类别 | 识别依据 | 流程动作 | 对外提示 |
|---|---|---|---|
| 鉴权失败 | 状态码与错误码指向凭据无效或过期 | 不重试,交给凭据侧刷新后重新发起一次新任务 | 提示“凭据需要重新获取”,带上 trace_id |
| 配额耗尽 | 返回明确的配额类错误码 | 不循环重试,按建议等待时长延后或转为排队 | 提示“当前额度已用尽,稍后再试”,带上建议等待时长 |
| 超时 | 连接或读取超时、网关超时类状态 | 按步骤是否幂等决定重试一次还是直接失败 | 提示“上游响应超时”,带上下游步骤名 |
三类之外的返回,建议统一归到“未知失败”,只记录、不自动重试,保留原始响应体供人判断。这样做的代价是部分可恢复错误没有自动恢复,换来的是不会因为错误分类猜错而放大问题。
用一次配额打满的场景验证分工
验证不需要真实把额度用光。可以先在云服务侧把该凭据的配额上限调到一个刚好够当前用量的值,或使用沙箱配额,然后触发一次目标调用。
流程应当表现出的行为是:不进入重试循环,不修改任何凭据,不抛出看起来像代码异常的堆栈,而是走延后或失败分支,并把上游返回的错误标识原样带出去。可以通过以下命令侧看调用情况,替换成自己的入口脚本。
# 触发一次并直接看编排层输出
your_flow_runner run `--step` call_upstream `--once`
# 观察日志中是否出现配额类字段
rg "upstream_status|upstream_error_code|retry_after|action_taken" ./logs/latest.log日志里建议至少能看到这些字段:trace_id、step、attempt、upstream_status、upstream_error_code、retry_after、action_taken。若日志里只看到“调用失败”这一句,说明错误映射还没做,职责边界仍然是模糊的。验证完成后把配额上限改回去,确认正常调用路径行为没有变化,这一步常常被跳过。