Shopee AI应用开发 一面
1. 自我介绍,围绕一条链路说明你做过什么、你负责什么、难点在哪
2. 一个面向多业务域的 Agent 系统,为什么不能只用一个大 Agent 把所有事情做完
单 Agent 的问题不是“不能做”,而是做大以后上下文会混杂,知识边界、工具边界和状态边界都不清楚。比如订单查询、风控审计、赔付建议、配置解释,本质上就是不同任务类型,它们需要的知识、状态和可调用工具完全不同。如果硬塞到一个 Agent,模型容易被无关上下文干扰,也容易误用工具。实际工程里更稳的做法是做任务拆分:入口先做路由,再把请求交给垂直 Agent 或垂直 Skill 处理。
3. 对多场景、多上下文的请求做路由时,如何判断当前应该调哪个 Agent
不能只靠 prompt 里写“你来判断”,要做显式路由层。一般会先抽取任务槽位,比如业务域、对象 ID、是否需要实时数据、是否涉及写操作、风险等级,然后结合规则和轻量分类模型缩小候选范围,最后再让模型在有限集合内做决策。这样比让模型直接在几十个能力里盲选稳定得多,而且便于审计和灰度。
public String route(TaskContext ctx) {
if (ctx.isWriteOperation()) return "guarded_executor";
if ("order".equals(ctx.getDomain()) && ctx.hasOrderId()) return "order_agent";
if ("risk".equals(ctx.getDomain())) return "risk_agent";
return "general_qa_agent";
}
4. 如果前端只有一个对话框,但后端实际上拆成多个 Agent,怎么保证用户不会感觉系统“失忆”
前端一个对话框不代表后端只能有一份上下文。工程上通常会把记忆拆成三层:会话层保存用户当前表达,任务层保存结构化状态,领域层保存长期知识和执行痕迹。用户感觉连续,是因为外层会话 ID 没变;系统不失控,是因为真正参与推理的不是整段聊天记录,而是被提炼过的任务状态和相关上下文。这样即使请求被路由到不同 Agent,也能共享必要状态,而不会把所有历史都塞进去。
5. 同一个问题,你怎么评估不同 LLM 在你的系统里到底谁更适合上线
不能只看人工体验,也不能只看一个公开 benchmark。真正要评的是任务级指标,比如路由正确率、工具选择正确率、参数填写正确率、拒答准确率、平均调用次数、总耗时和成本。再往下还要看高风险场景,比如是否容易越权、是否会编造工具参数、是否在证据不足时乱答。上线选型本质上不是“谁最聪明”,而是谁在你的业务约束下最稳定。
6. Redis 缓存在这类 Agent / 后端系统里通常放在哪几层,为什么不能只理解成“缓存数据库查询”
Redis 的价值不只是缓存查询结果。它可以放在热点对象缓存层,比如用户画像、配置快照、知识片段;也可以放在会话态层,比如短期上下文、幂等标记、路由结果;还可以放在调度层,比如延迟任务、限流计数、分布式锁。很多系统把 Redis 只当数据库前挡板,其实它更像一个高性能状态中间层,承担的是“把慢数据、热数据、临时状态和协调信息分开”。
7. 如果没有热点缓存,系统里的数据通常会从哪里取,代价差异体现在哪
没有缓存时,数据通常会回源到 MySQL、ES、对象存储、向量检索库或者下游 RPC 服务。问题不在“都能查到”,而在代价模型完全不同。数据库擅长精确查,但扛不住高频重复访问;ES 擅长复杂检索,但写放大和一致性成本高;RPC 查实时状态最准确,但依赖链长、尾延迟高。缓存的核心价值就是把“高频但重复”的读取从这些高代价路径上摘下来。
8. 如果业务方说数据库 10ms 和缓存 1ms 差得没那么夸张,为什么你还坚持要做缓存
不能只看单次延迟差,要看系统总体形态。第一,热点请求下 10ms 的数据库访问会形成连接池争抢、锁竞争和 QPS 放大,而 1ms 的缓存访问更容易横向扩展。第二,缓存能减少下游抖动放大,数据库一旦慢下来,问题会沿着线程池和连接池扩散。第三,高并发系统关注的不只是平均值,更是 P99 和雪崩风险。缓存不是为了把 10ms 变成 1ms,而是为了改掉整条链路的脆弱性。
9. 假如缓存失效后仍然大量回源,而且数据库已经慢了,你会优先做哪些止血措施
先做限流和热点隔离,避免所有请求同时回源。然后做 singleflight 或请求合并,让同一批热点 key 只有一个线程去查库。再往后是本地缓存兜底、过期时间错峰、热点 key 永不过期加主动刷新。真正到数据库层面,才考虑索引优化、只读副本、结果裁剪和旁路降级。线上止血的顺序通常是先控流量,再减回源,再修数据层。
public String getWithSingleFlight(String key) {
String value = redis.get(key);
if (value != null) return value;
synchronized (("LOCK_" + key).intern()) {
value = redis.get(key);
if (value != null) return value;
value = queryDb(key);
if (value != null) redis.setex(key, 300, value);
return value;
}
}
10. 数据量特别大时,如果只按一个维度分库分表,为什么后面经常会被低频复杂查询拖死
因为分库分表解决的是主访问路径,不会自动解决所有查询路径。比如订单按用户 ID 分片之后,按时间范围、状态、商家 ID、地区组合查询就会变成跨分片扫描,低频时还能忍,高一点就会形成后台慢查询和资源竞争。很多系统误以为“分了表就快”,实际上只是把主路径优化了,复杂查询问题只是被推迟了。
11. 如果主表按订单 ID 或用户 ID 分片,但后台还要支持按创建时间、状态、店铺 ID 组合筛选,你会怎么设计
常见做法不是直接跨库扫主表,而是补一个查询友好的旁路索引层。可以是 ES、宽表索引库、查询中间表,甚至是按业务条件预聚合的检索表。核心思想是把“事务主存储”和“检索友好存储”拆开。主表负责正确性和写入,索引层负责复杂读查询。这样复杂条件检索就不必压在主事务库上。
12. 这种做法本质上是不是旁路索引,旁路索引的数据一致性怎么保证
是,本质上就是旁路索引。旁路索引最大的难点不是建出来,而是如何和主链路保持足够一致。常见方法是主表写入后发 binlog/消息流,由异步消费者更新索引;为了避免丢数据,还要做重放、补偿和定时对账。这里通常追求的是“可接受延迟的一致性”,而不是强一致。真正高风险的操作仍然要回主库确认。
CREATE TABLE order_query_index (
order_id BIGINT PRIMARY KEY,
shop_id BIGINT NOT NULL,
user_id BIGINT NOT NULL,
order_status INT NOT NULL,
created_at DATETIME NOT NULL,
INDEX idx_
剩余60%内容,订阅专栏后可继续查看/也可单篇购买
本专栏聚焦 AI-Agent 面试高频考点,内容来自真实面试与项目实践。系统覆盖大模型基础、Prompt工程、RAG、Agent架构、工具调用、多Agent协作、记忆机制、评测、安全与部署优化等核心模块。以“原理+场景+实战”为主线,提供高频题解析、标准答题思路与工程落地方法,帮助你高效查漏补缺.

查看9道真题和解析