768P → 2K 的api价格是0.05usd/s, 本地直接生1080p很不划算, 大概是768p的3x时间, 还到不了2k,
可以本地抽卡768p 再用云端api 升到2k

768P → 2K 的api价格是0.05usd/s, 本地直接生1080p很不划算, 大概是768p的3x时间, 还到不了2k,
可以本地抽卡768p 再用云端api 升到2k

上面正文测的是 Lite 档。Lite 几下就烧光了一周的(阿里现在临时去掉了5小时额度), 后来升到了 Standard($18),把同一套探针原样又跑了一遍,约 900 个请求 / 200 多万 token。
一句话:并发涨了 3 倍多,token 桶容量("速率") 一动没动 —— 而后者是决定「能不能并行开大上下文」的那堵墙. 如果你的使用场景涉及到这个点, 也很重要, 升级套餐档位无效.
| 墙 | Lite | Standard($18) | 变化 |
|---|---|---|---|
| 并发数(同时在飞) | 12(硬闸) | ~40(有毛边, 到过48, 60) | ≈3.3× |
| token 桶容量 | 1,219,071 | 1,259,676 | 基本没变(测试多次) |
| 请求速率(QPS) | 没测到上限 | 未复测 | — |
两种 429 的文案和 Lite 完全一致(API-Key Requests rate limit exceeded / Allocated quota exceeded),响应头里依然一个 x-ratelimit-* 都没有,retry-after 也没有。
官方标称的「并发 agent」数(Lite 1–2 / Standard 3–4)两档都不是 HTTP 层的闸,实测都高出一个数量级。正文那条结论继续成立。
桶容量:排空后一口气能推进去的 token 总量,实测约 1.22M(Lite/Standard 一致)——它是存量上限,不是速率,超了报 Allocated quota exceeded。(对应的回填速率约 340k/min,只有单点估算,
未确证)
deepseek 官方 api 要提价了

