[控场AI]
· 7 分钟阅读· 3,865 字

一个LLM请求的九步解剖:从点击到响应的全链路

一个LLM请求的九步解剖:从点击到响应的全链路

LLM请求九步中仅两步属模型,七步是工程契约,验证与预算才是成败关键。

一位资深前端工程师系统拆解了LLM应用的完整请求链路,将其分为九个步骤,并指出只有prefill和decode两步属于模型内部,其余七步——客户端转发、入参验证、提示词拼接、token预算、流式传输、输出验证和重试日志——全是工程契约。文章强调API密钥必须留在服务端,Pydantic验证是最便宜的止损点,模型API默认无状态导致历史token重复计费,流式输出中错误可能藏在200状态码之后,429可能代表限流也可能代表配额耗尽。核心心法是:把模型当作不可靠、按量计费的远程依赖,在它两侧都放上验证。

大多数开发者第一次接入LLM时,只关心一件事:调API、拿结果。但一个前端工程师在系统梳理LLM应用请求链路后发现,真正决定成败的往往不是模型本身——九个步骤里只有两步属于模型,其余七步全是工程契约。

LLM应用请求的九步流程图

为什么要拆解一个LLM请求

作者有8年以上React/TypeScript前端经验,正在公开学习应用AI,目标是成为AI工程师乃至AI前向部署工程师(FDE)。在深入token、RAG、agent之前,他先画了一张地图:当用户点击"Summarize"按钮时,系统里到底发生了什么。

这个视角切换很重要。对前端工程师来说,LLM功能看起来只是"把文本发给后端、等结果回来",但中间隐藏着验证、预算、流式传输、错误处理等一系列工程决策。把这些拆开看,能帮助团队在接入AI时少踩坑。

调用前:步骤1-4

客户端发请求,密钥必须留在服务端

从浏览器视角看,LLM功能就是一次普通的HTTP调用:POST /summarize,带上用户文本,聊天场景再加会话id。OpenAI官方文档明确要求绝不能在客户端代码中暴露API密钥,所以密钥放在服务器上,只有服务器与模型提供商通信。

各组件运行位置:浏览器→FastAPI后端→模型提供商

入参验证是最便宜的止损点

FastAPI用Pydantic模型校验请求体。数据不合法根本到不了业务处理函数:FastAPI抛出RequestValidationError,默认处理器返回HTTP 422。一旦验证通过,Pydantic保证字段类型符合声明。这是拦截坏输入成本最低的位置——此时还没有消耗任何token。

提示词拼接:无状态是默认设定

模型只能看到你发过去的内容。Anthropic的Messages API是无状态的,每次都要重发完整历史;OpenAI同样将每个请求视为独立无状态,虽然提供可选的会话状态管理,但明确指出链条中较早的输入token在每一轮仍然计费。

每个请求独立:三轮请求重复发送系统提示和完整历史

token计数与预算检查

模型读的是token而非字符,且分词器与模型绑定,不同模型的计数结果不同。系统提示、工具定义、历史记录和输出共享同一个上下文窗口。如果仅输入就超限,请求直接失败(Anthropic返回400,提示"prompt is too long")。发送前估算token数量,并设置最大输出长度,是控制成本的基本功。

token与上下文窗口的关系

模型内部:步骤5-6

Prefill:并行读入全部输入

推理分两个阶段。Prefill阶段模型并行处理所有输入token,计算密集,是模型侧决定首token延迟(TTFT)的主要因素。网络传输和提供商排队时间会叠加在这段停顿上。

Prefill与Decode时间线:从请求发出到最后一个token

KV缓存(Key-Value Cache)是理解Decode阶段性能的关键机制。在Transformer的自注意力计算中,每个token都会生成对应的Key和Value向量,而这些向量在后续生成新token时需要被反复使用。如果每一步都重新计算所有历史token的K和V,计算量会随序列长度呈平方级增长。KV缓存的做法是把已计算的K和V矩阵保存在显存中,生成新token时只计算当前token的K和V并追加到缓存,从而使每步解码的计算量近似恒定。代价是显存占用随上下文长度线性增长,这也是长上下文请求成本更高的原因之一。Prefill阶段正是填充这份缓存的时刻,因此它计算密集;Decode阶段则因缓存的存在而变得相对轻量,但受限于显存带宽。

Decode:逐token自回归生成

随后模型自回归生成:对词表中每个token打分、选一个、追加、重复。KV缓存保留先前计算,避免每步从头开始,每步的节奏就是每个输出token的耗时。遇到结束符、停止序列或token上限时停止。

