企业私有 AI 算力服务器架构设计:异构四节点 + QUIC 微服务

企业私有 AI 算力服务器架构设计:异构四节点 + QUIC 微服务

一、为什么需要这台一体机

在企业级私有化部署场景中,AI 应用普遍面临三个痛点:

  1. 数据安全:业务数据出不了内网,公有云 API 不可接受;
  2. 算力利用率:整租 GPU 服务器跑一个对话应用,显存和算力大量闲置;
  3. 离线环境:工厂、涉密单位等场景没有稳定外网,依赖拉取困难。

我们的答案是造一台企业私有 AI 算力服务器 :一个机箱、四个节点、七个微服务,对外只暴露一个 API 网关,对内跑通"对话 → RAG → Agent → 工具调用"全链路。算力核心是一块 NVIDIA RTX 5090(32GB GDDR7) ,跑在 x86-64 平台上;而网关、存储、消息这三个 I/O 密集节点,则采用 aarch64(ARM64) 平台,构成一套异构混合部署架构。

本文不谈硬件选型的来龙去脉,重点聊四个架构决策:异构节点怎么分、协议栈怎么分层、单卡 32GB 显存怎么调度、Agent 工具调用怎么做才稳

软件采用微服务架构,每个服务负责特定功能模块,通过 API 通信。当前方案将多个算力微服务部署在单台服务器上;后续视资源情况,可拆分部署到多台算力服务器上,提升算力管理与并发能力。本方案作为初始版本,会随实际开发情况和需求变化持续调整,硬件配置(GPU 型号、数量、内存)也可能随之升级。

术语约定

术语 说明
aarch64 ARM64 架构(如鲲鹏、飞腾等国产化服务器平台),用于网关/存储/消息节点
x64 服务器级 x86-64 CPU 平台(如 Intel Xeon / AMD EPYC),仅用于算力节点
RTX 5090 NVIDIA Blackwell 架构旗舰 GPU,32GB GDDR7 显存,FP4 算力约 3352 TOPS
vLLM 高吞吐 LLM 推理框架,PagedAttention KV Cache 管理
M1/M2/M3 推理调度三种模式(见第四节)
Two-Stage Agent 二次推理范式(先工具决策,再最终回答)
SSE / MQTT / QUIC 流式推送 / 轻量级消息协议 / 基于 UDP 的现代传输协议
libhv 高性能 C++ HTTP/QUIC 网络框架

二、总体架构:一套机箱,四种角色

2.1 设计原则

原则 说明
软硬一体 四类物理节点集成于单一机箱,形成完整 AI 一体机
异构分工 I/O 密集节点用 aarch64(低功耗、低成本、国产化友好),算力节点独占 x64 + CUDA 生态
协议分层 对外 HTTP(APISIX),内部 QUIC 统一传输(流式 RPC + JSON-RPC),异步走 MQTT,存储走 gRPC
轻量优先 MQTT Broker 选用 NanoMQ,内存占用低至 200KB
前端现代化 Vue 3 + Composition API + TypeScript
合规底线 GPL/AGPL 组件进程/网络隔离
数据驱动决策 所有性能目标(TPS、并发、带宽)必须基于实测冻结
Two-Stage 范式 Agent 工具调用采用二次推理,废弃流式解析器
模型适配硬件 模型选型以 RTX 5090 32GB 显存为约束

2.2 四节点物理部署

