liquid-loop 2.0.0__tar.gz → 2.0.2__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (76) hide show
  1. {liquid_loop-2.0.0 → liquid_loop-2.0.2}/PKG-INFO +2 -2
  2. {liquid_loop-2.0.0 → liquid_loop-2.0.2}/README.md +1 -1
  3. {liquid_loop-2.0.0 → liquid_loop-2.0.2}/liquid_loop/__init__.py +5 -4
  4. liquid_loop-2.0.2/liquid_loop/dsh_reversible.py +184 -0
  5. liquid_loop-2.0.2/liquid_loop/guard.py +324 -0
  6. liquid_loop-2.0.2/liquid_loop/recall_multi_key.py +150 -0
  7. {liquid_loop-2.0.0 → liquid_loop-2.0.2}/liquid_loop/selfspin.py +41 -6
  8. {liquid_loop-2.0.0 → liquid_loop-2.0.2}/liquid_loop/storage.py +118 -1
  9. {liquid_loop-2.0.0 → liquid_loop-2.0.2}/liquid_loop/workspace.py +23 -4
  10. {liquid_loop-2.0.0 → liquid_loop-2.0.2}/liquid_loop.egg-info/PKG-INFO +2 -2
  11. {liquid_loop-2.0.0 → liquid_loop-2.0.2}/liquid_loop.egg-info/SOURCES.txt +5 -0
  12. {liquid_loop-2.0.0 → liquid_loop-2.0.2}/pyproject.toml +1 -1
  13. {liquid_loop-2.0.0 → liquid_loop-2.0.2}/tests/test_attention_gain.py +7 -3
  14. liquid_loop-2.0.2/tests/test_guard_og.py +48 -0
  15. liquid_loop-2.0.2/tests/test_save_regression_guard.py +78 -0
  16. liquid_loop-2.0.2/tests/test_undo_snapshot.py +66 -0
  17. liquid_loop-2.0.0/liquid_loop/guard.py +0 -174
  18. {liquid_loop-2.0.0 → liquid_loop-2.0.2}/LICENSE +0 -0
  19. {liquid_loop-2.0.0 → liquid_loop-2.0.2}/liquid_loop/__main__.py +0 -0
  20. {liquid_loop-2.0.0 → liquid_loop-2.0.2}/liquid_loop/audit.py +0 -0
  21. {liquid_loop-2.0.0 → liquid_loop-2.0.2}/liquid_loop/cli.py +0 -0
  22. {liquid_loop-2.0.0 → liquid_loop-2.0.2}/liquid_loop/cognitive_budget.py +0 -0
  23. {liquid_loop-2.0.0 → liquid_loop-2.0.2}/liquid_loop/context_compress.py +0 -0
  24. {liquid_loop-2.0.0 → liquid_loop-2.0.2}/liquid_loop/cpe.py +0 -0
  25. {liquid_loop-2.0.0 → liquid_loop-2.0.2}/liquid_loop/entropy.py +0 -0
  26. {liquid_loop-2.0.0 → liquid_loop-2.0.2}/liquid_loop/liquid_reweight.py +0 -0
  27. {liquid_loop-2.0.0 → liquid_loop-2.0.2}/liquid_loop/procedural_memory.py +0 -0
  28. {liquid_loop-2.0.0 → liquid_loop-2.0.2}/liquid_loop/rar.py +0 -0
  29. {liquid_loop-2.0.0 → liquid_loop-2.0.2}/liquid_loop/recall_filter.py +0 -0
  30. {liquid_loop-2.0.0 → liquid_loop-2.0.2}/liquid_loop/self_eval.py +0 -0
  31. {liquid_loop-2.0.0 → liquid_loop-2.0.2}/liquid_loop/self_refine.py +0 -0
  32. {liquid_loop-2.0.0 → liquid_loop-2.0.2}/liquid_loop/session.py +0 -0
  33. {liquid_loop-2.0.0 → liquid_loop-2.0.2}/liquid_loop/textutil.py +0 -0
  34. {liquid_loop-2.0.0 → liquid_loop-2.0.2}/liquid_loop.egg-info/dependency_links.txt +0 -0
  35. {liquid_loop-2.0.0 → liquid_loop-2.0.2}/liquid_loop.egg-info/entry_points.txt +0 -0
  36. {liquid_loop-2.0.0 → liquid_loop-2.0.2}/liquid_loop.egg-info/requires.txt +0 -0
  37. {liquid_loop-2.0.0 → liquid_loop-2.0.2}/liquid_loop.egg-info/top_level.txt +0 -0
  38. {liquid_loop-2.0.0 → liquid_loop-2.0.2}/liquid_loop_exp/__init__.py +0 -0
  39. {liquid_loop-2.0.0 → liquid_loop-2.0.2}/liquid_loop_exp/error_loop/__init__.py +0 -0
  40. {liquid_loop-2.0.0 → liquid_loop-2.0.2}/liquid_loop_exp/error_loop/low_stability_recall.py +0 -0
  41. {liquid_loop-2.0.0 → liquid_loop-2.0.2}/liquid_loop_exp/liquid_persist_ab.py +0 -0
  42. {liquid_loop-2.0.0 → liquid_loop-2.0.2}/liquid_loop_exp/liquid_recall_ab.py +0 -0
  43. {liquid_loop-2.0.0 → liquid_loop-2.0.2}/liquid_loop_exp/multigran/__init__.py +0 -0
  44. {liquid_loop-2.0.0 → liquid_loop-2.0.2}/liquid_loop_exp/multigran/ab_multigran.py +0 -0
  45. {liquid_loop-2.0.0 → liquid_loop-2.0.2}/liquid_loop_exp/multigran/hard_ab_multigran.py +0 -0
  46. {liquid_loop-2.0.0 → liquid_loop-2.0.2}/liquid_loop_exp/multigran/multigran_core.py +0 -0
  47. {liquid_loop-2.0.0 → liquid_loop-2.0.2}/liquid_loop_exp/multigran/sim_structured.py +0 -0
  48. {liquid_loop-2.0.0 → liquid_loop-2.0.2}/liquid_loop_exp/multigran/test_multigran.py +0 -0
  49. {liquid_loop-2.0.0 → liquid_loop-2.0.2}/liquid_loop_exp/r16_repro/__init__.py +0 -0
  50. {liquid_loop-2.0.0 → liquid_loop-2.0.2}/liquid_loop_exp/r16_repro/compare_rar.py +0 -0
  51. {liquid_loop-2.0.0 → liquid_loop-2.0.2}/liquid_loop_exp/r16_repro/r16_repro.py +0 -0
  52. {liquid_loop-2.0.0 → liquid_loop-2.0.2}/liquid_loop_exp/skill_refine/__init__.py +0 -0
  53. {liquid_loop-2.0.0 → liquid_loop-2.0.2}/liquid_loop_exp/skill_refine/skill_refine_loop.py +0 -0
  54. {liquid_loop-2.0.0 → liquid_loop-2.0.2}/setup.cfg +0 -0
  55. {liquid_loop-2.0.0 → liquid_loop-2.0.2}/tests/test_auth_guard.py +0 -0
  56. {liquid_loop-2.0.0 → liquid_loop-2.0.2}/tests/test_cli_version.py +0 -0
  57. {liquid_loop-2.0.0 → liquid_loop-2.0.2}/tests/test_consensus_expansion.py +0 -0
  58. {liquid_loop-2.0.0 → liquid_loop-2.0.2}/tests/test_context_compress.py +0 -0
  59. {liquid_loop-2.0.0 → liquid_loop-2.0.2}/tests/test_entropy.py +0 -0
  60. {liquid_loop-2.0.0 → liquid_loop-2.0.2}/tests/test_guard_session_recall.py +0 -0
  61. {liquid_loop-2.0.0 → liquid_loop-2.0.2}/tests/test_layer1_persistence.py +0 -0
  62. {liquid_loop-2.0.0 → liquid_loop-2.0.2}/tests/test_lifecycle.py +0 -0
  63. {liquid_loop-2.0.0 → liquid_loop-2.0.2}/tests/test_liquid_reweight.py +0 -0
  64. {liquid_loop-2.0.0 → liquid_loop-2.0.2}/tests/test_nucleate_dual_track.py +0 -0
  65. {liquid_loop-2.0.0 → liquid_loop-2.0.2}/tests/test_peek_seal.py +0 -0
  66. {liquid_loop-2.0.0 → liquid_loop-2.0.2}/tests/test_perception_gate.py +0 -0
  67. {liquid_loop-2.0.0 → liquid_loop-2.0.2}/tests/test_procedural_memory.py +0 -0
  68. {liquid_loop-2.0.0 → liquid_loop-2.0.2}/tests/test_rar.py +0 -0
  69. {liquid_loop-2.0.0 → liquid_loop-2.0.2}/tests/test_recall_filter.py +0 -0
  70. {liquid_loop-2.0.0 → liquid_loop-2.0.2}/tests/test_replay_pressure.py +0 -0
  71. {liquid_loop-2.0.0 → liquid_loop-2.0.2}/tests/test_seal_persistence.py +0 -0
  72. {liquid_loop-2.0.0 → liquid_loop-2.0.2}/tests/test_self_eval.py +0 -0
  73. {liquid_loop-2.0.0 → liquid_loop-2.0.2}/tests/test_self_evolve.py +0 -0
  74. {liquid_loop-2.0.0 → liquid_loop-2.0.2}/tests/test_selfspin.py +0 -0
  75. {liquid_loop-2.0.0 → liquid_loop-2.0.2}/tests/test_semantica_borrow.py +0 -0
  76. {liquid_loop-2.0.0 → liquid_loop-2.0.2}/tests/test_workspace.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: liquid-loop
