阿里巴巴千问技术部一面:手撕滑动窗口限流、训练集群指标链路与 Agent 运行时
- 轮次
- 一面
- 时间
- 2026-09
- 来源
- 牛客网
《面试题目》
一、自我介绍与项目
- 个人介绍。
- 里面有哪些项目 / 需求,是你从头到尾参与设计的?
- 手撕:实现一个线程安全的滑动窗口限流算法。
- 你的这个并发处理耗时请求是怎么设计的?
二、可观测与消息链路
- 假设我有一个训练集群,每个节点都有大量 metrics 需要记录和分析,你怎么设计?
- 令牌是针对不同的资源吗?还是针对协程?
- 假设我现在需要进行分布式的收集和聚合分析,整个处理链路你怎么设计?
- 实习中你的延迟队列是用来做什么的?底层是怎么实现的?
- 如果用 Kafka、RocketMQ 这些,你会怎么做?
- 如何保证大流量下消息处理的鲁棒性?
- 使用 Kafka,消费者在并发处理时,会导致丢消息吗?为什么?
- 那你怎么处理丢消息的问题?
三、Agent 运行时
- 介绍一下你们的 TSM 平台和 Agent 功能。
- 说一下一个用户发起一个对话时,底层执行的链路。
- 你们的这个 plan 能力是怎么和 agent loop 配合的?
- 执行过程中 agent 怎么知道要不要进行重规划?
- 你说有些模型不支持工具调用,那工具调用能力是怎么实现的?
- 更底层的实现有没有了解过?agent 怎么知道要进行工具调用、以及选择哪个工具调用?
- 你们的上下文是怎么组装的?
- 上下文长度爆了怎么办,你们是怎么处理的?
- 你们用的底层模型有哪些?
- 用户在问到一半切换了模型,你们怎么处理?
- 中途切换模型一定会有影响吗?具体可能有哪些影响?
- 等待模型输出时,用户刷新了页面或者关闭了页面,重新打开后你们怎么支持重连?
- 你们有对 agent 进行评测吗?
- agent 本身修改后,你们如何去衡量此次修改的好坏?
- 说一下 RSI,以及如果你去设计,怎么在你们现有的 agent 里加上 RSI?
- 你们现有的评测里面用了哪些指标?为什么选这些指标?
- review agent 怎么确保它的评价是正确的?
- 你们的数据集是怎么构建的?有多少?
- 回顾一下你们整个 agent 的开发和设计,如果让你重新开发,你会有哪些方面的优化或者修改?
- 反问。
《参考解析》
滑动窗口限流。 先定义语义:任意时刻,最近窗口 T 内最多放行 N 个请求;等于左边界的时间戳视为过期;被拒绝的请求不计入。核心不变量是「清理过期、判断容量、记录放行」必须在同一个临界区里完成。用固定容量环形队列存最近放行的时间戳(容量就是 N),用互斥锁保证线性化,且时间戳要在锁内取单调时钟,否则排队前取的时间会让记录顺序错乱。复杂度 O(1) 摊销。要补一句边界:这是单进程单配额键的精确滑动日志;多实例全局限流得靠 Redis + Lua 或集中式配额服务,此时还要处理时钟偏移与原子性。另一条常见追问是「和令牌桶/漏桶的区别」:滑动窗口限制的是窗口内总量、能严格不超发,令牌桶允许突发、更适合限速。
训练集群 metrics 链路。 采集端每节点一个 agent(node_exporter 类)拉取或接收,按 {租户, 集群, 节点, 指标名, 标签} 组织序列;写入端按序列键哈希分区保证同一序列的顺序,事件时间与处理时间分开,用 watermark + 允许迟到决定窗口何时关闭。聚合要尊重指标语义:counter 求速率要处理进程重启导致的 reset,gauge 按时段选最后值/最大值/时间加权,平均值用 sum/count 而不是平均各节点值,P99 不能对节点 P99 取平均(要合并直方图桶再估分位数)。高可用采集会产生重复样本,用副本标签 + 后端去重。这条链路的瓶颈通常在「标签基数」而不是写入吞吐——高基数标签是监控系统最常见的自伤方式。
延迟队列。 常见用途:任务超时检查、订单到期、失败退避、租约清理。要点是「到期只代表具备执行资格」,执行前必须重读业务状态。单机用最小堆 + 定时器,插入更早的任务时唤醒重设;大量定时任务用时间轮换取更低调度开销。分布式实现必须有持久化与原子领取:Redis ZSET 用到期时间做 score,但不能「先查再各自删」——要用 Lua 原子移到处理中集合并带租约、尝试次数与恢复扫描;直接弹出后执行、进程挂了就丢任务。数据库任务表用到期索引 + 条件更新领取,并为每次执行记幂等键。Kafka 的记录 timestamp 不能当「到期才投递」的保证,要按延迟等级设 retry topic 或外部调度器;绝不能在消费线程里 sleep 等到期,那会阻塞同分区其它消息并让 poll 失活。
Kafka 并发消费会不会丢消息。 并发本身不丢,丢的是「位点越过了尚未完成的记录」。Kafka 里有三个不同的位置:已拉取位置、业务完成位置、已提交位置。同一分区 100/101/102 并行处理,102 先完成就提交 103,此时崩溃恢复从 103 开始,100 和 101 就被跳过。修法是按分区维护「已完整处理的前缀」,只提交这个前缀之后的安全恢复位置;需要分区内并发时,按交付顺序登记在途记录,worker 回报状态,从队首逐个移除已完成项,遇到未完成就停止推进(注意 offset 可能是空洞的,要用实际交付记录推进)。再加上业务侧唯一键/幂等接口防重放;重平衡时停止派发、只提交已完成前缀,用分区代次或 fencing 挡住失去所有权的旧 worker。
Agent 对话的完整链路。 鉴权与租户识别 → 组装上下文(固定指令、工具定义、历史、检索结果,并做权限过滤与 token 预算)→ 模型适配层把内部消息映射成供应商协议 → 流式接收文本与工具调用事件 → 服务端做参数解析、Schema 校验、鉴权、预算与副作用检查 → 执行工具、按调用 ID 写回状态 → 计划与验证器决定继续/重试/重规划/等人工/结束(loop 要有步数与费用上限)→ 输出事件按 run 内递增序号持久化并推给客户端,完成后保存产物与状态,trace 串起模型请求、工具调用和外部回执。被追问「不支持工具调用的模型怎么办」,答案是降级成提示词约定的结构化输出 + 服务端解析校验(ReAct 文本协议),并保留失败重试与格式纠错。「agent 怎么选工具」落到:工具描述与参数 Schema 进上下文,模型输出调用名 + 参数,服务端按名字路由;所以工具命名、描述与参数设计本身就是召回质量的一部分。
Plan 与 loop、重规划。 Plan 不是塞进 Prompt 的一段死文本,要存成版本化结构:每个 step 有 id、目标、依赖、状态、预期产物与验收条件;loop 每次只取「依赖已满足」的步骤,让模型提出行动,再由确定性代码推进状态。重规划的触发条件要显式:连续失败、观察结果与预期不符、依赖不可用、预算将尽、发现新前置条件——把这些写成规则而不是让模型凭感觉。上下文组装同理:先预算,再按优先级装入指令、状态、证据、近期消息。
上下文爆了与模型切换。 超限的处理顺序是:压缩旧工具输出 → 摘要已完成的历史段 → 必要时丢弃低价值证据,但绝不截断正在进行的工具调用序列。中途换模型时,把会话状态(历史、摘要、工具调用记录)按内部格式保存、而不是按某个供应商的格式保存,换模型只需重新映射;影响主要有三块:不同模型的工具调用协议与并行调用支持不同、上下文窗口与 token 计费不同、行为风格与指令遵循差异会让已有 Prompt 效果变化。所以切换要有评测基线与灰度,并记录切换点前后的指标。
断线重连。 模型输出期间客户端断开,服务端不要把任务杀掉:run 有独立生命周期,输出事件按序号持久化(数据库或事件流),客户端重连时带上 run_id + 最后收到的事件序号做增量拉取,缺的部分从存储补发;期间允许的缓冲窗口要有限(比如保留最近 N 分钟或直到 run 结束),否则存储无限膨胀。如果用户主动取消,要传播取消信号到在途工具调用,只提交已完成的前缀,并对不可撤销的副作用做补偿。
评测与 RSI。 评测集要覆盖场景矩阵而不是堆数量:任务类型 × 难度 × 工具组合 × 长尾失败模式。指标分层:结果层(任务成功率、事实一致性、人工替代率)、过程层(工具选择准确率、参数合法率、平均步数、重规划次数)、体验层(首字延迟、总时长)、成本层(每成功任务 token 花费)。有主观性的 case(30% 那类)用「评分标准 + 双人/双模型交叉 + 抽样人工校准」,并报告一致性(如 Kappa)而不是只报平均分。判断改动好坏:同一批固定 case 上跑新旧两版(AB/BA 消偏),看净胜率,同时检查有没有把某类场景改坏。RSI(Recursive Self-Improvement)落地成受控的外层循环:收集失败轨迹 → 按根因聚类 → 生成 Prompt/工具说明/Skill/代码改动候选 → 在回归集与真实回放上验证 → 通过才灰度,绝不等于让模型在线改自己。
Review Agent 的正确性。 用确定性检查兜底:能编译/能跑测试/能通过 schema 校验的先过机器关;评审结论必须引用具体证据(文件、行号、复现步骤),拒绝「我觉得」;对同一改动做双评审或换序评审看结论是否稳定;把评审结论与后续真实结果(是否回滚、是否出故障)对齐,长期统计精确率与召回率,持续校准评分标准。
数据集构建。 来源分三类:真实线上轨迹(脱敏后按失败类型分层采样)、人工构造的边界与对抗 case、从文档/工单合成的任务。每条样本要带环境快照、工具权限、验收条件与期望产物,可重放才算合格。规模不是关键指标——几百条覆盖良好的回归集比几万条同质样本有用,且要持续把线上 badcase 回流。