← 全部极客知识库

AI 面试 / 150 道 AI 面试题

11. 工程实现细节(生产可靠性)

原文备份 · 第 12 / 22 篇 · 查看极客时间原知识库 ↗

本篇目录 · 16 节

来源:150 道 AI 面试题

题量最大类之一。不考概念,考"真刀真枪落地怎么处理":流式、重试、降级、幂等、成本、可观测。每道都有明确的工程正解和"没上过生产"的错误答案。

119. LLM 调用该怎么重试?哪些状态码能重试、哪些绝不能?

直接答案:联合 HTTP 状态、供应商 error.code/type 和请求语义判断暂态故障,不能只背状态码。

  • 429:可能是速率限制,也可能是余额/配额耗尽——前者可按 Retry-After 退避,后者应停止并提示补充额度或更改配置。
  • 5xx、连接中断、超时:只能在总预算内有限重试,带副作用时先查状态。
  • 400 参数错误、401/403:通常需修正请求或授权,不原样循环重发。

知识点讲解:

  • "400 是你在给 bug 付钱":400 表示请求本身有问题,重试一万次也一样,正确做法是修请求而非重发。
  • 429 的两种语义必须区分:限流(可退避重试)vs 额度耗尽(重试无益)——这是"只背状态码"会踩的坑。
  • 退避三要素:指数退避 + jitter(抖动,防惊群)+ 最大次数 + 总截止时间。
  • jitter 的意义:批量任务同时失败同时重试,会形成"自残式 DDoS",jitter 把重试时间打散。
  • 记录最后错误和已消耗预算:便于诊断和防止无限重试。

参考:OpenAI Error Codes。


120. 为什么要把"模型调用"和"副作用"分离?幂等键怎么用?

直接答案:纯生成通常不直接造成重复业务写入,但重复模型调用可能产生不同文本、重复计费,不能据此称为幂等。

把模型的建议与实际付款、发邮件、创建工单分开。业务执行前保存唯一幂等键和 pending 状态,由服务端对相同键复用结果;超时后先查询状态,避免重复执行。

知识点讲解:

  • "超时不等于失败":一次 LLM 调用超时了,但服务端可能已完成(比如"生成邮件并发送")。盲目重试会导致发两次邮件、扣两次款。
  • 幂等键的正确用法:调用前写意图(幂等键 + pending)→ 执行 → 更新结果;重试时先查键,已存在则复用结果。
  • temperature=0 不能推导幂等:供应商是否支持请求幂等,需要核对具体 API,不是"确定输出"就能保证不重复执行副作用。
  • 三类故障要分别演练:生成失败、工具失败、结果返回丢失是不同故障,处理方式不同(返回丢失最隐蔽,因为"实际成功了但客户端没收到")。
  • 架构原则:模型只负责"建议动作",业务层负责"幂等执行"——这是可信边界的关键。

121. 怎么防止"单个 bug 把账单烧穿"?成本熔断器该在哪一层、按什么指标触发?

直接答案:成本熔断器防止"单个 bug 烧穿账单"。机制:

  1. 记录每笔调用花费(token、模型、成本)。
  2. 设硬配额(按用户/天)。
  3. 达到预算 90% 触发 kill switch 或降级。

关键:熔断器从外部切入调用链,是横切保护,不是业务代码"记得省"。

知识点讲解:

  • "cost circuit breaker 不是优化,是保险丝":优化是"尽量省",保险丝是"超过就断",两者目标不同。
  • 触发指标:单用户预算、单租户预算、全局预算多层设限;按 token 数、成本金额、调用次数均可。
  • 组合拳(省成本手段):按复杂度路由(简单任务用便宜模型)、prompt 缓存(命中前缀省钱)、截断长对话、Batch API(省约 50%)。
  • 对应 OWASP LLM10(无界消费):Agent 陷入循环、上下文滚大都是成本失控的典型场景。
  • 熔断后的动作:不是简单报错,而是降级到便宜模型、或停止并告警。

122. 模型返回的工具参数不合法,怎么处理?

直接答案:永远不要信任模型输出,执行层必须有独立校验。链路:

  1. 严格校验(schema 校验函数名、参数类型、必填)。
  2. 自动修复(json_repair 修格式)。
  3. 反馈重试(带错误回传,只重试一次)。
  4. 兜底降级(明确报错或降级纯文本)。

