情感态energy昼夜节律nudge #124
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 条待拍板(条目 9、19)
What to build
ADR-0005/0017 拍板:快情感态 energy 方程要新增一项"昼夜节律 nudge",深夜时段给该项额外向下的值——"深夜"必须经 nudge 写进 energy 本身,已读延迟窗这个新 gate 绝不自己读墙钟,保"情感态是所有拟人参数唯一中央调节器"的边界不被绕过。这是本仓罕见的"裁决理由被原文逐字完整支撑"的一条,但产出方在 origin/main 上完全不存在(只有两处 docstring 说"这不是我")。
energy 今天已经有三个活着的消费方(门控阈值、打字节奏、mood_congruence),昼夜节律 nudge 落地当天就有可观测行为,不必等已读延迟窗片先上线。
Acceptance criteria
circadian_energy_nudge(now)(时钟经参数注入,可性质测试),形状仿temporal_awareness.py的时段分桶函数原文(docs/adr/0017-reactive-timing-and-coverage.md「决策 → 已读延迟窗」节):
原文(同 ADR「拒绝了」节——点名否决的替代方案):
原文(docs/adr/0005-affective-state.md「更新(2026-07-06 已读延迟窗,见 ADR-0017)」节,方程原文):
update_fast_state的三轴衰减是调用驱动的(不看经过时间)。新建一个"每 N 分钟施加昼夜节律 nudge"的周期驱动器直接调update_fast_state,会给 valence 和 dominance 各加一个白挨的衰减驱动源——正是 #94 明文防的事。片内二选一并写进 AC:delta._apply_interaction_polarity的既有手法:算完只取 energy(replace(fast_record.state, energy=updated.energy)),另外两轴原样保留;或/why的快照语义要跟着定,须在 ADR-0005 追加更新节_drive_tick/run_delta_compression_cycle的车"这个陷阱——两者都有各自的触发条件过滤,搭车会让 nudge 在无 delta / 无到期 chat 时不发生,达不到"按时间自然演进"的效果temporal_awareness.py的时段分桶(深夜/清晨/白天/晚上)可复用,但不能让它变成"驱动数值方程"——它的模块契约明写"不驱动任何数值方程",与本条是两件不同的事,互不冲突,不要合并实现datetime.now()/墙钟读取——归属边界不能只靠代码审查Blocked by
None — can start immediately
2026-08-14 裁决(评估侧):方案 (b) 读时施加,撤掉 #123 的局部机制
诊断:
read_delay.py::circadian_energy_adjustment不是"另一种设计",是 ADR-0017「拒绝了」节点名否决的那个形状:该函数自己的 docstring 论证"gate 不单独读墙钟"落在调用方——把"gate"缩小到只剩
read_delay_window这个纯 sleep 协程,从而字面不违反;但它读datetime.now()、算出的调整量只喂给entry_reactive._debounced_flush自己用,gate_pipeline/runtime_loop.py两处(打字节奏、mood_congruence)完全看不到——这正是被否决方案的实质:"新 gate 与 energy 并列各自读时钟",只是技术上绕开了字面否决。且它长在read_delay.py(消费方),不在affective_state.py(energy 真正的归属地)——机制归属从一开始就点错了模块。裁决:撤掉
circadian_energy_adjustment在read_delay.py里的本体和调用,改成方案 (b)——读时施加,四个消费点共用同一个 helper。具体形状
两者都搬进
affective_state.py(该文件已有AffectiveStateStoragePort,是这个决定的天然归属地):replace(state, energy=...)不是新手法——是_apply_interaction_polarity那条"快层只写 energy/valence 轴,其它轴原样保留"先例(issue #94,ADR-0005"更新 2026-07-08"节),只是从写时挪到读时。四个消费点全部改走这个 helper,不再各自
get_fast_affective_state(...).state/.state.energy:gate_pipeline.py:192resolved_affective_state = fast_record.state(要整个 state)resolved_affective_state = await get_effective_fast_state(ports.storage, chat_id)runtime_loop.py:568(_scored_events,mood_congruence)current_mood = fast_record.state(要整个 state)current_mood = await get_effective_fast_state(self._storage, chat_id)runtime_loop.py:1971(打字节奏)fast_energy = fast_record.state.energy(只要 float)fast_energy = (await get_effective_fast_state(self._storage, chat_id)).energyentry_reactive.py:429(已读延迟)energy = fast_record.state.energy + circadian_energy_adjustment(...)energy = (await get_effective_fast_state(ports.storage, chat_id)).energy#123 留下的三个 config 项(
arise_circadian_energy_nudge_start_hour/end_hour/amount)原样复用,不用碰config.py。选 (b) 不选 (a) 的理由(不只是"改动面小")
当前时段是全局时钟事实,不是逐 chat 状态。(a) 必须新建周期任务扫全部已知 chat(storage 现在没有这个枚举能力)才能保证"随便哪个 chat 被读到时节律值都是新的",本质是用持久化去伪造一个本来免费的计算,且要小心不能搭
_drive_tick/delta 压缩周期的车(否则闲置 chat 永远不生效,#94 已经因为同一类"意外多驱动一次衰减"的陷阱吃过亏)。(b) 只在真正被读的那一刻算,没有"还没扫到这个 chat"的陈旧窗口,且是"端口/客户端/回调/registry 不进 config,标量计算天然是读时"这条更简单的默认形态。顺手钉住两处(AC 原文没展开,实现期请一并处理)
/why决策快照要记的是get_effective_fast_state算出来的修正值,不是原始存储值,否则解释和实际门控用的值对不上(ADR-0010 可解释性)。read_delay.py路径内不出现datetime.now(),顺手断言该文件不再import datetime——归属边界更彻底可查。ADR-0005 更新节要写什么
energy从"纯存储值"变成"存储值 + 读时修正(get_effective_fast_state)"。affective_state.py)。circadian_energy_adjustment/#123 的局部版本已废弃、被本次取代。