一个典型的澄清模块设计:
前置守护
-
Pod级信号量限流判断:它防止单个 Pod 同时处理过多请求而被压垮
-
Redis SETNX 幂等守卫:
因为 Redis 是共享的,所以即使两个相同请求落到不同 Pod,守卫也是生效的。
- 首次请求:通过 SETNX 写入带 TTL的PROCESSING,写入成功后进入业务流程,并在业务流程中不断更新TTL。
- 重复请求:只要状态为 PROCESSING 或 DONE,均判定为重复并拒绝。
- 执行成功:将状态更新为 DONE,并设置去重窗口 TTL;TTL 到期前,相同 reqId 仍会被拒绝。
- 执行失败或取消:删除 Redis 记录,释放幂等占位,允许相同请求再次重试。
上下文聚合
拉取工作记忆、短期记忆、情景记忆、程序性记忆。
主路记忆写入
首先,在任务执行流程中,先记录用户当前问题,答案留空。
这样即使后台处理较慢,用户问题也可以先被记录。后续通过 reqid 精确找到该记录并补全回答。
Agent Loop
闲聊/任务 二分类判定,只加载短期会话记忆,如果是任务记忆,则加载全量上下文,进入任务分支判定。
reply:直接回答
clarify:要求澄清
task:进入分支路由,任务管理(创建即时/定时、更新、删除、作为代理发给下游Excutor)分支。
流式返回
服务端需要把编排结果转换成标准SSE 帧,包括
Processing:中间结果,SSE 名称为 processing
Complete:最终结果或错误结果,名称为 complete
SSE 基于 HTTP 长连接,服务端写数据到 response body,TCP 层面保证数据包送达对端操作系统——但应用层(浏览器/客户端代码)是否成功读取、解析、渲染,后端不知道。
工程上的补救方案:
| 方案 | 做法 | 适用场景 |
|---|---|---|
| TCP 层面兜底 | 连接断开 → 后端 write() 抛异常 → 知道客户端已离开 |
断连检测(最低限度) |
| 客户端 ACK(应用层确认) | 前端收到 Complete 后,单独发一个 HTTP POST 告知”已收到完整结果” |
需要可靠交付的场景 |
| Event ID + Last-Event-ID | SSE 规范自带:服务端每条消息带 id:,客户端重连时带 Last-Event-ID 头,告诉服务端”上次收到哪了”,服务端从断点续推 |
断线重连、不丢消息 |
| 双向通道兜底 | 不用纯 SSE,改用 WebSocket,客户端收到后回一个 ACK 帧 | 需要双向确认的实时场景 |
旁路记忆回写
前请求的 SSE 流已经结束了,更新任务执行轨迹。
优先保证用户请求和 SSE 可用,记忆只做旁路增强;记忆失败最多导致后续上下文缺失,不让本次请求失败。