面灵AI→

携程 AI 应用开发实习一面:上下文管理与联合索引连环问

轮次
一面
结果
OC
时间
2026-09
来源
牛客网

《面试题目》

Agent 上下文管理

  1. 对 Agent 的上下文管理有什么了解?
  2. 除了工具压缩还有哪些压缩机制?
  3. 保留什么?压缩什么?采用什么方式?
  4. 什么时候触发压缩?
  5. 压缩后上下文里主要包含些什么?按照什么顺序排序?

Java 并发

  1. Java 的 AQS 和 CAS 分别是什么?

MySQL 索引

  1. 什么是覆盖索引?它的核心是什么?
  2. 联合索引 (A, B, C),假设 WHERE 条件已经满足要求,SELECT 后面写什么可以不回表?
  3. 联合索引 (A, B, C),如果条件只有 A 和 B 的等值条件,还可以使用这个索引吗?
  4. 把条件顺序写成 B = 某值 AND A = 某值,还可以使用这个索引吗?
  5. 如果只有 B = 某值 AND C = 某值,索引会怎么走?
  6. 没有 A 条件时,你说的”把 A 的值拼出来”具体怎么做?
  7. 如果条件是 A = 某值 AND C = 某值,中间没有 B,索引怎么走?
  8. 上述查询在索引树上具体是什么流程?

Redis 缓存一致性

  1. 数据库和缓存各有一份数据,数据库发生修改后,怎么让两者保持最终一致?
  2. 延迟双删相对”先更新数据库,再删除缓存”,具体加强在哪里?解决了什么问题?

MQ 与子线程异步调用

  1. 下面两种异步任务的实现方式有什么区别?方式一:应用 A 向 MQ 发送消息,由应用 B 消费消息并执行任务;方式二:应用 A 启动子线程,由子线程通过同步 HTTP 或 RPC 调用应用 B,A 的主线程不等待子线程结束。
  2. 你说主线程与子线程有关联,这种关联具体体现在哪里?
  3. 除了上面说的,这两种方式还有其他区别或要补充的地方吗?

AICoding 提效与局限性

  1. AI Coding 对你来说带来了哪些提效?你认为它有什么局限?

《参考解析》

Agent 上下文压缩:先分类,再谈策略。上下文里的内容按”丢了会不会出事”排序,压缩就是按这个顺序从后往前砍:系统指令与工具 schema、用户当前任务与硬约束属于不可丢;当前任务的中间结果和最近几轮对话要保留;历史轮次、完整工具返回、检索原文属于可压缩对象。压缩手段通常叠加使用:截断(按 token 预算丢掉最早的轮次)、摘要(把一段历史交给便宜模型压缩成要点)、工具结果瘦身(只回灌必要字段而非整段 JSON)、结构化记忆(把稳定事实抽成 profile 或 key-value 常驻,代替长篇对话)。触发时机有两种:定量的水位触发(用到窗口的 70%~80%)和定性的节点触发(任务阶段切换、工具返回超大结果时)。压缩后的上下文顺序一般按”稳定性递减”排:固定的系统指令与工具定义放最前面吃前缀缓存,然后是长期记忆与任务目标,最后才是逐轮对话与最新工具结果——把最易变的内容放尾部,能最大化缓存命中率。

联合索引与最左前缀。覆盖索引的核心是”查询需要的列全部在索引里,不用回表”,所以 SELECT A, B, C 可以不回表,SELECT A, B, C, D 就需要。等值条件里 A、B 都有时,WHERE A=? AND B=? 能用到索引的 A、B 两列;条件书写顺序不影响,优化器会自己排。只有 B、C 而没有 A 时,用不上这个索引(因为无法从索引树的根开始定位),走全表或别的索引。A=? AND C=?(中间缺 B)的情况比较微妙:A 能用来定位,C 用不上索引的有序性做范围收缩,但**在 MySQL 8.0 的索引条件下推(ICP)**下,C 可以在索引层过滤掉不匹配的记录、减少回表次数,代价是不能减小子树的扫描范围——所以执行计划里 key 仍然用上、(A, C) 组合只能算部分利用。至于”把 A 的值拼出来”这类说法,往往指优化器的等值传播:当 A 是常量时,索引里隐含的 A 段是确定的,扫描可以从 (A, ?) 这个前缀开始而不是从头遍历整棵树。回答这一串时,最有说服力的是能把”能不能用索引”和”用得多深”分开讲:能不能用取决于最左前缀,用得多深取决于范围条件之后的有序性是否还在。

Redis 与数据库的最终一致。主流做法是 Cache Aside:写时先更新数据库、再删除缓存;读时未命中就查库回填。不选”更新缓存”是因为并发写会让两个请求的更新顺序错乱,留下旧值;删除是幂等的,最终一定会回填成新值。它仍有窗口:读请求刚好在”更新库”和”删缓存”之间回填了旧值。延迟双删是针对这个窗口打的补丁——更新库后删一次缓存,隔一小段时间(略大于一次读请求回填的耗时)再删一次,把中间回填进去的旧值清掉。它加强的是”清掉竞态期间被写回的脏数据”,代价是第二次删除的延迟不好估、也不保证 100%;更稳的做法是订阅 binlog(Canal)异步失效缓存,把删除动作与业务代码解耦。

MQ 异步 vs 子线程异步。本质区别在”可靠性和边界”:MQ 有持久化、重试、死信队列,消息在进程崩溃或下游不可用时不会丢,而且天然解耦(A 不需要知道 B 在哪);子线程是自己进程内的并发,进程一挂线程就没了,任务丢失且没有重试语义。子线程的关联体现在它与主线程共享进程资源——共享内存与连接池、主线程退出时子线程可能被强制中断、异常不会被自动捕获(要显式处理,否则静默丢失)。此外还有容量与背压:MQ 可以堆积削峰,线程池队列会满、满了要么拒绝要么阻塞主线程。选型上,跨服务、要求可靠投递的用 MQ;同进程内、允许丢失、只是不想阻塞主线程的小任务才用线程池。

AQS 与 CAS。CAS 是一条 CPU 原子指令(比较并交换),是乐观锁的底座,AQS 则是在 CAS 加 volatile 之上搭出来的同步器框架:用一个 volatile int state 表示同步状态,用一个 CLH 变体的双向队列挂住抢不到锁的线程,ReentrantLock、Semaphore、CountDownLatch 都基于它实现。典型问题也要能答:CAS 的 ABA 问题(用版本号或 AtomicStampedReference 解决)、自旋开销(竞争激烈时改走排队阻塞)、以及 AQS 里公平锁与非公平锁的差异(非公平锁允许插队、吞吐更高)。