@furongjun1999/dsh-memory 0.4.7 → 0.4.8
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.
- package/README.md +92 -37
- package/codebuddy/CODEBUDDY.md +195 -185
- package/docs/Pi/345/217/257/345/255/246/344/271/240/344/274/230/347/202/271_/347/201/265/346/236/242/345/244/247/350/204/221/346/224/271/350/277/233/344/272/244/346/216/245_20260915.md +144 -0
- package/docs/README.md +111 -0
- package/docs/discipline/harnesses.yaml +215 -152
- package/docs/discipline/templates/full.md.tmpl +60 -58
- package/docs/eval//347/254/254/344/270/211/346/226/271/351/252/214/350/257/201/346/212/245/345/221/212_LoCoMo_/347/201/265/346/236/242_.md +127 -0
- package/docs/eval//347/254/254/344/270/211/346/226/271/351/252/214/350/257/201/346/212/245/345/221/212_LoCoMo_/347/201/265/346/236/242_.png +0 -0
- package/docs/eval//347/254/254/344/270/211/346/226/271/351/252/214/350/257/201/346/212/245/345/221/212_/347/201/265/346/236/242_vs_dejavu_/347/273/237/344/270/200/350/257/204/345/210/206_v7.md +202 -0
- package/docs/experiments/m4-role-probe/census.py +86 -0
- package/docs/experiments/m4-role-probe/probe.py +89 -0
- package/docs/experiments/mapped_confidence/bench_mapped_conf.py +216 -0
- package/docs/experiments/mapped_confidence/post_check.py +75 -0
- package/docs/experiments/mapped_confidence/repro_thirdparty_tol.py +83 -0
- package/docs/experiments/mapped_confidence/result_mapped_conf.json +215 -0
- package/docs/{ → hive/}/350/234/202/345/267/242/345/267/245/344/275/234/350/256/260/345/277/206_/351/241/271/347/233/256/350/256/241/345/210/222.md +14 -2
- package/docs/{README → mdcg/README}/350/257/246/347/273/206/347/211/210_v0.4.5.md +9 -9
- package/docs/{ → mdcg/}/344/270/273/344/273/243/347/220/206/345/255/220/344/273/243/347/220/206/350/256/260/345/277/206/346/236/266/346/236/204/350/256/276/350/256/241.md +1 -1
- package/docs/mdcg//345/212/237/350/203/275/350/260/203/347/224/250/346/230/240/345/260/204/350/241/250_v0.1.md +261 -0
- package/docs/{ → mdcg/}/345/215/225/345/205/203/350/207/252/346/210/221/351/224/232/347/202/271_/347/263/273/347/273/237/346/217/220/347/244/272/350/257/215/346/240/207/345/207/206_v0.3.md +1 -1
- package/docs/mdcg//345/255/220/344/273/243/347/220/206/351/205/215/347/275/256/346/240/207/345/207/206_v0.4.md +148 -0
- package/docs/{ → mdcg/}/346/272/220/347/240/201/347/272/247/346/236/266/346/236/204/345/256/241/350/256/241_GPT/346/211/271/350/257/204/345/257/271/347/205/247_v1.0.md +2 -2
- package/docs/{ → mdcg/}/347/201/265/346/236/242/344/270/211/345/261/202/346/213/206/345/210/206/350/247/204/345/210/222_v0.1.md +181 -181
- package/docs/{ → mdcg/}/347/274/272/345/217/243/345/215/225_P0/346/224/266/345/217/243_v0.1.md +2 -2
- package/docs/{ → mdcg/}/350/256/244/347/237/245/345/233/276_G4-G8/347/274/272/345/217/243/350/243/201/345/256/232/345/215/225_v0.1.md +1 -1
- package/docs/{ → mdcg/}/350/256/244/347/237/245/345/233/276_/347/264/242/345/274/225/344/270/216/345/267/245/347/250/213/350/247/204/350/214/203/345/214/226_/350/256/241/345/210/222_v0.1.md +4 -4
- package/docs/{ → mdcg/}/350/256/260/345/277/206/346/223/215/344/275/234/347/263/273/347/273/237_MdCGOS/344/270/216MCP/346/216/245/345/205/245_v0.1.md +2 -2
- package/docs/plans/GridWorld/346/234/200/345/260/217/351/227/255/347/216/257/350/247/204/346/240/274_v0.1.md +176 -0
- package/docs/{ → swarm/}/350/234/202/347/276/244/344/272/222/350/201/224_v0.1.md +3 -3
- package/docs/swarm//350/234/202/347/276/244/345/220/214/351/224/231/346/243/200/346/265/213/345/256/236/351/252/214/345/215/217/350/256/256_v0.1.md +242 -0
- package/docs/theory//345/215/225/347/272/277/347/250/213/344/270/216/346/263/250/346/204/217/345/212/233/351/233/206/344/270/255_/346/227/240/344/272/211/350/256/256/347/220/206/350/256/272/346/226/207/346/241/243_v1.0.md +129 -0
- package/docs/theory//345/271/266/345/217/221/345/277/205/347/204/266/346/200/247/347/220/206/350/256/272_v0.2.md +134 -0
- package/docs/theory//346/246/202/345/277/265/345/210/206/345/261/202/345/257/271/351/275/220/350/241/250_v0.1.md +145 -0
- package/docs/{ → theory/}/347/220/206/350/256/272_/346/234/272/345/210/266_/344/273/243/347/240/201_/345/256/236/351/252/214_/347/274/272/345/217/243/347/237/251/351/230/265_v0.1.md +3 -3
- package/docs/{ → theory/}/347/220/206/350/256/272/344/273/223/346/213/206/345/210/206/344/270/216/347/231/275/347/256/261/347/237/245/350/257/206/345/272/223/345/206/205/350/277/201_v0.1.md +1 -1
- package/docs/theory//350/256/244/347/237/245/344/273/243/347/220/206/344/270/216/346/224/266/346/225/233/347/273/223/346/236/204_/346/235/241/344/273/266/350/256/272/351/207/215/346/236/204_v0.1.md +221 -0
- package/docs/world_model//344/270/226/347/225/214/346/250/241/345/236/213_/347/245/236/347/273/217/347/275/221/347/273/234/345/272/225/345/261/202/346/236/266/346/236/204_v1.0.md +237 -0
- package/docs//345/255/230/347/256/227/344/270/200/344/275/223/346/236/266/346/236/204/345/255/246/344/271/240_/345/244/226/351/203/250/347/220/206/350/256/272/345/257/271/347/205/247/344/270/216/350/267/257/347/272/277/344/272/244/346/216/245_20260916.md +98 -0
- package/docs//345/255/230/347/256/227/344/270/200/344/275/223/347/245/236/347/273/217/347/275/221/347/273/234/346/236/266/346/236/204_/346/200/273/347/272/262/344/270/216/344/272/244/346/216/245_20260916.md +109 -0
- package/docs//345/267/245/344/275/234/347/272/252/345/276/213_/350/256/244/347/237/245/345/233/276/346/235/241/347/233/256_v1.1.json +34 -6
- package/docs//347/231/275/347/256/261/345/214/226/347/272/262/351/242/206_/344/270/211/351/241/271/347/233/256/345/255/246/344/271/240/346/200/273/346/236/266/346/236/204/344/270/216Ghidra/344/272/244/346/216/245_20260916.md +104 -0
- package/docs//350/256/260/345/277/206/350/257/204/345/256/241/347/263/273/347/273/237_/347/253/213/351/241/271/350/256/276/350/256/241/344/270/216/346/226/275/345/267/245/344/272/244/346/216/245_20260915.md +183 -0
- package/dsh/README.md +1 -1
- package/lib/hooks.d.ts +5 -0
- package/lib/hooks.js +10 -1
- package/lib/lib/prompt_safety.d.ts +48 -0
- package/lib/lib/prompt_safety.js +62 -0
- package/md_cg/audit.py +76 -2
- package/md_cg/bench_membench.py +10 -5
- package/md_cg/bench_progressive.py +48 -9
- package/md_cg/branches.py +275 -0
- package/md_cg/ccgc.py +884 -0
- package/md_cg/conformance.py +662 -0
- package/md_cg/consistency.py +1 -1
- package/md_cg/corpus.py +1 -1
- package/md_cg/eval_common.py +6 -5
- package/md_cg/forgetting.py +11 -3
- package/md_cg/fsutil.py +63 -1
- package/md_cg/identity.py +2 -2
- package/md_cg/insight.py +2 -2
- package/md_cg/lifecycle.py +262 -0
- package/md_cg/mcp_server.py +547 -140
- package/md_cg/mdcg.py +206 -18
- package/md_cg/mdcos.py +512 -50
- package/md_cg/metacognition.py +2 -2
- package/md_cg/mreview/__init__.py +25 -0
- package/md_cg/mreview/__main__.py +107 -0
- package/md_cg/mreview/bundle.py +170 -0
- package/md_cg/mreview/candidates.py +253 -0
- package/md_cg/mreview/govern.py +674 -0
- package/md_cg/mreview/locate.py +905 -0
- package/md_cg/mreview/pipeline.py +701 -0
- package/md_cg/mreview/rules/duplication.json +21 -0
- package/md_cg/mreview/rules/field_coverage.json +54 -0
- package/md_cg/mreview/rules/source_license.json +21 -0
- package/md_cg/mreview/rules/template_flow.json +21 -0
- package/md_cg/mreview/ruleset.py +238 -0
- package/md_cg/nodefile.py +38 -0
- package/md_cg/predict.py +24 -6
- package/md_cg/protect.py +4 -4
- package/md_cg/refindex.py +41 -2
- package/md_cg/scrub.py +5 -1
- package/md_cg/self_state.py +1 -1
- package/md_cg/semantic/en_normalizer.py +85 -25
- package/md_cg/sustain.py +18 -0
- package/md_cg/tasks.py +447 -0
- package/md_cg/test_action_derive.py +203 -0
- package/md_cg/test_audit_rotate.py +270 -0
- package/md_cg/test_branches.py +249 -0
- package/md_cg/test_ccg_perturb.py +1 -1
- package/md_cg/test_ccgc.py +423 -0
- package/md_cg/test_conformance.py +343 -0
- package/md_cg/test_en_pipeline.py +24 -0
- package/md_cg/test_health_scale.py +173 -0
- package/md_cg/test_identity_attribution.py +6 -0
- package/md_cg/test_index_durability.py +224 -0
- package/md_cg/test_lifecycle.py +309 -0
- package/md_cg/test_mr_m1.py +679 -0
- package/md_cg/test_mr_m2.py +587 -0
- package/md_cg/test_mr_m3.py +703 -0
- package/md_cg/test_mr_m4.py +485 -0
- package/md_cg/test_p1.py +1 -1
- package/md_cg/test_p11_consistency.py +1 -1
- package/md_cg/test_p12_metacognition.py +2 -2
- package/md_cg/test_p16_self_state.py +1 -1
- package/md_cg/test_p26_refindex.py +1 -1
- package/md_cg/test_p27_docindex.py +22 -2
- package/md_cg/test_p28_refcheck.py +1 -1
- package/md_cg/test_p29_session_ingest_export.py +1 -1
- package/md_cg/test_p2_mcp.py +7 -1
- package/md_cg/test_p38_contextualize.py +1 -1
- package/md_cg/test_p39_vision_evidence.py +1 -1
- package/md_cg/test_p40_refine_worklist.py +1 -1
- package/md_cg/test_p41_evolve_patrol.py +1 -1
- package/md_cg/test_p42_provenance.py +1 -1
- package/md_cg/test_p43_pooling.py +41 -13
- package/md_cg/test_read_clip.py +137 -0
- package/md_cg/test_review_conformance.py +310 -0
- package/md_cg/test_tasks.py +409 -0
- package/md_cg/test_tool_face.py +189 -0
- package/md_cg/test_twophase.py +286 -0
- package/md_cg/test_writepipe.py +210 -0
- package/md_cg/theory.py +1 -1
- package/md_cg/tokens.py +95 -5
- package/md_cg/tool_face.py +250 -0
- package/md_cg/twophase.py +221 -0
- package/md_cg/units.py +546 -0
- package/md_cg/vision_evidence.py +1 -1
- package/md_cg/weights.py +1 -1
- package/md_cg/whitebox.py +1 -1
- package/md_cg/whitebox_kb/data/verify_cache.json +28 -0
- package/md_cg/whitebox_kb/data/verify_savings.jsonl +46 -0
- package/md_cg/whitebox_kb/wisdom/audit_log/chain_heat.json +10 -10
- package/md_cg/whitebox_kb/wisdom/wisdom-book-cloud.db-shm +0 -0
- package/md_cg/whitebox_kb/wisdom/wisdom-book-cloud.db-wal +0 -0
- package/md_cg/writelimit.py +19 -3
- package/md_cg/writepipe.py +372 -0
- package/package.json +1 -1
- package/src/hooks.ts +10 -1
- package/src/lib/prompt_safety.ts +62 -0
- package/zcode/AGENTS.md +195 -185
- package/docs//345/212/237/350/203/275/350/260/203/347/224/250/346/230/240/345/260/204/350/241/250_v0.1.md +0 -221
- /package/docs/{AGI → eval/AGI}/344/270/203/347/273/264/350/257/204/345/210/206/346/212/245/345/221/212_md_cg_v1.0.md" +0 -0
- /package/docs/{AGI → eval/AGI}/344/270/203/347/273/264/350/257/204/345/210/206/346/212/245/345/221/212_md_cg_v2.0.md" +0 -0
- /package/docs/{ → eval/}/345/256/236/351/252/214/346/226/271/346/241/210_/345/255/246/344/271/240/351/227/255/347/216/257AB/344/270/216/346/250/252/350/257/204_v1.0.md" +0 -0
- /package/docs/{ → eval/}/346/250/252/350/257/204_/345/205/255/345/256/266100/351/242/230/344/270/255/350/213/261/345/217/214/346/237/245_v1.0.md" +0 -0
- /package/docs/{guardrail-charter.md → mdcg/guardrail-charter.md} +0 -0
- /package/docs/{lingshu_tutorial.html → mdcg/lingshu_tutorial.html} +0 -0
- /package/docs/{memory-assessment.html → mdcg/memory-assessment.html} +0 -0
- /package/docs/{memory_score.html → mdcg/memory_score.html} +0 -0
- /package/docs/{memory_score.png → mdcg/memory_score.png} +0 -0
- /package/docs/{release_v0.3.0.md → mdcg/release_v0.3.0.md} +0 -0
- /package/docs/{release_v0.4.5.md → mdcg/release_v0.4.5.md} +0 -0
- /package/docs/{tool_table_v0.3.0.md → mdcg/tool_table_v0.3.0.md} +0 -0
- /package/docs/{ → mdcg/}/344/273/244/347/211/214/344/270/216/350/247/222/350/211/262/346/235/203/350/201/214/345/210/206/347/246/273_v0.1.md" +0 -0
- /package/docs/{ → mdcg/}/347/201/265/346/236/24282/345/267/245/345/205/267_/345/212/237/350/203/275/346/225/264/347/220/206/344/270/216/350/277/201/347/247/273/346/230/240/345/260/204_v0.1.md" +0 -0
- /package/docs/{ → mdcg/}/347/201/265/346/236/242MCP/345/267/245/345/205/267/346/200/273/350/241/250_v3.4.md" +0 -0
- /package/docs/{ → mdcg/}/347/201/265/346/236/242_/350/207/252/346/210/221/345/261/202/345/256/232/344/271/211.md" +0 -0
- /package/docs/{ → mdcg/}/350/256/244/347/237/245/345/233/276_MD/347/233/256/345/275/225/346/226/271/346/241/210_v0.1.md" +0 -0
- /package/docs/{ → mdcg/}/350/256/244/347/237/245/345/233/276_/346/235/241/344/273/266/347/251/272/351/227/264/345/220/210/346/210/220/344/270/216/347/224/237/346/225/210/346/235/241/344/273/266/345/217/243/345/276/204_v0.1.md" +0 -0
- /package/docs/{ → mdcg/}/350/256/244/347/237/245/345/233/276/344/275/234/344/270/272/350/256/260/345/277/206/346/223/215/344/275/234/347/263/273/347/273/237_/350/257/204/344/274/260/344/270/216/350/267/257/347/272/277_v0.1.md" +0 -0
- /package/docs/{ → plans/}/345/221/275/345/220/215/346/262/273/347/220/206_/351/241/271/347/233/256/350/256/241/345/210/222.md" +0 -0
- /package/docs/{ → plans/}/347/237/245/350/257/206/345/233/276/350/260/261/351/241/271/347/233/256_/344/272/244/346/216/245/346/226/207/346/241/243_20260914.md" +0 -0
- /package/docs/{ → swarm/}/350/234/202/347/276/244/345/244/232/346/231/272/350/203/275/344/275/223_/344/275/277/347/224/250/346/212/245/345/221/212_20260913.md" +0 -0
- /package/docs/{ → swarm/}/350/234/202/347/276/244/345/244/232/346/231/272/350/203/275/344/275/223_/345/212/237/350/203/275/350/257/264/346/230/216_v0.6.md" +0 -0
- /package/docs/{ → swarm/}/350/234/202/347/276/244/347/233/262/345/214/272/346/240/207/350/256/260_v1.0.md" +0 -0
- /package/docs/{ → theory/}/344/277/241/346/201/257/345/267/256/344/270/272/344/273/200/344/271/210/345/277/205/347/204/266/345/255/230/345/234/250/344/270/224/350/207/252/347/204/266/346/211/251/345/244/247.md" +0 -0
- /package/docs/{ → theory/}/346/231/272/350/203/275/347/232/204/345/205/254/347/220/206/345/214/226/345/237/272/347/237/263.md" +0 -0
- /package/docs/{ → theory/}/346/231/272/350/203/275/347/232/204/350/256/244/347/237/245/350/277/207/347/250/213.md" +0 -0
- /package/docs/{ → theory/}/346/231/272/350/203/275/350/256/2723.4.md" +0 -0
- /package/docs/{ → theory/}/347/231/275/347/256/261/346/231/272/350/203/275/346/230/257/344/273/200/344/271/210/357/274/237.md" +0 -0
- /package/docs/{ → theory/}/347/231/275/347/256/261/346/231/272/350/203/275/347/263/273/345/210/227/302/267/347/254/254/344/272/224/347/257/207/357/274/232/350/256/251AI/347/234/237/346/255/243/350/243/205/344/270/212/350/256/260/345/277/206.md" +0 -0
package/md_cg/mdcg.py
CHANGED
|
@@ -20,10 +20,13 @@ import os
|
|
|
20
20
|
import re
|
|
21
21
|
import json
|
|
22
22
|
import time
|
|
23
|
+
import atexit
|
|
24
|
+
import weakref
|
|
23
25
|
import hashlib
|
|
24
26
|
import threading
|
|
25
27
|
|
|
26
|
-
from . import nodefile, protect, routing, subgraph, chain, provenance, pooling
|
|
28
|
+
from . import (nodefile, protect, routing, subgraph, chain, provenance, pooling,
|
|
29
|
+
lifecycle)
|
|
27
30
|
from .fsutil import (FileLock, ShardedLog, atomic_write, append_jsonl,
|
|
28
31
|
read_jsonl, sweep_stale_temps)
|
|
29
32
|
|
|
@@ -57,6 +60,69 @@ DEFAULT_RECENT_WINDOW = 200
|
|
|
57
60
|
# 全量回退的读取上限,对齐 sqlite 版 `ORDER BY importance DESC, created_at DESC LIMIT 500`
|
|
58
61
|
GLOBAL_CAP = 500
|
|
59
62
|
|
|
63
|
+
# ---------- 索引持久化 · 进程退出兜底(2026-09-16 取证) ----------
|
|
64
|
+
# 脏索引(`_dirty`)靠调用方显式 flush()/close() 落分片日志。一次性脚本/CLI
|
|
65
|
+
# (review_cli、backfill、migrate_* 等)写入 1~2 条后直接退出,未达 autoflush(64)
|
|
66
|
+
# 阈值 → 日志无记录;而**已有 `_index.json` 的根重开时不重扫目录**,于是节点
|
|
67
|
+
# 「在盘上但索引无条目」:其它进程与重载后的长驻进程都检索不到,只能靠某次全量
|
|
68
|
+
# rebuild_index() 偶然救回(首例=审核裁决通路,见 mdcos.review_decide)。
|
|
69
|
+
# 兜底语义(刻意收窄):**正常进程退出时**对所有存活实例补一次 close()——
|
|
70
|
+
# 幂等(`_dirty` 为空即 no-op)、不改变正常路径行为、不替代显式收尾
|
|
71
|
+
# (异常退出 / 被 kill 仍须调用方自己保证)。WeakSet 不阻止实例回收。
|
|
72
|
+
_LIVE_CGS = weakref.WeakSet()
|
|
73
|
+
_ATEXIT_HOOK = None
|
|
74
|
+
|
|
75
|
+
|
|
76
|
+
def _atexit_flush_all():
|
|
77
|
+
"""进程退出兜底:把仍存活实例的脏索引落盘(异常吞掉——退出路径不该再抛)。"""
|
|
78
|
+
for cg in list(_LIVE_CGS):
|
|
79
|
+
try:
|
|
80
|
+
cg.close()
|
|
81
|
+
except Exception: # noqa: BLE001
|
|
82
|
+
pass
|
|
83
|
+
|
|
84
|
+
|
|
85
|
+
def _register_atexit_hook():
|
|
86
|
+
global _ATEXIT_HOOK
|
|
87
|
+
if _ATEXIT_HOOK is None:
|
|
88
|
+
atexit.register(_atexit_flush_all)
|
|
89
|
+
_ATEXIT_HOOK = _atexit_flush_all
|
|
90
|
+
return _ATEXIT_HOOK
|
|
91
|
+
|
|
92
|
+
|
|
93
|
+
def cut_by_relevance(docs, scored, total, pools=None, key_of=None, stat=None):
|
|
94
|
+
"""候选截断:**先按相关度排序,再截断到 `total`**(截断依据=相关度)。
|
|
95
|
+
|
|
96
|
+
动机(真库实测 2026-09-16,11521 节点 / cap=500):原实现是「LIKE 命中集
|
|
97
|
+
沿 `entries`(目录枚举序)取前 `total` 条 → 再打分」——命中集超上限时,
|
|
98
|
+
等价于用**写入顺序抽签**决定谁进候选池。实测 40 个抽样查询中 27 个命中数
|
|
99
|
+
超 cap(最差 10963/11073 ≈ 99%),被现状丢弃的候选平均分 0.650、被保留的
|
|
100
|
+
平均分 0.340,gold 进 top10 由 19/40 降至 29/40。
|
|
101
|
+
|
|
102
|
+
本函数把 cap 语义从「候选生成上限」降级为「输出上限」:候选质量责任交还
|
|
103
|
+
排序(与 `mdcos._lexical` 同源先例一致),**cap 值本身不变**。
|
|
104
|
+
|
|
105
|
+
`docs` 与 `scored` 必须同序同长(`MdCG._score` 输出序=输入序)。不一致时
|
|
106
|
+
不猜:退回原序截断,并在 `stat["cut_order"]` 标 `insert_fallback` 供审计。
|
|
107
|
+
分池生效时在**分数序**内按池额度截断(池内原序=分数序)。
|
|
108
|
+
返回 `(picked, report)`。
|
|
109
|
+
"""
|
|
110
|
+
if len(scored) != len(docs):
|
|
111
|
+
if stat is not None:
|
|
112
|
+
stat["cut_order"] = "insert_fallback"
|
|
113
|
+
return pooling.cut_report(docs, total, pools=pools,
|
|
114
|
+
key_of=key_of or pooling.doc_key)
|
|
115
|
+
order = sorted(
|
|
116
|
+
range(len(docs)),
|
|
117
|
+
key=lambda i: (-float(scored[i][1]),
|
|
118
|
+
-float(scored[i][0]["frontmatter"].get("importance") or 0),
|
|
119
|
+
-float(scored[i][0]["frontmatter"].get("created_at") or 0)))
|
|
120
|
+
ranked = [docs[i] for i in order]
|
|
121
|
+
if stat is not None:
|
|
122
|
+
stat["cut_order"] = "relevance"
|
|
123
|
+
return pooling.cut_report(ranked, total, pools=pools,
|
|
124
|
+
key_of=key_of or pooling.doc_key)
|
|
125
|
+
|
|
60
126
|
# 条件论「观测时间」栏的默认观测窗口(秒):调用方未提供 time_window 时,
|
|
61
127
|
# 以写入时刻为锚开一个 1 小时窗口(与 AEIS 既有约定一致)。
|
|
62
128
|
OBSERVATION_WINDOW_SEC = 3600.0
|
|
@@ -350,8 +416,10 @@ def normalize_en(text: str) -> str:
|
|
|
350
416
|
# **评测侧**改用 eval_common.use_jaccard() 显式注入——长 turn 语料上
|
|
351
417
|
# precise 0%→8.97% / temporal 0%→6.77% / interference 1.28%→20.51% /
|
|
352
418
|
# reference 0%→3.76%(与 Rust mdcg-eval --score jaccard 逐位一致)。
|
|
353
|
-
#
|
|
354
|
-
#
|
|
419
|
+
# 主库不切:收益随文档长度单调增长,短条目语料上无增益(该语料本无长度偏置)。
|
|
420
|
+
# 另注:test_p17_predict G4 曾出现的「可预测锚点由因果起点 a 错配到语义邻居 x」
|
|
421
|
+
# 已由 predict.anchor_from_description 的**可起推资格**修复(2026-09-16,出边
|
|
422
|
+
# 非空优先),锚点漂移不再是切换打分口径的理由;口径切换仍按上列数据收益决定。
|
|
355
423
|
SCORE_MODES = ("legacy", "jaccard")
|
|
356
424
|
SCORE_MODE = os.environ.get("MDCG_SCORE_MODE") or "legacy"
|
|
357
425
|
|
|
@@ -493,6 +561,9 @@ class MdCG:
|
|
|
493
561
|
self._log = None
|
|
494
562
|
self.index = self._load_index()
|
|
495
563
|
sweep_stale_temps(self.root)
|
|
564
|
+
# 进程退出兜底登记(见模块级 _LIVE_CGS):一次性脚本漏收尾时索引仍能落盘。
|
|
565
|
+
_LIVE_CGS.add(self)
|
|
566
|
+
_register_atexit_hook()
|
|
496
567
|
|
|
497
568
|
# ---------- 索引(派生物,可重建) ----------
|
|
498
569
|
|
|
@@ -510,7 +581,13 @@ class MdCG:
|
|
|
510
581
|
idx = {"schema": SCHEMA, "nodes": self._scan_nodes(), "buckets": {}}
|
|
511
582
|
for rec in ShardedLog.read_all(self.index_log_dir):
|
|
512
583
|
nid, e = rec.get("id"), rec.get("e")
|
|
513
|
-
if nid
|
|
584
|
+
if not nid:
|
|
585
|
+
continue
|
|
586
|
+
if e is None:
|
|
587
|
+
# 删除记录(tombstone):删除必须能重放,否则已删节点会在下次
|
|
588
|
+
# 启动时从旧记录里复活成**幽灵条目**(索引有条目、文件不存在)。
|
|
589
|
+
idx["nodes"].pop(nid, None)
|
|
590
|
+
else:
|
|
514
591
|
idx["nodes"][nid] = e
|
|
515
592
|
idx["buckets"] = self._count_buckets(idx["nodes"])
|
|
516
593
|
return idx
|
|
@@ -537,7 +614,11 @@ class MdCG:
|
|
|
537
614
|
pass
|
|
538
615
|
for rec in ShardedLog.read_all(self.index_log_dir):
|
|
539
616
|
nid, e = rec.get("id"), rec.get("e")
|
|
540
|
-
if nid
|
|
617
|
+
if not nid:
|
|
618
|
+
continue
|
|
619
|
+
if e is None: # 删除记录(tombstone),见 _load_index
|
|
620
|
+
idx["nodes"].pop(nid, None)
|
|
621
|
+
else:
|
|
541
622
|
idx["nodes"][nid] = e
|
|
542
623
|
idx["buckets"] = self._count_buckets(idx["nodes"])
|
|
543
624
|
atomic_write(self.index_path, json.dumps(idx, ensure_ascii=False))
|
|
@@ -596,12 +677,19 @@ class MdCG:
|
|
|
596
677
|
"created_at": fm.get("created_at", 0),
|
|
597
678
|
"verification_basis": fm.get("verification_basis"),
|
|
598
679
|
"has_neg_conditions": nodefile.has_non_applicable(content),
|
|
599
|
-
|
|
600
|
-
|
|
680
|
+
# 内容指纹走 nodefile 的唯一实现(两段式对账依赖同一算法)
|
|
681
|
+
"content_hash": nodefile.content_hash(content),
|
|
601
682
|
# 时空字段入索引快照:STG 查询免读文件(大域/目录索引的延伸)
|
|
602
683
|
"temporal": fm.get("temporal"),
|
|
603
684
|
"spatial": fm.get("spatial"),
|
|
604
685
|
"time_window": (fm.get("condition_space") or {}).get("time_window"),
|
|
686
|
+
# 生命周期状态(② 显式状态机):索引入快照 → 免读文件可查,
|
|
687
|
+
# 写入路径也因此无需读盘就能校验迁移合法性。重建口径与
|
|
688
|
+
# _stage 一致(旧库无该字段 → None → state_of 视为 active)。
|
|
689
|
+
lifecycle.STATE_FIELD: fm.get(lifecycle.STATE_FIELD),
|
|
690
|
+
# 记忆演化分支(④):重建口径与 _stage 一致
|
|
691
|
+
"branch_id": fm.get("branch_id"),
|
|
692
|
+
"branched_from": fm.get("branched_from"),
|
|
605
693
|
"evidence_count": fm.get("evidence_count", 0),
|
|
606
694
|
# 嵌套子图 / 关系边入索引快照:递归展开与链式遍历免读文件
|
|
607
695
|
"subgraph": fm.get("subgraph"),
|
|
@@ -722,6 +810,41 @@ class MdCG:
|
|
|
722
810
|
"evidence_count": 0, "positive_evidence": 0, "negative_evidence": 0,
|
|
723
811
|
}
|
|
724
812
|
fm.update(extra)
|
|
813
|
+
# 生命周期状态(② 显式状态机,真源 `lifecycle.py`):add 是**全量重建
|
|
814
|
+
# fm** 而非增量更新,故必须显式处理状态——否则已定型/已降权节点会被静默
|
|
815
|
+
# 打回 active(与 ④ rewrite 必须重传 branch_id 同构的坑)。口径:
|
|
816
|
+
# · 新节点:显式落 active(不再依赖「缺省即 active」的隐式约定);
|
|
817
|
+
# · 覆写既有节点:**继承旧状态**;显式传入 state 时走迁移裁决,非法迁移
|
|
818
|
+
# **拒绝**(负路由:抛 TransitionError,与非法层名同风格)。
|
|
819
|
+
# 取旧状态优先读**索引快照**(免读盘);索引缺该键(升级前的旧库)才回读
|
|
820
|
+
# 节点文件兜底——不兜底会把存量 converged/demoted 节点误判成 active。
|
|
821
|
+
prev_entry = (self.index.get("nodes") or {}).get(node_id)
|
|
822
|
+
prev_state = lifecycle.state_of(prev_entry)
|
|
823
|
+
if prev_entry and lifecycle.STATE_FIELD not in prev_entry:
|
|
824
|
+
prev_state = lifecycle.state_of(
|
|
825
|
+
(self.get(node_id) or {}).get("frontmatter"))
|
|
826
|
+
# 显式入口兼容两种写法:`state=`(调用方直觉)与 `lifecycle_state=`。
|
|
827
|
+
# `state` 必须 **pop 掉**——该键名已属裁决四态(ACCEPT/REJECT/DEFER/
|
|
828
|
+
# BLINDSPOT),落进 frontmatter 只会在读面制造同名歧义;归一到真字段名后
|
|
829
|
+
# 再走同一裁决。两者同时给出时以 `lifecycle_state=` 为准(字段名更明确)。
|
|
830
|
+
_alias = fm.pop("state", None)
|
|
831
|
+
want_state = fm.get(lifecycle.STATE_FIELD)
|
|
832
|
+
if want_state is None:
|
|
833
|
+
want_state = _alias
|
|
834
|
+
if want_state is None:
|
|
835
|
+
fm[lifecycle.STATE_FIELD] = prev_state
|
|
836
|
+
else:
|
|
837
|
+
# 受保护判定取**索引快照**(兼具两层含义):本次 fm 里的保护标记、以及
|
|
838
|
+
# 节点**既有**的保护(快照带 protected/immutable)。只看本次 fm 会漏掉
|
|
839
|
+
# 「既有受保护节点被覆写时降级」——保护 = 不可遗忘,不因一次 add 失守。
|
|
840
|
+
# 注:该参数只在降级迁移上生效(check 内按方向判定),回升不受限。
|
|
841
|
+
_pe = prev_entry or {}
|
|
842
|
+
lifecycle.require_transition(
|
|
843
|
+
prev_state, want_state,
|
|
844
|
+
protected=bool(fm.get("protected") or fm.get("immutable")
|
|
845
|
+
or _pe.get("protected") or _pe.get("immutable")),
|
|
846
|
+
override=override)
|
|
847
|
+
fm[lifecycle.STATE_FIELD] = want_state
|
|
725
848
|
# G8 派生溯源:把「来源声明」写进 frontmatter(单一真相源),台账为派生物。
|
|
726
849
|
# 只声明事实、不做校验式拒绝——关系名非法仅回退默认值,不阻断写入。
|
|
727
850
|
parents = provenance.as_list(derived_from)
|
|
@@ -759,6 +882,11 @@ class MdCG:
|
|
|
759
882
|
"writer": fm.get("writer"),
|
|
760
883
|
"session": fm.get("session"),
|
|
761
884
|
"harness": fm.get("harness"),
|
|
885
|
+
# 记忆演化分支(④):fork 副本带分支归属与溯源主支
|
|
886
|
+
"branch_id": fm.get("branch_id"),
|
|
887
|
+
"branched_from": fm.get("branched_from"),
|
|
888
|
+
# 生命周期状态(②):索引快照透出 → 免读文件可查(与 _scan_nodes 同口径)
|
|
889
|
+
lifecycle.STATE_FIELD: fm.get(lifecycle.STATE_FIELD),
|
|
762
890
|
})
|
|
763
891
|
subgraph.invalidate_cache(self)
|
|
764
892
|
chain.invalidate_cache(self)
|
|
@@ -771,6 +899,32 @@ class MdCG:
|
|
|
771
899
|
actor=extra.get("actor"))
|
|
772
900
|
return node_id
|
|
773
901
|
|
|
902
|
+
def set_state(self, node_id: str, dst: str, reason: str = None,
|
|
903
|
+
actor: str = None, override: bool = False) -> dict:
|
|
904
|
+
"""节点生命周期状态推进(② 显式状态机:**唯一推进入口**,见 lifecycle.py)。
|
|
905
|
+
|
|
906
|
+
非法迁移**拒绝**(返回 `ok=False` + `error` 机器码,不抛异常——负路由);
|
|
907
|
+
受保护节点(`protected`/`immutable`)不接受降级,需 `override=True`。
|
|
908
|
+
状态与迁移历史落 frontmatter,索引快照同步,审计追加 `_lifecycle.jsonl`。
|
|
909
|
+
"""
|
|
910
|
+
return lifecycle.set_state(self, node_id, dst, reason=reason,
|
|
911
|
+
actor=actor, override=override)
|
|
912
|
+
|
|
913
|
+
def reconcile_writes(self, apply: bool = True, limit: int = 2000) -> dict:
|
|
914
|
+
"""写入两段式对账(③ 两段式提交):把崩溃遗留的半途写入**补账或标记**。
|
|
915
|
+
|
|
916
|
+
幂等(补出的 outcome 与正常 outcome 同形,二次扫描无未配对项);
|
|
917
|
+
`apply=False` 为只读盘点。返回 `{"unpaired","committed","interrupted",
|
|
918
|
+
"applied","details"}`;账本为 root 下 `_write_2pc.jsonl`(append-only)。
|
|
919
|
+
"""
|
|
920
|
+
from . import twophase # 延迟导入:与写路径解耦,避免模块环
|
|
921
|
+
return twophase.reconcile(self, apply=apply, limit=limit)
|
|
922
|
+
|
|
923
|
+
def pending_writes(self, limit: int = 200) -> list:
|
|
924
|
+
"""未结清的写入意图(有 intent 无 outcome)——只读,不改账本。"""
|
|
925
|
+
from . import twophase
|
|
926
|
+
return twophase.pending(self, limit=limit)
|
|
927
|
+
|
|
774
928
|
# ------------------------------------------------------------------
|
|
775
929
|
# 边域窄原语(写路径收口:白箱工具写 md 真源的唯一正路)。
|
|
776
930
|
#
|
|
@@ -1077,6 +1231,26 @@ class MdCG:
|
|
|
1077
1231
|
if len(self._dirty) >= self.autoflush:
|
|
1078
1232
|
self.flush()
|
|
1079
1233
|
|
|
1234
|
+
def _unstage(self, node_id):
|
|
1235
|
+
"""摘除索引条目并**持久化**——与 `_stage` 对称的删除原语。
|
|
1236
|
+
|
|
1237
|
+
只 pop 内存索引是不够的:`close()` 注释里记过同构的坑(已有
|
|
1238
|
+
`_index.json` 的根重开不重扫目录),于是「删掉的条目」会在
|
|
1239
|
+
`_index_log` 重放时复活成**幽灵条目**(索引有条目、节点文件不存在)。
|
|
1240
|
+
幽灵条目的代价:检索白跑候选、`ref action=prune` 因 `cg.get` 取不回
|
|
1241
|
+
而够不着它,`check` 的 dangling 永不归零(本机实测累积数百条)。
|
|
1242
|
+
"""
|
|
1243
|
+
e = self.index["nodes"].pop(node_id, None)
|
|
1244
|
+
if e and e.get("bucket"):
|
|
1245
|
+
b = e["bucket"]
|
|
1246
|
+
left = self.index["buckets"].get(b, 0) - 1
|
|
1247
|
+
if left > 0:
|
|
1248
|
+
self.index["buckets"][b] = left
|
|
1249
|
+
else:
|
|
1250
|
+
self.index["buckets"].pop(b, None)
|
|
1251
|
+
self._dirty[node_id] = None # None = 删除记录,随 flush 落分片日志
|
|
1252
|
+
self.flush() # 删除不可延迟到 autoflush 阈值
|
|
1253
|
+
|
|
1080
1254
|
# ---------- 内容封装钩子(默认恒等;MdCGSecure 覆盖为「私有内容加密」)----
|
|
1081
1255
|
|
|
1082
1256
|
def _seal_content(self, node_id: str, content: str,
|
|
@@ -1247,7 +1421,7 @@ class MdCG:
|
|
|
1247
1421
|
def search(self, query: str, layer: str = None, k: int = 20,
|
|
1248
1422
|
context=None, min_results: int = 1, record: bool = True,
|
|
1249
1423
|
include_neg: bool = True, judge: bool = True, pools=None,
|
|
1250
|
-
session=None):
|
|
1424
|
+
session=None, branch=None):
|
|
1251
1425
|
"""返回 (results, meta)。results = [(node_dict, score, qualification)]。
|
|
1252
1426
|
|
|
1253
1427
|
meta 含 tier(性能层级)、scanned(读取节点数)、bucket(路由桶)、candidates。
|
|
@@ -1275,7 +1449,10 @@ class MdCG:
|
|
|
1275
1449
|
# 默认排除掉负记忆层的节点进入正排打分,仅作为「覆盖标记」用
|
|
1276
1450
|
entries = [e for e in self.index["nodes"].values()
|
|
1277
1451
|
if (not layer or e["layer"] == layer)
|
|
1278
|
-
and (not session or e.get("session") == session)
|
|
1452
|
+
and (not session or e.get("session") == session)
|
|
1453
|
+
# 分支实验场:默认(branch=None)分支节点全部隐身;
|
|
1454
|
+
# branch=<id> 时主支 + 本分支可见、其他分支仍隐身
|
|
1455
|
+
and e.get("branch_id") in (None, branch)]
|
|
1279
1456
|
if not entries:
|
|
1280
1457
|
return [], {"tier": None, "reason": "no_candidates", "scanned": 0}
|
|
1281
1458
|
|
|
@@ -1334,26 +1511,30 @@ class MdCG:
|
|
|
1334
1511
|
return out
|
|
1335
1512
|
|
|
1336
1513
|
# T2:跨桶 LIKE(§七 截断点:分池截断,索引类不再挤掉知识类)
|
|
1514
|
+
# 截断依据=相关度(先全量打分再排序截断):命中集沿 entries(目录枚举序)
|
|
1515
|
+
# 排列,原来「取前 GLOBAL_CAP 条再打分」等价于用写入顺序抽签决定谁进
|
|
1516
|
+
# 候选池。cap 值不变,变的只是拿什么排序(见 cut_by_relevance)。
|
|
1337
1517
|
docs_all = self._read_many(entries, stat)
|
|
1338
1518
|
# 语义资格(MDCG_SEMANTIC=1):fm.semantic 节点无条件入池
|
|
1339
1519
|
hits = [d for d in docs_all if self._like(d[2], d[1], terms)
|
|
1340
1520
|
or (semantic_on() and d[1].get("semantic"))]
|
|
1341
1521
|
stat["pre_cap"] = len(hits)
|
|
1342
1522
|
stat["cap"] = GLOBAL_CAP
|
|
1343
|
-
hits, _rep =
|
|
1344
|
-
|
|
1523
|
+
hits, _rep = cut_by_relevance(hits, self._score(hits, q, qb, pool_cfg),
|
|
1524
|
+
GLOBAL_CAP, pools=pool_cfg,
|
|
1525
|
+
key_of=pooling.doc_key, stat=stat)
|
|
1345
1526
|
pooling.record_audit(stat, _rep)
|
|
1346
1527
|
out = try_stage(hits, TIER_GLOBAL_LIKE)
|
|
1347
1528
|
if out:
|
|
1348
1529
|
return out
|
|
1349
1530
|
|
|
1350
|
-
# T3
|
|
1351
|
-
docs_all.sort(key=lambda d: (-float(d[1].get("importance") or 0),
|
|
1352
|
-
-float(d[1].get("created_at") or 0)))
|
|
1531
|
+
# T3:全量兜底(同为分池截断点;截断依据同为相关度,importance 作次级键)
|
|
1353
1532
|
stat["pre_cap"] = len(docs_all)
|
|
1354
1533
|
stat["cap"] = GLOBAL_CAP
|
|
1355
|
-
picked, _rep =
|
|
1356
|
-
|
|
1534
|
+
picked, _rep = cut_by_relevance(docs_all,
|
|
1535
|
+
self._score(docs_all, q, qb, pool_cfg),
|
|
1536
|
+
GLOBAL_CAP, pools=pool_cfg,
|
|
1537
|
+
key_of=pooling.doc_key, stat=stat)
|
|
1357
1538
|
pooling.record_audit(stat, _rep)
|
|
1358
1539
|
scored = self._score(picked, q, qb, pool_cfg)
|
|
1359
1540
|
return self._emit(scored, k, TIER_GLOBAL_SCAN, stat, route_bucket,
|
|
@@ -1461,6 +1642,8 @@ class MdCG:
|
|
|
1461
1642
|
"candidates": candidates,
|
|
1462
1643
|
# §七 分池审计:截断前候选数 / 全局额度 / 生效分池计划
|
|
1463
1644
|
"pre_cap": stat.get("pre_cap"), "cap": stat.get("cap"),
|
|
1645
|
+
# 截断依据审计:relevance=先打分再排序截断(现行)
|
|
1646
|
+
"cut_order": stat.get("cut_order"),
|
|
1464
1647
|
"pools": pool_plan,
|
|
1465
1648
|
"covered_neg": [nc["path"] for nc in neg_coverage],
|
|
1466
1649
|
# 阶段 1 大域收敛结果 + 完整打分明细(白箱可审计)
|
|
@@ -1599,8 +1782,7 @@ class MdCG:
|
|
|
1599
1782
|
verification_basis="test")
|
|
1600
1783
|
# 从原位置删除(节点进入 rejected 层)
|
|
1601
1784
|
os.remove(os.path.join(self.root, node["path"]))
|
|
1602
|
-
self.
|
|
1603
|
-
self._dirty.pop(node_id, None)
|
|
1785
|
+
self._unstage(node_id) # 同上:索引删除必须可重放(防幽灵条目)
|
|
1604
1786
|
return {"action": "falsified", "new_id": None,
|
|
1605
1787
|
"evidence_count": None, "demoted": None}
|
|
1606
1788
|
# confirmed/weakened:调整 confidence(白箱第 5 篇:
|
|
@@ -1622,6 +1804,12 @@ class MdCG:
|
|
|
1622
1804
|
demoted = self._move_layer(
|
|
1623
1805
|
node_id, "contextual",
|
|
1624
1806
|
reason=f"confidence {fm['confidence']} < {DEMOTE_CONFIDENCE}")
|
|
1807
|
+
# 层降级 = 生命周期降级(②):knowledge→contextual 即 state→demoted。
|
|
1808
|
+
# 受保护节点已被 `guard_move` 拦在上一层(走不到这里);此处仍按负路由
|
|
1809
|
+
# 柔和处理——ok=False 只说明状态不可降,不回滚已完成的层迁移。
|
|
1810
|
+
self.set_state(node_id, "demoted",
|
|
1811
|
+
reason=f"confidence {fm['confidence']} < {DEMOTE_CONFIDENCE}",
|
|
1812
|
+
actor="verify:weakened")
|
|
1625
1813
|
else:
|
|
1626
1814
|
self._write_node(node_id, os.path.join(self.root, node["path"]),
|
|
1627
1815
|
fm, node["content"])
|