liquid-loop 0.9.0__tar.gz → 0.9.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 (28) hide show
  1. {liquid_loop-0.9.0 → liquid_loop-0.9.2}/PKG-INFO +25 -1
  2. {liquid_loop-0.9.0 → liquid_loop-0.9.2}/README.md +24 -0
  3. {liquid_loop-0.9.0 → liquid_loop-0.9.2}/liquid_loop/__init__.py +2 -2
  4. {liquid_loop-0.9.0 → liquid_loop-0.9.2}/liquid_loop/storage.py +4 -0
  5. {liquid_loop-0.9.0 → liquid_loop-0.9.2}/liquid_loop/workspace.py +83 -39
  6. {liquid_loop-0.9.0 → liquid_loop-0.9.2}/liquid_loop.egg-info/PKG-INFO +25 -1
  7. {liquid_loop-0.9.0 → liquid_loop-0.9.2}/liquid_loop.egg-info/SOURCES.txt +1 -0
  8. {liquid_loop-0.9.0 → liquid_loop-0.9.2}/pyproject.toml +1 -1
  9. liquid_loop-0.9.2/tests/test_layer1_persistence.py +71 -0
  10. {liquid_loop-0.9.0 → liquid_loop-0.9.2}/LICENSE +0 -0
  11. {liquid_loop-0.9.0 → liquid_loop-0.9.2}/liquid_loop/__main__.py +0 -0
  12. {liquid_loop-0.9.0 → liquid_loop-0.9.2}/liquid_loop/cli.py +0 -0
  13. {liquid_loop-0.9.0 → liquid_loop-0.9.2}/liquid_loop/cognitive_budget.py +0 -0
  14. {liquid_loop-0.9.0 → liquid_loop-0.9.2}/liquid_loop/entropy.py +0 -0
  15. {liquid_loop-0.9.0 → liquid_loop-0.9.2}/liquid_loop/mesh/__init__.py +0 -0
  16. {liquid_loop-0.9.0 → liquid_loop-0.9.2}/liquid_loop/mesh/v2.py +0 -0
  17. {liquid_loop-0.9.0 → liquid_loop-0.9.2}/liquid_loop.egg-info/dependency_links.txt +0 -0
  18. {liquid_loop-0.9.0 → liquid_loop-0.9.2}/liquid_loop.egg-info/entry_points.txt +0 -0
  19. {liquid_loop-0.9.0 → liquid_loop-0.9.2}/liquid_loop.egg-info/requires.txt +0 -0
  20. {liquid_loop-0.9.0 → liquid_loop-0.9.2}/liquid_loop.egg-info/top_level.txt +0 -0
  21. {liquid_loop-0.9.0 → liquid_loop-0.9.2}/setup.cfg +0 -0
  22. {liquid_loop-0.9.0 → liquid_loop-0.9.2}/tests/test_consensus_expansion.py +0 -0
  23. {liquid_loop-0.9.0 → liquid_loop-0.9.2}/tests/test_entropy.py +0 -0
  24. {liquid_loop-0.9.0 → liquid_loop-0.9.2}/tests/test_nucleate_dual_track.py +0 -0
  25. {liquid_loop-0.9.0 → liquid_loop-0.9.2}/tests/test_peek_seal.py +0 -0
  26. {liquid_loop-0.9.0 → liquid_loop-0.9.2}/tests/test_seal_persistence.py +0 -0
  27. {liquid_loop-0.9.0 → liquid_loop-0.9.2}/tests/test_self_evolve.py +0 -0
  28. {liquid_loop-0.9.0 → liquid_loop-0.9.2}/tests/test_workspace.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: liquid-loop
3
- Version: 0.9.0
3
+ Version: 0.9.2
4
4
  Summary: Self-Organizing Cognitive Memory for AI Agents — Liquid Loop theory implementation
5
5
  Author: fishbook0001
6
6
  Maintainer: fishbook0001
@@ -227,6 +227,30 @@ print(health["CCI"], health["consensus_crystals"])
227
227
 
228
228
  ---
