面灵AI→

字节火山引擎测开一面:云原生 + AI 提效

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

《面试题目》

  1. 自我介绍。
  2. 你在百度智能云主要负责什么项目?详细介绍一下。
  3. 是 IaaS 方申请实例吗?CPU 和 GPU 怎么区分?
  4. 我买了一台机器,另外一个租户也买了一台机器,网络是否联通?这个具体是怎么设计的?
  5. PaaS、集群、VPC 分别是什么?VPC 是什么意思?
  6. 云审计的接口自动化建设,详细介绍一下。
  7. 怎么嵌到 ECS 中?怎么和 ECS 做交互?
  8. 审计过程中功能出了异常,怎么定位这个问题?
  9. 延迟、轮询方式怎么设计?
  10. Agent、LangGraph,整个链路中怎么管理节点的状态?
  11. 用例生成的过程中,大模型有些错误,Agent 怎么去做?识别、修复。
  12. 覆盖率怎么得到、怎么拿到?统计口径是什么?怎么在生成 case 的过程中获得这个指标并相应修改?
  13. RAG 系统详细介绍一下,怎么实现的?分块策略和检索方法是什么?对 size 和大小有没有要求?
  14. Celery,生成 100 个测试用例的任务,Django 和 Celery 是怎么工作的?怎么保证前端可以实时稳定地工作?
  15. 异步和同步的区别是什么?
  16. Redis 怎么管理 SSE、协同交互?SSE 怎么保证长连接持续生成?
  17. Linux,502 的错误怎么排查?
  18. 进程和端口是什么关系?

《参考解析》

多租户网络:两台机器到底通不通

面试官问的场景(「我买了一台,另一个租户也买了一台,网络联通吗」)是在验你对云网络隔离模型的理解。标准答法:

  • 默认不通。VPC(Virtual Private Cloud)是每个账号/租户独享的虚拟网络,有自己的网段、路由表、网关和安全策略;跨 VPC 默认完全隔离,这是多租户安全的基本前提。
  • 想让它们通,必须显式打通:VPC 对等连接(Peering)、云企业网(CEN)、VPN 网关或专线。打通后还要安全组 / 网络 ACL 放行端口。
  • 同一 VPC 内,同子网的机器走二层互通,跨子网走 VPC 路由表;能否通还受安全组、ACL、路由条目影响。经典网络(老一代)是所有租户共享地址空间,才存在「同网段可能互通」的问题,现在的 VPC 模型正是为了解决它。
  • 底层实现通常是 Overlay:每个租户的流量被 VXLAN 之类的隧道封装,用 VNI 标识租户,物理网络只看到隧道包,租户之间互相看不到对方的路由。

顺带把 IaaS/PaaS 和 CPU/GPU 区分答掉:IaaS 提供计算/存储/网络这些基础设施(ECS 实例、云盘、VPC),PaaS 在其上提供运行环境和中间件(数据库、容器平台、函数计算)。CPU 实例和 GPU 实例的差别在于资源类型和售卖粒度:GPU 机型以卡数(如 1/2/4/8 卡)和显存为规格,通常配 NUMA 亲和、RDMA/高速网络,计费更贵,申请时需要在 IaaS 侧指定机型族(如 GPU 计算型),并且要区分「整卡」和「vGPU 切分」;面试官问这句其实是想确认你在云项目里接触过真实的资源申请链路,而不是只写了测试用例。

云审计接口自动化怎么和 ECS 交互

可讲清楚的链路是:云审计(CloudTrail 类产品)负责采集控制面操作事件(谁在什么时候对哪个资源做了什么),数据来源有两条路——无侵入的 API/事件总线(各服务把操作日志投递到消息队列,审计侧消费入库)和实例内 Agent(装在 ECS 里采集系统调用、进程、文件变更等数据面行为)。测试自动化要覆盖的正是这两条路的正确性:事件是否漏采、字段是否完整、时序是否一致、跨账号/跨 region 是否正确隔离。

「功能异常怎么定位」的回答顺序很关键:先确定断在哪一段——事件源有没有产生(对比云服务侧的操作日志)→ 消息有没有到(查 MQ 的堆积与消费位点)→ 入库是否正确(查审计表/检索接口)→ 查询链路是否正常(API 返回、索引延迟)。每段都有对应的可观测手段,别一上来就说「看日志」。审计类系统的典型坑是最终一致性:操作完成后事件要过几秒到几十秒才可查,所以测试断言必须带重试 + 超时,不能查一次没有就判失败。