知识点讲解:

  • "稳定捏造"问题:模型可能稳定地捏造某个参数(每次都错同一个),重试无效——要靠源头约束 + 代码兜底,而非无限重试。
  • 校验是代码级,不靠 prompt:prompt 说"参数要合法"是软约束,代码校验才是硬约束。
  • 每层都要有退出条件:不能"修复→失败→再修"无限循环,每层设一次尝试上限。
  • 修复工具的边界:json_repair 只能修语法,不能修"值错误"(金额为负、ID 不存在),后者要业务校验。
  • 降级要明确:多次失败后明确告诉用户"无法完成",而非静默执行一个"猜"出来的参数。

123. 流式输出的分片怎么拼接和解析?(衔接分类 2)

直接答案:先区分网络字节块、SSE 事件、模型内容增量三个层次,它们不是一回事:

  1. UTF-8 字符与事件分隔符都可能跨网络块——先增量解码。
  2. 再按 SSE 事件边界解析(data: 行)。
  3. 随后根据事件类型合并文本或同一工具调用的参数。

知识点讲解:

  • 完整 SSE data 事件可能是合法 JSON,但工具参数字符串增量通常不完整:一个 SSE data 可能是合法 JSON,但里面携带的 tool_arguments 字符串是逐步累积的,中间状态不完整。
  • 按调用 ID 累积参数:收到完成标志(finish_reason)后再解析并验证 schema;部分 JSON 只能用于展示,不能触发业务写入。
  • 断流处理:处理错误事件和未完成结束,不能把断流内容当作完整结果。
  • 重连的局限:EventSource 的重连与 Last-Event-ID 需要服务端事件 ID、保存和回放能力配合;普通 fetch 流、SDK 或供应商 API 不自动具备可恢复生成。
  • 没有续传协议时:明确报告中断,重试时防止工具副作用重复。

参考:MDN SSE。


124. LLM 应用怎么降级(Degradation)?降级策略有哪些?

直接答案:降级应遵守原功能的证据和权限约束,并明确告知用户当前能力。

  • 有来源要求的知识库问答在检索失败时:返回暂不可用、使用仍有效且当前用户有权读取的缓存、或转人工。不能悄悄换成纯生成。
  • 只有本来允许开放知识回答的功能,才可标明证据缺失后降级生成。

知识点讲解:

  • 降级不能破坏"证据约束":一个有引用要求的功能,检索失败后偷偷改成"自由发挥",等于把"有据可查"变成"无据编造"——这是降级最常见的错误。
  • 换模型/供应商也要重验:工具协议、数据地域、输出质量、权限都要重新验证,不能"换个更便宜的模型就完事"。
  • 缓存不能绕过授权:降级到缓存时,必须检查当前用户仍有权读取该内容。
  • 预先测试三类故障:主服务失败、缓存过期、用户权限变化。

125. LLM 应用的双层限流怎么设计?上游 429 和自身限流各怎么应对?

直接答案:限流分两层:

  • 应用侧:按租户、用户、并发、请求与 token 预算限流。
  • 供应商侧:分别看 RPM、TPM、并发及账户额度。

应对:

  • 排队要有上限和 deadline,超过容量返回明确错误,避免无限积压。
  • 上游 429 必须结合错误码:短时限速可按 Retry-After 或带抖动的指数退避重试;余额不足/配额耗尽应停止无效重试并报告原因。

知识点讲解:

  • 双重重试的陷阱:SDK 自动重试 + 业务层又重试,会放大流量——重试要统一由一层控制。
  • 重试也消耗预算:重试本身也在消耗限流配额和成本,须限制次数和总时长。
  • 测试四条路径:瞬时限流恢复、持续额度不足、队列超时、调用方取消——验证"重试成功"不等于"业务副作用只执行一次"。

126. LLM 应用怎么做缓存?缓存什么、怎么失效?

直接答案:三种缓存,失效条件不同:

  • 结果缓存:复用已生成答案。
  • 供应商 prompt cache:复用输入前缀计算。
  • embedding cache:复用指定模型对文本的向量。

知识点讲解:

  • 结果缓存的键设计:键或命名空间需覆盖租户、用户有效权限范围、必要会话上下文、知识版本、模型/提示词版本和查询。命中后仍需检查当前授权(权限撤销后缓存不能继续命中)。
  • 时效性由业务决定:是否允许旧答案复用由业务时效要求决定;temperature=0 既非必要条件,也不保证确定性。
  • 成本计算要诚实:按命中率、可缓存输入占比、写入费、读取费与输出费计算;输入前缀折扣不能当作整张账单的节省比例(只有命中前缀的部分省钱)。
  • 负向测试:至少覆盖跨租户、权限撤销、知识更新三类。

127. LLM 应用的可观测性怎么做?要监控哪些指标?

