---
title: 成本、容量与可靠性
url: https://doc.liz6.com/ai/04-evaluation-and-production/02-cost-performance-and-reliability
locale: zh
area: ai
tags:
- 大模型与 Agent
- 评估与生产
date: 2026-06-30
modified: 2026-09-10
description: 质量达到要求之后，服务还必须在费用、配额和时间约束内运行。本文以完整任务为计量单位，从成本账走到配额瓶颈与平均在途量，再把重试放进总期限。模型内部 KV 与算子开销见推理资源篇，这里关注服务调度与失败处理。
---

# 成本、容量与可靠性

质量达到要求之后，服务还必须在费用、配额和时间约束内运行。本文以完整任务为计量单位，从成本账走到配额瓶颈与平均在途量，再把重试放进总期限。模型内部 KV 与算子开销见推理资源篇，这里关注服务调度与失败处理。

## 成本按完整执行链计算

Token 是常见计费量，但不是唯一成本来源。模型请求可能区分普通输入、缓存写入、缓存读取和输出；工具、检索、存储、执行容器、网络及自托管算力也会产生费用。不同供应商与产品有不同口径，不能把“输出固定比输入贵五倍”作为通用规则。

**任务成本 = 所有模型请求费用之和 + 工具与运行费用 + 相关存储/检索等费用**

假设某服务的教学价格为输入每百万 token 2 个货币单位、输出 10 个单位。一次请求输入 20,000、输出 2000，模型费用是 0.04+0.02=0.06；再加工具费用 0.01，总量为 0.07。一个任务需要三次同样规模的调用，才是 0.21；额外失败尝试也要按实际产生的费用加入。**这些数字仅用于演算，不是当前产品报价。**

| 优化手段 | 可能节省什么 | 需要一同观察 |
|---|---|---|
| 删除无用输入 | 输入费用和部分处理时间 | 是否丢失必要证据 |
| 前缀缓存 | 重复前缀的计算或费用 | 命中条件、写入开销和有效期 |
| 调整模型与 effort | 单次计算投入 | 完整任务成功率与重试 |
| 控制最终篇幅 | 可见输出量 | 是否覆盖任务要求 |
| 非实时批处理 | 取决于服务的批量价格 | 完成时限、逐项失败与重复提交 |

缓存收益可以用不含真实定价的模型核算：前缀长度 L，普通输入单价 u，写入单价 w，读取单价 r，复用 N 次且其余请求都命中，比较 `N×L×u` 与 `L×w+(N−1)×L×r`。TTL 内调用太少或前缀经常变化时，写入溢价可能收不回来；机制与条件见 [上下文工程](/ai/02-context-and-interfaces/02-context-engineering.md)。

换便宜模型也要算整个任务。若单次费用减半，但失败导致三次额外尝试，未必更省。报告每项成功分摊的总成本，并记录未成功任务耗费，而不是只比较一次请求的报价。

## 拆解延迟与吞吐

端到端时间包含排队、网络、输入处理、推理/生成、工具和应用验证等阶段。串行依赖相加，并行分支按关键路径计算；“延迟主要由输出长度决定”只适合部分工作负载。

| 时间指标 | 从哪里量到哪里 | 用来发现什么 |
|---|---|---|
| 排队时间 | 进入服务到开始处理 | 准入或后端容量不足 |
| 首个可见文本时间 | 请求开始到有用文本出现 | 输入、思考、网络与展示等待 |
| 生成阶段时间 | 生成开始到本次输出完成 | 解码、输出规模或停顿 |
| 工具阶段时间 | 实际分派到结果确认 | 外部服务和串行依赖 |
| 任务完成时间 | 用户提交到验收完成 | 用户实际体验 |

流式让结果逐步交付，通常改善等待感知，并可配合连接保活；它不会自动提高模型生成速度、解除输出上限或保证所有代理与网络都不超时。需要分别设置连接、读取空闲、单次调用和任务总期限；接收部分文本后断流，也不能把半截结构化结果标为完成。

缓存、缩短输入、批量、并行和更少探索都可能改善某些阶段，但要根据 trace 找瓶颈。若工具查询花 20 秒，削掉 100 个输出 token 对整体影响可能很小；若排队占大头，单独降低前端超时只会制造更多失败重试。

## 配额、准入与有界排队