229
229
 
230
+ ## 固态 A2A 通道(任意 MCP 客户端接入)
231
+
232
+ 把共享液环后端(地址由环境变量 `LIQUID_LOOP_BASE` 决定,默认 `http://127.0.0.1:8790`)封装成一个
233
+ **stdio JSON-RPC 的 MCP server**,让任意支持 Model Context Protocol 的客户端(本例以 TRAE SOLO CN 演示)
234
+ **原生读写同一份共享记忆**——这就是多 agent 间的固化(solidified)A2A 通道。
235
+
236
+ > 后端说明:桥接只做协议翻译,**不内置 8790 服务**;后端由你自己部署(运行你自己的液环 SSE 服务,
237
+ > 把地址通过 `LIQUID_LOOP_BASE` 传给桥接)。成核 / 共识 / 审计链全部由后端按液环理论执行。
238
+
239
+ ```bash
240
+ # 在你的 MCP 客户端注册该 server(以 TRAE 为例;其 code CLI 路径随安装而异,请替换为你的路径)
241
+ export PY=python3 # 任意 Python 3.10+ 解释器
242
+ export SVR=examples/trae_mesh_mcp/mcp_server.py # 本仓库内路径
243
+ export LIQUID_LOOP_BASE=http://127.0.0.1:8790 # 改成你的后端地址
244
+ "<path-to-your-trae-code-cli>" \
245
+ --add-mcp '{"servers":{"liquidloop-mesh":{"command":"'"$PY"'","args":["'"$SVR"'"]}}}'
246
+ ```
247
+
248
+ 桥接暴露 `liquidloop_remember` / `liquidloop_recall` / `liquidloop_metrics` 三个工具(写入**必须声明 `agent_id`**)。
249
+ 压测脚本与运维说明见 [`examples/trae_mesh_mcp/README.md`](examples/trae_mesh_mcp/README.md)
250
+ (直连 + 经桥双路并发,零丢写 / 共识幂等 / 崩溃恢复三关全 PASS;所有路径走环境变量,适配不同部署拓扑)。
251
+
252
+ ---
253
+
230
254
  ## 定位:Self-Regulating Memory State Evolution
231
255
 
232
256
  > **North-Star 公理(一切代码与论文围绕它校验):**
@@ -188,6 +188,30 @@ print(health["CCI"], health["consensus_crystals"])
188
188
 
189
189
  ---
190
190
 
191
+ ## 固态 A2A 通道(任意 MCP 客户端接入)
192
+
193
+ 把共享液环后端(地址由环境变量 `LIQUID_LOOP_BASE` 决定,默认 `http://127.0.0.1:8790`)封装成一个
194
+ **stdio JSON-RPC 的 MCP server**,让任意支持 Model Context Protocol 的客户端(本例以 TRAE SOLO CN 演示)
195
+ **原生读写同一份共享记忆**——这就是多 agent 间的固化(solidified)A2A 通道。
196
+
197
+ > 后端说明:桥接只做协议翻译,**不内置 8790 服务**;后端由你自己部署(运行你自己的液环 SSE 服务,
198
+ > 把地址通过 `LIQUID_LOOP_BASE` 传给桥接)。成核 / 共识 / 审计链全部由后端按液环理论执行。
199
+
200
+ ```bash
201
+ # 在你的 MCP 客户端注册该 server(以 TRAE 为例;其 code CLI 路径随安装而异,请替换为你的路径)
202
+ export PY=python3 # 任意 Python 3.10+ 解释器
203
+ export SVR=examples/trae_mesh_mcp/mcp_server.py # 本仓库内路径
204
+ export LIQUID_LOOP_BASE=http://127.0.0.1:8790 # 改成你的后端地址
205
+ "<path-to-your-trae-code-cli>" \
206
+ --add-mcp '{"servers":{"liquidloop-mesh":{"command":"'"$PY"'","args":["'"$SVR"'"]}}}'
207
+ ```
208
+
209
+ 桥接暴露 `liquidloop_remember` / `liquidloop_recall` / `liquidloop_metrics` 三个工具(写入**必须声明 `agent_id`**)。
210
+ 压测脚本与运维说明见 [`examples/trae_mesh_mcp/README.md`](examples/trae_mesh_mcp/README.md)
211
+ (直连 + 经桥双路并发,零丢写 / 共识幂等 / 崩溃恢复三关全 PASS;所有路径走环境变量,适配不同部署拓扑)。
212
+
213
+ ---
214
+
191
215
  ## 定位:Self-Regulating Memory State Evolution