#mermaid-svg-lxzPJrkpYYaIR2zE{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-lxzPJrkpYYaIR2zE .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-lxzPJrkpYYaIR2zE .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-lxzPJrkpYYaIR2zE .error-icon{fill:#552222;}#mermaid-svg-lxzPJrkpYYaIR2zE .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-lxzPJrkpYYaIR2zE .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-lxzPJrkpYYaIR2zE .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-lxzPJrkpYYaIR2zE .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-lxzPJrkpYYaIR2zE .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-lxzPJrkpYYaIR2zE .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-lxzPJrkpYYaIR2zE .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-lxzPJrkpYYaIR2zE .marker{fill:#333333;stroke:#333333;}#mermaid-svg-lxzPJrkpYYaIR2zE .marker.cross{stroke:#333333;}#mermaid-svg-lxzPJrkpYYaIR2zE svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-lxzPJrkpYYaIR2zE p{margin:0;}#mermaid-svg-lxzPJrkpYYaIR2zE .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-lxzPJrkpYYaIR2zE .cluster-label text{fill:#333;}#mermaid-svg-lxzPJrkpYYaIR2zE .cluster-label span{color:#333;}#mermaid-svg-lxzPJrkpYYaIR2zE .cluster-label span p{background-color:transparent;}#mermaid-svg-lxzPJrkpYYaIR2zE .label text,#mermaid-svg-lxzPJrkpYYaIR2zE span{fill:#333;color:#333;}#mermaid-svg-lxzPJrkpYYaIR2zE .node rect,#mermaid-svg-lxzPJrkpYYaIR2zE .node circle,#mermaid-svg-lxzPJrkpYYaIR2zE .node ellipse,#mermaid-svg-lxzPJrkpYYaIR2zE .node polygon,#mermaid-svg-lxzPJrkpYYaIR2zE .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-lxzPJrkpYYaIR2zE .rough-node .label text,#mermaid-svg-lxzPJrkpYYaIR2zE .node .label text,#mermaid-svg-lxzPJrkpYYaIR2zE .image-shape .label,#mermaid-svg-lxzPJrkpYYaIR2zE .icon-shape .label{text-anchor:middle;}#mermaid-svg-lxzPJrkpYYaIR2zE .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-lxzPJrkpYYaIR2zE .rough-node .label,#mermaid-svg-lxzPJrkpYYaIR2zE .node .label,#mermaid-svg-lxzPJrkpYYaIR2zE .image-shape .label,#mermaid-svg-lxzPJrkpYYaIR2zE .icon-shape .label{text-align:center;}#mermaid-svg-lxzPJrkpYYaIR2zE .node.clickable{cursor:pointer;}#mermaid-svg-lxzPJrkpYYaIR2zE .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-lxzPJrkpYYaIR2zE .arrowheadPath{fill:#333333;}#mermaid-svg-lxzPJrkpYYaIR2zE .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-lxzPJrkpYYaIR2zE .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-lxzPJrkpYYaIR2zE .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-lxzPJrkpYYaIR2zE .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-lxzPJrkpYYaIR2zE .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-lxzPJrkpYYaIR2zE .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-lxzPJrkpYYaIR2zE .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-lxzPJrkpYYaIR2zE .cluster text{fill:#333;}#mermaid-svg-lxzPJrkpYYaIR2zE .cluster span{color:#333;}#mermaid-svg-lxzPJrkpYYaIR2zE div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-lxzPJrkpYYaIR2zE .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-lxzPJrkpYYaIR2zE rect.text{fill:none;stroke-width:0;}#mermaid-svg-lxzPJrkpYYaIR2zE .icon-shape,#mermaid-svg-lxzPJrkpYYaIR2zE .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-lxzPJrkpYYaIR2zE .icon-shape p,#mermaid-svg-lxzPJrkpYYaIR2zE .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-lxzPJrkpYYaIR2zE .icon-shape .label rect,#mermaid-svg-lxzPJrkpYYaIR2zE .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-lxzPJrkpYYaIR2zE .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-lxzPJrkpYYaIR2zE .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-lxzPJrkpYYaIR2zE :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 🌐 外部
📦 企业私有AI算力服务器 --- 完整机箱
📨 节点4: 消息机 (aarch64)
⚡ 节点3: AI算力机 (x64)
💾 节点2: 存储机 (aarch64)
🖥️ 节点1: 网关机 (aarch64)
HTTP
HTTP
HTTP
HTTP
HTTP
HTTP
配置读写
QUIC RPC
QUIC RPC
QUIC RPC
QUIC JSON-RPC
MQTT
MQTT
MQTT
MQTT
MQTT
gRPC
文件读取
文件读取
Apache APISIX

(路由·认证·限流·RBAC)
etcd

(配置存储)
Vue 3 静态资源
Milvus

(向量数据库)
MinIO / 本地FS

(文档存储)
PostgreSQL

(元数据·审计·日志)
服务2: 会话服务
服务3: 推理调度⭐

(RTX 5090 32GB GDDR7)
服务4: RAG服务
服务5-A: Agent Gateway

(Two-Stage推理)
服务5-B: Tool Registry
服务6: 硬件监控

(含Watchdog)
服务7: 模型管理
NanoMQ

(MQTT Broker)
Web UI / SDK / 管理台

2.3 为什么是"三轻一重"的异构结构

节点 角色 核心组件 硬件配置
节点1:网关机 API 网关 + 前端交付 APISIX + etcd + Vue3 静态资源 8核 aarch64 + 16GB + 256GB NVMe
节点2:存储机 数据持久化 Milvus + PostgreSQL + MinIO + Loki 16核 aarch64 + 64GB + 4TB NVMe
节点3:AI算力机 核心推理 7个微服务 + RTX 5090(32GB) 32核 x64 + 128GB DDR5 + RTX 5090 32GB GDDR7(575W TGP) + 2TB NVMe
节点4:消息机 异步消息中枢 NanoMQ 4核 aarch64 + 8GB + 64GB

这个划分背后的逻辑很直接:

  • 网关、存储、消息都是 I/O 密集型负载。APISIX 转发、etcd 读写、Milvus 向量检索、MQTT 收发,瓶颈在磁盘和网络而非 CPU 主频,aarch64 平台完全够用,且功耗低、成本低,对国产化/信创需求友好。这些组件(APISIX、etcd、Milvus、PostgreSQL、MinIO、NanoMQ)在 ARM64 上均有成熟的容器镜像与长期支持。
  • 算力节点必须是 x86。CUDA / TensorRT / vLLM 生态目前只落在 x86_64 平台,GPU 推理栈没有替代方案,这一步不能省。
  • 功耗预算向 GPU 倾斜。RTX 5090 单卡 TGP 575W,整机峰值功耗超 1000W。三个 aarch64 节点的整机功耗可以压得很低,等于把电源和散热预算尽量留给 GPU,机箱内的电源、风道设计也因此更从容。

于是整台机器形成"三轻一重":三个 aarch64 轻节点处理外围 I/O,一个 x64 重节点独占 GPU 做推理。节点之间通过机箱内部网络互通,部署上采用容器化(Docker Compose 编排),保证四个节点的软件交付一致。


三、微服务划分:七个服务,一条链路

