用 anthropic 0.40 的流式接口做长文生成,把每个 content_block_delta 里的 text 直接拼接,最终文本里夹了几个空段落,日志里能看到事件类型是 ping,SDK 从 0.34 升上来之后才出现。
一开始怀疑是「自定义解析器把 event 行和 data 行错位配对」,查了一圈,方向不对。
排查方向其实有限,先把可能性排一排。只判断了 delta.type 是 text_delta,没有过滤其他事件。少数情况下是事件循环用 elif 串联,漏掉了 content_block_stop 之后的空 delta。
还有一种情况,网关注入了自己的心跳事件,SDK 不认识这个类型。
动手时先把事件类型逐条打印出来核对
python dump_events.py --model claude-3-5-sonnet-latest --max-tokens 64
只对 text_delta 追加文本
if e.type == 'content_block_delta' and e.delta.type == 'text_delta': buf.append(e.delta.text)
接着改用官方累积器替代手写拼接
python fix_stream.py --use accumulate_event
回归一遍三种事件混排的样例
pytest tests/test_stream.py -k ping -q
别只看报错消失,验证要多做一步。同一提示词输出的文本与关闭流式的结果一致,空段落数为零,事件计数与日志对得上。
容易忽略的是,流式协议里除正文还有 ping、message_delta 等事件,按 type 白名单取值比事后清洗文本可靠。报错文案容易误导,真正的原因往往在相邻那一层的日志里。