192
216
 
193
217
  > **North-Star 公理(一切代码与论文围绕它校验):**
@@ -1,5 +1,5 @@
1
- """Liquid Loop — Workspace Cognitive Runtime v0.9.0 (反证轨 + 时间动力学, CPE-fused, SEAL dual-opt, dual-track multi-agent, MESH v2 integration)"""
2
- __version__ = "0.9.0"
1
+ """Liquid Loop — Workspace Cognitive Runtime v0.9.2 (Layer-1 修复: 结构化投影层 + 冲突惩罚持久化 + 有效迭代 τ)"""
2
+ __version__ = "0.9.2"
3
3
 
4
4
  from .workspace import (
5
5
  WorkspaceState, Anchor, Evidence, Memory, Conflict,
@@ -65,6 +65,9 @@ def save(state: WorkspaceState, workspace_root: Path):
65
65
  data = asdict(state)
66
66
  # overlap_cache 仅为运行时熵计算缓存,键为 tuple,不可 JSON 序列化,且不具持久价值
67
67
  data.pop("overlap_cache", None)
68
+ # canon_fn 是运行时注入的可替换投影层(可能为不可序列化的函数),不持久化;
69
+ # 需用时由调用方重新注入。不 pop 会导致 json.dump 对函数崩溃(P0 隐患)。
70
+ data.pop("canon_fn", None)
68
71
  tmp = path.with_suffix(".tmp")
69
72
  with open(tmp, "w", encoding="utf-8") as f:
70
73
  json.dump(data, f, indent=2, ensure_ascii=False)
@@ -104,4 +107,5 @@ def _from_dict(data: dict) -> WorkspaceState:
104
107
  self_refine_probes=data.get("self_refine_probes", []),
105
108
  self_refine_results=data.get("self_refine_results", []),
106
109
  self_refine_repair_count=data.get("self_refine_repair_count", 0),
110
+ _iteration=data.get("_iteration", 0), # Layer-1 修复:τ=有效迭代计数必须持久化,否则重启后归零→强化门控恒真→时间衰减失效
107
111
  )
@@ -89,6 +89,7 @@ class Anchor:
89
89
  value_score: float = 1.0
90
90
  anchor_strength: float = 1.0
91
91
  seal_adjust: float = 0.0 # SEAL 自评层增量;_recalc 合成进 stability,不被覆盖(解 v0.6.3 假落地)
92
+ conflict_penalty: float = 1.0 # 冲突惩罚累积乘子(Layer-1 修复:持久化,recalc/step/load 后保留)
92
93
 
93
94
  def decay_value(self, factor: float = 0.95, evidence_count: int = 0) -> float:
94
95
  if not self.created_at:
@@ -158,6 +159,7 @@ class Evidence:
158
159
  agent_id: str = "" # 多智能体共用:写入者标识(共用机制,缺省=legacy/单实例)
159
160
  relation: str = "support" # 反证轨:evidence 与 memory 的关系 "support"(一致) | "contradiction"(冲突) | ""(legacy 视为 support)
160
161
  target_memory_id: str = "" # relation=contradiction 时,可选显式指向被反驳的 memory(缺省=同锚点最近结晶)
162
+ added_iter: int = 0 # 证据写入时的有效状态更新序号(τ=Effective Iteration);强化门控据此判"自上次 step 以来是否有新 support 抵达"
161
163
 
162
164
 
163
165
  @dataclass
@@ -173,7 +175,8 @@ class Memory:
173
175
  stability: float = 1.0 # 反证轨稳定性:一致增稳、冲突降稳(0~1)