服务编号有个跳号说明:服务1 是基础设施集合(APISIX + etcd + NanoMQ + Milvus + PG + MinIO),随节点部署,不单独开发;业务微服务从服务2开始。服务5 拆分为 5-A(Agent Gateway)和 5-B(Tool Registry)两个独立部署单元。

服务 端口 主协议 内部通信 核心职责
服务2:会话 8081 HTTP/SSE QUIC RPC(→服务3) 多轮对话、上下文生命周期、流式响应
服务3:推理调度⭐ 8082(UDP/QUIC) QUIC RPC 算力节点内部核心 GPU 资源调度、M1/M2/M3 模式、PagedAttention、故障降级
服务4:RAG 8083 HTTP QUIC RPC(→服务3)+ gRPC(→Milvus) 文档解析、切片向量化、多路召回、引用溯源
服务5-A:Agent Gateway 8084 HTTP/WS QUIC RPC(→服务3)+ QUIC JSON-RPC(→5-B) Assistants API 兼容、MCP 协议、Two-Stage 推理编排
服务5-B:Tool Registry ---(复用8082) QUIC JSON-RPC 被服务5-A调用 工具注册、调用路由、插件沙箱
服务6:硬件监控 8085 HTTP MQTT 温度/功耗采集、PID 风扇调速、多级告警、Watchdog
服务7:模型管理 8086 HTTP MQTT 模型版本管理、加载/卸载、格式转换

业务微服务统一使用 C++17 + libhv(Apache-2.0)实现,理由有三:与 QUIC 框架同源、减少第三方依赖;单二进制部署轻量;推理链路对时延敏感,C++ 兜得住底。服务4、服务7 另带 Python 辅助子进程处理文档解析与模型转换。

3.1 服务2:会话与对话服务

定位:对话链路的"门面"------所有外部对话请求从这里进来,但这里没有一行代码碰 GPU。

端口:8081(HTTP/SSE)。

核心职责

  1. 多轮对话上下文生命周期管理(会话创建、上下文窗口裁剪、超时回收);
  2. 对话历史持久化;
  3. SSE 流式响应------token 从服务3 到达后原样推给客户端,不做任何加工;
  4. 作为 QUIC 客户端调用推理调度服务;
  5. 订阅硬件告警,降级发生时同步会话状态。

对外接口(OpenAI 兼容是刻意为之------现有 SDK、Dify、LangChain 等工具零改造即可接入):

端点 方法 说明
/api/v1/chat/completions POST OpenAI 兼容对话(SSE 流式)
/api/v1/session/create POST 创建会话
/api/v1/session/{id}/history GET 获取历史
/api/v1/session/{id}/delete DELETE 删除会话

对内通信:QUIC RPC → 服务3。会话服务对推理的全部诉求收敛为一条流式方法:

protobuf 复制代码
// 序列化 Protobuf,传输层 QUIC
service Inference {
    rpc Infer(InferRequest) returns (stream InferResponse);
}

