AI应用开发工程师面试题汇总(三):工程化与性能优化35问
文章目录
- AI应用开发工程师面试题汇总(三):工程化与性能优化35问
-
- 前言
- 一、Token与成本优化
-
- 1. Token是如何计算的?如何在请求前准确预估Token消耗?
- 2. Prompt压缩有哪些方法?如何在不损失语义的前提下减少Token消耗?
- 3. 多轮对话中上下文如何裁剪?有哪些策略和注意事项?
- 4. 大模型应用的缓存策略有哪些?语义缓存和精确缓存有什么区别?
- 5. 什么是模型路由与降级?如何设计一个多模型调度策略?
- 6. 批量请求如何优化?同步批处理和异步批处理各有什么适用场景?
- 7. 如何设计Token监控与告警体系?
- 8. 如何实现AI应用的成本归因与分摊?
- 二、大模型可靠性设计
-
- 9. 大模型调用的超时与重试策略如何设计?
- 10. 如何对大模型服务做限流与熔断?
- 11. 大模型应用的降级策略有哪些?如何设计多级降级方案?
- 12. 大模型应用中如何保证幂等性?
- 13. 大模型调用的错误处理与恢复策略如何设计?
- 14. 如何对大模型的输出做校验?有哪些校验策略?
- 15. 大模型应用的异常检测与告警如何设计?
- 三、流式输出与用户体验
-
- 16. SSE流式协议的原理是什么?与WebSocket有什么区别?
- 17. 大模型流式输出如何实现?后端到前端的完整链路是怎样的?
- 18. 打字机效果如何实现?有哪些性能优化技巧?
- 19. 流式输出中途中断如何处理?前端和后端各需要做什么?
- 20. 前端流式渲染有哪些挑战?如何实现高性能的流式UI?
- 21. 流式输出与结构化输出如何兼容?流式场景下如何做JSON解析?
- 四、高并发与性能
-
- 22. 大模型应用的并发控制如何设计?有哪些关键参数?
- 23. 连接池管理在大模型应用中有哪些特殊考虑?
- 24. 大模型应用中异步处理有哪些模式?如何选择同步和异步?
- 25. 队列与削峰在大模型应用中如何设计?
- 26. GPU资源调度策略有哪些?如何提高GPU利用率?
- 27. 大模型推理加速有哪些技术方案?
- 28. KV Cache优化有哪些策略?如何平衡显存占用和性能?
- 29. 批处理与动态批处理在大模型推理中如何实现?
- 五、监控与运维
-
- 30. AI应用需要监控哪些核心指标?与传统应用监控有什么区别?
- 31. 日志收集与链路追踪在AI应用中如何实现?
- 32. Prompt版本管理如何做?有哪些最佳实践?
- 33. A/B测试与灰度发布在AI应用中如何实现?
- 34. 模型漂移检测如何实现?如何应对模型行为变化?
- 35. 线上问题排查方法论是什么?AI应用与传统应用排查有什么不同?
前言
随着大模型应用从原型验证走向生产部署,工程化能力已成为AI应用开发工程师的核心竞争力。仅仅能调用API让模型返回结果远远不够——如何在千万级日活下控制Token成本、如何在模型推理不稳定时保证业务连续性、如何让流式输出既快又不丢数据、如何在GPU资源紧张时做合理的调度与加速,这些问题直接决定了AI应用能否真正上线并持续运行。
本篇是AI应用开发工程师面试题系列的第三篇,聚焦工程化与性能优化,共35道题目,覆盖Token与成本优化、大模型可靠性设计、流式输出与用户体验、高并发与性能、监控与运维五大领域。每道题都从原理出发,结合工程实践和面试加分点,帮助你在面试中展现扎实的工程功底。
一、Token与成本优化
1. Token是如何计算的?如何在请求前准确预估Token消耗?
参考答案:
Token是大模型处理文本的基本单位,介于字符和词之间。不同语言的Token化策略不同:英文大约1个Token对应0.75个单词,中文通常1个汉字对应1-2个Token,代码和特殊符号的Token化规则更为复杂。
Token计算的原理基于分词器(Tokenizer),常用的分词算法包括BPE(Byte Pair Encoding)、WordPiece和SentencePiece。大模型在训练阶段确定分词词表后,推理时按照相同的词表对输入文本进行切分,每一段对应一个Token ID。
在实际工程中,预估Token消耗有以下方法:
- 离线计算:使用与目标模型匹配的分词器库(如tiktoken、transformers的tokenizer),对Prompt文本进行编码,统计Token数量。这种方法精度最高,但需要依赖具体分词器。
- 近似估算:对于英文按"字符数÷4"近似,中文按"字符数×1.5"近似。适用于快速评估,不适合精确计费场景。
- 在线统计:调用API返回的usage字段获取实际消耗的prompt_tokens、completion_tokens和total_tokens,用于事后核算和监控。
需要注意的是,Prompt中的系统提示、历史对话、工具描述都会计入输入Token。多轮对话场景下,输入Token会随对话轮次线性增长,这是成本失控的常见原因。
面试加分点:了解Function Calling/Tool Use的Token开销(工具定义本身占用输入Token),能说出多轮对话中输入Token累积增长的数学模型,以及如何用对话窗口管理策略控制增长。
2. Prompt压缩有哪些方法?如何在不损失语义的前提下减少Token消耗?
参考答案:
Prompt压缩是在保持语义信息的前提下减少输入Token数量的技术,直接影响API调用成本和推理延迟。
主要方法分为以下几类:
结构化精简:去除冗余表述,将自然语言指令改为结构化格式。例如将"请你帮我分析一下这段代码存在什么问题,然后给出修改建议"压缩为"分析代码问题并给出修改建议"。用JSON Schema替代长文本描述输出格式,用枚举替代长段落的选项说明。
上下文裁剪:对多轮对话历史进行摘要压缩。保留最近N轮原文,更早的对话用摘要替代。摘要可以由模型生成,也可以用抽取式方法选取关键信息。注意摘要本身也有Token成本,需要在压缩收益和摘要成本之间做权衡。
信息检索过滤:在RAG场景中,不是将所有检索到的文档都塞入Prompt,而是先做相关性排序,只保留Top-K最相关的片段。进一步可以用重排序模型(Reranker)提升排序质量,确保保留的上下文信息密度最高。
符号化压缩:用缩写、代号或符号替代重复出现的长文本。例如将反复出现的"请按照以下格式输出"替换为"格式:“[占位符]”"。将长URL替换为短标识,在输出时再还原。
专门压缩模型:使用专门训练的Prompt压缩模型,将长Prompt压缩为短Prompt。这类模型学习如何在压缩过程中保留关键指令和上下文信息。使用时需注意压缩模型本身的推理成本。
实践中通常组合使用多种方法。一个典型的压缩流水线是:检索过滤→摘要压缩→结构化精简→符号化替换,每一步都验证输出质量未受明显影响。
面试加分点:能讨论压缩率和语义保留之间的权衡,了解LLMLingua等Prompt压缩方法的核心思想,能说出如何设计A/B测试验证压缩后输出质量未下降。
3. 多轮对话中上下文如何裁剪?有哪些策略和注意事项?
参考答案:
多轮对话的上下文裁剪是控制输入Token增长的核心手段。随着对话轮次增加,如果不做裁剪,每轮请求的输入Token会线性甚至指数级增长,很快超出模型上下文窗口限制并导致成本飙升。
常用裁剪策略:
滑动窗口策略:保留最近N轮对话原文,丢弃更早的对话。实现简单,但会丢失早期上下文信息。适用于短对话场景(如单次问答、简单任务)。
摘要+窗口混合策略:将早期对话压缩为摘要文本,与最近N轮原文拼接。摘要由模型在后台异步生成,不阻塞主对话流程。这是目前生产系统中最常用的方案。关键设计点包括:摘要触发的Token阈值、摘要生成的频率、摘要本身的最大长度。
重要性筛选策略:对历史对话按信息价值打分,保留高分轮次。打分维度包括:是否包含用户关键需求、是否包含模型重要结论、是否包含工具调用结果。可以结合嵌入相似度,只保留与当前问题最相关的历史轮次。
分桶管理策略:将不同类型的上下文分桶管理,各桶独立裁剪。例如将系统提示、用户偏好、任务状态、对话历史分为不同桶,各桶设置独立的Token预算和裁剪规则。
注意事项:
- 工具调用上下文:Function Calling的工具定义占用固定的输入Token,不受对话裁剪影响,但工具调用的历史结果需要纳入裁剪范围。
- 引用一致性:裁剪后如果模型在摘要中引用了被裁剪的原文细节,会产生幻觉。摘要应保留关键结论而非细节描述。
- 裁剪时机:应在发送请求前做裁剪,而不是在存储时裁剪。这样可以保留完整历史用于审计和回溯。
- Token预算分配:为上下文各部分设定Token预算上限(如系统提示500、历史对话2000、当前问题500、输出预留1000),确保总量不超过模型窗口。
面试加分点:能说出Token预算管理的具体数字分配方案,了解"上下文蒸馏"(Context Distillation)的概念,能讨论裁剪对多轮推理任务(如Agent多步规划)的影响。
4. 大模型应用的缓存策略有哪些?语义缓存和精确缓存有什么区别?
参考答案:
缓存是降低大模型调用成本和延迟的有效手段。根据缓存键的匹配方式,主要分为精确缓存和语义缓存两类。
精确缓存:
以请求的完整Prompt(或其哈希)作为缓存键,当完全相同的Prompt再次出现时直接返回缓存的响应。实现简单,缓存命中判断零延迟。适用于系统提示固定、用户输入高度标准化的场景(如模板化报告生成)。
精确缓存的局限是命中率低——即使用户只改了一个标点,缓存也会失效。实际生产中精确缓存的命中率通常在5%-15%。
语义缓存:
将用户请求转换为嵌入向量,当新请求的向量与缓存中某条记录的相似度超过阈值时(如余弦相似度>0.95),直接返回缓存的响应。语义缓存能捕获"换一种说法表达相同意思"的请求,命中率显著高于精确缓存。
语义缓存的核心设计点:
- 嵌入模型选择:用轻量级嵌入模型生成向量,避免嵌入计算成为瓶颈。
- 相似度阈值:阈值过高则命中率低,过低则误命中(返回不相关答案)。通常从0.92开始调优。
- 缓存失效:模型版本更新、系统提示变更时需批量失效缓存。可以给缓存条目设置TTL。
- 向量检索:生产环境用向量数据库存储缓存向量,支持高效近似最近邻检索。
混合缓存策略:先查精确缓存(O(1)哈希查找),未命中再查语义缓存(O(logN)向量检索),仍未命中再调用模型。这样兼顾命中率和查询效率。
缓存层位置:可以在应用层缓存(业务逻辑控制)、网关层缓存(对应用透明)、CDN层缓存(边缘节点缓存)。多层缓存可以叠加使用。
面试加分点:能讨论语义缓存的误命中风险和兜底策略(如缓存结果置信度评分),了解缓存对非确定性输出的影响(温度参数>0时同一Prompt可能返回不同结果),能设计缓存预热和淘汰策略。
5. 什么是模型路由与降级?如何设计一个多模型调度策略?
参考答案:
模型路由是指根据请求特征(任务类型、复杂度、延迟要求、成本预算)将请求分发到不同模型进行处理。降级是在主模型不可用或响应超时时自动切换到备用模型的机制。两者结合构成多模型调度策略。
模型路由的维度:
- 任务复杂度路由:简单任务(如格式转换、简单问答)路由到轻量级模型,复杂任务(如推理、代码生成)路由到能力更强的模型。可以通过前置分类器或规则引擎判断复杂度。
- 成本路由:对成本敏感的请求路由到低成本模型,对质量敏感的请求路由到高质量模型。通过业务标记或用户层级区分。
- 延迟路由:对实时性要求高的请求路由到推理速度快的模型,对批处理任务路由到吞吐量高的模型。
- 负载路由:根据各模型的当前负载和队列长度做负载均衡,避免单模型过载。
降级策略设计:
- 超时降级:主模型在设定时间内未返回结果,自动切换到备用模型。需要设置合理的超时阈值,避免用户等待过久。
- 错误降级:主模型返回错误(如限流、服务不可用)时自动切换。需要实现重试与降级的协同——先重试1-2次,仍失败则降级。
- 质量降级:主模型输出未通过校验(如JSON解析失败、内容不合规)时,用备用模型重新生成。需要设置降级链,避免无限降级。
多模型调度架构:通常设计为路由层+执行层的架构。路由层负责决策,执行层负责调用具体模型。路由规则可以基于配置文件动态更新,支持A/B测试和灰度切换。
注意事项:不同模型的Token化方式、API格式、输出风格可能不同,路由层需要做适配转换。降级时需要保证备用模型的输出格式与主模型一致,否则下游处理会出错。
面试加分点:能设计基于强化学习的自适应路由策略(根据历史命中率和成本动态调整路由规则),能讨论路由决策本身的延迟开销(通常应<10ms),了解多模型场景下的监控和归因方案。
6. 批量请求如何优化?同步批处理和异步批处理各有什么适用场景?
参考答案:
批量请求优化是提升大模型API吞吐量和降低成本的重要手段。大模型推理的GPU利用率与批大小密切相关——单个请求的batch size为1时GPU利用率通常不到30%,而合理批处理后可以提升到70%以上。
批量请求的主要方式:
API层面的批量调用:部分大模型服务提供Batch API,允许将多个独立请求打包提交,服务端在内部做批处理推理后返回结果。批量请求通常有延迟换价格的折扣(如50%折扣),但响应时间从秒级变为分钟级。适用于非实时场景(如数据标注、内容审核、批量翻译)。
应用层面的请求合并:将短时间内到达的多个请求在应用层缓冲,积累到一定数量或等待一定时间后合并提交。需要设置最大缓冲时间和最大缓冲数量两个阈值,避免请求等待过久。
同步批处理:请求提交后阻塞等待批量结果返回。实现简单,适合请求量稳定、延迟容忍度较高的场景。缺点是单个请求的延迟等于整个批次的处理时间。
异步批处理:请求提交后立即返回任务ID,结果通过回调或轮询获取。适合请求量波动大、延迟容忍度高的场景。需要设计任务状态管理、结果存储、回调通知等基础设施。
动态批(Dynamic Batching):推理服务端在时间窗口内动态收集请求,根据当前GPU显存和计算能力自动调整批大小。与静态批处理相比,动态批能在低流量时减少等待、高流量时增大批次。这是推理框架(如vLLM、TGI)的核心特性之一。
注意事项:
- 批量请求中如果某个请求的输出特别长,会拖慢整个批次的返回。需要对单请求输出长度设上限,或实现"先完成先返回"的批处理。
- 不同请求的Prompt长度差异大时,批处理的padding浪费会增加。可以通过长度分桶减少padding。
- 批量请求的失败处理需要设计部分成功机制——不能因为一个请求失败而丢弃整个批次。
面试加分点:了解连续批处理(Continuous Batching)与传统静态批处理的区别,能讨论批处理对首Token延迟(TTFT)和Token间延迟(ITL)的不同影响,能设计批量请求的SLA分级方案。
7. 如何设计Token监控与告警体系?
参考答案:
Token监控与告警是成本控制和容量规划的基础。没有完善的Token监控,线上AI应用很容易出现成本失控而无人察觉的情况。
监控指标体系:
- 基础指标:每分钟Token消耗量、每请求平均Token、输入/输出Token比例、请求成功率、请求延迟。
- 成本指标:每小时/每天/每月Token总费用、各业务线Token费用占比、单用户平均Token费用、成本环比增长率。
- 质量指标:平均输出长度、输出截断率(因max_tokens截断的比例)、重试率(因输出不合规需要重新生成的比例)。
- 容量指标:当前并发数、队列长度、Token消耗速率与配额的比值。
监控架构:
数据采集层在API调用链路中埋点,记录每次请求的Token消耗、延迟、状态码等。数据通过消息队列发送到时序数据库存储。展示层用Grafana等工具搭建Dashboard,实时展示关键指标。告警层基于规则引擎在指标越界时触发告警。
告警规则设计:
- 阈值告警:单小时Token消耗超过预算的80%时告警,日消耗超过预算的90%时告警。
- 异常检测告警:Token消耗速率突然增长(如5分钟内增长超过3倍标准差)时告警,可能指示Bug或滥用。
- 预测告警:基于历史趋势预测本月总消耗将超预算时提前告警,留出人工干预时间。
- 业务告警:某业务线Token占比异常增长、单用户Token消耗异常偏高时告警。
告警通道:根据严重级别选择不同通道。P0级别(即将超额导致服务停止)打电话+短信,P1级别(趋势异常)发即时消息,P2级别(日常报表)发邮件。
面试加分点:能设计多维度归因的Token监控(按业务线、用户、模型、任务类型分别统计),了解Token消耗的异常检测算法(如时间序列分解+残差分析),能讨论Token配额管理和限流机制的设计。
8. 如何实现AI应用的成本归因与分摊?
参考答案:
成本归因与分摊是AI应用从"能跑"到"可运营"的关键环节。当多个业务线、多个功能模块共享同一套大模型服务时,准确归因每笔Token费用到具体业务和用户,是成本控制和商业化的前提。
成本归因的粒度:
- 按业务线归因:为每个业务线分配唯一的调用标识,在API请求的元数据中携带,账单按标识汇总。
- 按功能模块归因:在应用内部为每个功能模块(如问答、摘要、翻译、代码生成)设置成本中心,统计各模块的Token消耗。
- 按用户归因:在请求中携带用户ID,实现单用户级别的成本追踪。适用于ToB场景的按量计费和ToC场景的用量限制。
- 按请求阶段归因:区分意图识别、检索增强、生成回答等不同阶段的Token消耗,找出成本热点。
分摊策略:
- 直接归因:能明确归属到某业务/用户/模块的费用直接计入。这是最精确的方式。
- 分摊归因:共享费用(如通用系统提示的Token开销、向量检索的基础设施成本)按各业务的使用量比例分摊。
- 阶梯分摊:设定基础用量免费,超出部分按阶梯定价分摊。需要设计阶梯边界和定价规则。
技术实现:
在API调用链路中注入追踪上下文(Trace Context),携带业务标识、模块标识、用户标识。调用完成后,将Token使用量与追踪上下文一起写入计费数据库。定期(如每小时)汇总生成账单。
对于多级调用场景(如Agent的多步推理,每步可能调用不同模型),需要将整个调用链的Token消耗聚合到根请求,实现请求级别的完整成本归因。
对账机制:建立应用侧计费记录与服务侧API账单的对账机制,发现差异及时排查。常见差异来源包括:重试请求的Token计算、流式输出的Token统计口径不一致、缓存命中请求的计费规则。
面试加分点:能设计基于OpenTelemetry的分布式追踪+成本归因方案,了解实时计费(Streaming Billing)的挑战,能讨论成本归因在多租户SaaS场景下的隐私和隔离问题。
二、大模型可靠性设计
9. 大模型调用的超时与重试策略如何设计?
参考答案:
大模型服务的响应时间波动较大(从几百毫秒到几十秒不等),合理的超时与重试策略是保证业务连续性的基础。
超时设计:
- 连接超时:TCP连接建立的超时,通常设置3-5秒。连接超时通常意味着服务不可用,应快速失败。
- 读取超时:等待响应数据的超时。对于非流式调用,通常设置30-60秒;对于流式调用,首Token超时设置10-15秒,Token间超时设置5-10秒(超过5秒没有新Token可能意味着服务端异常)。
- 总超时:整个请求的最大允许时间,防止异常情况下请求无限挂起。根据业务SLA设置,通常60-120秒。
超时值应根据模型的实际P99延迟动态调整。监控不同模型、不同任务类型的延迟分布,据此设置合理的超时阈值。
重试设计:
- 重试条件:不是所有错误都适合重试。网络错误、连接超时、5xx服务端错误可以重试;4xx客户端错误(如参数错误、鉴权失败)不应重试;429限流错误应等待后重试。
- 退避策略:固定间隔重试可能导致雪崩效应,应使用指数退避+随机抖动。例如初始等待1秒,每次翻倍,加上0-1秒的随机抖动,避免大量请求同步重试。
- 最大重试次数:通常设置2-3次。超过最大次数后执行降级策略或返回错误。
- 幂等性保证:重试可能导致同一请求被执行多次。对于非幂等操作(如消息发送),需要在请求中携带幂等键,服务端做去重。
流式调用的特殊处理:流式输出已经开始接收Token后中断,重试需要考虑已接收部分的处理。可以选择:丢弃已接收内容重新请求、保留已接收内容并从断点续传(如果服务端支持)、用已接收内容+降级策略拼接结果。
面试加分点:能设计分级超时策略(不同任务类型设置不同超时),了解熔断器模式与重试的关系(熔断器打开时直接拒绝请求而非重试),能讨论重试风暴的成因和防御机制。
10. 如何对大模型服务做限流与熔断?
参考答案:
限流和熔断是保护系统在大模型服务异常或流量突增时不雪崩的两大核心机制。
限流设计:
- 限流维度:按用户限流(防止单用户耗尽资源)、按业务线限流(保证核心业务资源)、按模型限流(防止单模型过载)、按全局限流(保护整个系统)。
- 限流算法:令牌桶算法适合允许突发流量的场景,漏桶算法适合需要平滑流量的场景。实际中常用令牌桶,因为它既能限制平均速率又能允许短暂突发。
- 限流粒度:QPS限流(每秒请求数)和TPS限流(每秒Token数)要结合使用。大模型请求的Token消耗差异大,仅限QPS无法防止少量但Token巨大的请求耗尽配额。
- 限流响应:被限流的请求可以返回429状态码+Retry-After头,或进入排队队列等待处理,或直接降级到轻量模型。
熔断设计:
熔断器的三个状态:
- 关闭状态(Closed):正常放行请求,同时统计失败率。
- 打开状态(Open):失败率超过阈值时熔断器打开,直接拒绝请求(快速失败),不再调用下游服务。
- 半开状态(Half-Open):熔断器打开一段时间后,允许少量请求试探性通过。如果试探请求成功,熔断器关闭恢复正常;如果失败,熔断器重新打开。
熔断器参数:
- 失败率阈值:通常设置50%。窗口内的失败请求数/总请求数超过此值则熔断。
- 统计窗口:滑动窗口大小,通常10-30秒。
- 最小请求数:窗口内请求数低于此值时不触发熔断,避免样本不足误判。
- 熔断持续时间:熔断器打开后多久进入半开状态,通常5-30秒。
- 半开试探请求数:半开状态下允许通过的请求数,通常3-5个。
限流与熔断的配合:限流是预防性措施,在正常流量下就开始工作;熔断是应对性措施,在服务已经出现问题时快速失败防止雪崩。两者结合使用,限流在前防止过载,熔断在后防止级联故障。
面试加分点:能设计多级限流策略(网关层QPS限流+应用层Token限流+模型层并发限流),了解自适应限流(根据系统负载动态调整限流阈值),能讨论熔断器在多模型场景下的设计(对每个模型独立熔断,某模型熔断后自动路由到备用模型)。
11. 大模型应用的降级策略有哪些?如何设计多级降级方案?
参考答案:
降级策略是在大模型服务不可用或性能不达标时,用替代方案保证核心业务可用的机制。合理的降级方案应该有多级fallback,确保最后一级也有可接受的输出。
降级方案分级:
一级降级——模型替换:主模型不可用时切换到备用模型。备用模型应具备相似的能力,但可以在速度和成本上做权衡。例如从大参数模型降级到中等参数模型。降级时需要做Prompt适配——不同模型对Prompt格式和长度的要求可能不同。
二级降级——功能简化:模型替换不可行时,简化功能逻辑。例如从多步Agent推理降级为单次问答,从RAG增强回答降级为直接回答,从代码生成降级为代码模板推荐。简化后的功能虽然不如完整版本,但能提供基本的可用性。
三级降级——缓存回退:如果存在历史缓存或预计算结果,直接返回缓存内容。例如常见问题的预设答案、热门查询的缓存结果。虽然不能处理新问题,但能覆盖高频常见场景。
四级降级——规则兜底:最底层的兜底方案,基于规则引擎返回固定回复或引导用户稍后重试。例如"当前服务繁忙,已为您记录问题,稍后将通过邮件回复"。
降级触发条件:
- 错误率触发:连续错误或错误率超过阈值。
- 超时触发:主模型响应超时。
- 质量触发:模型输出未通过内容校验或格式校验。
- 资源触发:GPU资源不足、队列积压超过阈值。
降级恢复:降级不是永久的,需要设计恢复机制。当主服务恢复健康后,逐步将流量从降级方案切回主方案。可以采用灰度恢复——先将10%流量切回,观察5分钟无异常后逐步增加。
注意事项:降级方案需要定期演练,确保在真正需要时能正常工作。降级决策应自动化,避免人工介入延迟。降级过程中的用户体验应保持一致——用户不应感知到后端切换了模型,只是可能响应质量略有下降。
面试加分点:能设计降级决策的状态机(包括降级触发、降级执行、降级恢复的完整状态流转),了解"优雅降级"的概念(在降级时向用户透明地说明可能的质量变化),能讨论降级方案的可测试性和可观测性。
12. 大模型应用中如何保证幂等性?
参考答案:
幂等性是指同一操作执行一次和多次产生相同结果的特性。在大模型应用中,由于网络重试、用户重复提交、流式中断重连等场景,同一请求可能被多次执行,幂等性设计至关重要。
需要幂等性的场景:
- 内容生成:用户点击"生成"按钮后网络超时,前端自动重试,可能导致同一内容被生成两次,浪费Token甚至产生重复扣费。
- 消息发送:Agent生成回复后发送到消息队列,如果ACK丢失导致重试,同一回复可能被发送多次。
- 工具调用:Agent调用外部工具(如发邮件、创建订单),重试可能导致同一操作被执行多次。
- 状态更新:Agent更新任务状态,重试可能导致状态被重复设置。
幂等性实现方案:
幂等键方案:客户端为每个请求生成唯一的幂等键(如UUID),在请求中携带。服务端收到请求后先检查幂等键是否已处理:已处理则直接返回缓存结果,未处理则正常执行并缓存结果。这是最通用的幂等性方案。
内容指纹方案:对请求内容计算哈希(如SHA-256),作为幂等键。同一内容的重复请求会生成相同的哈希,服务端据此去重。适用于请求内容本身就是唯一标识的场景。
乐观锁方案:在状态更新场景中,携带版本号或时间戳。服务端只在版本号匹配时更新,重复请求因版本号已变更而更新失败。
大模型场景的特殊考虑:
- 非确定性输出:大模型在温度参数>0时,同一Prompt可能返回不同结果。幂等性需要通过缓存首次结果实现,而非重新调用模型。
- 流式输出:流式请求的中断重连不能简单重试,需要基于已接收的Token续传或重新开始。可以在请求中携带"已接收偏移量"实现续传。
- Agent多步推理:Agent的每一步工具调用都应携带幂等键,确保重试不会导致工具被重复调用。如果Agent在第三步失败重试,前两步的结果应被复用而非重新执行。
存储设计:幂等键与结果的映射需要持久化存储,不能仅用内存缓存(进程重启后丢失)。通常用Redis做短期缓存(如1小时),用数据库做长期持久化。
面试加分点:能设计Agent多步推理的幂等性方案(检查点机制,每步结果持久化,重试从检查点恢复),了解幂等键的生命周期管理(过期清理、容量控制),能讨论幂等性与事务一致性的关系。
13. 大模型调用的错误处理与恢复策略如何设计?
参考答案:
大模型服务的错误类型多样,从网络层到应用层都可能出现问题。完善的错误处理策略需要针对不同错误类型设计不同的恢复路径。
错误分类体系:
网络层错误:
- 连接超时、DNS解析失败、TCP连接重置——通常是网络基础设施问题,适合重试。
- SSL证书错误——配置问题,不适合重试,需要人工介入。
HTTP层错误:
- 429 Too Many Requests——限流,需要等待后重试,Retry-After头指定等待时间。
- 401/403——认证授权问题,可能是Token过期或权限不足,不适合重试,需要刷新凭证。
- 400 Bad Request——请求格式错误,不适合重试,需要检查请求体。
- 500/502/503——服务端错误,适合重试。
- 504 Gateway Timeout——网关超时,适合重试。
模型层错误:
- 输出为空——可能是Prompt问题或模型异常,可以调整参数重试。
- 输出格式不合规——JSON解析失败、包含禁止内容等,可以修改Prompt重试。
- 输出被截断——max_tokens设置过小,增大参数后重试。
- 内容安全过滤——输入或输出触发安全策略,不适合重试,需要调整内容。
恢复策略矩阵:
| 网络超时 | ✅指数退避 | ✅备用模型 | ✅ | P2 |
| 限流429 | ✅等待后重试 | ✅轻量模型 | ✅ | P1 |
| 认证失败 | ❌ | ❌ | ❌ | P0 |
| 格式错误 | ❌ | ❌ | ❌ | P1 |
| 服务端5xx | ✅指数退避 | ✅备用模型 | ✅ | P1 |
| 输出空/不合规 | ✅调参重试 | ✅简化任务 | ❌ | P2 |
| 安全过滤 | ❌ | ❌ | ❌ | P0 |
错误传播策略:对于面向用户的请求,错误信息应做友好转换。不暴露内部错误细节(如堆栈跟踪、模型名),而是返回用户可理解的提示(如"服务暂时不可用,请稍后重试")。同时将详细错误记录到日志系统供排查。
错误恢复的自动化:通过错误分类器自动判断错误类型并执行对应的恢复策略。错误分类器可以基于HTTP状态码、错误消息关键词、历史模式等做判断。对于难以分类的错误,默认执行安全策略(重试1次+降级+告警)。
面试加分点:能设计错误分类的决策树,了解"错误预算"(Error Budget)的概念并在SLO框架下管理错误恢复策略,能讨论错误处理的可观测性(错误率、恢复时间、降级触发频率等指标)。
14. 如何对大模型的输出做校验?有哪些校验策略?
参考答案:
大模型的输出本质上是概率性的,不保证格式正确、内容准确、符合业务约束。输出校验是保证AI应用可靠性的最后一道防线。
校验维度:
格式校验:
- JSON Schema校验:如果要求模型输出JSON,用JSON Schema验证结构正确性(字段名、类型、必填项、枚举值)。校验失败可以选择修复(尝试解析+补全缺失字段)或重新生成。
- 正则表达式校验:对特定格式的输出(如日期、电话号码、代码)用正则验证。
- Markdown/HTML校验:对文档类输出验证标签匹配、链接有效性。
内容校验:
- 安全过滤:检测输出中是否包含敏感信息(个人隐私、商业机密)、有害内容(暴力、歧视)、不符合品牌调性的表述。
- 事实核查:对关键事实性陈述进行验证。可以通过RAG检索可信来源对比,或调用专用事实验证API。
- 引用验证:如果输出包含引用或链接,验证引用是否存在、链接是否可访问、引用内容是否与表述一致。
- 一致性检查:输出内容与输入信息是否一致。例如摘要是否忠实于原文、翻译是否保留原意。
业务校验:
- 业务规则验证:输出是否符合业务逻辑。例如Agent生成的SQL是否在语法正确的前提下也满足业务约束(不查询未授权表、不执行危险操作)。
- 上下文一致性:多轮对话中,输出是否与之前的对话内容一致,不自相矛盾。
- 完整性检查:输出是否完整回答了用户问题,是否遗漏关键信息。
校验失败的处理策略:
- 自动修复:对格式类问题(如JSON多了逗号、缺了括号),尝试自动修复。
- 重新生成:在校验失败时,将错误信息附加到Prompt中重新请求模型生成。需要设置最大重试次数避免无限循环。
- 人工审核:对高风险场景(如法律文档、医疗建议),校验失败后转入人工审核流程。
- 降级输出:如果多次重新生成仍无法通过校验,输出一个安全的默认回复。
校验性能优化:校验逻辑可能成为性能瓶颈,特别是事实核查需要额外网络请求。可以采用并行校验(多维度同时检查)、分级校验(先做快速校验,通过后再做慢速校验)、异步校验(先返回结果,后台做深度校验,发现问题时追加通知)。
面试加分点:能设计多层校验流水线(快速格式校验→中等内容校验→深度事实校验),了解基于规则与基于模型的混合校验方案,能讨论校验的假阳性(误拒正确输出)和假阴性(放过错误输出)的权衡。
15. 大模型应用的异常检测与告警如何设计?
参考答案:
大模型应用的异常不同于传统Web服务——除了基础设施异常外,还需要关注模型行为异常和输出质量异常。异常检测需要覆盖多个层面。
异常类型与检测方法:
基础设施异常:
- API错误率突增——通过HTTP状态码统计,5xx错误率超过阈值告警。
- 响应延迟突增——P99延迟相比历史基线增长超过50%告警。
- 吞吐量突降——请求量相比同时段历史均值下降超过60%告警。
模型行为异常:
- 输出长度异常——平均输出Token数突然大幅增长或缩短,可能意味着模型行为变化或Prompt被篡改。
- 拒答率突增——模型频繁拒绝回答,可能是输入分布偏移或安全策略变更。
- 工具调用异常——Agent频繁调用失败或循环调用同一工具,可能是工具服务异常或模型推理错误。
- 重复输出——同一请求返回高度相似的输出(输出多样性下降),可能意味着模型温度参数配置问题。
输出质量异常:
- 用户负反馈率突增——用户标记"不满意"或"回答错误"的比例上升。
- 校验失败率突增——JSON解析失败、格式不合规的比例上升。
- 安全告警——输出触发内容安全策略的频率上升。
成本异常:
- Token消耗速率异常——每分钟Token消耗相比历史基线增长超过3倍标准差。
- 单请求Token异常——单个请求的Token消耗远超历史P99,可能意味着Prompt注入或超长输入。
检测算法:
- 固定阈值:适用于有明确SLA的指标(如错误率>5%告警)。简单直接但不适应变化。
- 统计阈值:基于历史数据的均值和标准差设定动态阈值(如均值±3σ)。适用于周期性波动的指标。
- 时间序列分解:将指标分解为趋势、周期和残差三个分量,对残差部分做异常检测。能识别趋势变化和季节性波动中的真正异常。
- 突变检测:使用CUSUM或Page-Hughey等算法检测指标均值的突变点。适合检测"突然变差"的场景。
告警分级与通知:
- P0(紧急):服务完全不可用、安全事件(输出包含严重有害内容)、成本异常飙升。立即通知值班人员(电话/短信)。
- P1(重要):错误率或延迟显著超标、降级策略被触发、核心功能受影响。通知值班人员(即时通讯工具)。
- P2(提醒):非核心指标异常、负反馈率轻微上升、Token消耗趋势偏高。记录到仪表盘,汇总日报。
告警治理:告警需要定期review,避免告警疲劳。合并相关告警(如同一根因导致的多个指标告警合并为一条),设置告警静默期(同一告警在恢复前不重复发送),定期清理长期不触发的告警规则。
面试加分点:能讨论大模型特有的异常模式(如"模型退化"——同一Prompt的输出质量随时间下降),了解在线A/B测试作为异常检测手段(将新版本与稳定版本对比),能设计从异常检测到自动回滚的闭环流程。
三、流式输出与用户体验
16. SSE流式协议的原理是什么?与WebSocket有什么区别?
参考答案:
SSE(Server-Sent Events)是一种基于HTTP的服务器推送技术,允许服务器通过单个HTTP连接持续向客户端发送数据。它是大模型流式输出的主流协议选择。
SSE的工作原理:
- 客户端通过标准HTTP GET请求建立连接,设置Accept: text/event-stream头。
- 服务器响应Content-Type: text/event-stream,保持连接不关闭。
- 服务器以data: <内容>\\n\\n的格式持续发送消息块。每个消息块以双换行符分隔。
- 连接是单向的(服务器→客户端),客户端通过关闭连接来结束接收。
- 浏览器原生支持EventSource API来接收SSE消息,也支持自动重连。
SSE vs WebSocket对比:
| 通信方向 | 单向(服务器→客户端) | 双向 |
| 协议基础 | HTTP | 独立协议(握手基于HTTP Upgrade) |
| 断线重连 | 浏览器自动重连 | 需手动实现 |
| 代理/防火墙兼容性 | 好(标准HTTP) | 可能被拦截 |
| 数据格式 | 文本(UTF-8) | 文本和二进制 |
| 连接数限制 | 浏览器对同域名有连接数限制 | 无特殊限制 |
| 实现复杂度 | 低 | 中 |
| 适合场景 | 大模型流式输出、通知推送 | 实时聊天、协同编辑 |
大模型选择SSE的原因:大模型流式输出本质是"服务器持续发送生成内容给客户端",是典型的单向推送场景。SSE基于HTTP,无需协议升级,对基础设施(负载均衡、CDN、反向代理)兼容性好,实现简单。大模型API普遍采用SSE作为流式输出协议。
SSE在大模型场景的实践细节:
- 心跳机制:服务器定期发送注释行(如: heartbeat\\n\\n)保持连接活跃,防止中间代理超时断开。
- 事件类型:通过event:字段区分不同事件类型(如event: token、event: done、event: error)。
- 重连恢复:SSE支持Last-Event-ID头,客户端重连时可以告知服务器上次接收到的最后一条消息ID,服务器据此续传。不过大模型流式输出通常不支持断点续传,断连后需要重新生成。
- 代理配置:Nginx等反向代理需要关闭缓冲(proxy_buffering off),否则流式输出会被缓冲导致延迟。
面试加分点:能讨论SSE在HTTP/2下的多路复用优势,了解SSE连接数限制的解决方案(如使用不同子域名),能比较SSE与WebSocket在大模型场景的工程权衡。
17. 大模型流式输出如何实现?后端到前端的完整链路是怎样的?
参考答案:
流式输出的完整链路涉及模型推理层、应用服务层和前端展示层三个环节,每层都需要正确处理流式数据。
模型推理层:大模型推理时采用逐Token生成的方式。每次前向传播生成一个Token后,立即通过SSE推送给下游,而不是等待完整响应。推理引擎通常提供streaming模式,通过回调或迭代器暴露Token流。
应用服务层:
- 接收模型的Token流,封装为SSE格式推送给前端。
- 在每个Token到达时立即转发,不做缓冲(或仅做极小缓冲以合并高频Token)。
- 处理异常情况:模型推理中断时发送error事件并关闭连接;正常结束时发送done事件。
- 需要注意SSE连接的管理——设置合理的超时时间、处理客户端断开(客户端断开后应及时通知模型推理层停止生成以节省资源)。
前端展示层:
- 使用EventSource或fetch+ReadableStream接收SSE数据。
- EventSource简单但只支持GET请求,且无法自定义请求头。对于需要POST请求体和自定义头(如Authorization)的场景,需要用fetch+ReadableStream替代。
- 收到Token后增量更新UI,避免每次更新都触发完整的DOM重渲染。
后端伪代码示例:
async def stream_chat(request):
async def event_generator():
try:
async for token in model_client.stream_generate(prompt=request.prompt):
yield f"data: {json.dumps({'token': token})}\\n\\n"
yield f"data: {json.dumps({'done': True})}\\n\\n"
except Exception as e:
yield f"event: error\\ndata: {json.dumps({'error': str(e)})}\\n\\n"
return StreamingResponse(event_generator(), media_type="text/event-stream")
前端伪代码示例:
const response = await fetch('/api/chat', {
method: 'POST',
headers: {'Content-Type': 'application/json', 'Authorization': `Bearer ${token}`},
body: JSON.stringify({prompt: '你好'})
});
const reader = response.body.getReader();
const decoder = new TextDecoder();
let buffer = '';
while (true) {
const {done, value} = await reader.read();
if (done) break;
buffer += decoder.decode(value, {stream: true});
const lines = buffer.split('\\n\\n');
buffer = lines.pop();
for (const line of lines) {
if (line.startsWith('data: ')) {
const data = JSON.parse(line.slice(6));
if (data.token) appendToUI(data.token);
if (data.done) finishGeneration();
}
}
}
关键工程问题:
- 背压(Backpressure)处理:如果前端消费速度慢于模型生成速度,需要在应用层做流量控制,避免内存堆积。可以使用有界队列,队列满时暂停从模型读取。
- 连接管理:SSE连接是长连接,需要注意连接泄漏。设置最大连接时长,客户端断开时及时释放服务端资源。
- 错误传递:流式传输中发生错误时,需要通过SSE事件通知前端,而不是简单地关闭连接(关闭连接会让前端误以为是正常结束)。
面试加分点:能讨论流式输出中的Token解析边界问题(SSE消息边界与Token边界不对齐),了解基于gRPC的双向流作为SSE替代方案,能设计支持断点续传的流式输出方案(通过生成ID+Token偏移量)。
18. 打字机效果如何实现?有哪些性能优化技巧?
参考答案:
打字机效果是指大模型生成内容逐字或逐词显示在前端,模拟人类打字的视觉效果。好的打字机效果既要视觉流畅,又要避免性能问题。
基础实现:
- 收到Token后不直接渲染,而是放入缓冲队列。
- 使用requestAnimationFrame或setInterval定时从队列取出Token渲染。
- 控制渲染速度(如每帧渲染1-3个字符),模拟打字节奏。
性能优化技巧:
批量渲染:不要逐字符更新DOM,而是累积几个Token后批量更新。减少DOM操作次数。可以使用DocumentFragment或虚拟DOM来优化批量更新。
Markdown增量解析:如果输出是Markdown格式,需要实时解析并渲染。但完整Markdown解析是CPU密集的,对每个Token都重新解析会导致卡顿。优化方案:
- 只对新增内容做增量解析,保留已解析部分。
- 使用支持增量更新的Markdown解析器。
- 先以纯文本显示,在段落结束时再做Markdown渲染。
滚动优化:流式输出时页面需要持续滚动到底部。直接操作scrollTop会触发布局计算,导致卡顿。优化方案:
- 使用scrollIntoView({behavior: 'auto'})或CSS scroll-behavior: auto。
- 使用IntersectionObserver检测是否需要自动滚动(用户手动上滚时暂停自动滚动)。
- 用transform: translateY替代scrollTop实现滚动,利用GPU加速。
代码高亮优化:如果输出包含代码块,实时语法高亮是性能瓶颈。优化方案:
- 在代码块未结束时以纯文本显示,代码块结束后再做完整高亮。
- 使用Web Worker做语法高亮,避免阻塞主线程。
内存管理:长对话中,已渲染的内容会持续占用DOM节点。可以设置虚拟滚动(只渲染可视区域内的消息)或对早期消息做折叠/懒渲染。
速度自适应:根据Token到达速率动态调整打字速度。如果Token到达速度快(模型生成快),打字速度也加快;如果Token到达慢(如推理压力大),打字速度放缓,保证不会出现长时间等待后突然刷出一大段内容的情况。
面试加分点:能讨论打字机效果的可访问性问题(屏幕阅读器对增量内容支持不好,需要aria-live区域),了解Content-Visibility CSS属性对长内容渲染的优化,能设计"跳过动画"功能(用户点击直接显示完整内容)。
19. 流式输出中途中断如何处理?前端和后端各需要做什么?
参考答案:
流式输出中途中断是常见的生产问题,可能由网络波动、服务端错误、用户主动取消等多种原因导致。需要从前端和后端两侧设计健壮的中断处理机制。
中断原因分类:
- 用户主动中断:用户点击"停止生成"按钮。
- 网络中断:WiFi切换、弱网环境、代理超时。
- 服务端中断:模型推理出错、服务重启、限流触发。
- 超时中断:客户端或服务端的超时机制触发。
前端处理:
- 用户主动中断:调用AbortController.abort()终止fetch请求,同时通知后端停止生成(通过独立的取消API或关闭SSE连接)。
- 网络中断检测:监听ReadableStream的error或close事件,区分正常结束(收到done事件)和异常中断(未收到done事件连接就断开)。
- 状态管理:中断时保留已接收的内容,UI上标注"生成中断",提供"继续生成"和"重新生成"选项。
- 自动重连:对于网络中断,可以尝试自动重连。但大模型流式输出通常不支持断点续传,自动重连意味着重新生成,需要谨慎设计避免重复生成。
后端处理:
- 客户端断开检测:通过HTTP连接的断开事件检测客户端是否还在接收。客户端断开后应立即停止模型推理,避免浪费算力。在Python中可以通过request.is_disconnected()或ASGI的disconnect消息检测。
- 资源清理:连接断开后释放相关资源——取消模型推理任务、清理中间状态、记录中断日志。
- 取消接口:提供独立的取消API,前端在用户主动中断时调用。取消接口需要幂等性设计,传入生成任务的ID,服务端据此取消对应的推理任务。
数据一致性:
- 中断时已输出的内容是否需要保留?取决于业务场景。对于对话场景,保留已生成内容并提供"继续"选项是更好的体验。对于结构化输出(如JSON),半截输出无法使用,需要标注为"生成失败"。
- 如果采用部分结果保存策略,需要确保保存逻辑的原子性——要么完整保存,要么不保存,避免出现数据损坏。
中断后的恢复策略:
- 继续生成:将已生成内容作为上下文,让模型从中断处继续。适用于长文本生成场景。需要注意模型可能不完美衔接,需要做一定的后处理。
- 重新生成:放弃已有内容,从头开始。适用于短回答场景。需要确认用户是否同意重新生成(避免资源浪费)。
- 保留片段:将已生成内容标记为"片段"保留在对话历史中,用户可以基于片段追问或要求重新生成。
面试加分点:能讨论中断处理的幂等性设计(取消请求可能被发送多次),了解基于WebSocket的双向通信在中断处理上的优势(服务端可以主动通知中断原因),能设计中断率的监控指标和告警阈值。
20. 前端流式渲染有哪些挑战?如何实现高性能的流式UI?
参考答案:
前端流式渲染是大模型应用用户体验的关键环节。流式输出特点——持续增量到达、内容可能包含Markdown/代码/公式——给前端渲染带来独特挑战。
挑战一:增量Markdown渲染
大模型输出通常是Markdown格式,且是逐Token到达的。完整的Markdown解析器需要完整文本,逐Token解析会遇到语法不完整的问题(如**只出现了一个星号、代码块未闭合)。
解决方案:
- 分块解析:按段落(双换行)分块,每个完整段落解析后渲染。未闭合的段落缓存在内存中等待后续Token。
- 渐进式渲染:先以纯文本渲染正在生成的段落,段落结束后再做Markdown渲染。用户看到的效果是"先出文字,再变格式"。
- 行级解析:对于简单Markdown(无嵌套),按行解析渲染。适合大多数对话场景。
挑战二:代码块实时渲染
代码块在流式输出中经历"未闭合→闭合"的过程。未闭合时代码高亮器可能报错或显示不正确。
解决方案:
- 检测到代码块开始标记(````)后切换到"代码模式",以纯文本渲染后续Token。
- 检测到代码块结束标记后,对完整代码块做语法高亮。
- 代码块渲染使用Web Worker避免阻塞主线程。
挑战三:数学公式渲染
LaTeX公式渲染(如KaTeX/MathJax)是CPU密集型操作。逐Token更新公式会导致频繁重新渲染。
解决方案:
- 检测公式定界符($…$或$$…$$),在定界符闭合后再渲染公式。
- 使用KaTeX的renderToString在Web Worker中预渲染。
挑战四:DOM性能
长输出会产生大量DOM节点。每秒数十次DOM更新会导致主线程阻塞、页面卡顿。
解决方案:
- 虚拟滚动:只渲染可视区域内的消息,上下用占位符维持滚动位置。
- 批量更新:使用requestAnimationFrame合并同一帧内的多次更新。
- CSS containment:使用contain: content或content-visibility: auto限制浏览器重排重绘范围。
- 离屏渲染:用DocumentFragment构建内容后一次性插入DOM。
挑战五:自动滚动
流式输出时需要自动滚动到底部,但用户可能想上滚查看之前的内容。
解决方案:
- 检测用户是否在底部(距离底部<100px视为"在底部")。
- 用户在底部时自动滚动;用户上滚后暂停自动滚动。
- 提供"回到最新"悬浮按钮,点击后滚动到底部并恢复自动滚动。
- 滚动使用scrollTo({top, behavior: 'smooth'}),但高频更新时用behavior: 'auto'。
面试加分点:能讨论流式渲染的Hydration问题(SSR场景下服务端渲染的HTML与客户端流式更新的衔接),了解基于Canvas的文本渲染方案(绕过DOM性能瓶颈),能设计流式输出的无障碍访问支持(aria-live、屏幕阅读器兼容)。
21. 流式输出与结构化输出如何兼容?流式场景下如何做JSON解析?
参考答案:
大模型应用中,经常需要模型输出结构化数据(如JSON),同时又希望提供流式输出体验。两者存在天然矛盾——JSON在完整生成前无法正确解析,而流式输出要求逐Token展示。
矛盾分析:
- 流式输出要求:每个Token到达后立即展示。
- JSON解析要求:完整的JSON字符串才能正确解析。
- 如果直接流式展示JSON文本,用户看到的是裸JSON语法,体验差。
- 如果等JSON完整再解析展示,失去了流式体验。
解决方案一:流式JSON解析器
使用支持增量解析的JSON解析器,在JSON尚未完整时提取已确定的部分。
工作原理:
- 维护一个解析状态栈,跟踪当前在JSON结构中的位置。
- 当检测到一个完整的key-value对时,立即提取并展示。
- 对于尚未完成的值,不展示或展示为"生成中…"。
开源工具如partial-json-parser、jsonrepair等可以实现这个功能。
解决方案二:分离流式输出和结构化数据
让模型输出两部分——自然语言部分流式展示,结构化数据部分在输出结束后解析。
Prompt设计示例:
请先流式输出分析说明,最后在<json>标签中输出结构化结果:
<json>{"action": "…", "params": {…}}</json>
前端处理:流式展示<json>标签之前的内容,检测到<json>标签后切换到"结构化数据解析"模式,标签闭合后解析JSON。
解决方案三:函数调用/工具调用模式
使用大模型的函数调用(Function Calling)能力,将结构化输出与自然语言输出分离。模型的自然语言回复通过流式输出展示,函数调用参数在流式结束后解析。
优势是结构化数据格式由Schema约束,解析可靠性高。劣势是依赖模型支持函数调用能力。
解决方案四:流式XML/自定义标签解析
让模型输出XML或自定义标签格式,流式解析器基于标签做增量解析。XML的标签结构天然支持增量解析——检测到开始标签后开始收集内容,检测到结束标签后处理内容。
JSON流式解析的工程细节:
- 转义字符处理:JSON字符串中的转义字符(如\\"、\\n)在流式中可能被拆分(如\\和n分两个Token到达),需要做缓冲合并。
- Unicode处理:UTF-8多字节字符可能被拆分,需要在Token边界做字符完整性检查。
- 错误恢复:如果最终JSON不合规,需要尝试修复(补全缺失的括号、引号)或降级为纯文本展示。
面试加分点:能讨论流式JSON解析的边界情况(嵌套对象、数组、字符串内的特殊字符),了解JSONPath在增量提取中的应用,能比较不同方案在用户体验和实现复杂度上的权衡。
四、高并发与性能
22. 大模型应用的并发控制如何设计?有哪些关键参数?
参考答案:
大模型应用的并发控制与传统Web服务不同——瓶颈不在CPU或内存,而在大模型API的并发限制和GPU推理资源。需要从客户端、网关、模型服务多个层面做并发控制。
并发控制的层次:
客户端层:
- 连接池限制:到模型API的HTTP连接池大小限制,避免连接数爆炸。
- 请求队列:超出并发限制的请求进入队列等待,队列满时拒绝或降级。
- 客户端限流:基于令牌桶或漏桶算法限制请求发送速率。
网关层:
- 全局限流:对到模型服务的总请求量做全局限制,防止超出模型服务的并发上限。
- 租户限流:按用户/业务线/API Key维度做限流,保证公平性,防止某一租户耗尽资源。
- 优先级队列:不同请求设置不同优先级。高优先级请求(如实时对话)优先调度,低优先级请求(如批量处理)在空闲时处理。
模型服务层:
- 推理并发限制:GPU显存决定了最大并发推理数。超过限制的请求需要排队。
- KV Cache管理:限制同时缓存的会话数,超出时按LRU淘汰。
- 批处理窗口:控制动态批的时间窗口,平衡吞吐量和延迟。
关键参数:
- max_concurrent_requests:最大并发请求数。根据模型服务容量和SLA确定。
- max_queue_size:最大队列长度。队列过长导致延迟不可接受,需要设置合理上限。
- request_timeout:请求超时时间。包括排队等待时间+推理时间。
- max_tokens_per_request:单请求最大Token数。防止单个请求占用过多资源。
- rate_limit:请求速率限制。如每秒最多N个请求。
令牌桶限流实现:
- 桶容量(burst):允许短时间突发请求量。
- 补充速率(rate):长期平均允许的请求速率。
- 请求到达时从桶中取令牌,桶空时请求等待或拒绝。
并发控制的挑战:
- 长请求问题:大模型请求持续时间长(几秒到几十秒),传统基于QPS的限流可能不适用。需要基于并发数而非QPS做限流。
- 资源不均匀:不同请求消耗的Token数差异大,单纯按请求数限流不够精确。可以按Token消耗量做限流。
- 流式请求的特殊性:流式请求是长连接,并发连接数容易超标。需要对流式和非流式请求分别做并发控制。
面试加分点:能设计基于Token预算的并发控制(按Token消耗量而非请求计数限流),了解大模型推理引擎的调度策略(如连续批处理Continuous Batching),能讨论并发控制与用户体验的权衡(限流过严导致排队等待,过松导致服务降级)。
23. 连接池管理在大模型应用中有哪些特殊考虑?
参考答案:
大模型应用的连接池管理与传统Web服务有显著差异——连接持续时间长(流式输出可能持续数十秒)、连接数容易爆炸、需要支持SSE长连接。
连接池设计要点:
连接生命周期管理:
- 大模型API请求分为短请求(非流式,几秒内返回)和长请求(流式,可能持续30秒以上)。两种请求应使用不同的连接池。
- 非流式连接池:设置max_connections(如100)、connection_timeout(如5s)、read_timeout(如30s)。
- 流式连接池:设置更大的max_connections(如500)、更长的read_timeout(如120s)、更短的idle_timeout(如30s,快速回收空闲的流式连接)。
连接复用:
- HTTP/2多路复用:多个请求可以复用同一TCP连接,显著减少连接数。大模型API如果支持HTTP/2,应优先使用。
- 连接保活:设置keep-alive头,避免频繁建立连接。但要注意长时间保活的连接可能被中间代理断开,需要发送心跳包。
连接泄漏防护:
- 流式连接是最容易泄漏的——如果客户端断开但服务端没有正确关闭连接,连接会一直占用资源。
- 使用try-finally或上下文管理器确保连接一定被释放。
- 设置连接的最大生命周期(如5分钟),超时后强制关闭重建,防止老化连接导致问题。
- 定期检查连接池中连接的状态,清理已失效的连接。
连接池监控:
- 活跃连接数、空闲连接数、等待获取连接的线程数。
- 连接获取平均等待时间——如果持续上升,说明连接池不够大。
- 连接泄漏检测——记录连接获取和释放的调用栈,找出未正确释放连接的代码路径。
与反向代理的配合:
- Nginx的proxy_read_timeout需要大于流式输出的最大持续时间,否则Nginx会在输出中途断开连接。
- proxy_buffering off避免Nginx缓冲流式输出。
- proxy_http_version 1.1确保支持长连接。
面试加分点:能讨论HTTP/2连接复用在流式输出场景的优势和注意事项,了解连接池的"惊群效应"(大量请求同时争抢空闲连接),能设计连接池的自适应调整策略(根据负载动态调整连接池大小)。
24. 大模型应用中异步处理有哪些模式?如何选择同步和异步?
参考答案:
大模型应用中,请求处理时间通常较长(几秒到几十秒),同步处理会导致请求线程长时间阻塞,降低系统吞吐量。异步处理是提升并发能力的关键手段。
异步处理模式:
模式一:异步IO(单请求内)
- 使用async/await或协程,在等待模型响应时不阻塞线程。
- 一个线程可以处理多个并发请求,通过事件循环切换。
- 适用于流式输出场景——在等待下一个Token到达时可以处理其他请求。
模式二:异步任务队列
- 请求到达后不立即处理,而是放入消息队列(如Redis、RabbitMQ、Kafka)。
- Worker进程从队列消费任务,调用模型处理,结果写入存储。
- 客户端通过轮询或WebSocket获取结果。
- 适用于批处理、长耗时任务(如文档摘要、批量翻译)。
模式三:Reactive流式处理
- 基于Reactor/RxJS等响应式编程框架,将模型输出建模为数据流。
- 通过map、filter、flatMap等操作符组合处理逻辑。
- 天然支持背压(Backpressure),消费者可以控制消费速率。
模式四:Callback回调模式
- 请求时注册回调函数,模型处理完成后触发回调。
- 适用于Webhook场景——模型服务处理完成后回调通知业务服务。
同步vs异步的选择原则:
| 实时对话 | 异步IO+流式输出 | 用户需要实时看到生成进度 |
| 文档摘要 | 异步任务队列 | 处理时间长,不需要实时响应 |
| 批量分类 | 异步任务队列 | 可并行处理,关注吞吐量 |
| 简单问答 | 同步处理 | 延迟可控,实现简单 |
| Agent多步推理 | 异步IO+中间状态推送 | 多步推理耗时长,需要展示进度 |
| RAG检索+生成 | 异步IO | 检索和生成分步执行 |
异步处理的工程挑战:
- 错误传播:异步任务的错误不像同步调用那样直接抛出,需要通过回调、Promise reject或队列消息传递。
- 超时处理:异步任务可能"悬挂"(既不完成也不失败),需要设置超时机制和心跳检测。
- 结果通知:异步任务完成后需要通知客户端。可选方案:轮询(简单但浪费资源)、WebSocket推送(实时但复杂)、Server-Sent Events(适合单向通知)、Webhook(需要客户端可达)。
- 任务幂等性:异步任务可能因为网络问题被重复提交,需要幂等性设计(如基于request_id去重)。
面试加分点:能讨论异步编程的"回调地狱"问题及其解决方案(async/await、协程),了解Virtual Thread(虚拟线程)在Java异步编程中的应用,能设计异步任务的进度跟踪和取消机制。
25. 队列与削峰在大模型应用中如何设计?
参考答案:
大模型应用的流量模式经常出现突发高峰——如产品发布、营销活动、新闻事件。队列与削峰机制保护后端模型服务不被流量冲垮,同时保证请求的公平处理。
队列架构设计:
单级队列:
- 所有请求进入一个队列,Worker按FIFO消费。
- 简单但无法区分优先级,低优先级任务可能阻塞高优先级任务。
多级优先级队列:
- 按业务场景设置不同优先级队列:实时对话(P0)、API调用(P1)、批量处理(P2)、后台任务(P3)。
- Worker优先消费高优先级队列,高优先级队列为空时才消费低优先级队列。
- 需要防止低优先级请求"饿死"——可以采用加权轮转(Weighted Round Robin)或老化机制(等待时间越长优先级越高)。
队列参数设计:
- 队列容量:队列最大长度。过长导致等待时间不可接受,过短导致大量拒绝。根据SLA确定——如果P99延迟要求10秒,队列长度不应超过10秒 × 吞吐量。
- 消费并发数:Worker数量。根据模型服务并发能力确定,通常等于模型服务最大并发数。
- 请求超时:请求在队列中的最大等待时间。超时后直接返回错误,避免用户长时间等待。
- 最大重试次数:处理失败的请求是否重新入队。
削峰策略:
- 请求丢弃:队列满时直接拒绝新请求,返回429 Too Many Requests。配合客户端重试实现削峰。
- 降级处理:队列接近满时,将部分请求降级处理(如使用更小的模型、跳过RAG检索、返回缓存结果)。
- 速率限制:在入队前做速率限制,超过速率的请求直接拒绝或降级。
- 弹性扩容:队列长度超过阈值时自动扩容Worker(如果模型服务有弹性能力)。
队列实现选型:
- Redis List:简单队列,适合低延迟场景。LPUSH+BRPOP实现生产消费。
- Redis Stream:支持消费者组、消息确认、持久化,适合需要可靠投递的场景。
- RabbitMQ:功能完善的消息队列,支持优先级队列、死信队列、延迟队列。
- Kafka:高吞吐量,适合日志类和事件流场景。不适合需要低延迟的场景。
死信队列(DLQ):处理失败的请求进入死信队列,供后续分析和重试。DLQ中的请求需要人工或自动化流程处理——分析失败原因、修复后重新提交。
面试加分点:能讨论队列的公平性问题(FIFO可能导致队头阻塞),了解基于Token预算的队列调度(按Token消耗量而非请求数分配资源),能设计队列的可观测性指标(入队速率、出队速率、队列深度、处理延迟分布)。
26. GPU资源调度策略有哪些?如何提高GPU利用率?
参考答案:
GPU是大模型推理中最昂贵和最稀缺的资源。合理的调度策略可以显著提高GPU利用率,降低单位推理成本。
GPU资源调度策略:
策略一:连续批处理(Continuous Batching)
- 传统的静态批处理需要等待批次填满或超时后才统一推理,导致延迟和吞吐量不平衡。
- 连续批处理在每次推理迭代时动态调整批次——新请求可以随时加入批次,已完成的请求随时退出。
- 优势:减少排队等待时间,提高GPU利用率。新请求不需要等待当前批次完成。
- 这是当前主流推理引擎的核心调度策略。
策略二:KV Cache管理
- KV Cache是大模型推理中缓存注意力机制的Key和Value矩阵,避免重复计算。
- KV Cache占用大量GPU显存(与序列长度和批大小成正比),是GPU利用率的关键瓶颈。
- PagedAttention:将KV Cache组织为固定大小的页面(类似操作系统的虚拟内存),按需分配和回收,减少显存碎片。
- 前缀缓存:对共享相同前缀的请求(如System Prompt),缓存前缀的KV Cache,多个请求复用。
- LRU淘汰:显存不足时按最近最少使用原则淘汰KV Cache页面。
策略三:多模型GPU共享
- 多个模型部署在同一GPU上,通过时间片轮转或按需加载切换。
- 适用于低流量场景,提高GPU利用率。但模型切换有开销(权重加载),需要合理调度。
- 可以使用模型热加载技术——将模型权重常驻CPU内存,GPU按需加载,减少切换开销。
策略四:弹性伸缩
- 根据负载动态调整GPU实例数。负载低时缩容节省成本,负载高时扩容保证SLA。
- GPU实例启动时间较长(加载模型权重),需要预热机制——提前扩容,避免冷启动延迟。
- 可以基于队列深度、GPU利用率、请求延迟等指标触发扩缩容。
GPU利用率优化:
- 批大小调优:批大小过小导致GPU计算单元利用不充分,过大导致显存不足。需要根据模型大小和序列长度找到最优批大小。
- 量化推理:将模型从FP16量化为INT8或INT4,减少显存占用和计算量,提高吞吐量。精度损失需要在业务可接受范围内。
- 张量并行:大模型无法放入单个GPU时,将模型权重切分到多个GPU上,通过AllReduce通信同步。适合超大模型场景。
- 流水线并行:将模型的不同层分布到不同GPU上,请求在GPU间流水线处理。通信开销比张量并行小,但延迟更高。
监控指标:
- GPU利用率(GPU Utilization)——GPU计算单元的繁忙程度。
- GPU显存使用率——已用显存/总显存。
- 请求吞吐量——每秒处理的请求数或Token数。
- 平均延迟和P99延迟。
- KV Cache命中率——前缀缓存命中的比例。
面试加分点:能讨论Continuous Batching的实现细节(iteration级别的调度、prefill和decode阶段的不同调度策略),了解Speculative Decoding(投机解码)作为推理加速手段,能设计基于SLA和成本的多目标GPU调度优化方案。
27. 大模型推理加速有哪些技术方案?
参考答案:
大模型推理加速是工程化落地的核心问题。加速技术从算法优化、系统优化和硬件优化三个层面展开。
算法层面加速:
KV Cache:
- 缓存已计算Token的Key和Value矩阵,避免重复计算。是所有推理引擎的基础优化。
- 空间换时间——KV Cache占用显存但大幅减少计算量。
Speculative Decoding(投机解码):
- 使用一个小模型快速生成草稿Token,大模型验证草稿的正确性。
- 验证通过的Token直接接受,验证失败的Token由大模型重新生成。
- 小模型生成速度快,大模型验证(并行验证多个Token)比逐个生成快。
- 加速效果取决于小模型与大模型的接受率——接受率越高加速越明显。
注意力优化:
- Flash Attention:通过分块计算和IO优化减少HBM(高带宽内存)读写,显著加速注意力计算。
- Paged Attention:优化KV Cache的内存管理,减少碎片,提高显存利用率。
- Sliding Window Attention:限制注意力范围到一个滑动窗口,减少长序列的计算量。
量化:
- 权重量化:将模型权重从FP16量化为INT8或INT4,减少显存占用和内存带宽压力。
- KV Cache量化:将KV Cache从FP16量化为INT8,减少KV Cache的显存占用。
- 激活量化:将激活值也量化,配合权重量化实现全整数的推理计算。
- 量化精度损失需要评估——INT4量化通常有2-5%的精度下降,需要根据业务场景权衡。
系统层面加速:
批处理优化:
- 连续批处理:动态调整批次内容,最大化GPU利用率。
- Prefill-Decode分离:Prefill阶段(处理输入Prompt)和Decode阶段(生成Token)的计算特征不同,分别优化调度。Prefill是计算密集型,Decode是内存密集型,分离调度可以分别优化。
编译优化:
- 算子融合:将多个小算子融合为一个大算子,减少Kernel Launch开销和中间结果读写。
- 图优化:计算图的常量折叠、死代码消除、布局变换等优化。
- 自动向量化:编译器自动将循环展开为SIMD指令,提高计算密度。
硬件层面加速:
- 高性能GPU:使用最新的GPU架构,提高计算能力和内存带宽。
- 多GPU并行:张量并行(Tensor Parallelism)和流水线并行(Pipeline Parallelism)分布计算到多个GPU。
- 专用加速芯片:使用专为AI推理设计的芯片(如TPU、NPU),在特定场景下比GPU更高效。
推理加速的工程选择:
- 加速技术不是互斥的,可以组合使用。如同时使用KV Cache + Flash Attention + 量化 + 连续批处理。
- 不同场景的优化重点不同——实时对话优先降低延迟,批量处理优先提高吞吐量。
- 加速效果需要端到端评估——某项优化可能提高了推理速度但增加了预处理开销。
面试加分点:能讨论Speculative Decoding的接受率优化策略(如调整小模型的温度参数、使用模型蒸馏提高小模型与大模型的对齐度),了解Prefix Caching在RAG场景的特殊优势(检索结果作为前缀缓存),能设计推理加速的A/B测试方案。
28. KV Cache优化有哪些策略?如何平衡显存占用和性能?
参考答案:
KV Cache是大模型自回归推理的核心机制——缓存每层注意力中已计算Token的Key和Value矩阵,避免对历史Token重复计算。KV Cache的大小与序列长度和批大小成正比,是推理过程中最大的显存消费者,通常占用的显存是模型权重本身的数倍。
KV Cache的显存挑战:
- 一个典型的对话场景,输入Prompt 1000 Token + 输出1000 Token,KV Cache可能占用数GB显存。
- 多并发请求时KV Cache占用线性增长,很快耗尽GPU显存。
- 显存不足导致批大小受限,GPU利用率下降。
优化策略一:PagedAttention(分页缓存)
将KV Cache组织为固定大小的"页面"(Page),类似操作系统的虚拟内存管理:
- 每个页面存储固定数量Token的KV矩阵(如16个Token)。
- 按需分配页面,不需要预分配连续内存。
- 请求之间不产生内存碎片,显存利用率接近100%。
- 支持写时复制(Copy-on-Write),前缀相同的请求共享页面。
优化策略二:Prefix Caching(前缀缓存)
缓存共享前缀的KV Cache,多个请求复用:
- 在对话场景中,System Prompt和Few-shot示例是所有请求共享的前缀。
- 在RAG场景中,检索到的文档内容是共享前缀。
- 前缀缓存命中时直接复用KV Cache,跳过Prefill计算,显著降低首Token延迟。
- 需要设计缓存失效策略——当Prompt模板变更时需要失效对应的前缀缓存。
优化策略三:KV Cache量化
将KV Cache从FP16量化为INT8或INT4:
- INT8量化减少50%的KV Cache显存占用,精度损失通常可忽略。
- INT4量化减少75%的显存占用,但精度损失较大,需要评估业务影响。
- 量化可以与反量化结合——在计算时反量化为FP16,计算完再量化存储。现代GPU支持INT8矩阵运算,可以避免反量化开销。
优化策略四:KV Cache淘汰策略
当显存不足时,按策略淘汰KV Cache:
- LRU(最近最少使用):淘汰最久未使用的会话。简单但可能淘汰即将使用的会话。
- LFU(最不常用):淘汰使用频率最低的会话。适合有明显冷热分布的场景。
- 基于TTL:设置KV Cache的生存时间,超时自动淘汰。适合短对话场景。
- 基于优先级:高优先级请求的KV Cache优先保留,低优先级的优先淘汰。
优化策略五:KV Cache Offloading
将不活跃的KV Cache从GPU显存卸载到CPU内存:
- 利用GPU-CPU间的高带宽互联(如NVLink、PCIe)做数据搬运。
- 活跃会话的KV Cache保留在GPU,不活跃的卸载到CPU。
- 会话重新活跃时将KV Cache从CPU加载回GPU。
- 适合长会话场景——单个会话的KV Cache可能超过GPU显存,但可以通过Offloading支持。
显存与性能的平衡:
- KV Cache越大,可支持的并发越多,但显存占用越高。
- 量化减少显存但引入精度损失,需要在业务指标上验证。
- 淘汰策略减少显存但可能导致缓存未命中,增加重计算开销。
- Offloading减少GPU显存但增加数据搬运延迟。
面试加分点:能讨论PagedAttention的实现细节(页面表、地址翻译、碎片整理),了解KV Cache-aware调度(调度器考虑KV Cache复用来决定请求执行顺序),能设计KV Cache的监控指标(缓存命中率、显存利用率、淘汰率)。
29. 批处理与动态批处理在大模型推理中如何实现?
参考答案:
批处理是提高大模型推理吞吐量的核心手段——将多个请求合并为一个批次统一推理,充分利用GPU的并行计算能力。但大模型推理的批处理比传统深度学习推理复杂得多。
静态批处理的问题:
- 需要等待批次填满或超时才能开始推理,增加排队延迟。
- 批次内所有请求必须同时完成才能返回,短请求等待长请求造成"队头阻塞"。
- 输入长度不一时需要Padding到最长请求的长度,浪费计算资源。
动态批处理(Dynamic Batching):
- 在推理过程中动态调整批次内容——新请求随时加入,完成的请求随时退出。
- 不需要等待批次填满,到达即可加入当前推理迭代。
- 短请求完成后立即返回,不影响长请求的继续生成。
Continuous Batching(连续批处理)的实现:
大模型推理分为两个阶段:
- Prefill阶段:处理输入Prompt,计算KV Cache。计算密集型,GPU利用率高。
- Decode阶段:逐Token生成,每步生成一个Token。内存带宽密集型,GPU利用率低。
连续批处理在迭代级别做调度:
动态批处理的关键参数:
- max_batch_size:最大批大小。受GPU显存限制,需要在吞吐量和延迟间权衡。
- max_tokens_in_batch:批次中所有请求的Token总数上限。防止超长Prompt导致显存溢出。
- prefill_timeout:等待Prefill的超时时间。平衡排队延迟和批大小。
- iteration_timeout:Decode迭代间隔的最大时间。防止某个长请求独占GPU。
批处理调度的优化策略:
长度感知调度:将输入长度相近的请求分到同一批次,减少Padding浪费。可以通过维护不同长度区间的队列实现。
Prefill-Decode分离调度:Prefill和Decode对资源的需求不同,可以分别调度。如集中做Prefill(提高计算密度),再做Decode(利用已有的KV Cache)。
优先级感知调度:高优先级请求优先加入批次,低优先级请求在空闲时处理。
预测性调度:根据请求的输入长度预测输出长度,据此做批次规划,避免长输出请求集中在同一批次。
批处理的性能指标:
- 吞吐量:每秒处理的Token数或请求数。
- 平均批大小:批次中平均包含的请求数。越大说明GPU利用越充分。
- Padding率:Padding Token占总Token的比例。越低说明长度分组越有效。
- 排队延迟:请求从到达到开始推理的等待时间。
面试加分点:能讨论Continuous Batching中Prefill对正在进行的Decode的干扰(计算资源抢占),了解Chunked Prefill(将长Prompt的Prefill分块,避免单次Prefill占用过多时间),能设计批处理的自适应参数调整策略(根据负载动态调整max_batch_size)。
五、监控与运维
30. AI应用需要监控哪些核心指标?与传统应用监控有什么区别?
参考答案:
AI应用的监控比传统Web应用复杂得多——除了基础设施指标外,还需要监控模型行为、输出质量、Token消耗等AI特有维度。
监控指标体系:
一、基础设施层:
- API可用性:模型服务API的成功率、错误率。
- 响应延迟:P50/P90/P99延迟,首Token延迟(TTFT),Token间延迟。
- 吞吐量:每秒请求数(QPS)、每秒Token数(TPS)。
- 资源使用率:CPU、内存、GPU利用率、网络带宽。
- 连接数:活跃连接数、连接池使用率。
二、模型行为层:
- Token消耗:输入Token、输出Token、总Token消耗的实时速率和历史趋势。
- 输出长度分布:平均输出长度、输出长度分布,突增或突降可能意味着模型行为变化。
- 工具调用统计:Agent场景下工具调用的成功率、频率、耗时。
- 拒绝率:模型拒绝回答的比例,突增可能意味着输入分布偏移或安全策略变化。
- 重试率:因输出不合规而重新生成的比例。
三、输出质量层:
- 用户反馈率:用户标记"满意"/"不满意"的比例。
- 输出校验通过率:JSON解析成功率、格式合规率。
- 事实准确率:通过RAG交叉验证或人工抽检的事实准确率。
- 安全告警数:输出触发内容安全策略的次数。
- 幻觉率:基于抽样评估或自动检测的幻觉比例。
四、成本层:
- 单位成本:每请求平均成本、每Token平均成本。
- 成本趋势:日/周/月Token消耗趋势和费用。
- 成本归因:按业务线、用户、功能模块拆分的成本分布。
与传统应用监控的区别:
- 非确定性:传统应用对相同输入产生相同输出,监控关注错误和延迟。AI应用对相同输入可能产生不同输出,还需要监控输出质量和一致性。
- 长尾问题:AI应用中个别请求可能消耗异常多的资源(超长Prompt、超长输出),需要更关注长尾分布而非均值。
- 质量退化:传统应用的Bug通常在代码变更后立即出现。AI应用的质量可能随时间缓慢退化(模型漂移、用户输入分布变化),需要长期趋势监控。
- 成本可变性:传统应用单位请求成本基本固定。AI应用的成本与Token消耗量正相关,波动大。
监控架构设计:
- 指标采集:通过中间件/SDK在请求链路中自动采集指标,推送到监控系统(如Prometheus)。
- 日志记录:结构化日志记录请求/响应详情,供排查和审计使用。注意脱敏处理。
- 链路追踪:分布式追踪覆盖从API入口到模型推理的完整链路。
- 仪表盘:实时仪表盘展示核心指标,支持按业务线/模型/时间维度筛选。
- 告警:基于阈值和异常检测算法的自动告警。
面试加分点:能设计AI应用的SLI/SLO体系(如"99%的请求在3秒内返回首Token"),了解基于统计过程控制(SPC)的质量监控方法,能讨论监控数据的隐私和合规问题。
31. 日志收集与链路追踪在AI应用中如何实现?
参考答案:
AI应用的日志和链路追踪需要覆盖从用户请求到模型推理的完整链路,且要处理大模型特有的长文本、流式输出、多步推理等场景。
核心日志字段:
- request_id:唯一请求标识,贯穿整个调用链。
- user_id / tenant_id:用户和租户标识,用于成本归因和问题定位。
- prompt:输入Prompt(可能需要脱敏或截断)。
- model:调用的模型标识。
- input_tokens / output_tokens:Token消耗。
- latency:总延迟、首Token延迟、推理延迟。
- status:请求状态(成功/失败/降级)。
- error_type / error_message:错误信息。
- tools_called:Agent场景下调用的工具列表。
- timestamp:时间戳。
日志分级:
- INFO:正常请求日志,包含核心字段。
- WARN:降级、重试、超长输入等异常但可处理的情况。
- ERROR:请求失败、模型服务不可用等错误。
- DEBUG:详细调试信息(如完整Prompt、完整输出),仅开发环境开启。
日志脱敏:
- Prompt中可能包含用户隐私(姓名、电话、身份证号),日志中需要做脱敏处理。
- 可以使用正则表达式或NER模型检测敏感信息并替换为占位符。
- 脱敏规则需要可配置——不同业务场景的敏感信息定义不同。
日志存储:
- 热数据(最近7天):Elasticsearch或Loki,支持快速检索。
- 冷数据(7天以上):对象存储(如S3),按日期分区,用于长期分析和审计。
- 日志保留策略根据合规要求确定——金融场景可能需要保留一年以上。
链路追踪:
AI应用的调用链通常为:用户请求 → API网关 → 应用服务 → RAG检索 → 模型推理 → 工具调用 → 后处理 → 响应。
追踪设计:
- 每个环节创建一个Span,记录开始时间、结束时间、关键属性。
- 父子Span通过trace_id和parent_span_id关联。
- 对于流式输出,每个Token的推送可以不创建独立Span(数据量太大),但需要记录首Token时间和最后Token时间。
AI特有的追踪场景:
- 多步推理:Agent场景下一次用户请求可能触发多次模型调用和工具调用,需要在追踪中清晰展示每一步的输入、输出和耗时。
- RAG检索:追踪需要记录检索查询、检索结果、排序分数,帮助排查RAG质量问题。
- 流式输出:追踪中记录流式输出的开始时间、结束时间、Token数量,计算Token速率。
- 降级和重试:当请求被降级或重试时,追踪中记录降级原因和重试次数。
链路追踪的实现:
- 使用OpenTelemetry SDK在应用代码中埋点。
- 对于模型API调用,通过HTTP拦截器自动创建Span。
- 对于工具调用,在工具执行前后创建Span。
- 追踪数据推送到Jaeger/Zipkin等分布式追踪系统。
日志与追踪的关联:
- 日志中包含trace_id,可以通过trace_id在追踪系统中找到对应的调用链。
- 追踪Span中也可以附加关键日志事件(如"降级触发"、“输出校验失败”),方便在追踪视图中快速定位问题。
面试加分点:能讨论大模型日志的存储成本优化(采样策略——对正常请求采样记录,异常请求全量记录),了解基于OpenTelemetry的AI应用可观测性标准(Semantic Conventions for GenAI),能设计基于日志的自动问题诊断流程。
32. Prompt版本管理如何做?有哪些最佳实践?
参考答案:
Prompt是大模型应用的核心"代码"——它决定了模型的行为和输出质量。然而Prompt通常以字符串形式散落在代码中,缺乏版本管理、变更追溯和质量保障。Prompt版本管理是AI工程化的重要环节。
Prompt版本管理的挑战:
- Prompt是自然语言文本,变更不像代码那样有明确的语法边界,容易"改一个词就变味"。
- 同一Prompt在不同模型上的表现不同,需要按模型维度管理版本。
- Prompt的效果需要通过评估才能确定,变更后需要回归测试。
- Prompt通常包含变量模板,版本管理需要同时追踪模板和变量。
版本管理方案:
方案一:基于Git的文本管理
- 将Prompt存储为独立文件(如prompts/rag_summary_v1.txt),纳入Git版本控制。
- 优点:简单直接,与代码版本控制一致。
- 缺点:缺乏Prompt特有的元数据管理(如评估结果、模型兼容性)。
方案二:Prompt管理平台
- 使用专门的Prompt管理工具,提供版本管理、评估、A/B测试等功能。
- 优点:功能完善,支持评估和对比。
- 缺点:引入额外依赖,可能需要适配现有架构。
方案三:配置中心管理
- 将Prompt存储在配置中心(如Apollo、Nacos),通过配置版本管理实现Prompt版本控制。
- 优点:与现有基础设施集成,支持热更新。
- 缺点:配置中心通常不针对Prompt做优化,缺乏评估能力。
最佳实践:
Prompt模板化:使用模板引擎管理Prompt,将变量与模板分离。模板文件包含固定文本和变量占位符,运行时填充变量。这样版本管理只需追踪模板变更。
Prompt代码化:将Prompt视为代码,遵循代码管理的最佳实践——Code Review、分支管理、变更说明。Prompt变更需要经过评审,不能随意修改。
评估驱动变更:任何Prompt变更都需要经过评估集测试,只有评估分数不低于当前版本才能上线。评估集应覆盖典型场景、边界情况和已知问题。
版本标签:为每个Prompt版本打标签,包含版本号、适配的模型、评估分数和评估日期、变更说明、状态(草稿/测试中/生产/已废弃)。
灰度发布:新版本Prompt先灰度到小比例流量,监控关键指标(用户反馈、校验通过率、延迟),指标正常后逐步扩大流量比例。
回滚机制:保留历史版本的Prompt,发现问题可以快速回滚到上一个稳定版本。回滚不需要重新部署,通过配置切换即可。
Prompt差异对比:工具支持版本间的差异对比(类似Git diff),帮助评审者理解变更内容。对于关键场景,甚至可以做Token级别的差异分析。
面试加分点:能讨论Prompt的可复用性设计(通过Prompt组合和继承减少重复),了解基于自动优化的Prompt调优工具(如DSPy),能设计Prompt变更的CI/CD流水线(变更 → 评估 → 灰度 → 全量)。
33. A/B测试与灰度发布在AI应用中如何实现?
参考答案:
A/B测试和灰度发布是AI应用迭代的核心手段——验证Prompt变更、模型升级、新功能的效果,同时控制变更风险。
A/B测试设计:
流量分配:
- 将用户请求按比例分配到不同版本(如控制组90%用旧版,实验组10%用新版)。
- 分配策略需要保证一致性——同一用户始终进入同一组,避免体验不一致。
- 分配可以基于用户ID哈希、随机分流或按业务维度分层(如新用户/老用户分别做A/B)。
评估指标:
- 业务指标:用户满意度、任务完成率、留存率等。这些指标变化缓慢,需要较长时间收集。
- 过程指标:校验通过率、重试率、工具调用成功率。这些指标实时变化,可以快速反馈。
- 成本指标:Token消耗、平均延迟。新版本是否更贵或更慢。
- 安全指标:安全告警数、用户投诉数。新版本是否引入安全风险。
统计显著性:A/B测试结果需要做统计显著性检验,避免因随机波动导致错误结论。常用的方法包括t检验、卡方检验、贝叶斯A/B测试。需要预先计算所需样本量,确保测试有足够的统计功效。
AI应用A/B测试的特殊性:
- 非确定性输出:同一用户对同一Prompt,不同版本的输出不同,但用户反馈可能受随机因素影响。需要更多样本才能得出可靠结论。
- 长尾影响:个别极端案例(如超长输出、幻觉)可能在均值统计中被掩盖。需要关注分布而非仅看均值。
- 学习效应:用户对新版本可能有一个适应期,初期的反馈可能不代表长期效果。需要设置"预热期"再开始统计。
灰度发布设计:
灰度策略:
- 按流量比例:从1%开始,逐步增加到5%、10%、50%、100%。每一步观察指标,无异常才进入下一步。
- 按用户群体:先内部用户 → Beta用户 → 小范围生产用户 → 全量用户。
- 按功能模块:先在非核心功能灰度,再扩展到核心功能。
灰度门槛:
- 设定明确的"继续/暂停/回滚"规则。如:错误率上升超过2% → 暂停灰度;用户负反馈率上升超过5% → 自动回滚。
- 灰度的每一步都需要设定观察期(如24小时),确保时间足够发现问题。
自动回滚:灰度期间监控系统实时检测指标,触发回滚条件时自动切回旧版本。自动回滚需要确保切换的原子性和快速性——用户不应感知到服务中断。
Prompt/模型变更的灰度:
- Prompt变更的灰度相对简单——通过配置中心切换不同版本的Prompt,不需要重新部署服务。
- 模型升级的灰度更复杂——新模型需要预热(加载权重),灰度期间新旧模型同时运行,资源消耗翻倍。
面试加分点:能讨论多变量A/B测试(同时测试多个变更的交互效应),了解基于Multi-Armed Bandit的动态流量分配(自动将更多流量分配给表现好的版本),能设计灰度发布的自动化决策流程。
34. 模型漂移检测如何实现?如何应对模型行为变化?
参考答案:
模型漂移是指大模型的行为随时间发生变化的现象——同一Prompt的输出质量、格式、风格发生变化。漂移可能由模型版本更新、输入分布变化、安全策略调整等多种因素导致。
漂移类型:
模型版本漂移:模型提供方更新模型版本后,同一Prompt的输出发生变化。这种变化可能是改进也可能是退化。
输入分布漂移:用户输入的Prompt分布发生变化——新话题增多、输入长度变化、语言风格变化。模型在新分布上的表现可能不如旧分布。
概念漂移:模型应该输出的"正确答案"随时间变化。如政策法规更新后,之前正确的回答可能不再准确。
安全策略漂移:模型提供方调整内容安全策略,导致之前可以输出的内容现在被拒绝,或拒绝阈值变化。
漂移检测方法:
一、基于评估集的定期检测:
- 维护一个固定的评估集(涵盖典型场景和边界情况),定期对模型运行评估集。
- 对比当前评估分数与历史基线,分数下降超过阈值时告警。
- 评估集需要人工标注"期望输出",通过自动评估或人工评估打分。
二、基于统计指标的检测:
- 监控输出特征的统计分布——平均输出长度、Token分布、拒绝率、JSON解析成功率。
- 使用统计检验(如KS检验、卡方检验)检测分布是否发生显著变化。
- 设定控制图(Control Chart),指标超出控制限(如均值±3σ)时告警。
三、基于用户反馈的检测:
- 监控用户负反馈率("不满意"标记、投诉、重试率)的趋势。
- 负反馈率上升是模型漂移的强信号——用户是最直接的"评估器"。
- 需要区分漂移导致的负反馈和功能缺陷导致的负反馈。
四、基于影子评估的检测:
- 对生产流量做采样,用自动评估器对输出打分。
- 实时跟踪评分趋势,发现下降时告警。
- 影子评估不影响生产流量,但增加额外成本。
漂移应对策略:
一、快速回滚:检测到严重漂移时,快速回滚到上一个稳定版本。需要维护历史版本的模型和Prompt快照。
二、Prompt适配:针对漂移做Prompt调整。如模型升级后输出格式变化,可以通过修改Prompt中的格式说明来补偿。
三、评估集更新:定期更新评估集,纳入新的典型场景和已知问题。
四、多模型备份:不依赖单一模型,维护备用模型。当主模型发生严重漂移时切换到备用模型。
五、用户沟通:对影响用户体验的漂移,及时告知用户可能出现的变化。
漂移检测的工程挑战:
- 评估集维护成本:人工标注评估集是昂贵的,需要建立高效的标注流程。
- 评估集污染:评估集可能被意外泄露到训练数据中,导致评估分数虚高。需要定期刷新评估集。
- 多维度漂移:模型可能在某些能力上改进、某些能力上退化,需要按维度分别监控。
- 归因困难:发现漂移后,确定漂移的根因需要大量排查。
面试加分点:能讨论基于对抗样本的漂移检测(生成能区分新旧模型的Prompt),了解模型注册表(Model Registry)在版本管理中的作用,能设计从漂移检测到自动回滚的闭环流程。
35. 线上问题排查方法论是什么?AI应用与传统应用排查有什么不同?
参考答案:
AI应用的线上问题排查比传统应用复杂——问题的根因可能不在代码逻辑中,而在Prompt设计、模型行为、数据质量等"非代码"因素中。需要建立系统化的排查方法论。
排查方法论:
第一步:问题定界
确定问题属于哪个层面:
- 基础设施问题:服务不可用、网络超时、资源耗尽。通过基础设施监控快速定位。
- 应用逻辑问题:代码Bug、配置错误、依赖服务故障。通过日志和链路追踪定位。
- 模型行为问题:输出质量下降、格式错误、幻觉、拒答。需要复现请求并分析Prompt和输出。
- 数据问题:RAG检索结果不相关、知识库内容过期、向量索引损坏。需要检查检索链路。
第二步:信息收集
收集排查所需的关键信息:
- 请求详情:完整的Prompt、模型参数(temperature、max_tokens等)、完整输出。
- 上下文信息:多轮对话的完整历史、RAG检索的文档和分数、Agent的工具调用记录。
- 环境信息:模型版本、Prompt版本、服务版本。
- 时间线:问题首次出现的时间、影响的用户范围、是否有变更(模型更新、Prompt修改、代码部署)。
第三步:复现与定位
- 复现问题:使用收集到的完整请求信息在测试环境复现。大模型输出的非确定性可能导致难以复现——需要记录生成参数(如随机种子,如果模型支持)。
- 对比分析:将问题请求与正常请求对比,找出差异点。如Prompt差异、输入长度差异、上下文差异。
- 消融实验:逐步简化输入(移除部分上下文、缩短Prompt),定位是哪个因素导致问题。
- A/B对比:用旧版本和新版本分别处理同一请求,对比输出差异。
第四步:根因分析与修复
常见的根因和对应修复:
- Prompt缺陷:Prompt描述不清晰导致模型误解。修复:优化Prompt措辞,增加约束条件或示例。
- 上下文过长:输入Token过多导致模型"遗忘"关键信息。修复:裁剪上下文,使用RAG替代全量注入。
- RAG检索质量差:检索到的文档不相关导致输出偏离。修复:优化检索参数(top_k、相似度阈值)、改进向量索引。
- 模型能力不足:任务复杂度超出模型能力。修复:简化任务、拆分为多步、升级模型。
- 安全策略误触发:正常请求被安全策略拦截。修复:调整安全策略阈值或增加白名单。
- 并发/资源问题:高并发导致模型推理超时或质量下降。修复:扩容或限流。
第五步:预防措施
- 将问题案例加入评估集,防止回归。
- 添加监控告警规则,在类似问题再次出现时及早发现。
- 更新排查手册,积累排查经验。
AI应用排查与传统应用的区别:
- 非确定性:传统应用的问题通常可以稳定复现。AI应用的问题可能是概率性的——同一请求有时正常有时异常,需要多次复现。
- 根因在"数据"而非"代码":很多问题的根因是输入数据(Prompt、检索结果)的问题,而非代码逻辑错误。排查时需要深入到数据层面。
- 模型黑盒:模型内部的推理过程不可解释,无法像调试代码那样逐步跟踪。需要通过输入-输出对比和消融实验间接定位。
- 版本管理的复杂性:模型版本由提供方控制,可能在不通知的情况下更新,导致"代码没变但行为变了"的问题。需要监控模型版本变化。
排查工具箱:
- 请求回放工具:保存线上请求的完整信息,支持在测试环境回放。
- Prompt调试工具:交互式修改Prompt并观察输出,快速验证假设。
- 输出对比工具:并排展示不同版本/参数下的输出差异。
- Token分析工具:可视化Token化结果,帮助理解模型"看到"的内容。
面试加分点:能讨论基于LLM的自动根因分析(用另一个大模型分析问题请求的输出,辅助定位),了解模型可解释性技术(如注意力可视化、Token重要性分析)在排查中的应用,能设计从线上问题到评估集回归的闭环流程。


