面灵AI→

水滴测试开发一面:Agent 项目深挖与并发八股

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

《面试题目》

  1. 请做一下自我介绍。
  2. 介绍一下你的这个 Agent 项目。
  3. 你在搭建这个 Agent 项目的时候,是怎么想到要用这个架构去实现这个功能的?
  4. 聊一下实习的工作?
  5. 选一个你比较熟悉的需求,聊一下这个需求,你是怎么设计对它进行测试的?
  6. 在这个过程中你用了哪些工具?测试这些东西的时候写过脚本吗?
  7. 测试报告是怎么写的,例如复测率、测试方面的统计指标?
  8. 你认为最难定位的 bug 是哪一个,你是怎么定位到的?
  9. 使用 Claude Code 做了哪些提效?
  10. 你在测试的过程中有没有遇到过需要并发解决的一些问题?
  11. 怎么做的这个自动生成测试用例,以及怎么计算得到 70% 的效果?
  12. 在日常生活中平时写代码,AI 用到了什么程度?
  13. 线程和进程、协程的区别?
  14. 为什么线程切换比进程快呢?
  15. 什么场景适合用多进程,什么场景适合用多线程?
  16. 什么情况会遇到线程安全的问题?
  17. 自己初始化线程池的话,有哪几种方法?
  18. 用构造函数初始化线程池,它的构造参数里面有哪些配置?
  19. 任务队列有哪几种?
  20. 你这个 Agent 项目如果让你重新设计,你会如何去控制输入、模型的选择、包括上下文以及工具调用呢?
  21. 之前 GPT 做心理咨询,它出现过不符合伦理的回答,你是怎么控制你这个项目的输出结果的?

《参考解析》

进程、线程、协程的区别

从资源归属和调度主体两个维度看最清楚。进程是资源分配的最小单位,有独立的虚拟地址空间、文件描述符表和页表,进程间互不共享内存,通信要靠管道、共享内存、socket 这类 IPC;线程是 CPU 调度的最小单位,同一进程内的线程共享地址空间和堆,各自只有独立的栈、寄存器和线程局部存储,所以线程间通信几乎零成本,代价是共享数据要自己加同步。

协程是用户态的轻量执行体,由程序自己(或运行时)调度,内核完全不感知,一个线程里可以挂很多协程,切换只在用户态保存少量寄存器,没有陷入内核、也没有 TLB/页表切换的开销,所以能轻松支撑几万到几十万个并发单元。它的局限也很明确:一个协程做阻塞式系统调用会卡住整个线程,因此协程库通常要求配套的非阻塞 IO 和事件循环(Go 的 GMP 会在阻塞时把 P 交给别的 M 来缓解这一点)。面试里常被追问的一句话是:协程解决的是并发规模和切换开销,不是并行能力,真正的并行仍然取决于 CPU 核数。

为什么线程切换比进程快

切换本身的开销分两块:直接开销和间接开销。直接开销是保存/恢复上下文——进程切换要换页表基址寄存器(CR3)、刷新 TLB 或打上 PCID 标记、切换内核栈和硬件上下文;线程切换因为地址空间不变,CR3 不用动,TLB 和 cache 里的映射与数据大部分还能命中。间接开销更致命:切换后访问的内存、指令缓存几乎全冷,进程切换后要重新预热页表和 cache,线程切换的惩罚小得多。

所以回答时最好补一句:进程切换慢主要慢在「地址空间和缓存失效」,而不是慢在保存寄存器那几十条指令。再往下追问通常会到协程——协程切换省掉了内核态与用户态的往返,开销又降一个数量级。

线程池构造参数与任务队列怎么选

ThreadPoolExecutor 的核心参数:corePoolSize、maximumPoolSize、keepAliveTime + unit、workQueue、threadFactory、handler。关键在于把「任务提交后的决策顺序」讲清楚:先看当前线程数是否小于核心数,是就直接新建核心线程;否则尝试入队;队列满了才把线程扩到最大线程数;仍然满就触发拒绝策略。这也解释了为什么用无界队列(LinkedBlockingQueue 默认容量 Integer.MAX_VALUE)时 maximumPoolSize 形同虚设、且任务持续堆积会 OOM。

队列的取舍:ArrayBlockingQueue 有界数组、可控但吞吐受锁限制;LinkedBlockingQueue 链表、吞吐高但默认无界,生产上一定要指定容量;SynchronousQueue 不存储元素、来了必须马上有线程接手,配合大 maximumPoolSize 就是 CachedThreadPool 的行为;PriorityBlockingQueue 按优先级出队;DelayQueue 做定时/延迟任务。线程数经验值:CPU 密集型取核数 +1 左右,IO 密集型可以按「核数 × (1 + 平均等待时间 / 平均计算时间)」放大,但最终必须靠压测定;拒绝策略里 CallerRunsPolicy 能形成天然的背压,比直接丢弃更容易发现容量问题。

自动生成测试用例,70% 这个效果怎么算

先要定义清楚「70%」的分子分母,否则这个数字没有意义。可控的口径是:以人工评审通过的历史用例集为基线(golden set),让模型对同一批需求/接口文档生成用例,统计基线用例被覆盖到的比例(召回,衡量有没有漏测)和生成用例中被评审判定为有效的比例(准确率,衡量噪声多少),再补一个上游指标——需求点覆盖率(每个验收点是否至少有一条对应用例)。做自动化生成时,经验是接口层比 UI 层好做得多:把接口契约(OpenAPI)、字段约束、枚举值、历史缺陷单一起喂进去,让模型按等价类 + 边界值 + 异常组合的模板展开,输出的用例再走一层去重(按接口 + 参数组合指纹)和可执行性校验(能不能跑通、断言是否有意义)。

要注意的坑:拿「用例条数」当效果指标会被模型灌水,所以必须统计相对基线的增量覆盖,而不是绝对数量;另外生成集要与基线分开维护,否则自己的用例会污染评测集,指标会虚高。

怎么控制大模型项目的输出合规

思路是分层设防,而不是指望一句 system prompt。第一层是输入侧:对用户输入做意图与风险分类,命中自伤、医疗诊断、法律意见这类高风险域时走预设的安全回复或转人工,不进正常生成链路。第二层是提示词与角色约束:明确角色边界和能力边界,规定「不做什么」的同时要给出「遇到越界请求时该做什么」,只写禁止项模型容易在诱导下绕开。第三层是输出侧审核:用小模型或规则做一遍分类(涉政、涉黄、涉暴、医疗用药、心理危机干预),命中即拦截或改写,并对流式输出做分段检测,避免整段生成完才发现问题。第四层是兜底与可追溯:保留会话与拦截日志、给用户一键转人工/求助热线的出口,高危场景(心理咨询尤其)在首轮就声明能力边界并展示危机干预资源。

还有一点面试官爱追问:不要用「加个关键词黑名单」当答案——黑名单只能防低级绕过,真正的做法是风险分类器 + 场景化的可控回复模板,再配合灰度上线和人工抽检迭代分类器。