此外通过 MQTT 订阅硬件告警主题(alerts/hardware/*,示例省略业务命名空间前缀):GPU 过温、模式降级发生时,会话层能感知并调整后续请求行为,而不是等用户看到超时才后知后觉。

数据存储

数据 位置 说明
会话元数据 PostgreSQL(节点2) session_id、user_id、created_at
消息历史 PostgreSQL(节点2) role、content、timestamp
KV Cache 服务3 显存 推理上下文缓存,不落盘

架构要点 :服务2 只做"对话的壳",服务3 独占推理。切分的价值在于推理服务可以独立重启、独立降级、独立扩容------将来要把推理拆到多台算力服务器,只需把服务3 的 QUIC 端点换成多实例负载均衡,服务2 及上游一概不改。

3.2 服务3:推理调度服务(核心护城河)

定位:全机唯一拥有大模型推理调度权的服务,是所有推理请求的必经之路,也是整套架构中技术密度最高的一层。

端口:8082(UDP/QUIC)。

核心职责 :① RTX 5090 GPU 资源调度;② M1/M2/M3 三模式管理与在线切换;③ 模型加载/卸载;④ PagedAttention KV Cache 管理;⑤ 故障隔离与自动降级。推理引擎不重复造轮子,集成 vLLM / TensorRT-LLM(PagedAttention + Continuous Batching),自研部分聚焦在调度、模式管理与降级控制

QUIC RPC 服务定义------四组接口本身就是一份架构说明书:

protobuf 复制代码
service InferenceScheduler {
    // 核心推理接口(流式)------ 1条QUIC stream = 1次推理
    rpc Infer(InferRequest) returns (stream InferResponse);

    // 模型管理
    rpc LoadModel(LoadRequest) returns (LoadResponse);
    rpc UnloadModel(UnloadRequest) returns (UnloadResponse);
    rpc ListModels(Empty) returns (ModelList);

    // 模式管理
    rpc SwitchMode(SwitchModeRequest) returns (SwitchModeResponse);
    rpc GetMode(Empty) returns (ModeResponse);

    // 状态查询
    rpc GetStatus(Empty) returns (StatusResponse);
    rpc GetGPUStatus(GPUStatusRequest) returns (GPUStatusResponse);
    rpc GetBandwidth(Empty) returns (BandwidthResponse);
}
  • Infer:每次推理占一条独立 QUIC stream,天然并发、互不阻塞;
  • SwitchMode:把"重排显存"变成一个 RPC 调用,运维接口和自动降级共用同一条路径;
  • GetGPUStatus / GetBandwidth:把硬件探测能力暴露成 RPC,启动自检和监控面板复用同一套实现。

MQTT 订阅(服务6 告警与服务7 命令的接入点):

主题 QoS 动作
alerts/hardware/critical 1 执行降载,暂停新模型加载
commands/scheduler/shutdown 1 安全卸载所有模型,释放显存
commands/scheduler/degrade 1 M1→M2 切换(卸大模型、载小模型)

数据存储:模型权重存放在节点2 模型存储区,加载时经 NVMe + PCIe 5.0 流式进 GPU;KV Cache 驻留 GDDR7、纯显存不持久化;GPU 状态(显存占用、温度、功耗、SM 利用率)保留在本服务内存中。

M1/M2/M3 三模式定义与 KV Cache 的深入展开见第四节(决策二、决策三)。

3.3 服务4:知识工程服务(RAG)

定位:企业知识的"摄取 + 检索"双引擎------前半段把文档变成向量,后半段把问题变成答案并附上出处。

端口:8083(HTTP)。

核心职责:① 多格式文档解析;② 语义切片与向量化;③ 向量数据库管理;④ 多路召回 + 重排序;⑤ 引用溯源。最后一点在企业场景常常比检索精度更致命:答案里的每个结论都要能指回原文档的具体位置,否则知识库就会沦为"不可信的黑盒"。

对外接口

端点 方法 说明
/api/v1/knowledge/upload POST 上传文档
/api/v1/knowledge/search POST 语义检索
/api/v1/knowledge/chat POST RAG 问答
/api/v1/knowledge/collections GET 列出知识库

处理流水线与合规隔离
#mermaid-svg-xwSTXbcbmtBj6PCZ{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-xwSTXbcbmtBj6PCZ .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-xwSTXbcbmtBj6PCZ .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-xwSTXbcbmtBj6PCZ .error-icon{fill:#552222;}#mermaid-svg-xwSTXbcbmtBj6PCZ .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-xwSTXbcbmtBj6PCZ .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-xwSTXbcbmtBj6PCZ .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-xwSTXbcbmtBj6PCZ .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-xwSTXbcbmtBj6PCZ .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-xwSTXbcbmtBj6PCZ .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-xwSTXbcbmtBj6PCZ .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-xwSTXbcbmtBj6PCZ .marker{fill:#333333;stroke:#333333;}#mermaid-svg-xwSTXbcbmtBj6PCZ .marker.cross{stroke:#333333;}#mermaid-svg-xwSTXbcbmtBj6PCZ svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-xwSTXbcbmtBj6PCZ p{margin:0;}#mermaid-svg-xwSTXbcbmtBj6PCZ .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-xwSTXbcbmtBj6PCZ .cluster-label text{fill:#333;}#mermaid-svg-xwSTXbcbmtBj6PCZ .cluster-label span{color:#333;}#mermaid-svg-xwSTXbcbmtBj6PCZ .cluster-label span p{background-color:transparent;}#mermaid-svg-xwSTXbcbmtBj6PCZ .label text,#mermaid-svg-xwSTXbcbmtBj6PCZ span{fill:#333;color:#333;}#mermaid-svg-xwSTXbcbmtBj6PCZ .node rect,#mermaid-svg-xwSTXbcbmtBj6PCZ .node circle,#mermaid-svg-xwSTXbcbmtBj6PCZ .node ellipse,#mermaid-svg-xwSTXbcbmtBj6PCZ .node polygon,#mermaid-svg-xwSTXbcbmtBj6PCZ .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-xwSTXbcbmtBj6PCZ .rough-node .label text,#mermaid-svg-xwSTXbcbmtBj6PCZ .node .label text,#mermaid-svg-xwSTXbcbmtBj6PCZ .image-shape .label,#mermaid-svg-xwSTXbcbmtBj6PCZ .icon-shape .label{text-anchor:middle;}#mermaid-svg-xwSTXbcbmtBj6PCZ .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-xwSTXbcbmtBj6PCZ .rough-node .label,#mermaid-svg-xwSTXbcbmtBj6PCZ .node .label,#mermaid-svg-xwSTXbcbmtBj6PCZ .image-shape .label,#mermaid-svg-xwSTXbcbmtBj6PCZ .icon-shape .label{text-align:center;}#mermaid-svg-xwSTXbcbmtBj6PCZ .node.clickable{cursor:pointer;}#mermaid-svg-xwSTXbcbmtBj6PCZ .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-xwSTXbcbmtBj6PCZ .arrowheadPath{fill:#333333;}#mermaid-svg-xwSTXbcbmtBj6PCZ .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-xwSTXbcbmtBj6PCZ .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-xwSTXbcbmtBj6PCZ .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-xwSTXbcbmtBj6PCZ .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-xwSTXbcbmtBj6PCZ .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-xwSTXbcbmtBj6PCZ .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-xwSTXbcbmtBj6PCZ .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-xwSTXbcbmtBj6PCZ .cluster text{fill:#333;}#mermaid-svg-xwSTXbcbmtBj6PCZ .cluster span{color:#333;}#mermaid-svg-xwSTXbcbmtBj6PCZ div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-xwSTXbcbmtBj6PCZ .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-xwSTXbcbmtBj6PCZ rect.text{fill:none;stroke-width:0;}#mermaid-svg-xwSTXbcbmtBj6PCZ .icon-shape,#mermaid-svg-xwSTXbcbmtBj6PCZ .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-xwSTXbcbmtBj6PCZ .icon-shape p,#mermaid-svg-xwSTXbcbmtBj6PCZ .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-xwSTXbcbmtBj6PCZ .icon-shape .label rect,#mermaid-svg-xwSTXbcbmtBj6PCZ .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-xwSTXbcbmtBj6PCZ .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-xwSTXbcbmtBj6PCZ .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-xwSTXbcbmtBj6PCZ :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} ⚠️ 进程隔离
popen()
上传文档
📄 文档解析

(LibreOffice子进程)
✂️ 语义切片
🧠 向量化

(RTX 5090 CUDA + bge-m3)
💾 存储

(Milvus + MinIO)
LibreOffice

(GPLv3)

流水线中最危险的一环是格式转换------LibreOffice 是 GPLv3 组件,且有解析恶意文档的历史前科。架构上把它关进"进程隔离的笼子":以独立子进程调用、不链接进主程序,崩溃和漏洞都被限制在子进程边界内,主服务与许可合规两不误。

另有一个容易被忽略的细节:向量化(bge-m3 embedding)与大模型推理共享同一块 RTX 5090。embedding 是毫秒级短任务,与长推理流天然错峰,这也是 RAG 服务与推理调度同节点部署的原因之一。

数据存储

数据 位置 说明
向量数据 Milvus(节点2,gRPC) 向量 + 元数据
原始文档 MinIO(节点2) PDF / Word 等
文档元数据 PostgreSQL(节点2) doc_id、filename、chunk_count

3.4 服务5-A:Agent Gateway

定位:Agent 能力的协议适配层------把 OpenAI Assistants API 和 MCP 协议翻译成内部调用,自己不执行任何工具。

端口:8084(HTTP + WebSocket)。

核心职责:① OpenAI Assistants API 兼容;② MCP 协议支持;③ Two-Stage 推理编排(工具决策 → 执行 → 最终回答);④ 工具调用编排。Two-Stage 范式的完整推演与效果对比见第四节(决策四),这里只强调一点:5-A 是"编排者",每次工具调用都经过 QUIC JSON-RPC 下发给 5-B 执行,自己保持无状态、可随时重启。

对外接口

端点 方法 说明
/api/v1/assistants POST/GET 智能体管理
/api/v1/threads POST 创建线程
/api/v1/threads/{id}/runs POST 执行(SSE 流式)
/mcp WebSocket MCP 协议端点

MCP 交付边界(按"够用就好"裁剪):

功能 交付状态 说明
initialize 握手 ✅ 交付 协议版本 + 服务能力
tools/list ✅ 交付 列出可用工具
tools/call ✅ 交付 执行工具调用
Streamable HTTP ⚠️ 简化版 支持 SSE 流式返回
完整 MCP 规范 ❌ 后续版本 采样、资源订阅等复杂场景后续迭代

数据存储:Assistant 配置、Thread 消息、Run 状态均落 PostgreSQL(节点2),5-A 自身无本地状态。

3.5 服务5-B:Tool Registry

定位:工具执行的"总线"------所有工具在这里注册、被路由、被执行,第三方插件与 Agent 主链路之间的防火墙。

端口:无独立对外端口,监听在 QUIC 8082 上,与推理调度按 stream ID 区分,零额外网络开销。

核心职责:① 工具元数据注册;② 工具调用路由;③ 内置工具执行;④ 自定义插件沙箱。

QUIC JSON-RPC 2.0 方法(JSON-RPC 消息体承载于 QUIC 流之上,每条工具调用一条独立 stream,天然并发):

方法 参数 返回
tools.register {name, description, schema} {tool_id}
tools.unregister {tool_id} {success}
tools.list {} {tools: [...]}
tools.call {tool_name, args} {result}

内置工具

工具 实现 说明
web_search HTTP 调用 SearXNG 联网搜索,外联流量与内网服务边界隔离
calculator 自研表达式解析器 纯本地计算
get_time 系统调用 最简单的可验证工具

架构要点:协议适配(5-A)与工具执行(5-B)拆成两个部署单元,是 Agent 链路最重要的一次"泄压"------第三方插件崩溃、超时甚至被恶意构造的输入打挂,都只发生在 5-B 进程内,杀掉重启即可,Agent 入口始终在线。

3.6 服务6:硬件监控服务

定位:整机的"体温计 + 心跳器",软件世界与硬件世界之间的翻译官。

端口:8085(HTTP)。

核心职责:① 温度/功耗采集;② PID 风扇调速;③ 多级告警;④ 内核 Watchdog 喂狗。

它有两个架构角色值得单独强调:

  • 事件翻译器 :把 GPU 温度、功耗等硬件信号翻译成 MQTT 告警(alerts/hardware/*)广播出去,服务3、服务2 各自订阅后按需降级。硬件状态走总线广播,而不是每个服务自己去读传感器------一套采集,多方消费;
  • 最后防线:Watchdog 每 10 秒喂一次、30 秒超时硬重启。进程死锁、内核卡死这些软件兜不住的故障,物理兜底。

多级温度告警的完整联动策略(70℃ 提风扇 → 88℃ 切模式 → 95℃ 关机)见第五节 5.3。

3.7 服务7:模型管理服务

定位:模型资产的"仓库管理员"------管版本、管上下架、管格式转换。

端口:8086(HTTP)。

核心职责:① 模型版本管理;② 模型加载/卸载(实际调用 vLLM / TensorRT-LLM);③ 模型格式转换(HuggingFace → TensorRT Engine)。

模型基线(单卡 32GB GDDR7 约束下的"菜单",每一行都是一次显存预算审查的结论):

模型 量化方式 显存占用 状态 吞吐量级 说明
Qwen3-32B Q4_K_M ~20 GB 默认主推(M1) 十级 TPS 高质量问答、推理
Qwen3-14B Q4_K_M ~9 GB M2 主推 数十级 TPS 多实例并发场景
Qwen3-14B FP8 (Blackwell) ~14 GB ✅ 推荐 数十~百级 TPS 利用 Blackwell FP8
Qwen3-7B FP16 ~15 GB ✅ 可选 百级 TPS 低延迟场景
Llama-3.3-70B Q2_K ~25 GB ⚠️ 实验性 个位~十级 TPS 极限量化,质量损失
DeepSeek-V3-0324 FP8 蒸馏 ~30 GB ⚠️ 紧凑 个位数 TPS 满显存运行,KV Cache 极少
Qwen3-72B Q4_K_M ~40 GB ❌ 不支持 --- 超过 32GB 显存上限

推荐组合:M1 模式用 Qwen3-32B-Q4_K_M;M2/M3 模式用 Qwen3-14B-Q4_K_M ×2 或 FP8 ×2(表中吞吐仅标注量级,具体基线数值随实测冻结、不对外公开)。这张"菜单"不是下载下来试试看拼凑的,而是模型适配硬件原则的落地------加载前逐个过显存预检(第四节决策三),不达标直接拒绝。


四、核心架构决策

决策一:三层协议栈------对外 HTTP,对内 QUIC,异步 MQTT

层级 协议 用途 承载路径
对外 HTTP/HTTPS(APISIX) 外部 API + 前端资源 节点1 ↔ 节点3
对外 SSE / WebSocket 流式推理响应 / MCP 协议 节点3 → 外部
对内(同步) QUIC RPC 推理调度(流式,Protobuf) 节点3 内部
对内(同步) QUIC JSON-RPC 工具调用(JSON-RPC 2.0) 节点3 内部
对内(异步) MQTT 告警、事件、命令 节点3 ↔ 节点4
存储 gRPC Milvus 向量检索 节点3 → 节点2

对内为什么不用 gRPC 而选 QUIC?对比一下:

维度 gRPC QUIC RPC
传输层 TCP(HTTP/2) UDP(QUIC)
多路复用 连接内流多路复用 原生 QUIC stream
队头阻塞 TCP 级队头阻塞 无队头阻塞
握手开销 TCP 三次握手 + TLS 0-RTT 支持
流控 HTTP/2 窗口 QUIC 自适应流控
依赖 grpc-core 全家桶 基于 libhv 自研薄封装

推理场景是典型的"多条并发流式推理共享一条链路":一次对话一条 token 流,TCP 队头阻塞会让一条慢流拖累同连接上的所有推理;QUIC 的 stream 之间互不影响,0-RTT 还能让服务重启后快速重连。同时推理 RPC 与工具调用 JSON-RPC 共享同一个 QUIC 传输层,网络栈只有一种------所有内部同步通信收敛到 UDP 8082 一个端口,靠 stream ID 多路复用

复制代码
┌─────────────────────────────────────────────────────────┐
│                    节点3:AI算力机 (x64)                   │
│                                                         │
│  ┌──────┐   QUIC RPC    ┌──────────┐   QUIC JSON-RPC   │
│  │ 服务2 │ ═════════════►│          │◄═════════════════│
│  │      │   (Protobuf)   │  服务3   │    (JSON-RPC)    │
│  └──────┘                │ 推理调度  │                  │
│  ┌──────┐                │          │                  │
│  │ 服务4 │ ═════════════►│          │                  │
│  └──────┘                └──────────┘                  │
│  ┌──────┐   QUIC JSON-RPC ┌──────────┐                  │
│  │ 5-A  │ ═══════════════►│ 服务5-B  │                  │
│  │      │   (JSON-RPC)    │ Tool Reg │                  │
│  └──────┘                 └──────────┘                  │
│                                                         │
│  ▲ 所有内部 QUIC 连接共享一个 UDP 端口,靠 stream 多路复用   │
└─────────────────────────────────────────────────────────┘

MQTT 则负责所有"不需要立刻返回结果"的事:硬件告警、模式切换命令、安全停机。主题设计约定了 QoS 1 与明确的命名空间(如 alerts/hardware/criticalcommands/scheduler/degrade,示例省略业务命名空间前缀),同步链路与异步事件互不干扰。

决策二:单卡 32GB 的三种调度模式(M1/M2/M3)

RTX 5090 单卡 32GB 显存是所有推理架构决策的物理约束。我们没有做虚的弹性调度,而是定义了三种互斥、可在线切换的静态模式:

模式 显存分配 适用场景 启用条件
M1(大模型单实例) 1× Qwen3-32B-Q4_K_M(约 20GB)+ KV Cache 12GB 高质量问答、复杂推理 默认启用
M2(多实例并发) 2× Qwen3-14B-Q4_K_M(各约 9GB)+ KV Cache 各 7GB 客服/多用户工作台 高并发场景
M3(内外分区) 1× 14B 内网 + 1× 14B 外网,显存按比例切分 内外隔离 合规场景

M1 与 M2 互斥------显存就那么多,同一时刻只能选"一个大模型"或"两个小模型"。切换通过服务3 的 SwitchMode RPC 在线完成:卸载当前模型 → 释放 KV Cache → 加载新模型。

更重要的是模式选择由实测数据驱动,而不是拍脑袋。服务3 启动时先探测 GPU 能力:

cpp 复制代码
// 服务3启动时执行 GPU 显存带宽 + 算力检测
class GPUBenchmark {
public:
    struct GPUCapability {
        size_t total_vram_mb;        // 总显存(MB)
        size_t usable_vram_mb;       // 可用显存(扣除 CUDA context 预留)
        float   vram_bandwidth_gbps; // GDDR7 实测显存带宽(GB/s)
        float   tokens_per_second;   // 14B-Q4_K_M 实测 TPS(基线内部冻结)
        bool    fp8_supported;       // Blackwell FP8 支持
        bool    fp4_supported;       // Blackwell FP4 支持
    };

    GPUCapability probe() {
        cudaDeviceProp prop;
        cudaGetDeviceProperties(&prop, 0);

        GPUCapability cap;
        cap.total_vram_mb      = prop.totalGlobalMem / (1024 * 1024);
        cap.usable_vram_mb     = cap.total_vram_mb - 512;  // 预留 CUDA runtime
        cap.vram_bandwidth_gbps = runBandwidthTest();
        cap.tokens_per_second  = runTPSBenchmark("qwen3-14b-q4_k_m");
        cap.fp8_supported = (prop.major >= 10);  // Blackwell SM 10.x
        cap.fp4_supported = (prop.major >= 10);
        return cap;
    }

    ModeRecommendation recommendMode(const GPUCapability& cap) {
        // tps_baseline_:启动时实测冻结的基线值(数值不对外)
        if (cap.tokens_per_second >= tps_baseline_) {
            return {.recommended = "M1(默认)/M2/M3", .note = "性能达标,按业务选择"};
        }
        return {.recommended = "M2/M3", .note = "TPS 不足,建议多实例并发"};
    }
};

冻结的基准指标作为架构的"物理常数":14B、32B 各有一条实测 TPS 基线,显存带宽以 GDDR7 理论峰值为锚。具体基线数值在项目内部冻结存档,本文只保留量级------14B 量化版吞吐在数十 TPS 量级,32B 在十 TPS 量级,实测带宽贴近理论峰值。所有上层承诺都建立在这组实测数字之上。

决策三:PagedAttention 管 KV Cache------把显存当内存管理

推理服务最大的隐形成本不是模型权重,而是 KV Cache。我们采用 vLLM 的 PagedAttention 方案:把 KV Cache 切成固定大小 page(每页 16 token),按需分配,配合前缀复用(相同 system prompt 共享 KV)和 LRU 淘汰:

cpp 复制代码
class PagedKVCacheManager {
public:
    CacheHandle allocate(const string& session_id, int initial_pages);
    void appendTokens(CacheHandle handle, int num_new_tokens); // 自动扩展 page
    CacheHandle lookupPrefix(const string& prompt_prefix);     // 前缀复用
    void evictPages(size_t target_free_mb);                    // LRU 淘汰
    void releaseForModeSwitch();                               // M1→M2 切换时回收

private:
    unordered_map<PageID, PageMeta> page_table_;               // 物理页 → 引用计数
    unordered_map<string, vector<PageID>> session_pages_;      // 会话 → 逻辑页表
    size_t total_vram_used_;
    size_t vram_capacity_;   // RTX 5090: 32768 MB
    int    page_size_;       // 每 page 16 tokens
};

以 M1 模式为例算一笔显存账:权重约 20GB,KV Cache 可用约 12GB;按 32B 模型的单 token KV 开销折算,可支撑数千 token 上下文与个位数并发会话------前缀复用后实际更高。每一次模型加载都要过这道校验,含 15% 余量,不达标直接拒绝

cpp 复制代码
bool validateModelLoad(const string& model_id, Mode target_mode) {
    ModelInfo info = getModelInfo(model_id);
    size_t total_vram_mb = getGPUVramBytes() / (1024 * 1024);   // 32768
    size_t usable_mb = total_vram_mb - 512 /*runtime*/ - 4096 /*KV最小值*/;
    size_t per_instance_mb = (target_mode == M2) ? usable_mb / 2 : usable_mb;

    if (info.model_size_mb > per_instance_mb * 0.85f) {
        return false;  // 显存超限,拒绝加载
    }
    if (info.requires_fp8 && !getGPUCapability().fp8_supported) {
        return false;  // FP8 模型必须 Blackwell
    }
    return true;
}