前两天 qwen3.8-max 发布,想试试, 顺带发现阿里的 Token Plan 里正好也有我长期在用的 deepseek-v4-flash, 就去翻了三档套餐(Lite / Standard / Pro)的额度和参数。
结果卡在一个含糊的地方: 并发那一栏只写了「支持 Agent 并发 Lite 1–2、Standard 3–4、Pro 6–8」,却没说这个数字到底是什么意思。是 RPS?是同时在飞的请求数?还是别的什么口径? 搜了一圈, 没有一个靠谱的答案。
而并发恰恰是日常折腾里(尤其多agent时)最先撞上的那堵墙 —— 额度是慢慢烧掉的, 并发是当场就拒的。所以在买大包之前得先把它弄明白:先买一个 Lite 回来,把它测穿。 测完一想,反正都干了,不如让 AI 顺手总结一篇发出来, 给大伙乐呵乐呵。
结论是三个数字,和一条比数字更重要的发现:同一个 HTTP 429,两种完全不同的报错,指向两堵完全不同的墙 —— 其中一条会把你骗去查额度面板,而额度根本没事。
我注册不了国服,测的是新加坡(International)的 Lite 档; 国服的限制应该一致(而且还便宜一点点),但我没有验证过。
题外话:另外一点有意义的是, 这次测试是
Opus 5做 plan、deepseek-v4-flash做 worker 配合完成的, 目测工作量三七开 —— 结果我完全满意。
deepseek-v4-flash-0731出来之后,我的主观感受是:个别场景能追平 Opus 5,日常足以平替 Sonnet 5,对 Haiku 4.5 则是完爆。所以最近我一直在日常使用里刻意观察它真正干活时的表现,而不是看榜单。现实里还有另一条线。很多公司出于合规不允许用国产模型;但最近陆续听身边朋友说,他们公司「烧不起 Claude 了」,开始限额,有的甚至在逐步放开对国产模型的限制 —— 理由很直接:省 token 开销。
我自己的分工是:正经干活还是只用 Claude,日常折腾和自己的东西(尤其是常驻的 agent)早就大部分切到 v4-flash 了。 而现在的感觉是,「正经活」也可以分一部分出去了。
测试对象:阿里云 Token Plan 个人版 Lite 档 1-2 Agent 并发, OpenAI 兼容端点, 模型 deepseek-v4-flash-0731。
| 限制 | 实测阈值 | 报错 |
|---|---|---|
| 并发数(同时在飞的请求) | 12 个 | 429 · API-Key Requests rate limit exceeded · type: limit_requests |
| 请求速率(QPS) | 没测到上限 | — |
| token 桶容量 | 约 1,219,000 token | 429 · Allocated quota exceeded, please increase your quota limit |
| 5h / 7d credits 预算 | Lite:700 / 5h · 2500 / 7 天 | 与上一行 429 相同(官方 FAQ) |
️ 第三行是「桶容量」,不是 TPM。 它的定义是排空后一口气能推进去的 token 总量 —— 存量上限,不是速率。实测撞墙发生在 32 秒内,所以它回答的是「一口气能推多少」,不是「每分钟能推多少」。稳态速率这套测试没测(见 §10 末尾和 §13)。
其他两档 (Standard, Pro) 可以类推. 知道了并发限制后, 具体 token 需求多少 大家根据自己的使用情况决定什么档位合适. 粗糙估算, 套餐如果合理打满, 用量应该是 单买token 的 3-6x ( 具体情况要看自己的使用场景, in, out, cache hit 的比例等等)
三个反直觉的点:
1-2 那个数字不是闸。**Allocated quota exceeded 不等于额度用光。** 触发它的那一刻,控制台显示 5 小时窗口只用了 27.37%。包月 LLM 套餐的宣传页通常只给两类数字:每月多少额度、支持几个「并发 agent」。前者是账单口径,后者是营销口径 —— 两者都不是你写代码时需要的那个数。
我要知道的是:同时发多少个请求会被拒?一分钟能推多少 token?被拒之后多久恢复?这些数字决定了并发编排怎么写、重试策略怎么定、以及最终那个问题 —— 要不要升档。
官方文档没有。响应头里也没有(见 §4)。那就只能撞。
️ 先说一个错误做法最自然的想法是:开 N 个 agent 会话,看第几个报错。
这个探针是坏的,四个理由:
| 问题 | 后果 |
|---|---|
| agent 会话要加载 system prompt + tool definitions + 历史,单次输入 40–80k token | 撞的可能是 token 桶容量,不是并发数上限 —— 但报错长得一样 |
| 本地同时起 N 个 agent 进程 | VM客户端 CPU 先饱和,你测的瓶颈是自己的机器 (我的 hermes agents 都是跑在 PVE VM上的) |
| 几乎所有 SDK 和 agent 框架自带重试 + 退避 | 429 被吞掉重试掉,「第几个失败」的观测直接失真 |
| 每次会话有真实工具调用和长输出 | 慢、贵、不可重复 |
一条报错对应多个真因时,先用一个绕开被测系统的最小实验把变量砍掉。 所以全程用裸 HTTP 请求当探针,agent 框架完全不参与。
四个互相独立的实验,分别对准一堵墙:
| 实验 | 怎么做 | 判据 |
|---|---|---|
| 并发梯度 | 同时发 N 个,N 逐级上升 | 成功数卡在固定值 → 在飞数上限 |
| 串行连发 | 一个接一个,全程只有 1 个在飞 | 这里也失败 → 撞的是速率不是并发 |
| 满并发多波 | 打满并发,波次之间不停歇 | 中途开始挂 → 存在每分钟请求数窗口 |
| 大 payload 满载 | 每请求几万 token,数到被拦为止 | 累计 token 数 = 桶容量 |
三个设计细节,每一个都直接决定数据是否可信:
① 发射抖动必须是毫秒级(0–300ms)。
单请求耗时以秒计,所以 0–300ms 的抖动下 N 个请求仍然全部同时在飞,并发梯度成立;同时避开「N 个 TCP 连接在同一毫秒握手」这种人造的 thundering herd。窗口一旦开到秒级,请求就不重叠了,测出来的会变成 QPS 而不是并发。
② max_tokens 不能设太小。
设成 16 的话请求几百毫秒就结束,N 个根本重叠不上,并发上限永远测不出来。要让单请求持续数秒,槽位才被真实占住。这里用 600,配合推理模型实测每个请求 4–6 秒。
③ prompt 必须每个都不同,而且 nonce 要放在最前面。
用 16 条互不相同的真实技术问题,而不是 say OK:
PROMPTS = [
"Proxmox 里 qm set --onboot 1 和 cloud-init 的 runcmd 谁先执行?顺序为什么重要?",
"systemd user service 在用户没登录时靠什么保持运行?loginctl enable-linger 改了什么?",
"OpenAI 兼容 API 里 reasoning_effort 和 extra_body.thinking 语义上有什么区别?",
"LLM 推理里 prefix cache 的命中条件是什么?为什么 JSON key 顺序变了就 miss?",
# … 共 16 条
]
理由是隐式缓存:阿里对 messages 做前缀匹配,命中的 token 单价只有输入的 20%,而且响应快得多。内容相同的请求会互相命中缓存,延迟和 token 计数全部失真。 我在 §8 真的踩了这个坑。
发射函数长这样:
def one(cfg, rung, idx, prompt, max_tokens, jitter_ms, t0):
time.sleep(random.uniform(0, jitter_ms) / 1000.0) # 毫秒级抖动
launch = (time.time() - t0) * 1000 # 记下实际发射偏移
body = json.dumps({"model": cfg.model,
"messages": [{"role": "user", "content": prompt}],
"max_tokens": max_tokens}).encode()
...
发射偏移要记进结果,事后才能验证「这 N 个请求确实重叠过」,而不是靠假设。
最便宜的一步 —— 打一发,把完整响应头 dump 出来。有 x-ratelimit-limit-* 的话,整套压测都可以省掉。
$ python3 probe1.py
http 200 latency_ms 3754
--- headers ---
content-type: application/json; charset=utf-8
grpc-encoding: identity
grpc-accept-encoding: gzip
x-envoy-upstream-service-time: 2784
date: Tue, 04 Aug 2026 18:01:41 GMT
server: istio-envoy
x-request-id: d95a4a3e-b197-4e55-9caf-59e49d86034f
req-cost-time: 2784
req-arrive-time: 1785866499350
resp-start-time: 1785866502134
vary: Accept-Encoding
connection: close
transfer-encoding: chunked
--- usage ---
{"prompt_tokens": 33, "total_tokens": 334, "completion_tokens": 301,
"prompt_tokens_details": {"cached_tokens": 0},
"completion_tokens_details": {"reasoning_tokens": 300}}
一个 x-ratelimit-* 都没有,retry-after 也没有。 只能靠撞。
顺带一个观察:
max_tokens=300全被 reasoning 吃光了(reasoning_tokens: 300),content是空的。给推理模型设max_tokens时要把思考预算算进去。
N = 2 → 4 → 6 → 8 → 12 → 16,每梯之间静置 10 秒。
$ python3 conc.py 1
=== 阶段 1:并发梯度(max_tokens=600, jitter 0-300ms) ===
[P1-n2] n=2 ok=2 fail=0 wall=5898ms lat_min=5483 lat_max=5656
[P1-n4] n=4 ok=4 fail=0 wall=5567ms lat_min=4808 lat_max=5547
[P1-n6] n=6 ok=6 fail=0 wall=6008ms lat_min=4924 lat_max=5875
[P1-n8] n=8 ok=8 fail=0 wall=6087ms lat_min=5036 lat_max=6000
[P1-n12] n=12 ok=12 fail=0 wall=6237ms lat_min=4228 lat_max=5962
[P1-n16] n=16 ok=12 fail=4 wall=6223ms lat_min=4835 lat_max=6210
✗ idx=6 http=429 retry_after='' err={"error":{"message":"API-Key Requests rate limit exceeded, please try again later.","type":"limit_requests"...
✗ idx=9 http=429 ...
✗ idx=11 http=429 ...
✗ idx=13 http=429 ...
注意 wall 一列:每梯的总耗时都在 6 秒左右,和单请求延迟(5–6 秒)基本相等 —— 证明这 N 个请求确实是并行的,不是排队跑完的。
N=12 全过,N=16 挂了 4 个。边界在 12 和 16 之间。
一次失败可能只是服务端抖动,所以把 13/14/15/16 逐个测,16 测两遍,最后加一个 20。
[P1b-n13] n=13 ok=12 fail=1 wall=6655ms lat_min=5097 lat_max=6437
[P1b-n14] n=14 ok=12 fail=2 wall=6072ms lat_min=4726 lat_max=5884
[P1b-n15] n=15 ok=12 fail=3 wall=5858ms lat_min=4856 lat_max=5766
[P1b-n16] n=16 ok=12 fail=4 wall=7392ms lat_min=4707 lat_max=7227
[P1b-n16] n=16 ok=12 fail=4 wall=6213ms lat_min=4717 lat_max=5958
[P1b-n20] n=20 ok=12 fail=8 wall=6018ms lat_min=4743 lat_max=5885
(每行下面还有对应数量的 429 明细,文案完全一致,这里省略。)
六次独立测量,ok 全部恰好等于 12。 不管发 13 个还是 20 个,永远放行 12 个,多出来的当场拒绝 —— 没有排队,没有等待,立即失败。
这是全文最干净的一个数字。
并发数卡在 12,但这到底是「同时在飞的数量」还是「单位时间的请求数」?两者现象一样。
实验:串行连发 20 个,全程只有 1 个在飞。
$ python3 conc.py 2
=== 阶段 2:串行 QPS(20 连发, 间隔 50-200ms, 全程 1 个在飞) ===
#00 http=200 lat=1473ms
#01 http=200 lat=1714ms
#02 http=200 lat=1773ms
...
#18 http=200 lat=1852ms
#19 http=200 lat=1703ms
[P2] ok=20/20 wall=36410ms
20/20 全过。但串行速率只有 0.55 req/s,不够快,不足以排除「每分钟请求数」这种窗口限制。
再来一个:5 波 × 12 并发,波次之间完全不停。
[P2b-wave0] n=12 ok=12 fail=0 wall=4482ms lat_min=2925 lat_max=4348
[P2b-wave1] n=12 ok=12 fail=0 wall=5125ms lat_min=2500 lat_max=5096
[P2b-wave2] n=12 ok=12 fail=0 wall=4053ms lat_min=2801 lat_max=3830
[P2b-wave3] n=12 ok=12 fail=0 wall=4553ms lat_min=2567 lat_max=4345
[P2b-wave4] n=12 ok=12 fail=0 wall=3946ms lat_min=2795 lat_max=3646
总计 60 个请求 / 22s,ok=60
60/60 全过,持续 2.7 req/s 无一失败。
结论:限的是同时在飞的数量,不是每秒发多少个。 只要不超过 12 个并发,请求速率随便打。
小请求测出来的边界不能直接套到生产上:真实 agent 的单请求是几万 token,比探针大三个数量级。很可能出现「16 个小请求都没事,2 个真实请求就撞墙」。
于是把 prompt padding 到约 25k token,再跑一遍梯度:
padding 字符数: 47628
[P3-n2] n=2 ok=2 fail=0 wall=2883ms lat_min=2706 lat_max=2763
[P3-n4] n=4 ok=4 fail=0 wall=3228ms lat_min=2628 lat_max=3142
[P3-n8] n=8 ok=8 fail=0 wall=4165ms lat_min=2401 lat_max=3964
[P3-n12] n=12 ok=12 fail=0 wall=7450ms lat_min=1783 lat_max=7285
单请求 prompt_tokens: 25661 | 缓存命中: {0, 25600}
️ 最后一行暴露了一个错误。
cached_tokens 出现了 25600 —— 说明有请求命中了隐式缓存。原因是我的 nonce 每梯只生成一次,同一梯内 12 个请求共用同一段 padding,前缀完全相同,从第二个请求开始就命中缓存了。
这批数据作废:命中缓存的请求走的是不同的计费路径和不同的延迟,拿它们衡量吞吐是错的。
修法:nonce 必须每请求独立生成,而且放在 prompt 的最前面 —— 阿里的前缀匹配从 token 0 开始算,nonce 放中间是没用的。
prompt = f"[{uuid.uuid4().hex}] {pad}{PROMPTS[i % len(PROMPTS)]}"
# ^^^^^^^^^^^^^^^^^^^^ 每请求独立,且必须在最前面
[P3b-n12] n=12 ok=12 fail=0 wall=4485ms 输入token合计=375000 缓存命中={0}
[P3b-n16] n=16 ok=15 fail=1 wall=9101ms 输入token合计=468728 缓存命中={0}
✗ idx=1 http=429 {"error":{"message":"API-Key Requests rate limit exceeded, ...
[P3b-sustain0] n=12 ok=12 fail=0 wall=8425ms 输入token合计=374998 缓存命中={0}
[P3b-sustain1] n=12 ok=0 fail=12 wall=1645ms 输入token合计=0 缓存命中=set()
✗ idx=0 http=429 {"error":{"message":"Allocated quota exceeded, please increase your quota limit. For details, see: https://www.alibabacl...
✗ idx=1 http=429 {"error":{"message":"Allocated quota exceeded, ...
✗ idx=2 http=429 {"error":{"message":"Allocated quota exceeded, ...
... (12 个全挂,文案一致)
[P3b-sustain2] n=12 ok=0 fail=12 wall=1090ms 输入token合计=0
... (同样 12 个全挂)
两件事同时发生:
① n=16 这次过了 15 个,不是 12。
这不矛盾,反而是佐证:payload 大了三个数量级,上传耗时被拉长,请求的到达时间被抹开了 —— 同一瞬间在飞的从来没超过 12 个。这说明限制器数的是「此刻在飞的请求数」,不是「你一批发了几个」。
② 出现了一个文案完全不同的 429。
"Allocated quota exceeded, please increase your quota limit."
它和前面那个 API-Key Requests rate limit exceeded 完全不是一回事。而且这句话极具误导性 —— 它看起来就是在说「你的额度用完了,去买更多」。
第一反应是「5 小时窗口的 credits 打空了」。去控制台一看:
5-hour Quota
Will reset at 2026-08-05 08:46:00
27.37% Used
只用了 27.37%。 额度好好的。
那它是不是「触发后关门 N 秒」的固定窗口?测一下 —— 被拦之后每 10 秒探一个小请求:
t+ 1s → OK
→ 恢复用时约 1s
1 秒就恢复了。
所以它既不是额度耗尽,也不是固定时间窗口,而是按瞬时 token 体量放行的限流器:大批量推不进去时拒绝,小请求随时能过。
为了量出这个桶有多大,静置 90 秒清空计数,然后满载连发,数到被拦为止:
静置 90s 让速率窗口清空…
wave0: ok=12 速率429=0 额度429=0 本波token=304,801 累计耗时=8s
← 累计成功 token=304,801
wave1: ok=12 速率429=0 额度429=0 本波token=304,723 累计耗时=16s
← 累计成功 token=609,524
wave2: ok=12 速率429=0 额度429=0 本波token=304,738 累计耗时=24s
← 累计成功 token=914,262
wave3: ok=12 速率429=0 额度429=0 本波token=304,809 累计耗时=31s
← 累计成功 token=1,219,071
wave4: ok=0 速率429=0 额度429=12 本波token=0 累计耗时=32s
→ 第 4 波触发额度型 429,此前累计 1,219,071 token / 32s
1,219,071 token。 而 §9 那次意外触发时的累计量是 375k + 469k + 375k = 1,219,000 出头 —— 两次独立测量落在同一个数上。这是个硬桶。
最后验一下正常节奏下会不会碰到它 —— 每 20 秒一波满载:
wave0 @t+ 7s: ok=12/12 额度429=0 token=127,188
wave1 @t+22s: ok=12/12 额度429=0 token=127,179
wave2 @t+43s: ok=12/12 额度429=0 token=127,149
约 530k token/min 持续无失败。(这一波的 padding 比校准时短,所以只推到 530k/min,没有逼近上限,不能当作稳态吞吐的上界 —— 这一项测得不彻底。)
️ 更要紧的是:这组根本不能当稳态速率的证据。 它只跑了 43 秒、累计 381k —— 全程在吃桶里的存量,压根没触到回填底。要测稳态只有一个办法:固定供给速率跑够长(排空后以 250k/min 持续 5 分钟,再试 400k、600k,第一个撑不住的就是上限)。本文没测。
| 墙 | 阈值 | 报错 | 什么时候会撞上 |
|---|---|---|---|
| 并发数 | 12 个在飞请求 | API-Key Requests rate limit exceeded |
多 worker 编排 |
| 请求速率 | 没测到上限 | — | 基本撞不上 |
| token 桶容量 | ~1.22M token | Allocated quota exceeded |
超大批量;正常对话碰不到 |
| credits 预算 | 按档位 | 文案与上一行相同 | 真的用完时 |
️ 两个 429 的辨析(全文最该记住的一条)"API-Key Requests rate limit exceeded" → 并发数满了,降低同时在飞的请求数
"Allocated quota exceeded, please …" → 突发量超过桶容量,额度可能好好的
排查口径:看到 Allocated quota exceeded,先看控制台的额度窗口用了百分之多少。 没用完就是突发量的问题,把批量拆小即可 —— 别去升档,升档买的是额度,解决不了桶。
官方标称 Lite 支持 1–2 个并发 agent,实测 HTTP 层能同时跑 12 个。那个数字是按 agent 工具的典型行为估的,不是硬闸。 如果你的升档理由是「并发不够」,先测一下 —— 大概率不是并发的问题,而是额度的问题,而这两件事的解法不一样。
换任何一家 OpenAI 兼容后端之前,这五条都成立:
x-ratelimit-* 就直接读数,整套压测都省了。max_tokens 别太小。 前者防握手风暴,后者保证请求真的重叠。stream=true,流式请求占用并发槽位的方式可能不同,本文结论不能直接外推。
️ 稳态吞吐没测。 本文量到的是桶容量(存量),不是速率。§10 最后那组只跑了 43s / 381k,全程在吃桶里的存量,不能当稳态吞吐的上界。要测得固定供给速率跑够长。只用 Python 标准库,不需要 pip install。key 只从环境变量或 600 权限的文件读,不接受命令行参数 —— 否则会留在 shell history 和进程列表里。
#!/usr/bin/env python3
"""OpenAI 兼容 endpoint 的并发 / 限流探针。
ladder 并发梯度 → 在飞数上限
serial 串行连发 → 是不是 QPS 限制
sustain 满并发多波 → 有没有每分钟请求数窗口
bulk 大 payload 满载 → token 桶容量(存量上限,不是 TPM)
recover 被拦后每 10s 探一次 → 恢复时间
用法:
export PROBE_KEY=sk-...
./probe.py ladder
./probe.py ladder --base-url https://api.deepseek.com/v1 --model deepseek-v4-flash
"""
from __future__ import annotations
import argparse, csv, json, os, random, sys, time, uuid
import urllib.error, urllib.request
from concurrent.futures import ThreadPoolExecutor
DEFAULT_BASE_URL = "https://token-plan.ap-southeast-1.maas.aliyuncs.com/compatible-mode/v1"
DEFAULT_MODEL = "deepseek-v4-flash-0731"
# 各不相同且真的想知道答案的题 —— 不用 "say OK"。
# 内容不同 = 天然绕开隐式缓存。
PROMPTS = [
"Proxmox 里 qm set --onboot 1 和 cloud-init 的 runcmd 谁先执行?顺序为什么重要?",
"systemd user service 在用户没登录时靠什么保持运行?loginctl enable-linger 改了什么?",
"Ubuntu 24.04 非交互 SSH 为什么读不到 .bashrc 里的 PATH?最小修复是什么?",
"OpenAI 兼容 API 里 reasoning_effort 和 extra_body.thinking 语义上有什么区别?",
"LXC 和 VM 在容器逃逸场景下的爆炸半径差异,一句话说清楚。",
"ZFS 的 ARC 和 Linux page cache 会重复缓存同一份数据吗?为什么?",
"为什么 pgrep -f 写在 while 循环里会匹配到轮询进程自身?怎么避免?",
"LLM 推理里 prefix cache 的命中条件是什么?为什么 JSON key 顺序变了就 miss?",
"Docker bridge 网络和 macvlan 在容器直连局域网时的取舍是什么?",
"PVE 的 thin pool 超配到 100% 会发生什么?怎么提前发现?",
"为什么 NVMe 的 SMART 里 Percentage Used 超过 100% 不等于盘坏了?",
"systemd 的 Restart=always 和 RestartSec 在服务快速崩溃循环时会怎样?",
"SSH 的 ControlMaster 复用连接时,一个连接挂了会影响其他会话吗?",
"MoE 模型的 active parameters 和 total parameters 分别决定了什么成本?",
"为什么 rsync 的 --delete 在源目录挂载失败时特别危险?怎么防?",
"HTTP/2 的多路复用会让服务端的并发限制以什么维度计算?",
]
# bulk 阶段的填充文本,任意内容都行,重复到需要的长度。实测重复 400 遍 ≈ 25.6k token。
PAD_UNIT = ("虚拟化节点上运行若干虚拟机与容器,存储为 thin pool,网卡桥接到内网网段,"
"存储池由多块 NVMe 组成 raidz1,监控采集间隔 30 秒,日志保留 14 天。")
FIELDS = ["rung", "idx", "launch_ms", "http", "latency_ms", "prompt_tokens",
"completion_tokens", "reasoning_tokens", "cached_tokens", "retry_after", "err"]
def classify(row: dict) -> str:
"""把 429 分成两类 —— 文案完全不同,含义完全不同。"""
if row["http"] == 200:
return "ok"
e = row["err"]
if "Requests rate limit" in e:
return "并发/请求数"
if "Allocated quota" in e:
return "token桶"
if row["http"] == 429:
return "429其他"
return f"http{row['http']}"
def one(cfg, rung, idx, prompt, max_tokens, jitter_ms, t0):
"""发一个请求。抖动窗口保持毫秒级 —— 单请求耗时以秒计,N 个仍然全部同时在飞。"""
time.sleep(random.uniform(0, jitter_ms) / 1000.0)
launch = (time.time() - t0) * 1000
body = json.dumps({"model": cfg.model,
"messages": [{"role": "user", "content": prompt}],
"max_tokens": max_tokens}).encode()
req = urllib.request.Request(
cfg.base_url.rstrip("/") + "/chat/completions", data=body,
headers={"Content-Type": "application/json", "Authorization": "Bearer " + cfg.key})
t = time.time()
code, err, usage, retry_after = 0, "", {}, ""
try:
r = urllib.request.urlopen(req, timeout=cfg.timeout)
code, retry_after = r.status, r.headers.get("retry-after", "")
usage = json.loads(r.read()).get("usage") or {}
except urllib.error.HTTPError as e:
code, retry_after = e.code, e.headers.get("retry-after", "")
err = e.read()[:200].decode("utf-8", "replace").replace("\n", " ")
except Exception as e: # 连接层失败也要记,别和配额混为一谈
code, err = -1, f"{type(e).__name__}: {e}"[:200]
pd = usage.get("prompt_tokens_details") or {}
cd = usage.get("completion_tokens_details") or {}
return dict(rung=rung, idx=idx, launch_ms=int(launch), http=code,
latency_ms=int((time.time() - t) * 1000),
prompt_tokens=usage.get("prompt_tokens", 0),
completion_tokens=usage.get("completion_tokens", 0),
reasoning_tokens=cd.get("reasoning_tokens", 0),
cached_tokens=pd.get("cached_tokens", 0),
retry_after=retry_after, err=err)
def burst(cfg, label, n, max_tokens, pad_repeat=0):
"""同时发 n 个。⚠️ nonce 必须每请求独立 —— 每梯只生成一次的话,同梯请求共享前缀
会命中隐式缓存,token 数和延迟全部失真。"""
pad = (PAD_UNIT * pad_repeat + "\n\n问题:") if pad_repeat else ""
t0 = time.time()
with ThreadPoolExecutor(max_workers=n) as ex:
futs = [ex.submit(one, cfg, label, i,
f"[{uuid.uuid4().hex}] {pad}{PROMPTS[i % len(PROMPTS)]}",
max_tokens, cfg.jitter, t0) for i in range(n)]
rows = [f.result() for f in futs]
rows.sort(key=lambda r: r["idx"])
ok = [r for r in rows if r["http"] == 200]
tok = sum(r["prompt_tokens"] + r["completion_tokens"] for r in ok)
cached = {r["cached_tokens"] for r in ok}
lat = [r["latency_ms"] for r in ok]
print(f"[{label}] n={n} ok={len(ok)} fail={n - len(ok)} "
f"wall={int((time.time() - t0) * 1000)}ms token={tok:,} "
f"lat={min(lat) if lat else '-'}~{max(lat) if lat else '-'}ms"
+ (f" ⚠️缓存命中={cached}" if cached - {0} else ""))
for r in rows:
if r["http"] != 200:
print(f" ✗ idx={r['idx']} http={r['http']} [{classify(r)}] {r['err'][:110]}")
return rows
def ph_ladder(cfg):
"""并发梯度。首次失败不停,再爬两梯看形状。"""
rows = []
for n in [2, 4, 6, 8, 12, 13, 14, 16, 20]:
rows += burst(cfg, f"ladder-n{n}", n, cfg.max_tokens)
time.sleep(cfg.gap)
return rows
def ph_serial(cfg):
"""串行连发:全程只有 1 个在飞。这里也失败 = 撞的是速率,不是并发。"""
rows, t0 = [], time.time()
for i in range(20):
r = one(cfg, "serial", i, f"[{uuid.uuid4().hex}] {PROMPTS[i % len(PROMPTS)]}", 120, 0, t0)
print(f" #{i:02d} http={r['http']} lat={r['latency_ms']}ms [{classify(r)}]")
rows.append(r)
time.sleep(random.uniform(0.05, 0.2))
print(f"[serial] ok={sum(1 for r in rows if r['http'] == 200)}/20 "
f"wall={int((time.time() - t0) * 1000)}ms")
return rows
def ph_sustain(cfg):
"""满并发多波不停歇:验有没有「每分钟请求数」这类窗口限制。"""
rows, t0 = [], time.time()
for w in range(5):
rows += burst(cfg, f"sustain-w{w}", cfg.concurrency, 300)
ok = sum(1 for r in rows if r["http"] == 200)
print(f"[sustain] {ok}/{len(rows)} 过 / {int(time.time() - t0)}s")
return rows
def ph_bulk(cfg):
"""大 payload 满载连发,数到被拦为止 → token 桶容量。
⚠️ 量到的是**桶容量**(排空后一口气能推进去的总量),**不是 TPM** —— 实测撞墙
发生在 32 秒内。稳态速率要用「固定供给速率跑够长」另测,本阶段测不出。
先静置,让上一次测试的计数清空,否则测的是残留。"""
print(f"静置 {cfg.settle}s 让速率窗口清空…")
time.sleep(cfg.settle)
rows, total, t0 = [], 0, time.time()
for i in range(cfg.waves):
w = burst(cfg, f"bulk-w{i}", cfg.concurrency, 150, pad_repeat=cfg.pad)
rows += w
ok = [r for r in w if r["http"] == 200]
total += sum(r["prompt_tokens"] + r["completion_tokens"] for r in ok)
print(f" ← 累计成功 token={total:,} @t+{int(time.time() - t0)}s")
if any(classify(r) == "token桶" for r in w):
print(f"→ 第 {i} 波触发 token 桶容量上限,此前累计 {total:,} token / {int(time.time() - t0)}s")
break
return rows
def ph_recover(cfg):
"""被拦之后每 10s 探一个小请求,量恢复时间。
⚠️ 小请求可能立刻就过 —— 那说明限制器按瞬时体量放行,不是「关门 N 秒」。"""
rows, t0 = [], time.time()
for i in range(30):
r = one(cfg, "recover", i, f"[{uuid.uuid4().hex}] {PROMPTS[i % len(PROMPTS)]}", 40, 0, t0)
rows.append(r)
print(f" t+{int(time.time() - t0):3d}s → [{classify(r)}] {r['err'][:80]}")
if r["http"] == 200:
print(f"→ 恢复用时约 {int(time.time() - t0)}s")
break
time.sleep(10)
return rows
PHASES = {"ladder": ph_ladder, "serial": ph_serial, "sustain": ph_sustain,
"bulk": ph_bulk, "recover": ph_recover}
def main():
ap = argparse.ArgumentParser(description=__doc__,
formatter_class=argparse.RawDescriptionHelpFormatter)
ap.add_argument("phase", choices=sorted(PHASES))
ap.add_argument("--base-url", default=os.environ.get("PROBE_BASE_URL", DEFAULT_BASE_URL))
ap.add_argument("--model", default=os.environ.get("PROBE_MODEL", DEFAULT_MODEL))
ap.add_argument("--key-file", help="装 API key 的文件(权限 600)。不给就读 $PROBE_KEY")
ap.add_argument("--concurrency", type=int, default=12)
ap.add_argument("--max-tokens", type=int, default=600,
help="别设太小 —— 请求太快结束就重叠不上,并发上限永远测不出来")
ap.add_argument("--pad", type=int, default=400, help="bulk 填充重复次数,400 ≈ 25.6k token")
ap.add_argument("--waves", type=int, default=8)
ap.add_argument("--jitter", type=int, default=300, help="发射抖动窗口(ms)")
ap.add_argument("--gap", type=int, default=10, help="ladder 梯间静置秒数")
ap.add_argument("--settle", type=int, default=90, help="bulk 开跑前静置秒数")
ap.add_argument("--timeout", type=int, default=300)
ap.add_argument("--out")
cfg = ap.parse_args()
cfg.key = (open(cfg.key_file).read().strip() if cfg.key_file
else os.environ.get("PROBE_KEY", "").strip())
if not cfg.key:
sys.exit("没有 key:设 $PROBE_KEY 或给 --key-file。"
"别把 key 写进命令行 —— 会留在 shell history 和进程列表里。")
print(f"endpoint={cfg.base_url}\nmodel={cfg.model}\nphase={cfg.phase}\n")
rows = PHASES[cfg.phase](cfg)
out = cfg.out or f"probe-{cfg.phase}-{time.strftime('%Y%m%d-%H%M%S')}.csv"
with open(out, "w", newline="") as f:
w = csv.DictWriter(f, fieldnames=FIELDS)
w.writeheader()
w.writerows(rows)
ok = [r for r in rows if r["http"] == 200]
print(f"\n合计 {len(rows)} 个请求 · 成功 {len(ok)} · "
f"input={sum(r['prompt_tokens'] for r in ok):,} "
f"output={sum(r['completion_tokens'] for r in ok):,}\ncsv → {out}")
if __name__ == "__main__":
main()
全部测试合计 312 个记录在案的请求 + 若干满载波次,约 350 万 token。测试时间 2026-08-05。
这次的 3.8 max 2.4T 看 Arena 的跑分很给力, 要掀桌子了. (更期待 ds v4 pro 了)


看 max 这个提升节奏, 猜测3.8 27b 对比 3.6 应该有不少提升
@terry 现在好了
目前 8月3号,Qwen3.8 API 已上线,Qwen3.8-Max 预计下周开源,将同时开源 Qwen3.8-27B。

https://x.com/Alibaba_Qwen/status/2084100707423289643
https://qwen.ai/blog?id=qwen3.8
持续关注中, 随时补充
补充:
亲测, v4flash 模型现在支持 内置搜索了(通过 Responses API, 搜索在 ds的服务端完成) 不需要配第三方搜索mcp 也可以工作.
收费模式不按次, 整合进token消耗了, 官方没有具体数字, 估算每次1,2分人民币
"if the model determines that your question requires a web search, it will invoke the Web Search tool and perform the search through the API provided by DeepSeek. Because invoking the Web Search tool generates additional LLM API requests to summarize the retrieved search content, additional model token costs will be incurred."
ref: https://api-docs.deepseek.com/quick_start/agent_integrations/claude_code , 这段只在 子条目 claude code 集成部分有提, 但是 同理其他的agent 一样的.
v4 pro 暂不支持, (因不支持 Responses API, 现在只能用 Chat Completions 不支持内置 web_search, 官方说8月会支持)
补充:
关于 v4 flash 0731 这个正式版
后训练威力被广泛认可:很多人感叹“架构不动、参数不变,只靠后训练就能反超自家 1.6T 预览版”,认为这是当前最有价值的信号之一。
Agent / Coding 场景提升明显:Terminal、工具调用、长链路任务、代码修复(DeepSWE 从 7.3 → 54.4)进步很大。开发者反馈在 Claude Code、Codex、OpenCode 等框架下使用体验显著变好,成本极低(有人连续跑几个小时只花几毛到一两块钱)。
性价比之王定位更牢固:被很多人当作日常 Agent / 编程主力,尤其适合高频、多轮、工具调用场景。
@terry Boris (claude code 负责人) 很早就直接确认了,说这是他们用来衡量用户体验的信号之一,内部还专门做了个叫 “fucks” chart 的仪表盘来看这个数据。

所以继续骂 不要停 百利无一害