174
176
  support_count: int = 0 # 支持该 memory 的证据数
175
177
  contradiction_count: int = 0 # 反驳该 memory 的证据数
176
- last_reinforced: str = "" # 最近一次被 support 证据强化的时间戳(时间动力学用)
178
+ last_reinforced: str = "" # 最近一次被 support 证据强化的时间戳(审计展示用)
179
+ last_reinforced_iter: int = 0 # 最近一次被 support 强化的迭代序号(τ=Effective Iteration,非墙钟;强化门控以此为准)
177
180
 
178
181
 
179
182
  @dataclass
@@ -238,6 +241,8 @@ class WorkspaceState:
238
241
  cpe_erosion_warnings: list[dict] = field(default_factory=list) # 能力侵蚀告警
239
242
  cpe_regularization_count: int = 0 # 累计正则化干预次数
240
243
  overlap_cache: dict = field(default_factory=dict) # 缓存关键词重叠度
244
+ _iteration: int = 0 # 有效状态更新次数(τ = Effective Iteration);时间动力学以之为时间变量而非墙钟
245
+ canon_fn: object = None # 可替换 Projection Layer(Layer-1 修复):注入后冲突检测复用此投影,None=旧关键词重叠回退
241
246
 
242
247
  # ── API 层:add_anchor / add_evidence(让 README 示例能跑通)──
243
248
 
@@ -285,6 +290,7 @@ class WorkspaceState:
285
290
  id=uid(), anchor_id=target.id, content=content,
286
291
  quality=quality, timestamp=now(), agent_id=agent_id,
287
292
  relation=relation, target_memory_id=target_memory_id,
293
+ added_iter=self._iteration,
288
294
  )
289
295
  self.evidences.append(e)
290
296
  target.evidence_ids.append(e.id)
@@ -392,51 +398,88 @@ class WorkspaceState:
392
398
  anchor.description = best.content
393
399
 
394
400
  def _detect_conflicts(self, anchor_id: str):
