LLM ChatServer 实战 - 会话管理、SSE 流式与错误语义的接口设计(附清单)

客服机器人从命令行 demo 变成给三个人用的内部工具,中间隔着一层 HTTP 服务。模型本身早就调通了,这两天把服务层重构了一遍,精力主要消耗在把会话管理、流式输出和错误语义这些杂事做对。这篇文章把接口设计里的关键决策和踩过的坑整理出来。

先给结论

接口 方法 功能 关键点
/api/session POST 创建会话 绑定模型,返回 session_id
/api/sessions GET 会话列表 附带首条消息,方便 UI 展示
/api/session/{id}/history GET 历史消息 时间戳统一 int64
/api/session/{id} DELETE 删除会话 不存在返回 404
/api/message POST 发消息(全量) 简单场景用
/api/message/async POST 发消息(SSE 流式) 前端体验的刚需

多轮对话场景直接用流式接口,全量接口留着给后台批处理和测试用。

统一响应格式

所有接口返回同一个骨架,前端只需要写一次解析逻辑。

json 复制代码
{
  "success": true,
  "message": "create session success",
  "data": {}
}

错误语义也固定下来,不要每个接口自己发明。

状态码 含义
200 业务成功
400 请求体解析失败、字段缺失
404 session_id 不存在
500 模型调用失败等内部错误

我专门写了 build_response(message, success) 这么一个函数,所有错误响应都从它出去。一开始每个接口里手写 JSON 序列化,改一次错误格式要动七个地方,后来收拢到一处,字段增减只改一行。

流式接口是体验的生死线

全量接口下用户盯着转圈等十几秒,感觉系统死了。改用 SSE(Server-Sent Events)分块传输后,首字响应压到一秒内,体感完全不同。

几个协议层面的细节值得单独拎出来。

  1. 响应头设 text/event-stream,关缓存、保持长连接
  2. 每帧以 data: 开头,两行换行符收尾,收尾时发一个 [DONE] 标记
  3. 模型吐出的增量文本必须做 JSON 转义再放进帧里。模型输出里出现引号和换行是常态,不转义直接把前端的事件解析打碎,而且这种 bug 表现为随机丢字,很难定位
  4. 连接建立后立刻发一个空帧。模型首 token 之前有一段静默期,不发空帧某些代理和网关会判定连接超时掐掉
python 复制代码
async def stream_chat(session_id: str, message: str):
    yield sse_frame("")          # 先发空帧防网关超时
    async for chunk in llm.call_stream(session_id, message):
        yield sse_frame(json.dumps({"delta": chunk}))
    yield sse_frame("[DONE]")

另外踩过一个隐蔽的坑。流式回调函数返回 false 时,底层直接断流,后半段内容全部丢失且没有任何报错。回调里的异常处理一定要包住,转成日志而不是让它炸穿传输层。

分层

协议归协议,业务归业务。服务层只做三件事,解析请求、校验参数、组织响应。会话管理和模型调用全部下沉到 SDK 层。

刚开始我把模型重试逻辑写在接口处理器里,三个接口各复制了一份,后来一次超时参数调整只改了两处,漏了第三处,线上行为不一致折腾了半天。分层是给未来的自己留活路。协议层保持轻薄,业务变更就不会碰坏传输层。

踩坑清单速查

  1. 时间戳序列化时统一转 int64,否则 JSON 输出类型飘忽不定
  2. 路径参数用正则捕获组取值,下标写错会静默取到空值
  3. SSE 帧少了尾部换行符,前端 EventSource 解析不到事件
  4. 删除不存在的会话返回 404,别图省事返回 200
  5. 流式回调里的异常必须捕获,否则静默断流

结合系列

  • 模型调用失败的重试与降级,在《多模型路由实战》里有完整方案
  • 流式首字延迟的测量口径见《LLM 延迟实战》里的 TTFT、TPOT 与首字慢的排查顺序
  • 会话历史越积越长之后怎么办,见《Agent 记忆实战》的截断与摘要方案
  • 接口鉴权和防注入边界,参考《Agent 安全护栏实战》

写在最后。服务层没有算法难点,全是约定和细节,但这些约定直接决定前端工程师骂不骂你。把响应格式、错误语义、流式协议三件事定死,后面的接口就是在流水线上复制粘贴。

下一篇写会话服务加上多用户隔离的实践,觉得有用的话点个关注,有问题评论区聊。

参考《【AI大模型接入SDK】ChatServer接口实现》,作者 艾莉丝努力练剑。结构借鉴,实现与结论按本项目场景独立重写。

相关推荐
火山引擎开发者社区1 小时前
接大模型别再自己配 Key、搭代理了,火山引擎 IGA Pages 一步搞定
llm
liferecords8 小时前
不训练一个参数,把图像编辑器变成视频编辑器:EditVid 的局部-全局拆解术
人工智能·llm·aigc·多模态
装不满的克莱因瓶8 小时前
【SpringAI Alibaba】ReactAgent响应SSE实时流式传输案例实战
java·人工智能·ai·sse·springai
泯泷21 小时前
为什么你的 AI Agent“越改越差”?Anthropic 总结了 8 步评测方法
llm·agent·claude
goehou1 天前
Ollama 的流式接口不是 SSE,客户端要改三处
sse·ollama·流式输出·客户端开发
DigitalOcean1 天前
Codex 如何接入 Claude 等其它模型?使用 CC Switch 接入 FluxLane API 教程
llm·agent
泯泷1 天前
做 AI 产品,路线图该数“实验”,而不是数“功能”
人工智能·llm·产品
火山引擎开发者社区1 天前
OpenVIking 连接各种数据源,一键编译 LLM Wiki
llm
吴凡wudianmei1 天前
从0搭AI Agent(六):什么时候该把任务拆给多个 Agent
langchain·llm·ai编程
愤怒的代码1 天前
上下文窗口:模型的短期记忆
人工智能·llm·agent