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.
- {liquid_loop-0.9.0 → liquid_loop-0.9.2}/PKG-INFO +25 -1
- {liquid_loop-0.9.0 → liquid_loop-0.9.2}/README.md +24 -0
- {liquid_loop-0.9.0 → liquid_loop-0.9.2}/liquid_loop/__init__.py +2 -2
- {liquid_loop-0.9.0 → liquid_loop-0.9.2}/liquid_loop/storage.py +4 -0
- {liquid_loop-0.9.0 → liquid_loop-0.9.2}/liquid_loop/workspace.py +83 -39
- {liquid_loop-0.9.0 → liquid_loop-0.9.2}/liquid_loop.egg-info/PKG-INFO +25 -1
- {liquid_loop-0.9.0 → liquid_loop-0.9.2}/liquid_loop.egg-info/SOURCES.txt +1 -0
- {liquid_loop-0.9.0 → liquid_loop-0.9.2}/pyproject.toml +1 -1
- liquid_loop-0.9.2/tests/test_layer1_persistence.py +71 -0
- {liquid_loop-0.9.0 → liquid_loop-0.9.2}/LICENSE +0 -0
- {liquid_loop-0.9.0 → liquid_loop-0.9.2}/liquid_loop/__main__.py +0 -0
- {liquid_loop-0.9.0 → liquid_loop-0.9.2}/liquid_loop/cli.py +0 -0
- {liquid_loop-0.9.0 → liquid_loop-0.9.2}/liquid_loop/cognitive_budget.py +0 -0
- {liquid_loop-0.9.0 → liquid_loop-0.9.2}/liquid_loop/entropy.py +0 -0
- {liquid_loop-0.9.0 → liquid_loop-0.9.2}/liquid_loop/mesh/__init__.py +0 -0
- {liquid_loop-0.9.0 → liquid_loop-0.9.2}/liquid_loop/mesh/v2.py +0 -0
- {liquid_loop-0.9.0 → liquid_loop-0.9.2}/liquid_loop.egg-info/dependency_links.txt +0 -0
- {liquid_loop-0.9.0 → liquid_loop-0.9.2}/liquid_loop.egg-info/entry_points.txt +0 -0
- {liquid_loop-0.9.0 → liquid_loop-0.9.2}/liquid_loop.egg-info/requires.txt +0 -0
- {liquid_loop-0.9.0 → liquid_loop-0.9.2}/liquid_loop.egg-info/top_level.txt +0 -0
- {liquid_loop-0.9.0 → liquid_loop-0.9.2}/setup.cfg +0 -0
- {liquid_loop-0.9.0 → liquid_loop-0.9.2}/tests/test_consensus_expansion.py +0 -0
- {liquid_loop-0.9.0 → liquid_loop-0.9.2}/tests/test_entropy.py +0 -0
- {liquid_loop-0.9.0 → liquid_loop-0.9.2}/tests/test_nucleate_dual_track.py +0 -0
- {liquid_loop-0.9.0 → liquid_loop-0.9.2}/tests/test_peek_seal.py +0 -0
- {liquid_loop-0.9.0 → liquid_loop-0.9.2}/tests/test_seal_persistence.py +0 -0
- {liquid_loop-0.9.0 → liquid_loop-0.9.2}/tests/test_self_evolve.py +0 -0
- {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.
|
|
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.
|
|
2
|
-
__version__ = "0.9.
|
|
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
|
-
"""
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
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) <
|
|
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
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
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
|
-
|
|
429
|
-
|
|
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
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
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.
|
|
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
|
|
523
|
-
|
|
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.
|
|
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 公理(一切代码与论文围绕它校验):**
|
|
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "liquid-loop"
|
|
7
|
-
version = "0.9.
|
|
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
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|