直接答案:LLM 应用可观测要把每次调用变成可追踪的记录,监控四个维度:

  1. 质量指标:准确率、忠实度、用户满意度。
  2. 性能指标:TTFT、总延迟、吞吐。
  3. 成本指标:token 用量、调用成本、成本趋势。
  4. 可靠性指标:错误率、重试率、限流率、降级触发。

做法:用 Langfuse/LangSmith 等埋点,每次调用记录 prompt、输出、token、延迟、成本。

知识点讲解:

  • 核心:LLM 应用是黑盒,没有埋点出问题就抓瞎——可观测是评估(分类 10)和成本控制(本题)的基础。
  • 埋点的关键字段:trace_id(串联一次请求)、prompt、输出、模型版本、token 用量、延迟、成本、错误信息。
  • trace 的关联价值:一个用户请求可能触发多次模型调用 + 工具调用,trace 把它们串起来才能端到端归因。
  • 告警而非只看报表:关键指标(错误率突增、成本异常、延迟飙升)要设告警阈值,而非事后看 dashboards。
  • 面试加分点:能说出"质量/性能/成本/可靠性四维监控",并知道 trace 是归因主键。

128. 异步处理里,怎么保证 LLM 任务的"不丢、不重、不卡死"?

直接答案:异步 + 队列解耦长任务后,真正的难点是可靠性三问题——不丢、不重、不卡死:

  1. 不丢:任务入队前先持久化(写数据库记录 pending),worker 消费后再更新状态,避免"队列消息丢了任务也没了"。
  2. 不重:用幂等键——同一任务重试/重复消费时,通过唯一 key 去重,避免执行两次(尤其带副作用的任务)。
  3. 不卡死:设超时 + 心跳 + 重试上限——worker 处理超时要有兜底(重试或标记失败),避免任务卡住永不结束。

知识点讲解:

  • 核心:消息队列本身不保证可靠,可靠要自己设计——队列是"传递",持久化 + 幂等 + 超时才是"可靠"。
  • 状态机:任务状态流转 pending → processing → done/failed,任何状态都能查询、能恢复。
  • "at-least-once"语义:多数队列是至少一次投递,所以消费者必须幂等,否则重复消费会重复执行。
  • 断点续跑:批量任务记录进度,失败后从中断处继续,而非从头重跑。
  • 加分点:能把"异步"和"可靠性三件套(持久化 + 幂等 + 超时)"关联起来讲。

129. 多个 LLM 调用怎么编排?串行、并行、DAG 怎么选?

直接答案:多个 LLM 调用的编排取决于依赖关系:

  • 串行:有依赖(上一步输出是下一步输入)。
  • 并行:无依赖(可同时调多个模型/多个子任务)。
  • DAG(有向无环图):部分依赖、部分并行,用图结构表达(LangGraph、自研编排器)。

知识点讲解:

  • 核心:先画依赖图,无依赖并行、有依赖串行,最大化吞吐、最小化延迟。
  • 并行的限流:并发度要限流,避免打爆上游(触发 429)。
  • 错误传播:DAG 里某个节点失败,要考虑是"局部降级"还是"整体失败",依赖图决定错误影响范围。
  • 编排的确定性:能显式表达的依赖(DAG)优先用确定性编排,只有"无法预知下一步"才交给 Agent 动态决策。
  • 面试加分点:能说清"依赖决定编排拓扑",并知道 DAG 是"确定性编排"、Agent 是"动态编排"。

130. 流式场景下游消费不过来怎么办?背压机制如何防止内存被拖垮?

直接答案:背压是下游处理不过来时,向上游传递"慢一点"的信号,防止系统被压垮。

LLM 应用场景:上游 LLM 流式输出很快,但下游(数据库、下游服务、客户端)消费慢,需要背压机制避免内存堆积。

处理:有界队列 + 阻塞、批量限速、根据下游能力控制并发。

知识点讲解:

  • 核心:别让数据在生产者和消费者之间无限堆积——无界缓冲会耗尽内存。
  • 背压的传递:从最慢的环节向上游逐级传导"慢下来",最终影响 LLM 的生成速率或暂停读取。
  • 有界队列的语义:队列满时是"阻塞生产者"还是"丢弃并报错",要按业务选择(关键数据不能丢)。
  • 流式场景尤其重要:LLM 吐 token 快(毫秒级),下游写库慢(秒级),速率不匹配会迅速堆积。
  • 面试加分点:能说出"有界缓冲 + 背压信号"。

131. LLM 应用怎么做灰度发布和回滚?

