面试场景还原
面试官:“在开发类似豆包、ChatGPT 等大模型(LLM)对话应用时,当用户进行数十甚至上百轮的长对话时,系统面临两个核心矛盾:一是用户不愿等待长达数秒的完整推理,需要极致的首字延迟与平滑的打字机输出;二是
Prompt 会随着对话轮次呈线性暴涨,极易触发 Token 上限并导致 API 计费爆表。 如果让你设计这套系统的后端架构,你会如何解决海量上下文管理与平滑流式传输的问题?”
考点拆解
这道题是 AI Native 架构设计中的经典场景题,表面上考查长连接与数据库,实则考察候选人是否具备以下架构思维:
高并发非阻塞 I/O:能否避免长连接拖垮服务器线程池(C1000K 问题)。 前端流式平滑化:对底层推理吐字抖动现象的工程处理。 上下文控制与控本:Tokenizer 精确计算、上下文分层策略(滑窗、摘要、RAG)。 生产级高可用与算力保护:前缀缓存(Prefix Caching)、断连取消(Cancel Token)与数据隔离。
核心解答方案
一、 传输层与平滑输出:SSE + 响应式架构 + 动态缓冲
1. 网络协议对比与选型
在流式输出场景下,服务端向客户端单向推送数据。
结论: 选择 SSE。对于大模型“单次提问,连续单向输出”的业务模式,SSE 在协议开销、网络穿透性及连接维护成本上均优于 WebSocket。
2. 后端高并发架构:Spring WebFlux (Netty)
传统 Servlet 阻塞模型(如 Tomcat)一个请求绑定一个线程,在 LLM 长连接维持数十秒的场景下,线程池会迅速耗尽。
必须采用基于 Reactor 模型的非阻塞框架(如 Spring WebFlux),通过 EventLoop 线程复用处理海量长连接:
@RestController
@RequestMapping("/api/chat")
public class ChatStreamController {
@Autowired
private LLMInferenceService llmInferenceService;
@PostMapping(value = "/stream", produces = MediaType.TEXT_EVENT_STREAM_VALUE)
public Flux<ServerSentEvent<String>> streamChat(@RequestBody ChatRequest request) {
return llmInferenceService.callLLMStream(request)
.map(token -> ServerSentEvent.<String>builder()
.data(token)
.build())
.doOnCancel(() -> {
// 核心:客户端主动断开连接(如点击取消或关掉页面),立即通知底层推理引擎中断生成,节省算力
llmInferenceService.cancelInference(request.getTaskCode());
});
}
}
3. 前端输出平滑化:动态 FIFO 缓冲队列
大模型吐出 Token 的时间间隔是不均匀的(遇到复杂 Reasoning 时会卡顿,之后连续吐出多个字符)。如果收到一个 Token 就渲染一个,会导致视觉上剧烈闪烁。
工程解决方案: 前端建立 动态 FIFO 缓冲队列,结合 requestAnimationFrame 渲染引擎。
后端推送到前端的 Token 统一先进入 Queue。 渲染引擎以固定的速率(如每 30ms 消费一个字符)从队列提取并打印。 队列堆积过长时自动按比例加速,队列为空时平滑等待,保证平滑的视觉打印体验。
二、 上下文管理:三级记忆架构
针对长对话,绝对不能将历史记录原样拼接投递,需构建 短期 + 中期 + 长期 的三级记忆体系。
1. 短期记忆:基于 Tokenizer 的动态滑动窗口
规则置顶: System Prompt 放置在最高优先级。精确截断: 放弃粗暴的“按字数截断”,采用与模型匹配的 Tokenizer(如 Tiktoken)精准计算 Token 数量。从最新消息反向倒推,确保在不超过设定预算(如 4096 Tokens)的前提下截断保留。
2. 中期记忆:异步对话摘要(Conversation Summary)
当对话超过设定的轮次阈值时,在后台启动异步任务:调用轻量级模型对早期的冗长对话进行语义提取,生成摘要信息:
[背景摘要:用户姓名Fox,技术栈为 Java,目前正在探讨 WebFlux 高并发架构优化。]
将该 Summary 插入到 System
3. 长期记忆:RAG 向量检索
处理跨天、跨会话的历史记忆:
落库: 对话结束时,异步将文本调用 Embedding 模型转换为向量,存储至向量数据库(如 Milvus / Qdrant)。检索: 当用户提出新问题时,将新问题向量化,去向量库中做 Top-K 相似度检索。 注入: 将召回的相关片段作为 Prompt 上下文插入到当前请求中。
三、 生产级工程与控本优化
1. 算力止损:Cancel Token 链路传递
当用户中途关闭页面或点击“停止生成”时:
前端断开 SSE 连接; 后端 WebFlux 捕获 doOnCancel事件;后端向底层推理引擎(vLLM / Triton)发送 Cancel 信号,中断该 Request 的后续 Token 计算,避免 GPU 算力无意义浪费。
2. 降低首字延迟:Prefix Caching(前缀缓存)
主打推理引擎(如 vLLM、SGLang)普遍支持 KV Cache 的前缀复用。
为了提高缓存命中率,
SystemPrompt -> 静态背景 / RAG 召回文本 -> 滑动窗口历史 -> 当前最新提问
将不变的静态内容排在前面,变动内容放在末尾,推理引擎可以直接复用头部的 KV Cache,将首字延迟(TTFT)降低 50%~70%。
3. 向量数据隔离
在多租户系统设计中,RAG 检索时必须将 user_id 和 session_id 作为 Metadata 写入向量节点。执行相似度检索时,强制叠加标量过滤(Filtered Vector Search),杜绝跨用户隐私数据越权访问。
满分答题逻辑总结
在面试中回答此题,建议按以下逻辑作答:
说明架构痛点:先指出首字延迟、C1000K 长连接开销以及 Token 成本这三个核心矛盾。 给出传输方案:选择 WebFlux + SSE 解决高并发非阻塞连接,配合前端缓冲队列平滑打字节奏。 阐述记忆机制:提出 滑窗截断(短期) + 增量摘要(中期) + RAG 向量召回(长期) 的三级架构,精确控制 Token 成本。 展现实践经验:抛出 Cancel Token(算力止损)、Prefix Caching(降低 TTFT)和 Metadata Filter(数据安全隔离)等生产级细节。
在你们公司的 AI 应用落地过程中,遇到过哪些 Token 计费爆表或首字卡顿的“大坑”?欢迎在评论区分享你的踩坑经验!
更多大厂高频面试题解析与 AI 架构实战,欢迎关注公众号 【Fox爱分享】。