Omrylo 文章

如何设计一个简单、可演进的 AI 后端

从 Session、Agent 与 Event 的边界出发,拆解流式输出、连接管理、持久化和渐进式实现。

一个 AI 对话 Demo 很容易跑起来,但当它需要支持历史记录、流式输出、工具调用、断线重连和多个模型时,系统边界很快会变得模糊。

我的原则是先把最小闭环跑通,再让架构跟随真实问题演进。第一版不需要预先搭建复杂的 Agent 平台,但需要从一开始区分几个关键概念。

先分清 Session、Agent 与 Event

这三个标识解决的是不同生命周期。如果把它们混在一起,重试会产生重复消息,连接断开后也难以判断应该恢复哪一段执行。

  • Session 是持续存在的对话容器,保存消息历史和用户上下文。
  • Agent 是一次问题触发的执行实例,负责模型调用、工具使用和本轮状态。
  • Event 是执行过程中产生的有序事件,例如开始、文本片段、工具调用、错误和完成。

流式输出为什么通常从 SSE 开始

AI 回复主要是服务端持续向浏览器输出事件。对于这种单向、基于 HTTP 的场景,Server-Sent Events 通常比 WebSocket 更轻,也更容易穿过现有网关。浏览器原生支持重连,还可以通过 Last-Event-ID 衔接已经收到的事件。

SSE 不是唯一答案。如果产品需要高频双向通信、实时协作或复杂二进制传输,WebSocket 仍然合适。技术选择应该跟随通信模型,而不是跟随流行程度。

一个最小但清晰的接口

客户端先创建一次执行,拿到 agentId,再订阅对应事件。业务后端负责认证、上下文和模型调用;连接管理模块只维护订阅与事件分发。

Minimal API surface
POST /api/v1/agent/chat
{ sessionId, prompt } -> { agentId, status }

GET /api/v1/events/subscribe/:agentId
Accept: text/event-stream

按风险逐步增加能力

每一阶段都应该有可验证的用户体验,而不是只增加基础设施。能否稳定结束一次回答,往往比提前实现复杂的多 Agent 编排更重要。

  • V0.1:完成一次请求、一次流式返回和明确的结束状态。
  • V0.2:持久化 Session 和最终答案,支持页面刷新后恢复历史。
  • V0.3:为 Event 分配顺序号,处理重连、重复消费和超时。
  • V0.4:加入模型路由、工具调用、配额、日志和可观察性。

真正决定稳定性的工程边界

一个可演进的 AI 后端,不是组件越多越好,而是生命周期、数据归属和失败方式足够清楚。先守住这些边界,再扩展模型和 Agent 能力,系统会更容易维护。

  • 幂等:相同请求和重试不会产生无法识别的重复结果。
  • 恢复:连接中断后知道从哪个 Event 继续,而不是重新生成全部内容。
  • 持久化:流式片段用于体验,最终消息用于长期记录,两者职责分开。
  • 可观察性:能够关联 Session、Agent、Event、模型调用和错误。
  • 安全:在模型调用前完成身份、权限、输入大小和工具范围校验。