AI 应用的多服务架构设计

AI 应用把系统拆成前端、业务后端、AI 服务三个进程的核心原因、职责边界划分原则、通信协议选型,以及从模块化单体到微服务的演进路径。

#type / concept #status / growing #tech / architecture #tech / ai

[!info] related notes

AI 应用的多服务架构设计

这篇解决什么问题

一个 AI 应用同时涉及 UI 交互、业务逻辑、模型调用三种差异巨大的能力。用一种语言/框架覆盖所有场景,要么性能不够,要么生态不匹配,要么团队能力不匹配。怎么拆?拆成几层?边界在哪?

核心决策:按技术生态的自然边界拆

不要按”微服务”的思路拆(用户服务、订单服务……),而是按技术栈的最佳适用域拆:

核心诉求最佳生态典型语言
用户界面交互流畅、组件丰富、状态管理React/Vue 生态TypeScript
业务逻辑高并发、强类型、稳定可靠、部署轻量Go/Java/RustGo
AI 能力LLM 调用、Agent 编排、RAG、OCRPython AI 生态Python

这三层不是随意拆的,是语言生态的自然边界

  • Python 做不了高并发 API 网关(不是不行,是不擅长)
  • Go 缺少 LangChain/LangGraph 等 Agent 框架
  • TypeScript 在 CPU 密集型任务和部署体积上不占优

三层职责边界

前端层

只做:UI 渲染、路由、状态管理、SSE 消费、用户交互

不做:直接调 LLM API、直接访问数据库、业务规则判断

业务后端层

只做:API 网关、认证鉴权、业务逻辑、数据持久化、SSE 代理转发

不做:LLM 调用、向量检索、OCR 处理

AI 服务层

只做:LLM 调用、Agent 工作流、RAG 检索、Function Calling、安全检测

不做:用户认证、业务数据 CRUD

关键原则:AI 服务不暴露到外网,只通过业务后端访问。这样 AI 服务不需要处理认证,业务后端控制所有访问权限。

通信协议选型

场景推荐协议原因
普通 CRUDREST (JSON)简单、通用、工具链成熟
AI 对话流式输出SSE单向推送、基于 HTTP、比 WebSocket 简单
高频内部调用gRPC强类型、高性能、有代码生成
异步任务消息队列解耦、削峰、重试

AI 对话场景选 SSE 而不是 WebSocket 的理由:

  • 服务器→客户端的单向推送就够了(用户发消息用普通 POST)
  • SSE 基于 HTTP,不需要额外的连接管理
  • 浏览器原生支持 EventSource(虽然实际用 fetch 更灵活)

数据流设计:一次 AI 对话

用户消息 → 前端 POST → 业务后端(鉴权+注入上下文)
  → 业务后端 POST → AI 服务(SSE)
  → AI 服务流式生成事件
  → 业务后端逐行透传
  → 前端逐事件分发到 UI

设计要点

  • 业务后端是透传,不缓冲 SSE 数据(否则延迟增加)
  • AI 服务产出的事件类型要提前定义好(text、extracted_info、phase_changed、red_flag、citation、done)
  • 前端按事件类型分发到不同 UI 面板,不是所有事件都进聊天框

信任边界

前端 ──(不信任)──► 业务后端 ──(不信任)──► AI 服务
        JWT 验证              输入校验
  • 前端→后端:所有请求必须带 JWT,后端验证
  • 后端→AI 服务:AI 服务返回的数据后端要校验再存库
  • AI 服务→后端:AI 服务检测 red flag 后通知后端
  • 服务间通信走内网,不暴露 AI 服务端口

与微服务的区别

这不是微服务,是模块化单体(Modular Monolith):

维度微服务模块化单体
拆分依据业务领域技术栈
部署每个服务独立版本统一版本、统一部署
数据库每个服务独立共享
服务发现需要不需要(固定地址)

为什么不搞微服务:MVP 阶段团队小、功能还在快速迭代,微服务的运维成本(服务发现、链路追踪、分布式事务)远超收益。等规模上来再拆也不迟。

什么时候该拆得更细

  • AI 服务需要 GPU 资源,和业务后端的部署环境不同
  • 某个 AI 能力(如 OCR)需要独立扩缩
  • 团队规模增长,需要独立部署和发布
  • 引入消息队列解耦同步调用

常见错误

前端直接调 AI 服务

// ❌ 前端直连 AI 服务
const res = await fetch('http://ai-service:8100/chat');

// ✅ 通过业务后端代理
const res = await fetch('/api/v1/consultation/message');

绕过业务后端意味着绕过认证和业务规则。

业务后端缓冲 SSE 数据

// ❌ 读完所有数据再返回
body, _ := io.ReadAll(resp.Body)
c.Data(200, "text/event-stream", body)

// ✅ 逐行透传
scanner := bufio.NewScanner(resp.Body)
for scanner.Scan() {
    fmt.Fprintf(c.Writer, "%s\n", scanner.Text())
    c.Writer.Flush()
}

缓冲会导致用户等待整个 LLM 响应完成才能看到第一个字。

AI 服务直接操作业务数据库

# ❌ AI 服务直接写用户表
db.execute("UPDATE users SET ...")

# ✅ AI 服务只返回结果,业务后端负责持久化
return {"extracted_info": [...], "phase": "collecting"}

AI 服务不应该知道业务数据的 schema。

创建于 2026/6/25 更新于 2026/6/25