决策四:Agent 用 Two-Stage 推理,不用流式解析

让模型在流式输出中"边说边决定"是否调用工具,看起来优雅,实际误触发率高到无法接受。架构上我们彻底放弃这条路,改为 Two-Stage 二次推理

  1. Stage 1(工具决策) :强制 System Prompt 只输出 JSON,temperature = 0.0 保证确定性,严格解析出 {tool, args};解析失败一律视为"不调用工具",安全兜底。
  2. Stage 2(最终回答):需要工具则先执行工具,再把结果拼进 prompt 做一次流式推理;不需要则直接流式回答。

代价是首 token 时延增加一次完整的推理往返,换来的是误触发率下降一个数量级、达到可量产水平,且代码从复杂状态机简化为两段顺序逻辑。对于企业内部工具调用场景,稳定性的优先级高于数百毫秒的时延

MCP 协议同样按"够用就好"的原则裁剪:交付 initialize / tools/list / tools/call 三个核心方法与简化版 Streamable HTTP,采样、资源订阅等复杂能力留待后续迭代。


五、横切关注点:安全与可观测

5.1 认证:一处鉴权,内部免验

所有外部请求经节点1 APISIX 网关统一鉴权,采用 JWT + API-Key 双轨:

角色 认证方式 权限范围
admin JWT 模型管理、监控、用户管理
user JWT 对话、知识库检索
external API-Key(限频) 仅外部问答接口

