@furongjun1999/dsh-memory 0.4.8 → 0.4.10
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 +54 -26
- package/codebuddy/CODEBUDDY.md +196 -195
- package/codebuddy/README.md +13 -1
- package/codebuddy/mcp.json +9 -0
- package/docs/GBrain/345/217/257/345/200/237/351/211/264/347/202/271_/347/201/265/346/236/242/350/220/275/347/202/271/344/272/244/346/216/245_20260919.md +169 -0
- package/docs/README.md +1 -1
- package/docs/discipline/harnesses.yaml +18 -7
- package/docs/discipline/templates/full.md.tmpl +4 -3
- package/docs/experiments/linkref_backfill/candidates_20260917.json +726 -0
- package/docs/experiments/linkref_backfill/candidates_internal_20260917.json +602 -0
- package/docs/experiments/linkref_backfill/candidates_internal_v2.json +603 -0
- package/docs/experiments/linkref_backfill/candidates_secret_20260917.json +884 -0
- package/docs/experiments/linkref_backfill/candidates_secret_v2.json +789 -0
- package/docs/hive//345/244/232/347/253/257harness/351/200/232/344/277/241/345/245/221/347/272/246_v0.1.md +42 -0
- package/docs/hive//346/243/200/347/264/242/346/224/266/346/225/233/345/256/236/346/265/213/344/270/216S1b/350/256/276/350/256/241_v0.1.md +43 -0
- package/docs/hive//346/243/200/347/264/242/350/267/257/345/276/204/344/270/216/350/256/244/347/237/245/347/273/223/346/236/204/345/245/221/347/272/246_v0.1.md +102 -0
- package/docs/hive//347/234/237/345/256/236/345/272/223/347/253/257/345/210/260/347/253/257/345/256/236/346/265/213_S1b/344/270/216/345/217/254/345/233/236/346/235/203/350/241/241_v0.1.md +57 -0
- package/docs/hive//347/234/237/345/256/236/345/272/223/347/253/257/345/210/260/347/253/257/345/256/236/346/265/213_S7/345/200/222/346/216/222/345/200/231/351/200/211/345/261/202_v0.1.md +126 -0
- package/docs/hive//350/234/202/345/267/242/345/217/214/345/256/236/344/276/213/344/272/222/351/252/214_/350/256/276/350/256/241/345/256/232/347/250/277.md +503 -0
- package/docs/mdcg/D_meta_/345/267/245/347/250/213/345/214/226/346/226/271/346/241/210_v0.2.md +216 -0
- package/docs/mdcg/README/350/257/246/347/273/206/347/211/210_v0.4.5.md +631 -625
- package/docs/mdcg//344/273/243/347/240/201/350/257/204/345/256/241/344/270/216/346/235/241/344/273/266/345/214/226/346/263/250/351/207/212_/345/245/221/347/272/246_v0.1.md +82 -0
- package/docs/mdcg//345/205/250/345/272/223/344/273/243/347/240/201/350/257/204/345/256/241/344/270/216/346/235/241/344/273/266/345/214/226/346/263/250/351/207/212_/350/256/241/345/210/222_v0.1.md +600 -0
- 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 +47 -45
- 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 → 206_v0.5.md} +92 -4
- package/docs/mdcg//347/201/265/346/236/242/350/256/260/345/277/206/345/212/250/350/257/215/345/215/217/350/256/256_v1.0-draft.md +172 -0
- package/docs/mdcg//347/216/257/344/272/214_/347/231/275/347/256/261/345/241/253/345/205/205/346/265/201/346/260/264/347/272/277_v0.1.md +31 -0
- package/docs/mdcg//350/267/250/347/253/257/351/252/214/350/257/201/344/270/216/345/220/214/346/255/245/345/215/217/350/256/256_v0.1.md +93 -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 +2 -2
- 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//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 +2 -2
- 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 +434 -433
- package/docs//347/201/265/346/236/242/350/207/252/346/210/221/346/224/271/350/277/233/345/267/245/344/275/234/350/256/241/345/210/222_/345/244/226/351/203/250/347/240/224/347/251/266/347/263/273/345/210/227/345/220/270/346/224/266_v1_20260919.md +286 -0
- package/dsh/README.md +33 -0
- package/dsh/cordis.yml.example +13 -7
- package/dsh/hive-mcp-probe.mjs +94 -0
- package/dsh/hive-mcp.example.yml +62 -0
- package/dsh/update-lingshu.bat +11 -0
- package/dsh/update-lingshu.ps1 +337 -0
- package/lib/hooks.d.ts +3 -0
- package/lib/hooks.js +17 -23
- package/lib/index.d.ts +4 -2
- package/lib/index.js +29 -6
- package/lib/lib/datapath.d.ts +76 -1
- package/lib/lib/datapath.js +199 -13
- package/lib/lib/mdcg_client.d.ts +40 -3
- package/lib/lib/mdcg_client.js +46 -22
- package/lib/lib/mutual.js +4 -4
- package/lib/lib/token_store.js +4 -5
- package/md_cg/audit.py +17 -2
- package/md_cg/autonomy.py +86 -15
- package/md_cg/backfill.py +36 -1
- package/md_cg/backfill_bigdomain.py +34 -0
- package/md_cg/bench6_arms.py +28 -1
- package/md_cg/bench6_common.py +10 -1
- package/md_cg/bench6_competitors.py +6 -1
- package/md_cg/bench_axis_domain.py +9 -1
- package/md_cg/bench_blind_comp.py +7 -1
- package/md_cg/bench_en_atoms_public.py +9 -0
- package/md_cg/bench_governance.py +348 -0
- package/md_cg/bench_lme_zh.py +16 -1
- package/md_cg/bench_locomo.py +2 -1
- package/md_cg/bench_locomo_zh.py +16 -1
- package/md_cg/bench_locomo_zh_public.py +4 -1
- package/md_cg/bench_longmem.py +2 -1
- package/md_cg/bench_membench.py +27 -1
- package/md_cg/bench_p0.py +4 -1
- package/md_cg/bench_progressive.py +13 -1
- package/md_cg/bench_role_views.py +238 -0
- package/md_cg/bench_task_ab.py +8 -1
- package/md_cg/bench_task_ab_llm.py +13 -1
- package/md_cg/bench_unified_en.py +6 -1
- package/md_cg/bench_zh_mad.py +20 -1
- package/md_cg/blindspot_tickets.py +123 -0
- package/md_cg/branches.py +12 -1
- package/md_cg/build_postings.py +73 -0
- package/md_cg/ccgc.py +67 -2
- package/md_cg/census.py +5 -1
- package/md_cg/chain.py +24 -3
- package/md_cg/codeindex.py +134 -17
- package/md_cg/coldverify.py +265 -0
- package/md_cg/comment_gate.py +338 -0
- package/md_cg/cond_compose.py +190 -0
- package/md_cg/cond_facts.py +155 -0
- package/md_cg/cond_template.json +107 -0
- package/md_cg/condition_anchor.py +143 -0
- package/md_cg/conformance.py +69 -4
- package/md_cg/consistency.py +24 -1
- package/md_cg/consolidate.py +53 -2
- package/md_cg/corpus.py +4 -0
- package/md_cg/crosscheck.py +42 -2
- package/md_cg/crypto.py +35 -1
- package/md_cg/d_meta.py +310 -0
- package/md_cg/datapath.py +201 -26
- package/md_cg/docindex.py +122 -1
- package/md_cg/eval_common.py +29 -1
- package/md_cg/evidence.py +27 -1
- package/md_cg/evolution.py +21 -1
- package/md_cg/export.py +11 -1
- package/md_cg/forgetting.py +23 -1
- package/md_cg/fsutil.py +18 -1
- package/md_cg/hotcache.py +214 -0
- package/md_cg/hyperedge.py +251 -0
- package/md_cg/identity.py +18 -1
- package/md_cg/insight.py +17 -1
- package/md_cg/lexicon/build_cedict_en_zh.py +9 -0
- package/md_cg/lexicon/build_standard_en.py +171 -168
- package/md_cg/lexicon/expand_en_zh.py +6 -0
- package/md_cg/lifecycle.py +12 -1
- package/md_cg/linkref.py +281 -0
- package/md_cg/links.py +29 -1
- package/md_cg/mcp_server.py +362 -43
- package/md_cg/md_whitebox.py +53 -1
- package/md_cg/mdcg.py +1003 -27
- package/md_cg/mdcos.py +558 -36
- package/md_cg/metacognition.py +37 -2
- package/md_cg/migrate.py +4 -0
- package/md_cg/migrate_aeis.py +221 -213
- package/md_cg/migrate_roleplay.py +8 -0
- package/md_cg/migrate_wisdom_graph.py +14 -1
- package/md_cg/mreview/__main__.py +3 -0
- package/md_cg/mreview/bundle.py +8 -0
- package/md_cg/mreview/candidates.py +9 -0
- package/md_cg/mreview/govern.py +21 -1
- package/md_cg/mreview/locate.py +34 -0
- package/md_cg/mreview/pipeline.py +29 -1
- package/md_cg/mreview/ruleset.py +16 -1
- package/md_cg/nodefile.py +233 -3
- package/md_cg/pooling.py +23 -1
- package/md_cg/postings.py +298 -0
- package/md_cg/predict.py +89 -9
- package/md_cg/progressive.py +3 -0
- package/md_cg/protect.py +14 -1
- package/md_cg/protocol.py +372 -0
- package/md_cg/provenance.py +262 -0
- package/md_cg/reach.py +453 -0
- package/md_cg/refindex.py +47 -2
- package/md_cg/refine.py +20 -1
- package/md_cg/roleviews.py +89 -0
- package/md_cg/routing.py +76 -0
- package/md_cg/scrub.py +63 -2
- package/md_cg/security.py +26 -1
- package/md_cg/self_state.py +64 -1
- package/md_cg/selfreport.py +151 -0
- package/md_cg/semantic/canonical.py +5 -0
- package/md_cg/semantic/en_normalizer.py +364 -355
- package/md_cg/semantic/zh_en_atoms.py +139 -136
- package/md_cg/signer.py +41 -1
- package/md_cg/sources.py +583 -547
- package/md_cg/statushdr.py +179 -0
- package/md_cg/stg.py +59 -18
- package/md_cg/subgraph.py +23 -0
- package/md_cg/sustain.py +56 -1
- package/md_cg/tasks.py +26 -2
- package/md_cg/test_autonomy.py +26 -0
- package/md_cg/test_bench_governance.py +102 -0
- package/md_cg/test_blindspot_tickets.py +166 -0
- package/md_cg/test_ccgc.py +10 -0
- package/md_cg/test_codeindex.py +338 -0
- package/md_cg/test_comment_gate.py +187 -0
- package/md_cg/test_cond_compose_anchors.py +76 -0
- package/md_cg/test_condition_anchor.py +82 -0
- package/md_cg/test_d_meta.py +412 -0
- package/md_cg/test_datapath_root.py +188 -0
- package/md_cg/test_gain_gate.py +47 -1
- package/md_cg/test_hot_cold.py +187 -0
- package/md_cg/test_hyperedge.py +245 -0
- package/md_cg/test_linkref.py +306 -0
- package/md_cg/test_md_access_parity.py +15 -3
- package/md_cg/test_mr_m1.py +108 -18
- package/md_cg/test_mr_m3.py +8 -1
- package/md_cg/test_p26_refindex.py +49 -20
- package/md_cg/test_p27_docindex.py +236 -2
- package/md_cg/test_p2_mcp.py +1 -1
- package/md_cg/test_p31_insight.py +24 -0
- package/md_cg/test_p44_md_whitebox.py +14 -1
- package/md_cg/test_protocol.py +243 -0
- package/md_cg/test_reach.py +378 -0
- package/md_cg/test_reach_keys.py +201 -0
- package/md_cg/test_reach_meta_exits.py +145 -0
- package/md_cg/test_read_clip.py +8 -4
- package/md_cg/test_retr_s1.py +340 -0
- package/md_cg/test_retr_s1b.py +209 -0
- package/md_cg/test_retr_s3.py +194 -0
- package/md_cg/test_retr_s4.py +163 -0
- package/md_cg/test_retr_s5.py +200 -0
- package/md_cg/test_retr_s6.py +157 -0
- package/md_cg/test_retr_s7.py +385 -0
- package/md_cg/test_retr_s8_time.py +316 -0
- package/md_cg/test_retr_s9_edges.py +286 -0
- package/md_cg/test_retr_s9_entity_ctx.py +175 -0
- package/md_cg/test_review_conformance.py +59 -2
- package/md_cg/test_role_views.py +354 -0
- package/md_cg/test_subproc_encoding.py +188 -0
- package/md_cg/test_trust.py +361 -0
- package/md_cg/test_units_poll.py +71 -0
- package/md_cg/test_v14_fixes.py +397 -0
- package/md_cg/test_validity_filter.py +280 -0
- package/md_cg/test_wisdom_md_store.py +7 -3
- package/md_cg/test_writepipe.py +5 -1
- package/md_cg/theory.py +16 -1
- package/md_cg/tokens.py +40 -8
- package/md_cg/tool_face.py +13 -2
- package/md_cg/trust.py +943 -0
- package/md_cg/twophase.py +12 -1
- package/md_cg/units.py +132 -10
- package/md_cg/vision_evidence.py +24 -1
- package/md_cg/weights.py +24 -1
- package/md_cg/whitebox.py +32 -1
- package/md_cg/whitebox_kb/data/verify_cache.json +21210 -365
- package/md_cg/whitebox_kb/data/verify_savings.jsonl +5078 -0
- package/md_cg/whitebox_kb/wisdom/audit_log/chain_heat.json +10 -10
- package/md_cg/whitebox_kb/wisdom/code_compose.py +113 -6
- package/md_cg/whitebox_kb/wisdom/code_solidified.json +1 -1
- package/md_cg/whitebox_kb/wisdom/verifier.py +340 -55
- package/md_cg/whitebox_kb/wisdom/wisdom-book-cloud.db +0 -0
- package/md_cg/writelimit.py +18 -4
- package/md_cg/writepipe.py +178 -7
- package/package.json +2 -2
- package/skills/skills/designer-perspective/scripts/__pycache__/designer.cpython-310.pyc +0 -0
- package/skills/skills/designer-perspective/scripts/designer.py +17 -1
- package/skills/skills/designer-perspective/tests/selftest.py +3 -1
- package/src/hooks.ts +17 -21
- package/src/index.ts +33 -6
- package/src/lib/datapath.ts +211 -13
- package/src/lib/mdcg_client.ts +64 -25
- package/src/lib/mutual.ts +411 -411
- package/src/lib/token_store.ts +4 -5
- package/zcode/AGENTS.md +196 -195
- package/zcode/README.md +4 -0
- 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/self_state.py
CHANGED
|
@@ -92,12 +92,14 @@ STATE_LINK_TYPE = "refers_to"
|
|
|
92
92
|
|
|
93
93
|
# ---------------------------------------------------------------- 基础工具
|
|
94
94
|
|
|
95
|
+
# 生效条件:对 required 形参 text,返回 str(text or "") 经正则把非中英文数字连续段折叠为下划线、strip('_') 并 lower 后的字符串(text 为假值时返回空串);
|
|
95
96
|
def _slug(text):
|
|
96
97
|
"""归一化 id 片段:保留中英文数字,其余折叠为下划线。"""
|
|
97
98
|
s = re.sub(r"[^0-9A-Za-z\u4e00-\u9fff]+", "_", str(text or ""))
|
|
98
99
|
return s.strip("_").lower()
|
|
99
100
|
|
|
100
101
|
|
|
102
|
+
# 生效条件:对 required 形参 ts,当 float(ts) 可转换且 time.localtime 不抛 OSError 时返回本地时间 "%Y-%m-%d %H:%M:%S" 格式串;当 float(ts) 抛 TypeError/ValueError 或 time.localtime 抛 OSError 时返回空串;
|
|
101
103
|
def _iso(ts):
|
|
102
104
|
try:
|
|
103
105
|
return time.strftime("%Y-%m-%d %H:%M:%S", time.localtime(float(ts)))
|
|
@@ -105,24 +107,29 @@ def _iso(ts):
|
|
|
105
107
|
return ""
|
|
106
108
|
|
|
107
109
|
|
|
110
|
+
# 生效条件:subject 未传时取模块常量 DEFAULT_SUBJECT,返回模块常量 STATE_PREFIX 拼接 _slug(subject) 的结果;
|
|
108
111
|
def state_node_id(subject=DEFAULT_SUBJECT):
|
|
109
112
|
"""状态卡节点 id(单例:同一 subject 只有一个)。"""
|
|
110
113
|
return STATE_PREFIX + _slug(subject)
|
|
111
114
|
|
|
112
115
|
|
|
116
|
+
# 生效条件:对 required 形参 frm 与 to,返回模块常量 RELATION_PREFIX 拼接 _slug(frm)、"__"、_slug(to) 的字符串;
|
|
113
117
|
def relation_node_id(frm, to):
|
|
114
118
|
"""关系节点 id(有向)。"""
|
|
115
119
|
return f"{RELATION_PREFIX}{_slug(frm)}__{_slug(to)}"
|
|
116
120
|
|
|
117
121
|
|
|
122
|
+
# 生效条件:对 required 形参 cg,返回 os.path.join(cg.root, LOG_FILE)(LOG_FILE 为模块常量);
|
|
118
123
|
def log_path(cg):
|
|
119
124
|
return os.path.join(cg.root, LOG_FILE)
|
|
120
125
|
|
|
121
126
|
|
|
127
|
+
# 生效条件:对 required 形参 dim 与 value,返回 f"{dim}:{_slug(value)}";
|
|
122
128
|
def dim_tag(dim, value):
|
|
123
129
|
return f"{dim}:{_slug(value)}"
|
|
124
130
|
|
|
125
131
|
|
|
132
|
+
# 生效条件:对 required 形参 p_trust,若 p_trust is None 或 float(p_trust) 抛 TypeError/ValueError 则返回 None;否则按模块常量 TRUST_BANDS 顺序取首个 v >= lo 的档名,均不满足返回 "low";
|
|
126
133
|
def trust_band(p_trust):
|
|
127
134
|
"""把 P_trust 映射到信任档(索引维度之一)。"""
|
|
128
135
|
if p_trust is None:
|
|
@@ -139,6 +146,7 @@ def trust_band(p_trust):
|
|
|
139
146
|
|
|
140
147
|
# ---------------------------------------------------------------- 留痕
|
|
141
148
|
|
|
149
|
+
# 生效条件:对 required 形参 cg 与 rec,调用 append_jsonl(log_path(cg), rec);若抛出 OSError 则静默忽略,无返回值;
|
|
142
150
|
def _append_log(cg, rec):
|
|
143
151
|
try:
|
|
144
152
|
append_jsonl(log_path(cg), rec)
|
|
@@ -146,6 +154,7 @@ def _append_log(cg, rec):
|
|
|
146
154
|
pass
|
|
147
155
|
|
|
148
156
|
|
|
157
|
+
# 生效条件:对 required 形参 cg 与可选 limit/subject,读取 log_path(cg) 留痕(OSError 时为空列表);subject 为真值时只保留 r.get("subject") == subject 的记录;倒序后,limit 为真值时返回 recs[:int(limit)],limit 为假值(如 0)时返回全部倒序记录;
|
|
149
158
|
def history(cg, limit=100, subject=None):
|
|
150
159
|
"""自我状态留痕(倒序)。"""
|
|
151
160
|
try:
|
|
@@ -158,6 +167,7 @@ def history(cg, limit=100, subject=None):
|
|
|
158
167
|
return recs[:int(limit)] if limit else recs
|
|
159
168
|
|
|
160
169
|
|
|
170
|
+
# 生效条件:对 required 形参 cg 与可选 subject,调用 history(cg, limit=0, subject=subject) 取全部倒序留痕;有记录时返回 recs[0](最新一条),否则返回 None;
|
|
161
171
|
def _last_log(cg, subject=None):
|
|
162
172
|
recs = history(cg, limit=0, subject=subject)
|
|
163
173
|
return recs[0] if recs else None
|
|
@@ -165,6 +175,7 @@ def _last_log(cg, subject=None):
|
|
|
165
175
|
|
|
166
176
|
# ---------------------------------------------------------------- 读卡
|
|
167
177
|
|
|
178
|
+
# 生效条件:对 required 形参 cg 与 node_id,尝试返回 cg.get(node_id);若 cg.get 抛任何异常则返回 None;
|
|
168
179
|
def _node(cg, node_id):
|
|
169
180
|
try:
|
|
170
181
|
return cg.get(node_id)
|
|
@@ -172,15 +183,18 @@ def _node(cg, node_id):
|
|
|
172
183
|
return None
|
|
173
184
|
|
|
174
185
|
|
|
186
|
+
# 生效条件:对 required 形参 cg 与 node_id,取 _node(cg, node_id) 结果(假值时用空字典)的 "frontmatter" 值;该值假值时返回空字典,否则返回该 frontmatter;
|
|
175
187
|
def _fm_of(cg, node_id):
|
|
176
188
|
node = _node(cg, node_id)
|
|
177
189
|
return (node or {}).get("frontmatter") or {}
|
|
178
190
|
|
|
179
191
|
|
|
192
|
+
# 生效条件:对 required 形参 cg 与 node_id,从 cg.index(getattr 取不到或假值时用空字典)的 "nodes" 字段(假值时用空字典)中取 node_id 对应条目;缺键返回 None;
|
|
180
193
|
def _entry(cg, node_id):
|
|
181
194
|
return ((getattr(cg, "index", None) or {}).get("nodes") or {}).get(node_id)
|
|
182
195
|
|
|
183
196
|
|
|
197
|
+
# 生效条件:对 required 形参 cg 与 subject(未传时 DEFAULT_SUBJECT),若 state_node_id(subject) 在 cg.index.nodes 中不存在,或该节点没有非空 frontmatter,返回 None;否则返回由该 frontmatter 扁平化出的 dict,其中 subject 为 fm.get("state_subject") or subject,d_current/p_trust 分别从 information_gap/trust 子字典取;
|
|
184
198
|
def snapshot(cg, subject=DEFAULT_SUBJECT):
|
|
185
199
|
"""读当前状态卡 → 扁平 dict(不存在返回 None)。只读,不写盘。"""
|
|
186
200
|
nid = state_node_id(subject)
|
|
@@ -205,6 +219,7 @@ def snapshot(cg, subject=DEFAULT_SUBJECT):
|
|
|
205
219
|
return out
|
|
206
220
|
|
|
207
221
|
|
|
222
|
+
# 生效条件:对 required 形参 state,返回对指定核心字段(subject、information_gap、trust、emotion、affect、importance_self、important_refs、identity_ref、short_term、relations、prediction、dimensions、state_links)做 sort_keys=True、ensure_ascii=False、default=str 的 JSON 序列化后 SHA-256 前 16 位十六进制(不含 state_version);
|
|
208
223
|
def _fingerprint(state):
|
|
209
224
|
"""状态指纹:只对「会漂移的实质字段」取哈希,用于幂等刷新与版本链。
|
|
210
225
|
|
|
@@ -232,6 +247,30 @@ def _fingerprint(state):
|
|
|
232
247
|
|
|
233
248
|
# ---------------------------------------------------------------- 聚合(九项)
|
|
234
249
|
|
|
250
|
+
# 生效条件:对 required 形参 cg,返回近 int(window)(缺省 20)条反思留痕里 d_meta.unmodeled_growth 的均值(仅收数值型、bool 不计),无可用留痕时回落 d_meta.pressure(d_meta.compute(cg)),d_meta 不可用或抛异常时返回 None;
|
|
251
|
+
def _d_meta_trend(cg, window: int = 20):
|
|
252
|
+
"""近 window 条反思留痕的 D_meta 边界压力均值(确定性函数,纯读)。
|
|
253
|
+
|
|
254
|
+
口径(智能论3.4 §2.7.0 DEV-002a):只聚合已落盘的
|
|
255
|
+
`d_meta.unmodeled_growth`——**不合成三代理**、不参与 D_task。
|
|
256
|
+
留痕无该字段(旧记录/总闸关闭)时回落当前 `d_meta.compute` 的**单一
|
|
257
|
+
代理**值;两者皆不可用 → None(未知即未知,不编造)。
|
|
258
|
+
"""
|
|
259
|
+
try:
|
|
260
|
+
from . import d_meta as _d_meta
|
|
261
|
+
vals = []
|
|
262
|
+
for r in metacognition._reflections(cg)[-int(window):]:
|
|
263
|
+
v = (r.get("d_meta") or {}).get("unmodeled_growth")
|
|
264
|
+
if isinstance(v, (int, float)) and not isinstance(v, bool):
|
|
265
|
+
vals.append(float(v))
|
|
266
|
+
if vals:
|
|
267
|
+
return round(sum(vals) / len(vals), 4)
|
|
268
|
+
return _d_meta.pressure(_d_meta.compute(cg))
|
|
269
|
+
except Exception: # noqa: BLE001
|
|
270
|
+
return None
|
|
271
|
+
|
|
272
|
+
|
|
273
|
+
# 生效条件:对 required 形参 cg,返回预测校准 dict;samples==0 时 ok=False、hit_rate=None 且 note 提示无预测留痕;samples>0 且 th.get("reflect") 为真时 note 建议反思,否则 note 正常;ece 与 calibration 仅在 metacognition.calibration 返回 ok 为真时给出,否则为 None;d_meta_trend 恒由 _d_meta_trend(cg) 给出(近 20 条反思的 D_meta 单代理均值,不可用时为 None);
|
|
235
274
|
def _prediction_face(cg):
|
|
236
275
|
"""预测校准(第九项):我预测得准吗、该不该反思?
|
|
237
276
|
|
|
@@ -239,6 +278,8 @@ def _prediction_face(cg):
|
|
|
239
278
|
· `predict._hit_history` / `predict.dynamic_hit_threshold`
|
|
240
279
|
← `_prediction.jsonl`(predict_feedback 留痕)
|
|
241
280
|
· `metacognition.calibration` ← evidence_log(verify 留痕)的 ECE
|
|
281
|
+
· `d_meta.compute` / 反思留痕的 `d_meta` ← 边界压力趋势(d_meta_trend,
|
|
282
|
+
**不合成三代理**、不参与 D_task:D_task 与 D_meta 对象不同)
|
|
242
283
|
|
|
243
284
|
诚实边界:无预测留痕 → ok=False、hit_rate=None(未知即未知);
|
|
244
285
|
ECE 仅在 metacognition 有足够证据时给出,否则 None。绝不编造。
|
|
@@ -257,6 +298,7 @@ def _prediction_face(cg):
|
|
|
257
298
|
cal = metacognition.calibration(cg)
|
|
258
299
|
except Exception: # noqa: BLE001
|
|
259
300
|
cal = {}
|
|
301
|
+
meta_trend = _d_meta_trend(cg)
|
|
260
302
|
if samples == 0:
|
|
261
303
|
note = "无预测留痕:先 predict_feedback() 积累命中记录"
|
|
262
304
|
elif th.get("reflect"):
|
|
@@ -273,10 +315,12 @@ def _prediction_face(cg):
|
|
|
273
315
|
"reflect": bool(th.get("reflect")),
|
|
274
316
|
"ece": cal.get("ece") if cal.get("ok") else None,
|
|
275
317
|
"calibration": cal.get("verdict") if cal.get("ok") else None,
|
|
318
|
+
"d_meta_trend": meta_trend,
|
|
276
319
|
"note": note,
|
|
277
320
|
}
|
|
278
321
|
|
|
279
322
|
|
|
323
|
+
# 生效条件:对 required 形参 cg 与 window,尝试用 cg.recent_events(limit=int(window), newest_first=False) 取记录(异常时 recs=[]),返回 n=len(recs)、span=[min(ts), max(ts)] if ts else None、roles 角色计数、ptr 为 cg.recent_log 的 basename(缺省 "_recent.jsonl");
|
|
280
324
|
def _recent_summary(cg, window):
|
|
281
325
|
"""短期记忆:只做窗口摘要,原文仍留在 _recent.jsonl(薄自我的关键取舍)。"""
|
|
282
326
|
try:
|
|
@@ -293,6 +337,7 @@ def _recent_summary(cg, window):
|
|
|
293
337
|
"ptr": os.path.basename(getattr(cg, "recent_log", "_recent.jsonl"))}
|
|
294
338
|
|
|
295
339
|
|
|
340
|
+
# 生效条件:对 required 形参 cg 与 subject,若 identity.profile(cg, subject) 抛异常则返回 (None, 0);否则返回 (prof.anchor.node_id(缺省 None), len(prof.get("anchors") or []));
|
|
296
341
|
def _identity_ref(cg, subject):
|
|
297
342
|
"""身份:只存指针(锚点节点 id),锚点内容仍在 self 层原节点里。"""
|
|
298
343
|
try:
|
|
@@ -304,6 +349,7 @@ def _identity_ref(cg, subject):
|
|
|
304
349
|
return ref, len(anchors)
|
|
305
350
|
|
|
306
351
|
|
|
352
|
+
# 生效条件:对 required 形参 cg 与 subject,遍历 cg.index.nodes 中标签含 TAG_RELATION 或以 RELATION_PREFIX 开头的节点,统计 tags 含 "rel_from:{_slug(subject)}" 的个数为 out、含 "rel_to:{_slug(subject)}" 的个数为 in,返回 {"out": out, "in": inn};
|
|
307
353
|
def _relation_counts(cg, subject):
|
|
308
354
|
"""关系:从索引里的关系节点统计出/入度(不读文件正文)。"""
|
|
309
355
|
slug = _slug(subject)
|
|
@@ -320,6 +366,7 @@ def _relation_counts(cg, subject):
|
|
|
320
366
|
return {"out": out, "in": inn}
|
|
321
367
|
|
|
322
368
|
|
|
369
|
+
# 生效条件:对 required 形参 dimensions(假值时按空字典),只保留键 str(dim).strip().lower() 属于模块常量 DIMENSIONS 且 val 非 None 的维度;val 为 list/tuple/set 时展开,否则包装为单元素;过滤空字符串并去重后放入 out,返回 out;
|
|
323
370
|
def _normalize_dimensions(dimensions):
|
|
324
371
|
"""维度入参 → {dim: [value...]}(只保留已知维度)。"""
|
|
325
372
|
out = {}
|
|
@@ -337,6 +384,7 @@ def _normalize_dimensions(dimensions):
|
|
|
337
384
|
return out
|
|
338
385
|
|
|
339
386
|
|
|
387
|
+
# 生效条件:对 required 形参 cg、subject、window、importance、important_refs、dimensions、links、old,聚合 trace/trust/recent/identity/relations/prediction 等生成薄状态 dict;emotion 与 affect 仅在对应 trace/trust ok 且二阶值为非 None 时取 emotion,否则为 "unknown";importance 为 None 时回落 old.importance_self 再回落 1.0;important_refs 为 None 时回落 old.important_refs;dimensions 归一化后为空则回落 old.dimensions,并自动追加 trust band 与今日时间;links 为 None 时回落 old.state_links;
|
|
340
388
|
def _derive(cg, subject, window, importance, important_refs, dimensions,
|
|
341
389
|
links, old):
|
|
342
390
|
"""聚合九项自我信息 → 薄状态(纯读,不写盘)。"""
|
|
@@ -394,6 +442,7 @@ def _derive(cg, subject, window, importance, important_refs, dimensions,
|
|
|
394
442
|
|
|
395
443
|
# ---------------------------------------------------------------- 写卡
|
|
396
444
|
|
|
445
|
+
# 生效条件:对 required 形参 state,读取其 information_gap、trust、short_term、prediction、dimensions 等字段,渲染并返回末尾带换行的状态卡正文(含 D_meta 边界压力趋势一行:prediction.d_meta_trend 缺失时显示 None);span 为空时显示(无),角色为空时显示(无);
|
|
397
446
|
def _render(state):
|
|
398
447
|
"""状态卡正文:人类可读,且与 frontmatter 字段一一对应。"""
|
|
399
448
|
ig, tt = state["information_gap"], state["trust"]
|
|
@@ -427,6 +476,8 @@ def _render(state):
|
|
|
427
476
|
f";后验可信度={pd.get('beta_mean')}"
|
|
428
477
|
f"(95%CI {pd.get('beta_ci95')})"
|
|
429
478
|
f";ECE={pd.get('ece')}({pd.get('calibration')})",
|
|
479
|
+
f"# D_meta:边界压力趋势(unmodeled_growth 均值)={pd.get('d_meta_trend')}"
|
|
480
|
+
f"(三代理分别观测、不合成单值,不参与 D_task)",
|
|
430
481
|
"# 索引:" + ";".join(
|
|
431
482
|
f"{d}={','.join(dims.get(d) or []) or '-'}" for d in DIMENSIONS),
|
|
432
483
|
"# 说明:本卡是薄自我——只登记当前值与指针;具体任务/人物/会话/"
|
|
@@ -435,6 +486,7 @@ def _render(state):
|
|
|
435
486
|
return "\n".join(lines) + "\n"
|
|
436
487
|
|
|
437
488
|
|
|
489
|
+
# 生效条件:当 cg 可用时,refresh 以 subject(默认 DEFAULT_SUBJECT)和 window(默认 RECENT_WINDOW)聚合九项;若留痕末条存在且状态卡缺失或 state_version/state_hash 与留痕末条不一致,则按留痕重建元数据;若 force 为假且未触发重建且旧 state_hash 等于新指纹,返回 changed=False 的幂等跳过;否则若留痕末条存在且状态卡 state_hash(状态卡缺失时视为 None)与留痕末条不同,则 strict 为真时返回 ok=False、error='chain_broken',strict 为假时以留痕末条 hash 修复 prev_hash 并写入,最终写入时返回 changed=True 及新 state_version/state_hash;
|
|
438
490
|
def refresh(cg, subject=DEFAULT_SUBJECT, window=RECENT_WINDOW, importance=None,
|
|
439
491
|
important_refs=None, dimensions=None, links=None,
|
|
440
492
|
actor="self_state", force=False, strict=False, session=None):
|
|
@@ -559,6 +611,7 @@ def refresh(cg, subject=DEFAULT_SUBJECT, window=RECENT_WINDOW, importance=None,
|
|
|
559
611
|
|
|
560
612
|
# ---------------------------------------------------------------- 关系
|
|
561
613
|
|
|
614
|
+
# 生效条件:对 required 形参 cg、frm、to 与可选 relation_type/strength/reciprocal,若 _slug(frm) == _slug(to) 返回 {"ok": False, "error": "self_loop", ...};否则 relation_type 不在模块常量 RELATION_TYPES 时回落 "other",strength 的 float 失败时 w=0.5、成功时夹在 0.0~1.0,写入关系节点,reciprocal 为真时同时写反向,返回 ok=True 及 written 列表;
|
|
562
615
|
def relate(cg, frm, to, relation_type="collaborator", strength=0.5,
|
|
563
616
|
condition="", note="", reciprocal=False, actor="self_state"):
|
|
564
617
|
"""写一条有向关系(自我 ↔ 其他智能)。reciprocal=True 时同时写反向。"""
|
|
@@ -597,6 +650,7 @@ def relate(cg, frm, to, relation_type="collaborator", strength=0.5,
|
|
|
597
650
|
"reciprocal": bool(reciprocal)}
|
|
598
651
|
|
|
599
652
|
|
|
653
|
+
# 生效条件:对 required 形参 cg 与可选 subject/direction,遍历 cg.index.nodes 中标签含 TAG_RELATION 或以 RELATION_PREFIX 开头的节点;subject 为真值时按 _slug(subject) 过滤,direction 为 "out" 只保留出边、"in" 只保留入边、"both" 保留出或入边、其他 direction 不做方向过滤;返回按 strength(importance)降序、再按 node_id 升序排序的关系列表 out;
|
|
600
654
|
def relations(cg, subject=None, direction="both"):
|
|
601
655
|
"""列出关系节点(按 subject 过滤出/入)。"""
|
|
602
656
|
nodes = ((getattr(cg, "index", None) or {}).get("nodes") or {})
|
|
@@ -629,6 +683,7 @@ def relations(cg, subject=None, direction="both"):
|
|
|
629
683
|
|
|
630
684
|
# ---------------------------------------------------------------- 索引
|
|
631
685
|
|
|
686
|
+
# 生效条件:对 required 形参 cg、dim、value 与可选 limit/with_content,若 dim 小写去空白后不在模块常量 DIMENSIONS 中,返回 {"ok": False, "error": "unknown_dimension", "allowed": list(DIMENSIONS)};否则匹配 cg.index.nodes 中 tags 含 dim_tag(dim, value) 的节点,按 importance 降序、created_at 降序排序,取 hits[:int(limit)](limit=0 时切片为空),with_content 为真时附加节点 content 前 400 字符,返回 ok=True 及 count/items;
|
|
632
687
|
def index(cg, dim, value, limit=50, with_content=False):
|
|
633
688
|
"""按五维索引反查具体详情节点(认知图连接,不是内容搬运)。"""
|
|
634
689
|
d = str(dim or "").strip().lower()
|
|
@@ -640,7 +695,8 @@ def index(cg, dim, value, limit=50, with_content=False):
|
|
|
640
695
|
hits = [(nid, e) for nid, e in nodes.items()
|
|
641
696
|
if tag in (e.get("tags") or [])]
|
|
642
697
|
hits.sort(key=lambda kv: (-float(kv[1].get("importance") or 0),
|
|
643
|
-
-float(kv[1].get("created_at") or 0)
|
|
698
|
+
-float(kv[1].get("created_at") or 0),
|
|
699
|
+
str(kv[0] or "")))
|
|
644
700
|
items = []
|
|
645
701
|
for nid, e in hits[:int(limit)]:
|
|
646
702
|
item = {"node_id": nid, "layer": e.get("layer"),
|
|
@@ -654,6 +710,7 @@ def index(cg, dim, value, limit=50, with_content=False):
|
|
|
654
710
|
"count": len(hits), "items": items}
|
|
655
711
|
|
|
656
712
|
|
|
713
|
+
# 生效条件:对 required 形参 cg 与 subject(未传时 DEFAULT_SUBJECT),若 snapshot(cg, subject) 返回假值则返回 {"ok": False, "error": "no_state", "subject": subject};否则返回 {"ok": True, "subject": subject, "dimensions": st.get("dimensions") or {}};
|
|
657
714
|
def dimensions(cg, subject=DEFAULT_SUBJECT):
|
|
658
715
|
"""状态卡登记的五维索引标签。"""
|
|
659
716
|
st = snapshot(cg, subject)
|
|
@@ -665,12 +722,14 @@ def dimensions(cg, subject=DEFAULT_SUBJECT):
|
|
|
665
722
|
|
|
666
723
|
# ---------------------------------------------------------------- 审计
|
|
667
724
|
|
|
725
|
+
# 生效条件:对 required 形参 code、severity、why,返回含这三个键并合并 **extra 的 dict;
|
|
668
726
|
def _issue(code, severity, why, **extra):
|
|
669
727
|
d = {"code": code, "severity": severity, "why": why}
|
|
670
728
|
d.update(extra)
|
|
671
729
|
return d
|
|
672
730
|
|
|
673
731
|
|
|
732
|
+
# 生效条件:当 cg 可用时,audit 对 subject(默认 DEFAULT_SUBJECT)和 window(默认 RECENT_WINDOW)执行一致性审计;若对应状态卡不存在则返回 ok=False、verdict='absent'(含已收集的 issues);否则基于留痕历史与现算值收集 issues,按有无 error 返回 verdict='broken'、无 error 但有 warn 返回 'drift'、均无返回 'consistent',且 ok 为无 error;
|
|
674
733
|
def audit(cg, subject=DEFAULT_SUBJECT, window=RECENT_WINDOW):
|
|
675
734
|
"""自我信息一致性审计:全部判定可重算(不依赖人的判断)。"""
|
|
676
735
|
from . import protect as _protect
|
|
@@ -847,6 +906,7 @@ def audit(cg, subject=DEFAULT_SUBJECT, window=RECENT_WINDOW):
|
|
|
847
906
|
|
|
848
907
|
# ---------------------------------------------------------------- 加载 / 概览
|
|
849
908
|
|
|
909
|
+
# 生效条件:对 required 形参 cg 与可选 subject/window/auto_refresh/actor,先取 snapshot(cg, subject);若 st 为 None 且 auto_refresh 为真则 refresh 后重新 snapshot;若 st 非 None 且 auto_refresh 为真且 st.state_ts 的年龄 > 模块常量 STALE_AFTER 则 refresh 后重新 snapshot;然后取 relations、history(limit=5, subject)、dimensions,返回 ok=st is not None 及这些字段;
|
|
850
910
|
def bootstrap(cg, subject=DEFAULT_SUBJECT, window=RECENT_WINDOW,
|
|
851
911
|
auto_refresh=True, actor="bootstrap"):
|
|
852
912
|
"""会话启动:加载自我状态卡 + 关系 + 最近留痕 + 索引概览。
|
|
@@ -874,6 +934,7 @@ def bootstrap(cg, subject=DEFAULT_SUBJECT, window=RECENT_WINDOW,
|
|
|
874
934
|
"loaded_at": time.time()}
|
|
875
935
|
|
|
876
936
|
|
|
937
|
+
# 生效条件:对 required 形参 cg、out、st、session,若 session 为假值直接返回 out;否则写入 out["session"]=str(session)、out["session_registered"]=bool(s in st.dimensions.session 列表)、out["session_refs"](调用 index(cg, "session", s, limit=20) 的结果;异常时写入 count=0、degraded=True),返回 out;
|
|
877
938
|
def _with_session_slice(cg, out, st, session):
|
|
878
939
|
"""给 summary 结果补「本会话切片」(薄卡 + 富索引,不改单例语义)。
|
|
879
940
|
|
|
@@ -895,6 +956,7 @@ def _with_session_slice(cg, out, st, session):
|
|
|
895
956
|
return out
|
|
896
957
|
|
|
897
958
|
|
|
959
|
+
# 生效条件:对 required 形参 cg 与可选 subject/session,取 snapshot(cg, subject);若为假值,经 _with_session_slice 返回 {"ok": False, "subject": subject, "note": "无状态卡"};否则构造含 version、d_current、emotion、p_trust、trust_band、text 等字段的 dict,再经 _with_session_slice 返回;
|
|
898
960
|
def summary(cg, subject=DEFAULT_SUBJECT, session=None):
|
|
899
961
|
"""一句话自我状态(供 health / 面板)。
|
|
900
962
|
|
|
@@ -927,6 +989,7 @@ def summary(cg, subject=DEFAULT_SUBJECT, session=None):
|
|
|
927
989
|
return _with_session_slice(cg, out, st, session)
|
|
928
990
|
|
|
929
991
|
|
|
992
|
+
# 生效条件:无参调用时返回模块自描述 dict,内含模块常量 SCHEMA_VERSION、DEFAULT_SUBJECT、LOG_FILE、DIMENSIONS、TRUST_BANDS、RELATION_TYPES、DRIFT_TOL、STALE_AFTER 等;
|
|
930
993
|
def catalog():
|
|
931
994
|
"""自描述:九项自我信息 + 五维索引 + 审计规则(供协议对照验证)。"""
|
|
932
995
|
return {
|
|
@@ -0,0 +1,151 @@
|
|
|
1
|
+
# -*- coding: utf-8 -*-
|
|
2
|
+
"""常驻进程自报:把「我是谁 / 我从哪个 md_cg 包加载 / 我持哪一代 render 契约」
|
|
3
|
+
落成**外部可读的事实**。
|
|
4
|
+
|
|
5
|
+
# 生效条件:载体/位置:仓根 md_cg/selfreport.py(import 路径 md_cg.selfreport);时间:常驻 md_cg MCP server 进程启动时由 mcp_server.main 调用 report();方法:info() 采集进程事实(pid/ppid/cwd/argv/sys.path[0]),source_dir/source_file/render_version 由 codeindex 的实际加载位置与常量求出,report() 原子写 <tempdir>/md_cg_servers/<pid>.json;约束:**任何异常一律吞掉并返回 None/空**——自报是诊断设施,不得成为服务故障源;不记录环境变量值,只记录 MDCG_ROOT/MDCG_TOKEN 是否已设置。
|
|
6
|
+
|
|
7
|
+
## 为什么需要它(根因,非猜测)
|
|
8
|
+
|
|
9
|
+
判「一个常驻 md_cg 进程会不会用旧契约覆盖重建成果」,原先只能比「进程启动时间 vs
|
|
10
|
+
md_cg/*.py 最新 mtime」。该判据有**活体盲区**:npm 副本内的进程启动于 11:37~11:39,
|
|
11
|
+
而源码 mtime 为 09:24 —— `stale=False`,但它加载的是插件包 0.4.8 的
|
|
12
|
+
`codeindex.render`(旧契约:合成 `# 生效条件:载体/位置:`),会把全量重建成果刷回
|
|
13
|
+
`old_synth`(AGENTS.md §5 运维注记实证)。mtime 是**相对量**,每次须复算且天然滞后。
|
|
14
|
+
|
|
15
|
+
自报给的是**绝对事实**:进程**自己**报出它实际加载的 `md_cg` 包目录与契约代际。
|
|
16
|
+
外部脚本据此机械裁决,不再推断。旧包不含本模块,故**「无自报」本身即旧契约的证据**
|
|
17
|
+
(唯一例外见 `scripts/mdcg_stale_servers.py` 的 cwd 兜底分支)。
|
|
18
|
+
|
|
19
|
+
## 边界
|
|
20
|
+
|
|
21
|
+
- 自报目录取 `tempfile.gettempdir()`(同用户跨端一致),不依赖 MDCG_ROOT——
|
|
22
|
+
库根未设时也要能判「进程持哪代契约」。
|
|
23
|
+
- 只报事实,不做裁决:`render_version` 由消费方与本仓 `codeindex.RENDER_VERSION`
|
|
24
|
+
比对(同源,不写死版本号,避免两处漂移)。
|
|
25
|
+
"""
|
|
26
|
+
from __future__ import annotations
|
|
27
|
+
|
|
28
|
+
import json
|
|
29
|
+
import os
|
|
30
|
+
import sys
|
|
31
|
+
import tempfile
|
|
32
|
+
import time
|
|
33
|
+
|
|
34
|
+
# 自报目录:同用户、跨端一致(DSH 与 CodeBuddy 的 python 子进程共用同一 tempdir)。
|
|
35
|
+
SELF_REPORT_DIR = os.path.join(tempfile.gettempdir(), "md_cg_servers")
|
|
36
|
+
# 自报格式代际:本模块自身结构变化时才 +1(与 codeindex.RENDER_VERSION 无关,
|
|
37
|
+
# 后者描述「节点正文由哪代 render 产出」,属索引契约,两者不可混用)。
|
|
38
|
+
SELF_REPORT_SCHEMA = 1
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
# 生效条件:无入参;返回 (source_dir, render_version, source_file)——均取自 codeindex 模块**实际加载位置**(而非本文件所在包),故包被 __path__ 重定向后报的是真源;import 失败时三项均 None,不抛。
|
|
42
|
+
def _source():
|
|
43
|
+
try:
|
|
44
|
+
from . import codeindex
|
|
45
|
+
return (os.path.dirname(os.path.abspath(codeindex.__file__)),
|
|
46
|
+
getattr(codeindex, "RENDER_VERSION", None),
|
|
47
|
+
os.path.abspath(codeindex.__file__))
|
|
48
|
+
except Exception: # noqa: BLE001 —— 自报失败降级为 None
|
|
49
|
+
return None, None, None
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
# 生效条件:tag 为任意字符串(缺省空串);返回本进程自报字典,键固定为 schema/pid/ppid/ts/iso/tag/cwd/argv/sys_path0/python/source_dir/source_file/render_version/mdcg_root_set/mdcg_token_set;任何取值失败均降级为 None 而不抛。
|
|
53
|
+
def info(tag: str = "") -> dict:
|
|
54
|
+
"""采集本进程事实(纯读取,不写盘)。"""
|
|
55
|
+
sd, rv, sf = _source()
|
|
56
|
+
try:
|
|
57
|
+
cwd = os.getcwd()
|
|
58
|
+
except Exception: # noqa: BLE001
|
|
59
|
+
cwd = None
|
|
60
|
+
try:
|
|
61
|
+
ppid = os.getppid()
|
|
62
|
+
except Exception: # noqa: BLE001
|
|
63
|
+
ppid = None
|
|
64
|
+
try:
|
|
65
|
+
sp0 = sys.path[0] if sys.path else None
|
|
66
|
+
except Exception: # noqa: BLE001
|
|
67
|
+
sp0 = None
|
|
68
|
+
now = time.time()
|
|
69
|
+
return {
|
|
70
|
+
"schema": SELF_REPORT_SCHEMA,
|
|
71
|
+
"pid": os.getpid(),
|
|
72
|
+
"ppid": ppid,
|
|
73
|
+
"ts": now,
|
|
74
|
+
"iso": time.strftime("%Y-%m-%d %H:%M:%S", time.localtime(now)),
|
|
75
|
+
"tag": str(tag or ""),
|
|
76
|
+
"cwd": cwd,
|
|
77
|
+
"argv": list(sys.argv),
|
|
78
|
+
"sys_path0": sp0,
|
|
79
|
+
"python": "%d.%d.%d" % sys.version_info[:3],
|
|
80
|
+
# 只报「是否设置」,不落值——避免令牌/库根出现在可被他人读取的临时文件里。
|
|
81
|
+
"mdcg_root_set": bool(os.environ.get("MDCG_ROOT")),
|
|
82
|
+
"mdcg_token_set": bool(os.environ.get("MDCG_TOKEN")),
|
|
83
|
+
"source_dir": sd,
|
|
84
|
+
"source_file": sf,
|
|
85
|
+
"render_version": rv,
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
|
|
89
|
+
# 生效条件:tag 为任意字符串;成功时把 info(tag) 原子写入 <tempdir>/md_cg_servers/<pid>.json(先写 .tmp 再 os.replace),并追加 self_report_path 键后返回该字典;任何异常(目录不可建/不可写/序列化失败)一律吞掉并返回 None——自报故障绝不向上传播。
|
|
90
|
+
def report(tag: str = "mcp_server"):
|
|
91
|
+
"""写自报文件(fail-safe:失败返回 None,不抛)。"""
|
|
92
|
+
try:
|
|
93
|
+
data = info(tag)
|
|
94
|
+
os.makedirs(SELF_REPORT_DIR, exist_ok=True)
|
|
95
|
+
path = os.path.join(SELF_REPORT_DIR, "%d.json" % data["pid"])
|
|
96
|
+
tmp = path + ".tmp"
|
|
97
|
+
with open(tmp, "w", encoding="utf-8") as fh:
|
|
98
|
+
json.dump(data, fh, ensure_ascii=False, indent=2)
|
|
99
|
+
os.replace(tmp, path)
|
|
100
|
+
data["self_report_path"] = path
|
|
101
|
+
return data
|
|
102
|
+
except Exception: # noqa: BLE001
|
|
103
|
+
return None
|
|
104
|
+
|
|
105
|
+
|
|
106
|
+
# 生效条件:无入参;返回 {pid(int): 自报字典}——读 SELF_REPORT_DIR 下全部 *.json,逐文件 json.loads 失败或 pid 键缺失/非整数者静默跳过(坏文件不使整体失败);目录不存在时返回空字典。
|
|
107
|
+
def read_all() -> dict:
|
|
108
|
+
"""读全部自报(只读,无副作用)。"""
|
|
109
|
+
out = {}
|
|
110
|
+
try:
|
|
111
|
+
names = os.listdir(SELF_REPORT_DIR)
|
|
112
|
+
except OSError:
|
|
113
|
+
return out
|
|
114
|
+
for n in names:
|
|
115
|
+
if not n.endswith(".json"):
|
|
116
|
+
continue
|
|
117
|
+
try:
|
|
118
|
+
with open(os.path.join(SELF_REPORT_DIR, n), "r",
|
|
119
|
+
encoding="utf-8", errors="replace") as fh:
|
|
120
|
+
d = json.load(fh)
|
|
121
|
+
pid = int(d.get("pid"))
|
|
122
|
+
except Exception: # noqa: BLE001
|
|
123
|
+
continue
|
|
124
|
+
out[pid] = d
|
|
125
|
+
return out
|
|
126
|
+
|
|
127
|
+
|
|
128
|
+
# 生效条件:pids 为可迭代的 pid 集合(通常是「当前进程表中仍在的 md_cg 进程」);删除自报目录中 pid 不在该集合内的 *.json 与遗留 *.tmp,返回删除条数;目录不存在或删除失败逐项吞掉,不抛。
|
|
129
|
+
def purge(pids) -> int:
|
|
130
|
+
"""清掉不属于「仍在的进程」的自报文件(防目录随重启次数膨胀)。"""
|
|
131
|
+
keep = {int(p) for p in (pids or ())}
|
|
132
|
+
n = 0
|
|
133
|
+
try:
|
|
134
|
+
names = os.listdir(SELF_REPORT_DIR)
|
|
135
|
+
except OSError:
|
|
136
|
+
return 0
|
|
137
|
+
for name in names:
|
|
138
|
+
try:
|
|
139
|
+
if name.endswith(".tmp"):
|
|
140
|
+
os.remove(os.path.join(SELF_REPORT_DIR, name))
|
|
141
|
+
n += 1
|
|
142
|
+
continue
|
|
143
|
+
if not name.endswith(".json"):
|
|
144
|
+
continue
|
|
145
|
+
if int(name[:-5]) in keep:
|
|
146
|
+
continue
|
|
147
|
+
os.remove(os.path.join(SELF_REPORT_DIR, name))
|
|
148
|
+
n += 1
|
|
149
|
+
except Exception: # noqa: BLE001
|
|
150
|
+
continue
|
|
151
|
+
return n
|
|
@@ -32,6 +32,7 @@ WIN = 6 # 组合共现窗口(token 距离,探针实证口径)
|
|
|
32
32
|
_ATOMS_ZH = None
|
|
33
33
|
|
|
34
34
|
|
|
35
|
+
# 生效条件:模块级常量 `_ATOMS_ZH` 为 None 时按 `__file__` 所在目录的 atoms.json 取 `data.get("atoms", [])` 各项 `zh` 建集合并缓存,非 None 时直接返回 `_ATOMS_ZH`。
|
|
35
36
|
def atoms_zh():
|
|
36
37
|
"""标准语义基准词表(atoms.json 的 zh 概念集,模块级缓存)。"""
|
|
37
38
|
global _ATOMS_ZH
|
|
@@ -44,6 +45,7 @@ def atoms_zh():
|
|
|
44
45
|
return _ATOMS_ZH
|
|
45
46
|
|
|
46
47
|
|
|
48
|
+
# 生效条件:`text` 为 None 或空串时 `(text or "").split()` 得空序列、返回空列表 out;否则按空白切分后逐段经 `zh_en_atoms.segment` 展开 extend 进 out 并返回。
|
|
47
49
|
def semantic_atoms(text):
|
|
48
50
|
"""semantic 字段容错切分:按空格分段逐段贪心匹配。
|
|
49
51
|
|
|
@@ -57,6 +59,7 @@ def semantic_atoms(text):
|
|
|
57
59
|
return out
|
|
58
60
|
|
|
59
61
|
|
|
62
|
+
# 生效条件:`text` 传入后,返回 `semantic_atoms(text)` 结果中不属于 `atoms_zh()` 词表集合的 token 列表(无匹配项时为空列表)。
|
|
60
63
|
def oov_of(text):
|
|
61
64
|
"""OOV 审计:切分产物中不在标准词表的 token 列表(空列表=全部合法)。"""
|
|
62
65
|
zh = atoms_zh()
|
|
@@ -64,6 +67,7 @@ def oov_of(text):
|
|
|
64
67
|
|
|
65
68
|
|
|
66
69
|
@functools.lru_cache(maxsize=256)
|
|
70
|
+
# 生效条件:t = text or ""(text 为 None/空串时按 ""),仅当 t 含 [A-Za-z] 时经 normalize_en_query 归一——含字母的 term 原样保留、其余经 zh_en_atoms.segment 展开——返回 tuple(out);归一抛异常或 t 无字母时返回 tuple(semantic_atoms(t));
|
|
67
71
|
def query_atoms(text):
|
|
68
72
|
"""query 侧归一(同 semantic_atoms,缓存——每轮检索全 doc 复用)。
|
|
69
73
|
|
|
@@ -91,6 +95,7 @@ def query_atoms(text):
|
|
|
91
95
|
return tuple(semantic_atoms(t))
|
|
92
96
|
|
|
93
97
|
|
|
98
|
+
# 生效条件:`doc_semantic` 经 `semantic_atoms` 切分后相邻原子对集合 pairs 为空(单原子或无原子)时返回 0.0;否则以 `query_atoms(qtext)` 建立 token→位置表,统计两原子位置差绝对值 ≤ `win`(默认形参 WIN)的命中对数 hit,返回 hit/len(pairs)。
|
|
94
99
|
def pair_hits(doc_semantic, qtext, win=WIN):
|
|
95
100
|
"""组合窗口共现命中率 ∈ [0,1]。
|
|
96
101
|
|