<svg viewBox="0 0 760 346.60760498046875" xmlns="http://www.w3.org/2000/svg" role="img" aria-label="排队和并发是资源控制，不能靠无限重试增加容量" style="max-width:100%;height:auto" font-family="Source Han Sans CN,Microsoft YaHei,sans-serif">
<defs><marker id="service-admission-arrow" markerWidth="8" markerHeight="8" refX="7" refY="4" orient="auto"><path d="M0,0 L8,4 L0,8 Z" fill="#64748b"></path></marker></defs>
<rect width="760" height="346.60760498046875" rx="12" fill="#f8fafc"></rect>


<g transform="translate(0 0)"><rect x="30" y="95" width="200" height="90" rx="8" fill="#e0e7ff" stroke="#c7d2fe"></rect><text x="130.0" y="137.0" font-size="15" fill="#1e293b" text-anchor="middle" font-weight="600">到达请求</text><text x="130.0" y="159.0" font-size="12" fill="#475569" text-anchor="middle" font-weight="400">实时任务与离线任务</text><line x1="230" y1="140" x2="275" y2="140" stroke="#64748b" stroke-width="1.8" marker-end="url(#service-admission-arrow)"></line><rect x="280" y="95" width="200" height="90" rx="8" fill="#e0e7ff" stroke="#c7d2fe"></rect><text x="380.0" y="137.0" font-size="15" fill="#1e293b" text-anchor="middle" font-weight="600">有界队列与准入</text><text x="380.0" y="159.0" font-size="12" fill="#475569" text-anchor="middle" font-weight="400">期限、配额、并发槽位</text><line x1="480" y1="140" x2="525" y2="140" stroke="#64748b" stroke-width="1.8" marker-end="url(#service-admission-arrow)"></line><rect x="530" y="95" width="200" height="90" rx="8" fill="#e0e7ff" stroke="#c7d2fe"></rect><text x="630.0" y="137.0" font-size="15" fill="#1e293b" text-anchor="middle" font-weight="600">模型与工具服务</text><text x="630.0" y="159.0" font-size="12" fill="#475569" text-anchor="middle" font-weight="400">有限吞吐与资源</text><rect x="280" y="240" width="200" height="55" rx="8" fill="#fef3c7" stroke="#c7d2fe"></rect><text x="380.0" y="264.5" font-size="15" fill="#1e293b" text-anchor="middle" font-weight="600">延期、降级或拒绝</text><text x="380.0" y="286.5" font-size="12" fill="#475569" text-anchor="middle" font-weight="400">明确返回处理状态</text><line x1="380" y1="190" x2="380" y2="235" stroke="#64748b" stroke-width="1.8" marker-end="url(#service-admission-arrow)"></line><text x="28" y="321" font-size="13" fill="#475569" text-anchor="start" font-weight="400">当等待会超过任务期限，继续排队只会把过载变成更长尾延迟。</text></g><text x="24" y="29" font-size="19" fill="#0f172a" text-anchor="start" font-weight="700"><tspan x="24" dy="0">排队和并发是资源控制，不能靠无限重试增加容量</tspan></text>
</svg>