3
- Version: 2.0.0
3
+ Version: 2.0.2
4
4
  Summary: Self-Organizing Cognitive Memory for AI Agents — Liquid Loop theory implementation
5
5
  Author: fishbook0001
6
6
  Maintainer: fishbook0001
@@ -43,7 +43,7 @@ Dynamic: license-file
43
43
 
44
44
  > **Self-Organizing Cognitive Memory for AI Agents** — Zero LLM dependency, pure Python implementation of the Liquid Loop theory.
45
45
 
46
- > **当前包版本:`2.0.0`**(2026-08-24 统一版号里程碑,详见 [CHANGELOG.md](CHANGELOG.md)。数据 schema `0.4.0` 与 workspace state `0.5.1` 为记忆层数据格式版本,独立于包发布版本)。
46
+ > **当前包版本:`2.0.1`**(2026-08-25 零向量召回超越词频基线,详见 [CHANGELOG.md](CHANGELOG.md)。数据 schema `0.4.0` 与 workspace state `0.5.1` 为记忆层数据格式版本,独立于包发布版本)。
47
47
 
48
48
  [![PyPI](https://img.shields.io/pypi/v/liquid-loop.svg)](https://pypi.org/project/liquid-loop/)
49
49
  [![Python](https://img.shields.io/pypi/pyversions/liquid-loop.svg)](https://pypi.org/project/liquid-loop/)
@@ -4,7 +4,7 @@
4
4
 
5
5
  > **Self-Organizing Cognitive Memory for AI Agents** — Zero LLM dependency, pure Python implementation of the Liquid Loop theory.
6
6
 
7
- > **当前包版本:`2.0.0`**(2026-08-24 统一版号里程碑,详见 [CHANGELOG.md](CHANGELOG.md)。数据 schema `0.4.0` 与 workspace state `0.5.1` 为记忆层数据格式版本,独立于包发布版本)。
7
+ > **当前包版本:`2.0.1`**(2026-08-25 零向量召回超越词频基线,详见 [CHANGELOG.md](CHANGELOG.md)。数据 schema `0.4.0` 与 workspace state `0.5.1` 为记忆层数据格式版本,独立于包发布版本)。
8
8
 
9
9
  [![PyPI](https://img.shields.io/pypi/v/liquid-loop.svg)](https://pypi.org/project/liquid-loop/)
10
10
  [![Python](https://img.shields.io/pypi/pyversions/liquid-loop.svg)](https://pypi.org/project/liquid-loop/)
@@ -1,11 +1,11 @@
1
- """Liquid Loop — Workspace Cognitive Runtime v2.0.0 (禁向量·活态液态神经网络:成核门槛/反证轨/老化回收/原理优先成核/因果演化/软取代supersede+治理查询state_at/impact/duplicates+程序性记忆层procedural_memory+PerceptionGate因果共生门控)"""
2
- __version__ = "2.0.0"
1
+ """Liquid Loop — Workspace Cognitive Runtime v2.0.1 (禁向量·活态液态神经网络:成核门槛/反证轨/老化回收/原理优先成核/因果演化/软取代supersede+治理查询state_at/impact/duplicates+程序性记忆层procedural_memory+PerceptionGate因果共生门控)"""
2
+ __version__ = "2.0.2"
3
3
 
4
4
  from .workspace import (
5
5
  WorkspaceState, Anchor, Evidence, Memory, Conflict,
6
6
  AuditChain, CPERegularizer, SelfRefineEngine,
7
7
  )
8
- from .storage import load, save, locked_state
8
+ from .storage import load, save, locked_state, StateRegressionGuardError
9
9
  from .entropy import calculate, calculate_detail, calculate as calculate_entropy
10
10
  from .selfspin import LiquidSelfSpin
11
11
  from .liquid_reweight import LiquidReweight
@@ -16,7 +16,8 @@ from .procedural_memory import (
16
16
  ProceduralMemory, ProceduralRegistry, procmem_recall, procmem_admit,
17
17
  )
18
18
  # 蒸馏落地的公共原语(零侵入,供 cli / server / agent loop 接入)
19
- from .guard import should_escalate, confirm_gate, CapabilityMenu
19
+ from .guard import should_escalate, confirm_gate, CapabilityMenu, PerceptionGate
20
+ from .dsh_reversible import ReversibleRegistry # 可逆副作用底座(蒸馏 DeepSeek Harness #204,2026-08-28 接活)
20
21
  from .session import SessionState, mark_abort, recover, save_session, load_session
21
22
  from .recall_filter import adaptive_recall
22
23
  from .rar import RARIndex, build_or_cache # 公共检索 API(原 workspace 局部 import)
@@ -0,0 +1,184 @@
1
+ #!/usr/bin/env python3
2
+ # -*- coding: utf-8 -*-
3
+ """
4
+ dsh_reversible.py — Cordis 可逆副作用(Disposable Pattern)零依赖原语
5
+ ==============================================================================
6
+ 蒸馏自 DeepSeek Harness #204(2026-08-14 一手核验),2026-08-28 接活进液环运行时。
7
+
8
+ Cordis 的核心机制(Koishi/Shigma 元框架,DeepSeek vendored + 北大联合扩展):
9
+ - 每个插件运行在一个 Context 里,凡是它"占用的东西"都挂在 Context 上
10
+ - dispose 时按"子先于父、注册逆序"自动撤销全部副作用
11
+ - 副作用全追踪、卸载自动回滚、生命周期包含(provider 比 consumer 活得久)
12
+
13
+ 本原语把它极简成可被液环/vera 脚本 import 的通用"可回滚状态变更"底座:
14
+ register() 登记一个副作用 + 其撤销函数 → dispose() 时自动按序回滚。
15
+
16
+ 与液环同构:append-only + supersede/tombstone(软删除可回滚)↔ 可逆副作用;
17
+ "no privileged core" ↔ 本原语无特权对象,全部副作用一视同仁可回滚。
18
+ 与 Vera 红线#1 safe_rm 同构:删除/移动先登记 undo,dispose 时 mv 回原位即无损回滚。
19
+
20
+ 依赖:仅 Python 标准库。零外部依赖。
21
+ 接活范式(08-12 三段式):挂模块 + __init__ 暴露 + 自测 → server 经 liquid_loop 包 import。
22
+ """
23
+ from __future__ import annotations
24
+
25
+ from dataclasses import dataclass
26
+ from typing import Callable, List
27
+
28
+
29
+ @dataclass
30
+ class _Effect:
31
+ """一个已登记的副作用:apply 时的动作名 + 撤销回调。"""
32
+ label: str
33
+ undo: Callable[[], None]
34
+
35
+
36
+ class ReversibleRegistry:
37
+ """
38
+ 可逆副作用注册表(Cordis Context 的最小实现)。
39
+
40
+ 语义:
41
+ - register(label, undo) 登记副作用;dispose() 按注册逆序回滚。
42
+ - child() 派生子作用域;父 dispose 前先 dispose 全部子作用域(子先于父)。
43
+ - require(other) 声明"我依赖 other"(生命周期包含):
44
+ 被依赖方 dispose 前,先 dispose 依赖它的注册表(provider 活得比 consumer 久)。
45
+ - dispose() 幂等;重复调用安全。
46
+ - 无特权对象:本类不做任何"特殊保护",一切副作用同等可回滚。
47
+
48
+ 设计约束(蒸馏铁律):
49
+ - 只处理"可逆副作用",不实现事件总线/依赖注入容器——那是 Cordis 的其余部分,
50
+ 需要时另行蒸馏。保持单文件、零依赖、可独立测试。
51
+ """
52
+
53
+ def __init__(self, name: str = "root") -> None:
54
+ self.name = name
55
+ self._effects: List[_Effect] = []
56
+ self._children: List["ReversibleRegistry"] = []
57
+ self._dependents: List["ReversibleRegistry"] = [] # 依赖我的注册表
58
+ self._disposed = False
59
+
60
+ # ---- 登记与派生 ----
61
+
62
+ def register(self, label: str, undo: Callable[[], None]) -> None:
63
+ """登记一个副作用及其撤销回调。dispose 时按注册逆序调用 undo。"""
64
+ self._ensure_alive()
65
+ self._effects.append(_Effect(label=label, undo=undo))
66
+
67
+ def child(self, name: str) -> "ReversibleRegistry":
68
+ """派生子作用域。父 dispose 前先 dispose 全部子作用域(子先于父)。"""
69
+ self._ensure_alive()
70
+ c = ReversibleRegistry(name=f"{self.name}/{name}")
71
+ self._children.append(c)
72
+ return c
73
+
74
+ def require(self, provider: "ReversibleRegistry") -> None:
75
+ """
76
+ 生命周期包含:本注册表依赖 provider。
77
+ provider 被 dispose 前,会先 dispose 所有声明依赖它的注册表(consumer)。
78
+ 避免"提供者已卸载、消费者还在调用"的悬空引用。
79
+ """
80
+ self._ensure_alive()
81
+ provider._dependents.append(self)
82
+
83
+ # ---- 回滚 ----
84
+
85
+ def dispose(self) -> None:
86
+ """按顺序回滚:依赖我的(consumer)→ 子作用域 → 自身副作用(注册逆序)。幂等。"""
87
+ if self._disposed:
88
+ return
89
+ self._disposed = True
90
+ for dep in reversed(self._dependents):
91
+ dep.dispose()
92
+ for c in reversed(self._children):
93
+ c.dispose()
94
+ for e in reversed(self._effects):
95
+ try:
96
+ e.undo()
97
+ except Exception as exc: # 单条撤销失败不阻断整体回滚
98
+ print(f"[{self.name}] undo({e.label}) 失败: {exc!r}")
99
+ self._effects.clear()
100
+
101
+ def _ensure_alive(self) -> None:
102
+ if self._disposed:
103
+ raise RuntimeError(f"registry '{self.name}' 已 dispose,禁止再登记副作用")
104
+
105
+ # ---- 上下文管理(with 语句自动回滚) ----
106
+
107
+ def __enter__(self) -> "ReversibleRegistry":
108
+ return self
109
+
110
+ def __exit__(self, *exc) -> None:
111
+ self.dispose()
112
+
113
+ # ---- 只读状态 ----
114
+
115
+ @property
116
+ def disposed(self) -> bool:
117
+ return self._disposed
118
+
119
+ @property
120
+ def effect_count(self) -> int:
121
+ return len(self._effects)
122
+
123
+ def __repr__(self) -> str:
124
+ return (f"ReversibleRegistry(name={self.name!r}, effects={len(self._effects)}, "
125
+ f"children={len(self._children)}, disposed={self._disposed})")
126
+
127
+
128
+ # ---------------------------------------------------------------------------
129
+ # 自测(运行时验证)
130
+ # ---------------------------------------------------------------------------
131
+ def _selftest() -> None:
132
+ log: List[str] = []
133
+
134
+ # 1. 基本可逆:注册逆序回滚
135
+ r = ReversibleRegistry("base")
136
+ r.register("a", lambda: log.append("undo-a"))
137
+ r.register("b", lambda: log.append("undo-b"))
138
+ r.dispose()
139
+ assert log == ["undo-b", "undo-a"], f"逆序回滚失败: {log}"
140
+ r.dispose() # 幂等
141
+ print("✓ 基本可逆 + 幂等")
142
+
143
+ # 2. 子先于父
144
+ log.clear()
145
+ r = ReversibleRegistry("root")
146
+ r.register("root-e", lambda: log.append("undo-root"))
147
+ c = r.child("sub")
148
+ c.register("sub-e", lambda: log.append("undo-sub"))
149
+ r.dispose()
150
+ assert log == ["undo-sub", "undo-root"], f"子先于父失败: {log}"
151
+ print("✓ 子先于父回滚")
152
+
153
+ # 3. 生命周期包含:provider 先卸 consumer
154
+ log.clear()
155
+ provider = ReversibleRegistry("provider")
156
+ consumer = ReversibleRegistry("consumer")
157
+ consumer.require(provider) # consumer 依赖 provider
158
+ provider.register("p", lambda: log.append("undo-provider"))
159
+ consumer.register("c", lambda: log.append("undo-consumer"))
160
+ provider.dispose()
161
+ assert log == ["undo-consumer", "undo-provider"], f"生命周期包含失败: {log}"
162
+ print("✓ 生命周期包含(consumer 先于 provider 回滚)")
163
+
164
+ # 4. with 语句自动回滚
165
+ log.clear()
166
+ with ReversibleRegistry("ctx") as ctx:
167
+ ctx.register("x", lambda: log.append("undo-x"))
168
+ assert log == ["undo-x"], f"with 自动回滚失败: {log}"
169
+ print("✓ with 语句自动回滚")
170
+
171
+ # 5. 单条撤销失败不阻断整体
172
+ log.clear()
173
+ r = ReversibleRegistry("faulty")
174
+ r.register("bad", lambda: (_ for _ in ()).throw(RuntimeError("boom")))
175
+ r.register("ok", lambda: log.append("undo-ok"))
176
+ r.dispose()
177
+ assert log == ["undo-ok"], f"失败隔离失败: {log}"
178
+ print("✓ 单条撤销失败不阻断整体")
179
+
180
+ print("SELFTEST ALL GREEN")
181
+
182
+
183
+ if __name__ == "__main__":
184
+ _selftest()
@@ -0,0 +1,324 @@
1
+ """蒸馏 #197 + #198 守卫原语:能力菜单 + 升级刹车 + 确认闸。
2
+
3
+ #197 MCP 安全边界:能力描述/标签只是提示语,不是沙箱;隔离靠部署层。
4
+ → CapabilityMenu:声明带名字/参数/风险等级的能力;高危调用前插确认闸。
5
+ #198 Agent 停下问人:升级条件 = 低置信 ∨ 不可逆 ∨ 超预算;审批=方向盘。
6
+ → should_escalate(confidence, irreversible, over_budget) + confirm_gate。
7
+
8
+ 液环 cli 已有 --force 确认雏形(cpe_check);本模块提供通用原语,供破坏性操作接入,
9
+ 让「动作前问人」成为可配置策略而非散落各处的硬编码。零依赖:仅标准库。
10
+ """
11
+ from __future__ import annotations
12
+
13
+ RISK_LEVELS = ("low", "medium", "high")
14
+
15
+
16
+ def validate_content(content: str) -> str | None:
17
+ """内容质量 ValidationRule(08-18 审计补盲):挡无意义/垃圾写入。
18
+
19
+ 返回拒绝原因字符串;通过返回 None。
20
+ 规则(纯规则零依赖,只挡"无意义",不评"价值"):
21
+ 1) 空内容;
22
+ 2) 无字母/汉字 且 过短(<8) 的纯占位/符号噪音(如 "1"、"!"、"!!!");
23
+ 3) 字符多样性过低(熵代理)的重复噪音(如 "aaaaaa"、"111111")。
24
+ 单点维护:server ll_remember 与 workspace.add_evidence 共用本函数。
25
+ """
26
+ c = (content or "").strip()
27
+ if not c:
28
+ return "content 为空: 疑似无意义写入"
29
+ has_text = any(not ch.isdigit() and ch.isalnum() for ch in c)
30
+ if not has_text and len(c) < 8:
31
+ return "content 无文字内容且过短: 疑似占位噪音"
32
+ if len(set(c)) / len(c) < 0.2:
33
+ return "content 字符多样性过低(熵代理): 疑似重复噪音"
34
+ return None
35
+
36
+
37
+ # ── 反绝对化词表(m60 DeepTutor 借鉴落地 · 2026-08-29)──
38
+ # DeepTutor 训练师提示词要求"避免绝对化、过度自信表述"(不/不要/绝不/总是/完全/彻底/
39
+ # 专家/热爱/恨 等),并配引用池约束(结论必须能锚定到已蒸馏引文)。
40
+ # 液环落地为「警告级」检测:只标记、不拒绝——液环允许主体自由表达,但把过度概括
41
+ # 显式暴露给写入方(og_flags),供上层蒸馏/内化时复核真实性,而非静默放行。
42
+ OVERGEN_TERMS: tuple[tuple[str, str], ...] = (
43
+ # (命中词, 类别)
44
+ ("总是", "absolute"), ("从来不", "absolute"), ("从不", "absolute"), ("永远", "absolute"),
45
+ ("所有", "absolute"), ("全部", "absolute"), ("一切", "absolute"), ("任何", "absolute"),
46
+ ("完全", "absolute"), ("彻底", "absolute"), ("绝对", "absolute"), ("一定", "absolute"),
47
+ ("必须", "prescriptive"), ("禁止", "prescriptive"), ("绝不", "absolute"),
48
+ ("专家", "claim"), ("大师", "claim"), ("完美", "claim"), ("精通", "claim"),
49
+ ("热爱", "claim"), ("深爱", "claim"), ("憎恨", "claim"), ("讨厌", "claim"),
50
+ ("always", "absolute"), ("never", "absolute"), ("every", "absolute"),
51
+ ("all", "absolute"), ("none", "absolute"), ("completely", "absolute"),
52
+ ("totally", "absolute"), ("absolutely", "absolute"), ("perfectly", "claim"),
53
+ ("expert", "claim"), ("master", "claim"), ("perfect", "claim"),
54
+ ("love", "claim"), ("hate", "claim"), ("passion", "claim"),
55
+ ("fully understands", "claim"), ("deeply", "claim"), ("truly", "claim"),
56
+ )
57
+
58
+
59
+ def overgeneralization_flags(content: str) -> list[dict]:
60
+ """反绝对化词表检测(m60 建议2 落地):返回命中的过度概括表述。
61
+
62
+ 返回 [{term, category}],按出现顺序去重;无命中返回 []。
63
+ 只标记不拒绝(警告级):液环允许自由表达,但显式暴露给上层复核。
64
+ 零依赖,纯规则,与 validate_content 同一风格。
65
+ """
66
+ c = (content or "").lower()
67
+ flags: list[dict] = []
68
+ seen: set = set()
69
+ for term, cat in OVERGEN_TERMS:
70
+ if term in c and term not in seen:
71
+ seen.add(term)
72
+ flags.append({"term": term, "category": cat})
73
+ return flags
74
+
75
+
76
+ def should_escalate(confidence: float = 1.0, irreversible: bool = False,
77
+ over_budget: bool = False) -> bool:
78
+ """升级刹车:命中任一红线即交回人。低置信 ∨ 不可逆 ∨ 超预算。
79
+
80
+ 全自动不是高级,敢于在关键时刻说"我需要你"才是设计出来的安全网。
81
+ """
82
+ return (confidence < 0.5) or bool(irreversible) or bool(over_budget)
83
+
84
+
85
+ class CapabilityMenu:
86
+ """能力菜单:声明带风险等级的能力,隔离靠部署层而非标签。"""
87
+
88
+ def __init__(self):
89
+ self._caps: dict = {}
90
+
91
+ def declare(self, name: str, risk: str = "low", description: str = "",
92
+ params: list | None = None) -> None:
93
+ if risk not in RISK_LEVELS:
94
+ raise ValueError(f"risk 必须是 {RISK_LEVELS},收到 {risk!r}")
95
+ self._caps[name] = {
96
+ "risk": risk,
97
+ "description": description,
98
+ "params": params or [],
99
+ }
100
+
101
+ def risk_of(self, name: str) -> str | None:
102
+ cap = self._caps.get(name)
103
+ return cap["risk"] if cap else None
104
+
105
+ def requires_confirm(self, name: str) -> bool:
106
+ """high 风险能力调用前必须确认(对齐液环确认闸门)。"""
107
+ return self.risk_of(name) == "high"
108
+
109
+ def as_menu(self) -> dict:
110
+ """供协议层展示的菜单(提示语,非沙箱)。"""
111
+ return dict(self._caps)
112
+
113
+
114
+ def confirm_gate(action: str, risk: str = "high", auto_approve: bool = False) -> bool:
115
+ """确认闸:high 风险且未 auto 确认 → 返回 False(拦截,交回人)。
116
+
117
+ auto_approve=True 用于 dry-run / 测试;真实高危操作应默认拦截。
118
+ 返回 True=放行,False=拦截(需人工确认)。
119
+ """
120
+ if risk not in RISK_LEVELS:
121
+ risk = "high" # 未知风险按最高处置
122
+ if risk == "high" and not auto_approve:
123
+ return False
124
+ return True
125
+
126
+
127
+ class PerceptionGate:
128
+ """门控-液环因果共生环的胶水层(“单终端机器人”概念落地件)。
129
+
130
+ 设计定位(克制原则:精选 4 模块,不重新膨胀):
131
+ - 输入候选感知动作 + 预算余量 + 灵敏度,输出 allow / degrade / block + 理由。
132
+ - 内部消费:
133
+ 1) guard.should_escalate —— 不可逆变 + 低置信 → 交回人(block)。
134
+ 2) 因果边(workspace.causal:enables/causes/contradicts)→ 「因果核心永饿死」
135
+ 豁免:命中因果核心的动作即便超预算也放行,保证液环主链路不被闸饿死。
136
+ 3) adaptive_recall 的负载双模(load<0.5 冗余验证 / ≥0.5 分工扩容)→
137
+ 超预算时按当前认知负载决定 degrade(省算力)还是 allow(精度优先)。
138
+ 4) build_or_cache(rar 本地索引缓存)→ 本地算力溶解「重建索引」成本,
139
+ 使高载降级 skip 无后顾之忧(详见测试 test_perception_gate.py)。
140
+ 5) env_anchor_probe(v3.2 环境锚时效门,可选)→ 涉及环境类断言的动作
141
+ 使用前强制重取证(curl/ps/ipconfig/hostname 正对照),返回 stale 即 block,
142
+ 把"认知基线纪律"升为机制强制步;无注入则跳过(零影响)。
143
+
144
+ 零依赖:真实因果边 / 负载 / 环境锚重取证来自 liquid-loop 的 workspace / recall_filter / rar
145
+ 或调用方注入,由 adapter 注入(causal_core_predicate / load_probe / env_anchor_probe),
146
+ 不在本模块 import 重型依赖。这样门控成为「可配置策略」,而非把整套液环塞进门里的膨胀方案。
147
+ """
148
+
149
+ def __init__(self, budget: float = 1.0, sensitivity: str = "normal",
150
+ causal_core_predicate=None, load_probe=None, env_anchor_probe=None):
151
+ self.budget = float(budget)
152
+ self.sensitivity = sensitivity
153
+ self._spent = 0.0
154
+ # 默认谓词:无注入时一律非因果核心(保守,闸正常生效)
155
+ self.causal_core_predicate = causal_core_predicate or (lambda action: False)
156
+ # 默认负载探测器:无注入时返回 0.0(低载,精度优先路径)
157
+ self.load_probe = load_probe or (lambda: 0.0)
158
+ # v3.2 环境锚时效门:无注入时返回 None(跳过,零影响现有门)
159
+ self.env_anchor_probe = env_anchor_probe
160
+
161
+ def decide(self, action, cost: float = 0.0, confidence: float = 1.0,
162
+ irreversible: bool = False) -> dict:
163
+ """对一条候选感知动作做门控裁决。
164
+
165
+ 返回 dict: {"decision": allow|degrade|block, "reason": str, ...}
166
+ - block : 需人工确认(交回人),不消耗预算。
167
+ - allow : 放行(因果核心豁免 / 正常 / 低载精度优先)。
168
+ - degrade : 超预算 + 高载 → 降采样 / 跳过,省算力;不消耗预算。
169
+ """
170
+ # 0) v3.2 环境锚时效门(最高优先):涉及环境类断言须先重取证,stale 即拦截
171
+ if self.env_anchor_probe is not None:
172
+ try:
173
+ _ap = self.env_anchor_probe(action)
174
+ except Exception:
175
+ _ap = None # 重取证异常 fail-open,不误伤(零爆破半径)
176
+ if _ap is True or (isinstance(_ap, dict) and _ap.get("stale")):
177
+ return {
178
+ "decision": "block",
179
+ "reason": f"环境锚时效门: 须重取证 ({_ap.get('detail','') if isinstance(_ap, dict) else ''})",
180
+ "env_anchor_stale": True,
181
+ }
182
+
183
+ # 1) 不可逆变 + 低置信 → 交回人(should_escalate 红线)
184
+ if should_escalate(confidence=confidence, irreversible=irreversible):
185
+ return {
186
+ "decision": "block",
187
+ "reason": "irreversible+low_confidence→需人工确认",
188
+ "escalate": True,
189
+ }
190
+
191
+ # 2) 因果核心 → 永放行(never starve causal core)
192
+ if self.causal_core_predicate(action):
193
+ return {
194
+ "decision": "allow",
195
+ "reason": "因果核心动作永放行(保护液环主链路)",
196
+ "causal_core": True,
197
+ }
198
+
199
+ # 3) 预算检查
200
+ remaining = self.budget - self._spent
201
+ if cost > remaining:
202
+ load = float(self.load_probe())
203
+ if load >= 0.5:
204
+ # 高载:降级 skip / 降采样,省算力
205
+ # (rar.build_or_cache 已本地化索引重建成本,skip 无后顾之忧)
206
+ return {
207
+ "decision": "degrade",
208
+ "reason": f"超预算+高载({load:.2f})→降采样/跳过省算力",
209
+ "load": load,
210
+ "over_budget": True,
211
+ }
212
+ # 低载:冗余验证,精度优先(仍消耗预算)
213
+ self._spent += cost
214
+ return {
215
+ "decision": "allow",
216
+ "reason": f"超预算+低载({load:.2f})→冗余验证精度优先",
217
+ "load": load,
218
+ "over_budget": True,
219
+ }
220
+
221
+ # 4) 正常放行
222
+ self._spent += cost
223
+ return {
224
+ "decision": "allow",
225
+ "reason": "正常放行",
226
+ "load": float(self.load_probe()),
227
+ }
228
+
229
+ def remaining(self) -> float:
230
+ """剩余预算。"""
231
+ return self.budget - self._spent
232
+
233
+
234
+ # ── 评估准则:延迟跃变防误判(m69 过度递归衰减 + m65 世界模型自演化 蒸馏落地 · 2026-08-29)──
235
+ # 自演化系统最危险的评估错误是「过早判失败」:机制的效果常表现为延迟跃变
236
+ # (先震荡/持平,积累到阈值后跳变),若在跃变前按短期指标关停,就永远等不到
237
+ # 跃变。本准则供上层评测门控(如 SoMEval 接入的评估门)作为独立校验函数使用:
238
+ # 对某机制给出「是否继续运行」建议时,先检查评估窗口是否足以覆盖延迟跃变期。
239
+ EVAL_MIN_WINDOW_DAYS = 7 # 机制效果最小评估窗口(天)
240
+ EVAL_RECENCY_WEIGHT = 0.5 # 近端窗口权重阈值:近端占比过高说明评估窗口过短
241
+
242
+
243
+ def eval_patience_check(
244
+ history: list[dict] | None = None,
245
+ window_days: float = EVAL_MIN_WINDOW_DAYS,
246
+ recency_weight: float = EVAL_RECENCY_WEIGHT,
247
+ ) -> dict:
248
+ """评估准则:防「过早判失败」的延迟跃变检查。
249
+
250
+ 入参 history: [{"ts": 秒级时间戳, "metric": float}, ...](按时间升序)。
251
+ 无历史/历史过短 → 返回 verdict="insufficient"(建议延长窗口再判)。
252
+ 返回:
253
+ {
254
+ "verdict": "ok" | "insufficient" | "too_short_window",
255
+ "reason": str,
256
+ "window_days": float, # 覆盖的实际时间跨度(天)
257
+ "recency_ratio": float | None, # 近 1/3 窗口指标占整体比重(不稳定时显式暴露)
258
+ "trend": "rising"|"flat"|"declining"|"unknown",
259
+ }
260
+ 设计要点(蒸馏自 m69 过度递归衰减 / m65 世界模型自演化):
261
+ - 机制的自演化收益是延迟跃变而非线性累积 → 评估窗口必须覆盖跃变期;
262
+ - 近端比重过高 = 评估窗口过短,直接建议延长而非判失败;
263
+ - 本函数只做「评估是否充分」的裁决,不代替业务指标计算,保持零依赖纯函数。
264
+ """
265
+ if not history or len(history) < 3:
266
+ return {
267
+ "verdict": "insufficient",
268
+ "reason": "评估历史不足(需≥3个采样点),无法判断延迟跃变",
269
+ "window_days": 0.0,
270
+ "recency_ratio": None,
271
+ "trend": "unknown",
272
+ }
273
+ try:
274
+ ts0 = float(history[0]["ts"])
275
+ ts1 = float(history[-1]["ts"])
276
+ span_days = (ts1 - ts0) / 86400.0
277
+ except (KeyError, TypeError, ValueError):
278
+ return {
279
+ "verdict": "insufficient",
280
+ "reason": "历史采样点缺 ts/metric 字段,无法计算评估窗口",
281
+ "window_days": 0.0,
282
+ "recency_ratio": None,
283
+ "trend": "unknown",
284
+ }
285
+ if span_days < window_days:
286
+ return {
287
+ "verdict": "too_short_window",
288
+ "reason": f"评估窗口 {span_days:.1f} 天 < 建议最小 {window_days} 天,延迟跃变未到观察期,禁止判失败",
289
+ "window_days": span_days,
290
+ "recency_ratio": None,
291
+ "trend": "unknown",
292
+ }
293
+ # 近端 1/3 采样点指标均值 vs 整体均值:近端占比过高 → 窗口仍在跃变爬升期
294
+ n = len(history)
295
+ recent = history[max(0, n - max(1, n // 3)):]
296
+ try:
297
+ avg_all = sum(float(h["metric"]) for h in history) / n
298
+ avg_recent = sum(float(h["metric"]) for h in recent) / len(recent)
299
+ ratio = (avg_recent / avg_all) if avg_all else 1.0
300
+ except (KeyError, TypeError, ValueError):
301
+ ratio = None
302
+ trend = "unknown"
303
+ if ratio is not None:
304
+ if ratio > 1.05:
305
+ trend = "rising"
306
+ elif ratio < 0.95:
307
+ trend = "declining"
308
+ else:
309
+ trend = "flat"
310
+ if ratio is not None and ratio > (1.0 + recency_weight):
311
+ return {
312
+ "verdict": "too_short_window",
313
+ "reason": f"近端指标占比 {ratio:.2f} 过高(>1+{recency_weight}),机制处于跃变爬升期,须延长窗口",
314
+ "window_days": span_days,
315
+ "recency_ratio": ratio,
316
+ "trend": trend,
317
+ }
318
+ return {
319
+ "verdict": "ok",
320
+ "reason": f"评估窗口 {span_days:.1f} 天覆盖延迟跃变期,可正常评估",
321
+ "window_days": span_days,
322
+ "recency_ratio": ratio,
323
+ "trend": trend,
324
+ }