下一个token如何选出:玩具概率分布

至于"怎么选",属于采样设置:贪心,或带temperature、top-k、top-p的采样。别假设这些参数在所有模型上都可用——Anthropic文档说明Claude 4.7及以后版本,若将这些参数设为非默认值会返回400。

SSE(Server-Sent Events)是一种基于HTTP的服务器推送技术,允许服务器在单个连接上持续向客户端发送文本事件流。与WebSocket不同,SSE是单向的(服务器到客户端),使用普通HTTP协议,天然兼容现有基础设施和代理。在LLM场景中,提供商将每个生成的token封装为一个data:事件逐步推送,客户端通过EventSource或流式fetch读取。文章提到的两个坑正源于SSE的协议特性:一旦响应头以200发出,后续错误只能作为流内事件传递,HTTP状态码已无法反映;同时,流式输出意味着用户和审核系统在生成完成前就看到了部分内容,给内容安全审核带来时序上的挑战。

调用后:步骤7-9

流式返回:错误可能藏在200之后

OpenAI和Anthropic都在解码过程中以SSE流式推送token,所以步骤6和7是重叠的。文档里有两个容易忽略的坑:错误可能在流中途到达,此时状态码已经是200;以及部分输出比完整响应更难审核。

SSE流式传输时序图

输出也要验证

结构化输出能约束模型遵循你提供的JSON Schema(OpenAI支持其中一部分),但其指南也承认仍可能出错,并列出两种必须处理的情况:拒绝响应和因token上限被截断的响应。如果采用流式,要等流结束后再验证。FastAPI的response_model是第二道检查:类型不匹配会变成服务器错误,而不是返回错误数据。但要记住,格式合法不等于答案正确——正确性评估是后续话题。

验证模型输出:拒绝、截断、解析三步检查

重试要分情况,日志要记全

同样的状态码含义可能不同:429通常是限流,但Anthropic在你触达月度支出上限时也会返回429,所以重试前先读错误体。配额或计费错误绝不重试。对于瞬时错误,有Retry-After就遵守,否则用带抖动的指数退避,并限制重试次数和总时长。官方SDK本身已会重试部分错误(Anthropic默认两次),设定自己的重试上限时要把它算进去,而不是层层嵌套循环。

最后,记下输入/输出token用量、延迟和提供商的request id。这些记录支撑调试、成本追踪,最终也支撑评估体系。

重试决策树:限流可重试,配额计费不重试

指数退避(Exponential Backoff)与抖动(Jitter)是分布式系统中处理瞬时故障的标准策略。基本思路是每次重试的等待时间按指数增长(如1秒、2秒、4秒、8秒),避免在服务端过载时持续高频冲击。但纯指数退避有一个隐患:多个客户端可能在同一时刻发起重试,形成“惊群”效应,再次压垮服务。抖动通过在退避时间上叠加随机量来打散重试时刻,常见形式有全抖动(在0到计算值之间随机)和等抖动(在计算值的一半到全值之间随机)。结合Retry-After响应头使用时,应优先遵循服务端给出的等待建议,仅在缺失时回退到自己的退避策略,并始终设置最大重试次数和总超时,防止无限重试拖垮请求链路。

核心心法:把模型当作不可靠的、计费的远程依赖

作为前端工程师,作者觉得这套流程意外地熟悉:这就是一个请求生命周期,只是中间换了个不寻常的后端。九个步骤中只有两步属于模型,其余七步是契约、预算、流和失败处理——这些恰恰是工程师能掌控的部分。

他的工作准则由此而来:把模型当作一个不可靠、按量计费的远程依赖,在它的两侧都放上验证。

这个判断对正在接入LLM的团队有现实意义。许多项目把精力集中在提示词调优上,却在入参验证、token预算、流式错误处理和重试策略上栽跟头。模型能力会持续进化,但请求链路的工程约束不会消失。

作者的学习路线图将这一篇归入基础模块:Python类型与异步、FastAPI与Pydantic、LLM机制(token、上下文、推理)。下一篇将聚焦"面向AI后端的Python:TypeScript工程师的复习课"。他也向读者抛出一个问题:你构建的第一个LLM功能,这九步里最先崩的是哪一步?

#LLM应用请求流程#LLM推理原理#token与上下文窗口#prefill与decode#SSE流式输出#FastAPI Pydantic验证#LLM错误重试策略#AI工程师入门
分享:

相关推荐