AI 个人计划助手的重试与恢复设计
1. 前言
最近做了一个AI个人计划助手,整体架构自己是比较清晰了,但是有一些可靠性设计还是记录一下。
2. 哪些环节会失败,系统如何处理?
| 环节 | 失败场景 | 当前处理 |
|---|---|---|
| 微信 → Java Webhook | Java 已收到消息,但微信没有收到回调响应,于是再次推送同一消息 | 根据消息标识生成的 message_key 查询 inbound_message;若已处理,直接返回上次保存的 response_text,不再收集或提交一次 |
| Java → Python Agent:请求报错 | Agent 不可用、模型调用失败,或 Java 等待 Agent 响应超时 | BatchProcessor 捕获异常;未达到处理次数上限时,将批次从 RUNNING 改回 SUBMITTED,下一轮重新调用 Agent。当前是最多领取处理 3 次,不是“额外重试 3 次” |
| Java 批次 Worker:进程中断 | 批次已处于 RUNNING,但进程退出,来不及执行异常处理 |
批次保留 RUNNING;15 分钟租约到期后,后台任务可以重新领取同一个批次。租约解决的是“任务卡住没人接手”,不是普通 HTTP 超时的计时器 |
| 批次重新调用 Agent | 首次分析后已执行部分操作,重试时若重新分析,动作内容或顺序可能变化 | 首次模型分析在执行工具前写入 analysis_json;同一批次重跑 /agent/runs 时优先读取它,沿用原动作清单 |
| Python Agent → Java 写入工具 | Java 已提交计划,但工具调用的 HTTP 返回丢失,Python 不知道是否成功 | Python 本次处理失败后,由 Java 重新处理整个批次。再次执行到同一动作时,Java 根据相同的 operation_key 和参数,从 operation_result 返回原结果,不重复写库 |
| 追问回答 → Agent | 用户已经回答,但 /agent/resume 处理失败 |
原批次仍保存 clarification_json 和 clarification_answer;Java 重新领取后再次调用 /agent/resume,从待处理动作继续。已完成的写操作仍由 operation_result 去重 |
| 语音消息 → 转写服务 | 下载语音或腾讯云 ASR 调用失败 | 语音记录保持可重试状态,转写 Worker 最多尝试 3 次;转写期间,批次等待识别结果,不把空白语音交给 Agent 猜测。最终识别失败则告知用户 |
| Java → 微信普通回复 | 处理结果已生成,但发送客服消息失败 | 先将回复写入 outbound_message。可确定需要重试的异常会有限次重发;微信明确拒绝标为 FAILED;网络中断等无法判断是否已送达的情况标为 UNKNOWN,不自动重发 |
| 提醒任务 → 微信模板消息 | 到期提醒发送失败 | task.reminder_status 保持 PENDING,下次扫描再次尝试。提醒目前直接调用微信接口,没有使用普通回复的 outbound_message 机制 |
3. 为什么重试时要读取 analysis_json?
假设同一批次 B 包含两句话:“周三开会,周四交报告”。
首次分析得到固定的动作顺序:
动作 1:创建“周三开会” → operation_key = B:create:1
动作 2:创建“周四交报告” → operation_key = B:create:2
系统先保存这份分析,再执行工具。假设动作 1 已经写库成功,动作 2 执行前 Agent 报错。Java 随后重新调用 /agent/runs:
- Python 的
analyze节点查到批次B已有analysis_json,直接读取原动作顺序。 - 重新调用动作 1;Java 查到
B:create:1已成功,返回上次的结果,不再创建第二条“周三开会”。 - 执行动作 2;
B:create:2尚无成功记录,于是创建“周四交报告”。
如果没有缓存分析,第二次模型可能调换两个动作的顺序。此时“周四交报告”拿到已属于“周三开会”的 B:create:1,操作键不一样了,你原来的操作键已经存在,但是对应的数据库操作不一样啊。
因此,analysis_json 保证重试沿用同一份动作清单;operation_result 保证清单中已完成的数据库修改不重复执行。首次分析尚未保存就失败时,可以重新分析,因为此时还没有开始执行这些动作。
追问使用的是另一份状态:clarification_json 记录等待回答的动作、位置和之前的执行结果。用户回答后走 /agent/resume,不会把回答当成一批全新的指令重新分析。
4. 部分通俗解释:
- 微信 → Java 回调:用户在公众号发了一条消息,微信会推送到你的服务器,你的服务器要应答,这个过程中,如果微信没有收到应答,会重新推送一次。那如果实际上你收到了,只是应答过程断了或者怎么样,微信的重复发送会让你收到两条一样的内容,如果你有的业务操作不能说去重复执行的话,那就会有问题,所以这里可以根据微信推送消息自带的key来校验去重,防止重复收到回调消息。
- Java → Python Agent:java在调用agent应用时,agent执行过程中报错,或者没有报错但是一直运行没结束,比如调用模型什么的要很久或者agent循环思考等,这样会一直卡着没有处理完,用户那边也长时间收不到反馈,所以这里会判断交给agent处理的时间窗口,如果超过的预设的时间,那么会设置消息 将
RUNNING批次改回SUBMITTED,后续的Java可以重新调用agent执行处理这个批次的消息。 - Python Agent → Java 写入工具:agent调用java工具执行力数据库操作,java工具执行情况需要返回给agent,如果agent没有收到,会重新执行,那么这时候就会有Python Agent → Java 写入工具的重试。可是如果实际写入成功,只是java工具的反馈没有传到agent呢?那这样就会出现重复执行数据库操作的情况,所以我们会给数据库操作绑定一个key:包含消息批次id、数据库操作、消息序号id。这样可以去重同样的数据库操作。
- Java → 微信客服消息:最终回复由java发给微信用户,这个过程也可能出错导致用户接收不到,所以我们会先把要回复用户的信息写库并维持状态,保证消息一定正确发送给微信。
浙公网安备 33010602011771号