想把 Claude Haiku 5.5 这一档轻量模型接进批量任务,先不要动流水线,先在本地拿二三十条真实输入跑一轮,把单条延迟分布、失败原因分类和重试的实际代价记下来,再决定并发、超时和重试上限。单次调用快,不代表批量跑起来不被限流和长输入拖慢;直接拿全量来试,又容易把偶发失败放大成整批重跑。
判断方向:轻量模型能不能扛批处理,取决于小样本测出的 P95 延迟、失败分类构成,以及重试之后是否真的变成成功,而不是单次响应快不快。操作上,固定一批覆盖短问答、摘要、结构化抽取的输入,逐条记录耗时、报错类型和返回长度,再用日志把超时、限流、内容拦截分开。边界:并发、超时、重试次数都要按自己账号的配额和输入长度调整,样本结果只适合用来定起步参数,不能直接当成全量任务的承诺。
在本地目录固定一批有代表性的测试输入
目标是让不同任务、不同时间的比较结果可复现。建议在本机建一个 samples 目录,按任务类型分成三到五个子目录,每类放同一批固定输入,不要每次重新挑。典型取舍是:短问答看基础往返,摘要和长文改写看长输入的耗时,结构化抽取看输出是否符合字段要求。
samples/
short_qa/ 短问答:一问一答、带少量上下文
summarize/ 摘要:一段或多段正文
extract/ 结构化抽取:要求输出固定字段
rewrite/ 改写:长文本、要求保持原意
chat_multi/ 多轮:三步以上指令
每个子目录里放 jsonl 文件,一行一条输入,文件命名用「类别-三位序号.jsonl」,例如 short_qa-001.jsonl。每行至少带 id、input 和期望输出的要点,便于人工快速判断返回是否可用。长文本单独成文件,不要和短输入混在一起统计耗时,否则延迟分布会看不出问题出在哪一类。
用最小调用骨架循环跑完样本并逐条记录
骨架不绑定具体 SDK,把 client.create 换成自己封装好的调用函数即可。关键是每条样本都产生一行记录,而不是只打印一个总耗时。
for task_type, path in sample_files:
for rec in load_jsonl(path):
t0 = time.perf_counter()
retry = 0
while True:
try:
resp = client.create(
model=MODEL_ID,
messages=build_messages(rec),
timeout=timeout_s,
)
status = 'ok'
err_class = ''
out_len = len(resp.text)
break
except Exception as e:
err_class = classify(e) # timeout / rate_limit / content_block / other
if err_class in ('timeout', 'rate_limit') and retry < max_retries:
retry += 1
sleep(backoff(retry))
continue
status = 'fail'
out_len = 0
break
latency = (time.perf_counter() - t0) * 1000
append_row(csv_path, {
'request_id': rec['id'],
'task_type': task_type,
'latency_ms': round(latency, 1),
'status': status,
'err_class': err_class,
'out_len': out_len,
'retry_count': retry,
})
必须记录的字段是 request_id、task_type、latency_ms、status、err_class、out_len、retry_count。out_len 用来发现「调用成功但内容被截断」这类问题,retry_count 用来估算重试把实际调用量放大了多少。如果只记一个总耗时,后面调并发时就没有依据。
在日志里把超时、限流、内容拦截分开归类
三类失败的耗时特征和处理动作完全不同,合成一个 fail 计数,后面的参数就没法调。
- 超时:耗时接近自己设的超时值,通常拿不到响应体,或者连接中途断开。动作是先看是否集中在长输入,再判断该提高超时还是把输入拆短。
- 限流:返回得很快,耗时明显低于正常调用,响应头或响应体里出现速率、配额、请求过多这类字样。动作是降并发并加退避等待,靠加大超时解决不了。
- 内容拦截:调用本身成功,但返回为空、被拒绝,或结束原因不是正常结束。动作是不要重试,原样落盘,人工看是输入问题还是提示词问题。
判断方法是从响应头、响应体和耗时三处交叉对照,不依赖某个平台的固定错误码。日志里 err_class 只允许填上面几类加一个 other,避免随手写自由文本导致没法聚合。
按失败比例反推并发与重试上限
并发从 1~2 起步,跑稳之后按倍数往上翻,一旦失败比例抬头就退回上一档。起步区间可以先按:并发 2~8,超时取样本 P95 的 2~3 倍并设一个硬上限,重试 0~2 次、指数退避加随机抖动。重试只覆盖超时和限流两类,内容拦截不计入重试次数。
每次调整后要观察的指标:总失败比例、其中限流占比、平均每条的重试次数、整批完成时间,以及不带重试时的 P95 延迟。如果重试次数上升而失败比例没降,说明瓶颈在限流而不是偶发抖动,应该优先降并发,而不是继续加重试。
把测出的参数写回流水线配置
把结论落到一个可复制的配置块里,本地样本和线上流水线用同一份参数,避免测一套、跑一套。
batch:
concurrency: 4
timeout_ms: 60000
max_retries: 1
backoff_ms: [500, 2000]
jitter: true
retry_on: [timeout, rate_limit]
fail_dump: ./failed/{date}/{request_id}.json
log_fields:
- request_id
- task_type
- latency_ms
- status
- err_class
- out_len
- retry_count
验证方式是:先用同一批样本重跑,对比失败落盘条数和整批耗时是否落在可接受范围;再从全量任务里切一小段先跑,确认失败分类的构成和样本阶段一致,然后才逐步放开并发。timeout_ms 和 max_retries 都写成明确上限,失败任务统一落到 fail_dump,别让个别慢请求把整条流水线卡住。