蚂蚁 AI应用开发 一面
1. 自我介绍
2. SSE 跟 WebSocket 有什么区别?为什么模型推流场景很多时候选 SSE,不选 WebSocket?
答案:SSE 是服务器到客户端的单向流式推送,基于 HTTP,天然适合把模型生成的 token 一段一段往前端推。WebSocket 是全双工协议,更适合双方都要频繁通信的场景,比如协同编辑、实时游戏、双向控制。模型推流大多数时候是服务端连续输出、客户端被动接收,所以通信模式上 SSE 更贴近需求。
另外 SSE 在工程上更轻,和现有 HTTP 网关、鉴权、中间件、日志系统、Nginx 代理兼容性更好,浏览器原生支持 EventSource,实现成本低。很多公司不选 WebSocket,不是因为 WebSocket 不行,而是因为它引入了额外的连接管理、心跳、代理兼容和负载均衡复杂度。只有在需要客户端中途强交互、打断生成、实时控制工具调用时,WebSocket 的优势才更明显。
from fastapi import FastAPI
from fastapi.responses import StreamingResponse
import time
app = FastAPI()
def event_stream():
for i in range(5):
yield f"data: token_{i}\n\n"
time.sleep(0.5)
@app.get("/sse")
def sse():
return StreamingResponse(event_stream(), media_type="text/event-stream")
3. RocketMQ 怎么保证消息不丢?又怎么保证消费尽量幂等?
答案:消息不丢本质上要分生产端、Broker 端和消费端来看。生产端要用同步发送或可靠重试,发送结果必须确认;Broker 端要开启刷盘策略和主从复制,避免单点宕机导致消息丢失;消费端要在业务真正处理成功后再 ack,不能刚收到消息就确认。真正线上不会说“绝对不丢”,而是通过多层机制把丢失概率压到足够低。
幂等性则是另一个问题。MQ 的语义通常更接近“至少一次”,所以重复消费是现实存在的。解决方式一般是给每条业务消息一个唯一业务键,比如订单号、请求号、操作流水号,消费前先查幂等表或 Redis 去重键,已经处理过就直接跳过。对支付、退款、审核这类业务,消费逻辑必须天然幂等,不能依赖“只会来一次”。
processed = set()
def consume(msg):
biz_id = msg["biz_id"]
if biz_id in processed:
return "duplicate_skip"
# 真实业务处理
processed.add(biz_id)
return "ok"
4. 对于 RAG 来说,和直接把知识写进 Prompt 相比,RAG 的核心好处是什么?
答案:直接把知识写进 Prompt 最大的问题是静态、昂贵且不可维护。知识一旦变化,就要手改 Prompt;知识一多,Prompt 会迅速膨胀,成本、延迟和噪声都会上升。RAG 的核心价值不是“让模型看更多字”,而是把知识管理和模型推理解耦,让模型只在需要时取回当前最相关的证据。
这样做的好处有几个。第一,知识可以动态更新,不需要重训模型。第二,检索出来的是与当前问题强相关的一小部分上下文,噪声更低。第三,答案可以带证据引用,便于审计和纠错。第四,系统能做权限控制、版本控制、时效过滤,而不是让模型在一大段静态 Prompt 里“自己悟”。所以 RAG 本质上是知识供给系统,不只是一个 Prompt 技巧。
5. 为什么模型部署很多人会选 vLLM,而不是直接用 Ollama?
答案:vLLM 更偏生产级高吞吐推理引擎,核心优势在于对 KV Cache 和批处理调度做了大量优化,尤其是 PagedAttention、continuous batching、prefix cache 这些能力,适合高并发、多请求、长上下文的线上服务。Ollama 更像开箱即用的本地模型运行工具,适合开发测试、单机体验和简单服务封装,上手很方便,但在复杂生产环境下的调度能力、扩展能力和可控性通常不如 vLLM。
如果面试里问为什么选 vLLM,本质上要回答吞吐、延迟、资源利用率和服务稳定性。模型服务不是“能跑起来”就够,真正难的是在高并发下维持较好的 token/s、P95 延迟和显存利用。vLLM 在这方面明显更适合线上部署,而 Ollama 更适合个人开发或轻量场景。
6. 介绍一下 LoRA 的数学原理,为什么它能降低显存占用?
答案:LoRA 的核心思想是冻结原始大模型参数,只在某些线性层旁边学习一个低秩增量矩阵。原始权重如果是 W,LoRA 不直接训练完整的 ΔW,而是把它分解成两个低秩矩阵的乘积:
ΔW = B A
其中 A 和 B 的秩很小,远小于原矩阵维度,所以可训练参数量会大幅减少。前向时实际上是:
y = W x + B A x
这样显存占用降低,主要不是因为前向完全不用原模型,而是因为训练时不需要保存和更新全量参数的梯度、优化器状态和权重副本。全参微调最贵的部分往往就是这些状态,而 LoRA 只训练极少数参数,所以显存压力会小很多。
# 单层 LoRA 参数量近似
def lora_params(in_dim, out_dim, r):
return r * in_dim + out_dim * r
print(lora_params(4096, 4096, 8))
7. HyDE 是什么?为什么它能优化检索?
答案:HyDE 就是 Hypothetical Document Embeddings,思路是先让大模型根据用户问题生成一段“假想答案”或“假想文档”,然后不用原始 query 去做 embedding,而是用这段更完整、更接近知识文档分布的文本去做向量检索。它之所以有效,是因为很多用户 query 很短、很口语、信息不完整,而知识库里的文档通常是更正式、更完整的表达,两者分布差异很大。
HyDE 相当于在检索前做了一次“语义投影”,把用户问题转换到更像目标文档的语义空间里。这样对于语义鸿沟比较大的场景会更有效,尤其是问答式 query 对技术文档、制度文档、学术文档检索时。但 HyDE 也不是必胜的,如果生成的假想文档方向错了,也会把检索带偏,所以通常要和原 query 检索做融合,而不是完全替代。
8. 如果用户中文描述一个需求,系统怎么去代码库里找到对应代码?代码检索为什么不能只靠向量?
答案:中文需求映射到代码,通常至少要经过 query 理解、实体抽取、混合检索、重排和证据扩展几个阶段。比如用户说“查一下订单超时自动取消是在哪处理的”,系统要先抽出“订单、超时、取消”这些业务意图,再结合仓库里的函数名、类名、
剩余60%内容,订阅专栏后可继续查看/也可单篇购买
本专栏聚焦 AI-Agent 面试高频考点,内容来自真实面试与项目实践。系统覆盖大模型基础、Prompt工程、RAG、Agent架构、工具调用、多Agent协作、记忆机制、评测、安全与部署优化等核心模块。以“原理+场景+实战”为主线,提供高频题解析、标准答题思路与工程落地方法,帮助你高效查漏补缺.
