Delta 压缩容错 + 互动极性作为 valence 产出方 #99
Loading…
Add table
Add a link
Reference in a new issue
No description provided.
Delete branch "fix/94-delta-resilience-and-valence"
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 #94
两件事必须同票:产出方要挂在 Delta 压缩那次 LLM 调用上,而那正是会丢数据的那条路径 —— 先落容错,再挂产出方。
A. 压缩失败不再丢原文
drain_delta是 select → delete → commit → return,删除先于压缩;而delta.py此前全文零try、llm_client.complete是裸调用。一次 LLM 抛异常 = 该 chat 本批对话原文永久消失,不需要 host、不需要真实流量。降级方式是原样写回 Delta 缓冲(AC 明文允许的等价做法):原文回到它本来的位置,
list_chats_with_pending_delta下一轮自然重新捡起来 —— ADR-0009 要的「标记待重试 + 降级状态能被自愈机制探测」由此天然满足,不新增状态或字段。try罩住整段 per-chat 处理而不只是compress():写事件要调 embedding、写 Knowledge 要调judge_persona_drift,任何一处抛出的后果都一样。熔断单独接(PoolExhausted→ 写回 +break):__init__.py在 cycle 开始前就查过闸,中途熔断时让剩下每个 chat 各撞一次只会得到 N 份空转和 N 条 traceback。代价已在代码里逐条写清,不是零:
record_trust_disclosure增益会重复施加、极性会再推一次 valence(极性更新在写库段之前);写回是 append 到队尾而 drain 按 id 排序,失败期间新到的消息会让重试批时序错乱;失败若源于内容本身(上下文超长/内容过滤)则无限重试、缓冲只涨不消 —— ADR-0009 为此设计的下一档降级(连续失败转低置信 tentative)本票没做,那需要跨重启的失败计数(新列 + schema 变更),是独立决定。B. valence 三层不再恒为 0
ADR-0005/CONTEXT.md 断言
valence += f(互动极性) − k_fast·(valence − slow_valence),但f(互动极性)从来没有产出方 ——valence_raw_delta在整个src/里只出现在affective_state.py自己的签名与函数体内。三层全恒 0,下游三处全是死码。抽取 schema 新增两个搭便车字段(零额外 LLM 调用,不碰前台回复路径):
interaction_polarity(整批 → 快层)+speaker_polarity(逐发言人 → 慢层 per-user)。与 AC 的两处偏离(均已征询用户并获同意)
update_fast_state调用点」,实际新增了第三个调用点(在 cycle 内)。那两个现存调用点是环境信号 tier-1 分支与反思闭环,都跑不到普通对话上 —— 纯聊天的 chat 永远等不到它们触发,按字面接等于产出方对最常见的场景不生效。三处刻意的设计
update_fast_state让三轴各自向慢层衰减一步,而这一步是调用驱动的;新增一个调用源若顺带推动另外两轴,等于给 energy/dominance 加了一个「每个压缩周期多衰减一次」的驱动源。那两轴不是本票的事(issue #94 明说 energy 维一直有效)。FastAffectiveStateRecord的文档把last_stimulus_label定义为互动极性刺激分类的槽位(ADR-0005),指定主人正是本票这个产出方;但实际占着它的是 tier-1 环境信号的 energy 轴判定。本票不收回槽位(那要动 tier-1 的抗刷行为,是独立决定),改依赖 300 秒调度频率兜住,并原样保留那对字段不覆盖 tier-1 的状态。annoyed端到端 —— AC 点名「最容易假通过的地方」test_reflection_cycle.py里已有一条产出annoyed的用例,但它是手写update_fast_affective_state(valence=-0.2)造出跌幅的;在接上产出方之前生产代码造不出这个跌幅,所以那条证明的是「分类器纯函数没写错」,不是「这条链在真实系统里通」。新增
test_annoyed_end_to_end.py:全程用生产默认值,跌幅由真实产出方造出,断言反思 LLM 收到的情况描述是「对方回应了,但情感态明显变差」—— offset 的正负分不出annoyed与fell-flat,不能拿它当结局证据。另带两条反向对照(暖场 / 中性都必须判landed)。守卫
src/:确认真的有一处以非零valence_raw_delta调过情感态更新(#94 之前会红)。已知局限:它防「被摘掉」有效,防「被阉掉」(改成0.0 * polarity)无效 —— 那靠上面那些行为用例。arise_valence_polarity_scale默认值必须大于annoyed判定阈值,否则单次强负面互动推不出annoyed,三态分类器实际仍只跑两态。AC6:valence 维真的参与排序
test_the_valence_dimension_now_changes_the_ranking把两条由真实产出方打上不同 valence 标签的事件放进同一次真实打分:负心情下负标签那条的mood_congruence必须更高(此前两条标签都是 0,这一维贡献相同、对排序零影响)。顺带让recall那道负性护栏真的触发一次。两轴 review 抓到的(第二个 commit)
ValenceProducerConfig,同姊妹 cycle 的ReflectionCycleConfig先例,一致性守卫随之删除。关于
Not in scope「未获回应升级」与
send失败兜底完全未触碰。_compress_one_chat的提取是这次修复自己需要的局部提取(为了用一个try罩住整段而不让 60 行循环体再深一层缩进),不是 Not in scope 说的那种结构调整(拆__init__.py、抽StoragePort在结构批 #97)。验证
uv run pytest -n auto连跑两次 1471 passed(新增 27)。ruff check全过。变异复验(每次都先核实变异真写进了文件):去掉原文写回 → 4 红;产出方置零 → 8 红;快层不再保留 energy/dominance → 1 红;去掉 per-group 跟随 → 1 红。还原后 md5 一致、sentinel 残留 0。