内部微服务间调用(QUIC RPC、QUIC JSON-RPC、MQTT)依赖 QUIC 连接级身份标识 + 节点内网络隔离,不重复鉴权------鉴权逻辑只存在于边界上,内部链路保持简单。

5.2 可观测:结构化日志一路到底

所有微服务统一输出 JSON 结构化日志,采集链路为:

复制代码
微服务 → stdout JSON → Promtail(节点3 sidecar)→ Loki(节点2)→ Grafana 查询

GPU 侧由服务6 采集温度、功耗、显存占用,经 MQTT 上报,与日志体系在 Grafana 汇合。

5.3 硬件看门狗:软件故障的最后防线

RTX 5090 是单点,硬件监控服务(服务6)因此承担三件事:

  1. 多级温度告警联动:70~80℃ 提风扇转速 → 80~88℃ 降载限并发 → 88~95℃ 触发 M1→M2 切换(卸载 32B 大模型加载 14B)→ >95℃ 安全关机。温控动作直接映射到第四节的三模式架构,硬件保护与软件调度是同一套语言。
  2. 内核 Watchdog 喂狗:每 10 秒喂一次,超时 30 秒硬重启,进程级死锁也有兜底。
  3. 功耗监测:整机峰值 >1000W,实时监测 GPU 功耗曲线,配合 ≥1500W 金牌电源与浪涌保护。

