dsh-data-domain-knowledge 0.1.0-alpha.9 → 0.1.1-alpha.2
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 +118 -567
- package/cordis.patch.yml +5 -418
- package/eval/README.md +19 -8
- package/eval/benchmark-extraction-format.mjs +20 -0
- package/eval/evaluate-retrieval.mjs +17 -8
- package/eval/queries.example.jsonl +1 -0
- package/lib/client.js +4181 -3239
- package/lib/contract-envelope.d.ts +91 -0
- package/lib/contract-envelope.js +106 -0
- package/lib/contract-layout.d.ts +130 -0
- package/lib/contract-layout.js +138 -0
- package/lib/contract-refs.d.ts +39 -0
- package/lib/contract-refs.js +9 -0
- package/lib/contract-version.d.ts +311 -0
- package/lib/contract-version.js +270 -0
- package/lib/contract-wire.d.ts +147 -0
- package/lib/contract-wire.js +99 -0
- package/lib/contracts.d.ts +12 -0
- package/lib/contracts.js +12 -0
- package/lib/doc-page.js +2 -2
- package/lib/pages.js +3329 -1296
- package/lib/types/agent-behavior-rules.d.ts +10 -0
- package/lib/types/agent-behavior-rules.d.ts.map +1 -0
- package/lib/types/agent-behavior-rules.js +28 -0
- package/lib/types/agent-behavior-rules.js.map +1 -0
- package/lib/types/axis-registry.d.ts +37 -5
- package/lib/types/axis-registry.d.ts.map +1 -1
- package/lib/types/axis-registry.js +234 -26
- package/lib/types/axis-registry.js.map +1 -1
- package/lib/types/card-organize.d.ts +27 -12
- package/lib/types/card-organize.d.ts.map +1 -1
- package/lib/types/card-organize.js +58 -73
- package/lib/types/card-organize.js.map +1 -1
- package/lib/types/compiler/embedding-identity.d.ts +3 -0
- package/lib/types/compiler/embedding-identity.d.ts.map +1 -0
- package/lib/types/compiler/embedding-identity.js +24 -0
- package/lib/types/compiler/embedding-identity.js.map +1 -0
- package/lib/types/compiler/snapshot.d.ts +16 -0
- package/lib/types/compiler/snapshot.d.ts.map +1 -0
- package/lib/types/compiler/snapshot.js +2 -0
- package/lib/types/compiler/snapshot.js.map +1 -0
- package/lib/types/config-schema.d.ts +6 -3
- package/lib/types/config-schema.d.ts.map +1 -1
- package/lib/types/config-schema.js +6 -2
- package/lib/types/config-schema.js.map +1 -1
- package/lib/types/convert/host.d.ts +6 -5
- package/lib/types/convert/host.d.ts.map +1 -1
- package/lib/types/convert/host.js +58 -18
- package/lib/types/convert/host.js.map +1 -1
- package/lib/types/convert/ingest.d.ts +10 -1
- package/lib/types/convert/ingest.d.ts.map +1 -1
- package/lib/types/convert/ingest.js +68 -9
- package/lib/types/convert/ingest.js.map +1 -1
- package/lib/types/derive-contract.d.ts +3 -0
- package/lib/types/derive-contract.d.ts.map +1 -0
- package/lib/types/derive-contract.js +15 -0
- package/lib/types/derive-contract.js.map +1 -0
- package/lib/types/diagnosis/cards.d.ts +7 -3
- package/lib/types/diagnosis/cards.d.ts.map +1 -1
- package/lib/types/diagnosis/cards.js +13 -8
- package/lib/types/diagnosis/cards.js.map +1 -1
- package/lib/types/diagnosis/run.d.ts +4 -13
- package/lib/types/diagnosis/run.d.ts.map +1 -1
- package/lib/types/diagnosis/run.js +26 -16
- package/lib/types/diagnosis/run.js.map +1 -1
- package/lib/types/diagnosis/schema.js +2 -2
- package/lib/types/diagnosis/schema.js.map +1 -1
- package/lib/types/diagnosis/seed-source.d.ts +1 -1
- package/lib/types/diagnosis/seed-source.d.ts.map +1 -1
- package/lib/types/diagnosis/seed-source.js +5 -4
- package/lib/types/diagnosis/seed-source.js.map +1 -1
- package/lib/types/diagnosis/seed.d.ts +4 -4
- package/lib/types/diagnosis/seed.d.ts.map +1 -1
- package/lib/types/diagnosis/seed.js +6 -8
- package/lib/types/diagnosis/seed.js.map +1 -1
- package/lib/types/diagnosis/store.d.ts +24 -18
- package/lib/types/diagnosis/store.d.ts.map +1 -1
- package/lib/types/diagnosis/store.js +130 -65
- package/lib/types/diagnosis/store.js.map +1 -1
- package/lib/types/diagnosis/types.d.ts +47 -15
- package/lib/types/diagnosis/types.d.ts.map +1 -1
- package/lib/types/diagnosis/types.js.map +1 -1
- package/lib/types/environment/dsh-python.d.ts +77 -0
- package/lib/types/environment/dsh-python.d.ts.map +1 -0
- package/lib/types/environment/dsh-python.js +139 -0
- package/lib/types/environment/dsh-python.js.map +1 -0
- package/lib/types/environment/install.d.ts +15 -0
- package/lib/types/environment/install.d.ts.map +1 -0
- package/lib/types/environment/install.js +297 -0
- package/lib/types/environment/install.js.map +1 -0
- package/lib/types/environment/manifest.d.ts +208 -0
- package/lib/types/environment/manifest.d.ts.map +1 -0
- package/lib/types/environment/manifest.js +26 -0
- package/lib/types/environment/manifest.js.map +1 -0
- package/lib/types/environment/model-catalog.d.ts +223 -0
- package/lib/types/environment/model-catalog.d.ts.map +1 -0
- package/lib/types/environment/model-catalog.js +129 -0
- package/lib/types/environment/model-catalog.js.map +1 -0
- package/lib/types/environment/process.d.ts +10 -0
- package/lib/types/environment/process.d.ts.map +1 -0
- package/lib/types/environment/process.js +54 -0
- package/lib/types/environment/process.js.map +1 -0
- package/lib/types/environment/selection.d.ts +9 -0
- package/lib/types/environment/selection.d.ts.map +1 -0
- package/lib/types/environment/selection.js +29 -0
- package/lib/types/environment/selection.js.map +1 -0
- package/lib/types/environment/service.d.ts +64 -0
- package/lib/types/environment/service.d.ts.map +1 -0
- package/lib/types/environment/service.js +424 -0
- package/lib/types/environment/service.js.map +1 -0
- package/lib/types/environment/types.d.ts +56 -0
- package/lib/types/environment/types.d.ts.map +1 -0
- package/lib/types/environment/types.js +2 -0
- package/lib/types/environment/types.js.map +1 -0
- package/lib/types/events.d.ts +76 -0
- package/lib/types/events.d.ts.map +1 -0
- package/lib/types/events.js +52 -0
- package/lib/types/events.js.map +1 -0
- package/lib/types/extract-contract.d.ts +3 -0
- package/lib/types/extract-contract.d.ts.map +1 -0
- package/lib/types/extract-contract.js +41 -0
- package/lib/types/extract-contract.js.map +1 -0
- package/lib/types/extract-model.d.ts +5 -0
- package/lib/types/extract-model.d.ts.map +1 -1
- package/lib/types/extract-model.js.map +1 -1
- package/lib/types/extract-table-contract.d.ts +3 -0
- package/lib/types/extract-table-contract.d.ts.map +1 -0
- package/lib/types/extract-table-contract.js +27 -0
- package/lib/types/extract-table-contract.js.map +1 -0
- package/lib/types/extract-table.d.ts +8 -0
- package/lib/types/extract-table.d.ts.map +1 -0
- package/lib/types/extract-table.js +158 -0
- package/lib/types/extract-table.js.map +1 -0
- package/lib/types/extraction-runs.d.ts +30 -6
- package/lib/types/extraction-runs.d.ts.map +1 -1
- package/lib/types/extraction-runs.js +69 -8
- package/lib/types/extraction-runs.js.map +1 -1
- package/lib/types/fs-atomic.d.ts +0 -7
- package/lib/types/fs-atomic.d.ts.map +1 -1
- package/lib/types/fs-atomic.js +17 -2
- package/lib/types/fs-atomic.js.map +1 -1
- package/lib/types/images.d.ts +26 -0
- package/lib/types/images.d.ts.map +1 -0
- package/lib/types/images.js +196 -0
- package/lib/types/images.js.map +1 -0
- package/lib/types/index.d.ts +2 -2
- package/lib/types/index.d.ts.map +1 -1
- package/lib/types/index.js +598 -209
- package/lib/types/index.js.map +1 -1
- package/lib/types/intent/engine.d.ts +12 -4
- package/lib/types/intent/engine.d.ts.map +1 -1
- package/lib/types/intent/engine.js +43 -5
- package/lib/types/intent/engine.js.map +1 -1
- package/lib/types/intent/host.d.ts +5 -8
- package/lib/types/intent/host.d.ts.map +1 -1
- package/lib/types/intent/host.js +56 -11
- package/lib/types/intent/host.js.map +1 -1
- package/lib/types/intent/scope.d.ts +25 -0
- package/lib/types/intent/scope.d.ts.map +1 -0
- package/lib/types/intent/scope.js +116 -0
- package/lib/types/intent/scope.js.map +1 -0
- package/lib/types/intent/tools.d.ts +24 -20
- package/lib/types/intent/tools.d.ts.map +1 -1
- package/lib/types/intent/tools.js +65 -23
- package/lib/types/intent/tools.js.map +1 -1
- package/lib/types/intent/types.d.ts +11 -4
- package/lib/types/intent/types.d.ts.map +1 -1
- package/lib/types/knowledge-bases.d.ts +11 -5
- package/lib/types/knowledge-bases.d.ts.map +1 -1
- package/lib/types/knowledge-bases.js +52 -20
- package/lib/types/knowledge-bases.js.map +1 -1
- package/lib/types/knowledge-contract.d.ts +32 -0
- package/lib/types/knowledge-contract.d.ts.map +1 -0
- package/lib/types/knowledge-contract.js +116 -0
- package/lib/types/knowledge-contract.js.map +1 -0
- package/lib/types/knowledge.d.ts +31 -9
- package/lib/types/knowledge.d.ts.map +1 -1
- package/lib/types/knowledge.js +137 -60
- package/lib/types/knowledge.js.map +1 -1
- package/lib/types/layout.d.ts +10 -29
- package/lib/types/layout.d.ts.map +1 -1
- package/lib/types/layout.js +17 -36
- package/lib/types/layout.js.map +1 -1
- package/lib/types/materialize-snapshot.d.ts +49 -0
- package/lib/types/materialize-snapshot.d.ts.map +1 -0
- package/lib/types/materialize-snapshot.js +2 -0
- package/lib/types/materialize-snapshot.js.map +1 -0
- package/lib/types/migrate.d.ts +11 -0
- package/lib/types/migrate.d.ts.map +1 -1
- package/lib/types/migrate.js +83 -2
- package/lib/types/migrate.js.map +1 -1
- package/lib/types/model-text.d.ts +58 -1
- package/lib/types/model-text.d.ts.map +1 -1
- package/lib/types/model-text.js +85 -30
- package/lib/types/model-text.js.map +1 -1
- package/lib/types/oplog.d.ts.map +1 -1
- package/lib/types/oplog.js +7 -0
- package/lib/types/oplog.js.map +1 -1
- package/lib/types/organize-contract.d.ts +18 -0
- package/lib/types/organize-contract.d.ts.map +1 -0
- package/lib/types/organize-contract.js +82 -0
- package/lib/types/organize-contract.js.map +1 -0
- package/lib/types/pipeline/engine.d.ts +66 -0
- package/lib/types/pipeline/engine.d.ts.map +1 -0
- package/lib/types/pipeline/engine.js +352 -0
- package/lib/types/pipeline/engine.js.map +1 -0
- package/lib/types/pipeline/store.d.ts +73 -0
- package/lib/types/pipeline/store.d.ts.map +1 -0
- package/lib/types/pipeline/store.js +143 -0
- package/lib/types/pipeline/store.js.map +1 -0
- package/lib/types/pipeline/types.d.ts +102 -0
- package/lib/types/pipeline/types.d.ts.map +1 -0
- package/lib/types/pipeline/types.js +5 -0
- package/lib/types/pipeline/types.js.map +1 -0
- package/lib/types/present.d.ts +2 -0
- package/lib/types/present.d.ts.map +1 -1
- package/lib/types/present.js +9 -4
- package/lib/types/present.js.map +1 -1
- package/lib/types/preset-ids.d.ts +4 -0
- package/lib/types/preset-ids.d.ts.map +1 -0
- package/lib/types/preset-ids.js +6 -0
- package/lib/types/preset-ids.js.map +1 -0
- package/lib/types/query-analysis.d.ts +13 -1
- package/lib/types/query-analysis.d.ts.map +1 -1
- package/lib/types/query-analysis.js +62 -6
- package/lib/types/query-analysis.js.map +1 -1
- package/lib/types/recall-rank.d.ts +3 -3
- package/lib/types/recall-rank.d.ts.map +1 -1
- package/lib/types/recall-rank.js +4 -4
- package/lib/types/recall-rank.js.map +1 -1
- package/lib/types/retention.d.ts +39 -0
- package/lib/types/retention.d.ts.map +1 -0
- package/lib/types/retention.js +42 -0
- package/lib/types/retention.js.map +1 -0
- package/lib/types/retrieval/vector-index.d.ts +32 -0
- package/lib/types/retrieval/vector-index.d.ts.map +1 -0
- package/lib/types/retrieval/vector-index.js +15 -0
- package/lib/types/retrieval/vector-index.js.map +1 -0
- package/lib/types/retrieval/visibility.d.ts +6 -0
- package/lib/types/retrieval/visibility.d.ts.map +1 -0
- package/lib/types/retrieval/visibility.js +11 -0
- package/lib/types/retrieval/visibility.js.map +1 -0
- package/lib/types/runtime.d.ts +272 -27
- package/lib/types/runtime.d.ts.map +1 -1
- package/lib/types/runtime.js +2751 -537
- package/lib/types/runtime.js.map +1 -1
- package/lib/types/settings-schema.d.ts +17 -15
- package/lib/types/settings-schema.d.ts.map +1 -1
- package/lib/types/settings-schema.js +50 -47
- package/lib/types/settings-schema.js.map +1 -1
- package/lib/types/settings-volatile.d.ts +4 -4
- package/lib/types/settings-volatile.js +4 -4
- package/lib/types/source-evidence.d.ts +14 -0
- package/lib/types/source-evidence.d.ts.map +1 -0
- package/lib/types/source-evidence.js +36 -0
- package/lib/types/source-evidence.js.map +1 -0
- package/lib/types/store/axis-references.d.ts +5 -0
- package/lib/types/store/axis-references.d.ts.map +1 -0
- package/lib/types/store/axis-references.js +106 -0
- package/lib/types/store/axis-references.js.map +1 -0
- package/lib/types/store/derived.d.ts +12 -1
- package/lib/types/store/derived.d.ts.map +1 -1
- package/lib/types/store/derived.js +71 -12
- package/lib/types/store/derived.js.map +1 -1
- package/lib/types/store/knowledge-db.d.ts +31 -1
- package/lib/types/store/knowledge-db.d.ts.map +1 -1
- package/lib/types/store/knowledge-db.js +252 -18
- package/lib/types/store/knowledge-db.js.map +1 -1
- package/lib/types/store/migrate.d.ts.map +1 -1
- package/lib/types/store/migrate.js +17 -6
- package/lib/types/store/migrate.js.map +1 -1
- package/lib/types/trajectory-types.d.ts +172 -0
- package/lib/types/trajectory-types.d.ts.map +1 -0
- package/lib/types/trajectory-types.js +36 -0
- package/lib/types/trajectory-types.js.map +1 -0
- package/lib/types/types.d.ts +59 -27
- package/lib/types/types.d.ts.map +1 -1
- package/lib/types/types.js +6 -15
- package/lib/types/types.js.map +1 -1
- package/lib/types/wiki-finalize.d.ts +16 -0
- package/lib/types/wiki-finalize.d.ts.map +1 -1
- package/lib/types/wiki-finalize.js +26 -0
- package/lib/types/wiki-finalize.js.map +1 -1
- package/lib/types/wiki-materialize.d.ts +4 -0
- package/lib/types/wiki-materialize.d.ts.map +1 -1
- package/lib/types/wiki-materialize.js +21 -7
- package/lib/types/wiki-materialize.js.map +1 -1
- package/lib/types/wiki-trial-types.d.ts +87 -0
- package/lib/types/wiki-trial-types.d.ts.map +1 -1
- package/lib/types/wiki-trial-types.js +0 -16
- package/lib/types/wiki-trial-types.js.map +1 -1
- package/lib/types/wiki-trial.d.ts +40 -16
- package/lib/types/wiki-trial.d.ts.map +1 -1
- package/lib/types/wiki-trial.js +337 -224
- package/lib/types/wiki-trial.js.map +1 -1
- package/package.json +29 -20
- package/python/prepare-models.py +19 -0
- package/python/requirements.txt +8 -6
- package/scripts/doctor.mjs +13 -19
- package/scripts/environment.mjs +29 -0
- package/scripts/setup-embedding.sh +15 -5
- package/src/agent-behavior-rules.ts +37 -0
- package/src/axis-registry.ts +234 -27
- package/src/card-organize.ts +69 -75
- package/src/client/AgentBehaviorRulesCard.tsx +65 -0
- package/src/client/AttributionEventGuide.tsx +41 -39
- package/src/client/AxisRegistrySection.tsx +204 -164
- package/src/client/CompileDialogs.tsx +44 -29
- package/src/client/DeleteDocumentDialog.tsx +36 -5
- package/src/client/DirectoryPicker.tsx +0 -10
- package/src/client/EnvironmentSetup.tsx +243 -0
- package/src/client/ExpansionGraph.tsx +13 -7
- package/src/client/ExtractionTasksPage.tsx +578 -0
- package/src/client/GuideArchitectureCanvas.tsx +470 -0
- package/src/client/ImportFilesDialog.tsx +2 -13
- package/src/client/IntentLogic.tsx +2 -2
- package/src/client/IntentTab.tsx +118 -47
- package/src/client/KnowledgeBaseTab.tsx +277 -197
- package/src/client/KnowledgeLayerSection.tsx +24 -6
- package/src/client/KnowledgePanel.tsx +58 -93
- package/src/client/KnowledgeSettingsDrawer.tsx +90 -0
- package/src/client/ListPager.tsx +15 -5
- package/src/client/MaterializeTrajectoryView.tsx +124 -0
- package/src/client/NodeSettings.tsx +210 -89
- package/src/client/NormalizeTrajectoryView.tsx +136 -0
- package/src/client/PipelineNotice.tsx +26 -0
- package/src/client/PreviewSections.tsx +118 -51
- package/src/client/RecallPanel.tsx +89 -85
- package/src/client/TabGuide.tsx +159 -184
- package/src/client/TrialReportView.tsx +391 -214
- package/src/client/TypeDefinitionDrawer.tsx +150 -23
- package/src/client/WikiCardPreview.tsx +62 -16
- package/src/client/WikiFlowPanel.tsx +4 -4
- package/src/client/arch/NodeLogicPanel.tsx +3 -4
- package/src/client/arch/model.ts +6 -9
- package/src/client/architecture.tsx +23 -33
- package/src/client/attribution/AttributionGraph.tsx +10 -6
- package/src/client/attribution/AttributionTab.tsx +136 -84
- package/src/client/attribution/CaseView.tsx +3 -1
- package/src/client/attribution/EventView.tsx +108 -0
- package/src/client/attribution/RunView.tsx +2 -2
- package/src/client/attribution/StageChrome.tsx +34 -3
- package/src/client/attribution/ViewSwitch.tsx +20 -5
- package/src/client/attribution/graph-options.ts +47 -39
- package/src/client/attribution/model.ts +17 -10
- package/src/client/config-scope.ts +3 -3
- package/src/client/graph/palette.ts +165 -0
- package/src/client/index.ts +49 -42
- package/src/client/pages.css +459 -135
- package/src/client/pages.tsx +37 -9
- package/src/client/problem-domains.ts +1 -1
- package/src/client/session-remote.ts +9 -0
- package/src/client/styles.css +205 -62
- package/src/client/wiki-trial.ts +44 -3
- package/src/compiler/embedding-identity.ts +21 -0
- package/src/compiler/snapshot.ts +17 -0
- package/src/config-schema.ts +8 -3
- package/src/convert/host.ts +56 -21
- package/src/convert/ingest.ts +74 -10
- package/src/derive-contract.ts +14 -0
- package/src/diagnosis/cards.ts +13 -8
- package/src/diagnosis/run.ts +27 -17
- package/src/diagnosis/schema.ts +2 -2
- package/src/diagnosis/seed-source.ts +5 -4
- package/src/diagnosis/seed.ts +6 -8
- package/src/diagnosis/store.ts +138 -69
- package/src/diagnosis/types.ts +34 -15
- package/src/environment/dsh-python.ts +156 -0
- package/src/environment/install.ts +244 -0
- package/src/environment/manifest.ts +28 -0
- package/src/environment/model-catalog.ts +147 -0
- package/src/environment/process.ts +54 -0
- package/src/environment/selection.ts +29 -0
- package/src/environment/service.ts +348 -0
- package/src/environment/types.ts +45 -0
- package/src/events.ts +108 -0
- package/src/extract-contract.ts +42 -0
- package/src/extract-model.ts +10 -1
- package/src/extract-table-contract.ts +26 -0
- package/src/extract-table.ts +132 -0
- package/src/extraction-runs.ts +70 -11
- package/src/fs-atomic.ts +19 -2
- package/src/images.ts +203 -0
- package/src/index.ts +553 -205
- package/src/intent/README.md +8 -6
- package/src/intent/engine.ts +44 -6
- package/src/intent/host.ts +59 -18
- package/src/intent/scope.ts +125 -0
- package/src/intent/tools.ts +70 -28
- package/src/intent/types.ts +11 -4
- package/src/knowledge-bases.ts +56 -20
- package/src/knowledge-contract.ts +114 -0
- package/src/knowledge.ts +138 -69
- package/src/layout.ts +17 -37
- package/src/materialize-snapshot.ts +46 -0
- package/src/migrate.ts +75 -2
- package/src/model-text.ts +109 -4
- package/src/oplog.ts +7 -0
- package/src/organize-contract.ts +83 -0
- package/src/pipeline/engine.ts +323 -0
- package/src/pipeline/store.ts +147 -0
- package/src/pipeline/types.ts +93 -0
- package/src/present.ts +10 -4
- package/src/preset-ids.ts +6 -0
- package/src/python/embedding_worker.py +66 -2
- package/src/query-analysis.ts +68 -8
- package/src/recall-rank.ts +5 -5
- package/src/retention.ts +76 -0
- package/src/retrieval/vector-index.ts +45 -0
- package/src/retrieval/visibility.ts +14 -0
- package/src/runtime.ts +2708 -567
- package/src/settings-schema.ts +11 -8
- package/src/settings-volatile.ts +4 -4
- package/src/source-evidence.ts +37 -0
- package/src/store/axis-references.ts +96 -0
- package/src/store/derived.ts +70 -12
- package/src/store/knowledge-db.ts +248 -17
- package/src/store/migrate.ts +16 -6
- package/src/trajectory-types.ts +142 -0
- package/src/types.ts +61 -35
- package/src/wiki-finalize.ts +28 -0
- package/src/wiki-materialize.ts +25 -6
- package/src/wiki-trial-types.ts +83 -1
- package/src/wiki-trial.ts +357 -185
- package/lib/types/intent/preset-tools.d.ts +0 -37
- package/lib/types/intent/preset-tools.d.ts.map +0 -1
- package/lib/types/intent/preset-tools.js +0 -45
- package/lib/types/intent/preset-tools.js.map +0 -1
- package/preset/tita-data-agent/agent.cordis.yml +0 -394
- package/preset/tita-data-agent/preset.yml +0 -3
- package/scripts/install-agent-preset.mjs +0 -92
- package/scripts/sync-agent-preset.mjs +0 -188
- package/src/client/AgentPresetCard.tsx +0 -247
- package/src/client/agent-preset.ts +0 -121
- package/src/client/convert/ConvertPanel.tsx +0 -583
- package/src/client/convert/styles.css +0 -138
- package/src/client/convert/styles.ts +0 -49
- package/src/intent/preset-tools.ts +0 -48
package/src/axis-registry.ts
CHANGED
|
@@ -34,17 +34,31 @@
|
|
|
34
34
|
* ## 为什么存在记忆段
|
|
35
35
|
*
|
|
36
36
|
* 注册表**不可重建**:它是人确认过的命名决定(改 canonical name、并两个实体、加一个别名),
|
|
37
|
-
*
|
|
38
|
-
*
|
|
37
|
+
* 重跑一次抽取不会得到同样的结果。所以它与 Knowledge 共享 `knowledge.db`,
|
|
38
|
+
* 经过同一个属主与 schema 版本入口校验。
|
|
39
39
|
*/
|
|
40
40
|
|
|
41
|
-
import {
|
|
42
|
-
import { dirname } from 'node:path'
|
|
41
|
+
import { createHash } from 'node:crypto'
|
|
43
42
|
import { loadSqlite, type SqliteDatabase } from './fts.js'
|
|
43
|
+
import { KnowledgeDb } from './store/knowledge-db.js'
|
|
44
44
|
|
|
45
45
|
/** 三个轴。三者地位相同(§3.1)—— 没有哪个更特殊。 */
|
|
46
46
|
export type AxisKind = 'object' | 'action' | 'scene'
|
|
47
47
|
|
|
48
|
+
/** 当前参与关系扩展召回的轴关系。它连接相关对象,不表示父子层级。 */
|
|
49
|
+
export const AXIS_RELATION_TYPES = ['related_to'] as const
|
|
50
|
+
export type AxisRelationType = typeof AXIS_RELATION_TYPES[number]
|
|
51
|
+
|
|
52
|
+
export interface AxisRelation {
|
|
53
|
+
id: string
|
|
54
|
+
fromId: string
|
|
55
|
+
toId: string
|
|
56
|
+
relationType: AxisRelationType
|
|
57
|
+
evidence: string
|
|
58
|
+
createdAt: string
|
|
59
|
+
updatedAt: string
|
|
60
|
+
}
|
|
61
|
+
|
|
48
62
|
/**
|
|
49
63
|
* 一个轴实体的状态。
|
|
50
64
|
*
|
|
@@ -215,6 +229,18 @@ CREATE TABLE IF NOT EXISTS entity_guidance (
|
|
|
215
229
|
created_at TEXT NOT NULL
|
|
216
230
|
);
|
|
217
231
|
CREATE INDEX IF NOT EXISTS idx_axis_guidance_entity ON entity_guidance(entity_id);
|
|
232
|
+
CREATE TABLE IF NOT EXISTS axis_relations (
|
|
233
|
+
id TEXT PRIMARY KEY,
|
|
234
|
+
from_id TEXT NOT NULL,
|
|
235
|
+
to_id TEXT NOT NULL,
|
|
236
|
+
relation_type TEXT NOT NULL,
|
|
237
|
+
evidence TEXT NOT NULL DEFAULT '',
|
|
238
|
+
created_at TEXT NOT NULL,
|
|
239
|
+
updated_at TEXT NOT NULL,
|
|
240
|
+
UNIQUE(from_id, to_id, relation_type)
|
|
241
|
+
);
|
|
242
|
+
CREATE INDEX IF NOT EXISTS idx_axis_relation_from ON axis_relations(from_id, relation_type);
|
|
243
|
+
CREATE INDEX IF NOT EXISTS idx_axis_relation_to ON axis_relations(to_id, relation_type);
|
|
218
244
|
`
|
|
219
245
|
|
|
220
246
|
/**
|
|
@@ -225,20 +251,27 @@ CREATE INDEX IF NOT EXISTS idx_axis_guidance_entity ON entity_guidance(entity_id
|
|
|
225
251
|
*/
|
|
226
252
|
export class AxisRegistry {
|
|
227
253
|
private db: SqliteDatabase | null = null
|
|
254
|
+
private owner: KnowledgeDb | null = null
|
|
228
255
|
|
|
229
|
-
constructor(private readonly file: string) {}
|
|
256
|
+
constructor(private readonly file: string, private readonly shared?: SqliteDatabase) {}
|
|
230
257
|
|
|
231
258
|
async open(): Promise<boolean> {
|
|
232
259
|
if (this.db !== null) return true
|
|
233
260
|
const sqlite = await loadSqlite()
|
|
234
261
|
if (sqlite === null) return false
|
|
235
262
|
try {
|
|
236
|
-
|
|
237
|
-
|
|
263
|
+
if (this.shared) this.db = this.shared
|
|
264
|
+
else {
|
|
265
|
+
const opened = await KnowledgeDb.open(this.file, sqlite.DatabaseSync)
|
|
266
|
+
if (!opened.ok) return false
|
|
267
|
+
this.owner = opened.db
|
|
268
|
+
this.db = opened.db.connection()
|
|
269
|
+
}
|
|
238
270
|
this.db.exec(SCHEMA)
|
|
271
|
+
this.db.exec('CREATE TABLE IF NOT EXISTS axis_input_revision (id INTEGER PRIMARY KEY CHECK(id = 1), revision INTEGER NOT NULL); INSERT OR IGNORE INTO axis_input_revision VALUES (1, 0)')
|
|
239
272
|
/*
|
|
240
|
-
*
|
|
241
|
-
*
|
|
273
|
+
* 主库已通过 `user_version` 校验,再幂等补齐存量注册表的扩展列。缺就补。
|
|
274
|
+
* `CREATE TABLE IF NOT EXISTS` 对
|
|
242
275
|
* 已经存在的表一个字都不改 —— 少了这一段,0068 之前建的库会永远没有这两列,
|
|
243
276
|
* 而症状是「定义存进去了,下次打开又没了」。
|
|
244
277
|
*/
|
|
@@ -250,11 +283,57 @@ export class AxisRegistry {
|
|
|
250
283
|
}
|
|
251
284
|
return true
|
|
252
285
|
} catch {
|
|
286
|
+
this.owner?.close()
|
|
287
|
+
this.owner = null
|
|
253
288
|
this.db = null
|
|
254
289
|
return false
|
|
255
290
|
}
|
|
256
291
|
}
|
|
257
292
|
|
|
293
|
+
inputRevision(): number {
|
|
294
|
+
return Number((this.db?.prepare('SELECT revision FROM axis_input_revision WHERE id = 1').get() as { revision?: number } | undefined)?.revision ?? 0)
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
/** Resolve against an isolated copy; commit only if the live registry still matches. */
|
|
298
|
+
async draft(): Promise<{ registry: AxisRegistry; commit: () => void; close: () => void }> {
|
|
299
|
+
const sqlite = await loadSqlite()
|
|
300
|
+
if (!sqlite || !this.db) throw new Error('AXIS_REGISTRY_UNAVAILABLE')
|
|
301
|
+
const live = this.db
|
|
302
|
+
const tables = ['axis_entities', 'axis_aliases', 'entity_guidance', 'axis_relations', 'axis_input_revision']
|
|
303
|
+
const read = (db: SqliteDatabase): Array<Array<Record<string, unknown>>> => tables.map(table => db.prepare(`SELECT * FROM ${table} ORDER BY rowid`).all() as Array<Record<string, unknown>>)
|
|
304
|
+
const before = read(live)
|
|
305
|
+
const copy = new sqlite.DatabaseSync(':memory:')
|
|
306
|
+
const registry = new AxisRegistry(':memory:', copy)
|
|
307
|
+
try {
|
|
308
|
+
if (!await registry.open()) throw new Error('AXIS_DRAFT_UNAVAILABLE')
|
|
309
|
+
tables.forEach((table, index) => {
|
|
310
|
+
copy.exec(`DELETE FROM ${table}`)
|
|
311
|
+
for (const row of before[index]!) {
|
|
312
|
+
const columns = Object.keys(row)
|
|
313
|
+
copy.prepare(`INSERT INTO ${table} (${columns.join(',')}) VALUES (${columns.map(() => '?').join(',')})`).run(...Object.values(row))
|
|
314
|
+
}
|
|
315
|
+
})
|
|
316
|
+
return {
|
|
317
|
+
registry,
|
|
318
|
+
commit: () => {
|
|
319
|
+
if (JSON.stringify(read(live)) !== JSON.stringify(before)) throw new Error('归一期间实体注册表已变化,请重试归一')
|
|
320
|
+
// Resolution only adds entities/aliases and fills model definitions.
|
|
321
|
+
for (const table of ['axis_entities', 'axis_aliases']) {
|
|
322
|
+
for (const row of copy.prepare(`SELECT * FROM ${table}`).all() as Array<Record<string, unknown>>) {
|
|
323
|
+
const columns = Object.keys(row)
|
|
324
|
+
const keys = table === 'axis_entities' ? ['id'] : ['entity_id', 'normalized', 'scope_id']
|
|
325
|
+
const updates = columns.filter(column => !keys.includes(column)).map(column => `${column}=excluded.${column}`)
|
|
326
|
+
live.prepare(`INSERT INTO ${table} (${columns.join(',')}) VALUES (${columns.map(() => '?').join(',')}) ON CONFLICT (${keys.join(',')}) DO UPDATE SET ${updates.join(',')}`).run(...Object.values(row))
|
|
327
|
+
}
|
|
328
|
+
}
|
|
329
|
+
},
|
|
330
|
+
close: () => { registry.close(); copy.close() },
|
|
331
|
+
}
|
|
332
|
+
} catch (cause) { registry.close(); copy.close(); throw cause }
|
|
333
|
+
}
|
|
334
|
+
|
|
335
|
+
private changedInput(): void { this.db?.prepare('UPDATE axis_input_revision SET revision = revision + 1 WHERE id = 1').run() }
|
|
336
|
+
|
|
258
337
|
available(): boolean {
|
|
259
338
|
return this.db !== null
|
|
260
339
|
}
|
|
@@ -316,10 +395,19 @@ export class AxisRegistry {
|
|
|
316
395
|
|
|
317
396
|
// 2 · 作用域内 alias
|
|
318
397
|
if (parentId) {
|
|
319
|
-
const aliased = this.
|
|
320
|
-
if (aliased) {
|
|
321
|
-
this.learn(aliased
|
|
322
|
-
return { mention, normalized, outcome: 'matched', entity: aliased
|
|
398
|
+
const aliased = this.findAliases(input.kind, normalized, parentId)
|
|
399
|
+
if (aliased.length === 1) {
|
|
400
|
+
this.learn(aliased[0]!, mention, normalized, parentId, input.description)
|
|
401
|
+
return { mention, normalized, outcome: 'matched', entity: aliased[0]!, how: 'scoped-alias' }
|
|
402
|
+
}
|
|
403
|
+
/**
|
|
404
|
+
* 同一作用域里两个实体可以合法地共有同一条别名(`axis_aliases` 的主键是
|
|
405
|
+
* `(entity_id, normalized, scope_id)`,不是 `(normalized, scope_id)`)。这时候
|
|
406
|
+
* **不能取第一个** —— 本文件的规矩是「不唯一就不猜」,猜错的代价是把一条知识挂到
|
|
407
|
+
* 错误的对象上并一路传到卡名。全局那条路(下面)一直是这么做的,只有这里漏了。
|
|
408
|
+
*/
|
|
409
|
+
if (aliased.length > 1) {
|
|
410
|
+
return { mention, normalized, outcome: 'ambiguous', candidates: aliased.slice().sort((left, right) => left.name.localeCompare(right.name)) }
|
|
323
411
|
}
|
|
324
412
|
}
|
|
325
413
|
|
|
@@ -447,6 +535,61 @@ export class AxisRegistry {
|
|
|
447
535
|
})
|
|
448
536
|
}
|
|
449
537
|
|
|
538
|
+
/** 列出对象关系;传实体 id 时只返回它的邻接关系。 */
|
|
539
|
+
async listRelations(entityId?: string): Promise<AxisRelation[]> {
|
|
540
|
+
if (this.db === null) throw new Error('AXIS_REGISTRY_UNAVAILABLE')
|
|
541
|
+
const params: Record<string, unknown> = {}
|
|
542
|
+
const filter = entityId === undefined ? '' : 'AND (r.from_id = :entityId OR r.to_id = :entityId)'
|
|
543
|
+
if (entityId !== undefined) params.entityId = entityId
|
|
544
|
+
const rows = this.db.prepare(`
|
|
545
|
+
SELECT r.* FROM axis_relations r
|
|
546
|
+
JOIN axis_entities a ON a.id = r.from_id
|
|
547
|
+
JOIN axis_entities b ON b.id = r.to_id
|
|
548
|
+
WHERE r.relation_type = 'related_to'
|
|
549
|
+
AND a.kind = 'object' AND b.kind = 'object'
|
|
550
|
+
AND a.status <> 'merged' AND b.status <> 'merged'
|
|
551
|
+
${filter}
|
|
552
|
+
ORDER BY r.from_id, r.to_id
|
|
553
|
+
`).all(params) as Array<Record<string, unknown>>
|
|
554
|
+
return rows.map(rowToRelation)
|
|
555
|
+
}
|
|
556
|
+
|
|
557
|
+
/** 建立无方向的 related_to 边;同一个对象不能关联自己。 */
|
|
558
|
+
async addRelation(fromId: string, toId: string, evidence = ''): Promise<AxisRelation | null> {
|
|
559
|
+
if (this.db === null) throw new Error('AXIS_REGISTRY_UNAVAILABLE')
|
|
560
|
+
if (!fromId || !toId || fromId === toId) return null
|
|
561
|
+
const [from, to] = await Promise.all([this.get(fromId), this.get(toId)])
|
|
562
|
+
if (from === null || to === null || from.kind !== 'object' || to.kind !== 'object' || from.status === 'merged' || to.status === 'merged') return null
|
|
563
|
+
const [left, right] = [fromId, toId].sort((a, b) => a.localeCompare(b))
|
|
564
|
+
const now = new Date().toISOString()
|
|
565
|
+
const relation: AxisRelation = {
|
|
566
|
+
id: axisRelationId(left, right, 'related_to'),
|
|
567
|
+
fromId: left,
|
|
568
|
+
toId: right,
|
|
569
|
+
relationType: 'related_to',
|
|
570
|
+
evidence: evidence.trim(),
|
|
571
|
+
createdAt: now,
|
|
572
|
+
updatedAt: now,
|
|
573
|
+
}
|
|
574
|
+
this.db.prepare(`
|
|
575
|
+
INSERT INTO axis_relations (id, from_id, to_id, relation_type, evidence, created_at, updated_at)
|
|
576
|
+
VALUES (?, ?, ?, ?, ?, ?, ?)
|
|
577
|
+
ON CONFLICT(from_id, to_id, relation_type) DO UPDATE SET
|
|
578
|
+
evidence = CASE WHEN excluded.evidence <> '' THEN excluded.evidence ELSE axis_relations.evidence END,
|
|
579
|
+
updated_at = excluded.updated_at
|
|
580
|
+
`).run(relation.id, relation.fromId, relation.toId, relation.relationType, relation.evidence, relation.createdAt, relation.updatedAt)
|
|
581
|
+
const row = this.db.prepare('SELECT * FROM axis_relations WHERE from_id = ? AND to_id = ? AND relation_type = ?')
|
|
582
|
+
.get(left, right, 'related_to') as Record<string, unknown> | undefined
|
|
583
|
+
return row === undefined ? null : rowToRelation(row)
|
|
584
|
+
}
|
|
585
|
+
|
|
586
|
+
/** 移除一条对象关系。 */
|
|
587
|
+
async removeRelation(id: string): Promise<boolean> {
|
|
588
|
+
if (this.db === null) throw new Error('AXIS_REGISTRY_UNAVAILABLE')
|
|
589
|
+
const result = this.db.prepare('DELETE FROM axis_relations WHERE id = ?').run(id) as { changes?: unknown }
|
|
590
|
+
return Number(result.changes) > 0
|
|
591
|
+
}
|
|
592
|
+
|
|
450
593
|
/** 每个轴各有多少个实体、其中几个待人确认。类型定义那一屏的计数读它。 */
|
|
451
594
|
async stats(): Promise<Record<AxisKind, { total: number; candidate: number }>> {
|
|
452
595
|
const empty = { total: 0, candidate: 0 }
|
|
@@ -475,6 +618,7 @@ export class AxisRegistry {
|
|
|
475
618
|
const now = new Date().toISOString()
|
|
476
619
|
this.db.prepare('UPDATE axis_entities SET name = ?, updated_at = ? WHERE id = ?').run(next, now, id)
|
|
477
620
|
this.insertAlias(id, current.name, normalizeAxisName(current.name), current.scopeId, now)
|
|
621
|
+
this.changedInput()
|
|
478
622
|
return this.get(id)
|
|
479
623
|
}
|
|
480
624
|
|
|
@@ -490,6 +634,7 @@ export class AxisRegistry {
|
|
|
490
634
|
const normalized = normalizeAxisName(alias)
|
|
491
635
|
if (!normalized) return false
|
|
492
636
|
this.insertAlias(id, alias.trim(), normalized, scopeId.trim(), new Date().toISOString())
|
|
637
|
+
this.changedInput()
|
|
493
638
|
return true
|
|
494
639
|
}
|
|
495
640
|
|
|
@@ -501,6 +646,7 @@ export class AxisRegistry {
|
|
|
501
646
|
async setStatus(id: string, status: AxisStatus): Promise<AxisEntity | null> {
|
|
502
647
|
if (this.db === null) throw new Error('AXIS_REGISTRY_UNAVAILABLE')
|
|
503
648
|
this.db.prepare('UPDATE axis_entities SET status = ?, updated_at = ? WHERE id = ?').run(status, new Date().toISOString(), id)
|
|
649
|
+
this.changedInput()
|
|
504
650
|
return this.get(id)
|
|
505
651
|
}
|
|
506
652
|
|
|
@@ -511,18 +657,58 @@ export class AxisRegistry {
|
|
|
511
657
|
* 它转成 `merged`,`scopeId` 改存目标 id —— 复用这一列而不是新加一列,因为
|
|
512
658
|
* 「它现在归谁」与「它在谁的下面」对解析来说问的是同一件事。
|
|
513
659
|
*/
|
|
514
|
-
async merge(fromId: string, intoId: string): Promise<boolean> {
|
|
660
|
+
async merge(fromId: string, intoId: string, redirect?: (kind: AxisKind) => void): Promise<boolean> {
|
|
515
661
|
if (this.db === null) throw new Error('AXIS_REGISTRY_UNAVAILABLE')
|
|
516
662
|
if (fromId === intoId) return false
|
|
517
663
|
const [from, into] = [await this.get(fromId), await this.get(intoId)]
|
|
518
|
-
if (from === null || into === null || from.kind !== into.kind) return false
|
|
664
|
+
if (from === null || into === null || from.kind !== into.kind || from.status === 'merged' || into.status === 'merged') return false
|
|
519
665
|
const now = new Date().toISOString()
|
|
520
|
-
this.db.
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
666
|
+
this.db.exec('SAVEPOINT axis_merge')
|
|
667
|
+
try {
|
|
668
|
+
this.db.prepare('UPDATE axis_entities SET status = ?, scope_id = ?, updated_at = ? WHERE id = ?').run('merged', intoId, now, fromId)
|
|
669
|
+
// 被并者的别名与名字一起搬过去:按旧名、旧别名都要搜得到。
|
|
670
|
+
this.insertAlias(intoId, from.name, normalizeAxisName(from.name), into.scopeId, now)
|
|
671
|
+
const aliases = this.db.prepare('SELECT alias, scope_id FROM axis_aliases WHERE entity_id = ?').all(fromId) as Array<Record<string, unknown>>
|
|
672
|
+
for (const row of aliases) this.insertAlias(intoId, String(row.alias), normalizeAxisName(String(row.alias)), String(row.scope_id ?? ''), now)
|
|
673
|
+
this.redirectRelations(fromId, intoId, now)
|
|
674
|
+
redirect?.(from.kind)
|
|
675
|
+
// Scope references on both canonical names and aliases move together.
|
|
676
|
+
this.db.prepare("UPDATE axis_entities SET scope_id = ? WHERE scope_id = ? AND status <> 'merged'").run(intoId, fromId)
|
|
677
|
+
this.db.prepare(`INSERT OR IGNORE INTO axis_aliases (entity_id, alias, normalized, scope_id, created_at)
|
|
678
|
+
SELECT entity_id, alias, normalized, ?, created_at FROM axis_aliases WHERE scope_id = ?`).run(intoId, fromId)
|
|
679
|
+
this.db.prepare('DELETE FROM axis_aliases WHERE scope_id = ?').run(fromId)
|
|
680
|
+
this.db.prepare('UPDATE entity_guidance SET entity_id = ? WHERE entity_id = ?').run(intoId, fromId)
|
|
681
|
+
this.changedInput()
|
|
682
|
+
this.db.exec('RELEASE axis_merge')
|
|
683
|
+
return true
|
|
684
|
+
} catch (cause) {
|
|
685
|
+
this.db.exec('ROLLBACK TO axis_merge; RELEASE axis_merge')
|
|
686
|
+
throw cause
|
|
687
|
+
}
|
|
688
|
+
}
|
|
689
|
+
|
|
690
|
+
/** 合并轴实体时转移相关关系,并折叠重复边与自环。 */
|
|
691
|
+
private redirectRelations(fromId: string, intoId: string, now: string): void {
|
|
692
|
+
if (this.db === null) return
|
|
693
|
+
const rows = this.db.prepare('SELECT * FROM axis_relations WHERE from_id = ? OR to_id = ?').all(fromId, fromId) as Array<Record<string, unknown>>
|
|
694
|
+
if (rows.length === 0) return
|
|
695
|
+
this.db.prepare('DELETE FROM axis_relations WHERE from_id = ? OR to_id = ?').run(fromId, fromId)
|
|
696
|
+
const insert = this.db.prepare(`
|
|
697
|
+
INSERT INTO axis_relations (id, from_id, to_id, relation_type, evidence, created_at, updated_at)
|
|
698
|
+
VALUES (?, ?, ?, ?, ?, ?, ?)
|
|
699
|
+
ON CONFLICT(from_id, to_id, relation_type) DO UPDATE SET
|
|
700
|
+
evidence = CASE WHEN excluded.evidence <> '' THEN excluded.evidence ELSE axis_relations.evidence END,
|
|
701
|
+
updated_at = excluded.updated_at
|
|
702
|
+
`)
|
|
703
|
+
for (const row of rows) {
|
|
704
|
+
const oldFrom = String(row.from_id)
|
|
705
|
+
const oldTo = String(row.to_id)
|
|
706
|
+
const pair = [oldFrom === fromId ? intoId : oldFrom, oldTo === fromId ? intoId : oldTo]
|
|
707
|
+
if (pair[0] === pair[1]) continue
|
|
708
|
+
const [left, right] = pair.sort((a, b) => a.localeCompare(b))
|
|
709
|
+
const type = String(row.relation_type) as AxisRelationType
|
|
710
|
+
insert.run(axisRelationId(left, right, type), left, right, type, String(row.evidence ?? ''), String(row.created_at ?? now), now)
|
|
711
|
+
}
|
|
526
712
|
}
|
|
527
713
|
|
|
528
714
|
/* ─────────────────────────────────────────── 定义(0068) ── */
|
|
@@ -535,6 +721,7 @@ export class AxisRegistry {
|
|
|
535
721
|
const next = text.trim()
|
|
536
722
|
this.db.prepare('UPDATE axis_entities SET description = ?, description_source = ?, updated_at = ? WHERE id = ?')
|
|
537
723
|
.run(next, next ? 'human' : '', new Date().toISOString(), id)
|
|
724
|
+
this.changedInput()
|
|
538
725
|
return this.get(id)
|
|
539
726
|
}
|
|
540
727
|
|
|
@@ -625,6 +812,7 @@ export class AxisRegistry {
|
|
|
625
812
|
}
|
|
626
813
|
this.db.prepare('INSERT INTO entity_guidance (id, entity_id, kind, text, author, created_at) VALUES (?, ?, ?, ?, ?, ?)')
|
|
627
814
|
.run(guidance.id, guidance.entityId, guidance.kind, guidance.text, guidance.author, guidance.createdAt)
|
|
815
|
+
this.changedInput()
|
|
628
816
|
return guidance
|
|
629
817
|
}
|
|
630
818
|
|
|
@@ -634,6 +822,7 @@ export class AxisRegistry {
|
|
|
634
822
|
const before = this.db.prepare('SELECT id FROM entity_guidance WHERE id = ?').get(id) as { id?: unknown } | undefined
|
|
635
823
|
if (before === undefined) return false
|
|
636
824
|
this.db.prepare('DELETE FROM entity_guidance WHERE id = ?').run(id)
|
|
825
|
+
this.changedInput()
|
|
637
826
|
return true
|
|
638
827
|
}
|
|
639
828
|
|
|
@@ -722,7 +911,8 @@ export class AxisRegistry {
|
|
|
722
911
|
}
|
|
723
912
|
|
|
724
913
|
close(): void {
|
|
725
|
-
this.
|
|
914
|
+
this.owner?.close()
|
|
915
|
+
this.owner = null
|
|
726
916
|
this.db = null
|
|
727
917
|
}
|
|
728
918
|
|
|
@@ -736,11 +926,12 @@ export class AxisRegistry {
|
|
|
736
926
|
return hit === undefined ? undefined : rowToEntity(hit)
|
|
737
927
|
}
|
|
738
928
|
|
|
739
|
-
|
|
740
|
-
|
|
741
|
-
|
|
742
|
-
|
|
743
|
-
|
|
929
|
+
/** 作用域内的别名命中。**可能不止一条** —— 调用方必须自己判唯一性。 */
|
|
930
|
+
private findAliases(kind: AxisKind, normalized: string, scopeId: string): AxisEntity[] {
|
|
931
|
+
const rows = this.db!.prepare(
|
|
932
|
+
"SELECT e.* FROM axis_aliases a JOIN axis_entities e ON e.id = a.entity_id WHERE a.normalized = ? AND a.scope_id = ? AND e.kind = ? AND e.status <> 'merged' ORDER BY e.id",
|
|
933
|
+
).all(normalized, scopeId, kind) as Array<Record<string, unknown>>
|
|
934
|
+
return rows.map(rowToEntity)
|
|
744
935
|
}
|
|
745
936
|
|
|
746
937
|
/** 全局 canonical 名精确命中。它可能不止一个 —— 两个实体同名时就是歧义。 */
|
|
@@ -862,6 +1053,22 @@ function rowToEntity(row: Record<string, unknown>): AxisEntity {
|
|
|
862
1053
|
}
|
|
863
1054
|
}
|
|
864
1055
|
|
|
1056
|
+
function axisRelationId(fromId: string, toId: string, relationType: AxisRelationType): string {
|
|
1057
|
+
return `axis_rel_${createHash('sha256').update(`${relationType}:${fromId}:${toId}`).digest('hex').slice(0, 14)}`
|
|
1058
|
+
}
|
|
1059
|
+
|
|
1060
|
+
function rowToRelation(row: Record<string, unknown>): AxisRelation {
|
|
1061
|
+
return {
|
|
1062
|
+
id: String(row.id),
|
|
1063
|
+
fromId: String(row.from_id),
|
|
1064
|
+
toId: String(row.to_id),
|
|
1065
|
+
relationType: String(row.relation_type) as AxisRelationType,
|
|
1066
|
+
evidence: String(row.evidence ?? ''),
|
|
1067
|
+
createdAt: String(row.created_at),
|
|
1068
|
+
updatedAt: String(row.updated_at),
|
|
1069
|
+
}
|
|
1070
|
+
}
|
|
1071
|
+
|
|
865
1072
|
function rowToGuidance(row: Record<string, unknown>): AxisGuidance {
|
|
866
1073
|
return {
|
|
867
1074
|
id: String(row.id),
|
package/src/card-organize.ts
CHANGED
|
@@ -63,10 +63,10 @@ import { createHash } from 'node:crypto'
|
|
|
63
63
|
import type { CardKnowledgeRow } from './store/knowledge-db.js'
|
|
64
64
|
|
|
65
65
|
/** 组织方案的版本。它进 `layout_hash` —— 改提示词就该让已有的方案失效重排。 */
|
|
66
|
-
export const ORGANIZE_VERSION =
|
|
66
|
+
export const ORGANIZE_VERSION = 4
|
|
67
67
|
|
|
68
|
-
/**
|
|
69
|
-
export const MAX_SECTIONS =
|
|
68
|
+
/** 章节计划的资源上限,覆盖当前单次组织的 Knowledge 上限;不是预设目录数量。 */
|
|
69
|
+
export const MAX_SECTIONS = 200
|
|
70
70
|
|
|
71
71
|
/** 小节标题的字符上限。 */
|
|
72
72
|
export const MAX_SECTION_TITLE = 60
|
|
@@ -112,6 +112,9 @@ export interface OrganizeBlock {
|
|
|
112
112
|
}
|
|
113
113
|
|
|
114
114
|
export interface OrganizeSection {
|
|
115
|
+
sectionId?: string
|
|
116
|
+
retrievalNeed?: string
|
|
117
|
+
knowledgeIds?: string[]
|
|
115
118
|
title: string
|
|
116
119
|
blocks: OrganizeBlock[]
|
|
117
120
|
}
|
|
@@ -144,11 +147,12 @@ export interface OrganizeInput {
|
|
|
144
147
|
cardTitle: string
|
|
145
148
|
coordinate: { object: string; action: string; scene: string }
|
|
146
149
|
/** 这个坐标下的**全部**知识(库里已有的 ∪ 本轮新增的)。 */
|
|
147
|
-
knowledge: Array<{ id: string; text: string }>
|
|
150
|
+
knowledge: Array<{ id: string; text: string; sourceHeadings?: string[] }>
|
|
148
151
|
/**
|
|
149
152
|
* 卡片骨架允许的小节(类型定义里那套)。**是建议,不是白名单** ——
|
|
150
153
|
* 一个小节该不该存在,取决于这个坐标下真的有什么知识,而那是模型这一次才看到的。
|
|
151
154
|
*/
|
|
155
|
+
existingSections?: SectionRecord[]
|
|
152
156
|
suggestedSections?: string[]
|
|
153
157
|
/** 骨架里的建议小节。下发进提示词,好让模型优先用它们承载对应内容。 */
|
|
154
158
|
requiredSections?: string[]
|
|
@@ -178,70 +182,9 @@ export interface OrganizeCheck {
|
|
|
178
182
|
summaryCited: string[]
|
|
179
183
|
}
|
|
180
184
|
|
|
181
|
-
// ---------------------------------------------------------------- 提示词
|
|
182
185
|
|
|
183
|
-
/**
|
|
184
|
-
|
|
185
|
-
*
|
|
186
|
-
* 四条规则是这份提示词的全部:**只组织不创造**、**每条知识都要落地**、
|
|
187
|
-
* **每句都要挂引用**。其余全是输出形状。
|
|
188
|
-
*/
|
|
189
|
-
export function buildOrganizeSystem(): string {
|
|
190
|
-
return [
|
|
191
|
-
'你在把一批已经抽好的知识组织成一张 Wiki 卡片。',
|
|
192
|
-
'',
|
|
193
|
-
'这批知识属于同一个坐标(对象 × 动作 × 场景)。它们是**事实真源**,你不许新增、',
|
|
194
|
-
'删除或改动它们说的事实。你能做的是:分小节、定顺序、把它们改写成读得下去的',
|
|
195
|
-
'叙述、给卡片起一个自然的标题、写一句概述。',
|
|
196
|
-
'',
|
|
197
|
-
'## 三条规则',
|
|
198
|
-
'',
|
|
199
|
-
'1. **只组织,不创造。** 你写的每一句话,说的都必须是这批知识里已经有的事。',
|
|
200
|
-
' 你觉得「应该还有一步」而知识里没有 —— 那就不要写。',
|
|
201
|
-
'2. **每条知识都要落地。** 输入给了几条,正文里就必须用上几条,一条都不能丢。',
|
|
202
|
-
' 可以把两条合成一行表格、合成一个步骤 —— 但它们的 id 都要挂上。',
|
|
203
|
-
'3. **每句都要挂引用。** 陈述事实的句子末尾挂 `[^知识id]`,可以挂多个。',
|
|
204
|
-
' 只有很短的导语(「这一节讲取数流程」)可以不挂。长句子不挂引用 = 没有来源的',
|
|
205
|
-
' 断言,整份方案会被打回。',
|
|
206
|
-
'4. **标题只做归纳,不要机械拼接。** 标题要能让人一眼知道卡片讲什么;不要照抄候选卡名,',
|
|
207
|
-
' 不要写成「对象的动作」「动作的动作」这类重复结构,不要加入知识里没有的新事实。',
|
|
208
|
-
'',
|
|
209
|
-
'## 可以做的改写',
|
|
210
|
-
'',
|
|
211
|
-
'- 缩写、重排、把陈述句改成祈使句(「楼层 ID 会变」→「不要写死楼层 ID」)。',
|
|
212
|
-
'- 把两条同类的知识合成一张表或一个步骤。',
|
|
213
|
-
'- 把字段名、SQL、URL 放进反引号。',
|
|
214
|
-
'- 加很短的小节导语,把一节的内容串起来。',
|
|
215
|
-
'',
|
|
216
|
-
'## 输出形状',
|
|
217
|
-
'',
|
|
218
|
-
'只回一个 JSON 对象,不要散文,不要代码围栏。',
|
|
219
|
-
'',
|
|
220
|
-
'```json',
|
|
221
|
-
'{',
|
|
222
|
-
' "title": "简洁、自然、可检索的卡片标题,不要机械拼接对象和动作",',
|
|
223
|
-
' "summary": "一句话说清这张卡讲什么。挂引用,如 [^k_abc]。",',
|
|
224
|
-
' "sections": [',
|
|
225
|
-
' {',
|
|
226
|
-
' "title": "一、这一节讲什么",',
|
|
227
|
-
' "blocks": [',
|
|
228
|
-
' { "kind": "prose", "text": "一段话。[^k_abc]" },',
|
|
229
|
-
' { "kind": "steps", "items": ["第一步。[^k_def]", "第二步。[^k_ghi]"] },',
|
|
230
|
-
' { "kind": "bullets", "items": ["一个要点。[^k_jkl]"] },',
|
|
231
|
-
' { "kind": "table", "header": ["", "甲", "乙"], "rows": [["来源", "A[^k_mno]", "B[^k_pqr]"]] },',
|
|
232
|
-
' { "kind": "note", "text": "一句提醒。[^k_stu]" }',
|
|
233
|
-
' ]',
|
|
234
|
-
' }',
|
|
235
|
-
' ]',
|
|
236
|
-
'}',
|
|
237
|
-
'```',
|
|
238
|
-
'',
|
|
239
|
-
'`kind` 只能是这五个之一:`prose`(一段话)、`steps`(有序步骤)、`bullets`(无序要点)、',
|
|
240
|
-
'`table`(对比表)、`note`(提醒)。表格的每一行列数必须与表头一致。',
|
|
241
|
-
'',
|
|
242
|
-
'小节标题自己起,要具体(「二、取数流程」而不是「二、步骤」)。小节数量 1 到 12。',
|
|
243
|
-
].join('\n')
|
|
244
|
-
}
|
|
186
|
+
/** 组织阶段的 system 段搬到了 `organize-contract.ts`(说明页要显示它,而这里带着 node:crypto)。 */
|
|
187
|
+
export { buildOrganizeSystem } from './organize-contract.js'
|
|
245
188
|
|
|
246
189
|
/** 组织阶段的 user 提示词。知识清单在这里下发。 */
|
|
247
190
|
export function buildOrganizePrompt(input: OrganizeInput): string {
|
|
@@ -258,23 +201,25 @@ export function buildOrganizePrompt(input: OrganizeInput): string {
|
|
|
258
201
|
lines.push('')
|
|
259
202
|
|
|
260
203
|
if (input.suggestedSections !== undefined && input.suggestedSections.length > 0) {
|
|
261
|
-
lines.push('#
|
|
204
|
+
lines.push('# 历史章节参考(不是固定目录)')
|
|
262
205
|
lines.push('')
|
|
263
206
|
const required = new Set(input.requiredSections ?? [])
|
|
264
207
|
for (const section of input.suggestedSections) {
|
|
265
208
|
lines.push(`- ${section}${required.has(section) ? '(建议)' : ''}`)
|
|
266
209
|
}
|
|
267
210
|
lines.push('')
|
|
268
|
-
lines.push('
|
|
211
|
+
lines.push('不要为了复用旧标题而合并不同检索需求。请按本批内容决定章节。')
|
|
269
212
|
lines.push('')
|
|
270
213
|
}
|
|
271
214
|
|
|
215
|
+
if (input.existingSections?.length) lines.push('# 已有章节身份(相同检索需求保留 section_id)', JSON.stringify(input.existingSections), '')
|
|
272
216
|
lines.push(`# 这批知识(${input.knowledge.length} 条)`)
|
|
273
217
|
lines.push('')
|
|
274
218
|
lines.push('每条形如 `id :: 原文`。正文里的引用必须用这些 id。')
|
|
275
219
|
lines.push('')
|
|
276
220
|
for (const item of input.knowledge) {
|
|
277
221
|
lines.push(`- \`${item.id}\` :: ${item.text.replace(/\s+/g, ' ').trim()}`)
|
|
222
|
+
if (item.sourceHeadings?.length) lines.push(` 原文标题路径(定位线索):${[...new Set(item.sourceHeadings)].join(';')}`)
|
|
278
223
|
}
|
|
279
224
|
lines.push('')
|
|
280
225
|
|
|
@@ -460,7 +405,7 @@ export function parseOrganize(raw: string): { plan?: OrganizePlan; problems: Org
|
|
|
460
405
|
}
|
|
461
406
|
problems.push({ code: 'ORGANIZE_BLOCK_KIND', message: `小节「${title}」里有一个认不出的块类型「${kind}」` })
|
|
462
407
|
}
|
|
463
|
-
if (blocks.length > 0) sections.push({ title, blocks })
|
|
408
|
+
if (blocks.length > 0) sections.push({ title, blocks, ...(asString(section.section_id) ? { sectionId: asString(section.section_id) } : {}), ...(asString(section.retrieval_need) ? { retrievalNeed: asString(section.retrieval_need) } : {}), ...(asStringList(section.knowledge_ids) ? { knowledgeIds: asStringList(section.knowledge_ids)! } : {}) })
|
|
464
409
|
}
|
|
465
410
|
|
|
466
411
|
if (sections.length === 0) problems.push({ code: 'ORGANIZE_NO_USABLE_SECTION', message: '没有一个可用的小节' })
|
|
@@ -488,6 +433,8 @@ export function checkOrganize(
|
|
|
488
433
|
plan: OrganizePlan,
|
|
489
434
|
known: string[],
|
|
490
435
|
options: {
|
|
436
|
+
/** Check literal changes; this does not prove semantic entailment. */
|
|
437
|
+
knowledge?: Array<{ id: string; text: string }>
|
|
491
438
|
/**
|
|
492
439
|
* 正文里**可以**指向的其他卡片(`buildOrganizePrompt` 下发的那一份)。
|
|
493
440
|
*
|
|
@@ -499,14 +446,36 @@ export function checkOrganize(
|
|
|
499
446
|
} = {},
|
|
500
447
|
): OrganizeCheck {
|
|
501
448
|
const problems: OrganizeProblem[] = []
|
|
449
|
+
for (const section of plan.sections) {
|
|
450
|
+
if (/\n|^#/.test(section.title) || section.blocks.some(block => blockTexts(block).some(text => /^#{1,2} /m.test(text)))) problems.push({ code: 'ORGANIZE_NESTED_H2', message: '章节正文不能插入一级或二级标题' })
|
|
451
|
+
}
|
|
452
|
+
for (const section of plan.sections) {
|
|
453
|
+
if (section.knowledgeIds) {
|
|
454
|
+
const actual = [...new Set(section.blocks.flatMap(block => blockTexts(block).flatMap(citationsIn)))].sort()
|
|
455
|
+
if (JSON.stringify([...new Set(section.knowledgeIds)].sort()) !== JSON.stringify(actual)) problems.push({ code: 'ORGANIZE_PLACEMENT_MISMATCH', message: `章节「${section.title}」的 knowledge_ids 与正文引用不一致` })
|
|
456
|
+
}
|
|
457
|
+
}
|
|
502
458
|
const knownSet = new Set(known)
|
|
503
459
|
const cited: string[] = []
|
|
504
460
|
const seen = new Set<string>()
|
|
461
|
+
const byId = new Map(options.knowledge?.map(item => [item.id, item.text]) ?? [])
|
|
462
|
+
const literals = (text: string): string[] => [...text.replace(/\[\^[^\]]+\]/g, '').matchAll(/(?<![A-Za-z0-9_])(?:[<>≤≥]=?\s*)?[+-]?\d+(?:\.\d+)?(?:%|%)?/g)].map(match => match[0].replace(/\s/g, '').replace('%', '%'))
|
|
463
|
+
if (options.knowledge) {
|
|
464
|
+
const supported = new Set(literals(citationsIn(plan.summary).map(id => byId.get(id) ?? '').join('\n')))
|
|
465
|
+
const unsupported = literals(plan.summary).filter(value => !supported.has(value))
|
|
466
|
+
if (unsupported.length > 0) problems.push({ code: 'ORGANIZE_UNSUPPORTED_LITERAL', message: `概述的数值或比较条件没有引用依据:${[...new Set(unsupported)].join('、')}` })
|
|
467
|
+
}
|
|
505
468
|
|
|
506
469
|
// 1 + 2:正文里的引用必须都存在,且每条知识都要落地。
|
|
507
470
|
for (const section of plan.sections) {
|
|
508
471
|
for (const block of section.blocks) {
|
|
509
472
|
for (const text of blockTexts(block)) {
|
|
473
|
+
if (options.knowledge) {
|
|
474
|
+
const citedText = citationsIn(text).map(id => byId.get(id) ?? '').join('\n')
|
|
475
|
+
const supported = new Set(literals(citedText))
|
|
476
|
+
const unsupported = literals(text).filter(value => !supported.has(value))
|
|
477
|
+
if (unsupported.length > 0) problems.push({ code: 'ORGANIZE_UNSUPPORTED_LITERAL', message: `小节「${section.title}」的数值或比较条件没有引用依据:${[...new Set(unsupported)].join('、')}` })
|
|
478
|
+
}
|
|
510
479
|
for (const id of citationsIn(text)) {
|
|
511
480
|
if (!knownSet.has(id)) {
|
|
512
481
|
problems.push({ code: 'ORGANIZE_UNKNOWN_CITATION', message: `小节「${section.title}」引用了这批知识里没有的 id「${id}」` })
|
|
@@ -708,16 +677,17 @@ export function placementsOf(plan: OrganizePlan, cited: string[]): CardKnowledge
|
|
|
708
677
|
*
|
|
709
678
|
* 输入是「知识集合 + 骨架 + 提示词版本」—— 三者任一变了才重排。所以:
|
|
710
679
|
*
|
|
711
|
-
* -
|
|
712
|
-
* -
|
|
680
|
+
* - 知识与来源标题路径都没变 → 指纹相同 → **不调模型**,复用库里那份方案。
|
|
681
|
+
* - 知识或来源标题路径改变 → 指纹变化 → 重排这一张卡(也只重排这一张)。
|
|
713
682
|
*
|
|
714
683
|
* 少了它,每次编译都会把每张卡重排一遍:模型会给出**不一样**的排版,于是「重编一次、
|
|
715
684
|
* 没变的卡片不产生写入」这条增量承诺失效,而人看到的是卡片莫名其妙地换了样子。
|
|
716
685
|
*/
|
|
717
|
-
export function layoutHash(input: { knowledgeIds: string[]; profileId: string; version?: number }): string {
|
|
686
|
+
export function layoutHash(input: { knowledgeIds: string[]; profileId: string; knowledgeContext?: string[]; version?: number }): string {
|
|
718
687
|
const ids = [...input.knowledgeIds].sort()
|
|
688
|
+
const context = [...(input.knowledgeContext ?? [])].sort()
|
|
719
689
|
return createHash('sha256')
|
|
720
|
-
.update(`v${input.version ?? ORGANIZE_VERSION}\u0001${input.profileId}\u0001${ids.join('\u0001')}`, 'utf8')
|
|
690
|
+
.update(`v${input.version ?? ORGANIZE_VERSION}\u0001${input.profileId}\u0001${ids.join('\u0001')}\u0001${context.join('\u0001')}`, 'utf8')
|
|
721
691
|
.digest('hex')
|
|
722
692
|
.slice(0, 16)
|
|
723
693
|
}
|
|
@@ -734,12 +704,36 @@ export function layoutHash(input: { knowledgeIds: string[]; profileId: string; v
|
|
|
734
704
|
* `origin` 是 `'fallback'` 时**下次编译会重试**:模型不可用是暂时的,而一次失败不该
|
|
735
705
|
* 把这张卡永久钉在确定性布局上。代价是失败会重复花钱 —— 所以只在真的调用过之后才写。
|
|
736
706
|
*/
|
|
707
|
+
export interface SectionRecord {
|
|
708
|
+
id: string
|
|
709
|
+
title: string
|
|
710
|
+
retrievalNeed: string
|
|
711
|
+
knowledgeIds: string[]
|
|
712
|
+
}
|
|
713
|
+
|
|
714
|
+
/** Stable identity follows the retrieval need and supporting Knowledge, never section order. */
|
|
715
|
+
export function sectionRecords(plan: OrganizePlan, previous: SectionRecord[] = []): SectionRecord[] {
|
|
716
|
+
const used = new Set<string>()
|
|
717
|
+
return plan.sections.map(section => {
|
|
718
|
+
const knowledgeIds = [...new Set(section.blocks.flatMap(block => blockTexts(block).flatMap(citationsIn)))].sort()
|
|
719
|
+
const candidates = previous.filter(old => !used.has(old.id)).map(old => ({ old,
|
|
720
|
+
overlap: old.knowledgeIds.filter(id => knowledgeIds.includes(id)).length,
|
|
721
|
+
sameTitle: old.title === section.title,
|
|
722
|
+
})).filter(item => item.sameTitle || item.overlap > 0).sort((a, b) => Number(b.sameTitle) - Number(a.sameTitle) || b.overlap - a.overlap)
|
|
723
|
+
const matched = candidates.find(item => item.old.id === section.sectionId) ?? candidates[0]
|
|
724
|
+
const id = matched?.old.id ?? `sec_${createHash('sha256').update(JSON.stringify([knowledgeIds, section.retrievalNeed ?? section.title])).digest('hex').slice(0, 16)}`
|
|
725
|
+
used.add(id)
|
|
726
|
+
return { id, title: section.title, retrievalNeed: section.retrievalNeed ?? section.title, knowledgeIds }
|
|
727
|
+
})
|
|
728
|
+
}
|
|
729
|
+
|
|
737
730
|
export interface CardLayoutRecord {
|
|
738
731
|
/** 排序时用的骨架 id。骨架换了,小节名就该重排。 */
|
|
739
732
|
profile: string
|
|
740
733
|
/** `layoutHash()` 的结果。相同 = 知识集合没变 = 不重排。 */
|
|
741
734
|
hash: string
|
|
742
735
|
origin: 'model' | 'fallback'
|
|
736
|
+
sections?: SectionRecord[]
|
|
743
737
|
}
|
|
744
738
|
|
|
745
739
|
/**
|
|
@@ -776,7 +770,7 @@ export function layoutOf(metadata: Record<string, unknown> | undefined): CardLay
|
|
|
776
770
|
const profile = typeof record.profile === 'string' ? record.profile : ''
|
|
777
771
|
const origin = record.origin === 'model' || record.origin === 'fallback' ? record.origin : undefined
|
|
778
772
|
if (hash === '' || origin === undefined) return undefined
|
|
779
|
-
return { hash, profile, origin }
|
|
773
|
+
return { hash, profile, origin, ...(Array.isArray(record.sections) ? { sections: record.sections as SectionRecord[] } : {}) }
|
|
780
774
|
}
|
|
781
775
|
|
|
782
776
|
/**
|