LLM 生成运行时、流式返回与重试协议底层图谱
从上下文提交到首 token、流式事件、停止条件、取消、超时、重试和最终落库,解释一次生成请求怎样被运行时可靠收尾
阅读定位: 上下文组装回答模型要看什么,
推理计算 / KV Cache回答 prefill 和 decode 的底层成本,
解码 / 采样回答下一个 token 怎样被选出来。
本页专门补运行协议层:一次生成请求进入模型服务后,运行时怎样排队、生成、流式返回、停止、取消、重试、记录证据并把结果交回产品。
Accepted
鉴权 / 配额
→
Queued
调度 / 合批
→
Prefill
读完整输入
→
Decode
逐 token
→
Stream
增量事件
→
Stop / Retry
收尾策略
→
Finalized
Trace / 计费
| 状态 | 发生了什么 | 关键字段 | 常见故障 |
| accepted | 请求通过鉴权、配额、风险和模型路由检查 | request_id、tenant、model_route、risk_tier | 限流误判、路由错误、预算缺失 |
| queued | 进入模型服务队列,等待合批、资源和优先级调度 | queue_ms、priority、pool_id、deadline | 队列堆积、长短请求互相拖累 |
| prefill | 模型处理完整输入上下文,准备生成第一个 token | input_tokens、prefill_ms、cache_hit | 上下文过长、首 token 慢、缓存失效 |
| decode | 模型逐 token 生成输出,并持续更新 KV Cache | output_tokens、decode_ms、tokens_per_second | 输出过长、速度抖动、结构破损 |
| streaming | 把 token、工具调用片段或结构化增量发给客户端 | event_id、chunk_index、finish_reason | 断流、重复片段、客户端取消未释放资源 |
| finalized | 生成完成、失败、取消或超时后收尾计费和写 Trace | finish_reason、cost、error_code、trace_complete | 最终状态不清、重试重复计费、无法复盘 |
| 分段 | 包含什么 | 为什么会慢 | 治理动作 |
| gateway_ms | 鉴权、配额、路由、策略、审计初始化 | 策略链过长、依赖外部系统、路由服务慢 | 本地缓存策略、路由规则预编译、关键路径瘦身 |
| context_ms | 上下文装配、token 估算、模板渲染 | RAG、工具结果、历史摘要和 token 计算在请求内串行 | 提前摘要、异步检索、上下文预算固定化 |
| queue_ms | 等待模型池、合批、显存和优先级调度 | 高峰期并发、长请求占池、低优先级排队 | SLA 分池、长短请求分流、限流和降级 |
| prefill_ms | 模型读取完整输入并建立 KV Cache | 输入 token 多、cache miss、长上下文和大模型 | Prompt Cache、上下文压缩、RAG Top-K 控制 |
| first_event_ms | 首 token 生成、后处理和首个流式事件发出 | 采样、过滤、网络缓冲、前端等待策略 | 首事件心跳、流式 flush、轻量后处理 |
TTFT 不是一个数字,而是一段路径
只记录“首 token 2.8 秒”不够。至少要拆成 gateway、context assembly、queue、prefill 和 first event。否则优化会变成猜。
文本 delta
最常见形式是增量文本片段。前端要处理拼接、光标、Markdown 未闭合、代码块未闭合和最终收尾。
结构化 delta
工具调用、JSON 字段、引用和状态事件可能逐步出现。客户端不能把半截 JSON 当成最终结果执行。
心跳事件
当 prefill 或工具执行较久时,心跳能告诉前端“还活着”。否则用户会误以为系统卡死。
| 事件类型 | 应该表达什么 | 客户端不要做什么 | Trace 要记录什么 |
| message_start | 生成开始、模型和请求元信息 | 不要展示为正文 | model、route、request_id |
| delta | 新增文本或结构化片段 | 不要假设每个 delta 是完整句子或完整 JSON | chunk_index、bytes、tokens_estimate |
| tool_call_delta | 工具名、参数片段、调用 id 的增量 | 不要在参数未闭合时执行工具 | tool_call_id、schema_version |
| citation_delta | 引用、证据 id、页码或片段范围 | 不要把引用和正文分离成不可追踪状态 | evidence_id、doc_version |
| heartbeat | 请求仍在运行,当前阶段未结束 | 不要当作模型输出 | stage、elapsed_ms |
| message_end | 最终文本完成、停止原因和计费摘要 | 不要忽略 finish_reason | finish_reason、usage、latency_breakdown |
| error | 失败类型、是否可重试、用户可见提示 | 不要丢掉已经生成的可用片段和错误原因 | error_code、retryable、partial_output_hash |
| finish_reason | 含义 | 用户体验 | 后续动作 |
| stop | 正常遇到 EOS 或 stop sequence | 回答完整结束 | 校验结构、写 Trace、计费 |
| length | 达到 max output tokens 或总窗口限制 | 回答可能被截断 | 提示可继续、调大预算或拆分任务 |
| tool_call | 模型生成了工具调用,需要进入工具执行阶段 | 用户可能看到“正在查询 / 执行” | 校验参数、权限、幂等和审批 |
| content_filter | 输出被安全策略拦截或改写 | 用户看到拒答或安全提示 | 记录风险标签、样本回流、安全评测 |
| cancelled | 用户取消、客户端断开或上游主动中止 | 输出中断 | 释放资源、标记取消来源、停止计费或按已用计费 |
| timeout | 超过入口、模型、工具或总任务期限 | 用户等待后失败 | 区分阶段超时,决定是否重试或降级 |
| error | 运行时、供应商、网络、解析或后处理失败 | 可能有部分输出 | 判断 retryable,保留错误上下文 |
finish_reason 是产品语义
同样是“没给出完整答案”,length、content_filter、timeout、cancelled 和 error 的用户提示、重试策略、计费与样本回流都不同。不要只存一个 success=false。
客户端取消
用户点击停止、关闭页面或网络断开时,服务端要尽快传播 cancel signal,避免模型继续 decode 无人消费的 token。
工具取消
如果生成已经触发工具或工作流,要区分可中止动作和不可中止副作用,避免取消后状态半提交。
资源释放
取消后要释放队列位置、KV Cache、连接、临时文件和锁。否则取消率越高,系统越慢。
| 对象 | 取消时要做什么 | 要记录什么 |
| 模型生成 | 停止 decode、释放请求状态和 KV Cache | cancel_stage、generated_tokens、resource_released |
| 流式连接 | 关闭 SSE / WebSocket / HTTP stream,避免继续写入 | disconnect_reason、last_event_id |
| 工具调用 | 能取消则取消,不能取消则记录不可逆副作用 | tool_status、side_effect_committed、compensation_id |
| 计费 | 按已用 token、已执行工具或产品策略计费 | billable_units、cancel_policy |
| 超时层 | 应该怎么设 | 可否重试 | 风险 |
| 入口超时 | 用户等待上限和产品体验上限 | 通常可换路由或后台继续 | 太短会误杀长任务,太长会拖垮体验 |
| 队列超时 | 排队超过 deadline 就降级或拒绝 | 可换模型池或低负载供应商 | 盲目等待会让 P95 / P99 爆炸 |
| prefill 超时 | 输入过长或模型池异常时触发 | 可压缩上下文后重试 | 重试不改上下文会重复失败 |
| decode 超时 | 输出过长或速度异常时触发 | 可截断收尾或继续生成 | 重复生成会造成重复内容和重复成本 |
| 工具超时 | 按工具副作用和业务 SLA 单独设置 | 只有幂等工具才适合自动重试 | 非幂等重试可能重复下单、重复发消息 |
| 后处理超时 | 结构解析、安全过滤和落库上限 | 通常可本地重试 | 模型已完成但最终响应失败,容易丢证据 |
重试前先判断失败阶段
队列失败、供应商 5xx、网络断开、结构解析失败、工具超时和安全拦截不是同一种失败。重试策略至少要记录 retry_count、retry_reason、idempotency_key、previous_request_id 和 changed_inputs。
安全可重试
队列超时、供应商临时失败、网络中断、只读检索失败通常可以重试,但要有退避和最大次数。
谨慎重试
结构化输出解析失败、长上下文 prefill 超时、模型格式漂移可以重试,但最好改变 Prompt、schema 或输出上限。
禁止自动重试
已经执行外部副作用的工具调用、人工审批后动作、支付和通知类动作,必须依赖幂等键、状态机或人工确认。
| 策略 | 适合场景 | 必须记录 | 常见坑 |
| same-model retry | 短暂网络 / 服务 5xx | retry_count、backoff_ms、same_prompt_hash | 连续重试造成请求风暴 |
| context-shrink retry | prefill 超时、窗口过长 | dropped_segments、new_token_budget | 静默丢证据后输出看似正常 |
| fallback model | 模型池拥堵、供应商故障、成本降级 | fallback_reason、from_model、to_model | 质量和格式不一致,没有回放证据 |
| repair retry | JSON 解析失败、schema 校验失败 | parse_error、repair_prompt_version | 修复过程中改变语义或丢字段 |
| resume / continue | length 截断、用户要求继续 | previous_output_hash、continuation_point | 续写重复、引用错位、上下文漂移 |
| 字段 | 记录什么 | 用于排查什么 |
| request_id / generation_id | 一次产品请求和一次模型生成尝试的关系 | 多次重试、Fallback 和工具循环的血缘 |
| stage_timing | gateway、context、queue、prefill、first_token、decode、postprocess | TTFT、总耗时、尾延迟根因 |
| stream_events | 事件类型、顺序、大小、结束原因 | 断流、重复、前端展示错位 |
| finish_reason | stop、length、tool_call、filter、cancelled、timeout、error | 不同失败形态的产品处理和样本回流 |
| retry_lineage | 重试次数、原因、退避、是否改变输入、前一次 id | 重复成本、重试风暴、不可复现坏例 |
| usage | input tokens、output tokens、cache hit、billable units | 成本归因、取消计费、预算策略 |
| runtime_snapshot | model pool、engine version、route、sampling、schema、stop rules | 版本漂移、线上回归、灰度差异 |
| partial_output | 失败或取消前的脱敏片段 hash / 摘要 | 判断是否可恢复、是否需要继续生成 |
| 事件节点 | 必须记录的字段 | 判断重点 |
| stream_start | generation_id、trace_id、client_id、model_route、request_deadline | 前端看到的这一条流,对应哪一次模型生成和哪条路由? |
| first_token | ttft、queue_time、prefill_time、input_tokens、cache_hit | 首 token 慢是排队、上下文过长、缓存未命中还是模型池拥堵? |
| delta_emit | event_seq、delta_size、decode_step、server_time、client_ack_state | 是否有乱序、重复、断流、前端未消费或服务端继续生成的问题? |
| retry_or_fallback | attempt、retry_reason、previous_generation_id、changed_input、fallback_model | 重试有没有改变上下文或模型,坏例是否还能复现? |
| cancel_or_timeout | cancel_source、cancel_time、tokens_generated、tokens_delivered、resource_released | 用户取消后资源是否释放,账单和部分输出如何处理? |
| stream_end | finish_reason、stop_rule_id、output_hash、usage、postprocess_result | 结束是正常完成、长度截断、过滤、工具调用、取消还是错误? |
首 token 慢但总耗时正常
多半是 queue 或 prefill 问题,先看输入 token、上下文装配、模型池水位和 Prompt Cache。
流式输出忽快忽慢
可能是 decode 调度、batch 混跑、网络缓冲或后处理阻塞。不要只看最终耗时。
用户取消后成本还在涨
cancel signal 没有传到模型服务,或者工具执行不可中止。需要检查资源释放和计费策略。
重试后答案变了
可能是采样随机性、Fallback 模型、上下文压缩或模板版本变化。需要 retry_lineage 和 prompt hash。
JSON 最后才坏
流式过程中 JSON 未闭合,前端或后端提前解析;应等 message_end 或使用结构化增量协议。
超时重试变成雪崩
没有退避、没有最大次数、没有幂等键,失败高峰期会把模型池打得更满。
| 相邻页 | 它回答什么 | 本页补什么 |
| 一次请求的一生 | 请求从用户输入到运营闭环的全链路 | 把“模型生成”阶段拆成运行时状态机、流式事件和收尾协议 |
| 上下文组装 / 窗口预算 | 模型真正看到哪些上下文 | 说明上下文提交后怎样影响 queue、prefill、TTFT 和重试 |
| 推理计算 / KV Cache | Prefill、Decode、KV Cache 的底层计算成本 | 把底层成本映射到请求状态、流式体验和 Trace 字段 |
| 解码 / 采样 / 结构化输出 | token 怎样被采样、停止和约束成结构化输出 | 补充流式 JSON、finish_reason、repair retry 和最终解析收尾 |
| 模型服务 / 推理网关 | 推理服务、网关、SLA、限流和降级 | 细化单次 generation 在网关和模型池里的状态证据 |
| 模型路由 / Fallback | 请求该走哪个模型,失败时怎么降级 | 补充 Fallback 进入生成运行时后的 retry_lineage 和质量差异记录 |
| LLMOps / 可观测 | Trace、Eval、Release 和线上指标 | 提供 generation runtime 必备字段,用于慢、错、贵和断流复盘 |