self_recall/edit平台撤回窗口+per-chat速率预算 #115
Loading…
Add table
Add a link
Reference in a new issue
No description provided.
Delete branch "%!s()"
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?
Parent
#103 第四批清单:31 条实现类落差 + 9 条待拍板(条目 17、18;顺带吸收一处同型未归宿的缺口)
What to build
ADR-0012 的"自撤/改口"机制默认 ON,其安全性建立在两根支柱上:硬约束(chat_id 归属校验,已由 issue #102 补上)与速率预算(per-chat 撤回/编辑动作速率上限)。速率预算这一根从未落地:模型在同一次 assistant 回合里可以批量发上百个
self_recall调用,三道既有上限(self_recall≤1/edit≤N/MAX_SENDS_PER_TURN)一道不触发,run_limit也不扣(SELF_RECALL/EDIT都在RUN_LIMIT_EXEMPT_TOOL_NAMES里,且计数按轮不按调用),core 会老实执行上百次egress.recall,撞上 QQ 撤回频控。同时,"平台撤回窗口"这半也从未落地——超过平台撤回窗口(QQ/OneBot v11 是 2 分钟)的句柄仍会被尝试
delete_msg。这依赖 Sent Log 补上时间字段,而 ADR-0012 §7 早已把这个字段定义为"既定后果",不是一个外部前置。两者落在同样两个函数(
_handle_self_recall/_handle_edit)、同样两道闸位(都必须在能力位分叉之前),同片做。Acceptance criteria
一、Sent Log 加时间字段(两者共同的基建前提,本片自带,不等外部票)
sent_log.SentLogEntry新增时间字段(建议sent_at: datetime,避免与DeltaEntry.time/EventRecord.time混淆)storage.SentLogModel加对应列;sent_log.StoragePort.record()协议签名、内存实现SentLog.record()、持久化实现PersistentStorage.record()与_to_entry()均适配;唯一写入点RuntimeLoop._flush()补传时间migrations/versions/只有__init__.py,真实 schema 由 host 迁移链管理),加可空列不需要在本仓补迁移文件sent_at=None被静默判成超窗口或静默放行)原文(docs/adr/0012-self-message-control-and-tool-driven-output.md「决策 → 7. 基础设施(既定后果)」):
二、平台撤回窗口硬约束
_handle_self_recall/_handle_edit拒绝已超出平台撤回窗口的句柄,窗口值进静态 configif handle in state.pending那条分支是 §1「纯 core、无平台调用、总是可用、零风控」,窗口闸只管已发路径"handle not found or already recalled"风格返回错误串;模型的退路是改用普通send_message发纠正文本(ADR §5 已指定)原文(docs/adr/0012「决策 → 2. 触发」节):
原文(同 ADR「决策 → 5. 编辑降级」节):
三、per-chat 动作速率预算
SilenceBudgetState/refill_silence_budget令牌桶先例:新 dataclass + 纯函数 + 存储 port 切片(内存 + 持久化两个实现 + 一张新表)render_tool_result(call.id, error=...)风格给稳定错误串),fail-closed 零平台调用,模型"不带 recall 继续"_handle_edit的降级路径(无原生 edit → recall+resend)第一步就是egress.recall,它也要过这道闸,且闸位与既有归属闸同侧(能力位分叉之前)egress调用次数为零(不是只断言"没有输出结果"——那在多种原因下都成立)原文(docs/adr/0012「决策 → 6. 治理:默认 ON + 速率预算兜底」节):
四、顺带吸收:
send_message的quote_message_id归属校验缺口issue #102 只补了
_handle_self_recall/_handle_edit两处的 chat_id 归属校验,send_message的quote_message_id参数(模型直给的平台 id、不经 Sent Log)没有同类校验,且该缺口目前在 #103/#101/#108 全文零命中、没有任何票认领。与本片同属"句柄/引用跨 chat 误用"这一类问题,顺带一起补:quote_message_id校验其所引用消息确属当前 chat,跨 chat 引用时按既有能力门控"无该能力则退化为不带引用的普通发送"的降级模式处理,不报错阻断对话Not in scope
self_recall/edit记决策快照"——已由 #101 留痕第 8 条认领并缓做(DecisionSnapshot 按"一轮触发"建模,塞进去要动数据形状),不在本片顺带实现Blocked by
None — can start immediately