395
- """群内自洽检测:同锚点证据一致性过低 -> 生成 Conflict 记录并降锚点稳定性。
396
-
397
- 阈值从环境变量 LIQUID_CONFLICT_THRESHOLD 读取(默认 0.2)。
398
- 复用 CPE 泛化防线思路:证据间平均关键词重叠 < threshold 且 >= 3 条 -> 潜在冲突/漂移。
399
- 保守处理:只标记'需人工确认',不武断判相反(避免过度工程化误报)。
400
-
401
- 性能(v0.9):原 O(g²) 全量成对扫描。相同 content 的两条证据重叠恒为 1.0,
402
- 对"内容分歧度"无信息量,故先按 content 去重为 distinct 集,只对 distinct 内容
403
- 求两两重叠 -> O(d²)(d = distinct content 数 ≤ g)。overlap_cache 复用。
404
- 语义更纯净:度量"不同论点间的分歧",重复写入不再稀释冲突信号。
401
+ """群内自洽检测(Layer-1 修复:复用可替换 Projection Layer,惩罚持久化)。
402
+
403
+ 旧实现用关键词重叠(Jaccard)近似"语义冲突"——本质是 Lexical Proxy,
404
+ 会导致:① 对真异事实(关键词不重叠)误触发;② 对近重复(关键词高重叠)
405
+ 反而不触发(反相关);③ 自建一套不可替换的投影启发式(Hidden Projection Layer)。
406
+
407
+ 修复:若 WorkspaceState.canon_fn 已注入,则冲突判定改用同一 Projection Layer——
408
+ 两条 distinct content 证据,若投影到【同一 CP】但 relation 相反(一 support 一
409
+ contradiction)-> 真冲突;若投影同 CP 同 relation -> 近重复(不冲突);
410
+ 若投影不同 CP -> 即使关键词重叠也不算冲突(语义独立)。
411
+
412
+ 惩罚持久化:锚点稳定性惩罚写入锚点字段 conflict_penalty(累积乘子),
413
+ _recalc_anchor 在合成 stability 时乘入,因此 load / step 重算后惩罚不丢失
414
+ (旧实现在 stability 字段上直接 *=0.9,被后续 recalc 覆盖,故不持久)。
415
+
416
+ canon_fn 为 None 时回退旧 _keyword_overlap 行为,保证生产向后兼容。
405
417
  """
406
418
  import os
407
419
  conflict_threshold = float(os.environ.get('LIQUID_CONFLICT_THRESHOLD', '0.2'))
408
-
420
+
409
421
  group = [e for e in self.evidences if e.anchor_id == anchor_id]
410
- if len(group) < 3:
422
+ if len(group) < 2:
411
423
  return
412
- # ── O(g²) -> O(d²):content 去重,相同 content 对的重叠恒为 1.0 不参与分歧度量 ──
413
424
  distinct: list = []
414
425
  seen: set = set()
415
426
  for e in group:
416
427
  if e.content and e.content not in seen:
417
428
  seen.add(e.content)
418
429
  distinct.append(e.content)
419
- n = len(distinct)
420
- if n < 2:
421
- return
422
- overlaps = []
423
- for i in range(n):
424
- for j in range(i + 1, n):
425
- overlaps.append(_keyword_overlap(distinct[i], distinct[j], self.overlap_cache))
426
- if not overlaps:
430
+
431
+ # ── 回退路径:未注入 Projection Layer -> 旧关键词重叠行为(向后兼容)──
432
+ if self.canon_fn is None:
433
+ if len(distinct) < 2:
434
+ return
435
+ overlaps = []
436
+ for i in range(len(distinct)):
437
+ for j in range(i + 1, len(distinct)):
438
+ overlaps.append(_keyword_overlap(distinct[i], distinct[j], self.overlap_cache))
439
+ if not overlaps:
440
+ return
441
+ avg = sum(overlaps) / len(overlaps)
442
+ if avg < conflict_threshold:
443
+ anchor = next((a for a in self.anchors if a.id == anchor_id), None)
444
+ if any(c.anchor_a == anchor_id for c in self.conflicts):
445
+ return
446
+ self.conflicts.append(Conflict(
447
+ anchor_a=anchor_id,
448
+ description=f"证据间平均一致度 {avg:.2f}(<{conflict_threshold}),存在潜在冲突/漂移,需人工确认",
449
+ severity=round(1.0 - avg, 2),
450
+ ))
451
+ if anchor:
452
+ anchor.conflict_penalty = max(0.1, anchor.conflict_penalty * 0.9)
427
453
  return
428
- avg = sum(overlaps) / len(overlaps)
429
- if avg < conflict_threshold:
454
+
455
+ # ── Layer-1 路径:复用同一可替换 Projection Layer ──
456
+ # 按 CP 分组 distinct content;同 CP 内若同时有 support 与 contradiction -> 真冲突
457
+ by_cp: dict = {}
458
+ rel_by_content: dict = {}
459
+ for e in group:
460
+ cp = self.canon_fn(e.content)
461
+ by_cp.setdefault(cp, []).append(e.content)
462
+ rel_by_content.setdefault(e.content, set()).add(e.relation or "support")
463
+ triggered = False
464
+ for cp, contents in by_cp.items():
465
+ if len(contents) < 2:
466
+ continue
467
+ distinct_rels = set()
468
+ for c in contents:
469
+ distinct_rels |= (rel_by_content.get(c) or {"support"})
470
+ if "support" in distinct_rels and "contradiction" in distinct_rels:
471
+ triggered = True
472
+ break
473
+ if triggered:
430
474
  anchor = next((a for a in self.anchors if a.id == anchor_id), None)
431
- if any(c.anchor_a == anchor_id for c in self.conflicts):
432
- return
433
- self.conflicts.append(Conflict(
434
- anchor_a=anchor_id,
435
- description=f"证据间平均一致度 {avg:.2f}(<{conflict_threshold}),存在潜在冲突/漂移,需人工确认",
436
- severity=round(1.0 - avg, 2),
437
- ))
475
+ if not any(c.anchor_a == anchor_id for c in self.conflicts):
476
+ self.conflicts.append(Conflict(
477
+ anchor_a=anchor_id,
478
+ description="Projection Layer 检测到同 CP 内存在 support 与 contradiction 异 relation 证据,判定为语义冲突",
479
+ severity=conflict_threshold,
480
+ ))
438
481
  if anchor:
439
- anchor.stability = max(0.1, anchor.stability * 0.9)
482
+ anchor.conflict_penalty = max(0.1, anchor.conflict_penalty * 0.9)
440
483
 
441
484
 
442
485
  def _recalc_anchor(self, anchor_id: str):
@@ -449,7 +492,7 @@ class WorkspaceState:
449
492
  # base = 证据权重均值;stability = base + SEAL 自评层(seal_adjust 不被覆盖)
450
493
  base = avg_weight
451
494
  a.base_stability = base
452
- a.stability = round(min(1.0, max(0.1, base + a.seal_adjust)), 3)
495
+ a.stability = round(min(1.0, max(0.1, base * a.conflict_penalty + a.seal_adjust)), 3)
453
496
  evidence_count = len(group)
454
497
  a.decay_value(evidence_count=evidence_count)
455
498
  a.recalc_strength(evidence_count)
@@ -497,6 +540,7 @@ class WorkspaceState:
497
540
  s / (s + CONTRADICTION_WEIGHT * c + 1))), 3)
498
541
  if supports:
499
542
  m.last_reinforced = max(e.timestamp for e in supports)
543
+ m.last_reinforced_iter = max(e.added_iter for e in supports)
500
544
 
501
545
  def step(self, dt: int = 1, decay_rate: float = 0.05):
502
546
  """显式时间动力学步(v0.8 液态循环核心):
