2026-11-03 AI Agent 面试训练
今日学习主题
代理指标的失效:当”绿”不等于”对”,如何重建一条可判定的证据链
今天的五道题从同一个问题出发——我们看到的大多数数字都是代理指标:CPU 利用率是”忙不忙”的代理,Faithfulness 是”对不对”的代理,离线通过率是”线上好不好”的代理,HPA 的 CPU 目标是”该不该扩”的代理,栈的栈顶元素是”栈里最小值是多少”的代理。代理指标在度量对象与被度量事实分离时会静默失效。重建判定链的方法是一致的:先问这个指标度量的是哪个对象,再问它失效时会出现什么形态,最后准备一条不依赖它的独立判定路径。
- B01:CPU 利用率、饱和度与火焰图分别度量什么,为什么”三项全绿”仍可能是错的。
- B02:RAG 评测的分层指标与其循环论证风险,为什么答案对不等于检索对。
- S01:离线评测全绿而线上掉 11 个点,如何把结果指标下钻到 step 级并定位到编排层。
- S02:压测通过的容量在真实斜坡流量下崩溃,如何把”扩容”从代理信号换成排队信号并纳入下游上界。
- L01:最小栈用一个辅助栈把全局聚合量变成 O(1),考察不变式设计。
题目速览
| 编号 | 题目 ID | 题目名称 | 题型 | 一级类型 | 难度 | 一句话考察点 |
|---|---|---|---|---|---|---|
| B01 | Q-20261103-B01 | CPU 100% 是不是结论:性能分析的度量对象与判定依据 | 非 Agent 基础 | 操作系统与网络 | 基础题(中高) | USE 三项与火焰图各自能回答什么、不能回答什么,以及平均值与 on-CPU 口径的失效点 |
| B02 | Q-20261103-B02 | 答案对了,能说明检索对了吗:RAG 评测的分层与归因 | Agent 基础 | RAG 与搜索 | 基础题(中高) | 检索层与生成层指标的分工、reference-free 的代价、答案正确 ≠ 检索正确 |
| S01 | Q-20261103-S01 | 离线评测全绿,线上成功率从 82% 掉到 71% | Agent 场景 | Agent 评测与可观测性(兼 AI Agent 核心) | 场景题(高) | 结果指标下钻到 step 级、工具错误语义分离、评测集覆盖缺口与失效模式回流 |
| S02 | Q-20261103-S02 | 压测通过的容量,开抢就崩:HPA 信号错配与下游容量上界 | 非 Agent 场景 | 软件工程与平台工程(兼操作系统与网络) | 场景题(高) | 扩容滞后链路的时间预算、代理信号的错配、副本放大与下游容量的乘法关系 |
| L01 | Q-20261103-L01 | 155. 最小栈 | LeetCode | 栈与队列(辅助栈 / 不变式) | medium(未直接核验) | 用同步辅助栈维护前缀最小值,重复最小值与不变式的严格表述 |
B01 · 非 Agent 基础题
题目 ID:Q-20261103-B01 一级类型:操作系统与网络 二级主题:CPU 与内存性能分析(USE 方法、火焰图、on-CPU / off-CPU、采样与平均的偏差) 标签:性能分析、USE 方法、火焰图、采样偏差、饱和度
题目
一个在线服务 P99 从 40 ms 涨到 900 ms。监控面板显示:CPU 利用率 62%(5 分钟平均)、内存剩余 30%、磁盘 busy 40%、无错误计数、网络吞吐远低于带宽上限。团队按”CPU 打满”的方向排查,加了一倍机器,P99 只从 900 ms 降到 870 ms。
请回答:
- USE 方法要求对每种资源检查哪三个指标,它们各自的定义是什么?这套指标能回答什么问题,明确不能回答什么问题?
- 在 USE 三项”看起来都不高”的情况下,哪三种失效形态会让这套面板给出错误结论?
- 团队用 CPU 火焰图定位”最宽的那块”,据此优化了一个热点函数,P99 没有变化。火焰图的 x 轴、y 轴和宽度分别代表什么?为什么”最宽的块”常常不是该优化的地方?
先思考
- 先想清楚”资源视角”和”请求视角”是两套不同的度量:一个回答”哪个资源是瓶颈”,一个回答”这个请求为什么慢”。
- 再想”平均值”对什么敏感、对什么免疫:一个 5 分钟平均的利用率,能表达一秒钟内发生的事吗?
- 然后区分”在用 CPU”和”在等别的东西”:一个线程在等锁、等磁盘、等下游时,CPU 是忙的还是闲的?
- 最后问:火焰图上的样本是在什么时刻采集的?那些时刻线程在做什么?不在做什么?
参考答案
结论:这套面板”全绿”是因为它度量的是资源的时间平均忙闲程度,而故障发生在排队与等待上;USE 只覆盖瓶颈与错误两类问题,火焰图默认只覆盖 on-CPU,两者都不直接度量请求时延。
准确定义:USE(Utilization Saturation and Errors)方法可以概括为一句话——“For every resource, check utilization, saturation, and errors.”(对每一种资源,检查利用率、饱和度与错误)。三项的定义分别是:
- utilization:资源忙于服务工作的平均时间(“the average time that the resource was busy servicing work”),通常表达为一段时间内的百分比。
- saturation:资源有多余工作无法服务、通常处于排队的程度(“the degree to which the resource has extra work which it can’t service, often queued”),通常表达为队列长度。
- errors:错误事件的计数(“the count of error events”)。
适用边界:USE 的定位是在性能调查的早期使用,用于识别系统性瓶颈(“It’s intended to be used early in a performance investigation, to identify systemic bottlenecks”)。它只能发现特定类型的问题——“It will, however, only find certain types of issues – bottlenecks and errors – and should be considered as one tool in a larger toolbox.” 延迟类问题需要基于延迟的方法论(原文点名 Method R)或后续的工作负载刻画(workload characterization)与下钻分析(drill-down analysis)。硬件缓存(如 MMU TLB、CPU cache)被有意排除在常规 USE 资源之外,因为缓存是在高利用率下提升性能的,而不是在高利用率下劣化的,命中率应在排除系统性瓶颈之后再查。
三种让”全绿”失效的形态:
- 时间平均掩盖突发。官方给出的判读原话是:利用率在较长时间段(数秒到数分钟)上测量时,“a total utilization of, say, 70% can hide short bursts of 100% utilization”。原文还记录了一个反直觉案例:客户的监控工具报告 CPU 利用率从不超过 80%,但真实故障是 CPU 饱和造成的延迟——因为监控报的是 5 分钟平均,而其中 CPU 会连续数秒打满 100%。题目中的 62% 完全可以是”大部分时间很闲 + 每秒数十毫秒打满”的平均结果。
- 低利用率不排除饱和。原文明确指出:“A burst of high utilization can cause saturation and performance issues, even though utilization is low when averaged over a long interval.” 饱和的正确判据是队列长度或调度延迟(原文 CPU 饱和指标示例为 “run-queue length or scheduler latency”),而”任何非零饱和都可能是问题”(“any degree of saturation can be a problem (non-zero)”)。只挂利用率不挂 run queue / scheduler latency,等于放弃了唯一能直接看到排队的那一项。
- errors 被静默吞掉。原文强调错误必须调查,因为”they can degrade performance, and may not be immediately noticed when the failure mode is recoverable”,典型就是失败后被重试的操作和冗余设备池中失效的设备。题目里”无错误计数”往往只是没采集到应用层的重试与软错误,而不是真的没有。
火焰图的读法与”最宽的块”陷阱:官方定义是——x 轴是栈样本总体,按字母序排列,不是时间的流逝(“The x-axis shows the stack profile population, sorted alphabetically (it is not the passage of time)”);y 轴是栈深度,从底部 0 开始计数;每个矩形是一个栈帧,帧越宽表示它在栈中出现的次数越多(“The wider a frame is, the more often it was present in the stacks”);顶边表示 on-CPU 的部分,下方是它的祖先调用链(“The top edge shows what is on-CPU, and beneath it is its ancestry”)。
由此得到三个陷阱:
- 火焰图默认只覆盖 on-CPU。锁等待、I/O 等待、调度延迟都不在这张图上——线程的 CPU 样本为零时它根本不会出现。这类等待要用专门的 off-CPU 火焰图(官方扩展类型之一)。题目中 P99 变慢而 CPU 只到 62%,正是”时间在 CPU 之外”的典型形态。
- 宽度是出现频次,不是调用次数,也不是耗时。它是采样命中的次数占比;一个被高频调用但每次很短的函数,和一个调用少但每次很长的函数,可能得到相近的宽度。
- x 轴不是时间,看不到时间模式。官方明确说明:火焰图按字母序重排样本以最大化帧合并、更好呈现全貌,而把时间放在 x 轴的是 flame chart(最早由 Chrome WebKit Web Inspector 引入),周期性活动需要 FlameScope 这类按时间范围切分的工具。所以”最宽的块”完全可能是把分散在整个观察窗口里的样本合并后的结果,而不是某个时刻的持续热点。
与相近概念的区别:
| 对比 | 前者 | 后者 |
|---|---|---|
| USE vs 请求视角指标 | 资源视角:哪个资源是瓶颈;输出是”CPU 饱和 / 磁盘繁忙” | 请求视角:这个请求为什么慢;输出是”某类请求 P99 = 900 ms” |
| utilization vs saturation | 忙不忙;可以在排队尚未形成时很高 | 排不排队;任何非零都可能是问题 |
| 火焰图 vs flame chart | x 轴按字母序重排,最大化合并,看全貌 | x 轴是时间流逝,可研究时间模式与周期性 |
| 采样 profiler vs 函数级 tracing | 有间隙、开销低、样本有偏 | 精确、开销高,会扰动被观测目标 |
典型应用(通用工程):建立一条从”红灯”到”证据”的四层链——请求时延分层(按接口/租户/ percentile)→ 服务内阶段耗时 → 资源 USE 三项 → 代码路径火焰图(先 on-CPU,再 off-CPU)。每一次下钻都必须换一个更接近事实的度量对象,而不是换一个更漂亮的图表。容量评估不能只看平均利用率,饱和(run queue、队列长度、调度延迟)才是容量耗尽的直接证据。
面试现场回答(1–2 分钟版本):
我不会先改机器,先换度量对象。USE 要求对每种资源查三项:利用率是资源忙于服务工作的平均时间,饱和度是有多少活排着队干不了,错误是错误事件计数。这套方法用于调查早期快速定位瓶颈和错误,但它只解决瓶颈和错误两类问题,不直接回答”这个请求为什么慢”。
这个面板全绿有三种可能失效:第一,62% 是 5 分钟平均,官方明确说过 70% 的平均可以隐藏秒级的 100% 突发,我见过利用率从没超过 80% 但真实原因是每秒打满几秒的调度延迟;第二,低利用率不等于无饱和,饱和要看 run queue 和 scheduler latency,而且任何非零饱和都可能是问题——我们根本没挂这两个指标;第三,错误被静默吞掉了,失败后重试的操作属于可恢复失败,性能差但不报错。
加机器只降了 30 ms,说明我们加错了资源。火焰图那边:x 轴是栈样本总体按字母序排列,不是时间流逝;宽度是样本中出现频次;顶边是 on-CPU。所以默认火焰图看不见锁等待、I/O 和调度延迟——那些要用 off-CPU 火焰图。最宽的块只是采样命中最多,可能是大量短调用的合并,而且按字母序重排会把它从整个窗口里拢到一起,不代表某个时刻的持续热点。
我的下一步是:先按请求类型拆 P99 确认是不是全局问题,然后挂上 CPU 的 scheduler latency 和 run queue,同时采一份 off-CPU 火焰图,最后才谈优化哪个函数。
关键点
- USE 只覆盖瓶颈与错误:官方原话是只会发现 “bottlenecks and errors”,延迟类问题需要 latency-based 方法论或 workload characterization / drill-down。
- 饱和是独立的第二项,且任何非零都可能是问题:利用率是”忙不忙”,饱和是”排不排队”,两者必须分别采集。
- 平均值是最大的谎言来源:长周期平均利用率会隐藏短时 100% 突发;低平均利用率不排除饱和。
- 火焰图只覆盖 on-CPU,x 轴不是时间,宽度是采样命中频次而非耗时或调用次数。
- 不可抢占资源与可抢占资源的 70% 含义不同:磁盘在利用率超 70% 后会频繁出现排队延迟,而 CPU 几乎可在任意时刻被抢占。
常见误区
- 把平均 CPU 利用率当成并发度或健康度结论。错误原因:利用率是”平均忙的时间占比”,既不包含排队信息(saturation 才是),也会被时间平均抹掉突发。官方记录过一个 5 分钟平均从不超过 80%、却因秒级 100% 突发导致 CPU 饱和延迟的真实案例。
- 把火焰图最宽的块当成必然的优化点。错误原因:宽度是采样命中频次;默认火焰图只含 on-CPU,等待型瓶颈根本不在图上;且 x 轴按字母序重排会把分散在整个窗口的样本合并,看不出”某个时刻”的热点。结果是优化了真正的 CPU 消耗者,而故障在等待侧。
- 认为”USE 三项全绿”就可以排除资源问题,直接去改应用代码。错误原因:USE 的清单是”about thirty metrics, some of which can’t be measured, and some of which are tricky to measure”——没挂上的指标不等于不存在;尤其 CPU 的 errors(可修正 ECC、faulted CPU)与饱和类指标在多数系统里默认没有采集。
追问
追问:如果 P99 变慢但 USE 三项全绿、off-CPU 火焰图也看不出明显等待,你怎么继续?
回答要点:
- 先确认度量窗口:把 5 分钟平均改成 1 秒甚至 100 毫秒粒度,重新看利用率与队列,突发形态常在降采样中消失。
- 转向工作负载刻画(workload characterization):先回答”谁在请求、请求什么、为什么请求”,很多”性能问题”其实是请求构成变化(某个下游超时导致重试、某类输入体积翻倍、缓存命中率变化)。
- 用排队分解把时延拆成服务时间与排队时间:若服务时间没变而排队时间涨,说明是到达率或并发度问题,不是代码热点。
- 检查依赖侧:下游 P99 的变化会通过同步调用直接变成本服务的等待,而本服务的 USE 面板完全看不到——这与 B01 的结论一致,必须有一条不依赖本服务指标的独立判定路径。
- 承认边界:如果上述都无果,说明问题在更长的时间尺度或非资源维度(锁竞争、GC、连接耗尽),应使用对应的专门方法论,而不是继续加宽 USE 清单。
延伸学习
- Brendan Gregg《The USE Method(Utilization Saturation and Errors)》,官方页面:https://www.brendangregg.com/usemethod.html —— 含三项的精确定义、完整资源清单(约 30 个指标)、Suggested Interpretations 与”低利用率不等于无饱和”的反直觉案例。
- Brendan Gregg《Flame Graphs》官方页面:https://www.brendangregg.com/flamegraphs.html —— 含 x/y 轴定义、宽度语义、on-CPU 边界、icicle 与 flame chart 的区别;同一站点另有 Off-CPU Flame Graphs 专页可作下一步。
B02 · AI Agent 基础题
题目 ID:Q-20261103-B02 一级类型:RAG 与搜索 二级主题:RAG 评测设计(检索层与生成层指标分工、reference-free 评测的代价、归因顺序与失效边界) 标签:RAG 评测、Faithfulness、Context Precision、reference-free、归因
题目
一个企业制度问答系统,线上人工抽检正确率 78%。团队用 Ragas 跑了一轮离线评测,得到:
- faithfulness = 0.91
- context precision = 0.62
负责人据此判断”生成没问题,是检索差”,把下一季度的资源全部投入检索优化,并停止了对提示词和生成侧的检查。
请回答:
- faithfulness 与 context precision 各自度量什么对象、依赖哪些输入、如何计算?它们分别需要 ground truth 吗?
- 从这组数字能推出什么结论,不能推出什么结论?请给出至少一个”这组数字很好但系统其实是错的”的具体反例。
- 这套指标在 Agent 系统里应该记录在哪一层(最终答案层还是工具调用层),为什么?
先思考
- 先问每个指标的”参照物”:faithfulness 是拿答案跟什么比?context precision 是拿检索结果跟什么比?参照物不同,结论的成立条件就不同。
- 再问”参照物本身可信吗”:如果 context precision 在没有标准答案时拿生成结果当参照,那么”生成错”会不会连带把检索判成对的?
- 然后构造反例:能不能让 faithfulness = 1.0 而答案在业务上是错的?
- 最后想记录粒度:Agent 一次会话可能检索 5 次,你只在最后记一个 faithfulness,能定位到哪一次检索吗?
参考答案
结论:这组数字只能支持”答案与检索到的上下文高度自洽、检索排序质量偏低”这一条结论;它不能支持”生成没问题”,更不能支持”把资源全部压到检索”。faithfulness 度量的是对上下文的忠实度而非对世界的正确性,而 context precision 在无 ground truth 时以生成结果为参照,本身就存在循环论证风险。
准确定义与计算(依据 Ragas 官方文档):
- Faithfulness:度量 response 与 retrieved context 的事实一致性,取值 0–1,越高越一致。原文定义:“The Faithfulness metric measures how factually consistent a response is with the retrieved context.” 判定标准原话是 “A response is considered faithful if all its claims can be supported by the retrieved context.” 计算分三步:① 识别 response 中的所有 claim;② 逐条检查该 claim 是否能由 retrieved context 推出;③ 计算 Faithfulness = 被上下文支持的 claim 数 / response 中 claim 总数。它不需要 ground truth,输入只需 user_input、response、retrieved_contexts。
- Context Precision:度量检索器把相关 chunk 排在前面的能力,“assesses the degree to which relevant chunks in the retrieved context are placed at the top of the ranking”。它是各 rank 上 precision@k 的加权均值:Precision@k = TP@k / (TP@k + FP@k),Context Precision@K = Σ(Precision@k × v_k) / top-K 中相关项总数,其中 v_k 是第 k 位的相关性指示(0/1)。它是位置敏感的:文档给出的例子里,无关块排在第二位时分数不变(≈1.0),排到第一位时降到 ≈0.5。
- 是否需要 ground truth:Faithfulness 明确不需要。Context Precision 有两种形态——有 reference 时用 reference 逐个比对 chunk(
ContextPrecision,官方示例传 reference);无 reference 时用生成的 response 代替参照(ContextUtilization,官方说明 “Use this when you don’t have a reference answer but have the response that was generated”,旧 API 名为LLMContextPrecisionWithoutReference)。此外还有完全不用 LLM 的IDBasedContextPrecision(比较检索到的 ID 与参考 ID 集合)与基于编辑距离的NonLLMContextPrecisionWithReference。
适用边界:Ragas 的原始定位是 reference-free evaluation,论文摘要原话是提出一套 “which can be used to evaluate these different dimensions without having to rely on ground truth human annotations”,目的是”faster evaluation cycles”。这既是它的价值也是它的边界:省掉人工标注的代价是参照物退化。这些指标是相对比较工具(用于版本 A/B 与趋势),不是绝对质量证明;分数同时取决于判定模型、提示词与 claim 切分粒度,不能反向推出业务正确率。
反例(数字很好但系统是错的):问”2025 年差旅报销标准是多少”,检索返回的是 2024 版制度(已废止,索引未更新)。生成的答案逐句都来自该上下文 → faithfulness = 1.0;而业务答案是错的。这说明 faithfulness 保证的是”内部一致”,不是”对世界为真”。 同理,若模型靠参数知识答对了但检索全是噪声,最终答案正确、context precision 很低——这时”检索差”是真的,但危害被掩盖,一旦知识更新就会集中爆发。
能推出 / 不能推出:
| 能推出 | 不能推出 |
|---|---|
| 答案与检索上下文高度自洽(低幻觉率) | 答案在业务上正确(faithfulness 不判对错) |
| 检索排序质量偏低,相关块常被排在后面 | 检索漏召严重(那是 context recall 的职责,且需要 ground truth) |
| 生成侧”编造”风险相对可控 | 生成侧的表述、完整性、可操作性没问题(都不在 faithfulness 里) |
| 值得优先查检索 | 可以停止检查生成侧(恰恰相反:context precision 无 reference 时以 response 为参照) |
在 Agent 系统中的应用:RAG 在 Agent 里通常是一个工具,因此指标必须记在工具调用层而不是最终答案层:
- 一次会话可能检索 3–7 次,只记最终答案的 faithfulness 无法定位是哪一次检索拉低了质量,也无法区分”第一次就错”和”重试后错了”。
- Agent 具备重试与改写查询的能力,这会产生掩蔽效应:检索失败但整体成功,端到端指标看起来很好,检索层退化被隐藏。因此必须按 (session, step, tool_call) 记录每次检索的指标。
- 评测集要按交互形态分层:单跳检索题(一次检索即可答)与多轮工具调用题(需要改写、重试、跨源交叉验证)应分别统计,否则 Agent 的”补救能力”会被计入检索的成功。
- 检索指标应与其他工具调用指标(工具选择正确率、参数校验通过率)放在同一条 trace 上,才能把”答非所问”归因到检索、工具还是规划。
与相近概念的区别:
- faithfulness(对上下文忠实)vs factual correctness(对世界为真):前者不需要 ground truth,后者需要。前者高而后者低 = “一本正经地引用了错误的源”。
- context precision(排序质量 / 惩罚噪声)vs context recall(覆盖度 / 惩罚漏召):precision 高不代表该找的都找到了。recall 类指标依赖 ground truth,本轮未逐页核验其公式。
- reference-free 指标 vs 有 reference 指标:前者省成本但参照物可能是生成结果本身;后者可靠但需标注。
- 检索层失败 vs 生成层失败:答案错 + 检索对 = 生成问题;答案错 + 检索错 = 检索问题;答案对 + 检索错 = 检索有隐患,被模型参数知识掩盖。
面试现场回答(1–2 分钟版本):
我会先说这组数字能推出什么。Faithfulness 度量的是答案和检索到的上下文是否事实一致,算法是把答案拆成 claim,逐条判断能不能从上下文推出来,最后算被支持的 claim 占比,取值 0 到 1,不需要标准答案。Context precision 度量的是检索有没有把相关 chunk 排在前面,它是各 rank 上 precision@k 的加权均值,而且位置敏感——官方例子里无关块排第二位分数不变,排到第一位就掉一半。
那么能推出的是:答案和上下文高度自洽、检索排序质量偏低。不能推出”生成没问题”,理由有两条。第一,faithfulness 只保证内部一致,不保证对世界为真——我举个反例:问 2025 年报销标准,检索召回的是已废止的 2024 版制度,答案句句有出处,faithfulness 满分,但业务答案是错的。第二,context precision 在没有标准答案时是拿生成结果当参照去判断 chunk 相关性的,也就是说生成如果错了,会连带把检索判成对的或错的——这是循环论证。
所以在 Agent 里我会把指标下沉到工具调用层:一次会话可能检索好几次,只记最终答案的一个分数定位不到是哪一次;而且 Agent 会重试和改写查询,检索失败但整体成功会掩盖检索层退化。评测集也要分单跳题和多轮工具调用题分别统计。负责人这个结论我只接受一半——检索确实该查,但生成侧的检查不能停。
关键点
- faithfulness = 被上下文支持的 claim 数 / 总 claim 数,判的是”能否由上下文推出”,不是”是否正确”。
- context precision 是位置敏感的排序质量指标:Precision@k = TP@k / (TP@k + FP@k),无关块前置才降分。
- reference-free 是有代价的:省掉人工标注的代价是参照物退化为生成结果(ContextUtilization),形成循环论证。
- 答案正确 ≠ 检索正确:可能靠参数知识蒙对,隐患会在知识更新后集中爆发。
- 在 Agent 中必须记录到工具调用层,并按交互形态(单跳 / 多轮)分层统计,否则重试能力会掩蔽检索退化。
常见误区
- 把 faithfulness 当成正确性指标。错误原因:它的定义就是”response 与 retrieved context 的事实一致性”,参照物是上下文而不是 ground truth。上下文全错而答案自洽时它拿满分。
- 认为用了 reference-free 指标就不需要人工标注。错误原因:Ragas 的设计目标是加快迭代周期(“faster evaluation cycles”),而非取代人工。检索层的 recall 类指标必须依赖 ground truth;precision 类在无 reference 时会用 response 当参照。
- 用端到端正确率当唯一指标并据此分配优化资源。错误原因:端到端正确率无法归因,且会掩盖”检索错但蒙对”的情形;本题负责人正是因此把资源错误地全部压到检索侧。归因必须分层:先判断”判定本身是否可信”,再看检索层,再看生成层,最后才看端到端。
追问
追问:如果只能选三个指标做上线门禁,你选什么,阈值怎么定?
回答要点:
- 分层各取其一:检索侧取一个覆盖度/排序指标(context precision 或 ID-based precision,后者不依赖 LLM 因而更稳定),生成侧取 faithfulness,端到端保留一个可断言指标(如引用有效性与关键字段精确匹配)。凡是能断言的不要用 LLM judge。
- 阈值靠基线加人工标定,不靠拍脑袋:先在人工标注的小样本上测出”judge 分数 ↔ 人工判定”的一致性,把阈值定在人工认可的分界点;并记录判定模型与提示词版本。
- 门禁分两级:阻断级(回归集通过率、可断言指标、引用有效性)与告警级(judge 类指标的漂移)。judge 类指标的波动用于观察,不直接阻断发布。
- 把判定模型变更当作一次门禁变更:换 judge 模型或改 prompt 后,必须重新标定阈值并重跑基线,否则分数不可比。
- 成本与稳定性:LLM 类指标每次评估都要调用模型,门禁要考虑评估时长;可用 HHEM 类小分类器替代部分判定(官方 faithfulness 页给出该替代路径)。
延伸学习
- Ragas 官方指标文档:Faithfulness(https://docs.ragas.io/en/stable/concepts/metrics/available_metrics/faithfulness/)与 Context Precision(https://docs.ragas.io/en/stable/concepts/metrics/available_metrics/context_precision/)——含公式、有无 reference 的两种形态、ID-based 与非 LLM 变体。
- arXiv:2309.15217《Ragas: Automated Evaluation of Retrieval Augmented Generation》(Es、James、Espinosa-Anke、Schockaert,2023-09-26 v1,2025-04-28 v2):reference-free 评测框架的原始论文,摘要明确了”without having to rely on ground truth human annotations”的设计取舍与”faster evaluation cycles”的动机。
S01 · AI Agent 场景题
题目 ID:Q-20261103-S01 一级类型:Agent 评测与可观测性(兼属 AI Agent 核心) 二级主题:线上故障定位与可观测性(结果指标下钻、工具错误语义、评测集覆盖缺口、失效模式回流) 标签:Agent 可观测性、评测体系、工具调用、重试预算、灰度门禁
题目
线上成功率从 82% 掉到 71%,离线评测集全绿
业务背景:企业 IT 工单处理 Agent,用户自然语言提交工单,Agent 负责分类、检索制度、调用内部系统处理并回复。42 个工具(其中 11 个为写操作),日均 8 万次工具调用,平均每个会话 6.3 轮、11 次模型调用。
当前架构:入口网关做身份与租户校验 → 编排器(状态机 + 步骤重试)→ 工具网关(统一参数校验、权限判定、错误结构化)→ 31 个业务系统与检索服务。会话状态与检查点存在关系库,trace 落地到可观测平台。
近两周指标:
| 指标 | 两周前 | 当前 |
|---|---|---|
| 任务成功率 | 82% | 71% |
| P50 完成时长 | 41 s | 58 s |
| 单次会话成本 | 0.31 元 | 0.44 元 |
| 离线 golden set(320 条)通过率 | 96% | 96% |
故障现象:
- 成功率下滑集中在”需要跨 3 个以上系统的工单”,单系统工单几乎无变化。
- 失败模式 Top 3:① 工具参数校验失败后模型反复重试同一工具,同一工具的连续失败次数从 1.4 涨到 3.8;② 答非所问并给出未引用依据的结论;③ 中途进入长时间空转(> 90 s 无任何进展)。
- 同期仅两处变更:上周把工具清单从 42 个精简到 31 个(删除 11 个重复工具);检索服务升级了一个依赖库的小版本。
目标:在不全量回滚的前提下把成功率恢复到 ≥ 80%,并建立”不会再重演”的机制。
限制条件:工具清单精简是季度 OKR,不能回滚;不能停服;线上数据含 PII,不得进入离线环境;每天只有一个灰度发布窗口。
需要你回答的取舍问题:
- 先修质量还是先修成本?
- 离线评测集要不要立刻扩容?扩到多少?
- 要不要回滚工具清单?
先思考
- 先问”离线全绿”意味着什么:是”系统没坏”,还是”没测到”?看失败集中在哪类输入。
- 再看三个失败模式有没有共同祖先:参数校验失败 → 重试 → 上下文变长 → 压缩 → 约束丢失 → 答非所问。这像不像一条链?
- 然后想”能力被删掉”和”参数填错”在错误信息里是否被区分开了。如果没区分,模型会怎么反应?
- 最后区分三类能力:哪些事必须靠模型做,哪些该由编排层硬性约束,哪些根本不该交给概率性系统。
参考答案
结论:这不是模型退化,而是”工具契约变更 + 评测集覆盖缺口”叠加的编排层故障。工具精简后,被删工具的意图被模型以”参数变形”的方式落到保留工具上,触发参数校验失败;而错误契约把”能力不存在”和”参数错误”混为一类,模型于是反复重试同一工具,重试既推高成本又污染上下文,长会话进入压缩后约束丢失,出现答非所问。离线集以单系统用例为主,跨系统编排根本没被覆盖,所以通过率不变——这个”全绿”是覆盖缺口的证据,不是系统健康的证据。
问题拆解(四层定界):
- 是否真坏:71% 与 82% 的差异来自输入分布还是系统行为?失败集中在跨系统工单、单系统工单无变化 → 不是全局容量或模型问题,是特定输入子集的故障。
- 坏在哪一层:按模型层 / 编排层 / 工具层三层拆。同一提示词、同一模型的离线回归通过 → 模型层大概率未退化;同一工具连续失败次数从 1.4 涨到 3.8 → 编排层的重试预算事实上失控;工具层参数校验失败率上升 → 工具契约或调用方参数来源发生了变化。
- 坏在哪一步:需要 trace 上的 step 级指标:每步的 tool_name、error_class、retry_index、step_latency、tokens。没有 step 级数据,成功率这个结果指标无法下钻。
- 哪个变更是原因:两处变更需分别验证。工具精简与”参数校验失败”存在机制上的直接关联(意图无处可去);检索依赖升级更可能影响召回质量而非参数校验。按变更前后分桶对比,不能因为”最近改了 A”就认定 A。
核心方案:
- 工具错误语义升级为结构化契约(止血,当天可做)。把工具返回的错误分成四类并强制契约化:
capability_not_found(该能力不存在)、invalid_argument(参数不合法,附字段级原因)、upstream_error(下游失败,可重试)、permission_denied(越权,不可重试)。关键动作是把 “能力不存在”从”参数错误”中分离出来——当前模型收到的只有”参数错误”,它唯一能做的合理动作就是”改改参数再试一次”,这正是 3.8 次连续失败的来源。 - 重试预算硬约束(止血)。同一工具同一语义指纹连续失败 ≤ 2 次;单会话总重试 ≤ 6 次;超过即强制换策略(先检索工具清单 / 换个系统)或升级人工。带指数退避与抖动。
- 失败轨迹的上下文治理(止血)。把失败尝试压成一行结构化摘要:“尝试过 X(原因:capability_not_found),已排除”,而不是把完整报错与参数保留在上下文里。工具结果永远优先于对话历史被清理。
- 评测补集(本周)。按线上真实输入分布补充跨系统工单用例(至少 60 条,覆盖 3 系统 / 4 系统 / 含写操作三类),每条用例必须声明判定方式(断言 / judge / 人工)与失效模式标签。新增三个可断言指标:工具调用正确性(期望工具集合的精确/集合匹配)、每会话重试次数上界、引用有效性(引用 ID 是否存在且内容匹配)。
- 观测补维度(本周)。失败会话 100% 采样,成功会话按 5% 尾部采样(保留 P99 形态);trace 上落
tool_name / error_class / retry_index / step_latency_ms / tokens_in / tokens_out / prompt_version / model / tool_manifest_version / retrieval_version。成本必须能归因到 (tenant, session, step, tool)。 - 变更门禁(本周)。工具清单变更视为破坏性变更,发布前必须在影子环境跑完整 golden set + 跨系统补集,并额外对比工具调用分布是否发生迁移(若某被删工具的调用量被邻近工具吸收,就是本次故障的直接前兆)。
关键执行流程:
- D0(止血):错误契约四类上线 + 重试硬上限 + 失败轨迹摘要化 → 灰度 10% → 观察 24 小时的”连续失败次数分布”与”无进展率”。
- D1–D2(归因验证):按两个变更分别做分桶对比;在影子环境回放 30 条真实失败会话(脱敏后的结构化参数与错误码,不含 PII 原文),确认”工具精简”是主因。
- D3–D5(机制化):补集 + 可断言指标 + 变更门禁 + 采样策略上线。
- D6+(灰度):按租户分桶灰度,守护指标越界自动回滚。
技术选型与选择依据:
| 决策点 | 选择 | 依据 | 备选方案与代价 |
|---|---|---|---|
| 错误语义 | 自研结构化错误契约(error_class + 字段级原因 + retryable + side_effect) | 错误分类是确定性能力,应放在传统软件层;让模型从自然语言报错里猜原因是概率性能力,不稳定且消耗 token | 保留自然语言报错、靠提示词教模型分辨——成本低但不可靠,且每次换模型都要重调 |
| 重试策略 | 硬上限 + 强制换策略 / 升级人工 | 重试的边际收益快速衰减(同一指纹第 3 次成功率极低),而成本与延迟线性增长,且污染上下文 | 无上限重试——表面成功率可能更高,但成本与尾延迟失控,且形成上下文污染 |
| 采样 | 失败全采 + 成功尾部采样 | 归因只需要失败样本的完整信息;成功样本用于基线与分布 | 全量采样——成本随调用量线性增长;纯头部采样——看不到尾部形态 |
| 判定方式 | 可断言优先(工具调用、重试次数、引用有效性),judge 只用于”答非所问” | 可断言指标确定、便宜、可回归;judge 有偏差且需版本化 | 全 judge——受位置/冗长偏好影响,且评估成本高 |
| 变更门禁 | 工具清单变更走破坏性变更流程 | 清单是模型可观察的”能力面”,改它等价于改 API | 视作普通配置发布——本次故障即由此产生 |
方案取舍:
- 先修质量还是先修成本:先修质量。成本上升是重试放大的结果,不是独立故障。直接压成本(降模型规格、砍上下文)会同时掩盖失败信号并恶化质量——这与 B01 的教训一致:修代理指标不等于修问题。
- 先扩量还是先补覆盖:先补覆盖缺口。盲目把 320 条扩到 1000 条,若仍按单系统分布采样,通过率依旧全绿。正确做法是按线上输入类型分层抽样,并把每一条线上失效模式沉淀为至少一条用例。
- 是否回滚工具清单:不回滚。回滚会带回 11 个重复工具导致的工具选择歧义,只是换一种失败形态。正确做法是保留精简成果,同时把”能力不存在”变成模型可理解、可据此换路的明确信号。
失败路径、边界条件、超时、重试、幂等与恢复:
- 工具调用三态:成功 / 可重试失败 / 终态失败。
permission_denied与capability_not_found必须是终态,不得进入重试。 - 超时 ≠ 未执行:写类工具超时后状态未知,必须由幂等键兜底,键为 (tool_name, 归一化参数指纹, 会话 ID) 的哈希,落在工具网关的确定性层。
- 幂等与去重:写操作在网关侧做指纹去重,覆盖上游系统无幂等能力的情况;去重窗口须长于最大重试时长。
- 空转恢复:会话级检查点记录”已完成的步骤与副作用”;超过 90 s 无进展即进入
no_progress状态,触发一次策略切换,仍无进展则升级人工并保留完整诊断包(trace ID、已尝试工具、失败原因序列)。 - 人工接管:接管的输入回填到上下文作为高优先级事实;等待不受会话寿命约束(持久化挂起 + 信号恢复)。
- 降级方向:检索服务不可用或 judge 不可用时,走向”降低自动化程度”(转人工 / 只做分类),不得走向”降低判定标准”(让模型自由发挥)。
- 上下文压缩:压缩是概率性操作,必须有确定性兜底——纯裁剪路径必须可用;pinned 层(约束、已完成副作用、关键标识符)不可被压缩掉。
安全、权限、成本、延迟与可靠性分析:
- 安全:工具精简后权限面变小是收益,但必须确认被删工具的权限没有被保留工具的”宽参数”重新打开(例如被删的是”仅查本人订单”,保留的是”按用户 ID 查订单”)。错误契约不得把内部堆栈、SQL、内部主机名回传给模型。越权尝试必须独立计数并告警,它是攻击信号而非普通失败。
- 权限:权限判定在工具网关这一唯一确定性执行点,不在提示词里。
- 成本:成本归因到 (tenant, session, step, tool),派生指标”重试 token 占比”应单独监控——它是本次故障最早的可见信号之一。
- 延迟:P50 从 41 s 涨到 58 s 主要来自重试轮次而非单步变慢,用 step 级 P95 + 重试次数分布验证;若单步 P95 未变而会话 P50 变长,即可确认是轮次增加。
- 可靠性:灰度按租户分桶;守护指标 = 成功率、重试次数 P95、无进展率、单次成本 P95,任一越界自动回滚;每天一个发布窗口意味着回滚窗口也要预留。
评测指标、测试方法与上线后观测:
- 门禁指标(阻断级):golden set 通过率(按单系统 / 跨系统分层统计)、工具调用准确率(集合匹配,可断言)、每会话重试次数 P95(可断言)、引用有效性(可断言)。
- 门禁指标(告警级):faithfulness / context precision 等 judge 类指标的漂移、单次成本 P95、P50/P95 完成时长。
- 线上观测:失败模式 Top-N 日报;失败会话 trace 100% 可查;按 prompt_version / model / tool_manifest_version / retrieval_version 四维分桶对比成功率与成本——版本维度是本次故障能否在 30 分钟内定位的关键。
- 回归测试方法:影子环境回放(脱敏后的结构化轨迹)、A/B 灰度、多次运行取统计口径(单次对比对概率性系统不可信)。
三层能力归属(本题必须区分):
- 模型本身的概率性能力:意图识别、参数生成、根据错误反馈调整策略、判断”答非所问”(judge)。都可能错,都要有外部约束。
- Agent 编排、状态与工作流能力:重试预算、状态机、上下文压缩、空转检测、升级人工。这些必须由确定性代码实现,不能交给模型自觉。
- 传统软件系统的确定性能力:参数校验、错误分类(error_class)、幂等键、权限判定、采样与指标、灰度与自动回滚、trace 落库。
关键点
- “离线全绿”在这里是覆盖缺口的证据,不是健康的证据:失败集中在跨系统工单、单系统无变化,说明故障在特定输入子集,而评测集没有覆盖该子集。
- 三个失败模式有共同祖先:能力不存在被误报为参数错误 → 反复重试 → 成本与上下文同时膨胀 → 压缩后约束丢失 → 答非所问。修根因(错误语义 + 重试预算)一次解决三个现象。
- 必须区分三类能力:错误分类、参数校验、幂等键、采样与灰度回滚属确定性系统;重试预算、状态机、空转检测、上下文压缩属编排层,也必须确定性实现;只有意图识别与策略调整属于模型的概率性能力。
- 可断言指标优先于 judge:工具调用正确性、重试次数上界、引用有效性都可断言,只有”答非所问”需要 judge,且 judge 必须版本化。
- 版本维度是定位速度的关键:trace 上必须落 prompt_version / model / tool_manifest_version / retrieval_version,否则无法区分两处变更各自的贡献。
追问
追问 1:如果确认是模型退化(同一提示词换模型版本后失败率上升),处置有什么不同?
回答要点:
- 先确认再行动:单次对比对概率性系统不可信,必须用同一 golden set 跑多次(如 3–5 次)取统计口径,并比较置信区间;同时固定 temperature / seed 与 system_fingerprint 以缩小非目标变量。
- 处置路径不同:工具契约问题改的是确定性层(改完立竿见影且可回归);模型退化改的是概率性层,处置是网关层模型回退 + 提示词适配 + 可能的微调,且需要更长观察窗。
- 回退也是变更:把模型版本回退当作一次配置变更走灰度,守护指标同上。
- 长期机制:模型版本必须是可一键切换的配置,且每次切换都要有对应的回归基线;保留”旧模型可用”的逃生通道。
追问 2:怎么避免离线评测集长期与线上分布脱钩?
回答要点:
- 失效模式回流机制:每周从失败 trace 中抽样,人工标注后沉淀为用例,并打上失效模式标签(工具选择错误 / 参数错误 / 检索漏召 / 上下文污染 / 越权)。
- 分布对齐:按线上输入类型分层抽样(单系统 / 跨系统 / 含写操作 / 长会话),每层保底条数,而不是整体随机。
- 用例生命周期:每条用例声明判定方式与失效模式;产品与制度变更后同步作废或更新过期用例,避免”用旧正确答案判定新系统”。
- 漂移观测:监控”线上失败模式中未被任何用例覆盖的比例”,这个比例本身就是评测集健康度指标。
追问 3:成本涨了 42% 但成功率只掉 11 个点,值得投入修吗?
回答要点:
- 值得,因为同源:成本上升与成功率下降都来自重试放大,修重试同时解决两者;且重试会污染上下文造成二次退化(答非所问),这部分损失尚未完全体现在当前成功率里。
- 但要分开核算:必须确认没有独立的成本泄漏——如上下文无上限增长、提示缓存命中率下降(工具清单变更会让前缀失效)、检索召回变多导致上下文变长。这三项各自对应不同处置。
- 量化口径:把成本拆成”有效 token”与”浪费 token(重试 + 被压缩丢弃 + 未命中缓存)“,浪费占比是比总成本更敏感的工程指标。
常见误区
- 看到离线全绿就判定”线上问题是环境或数据问题”。错误原因:golden set 的覆盖缺口本身就是产品缺陷;本次故障中,跨系统工单从未被覆盖,“通过率不变”恰恰是最强的不利证据。
- 直接降低模型规格或裁剪上下文来压成本。错误原因:成本上升是重试放大的结果,压成本会掩盖失败信号并造成质量二次劣化;正确顺序是先定位重试根因。
- 把所有失败归因到最近一次变更。错误原因:相关性不等于因果。同期有两处变更,必须按版本维度分桶对比;本次真正的机制关联是”工具精简 → 意图无处可去 → 参数变形 → 校验失败”,而检索依赖升级需要单独证伪。
小型实践(30–60 分钟)
取 30 条线上失败会话的 trace,做三件事:① 给每一步标注 error_class 与”这次重试是否必要”(必要的定义:换工具或换参数且带来了新信息);② 计算”可避免重试占比”与”重试 token 占比”两个派生指标;③ 从中挑 5 条改写成可断言的回归用例(断言目标是工具调用集合与重试次数上界,而不是最终答案文本),并给每条写上失效模式标签。
延伸学习
- Ragas 官方指标文档(Faithfulness / Context Precision):用于把 judge 类指标与可断言指标分开,见 B02 延伸学习。
- OpenTelemetry GenAI 语义约定(
open-telemetry/semantic-conventions-genai):agent 与工具调用 span 的模型化方式;注意该约定当前整体状态仍为 Development(gen_ai.*专属属性均为 Development,仅继承的error.type/server.address/server.port为 Stable),落地时应把它当作内部约定而非稳定契约(该状态为 2026-11-02 配置维护时第 5 次复核的结论,本轮沿用未再核验)。
S02 · 非 AI Agent 场景题
题目 ID:Q-20261103-S02 一级类型:软件工程与平台工程(兼属操作系统与网络) 二级主题:Kubernetes 扩缩容与容量治理(HPA 信号与滞后链路、QoS 与驱逐、下游容量上界、入口准入控制) 标签:Kubernetes、HPA、容量规划、限流、QoS
题目
压测通过的容量,开抢就崩
业务背景:电商订单服务,部署在 Kubernetes 集群,Deployment 固定 12 副本。单副本 requests: cpu=1, memory=1Gi,limits: cpu=2, memory=2Gi。每个副本维护 20 个数据库连接,数据库总连接上限 300。Pod 从启动到 Ready 需要 45 秒(含 JVM 预热)。
当前架构:负载均衡 → 网关(限流与鉴权)→ 订单服务(12 副本)→ 数据库主从。HPA 已配置:targetCPUUtilizationPercentage 语义下的 averageUtilization: 70,同步周期 15 秒(默认),缩容稳定窗口 5 分钟(默认)。节点池支持自动扩容,新节点就绪约 3 分钟。
压测结论(大促前两周):逐步加压到 6000 RPS,P95 = 180 ms,通过。
大促开抢当天:
- 流量在 30 秒内从 800 RPS 冲到 5200 RPS。
- 随后 3 分钟内 P95 从 190 ms 涨到 4.2 秒,错误率 6%。
- 部分 Pod 状态为 OOMKilled,节点出现 MemoryPressure 条件。
- 开抢后约 2 分钟才发生第一次扩容,最终扩到 24 副本;此后数据库连接池被打满(24 × 20 = 480 > 300),大量请求阻塞在获取连接上。
- 整个开抢期间 CPU 平均利用率只有 55%,未达到 70% 的扩容目标。
目标:让 30 秒内 5 倍流量突增可承受(P95 < 400 ms),且不打爆数据库。
限制条件:数据库连接上限 300 短期内不能调整;节点池扩容需要 3 分钟;不能靠无限加副本解决问题;不能为了容量牺牲下单主链路的可用性。
需要你回答的取舍问题:
- HPA 应该用 CPU 还是 RPS / 在途请求数作为扩容信号?
- 提前扩容(定时 / 预测)还是纯 reactive 扩容?
- 放大副本还是在入口限流?
先思考
- 先算时间账:突增窗口是 30 秒,而扩容链路包含哪些环节?每个环节各多久?加起来够不够?
- 再算容量账:数据库 300 个连接,每个副本 20 个,最多能有几个副本?扩到 24 会发生什么?
- 然后问”CPU 55%“这个数字:服务在等数据库连接的时候,CPU 是忙的还是闲的?用 CPU 当信号,能不能看到”连接不够”这件事?
- 最后想故障形态:如果注定有一部分请求要失败,你希望它们”全部超时”还是”部分快速失败”?
参考答案
结论:问题不在”没配 HPA”,而在三件事叠加——信号错配、滞后不可控、下游容量未纳入上界。CPU 平均只有 55% 说明瓶颈根本不在 CPU(在数据库连接与下游),用 CPU 做扩容信号必然滞后且永不达标;而 45 秒就绪 + 15 秒同步周期 + 可能的 3 分钟节点扩容,意味着纯 reactive 扩容在时间上不可能覆盖 30 秒的 5 倍突增。因此方案是:以数据库容量反推副本上界 → 定时提前扩容建立基线 → 用在途请求数/队列等待作为 reactive 信号 → 入口有界排队与按优先级限流 → 连接池按副本数自适应。
问题拆解:
- 时间预算:突增窗口 30 秒;扩容链路 = HPA 同步周期 15 s + 调度与镜像拉取 + Pod 就绪 45 s + 可能需要的新节点 3 min。即使一切顺利也 ≥ 60 秒,是突增窗口的两倍以上 → 纯 reactive 在物理上不可能。
- 信号预算:CPU 平均利用率 55% < 70% 目标,HPA 按公式不会扩;而 P95 已崩 → 饱和资源不是 CPU。用 CPU 当信号,等于用一个与故障无关的量做决策。
- 容量预算:数据库 300 连接 ÷ 每副本 20 = 副本上限 15。扩到 24 必然打爆连接池 → 扩容不是免费的,它会把压力转移到下游。
- 内存与 QoS:
requests(1Gi) ≠ limits(2Gi)→ Pod 的 QoS 为 Burstable。节点内存压力时 kubelet 会主动驱逐 Pod 回收资源,且驱逐不尊重 PodDisruptionBudget 与 terminationGracePeriodSeconds(硬驱逐阈值为 0 秒宽限,立即关闭)→ PDB 保护不了这类故障。 - 验证预算:压测用的是”逐步加压到 6000 RPS”的稳态,而真实流量是”30 秒 5 倍斜坡”。压测形态与真实形态不匹配,通过结论无效。
核心方案:
- 换扩容信号:从 CPU 换成 每副本在途请求数或队列等待时间(custom / external metrics),保留 CPU 作为辅助下限信号。依据:HPA 的
averageUtilization是”当前使用量 ÷ 该 Pod 的 request”的百分比再对所有 Pod 取平均——它是相对 request 的代理指标,服务瓶颈不在 CPU 时该信号恒不达标。 - 提前扩容建立基线:大促前 10 分钟用定时任务把
minReplicas提到容量预算算出的基线(15 副本),reactive 只负责尖峰余量;窗口结束后自动收缩。依据:45 秒就绪 + 可能的 3 分钟节点扩容,任何 reactive 都来不及。 - 副本上界硬约束为 15,由数据库容量反推,写进 HPA 的
maxReplicas并在 CI 中校验(改池大小必须同步改上界)。 - 连接池按副本数自适应:池大小 = min(⌊300 / 副本数⌋, 单副本上限),通过配置下发或启动探针读取;中期引入连接池代理(pgbouncer 类),把”连接数 × 副本数”的乘法关系解耦。
- 入口准入控制:网关侧按接口/租户分级的并发准入 + 有界队列 + deadline;队列满时按关键性拒绝(保下单,牺牲推荐、报表、导出);被拒请求返回”可判定”的错误码与
Retry-After。 - 就绪与初始化参数对齐:配置
startupProbe覆盖 JVM 预热期;把--horizontal-pod-autoscaler-cpu-initialization-period(默认 5 分钟)与--horizontal-pod-autoscaler-initial-readiness-delay(默认 30 秒)对齐真实启动时长。依据:官方文档明确,未就绪 Pod 与缺失指标的 Pod 在扩容计算中被保守假设为消耗 0%,会抑制扩容幅度——这正是”开抢后 2 分钟才扩”的放大器之一。 - 内存与 QoS 治理:关键服务把
memory requests = limits使其进入 Guaranteed(官方判据:Pod 中每个容器都必须设置 memory/CPU 的 limit 与 request,且 limit 必须等于 request;该判定对 init 容器同样适用)。sidecar 单独设限,避免把关键容器挤爆。把节点硬驱逐阈值(memory.available<100Mi)与 kubelet 监控间隔(housekeeping-interval默认 10 s)纳入容量评估——驱逐是事后动作,不是保护机制。
关键执行流程:
- 容量预算表:单副本稳态吞吐(压测实测)→ 目标峰值 RPS → 需要的副本数 → 与数据库上界取 min → 得到
maxReplicas;再算”扩容决策时延”(指标越界到新 Pod Ready 的实测值)→ 与突增窗口比较 → 决定提前扩容的提前量。 - 信号改造:接入 custom metrics(每副本在途数),HPA 改为
type: Pods或外部指标;CPU 降为辅助。 - 提前扩容:定时任务改
minReplicas,窗口结束回调。 - 池自适应与代理:先配置化,再引入代理。
- 入口限流:按优先级分桶 + 有界队列 + Retry-After。
- 压测复现:必须包含 30 秒 5 倍斜坡场景,而不只是稳态 6000 RPS。
- 灰度与演练:每次大促前跑一次容量演练,把”扩容决策时延”做成 SLO。
技术选型与选择依据:
| 决策点 | 选择 | 依据 | 备选方案与代价 |
|---|---|---|---|
| 扩容信号 | 在途请求数 / 队列等待(custom metrics) | 直接对应排队,与用户感知的延迟同源 | CPU averageUtilization——代理指标,瓶颈不在 CPU 时恒不达标;RPS per pod——更直观但不感知服务时间变化 |
| 扩容时机 | 定时提前扩容 + reactive 余量 | 扩容链路 ≥ 60 s > 30 s 突增窗口,reactive 物理上不可能 | 纯 reactive——必然滞后;纯固定容量——常态成本过高 |
| 副本上界 | 由数据库容量反推并硬编码 | 副本 × 连接数是乘法,无上界的扩容等于把故障转移给 DB | 无限扩容——短暂压住延迟,随后 DB 雪崩,故障更严重 |
| 连接池 | 先按副本数自适应,中期上池代理 | 自适应改动小、见效快;代理是通用解但需要额外组件与运维 | 直接调大 DB 上限——只是把瓶颈后移到 DB 的 CPU 与上下文切换 |
| 过载保护 | 入口有界队列 + 按优先级拒绝 | 让故障形态从”全部超时”变成”部分快速失败”,可保住主链路 | 无限排队——请求排队到超时,客户端重试进一步放大,形成雪崩 |
方案取舍:
- 先扩容还是先限流:先定上界再扩容。即”扩到能扩的上界(15),剩下的用限流拒”。否则扩容会把故障转移到数据库,而数据库的故障恢复时间远长于应用。
- 提前扩容的成本:常态多跑 N 个副本的成本 vs 一次大促故障的成本。用”大促窗口 + 自动收缩”把额外成本限制在窗口内;同时应评估是否用低优先级负载填充这些副本以提高利用率。
- 是否提高 requests 让 CPU 信号更敏感:会降低节点装箱密度、提高常态成本,且仍然度量错误的资源。更合理的是直接换信号;如果暂时不能换,可短期降低 request 使 CPU 利用率数值变敏感,但必须同时意识到这会改变调度语义与装箱。
- 强一致性与吞吐:本场景不涉及数据一致性取舍,但要注意限流拒绝会改变”下单成功率”的统计口径——可用性指标必须按关键性分层统计,否则整体成功率会掩盖主链路的健康。
失败路径、边界条件、超时、重试、幂等与恢复:
- 扩容失败路径:镜像拉取失败、节点资源不足、命名空间配额不足、云配额上限 → 需独立告警,并在入口侧降低准入(因为已知容量不足)。
- 就绪失败:
startupProbe未通过 → Pod 不进入 endpoints,且 HPA 将其计入”未就绪”并按 0% 消耗保守假设,抑制扩容 → 必须让初始化参数与真实启动时长对齐。 - 缩容抖动:缩容稳定窗口默认 5 分钟,控制器取窗口内的最高推荐值,因此缩容是渐近的。不应为了省成本把它调到很小,否则会在流量波动时反复扩缩。
- 客户端重试放大:扩容期间的请求失败会触发客户端重试,进一步打满队列 → 网关必须做重试折叠(同一请求签名的并发重试合并),被拒响应带
Retry-After与明确错误码,可重试性与幂等性分开声明。 - 幂等:下单请求必须携带幂等键;限流拒绝后客户端重试不会因为重复提交造成重复下单——这是”限流会返回失败”这一设计能被接受的前提。
- 恢复:节点 MemoryPressure 后 kubelet 可能同时驱逐多个 Pod,且不尊重 PDB;恢复依赖控制器重建(Deployment 会补副本),但重建同样要 45 秒就绪 → 恢复期内必须依靠入口限流保护,而不是等待扩容。
- OOM 边界:OOMKilled 说明容器触及 memory limit(2Gi)。把 request 调到接近真实工作集,或把关键容器的 request = limit,可以让调度与驱逐行为可预测。
性能、可用性与成本分析:
- 性能:P95 < 400 ms 的目标必须分解为”排队时间 + 服务时间 + 下游时间”三段,各自设阈值。只盯总的 P95 无法定位;而本场景中排队与下游(获取连接)是主要部分。
- 可用性:副本上界 + 限流使故障形态从”全部超时”变为”部分快速失败”。可用性指标应按关键性分层(下单 / 查询 / 报表),整体成功率是误导性指标。
- 成本:提前扩容的窗口成本、常驻基线的成本、限流造成的放弃成交额,三者应与故障成本一起进入决策表;常态应通过 HPA 缩容稳定窗口与低优先级负载填充来控制。
- 安全:节点池自动扩容涉及云凭证与配额,须设置上限与审批;镜像拉取与节点加入须在可信网络中完成。
验证指标与上线后观测:
- 压测必须复现真实形态:30 秒内 5 倍斜坡,而非仅稳态 6000 RPS;同时做一次”节点丢失”演练验证重建时长。
- 核心指标:P95 / P99 及其三段分解;每副本在途请求数;队列等待 P95;扩容决策时延(指标越界 → 新 Pod Ready 的实测耗时,作为 SLO);数据库连接数与水位;OOMKilled 次数;kubelet 驱逐次数(
MemoryPressure持续时长);按优先级分桶的被拒请求比例。 - HPA 可观测:关注 HPA 的推荐副本数与实际副本数的差值、以及”因未就绪 Pod 导致扩容被抑制”的事件——后者是本题中最容易被忽略的信号。
- 上线后:每次大促前跑容量演练;把容量预算表做成可执行的检查(CI 校验
maxReplicas × pool_size ≤ DB 上限)。
关键点
- 弹性需要三个条件同时成立:信号正确、滞后可控、下游有余量。本题三个条件全部不满足。
- HPA 的 CPU
averageUtilization是相对 request 的时间平均代理指标:瓶颈在 off-CPU 等待时它恒不达标,且平均会抹平个别繁忙副本。 - 副本数 × 每副本连接数是乘法:扩容会把压力转移到下游,
maxReplicas必须由下游容量反推,而不是由流量除以单副本吞吐得出。 - 未就绪 Pod 会抑制扩容:HPA 对未就绪与缺指标的 Pod 按 0% 消耗做保守假设,45 秒就绪期与初始化参数不匹配会显著放大滞后。
- PDB 保护不了节点压力驱逐:官方明确驱逐不尊重 PDB 与
terminationGracePeriodSeconds;关键服务要靠 QoS 类、真实内存 request 与容量余量。
追问
追问 1:如果数据库连接上限可以调,能不能直接调大解决?
回答要点:
- 只能缓解,不能解决。数据库的处理能力(CPU、锁、上下文切换)才是真正的上界;连接数增大后,活跃并发上升会拖慢每个查询,延迟反而上升,最终仍会崩,只是崩的位置更靠近 DB。
- 正确做法是把”每副本连接预算”变成部署参数:扩容时联动收缩池大小,使总连接数恒定;或者引入连接池代理,把 N×M 的连接关系变成代理层的固定小连接池。
- 容量预算的方向是反的:不是”DB 能撑多少副本”,而是”业务峰值需要多少副本,因此每个副本只能分到多少连接”。
追问 2:为什么 CPU 平均利用率只有 55%,P95 却崩了?
回答要点:
- 瓶颈在数据库连接等待,属于 off-CPU 等待:线程在等连接时 CPU 是空闲的,因此 CPU 利用率不会上升(这与 B01 的 on-CPU / off-CPU 区分是同一个结构)。
- HPA 的
averageUtilization是”使用量 ÷ request”的百分比再取平均,55% 意味着大部分副本都没到扩容阈值,平均值进一步把个别繁忙副本抹平。 - 平均值还会掩盖突发:即使 CPU 真的是瓶颈,长周期平均也会隐藏秒级打满。正确信号是排队类指标(在途数、队列等待),它直接对应用户感知的延迟。
追问 3:PodDisruptionBudget 能不能防止这次的 Pod 被驱逐?
回答要点:
- 不能。官方明确:节点压力驱逐时 “The kubelet does not respect your configured PodDisruptionBudget or the pod’s
terminationGracePeriodSeconds”;使用硬驱逐阈值时宽限期为 0 秒,立即关闭。 - PDB 只约束自愿中断(如节点排空、集群升级这类由 API 发起的驱逐),它保护不了资源压力导致的驱逐。
- 因此关键服务要靠 QoS 类(Guaranteed)+ 真实的内存 request + 节点余量 + 容量上界来保护,而不是靠 PDB;同时应理解 QoS 类在 Pod 创建时确定且生命周期内不变,改资源导致 QoS 变化会被控制面直接拒绝。
常见误区
- 认为配了 HPA 就等于拥有了弹性。错误原因:弹性受限于”信号正确 + 滞后可控 + 下游有余量”三个条件。本题滞后链路(15 s 周期 + 45 s 就绪 + 可能 3 min 节点)远长于 30 秒突增窗口,且信号本身错配。
- 用平均 CPU 利用率作为容量信号。错误原因:它是相对 request 的时间平均忙闲度,既看不到 off-CPU 等待(本题的真实瓶颈),也会被平均抹平个别副本的繁忙。
- 只扩应用层不看下游容量。错误原因:副本数 × 每副本连接数是乘法关系,无上界的扩容把故障从应用层转移到数据库,而数据库的恢复时间更长、影响面更大。正确顺序是先算下游上界,再定
maxReplicas。
小型实践(30–60 分钟)
写一个容量预算脚本或表格,输入:单副本稳态吞吐(压测实测 RPS)、目标峰值 RPS、Pod 就绪时长、HPA 同步周期、节点扩容时长、下游连接上限、每副本连接数。输出:① 需要的副本数;② 由下游反推的 maxReplicas 与二者取 min 的结果;③ 扩容决策时延估算,以及与突增窗口比较后的结论(是否需要提前扩容、需提前多久);④ 入口限流阈值与按优先级的拒绝顺序。同时在本地对现有 Deployment 清单做静态审查:确认 QoS 类(是否 Guaranteed)、requests 与 limits 是否相等、startupProbe 是否覆盖预热期、HPA 的 maxReplicas × pool_size 是否超过 DB 上限。
延伸学习
- Kubernetes 官方文档《Configure Quality of Service for Pods》:https://kubernetes.io/docs/tasks/configure-pod-container/quality-service-pod/ —— Guaranteed / Burstable / BestEffort 的精确判据、QoS 类在创建时确定且生命周期内不可变。
- Kubernetes 官方文档《Horizontal Pod Autoscaling》:https://kubernetes.io/docs/tasks/run-application/horizontal-pod-autoscale/ —— 期望副本数公式
desiredReplicas = ceil(currentReplicas × currentMetricValue / desiredMetricValue)、容差默认 0.1、同步周期默认 15 s、缩容稳定窗口默认 5 min、未就绪与缺指标 Pod 的保守假设。 - Kubernetes 官方文档《Node-pressure Eviction》:https://kubernetes.io/docs/concepts/scheduling-eviction/node-pressure-eviction/ ——
memory.available的计算(来自 cgroupfs、排除 inactive_file)、硬驱逐阈值默认memory.available<100Mi、housekeeping-interval默认 10 s、驱逐不尊重 PDB。
L01 · LeetCode
题目 ID:Q-20261103-L01 一级类型:栈与队列(辅助栈 / 不变式设计) 二级主题:用同步辅助栈维护前缀最小值 标签:LeetCode、栈、设计题、不变式
题目
设计一个支持 push、pop、top 操作,并能在常数时间内检索到最小元素的栈。实现 MinStack 类:
MinStack()初始化堆栈对象。void push(int value)将元素value推入堆栈。void pop()删除堆栈顶部的元素。int top()获取堆栈顶部的元素。int getMin()获取堆栈中的最小元素。
英文原题附加要求:每个函数的时间复杂度都必须是 O(1)(“You must implement a solution with O(1) time complexity for each function”)。
先思考
- 先想一个最朴素的方案:栈里只存数据,
getMin时遍历一遍。它违反了哪一条要求?在什么调用比例下它会成为瓶颈? - 再想”能不能把答案提前算好”:如果我在每一次 push 的时候,就把”到当前为止的最小值”记下来,push 时已知哪些信息?
- 然后想 pop:弹出一个元素后,之前记下来的那些”到某处为止的最小值”里,哪些还有效、哪些必须一起失效?它们放在什么结构里最自然?
- 最后考虑重复值:连续 push 三个 5,再 pop 两个,
getMin应该还是 5。你的结构会不会把 5 提前丢掉?
参考答案
题号 / 名称 / 难度 / 链接:
- 题号:155
- 中文名:最小栈;英文名:Min Stack
- 难度:medium(⚠️ 本次中英文两个官方页面均未渲染难度标签,此分类按 LeetCode 标准分类记录,未直接核验)
- 官方链接(已核验页面可访问且内容一致):
- 英文页面实时数据(会变动):Accepted 3,024,053 / 5.1M,Acceptance Rate 58.8%;标签为 Stack、Design
输入、输出、约束与示例:
- 输入:操作名数组与参数数组(构造器形式)。
- 输出:每个操作的返回值,
void操作返回null。 - 约束(中英文页一致):
-2^31 <= val <= 2^31 - 1pop、top、getMin总是在非空栈上调用push、pop、top、getMin总共最多调用3 * 10^4次
示例 1:
输入:["MinStack","push","push","push","getMin","pop","top","getMin"][[],[-2],[0],[-3],[],[],[],[]]
输出:[null,null,null,null,-3,null,0,-2]
解释:MinStack minStack = new MinStack();minStack.push(-2);minStack.push(0);minStack.push(-3);minStack.getMin(); --> 返回 -3.minStack.pop();minStack.top(); --> 返回 0.minStack.getMin(); --> 返回 -2.解题思路:
-
直观方案与其局限:只用一个列表存数据,
getMin时min(self._data)遍历全栈。空间 O(1) 额外、实现最简,但getMin是 O(n)。题目要求每个操作 O(1),因此在getMin被频繁调用(例如 3 万次调用中一半是 getMin)时整体退化到 O(n²) 量级——这就是它不可接受的原因。另一个直观想法是”缓存一个最小值变量”,但它在pop掉当前最小值后无法回答”次小值是多少”,除非重新遍历。 -
推荐方案的推导:把”缓存一个最小值”推广为”缓存每一个前缀的最小值”。设数据栈为
data,辅助栈为mins,令不变式:
mins[i] == min(data[0..i]),且len(mins) == len(data)恒成立。push(v):新前缀[0..n]的最小值 =min(旧前缀最小值, v)=min(mins[-1], v)(栈空时即v)。这是 O(1),且维持了不变式。pop():移除最后一个元素,同时移除mins的最后一个元素。由于mins与data等长,剩下的mins[i]仍然精确描述剩下的data[0..i]的前缀最小值——不变式在 pop 后自动保持,不需要重算。top():data[-1],O(1)。getMin():mins[-1],正是整个栈的前缀最小值,O(1)。
关键点在于:辅助栈不是”历史最小值的列表”,而是”与每个栈位置对齐的前缀最小值序列”。正因为对齐,pop 才是”同步弹一个”而不是”重新计算”。
-
正确性说明:用数学归纳法。初始时两栈皆空,不变式平凡成立。设 push 前不变式成立,push 后
mins[n] = min(mins[n-1], v) = min(min(data[0..n-1]), v) = min(data[0..n]),成立。设 pop 前不变式成立,pop 后data与mins同步移除末位,对任意i < n-1,mins[i]与data[0..i]的对应关系未变,成立。因此任意时刻mins[-1] == min(data),getMin返回正确值。 -
复杂度:四操作均为 O(1) 时间;空间 O(n),辅助栈最坏与主栈等长(严格递减输入时每个元素都要进
mins;若改用”仅在新值 ≤ 当前最小值时入栈”的压缩写法,最好可到 O(1) 额外,但最坏仍为 O(n),且 pop 需要一次相等判断)。
Python 参考代码:
class MinStack:
def __init__(self): self._data = [] self._mins = []
def push(self, val: int) -> None: self._data.append(val) if not self._mins: self._mins.append(val) else: self._mins.append(val if val < self._mins[-1] else self._mins[-1])
def pop(self) -> None: self._data.pop() self._mins.pop()
def top(self) -> int: return self._data[-1]
def getMin(self) -> int: return self._mins[-1]边界情况:
- 重复的最小值:连续 push 多个相同最小值时,同步辅助栈(每个位置都记)天然正确;若采用”仅更小才入栈”的压缩写法,必须改成”≤ 当前最小值时入栈”并同步出栈,或用 (值, 计数) 二元组,否则 pop 掉一个最小值时会把其余同值最小值一起丢掉。
- 最小值在栈顶:pop 后
getMin必须回退到次小值,同步出栈自动满足。 - 全负数与极值:
val可取到-2^31与2^31 - 1;本解法只做比较与存储,不涉及算术,不存在溢出问题(差值编码变体会涉及大数相减,需更大整数类型)。 - 单元素栈:push 一个元素后立即
getMin,应返回该元素;mins为空分支必须正确处理。 - 调用次数 3×10^4:递归实现会在长输入下爆栈,必须迭代实现——本解法只用列表操作,无此问题。
- 题目保证
pop/top/getMin在非空栈上调用,因此实现中不必处理空栈;若在面试中被追问,应明确这是题目给出的前提,而不是”不需要考虑”。
测试用例:
| # | 操作序列 | 期望输出 | 覆盖点 |
|---|---|---|---|
| 1 | MinStack, push(-2), push(0), push(-3), getMin, pop, top, getMin | [null,null,null,null,-3,null,0,-2] | 官方示例;pop 后回退到次小值 |
| 2 | MinStack, push(5), push(5), push(5), getMin, pop, getMin, pop, getMin | [null,null,null,null,5,null,5,null,5] | 重复最小值必须逐个存活 |
| 3 | MinStack, push(0), push(1), push(0), getMin, pop, getMin | [null,null,null,null,0,null,0] | 最小值与非最小值交替;零值 |
| 4 | MinStack, push(-2147483648), push(2147483647), getMin, top, pop, getMin | [null,null,null,-2147483648,2147483647,null,-2147483648] | int 边界值 |
| 5 | MinStack, push(3), pop, push(2), push(1), getMin, top | [null,null,null,null,null,null,1,1] | 栈被 pop 空后重新填充,辅助栈同步 |
(以上用例已在本机对参考代码执行并通过,另与”每次 getMin 遍历全栈”的朴素实现做了 3000 组随机操作序列对拍,结果完全一致。)
面试官可能提出的变体或追问:
变体:如果额外空间只能使用 O(1)(不允许辅助栈),怎么办?
回答要点:用差值编码——栈里存 val - current_min 的差值,另用一个变量保存当前最小值。push(v):若为首次,存 0 并令 cur_min = v;否则存 d = v - cur_min,若 d < 0 则更新 cur_min = v。pop():弹出 d,若 d < 0 说明被弹的是历史最小值,需还原 cur_min = cur_min - d。getMin() 返回 cur_min。代价是差值可能超出 32 位整数范围(v 与 cur_min 分别取极值时差值约为 2^32),实现时必须用更大宽度的整数类型——这正是它比辅助栈更容易出错的地方,也是面试中值得主动指出来的边界。若空间不是硬约束,辅助栈更清晰、更难写错。
可迁移的解题能力:
- 把”全局聚合量”变成”与位置对齐的前缀量”:最小值、最大值、前缀和、单调性标记都可以用这个模式把查询从 O(n) 降到 O(1)。关键不在于”多开一个栈”,而在于让辅助结构与主结构同步伸缩,从而使撤销操作免费。
- 不变式驱动的设计与归纳证明:先写出
mins[i] == min(data[0..i]),再让每个操作去维护它。这比”先写 push 再想 pop”要稳,也是把算法题讲清楚的标准方式。 - 撤销可逆性的判断:能用 O(1) 维护,当且仅当该聚合量的”撤销”是局部的(min/max/和可逆,中位数、众数不可逆——后者需要堆或计数结构的额外支持)。
- 与 AI Agent 工程的关系(仅在自然联系处):这与 Agent 会话状态设计同构——检查点里保存”派生量”还是”重算派生量”,本质就是辅助栈与朴素遍历的取舍。把”到当前为止的结论”随每一步同步落盘,恢复时无需重放全部历史;但派生量必须与原始状态同步失效(对应 pop 时同步弹辅助栈),否则就会出现”状态回滚了、结论没回滚”的不一致。
关键点
- 不变式是
mins[i] == min(data[0..i])且两栈等长——这是 pop 能 O(1) 同步出栈的根本原因。 - 压缩写法必须处理重复最小值:只在
val <= 当前最小值时入栈(或用计数对),否则会提前丢掉同值最小值。 - 题目保证非空调用是前提而非可忽略项;追问时应能说清这一点。
- 差值编码变体存在整数宽度陷阱,需要指出而不是回避。
- 空间是 O(n) 最坏、O(1) 最好(压缩写法),面试中要主动说明两种写法的取舍。
常见误区
- 只缓存一个最小值变量,pop 后重新遍历求次小值。错误原因:这使
pop退化为 O(n),违反了题目”每个函数 O(1)“的明确要求;在 3 万次调用规模下会被明显放大。 - 用”严格小于”作为辅助栈入栈条件(
if val < mins[-1])。错误原因:连续 push 相同的多个最小值时只记录一次,pop 掉其中一个后mins提前清空或丢失,后续getMin返回错误值(用例 2 正是为此设计)。 - 认为辅助栈方案”浪费空间”而直接上差值编码。错误原因:差值编码在
val取-2^31而cur_min取2^31 - 1时差值约为2^32,超出 32 位整数;在 Python 中虽无溢出,但在 Java / Go / C++ 中必须用 64 位整数,属于典型的”为了省空间引入正确性风险”。
延伸学习
- LeetCode 官方题页(中英文,见上):约束、示例与 O(1) 要求的原始出处。
- 延伸练习方向(本轮未核验页面内容,仅作为下一步训练方向):LeetCode 232 用栈实现队列(两个栈的摊销 O(1))与 LeetCode 716 最大栈(需要同时支持 O(1) 的 popMax,辅助栈需支持删除,难度高于本题)。
延伸资料
按「官方文档 / 标准规范 > 原始论文 > 官方工程博客 > 可信技术组织 > 可验证技术文章」的优先级给出,均为本次任务已核验的一手来源(除明确标注”未核验”者)。
B01 · 性能分析的度量对象
- Brendan Gregg《The USE Method(Utilization Saturation and Errors)》—— https://www.brendangregg.com/usemethod.html 。三项的精确定义、“For every resource, check utilization, saturation, and errors”、约 30 项指标清单、Suggested Interpretations 与”低平均利用率不等于无饱和”的真实案例。
- Brendan Gregg《Flame Graphs》—— https://www.brendangregg.com/flamegraphs.html 。x 轴是栈样本总体按字母序排列而非时间流逝、宽度是出现频次、顶边为 on-CPU、icicle 与 flame chart 的区别;同站另有 Off-CPU Flame Graphs 专页可作延伸。
B02 · RAG 评测设计
- Ragas 官方文档:Faithfulness(https://docs.ragas.io/en/stable/concepts/metrics/available_metrics/faithfulness/)与 Context Precision(https://docs.ragas.io/en/stable/concepts/metrics/available_metrics/context_precision/)——公式、有无 reference 的两种形态、ID-based 与非 LLM 变体。
- arXiv:2309.15217《Ragas: Automated Evaluation of Retrieval Augmented Generation》(Es、James、Espinosa-Anke、Schockaert;2023-09-26 v1,2025-04-28 v2)——reference-free 评测的原始论文,明确”without having to rely on ground truth human annotations”的取舍。
S01 · Agent 线上故障定位与评测
- OpenTelemetry GenAI 语义约定仓库
open-telemetry/semantic-conventions-genai——agent 与工具调用 span 的模型化方式。注意:该约定当前整体状态仍为 Development(gen_ai.*专属属性均为 Development,仅继承的error.type/server.address/server.port为 Stable)。该状态为 2026-11-02 配置维护时第 5 次复核的结论,本轮沿用未再逐页核验。 - Ragas 官方指标文档(同上)——用于区分”可断言指标”与”LLM judge 指标”的边界。
S02 · Kubernetes 扩缩容与容量治理
- 《Configure Quality of Service for Pods》—— https://kubernetes.io/docs/tasks/configure-pod-container/quality-service-pod/ 。Guaranteed / Burstable / BestEffort 的精确判据,QoS 类在创建时确定且生命周期内不可变。
- 《Horizontal Pod Autoscaling》—— https://kubernetes.io/docs/tasks/run-application/horizontal-pod-autoscale/ 。
desiredReplicas = ceil(currentReplicas × currentMetricValue / desiredMetricValue)、容差默认 0.1、同步周期默认 15 s、缩容稳定窗口默认 5 min、未就绪与缺指标 Pod 的保守假设。 - 《Node-pressure Eviction》—— https://kubernetes.io/docs/concepts/scheduling-eviction/node-pressure-eviction/ 。
memory.available的计算(来自 cgroupfs、排除 inactive_file)、硬驱逐阈值默认memory.available<100Mi、housekeeping-interval默认 10 s、驱逐不尊重 PDB 与terminationGracePeriodSeconds。
L01 · LeetCode 155
- LeetCode 官方题页(中文 https://leetcode.cn/problems/min-stack/ ;英文 https://leetcode.com/problems/min-stack/ )——约束、示例与 O(1) 要求的原始出处。两侧页面本次均未渲染难度标签,难度 medium 按标准分类记录,标注「未直接核验」。
- 延伸训练方向(未核验页面内容,仅作下一步方向):LeetCode 232 用栈实现队列、LeetCode 716 最大栈。
当日重点
- 代理指标必须标明它度量的是哪个对象:CPU 利用率度量的是”忙”,不是”排队”;faithfulness 度量的是”对上下文忠实”,不是”对世界为真”;离线通过率度量的是”评测集覆盖到的部分”,不是”线上”。
- 每一项代理指标都要准备一条不依赖它的独立判定路径:USE 之外是饱和度与队列长度;judge 之外是可断言指标;HPA 之外是在途请求数与队列等待。
- 降级方向的正确顺序:能力缺失走向”降低自动化程度”(转人工),而不是”降低判定标准”。
- 容量与弹性是两个问题:容量由下游反推上界,弹性由滞后链路的时间预算决定;两者都不由 CPU 平均利用率决定。
- 失败语义要结构化:
capability_not_found与invalid_argument必须分离,否则模型唯一能做的”合理动作”就是反复重试同一工具。 - 算法层面:把全局聚合量变成与位置对齐的前缀量,并让辅助结构与主结构同步伸缩——这是把查询从 O(n) 降到 O(1) 的通用模式。
归档与去重记录
配置读取状态
- 配置读取:成功。文件
../题库配置.md,版本 8(last_maintained: 2026-11-02)。 - 本次为 v8 生效后第 2 次每日出题。无暂停类型;「观察中的新技术」8 项均未标记「允许试学」,本次未从观察项出题。
- 未修改配置文件。运行前已确认目标目录与父目录仍无
AGENTS.md/CLAUDE.md。
去重结果
- 去重窗口:30 天(2026-10-04 至 2026-11-03)。参考来源:
题目索引.md(95 条 /indexed_runs: 17)、精选/README.md(4 条)、README.md。未读取更早的完整题目正文。 - 逐题比对结论:
| 题目 ID | 与近期题目的关系 | 结论 |
|---|---|---|
| Q-20261103-B01 | 与 2026-10-14 B01(容器 CPU 配额与 CFS 限流)、2026-10-27 B01(事件循环与 I/O 模型)同属操作系统与网络 | 不重复。前两者分别考察”配额是带宽而非并行度约束”与”阻塞调用隔离位置”;本题考察的是度量对象本身(USE 三项、平均值、on-CPU/off-CPU 口径),核心结论是”指标全绿仍可能错” |
| Q-20261103-B02 | 与 2026-10-13 B03(混合检索融合与重排归因)、2026-10-23 B02(Query Rewrite)、2026-10-29 B02(Graph RAG)同属 RAG 与搜索;与 2026-10-27 B02(Golden Dataset 分层)同属评测 | 不重复。前者考察检索链路的构建与归因顺序,2026-10-27 B02 考察评测集分层与离线/线上分工;本题考察的是评测指标的分层定义与 reference-free 的循环论证风险,核心结论是”答案对不等于检索对” |
| Q-20261103-S01 | 与 2026-10-22 B02(Trace / Span 数据模型)、2026-10-27 B02(Golden Dataset 分层)相邻 | 不重复。本题的核心方案是工具错误语义分离 + 重试预算 + 评测集覆盖缺口,故障现象是”离线全绿而线上掉 11 个点”,与前述两题的考察点不同 |
| Q-20261103-S02 | 与 2026-11-02 S02(备份与 PITR)、2026-10-19 S02(发布期 502)、2026-10-23 S01(GPU 容量规划)相邻 | 不重复。本题核心是扩容信号错配 + 滞后链路时间预算 + 下游容量上界,属集群调度与弹性维度,与”恢复演练""优雅终止""推理排队”三种故障形态不同 |
| Q-20261103-L01 | 已用题号:146、3、215、739、875、124、207、142、547、560、39、1109、435、208、92、516、31(另 322 已删未登记,仍规避) | 无题号重复。算法模式为「栈 + 同步辅助栈 / 不变式设计」,窗口内首次出现 |
- 类型集中度自查:上一轮已标注「工具与互操作协议在最近 5 次 Agent 场景题中出现 4 次,下次 S01 应轮换到 AI Agent 核心 / Agent 评测与可观测性 / RAG 与搜索」。本轮 S01 已按该建议轮换到「Agent 评测与可观测性」(兼 AI Agent 核心),工具与互操作协议本轮未出题。
- 场景题类型使用情况:本轮新增「线上故障定位与可观测性」(Agent 场景,首次)与「Kubernetes 扩缩容与容量治理」(非 Agent 场景,首次)。
覆盖变化与下一周期建议
- 本轮填补的标注缺口:操作系统与网络「CPU 与内存性能分析」(B01)、RAG 与搜索「RAG 评测设计」(B02)。
- 覆盖次数(索引窗口内,写入索引前的值 +1):操作系统与网络 7→8、RAG 与搜索 5→6、Agent 评测与可观测性 4→5、软件工程与平台工程 7→8。
- 下一周期优先方向:AI 安全与治理(4,路径与地址约束一致性的独立出题、SSRF 边界、跨站调用 Origin 校验)、LLM 应用工程(4,批处理与延迟的联合调优、跨厂商缓存收益对比)、上下文工程与能力封装(6,剩余缺口「上下文写入与选择策略」)、数据库与存储(9,执行计划回归的上线门禁、分区表统计盲区)、分布式系统(9,时钟不确定区间的工程落地)。
- LeetCode 下一轮建议轮换:单调队列应用型、栈与队列应用型(232 / 946)、Trie 变体(211)、二维差分、状压 / 位运算;避免连续出现栈设计、原地数组变换、区间 DP、链表操作、Trie、贪心、差分 / 前缀和、回溯、并查集、图论、二分主导模式。
本次参考的历史题目与文件
题目索引.md(95 条主表、indexed_runs: 17、total_featured: 4)精选/README.md(4 条精选题目,含复习节奏表)README.md(每日训练导航表)题库配置.md(版本 8,只读)- 未读取窗口外的历史训练文件正文。
保存与同步状态
- 本地保存:成功(路径
2026-11/2026-11-03-AI-Agent-面试训练.md)。 - 远端同步:未验证。写入本地 Obsidian 目录不等于 iCloud 或其他远端已同步,本次未做独立验证,不声称已同步。
- 本次执行未触发精选(用户未提出明确精选请求),未收到作答反馈,未修改任何历史训练文件正文。