客服机器人从命令行 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)分块传输后,首字响应压到一秒内,体感完全不同。
几个协议层面的细节值得单独拎出来。
- 响应头设
text/event-stream,关缓存、保持长连接 - 每帧以
data:开头,两行换行符收尾,收尾时发一个[DONE]标记 - 模型吐出的增量文本必须做 JSON 转义再放进帧里。模型输出里出现引号和换行是常态,不转义直接把前端的事件解析打碎,而且这种 bug 表现为随机丢字,很难定位
- 连接建立后立刻发一个空帧。模型首 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 层。
刚开始我把模型重试逻辑写在接口处理器里,三个接口各复制了一份,后来一次超时参数调整只改了两处,漏了第三处,线上行为不一致折腾了半天。分层是给未来的自己留活路。协议层保持轻薄,业务变更就不会碰坏传输层。
踩坑清单速查
- 时间戳序列化时统一转 int64,否则 JSON 输出类型飘忽不定
- 路径参数用正则捕获组取值,下标写错会静默取到空值
- SSE 帧少了尾部换行符,前端 EventSource 解析不到事件
- 删除不存在的会话返回 404,别图省事返回 200
- 流式回调里的异常必须捕获,否则静默断流
结合系列
- 模型调用失败的重试与降级,在《多模型路由实战》里有完整方案
- 流式首字延迟的测量口径见《LLM 延迟实战》里的 TTFT、TPOT 与首字慢的排查顺序
- 会话历史越积越长之后怎么办,见《Agent 记忆实战》的截断与摘要方案
- 接口鉴权和防注入边界,参考《Agent 安全护栏实战》
写在最后。服务层没有算法难点,全是约定和细节,但这些约定直接决定前端工程师骂不骂你。把响应格式、错误语义、流式协议三件事定死,后面的接口就是在流水线上复制粘贴。
下一篇写会话服务加上多用户隔离的实践,觉得有用的话点个关注,有问题评论区聊。
参考《【AI大模型接入SDK】ChatServer接口实现》,作者 艾莉丝努力练剑。结构借鉴,实现与结论按本项目场景独立重写。