send 失败不再吃掉这一轮:模型知情 + 对话内容无条件进 Delta #106
Loading…
Add table
Add a link
Reference in a new issue
No description provided.
Delete branch "fix/95-send-failure-fallback"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Closes #95
ADR-0012 §7 断言「send 失败下一轮回灌上下文(
m7 发送失败)供模型知情」,而_send_pending是裸调用。后果比 ADR 描述的更重:_append_delta在_flush之后才跑,所以一次平台侧失败会让这一轮对话在智能体的记忆里彻底不存在 —— 既不知道发失败,也不记得说过什么。QQ 的禁言/风控/频控让这条路径是常态而非异常。三层保障
continue—— 同批其余消息照常发(AC①)run()用try/finally无条件写 Delta(AC④,本票真正的价值)m7 发送失败(AC②③)finally而不是只包发送:_run_tool_loop里还有六个裸的平台调用(recall×2 /edit/poke_send/reaction_send/like_profile),它们跑在_flush之前,抛出来照样让本轮对话一个字都不进缓冲。本文件_handle_search_stickers那段既有注释早就写过这个形状(「_run_tool_loop没有任何 except,反应式那条路是裸create_task,冒泡出去整条回复会静默消失」)。finally不吞异常 —— 原异常照常上抛,只是抛之前先把话记下来。几处刻意的设计
message_id,塞个空串进已发日志就会让self_recall/edit去撤/改一条根本不存在的平台消息;不写反而让那两个工具自然回handle not found—— 对一个从没发出去的句柄,那正是正确答案。另外这是一次性通知,混进永久账本还得再加个「已告知」标志位。list_send_failures(只读)+clear_send_failures(模型真看过、context 已进过一次complete()才清)。合成「取完即消」的话,反应式那条可被下一条消息cancel()掉的裸 task 就会让通知在「已删、模型未看到」的窗口里静默消失。MAX_PENDING_SEND_FAILURES = 20,超出丢最早的):消费方只有反应式入口,「对方把机器人拉黑了」这种 chat(发送恒失败、Drive Tick 照跑、人再也不说话)会一直攒不消。send_message发出的自然语言回复参与常规记忆压缩」;沉默窗口打断早有「组装了但没送达 → 不进 Delta」先例。)nonebot.log(框架无关,同reflection.py/subagent.py先例);「平台报了什么错」host 自己的 egress 实现比 core 清楚。core 只记事实 —— 那是它需要告诉模型的东西。except Exception刻意宽:也会接住 host egress 的 bug 与能力位错配(NotImplementedError)。把那些报成「发送失败」不算错(消息确实没出去),放它冒泡就是把这个洞原样打开。已核实PoolExhausted到不了这里(熔断只包 LLM/embedding 客户端),CancelledError是BaseException不被接住。两轴 review 抓到的(第二个 commit)
标准轴那个 agent 因安全分类服务不可用没起来;规格轴抓到四条,全部成立:
try/finally覆盖全路径。MAX_PENDING_SEND_FAILURES。顺带把打字提示那段注释里已经不再准确的理由改了(有了
finally之后它跟「记忆」无关了,真实理由是「别让装饰性调用失败连带整批回复发不出去」)。已知未做
sends=0+skipped_message_ids=整批,与「模型选择沉默」不可区分,/why会答成「它选择没理你」。加字段要改三处 + 两个行映射,属 issue #79 的地盘。验证
按 AC 提醒,刻意不写「没崩溃就算过」那种断言 —— 每条用例锁的都是实际内容:Delta 缓冲里到底有什么、下一轮 prompt 里到底有哪句话。存储契约两套实现共用同一套断言(读完即清在 SQL 侧是两条语句,写错会变成重复告知或静默消失;条数上限在 SQL 侧是 subquery + delete)。
uv run pytest -n auto→ 1501 passed(新增 30)。ruff check全过。变异复验(每次都先核实变异真写进了文件;两次锚点/语法不合格的变异被脚本挡下并重做,没有当成证据):失败改为向上抛 → 6 红;不记失败 → 2 红;上下文不加失败段 → 2 红;只在无异常时写 Delta → 1 红;先清后跑 → 1 红;去掉条数上限 → 1 红;措辞改回「上一轮」→ 1 红;打字提示不再兜住 → 1 红。还原后各文件 md5 与备份一致。