配额可能同时限制请求数、输入 token、输出 token和加速增长速度；具体维度按服务文档与账户配置确认。[Claude Rate limits](https://platform.claude.com/docs/en/api/rate-limits) 只限制请求并发，不能保证 token 配额不超；只限制平均 RPM，也不能消除瞬间突发。

假设允许 100 请求/分钟、200,000 输入 token/分钟、50,000 输出 token/分钟，每个请求平均输入 10,000、输出 1000，则三个维度给出的平均上限分别为 100、20、50 请求/分钟，最紧约束是 **20 请求/分钟**。

若平均服务时间为 30 秒，在稳定、无积压的简化条件下，20 请求/分钟对应平均在途请求数约 10。这个 Little 定律算例是平均容量账，不是生产并发设为 10 就一定安全：长度波动、尾延迟、突发和外部共享配额还需余量与动态控制。

```python
from fractions import Fraction

rpm = 100
input_tpm, output_tpm = 200_000, 50_000
input_per_request, output_per_request = 10_000, 1_000
rate = min(Fraction(rpm), Fraction(input_tpm, input_per_request),
           Fraction(output_tpm, output_per_request))
mean_inflight = rate / 60 * 30
assert rate == 20
assert mean_inflight == 10
assert 3 * 3 == 9  # 两层各最多 3 次尝试的最坏放大
print("平均请求/分钟:", rate, "平均在途:", mean_inflight)
```

队列需要长度与等待期限，超出后按业务延期、降级或拒绝。在线交互与可延后的离线任务可分开调度，避免长批处理耗尽交互资源。过载时持续接收并让所有请求更慢，常比早期拒绝更难恢复；容量管理应依据真实资源而非单一请求计数。[Google SRE：Handling Overload](https://sre.google/sre-book/handling-overload/)

## 重试需要期限与幂等边界

### 错误类别比数字口诀更有用

| 类别 | 处理原则 |
|---|---|
| 无效参数或不支持能力 | 修正请求，原样重试通常无意义 |
| 身份或权限问题 | 按认证流程恢复或报告；不扩大权限绕过 |
| 限流或短时过载 | 尊重服务提示，有限退避并减少施压 |
| 连接、超时与部分服务错误 | 先判断操作是否可安全重放 |
| 写动作结果未知 | 查询状态或使用已支持的幂等协议 |
| 配额耗尽、长期不可用 | 等待条件变化或使用明确降级，不无限重试 |

不能把它压成“只有 429/5xx 才能重试”或“所有 5xx 都应重试”。408、409 等是否适合重试取决于协议和操作；供应商错误与 SDK 默认策略也应分别核查。[Claude Errors](https://platform.claude.com/docs/en/api/errors)

### 将退避放进同一个任务期限

常见退避上界为 `min(cap, base×2^attempt)`，在区间内引入 jitter，避免多个客户端同时醒来。服务返回 Retry-After 时按它的语义安排，并受任务剩余期限约束；如果等完后已经没有完成一次尝试的时间，就不应继续启动。

例如任务剩余 8 秒，服务要求至少等待 6 秒，而下次操作需要预留 5 秒，这轮重试不符合预算。把“每次请求 timeout=10 秒”设置好，也不代表任务只会等 10 秒；重试、退避、排队和工具阶段都会占用总时长。

<svg viewBox="0 0 760 346.60760498046875" xmlns="http://www.w3.org/2000/svg" role="img" aria-label="嵌套重试放大：一次逻辑调用最多变成九次底层请求" style="max-width:100%;height:auto" font-family="Source Han Sans CN,Microsoft YaHei,sans-serif">
<defs><marker id="retry-amplification-arrow" markerWidth="8" markerHeight="8" refX="7" refY="4" orient="auto"><path d="M0,0 L8,4 L0,8 Z" fill="#64748b"></path></marker></defs>
<rect width="760" height="346.60760498046875" rx="12" fill="#f8fafc"></rect>


<g transform="translate(0 0)"><rect x="30" y="130" width="165" height="80" rx="8" fill="#e0e7ff" stroke="#c7d2fe"></rect><text x="112.5" y="167.0" font-size="15" fill="#1e293b" text-anchor="middle" font-weight="600">调用方重试</text><text x="112.5" y="189.0" font-size="12" fill="#475569" text-anchor="middle" font-weight="400">最多 3 次尝试</text><rect x="280" y="65" width="175" height="55" rx="8" fill="#e0e7ff" stroke="#c7d2fe"></rect><text x="367.5" y="97.5" font-size="15" fill="#1e293b" text-anchor="middle" font-weight="600">外层尝试 1</text><rect x="545" y="65" width="185" height="55" rx="8" fill="#fef3c7" stroke="#c7d2fe"></rect><text x="637.5" y="97.5" font-size="15" fill="#1e293b" text-anchor="middle" font-weight="600">SDK 最多 3 次尝试</text><line x1="195" y1="170" x2="275" y2="92" stroke="#64748b" stroke-width="1.8" marker-end="url(#retry-amplification-arrow)"></line><line x1="455" y1="92" x2="540" y2="92" stroke="#64748b" stroke-width="1.8" marker-end="url(#retry-amplification-arrow)"></line><rect x="280" y="145" width="175" height="55" rx="8" fill="#e0e7ff" stroke="#c7d2fe"></rect><text x="367.5" y="177.5" font-size="15" fill="#1e293b" text-anchor="middle" font-weight="600">外层尝试 2</text><rect x="545" y="145" width="185" height="55" rx="8" fill="#fef3c7" stroke="#c7d2fe"></rect><text x="637.5" y="177.5" font-size="15" fill="#1e293b" text-anchor="middle" font-weight="600">SDK 最多 3 次尝试</text><line x1="195" y1="170" x2="275" y2="172" stroke="#64748b" stroke-width="1.8" marker-end="url(#retry-amplification-arrow)"></line><line x1="455" y1="172" x2="540" y2="172" stroke="#64748b" stroke-width="1.8" marker-end="url(#retry-amplification-arrow)"></line><rect x="280" y="225" width="175" height="55" rx="8" fill="#e0e7ff" stroke="#c7d2fe"></rect><text x="367.5" y="257.5" font-size="15" fill="#1e293b" text-anchor="middle" font-weight="600">外层尝试 3</text><rect x="545" y="225" width="185" height="55" rx="8" fill="#fef3c7" stroke="#c7d2fe"></rect><text x="637.5" y="257.5" font-size="15" fill="#1e293b" text-anchor="middle" font-weight="600">SDK 最多 3 次尝试</text><line x1="195" y1="170" x2="275" y2="252" stroke="#64748b" stroke-width="1.8" marker-end="url(#retry-amplification-arrow)"></line><line x1="455" y1="252" x2="540" y2="252" stroke="#64748b" stroke-width="1.8" marker-end="url(#retry-amplification-arrow)"></line><text x="28" y="321" font-size="13" fill="#475569" text-anchor="start" font-weight="400">统一规定重试层次、总尝试数和任务期限；每一次重试也占用配额与成本。</text></g><text x="24" y="29" font-size="19" fill="#0f172a" text-anchor="start" font-weight="700"><tspan x="24" dy="0">嵌套重试放大：一次逻辑调用最多变成九次底层请求</tspan></text>
</svg>

调用方重试三次、SDK 内部每次又最多尝试三次，最坏就是九次底层请求。要明确由哪一层管理重试，统计真实尝试数，必要时设置整个服务的重试预算；过载时重试放大会让本已不足的容量更紧张。[Google SRE：Addressing Cascading Failures](https://sre.google/sre-book/addressing-cascading-failures/)

### 人工确认不替代幂等

用户确认过“创建一条订单”，只授权了一次业务意图，不保证网络重试不会创建两条。幂等键必须对应同一个业务操作，并由实际服务保存和核验；对同一键发送不同参数也应按服务语义处理，不能只在模型 prompt 里写“不要重复”。

请求超时不能证明服务没执行。若动作已完成但响应未到，应先查询或重放已有结果，取消等待也不自动撤销动作。这条边界在 [Agent 循环](/ai/03-agent-systems/01-agent-loop.md) 中用完整时序展开。

### 先耗尽的是请求数还是 token？

先把不同单位的配额都换成请求速率，再取最小值，才能找到当前瓶颈。Little 定律中的 L 是平均在途请求数；它不是推荐的硬并发上限，也不能单靠这条公式保证排队尾延迟。

$$
r\leq\min\left(R,\frac{I_{\max}}{\bar I},\frac{O_{\max}}{\bar O}\right),\qquad L=\frac r{60}\,\bar W
$$

**先耗尽的是请求数还是 token？**

可持续吞吐不超过所有配额换算后的最小值；平均在途数还取决于每请求占用时间。


## 降级、批量与运行验证

### 降级仍需满足任务的最低要求

切换模型前核验上下文长度、结构化输出、工具格式、输入模态与权限边界。不能把只支持文本的后备模型静默接到图像任务，也不能因为备用服务支持范围不同，就删除关键约束后伪装成同等结果。

可选择返回已有可信部分、延后离线完成、使用兼容后备或明确报告暂不可用。缓存旧结果时标注适用时间与版本，并检查用户访问范围。恢复服务后，应防止积压任务同时重放形成新一轮突发。

### 批量提交按条目管理生命周期

非实时任务可以使用批量接口，但价格折扣、完成时限、大小限制和缓存交互是服务特定规则。批次结束不等于所有条目成功；按每项 ID 区分成功、失败、过期与取消，并只重试应重试的条目。[Claude Batch processing](https://platform.claude.com/docs/en/build-with-claude/batch-processing)

批量/并行结果可能乱序，保存请求映射，按 ID 对应输出。重新提交失败项时保留原任务与新尝试的关联，避免重复写入已成功结果；模型批处理本身也不保证下游业务操作恰好执行一次。

### 验证目标是可控的完整服务

建立包含短请求、长输入、长工具等待、限流、断流、结果未知和后备切换的运行样例。记录成功率、p50/p95 延迟、队列长度、实际尝试数、配额使用和每项成功成本。

### 每层只重试两次，会放大多少？

“重试两次”容易被误读成总共两次调用，这里统一使用包含首次调用的尝试上限。底层 SDK 与外层工作流都重试时，要按乘积核算最坏请求量，并共享截止时间和重试预算。

$$
N_{\mathrm{backend}}\leq A_{\mathrm{outer}}A_{\mathrm{inner}},\qquad t_{\mathrm{wait}}+t_{\mathrm{attempt}}\leq t_{\mathrm{remaining}}
$$

**每层只重试两次，会放大多少？**

嵌套重试的最坏调用量相乘；退避等待与下一次执行都必须计入同一个截止时间。


继续阅读：[发布验收、灰度与恢复](/ai/04-evaluation-and-production/03-release-and-recovery)。
