AI 应用的多服务架构设计
比较 AI 应用前端、业务后端与 AI 能力的职责和部署边界,说明服务间认证、流式协议与渐进拆分的条件。
[!info] related notes
- 通信模式: SSE, Function Calling
- 实践参考: BodySense 项目 MOC
- 相关概念: Agent Runtime
AI 应用的多服务架构设计
这篇解决什么问题
AI 应用可将前端、业务后端与 AI 执行能力拆成协作组件,但是否分进程应由资源需求、发布频率、隔离与维护成本决定。一个语言覆盖多个层也可以成立,不能从“Python 不擅长网关”或“Go 没有框架”等笼统判断推出必须三服务。
一种三组件划分
| 组件 | 主要职责 | 边界 |
|---|---|---|
| 前端 | 交互、视图状态、消费流式事件 | 可做即时输入反馈,不能作为可信授权边界 |
| 业务后端 | 用户认证、用例与持久业务状态 | 可代理 AI 调用,也可在合适规模下直接调用模型 |
| AI 服务 | 模型/检索/工作流与工具执行适配 | 根据任务上下文授权,不能凭模型参数访问任意资源 |
TypeScript 前端、Go 业务后端与 Python AI 服务是一种技术组合,不是架构定义。Python、Go、Java、Rust、TypeScript 都有各自可用的服务与 AI 集成方案,选型需测量负载并考虑团队。
服务边界仍需认证
AI 服务放在内网可以缩小暴露面,但不等于无需认证。服务间应有可验证的调用方身份,传递可信用户/租户/操作上下文,并在数据或工具执行处检查权限。
用户认证可集中在业务入口;内部服务仍要验证委托是否真实、范围是否匹配。不能把来自前端的 user_id 或任意请求头直接当成可信身份。用户认证方式也不必固定为 JWT,可以按系统使用会话等机制。
通信协议选择
| 场景 | 可选协议 | 需要考虑 |
|---|---|---|
| 普通 CRUD | HTTP/JSON | 契约、错误与幂等 |
| 单向生成事件 | SSE | 流生命周期、取消、心跳、缓冲与重连 |
| 双向实时交互 | WebSocket | 连接状态、背压与消息协议 |
| 内部类型化 RPC | gRPC 等 | 契约、deadline、运维与兼容性 |
| 异步长任务 | 作业队列 | 持久化、去重、重试与结果查询 |
SSE 使用 HTTP,仍需要连接管理。原生 EventSource 与 fetch 流读取能力不同,POST、鉴权头和恢复协议需按客户端实现选择。
一次流式对话
前端提交消息
→ 业务后端认证并生成可信任务上下文
→ AI 服务验证服务身份与任务范围
→ 执行并产生约定事件
→ 业务后端代理/校验事件
→ 前端按事件类型更新界面
透传代理应避免整段读取后才返回;若需要检验事件,则采用有界解析并保留 SSE 帧边界。逐行 Scanner 示例也有行长上限、错误处理与取消传播问题,不能当完整生产代理。
应明确响应状态与 Content-Type、下游断开清理、写入失败、超时和代理缓冲配置。text、phase_changed、citation、done 等只是应用事件例子,不是 SSE 标准内置事件。没有 done 时不能仅凭连接结束推断任务成功。
多服务、模块化单体与微服务
后端多个进程经网络协作就是分布式边界,即便同仓库、同版本、同机器发布,也不能仅因此称为模块化单体。模块化单体强调单一部署单元内的清晰模块边界;前端是单独交付物并不自动改变后端的分类。
微服务通常强调围绕业务能力的独立部署与演进。固定地址、共享数据库或使用一种语言都不是充分判据。一个三组件系统可以按资源与团队需要渐进演进,无需先套标签。
数据归属与演进
AI 服务可以持有自己的任务、检索或执行状态;是否能写某类业务数据取决于明确的所有权与接口契约,不是“一切 CRUD 都禁止”。通常通过业务服务的受控能力修改其拥有的聚合,避免直接写别人的表。
当某项能力需要 GPU、独立扩缩、故障隔离或独立发布时,再评估拆分收益。消息队列本身不消除分布式事务和重复投递问题。模型边界与部署边界的关系见 限界上下文。