水滴公司 AI 全栈工程师一面:从进程线程到 Agent 记忆设计
- 轮次
- 一面
- 时间
- 2026-10
- 来源
- 牛客网
《面试题目》
- 自我介绍。
- 你两个实习经历分别在什么地方?哪个城市?
- 进程和线程的区别?
- 进程之间会共享一些资源,对吧?还有其他的吗?
- 假如一个程序由多个进程组成,进程间通信的方式都有哪些?
- 还有其他的吗?
- 管道通信有哪些特点?
- 网络那块了解吗?你是什么专业的?
- 说一下 TCP,它的特性是什么?
- 你刚才讲 TCP 的「安全性」是什么意思?
- TCP 三次握手是走哪三次?
- 实际过程中也用过 Java,对吗?
- Java 创建线程池用 ThreadPoolExecutor,它有几个核心参数?
- 这几个参数的作用分别是什么?
- 拒绝策略里「放弃第一个」是啥意思?是放弃当前这个吗?
- 「放弃最后一个」是啥?
- 悲观锁和乐观锁是什么概念?都什么时候用?
- CAS 如果不满足条件会怎么样?
- CAS 有什么问题?还有其他弊端吗?
- CAS 跟悲观锁比有什么优势?
- 你刚说 CAS 等待时间会长,又说它并发快很多,这跟它矛盾吗?
- Java 里 volatile 关键字是什么意思?
- 平时会用到消息队列,比如 Kafka、RabbitMQ,它们的作用是什么?
- 后端接口或服务卡顿、响应慢,想排查的话有哪些维度或方面?
- 刚讲到一个慢查询,是吧?
- 负载就是 CPU,是吧?
- 内存,还有其他想到的吗?
- 假如你项目里依赖了 Redis,有什么需要观察的?
- 假如是因为 Redis 导致系统慢,Redis 你都看哪些指标?
- 假如现在要基于大模型做一个 Agent,让你来设计,你认为都需要设计哪些方面?
- 记忆和知识库,如果让你来设计,该怎么设计?都应该考虑哪些方面?
- 语义切分你怎么来做?
- 检索的时候只做了相似度检索吗?
- 写入的时候,图用的是什么数据库?
- 图检索的目的是什么?
- 工具层你怎么设计?有过经验吗?
- 都用过哪些 Agent 相关的框架?
- LangGraph 和 DeepAgents 有什么区别?
- 还有用过其他的框架吗?
- 平时编程过程中都用哪些模型?
- 我这边就问这么多,你有什么疑问吗?
《参考解析》
1. 进程、线程与 IPC。 进程是资源分配的最小单位,线程是 CPU 调度的最小单位;同一进程内的线程共享地址空间、文件描述符、信号处理和全局变量,各自拥有栈、寄存器和线程本地存储(TLS)。所以「进程共享什么」的准确答法是:共享的是进程的内存空间与内核资源,而不是进程之间;跨进程默认不共享用户态内存,必须通过 IPC。IPC 主要有:管道(匿名管道只能亲缘进程、半双工;命名管道 FIFO 可有名访问)、消息队列(内核维护、按消息边界、可设优先级)、共享内存(最快,但要自己做同步,通常配合信号量)、信号量(计数同步,不传数据)、信号(异步通知,信息量极小)、Socket(可跨主机,TCP/UDP/Unix Domain Socket)。共享内存的选择要讲清楚:省掉两次拷贝所以快,但多个进程同时写同一块内存必须加锁,否则数据竞争。
2. TCP 特性与三次握手。 TCP 提供面向连接、可靠、有序、字节流式、全双工的传输:序号 + 确认 + 超时重传保证可靠,滑动窗口做流量控制,拥塞控制(慢启动、拥塞避免、快重传、快恢复)适配网络,校验和检测比特错误。面试官问「安全性」时其实是在问这几个机制是不是加密——TCP 本身不加密、不认证,只保证「到达且有序」,安全要靠 TLS/HTTPS 叠加。三次握手:客户端发 SYN(seq=x)→ 服务端回 SYN+ACK(seq=y, ack=x+1)→ 客户端回 ACK(ack=y+1);两次不够是因为服务端无法确认「客户端还能收到自己的包」,且历史重复 SYN 会让服务端建立无效连接。
3. 线程池参数与拒绝策略。 七个参数:corePoolSize、maximumPoolSize、keepAliveTime、unit、workQueue、threadFactory、handler。执行顺序是「核心线程 → 队列 → 非核心线程 → 拒绝策略」,注意不是核心满了就立刻扩到最大,而是先塞队列,队列满了才扩容,这一点常被追问。四种内置拒绝策略:AbortPolicy 抛 RejectedExecutionException(默认);CallerRunsPolicy 让提交任务的线程自己跑,形成反压;DiscardPolicy 静默丢弃新任务;DiscardOldestPolicy 丢掉队列里最老的任务(队首)再重试提交。所以「放弃第一个」是 DiscardOldestPolicy,丢的是排队最久的那一个、不是当前任务;「放弃最后一个」对应 DiscardPolicy,丢的是刚提交的新任务。生产上更推荐自定义策略:落库/写 MQ 做补偿,或返回明确的失败码,避免静默丢任务。
4. CAS、乐观锁与悲观锁。 悲观锁假设冲突一定发生,先加锁再操作(synchronized、ReentrantLock、select ... for update),适合写多、冲突高的场景;乐观锁假设冲突少,先改再校验版本号/CAS。CAS 比较并交换,失败不会阻塞,而是返回 false 或自旋重试。三个经典问题:① ABA——值从 A 改回 A,CAS 察觉不到,解决用版本号或 AtomicStampedReference;② 自旋开销——高冲突下大量 CPU 空转,需要退避或改锁;③ 只能保证单个变量原子性,多变量要 AtomicReference 或加锁。优势是无锁、无上下文切换、读多写少时吞吐高;「等待时间长」和「并发快」不矛盾——单次失败要重试所以延迟可能更高,但没有阻塞和唤醒开销,吞吐量在高并发低冲突下更好,冲突一高优势就消失。
5. volatile 与消息队列。 volatile 保证可见性和有序性(禁止指令重排),不保证原子性;实现上靠内存屏障:写前 StoreStore、写后 StoreLoad,读后 LoadLoad/LoadStore,强制刷回主存并使其他核的缓存行失效(MESI)。典型用途是状态标志位、双重检查锁定的单例引用。消息队列解决三件事:解耦(生产方不关心谁消费)、削峰(把瞬时流量缓冲成可控速率)、异步(非关键链路不同步等待),附带重试、顺序、广播能力。代价是一致性变弱(最终一致)、系统复杂度上升(重复消费、消息丢失、积压),所以消费端必须幂等,生产端要做本地消息表或事务消息。
6. 接口慢的排查维度。 从外到内分层看:① 入口层——Nginx/网关的 QPS、连接数、5xx、超时,DNS 与 TLS 握手;② 应用层——线程池是否打满、GC 停顿(GC 日志、jstat)、CPU 使用率与 load、锁竞争(jstack 看 BLOCKED)、慢方法(Arthas trace、APM 链路);③ 存储层——慢查询(slow_query_log + EXPLAIN)、连接池等待、主从延迟、锁等待;④ 依赖层——Redis 的慢命令与大 key、MQ 积压、第三方接口超时;⑤ 容量层——带宽、磁盘 IO、文件句柄。排查原则是「先定位是哪个环节的耗时」而不是逐个猜:用分布式追踪拿到 P99 的 span 分布,再看该环节的资源指标。Redis 要看的指标包括 QPS、命中率、内存使用与碎片率、evicted keys、blocked clients、slowlog、主从复制延迟、连接数;常见慢因是大 key(hgetall 千万字段)、keys *、集中过期、以及单线程被长命令阻塞。
7. Agent 的整体设计。 分五层:① 输入与路由——意图识别、参数抽取、缺参澄清;② 规划——单步 ReAct 还是先 Plan 再执行,复杂任务要拆子任务并维护状态机;③ 工具层——工具描述(名称、参数 schema、何时用)、权限与副作用分级、超时与重试、结果裁剪;④ 记忆——短期是会话窗口与摘要,长期是用户偏好与事实,写入要有抽取、去重、过期与冲突消解;⑤ 评测与可观测——离线评测集、在线埋点(成功率、轮次、token、人工采纳率)、全链路 trace。约束上必须有步数预算、token 预算、wall-clock 超时和人工兜底,防止空转和死循环。
8. 记忆与知识库。 记忆按「事实 / 偏好 / 事件」三类存,键是用户 id + 类型,写入前做抽取与去重(相似度阈值 + LLM 裁决),带来源与时间戳,支持用户查看和删除;冲突时以「新且明确」覆盖「旧且推测」,不确定的标为待确认。知识库是共享的领域知识,走文档解析 → 切分 → 向量化 → 索引(向量 + 关键词 + 元数据)→ 检索 → 重排 → 生成。两者不能混在一张表:记忆是私有、可写、量小;知识库是公共、只读、量大。检索要混合:向量召回语义相近,BM25 召回专有名词和数字,元数据过滤缩小范围,最后 rerank 精排;只用相似度会在「精确编号」「同义词」两类查询上翻车。
9. 语义切分与图检索。 切分先按文档结构(标题、段落、表格)切,再对超长段落做语义切分:相邻句子做 embedding,计算相似度,在相似度低谷处断开,控制块大小与重叠;固定长度切分会把一句话劈开,导致召回片段缺主语。图数据库常用 Neo4j(或 NebulaGraph),写入的是实体与关系(人—公司—岗位、文档—概念—引用),目的是多跳推理和关联召回——比如「和这个人同组的同事面过哪些题」,向量检索只能找到相似文本,找不到关系路径。图检索一般作为混合检索的一路:先用实体链接定位节点,再按 1~2 跳扩展候选,最后和向量召回一起重排;跳数要限制,否则候选爆炸。
10. LangGraph 与 DeepAgents。 LangGraph 是「图编排」框架:节点是函数或模型调用,边是显式转移,支持条件分支、循环、检查点(checkpoint)和人工介入,本质是把 Agent 流程写成可恢复的状态机,适合流程确定、需要可控与可观测的场景。DeepAgents 更偏「开箱的深度 Agent」:内置规划(todo list)、子 Agent 派生、文件系统与上下文管理,让模型自己拆任务并长时间执行,适合开放式研究型任务,代价是可控性弱、成本高、调试难。选型判断:业务流程固定、要审计和回滚就用图编排;探索型、步骤无法预先枚举才用自治 Agent。工具层设计的关键是把「工具做什么、什么时候用、参数约束、失败怎么办」写清楚,副作用工具要二次确认,返回值要裁剪成模型能读的摘要,并在服务端做参数校验和权限检查。