1.2 一个 AI 对话引擎后端,到底由哪些组件构成 在 1.1 我们校准了"百万 QPS"的口径,并画出了流量画像。这一节我们把镜头拉远,看全局:支撑百万级对话的后端,在逻辑上由哪几层组成,每层负责什么,层与层之间靠什么契约连接。 读者读完这节,应该能画出一张属于自己系统的"分层架构图",并说清楚:当流量从用户端进来,它依次经过了哪些处理阶段,每个阶段可能在哪里"塌方"。 1.2.1 总览:五层逻辑架构 我把一个生产级的 AI 对话引擎后端,抽象成五层。注意这是逻辑分层,不是物理部署——同一层在真实部署时可能横跨多个微服务、多个集群。 逐层说明: 接入层(第 2 章重点):负责把海量用户的 TCP/WebSocket 长连接接住,做协议升级、身份鉴权、最前端的限流与连接数管控。
在 1.1 我们校准了"百万 QPS"的口径,并画出了流量画像。这一节我们把镜头拉远,看全局:支撑百万级对话的后端,在逻辑上由哪几层组成,每层负责什么,层与层之间靠什么契约连接。
读者读完这节,应该能画出一张属于自己系统的"分层架构图",并说清楚:当流量从用户端进来,它依次经过了哪些处理阶段,每个阶段可能在哪里"塌方"。
我把一个生产级的 AI 对话引擎后端,抽象成五层。注意这是逻辑分层,不是物理部署——同一层在真实部署时可能横跨多个微服务、多个集群。
U[用户] --> L0 L0 --> L1 L1 --> L2 L2 --> L3 L3 --> L1 L1 --> L0 L0 --> U L4 -.横切.-> L0 L4 -.横切.-> L1 L4 -.横切.-> L2 L4 -.横切.-> L3
</div> 逐层说明: - **接入层(第 2 章重点)**:负责把海量用户的 TCP/WebSocket 长连接接住,做协议升级、身份鉴权、最前端的限流与连接数管控。它的核心矛盾是"海量长连接"与"单机能持有的连接数上限"之间的斗争。 - **网关与编排层(第 4 章重点)**:会话状态、模型路由决策(该用哪个模型、是否要检索、是否要调用工具)、把多路子调用聚合成一个流式响应返回给用户。它的核心矛盾是"灵活编排"与"低延迟开销"之间的平衡。 - **上下文与缓存层(第 3 章重点)**:管理对话历史、做上下文压缩、提供语义缓存(相似问题命中缓存直接返回)。这是整个架构的"降本杠杆",也是百万量级下最容易被低估的一层。 - **模型服务层**:真正跑模型推理的地方,包括推理引擎(如 vLLM、TensorRT-LLM 等)、多模型接入、量化与批处理。核心矛盾是"吞吐"与"首字延迟"的此消彼长。 - **支撑与观测层**:横切所有层,提供配置中心、全链路监控、成本计量、内容安全等能力。没有它,百万系统就是一座黑箱,出了事只能盲猜。 ## 1.2.2 为什么是"分层"而不是"一个大服务" 有人会问:能不能就把所有逻辑塞进一个服务,靠堆机器解决?在百万量级下,答案是坚决不可以,原因有三: **一、各层的扩展维度不同。** 接入层是"连接数"维度扩展,模型层是"GPU 算力"维度扩展,缓存层是"内存/带宽"维度扩展。绑在一起,你就只能用"最大公约数的资源"去扩,浪费严重。 **二、各层的故障域不同。** 模型推理偶发超时、OOM,是高频事件;接入层应保持"即使模型挂了也不掉线"。分层后可以做熔断、降级、隔离,避免一个模型的热更新把整个接入层拖死。 **三、各层的演进速度不同。** 模型层可能每月换模型、换引擎;接入层相对稳定。分层让"换模型"这种高频变更,不会动到"接用户"这种生命线代码。 <div align="center"> ```mermaid flowchart LR Q[请求] --> CHK{分层检查} CHK -->|连接爆炸| A[接入层独立扩] CHK -->|缓存未命中| B[缓存层独立扩] CHK -->|模型慢| C[模型层独立扩] CHK -->|可观测| D[支撑层横切] A --> R[响应] B --> R C --> R
分层不是目的,层与层之间如何协作才是关键。这里有几个核心契约,请记住它们的形态:
我的主张:契约先于实现。在动手写任何一层之前,先把这几组接口的请求/响应结构定下来,用 mock 把各层打通,再做真实模型接入。这样当模型层还没 ready 时,你可以先用假模型把接入、缓存、编排全部跑通——这是缩短整体工期的秘诀。
分层架构在千级 QPS 时人人都画得出来。真正区分"能不能扛住百万"的,是下面这些在量级跃迁后质变的问题:
| 在千级时无所谓 | 在百万级变成致命问题 | 出事表现 |
|---|---|---|
| 每次请求查一次数据库拿用户配置 | 数据库被打成瓶颈 | 全站配置读取超时,雪崩 |
| 上下文全量塞进 prompt | 显存和 KV Cache 爆掉 | 长会话请求大规模失败 |
| 限流只做全局 QPS | 单个大客户/热点把资源吃光 | 其他用户集体无响应 |
| 缓存 key 用原始问题字符串 | 命中率极低,缓存形同虚设 | 推理集群始终满负荷 |
| 模型调用同步等待 | 线程池被长请求占满 | 新请求排队直至超时 |
这张表是后面每一章的"问题清单"。第 2 章解决接入与限流,第 3 章解决上下文与缓存,第 4 章解决模型路由与编排——你会发现,每一章都在回应这张表里的一行。
我建议你在确定分层后,立刻做一件反直觉但极有价值的事:画一张故障推演图,不是画"正常流程怎么走",而是画"每一层挂了会怎样"。
这张图会逼你想清楚三件事:哪些层必须有降级方案(模型层)、哪些层挂了等于全挂需要多活(接入层)、哪些层挂了能靠兜底扛住(缓存层)。能画清故障推演图的团队,和只画了"正常流程图"的团队,在第一次线上事故时的存活率天差地别。
前面画的都是静态结构。为了让分层真正"活"起来,我们追踪一次最典型的多轮对话请求,看数据如何穿过五层。这会帮你理解每一层的"职责边界"到底划在哪里。
端到端流程(以"用户追问一个带上下文的问题"为例):
注意几个关键设计点:
这套追踪也解释了为什么 1.1 说"瓶颈常不在 GPU":如果接入层限流没做好,大量非法/超额请求会穿透到网关层,把编排状态拖垮,模型层反而可能很闲——但用户侧已经全部超时了。
我必须单独点名一个高频反模式,因为它会直接葬送分层带来的所有好处:为了"省一次 RPC",把路由决策、上下文拼接、工具调用这些编排逻辑,直接塞进模型服务的封装里。
短期看似乎少了一跳调用,但代价巨大:
我的硬主张:模型服务层必须是"纯推理"的无状态组件,只接收 prompt、返回 token 流。 任何"业务逻辑"都不该出现在这一层。守住这条线,你的架构在模型快速迭代的年代里才能岿然不动。
分层架构定下来后,一个非常落地的动作是:为每一层单独列一份容量规划清单,因为它们扩展的"维度"和"单位"完全不同。下面是一份可直接照填的模板:
somaxconn、tcp_max_syn_backlog)。横向扩展靠加节点 + 负载均衡,几乎线性。关键提醒:这五层的扩展曲线不一样,尤其是模型层是非线性的(受显存墙、批处理收益递减影响)。做容量规划时,千万别用"接入层能线性扩,所以模型层也能"的错误类比。每一层都要单独压测、单独留余量。
1.2 给你留下这些可复述的结论:
带着这份要点,下一节我们下到最前线,直面第一道关口:接入层与百万长连接。
读完本节,你可以用下面几个问题快速自检当前系统的成熟度。答不出或答"没有"的,就是下一阶段要补的:
这五个问题对应五层。能全部答"是",说明你的分层是健康的;任一"否",都意味着某一层是"隐性单点",会在百万量级下成为第一个崩的点。
小结:逻辑上,AI 对话后端 = 接入层 + 网关编排层 + 上下文缓存层 + 模型服务层 + 支撑观测层。分层的核心收益是独立扩展、隔离故障、解耦演进。务必守住"模型层保持纯推理无状态"这条线,否则分层形同虚设;且五层扩展曲线不同,必须分层独立压测。下一节进入最前线的实战难题:如何接住百万长连接并限流。