六、风险与架构应对

风险 等级 架构级应对
RTX 5090 供货短缺 🔴 启动前锁定采购;备选 RTX 5080 16GB 方案
32B 模型 TPS 不达标 🟠 启用 Blackwell FP8 量化;M1 默认降级为 14B-FP8
算力单卡 SPOF 🟠 Watchdog + 故障自动降级(M1→M2)+ 电源冗余
散热不足导致 GPU 降频 🟠 独立 GPU 风道 + 温度联动模式切换
libhv QUIC 成熟度 🟡 部署前验证 hquic 模块稳定性,保留 TCP 回退路径
MCP 规范复杂度 🟡 简化交付,核心方法优先

可以看到,多数风险的应对手段不是"加人加班",而是架构里预埋好的降级路径------模式切换、量化降级、传输层回退,都是第一节设计原则的具体兑现。


七、结语

回顾整套架构,最值得复用的三条经验:

  1. 异构分工:不是所有节点都需要最强 CPU。I/O 密集的网关/存储/消息交给 aarch64,把功耗、成本、散热预算全部让给算力节点的 GPU;
  2. 协议收敛:对外一种入口(HTTP),对内一种同步传输(QUIC)、一种异步通道(MQTT)、一种存储协议(gRPC)。协议种类少,故障排查和性能调优才有抓手;
  3. 约束即架构:单卡 32GB 显存不是缺陷,而是把 M1/M2/M3 三模式、PagedAttention、显存预检、温度联动降级串成一条线的那根轴。性能目标全部实测冻结,架构决策全部可回溯。

