liquid-loop 0.3.0__tar.gz → 0.5.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.3.0 → liquid_loop-0.5.2}/PKG-INFO +1 -1
- liquid_loop-0.5.2/liquid_loop/__init__.py +9 -0
- liquid_loop-0.5.2/liquid_loop/__main__.py +5 -0
- {liquid_loop-0.3.0 → liquid_loop-0.5.2}/liquid_loop/cli.py +214 -13
- liquid_loop-0.5.2/liquid_loop/entropy.py +212 -0
- {liquid_loop-0.3.0 → liquid_loop-0.5.2}/liquid_loop/storage.py +9 -4
- liquid_loop-0.5.2/liquid_loop/workspace.py +824 -0
- {liquid_loop-0.3.0 → liquid_loop-0.5.2}/liquid_loop.egg-info/PKG-INFO +1 -1
- {liquid_loop-0.3.0 → liquid_loop-0.5.2}/liquid_loop.egg-info/SOURCES.txt +1 -0
- {liquid_loop-0.3.0 → liquid_loop-0.5.2}/pyproject.toml +1 -1
- liquid_loop-0.3.0/liquid_loop/__init__.py +0 -2
- liquid_loop-0.3.0/liquid_loop/entropy.py +0 -56
- liquid_loop-0.3.0/liquid_loop/workspace.py +0 -248
- {liquid_loop-0.3.0 → liquid_loop-0.5.2}/LICENSE +0 -0
- {liquid_loop-0.3.0 → liquid_loop-0.5.2}/README.md +0 -0
- {liquid_loop-0.3.0 → liquid_loop-0.5.2}/liquid_loop.egg-info/dependency_links.txt +0 -0
- {liquid_loop-0.3.0 → liquid_loop-0.5.2}/liquid_loop.egg-info/entry_points.txt +0 -0
- {liquid_loop-0.3.0 → liquid_loop-0.5.2}/liquid_loop.egg-info/requires.txt +0 -0
- {liquid_loop-0.3.0 → liquid_loop-0.5.2}/liquid_loop.egg-info/top_level.txt +0 -0
- {liquid_loop-0.3.0 → liquid_loop-0.5.2}/setup.cfg +0 -0
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
"""Liquid Loop — Workspace Cognitive Runtime v0.5.1 (CPE-fused, proactive)"""
|
|
2
|
+
__version__ = "0.5.2"
|
|
3
|
+
|
|
4
|
+
from .workspace import (
|
|
5
|
+
WorkspaceState, Anchor, Evidence, Memory, Conflict,
|
|
6
|
+
AuditChain, CPERegularizer, SelfRefineEngine,
|
|
7
|
+
)
|
|
8
|
+
from .storage import load, save
|
|
9
|
+
from .entropy import calculate, calculate_detail, calculate as calculate_entropy
|
|
@@ -4,10 +4,11 @@ from collections import Counter
|
|
|
4
4
|
from .workspace import (
|
|
5
5
|
WorkspaceState, Anchor, Evidence, Memory, StateSnapshot,
|
|
6
6
|
AnchorRelation, now, DensityLevel, CognitiveStage, LiquidityLevel,
|
|
7
|
+
SelfRefineEngine, meta_thinker_evaluate, meta_thinker_advice,
|
|
8
|
+
CPERegularizer,
|
|
7
9
|
)
|
|
8
10
|
from .storage import load, save, get_audit_chain
|
|
9
|
-
from .entropy import calculate
|
|
10
|
-
from .entropy import calculate
|
|
11
|
+
from .entropy import calculate, calculate_detail
|
|
11
12
|
|
|
12
13
|
|
|
13
14
|
WORKSPACE = Path.cwd()
|
|
@@ -68,7 +69,7 @@ def _recalc_stability(state: WorkspaceState, anchor_id: str):
|
|
|
68
69
|
|
|
69
70
|
@click.group()
|
|
70
71
|
def main():
|
|
71
|
-
"""Liquid Loop — Workspace Cognitive Runtime v0.
|
|
72
|
+
"""Liquid Loop — Workspace Cognitive Runtime v0.5 (CPE-fused)"""
|
|
72
73
|
pass
|
|
73
74
|
|
|
74
75
|
|
|
@@ -79,21 +80,26 @@ def init():
|
|
|
79
80
|
if s.anchors or s.evidences:
|
|
80
81
|
click.echo("工作区已初始化,无需重复操作。")
|
|
81
82
|
return
|
|
82
|
-
s.version = "0.2
|
|
83
|
+
s.version = "0.5.2"
|
|
83
84
|
_save(s)
|
|
84
|
-
click.echo(f"✓ Liquid Loop v0.
|
|
85
|
+
click.echo(f"✓ Liquid Loop v0.5.1 工作区已初始化: {WORKSPACE}/.liquid/")
|
|
85
86
|
|
|
86
87
|
|
|
87
88
|
@main.command()
|
|
88
89
|
def status():
|
|
89
|
-
"""
|
|
90
|
+
"""显示当前认知状态(CPE八维熵值)"""
|
|
90
91
|
s = _load()
|
|
91
92
|
ent = calculate(s)
|
|
92
93
|
level = "GREEN" if ent < 0.3 else ("YELLOW" if ent < 0.6 else "RED")
|
|
93
94
|
latest_ev = max((e.timestamp for e in s.evidences), default="无活动")
|
|
94
95
|
click.echo(f"锚点: {len(s.anchors)} | 证据: {len(s.evidences)} | 记忆: {len(s.memories)} | 冲突: {len(s.conflicts)} | 关系: {len(s.relations)}")
|
|
95
96
|
click.echo(f"熵值: {ent:.4f} [{level}] | 最新活动: {latest_ev}")
|
|
96
|
-
click.echo(f"版本: {s.version}")
|
|
97
|
+
click.echo(f"CPE干预: {s.cpe_regularization_count}次 | 版本: {s.version}")
|
|
98
|
+
|
|
99
|
+
# 八维熵值快速
|
|
100
|
+
det = calculate_detail(s)
|
|
101
|
+
click.echo(f" [原五维] 漂移{det['anchor_drift']:.2f} 冲突{det['conflict_density']:.2f} 碎片{det['evidence_fragmentation']:.2f} 活动{det['activity_gap']:.2f} 价值{det['value_decay']:.2f}")
|
|
102
|
+
click.echo(f" [CPE三维] 回顾{det['cpe_retrospective_decay']:.2f} 漂移{det['cpe_behavioral_drift']:.2f} 泛化{det['cpe_generalization_erosion']:.2f}")
|
|
97
103
|
|
|
98
104
|
|
|
99
105
|
@main.command()
|
|
@@ -116,6 +122,10 @@ def anchor_list():
|
|
|
116
122
|
f"证据:{ev_count} 价值:{a.value_score:.2f} "
|
|
117
123
|
f"锚定:[{strength_bar}] {a.anchor_strength:.2f}"
|
|
118
124
|
)
|
|
125
|
+
if ev_count > 0:
|
|
126
|
+
evs = [e for e in s.evidences if e.anchor_id == a.id]
|
|
127
|
+
if evs:
|
|
128
|
+
click.echo(f" 最近证据: {evs[-1].content[:40]}...")
|
|
119
129
|
|
|
120
130
|
|
|
121
131
|
@main.command()
|
|
@@ -225,18 +235,32 @@ def relation_list():
|
|
|
225
235
|
|
|
226
236
|
@main.command()
|
|
227
237
|
def check():
|
|
228
|
-
"""
|
|
238
|
+
"""检查工作区熵值(八维)"""
|
|
229
239
|
s = _load()
|
|
230
|
-
|
|
240
|
+
det = calculate_detail(s)
|
|
241
|
+
ent = det["combined"]
|
|
231
242
|
if ent < 0.3:
|
|
232
243
|
level, icon = "GREEN", "✓"
|
|
233
244
|
elif ent < 0.6:
|
|
234
245
|
level, icon = "YELLOW", "⚠"
|
|
235
246
|
else:
|
|
236
247
|
level, icon = "RED", "✗"
|
|
237
|
-
click.echo(f"{icon}
|
|
248
|
+
click.echo(f"{icon} 综合熵值: {ent:.4f} [{level}]")
|
|
249
|
+
|
|
250
|
+
# 显示各维度贡献
|
|
251
|
+
click.echo(f" ── 基础五维 ──")
|
|
252
|
+
click.echo(f" 锚点漂移: {det['anchor_drift']:.4f}")
|
|
253
|
+
click.echo(f" 冲突密度: {det['conflict_density']:.4f}")
|
|
254
|
+
click.echo(f" 证据碎片化: {det['evidence_fragmentation']:.4f}")
|
|
255
|
+
click.echo(f" 活动间隔: {det['activity_gap']:.4f}")
|
|
256
|
+
click.echo(f" 价值衰减: {det['value_decay']:.4f}")
|
|
257
|
+
click.echo(f" 锚定强度: {det['strength']:.4f}")
|
|
258
|
+
click.echo(f" ── CPE 三维 ──")
|
|
259
|
+
click.echo(f" 回顾性衰退: {det['cpe_retrospective_decay']:.4f} {'⚠' if det['cpe_retrospective_decay'] > 0.3 else '✓'}")
|
|
260
|
+
click.echo(f" 策略漂移: {det['cpe_behavioral_drift']:.4f} {'⚠' if det['cpe_behavioral_drift'] > 0.3 else '✓'}")
|
|
261
|
+
click.echo(f" 泛化崩塌: {det['cpe_generalization_erosion']:.4f} {'⚠' if det['cpe_generalization_erosion'] > 0.3 else '✓'}")
|
|
238
262
|
if ent >= 0.6:
|
|
239
|
-
click.echo("建议:
|
|
263
|
+
click.echo("建议: 运行 cpe-scan 检查能力侵蚀详情,或添加新证据降低熵值。")
|
|
240
264
|
|
|
241
265
|
|
|
242
266
|
@main.command()
|
|
@@ -280,7 +304,8 @@ def audit():
|
|
|
280
304
|
|
|
281
305
|
|
|
282
306
|
@main.command()
|
|
283
|
-
|
|
307
|
+
@click.option("--tail", "-n", default=None, type=int, help="仅显示最后 N 行日志")
|
|
308
|
+
def audit_log(tail):
|
|
284
309
|
"""查看审计日志"""
|
|
285
310
|
ac = get_audit_chain(WORKSPACE)
|
|
286
311
|
path = WORKSPACE / ".liquid" / "audit.log"
|
|
@@ -288,7 +313,10 @@ def audit_log():
|
|
|
288
313
|
click.echo("审计日志为空。")
|
|
289
314
|
return
|
|
290
315
|
with open(path, "r") as f:
|
|
291
|
-
|
|
316
|
+
lines = f.readlines()
|
|
317
|
+
if tail:
|
|
318
|
+
lines = lines[-tail:]
|
|
319
|
+
content = "".join(lines)
|
|
292
320
|
click.echo(content[-2000:] if len(content) > 2000 else content)
|
|
293
321
|
|
|
294
322
|
|
|
@@ -318,5 +346,178 @@ def anchor_add(name, description, density, stage):
|
|
|
318
346
|
click.echo(f"✓ 锚点已添加: {name} ({anchor.id}) [{anchor.value_density}/{anchor.cognitive_stage}/{anchor.liquidity}]")
|
|
319
347
|
|
|
320
348
|
|
|
349
|
+
@main.command()
|
|
350
|
+
@click.option("--evidence", "-e", default=None, help="只探测指定锚点的证据(逗号分隔锚点名)")
|
|
351
|
+
def self_refine(evidence):
|
|
352
|
+
"""后向自进化:探测QA验证 + 证据锚定修复(借鉴MemMA原位自进化)"""
|
|
353
|
+
s = _load()
|
|
354
|
+
engine = SelfRefineEngine(s)
|
|
355
|
+
|
|
356
|
+
evidence_ids = None
|
|
357
|
+
if evidence:
|
|
358
|
+
names = [n.strip() for n in evidence.split(",")]
|
|
359
|
+
evidence_ids = []
|
|
360
|
+
for name in names:
|
|
361
|
+
anchor = next((a for a in s.anchors if a.name == name), None)
|
|
362
|
+
if anchor:
|
|
363
|
+
evidence_ids.extend([e.id for e in s.evidences if e.anchor_id == anchor.id])
|
|
364
|
+
|
|
365
|
+
result = engine.run(evidence_ids)
|
|
366
|
+
|
|
367
|
+
if result.get("message"):
|
|
368
|
+
click.echo(f"⚠ {result['message']}")
|
|
369
|
+
return
|
|
370
|
+
|
|
371
|
+
click.echo(f"━━━ 后向自进化报告 ━━━")
|
|
372
|
+
click.echo(f" 探测总数: {result['total']}")
|
|
373
|
+
click.echo(f" 通过: {result['passed']}")
|
|
374
|
+
click.echo(f" 失败: {result['failed']}")
|
|
375
|
+
click.echo(f" 通过率: {result['pass_rate']:.0%}")
|
|
376
|
+
|
|
377
|
+
if result.get("repairs"):
|
|
378
|
+
click.echo(f"\n━━━ 修复操作 ━━━")
|
|
379
|
+
for r in result["repairs"]:
|
|
380
|
+
click.echo(f" {r}")
|
|
381
|
+
|
|
382
|
+
if result.get("failed_details"):
|
|
383
|
+
click.echo(f"\n━━━ 失败明细(前5条)━━━")
|
|
384
|
+
for d in result["failed_details"]:
|
|
385
|
+
click.echo(f" Q: {d['q']}")
|
|
386
|
+
click.echo(f" → {d['reason']}")
|
|
387
|
+
|
|
388
|
+
# 保存
|
|
389
|
+
_save(s)
|
|
390
|
+
click.echo(f"\n✓ 已保存,累计修复: {s.self_refine_repair_count} 次")
|
|
391
|
+
|
|
392
|
+
|
|
393
|
+
@main.command()
|
|
394
|
+
def strategy_health():
|
|
395
|
+
"""策略健康度检查(借鉴MemMA Meta-Thinker)"""
|
|
396
|
+
s = _load()
|
|
397
|
+
result = meta_thinker_evaluate(s)
|
|
398
|
+
|
|
399
|
+
status = "✓ 健康" if result["healthy"] else "✗ 异常"
|
|
400
|
+
click.echo(f"━━━ 策略健康度: {status} ━━━")
|
|
401
|
+
click.echo(f" 熵值: {result['entropy']:.4f}")
|
|
402
|
+
|
|
403
|
+
if result["issues"]:
|
|
404
|
+
for issue in result["issues"]:
|
|
405
|
+
icon = "⚠" if issue["severity"] == "warning" else "✗"
|
|
406
|
+
click.echo(f" {icon} [{issue['severity']}] {issue['issue']}")
|
|
407
|
+
if "anchors" in issue:
|
|
408
|
+
click.echo(f" 涉及: {', '.join(issue['anchors'])}")
|
|
409
|
+
else:
|
|
410
|
+
click.echo(" 无问题")
|
|
411
|
+
|
|
412
|
+
|
|
413
|
+
@main.command()
|
|
414
|
+
@click.argument("anchor_name")
|
|
415
|
+
@click.argument("new_evidence")
|
|
416
|
+
def strategy_advice(anchor_name, new_evidence):
|
|
417
|
+
"""策略建议:评估新证据是否应该添加到指定锚点(借鉴MemMA Meta-Thinker)"""
|
|
418
|
+
s = _load()
|
|
419
|
+
anchor = next((a for a in s.anchors if a.name == anchor_name), None)
|
|
420
|
+
if not anchor:
|
|
421
|
+
click.echo(f"锚点 '{anchor_name}' 不存在。")
|
|
422
|
+
return
|
|
423
|
+
result = meta_thinker_advice(anchor, s, new_evidence)
|
|
424
|
+
click.echo(f"━━━ 策略建议 ━━━")
|
|
425
|
+
click.echo(f" 锚点: {anchor_name}")
|
|
426
|
+
click.echo(f" 新证据: {new_evidence[:60]}...")
|
|
427
|
+
click.echo(f" 建议: [{result['action']}]")
|
|
428
|
+
click.echo(f" 原因: {result['reason']}")
|
|
429
|
+
if "suggestion" in result:
|
|
430
|
+
click.echo(f" 提示: {result['suggestion']}")
|
|
431
|
+
|
|
432
|
+
|
|
433
|
+
# ==============================================================================
|
|
434
|
+
# 【v0.5.0】CPE 正则化命令(借鉴 UIUC CPE 论文 arXiv:2605.09315)
|
|
435
|
+
# ==============================================================================
|
|
436
|
+
|
|
437
|
+
@main.command()
|
|
438
|
+
@click.argument("anchor_name")
|
|
439
|
+
@click.argument("new_evidence")
|
|
440
|
+
@click.option("--force", "-f", is_flag=True, help="强制通过(跳过BLOCK降为FLAG)")
|
|
441
|
+
def cpe_check(anchor_name, new_evidence, force):
|
|
442
|
+
"""CPE 正则化检查:评估新证据对锚点的能力侵蚀风险"""
|
|
443
|
+
s = _load()
|
|
444
|
+
regularizer = CPERegularizer(s)
|
|
445
|
+
result = regularizer.regularize(anchor_name, new_evidence, force=force)
|
|
446
|
+
action = result["action"]
|
|
447
|
+
icon = "✓" if action == "PASS" else ("!" if action == "FLAG" else "✗")
|
|
448
|
+
click.echo(f"━━━ CPE 正则化检查 ━━━")
|
|
449
|
+
click.echo(f" 锚点: {anchor_name}")
|
|
450
|
+
click.echo(f" 新证据: {new_evidence[:60]}...")
|
|
451
|
+
click.echo(f" 判定: [{icon} {action}] 风险评分: {result.get('score', 0):.3f}")
|
|
452
|
+
if result.get("reasons"):
|
|
453
|
+
for r in result["reasons"]:
|
|
454
|
+
click.echo(f" 原因: {r}")
|
|
455
|
+
if result.get("details"):
|
|
456
|
+
d = result["details"]
|
|
457
|
+
click.echo(f"\n 详情:")
|
|
458
|
+
click.echo(f" 与现有证据最大重叠: {d.get('max_overlap', 0):.3f}")
|
|
459
|
+
click.echo(f" 与锚点方向偏离: {d.get('drift_score', 0):.3f}")
|
|
460
|
+
click.echo(f" 保护权重(证据越多越敏感): {d.get('protection_weight', 0):.3f}")
|
|
461
|
+
_save(s)
|
|
462
|
+
|
|
463
|
+
|
|
464
|
+
@main.command()
|
|
465
|
+
def cpe_scan():
|
|
466
|
+
"""CPE 能力侵蚀扫描:检测全工作区的能力侵蚀信号"""
|
|
467
|
+
s = _load()
|
|
468
|
+
regularizer = CPERegularizer(s)
|
|
469
|
+
warnings = regularizer.scan_erosion()
|
|
470
|
+
|
|
471
|
+
click.echo(f"━━━ CPE 能力侵蚀扫描 ━━━")
|
|
472
|
+
if not warnings:
|
|
473
|
+
click.echo(" 无能力侵蚀信号 ✓")
|
|
474
|
+
else:
|
|
475
|
+
click.echo(f" 发现 {len(warnings)} 条侵蚀信号:")
|
|
476
|
+
for w in warnings:
|
|
477
|
+
icon = "⚠" if w["severity"] == "medium" else "🔴"
|
|
478
|
+
click.echo(f" {icon} [{w['type']}] {w['anchor']}: {w['detail']}")
|
|
479
|
+
|
|
480
|
+
click.echo(f"\n 正则化累计干预: {s.cpe_regularization_count} 次")
|
|
481
|
+
if s.blocked_evidences:
|
|
482
|
+
click.echo(f" 最近拦截记录 ({len(s.blocked_evidences)}):")
|
|
483
|
+
for b in s.blocked_evidences[-5:]:
|
|
484
|
+
click.echo(f" ⛔ {b}")
|
|
485
|
+
_save(s)
|
|
486
|
+
|
|
487
|
+
|
|
488
|
+
@main.command()
|
|
489
|
+
def cpe_status():
|
|
490
|
+
"""CPE 状态概览:显示当前能力保留状态"""
|
|
491
|
+
s = _load()
|
|
492
|
+
click.echo(f"━━━ CPE 正则化状态 ━━━")
|
|
493
|
+
click.echo(f" 正则化累计干预: {s.cpe_regularization_count} 次")
|
|
494
|
+
click.echo(f" 已通过检查的证据: {len(s.regularized_evidences)}")
|
|
495
|
+
click.echo(f" 被拦截/标记的证据: {len(s.blocked_evidences)}")
|
|
496
|
+
if s.cpe_erosion_warnings:
|
|
497
|
+
click.echo(f" 活跃侵蚀告警: {len(s.cpe_erosion_warnings)} 条")
|
|
498
|
+
for w in s.cpe_erosion_warnings:
|
|
499
|
+
icon = "⚠" if w["severity"] == "medium" else "🔴"
|
|
500
|
+
click.echo(f" {icon} [{w['type']}] {w['anchor']}: {w['detail']}")
|
|
501
|
+
else:
|
|
502
|
+
click.echo(f" 活跃侵蚀告警: 无")
|
|
503
|
+
|
|
504
|
+
# 八维熵值详情
|
|
505
|
+
from .entropy import calculate_detail
|
|
506
|
+
det = calculate_detail(s)
|
|
507
|
+
click.echo(f"\n━━━ 八维熵值详情 ━━━")
|
|
508
|
+
click.echo(f" 综合熵: {det['combined']:.4f}")
|
|
509
|
+
click.echo(f" ── 基础五维 ──")
|
|
510
|
+
click.echo(f" 锚点漂移: {det['anchor_drift']:.4f}")
|
|
511
|
+
click.echo(f" 冲突密度: {det['conflict_density']:.4f}")
|
|
512
|
+
click.echo(f" 证据碎片化: {det['evidence_fragmentation']:.4f}")
|
|
513
|
+
click.echo(f" 活动间隔: {det['activity_gap']:.4f}")
|
|
514
|
+
click.echo(f" 价值衰减: {det['value_decay']:.4f}")
|
|
515
|
+
click.echo(f" 锚定强度: {det['strength']:.4f}")
|
|
516
|
+
click.echo(f" ── CPE 三维 ──")
|
|
517
|
+
click.echo(f" 回顾性衰退(CPE): {det['cpe_retrospective_decay']:.4f}")
|
|
518
|
+
click.echo(f" 策略漂移(CPE): {det['cpe_behavioral_drift']:.4f}")
|
|
519
|
+
click.echo(f" 泛化崩塌(CPE): {det['cpe_generalization_erosion']:.4f}")
|
|
520
|
+
|
|
521
|
+
|
|
321
522
|
if __name__ == "__main__":
|
|
322
523
|
main()
|
|
@@ -0,0 +1,212 @@
|
|
|
1
|
+
from datetime import datetime, timezone, timedelta
|
|
2
|
+
from .workspace import WorkspaceState
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
def anchor_drift(state: WorkspaceState) -> float:
|
|
6
|
+
"""所有 Anchor 的 stability 偏离 1.0 的均值。0=全稳, 1=全漂"""
|
|
7
|
+
if not state.anchors:
|
|
8
|
+
return 0.0
|
|
9
|
+
return sum(1.0 - a.stability for a in state.anchors) / len(state.anchors)
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
def conflict_density(state: WorkspaceState) -> float:
|
|
13
|
+
"""冲突密度。0=无冲突, 1=每锚点一个冲突"""
|
|
14
|
+
if not state.anchors:
|
|
15
|
+
return 0.0
|
|
16
|
+
return min(len(state.conflicts) / len(state.anchors), 1.0)
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
def evidence_fragmentation(state: WorkspaceState) -> float:
|
|
20
|
+
"""孤立 Evidence 占比。0=全部有同伴, 1=全部孤立"""
|
|
21
|
+
if not state.evidences:
|
|
22
|
+
return 0.0
|
|
23
|
+
groups: dict[str, int] = {}
|
|
24
|
+
for e in state.evidences:
|
|
25
|
+
groups[e.anchor_id] = groups.get(e.anchor_id, 0) + 1
|
|
26
|
+
orphaned = sum(1 for count in groups.values() if count == 1)
|
|
27
|
+
return orphaned / len(groups) if groups else 0.0
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
def activity_gap(state: WorkspaceState) -> float:
|
|
31
|
+
"""距最新 Evidence 的天数。0=今天有活动, 1=超过7天无活动"""
|
|
32
|
+
if not state.evidences:
|
|
33
|
+
return 1.0
|
|
34
|
+
latest = max(e.timestamp for e in state.evidences)
|
|
35
|
+
try:
|
|
36
|
+
latest_dt = datetime.fromisoformat(latest)
|
|
37
|
+
except ValueError:
|
|
38
|
+
return 1.0
|
|
39
|
+
gap = datetime.now(timezone.utc) - latest_dt
|
|
40
|
+
return min(gap / timedelta(days=7), 1.0)
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
# 【借鉴KFG】四维熵值分量
|
|
44
|
+
|
|
45
|
+
def value_decay_entropy(state: WorkspaceState) -> float:
|
|
46
|
+
"""锚点价值衰减熵。0=全部高价值, 1=全部低价值 — 纯函数,不改 state"""
|
|
47
|
+
if not state.anchors:
|
|
48
|
+
return 0.0
|
|
49
|
+
scores = []
|
|
50
|
+
for a in state.anchors:
|
|
51
|
+
evs = [e for e in state.evidences if e.anchor_id == a.id]
|
|
52
|
+
# 快照+还原,保证不修改 state
|
|
53
|
+
orig_score = a.value_score
|
|
54
|
+
orig_strength = a.anchor_strength
|
|
55
|
+
score = a.decay_value(evidence_count=len(evs))
|
|
56
|
+
a.value_score = orig_score
|
|
57
|
+
a.anchor_strength = orig_strength
|
|
58
|
+
scores.append(score)
|
|
59
|
+
return 1.0 - (sum(scores) / len(scores))
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
def strength_entropy(state: WorkspaceState) -> float:
|
|
63
|
+
"""锚定强度熵。0=全部强锚定, 1=全部弱锚定 — 纯函数,不改 state"""
|
|
64
|
+
if not state.anchors:
|
|
65
|
+
return 0.0
|
|
66
|
+
strengths = [a.anchor_strength for a in state.anchors]
|
|
67
|
+
return 1.0 - (sum(strengths) / len(strengths))
|
|
68
|
+
|
|
69
|
+
|
|
70
|
+
# ==============================================================================
|
|
71
|
+
# 【新增 v0.5.0】CPE 能力侵蚀检测 — 借鉴 UIUC CPE 论文 (arXiv:2605.09315)
|
|
72
|
+
#
|
|
73
|
+
# CPE 能力侵蚀三大表现(§1):
|
|
74
|
+
# 1. 回顾性衰退(Retrospective decay):新证据覆盖旧锚点 → value_score 断崖
|
|
75
|
+
# 2. 策略漂移(Behavioral drift):锚点属性突变 → stability 在单次更新中大幅波动
|
|
76
|
+
# 3. 泛化崩塌(Generalization erosion):证据之间一致性下降 → cognitive_stage 降级
|
|
77
|
+
# ==============================================================================
|
|
78
|
+
|
|
79
|
+
def retrospective_decay_entropy(state: WorkspaceState) -> float:
|
|
80
|
+
"""回顾性衰退熵。0=无衰退, 1=大规模衰退(CPE §1: Retrospective decay)
|
|
81
|
+
|
|
82
|
+
纯函数快照,不改 state。每锚点计算最新 vs 次新证据的 value_score 变化比。
|
|
83
|
+
"""
|
|
84
|
+
if not state.anchors or not state.evidences:
|
|
85
|
+
return 0.0
|
|
86
|
+
decay_scores = []
|
|
87
|
+
for a in state.anchors:
|
|
88
|
+
evs = sorted(
|
|
89
|
+
[e for e in state.evidences if e.anchor_id == a.id],
|
|
90
|
+
key=lambda x: x.timestamp, reverse=True
|
|
91
|
+
)
|
|
92
|
+
if len(evs) < 2:
|
|
93
|
+
continue
|
|
94
|
+
# 快照原始值
|
|
95
|
+
orig_score = a.value_score
|
|
96
|
+
orig_strength = a.anchor_strength
|
|
97
|
+
# 最新证据时的 score
|
|
98
|
+
new_score = a.decay_value(evidence_count=len(evs))
|
|
99
|
+
new_score_val = a.value_score
|
|
100
|
+
# 少一条证据时的 score(传参模式,不依赖 state 缓存)
|
|
101
|
+
old_score = a.decay_value(evidence_count=len(evs) - 1)
|
|
102
|
+
# 还原 state
|
|
103
|
+
a.value_score = orig_score
|
|
104
|
+
a.anchor_strength = orig_strength
|
|
105
|
+
if old_score > 0:
|
|
106
|
+
ratio = max(0, 1 - new_score_val / old_score)
|
|
107
|
+
decay_scores.append(ratio)
|
|
108
|
+
return sum(decay_scores) / len(decay_scores) if decay_scores else 0.0
|
|
109
|
+
|
|
110
|
+
|
|
111
|
+
def behavioral_drift_entropy(state: WorkspaceState) -> float:
|
|
112
|
+
"""策略漂移熵。0=无漂移, 1=剧烈漂移(CPE §1: Behavioral policy drift)
|
|
113
|
+
|
|
114
|
+
纯函数快照,不改 state。测量锚点 stability 在最近一次更新中的变化幅度。
|
|
115
|
+
"""
|
|
116
|
+
if not state.anchors:
|
|
117
|
+
return 0.0
|
|
118
|
+
drifts = []
|
|
119
|
+
for a in state.anchors:
|
|
120
|
+
evs = [e for e in state.evidences if e.anchor_id == a.id]
|
|
121
|
+
if len(evs) < 2:
|
|
122
|
+
continue
|
|
123
|
+
evs_sorted = sorted(evs, key=lambda x: x.timestamp, reverse=True)
|
|
124
|
+
# 快照原始值
|
|
125
|
+
orig_strength = a.anchor_strength
|
|
126
|
+
# 模拟有/无最新证据时的 stability 差
|
|
127
|
+
old_count = len(evs_sorted) - 1
|
|
128
|
+
new_count = len(evs_sorted)
|
|
129
|
+
old_strength = a.recalc_strength(old_count)
|
|
130
|
+
new_strength = a.recalc_strength(new_count)
|
|
131
|
+
drifts.append(abs(new_strength - old_strength))
|
|
132
|
+
# 还原
|
|
133
|
+
a.anchor_strength = orig_strength
|
|
134
|
+
return sum(drifts) / len(drifts) if drifts else 0.0
|
|
135
|
+
|
|
136
|
+
|
|
137
|
+
def generalization_erosion_entropy(state: WorkspaceState) -> float:
|
|
138
|
+
"""泛化崩塌熵。0=一致性高, 1=完全不一致(CPE §1: Generalization erosion)
|
|
139
|
+
|
|
140
|
+
测量逻辑:同锚点下证据之间的关键词重叠度越低→一致性越差→崩塌风险越高。
|
|
141
|
+
"""
|
|
142
|
+
if not state.anchors:
|
|
143
|
+
return 0.0
|
|
144
|
+
erosion_scores = []
|
|
145
|
+
for a in state.anchors:
|
|
146
|
+
evs = [e for e in state.evidences if e.anchor_id == a.id]
|
|
147
|
+
if len(evs) < 2:
|
|
148
|
+
continue
|
|
149
|
+
# 两两计算重叠度
|
|
150
|
+
overlaps = []
|
|
151
|
+
for i in range(len(evs)):
|
|
152
|
+
for j in range(i + 1, len(evs)):
|
|
153
|
+
if evs[i].content and evs[j].content:
|
|
154
|
+
from .workspace import _keyword_overlap
|
|
155
|
+
overlaps.append(_keyword_overlap(evs[i].content, evs[j].content))
|
|
156
|
+
if overlaps:
|
|
157
|
+
avg_overlap = sum(overlaps) / len(overlaps)
|
|
158
|
+
# 重叠度越低 → 崩塌熵越高
|
|
159
|
+
erosion_scores.append(1.0 - avg_overlap)
|
|
160
|
+
return sum(erosion_scores) / len(erosion_scores) if erosion_scores else 0.0
|
|
161
|
+
|
|
162
|
+
|
|
163
|
+
def cpe_dimension_entropy(state: WorkspaceState) -> dict:
|
|
164
|
+
"""CPE 三维能力侵蚀熵汇总。"""
|
|
165
|
+
return {
|
|
166
|
+
"retrospective_decay": retrospective_decay_entropy(state),
|
|
167
|
+
"behavioral_drift": behavioral_drift_entropy(state),
|
|
168
|
+
"generalization_erosion": generalization_erosion_entropy(state),
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
|
|
172
|
+
# ==============================================================================
|
|
173
|
+
# 综合熵值(八维加权 — CPE三维 + 原六维去重后五维 = 八维)
|
|
174
|
+
# ==============================================================================
|
|
175
|
+
|
|
176
|
+
def calculate(state: WorkspaceState,
|
|
177
|
+
w_drift: float = 0.15, w_conflict: float = 0.10,
|
|
178
|
+
w_frag: float = 0.10, w_gap: float = 0.10,
|
|
179
|
+
w_decay: float = 0.10, w_strength: float = 0.10,
|
|
180
|
+
w_retro: float = 0.15, w_behavior: float = 0.10,
|
|
181
|
+
w_generalize: float = 0.10) -> float:
|
|
182
|
+
"""综合熵值 0.0-1.0(八维加权 — 新增CPE三维)
|
|
183
|
+
|
|
184
|
+
CPE 三维权重合计 0.35,与原五维(0.65)形成均衡。
|
|
185
|
+
"""
|
|
186
|
+
return (
|
|
187
|
+
w_drift * anchor_drift(state) +
|
|
188
|
+
w_conflict * conflict_density(state) +
|
|
189
|
+
w_frag * evidence_fragmentation(state) +
|
|
190
|
+
w_gap * activity_gap(state) +
|
|
191
|
+
w_decay * value_decay_entropy(state) +
|
|
192
|
+
w_strength * strength_entropy(state) +
|
|
193
|
+
w_retro * retrospective_decay_entropy(state) +
|
|
194
|
+
w_behavior * behavioral_drift_entropy(state) +
|
|
195
|
+
w_generalize * generalization_erosion_entropy(state)
|
|
196
|
+
)
|
|
197
|
+
|
|
198
|
+
|
|
199
|
+
def calculate_detail(state: WorkspaceState) -> dict:
|
|
200
|
+
"""返回八维详细熵值(用于 display 和诊断)"""
|
|
201
|
+
return {
|
|
202
|
+
"anchor_drift": anchor_drift(state),
|
|
203
|
+
"conflict_density": conflict_density(state),
|
|
204
|
+
"evidence_fragmentation": evidence_fragmentation(state),
|
|
205
|
+
"activity_gap": activity_gap(state),
|
|
206
|
+
"value_decay": value_decay_entropy(state),
|
|
207
|
+
"strength": strength_entropy(state),
|
|
208
|
+
"cpe_retrospective_decay": retrospective_decay_entropy(state),
|
|
209
|
+
"cpe_behavioral_drift": behavioral_drift_entropy(state),
|
|
210
|
+
"cpe_generalization_erosion": generalization_erosion_entropy(state),
|
|
211
|
+
"combined": calculate(state),
|
|
212
|
+
}
|
|
@@ -36,13 +36,15 @@ def load(workspace_root: Path) -> WorkspaceState:
|
|
|
36
36
|
def save(state: WorkspaceState, workspace_root: Path):
|
|
37
37
|
state.updated_at = now()
|
|
38
38
|
path = _ensure_dir(workspace_root) / STATE_FILE
|
|
39
|
-
|
|
40
|
-
# 审计:每次保存记录state快照哈希
|
|
39
|
+
# 审计链:记录并回写哈希
|
|
41
40
|
audit = get_audit_chain(workspace_root)
|
|
42
|
-
audit.append("state_save",
|
|
41
|
+
audit_hash = audit.append("state_save",
|
|
43
42
|
f"anchors={len(state.anchors)}_evidences={len(state.evidences)}"
|
|
44
43
|
f"_memories={len(state.memories)}_relations={len(state.relations)}"
|
|
45
44
|
)
|
|
45
|
+
state.audit_prev_hash = state.audit_chain_hash
|
|
46
|
+
state.audit_chain_hash = audit_hash
|
|
47
|
+
data = asdict(state)
|
|
46
48
|
with open(path, "w", encoding="utf-8") as f:
|
|
47
49
|
json.dump(data, f, indent=2, ensure_ascii=False)
|
|
48
50
|
|
|
@@ -55,8 +57,11 @@ def _from_dict(data: dict) -> WorkspaceState:
|
|
|
55
57
|
conflicts=[Conflict(**c) for c in data.get("conflicts", [])],
|
|
56
58
|
relations=[AnchorRelation(**r) for r in data.get("relations", [])],
|
|
57
59
|
snapshots=[StateSnapshot(**s) for s in data.get("snapshots", [])],
|
|
58
|
-
version=data.get("version", "0.
|
|
60
|
+
version=data.get("version", "0.4.0"),
|
|
59
61
|
updated_at=data.get("updated_at", ""),
|
|
60
62
|
audit_chain_hash=data.get("audit_chain_hash", "genesis"),
|
|
61
63
|
audit_prev_hash=data.get("audit_prev_hash", ""),
|
|
64
|
+
self_refine_probes=data.get("self_refine_probes", []),
|
|
65
|
+
self_refine_results=data.get("self_refine_results", []),
|
|
66
|
+
self_refine_repair_count=data.get("self_refine_repair_count", 0),
|
|
62
67
|
)
|