十二、工程化与部署运维
这一章检验 LLM 应用有没有真正"上过量"。架构、成本、延迟、监控、发布,每一题都对应一次真实的线上考验,面试官想听的是带数字、带取舍的实战经验。
119. 画一个高并发 LLM 应用的架构:网关、限流、队列、模型服务、缓存分别放在哪?怎么扩容?
必刷·中等
参考答案
文字版架构,从外到内分层描述:
- 接入层:WAF / CDN → 负载均衡 → API 网关。职责:认证、租户识别、请求校验、路由、SSE 流式透传(反代必须关缓冲)。无状态,直接水平扩。
- 限流层(挂在网关):多维度令牌桶/滑动窗口——用户级 QPS + 并发槽位数(LLM 请求是长连接慢请求,只限 QPS 不够,必须限制"同时在生成的请求数");租户级配额;全局兜底限流。限流状态放 Redis,网关实例无状态。
- 预处理层:输入内容安全初审、Prompt 组装、缓存查询。
- 缓存层:精确缓存(请求前缀/全文哈希 → Redis)在前,语义缓存(向量相似度命中)在后,命中直接返回,省掉整个下游。
- 队列层(模型服务之前):核心削峰组件。LLM 请求秒级到几十秒延迟,突发流量超过推理能力时排队而不是打死服务;按优先级分队列(付费/免费、交互/后台任务分流),配合等待时长估算和超时礼貌拒绝。
- 模型服务层:两条路线——自建推理集群(vLLM 等引擎 + K8s GPU 节点,实例内部用 Continuous Batching 做调度)或外部 API(经模型网关管理上游配额与多厂商容灾,见 Q124)。长请求和短请求分池部署,避免一个长文分析堵死一批快问答(队头阻塞)。
- 旁路与异步:输出内容审核可异步复核;日志、trace、计费入消息队列异步消费,不阻塞主链路。
扩容策略:
- 无状态层(网关、预处理、缓存)常规水平扩;
- 模型服务是真正瓶颈:自建路线先压测出单实例吞吐(见 Q123),按峰值并发 / 单实例并发槽数算实例数,再加 20~30% 冗余;GPU 扩容要分钟级,必须按流量时间曲线提前扩,缩容要慢(防抖动),扩缩信号用队列长度和排队时间而非 CPU;
- 外部 API 路线提前申请配额,配多厂商备份;
- 降级链路:高峰先限流新请求保在途请求 → 再降输出上限、关重功能 → 路由到小模型或缓存兜底。
一个常见误区:把 CRUD 时代的架构照搬过来。LLM 请求耗时长、GPU 资源贵、流量不均匀,这三点决定了并发槽管理 + 队列削峰 + 提前扩容是整套架构的中心,而不是数据库和连接池。
面试官可能追问
- SSE 流式场景下网关和链路要额外注意什么?(关缓冲、读超时大于最长生成时间、断连时要向上游取消请求止损 Token)
- 用户请求在队列里排了 30 秒,体验怎么保?(返回排队位置与预估等待时间,超时给重试建议或降级到缓存/小模型)
- 怎么判断该自建还是调 API?(按 QPS × 平均 Token 数估月成本对比 GPU TCO,叠加合规约束,参考第一章模型选型题)
120. LLM API 的限流、重试、降级策略怎么设计?指数退避和抖动了解吗?
高频·中等
参考答案
限流:客户端要维护并发槽(信号量)+ QPS 双重限制;对 429 响应读 Retry-After 头做服务端指示的退避;多 Key 轮转池可平滑单 Key 限额,但要看厂商条款是否允许。
重试:只重试瞬时且幂等的错误——429、5xx、网络超时;不盲重:400(参数错、超上下文,重试无用)、内容审核拒绝(重试还是拒)、流式已输出一半的失败(要考虑幂等和体验)。重试要设上限(2~4 次)和总预算,防止重试风暴放大负载。
指数退避 + 抖动:
- 退避间隔按指数增长:1s → 2s → 4s → 8s,设上限(如 30~60s),给上游恢复留时间;
- 抖动(jitter)必须加:一批请求同时失败后,若都按固定间隔重试,会在同一时刻集体再撞一次,形成周期性尖峰反复冲击上游(惊群),可能把一次抖动拖成雪崩;抖动给每个客户端的重试时间加随机量,把尖峰摊平;
- 典型实现:
sleep = min(cap, base × 2^n) × random(0.5, 1.5); - 配合熔断器:连续失败超阈值就熔断,流量走降级,冷却后半开试探恢复。
降级:主模型不可用 → 备用模型(提前做过 Prompt 兼容性验证,见第十二章迁移题)→ 再降为缓存相似问答、模板回复、排队稍后通知;核心业务常备一条低延迟小模型通道。对非核心业务,直接拒绝新请求保护在途请求,也是一种降级。
落地建议:用成熟的库或网关能力集中配置重试与熔断策略,别散落在各业务代码里各自为政。
面试官可能追问
- 为什么抖动是必需项而不是优化项?(同步重试造成周期性负载尖峰,是级联故障的经典成因)
- 流式请求中断在半路,重试会怎样?(已消耗的 Token 不退,重试等于双倍计费——要么容忍重来,要么记录半截结果让用户选择继续)
121. 怎么降低 Token 成本?说说精确缓存 / 语义缓存、Prompt 压缩、小模型路由等手段。
高频·中等
参考答案
按投入产出比排序:
- Prompt 瘦身(先做):审计 system prompt 和 few-shot,删冗余示例;system prompt 从 3000 Token 瘦到 1200,所有请求直接受益,零风险;
- 精确缓存:分两类——厂商 Prompt Caching / Context Caching:固定前缀(system prompt、长文档、few-shot)命中后输入按缓存价计费,约为正常输入价的十分之一量级(各厂商不同),要求前缀完全一致且稳定,所以固定内容放前、变量放后;自建精确缓存:请求完全相同直接返回缓存结果,适合模板类、重复请求;
- 语义缓存:query 向量化后与历史问答比对,相似度超阈值(经验值 0.95 左右)即返回缓存,命中省整次调用。风险是误命中("怎么取消订单"和"取消不了订单"向量很近但答案相反),只用于 FAQ、闲聊等容错场景,命中率做到 20~40% 已很可观;
- 小模型路由:入口先分类或规则判断,简单请求(格式化、常规问答)走便宜的小模型,难的升级旗舰。多数业务 60~80% 流量可被小模型接住,综合成本能降一半以上;路由层本身要便宜且快,误路由的代价是质量事故;
- Prompt 压缩:历史对话滚动摘要、检索片段先 rerank 再砍 top-k、用 LLMLingua 类工具压缩冗余 Token。代价:压缩本身有一次额外调用,且可能丢关键信息——上下文很长(如超 8K)才划算,短上下文反而负优化;
- 输出控制:限制回答长度、结构化输出去掉开场白和总结段、用 stop 序列提前截断;
- 批处理:离线任务走 Batch API,约五折换小时级延迟。
落地节奏:先做 1+2(无脑赚),再上 3+4(要设计),最后抠 5+6;同时建成本看板(单请求平均成本、分模型分场景),否则优化无法度量。
面试官可能追问
- Prompt 缓存为什么经常不命中?(前缀里混入了时间戳、用户名等变量,任何一字之差都从头算——审计前缀模板的稳定性)
- 语义缓存的阈值怎么定?(用标注的相似问对跑 ROC,按业务误伤容忍度选,宁可少命中不可错返回)
122. 大模型的响应延迟怎么优化?首 Token 延迟(TTFT)和整体吞吐分别怎么压?
中频·中等
参考答案
先分清指标:用户体感主要看 TTFT(首字出现即感知"开始回答了")和单请求总时延;系统容量看吞吐(tokens/s、QPS)。两者优化路径不同甚至冲突——小 batch 延迟低、大 batch 吞吐高,要按场景取舍。
TTFT 优化:
- 压缩输入:prefill 计算量与输入长度成正比,砍历史、砍检索片段、瘦身 system prompt 是第一杠杆;
- Prompt 缓存:固定前缀跳过重复 prefill,TTFT 显著下降;
- 流式输出:不加速生成本身,但把体感延迟从总时长变成 TTFT,感知收益最大;
- 模型与部署:换小模型、量化版本;自部署调 chunked prefill 防长请求饿死短请求;选就近 region、独享通道降网络 RTT;
- 投机解码:小模型起草、大模型并行验证,decode 阶段加速 2~3 倍(需自建部署,见第九章推理题)。
吞吐优化:
- Continuous Batching:逐步动态插入/移出请求,消除静态批的补齐浪费,吞吐提升数倍是常态;
- PagedAttention:KV Cache 分页管理,减少碎片、提高并发 batch;
- 量化(INT8/INT4):decode 是访存瓶颈,权重变小直接提速;
- 长短请求分池、多实例负载均衡、超大模型张量并行;
- 排队治理:排队时间常占总延迟大头,扩容和优先级调度比调模型更有效。
方法论:先把 P50/P95 延迟分段拆解(网络 → 审核 → 检索 → 排队 → prefill → decode),找到大头再对症下药;排队占 60% 时去调模型参数是无效优化。
面试官可能追问
- 为什么 decode 阶段快不起来?(每步只出一个 Token、反复读权重和 KV Cache,算力吃不饱;靠 batching 摊薄访存)
- TTFT 和吞吐都要时怎么权衡?(分场景:聊天类小 batch 保延迟,后台批任务大 batch 保吞吐,用队列优先级隔离两类流量)
123. 私有化部署一个开源模型,从选型、量化、压测到上线要做哪些事?
中频·中等
参考答案
按四个阶段推进:
选型:
- 按任务难度定尺寸:抽取、分类、常规问答 7B~14B 级够用,复杂推理 32B~70B 级起步;
- 候选模型(Qwen、DeepSeek、LLaMA 系等)用自己的评估集跑分(100~300 条业务真实样本 + bad case),不信公开榜单——榜单与业务表现相关性有限;
- 确认 license 允许商用。
量化与部署:
- 显存估算:FP16 权重约等于参数量 × 2 GB,再加 KV Cache 和激活值;7B FP16 约 14~16GB 显存,单张 24GB 卡可跑;70B 级量化后也要多卡;
- 量化取舍:INT8(AWQ/GPTQ)近乎无损;INT4 更省但复杂推理任务会有可感知下降——量化后必须重跑评估集验证,不能拍脑袋;
- 推理引擎按生态和吞吐需求选 vLLM / SGLang / TensorRT-LLM,确认支持 continuous batching 和长上下文。
压测(最容易偷懒的一步):
- 用真实流量分布压:输入输出长度、突发模式都要像线上,而不是等长的合成请求;
- 核心指标:单实例并发槽数、TTFT、每 Token 间隔(TPOT)、tokens/s、P95 延迟、显存峰值、失败率;
- 专门压长输入长输出的边界场景——KV Cache 随长度膨胀,最容易在这里 OOM;
- 由单实例容量推实例数(峰值流量 + 冗余),扩容信号选排队长度。
上线:
- K8s + GPU Operator 编排,健康检查、监控告警(Q125)先行;
- 发布走评估集回归 + 小流量灰度(Q126),模型文件版本化、可一键回滚;
- 合规侧:对外的生成服务完成内容审核与备案义务。
常见坑:只测单请求效果不测并发吞吐;上线后没有回滚预案;长上下文场景没压 KV Cache 峰值导致高峰 OOM。
面试官可能追问
- INT4 量化精度掉多少?(任务相关:分类抽取几乎无损,复杂推理可能掉几个点,必须以自己评估集实测为准)
- 单机多卡和多机多卡怎么选?(优先单机多卡(张量并行),跨机通信延迟和运维复杂度高,只在单机放不下时用多机)
124. 统一接入多家模型的模型网关怎么设计?路由、故障转移、配额、密钥管理怎么做?
中频·较难
参考答案
定位:模型网关是业务方访问所有模型的统一出口,屏蔽厂商差异,集中管路由、成本和安全四件事。
统一接入层:
- 对内暴露一套规范 API(事实标准是 OpenAI 风格的 chat/completions 接口),内部适配各厂商的参数名、流式格式、错误码;
- 横切能力全部下沉到网关:重试与熔断(Q120)、Token 计量、trace 日志(Q118)、敏感数据扫描(Q114)、Prompt 缓存——业务方零感知。
路由引擎:
- 业务侧用抽象模型名(如
chat-fast/chat-smart),网关映射到具体厂商模型,升级换供应商不动业务代码; - 路由维度:场景路由(分类走小模型、难任务走旗舰)、成本路由(效果达标的候选里选单价低的)、合规路由(含敏感数据的请求只路由到备案通过的通道)、灰度路由(新模型小流量试用);
- 规则放配置中心,支持热更新,每次路由决策落日志可审计。
故障转移:
- 主动健康探测 + 错误率/延迟熔断:单通道异常自动切备用;
- 分级降级:同模型多 Key → 同级备模型 → 低级模型 → 模板兜底;
- 关键约束:主备模型要预先做兼容性验证——换模型不是零成本,Prompt 行为差异可能导致质量下降,备选对要在评估集上过回归,明确已知的降级损失。
配额与成本:
- 按租户/应用/模型维度限 QPS、并发、日 Token 上限,超限拒绝或降级;
- 实时 Token 计量(请求前估算、响应后按 usage 校准),成本分摊到租户,预算告警;
- Key 池化:多 Key 轮转平滑单 Key 限流,Key 级健康状态管理。
密钥管理:
- 密钥全放 KMS 或 Vault 类密钥管理服务,配置和代码里无明文;
- 网关是唯一持钥方,业务方用内部签发的 token 调网关;Key 轮换对业务无感;所有密钥访问留审计日志。
可以自研,也可以基于开源网关(LiteLLM、One API 等)二次开发;多模型是长期状态的话,这层值得第一天就建。
面试官可能追问
- 成本路由怎么验证"效果达标"?(定期用评估集切片给候选模型打分,结合报价动态调整路由权重)
- 流式请求怎么做故障转移?(只在建连阶段切换;输出到一半出错,切换意味着用户看到断稿重来,一般选择重生成并告知,而不是无缝切换)
125. LLM 应用的监控告警看哪些指标?(延迟、错误率、Token 用量、拒绝率、人工接管率等)
中频·中等
参考答案
指标按"系统 → 质量 → 成本 → 用户"四类组织:
系统健康:
- 延迟:TTFT、总时延的 P50/P95/P99,且分段统计(排队、检索、prefill、decode、工具调用),否则定位不了慢在哪;
- 错误率:分类统计——API 错误、超时、审核拦截、格式校验失败、工具执行失败,各自含义不同;
- 饱和度:队列长度、并发槽占用率、自建侧的 GPU 利用率与显存。
质量健康(LLM 应用特有,也是最容易漏的):
- 拒绝率:回答"不知道/无法回答"的占比,突增通常意味着知识库覆盖或检索出了问题;
- 人工接管率:客服等场景的核心逆指标,直接反映 AI 解决率;
- 审核拦截率:突增可能是注入攻击或被用户找到绕过姿势;
- 缓存命中率与路由分布:异常波动关联成本和效果;
- 抽样质量分:LLM as Judge 自动打分 + 人工抽检校准的离线/准在线指标。
成本:
- 单请求 Token 消耗(输入/输出分开)、单请求成本、日总成本、分模型分场景拆分;
- Prompt 长度分布:均值持续上涨多半是历史累积或检索注入失控。
用户侧:
- 点踩率、投诉量、用户对答案的编辑幅度、会话完成率、转人工前轮次。
告警设计要点:
- 绝对值 + 变化率组合(质量指标绝对值意义有限,环比突增 2 倍才是信号);
- 排队时间单独告警——这是最领先的扩容信号,早于延迟恶化;
- 成本预算告警(日额度 80% 预警);
- 多指标共振才升级页面:接管率、投诉率、拒绝率同时涨,大概率是真实质量事故。
工具:指标和 trace 用 OpenTelemetry 标准接入,LLM 专项视图用 Langfuse / LangSmith 类平台,重点看每次调用的 prompt、成本和链路。
面试官可能追问
- 没有标注数据怎么在线监控质量?(用代理指标:用户反馈、接管率、编辑率,再加周期性抽样跑 LLM 评估)
- 哪个指标最先反映"模型变差"?(投诉率和接管率——用户用脚投票的指标最诚实)
126. 灰度发布和新模型上线流程怎么做?怎么快速发现"新模型反而变差了"?
中频·较难
参考答案
完整流程分四段:上线前验证 → 流量灰度 → 快速劣化检测 → 可回滚。
上线前(离线):
- 评估集回归:新模型跑全量业务评估集(含近期线上 bad case),对比旧模型——重点看分场景分数而非只看总分,总分持平但某场景掉 5 个点很常见也更危险;
- Prompt 兼容性:新模型对格式指令、system prompt 的遵循习惯不同,需要适配的先改 Prompt 并在评估集上验证(模型 + Prompt 绑定为一个发布单元,否则没法归因);
- 边界与安全测试:安全对齐、注入抗性、超长输入、格式稳定性压测;
- 成本与延迟重估:分词器不同导致 Token 消耗变化,输出风格影响长度和延迟。
灰度放量:
- 按 1% → 5% → 25% → 50% → 100% 阶梯放量,每档观察至少覆盖一个完整时段(不同时段用户群不同);
- 按用户哈希分流而非按请求随机——同一用户始终在同一组,保证体验一致、指标不被污染;
- 设对照组做同时段对比,抵消时间因素。
快速发现"变差"(核心):
- 用户代理指标:投诉率、点踩率、人工接管率、答案编辑幅度、会话完成率;
- 系统代理指标:格式校验失败率(新模型不守旧格式的第一信号)、输出长度分布漂移、安全拦截率、重试率;
- 自动护栏:预设阈值(如投诉率相对对照组 +50%),触发即自动暂停放量甚至回滚,不等人工发现;
- 灰度期间两组输出持续抽样,用 LLM as Judge 盲评对比,显著劣化即告警。
回滚:
- 模型路由配置版本化,一键切回;Prompt 若同步改过则一起回滚;
- 保留灰度期数据做事后归因,沉淀进评估集。
关键心态:新模型上线是假设检验,不是升级——榜单提升不等于业务指标提升,只有灰度数据说了算。
面试官可能追问
- 灰度期太短,质量问题滞后暴露怎么办?(加影子流量:新模型并行处理线上请求但不返回给用户,只用于离线对比,多花一份成本换安全)
- 灰度期间用户反馈能定位到组吗?(会话和分组标记全链路透传,反馈天然带组别属性,方便归因)
127. 长时间运行的 Agent 任务怎么做持久化和断点恢复?
低频·较难
参考答案
核心问题:Agent 任务跑几十分钟、上百步,中途崩溃(服务重启、上游超时、OOM)后必须能从断点续跑而不是从头再来——既是成本问题(Token 已花掉),也是体验问题(让用户再等 20 分钟不可接受)。
持久化什么(checkpoint 内容):
- 完整状态而非消息列表:消息历史只是之一,还要有执行计划与当前进度、中间产物(摘要、检索结果、生成的文件引用)、工具调用记录、重试计数;
- 状态设计成显式数据结构(可序列化的 dict / ORM 对象),而不是散落在各步变量里——这是能做 checkpoint 的前提;
- 幂等标识:每步(每次工具调用)有唯一步骤 ID 和幂等键,恢复时先查"这步是否已执行、结果是什么"。
何时持久化:
- 每步执行后落一次(一次 LLM 调用、一次工具执行各算一步);粒度权衡:太细写入开销大,太粗故障后重放代价高;
- 非幂等操作的顺序:先写意图日志,执行成功后更新状态(写前日志思路)——防止"执行了但没记录"(崩溃后重复执行,付款付两次)或"记录了但没执行"(任务漏步骤)。
怎么恢复:
- 加载最近 checkpoint → 重建上下文(历史消息塞回 prompt,过长则摘要 + 近期轮次)→ 从未完成步骤继续;
- 工具幂等分级:查询类直接重跑;副作用类先查下游实况(那笔付款是否已创建)再决定跳过或补偿,绝不盲目重试;
- 单步失败独立重试与降级,超过阈值转人工处理,不拖垮整个任务。
工程落地:LangGraph 的 checkpointer 提供开箱的 state 快照与线程恢复;自建可用 Redis(热状态)+ 关系库(持久)双层,天级长任务直接落库。另一个关键设计:计算与状态分离——推理实例无状态、随时可杀可扩,任务状态全在外部存储,这是长任务 Agent 能上 K8s 弹性调度的前提。
面试官可能追问
- checkpoint 损坏或版本不兼容怎么恢复?(快照带版本号,加载失败回退上一个快照重放,最坏标记任务失败并保留产物转人工)
- 几百步之后上下文远超窗口怎么办?(分层摘要 + 中间产物外置存储按 ID 引用,需要时再取回,参考第七章上下文管理)
128. Serverless GPU 和常驻服务怎么选?冷启动问题怎么缓解?
低频·中等
参考答案
选型逻辑是利用率 × 延迟敏感度的矩阵:
| 维度 | Serverless GPU | 常驻服务 |
|---|---|---|
| 流量形态 | 突发、间歇、日均利用率低 | 持续高负载、平稳 |
| 延迟要求 | 能容忍秒到几十秒冷启动 | TTFT 要求严格 |
| 成本模型 | 按用量计费,零闲置成本 | 固定支出,打满才划算 |
| 运维负担 | 低,免集群管理 | 高,容量规划和监控全自理 |
经验判断:日均利用率低于约两三成、流量毛刺大 → Serverless;平稳高负载 → 常驻(盈亏平衡点随厂商定价浮动,要按自己的流量曲线算总账,Serverless 单价常更高)。混合形态最常见:常驻池保底 + Serverless 弹性扛峰值;测试环境天然适合 Serverless。另一个关键差异:Serverless 通常绑定厂商运行时,推理引擎、量化方式、定制算子的自由度远低于自管集群。
冷启动缓解(大模型冷启动 = 拉镜像 + 加载权重 + 预热,可达几十秒到分钟级,远慢于 CPU 服务):
- 保活:用厂商的预留并发/保活实例,或定时心跳流量防止缩到零——但保活实例就是钱,本质变成半常驻,要按流量曲线算保活几个最划算;
- 缩小冷启动单元:模型加载占大头——选小模型、量化权重(文件小加载快);镜像预拉取到节点、权重缓存到节点本地盘,减少拉取时间;
- 预热调度:按时间曲线提前扩容(早高峰前预热),缩容设长冷却期(流量归零后等 10~20 分钟再释放);
- 请求侧兜底:冷请求路由到常驻池的双层架构,或排队 + 明确的等待反馈;
- 提高单实例复用:实例起来后用 continuous batching 多槽位承接,减少再次触发扩容。
同理适用于常驻服务的自动扩缩:GPU 实例扩容本身就要分钟级(加载权重),提前扩、延迟缩是通用原则。
面试官可能追问
- 具体怎么算划不划算?(用真实流量曲线分别模拟两种模式的月成本:常驻 = 实例数 × 单价,Serverless = 调用量 × 单价 + 保活费,交叉点即决策点)
- 为什么 GPU 冷启动远慢于 CPU?(多 GB 权重加载、CUDA 初始化、推理引擎预热编译,不只是起进程的时间)