直接答案:灰度发布是小流量逐步放量新模型/prompt,验证没问题再全量:

  1. 离线评测:先跑 golden set。
  2. 小流量灰度:5% → 20% → 50% → 100%。
  3. 监控对比:质量、延迟、成本、错误率对比基线。
  4. 一键回滚:出问题立即切回旧版本。

知识点讲解:

  • 核心:模型和 prompt 也要像代码一样灰度 + 可回滚——它们是"配置即代码"的一部分。
  • 版本化的必要性:记录模型版本、prompt 版本、配置版本,回滚时才能精确切回。
  • 灰度观察窗口:每档放量后要观察足够时间(覆盖流量峰值和长尾 case),再放下一档。
  • 回滚的触发:不仅看整体指标,关键安全/合规 case 退化要立即回滚。
  • prompt 灰度同样重要:prompt 改动也会导致退化,和换模型一样需要灰度。
  • 面试加分点:能说出"离线评测 → 小流量 → 全量 + 可回滚"。

132. 怎么保证 LLM 输出的"确定性"和"可复现"?

直接答案:LLM 天然有随机性,要确定性需:

  1. temperature=0:采样退化为贪心,减少随机性。
  2. 固定 seed:设置随机种子(部分模型支持)。
  3. 结构化输出:用 JSON Mode/Structured Output 保证格式确定。
  4. 固定输入:相同 prompt、相同参数、相同模型版本。

知识点讲解:

  • 核心:需要可复现(评测、回归、审计)的场景,必须锁死温度和 seed。
  • temperature=0 的边界:即使 temperature=0,跨模型版本/跨硬件也可能有微小差异(浮点、调度、batch),不能 100% 保证——这是"确定性"和"严格可复现"的区别。
  • 确定性 vs 结构化:格式确定性(结构化输出)比内容确定性更容易保证,要区分两者需求。
  • 审计场景的额外要求:记录模型版本、prompt 版本、参数、seed,才能事后复现。
  • 面试加分点:能说清"确定性 = 低温度 + 固定 seed + 锁版本",并知道"temperature=0 也不是绝对确定"。

133. LLM 应用的"超时、重试、幂等"三者怎么协同设计?

直接答案:三者是可靠性的一体三面,要协同:

  1. 超时:区分连接/首字/整体超时,超时后进入重试判断。
  2. 重试:只重试瞬态故障(429/5xx/超时),带退避 + jitter + 上限。
  3. 幂等:有副作用时,重试前查状态,用幂等键防重复执行。

协同逻辑:超时触发重试 → 重试前先判断是否有副作用 → 有副作用则走幂等查状态,而非盲目重发。

知识点讲解:

  • 核心:三者不是孤立的,而是一条可靠性决策链——超时是"发现问题",重试是"尝试恢复",幂等是"保证恢复不造成二次伤害"。
  • 经典错误:超时 → 盲目重试 → 副作用执行两次(发两封邮件、扣两次款)。
  • 决策树:超时 → 有没有副作用?没有 → 直接重试;有 → 先查状态(幂等键)→ 已完成则复用结果,未完成才重试。
  • 共享预算:重试次数和时间要共享总预算,避免"重试几次"后总耗时远超预期。
  • 面试加分点:能串起"超时→重试→幂等"的完整逻辑,并画出决策树。

134. 一个高并发 LLM 应用,你会怎么设计架构?

直接答案:高并发 LLM 应用架构分层:

  1. 接入层:API 网关(鉴权、限流、路由)。
  2. 应用层:业务逻辑 + LLM 调用编排。
  3. 模型层:模型网关(多模型路由、降级)+ 推理引擎(自部署时 vLLM)。
  4. 数据层:缓存(Redis)+ 队列(削峰)+ 数据库(状态持久化)。
  5. 可观测:全链路埋点(Langfuse/Prometheus)。

知识点讲解:

  • 六件套缺一不可:限流、缓存、异步队列、模型路由、降级、可观测。
  • 每层的可靠性手段:
    • 网关:鉴权 + 限流 + 超时。
    • 应用层:幂等 + 重试 + 降级。
    • 模型层:路由 + 熔断 + 多模型冗余。
    • 数据层:缓存 + 队列削峰 + 持久化。
    • 可观测:trace + 指标 + 告警。
  • 削峰填谷:突发流量用队列缓冲,异步处理长任务,保护模型层不被瞬时打爆。
  • 多模型冗余:主力模型故障时降级到备用模型/小模型,保证可用性。
  • 面试加分点:能画出分层架构,并说明每层的可靠性手段。