「延迟、轮询方式怎么设计」对应两个设计点:轮询间隔采用指数退避(如 1s、2s、4s、8s,上限 30s),既避免对服务端造成压力,又能让大多数事件在数秒内被确认;同时给一个总超时(比如 60s)判定失败,并把「超时后是否异步复查」写进用例,避免把系统的正常延迟报成 bug。

LangGraph 里的节点状态管理

LangGraph 的核心就是把 Agent 的执行过程建模成状态机,状态管理有三件事必须讲清:

  • State 定义与合并:图用一个共享的 state(通常是 TypedDict)在节点之间流转;每个字段可以配 reducer 决定并发/多次写入怎么合并——比如消息列表用 add_messages(追加而不是覆盖),计数用 operator.add。没配 reducer 的字段是「后写覆盖前写」,这正是多分支并发时状态丢失的常见原因。
  • 图的推进:节点是函数,接收当前 state 返回增量更新;边分普通边和条件边(add_conditional_edges 根据 state 决定下一步走哪个节点),加环就形成 ReAct 式循环,靠「达到最大步数 / 模型不再调用工具 / 命中终止条件」退出。校验与路由放在条件边里,比在每个节点里写 if 更清晰。
  • 持久化与恢复:checkpointer(Postgres/SQLite/内存实现)在每个 super-step 后写一份 state 快照,带 thread_id 就能断点续跑、支持 human-in-the-loop(暂停等人审批再继续)和时间旅行回放。测试/生产上要关注快照的写入频率与体积,消息列表不加裁剪会导致上下文和存储一起膨胀。

大模型生成用例的纠错闭环与覆盖率口径

「大模型出的用例有错,Agent 怎么识别和修复」——把它当成一个带反馈的流水线答,而不是「让模型自己检查一遍」:

  1. 结构校验(零成本、先做):强制结构化输出(JSON Schema / function calling 约束),校验字段完整性、枚举合法值、步骤可执行性;不通过就带着具体报错重试(错误信息里指出哪一步违反了什么)。
  2. 规则与静态校验:用例前置条件是否可满足、参数是否越界、是否与已有用例重复(相似度去重)、步骤与预期结果是否一一对应。
  3. 可执行反馈:能真跑的用例就跑——接口用例直接执行,断言失败时把请求/响应/堆栈回灌给模型让它改;这一步收益最大,因为它把「模型觉得对」换成了「环境证明对不对」。
  4. 交叉验证:同一需求生成两版让模型互评,或用一个独立的裁判模型按 rubric 打分,分歧大的挑出来给人看。
  5. 人工兜底与回流:低置信度用例进人工评审队列,修正结果作为 few-shot 样本回流,下一轮生成质量提升。

「覆盖率怎么算」要给出明确口径,因为不同口径差别巨大:接口自动化一般看接口覆盖率(已覆盖接口数 / 接口总数,按调用量加权更能反映风险)和参数/分支覆盖率(等价类与边界值是否覆盖);代码级则分行覆盖、分支覆盖、条件覆盖、路径覆盖。采集方式按被测形态选:后端用 Jacoco 之类的探针挂到被测服务上,跑完用例导出报告再和用例映射;接口维度则在网关或 SDK 里埋点统计。关键是把覆盖率和用例生成串成闭环:解析覆盖率报告 → 找出未覆盖的分支/参数组合 → 把「目标覆盖点」作为提示喂给模型定向生成用例 → 再跑一遍看覆盖率增量,用增量而不是总量衡量这轮生成有没有价值。

RAG 的分块策略与检索方法

  • 分块:固定长度分块(实现简单,但会在句子中间切断)→ 递归切分(按段落、句子、标点的层级切,RecursiveCharacterTextSplitter 的做法)→ 语义/结构感知(按标题层级切 Markdown、按表格/代码块边界切)。工程上最实用的是结构优先 + 递归兜底。两个可调参数:chunk_size 常见 300~800 token,chunk_overlap 取 size 的 10%~20% 用来缓解切断问题。没有万能值,要按文档类型和评测集调:问答型 FAQ 可以小块(一段一个 chunk),技术文档和合同要保留上下文,块要大一些甚至整节保留。另外给每个 chunk 带上元数据(来源、标题路径、时间、权限标签),检索时既能过滤也能溯源。
  • 检索:先做 query 改写(多轮对话消解指代、拆多意图)→ 混合检索(BM25 关键词 + 向量语义,两者互补,专有名词靠 BM25,同义表述靠向量)→ 融合与重排(RRF 融合,再用 cross-encoder/rerank 模型对 top-50 精排到 top-5)→ 阈值过滤(相似度低于阈值时宁可不答,让模型说「知识库没有」,比编答案安全)。进阶做法是 parent-child / small-to-big:用小块检索保证召回精度,把命中的父块整段喂给模型保证上下文完整;以及 HyDE(先让模型生成假想答案再检索)。
  • 评测:检索看 hit rate、MRR、召回@k,生成看忠实度(答案是否只用了检索内容)与答案相关性,RAGAS 这类框架可以直接跑。没有评测集的 RAG 调参就是瞎调,这一点面试时主动说出来很加分。