后续随业务演进,这套架构还有两个明确的演进方向:推理服务横向拆分到多台算力服务器(服务3 的 QUIC 端点天然支持多实例),以及 MCP/工具生态的逐步完善。架构的价值不在于一次设计到位,而在于每个扩展点都在该在的位置

相关推荐
闪学it16 分钟前
AE影视后期特效-遮罩/调色/抠像/MG动画/3D/Vlog制作
人工智能
m0_7345717616 分钟前
深入理解C++ 类型转换<三>const_cast
开发语言·c++
硬件工艺分享君21 分钟前
PCB厂家如何守住交期、品质与响应
大数据·运维·科技·制造·pcb工艺
Shulex21 分钟前
电商 AI 客服接管率怎么提升?从指标口径到转人工规则
大数据·人工智能·智能客服·跨境电商
海盗123421 分钟前
AI新闻日报_2026-08-24——AI 安全从理论风险变成现实事件——Coding Agent 越狱与具身机器人运动会共塑“干活即合规“新基线
人工智能·安全·机器人
库玛西23 分钟前
Linux网络编程:HTTP/HTTPS协议核心技术全解析
linux·运维·服务器·网络·c++·http·https
科技研学社29 分钟前
2026-2028全球工作服夹克智能制造发展趋势与海外工厂布局报告
大数据·人工智能
志栋智能36 分钟前
大模型驱动运维:重构故障根因分析的“思考”逻辑
运维·服务器·重构·自动化
2501_9304724438 分钟前
实战-腾讯云AI助手逆向导出Terraform-存量资源代码化
人工智能·腾讯云·terraform