面灵AI→

西安智唐科技后端面经:SingleFlight Key 设计、ASR 拼接与部署实践

时间
2026-10
来源
牛客网

《面试题目》

技术栈与项目背景

  1. 请介绍一下你熟练掌握的技术栈,包括编程语言和常用中间件?
  2. 讲一下你最熟悉的 AI 相关项目,整体架构和业务逻辑是什么?
  3. 这个平台的用户群体是谁?核心业务流程是怎样的?
  4. 项目中用的 Spring Boot 和 Spring AI 分别是什么版本?你个人负责哪些模块?
  5. 团队规模多大?项目周期多长?前后端是怎么分工的?

后端架构与中间件

  1. 你在项目中具体做了哪些后端优化工作?
  2. 为什么引入 SingleFlight 机制?请求 Key 是怎么构造的?
  3. 去重机制的作用范围是什么?能不能支持跨用户、跨会话去重?
  4. 为什么同时用 Redis 和 MongoDB?MongoDB 在这个项目里解决什么问题?
  5. WebSocket 通信是你负责的吗?主要对接了什么功能?

AI 接口与语音转写

  1. AI 接口调用成本高,你是怎么避免重复扣费的?
  2. 对接讯飞 ASR 语音转写时,遇到了哪些数据处理上的难点?
  3. 讯飞返回的数据结构比较特殊,你是怎么做排序和拼接的?
  4. 前端发送数据和讯飞消费数据的节奏不一致,你是怎么解决的?

部署与全栈能力

  1. 你平时是怎么做项目部署的?用过哪些工具?
  2. 你掌握哪些编程语言?有没有前端开发经验?

个人特质与团队协作

  1. 你觉得自己的最大优势是什么?
  2. 团队里任务一般怎么分配?你平时扮演什么角色?
  3. 你对自己未来的技术发展方向有什么规划?
  4. 刚才提到的去重方案是你独立设计的吗?

《参考解析》

项目架构怎么讲:用一张分层图讲清:接入层(Web/WebSocket、鉴权、限流)→ 应用层(会话编排、答题流程、评分)→ AI 能力层(模型网关、ASR、RAG、工具)→ 数据层(MySQL/Redis/MongoDB/向量库)。业务逻辑落到「用户创建面试 → 选择岗位与简历 → 实时对话/答题 → 转写与评分 → 生成报告」。版本号这类细节如实说,不记得就说不记得,别编。

SingleFlight 的 Key 构造与范围:key 要能唯一标识「一次可复用的执行」,通常由「用户 ID + 会话 ID + 操作类型 + 输入内容哈希」组成;如果希望跨用户复用,就去掉用户 ID,只保留「模型 + 提示词版本 + 输入哈希」。范围要明确:按用户去重能避免同一用户重复提交,但不同用户问同样的问题仍会各调一次;跨用户去重能省钱,但要注意权限与数据隔离(不能把 A 的简历内容返回给 B),所以只对「输入完全相同且不含私有数据」的请求做跨用户复用。回答时把「省钱」和「隔离」的边界讲清,比一味说能跨用户复用更加分。

Redis 与 MongoDB 的分工:Redis 存热状态、锁、去重 key、限流计数、会话最近消息,特点是读写快、可设 TTL;MongoDB 存会话快照、完整消息、工具调用记录,特点是文档结构灵活、可持久化、按会话查询方便。MongoDB 解决的是「长会话的状态持久化与恢复」:进程重启或断线后能按 sessionId 把上下文捞回来。两者不是替代关系,Redis 是加速层,MongoDB 是事实层。

WebSocket 对接:负责的话要讲清「连接建立 → 鉴权(token 放 query 或首帧)→ 会话绑定 → 消息收发 → 心跳保活 → 断线重连 → 关闭清理」。主要功能是实时面试的语音/文本流、评分进度、状态同步。要注意多实例下的连接路由(网关按 userId 粘性、或用 Redis Pub/Sub 广播)、消息顺序、以及背压。

避免 AI 重复扣费:三层。① 请求去重:SingleFlight + 幂等键,重复请求复用结果;② 结果缓存:相同输入(模型 + 提示词版本 + 内容哈希)命中缓存直接返回,设合理 TTL;③ 计费对账:每次调用记录 requestId、token 用量、渠道,和上游账单对账,发现重复调用能定位。还要防「重试导致重复计费」:写操作带幂等键,重试前先查状态。

讯飞 ASR 的数据处理难点:流式返回是分片、带序号、可能乱序或重复;中间结果会不断修正;连接可能中断;标点与断句由服务端追加。难点在「把分片还原成正确的文本」以及「把文本和说话人/时间对齐」。

排序与拼接:按 sn(序号)排序,用 map 暂存已到达的分片,维护 expectedSn 依次取;缺失时等待补包,超时则标记缺口并继续,不能阻塞整个会话。同一句的中间结果只保留最新版本,最终结果(ls=true)才落库。拼接时注意中英文空格、标点重复、以及跨分片的词被切断。

前后端节奏不一致:前端按 VAD 切段发送,ASR 消费是流式的,可能出现「前端已经发完、ASR 还没返回最终结果」。解决是解耦:音频帧进队列,ASR 结果按会话 ID 聚合,前端用「临时文本 + 最终文本」两种状态渲染;服务端维护「已确认序号」,前端拿到最终结果后替换临时文本;如果用户提前结束,服务端等待 ASR 的最终结果或超时后用已有文本兜底,并标记「可能不完整」。

部署:讲一条完整链路:本地构建(Maven/Gradle 或 npm)→ 打镜像(多阶段构建)→ 推镜像仓库 → 服务器拉取 → docker compose 或 K8s 启动 → Nginx 反向代理与 TLS → 健康检查与日志 → 回滚(保留上一个镜像 tag)。常用工具:Docker、Docker Compose、Nginx、Jenkins/GitHub Actions、云服务器与对象存储。要能说出「配置与密钥走环境变量、数据卷持久化、日志落盘并轮转」。

优势与协作:优势用证据说,比如「能把一个模糊需求拆成可交付的模块,并自己补监控和回滚」;团队协作讲清需求评审、任务拆分、接口约定、联调与验收的流程,自己通常承担「后端主程 + 对接」的角色。职业规划可以分短期(把 AI 应用的工程化做扎实:可观测、可评测、成本可控)和长期(往架构或 AI 平台方向走),不要空泛。

去重方案是否独立设计:如实回答。如果是自己设计的,讲清动机(发现重复调用浪费 token)、方案对比(本地锁 vs Redis vs 幂等表)、以及上线后的效果(调用量下降多少);如果是团队一起做的,说清自己负责哪部分。面试官在意的是你能否对方案负责,而不是独占功劳。