@@ -508,29 +552,29 @@ class WorkspaceState:
508
552
  若本轮有 support 证据 → 稳定性恢复到固有值(强化);否则按时间衰减(无强化流失)
509
553
  - 重算锚点稳定性与审计更新时间
510
554
  """
555
+ prev_iter = self._iteration # 上次 step 的迭代序号(τ 时间变量,非墙钟)
511
556
  # 1. 证据权重时间衰减
512
557
  for e in self.evidences:
513
558
  if not e.archived:
514
559
  e.weight = max(e.weight * ((1 - decay_rate) ** dt), 0.05)
515
560
  # 2. 记忆稳定性:固有(计数驱动) + 时间衰减/强化
516
561
  for a in self.anchors:
517
- self._update_memory_stability(a.id)
518
- last_step = getattr(self, "_last_step_at", "")
519
- now_dt = datetime.now(timezone.utc)
562
+ self._update_memory_stability(a.id) # 内部以各 support 的 added_iter 最大值更新 last_reinforced_iter
520
563
  for m in self.memories:
521
564
  intrinsic = m.stability # _update_memory_stability 已写入固有稳定性
522
- # 强化 = 自上次 step 以来有过新 support 证据(last_reinforced 晚于上次 step)
523
- reinforced = bool(m.last_reinforced) and (not last_step or m.last_reinforced > last_step)
565
+ # 强化 = 自上次 step(prev_iter)以来有新 support 证据抵达(last_reinforced_iter >= prev_iter)
566
+ # 以 τ=Effective Iteration 为时间变量,非墙钟,跨算力可比
567
+ reinforced = (m.last_reinforced_iter >= prev_iter)
524
568
  if reinforced:
525
569
  m.stability = intrinsic # 被强化:恢复到固有稳定性
526
570
  else:
527
571
  # 无新强化:时间衰减,且不超过固有上限
528
572
  m.stability = round(max(0.0, min(intrinsic,
529
573
  m.stability * ((1 - decay_rate) ** dt))), 3)
530
- self._last_step_at = now()
531
574
  # 3. 重算锚点稳定性
532
575
  for a in self.anchors:
533
576
  self._recalc_anchor(a.id)
577
+ self._iteration += dt # 推进有效状态更新计数(τ)
534
578
  self.updated_at = now()
535
579
 
536
580
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: liquid-loop
3
- Version: 0.9.0
3
+ Version: 0.9.2
4
4
  Summary: Self-Organizing Cognitive Memory for AI Agents — Liquid Loop theory implementation
5
5
  Author: fishbook0001
6
6
  Maintainer: fishbook0001
@@ -227,6 +227,30 @@ print(health["CCI"], health["consensus_crystals"])
227
227
 
228
228
  ---
229
229
 
230
+ ## 固态 A2A 通道(任意 MCP 客户端接入)
231
+
232
+ 把共享液环后端(地址由环境变量 `LIQUID_LOOP_BASE` 决定,默认 `http://127.0.0.1:8790`)封装成一个
233
+ **stdio JSON-RPC 的 MCP server**,让任意支持 Model Context Protocol 的客户端(本例以 TRAE SOLO CN 演示)
234
+ **原生读写同一份共享记忆**——这就是多 agent 间的固化(solidified)A2A 通道。
235
+
236
+ > 后端说明:桥接只做协议翻译,**不内置 8790 服务**;后端由你自己部署(运行你自己的液环 SSE 服务,
237
+ > 把地址通过 `LIQUID_LOOP_BASE` 传给桥接)。成核 / 共识 / 审计链全部由后端按液环理论执行。
238
+
239
+ ```bash
240
+ # 在你的 MCP 客户端注册该 server(以 TRAE 为例;其 code CLI 路径随安装而异,请替换为你的路径)
241
+ export PY=python3 # 任意 Python 3.10+ 解释器
242
+ export SVR=examples/trae_mesh_mcp/mcp_server.py # 本仓库内路径
243
+ export LIQUID_LOOP_BASE=http://127.0.0.1:8790 # 改成你的后端地址
244
+ "<path-to-your-trae-code-cli>" \
245
+ --add-mcp '{"servers":{"liquidloop-mesh":{"command":"'"$PY"'","args":["'"$SVR"'"]}}}'
246
+ ```
247
+
248
+ 桥接暴露 `liquidloop_remember` / `liquidloop_recall` / `liquidloop_metrics` 三个工具(写入**必须声明 `agent_id`**)。
249
+ 压测脚本与运维说明见 [`examples/trae_mesh_mcp/README.md`](examples/trae_mesh_mcp/README.md)
250
+ (直连 + 经桥双路并发,零丢写 / 共识幂等 / 崩溃恢复三关全 PASS;所有路径走环境变量,适配不同部署拓扑)。
251
+
252
+ ---
253
+
230
254
  ## 定位:Self-Regulating Memory State Evolution
231
255
 
232
256
  > **North-Star 公理(一切代码与论文围绕它校验):**
@@ -18,6 +18,7 @@ liquid_loop/mesh/__init__.py
18
18
  liquid_loop/mesh/v2.py
19
19
  tests/test_consensus_expansion.py
20
20
  tests/test_entropy.py
21
+ tests/test_layer1_persistence.py
21
22
  tests/test_nucleate_dual_track.py
22
23
  tests/test_peek_seal.py
23
24
  tests/test_seal_persistence.py
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "liquid-loop"
7
- version = "0.9.0"
7
+ version = "0.9.2"
8
8
  description = "Self-Organizing Cognitive Memory for AI Agents — Liquid Loop theory implementation"
9
9
  readme = "README.md"
10
10
  license = {text = "MIT"}
@@ -0,0 +1,71 @@
1
+ """Layer-1 修复回归测试:有效迭代 τ 持久化 + 可替换结构化投影层 + 冲突惩罚持久化。
2
+
3
+ 验证 v0.9.2 引入的修复在 save/load 往返后不丢失状态,且不破坏向后兼容。
4
+ """
5
+ import pytest
6
+ from pathlib import Path
7
+
8
+ from liquid_loop.workspace import WorkspaceState
9
+ from liquid_loop.storage import save, load
10
+
11
+
12
+ def test_effective_iteration_persists_across_reload(tmp_path: Path):
13
+ """τ=有效迭代计数必须随 save/load 往返;否则重启后归零→强化门控恒真→时间衰减失效。"""
14
+ ws = WorkspaceState()
15
+ a = ws.add_anchor("x")
16
+ ws.add_evidence(a, "e1 support")
17
+ ws.add_evidence(a, "e2 support")
18
+ ws.step(dt=1)
19
+ ws.step(dt=1)
20
+ ws.step(dt=1)
21
+ assert ws._iteration == 3, f"_iteration 应推进到 3,实为 {ws._iteration}"
22
+
23
+ save(ws, tmp_path)
24
+ loaded = load(tmp_path)
25
+ assert loaded._iteration == 3, f"重载后 _iteration 丢失,实为 {loaded._iteration}"
26
+
27
+
28
+ def test_canon_fn_structured_projection_triggers_true_conflict():
29
+ """canon_fn 注入后按 CP 精确分组:同 CP 内 support+contradiction 异 relation → 真冲突。"""
30
+ ws = WorkspaceState()
31
+ # 结构化投影:取首词为 CP(离散键,非向量/embedding——守住 North-Star 禁向量约束)
32
+ ws.canon_fn = lambda c: c.strip().split()[0]
33
+ a = ws.add_anchor("claim")
34
+ ws.add_evidence(a, "苹果很好吃 因为甜", relation="support")
35
+ ws.add_evidence(a, "苹果很好吃 因为脆", relation="contradiction") # 同 CP 异 relation
36
+
37
+ assert len(ws.conflicts) == 1, f"应检测到 1 条真冲突,实为 {len(ws.conflicts)}"
38
+ assert "Projection Layer" in ws.conflicts[0].description
39
+ assert ws.anchors[0].conflict_penalty == 0.9, "冲突后惩罚乘子应降为 0.9"
40
+ # 惩罚乘入 stability(非直接改 stability,故 recalc 后不丢)
41
+ assert ws.anchors[0].stability < 1.0
42
+
43
+
44
+ def test_conflict_penalty_persists_across_reload(tmp_path: Path):
45
+ """conflict_penalty 累积乘子必须持久化;旧实现直接改 stability 会被 recalc 覆盖。"""
46
+ ws = WorkspaceState()
47
+ ws.canon_fn = lambda c: c.strip().split()[0]
48
+ a = ws.add_anchor("claim")
49
+ ws.add_evidence(a, "猫很可爱 因为黏人", relation="support")
50
+ ws.add_evidence(a, "猫很可爱 因为高冷", relation="contradiction")
51
+
52
+ penalty_before = ws.anchors[0].conflict_penalty
53
+ assert penalty_before == 0.9
54
+
55
+ save(ws, tmp_path)
56
+ loaded = load(tmp_path)
57
+ assert loaded.anchors[0].conflict_penalty == 0.9, "重载后冲突惩罚丢失"
58
+ assert len(loaded.conflicts) == 1, "重载后冲突记录丢失"
59
+ # canon_fn 是运行时注入项,save 前应被 pop,重载后回退 None(向后兼容路径)
60
+ assert loaded.canon_fn is None, "canon_fn 不应被持久化(运行时注入项)"
61
+
62
+
63
+ def test_canon_fn_none_falls_back_to_keyword_overlap():
64
+ """canon_fn=None → 回退旧关键词重叠行为,保证生产向后兼容(不崩、能检测冲突)。"""
65
+ ws = WorkspaceState() # canon_fn 默认 None
66
+ a = ws.add_anchor("topic")
67
+ ws.add_evidence(a, "量子计算利用叠加态加速", relation="support")
68
+ ws.add_evidence(a, "古典吉他音色温暖动人", relation="support")
69
+ # 两组关键词零重叠 → 平均一致度 < 默认阈值 0.2 → 触发保守冲突标记
70
+ assert len(ws.conflicts) == 1, "回退路径应仍能检测低重叠潜在冲突"
71
+ assert "一致度" in ws.conflicts[0].description
File without changes
File without changes