@furongjun1999/dsh-memory 0.4.11 → 0.5.0
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 +16 -16
- package/codebuddy/CODEBUDDY.md +11 -3
- package/codebuddy/README.md +92 -90
- package/codebuddy/mcp.json +27 -27
- 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 -169
- 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 -144
- package/docs/README.md +142 -111
- package/docs/discipline/harnesses.yaml +244 -226
- package/docs/discipline/templates/full.md.tmpl +61 -61
- package/docs/discipline/templates/rules.mdc.tmpl +68 -0
- package/docs/discipline/templates/skill.md.tmpl +23 -23
- package/docs/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 +299 -299
- package/docs/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 +239 -239
- 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 +174 -174
- 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 +223 -223
- package/docs/{mdcg → hive}//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 +165 -160
- package/docs/hive//344/273/244/347/211/214/350/257/255/344/271/211/344/277/256/346/255/243_/344/273/262/350/243/201/344/275/215_v0.1.md +76 -0
- package/docs/{mdcg → hive}//345/255/220/344/273/243/347/220/206/351/205/215/347/275/256/346/240/207/345/207/206_v0.5.md +236 -236
- 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 -43
- package/docs/hive//346/243/200/347/264/242/347/256/227/346/263/225/345/217/243/345/276/204/345/257/271/347/205/247_v0.1.md +85 -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 -102
- package/docs/hive//350/234/202/345/267/242M6_ingest/345/256/236/346/226/275/350/256/241/345/210/222_v0.1.md +47 -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 -503
- 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 +85 -85
- package/docs/hive//350/234/202/345/267/242/350/256/276/350/256/241_/347/220/206/350/256/272/345/257/271/351/275/220_v0.1.md +191 -0
- package/docs/hive//350/234/202/345/267/242/350/277/255/344/273/243_/345/256/217/350/247/202/344/270/216/347/276/244/344/275/223/350/260/203/345/272/246_v0.1.md +90 -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 -216
- package/docs/mdcg/README/350/257/246/347/273/206/347/211/210_v0.4.10.md +646 -646
- package/docs/mdcg/lingshu_tutorial.html +14449 -14449
- package/docs/mdcg/release_v0.4.11.md +49 -0
- package/docs/mdcg/release_v0.4.5.md +55 -55
- package/docs/mdcg/tool_table_v0.3.0.md +117 -117
- 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 -600
- 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 +40 -40
- 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 +275 -275
- package/docs/mdcg//345/217/221/345/270/203/351/227/250/347/246/201/351/223/276_v0.1.md +54 -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 +130 -130
- 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 +278 -278
- 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 -172
- package/docs/mdcg//347/274/272/345/217/243/345/215/225_P0/346/224/266/345/217/243_v0.1.md +270 -270
- 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 +491 -491
- 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 +340 -340
- 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 +588 -588
- 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 -176
- 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 +81 -81
- package/docs/swarm//350/234/202/347/276/244/344/272/222/350/201/224_v0.1.md +704 -704
- 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 -242
- 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 -129
- 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 -134
- package/docs/theory//346/231/272/350/203/275/350/256/2723.4.md +5260 -5260
- 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 -145
- 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 +144 -144
- 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 +198 -198
- 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 -221
- 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 -237
- package/docs//345/217/221/345/270/203/344/273/266/345/233/236/346/272/257/350/257/264/346/230/216_v0.1.md +82 -82
- 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 +28 -2
- package/dsh/README.md +82 -82
- package/dsh/cordis.yml.example +139 -139
- package/dsh/update-lingshu.bat +11 -11
- package/lib/hooks.js +36 -2
- package/lib/lib/roleplay_web.js +427 -427
- package/md_cg/__init__.py +7 -7
- package/md_cg/audit.py +368 -368
- package/md_cg/autonomy.py +287 -287
- package/md_cg/backfill.py +1327 -1327
- package/md_cg/backfill_bigdomain.py +34 -34
- package/md_cg/bench6_arms.py +410 -410
- package/md_cg/bench6_common.py +230 -230
- package/md_cg/bench6_competitors.py +212 -212
- package/md_cg/bench_axis_domain.py +257 -257
- package/md_cg/bench_blind_comp.py +308 -308
- package/md_cg/bench_en_atoms_public.py +230 -230
- package/md_cg/bench_governance.py +348 -348
- package/md_cg/bench_lme_zh.py +410 -410
- package/md_cg/bench_locomo.py +121 -121
- package/md_cg/bench_locomo_zh.py +450 -450
- package/md_cg/bench_locomo_zh_public.py +147 -147
- package/md_cg/bench_longmem.py +112 -112
- package/md_cg/bench_membench.py +632 -632
- package/md_cg/bench_p0.py +149 -149
- package/md_cg/bench_progressive.py +287 -287
- package/md_cg/bench_role_views.py +238 -238
- package/md_cg/bench_task_ab.py +243 -243
- package/md_cg/bench_task_ab_llm.py +408 -408
- package/md_cg/bench_unified_en.py +204 -204
- package/md_cg/bench_zh_mad.py +601 -601
- package/md_cg/blindspot_tickets.py +123 -123
- package/md_cg/branches.py +285 -285
- package/md_cg/build_postings.py +73 -73
- package/md_cg/ccgc.py +1005 -948
- package/md_cg/census.py +132 -132
- package/md_cg/chain.py +300 -300
- package/md_cg/codeindex.py +531 -531
- package/md_cg/coldverify.py +292 -292
- package/md_cg/comment_gate.py +337 -337
- package/md_cg/cond_compose.py +190 -190
- package/md_cg/cond_facts.py +154 -154
- package/md_cg/cond_template.json +106 -106
- package/md_cg/condition_anchor.py +142 -142
- package/md_cg/conformance.py +726 -726
- package/md_cg/consistency.py +717 -717
- package/md_cg/consolidate.py +1536 -1439
- package/md_cg/corpus.py +110 -110
- package/md_cg/crosscheck.py +1097 -1097
- package/md_cg/crypto.py +437 -437
- package/md_cg/d_meta.py +310 -310
- package/md_cg/datapath.py +334 -334
- package/md_cg/docindex.py +473 -473
- package/md_cg/eval_common.py +575 -575
- package/md_cg/evidence.py +580 -580
- package/md_cg/evolution.py +477 -477
- package/md_cg/export.py +220 -220
- package/md_cg/forgetting.py +581 -581
- package/md_cg/fsutil.py +329 -329
- package/md_cg/hotcache.py +238 -214
- package/md_cg/hyperedge.py +251 -251
- package/md_cg/identity.py +390 -390
- package/md_cg/insight.py +500 -500
- package/md_cg/interop.py +199 -0
- package/md_cg/lexicon/build_cedict_en_zh.py +329 -329
- package/md_cg/lexicon/build_standard_en.py +171 -171
- package/md_cg/lexicon/expand_en_zh.py +211 -211
- package/md_cg/lifecycle.py +272 -272
- package/md_cg/linkref.py +280 -280
- package/md_cg/links.py +622 -622
- package/md_cg/mcp_server.py +129 -32
- package/md_cg/md_whitebox.py +345 -345
- package/md_cg/mdcg.py +176 -117
- package/md_cg/mdcos.py +79 -13
- package/md_cg/metacognition.py +591 -591
- package/md_cg/migrate.py +119 -119
- package/md_cg/migrate_aeis.py +221 -221
- package/md_cg/migrate_roleplay.py +293 -293
- package/md_cg/migrate_wisdom_graph.py +360 -360
- package/md_cg/mreview/__init__.py +25 -25
- package/md_cg/mreview/__main__.py +110 -110
- package/md_cg/mreview/bundle.py +178 -178
- package/md_cg/mreview/candidates.py +262 -262
- package/md_cg/mreview/govern.py +693 -693
- package/md_cg/mreview/locate.py +939 -939
- package/md_cg/mreview/pipeline.py +728 -728
- package/md_cg/mreview/rules/duplication.json +21 -21
- package/md_cg/mreview/rules/field_coverage.json +54 -54
- package/md_cg/mreview/rules/source_license.json +21 -21
- package/md_cg/mreview/rules/template_flow.json +21 -21
- package/md_cg/mreview/ruleset.py +252 -252
- package/md_cg/nodefile.py +575 -575
- package/md_cg/pooling.py +484 -472
- package/md_cg/postings.py +298 -298
- package/md_cg/predict.py +1100 -1100
- package/md_cg/progressive.py +123 -123
- package/md_cg/protect.py +272 -272
- package/md_cg/protocol/md_cg_gate.proto +33 -33
- package/md_cg/protocol.py +372 -372
- package/md_cg/provenance.py +582 -582
- package/md_cg/reach.py +453 -453
- package/md_cg/readcache.py +85 -0
- package/md_cg/refindex.py +833 -833
- package/md_cg/refine.py +604 -604
- package/md_cg/roleviews.py +89 -89
- package/md_cg/routing.py +365 -365
- package/md_cg/scrub.py +852 -852
- package/md_cg/security.py +274 -274
- package/md_cg/self_state.py +1029 -1029
- package/md_cg/selfreport.py +151 -151
- package/md_cg/semantic/__init__.py +10 -10
- package/md_cg/semantic/canonical.py +122 -122
- package/md_cg/semantic/en_normalizer.py +364 -364
- package/md_cg/semantic/en_zh_map.json +28694 -0
- package/md_cg/semantic/export_en_zh_map.py +64 -0
- package/md_cg/semantic/unify.py +45 -0
- package/md_cg/semantic/zh_en_atoms.py +139 -139
- package/md_cg/signer.py +562 -562
- package/md_cg/sources.py +815 -582
- package/md_cg/statushdr.py +179 -179
- package/md_cg/stg.py +48 -37
- package/md_cg/subgraph.py +729 -729
- package/md_cg/sustain.py +1138 -1138
- package/md_cg/tasks.py +470 -470
- package/md_cg/test_action_derive.py +203 -203
- package/md_cg/test_audit_rotate.py +270 -270
- package/md_cg/test_autonomy.py +143 -143
- package/md_cg/test_bench_governance.py +102 -102
- package/md_cg/test_blindspot_tickets.py +166 -166
- package/md_cg/test_branches.py +249 -249
- package/md_cg/test_ccg_perturb.py +184 -184
- package/md_cg/test_ccgc.py +433 -433
- package/md_cg/test_census_prune.py +81 -81
- package/md_cg/test_cond_compose_anchors.py +76 -76
- package/md_cg/test_cond_match.py +165 -165
- package/md_cg/test_condition_anchor.py +81 -81
- package/md_cg/test_d_meta.py +412 -412
- package/md_cg/test_datapath_root.py +199 -199
- package/md_cg/test_en_pipeline.py +166 -166
- package/md_cg/test_gain_gate.py +212 -212
- package/md_cg/test_health_scale.py +173 -173
- package/md_cg/test_hive_ingest.py +285 -0
- package/md_cg/test_hot_cold.py +215 -215
- package/md_cg/test_hyperedge.py +245 -245
- package/md_cg/test_i26_empty_first_write.py +116 -0
- package/md_cg/test_i27_e041_identity.py +128 -0
- package/md_cg/test_i28_hotcache_prodpath.py +122 -0
- package/md_cg/test_identity_attribution.py +147 -147
- package/md_cg/test_index_durability.py +224 -224
- package/md_cg/test_interop.py +93 -0
- package/md_cg/test_lifecycle.py +309 -309
- package/md_cg/test_linkref.py +306 -306
- package/md_cg/test_lock.py +43 -43
- package/md_cg/test_md_access_parity.py +255 -255
- package/md_cg/test_md_writepath.py +345 -345
- package/md_cg/test_mdstore_search_parity.py +160 -0
- package/md_cg/test_mr_m2.py +587 -587
- package/md_cg/test_mr_m3.py +710 -710
- package/md_cg/test_mr_m4.py +485 -485
- package/md_cg/test_p0.py +250 -250
- package/md_cg/test_p1.py +316 -316
- package/md_cg/test_p10_identity.py +173 -173
- package/md_cg/test_p11_consistency.py +233 -233
- package/md_cg/test_p12_metacognition.py +212 -212
- package/md_cg/test_p13_encryption.py +241 -241
- package/md_cg/test_p14_sustain.py +249 -249
- package/md_cg/test_p15_scrub.py +280 -280
- package/md_cg/test_p16_self_state.py +301 -301
- package/md_cg/test_p17_predict.py +354 -354
- package/md_cg/test_p18_whitebox.py +171 -171
- package/md_cg/test_p19_migrate_roleplay.py +149 -149
- package/md_cg/test_p20_evolution.py +315 -315
- package/md_cg/test_p21_tokens.py +293 -270
- package/md_cg/test_p22_theory.py +175 -175
- package/md_cg/test_p23_links.py +311 -311
- package/md_cg/test_p24_evidence.py +227 -227
- package/md_cg/test_p25_weights.py +156 -156
- package/md_cg/test_p26_refindex.py +416 -416
- package/md_cg/test_p27_docindex.py +765 -765
- package/md_cg/test_p28_refcheck.py +305 -305
- package/md_cg/test_p29_session_ingest_export.py +354 -333
- package/md_cg/test_p3.py +11 -2
- package/md_cg/test_p30_maintain.py +330 -330
- package/md_cg/test_p31_insight.py +534 -534
- package/md_cg/test_p32_backfill.py +298 -298
- package/md_cg/test_p33_ccg_wiring.py +293 -293
- package/md_cg/test_p34_crosscheck.py +331 -331
- package/md_cg/test_p35_conditioned_claim.py +252 -252
- package/md_cg/test_p36_kp_align.py +230 -230
- package/md_cg/test_p37_condition_space.py +248 -248
- package/md_cg/test_p38_concurrent_flush.py +102 -0
- package/md_cg/test_p38_contextualize.py +273 -273
- package/md_cg/test_p39_verify_flow.py +113 -0
- package/md_cg/test_p39_vision_evidence.py +369 -369
- package/md_cg/test_p40_refine_worklist.py +241 -241
- package/md_cg/test_p41_evolve_patrol.py +224 -224
- package/md_cg/test_p42_provenance.py +269 -269
- package/md_cg/test_p43_pooling.py +412 -398
- package/md_cg/test_p44_md_whitebox.py +231 -231
- package/md_cg/test_p45_session_identity.py +219 -219
- package/md_cg/test_p46_unit_scope.py +272 -272
- package/md_cg/test_p47_session_view.py +281 -0
- package/md_cg/test_p4_fuzzy.py +223 -223
- package/md_cg/test_p5_semantic.py +226 -226
- package/md_cg/test_p6_consolidate.py +440 -387
- package/md_cg/test_p7_goals_recent.py +202 -202
- package/md_cg/test_p8_subgraph_chain.py +200 -200
- package/md_cg/test_p9_forget_protect.py +231 -231
- package/md_cg/test_predict_beta.py +135 -135
- package/md_cg/test_preflight_failclosed.py +100 -100
- package/md_cg/test_progressive.py +146 -146
- package/md_cg/test_protocol.py +243 -243
- package/md_cg/test_reach.py +378 -378
- package/md_cg/test_reach_keys.py +201 -201
- package/md_cg/test_read_clip.py +141 -141
- package/md_cg/test_readcache_prodpath.py +155 -0
- package/md_cg/test_retr_gates_prodpath.py +140 -0
- package/md_cg/test_retr_s1.py +340 -340
- package/md_cg/test_retr_s1b.py +209 -209
- package/md_cg/test_retr_s3.py +194 -194
- package/md_cg/test_retr_s4.py +163 -163
- package/md_cg/test_retr_s5.py +200 -200
- package/md_cg/test_retr_s6.py +157 -157
- package/md_cg/test_retr_s7.py +384 -384
- package/md_cg/test_retr_s8_time.py +369 -316
- package/md_cg/test_retr_s9_edges.py +286 -286
- package/md_cg/test_retr_s9_entity_ctx.py +175 -175
- package/md_cg/test_review_conformance.py +367 -367
- package/md_cg/test_role_views.py +354 -354
- package/md_cg/test_sem_noise.py +242 -242
- package/md_cg/test_semantic_canonical.py +241 -241
- package/md_cg/test_subproc_encoding.py +192 -192
- package/md_cg/test_sustain_mutual.py +153 -153
- package/md_cg/test_tasks.py +409 -409
- package/md_cg/test_tool_face.py +189 -189
- package/md_cg/test_transfer.py +180 -180
- package/md_cg/test_trust.py +361 -361
- package/md_cg/test_twophase.py +286 -286
- package/md_cg/test_v14_fixes.py +397 -397
- package/md_cg/test_validity_filter.py +280 -280
- package/md_cg/test_verify_answer.py +138 -138
- package/md_cg/test_wisdom_md_store.py +292 -292
- package/md_cg/test_writelimit.py +197 -197
- package/md_cg/test_writepipe.py +214 -214
- package/md_cg/theory.py +273 -273
- package/md_cg/tokens.py +677 -663
- package/md_cg/tool_face.py +260 -260
- package/md_cg/trust.py +986 -950
- package/md_cg/twophase.py +231 -231
- package/md_cg/units.py +667 -667
- package/md_cg/vision_evidence.py +666 -666
- package/md_cg/weights.py +624 -624
- package/md_cg/whitebox.py +527 -527
- package/md_cg/whitebox_kb/__init__.py +37 -37
- package/md_cg/whitebox_kb/aeis_core/__init__.py +42 -42
- package/md_cg/whitebox_kb/aeis_core/semantic.py +280 -280
- package/md_cg/whitebox_kb/aeis_core/textutil.py +13 -13
- package/md_cg/whitebox_kb/engine.py +310 -310
- package/md_cg/whitebox_kb/seed_knowledge//346/231/272/350/203/275/350/256/2723.4.md +5260 -5260
- package/md_cg/whitebox_kb/wisdom/browser_units.py +2631 -2631
- package/md_cg/whitebox_kb/wisdom/causal_discover.py +432 -432
- package/md_cg/whitebox_kb/wisdom/chat_engine.py +1506 -1506
- package/md_cg/whitebox_kb/wisdom/code_solidified.json +6195 -6195
- package/md_cg/whitebox_kb/wisdom/compiler_code_units.py +3033 -3033
- package/md_cg/whitebox_kb/wisdom/condition_algebra.py +112 -112
- package/md_cg/whitebox_kb/wisdom/condition_frame.py +315 -315
- package/md_cg/whitebox_kb/wisdom/condition_kb.py +108 -108
- package/md_cg/whitebox_kb/wisdom/conflict_map.json +4445 -4445
- package/md_cg/whitebox_kb/wisdom/core/lexer.py +512 -512
- package/md_cg/whitebox_kb/wisdom/core/name_checker.py +1023 -1023
- package/md_cg/whitebox_kb/wisdom/cspmn.py +258 -258
- package/md_cg/whitebox_kb/wisdom/csre.py +264 -264
- package/md_cg/whitebox_kb/wisdom/danmaku_audit.py +252 -252
- package/md_cg/whitebox_kb/wisdom/distilled_condition_units.json +6417 -6417
- package/md_cg/whitebox_kb/wisdom/docs/WB-EVAL-20260902b.json +1950 -1950
- package/md_cg/whitebox_kb/wisdom/docs/WB-EVAL-20260902c.json +1296 -1296
- package/md_cg/whitebox_kb/wisdom/docs/whitebox_capability_graph_demo.json +59 -59
- package/md_cg/whitebox_kb/wisdom/graph_db_units.py +3089 -3089
- package/md_cg/whitebox_kb/wisdom/knowledge_points.py +318 -318
- package/md_cg/whitebox_kb/wisdom/md_access.py +470 -470
- package/md_cg/whitebox_kb/wisdom/md_store.py +276 -251
- package/md_cg/whitebox_kb/wisdom/migrate_wisdom.py +476 -476
- package/md_cg/whitebox_kb/wisdom/navigate.py +248 -248
- package/md_cg/whitebox_kb/wisdom/neural_retrieve.py +224 -224
- package/md_cg/whitebox_kb/wisdom/os_units.py +2735 -2735
- package/md_cg/whitebox_kb/wisdom/pattern_separation.py +376 -376
- package/md_cg/whitebox_kb/wisdom/prereq_map.json +364 -364
- package/md_cg/whitebox_kb/wisdom/python_code_units.py +2766 -2766
- package/md_cg/whitebox_kb/wisdom/role_solidified.json +7 -7
- package/md_cg/whitebox_kb/wisdom/route_memory.py +244 -244
- package/md_cg/whitebox_kb/wisdom/scene_reconstruction.py +148 -148
- package/md_cg/whitebox_kb/wisdom/snr_report.json +40 -40
- package/md_cg/whitebox_kb/wisdom/test_code_compose_domains.py +7541 -7541
- package/md_cg/whitebox_kb/wisdom/test_compiler_self_bootstrap.py +354 -354
- package/md_cg/whitebox_kb/wisdom/test_ecosystem_assembly.py +158 -158
- package/md_cg/whitebox_kb/wisdom/test_ecosystem_demos.py +193 -193
- package/md_cg/whitebox_kb/wisdom/test_graph_db.py +292 -292
- package/md_cg/whitebox_kb/wisdom/test_python_self_bootstrap.py +160 -160
- package/md_cg/whitebox_kb/wisdom/trigger_words_index.json +4366 -4366
- package/md_cg/whitebox_kb/wisdom/verifier.py +1297 -1297
- package/md_cg/writelimit.py +356 -356
- package/md_cg/writepipe.py +550 -542
- package/package.json +97 -96
- package/skills/plugin.json +54 -54
- package/skills/skills/designer-perspective/SKILL.md +158 -158
- package/skills/skills/designer-perspective/references/01-observation-position.md +66 -66
- package/skills/skills/designer-perspective/references/02-structure-recognition.md +62 -62
- package/skills/skills/designer-perspective/references/03-direction-judgment.md +55 -55
- package/skills/skills/designer-perspective/references/04-qualification-verdict.md +72 -72
- package/skills/skills/designer-perspective/references/05-condition-attribution.md +74 -74
- package/skills/skills/designer-perspective/scripts/designer.py +545 -545
- package/skills/skills/designer-perspective/tests/cases.jsonl +17 -17
- package/skills/skills/designer-perspective/tests/selftest.py +61 -61
- package/skills/skills/lingshu-browser/SKILL.md +60 -60
- package/skills/skills/lingshu-compiler/SKILL.md +56 -56
- package/skills/skills/lingshu-compiler/units/analyze-type-infer/SKILL.md +45 -45
- package/skills/skills/lingshu-compiler/units/check-name-real/SKILL.md +45 -45
- package/skills/skills/lingshu-compiler/units/compile-assign/SKILL.md +45 -45
- package/skills/skills/lingshu-compiler/units/compile-expr-tree/SKILL.md +45 -45
- package/skills/skills/lingshu-compiler/units/compile-full-pipeline/SKILL.md +45 -45
- package/skills/skills/lingshu-compiler/units/compile-func-def/SKILL.md +45 -45
- package/skills/skills/lingshu-compiler/units/compile-if-then/SKILL.md +45 -45
- package/skills/skills/lingshu-compiler/units/compile-logic-expr/SKILL.md +45 -45
- package/skills/skills/lingshu-compiler/units/compile-recursive/SKILL.md +45 -45
- package/skills/skills/lingshu-compiler/units/compile-scope/SKILL.md +45 -45
- package/skills/skills/lingshu-compiler/units/compile-type-check/SKILL.md +45 -45
- package/skills/skills/lingshu-compiler/units/compile-while/SKILL.md +45 -45
- package/skills/skills/lingshu-compiler/units/compiler-0010c4bf/SKILL.md +45 -45
- package/skills/skills/lingshu-compiler/units/compiler-0355bffb/SKILL.md +45 -45
- package/skills/skills/lingshu-compiler/units/compiler-0361708a/SKILL.md +45 -45
- package/skills/skills/lingshu-compiler/units/compiler-054a0414/SKILL.md +45 -45
- package/skills/skills/lingshu-compiler/units/compiler-05a1691a/SKILL.md +45 -45
- package/skills/skills/lingshu-compiler/units/compiler-05eeed1e/SKILL.md +45 -45
- package/skills/skills/lingshu-compiler/units/compiler-0622a1f6/SKILL.md +45 -45
- package/skills/skills/lingshu-compiler/units/compiler-08b54217/SKILL.md +45 -45
- package/skills/skills/lingshu-compiler/units/compiler-0a62b70c/SKILL.md +45 -45
- package/skills/skills/lingshu-compiler/units/compiler-0ab24d00/SKILL.md +45 -45
- package/skills/skills/lingshu-compiler/units/compiler-0e093688/SKILL.md +45 -45
- package/skills/skills/lingshu-compiler/units/compiler-0e82b966/SKILL.md +45 -45
- package/skills/skills/lingshu-compiler/units/compiler-0ee9b9b9/SKILL.md +45 -45
- package/skills/skills/lingshu-compiler/units/compiler-0f3787e6/SKILL.md +45 -45
- package/skills/skills/lingshu-compiler/units/compiler-1028685f/SKILL.md +45 -45
- package/skills/skills/lingshu-compiler/units/compiler-11897630/SKILL.md +45 -45
- package/skills/skills/lingshu-compiler/units/compiler-16661b9b/SKILL.md +45 -45
- package/skills/skills/lingshu-compiler/units/compiler-1f722303/SKILL.md +45 -45
- package/skills/skills/lingshu-compiler/units/compiler-25be1262/SKILL.md +45 -45
- package/skills/skills/lingshu-compiler/units/compiler-2a76ba07/SKILL.md +45 -45
- package/skills/skills/lingshu-compiler/units/compiler-2dbea54a/SKILL.md +45 -45
- package/skills/skills/lingshu-compiler/units/compiler-2df52f16/SKILL.md +45 -45
- package/skills/skills/lingshu-compiler/units/compiler-2ec2c9d2/SKILL.md +45 -45
- package/skills/skills/lingshu-compiler/units/compiler-2f8c8f39/SKILL.md +45 -45
- package/skills/skills/lingshu-compiler/units/compiler-38378ac7/SKILL.md +45 -45
- package/skills/skills/lingshu-compiler/units/compiler-39457d2e/SKILL.md +45 -45
- package/skills/skills/lingshu-compiler/units/compiler-39fb5926/SKILL.md +45 -45
- package/skills/skills/lingshu-compiler/units/compiler-47f4fbfa/SKILL.md +45 -45
- package/skills/skills/lingshu-compiler/units/compiler-4a8cd1f1/SKILL.md +45 -45
- package/skills/skills/lingshu-compiler/units/compiler-4b230b6b/SKILL.md +45 -45
- package/skills/skills/lingshu-compiler/units/compiler-4cbbda95/SKILL.md +45 -45
- package/skills/skills/lingshu-compiler/units/compiler-4d5a68ff/SKILL.md +45 -45
- package/skills/skills/lingshu-compiler/units/compiler-56dc9bdd/SKILL.md +45 -45
- package/skills/skills/lingshu-compiler/units/compiler-57e76ebe/SKILL.md +45 -45
- package/skills/skills/lingshu-compiler/units/compiler-61ff016b/SKILL.md +45 -45
- package/skills/skills/lingshu-compiler/units/compiler-63eae588/SKILL.md +45 -45
- package/skills/skills/lingshu-compiler/units/compiler-64c4224b/SKILL.md +45 -45
- package/skills/skills/lingshu-compiler/units/compiler-66377955/SKILL.md +45 -45
- package/skills/skills/lingshu-compiler/units/compiler-674ab4f6/SKILL.md +45 -45
- package/skills/skills/lingshu-compiler/units/compiler-6af76fd6/SKILL.md +45 -45
- package/skills/skills/lingshu-compiler/units/compiler-6d979db2/SKILL.md +45 -45
- package/skills/skills/lingshu-compiler/units/compiler-6de326c0/SKILL.md +45 -45
- package/skills/skills/lingshu-compiler/units/compiler-6f1c0eea/SKILL.md +45 -45
- package/skills/skills/lingshu-compiler/units/compiler-724d6c9b/SKILL.md +45 -45
- package/skills/skills/lingshu-compiler/units/compiler-7690177f/SKILL.md +45 -45
- package/skills/skills/lingshu-compiler/units/compiler-7b229a7d/SKILL.md +45 -45
- package/skills/skills/lingshu-compiler/units/compiler-7f471b3a/SKILL.md +45 -45
- package/skills/skills/lingshu-compiler/units/compiler-815cad08/SKILL.md +45 -45
- package/skills/skills/lingshu-compiler/units/compiler-83c61634/SKILL.md +45 -45
- package/skills/skills/lingshu-compiler/units/compiler-8c798c81/SKILL.md +45 -45
- package/skills/skills/lingshu-compiler/units/compiler-8dd747ac/SKILL.md +45 -45
- package/skills/skills/lingshu-compiler/units/compiler-90324d0a/SKILL.md +45 -45
- package/skills/skills/lingshu-compiler/units/compiler-94b8d72d/SKILL.md +45 -45
- package/skills/skills/lingshu-compiler/units/compiler-94f12231/SKILL.md +45 -45
- package/skills/skills/lingshu-compiler/units/compiler-95937c16/SKILL.md +45 -45
- package/skills/skills/lingshu-compiler/units/compiler-98a5625b/SKILL.md +45 -45
- package/skills/skills/lingshu-compiler/units/compiler-98b4c42f/SKILL.md +45 -45
- package/skills/skills/lingshu-compiler/units/compiler-98e3894a/SKILL.md +45 -45
- package/skills/skills/lingshu-compiler/units/compiler-9bdfe4b8/SKILL.md +45 -45
- package/skills/skills/lingshu-compiler/units/compiler-9d9b4e83/SKILL.md +45 -45
- package/skills/skills/lingshu-compiler/units/compiler-9f7be5ad/SKILL.md +45 -45
- package/skills/skills/lingshu-compiler/units/compiler-a6daf076/SKILL.md +45 -45
- package/skills/skills/lingshu-compiler/units/compiler-a7995a0c/SKILL.md +45 -45
- package/skills/skills/lingshu-compiler/units/compiler-a8399248/SKILL.md +45 -45
- package/skills/skills/lingshu-compiler/units/compiler-aabbd099/SKILL.md +45 -45
- package/skills/skills/lingshu-compiler/units/compiler-afe169d8/SKILL.md +45 -45
- package/skills/skills/lingshu-compiler/units/compiler-b0678bda/SKILL.md +45 -45
- package/skills/skills/lingshu-compiler/units/compiler-b09bd196/SKILL.md +45 -45
- package/skills/skills/lingshu-compiler/units/compiler-b1396e23/SKILL.md +45 -45
- package/skills/skills/lingshu-compiler/units/compiler-b9b31ce0/SKILL.md +45 -45
- package/skills/skills/lingshu-compiler/units/compiler-c10264a7/SKILL.md +45 -45
- package/skills/skills/lingshu-compiler/units/compiler-cb1e8e4b/SKILL.md +45 -45
- package/skills/skills/lingshu-compiler/units/compiler-ccafd438/SKILL.md +45 -45
- package/skills/skills/lingshu-compiler/units/compiler-cda9c262/SKILL.md +45 -45
- package/skills/skills/lingshu-compiler/units/compiler-ce648068/SKILL.md +45 -45
- package/skills/skills/lingshu-compiler/units/compiler-cf5776a4/SKILL.md +45 -45
- package/skills/skills/lingshu-compiler/units/compiler-d974e5d3/SKILL.md +45 -45
- package/skills/skills/lingshu-compiler/units/compiler-e3979fd3/SKILL.md +45 -45
- package/skills/skills/lingshu-compiler/units/compiler-eb1cf2b5/SKILL.md +45 -45
- package/skills/skills/lingshu-compiler/units/compiler-ecb30d5b/SKILL.md +45 -45
- package/skills/skills/lingshu-compiler/units/compiler-f8c8b24b/SKILL.md +45 -45
- package/skills/skills/lingshu-compiler/units/compiler-f99fedbe/SKILL.md +45 -45
- package/skills/skills/lingshu-compiler/units/compiler-fa8ff5f7/SKILL.md +45 -45
- package/skills/skills/lingshu-compiler/units/compiler-fe1b058d/SKILL.md +45 -45
- package/skills/skills/lingshu-compiler/units/lex-chinese-program/SKILL.md +45 -45
- package/skills/skills/lingshu-compiler/units/lex-dao-de-jing/SKILL.md +45 -45
- package/skills/skills/lingshu-compiler/units/lex-nine-chapters/SKILL.md +45 -45
- package/skills/skills/lingshu-compiler/units/vm-arithmetic/SKILL.md +45 -45
- package/skills/skills/lingshu-compiler/units/vm-array-ops/SKILL.md +45 -45
- package/skills/skills/lingshu-compiler/units/vm-closure-call/SKILL.md +45 -45
- package/skills/skills/lingshu-compiler/units/vm-closure-create/SKILL.md +45 -45
- package/skills/skills/lingshu-compiler/units/vm-compare/SKILL.md +45 -45
- package/skills/skills/lingshu-compiler/units/vm-cond-jump/SKILL.md +45 -45
- package/skills/skills/lingshu-compiler/units/vm-cond-space/SKILL.md +45 -45
- package/skills/skills/lingshu-compiler/units/vm-exception/SKILL.md +45 -45
- package/skills/skills/lingshu-compiler/units/vm-func-call/SKILL.md +45 -45
- package/skills/skills/lingshu-compiler/units/vm-loop-run/SKILL.md +45 -45
- package/skills/skills/lingshu-compiler/units/vm-profiling/SKILL.md +45 -45
- package/skills/skills/lingshu-compiler/units/vm-refcount/SKILL.md +45 -45
- package/skills/skills/lingshu-compiler/units/vm-run-loop/SKILL.md +45 -45
- package/skills/skills/lingshu-compiler/units/vm-short-circuit/SKILL.md +45 -45
- package/skills/skills/lingshu-compiler/units/vm-stack-guard/SKILL.md +45 -45
- package/skills/skills/lingshu-compiler/units/vm-stack-ops/SKILL.md +45 -45
- package/skills/skills/lingshu-compiler/units/vm-trust-accum/SKILL.md +45 -45
- package/skills/skills/lingshu-graph/SKILL.md +63 -63
- package/skills/skills/lingshu-net/SKILL.md +48 -48
- package/skills/skills/lingshu-os/SKILL.md +64 -64
- package/skills/skills/lingshu-pylang/SKILL.md +71 -71
- package/src/bridge.ts +401 -401
- package/src/hooks.ts +38 -2
- package/src/lib/datapath.ts +326 -326
- package/src/lib/mdcg_client.ts +413 -413
- package/src/lib/mutual.ts +428 -428
- package/src/lib/prompt_safety.ts +62 -62
- package/src/lib/python_path.ts +71 -71
- package/src/lib/roleplay_web.ts +932 -932
- package/src/lib/token_store.ts +192 -192
- package/src/tools.ts +212 -212
- package/zcode/AGENTS.md +11 -3
- package/zcode/README.md +41 -41
- /package/docs/{mdcg → hive}//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" +0 -0
package/md_cg/self_state.py
CHANGED
|
@@ -1,1029 +1,1029 @@
|
|
|
1
|
-
# -*- coding: utf-8 -*-
|
|
2
|
-
"""md_cg · 自我状态层(SELF STATE):薄自我 + 富索引
|
|
3
|
-
|
|
4
|
-
理论约束(本仓已有文档,非外部发明):
|
|
5
|
-
· `docs/mdcg/灵枢_自我层定义.md:6`
|
|
6
|
-
SELF 层 = 身份 + 价值观 + 图接口,**不可遗忘**、跨会话自动加载。
|
|
7
|
-
自我层是**薄的**——它不该变成事件仓库。
|
|
8
|
-
· 智能论 §十一:情绪 = 信息差二阶 d^2D/dt^2;§十:P_gap / P_trust;
|
|
9
|
-
情感 = 信任二阶 d^2T/dt^2。
|
|
10
|
-
|
|
11
|
-
由此定下本模块的形态:
|
|
12
|
-
|
|
13
|
-
self 层只放两类一等公民(都很薄):
|
|
14
|
-
1. 自我状态卡(单例)——九项自我信息的**当前值 + 指针**
|
|
15
|
-
2. 关系节点(有向)——自我与其他智能的关系
|
|
16
|
-
具体细节(哪次会话 / 哪个任务 / 哪个人 / 哪个时间窗 / 哪条证据)
|
|
17
|
-
仍然留在原本的层里,由**认知图的标签与边**连接、索引过来。
|
|
18
|
-
|
|
19
|
-
九项自我信息 → 薄卡字段 / 富索引指向:
|
|
20
|
-
|
|
21
|
-
┌ 信息差 D d_current / d1 / d2 / emotion ← _reflection.jsonl(reflect 留痕)
|
|
22
|
-
├ 信任 P p_gap / p_trust / d2t / affect ← 各节点 evidence_log(verify 留痕)
|
|
23
|
-
├ 情绪 emotion(= d^2D/dt^2)
|
|
24
|
-
├ 情感 affect(= d^2T/dt^2)
|
|
25
|
-
├ 短期记忆 recent{n, span, roles, ptr} ← _recent.jsonl(只存摘要 + 指针)
|
|
26
|
-
├ 重要性 importance_self / important_refs ← 各节点 frontmatter.importance
|
|
27
|
-
├ 身份 identity_ref ← self 层 identity 锚点
|
|
28
|
-
├ 关系 relations{out,in} ← self_relation_* 关系节点
|
|
29
|
-
└ 预测校准 hit_rate / threshold / reflect / ece ← _prediction.jsonl + evidence_log
|
|
30
|
-
|
|
31
|
-
第九项(预测校准)是「SELF = 自我描述」走向「SELF = 关于自身的预测模型」的最小一步:
|
|
32
|
-
它回答**我预测得准吗、该不该反思**,数据只来自 predict 的 feedback 留痕与
|
|
33
|
-
metacognition 的 ECE 校准,样本不足一律 unknown——不新增计算,也不编造。
|
|
34
|
-
|
|
35
|
-
五个索引维度(「具体任务 / 人物关系 / 会话 / 时间 / 信任」)用 tag 命名空间落地,
|
|
36
|
-
`index(cg, dim, value)` 从全图反查;状态卡只登记维度标签、不搬运内容:
|
|
37
|
-
|
|
38
|
-
task:<slug> 具体任务
|
|
39
|
-
person:<slug> 具体人物关系
|
|
40
|
-
session:<id> 具体会话
|
|
41
|
-
time:<bucket> 具体时间(YYYY-MM-DD)
|
|
42
|
-
trust:<band> 具体信任档(low | mid | high)
|
|
43
|
-
|
|
44
|
-
一致性(`audit` 的每条判定都可重算,不依赖人的判断):
|
|
45
|
-
|
|
46
|
-
单例 同一 subject 至多一个状态卡
|
|
47
|
-
版本链 state_version 连续 +1;prev_state_hash 必须等于上一条留痕的 hash
|
|
48
|
-
时序单调 留痕时间戳不得回退
|
|
49
|
-
留痕对齐 卡上的 state_hash 必须等于留痕末条
|
|
50
|
-
派生自洽 d2/d2t 缺失时 emotion/affect 必须是 unknown(不许编造)
|
|
51
|
-
跨面一致 卡值与 metacognition 现算值偏差 ≤ DRIFT_TOL
|
|
52
|
-
身份唯一 identity_ref 必须指向存在的身份锚点
|
|
53
|
-
关系完整 关系两端必须存在;禁止自环
|
|
54
|
-
保护一致 状态卡必须「可覆盖」(否则自我认知被永久冻结)且「不可遗忘」
|
|
55
|
-
索引不悬空 维度标签指向的详情节点必须存在
|
|
56
|
-
|
|
57
|
-
「不可遗忘 ≠ 不可覆盖」的延伸:
|
|
58
|
-
· 身份锚点:既不可遗忘,也不可覆盖(protect 层保护);
|
|
59
|
-
· 自我状态卡:**不可遗忘,但必须可覆盖**——自我状态本来就要演化。
|
|
60
|
-
靠 `frontmatter.self_state is True` 在 protect 里获得覆盖豁免,见 protect.py。
|
|
61
|
-
"""
|
|
62
|
-
import hashlib
|
|
63
|
-
import json
|
|
64
|
-
import os
|
|
65
|
-
import re
|
|
66
|
-
import time
|
|
67
|
-
|
|
68
|
-
from . import identity, metacognition
|
|
69
|
-
from .fsutil import append_jsonl, read_jsonl
|
|
70
|
-
|
|
71
|
-
DEFAULT_SUBJECT = "self:lingshu"
|
|
72
|
-
STATE_PREFIX = "self_state_"
|
|
73
|
-
RELATION_PREFIX = "self_relation_"
|
|
74
|
-
LOG_FILE = "_self_state.jsonl"
|
|
75
|
-
TAG_STATE = "self_state"
|
|
76
|
-
TAG_RELATION = "self_relation"
|
|
77
|
-
SCHEMA_VERSION = 1
|
|
78
|
-
|
|
79
|
-
# 跨面一致容差:卡里的 D/T 与 metacognition 现算值允许的最大偏差
|
|
80
|
-
DRIFT_TOL = 0.05
|
|
81
|
-
# 新鲜度:状态卡超过该秒数未刷新即视为 stale
|
|
82
|
-
STALE_AFTER = 24 * 3600.0
|
|
83
|
-
RECENT_WINDOW = 20
|
|
84
|
-
|
|
85
|
-
# 五维索引(具体任务 / 人物关系 / 会话 / 时间 / 信任)
|
|
86
|
-
DIMENSIONS = ("task", "person", "session", "time", "trust")
|
|
87
|
-
TRUST_BANDS = ((0.70, "high"), (0.40, "mid"), (0.00, "low"))
|
|
88
|
-
RELATION_TYPES = ("collaborator", "user", "peer", "mentor", "student",
|
|
89
|
-
"adversary", "tool", "other")
|
|
90
|
-
STATE_LINK_TYPE = "refers_to"
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
# ---------------------------------------------------------------- 基础工具
|
|
94
|
-
|
|
95
|
-
# 生效条件:对 required 形参 text,返回 str(text or "") 经正则把非中英文数字连续段折叠为下划线、strip('_') 并 lower 后的字符串(text 为假值时返回空串);
|
|
96
|
-
def _slug(text):
|
|
97
|
-
"""归一化 id 片段:保留中英文数字,其余折叠为下划线。"""
|
|
98
|
-
s = re.sub(r"[^0-9A-Za-z\u4e00-\u9fff]+", "_", str(text or ""))
|
|
99
|
-
return s.strip("_").lower()
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
# 生效条件:对 required 形参 ts,当 float(ts) 可转换且 time.localtime 不抛 OSError 时返回本地时间 "%Y-%m-%d %H:%M:%S" 格式串;当 float(ts) 抛 TypeError/ValueError 或 time.localtime 抛 OSError 时返回空串;
|
|
103
|
-
def _iso(ts):
|
|
104
|
-
try:
|
|
105
|
-
return time.strftime("%Y-%m-%d %H:%M:%S", time.localtime(float(ts)))
|
|
106
|
-
except (TypeError, ValueError, OSError):
|
|
107
|
-
return ""
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
# 生效条件:subject 未传时取模块常量 DEFAULT_SUBJECT,返回模块常量 STATE_PREFIX 拼接 _slug(subject) 的结果;
|
|
111
|
-
def state_node_id(subject=DEFAULT_SUBJECT):
|
|
112
|
-
"""状态卡节点 id(单例:同一 subject 只有一个)。"""
|
|
113
|
-
return STATE_PREFIX + _slug(subject)
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
# 生效条件:对 required 形参 frm 与 to,返回模块常量 RELATION_PREFIX 拼接 _slug(frm)、"__"、_slug(to) 的字符串;
|
|
117
|
-
def relation_node_id(frm, to):
|
|
118
|
-
"""关系节点 id(有向)。"""
|
|
119
|
-
return f"{RELATION_PREFIX}{_slug(frm)}__{_slug(to)}"
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
# 生效条件:对 required 形参 cg,返回 os.path.join(cg.root, LOG_FILE)(LOG_FILE 为模块常量);
|
|
123
|
-
def log_path(cg):
|
|
124
|
-
return os.path.join(cg.root, LOG_FILE)
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
# 生效条件:对 required 形参 dim 与 value,返回 f"{dim}:{_slug(value)}";
|
|
128
|
-
def dim_tag(dim, value):
|
|
129
|
-
return f"{dim}:{_slug(value)}"
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
# 生效条件:对 required 形参 p_trust,若 p_trust is None 或 float(p_trust) 抛 TypeError/ValueError 则返回 None;否则按模块常量 TRUST_BANDS 顺序取首个 v >= lo 的档名,均不满足返回 "low";
|
|
133
|
-
def trust_band(p_trust):
|
|
134
|
-
"""把 P_trust 映射到信任档(索引维度之一)。"""
|
|
135
|
-
if p_trust is None:
|
|
136
|
-
return None
|
|
137
|
-
try:
|
|
138
|
-
v = float(p_trust)
|
|
139
|
-
except (TypeError, ValueError):
|
|
140
|
-
return None
|
|
141
|
-
for lo, name in TRUST_BANDS:
|
|
142
|
-
if v >= lo:
|
|
143
|
-
return name
|
|
144
|
-
return "low"
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
# ---------------------------------------------------------------- 留痕
|
|
148
|
-
|
|
149
|
-
# 生效条件:对 required 形参 cg 与 rec,调用 append_jsonl(log_path(cg), rec);若抛出 OSError 则静默忽略,无返回值;
|
|
150
|
-
def _append_log(cg, rec):
|
|
151
|
-
try:
|
|
152
|
-
append_jsonl(log_path(cg), rec)
|
|
153
|
-
except OSError:
|
|
154
|
-
pass
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
# 生效条件:对 required 形参 cg 与可选 limit/subject,读取 log_path(cg) 留痕(OSError 时为空列表);subject 为真值时只保留 r.get("subject") == subject 的记录;倒序后,limit 为真值时返回 recs[:int(limit)],limit 为假值(如 0)时返回全部倒序记录;
|
|
158
|
-
def history(cg, limit=100, subject=None):
|
|
159
|
-
"""自我状态留痕(倒序)。"""
|
|
160
|
-
try:
|
|
161
|
-
recs = list(read_jsonl(log_path(cg)))
|
|
162
|
-
except OSError:
|
|
163
|
-
recs = []
|
|
164
|
-
if subject:
|
|
165
|
-
recs = [r for r in recs if r.get("subject") == subject]
|
|
166
|
-
recs.reverse()
|
|
167
|
-
return recs[:int(limit)] if limit else recs
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
# 生效条件:对 required 形参 cg 与可选 subject,调用 history(cg, limit=0, subject=subject) 取全部倒序留痕;有记录时返回 recs[0](最新一条),否则返回 None;
|
|
171
|
-
def _last_log(cg, subject=None):
|
|
172
|
-
recs = history(cg, limit=0, subject=subject)
|
|
173
|
-
return recs[0] if recs else None
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
# ---------------------------------------------------------------- 读卡
|
|
177
|
-
|
|
178
|
-
# 生效条件:对 required 形参 cg 与 node_id,尝试返回 cg.get(node_id);若 cg.get 抛任何异常则返回 None;
|
|
179
|
-
def _node(cg, node_id):
|
|
180
|
-
try:
|
|
181
|
-
return cg.get(node_id)
|
|
182
|
-
except Exception:
|
|
183
|
-
return None
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
# 生效条件:对 required 形参 cg 与 node_id,取 _node(cg, node_id) 结果(假值时用空字典)的 "frontmatter" 值;该值假值时返回空字典,否则返回该 frontmatter;
|
|
187
|
-
def _fm_of(cg, node_id):
|
|
188
|
-
node = _node(cg, node_id)
|
|
189
|
-
return (node or {}).get("frontmatter") or {}
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
# 生效条件:对 required 形参 cg 与 node_id,从 cg.index(getattr 取不到或假值时用空字典)的 "nodes" 字段(假值时用空字典)中取 node_id 对应条目;缺键返回 None;
|
|
193
|
-
def _entry(cg, node_id):
|
|
194
|
-
return ((getattr(cg, "index", None) or {}).get("nodes") or {}).get(node_id)
|
|
195
|
-
|
|
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 子字典取;
|
|
198
|
-
def snapshot(cg, subject=DEFAULT_SUBJECT):
|
|
199
|
-
"""读当前状态卡 → 扁平 dict(不存在返回 None)。只读,不写盘。"""
|
|
200
|
-
nid = state_node_id(subject)
|
|
201
|
-
if _entry(cg, nid) is None:
|
|
202
|
-
return None
|
|
203
|
-
fm = _fm_of(cg, nid)
|
|
204
|
-
if not fm:
|
|
205
|
-
return None
|
|
206
|
-
out = {"node_id": nid, "subject": fm.get("state_subject") or subject,
|
|
207
|
-
"layer": "self", "state_version": fm.get("state_version"),
|
|
208
|
-
"state_hash": fm.get("state_hash"),
|
|
209
|
-
"prev_state_hash": fm.get("prev_state_hash"),
|
|
210
|
-
"state_ts": fm.get("state_ts"),
|
|
211
|
-
"updated_at": _iso(fm.get("state_ts"))}
|
|
212
|
-
for key in ("information_gap", "trust", "short_term", "relations",
|
|
213
|
-
"prediction", "dimensions", "emotion", "affect",
|
|
214
|
-
"importance_self", "important_refs", "identity_ref",
|
|
215
|
-
"state_links"):
|
|
216
|
-
out[key] = fm.get(key)
|
|
217
|
-
out["d_current"] = (fm.get("information_gap") or {}).get("d_current")
|
|
218
|
-
out["p_trust"] = (fm.get("trust") or {}).get("p_trust")
|
|
219
|
-
return out
|
|
220
|
-
|
|
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);
|
|
223
|
-
def _fingerprint(state):
|
|
224
|
-
"""状态指纹:只对「会漂移的实质字段」取哈希,用于幂等刷新与版本链。
|
|
225
|
-
|
|
226
|
-
注意:**不含 state_version**——版本号是元数据,若纳入指纹则每次刷新
|
|
227
|
-
指纹必变,幂等判断失效(自我状态会在无信息变化时无限刷版本)。
|
|
228
|
-
"""
|
|
229
|
-
core = {
|
|
230
|
-
"subject": state.get("subject"),
|
|
231
|
-
"information_gap": state.get("information_gap"),
|
|
232
|
-
"trust": state.get("trust"),
|
|
233
|
-
"emotion": state.get("emotion"),
|
|
234
|
-
"affect": state.get("affect"),
|
|
235
|
-
"importance_self": state.get("importance_self"),
|
|
236
|
-
"important_refs": state.get("important_refs"),
|
|
237
|
-
"identity_ref": state.get("identity_ref"),
|
|
238
|
-
"short_term": state.get("short_term"),
|
|
239
|
-
"relations": state.get("relations"),
|
|
240
|
-
"prediction": state.get("prediction"),
|
|
241
|
-
"dimensions": state.get("dimensions"),
|
|
242
|
-
"state_links": state.get("state_links"),
|
|
243
|
-
}
|
|
244
|
-
blob = json.dumps(core, sort_keys=True, ensure_ascii=False, default=str)
|
|
245
|
-
return hashlib.sha256(blob.encode("utf-8")).hexdigest()[:16]
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
# ---------------------------------------------------------------- 聚合(九项)
|
|
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);
|
|
274
|
-
def _prediction_face(cg):
|
|
275
|
-
"""预测校准(第九项):我预测得准吗、该不该反思?
|
|
276
|
-
|
|
277
|
-
数据源(只读留痕,不新增计算负担):
|
|
278
|
-
· `predict._hit_history` / `predict.dynamic_hit_threshold`
|
|
279
|
-
← `_prediction.jsonl`(predict_feedback 留痕)
|
|
280
|
-
· `metacognition.calibration` ← evidence_log(verify 留痕)的 ECE
|
|
281
|
-
· `d_meta.compute` / 反思留痕的 `d_meta` ← 边界压力趋势(d_meta_trend,
|
|
282
|
-
**不合成三代理**、不参与 D_task:D_task 与 D_meta 对象不同)
|
|
283
|
-
|
|
284
|
-
诚实边界:无预测留痕 → ok=False、hit_rate=None(未知即未知);
|
|
285
|
-
ECE 仅在 metacognition 有足够证据时给出,否则 None。绝不编造。
|
|
286
|
-
"""
|
|
287
|
-
try:
|
|
288
|
-
from . import predict as _predict
|
|
289
|
-
hist = list(_predict._hit_history(cg))
|
|
290
|
-
th = _predict.dynamic_hit_threshold(cg)
|
|
291
|
-
# P-T-73/74 通道贝叶斯:可信度后验(随样本量向基线收缩,诚实小样本)
|
|
292
|
-
beta = (_predict.channel_posterior(cg) or {}).get("all") or {}
|
|
293
|
-
except Exception: # noqa: BLE001
|
|
294
|
-
hist, th, beta = [], {}, {}
|
|
295
|
-
samples = len(hist)
|
|
296
|
-
hit_rate = (sum(1 for x in hist if x) / samples) if samples else None
|
|
297
|
-
try:
|
|
298
|
-
cal = metacognition.calibration(cg)
|
|
299
|
-
except Exception: # noqa: BLE001
|
|
300
|
-
cal = {}
|
|
301
|
-
meta_trend = _d_meta_trend(cg)
|
|
302
|
-
if samples == 0:
|
|
303
|
-
note = "无预测留痕:先 predict_feedback() 积累命中记录"
|
|
304
|
-
elif th.get("reflect"):
|
|
305
|
-
note = "命中率低于动态阈值:建议反思(D-006)"
|
|
306
|
-
else:
|
|
307
|
-
note = "命中率正常"
|
|
308
|
-
return {
|
|
309
|
-
"ok": samples > 0,
|
|
310
|
-
"samples": samples,
|
|
311
|
-
"hit_rate": None if hit_rate is None else round(hit_rate, 4),
|
|
312
|
-
"beta_mean": beta.get("mean"),
|
|
313
|
-
"beta_ci95": beta.get("ci95"),
|
|
314
|
-
"threshold": th.get("threshold"),
|
|
315
|
-
"reflect": bool(th.get("reflect")),
|
|
316
|
-
"ece": cal.get("ece") if cal.get("ok") else None,
|
|
317
|
-
"calibration": cal.get("verdict") if cal.get("ok") else None,
|
|
318
|
-
"d_meta_trend": meta_trend,
|
|
319
|
-
"note": note,
|
|
320
|
-
}
|
|
321
|
-
|
|
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");
|
|
324
|
-
def _recent_summary(cg, window):
|
|
325
|
-
"""短期记忆:只做窗口摘要,原文仍留在 _recent.jsonl(薄自我的关键取舍)。"""
|
|
326
|
-
try:
|
|
327
|
-
recs = cg.recent_events(limit=int(window), newest_first=False)
|
|
328
|
-
except Exception:
|
|
329
|
-
recs = []
|
|
330
|
-
ts = [float(r.get("t") or 0.0) for r in recs]
|
|
331
|
-
roles = {}
|
|
332
|
-
for r in recs:
|
|
333
|
-
k = str(r.get("role") or "unknown")
|
|
334
|
-
roles[k] = roles.get(k, 0) + 1
|
|
335
|
-
return {"n": len(recs), "span": [min(ts), max(ts)] if ts else None,
|
|
336
|
-
"roles": roles,
|
|
337
|
-
"ptr": os.path.basename(getattr(cg, "recent_log", "_recent.jsonl"))}
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
# 生效条件:对 required 形参 cg 与 subject,若 identity.profile(cg, subject) 抛异常则返回 (None, 0);否则返回 (prof.anchor.node_id(缺省 None), len(prof.get("anchors") or []));
|
|
341
|
-
def _identity_ref(cg, subject):
|
|
342
|
-
"""身份:只存指针(锚点节点 id),锚点内容仍在 self 层原节点里。"""
|
|
343
|
-
try:
|
|
344
|
-
prof = identity.profile(cg, subject)
|
|
345
|
-
except Exception:
|
|
346
|
-
return None, 0
|
|
347
|
-
anchors = prof.get("anchors") or []
|
|
348
|
-
ref = (prof.get("anchor") or {}).get("node_id")
|
|
349
|
-
return ref, len(anchors)
|
|
350
|
-
|
|
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};
|
|
353
|
-
def _relation_counts(cg, subject):
|
|
354
|
-
"""关系:从索引里的关系节点统计出/入度(不读文件正文)。"""
|
|
355
|
-
slug = _slug(subject)
|
|
356
|
-
nodes = ((getattr(cg, "index", None) or {}).get("nodes") or {})
|
|
357
|
-
out = inn = 0
|
|
358
|
-
for nid, e in nodes.items():
|
|
359
|
-
tags = set(e.get("tags") or [])
|
|
360
|
-
if TAG_RELATION not in tags and not nid.startswith(RELATION_PREFIX):
|
|
361
|
-
continue
|
|
362
|
-
if f"rel_from:{slug}" in tags:
|
|
363
|
-
out += 1
|
|
364
|
-
if f"rel_to:{slug}" in tags:
|
|
365
|
-
inn += 1
|
|
366
|
-
return {"out": out, "in": inn}
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
# 生效条件:对 required 形参 dimensions(假值时按空字典),只保留键 str(dim).strip().lower() 属于模块常量 DIMENSIONS 且 val 非 None 的维度;val 为 list/tuple/set 时展开,否则包装为单元素;过滤空字符串并去重后放入 out,返回 out;
|
|
370
|
-
def _normalize_dimensions(dimensions):
|
|
371
|
-
"""维度入参 → {dim: [value...]}(只保留已知维度)。"""
|
|
372
|
-
out = {}
|
|
373
|
-
for dim, val in (dimensions or {}).items():
|
|
374
|
-
d = str(dim).strip().lower()
|
|
375
|
-
if d not in DIMENSIONS or val is None:
|
|
376
|
-
continue
|
|
377
|
-
vals = val if isinstance(val, (list, tuple, set)) else [val]
|
|
378
|
-
clean = [str(v).strip() for v in vals if str(v).strip()]
|
|
379
|
-
if clean:
|
|
380
|
-
out.setdefault(d, [])
|
|
381
|
-
for v in clean:
|
|
382
|
-
if v not in out[d]:
|
|
383
|
-
out[d].append(v)
|
|
384
|
-
return out
|
|
385
|
-
|
|
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;
|
|
388
|
-
def _derive(cg, subject, window, importance, important_refs, dimensions,
|
|
389
|
-
links, old):
|
|
390
|
-
"""聚合九项自我信息 → 薄状态(纯读,不写盘)。"""
|
|
391
|
-
tr = metacognition.trace(cg, window=max(2, int(window)))
|
|
392
|
-
tt = metacognition.trust(cg, window=max(3, int(window)))
|
|
393
|
-
d2 = tr.get("d2") if tr.get("ok") else None
|
|
394
|
-
d2t = tt.get("d2") if tt.get("ok") else None
|
|
395
|
-
# 派生自洽:无数据即 unknown,绝不编造
|
|
396
|
-
emotion = tr.get("emotion") if (tr.get("ok") and d2 is not None) else "unknown"
|
|
397
|
-
affect = tt.get("emotion") if (tt.get("ok") and d2t is not None) else "unknown"
|
|
398
|
-
|
|
399
|
-
information_gap = {"ok": bool(tr.get("ok")), "n": tr.get("n", 0),
|
|
400
|
-
"d_current": tr.get("d_current"), "d1": tr.get("d1"),
|
|
401
|
-
"d2": d2, "trend": tr.get("trend")}
|
|
402
|
-
trust = {"ok": bool(tt.get("ok")), "n_events": tt.get("n_events", 0),
|
|
403
|
-
"p_gap": tt.get("p_gap"), "p_trust": tt.get("p_trust"),
|
|
404
|
-
"d1": tt.get("d1"), "d2t": d2t,
|
|
405
|
-
"band": trust_band(tt.get("p_trust"))}
|
|
406
|
-
short_term = _recent_summary(cg, window)
|
|
407
|
-
identity_ref, n_anchors = _identity_ref(cg, subject)
|
|
408
|
-
relations = _relation_counts(cg, subject)
|
|
409
|
-
|
|
410
|
-
if importance is None:
|
|
411
|
-
importance = (old or {}).get("importance_self")
|
|
412
|
-
importance = 1.0 if importance is None else float(importance)
|
|
413
|
-
if important_refs is None:
|
|
414
|
-
important_refs = list((old or {}).get("important_refs") or [])
|
|
415
|
-
important_refs = [str(x) for x in important_refs if str(x).strip()]
|
|
416
|
-
|
|
417
|
-
dims = _normalize_dimensions(dimensions)
|
|
418
|
-
if not dims:
|
|
419
|
-
dims = dict((old or {}).get("dimensions") or {})
|
|
420
|
-
# 信任档与时间桶自动入索引(用户点名的「具体信任」「具体时间」)
|
|
421
|
-
if trust["band"]:
|
|
422
|
-
dims.setdefault("trust", [])
|
|
423
|
-
if trust["band"] not in dims["trust"]:
|
|
424
|
-
dims["trust"].append(trust["band"])
|
|
425
|
-
today = time.strftime("%Y-%m-%d", time.localtime())
|
|
426
|
-
dims.setdefault("time", [])
|
|
427
|
-
if today not in dims["time"]:
|
|
428
|
-
dims["time"].append(today)
|
|
429
|
-
|
|
430
|
-
if links is None:
|
|
431
|
-
links = list((old or {}).get("state_links") or [])
|
|
432
|
-
|
|
433
|
-
return {"subject": subject, "information_gap": information_gap,
|
|
434
|
-
"trust": trust, "emotion": emotion, "affect": affect,
|
|
435
|
-
"short_term": short_term, "importance_self": importance,
|
|
436
|
-
"important_refs": important_refs, "identity_ref": identity_ref,
|
|
437
|
-
"identity_anchors": n_anchors, "relations": relations,
|
|
438
|
-
"prediction": _prediction_face(cg),
|
|
439
|
-
"dimensions": dims,
|
|
440
|
-
"state_links": [str(x) for x in links if str(x).strip()]}
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
# ---------------------------------------------------------------- 写卡
|
|
444
|
-
|
|
445
|
-
# 生效条件:对 required 形参 state,读取其 information_gap、trust、short_term、prediction、dimensions 等字段,渲染并返回末尾带换行的状态卡正文(含 D_meta 边界压力趋势一行:prediction.d_meta_trend 缺失时显示 None);span 为空时显示(无),角色为空时显示(无);
|
|
446
|
-
def _render(state):
|
|
447
|
-
"""状态卡正文:人类可读,且与 frontmatter 字段一一对应。"""
|
|
448
|
-
ig, tt = state["information_gap"], state["trust"]
|
|
449
|
-
st = state["short_term"]
|
|
450
|
-
pd = state.get("prediction") or {}
|
|
451
|
-
dims = state["dimensions"]
|
|
452
|
-
span = st.get("span")
|
|
453
|
-
span_txt = f"{_iso(span[0])} ~ {_iso(span[1])}" if span else "(无)"
|
|
454
|
-
roles = "、".join(f"{k}:{v}" for k, v in
|
|
455
|
-
sorted((st.get("roles") or {}).items())) or "(无)"
|
|
456
|
-
lines = [
|
|
457
|
-
f"# 自我状态卡 · {state['subject']}",
|
|
458
|
-
f"# 更新时间:{_iso(state.get('state_ts'))}"
|
|
459
|
-
f"(version={state.get('state_version')})",
|
|
460
|
-
f"# 信息差:D={ig.get('d_current')} dD/dt={ig.get('d1')} "
|
|
461
|
-
f"d^2D/dt^2={ig.get('d2')} → 情绪={state['emotion']}"
|
|
462
|
-
f"(趋势={ig.get('trend')})",
|
|
463
|
-
f"# 信任:P_gap={tt.get('p_gap')} P_trust={tt.get('p_trust')} "
|
|
464
|
-
f"d^2T/dt^2={tt.get('d2t')} → 情感={state['affect']}"
|
|
465
|
-
f"(档={tt.get('band')})",
|
|
466
|
-
f"# 短期记忆:{st.get('n')} 条({span_txt})角色 {roles};"
|
|
467
|
-
f"原文见 {st.get('ptr')}",
|
|
468
|
-
f"# 重要性:self={state['importance_self']},"
|
|
469
|
-
f"显式重要 {len(state.get('important_refs') or [])} 条",
|
|
470
|
-
f"# 身份:锚点 {state.get('identity_ref') or '(未设)'}"
|
|
471
|
-
f"(共 {state.get('identity_anchors')} 条)",
|
|
472
|
-
f"# 关系:出 {state['relations'].get('out')} / "
|
|
473
|
-
f"入 {state['relations'].get('in')}",
|
|
474
|
-
f"# 预测:命中率={pd.get('hit_rate')}(样本 {pd.get('samples')})"
|
|
475
|
-
f"阈值={pd.get('threshold')} 反思={'是' if pd.get('reflect') else '否'}"
|
|
476
|
-
f";后验可信度={pd.get('beta_mean')}"
|
|
477
|
-
f"(95%CI {pd.get('beta_ci95')})"
|
|
478
|
-
f";ECE={pd.get('ece')}({pd.get('calibration')})",
|
|
479
|
-
f"# D_meta:边界压力趋势(unmodeled_growth 均值)={pd.get('d_meta_trend')}"
|
|
480
|
-
f"(三代理分别观测、不合成单值,不参与 D_task)",
|
|
481
|
-
"# 索引:" + ";".join(
|
|
482
|
-
f"{d}={','.join(dims.get(d) or []) or '-'}" for d in DIMENSIONS),
|
|
483
|
-
"# 说明:本卡是薄自我——只登记当前值与指针;具体任务/人物/会话/"
|
|
484
|
-
"时间/信任的细节由认知图按上述索引连接,不在此搬运。",
|
|
485
|
-
]
|
|
486
|
-
return "\n".join(lines) + "\n"
|
|
487
|
-
|
|
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;
|
|
490
|
-
def refresh(cg, subject=DEFAULT_SUBJECT, window=RECENT_WINDOW, importance=None,
|
|
491
|
-
important_refs=None, dimensions=None, links=None,
|
|
492
|
-
actor="self_state", force=False, strict=False, session=None):
|
|
493
|
-
"""刷新自我状态卡(幂等):聚合九项 → 写卡 + 版本链留痕。
|
|
494
|
-
|
|
495
|
-
dimensions: {"task": "...", "person": "...", "session": "...",
|
|
496
|
-
"time": "...", "trust": "..."}(值可为列表)
|
|
497
|
-
links: 指向具体详情节点的边(认知图连接,而非内容复制)
|
|
498
|
-
strict: 版本链断裂时是否拒绝写入(默认修复并记录 chain_repaired)
|
|
499
|
-
session: 会话归因(嵌套身份):并入 session 维度,不覆盖显式声明值
|
|
500
|
-
"""
|
|
501
|
-
raw = snapshot(cg, subject)
|
|
502
|
-
tail = _last_log(cg, subject=subject)
|
|
503
|
-
# 卡缺失 / 被绕过 refresh 破坏(版本或 hash 对不上留痕)→ 以 append-only
|
|
504
|
-
# 留痕为准恢复元数据(维度索引、重要性、边)。留痕是权威记录。
|
|
505
|
-
old, rebuild = raw, False
|
|
506
|
-
if tail and (raw is None or raw.get("state_version") is None
|
|
507
|
-
or raw.get("state_hash") != tail.get("state_hash")):
|
|
508
|
-
rebuild = True
|
|
509
|
-
old = {"state_version": tail.get("version"),
|
|
510
|
-
"state_hash": tail.get("state_hash"),
|
|
511
|
-
"dimensions": tail.get("dimensions"),
|
|
512
|
-
"importance_self": tail.get("importance_self"),
|
|
513
|
-
"important_refs": tail.get("important_refs"),
|
|
514
|
-
"state_links": tail.get("state_links"),
|
|
515
|
-
"identity_ref": tail.get("identity_ref")}
|
|
516
|
-
# 会话归因:把本会话并入 session 维度(不覆盖调用方显式声明的维度值)。
|
|
517
|
-
dims_in = dict(dimensions or {})
|
|
518
|
-
if session:
|
|
519
|
-
vals = dims_in.get("session") or []
|
|
520
|
-
vals = list(vals) if isinstance(vals, (list, tuple, set)) else [vals]
|
|
521
|
-
vals = [str(v) for v in vals if str(v).strip()]
|
|
522
|
-
if str(session) not in vals:
|
|
523
|
-
vals.append(str(session))
|
|
524
|
-
dims_in["session"] = vals
|
|
525
|
-
state = _derive(cg, subject, window=window, importance=importance,
|
|
526
|
-
important_refs=important_refs, dimensions=dims_in,
|
|
527
|
-
links=links, old=old)
|
|
528
|
-
card_v = int((old or {}).get("state_version") or 0)
|
|
529
|
-
log_v = int(tail.get("version") or 0) if tail else 0
|
|
530
|
-
version = max(card_v, log_v) + 1
|
|
531
|
-
state["state_version"] = version
|
|
532
|
-
fp = _fingerprint(state)
|
|
533
|
-
if not rebuild and old and old.get("state_hash") == fp and not force:
|
|
534
|
-
return {"ok": True, "changed": False, "subject": subject,
|
|
535
|
-
"node_id": state_node_id(subject), "state": state,
|
|
536
|
-
"note": "状态未变化,跳过写入(幂等)"}
|
|
537
|
-
|
|
538
|
-
# 版本链:prev 必须等于上一条留痕的 hash
|
|
539
|
-
prev_hash = (raw or {}).get("state_hash")
|
|
540
|
-
chain_issue = None
|
|
541
|
-
if tail is not None and prev_hash != tail.get("state_hash"):
|
|
542
|
-
chain_issue = {
|
|
543
|
-
"code": "chain_repaired",
|
|
544
|
-
"why": f"prev_state_hash={prev_hash} 与留痕末条 "
|
|
545
|
-
f"{tail.get('state_hash')} 不一致"}
|
|
546
|
-
if strict:
|
|
547
|
-
return {"ok": False, "changed": False, "subject": subject,
|
|
548
|
-
"error": "chain_broken", "detail": chain_issue}
|
|
549
|
-
prev_hash = tail.get("state_hash") # 以留痕为准修复
|
|
550
|
-
if raw is None and tail is not None:
|
|
551
|
-
chain_issue = {"code": "state_rebuilt",
|
|
552
|
-
"why": "状态卡缺失但留痕存在,按留痕续链重建"}
|
|
553
|
-
prev_hash = tail.get("state_hash")
|
|
554
|
-
|
|
555
|
-
state["prev_state_hash"] = prev_hash
|
|
556
|
-
state["state_ts"] = time.time()
|
|
557
|
-
state["state_hash"] = _fingerprint(state)
|
|
558
|
-
|
|
559
|
-
tags = [TAG_STATE, f"state_subject:{_slug(subject)}"]
|
|
560
|
-
for d in DIMENSIONS:
|
|
561
|
-
for v in (state["dimensions"].get(d) or []):
|
|
562
|
-
t = dim_tag(d, v)
|
|
563
|
-
if t not in tags:
|
|
564
|
-
tags.append(t)
|
|
565
|
-
edges = [{"target": t, "relation_type": STATE_LINK_TYPE,
|
|
566
|
-
"weight": 0.6} for t in state["state_links"]]
|
|
567
|
-
|
|
568
|
-
nid = state_node_id(subject)
|
|
569
|
-
cg.add(nid, _render(state), layer="self", tags=tags, edges=edges,
|
|
570
|
-
importance=float(state["importance_self"]), confidence=0.9,
|
|
571
|
-
verification_basis="data", override=True, actor=actor,
|
|
572
|
-
self_state=True, state_subject=subject, state_version=version,
|
|
573
|
-
state_hash=state["state_hash"], prev_state_hash=prev_hash,
|
|
574
|
-
state_ts=state["state_ts"],
|
|
575
|
-
information_gap=state["information_gap"], trust=state["trust"],
|
|
576
|
-
emotion=state["emotion"], affect=state["affect"],
|
|
577
|
-
short_term=state["short_term"],
|
|
578
|
-
importance_self=state["importance_self"],
|
|
579
|
-
important_refs=state["important_refs"],
|
|
580
|
-
identity_ref=state["identity_ref"], relations=state["relations"],
|
|
581
|
-
prediction=state["prediction"],
|
|
582
|
-
dimensions=state["dimensions"], state_links=state["state_links"])
|
|
583
|
-
|
|
584
|
-
_append_log(cg, {
|
|
585
|
-
"t": state["state_ts"], "subject": subject, "node_id": nid,
|
|
586
|
-
"version": version, "state_hash": state["state_hash"],
|
|
587
|
-
"prev_state_hash": prev_hash, "actor": actor,
|
|
588
|
-
"d_current": state["information_gap"].get("d_current"),
|
|
589
|
-
"d1": state["information_gap"].get("d1"),
|
|
590
|
-
"d2": state["information_gap"].get("d2"),
|
|
591
|
-
"emotion": state["emotion"],
|
|
592
|
-
"p_gap": state["trust"].get("p_gap"),
|
|
593
|
-
"p_trust": state["trust"].get("p_trust"),
|
|
594
|
-
"d2t": state["trust"].get("d2t"), "affect": state["affect"],
|
|
595
|
-
"importance_self": state["importance_self"],
|
|
596
|
-
"important_refs": state["important_refs"],
|
|
597
|
-
"state_links": state["state_links"],
|
|
598
|
-
"recent_n": state["short_term"].get("n"),
|
|
599
|
-
"identity_ref": state["identity_ref"],
|
|
600
|
-
"relations": state["relations"],
|
|
601
|
-
"prediction_hit_rate": (state.get("prediction") or {}).get("hit_rate"),
|
|
602
|
-
"prediction_samples": (state.get("prediction") or {}).get("samples"),
|
|
603
|
-
"prediction_reflect": (state.get("prediction") or {}).get("reflect"),
|
|
604
|
-
"prediction_beta_mean": (state.get("prediction") or {}).get("beta_mean"),
|
|
605
|
-
"dimensions": state["dimensions"], "issue": chain_issue})
|
|
606
|
-
return {"ok": True, "changed": True, "subject": subject, "node_id": nid,
|
|
607
|
-
"state_version": version, "state_hash": state["state_hash"],
|
|
608
|
-
"prev_state_hash": prev_hash, "state": state,
|
|
609
|
-
"issue": chain_issue}
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
# ---------------------------------------------------------------- 关系
|
|
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 列表;
|
|
615
|
-
def relate(cg, frm, to, relation_type="collaborator", strength=0.5,
|
|
616
|
-
condition="", note="", reciprocal=False, actor="self_state"):
|
|
617
|
-
"""写一条有向关系(自我 ↔ 其他智能)。reciprocal=True 时同时写反向。"""
|
|
618
|
-
rt = str(relation_type or "other").strip().lower()
|
|
619
|
-
if rt not in RELATION_TYPES:
|
|
620
|
-
rt = "other"
|
|
621
|
-
if _slug(frm) == _slug(to):
|
|
622
|
-
return {"ok": False, "error": "self_loop",
|
|
623
|
-
"detail": "关系两端不能是同一主体(自环)"}
|
|
624
|
-
try:
|
|
625
|
-
w = max(0.0, min(1.0, float(strength)))
|
|
626
|
-
except (TypeError, ValueError):
|
|
627
|
-
w = 0.5
|
|
628
|
-
written = []
|
|
629
|
-
for a, b in ((frm, to), (to, frm)) if reciprocal else ((frm, to),):
|
|
630
|
-
nid = relation_node_id(a, b)
|
|
631
|
-
tags = [TAG_RELATION, f"rel_from:{_slug(a)}", f"rel_to:{_slug(b)}",
|
|
632
|
-
f"relation:{rt}", f"person:{_slug(b)}",
|
|
633
|
-
f"state_subject:{_slug(a)}"]
|
|
634
|
-
body = (f"# 关系:{a} → {b}\n"
|
|
635
|
-
f"# 类型:{rt}\n"
|
|
636
|
-
f"# 强度:{w}\n"
|
|
637
|
-
f"# 条件:{condition or '(未声明)'}\n"
|
|
638
|
-
f"# 备注:{note or '(无)'}\n")
|
|
639
|
-
cg.add(nid, body, layer="self", tags=tags,
|
|
640
|
-
edges=[{"target": b, "relation_type": rt, "weight": w,
|
|
641
|
-
"condition": condition}],
|
|
642
|
-
importance=w, confidence=0.8, verification_basis="other",
|
|
643
|
-
override=True, actor=actor,
|
|
644
|
-
self_relation=True, rel_from=a, rel_to=b,
|
|
645
|
-
relation_type=rt, strength=w, condition=condition,
|
|
646
|
-
reciprocal=bool(reciprocal))
|
|
647
|
-
written.append(nid)
|
|
648
|
-
return {"ok": True, "written": written, "from": frm, "to": to,
|
|
649
|
-
"relation_type": rt, "strength": w,
|
|
650
|
-
"reciprocal": bool(reciprocal)}
|
|
651
|
-
|
|
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;
|
|
654
|
-
def relations(cg, subject=None, direction="both"):
|
|
655
|
-
"""列出关系节点(按 subject 过滤出/入)。"""
|
|
656
|
-
nodes = ((getattr(cg, "index", None) or {}).get("nodes") or {})
|
|
657
|
-
slug = _slug(subject) if subject else None
|
|
658
|
-
out = []
|
|
659
|
-
for nid, e in nodes.items():
|
|
660
|
-
tags = set(e.get("tags") or [])
|
|
661
|
-
if TAG_RELATION not in tags and not nid.startswith(RELATION_PREFIX):
|
|
662
|
-
continue
|
|
663
|
-
if slug:
|
|
664
|
-
is_out = f"rel_from:{slug}" in tags
|
|
665
|
-
is_in = f"rel_to:{slug}" in tags
|
|
666
|
-
if direction == "out" and not is_out:
|
|
667
|
-
continue
|
|
668
|
-
if direction == "in" and not is_in:
|
|
669
|
-
continue
|
|
670
|
-
if direction == "both" and not (is_out or is_in):
|
|
671
|
-
continue
|
|
672
|
-
rels = [t[len("relation:"):] for t in tags if t.startswith("relation:")]
|
|
673
|
-
frm = [t[len("rel_from:"):] for t in tags if t.startswith("rel_from:")]
|
|
674
|
-
to = [t[len("rel_to:"):] for t in tags if t.startswith("rel_to:")]
|
|
675
|
-
out.append({"node_id": nid, "from": frm[0] if frm else None,
|
|
676
|
-
"to": to[0] if to else None,
|
|
677
|
-
"relation_type": rels[0] if rels else None,
|
|
678
|
-
"strength": e.get("importance"),
|
|
679
|
-
"layer": e.get("layer")})
|
|
680
|
-
out.sort(key=lambda x: (-float(x.get("strength") or 0), x["node_id"]))
|
|
681
|
-
return out
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
# ---------------------------------------------------------------- 索引
|
|
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;
|
|
687
|
-
def index(cg, dim, value, limit=50, with_content=False):
|
|
688
|
-
"""按五维索引反查具体详情节点(认知图连接,不是内容搬运)。"""
|
|
689
|
-
d = str(dim or "").strip().lower()
|
|
690
|
-
if d not in DIMENSIONS:
|
|
691
|
-
return {"ok": False, "error": "unknown_dimension",
|
|
692
|
-
"allowed": list(DIMENSIONS)}
|
|
693
|
-
tag = dim_tag(d, value)
|
|
694
|
-
nodes = ((getattr(cg, "index", None) or {}).get("nodes") or {})
|
|
695
|
-
hits = [(nid, e) for nid, e in nodes.items()
|
|
696
|
-
if tag in (e.get("tags") or [])]
|
|
697
|
-
hits.sort(key=lambda kv: (-float(kv[1].get("importance") or 0),
|
|
698
|
-
-float(kv[1].get("created_at") or 0),
|
|
699
|
-
str(kv[0] or "")))
|
|
700
|
-
items = []
|
|
701
|
-
for nid, e in hits[:int(limit)]:
|
|
702
|
-
item = {"node_id": nid, "layer": e.get("layer"),
|
|
703
|
-
"importance": e.get("importance"), "tags": e.get("tags"),
|
|
704
|
-
"path": e.get("path")}
|
|
705
|
-
if with_content:
|
|
706
|
-
node = _node(cg, nid) or {}
|
|
707
|
-
item["content"] = (node.get("content") or "")[:400]
|
|
708
|
-
items.append(item)
|
|
709
|
-
return {"ok": True, "dimension": d, "value": str(value), "tag": tag,
|
|
710
|
-
"count": len(hits), "items": items}
|
|
711
|
-
|
|
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 {}};
|
|
714
|
-
def dimensions(cg, subject=DEFAULT_SUBJECT):
|
|
715
|
-
"""状态卡登记的五维索引标签。"""
|
|
716
|
-
st = snapshot(cg, subject)
|
|
717
|
-
if not st:
|
|
718
|
-
return {"ok": False, "error": "no_state", "subject": subject}
|
|
719
|
-
return {"ok": True, "subject": subject,
|
|
720
|
-
"dimensions": st.get("dimensions") or {}}
|
|
721
|
-
|
|
722
|
-
|
|
723
|
-
# ---------------------------------------------------------------- 审计
|
|
724
|
-
|
|
725
|
-
# 生效条件:对 required 形参 code、severity、why,返回含这三个键并合并 **extra 的 dict;
|
|
726
|
-
def _issue(code, severity, why, **extra):
|
|
727
|
-
d = {"code": code, "severity": severity, "why": why}
|
|
728
|
-
d.update(extra)
|
|
729
|
-
return d
|
|
730
|
-
|
|
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;
|
|
733
|
-
def audit(cg, subject=DEFAULT_SUBJECT, window=RECENT_WINDOW):
|
|
734
|
-
"""自我信息一致性审计:全部判定可重算(不依赖人的判断)。"""
|
|
735
|
-
from . import protect as _protect
|
|
736
|
-
issues = []
|
|
737
|
-
nid = state_node_id(subject)
|
|
738
|
-
nodes = ((getattr(cg, "index", None) or {}).get("nodes") or {})
|
|
739
|
-
st = snapshot(cg, subject)
|
|
740
|
-
|
|
741
|
-
# 1 单例:同 subject 的状态卡只能有一张
|
|
742
|
-
slug = _slug(subject)
|
|
743
|
-
cards = [n for n, e in nodes.items()
|
|
744
|
-
if TAG_STATE in (e.get("tags") or [])
|
|
745
|
-
and f"state_subject:{slug}" in (e.get("tags") or [])]
|
|
746
|
-
if len(cards) > 1:
|
|
747
|
-
issues.append(_issue("duplicate_state", "error",
|
|
748
|
-
f"同一 subject 有 {len(cards)} 张状态卡",
|
|
749
|
-
nodes=cards))
|
|
750
|
-
if st is None:
|
|
751
|
-
issues.append(_issue("missing_state", "warn",
|
|
752
|
-
"状态卡不存在:先 refresh() 建立", node_id=nid))
|
|
753
|
-
return {"ok": False, "verdict": "absent", "subject": subject,
|
|
754
|
-
"n_issues": len(issues), "issues": issues,
|
|
755
|
-
"checked_at": time.time()}
|
|
756
|
-
|
|
757
|
-
ordered = list(reversed(history(cg, limit=0, subject=subject)))
|
|
758
|
-
|
|
759
|
-
# 2 版本链:version 连续 +1,prev 必须等于上一条 hash
|
|
760
|
-
for i in range(1, len(ordered)):
|
|
761
|
-
p, c = ordered[i - 1], ordered[i]
|
|
762
|
-
if c.get("version") != (p.get("version") or 0) + 1:
|
|
763
|
-
issues.append(_issue(
|
|
764
|
-
"version_gap", "error",
|
|
765
|
-
f"版本不连续:{p.get('version')} → {c.get('version')}",
|
|
766
|
-
at=c.get("t")))
|
|
767
|
-
if c.get("prev_state_hash") != p.get("state_hash"):
|
|
768
|
-
issues.append(_issue(
|
|
769
|
-
"chain_broken", "error",
|
|
770
|
-
f"留痕断链:prev={c.get('prev_state_hash')} ≠ 上一条 "
|
|
771
|
-
f"{p.get('state_hash')}", at=c.get("t")))
|
|
772
|
-
if float(c.get("t") or 0) < float(p.get("t") or 0):
|
|
773
|
-
issues.append(_issue("time_regression", "error",
|
|
774
|
-
"留痕时间戳回退", at=c.get("t")))
|
|
775
|
-
|
|
776
|
-
# 3 留痕对齐:卡上的 hash 必须等于留痕末条
|
|
777
|
-
if ordered:
|
|
778
|
-
tail = ordered[-1]
|
|
779
|
-
if st.get("state_hash") != tail.get("state_hash"):
|
|
780
|
-
issues.append(_issue(
|
|
781
|
-
"log_tail_mismatch", "error",
|
|
782
|
-
f"卡 hash={st.get('state_hash')} ≠ 留痕末条 "
|
|
783
|
-
f"{tail.get('state_hash')}(卡被绕过 refresh 改过?)",
|
|
784
|
-
node_id=nid))
|
|
785
|
-
|
|
786
|
-
# 4 派生自洽:无数据时必须 unknown(不许编造)
|
|
787
|
-
ig = st.get("information_gap") or {}
|
|
788
|
-
tt = st.get("trust") or {}
|
|
789
|
-
if ig.get("d2") is None and st.get("emotion") not in (None, "unknown"):
|
|
790
|
-
issues.append(_issue("fabricated_emotion", "error",
|
|
791
|
-
f"d^2D/dt^2 缺失但 emotion={st.get('emotion')}"))
|
|
792
|
-
if tt.get("d2t") is None and st.get("affect") not in (None, "unknown"):
|
|
793
|
-
issues.append(_issue("fabricated_affect", "error",
|
|
794
|
-
f"d^2T/dt^2 缺失但 affect={st.get('affect')}"))
|
|
795
|
-
|
|
796
|
-
# 5 跨面一致:与 metacognition 现算值比对(drift)
|
|
797
|
-
now = _derive(cg, subject, window=window, importance=None,
|
|
798
|
-
important_refs=None, dimensions=None, links=None, old=None)
|
|
799
|
-
for name, old_v, new_v in (
|
|
800
|
-
("d_current", ig.get("d_current"),
|
|
801
|
-
now["information_gap"].get("d_current")),
|
|
802
|
-
("d2", ig.get("d2"), now["information_gap"].get("d2")),
|
|
803
|
-
("p_trust", tt.get("p_trust"), now["trust"].get("p_trust")),
|
|
804
|
-
("d2t", tt.get("d2t"), now["trust"].get("d2t"))):
|
|
805
|
-
if old_v is None and new_v is None:
|
|
806
|
-
continue
|
|
807
|
-
try:
|
|
808
|
-
if old_v is None or new_v is None or \
|
|
809
|
-
abs(float(old_v) - float(new_v)) > DRIFT_TOL:
|
|
810
|
-
issues.append(_issue(
|
|
811
|
-
"drift", "warn",
|
|
812
|
-
f"{name} 卡值={old_v} 现算={new_v}(容差 {DRIFT_TOL})",
|
|
813
|
-
field=name))
|
|
814
|
-
except (TypeError, ValueError):
|
|
815
|
-
issues.append(_issue("drift", "warn",
|
|
816
|
-
f"{name} 不可比较:{old_v} vs {new_v}",
|
|
817
|
-
field=name))
|
|
818
|
-
|
|
819
|
-
# 5b 预测面漂移:预测能力(命中率)随 predict_feedback 演化;卡值过时即为
|
|
820
|
-
# 漂移——这是「自我模型是否跟上自身预测表现」的可重算判定。
|
|
821
|
-
pd_old = st.get("prediction") or {}
|
|
822
|
-
pd_new = now.get("prediction") or {}
|
|
823
|
-
o_hr, n_hr = pd_old.get("hit_rate"), pd_new.get("hit_rate")
|
|
824
|
-
if not (o_hr is None and n_hr is None):
|
|
825
|
-
try:
|
|
826
|
-
if o_hr is None or n_hr is None or \
|
|
827
|
-
abs(float(o_hr) - float(n_hr)) > DRIFT_TOL:
|
|
828
|
-
issues.append(_issue(
|
|
829
|
-
"prediction_drift", "warn",
|
|
830
|
-
f"预测命中率 卡值={o_hr} 现算={n_hr}(容差 {DRIFT_TOL})",
|
|
831
|
-
field="hit_rate"))
|
|
832
|
-
except (TypeError, ValueError):
|
|
833
|
-
issues.append(_issue("prediction_drift", "warn",
|
|
834
|
-
f"预测命中率不可比较:{o_hr} vs {n_hr}",
|
|
835
|
-
field="hit_rate"))
|
|
836
|
-
|
|
837
|
-
# 6 身份唯一:identity_ref 必须指向存在的锚点
|
|
838
|
-
ref = st.get("identity_ref")
|
|
839
|
-
if not ref:
|
|
840
|
-
issues.append(_issue("identity_missing", "info",
|
|
841
|
-
"identity_ref 为空:先在 self 层写身份锚点"))
|
|
842
|
-
elif ref not in nodes:
|
|
843
|
-
issues.append(_issue("identity_dangling", "error",
|
|
844
|
-
f"identity_ref={ref} 指向不存在的节点", ref=ref))
|
|
845
|
-
|
|
846
|
-
# 7 关系完整:禁止自环、禁止同一有向对重复
|
|
847
|
-
seen_rel = {}
|
|
848
|
-
for rel in relations(cg, subject=None):
|
|
849
|
-
nid_r = rel["node_id"]
|
|
850
|
-
if rel["from"] == rel["to"]:
|
|
851
|
-
issues.append(_issue("relation_self_loop", "error",
|
|
852
|
-
f"关系自环:{rel['from']}", node_id=nid_r))
|
|
853
|
-
key = (rel["from"], rel["to"])
|
|
854
|
-
if key in seen_rel:
|
|
855
|
-
issues.append(_issue("duplicate_relation", "error",
|
|
856
|
-
f"重复关系 {key}", node_id=nid_r))
|
|
857
|
-
seen_rel[key] = nid_r
|
|
858
|
-
|
|
859
|
-
# 8 保护一致:状态卡必须可覆盖(否则自我认知被永久冻结)且不可遗忘
|
|
860
|
-
try:
|
|
861
|
-
imm, why = _protect.is_immutable(cg, nid)
|
|
862
|
-
if imm:
|
|
863
|
-
issues.append(_issue("protection_locked", "error",
|
|
864
|
-
f"状态卡不可覆盖:{why}(自我状态将无法演化)"))
|
|
865
|
-
pro, _ = _protect.is_protected(cg, nid)
|
|
866
|
-
if not pro:
|
|
867
|
-
issues.append(_issue("protection_missing", "warn",
|
|
868
|
-
"状态卡未受保护:自我信息可能被遗忘"))
|
|
869
|
-
except Exception as exc: # pragma: no cover
|
|
870
|
-
issues.append(_issue("protection_check_failed", "warn", str(exc)))
|
|
871
|
-
|
|
872
|
-
# 9 索引不悬空:维度标签指向的详情节点必须存在
|
|
873
|
-
dims = st.get("dimensions") or {}
|
|
874
|
-
for d, vals in dims.items():
|
|
875
|
-
if d not in DIMENSIONS:
|
|
876
|
-
issues.append(_issue("unknown_dimension", "warn",
|
|
877
|
-
f"未知索引维度 {d}"))
|
|
878
|
-
continue
|
|
879
|
-
for v in (vals or []):
|
|
880
|
-
tag = dim_tag(d, v)
|
|
881
|
-
holders = [n for n, e in nodes.items()
|
|
882
|
-
if tag in (e.get("tags") or [])]
|
|
883
|
-
if not holders:
|
|
884
|
-
issues.append(_issue("dimension_orphan", "warn",
|
|
885
|
-
f"索引 {tag} 无对应节点(悬空索引)",
|
|
886
|
-
dimension=d, value=v))
|
|
887
|
-
|
|
888
|
-
# 10 新鲜度
|
|
889
|
-
try:
|
|
890
|
-
age = time.time() - float(st.get("state_ts") or 0)
|
|
891
|
-
if age > STALE_AFTER:
|
|
892
|
-
issues.append(_issue("stale", "warn",
|
|
893
|
-
f"状态卡 {age / 3600:.1f} 小时未刷新"))
|
|
894
|
-
except (TypeError, ValueError):
|
|
895
|
-
issues.append(_issue("stale", "warn", "state_ts 不可解析"))
|
|
896
|
-
|
|
897
|
-
errors = [i for i in issues if i["severity"] == "error"]
|
|
898
|
-
warns = [i for i in issues if i["severity"] == "warn"]
|
|
899
|
-
verdict = "broken" if errors else ("drift" if warns else "consistent")
|
|
900
|
-
return {"ok": not errors, "verdict": verdict, "subject": subject,
|
|
901
|
-
"node_id": nid, "n_issues": len(issues),
|
|
902
|
-
"n_errors": len(errors), "issues": issues,
|
|
903
|
-
"state_version": st.get("state_version"),
|
|
904
|
-
"state_hash": st.get("state_hash"), "checked_at": time.time()}
|
|
905
|
-
|
|
906
|
-
|
|
907
|
-
# ---------------------------------------------------------------- 加载 / 概览
|
|
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 及这些字段;
|
|
910
|
-
def bootstrap(cg, subject=DEFAULT_SUBJECT, window=RECENT_WINDOW,
|
|
911
|
-
auto_refresh=True, actor="bootstrap"):
|
|
912
|
-
"""会话启动:加载自我状态卡 + 关系 + 最近留痕 + 索引概览。
|
|
913
|
-
|
|
914
|
-
这是「跨会话自动加载自我」的入口:self 层薄卡 + 认知图连接。
|
|
915
|
-
"""
|
|
916
|
-
st = snapshot(cg, subject)
|
|
917
|
-
if st is None and auto_refresh:
|
|
918
|
-
refresh(cg, subject, window=window, actor=actor)
|
|
919
|
-
st = snapshot(cg, subject)
|
|
920
|
-
elif st is not None and auto_refresh:
|
|
921
|
-
# 只在漂移或过期时刷新,避免每次启动都写盘
|
|
922
|
-
try:
|
|
923
|
-
age = time.time() - float(st.get("state_ts") or 0)
|
|
924
|
-
except (TypeError, ValueError):
|
|
925
|
-
age = STALE_AFTER + 1
|
|
926
|
-
if age > STALE_AFTER:
|
|
927
|
-
refresh(cg, subject, window=window, actor=actor)
|
|
928
|
-
st = snapshot(cg, subject)
|
|
929
|
-
rels = relations(cg, subject=subject)
|
|
930
|
-
return {"ok": st is not None, "subject": subject, "state": st,
|
|
931
|
-
"relations": rels, "relations_count": len(rels),
|
|
932
|
-
"recent_states": history(cg, limit=5, subject=subject),
|
|
933
|
-
"dimensions": (st or {}).get("dimensions") or {},
|
|
934
|
-
"loaded_at": time.time()}
|
|
935
|
-
|
|
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;
|
|
938
|
-
def _with_session_slice(cg, out, st, session):
|
|
939
|
-
"""给 summary 结果补「本会话切片」(薄卡 + 富索引,不改单例语义)。
|
|
940
|
-
|
|
941
|
-
· session_registered —— 本会话是否已登记在该状态卡的 session 维度上;
|
|
942
|
-
· session_refs —— 该维度反查到的详情节点指针(不搬运内容)。
|
|
943
|
-
"""
|
|
944
|
-
if not session:
|
|
945
|
-
return out
|
|
946
|
-
s = str(session)
|
|
947
|
-
out["session"] = s
|
|
948
|
-
dims = (st or {}).get("dimensions") or {}
|
|
949
|
-
out["session_registered"] = bool(s in (dims.get("session") or []))
|
|
950
|
-
try:
|
|
951
|
-
res = index(cg, "session", s, limit=20)
|
|
952
|
-
out["session_refs"] = {"count": res.get("count", 0),
|
|
953
|
-
"items": res.get("items", [])}
|
|
954
|
-
except Exception: # noqa: BLE001
|
|
955
|
-
out["session_refs"] = {"count": 0, "items": [], "degraded": True}
|
|
956
|
-
return out
|
|
957
|
-
|
|
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 返回;
|
|
960
|
-
def summary(cg, subject=DEFAULT_SUBJECT, session=None):
|
|
961
|
-
"""一句话自我状态(供 health / 面板)。
|
|
962
|
-
|
|
963
|
-
session:会话归因切片。**不改变单例卡语义**——只回报「本会话是否已登记在
|
|
964
|
-
该卡的 session 维度上」以及该维度反查到的详情节点指针(薄卡 + 富索引)。
|
|
965
|
-
"""
|
|
966
|
-
st = snapshot(cg, subject)
|
|
967
|
-
if not st:
|
|
968
|
-
return _with_session_slice(cg, {"ok": False, "subject": subject,
|
|
969
|
-
"note": "无状态卡"}, st, session)
|
|
970
|
-
ig, tt = st.get("information_gap") or {}, st.get("trust") or {}
|
|
971
|
-
out = {"ok": True, "subject": subject,
|
|
972
|
-
"version": st.get("state_version"),
|
|
973
|
-
"updated_at": st.get("updated_at"),
|
|
974
|
-
"d_current": ig.get("d_current"), "d2": ig.get("d2"),
|
|
975
|
-
"emotion": st.get("emotion"),
|
|
976
|
-
"p_trust": tt.get("p_trust"), "d2t": tt.get("d2t"),
|
|
977
|
-
"affect": st.get("affect"),
|
|
978
|
-
"trust_band": tt.get("band"),
|
|
979
|
-
"recent_n": (st.get("short_term") or {}).get("n"),
|
|
980
|
-
"relations": st.get("relations"),
|
|
981
|
-
"identity_ref": st.get("identity_ref"),
|
|
982
|
-
"hit_rate": (st.get("prediction") or {}).get("hit_rate"),
|
|
983
|
-
"prediction_reflect": (st.get("prediction") or {}).get("reflect"),
|
|
984
|
-
"text": (f"[{subject}] D={ig.get('d_current')} "
|
|
985
|
-
f"情绪={st.get('emotion')} P_trust={tt.get('p_trust')} "
|
|
986
|
-
f"情感={st.get('affect')} "
|
|
987
|
-
f"短期={((st.get('short_term') or {}).get('n'))}条 "
|
|
988
|
-
f"v{st.get('state_version')}")}
|
|
989
|
-
return _with_session_slice(cg, out, st, session)
|
|
990
|
-
|
|
991
|
-
|
|
992
|
-
# 生效条件:无参调用时返回模块自描述 dict,内含模块常量 SCHEMA_VERSION、DEFAULT_SUBJECT、LOG_FILE、DIMENSIONS、TRUST_BANDS、RELATION_TYPES、DRIFT_TOL、STALE_AFTER 等;
|
|
993
|
-
def catalog():
|
|
994
|
-
"""自描述:九项自我信息 + 五维索引 + 审计规则(供协议对照验证)。"""
|
|
995
|
-
return {
|
|
996
|
-
"module": "self_state",
|
|
997
|
-
"schema": SCHEMA_VERSION,
|
|
998
|
-
"default_subject": DEFAULT_SUBJECT,
|
|
999
|
-
"log": LOG_FILE,
|
|
1000
|
-
"principles": [
|
|
1001
|
-
"self 层是薄的:只放状态卡(单例)与关系节点,不搬运内容",
|
|
1002
|
-
"具体细节留在原层,由认知图的标签/边按五维索引连接过来",
|
|
1003
|
-
"不可遗忘 ≠ 不可覆盖:状态卡可演化,身份锚点两者皆禁",
|
|
1004
|
-
],
|
|
1005
|
-
"self_info": {
|
|
1006
|
-
"information_gap": "D/d1/d2 + emotion ← _reflection.jsonl",
|
|
1007
|
-
"trust": "p_gap/p_trust + d2t/affect ← evidence_log",
|
|
1008
|
-
"emotion": "d^2D/dt^2(信息差二阶)",
|
|
1009
|
-
"affect": "d^2T/dt^2(信任二阶)",
|
|
1010
|
-
"short_term": "窗口摘要 + 指针 ← _recent.jsonl",
|
|
1011
|
-
"importance": "importance_self + important_refs",
|
|
1012
|
-
"identity": "identity_ref → self 层身份锚点",
|
|
1013
|
-
"relations": "self_relation_* 有向关系节点",
|
|
1014
|
-
"prediction": "hit_rate/threshold/reflect + ECE ← _prediction.jsonl",
|
|
1015
|
-
},
|
|
1016
|
-
"dimensions": list(DIMENSIONS),
|
|
1017
|
-
"trust_bands": [b for _lo, b in TRUST_BANDS],
|
|
1018
|
-
"relation_types": list(RELATION_TYPES),
|
|
1019
|
-
"audit_rules": [
|
|
1020
|
-
"duplicate_state", "version_gap", "chain_broken",
|
|
1021
|
-
"time_regression", "log_tail_mismatch", "fabricated_emotion",
|
|
1022
|
-
"fabricated_affect", "drift", "prediction_drift",
|
|
1023
|
-
"identity_missing",
|
|
1024
|
-
"identity_dangling", "relation_self_loop", "duplicate_relation",
|
|
1025
|
-
"protection_locked", "protection_missing", "unknown_dimension",
|
|
1026
|
-
"dimension_orphan", "stale", "missing_state",
|
|
1027
|
-
],
|
|
1028
|
-
"tolerances": {"drift": DRIFT_TOL, "stale_after_sec": STALE_AFTER},
|
|
1029
|
-
}
|
|
1
|
+
# -*- coding: utf-8 -*-
|
|
2
|
+
"""md_cg · 自我状态层(SELF STATE):薄自我 + 富索引
|
|
3
|
+
|
|
4
|
+
理论约束(本仓已有文档,非外部发明):
|
|
5
|
+
· `docs/mdcg/灵枢_自我层定义.md:6`
|
|
6
|
+
SELF 层 = 身份 + 价值观 + 图接口,**不可遗忘**、跨会话自动加载。
|
|
7
|
+
自我层是**薄的**——它不该变成事件仓库。
|
|
8
|
+
· 智能论 §十一:情绪 = 信息差二阶 d^2D/dt^2;§十:P_gap / P_trust;
|
|
9
|
+
情感 = 信任二阶 d^2T/dt^2。
|
|
10
|
+
|
|
11
|
+
由此定下本模块的形态:
|
|
12
|
+
|
|
13
|
+
self 层只放两类一等公民(都很薄):
|
|
14
|
+
1. 自我状态卡(单例)——九项自我信息的**当前值 + 指针**
|
|
15
|
+
2. 关系节点(有向)——自我与其他智能的关系
|
|
16
|
+
具体细节(哪次会话 / 哪个任务 / 哪个人 / 哪个时间窗 / 哪条证据)
|
|
17
|
+
仍然留在原本的层里,由**认知图的标签与边**连接、索引过来。
|
|
18
|
+
|
|
19
|
+
九项自我信息 → 薄卡字段 / 富索引指向:
|
|
20
|
+
|
|
21
|
+
┌ 信息差 D d_current / d1 / d2 / emotion ← _reflection.jsonl(reflect 留痕)
|
|
22
|
+
├ 信任 P p_gap / p_trust / d2t / affect ← 各节点 evidence_log(verify 留痕)
|
|
23
|
+
├ 情绪 emotion(= d^2D/dt^2)
|
|
24
|
+
├ 情感 affect(= d^2T/dt^2)
|
|
25
|
+
├ 短期记忆 recent{n, span, roles, ptr} ← _recent.jsonl(只存摘要 + 指针)
|
|
26
|
+
├ 重要性 importance_self / important_refs ← 各节点 frontmatter.importance
|
|
27
|
+
├ 身份 identity_ref ← self 层 identity 锚点
|
|
28
|
+
├ 关系 relations{out,in} ← self_relation_* 关系节点
|
|
29
|
+
└ 预测校准 hit_rate / threshold / reflect / ece ← _prediction.jsonl + evidence_log
|
|
30
|
+
|
|
31
|
+
第九项(预测校准)是「SELF = 自我描述」走向「SELF = 关于自身的预测模型」的最小一步:
|
|
32
|
+
它回答**我预测得准吗、该不该反思**,数据只来自 predict 的 feedback 留痕与
|
|
33
|
+
metacognition 的 ECE 校准,样本不足一律 unknown——不新增计算,也不编造。
|
|
34
|
+
|
|
35
|
+
五个索引维度(「具体任务 / 人物关系 / 会话 / 时间 / 信任」)用 tag 命名空间落地,
|
|
36
|
+
`index(cg, dim, value)` 从全图反查;状态卡只登记维度标签、不搬运内容:
|
|
37
|
+
|
|
38
|
+
task:<slug> 具体任务
|
|
39
|
+
person:<slug> 具体人物关系
|
|
40
|
+
session:<id> 具体会话
|
|
41
|
+
time:<bucket> 具体时间(YYYY-MM-DD)
|
|
42
|
+
trust:<band> 具体信任档(low | mid | high)
|
|
43
|
+
|
|
44
|
+
一致性(`audit` 的每条判定都可重算,不依赖人的判断):
|
|
45
|
+
|
|
46
|
+
单例 同一 subject 至多一个状态卡
|
|
47
|
+
版本链 state_version 连续 +1;prev_state_hash 必须等于上一条留痕的 hash
|
|
48
|
+
时序单调 留痕时间戳不得回退
|
|
49
|
+
留痕对齐 卡上的 state_hash 必须等于留痕末条
|
|
50
|
+
派生自洽 d2/d2t 缺失时 emotion/affect 必须是 unknown(不许编造)
|
|
51
|
+
跨面一致 卡值与 metacognition 现算值偏差 ≤ DRIFT_TOL
|
|
52
|
+
身份唯一 identity_ref 必须指向存在的身份锚点
|
|
53
|
+
关系完整 关系两端必须存在;禁止自环
|
|
54
|
+
保护一致 状态卡必须「可覆盖」(否则自我认知被永久冻结)且「不可遗忘」
|
|
55
|
+
索引不悬空 维度标签指向的详情节点必须存在
|
|
56
|
+
|
|
57
|
+
「不可遗忘 ≠ 不可覆盖」的延伸:
|
|
58
|
+
· 身份锚点:既不可遗忘,也不可覆盖(protect 层保护);
|
|
59
|
+
· 自我状态卡:**不可遗忘,但必须可覆盖**——自我状态本来就要演化。
|
|
60
|
+
靠 `frontmatter.self_state is True` 在 protect 里获得覆盖豁免,见 protect.py。
|
|
61
|
+
"""
|
|
62
|
+
import hashlib
|
|
63
|
+
import json
|
|
64
|
+
import os
|
|
65
|
+
import re
|
|
66
|
+
import time
|
|
67
|
+
|
|
68
|
+
from . import identity, metacognition
|
|
69
|
+
from .fsutil import append_jsonl, read_jsonl
|
|
70
|
+
|
|
71
|
+
DEFAULT_SUBJECT = "self:lingshu"
|
|
72
|
+
STATE_PREFIX = "self_state_"
|
|
73
|
+
RELATION_PREFIX = "self_relation_"
|
|
74
|
+
LOG_FILE = "_self_state.jsonl"
|
|
75
|
+
TAG_STATE = "self_state"
|
|
76
|
+
TAG_RELATION = "self_relation"
|
|
77
|
+
SCHEMA_VERSION = 1
|
|
78
|
+
|
|
79
|
+
# 跨面一致容差:卡里的 D/T 与 metacognition 现算值允许的最大偏差
|
|
80
|
+
DRIFT_TOL = 0.05
|
|
81
|
+
# 新鲜度:状态卡超过该秒数未刷新即视为 stale
|
|
82
|
+
STALE_AFTER = 24 * 3600.0
|
|
83
|
+
RECENT_WINDOW = 20
|
|
84
|
+
|
|
85
|
+
# 五维索引(具体任务 / 人物关系 / 会话 / 时间 / 信任)
|
|
86
|
+
DIMENSIONS = ("task", "person", "session", "time", "trust")
|
|
87
|
+
TRUST_BANDS = ((0.70, "high"), (0.40, "mid"), (0.00, "low"))
|
|
88
|
+
RELATION_TYPES = ("collaborator", "user", "peer", "mentor", "student",
|
|
89
|
+
"adversary", "tool", "other")
|
|
90
|
+
STATE_LINK_TYPE = "refers_to"
|
|
91
|
+
|
|
92
|
+
|
|
93
|
+
# ---------------------------------------------------------------- 基础工具
|
|
94
|
+
|
|
95
|
+
# 生效条件:对 required 形参 text,返回 str(text or "") 经正则把非中英文数字连续段折叠为下划线、strip('_') 并 lower 后的字符串(text 为假值时返回空串);
|
|
96
|
+
def _slug(text):
|
|
97
|
+
"""归一化 id 片段:保留中英文数字,其余折叠为下划线。"""
|
|
98
|
+
s = re.sub(r"[^0-9A-Za-z\u4e00-\u9fff]+", "_", str(text or ""))
|
|
99
|
+
return s.strip("_").lower()
|
|
100
|
+
|
|
101
|
+
|
|
102
|
+
# 生效条件:对 required 形参 ts,当 float(ts) 可转换且 time.localtime 不抛 OSError 时返回本地时间 "%Y-%m-%d %H:%M:%S" 格式串;当 float(ts) 抛 TypeError/ValueError 或 time.localtime 抛 OSError 时返回空串;
|
|
103
|
+
def _iso(ts):
|
|
104
|
+
try:
|
|
105
|
+
return time.strftime("%Y-%m-%d %H:%M:%S", time.localtime(float(ts)))
|
|
106
|
+
except (TypeError, ValueError, OSError):
|
|
107
|
+
return ""
|
|
108
|
+
|
|
109
|
+
|
|
110
|
+
# 生效条件:subject 未传时取模块常量 DEFAULT_SUBJECT,返回模块常量 STATE_PREFIX 拼接 _slug(subject) 的结果;
|
|
111
|
+
def state_node_id(subject=DEFAULT_SUBJECT):
|
|
112
|
+
"""状态卡节点 id(单例:同一 subject 只有一个)。"""
|
|
113
|
+
return STATE_PREFIX + _slug(subject)
|
|
114
|
+
|
|
115
|
+
|
|
116
|
+
# 生效条件:对 required 形参 frm 与 to,返回模块常量 RELATION_PREFIX 拼接 _slug(frm)、"__"、_slug(to) 的字符串;
|
|
117
|
+
def relation_node_id(frm, to):
|
|
118
|
+
"""关系节点 id(有向)。"""
|
|
119
|
+
return f"{RELATION_PREFIX}{_slug(frm)}__{_slug(to)}"
|
|
120
|
+
|
|
121
|
+
|
|
122
|
+
# 生效条件:对 required 形参 cg,返回 os.path.join(cg.root, LOG_FILE)(LOG_FILE 为模块常量);
|
|
123
|
+
def log_path(cg):
|
|
124
|
+
return os.path.join(cg.root, LOG_FILE)
|
|
125
|
+
|
|
126
|
+
|
|
127
|
+
# 生效条件:对 required 形参 dim 与 value,返回 f"{dim}:{_slug(value)}";
|
|
128
|
+
def dim_tag(dim, value):
|
|
129
|
+
return f"{dim}:{_slug(value)}"
|
|
130
|
+
|
|
131
|
+
|
|
132
|
+
# 生效条件:对 required 形参 p_trust,若 p_trust is None 或 float(p_trust) 抛 TypeError/ValueError 则返回 None;否则按模块常量 TRUST_BANDS 顺序取首个 v >= lo 的档名,均不满足返回 "low";
|
|
133
|
+
def trust_band(p_trust):
|
|
134
|
+
"""把 P_trust 映射到信任档(索引维度之一)。"""
|
|
135
|
+
if p_trust is None:
|
|
136
|
+
return None
|
|
137
|
+
try:
|
|
138
|
+
v = float(p_trust)
|
|
139
|
+
except (TypeError, ValueError):
|
|
140
|
+
return None
|
|
141
|
+
for lo, name in TRUST_BANDS:
|
|
142
|
+
if v >= lo:
|
|
143
|
+
return name
|
|
144
|
+
return "low"
|
|
145
|
+
|
|
146
|
+
|
|
147
|
+
# ---------------------------------------------------------------- 留痕
|
|
148
|
+
|
|
149
|
+
# 生效条件:对 required 形参 cg 与 rec,调用 append_jsonl(log_path(cg), rec);若抛出 OSError 则静默忽略,无返回值;
|
|
150
|
+
def _append_log(cg, rec):
|
|
151
|
+
try:
|
|
152
|
+
append_jsonl(log_path(cg), rec)
|
|
153
|
+
except OSError:
|
|
154
|
+
pass
|
|
155
|
+
|
|
156
|
+
|
|
157
|
+
# 生效条件:对 required 形参 cg 与可选 limit/subject,读取 log_path(cg) 留痕(OSError 时为空列表);subject 为真值时只保留 r.get("subject") == subject 的记录;倒序后,limit 为真值时返回 recs[:int(limit)],limit 为假值(如 0)时返回全部倒序记录;
|
|
158
|
+
def history(cg, limit=100, subject=None):
|
|
159
|
+
"""自我状态留痕(倒序)。"""
|
|
160
|
+
try:
|
|
161
|
+
recs = list(read_jsonl(log_path(cg)))
|
|
162
|
+
except OSError:
|
|
163
|
+
recs = []
|
|
164
|
+
if subject:
|
|
165
|
+
recs = [r for r in recs if r.get("subject") == subject]
|
|
166
|
+
recs.reverse()
|
|
167
|
+
return recs[:int(limit)] if limit else recs
|
|
168
|
+
|
|
169
|
+
|
|
170
|
+
# 生效条件:对 required 形参 cg 与可选 subject,调用 history(cg, limit=0, subject=subject) 取全部倒序留痕;有记录时返回 recs[0](最新一条),否则返回 None;
|
|
171
|
+
def _last_log(cg, subject=None):
|
|
172
|
+
recs = history(cg, limit=0, subject=subject)
|
|
173
|
+
return recs[0] if recs else None
|
|
174
|
+
|
|
175
|
+
|
|
176
|
+
# ---------------------------------------------------------------- 读卡
|
|
177
|
+
|
|
178
|
+
# 生效条件:对 required 形参 cg 与 node_id,尝试返回 cg.get(node_id);若 cg.get 抛任何异常则返回 None;
|
|
179
|
+
def _node(cg, node_id):
|
|
180
|
+
try:
|
|
181
|
+
return cg.get(node_id)
|
|
182
|
+
except Exception:
|
|
183
|
+
return None
|
|
184
|
+
|
|
185
|
+
|
|
186
|
+
# 生效条件:对 required 形参 cg 与 node_id,取 _node(cg, node_id) 结果(假值时用空字典)的 "frontmatter" 值;该值假值时返回空字典,否则返回该 frontmatter;
|
|
187
|
+
def _fm_of(cg, node_id):
|
|
188
|
+
node = _node(cg, node_id)
|
|
189
|
+
return (node or {}).get("frontmatter") or {}
|
|
190
|
+
|
|
191
|
+
|
|
192
|
+
# 生效条件:对 required 形参 cg 与 node_id,从 cg.index(getattr 取不到或假值时用空字典)的 "nodes" 字段(假值时用空字典)中取 node_id 对应条目;缺键返回 None;
|
|
193
|
+
def _entry(cg, node_id):
|
|
194
|
+
return ((getattr(cg, "index", None) or {}).get("nodes") or {}).get(node_id)
|
|
195
|
+
|
|
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 子字典取;
|
|
198
|
+
def snapshot(cg, subject=DEFAULT_SUBJECT):
|
|
199
|
+
"""读当前状态卡 → 扁平 dict(不存在返回 None)。只读,不写盘。"""
|
|
200
|
+
nid = state_node_id(subject)
|
|
201
|
+
if _entry(cg, nid) is None:
|
|
202
|
+
return None
|
|
203
|
+
fm = _fm_of(cg, nid)
|
|
204
|
+
if not fm:
|
|
205
|
+
return None
|
|
206
|
+
out = {"node_id": nid, "subject": fm.get("state_subject") or subject,
|
|
207
|
+
"layer": "self", "state_version": fm.get("state_version"),
|
|
208
|
+
"state_hash": fm.get("state_hash"),
|
|
209
|
+
"prev_state_hash": fm.get("prev_state_hash"),
|
|
210
|
+
"state_ts": fm.get("state_ts"),
|
|
211
|
+
"updated_at": _iso(fm.get("state_ts"))}
|
|
212
|
+
for key in ("information_gap", "trust", "short_term", "relations",
|
|
213
|
+
"prediction", "dimensions", "emotion", "affect",
|
|
214
|
+
"importance_self", "important_refs", "identity_ref",
|
|
215
|
+
"state_links"):
|
|
216
|
+
out[key] = fm.get(key)
|
|
217
|
+
out["d_current"] = (fm.get("information_gap") or {}).get("d_current")
|
|
218
|
+
out["p_trust"] = (fm.get("trust") or {}).get("p_trust")
|
|
219
|
+
return out
|
|
220
|
+
|
|
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);
|
|
223
|
+
def _fingerprint(state):
|
|
224
|
+
"""状态指纹:只对「会漂移的实质字段」取哈希,用于幂等刷新与版本链。
|
|
225
|
+
|
|
226
|
+
注意:**不含 state_version**——版本号是元数据,若纳入指纹则每次刷新
|
|
227
|
+
指纹必变,幂等判断失效(自我状态会在无信息变化时无限刷版本)。
|
|
228
|
+
"""
|
|
229
|
+
core = {
|
|
230
|
+
"subject": state.get("subject"),
|
|
231
|
+
"information_gap": state.get("information_gap"),
|
|
232
|
+
"trust": state.get("trust"),
|
|
233
|
+
"emotion": state.get("emotion"),
|
|
234
|
+
"affect": state.get("affect"),
|
|
235
|
+
"importance_self": state.get("importance_self"),
|
|
236
|
+
"important_refs": state.get("important_refs"),
|
|
237
|
+
"identity_ref": state.get("identity_ref"),
|
|
238
|
+
"short_term": state.get("short_term"),
|
|
239
|
+
"relations": state.get("relations"),
|
|
240
|
+
"prediction": state.get("prediction"),
|
|
241
|
+
"dimensions": state.get("dimensions"),
|
|
242
|
+
"state_links": state.get("state_links"),
|
|
243
|
+
}
|
|
244
|
+
blob = json.dumps(core, sort_keys=True, ensure_ascii=False, default=str)
|
|
245
|
+
return hashlib.sha256(blob.encode("utf-8")).hexdigest()[:16]
|
|
246
|
+
|
|
247
|
+
|
|
248
|
+
# ---------------------------------------------------------------- 聚合(九项)
|
|
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);
|
|
274
|
+
def _prediction_face(cg):
|
|
275
|
+
"""预测校准(第九项):我预测得准吗、该不该反思?
|
|
276
|
+
|
|
277
|
+
数据源(只读留痕,不新增计算负担):
|
|
278
|
+
· `predict._hit_history` / `predict.dynamic_hit_threshold`
|
|
279
|
+
← `_prediction.jsonl`(predict_feedback 留痕)
|
|
280
|
+
· `metacognition.calibration` ← evidence_log(verify 留痕)的 ECE
|
|
281
|
+
· `d_meta.compute` / 反思留痕的 `d_meta` ← 边界压力趋势(d_meta_trend,
|
|
282
|
+
**不合成三代理**、不参与 D_task:D_task 与 D_meta 对象不同)
|
|
283
|
+
|
|
284
|
+
诚实边界:无预测留痕 → ok=False、hit_rate=None(未知即未知);
|
|
285
|
+
ECE 仅在 metacognition 有足够证据时给出,否则 None。绝不编造。
|
|
286
|
+
"""
|
|
287
|
+
try:
|
|
288
|
+
from . import predict as _predict
|
|
289
|
+
hist = list(_predict._hit_history(cg))
|
|
290
|
+
th = _predict.dynamic_hit_threshold(cg)
|
|
291
|
+
# P-T-73/74 通道贝叶斯:可信度后验(随样本量向基线收缩,诚实小样本)
|
|
292
|
+
beta = (_predict.channel_posterior(cg) or {}).get("all") or {}
|
|
293
|
+
except Exception: # noqa: BLE001
|
|
294
|
+
hist, th, beta = [], {}, {}
|
|
295
|
+
samples = len(hist)
|
|
296
|
+
hit_rate = (sum(1 for x in hist if x) / samples) if samples else None
|
|
297
|
+
try:
|
|
298
|
+
cal = metacognition.calibration(cg)
|
|
299
|
+
except Exception: # noqa: BLE001
|
|
300
|
+
cal = {}
|
|
301
|
+
meta_trend = _d_meta_trend(cg)
|
|
302
|
+
if samples == 0:
|
|
303
|
+
note = "无预测留痕:先 predict_feedback() 积累命中记录"
|
|
304
|
+
elif th.get("reflect"):
|
|
305
|
+
note = "命中率低于动态阈值:建议反思(D-006)"
|
|
306
|
+
else:
|
|
307
|
+
note = "命中率正常"
|
|
308
|
+
return {
|
|
309
|
+
"ok": samples > 0,
|
|
310
|
+
"samples": samples,
|
|
311
|
+
"hit_rate": None if hit_rate is None else round(hit_rate, 4),
|
|
312
|
+
"beta_mean": beta.get("mean"),
|
|
313
|
+
"beta_ci95": beta.get("ci95"),
|
|
314
|
+
"threshold": th.get("threshold"),
|
|
315
|
+
"reflect": bool(th.get("reflect")),
|
|
316
|
+
"ece": cal.get("ece") if cal.get("ok") else None,
|
|
317
|
+
"calibration": cal.get("verdict") if cal.get("ok") else None,
|
|
318
|
+
"d_meta_trend": meta_trend,
|
|
319
|
+
"note": note,
|
|
320
|
+
}
|
|
321
|
+
|
|
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");
|
|
324
|
+
def _recent_summary(cg, window):
|
|
325
|
+
"""短期记忆:只做窗口摘要,原文仍留在 _recent.jsonl(薄自我的关键取舍)。"""
|
|
326
|
+
try:
|
|
327
|
+
recs = cg.recent_events(limit=int(window), newest_first=False)
|
|
328
|
+
except Exception:
|
|
329
|
+
recs = []
|
|
330
|
+
ts = [float(r.get("t") or 0.0) for r in recs]
|
|
331
|
+
roles = {}
|
|
332
|
+
for r in recs:
|
|
333
|
+
k = str(r.get("role") or "unknown")
|
|
334
|
+
roles[k] = roles.get(k, 0) + 1
|
|
335
|
+
return {"n": len(recs), "span": [min(ts), max(ts)] if ts else None,
|
|
336
|
+
"roles": roles,
|
|
337
|
+
"ptr": os.path.basename(getattr(cg, "recent_log", "_recent.jsonl"))}
|
|
338
|
+
|
|
339
|
+
|
|
340
|
+
# 生效条件:对 required 形参 cg 与 subject,若 identity.profile(cg, subject) 抛异常则返回 (None, 0);否则返回 (prof.anchor.node_id(缺省 None), len(prof.get("anchors") or []));
|
|
341
|
+
def _identity_ref(cg, subject):
|
|
342
|
+
"""身份:只存指针(锚点节点 id),锚点内容仍在 self 层原节点里。"""
|
|
343
|
+
try:
|
|
344
|
+
prof = identity.profile(cg, subject)
|
|
345
|
+
except Exception:
|
|
346
|
+
return None, 0
|
|
347
|
+
anchors = prof.get("anchors") or []
|
|
348
|
+
ref = (prof.get("anchor") or {}).get("node_id")
|
|
349
|
+
return ref, len(anchors)
|
|
350
|
+
|
|
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};
|
|
353
|
+
def _relation_counts(cg, subject):
|
|
354
|
+
"""关系:从索引里的关系节点统计出/入度(不读文件正文)。"""
|
|
355
|
+
slug = _slug(subject)
|
|
356
|
+
nodes = ((getattr(cg, "index", None) or {}).get("nodes") or {})
|
|
357
|
+
out = inn = 0
|
|
358
|
+
for nid, e in nodes.items():
|
|
359
|
+
tags = set(e.get("tags") or [])
|
|
360
|
+
if TAG_RELATION not in tags and not nid.startswith(RELATION_PREFIX):
|
|
361
|
+
continue
|
|
362
|
+
if f"rel_from:{slug}" in tags:
|
|
363
|
+
out += 1
|
|
364
|
+
if f"rel_to:{slug}" in tags:
|
|
365
|
+
inn += 1
|
|
366
|
+
return {"out": out, "in": inn}
|
|
367
|
+
|
|
368
|
+
|
|
369
|
+
# 生效条件:对 required 形参 dimensions(假值时按空字典),只保留键 str(dim).strip().lower() 属于模块常量 DIMENSIONS 且 val 非 None 的维度;val 为 list/tuple/set 时展开,否则包装为单元素;过滤空字符串并去重后放入 out,返回 out;
|
|
370
|
+
def _normalize_dimensions(dimensions):
|
|
371
|
+
"""维度入参 → {dim: [value...]}(只保留已知维度)。"""
|
|
372
|
+
out = {}
|
|
373
|
+
for dim, val in (dimensions or {}).items():
|
|
374
|
+
d = str(dim).strip().lower()
|
|
375
|
+
if d not in DIMENSIONS or val is None:
|
|
376
|
+
continue
|
|
377
|
+
vals = val if isinstance(val, (list, tuple, set)) else [val]
|
|
378
|
+
clean = [str(v).strip() for v in vals if str(v).strip()]
|
|
379
|
+
if clean:
|
|
380
|
+
out.setdefault(d, [])
|
|
381
|
+
for v in clean:
|
|
382
|
+
if v not in out[d]:
|
|
383
|
+
out[d].append(v)
|
|
384
|
+
return out
|
|
385
|
+
|
|
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;
|
|
388
|
+
def _derive(cg, subject, window, importance, important_refs, dimensions,
|
|
389
|
+
links, old):
|
|
390
|
+
"""聚合九项自我信息 → 薄状态(纯读,不写盘)。"""
|
|
391
|
+
tr = metacognition.trace(cg, window=max(2, int(window)))
|
|
392
|
+
tt = metacognition.trust(cg, window=max(3, int(window)))
|
|
393
|
+
d2 = tr.get("d2") if tr.get("ok") else None
|
|
394
|
+
d2t = tt.get("d2") if tt.get("ok") else None
|
|
395
|
+
# 派生自洽:无数据即 unknown,绝不编造
|
|
396
|
+
emotion = tr.get("emotion") if (tr.get("ok") and d2 is not None) else "unknown"
|
|
397
|
+
affect = tt.get("emotion") if (tt.get("ok") and d2t is not None) else "unknown"
|
|
398
|
+
|
|
399
|
+
information_gap = {"ok": bool(tr.get("ok")), "n": tr.get("n", 0),
|
|
400
|
+
"d_current": tr.get("d_current"), "d1": tr.get("d1"),
|
|
401
|
+
"d2": d2, "trend": tr.get("trend")}
|
|
402
|
+
trust = {"ok": bool(tt.get("ok")), "n_events": tt.get("n_events", 0),
|
|
403
|
+
"p_gap": tt.get("p_gap"), "p_trust": tt.get("p_trust"),
|
|
404
|
+
"d1": tt.get("d1"), "d2t": d2t,
|
|
405
|
+
"band": trust_band(tt.get("p_trust"))}
|
|
406
|
+
short_term = _recent_summary(cg, window)
|
|
407
|
+
identity_ref, n_anchors = _identity_ref(cg, subject)
|
|
408
|
+
relations = _relation_counts(cg, subject)
|
|
409
|
+
|
|
410
|
+
if importance is None:
|
|
411
|
+
importance = (old or {}).get("importance_self")
|
|
412
|
+
importance = 1.0 if importance is None else float(importance)
|
|
413
|
+
if important_refs is None:
|
|
414
|
+
important_refs = list((old or {}).get("important_refs") or [])
|
|
415
|
+
important_refs = [str(x) for x in important_refs if str(x).strip()]
|
|
416
|
+
|
|
417
|
+
dims = _normalize_dimensions(dimensions)
|
|
418
|
+
if not dims:
|
|
419
|
+
dims = dict((old or {}).get("dimensions") or {})
|
|
420
|
+
# 信任档与时间桶自动入索引(用户点名的「具体信任」「具体时间」)
|
|
421
|
+
if trust["band"]:
|
|
422
|
+
dims.setdefault("trust", [])
|
|
423
|
+
if trust["band"] not in dims["trust"]:
|
|
424
|
+
dims["trust"].append(trust["band"])
|
|
425
|
+
today = time.strftime("%Y-%m-%d", time.localtime())
|
|
426
|
+
dims.setdefault("time", [])
|
|
427
|
+
if today not in dims["time"]:
|
|
428
|
+
dims["time"].append(today)
|
|
429
|
+
|
|
430
|
+
if links is None:
|
|
431
|
+
links = list((old or {}).get("state_links") or [])
|
|
432
|
+
|
|
433
|
+
return {"subject": subject, "information_gap": information_gap,
|
|
434
|
+
"trust": trust, "emotion": emotion, "affect": affect,
|
|
435
|
+
"short_term": short_term, "importance_self": importance,
|
|
436
|
+
"important_refs": important_refs, "identity_ref": identity_ref,
|
|
437
|
+
"identity_anchors": n_anchors, "relations": relations,
|
|
438
|
+
"prediction": _prediction_face(cg),
|
|
439
|
+
"dimensions": dims,
|
|
440
|
+
"state_links": [str(x) for x in links if str(x).strip()]}
|
|
441
|
+
|
|
442
|
+
|
|
443
|
+
# ---------------------------------------------------------------- 写卡
|
|
444
|
+
|
|
445
|
+
# 生效条件:对 required 形参 state,读取其 information_gap、trust、short_term、prediction、dimensions 等字段,渲染并返回末尾带换行的状态卡正文(含 D_meta 边界压力趋势一行:prediction.d_meta_trend 缺失时显示 None);span 为空时显示(无),角色为空时显示(无);
|
|
446
|
+
def _render(state):
|
|
447
|
+
"""状态卡正文:人类可读,且与 frontmatter 字段一一对应。"""
|
|
448
|
+
ig, tt = state["information_gap"], state["trust"]
|
|
449
|
+
st = state["short_term"]
|
|
450
|
+
pd = state.get("prediction") or {}
|
|
451
|
+
dims = state["dimensions"]
|
|
452
|
+
span = st.get("span")
|
|
453
|
+
span_txt = f"{_iso(span[0])} ~ {_iso(span[1])}" if span else "(无)"
|
|
454
|
+
roles = "、".join(f"{k}:{v}" for k, v in
|
|
455
|
+
sorted((st.get("roles") or {}).items())) or "(无)"
|
|
456
|
+
lines = [
|
|
457
|
+
f"# 自我状态卡 · {state['subject']}",
|
|
458
|
+
f"# 更新时间:{_iso(state.get('state_ts'))}"
|
|
459
|
+
f"(version={state.get('state_version')})",
|
|
460
|
+
f"# 信息差:D={ig.get('d_current')} dD/dt={ig.get('d1')} "
|
|
461
|
+
f"d^2D/dt^2={ig.get('d2')} → 情绪={state['emotion']}"
|
|
462
|
+
f"(趋势={ig.get('trend')})",
|
|
463
|
+
f"# 信任:P_gap={tt.get('p_gap')} P_trust={tt.get('p_trust')} "
|
|
464
|
+
f"d^2T/dt^2={tt.get('d2t')} → 情感={state['affect']}"
|
|
465
|
+
f"(档={tt.get('band')})",
|
|
466
|
+
f"# 短期记忆:{st.get('n')} 条({span_txt})角色 {roles};"
|
|
467
|
+
f"原文见 {st.get('ptr')}",
|
|
468
|
+
f"# 重要性:self={state['importance_self']},"
|
|
469
|
+
f"显式重要 {len(state.get('important_refs') or [])} 条",
|
|
470
|
+
f"# 身份:锚点 {state.get('identity_ref') or '(未设)'}"
|
|
471
|
+
f"(共 {state.get('identity_anchors')} 条)",
|
|
472
|
+
f"# 关系:出 {state['relations'].get('out')} / "
|
|
473
|
+
f"入 {state['relations'].get('in')}",
|
|
474
|
+
f"# 预测:命中率={pd.get('hit_rate')}(样本 {pd.get('samples')})"
|
|
475
|
+
f"阈值={pd.get('threshold')} 反思={'是' if pd.get('reflect') else '否'}"
|
|
476
|
+
f";后验可信度={pd.get('beta_mean')}"
|
|
477
|
+
f"(95%CI {pd.get('beta_ci95')})"
|
|
478
|
+
f";ECE={pd.get('ece')}({pd.get('calibration')})",
|
|
479
|
+
f"# D_meta:边界压力趋势(unmodeled_growth 均值)={pd.get('d_meta_trend')}"
|
|
480
|
+
f"(三代理分别观测、不合成单值,不参与 D_task)",
|
|
481
|
+
"# 索引:" + ";".join(
|
|
482
|
+
f"{d}={','.join(dims.get(d) or []) or '-'}" for d in DIMENSIONS),
|
|
483
|
+
"# 说明:本卡是薄自我——只登记当前值与指针;具体任务/人物/会话/"
|
|
484
|
+
"时间/信任的细节由认知图按上述索引连接,不在此搬运。",
|
|
485
|
+
]
|
|
486
|
+
return "\n".join(lines) + "\n"
|
|
487
|
+
|
|
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;
|
|
490
|
+
def refresh(cg, subject=DEFAULT_SUBJECT, window=RECENT_WINDOW, importance=None,
|
|
491
|
+
important_refs=None, dimensions=None, links=None,
|
|
492
|
+
actor="self_state", force=False, strict=False, session=None):
|
|
493
|
+
"""刷新自我状态卡(幂等):聚合九项 → 写卡 + 版本链留痕。
|
|
494
|
+
|
|
495
|
+
dimensions: {"task": "...", "person": "...", "session": "...",
|
|
496
|
+
"time": "...", "trust": "..."}(值可为列表)
|
|
497
|
+
links: 指向具体详情节点的边(认知图连接,而非内容复制)
|
|
498
|
+
strict: 版本链断裂时是否拒绝写入(默认修复并记录 chain_repaired)
|
|
499
|
+
session: 会话归因(嵌套身份):并入 session 维度,不覆盖显式声明值
|
|
500
|
+
"""
|
|
501
|
+
raw = snapshot(cg, subject)
|
|
502
|
+
tail = _last_log(cg, subject=subject)
|
|
503
|
+
# 卡缺失 / 被绕过 refresh 破坏(版本或 hash 对不上留痕)→ 以 append-only
|
|
504
|
+
# 留痕为准恢复元数据(维度索引、重要性、边)。留痕是权威记录。
|
|
505
|
+
old, rebuild = raw, False
|
|
506
|
+
if tail and (raw is None or raw.get("state_version") is None
|
|
507
|
+
or raw.get("state_hash") != tail.get("state_hash")):
|
|
508
|
+
rebuild = True
|
|
509
|
+
old = {"state_version": tail.get("version"),
|
|
510
|
+
"state_hash": tail.get("state_hash"),
|
|
511
|
+
"dimensions": tail.get("dimensions"),
|
|
512
|
+
"importance_self": tail.get("importance_self"),
|
|
513
|
+
"important_refs": tail.get("important_refs"),
|
|
514
|
+
"state_links": tail.get("state_links"),
|
|
515
|
+
"identity_ref": tail.get("identity_ref")}
|
|
516
|
+
# 会话归因:把本会话并入 session 维度(不覆盖调用方显式声明的维度值)。
|
|
517
|
+
dims_in = dict(dimensions or {})
|
|
518
|
+
if session:
|
|
519
|
+
vals = dims_in.get("session") or []
|
|
520
|
+
vals = list(vals) if isinstance(vals, (list, tuple, set)) else [vals]
|
|
521
|
+
vals = [str(v) for v in vals if str(v).strip()]
|
|
522
|
+
if str(session) not in vals:
|
|
523
|
+
vals.append(str(session))
|
|
524
|
+
dims_in["session"] = vals
|
|
525
|
+
state = _derive(cg, subject, window=window, importance=importance,
|
|
526
|
+
important_refs=important_refs, dimensions=dims_in,
|
|
527
|
+
links=links, old=old)
|
|
528
|
+
card_v = int((old or {}).get("state_version") or 0)
|
|
529
|
+
log_v = int(tail.get("version") or 0) if tail else 0
|
|
530
|
+
version = max(card_v, log_v) + 1
|
|
531
|
+
state["state_version"] = version
|
|
532
|
+
fp = _fingerprint(state)
|
|
533
|
+
if not rebuild and old and old.get("state_hash") == fp and not force:
|
|
534
|
+
return {"ok": True, "changed": False, "subject": subject,
|
|
535
|
+
"node_id": state_node_id(subject), "state": state,
|
|
536
|
+
"note": "状态未变化,跳过写入(幂等)"}
|
|
537
|
+
|
|
538
|
+
# 版本链:prev 必须等于上一条留痕的 hash
|
|
539
|
+
prev_hash = (raw or {}).get("state_hash")
|
|
540
|
+
chain_issue = None
|
|
541
|
+
if tail is not None and prev_hash != tail.get("state_hash"):
|
|
542
|
+
chain_issue = {
|
|
543
|
+
"code": "chain_repaired",
|
|
544
|
+
"why": f"prev_state_hash={prev_hash} 与留痕末条 "
|
|
545
|
+
f"{tail.get('state_hash')} 不一致"}
|
|
546
|
+
if strict:
|
|
547
|
+
return {"ok": False, "changed": False, "subject": subject,
|
|
548
|
+
"error": "chain_broken", "detail": chain_issue}
|
|
549
|
+
prev_hash = tail.get("state_hash") # 以留痕为准修复
|
|
550
|
+
if raw is None and tail is not None:
|
|
551
|
+
chain_issue = {"code": "state_rebuilt",
|
|
552
|
+
"why": "状态卡缺失但留痕存在,按留痕续链重建"}
|
|
553
|
+
prev_hash = tail.get("state_hash")
|
|
554
|
+
|
|
555
|
+
state["prev_state_hash"] = prev_hash
|
|
556
|
+
state["state_ts"] = time.time()
|
|
557
|
+
state["state_hash"] = _fingerprint(state)
|
|
558
|
+
|
|
559
|
+
tags = [TAG_STATE, f"state_subject:{_slug(subject)}"]
|
|
560
|
+
for d in DIMENSIONS:
|
|
561
|
+
for v in (state["dimensions"].get(d) or []):
|
|
562
|
+
t = dim_tag(d, v)
|
|
563
|
+
if t not in tags:
|
|
564
|
+
tags.append(t)
|
|
565
|
+
edges = [{"target": t, "relation_type": STATE_LINK_TYPE,
|
|
566
|
+
"weight": 0.6} for t in state["state_links"]]
|
|
567
|
+
|
|
568
|
+
nid = state_node_id(subject)
|
|
569
|
+
cg.add(nid, _render(state), layer="self", tags=tags, edges=edges,
|
|
570
|
+
importance=float(state["importance_self"]), confidence=0.9,
|
|
571
|
+
verification_basis="data", override=True, actor=actor,
|
|
572
|
+
self_state=True, state_subject=subject, state_version=version,
|
|
573
|
+
state_hash=state["state_hash"], prev_state_hash=prev_hash,
|
|
574
|
+
state_ts=state["state_ts"],
|
|
575
|
+
information_gap=state["information_gap"], trust=state["trust"],
|
|
576
|
+
emotion=state["emotion"], affect=state["affect"],
|
|
577
|
+
short_term=state["short_term"],
|
|
578
|
+
importance_self=state["importance_self"],
|
|
579
|
+
important_refs=state["important_refs"],
|
|
580
|
+
identity_ref=state["identity_ref"], relations=state["relations"],
|
|
581
|
+
prediction=state["prediction"],
|
|
582
|
+
dimensions=state["dimensions"], state_links=state["state_links"])
|
|
583
|
+
|
|
584
|
+
_append_log(cg, {
|
|
585
|
+
"t": state["state_ts"], "subject": subject, "node_id": nid,
|
|
586
|
+
"version": version, "state_hash": state["state_hash"],
|
|
587
|
+
"prev_state_hash": prev_hash, "actor": actor,
|
|
588
|
+
"d_current": state["information_gap"].get("d_current"),
|
|
589
|
+
"d1": state["information_gap"].get("d1"),
|
|
590
|
+
"d2": state["information_gap"].get("d2"),
|
|
591
|
+
"emotion": state["emotion"],
|
|
592
|
+
"p_gap": state["trust"].get("p_gap"),
|
|
593
|
+
"p_trust": state["trust"].get("p_trust"),
|
|
594
|
+
"d2t": state["trust"].get("d2t"), "affect": state["affect"],
|
|
595
|
+
"importance_self": state["importance_self"],
|
|
596
|
+
"important_refs": state["important_refs"],
|
|
597
|
+
"state_links": state["state_links"],
|
|
598
|
+
"recent_n": state["short_term"].get("n"),
|
|
599
|
+
"identity_ref": state["identity_ref"],
|
|
600
|
+
"relations": state["relations"],
|
|
601
|
+
"prediction_hit_rate": (state.get("prediction") or {}).get("hit_rate"),
|
|
602
|
+
"prediction_samples": (state.get("prediction") or {}).get("samples"),
|
|
603
|
+
"prediction_reflect": (state.get("prediction") or {}).get("reflect"),
|
|
604
|
+
"prediction_beta_mean": (state.get("prediction") or {}).get("beta_mean"),
|
|
605
|
+
"dimensions": state["dimensions"], "issue": chain_issue})
|
|
606
|
+
return {"ok": True, "changed": True, "subject": subject, "node_id": nid,
|
|
607
|
+
"state_version": version, "state_hash": state["state_hash"],
|
|
608
|
+
"prev_state_hash": prev_hash, "state": state,
|
|
609
|
+
"issue": chain_issue}
|
|
610
|
+
|
|
611
|
+
|
|
612
|
+
# ---------------------------------------------------------------- 关系
|
|
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 列表;
|
|
615
|
+
def relate(cg, frm, to, relation_type="collaborator", strength=0.5,
|
|
616
|
+
condition="", note="", reciprocal=False, actor="self_state"):
|
|
617
|
+
"""写一条有向关系(自我 ↔ 其他智能)。reciprocal=True 时同时写反向。"""
|
|
618
|
+
rt = str(relation_type or "other").strip().lower()
|
|
619
|
+
if rt not in RELATION_TYPES:
|
|
620
|
+
rt = "other"
|
|
621
|
+
if _slug(frm) == _slug(to):
|
|
622
|
+
return {"ok": False, "error": "self_loop",
|
|
623
|
+
"detail": "关系两端不能是同一主体(自环)"}
|
|
624
|
+
try:
|
|
625
|
+
w = max(0.0, min(1.0, float(strength)))
|
|
626
|
+
except (TypeError, ValueError):
|
|
627
|
+
w = 0.5
|
|
628
|
+
written = []
|
|
629
|
+
for a, b in ((frm, to), (to, frm)) if reciprocal else ((frm, to),):
|
|
630
|
+
nid = relation_node_id(a, b)
|
|
631
|
+
tags = [TAG_RELATION, f"rel_from:{_slug(a)}", f"rel_to:{_slug(b)}",
|
|
632
|
+
f"relation:{rt}", f"person:{_slug(b)}",
|
|
633
|
+
f"state_subject:{_slug(a)}"]
|
|
634
|
+
body = (f"# 关系:{a} → {b}\n"
|
|
635
|
+
f"# 类型:{rt}\n"
|
|
636
|
+
f"# 强度:{w}\n"
|
|
637
|
+
f"# 条件:{condition or '(未声明)'}\n"
|
|
638
|
+
f"# 备注:{note or '(无)'}\n")
|
|
639
|
+
cg.add(nid, body, layer="self", tags=tags,
|
|
640
|
+
edges=[{"target": b, "relation_type": rt, "weight": w,
|
|
641
|
+
"condition": condition}],
|
|
642
|
+
importance=w, confidence=0.8, verification_basis="other",
|
|
643
|
+
override=True, actor=actor,
|
|
644
|
+
self_relation=True, rel_from=a, rel_to=b,
|
|
645
|
+
relation_type=rt, strength=w, condition=condition,
|
|
646
|
+
reciprocal=bool(reciprocal))
|
|
647
|
+
written.append(nid)
|
|
648
|
+
return {"ok": True, "written": written, "from": frm, "to": to,
|
|
649
|
+
"relation_type": rt, "strength": w,
|
|
650
|
+
"reciprocal": bool(reciprocal)}
|
|
651
|
+
|
|
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;
|
|
654
|
+
def relations(cg, subject=None, direction="both"):
|
|
655
|
+
"""列出关系节点(按 subject 过滤出/入)。"""
|
|
656
|
+
nodes = ((getattr(cg, "index", None) or {}).get("nodes") or {})
|
|
657
|
+
slug = _slug(subject) if subject else None
|
|
658
|
+
out = []
|
|
659
|
+
for nid, e in nodes.items():
|
|
660
|
+
tags = set(e.get("tags") or [])
|
|
661
|
+
if TAG_RELATION not in tags and not nid.startswith(RELATION_PREFIX):
|
|
662
|
+
continue
|
|
663
|
+
if slug:
|
|
664
|
+
is_out = f"rel_from:{slug}" in tags
|
|
665
|
+
is_in = f"rel_to:{slug}" in tags
|
|
666
|
+
if direction == "out" and not is_out:
|
|
667
|
+
continue
|
|
668
|
+
if direction == "in" and not is_in:
|
|
669
|
+
continue
|
|
670
|
+
if direction == "both" and not (is_out or is_in):
|
|
671
|
+
continue
|
|
672
|
+
rels = [t[len("relation:"):] for t in tags if t.startswith("relation:")]
|
|
673
|
+
frm = [t[len("rel_from:"):] for t in tags if t.startswith("rel_from:")]
|
|
674
|
+
to = [t[len("rel_to:"):] for t in tags if t.startswith("rel_to:")]
|
|
675
|
+
out.append({"node_id": nid, "from": frm[0] if frm else None,
|
|
676
|
+
"to": to[0] if to else None,
|
|
677
|
+
"relation_type": rels[0] if rels else None,
|
|
678
|
+
"strength": e.get("importance"),
|
|
679
|
+
"layer": e.get("layer")})
|
|
680
|
+
out.sort(key=lambda x: (-float(x.get("strength") or 0), x["node_id"]))
|
|
681
|
+
return out
|
|
682
|
+
|
|
683
|
+
|
|
684
|
+
# ---------------------------------------------------------------- 索引
|
|
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;
|
|
687
|
+
def index(cg, dim, value, limit=50, with_content=False):
|
|
688
|
+
"""按五维索引反查具体详情节点(认知图连接,不是内容搬运)。"""
|
|
689
|
+
d = str(dim or "").strip().lower()
|
|
690
|
+
if d not in DIMENSIONS:
|
|
691
|
+
return {"ok": False, "error": "unknown_dimension",
|
|
692
|
+
"allowed": list(DIMENSIONS)}
|
|
693
|
+
tag = dim_tag(d, value)
|
|
694
|
+
nodes = ((getattr(cg, "index", None) or {}).get("nodes") or {})
|
|
695
|
+
hits = [(nid, e) for nid, e in nodes.items()
|
|
696
|
+
if tag in (e.get("tags") or [])]
|
|
697
|
+
hits.sort(key=lambda kv: (-float(kv[1].get("importance") or 0),
|
|
698
|
+
-float(kv[1].get("created_at") or 0),
|
|
699
|
+
str(kv[0] or "")))
|
|
700
|
+
items = []
|
|
701
|
+
for nid, e in hits[:int(limit)]:
|
|
702
|
+
item = {"node_id": nid, "layer": e.get("layer"),
|
|
703
|
+
"importance": e.get("importance"), "tags": e.get("tags"),
|
|
704
|
+
"path": e.get("path")}
|
|
705
|
+
if with_content:
|
|
706
|
+
node = _node(cg, nid) or {}
|
|
707
|
+
item["content"] = (node.get("content") or "")[:400]
|
|
708
|
+
items.append(item)
|
|
709
|
+
return {"ok": True, "dimension": d, "value": str(value), "tag": tag,
|
|
710
|
+
"count": len(hits), "items": items}
|
|
711
|
+
|
|
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 {}};
|
|
714
|
+
def dimensions(cg, subject=DEFAULT_SUBJECT):
|
|
715
|
+
"""状态卡登记的五维索引标签。"""
|
|
716
|
+
st = snapshot(cg, subject)
|
|
717
|
+
if not st:
|
|
718
|
+
return {"ok": False, "error": "no_state", "subject": subject}
|
|
719
|
+
return {"ok": True, "subject": subject,
|
|
720
|
+
"dimensions": st.get("dimensions") or {}}
|
|
721
|
+
|
|
722
|
+
|
|
723
|
+
# ---------------------------------------------------------------- 审计
|
|
724
|
+
|
|
725
|
+
# 生效条件:对 required 形参 code、severity、why,返回含这三个键并合并 **extra 的 dict;
|
|
726
|
+
def _issue(code, severity, why, **extra):
|
|
727
|
+
d = {"code": code, "severity": severity, "why": why}
|
|
728
|
+
d.update(extra)
|
|
729
|
+
return d
|
|
730
|
+
|
|
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;
|
|
733
|
+
def audit(cg, subject=DEFAULT_SUBJECT, window=RECENT_WINDOW):
|
|
734
|
+
"""自我信息一致性审计:全部判定可重算(不依赖人的判断)。"""
|
|
735
|
+
from . import protect as _protect
|
|
736
|
+
issues = []
|
|
737
|
+
nid = state_node_id(subject)
|
|
738
|
+
nodes = ((getattr(cg, "index", None) or {}).get("nodes") or {})
|
|
739
|
+
st = snapshot(cg, subject)
|
|
740
|
+
|
|
741
|
+
# 1 单例:同 subject 的状态卡只能有一张
|
|
742
|
+
slug = _slug(subject)
|
|
743
|
+
cards = [n for n, e in nodes.items()
|
|
744
|
+
if TAG_STATE in (e.get("tags") or [])
|
|
745
|
+
and f"state_subject:{slug}" in (e.get("tags") or [])]
|
|
746
|
+
if len(cards) > 1:
|
|
747
|
+
issues.append(_issue("duplicate_state", "error",
|
|
748
|
+
f"同一 subject 有 {len(cards)} 张状态卡",
|
|
749
|
+
nodes=cards))
|
|
750
|
+
if st is None:
|
|
751
|
+
issues.append(_issue("missing_state", "warn",
|
|
752
|
+
"状态卡不存在:先 refresh() 建立", node_id=nid))
|
|
753
|
+
return {"ok": False, "verdict": "absent", "subject": subject,
|
|
754
|
+
"n_issues": len(issues), "issues": issues,
|
|
755
|
+
"checked_at": time.time()}
|
|
756
|
+
|
|
757
|
+
ordered = list(reversed(history(cg, limit=0, subject=subject)))
|
|
758
|
+
|
|
759
|
+
# 2 版本链:version 连续 +1,prev 必须等于上一条 hash
|
|
760
|
+
for i in range(1, len(ordered)):
|
|
761
|
+
p, c = ordered[i - 1], ordered[i]
|
|
762
|
+
if c.get("version") != (p.get("version") or 0) + 1:
|
|
763
|
+
issues.append(_issue(
|
|
764
|
+
"version_gap", "error",
|
|
765
|
+
f"版本不连续:{p.get('version')} → {c.get('version')}",
|
|
766
|
+
at=c.get("t")))
|
|
767
|
+
if c.get("prev_state_hash") != p.get("state_hash"):
|
|
768
|
+
issues.append(_issue(
|
|
769
|
+
"chain_broken", "error",
|
|
770
|
+
f"留痕断链:prev={c.get('prev_state_hash')} ≠ 上一条 "
|
|
771
|
+
f"{p.get('state_hash')}", at=c.get("t")))
|
|
772
|
+
if float(c.get("t") or 0) < float(p.get("t") or 0):
|
|
773
|
+
issues.append(_issue("time_regression", "error",
|
|
774
|
+
"留痕时间戳回退", at=c.get("t")))
|
|
775
|
+
|
|
776
|
+
# 3 留痕对齐:卡上的 hash 必须等于留痕末条
|
|
777
|
+
if ordered:
|
|
778
|
+
tail = ordered[-1]
|
|
779
|
+
if st.get("state_hash") != tail.get("state_hash"):
|
|
780
|
+
issues.append(_issue(
|
|
781
|
+
"log_tail_mismatch", "error",
|
|
782
|
+
f"卡 hash={st.get('state_hash')} ≠ 留痕末条 "
|
|
783
|
+
f"{tail.get('state_hash')}(卡被绕过 refresh 改过?)",
|
|
784
|
+
node_id=nid))
|
|
785
|
+
|
|
786
|
+
# 4 派生自洽:无数据时必须 unknown(不许编造)
|
|
787
|
+
ig = st.get("information_gap") or {}
|
|
788
|
+
tt = st.get("trust") or {}
|
|
789
|
+
if ig.get("d2") is None and st.get("emotion") not in (None, "unknown"):
|
|
790
|
+
issues.append(_issue("fabricated_emotion", "error",
|
|
791
|
+
f"d^2D/dt^2 缺失但 emotion={st.get('emotion')}"))
|
|
792
|
+
if tt.get("d2t") is None and st.get("affect") not in (None, "unknown"):
|
|
793
|
+
issues.append(_issue("fabricated_affect", "error",
|
|
794
|
+
f"d^2T/dt^2 缺失但 affect={st.get('affect')}"))
|
|
795
|
+
|
|
796
|
+
# 5 跨面一致:与 metacognition 现算值比对(drift)
|
|
797
|
+
now = _derive(cg, subject, window=window, importance=None,
|
|
798
|
+
important_refs=None, dimensions=None, links=None, old=None)
|
|
799
|
+
for name, old_v, new_v in (
|
|
800
|
+
("d_current", ig.get("d_current"),
|
|
801
|
+
now["information_gap"].get("d_current")),
|
|
802
|
+
("d2", ig.get("d2"), now["information_gap"].get("d2")),
|
|
803
|
+
("p_trust", tt.get("p_trust"), now["trust"].get("p_trust")),
|
|
804
|
+
("d2t", tt.get("d2t"), now["trust"].get("d2t"))):
|
|
805
|
+
if old_v is None and new_v is None:
|
|
806
|
+
continue
|
|
807
|
+
try:
|
|
808
|
+
if old_v is None or new_v is None or \
|
|
809
|
+
abs(float(old_v) - float(new_v)) > DRIFT_TOL:
|
|
810
|
+
issues.append(_issue(
|
|
811
|
+
"drift", "warn",
|
|
812
|
+
f"{name} 卡值={old_v} 现算={new_v}(容差 {DRIFT_TOL})",
|
|
813
|
+
field=name))
|
|
814
|
+
except (TypeError, ValueError):
|
|
815
|
+
issues.append(_issue("drift", "warn",
|
|
816
|
+
f"{name} 不可比较:{old_v} vs {new_v}",
|
|
817
|
+
field=name))
|
|
818
|
+
|
|
819
|
+
# 5b 预测面漂移:预测能力(命中率)随 predict_feedback 演化;卡值过时即为
|
|
820
|
+
# 漂移——这是「自我模型是否跟上自身预测表现」的可重算判定。
|
|
821
|
+
pd_old = st.get("prediction") or {}
|
|
822
|
+
pd_new = now.get("prediction") or {}
|
|
823
|
+
o_hr, n_hr = pd_old.get("hit_rate"), pd_new.get("hit_rate")
|
|
824
|
+
if not (o_hr is None and n_hr is None):
|
|
825
|
+
try:
|
|
826
|
+
if o_hr is None or n_hr is None or \
|
|
827
|
+
abs(float(o_hr) - float(n_hr)) > DRIFT_TOL:
|
|
828
|
+
issues.append(_issue(
|
|
829
|
+
"prediction_drift", "warn",
|
|
830
|
+
f"预测命中率 卡值={o_hr} 现算={n_hr}(容差 {DRIFT_TOL})",
|
|
831
|
+
field="hit_rate"))
|
|
832
|
+
except (TypeError, ValueError):
|
|
833
|
+
issues.append(_issue("prediction_drift", "warn",
|
|
834
|
+
f"预测命中率不可比较:{o_hr} vs {n_hr}",
|
|
835
|
+
field="hit_rate"))
|
|
836
|
+
|
|
837
|
+
# 6 身份唯一:identity_ref 必须指向存在的锚点
|
|
838
|
+
ref = st.get("identity_ref")
|
|
839
|
+
if not ref:
|
|
840
|
+
issues.append(_issue("identity_missing", "info",
|
|
841
|
+
"identity_ref 为空:先在 self 层写身份锚点"))
|
|
842
|
+
elif ref not in nodes:
|
|
843
|
+
issues.append(_issue("identity_dangling", "error",
|
|
844
|
+
f"identity_ref={ref} 指向不存在的节点", ref=ref))
|
|
845
|
+
|
|
846
|
+
# 7 关系完整:禁止自环、禁止同一有向对重复
|
|
847
|
+
seen_rel = {}
|
|
848
|
+
for rel in relations(cg, subject=None):
|
|
849
|
+
nid_r = rel["node_id"]
|
|
850
|
+
if rel["from"] == rel["to"]:
|
|
851
|
+
issues.append(_issue("relation_self_loop", "error",
|
|
852
|
+
f"关系自环:{rel['from']}", node_id=nid_r))
|
|
853
|
+
key = (rel["from"], rel["to"])
|
|
854
|
+
if key in seen_rel:
|
|
855
|
+
issues.append(_issue("duplicate_relation", "error",
|
|
856
|
+
f"重复关系 {key}", node_id=nid_r))
|
|
857
|
+
seen_rel[key] = nid_r
|
|
858
|
+
|
|
859
|
+
# 8 保护一致:状态卡必须可覆盖(否则自我认知被永久冻结)且不可遗忘
|
|
860
|
+
try:
|
|
861
|
+
imm, why = _protect.is_immutable(cg, nid)
|
|
862
|
+
if imm:
|
|
863
|
+
issues.append(_issue("protection_locked", "error",
|
|
864
|
+
f"状态卡不可覆盖:{why}(自我状态将无法演化)"))
|
|
865
|
+
pro, _ = _protect.is_protected(cg, nid)
|
|
866
|
+
if not pro:
|
|
867
|
+
issues.append(_issue("protection_missing", "warn",
|
|
868
|
+
"状态卡未受保护:自我信息可能被遗忘"))
|
|
869
|
+
except Exception as exc: # pragma: no cover
|
|
870
|
+
issues.append(_issue("protection_check_failed", "warn", str(exc)))
|
|
871
|
+
|
|
872
|
+
# 9 索引不悬空:维度标签指向的详情节点必须存在
|
|
873
|
+
dims = st.get("dimensions") or {}
|
|
874
|
+
for d, vals in dims.items():
|
|
875
|
+
if d not in DIMENSIONS:
|
|
876
|
+
issues.append(_issue("unknown_dimension", "warn",
|
|
877
|
+
f"未知索引维度 {d}"))
|
|
878
|
+
continue
|
|
879
|
+
for v in (vals or []):
|
|
880
|
+
tag = dim_tag(d, v)
|
|
881
|
+
holders = [n for n, e in nodes.items()
|
|
882
|
+
if tag in (e.get("tags") or [])]
|
|
883
|
+
if not holders:
|
|
884
|
+
issues.append(_issue("dimension_orphan", "warn",
|
|
885
|
+
f"索引 {tag} 无对应节点(悬空索引)",
|
|
886
|
+
dimension=d, value=v))
|
|
887
|
+
|
|
888
|
+
# 10 新鲜度
|
|
889
|
+
try:
|
|
890
|
+
age = time.time() - float(st.get("state_ts") or 0)
|
|
891
|
+
if age > STALE_AFTER:
|
|
892
|
+
issues.append(_issue("stale", "warn",
|
|
893
|
+
f"状态卡 {age / 3600:.1f} 小时未刷新"))
|
|
894
|
+
except (TypeError, ValueError):
|
|
895
|
+
issues.append(_issue("stale", "warn", "state_ts 不可解析"))
|
|
896
|
+
|
|
897
|
+
errors = [i for i in issues if i["severity"] == "error"]
|
|
898
|
+
warns = [i for i in issues if i["severity"] == "warn"]
|
|
899
|
+
verdict = "broken" if errors else ("drift" if warns else "consistent")
|
|
900
|
+
return {"ok": not errors, "verdict": verdict, "subject": subject,
|
|
901
|
+
"node_id": nid, "n_issues": len(issues),
|
|
902
|
+
"n_errors": len(errors), "issues": issues,
|
|
903
|
+
"state_version": st.get("state_version"),
|
|
904
|
+
"state_hash": st.get("state_hash"), "checked_at": time.time()}
|
|
905
|
+
|
|
906
|
+
|
|
907
|
+
# ---------------------------------------------------------------- 加载 / 概览
|
|
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 及这些字段;
|
|
910
|
+
def bootstrap(cg, subject=DEFAULT_SUBJECT, window=RECENT_WINDOW,
|
|
911
|
+
auto_refresh=True, actor="bootstrap"):
|
|
912
|
+
"""会话启动:加载自我状态卡 + 关系 + 最近留痕 + 索引概览。
|
|
913
|
+
|
|
914
|
+
这是「跨会话自动加载自我」的入口:self 层薄卡 + 认知图连接。
|
|
915
|
+
"""
|
|
916
|
+
st = snapshot(cg, subject)
|
|
917
|
+
if st is None and auto_refresh:
|
|
918
|
+
refresh(cg, subject, window=window, actor=actor)
|
|
919
|
+
st = snapshot(cg, subject)
|
|
920
|
+
elif st is not None and auto_refresh:
|
|
921
|
+
# 只在漂移或过期时刷新,避免每次启动都写盘
|
|
922
|
+
try:
|
|
923
|
+
age = time.time() - float(st.get("state_ts") or 0)
|
|
924
|
+
except (TypeError, ValueError):
|
|
925
|
+
age = STALE_AFTER + 1
|
|
926
|
+
if age > STALE_AFTER:
|
|
927
|
+
refresh(cg, subject, window=window, actor=actor)
|
|
928
|
+
st = snapshot(cg, subject)
|
|
929
|
+
rels = relations(cg, subject=subject)
|
|
930
|
+
return {"ok": st is not None, "subject": subject, "state": st,
|
|
931
|
+
"relations": rels, "relations_count": len(rels),
|
|
932
|
+
"recent_states": history(cg, limit=5, subject=subject),
|
|
933
|
+
"dimensions": (st or {}).get("dimensions") or {},
|
|
934
|
+
"loaded_at": time.time()}
|
|
935
|
+
|
|
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;
|
|
938
|
+
def _with_session_slice(cg, out, st, session):
|
|
939
|
+
"""给 summary 结果补「本会话切片」(薄卡 + 富索引,不改单例语义)。
|
|
940
|
+
|
|
941
|
+
· session_registered —— 本会话是否已登记在该状态卡的 session 维度上;
|
|
942
|
+
· session_refs —— 该维度反查到的详情节点指针(不搬运内容)。
|
|
943
|
+
"""
|
|
944
|
+
if not session:
|
|
945
|
+
return out
|
|
946
|
+
s = str(session)
|
|
947
|
+
out["session"] = s
|
|
948
|
+
dims = (st or {}).get("dimensions") or {}
|
|
949
|
+
out["session_registered"] = bool(s in (dims.get("session") or []))
|
|
950
|
+
try:
|
|
951
|
+
res = index(cg, "session", s, limit=20)
|
|
952
|
+
out["session_refs"] = {"count": res.get("count", 0),
|
|
953
|
+
"items": res.get("items", [])}
|
|
954
|
+
except Exception: # noqa: BLE001
|
|
955
|
+
out["session_refs"] = {"count": 0, "items": [], "degraded": True}
|
|
956
|
+
return out
|
|
957
|
+
|
|
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 返回;
|
|
960
|
+
def summary(cg, subject=DEFAULT_SUBJECT, session=None):
|
|
961
|
+
"""一句话自我状态(供 health / 面板)。
|
|
962
|
+
|
|
963
|
+
session:会话归因切片。**不改变单例卡语义**——只回报「本会话是否已登记在
|
|
964
|
+
该卡的 session 维度上」以及该维度反查到的详情节点指针(薄卡 + 富索引)。
|
|
965
|
+
"""
|
|
966
|
+
st = snapshot(cg, subject)
|
|
967
|
+
if not st:
|
|
968
|
+
return _with_session_slice(cg, {"ok": False, "subject": subject,
|
|
969
|
+
"note": "无状态卡"}, st, session)
|
|
970
|
+
ig, tt = st.get("information_gap") or {}, st.get("trust") or {}
|
|
971
|
+
out = {"ok": True, "subject": subject,
|
|
972
|
+
"version": st.get("state_version"),
|
|
973
|
+
"updated_at": st.get("updated_at"),
|
|
974
|
+
"d_current": ig.get("d_current"), "d2": ig.get("d2"),
|
|
975
|
+
"emotion": st.get("emotion"),
|
|
976
|
+
"p_trust": tt.get("p_trust"), "d2t": tt.get("d2t"),
|
|
977
|
+
"affect": st.get("affect"),
|
|
978
|
+
"trust_band": tt.get("band"),
|
|
979
|
+
"recent_n": (st.get("short_term") or {}).get("n"),
|
|
980
|
+
"relations": st.get("relations"),
|
|
981
|
+
"identity_ref": st.get("identity_ref"),
|
|
982
|
+
"hit_rate": (st.get("prediction") or {}).get("hit_rate"),
|
|
983
|
+
"prediction_reflect": (st.get("prediction") or {}).get("reflect"),
|
|
984
|
+
"text": (f"[{subject}] D={ig.get('d_current')} "
|
|
985
|
+
f"情绪={st.get('emotion')} P_trust={tt.get('p_trust')} "
|
|
986
|
+
f"情感={st.get('affect')} "
|
|
987
|
+
f"短期={((st.get('short_term') or {}).get('n'))}条 "
|
|
988
|
+
f"v{st.get('state_version')}")}
|
|
989
|
+
return _with_session_slice(cg, out, st, session)
|
|
990
|
+
|
|
991
|
+
|
|
992
|
+
# 生效条件:无参调用时返回模块自描述 dict,内含模块常量 SCHEMA_VERSION、DEFAULT_SUBJECT、LOG_FILE、DIMENSIONS、TRUST_BANDS、RELATION_TYPES、DRIFT_TOL、STALE_AFTER 等;
|
|
993
|
+
def catalog():
|
|
994
|
+
"""自描述:九项自我信息 + 五维索引 + 审计规则(供协议对照验证)。"""
|
|
995
|
+
return {
|
|
996
|
+
"module": "self_state",
|
|
997
|
+
"schema": SCHEMA_VERSION,
|
|
998
|
+
"default_subject": DEFAULT_SUBJECT,
|
|
999
|
+
"log": LOG_FILE,
|
|
1000
|
+
"principles": [
|
|
1001
|
+
"self 层是薄的:只放状态卡(单例)与关系节点,不搬运内容",
|
|
1002
|
+
"具体细节留在原层,由认知图的标签/边按五维索引连接过来",
|
|
1003
|
+
"不可遗忘 ≠ 不可覆盖:状态卡可演化,身份锚点两者皆禁",
|
|
1004
|
+
],
|
|
1005
|
+
"self_info": {
|
|
1006
|
+
"information_gap": "D/d1/d2 + emotion ← _reflection.jsonl",
|
|
1007
|
+
"trust": "p_gap/p_trust + d2t/affect ← evidence_log",
|
|
1008
|
+
"emotion": "d^2D/dt^2(信息差二阶)",
|
|
1009
|
+
"affect": "d^2T/dt^2(信任二阶)",
|
|
1010
|
+
"short_term": "窗口摘要 + 指针 ← _recent.jsonl",
|
|
1011
|
+
"importance": "importance_self + important_refs",
|
|
1012
|
+
"identity": "identity_ref → self 层身份锚点",
|
|
1013
|
+
"relations": "self_relation_* 有向关系节点",
|
|
1014
|
+
"prediction": "hit_rate/threshold/reflect + ECE ← _prediction.jsonl",
|
|
1015
|
+
},
|
|
1016
|
+
"dimensions": list(DIMENSIONS),
|
|
1017
|
+
"trust_bands": [b for _lo, b in TRUST_BANDS],
|
|
1018
|
+
"relation_types": list(RELATION_TYPES),
|
|
1019
|
+
"audit_rules": [
|
|
1020
|
+
"duplicate_state", "version_gap", "chain_broken",
|
|
1021
|
+
"time_regression", "log_tail_mismatch", "fabricated_emotion",
|
|
1022
|
+
"fabricated_affect", "drift", "prediction_drift",
|
|
1023
|
+
"identity_missing",
|
|
1024
|
+
"identity_dangling", "relation_self_loop", "duplicate_relation",
|
|
1025
|
+
"protection_locked", "protection_missing", "unknown_dimension",
|
|
1026
|
+
"dimension_orphan", "stale", "missing_state",
|
|
1027
|
+
],
|
|
1028
|
+
"tolerances": {"drift": DRIFT_TOL, "stale_after_sec": STALE_AFTER},
|
|
1029
|
+
}
|