Celery 异步 + Redis + SSE 怎么撑住长连接

「生成 100 个用例」这种任务是典型的长耗时批处理,Django 请求里同步做必然超时,所以要拆三层:

  • Django 侧:接口只做参数校验 + 落任务记录(状态 PENDING)+ .delay() 投递到 broker(RabbitMQ 或 Redis),立刻返回 task_id,前端拿到 id 去订阅。
  • Celery 侧:worker 消费任务执行,把进度写回(Redis 或数据库),并设置合理配置——acks_late、prefetch_multiplier=1(长任务避免一个 worker 预取一堆)、soft_time_limit/time_limit、任务幂等(同一个 task_id 重跑不产生重复数据)、失败重试与死信处理。
  • 实时推送:worker 每完成一步就往 Redis Pub/Sub 或 Stream 发一条进度消息;Web 层用 SSE(text/event-stream)把消息推给浏览器,Django 侧若用同步 worker(gunicorn sync worker)会被长连接占满,常用的解法是 ASGI(Daphne/Uvicorn)+ StreamingHttpResponse,或者把 SSE 独立成服务。

SSE 保活的具体做法:服务端周期性发心跳(注释行 : ping\n\n,15~30s 一次)防止中间的 Nginx/网关按空闲超时断开;Nginx 要关掉缓冲并放宽超时(proxy_buffering off; proxy_read_timeout 3600s;);断线重连由浏览器 EventSource 自动完成,服务端可以用 Last-Event-ID 做断点续推(把进度事件编号存 Redis 里)。另外注意浏览器同域并发连接数限制(HTTP/1.1 下 6 条),多标签页同时订阅会互相挤;用 HTTP/2 或把进度合并到一条连接能缓解。

同步和异步的区别要一句话说准:同步调用会阻塞当前线程直到拿到结果(实现简单、易调试,但并发能力受线程数限制);异步不阻塞、靠回调/事件/协程拿结果(吞吐高、但错误处理和调试更复杂)。在 Django 里还要提一层:ORM 是同步的,异步视图里调用要 sync_to_async 包装,否则会阻塞事件循环。

502 排查与进程、端口的关系

502 Bad Gateway 的含义是网关(Nginx/SLB)连上了自己配置的 upstream,但没拿到合法响应,所以排查方向是把这条链路逐段验证:

  1. 后端进程活着吗:ps -ef | grep gunicorn、systemctl status;进程挂了最常见的原因是 OOM(dmesg | grep -i oom)、启动即崩溃(看应用日志)、或部署后没起来。
  2. 端口在监听吗:ss -lntp | grep 8000;没监听说明进程没绑上端口,或绑到了 127.0.0.1 而 Nginx 连的是别的地址。
  3. 网络与配置对得上吗:Nginx proxy_pass 的地址端口和实际监听是否一致、防火墙/SELinux/安全组是否放行、upstream 是否写了已下线的节点。
  4. 是超时还是真连不上:看 error.log 里是 connect() failed (111: Connection refused)(没人监听)还是 upstream timed out(后端处理太慢,要调 proxy_read_timeout 或查后端慢请求)。
  5. 资源是否耗尽:工作进程/连接池打满(worker_connections、数据库连接池)、文件描述符 ulimit 到顶、磁盘写满——都会表现为时好时坏的 502。
  6. 偶发场景:后端重启/滚动发布期间 upstream 短暂不可用,要靠健康检查和优雅退出兜住。

进程和端口的关系:端口是传输层的逻辑编号,进程通过 socket() + bind() + listen() 在某个 (协议, IP, 端口) 上监听,内核维护监听队列,连接到来时 accept() 返回一个新的 fd 给进程。所以一个进程可以监听多个端口,一个端口在同一时刻一般只能被一个进程监听(除非 SO_REUSEPORT,多进程负载均衡常用它,或 SO_REUSEADDR 用于快速重启);ss -lntp 显示的 users:(("nginx",pid=123,fd=6)) 就是把端口反查到进程的方式。追问也常从这里出发:为什么 TIME_WAIT 会导致端口耗尽、too many open files 怎么处理、四元组(源 IP、源端口、目的 IP、目的端口)如何唯一标识一条连接。