Skip to content

模型适配、流式事件与最终提交 ​

用户看到“正在生成”与业务收到“已完成”是两个不同信号。流式增量可以显示在临时区域,只有完成事件到达、结构校验通过后才能提交业务产物;连接中断不能把半段 JSON 当成结果。

在适配层保留协议状态 ​

完整标准库实验:事件代表一个模型连接的已解析输出,真实供应商的 SSE 解析在模型 API 课程。适配层把文本、工具、用量和错误统一为业务事件,但保留供应商 request ID 供排障。这里检验提交边界,不模拟真实网络可靠性。

python
import json

def collect(events):
    chunks, finished = [], False
    for event in events:
        if finished:
            raise ValueError("event after completion")
        if event["type"] == "delta":
            chunks.append(event["text"])
        elif event["type"] == "done":
            finished = True
        elif event["type"] == "error":
            raise RuntimeError("upstream failure")
        else:
            raise ValueError("unknown event")
    if not finished:
        raise RuntimeError("incomplete")
    value = json.loads("".join(chunks))
    if set(value) != {"answer", "source"} or not all(isinstance(v, str) for v in value.values()):
        raise ValueError("invalid output")
    return value

ok = [{"type": "delta", "text": '{"answer":"68 F",'},
      {"type": "delta", "text": '"source":"calculator"}'}, {"type": "done"}]
assert collect(ok)["answer"] == "68 F"
try: collect(ok[:-1])
except RuntimeError as e: assert str(e) == "incomplete"
else: raise AssertionError("partial committed")
print("complete only")

缓存的文本串不能表示“供应商已完成”,所以 finished 单独存在。JSON 合法也不代表事实正确,计算结果应与工具证据对照。工具参数增量同样必须聚合完整后才执行,绝不能每收到一点就调用一次工具。

错误分类决定重试位置 ​

参数和认证错误重试同一请求通常无效;限流需遵守服务端等待提示,在总体 deadline 内退避;连接断开后是否可重试取决于请求有没有副作用。模型调用自动重试、工具包装器重试、队列重试叠加会放大次数,应明确哪层负责,并把每次 attempt 单独记账。

异步并发不是无限任务创建。入口限流保护租户额度,工作池限制下游并发,流式消费者慢时通过有界队列形成背压。客户端断连仅表示用户不再接收,不自动取消数据库操作;服务端应标记取消请求,在副作用前检查,结束后查询业务状态。

同步短请求直接返回结果,长任务先返回 task ID,流式接口发送带序号的事件。恢复订阅从最后确认序号开始,客户端去重;完成事件包含结果版本,避免重连后旧结果覆盖新任务。

练习 ​

增加测试:done 后继续 delta 必须 ValueError;已 done 但答案字段为数字也必须 ValueError。保留两种失败区别。

核对本章答案

下一章

用心记录每一步成长