noteconnection 1.7.0 → 1.9.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/LICENSE +674 -21
- package/README.md +260 -1626
- package/dist/src/agent_workspace.contract.parity.test.js +159 -0
- package/dist/src/agent_workspace.frontend.test.js +10388 -0
- package/dist/src/agent_workspace.locale.contract.test.js +95 -0
- package/dist/src/agent_workspace.runtime.behavior.test.js +5072 -0
- package/dist/src/android.knowledgebase.picker.contract.test.js +102 -0
- package/dist/src/appimage.patchelf.test.js +113 -0
- package/dist/src/appimage.portability.test.js +156 -0
- package/dist/src/backend/FileLoader.js +18 -4
- package/dist/src/backend/FileLoader.test.js +125 -0
- package/dist/src/backend/GraphBuilder.identity.test.js +86 -0
- package/dist/src/backend/GraphBuilder.js +66 -18
- package/dist/src/backend/GraphBuilder.matching.test.js +127 -0
- package/dist/src/backend/GraphMetrics.js +0 -3
- package/dist/src/backend/ResourceIdentity.js +155 -0
- package/dist/src/backend/ResourceIdentity.test.js +113 -0
- package/dist/src/backend/algorithms/CycleDetection.js +0 -1
- package/dist/src/backend/algorithms/HybridEngine.js +0 -3
- package/dist/src/backend/algorithms/LayoutEngine.js +0 -1
- package/dist/src/backend/algorithms/StatisticalAnalyzer.js +0 -4
- package/dist/src/backend/algorithms/TopologicalSort.js +0 -2
- package/dist/src/backend/build_dag.js +0 -1
- package/dist/src/capacitor.bridge.serialization.contract.test.js +3 -0
- package/dist/src/capacitor.runtime.contract.test.js +11 -2
- package/dist/src/copy.assets.contract.test.js +130 -0
- package/dist/src/core/Graph.js +138 -42
- package/dist/src/core/Graph.test.js +74 -0
- package/dist/src/core/NoteConnection.js +2 -2
- package/dist/src/core/PathBridge.js +293 -5
- package/dist/src/core/ResourceReference.js +17 -0
- package/dist/src/desktop.godot.pack.test.js +142 -0
- package/dist/src/desktop.runtime.graph.test.js +66 -0
- package/dist/src/export/WorkspaceExportBundle.js +1174 -0
- package/dist/src/export/WorkspaceExportBundle.test.js +1541 -0
- package/dist/src/export/index.js +18 -0
- package/dist/src/export/types.js +2 -0
- package/dist/src/fixrisk.issue.verifier.contract.test.js +3 -1
- package/dist/src/foundation.ann.runtime.contract.test.js +81 -0
- package/dist/src/foundation.release.evidence.contract.test.js +524 -0
- package/dist/src/foundation.sqlite.runtime.contract.test.js +88 -0
- package/dist/src/frontend/README.md +260 -1626
- package/dist/src/frontend/agent_workspace.js +6011 -0
- package/dist/src/frontend/agent_workspace_runtime.js +4434 -0
- package/dist/src/frontend/analysis.js +31 -4
- package/dist/src/frontend/app.js +2178 -62
- package/dist/src/frontend/focus_mode_interactions.js +33 -0
- package/dist/src/frontend/godot_future_path_renderer.js +540 -0
- package/dist/src/frontend/godot_tree_interactions.js +178 -0
- package/dist/src/frontend/graph_state.mjs +105 -0
- package/dist/src/frontend/hosted_future_path_runtime.js +157 -0
- package/dist/src/frontend/i18n.mjs +186 -0
- package/dist/src/frontend/index.html +672 -268
- package/dist/src/frontend/knowledge_projection_contract.js +230 -0
- package/dist/src/frontend/knowledge_projection_store.js +214 -0
- package/dist/src/frontend/layout_gpu.js +12 -4
- package/dist/src/frontend/libs/path_core.js +158 -42
- package/dist/src/frontend/locales/en.json +753 -5
- package/dist/src/frontend/locales/zh.json +753 -5
- package/dist/src/frontend/main.mjs +60 -0
- package/dist/src/frontend/markdown_runtime.js +827 -0
- package/dist/src/frontend/mobile_budget_runtime.js +33 -0
- package/dist/src/frontend/mobile_exact_analyzer.js +393 -0
- package/dist/src/frontend/mobile_identity_contract.js +221 -0
- package/dist/src/frontend/mobile_semantic_comparator.js +264 -0
- package/dist/src/frontend/notemd.css +49 -0
- package/dist/src/frontend/notemd.html +7 -1
- package/dist/src/frontend/notemd.js +64 -0
- package/dist/src/frontend/path.html +113 -2
- package/dist/src/frontend/path_app.js +2108 -130
- package/dist/src/frontend/path_layout.mjs +143 -0
- package/dist/src/frontend/path_mermaid_utils.mjs +108 -0
- package/dist/src/frontend/path_modules_bridge.js +486 -0
- package/dist/src/frontend/path_state.mjs +118 -0
- package/dist/src/frontend/path_styles.css +146 -0
- package/dist/src/frontend/path_worker_bridge.mjs +85 -0
- package/dist/src/frontend/reader.js +393 -33
- package/dist/src/frontend/runtime_bridge.js +67 -54
- package/dist/src/frontend/runtime_bridge.mjs +279 -0
- package/dist/src/frontend/settings.js +130 -12
- package/dist/src/frontend/simulationWorker.js +1 -1
- package/dist/src/frontend/source_manager.js +297 -22
- package/dist/src/frontend/storage_provider.js +661 -71
- package/dist/src/frontend/styles.css +2814 -72
- package/dist/src/frontend/workbench_state.mjs +101 -0
- package/dist/src/frontend/workspace_panes.js +10272 -0
- package/dist/src/frontend.locale.contract.test.js +62 -0
- package/dist/src/frontend.runtime.assets.contract.test.js +81 -0
- package/dist/src/godot.sidecar.bootstrap.contract.test.js +244 -0
- package/dist/src/graph.answer.operator.contract.test.js +72 -0
- package/dist/src/index.js +6 -2
- package/dist/src/index.knowledge-root.test.js +118 -0
- package/dist/src/indexing/IndexLifecycle.js +217 -0
- package/dist/src/indexing/IndexLifecycle.test.js +73 -0
- package/dist/src/indexing/SegmentBuilder.js +64 -0
- package/dist/src/indexing/UnitBuilder.js +48 -0
- package/dist/src/indexing/types.js +2 -0
- package/dist/src/knowledge.api.contract.test.js +172 -0
- package/dist/src/knowledge_projection.contract.test.js +134 -0
- package/dist/src/knowledge_projection_store.test.js +200 -0
- package/dist/src/learning/AnswerQualityEvaluation.js +153 -0
- package/dist/src/learning/AnswerQualityEvaluation.test.js +118 -0
- package/dist/src/learning/KnowledgeLearningPlatform.js +11181 -0
- package/dist/src/learning/KnowledgeLearningPlatform.persistence.test.js +745 -0
- package/dist/src/learning/KnowledgeLearningPlatform.program-f.test.js +99 -0
- package/dist/src/learning/KnowledgeLearningPlatform.test.js +3208 -0
- package/dist/src/learning/KnowledgeWorkspaceConversationRegression.js +2998 -0
- package/dist/src/learning/KnowledgeWorkspaceConversationRegression.test.js +3936 -0
- package/dist/src/learning/MeasurementEvidenceConflicts.test.js +78 -0
- package/dist/src/learning/PublicAnswerContract.test.js +85 -0
- package/dist/src/learning/PublicEvidenceSelection.test.js +94 -0
- package/dist/src/learning/PublicMeasurementDisclosure.test.js +54 -0
- package/dist/src/learning/agentConversationExecution.js +211 -0
- package/dist/src/learning/agentConversationExecution.test.js +209 -0
- package/dist/src/learning/agentConversationSerialization.js +266 -0
- package/dist/src/learning/agentConversationSerialization.test.js +165 -0
- package/dist/src/learning/agentResponseBudget.js +179 -0
- package/dist/src/learning/agentResponseBudget.test.js +97 -0
- package/dist/src/learning/agentWorkspaceCapabilityContracts.js +65 -0
- package/dist/src/learning/answerReleaseReview.js +5260 -0
- package/dist/src/learning/answerReleaseReview.test.js +4174 -0
- package/dist/src/learning/answerTaskPlan.js +473 -0
- package/dist/src/learning/api.js +2 -0
- package/dist/src/learning/conversationComposer.js +1721 -0
- package/dist/src/learning/conversationComposer.test.js +3220 -0
- package/dist/src/learning/domains/ConversationManager.js +53 -0
- package/dist/src/learning/domains/KnowledgeIngestor.js +238 -0
- package/dist/src/learning/domains/KnowledgeQuerier.js +187 -0
- package/dist/src/learning/domains/MasteryEngine.js +387 -0
- package/dist/src/learning/domains/MemoryPolicyManager.js +408 -0
- package/dist/src/learning/domains/QualityEvaluator.js +307 -0
- package/dist/src/learning/domains/TutorRouter.js +313 -0
- package/dist/src/learning/domains/index.js +33 -0
- package/dist/src/learning/domains/types.js +7 -0
- package/dist/src/learning/errors.js +29 -0
- package/dist/src/learning/evidenceContextAssembler.js +1368 -0
- package/dist/src/learning/evidenceContextAssembler.test.js +6332 -0
- package/dist/src/learning/graphAnswerCoverage.js +37 -0
- package/dist/src/learning/graphAnswerCoverage.test.js +93 -0
- package/dist/src/learning/graphAnswerCoverageCalibration.js +59 -0
- package/dist/src/learning/graphAnswerCoverageCalibration.test.js +46 -0
- package/dist/src/learning/graphAnswerFacts.js +94 -0
- package/dist/src/learning/graphAnswerFacts.test.js +39 -0
- package/dist/src/learning/graphAnswerPlan.js +908 -0
- package/dist/src/learning/graphAnswerPlan.test.js +907 -0
- package/dist/src/learning/graphAnswerQualityCalibration.js +255 -0
- package/dist/src/learning/graphAnswerQualityCalibration.test.js +72 -0
- package/dist/src/learning/graphAnswerQualityPolicy.js +30 -0
- package/dist/src/learning/graphClaimMatcher.js +104 -0
- package/dist/src/learning/graphConditionedContext.js +109 -0
- package/dist/src/learning/graphConditionedContext.test.js +99 -0
- package/dist/src/learning/graphContextAssembler.js +860 -0
- package/dist/src/learning/graphContextAssembler.test.js +1033 -0
- package/dist/src/learning/graphExpansionPolicy.js +22 -0
- package/dist/src/learning/graphExpansionPolicy.test.js +33 -0
- package/dist/src/learning/identityMetadata.test.js +26 -0
- package/dist/src/learning/index.js +29 -0
- package/dist/src/learning/mobileAnswerProjection.js +104 -0
- package/dist/src/learning/mobileAnswerProjection.test.js +102 -0
- package/dist/src/learning/queryBackend.js +1922 -0
- package/dist/src/learning/queryBackend.test.js +976 -0
- package/dist/src/learning/ragContextPack.js +356 -0
- package/dist/src/learning/ragContextPack.test.js +311 -0
- package/dist/src/learning/ragEvidenceQuality.js +96 -0
- package/dist/src/learning/ragEvidenceQuality.test.js +29 -0
- package/dist/src/learning/ragPublicText.js +101 -0
- package/dist/src/learning/ragPublicText.test.js +57 -0
- package/dist/src/learning/ragSufficiencyJudge.js +171 -0
- package/dist/src/learning/ragSufficiencyJudge.test.js +177 -0
- package/dist/src/learning/ragSufficiencyProviderJudge.js +217 -0
- package/dist/src/learning/ragSufficiencyProviderJudge.test.js +207 -0
- package/dist/src/learning/requestNormalization.js +304 -0
- package/dist/src/learning/requestNormalization.test.js +101 -0
- package/dist/src/learning/runtimeCapability.js +4677 -0
- package/dist/src/learning/runtimeCapability.test.js +3635 -0
- package/dist/src/learning/store.js +1344 -0
- package/dist/src/learning/store.test.js +1425 -0
- package/dist/src/learning/tutorAdapter.js +2 -0
- package/dist/src/learning/types.js +2 -0
- package/dist/src/learning/vectorAccelerationAdapter.js +958 -0
- package/dist/src/learning/vectorAccelerationAdapter.test.js +412 -0
- package/dist/src/learning/workspaceHydration.js +168 -0
- package/dist/src/learning/workspaceHydration.test.js +30 -0
- package/dist/src/lfs.asset.policy.contract.test.js +153 -0
- package/dist/src/license.policy.contract.test.js +71 -0
- package/dist/src/markdown/MarkdownGateway.js +4 -0
- package/dist/src/markdown/MarkdownGateway.test.js +59 -0
- package/dist/src/memory/MemoryGovernance.js +74 -0
- package/dist/src/memory/MemoryGovernance.test.js +46 -0
- package/dist/src/memory/types.js +2 -0
- package/dist/src/mermaid.frontend.guard.contract.test.js +77 -0
- package/dist/src/middleware/SseResponseWriter.js +122 -0
- package/dist/src/middleware/SseResponseWriter.test.js +54 -0
- package/dist/src/middleware/auth.js +38 -0
- package/dist/src/middleware/auth.test.js +33 -0
- package/dist/src/middleware/body-parser.js +45 -0
- package/dist/src/middleware/cors.js +44 -0
- package/dist/src/middleware/index.js +22 -0
- package/dist/src/middleware/request-trace.js +96 -0
- package/dist/src/mobile.android.evidence.contract.test.js +146 -0
- package/dist/src/mobile.android.signing.contract.test.js +66 -0
- package/dist/src/mobile.artifact.contract.test.js +144 -0
- package/dist/src/mobile.budget.contract.test.js +90 -0
- package/dist/src/mobile.canonical-id.readiness.test.js +54 -0
- package/dist/src/mobile.graph.parity.contract.test.js +207 -0
- package/dist/src/mobile.identity.contract.test.js +66 -0
- package/dist/src/mobile.identity.corpus.manifest.test.js +72 -0
- package/dist/src/mobile.identity.corpus.test.js +40 -0
- package/dist/src/mobile.native.recovery.contract.test.js +64 -0
- package/dist/src/mobile.pipeline.test.js +11 -1
- package/dist/src/mobile.profile.contract.test.js +225 -0
- package/dist/src/mobile.semantic.parity.test.js +92 -0
- package/dist/src/mobile_exact_analyzer.test.js +113 -0
- package/dist/src/notemd/AppConfigToml.js +6 -4
- package/dist/src/notemd/ContentGenerator.js +4 -9
- package/dist/src/notemd/FileProcessor.js +4 -9
- package/dist/src/notemd/MermaidProcessor.js +400 -50
- package/dist/src/notemd/NotemdService.js +506 -26
- package/dist/src/notemd/PromptManager.js +15 -0
- package/dist/src/notemd/Translator.js +4 -9
- package/dist/src/notemd/cli/commands.js +357 -0
- package/dist/src/notemd/cli/dispatcher.js +225 -0
- package/dist/src/notemd/cli/index.js +169 -0
- package/dist/src/notemd/cli/parser.js +68 -0
- package/dist/src/notemd/cli/types.js +2 -0
- package/dist/src/notemd/constants.js +43 -0
- package/dist/src/notemd/diagram/diagramGenerationService.js +78 -0
- package/dist/src/notemd/diagram/diagramSpec.js +79 -0
- package/dist/src/notemd/diagram/diagramSpecResponseParser.js +131 -0
- package/dist/src/notemd/diagram/intent.js +95 -0
- package/dist/src/notemd/diagram/planner.js +71 -0
- package/dist/src/notemd/diagram/prompts/diagramSpecPrompt.js +42 -0
- package/dist/src/notemd/diagram/types.js +18 -0
- package/dist/src/notemd/index.js +26 -0
- package/dist/src/notemd/operations/capabilityManifest.js +23 -0
- package/dist/src/notemd/operations/cliContracts.js +17 -0
- package/dist/src/notemd/operations/configProfileCommands.js +85 -0
- package/dist/src/notemd/operations/registry.contract.test.js +95 -0
- package/dist/src/notemd/operations/registry.js +991 -0
- package/dist/src/notemd/operations/types.js +2 -0
- package/dist/src/notemd/progressReporter.js +9 -0
- package/dist/src/notemd/providerDiagnostics.js +220 -0
- package/dist/src/notemd/providerProfiles.js +42 -0
- package/dist/src/notemd/providerTemplates.js +231 -0
- package/dist/src/notemd/search/DuckDuckGoProvider.js +39 -0
- package/dist/src/notemd/search/SearchManager.js +13 -0
- package/dist/src/notemd/search/SearchProvider.js +2 -0
- package/dist/src/notemd/search/TavilyProvider.js +44 -0
- package/dist/src/notemd.agent.manifest.test.js +85 -0
- package/dist/src/notemd.api.contract.test.js +14 -1
- package/dist/src/notemd.app_config_toml.test.js +2 -1
- package/dist/src/notemd.batch.workflow.test.js +117 -0
- package/dist/src/notemd.cli.e2e.test.js +136 -0
- package/dist/src/notemd.core.test.js +51 -0
- package/dist/src/notemd.diagram.pipeline.test.js +233 -0
- package/dist/src/notemd.providerTemplates.test.js +34 -0
- package/dist/src/notemd.server.integration.test.js +262 -39
- package/dist/src/notemd.workflow.pipeline.test.js +162 -0
- package/dist/src/pathbridge.handshake.contract.test.js +45 -2
- package/dist/src/pathbridge.host-adapter.contract.test.js +133 -0
- package/dist/src/pathcore.bundle.contract.test.js +8 -0
- package/dist/src/pathmode.background.contract.test.js +69 -0
- package/dist/src/pathmode.settings.api.contract.test.js +9 -0
- package/dist/src/pkg.sidecar.contract.test.js +9 -3
- package/dist/src/platform/ExportProfile.js +86 -0
- package/dist/src/platform/MobileBudget.js +11 -0
- package/dist/src/platform/PlatformCapabilities.js +59 -0
- package/dist/src/platform/PlatformCapabilities.test.js +36 -0
- package/dist/src/platform/RenderMaterializer.js +33 -0
- package/dist/src/platform/RenderMaterializer.test.js +32 -0
- package/dist/src/query_backend.external_http.integration.test.js +410 -0
- package/dist/src/reader_renderer.js +404 -3
- package/dist/src/reader_renderer.test.js +87 -0
- package/dist/src/release.godot.mirror.contract.test.js +97 -0
- package/dist/src/resources/ResourceRegistry.js +263 -0
- package/dist/src/resources/ResourceRegistry.test.js +97 -0
- package/dist/src/resources/types.js +2 -0
- package/dist/src/routes/agentWorkspaceDiagnostics.js +173 -0
- package/dist/src/routes/index.js +19 -0
- package/dist/src/routes/knowledge.js +981 -0
- package/dist/src/routes/knowledgePayload.js +185 -0
- package/dist/src/routes/knowledgePayload.test.js +53 -0
- package/dist/src/routes/markdown.js +295 -0
- package/dist/src/routes/notemd.js +1321 -0
- package/dist/src/routes/registry.contract.test.js +133 -0
- package/dist/src/routes/registry.shadow.contract.test.js +22 -0
- package/dist/src/routes/render.js +248 -0
- package/dist/src/routes/runtimeRunbookRouteOps.js +149 -0
- package/dist/src/routes/runtimeRunbookRouteOps.test.js +194 -0
- package/dist/src/routes/settings.js +6 -0
- package/dist/src/routes/staticFiles.js +94 -0
- package/dist/src/routes/types.js +2 -0
- package/dist/src/runtime.capabilities.test.js +18 -2
- package/dist/src/runtime.transport.adapter.contract.test.js +81 -0
- package/dist/src/server.js +10342 -1325
- package/dist/src/server.migration.test.js +584 -10
- package/dist/src/server.port.fallback.contract.test.js +63 -0
- package/dist/src/session/SessionStateStore.js +81 -0
- package/dist/src/session/SessionStateStore.test.js +58 -0
- package/dist/src/session/types.js +2 -0
- package/dist/src/settings.runtime.contract.test.js +50 -0
- package/dist/src/shared/types.contract.test.js +107 -0
- package/dist/src/shared/types.js +22 -0
- package/dist/src/sidecar.build.lock.contract.test.js +201 -0
- package/dist/src/sidecar.freshness.contract.test.js +184 -0
- package/dist/src/sidecar.replacement.boundary.contract.test.js +131 -0
- package/dist/src/sidecar.supply.readiness.contract.test.js +155 -0
- package/dist/src/simulation.worker.offline.test.js +104 -0
- package/dist/src/source_manager.loadflow.test.js +46 -0
- package/dist/src/startup.layout.snapshot.contract.test.js +57 -0
- package/dist/src/storage.provider.capacitor.content.contract.test.js +4 -1
- package/dist/src/storage.provider.contract.test.js +152 -1
- package/dist/src/tauri.frontend.build.contract.test.js +68 -0
- package/dist/src/tauri.sidecar.cleanup.contract.test.js +21 -0
- package/dist/src/tauri.window.evidence.execution.test.js +68 -0
- package/dist/src/utils/RuntimePaths.js +10 -13
- package/dist/src/utils/RuntimePaths.test.js +27 -0
- package/dist/src/utils/platform.js +153 -0
- package/dist/src/windows.installer.build.test.js +109 -0
- package/dist/src/windows.installer.payload.test.js +76 -0
- package/dist/src/windows.installer.runtime.test.js +69 -0
- package/dist/src/workflows/WorkflowArtifactStore.js +96 -0
- package/dist/src/workflows/WorkflowArtifactStore.test.js +80 -0
- package/dist/src/workflows/types.js +2 -0
- package/dist/src/workspace/WorkspaceRegistry.js +135 -0
- package/dist/src/workspace/WorkspaceRegistry.test.js +29 -0
- package/dist/src/workspace/types.js +2 -0
- package/package.json +80 -15
|
@@ -0,0 +1,908 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/// <reference lib="es2022.intl" />
|
|
3
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
4
|
+
exports.selectQueryConnectedKnowledgePoints = selectQueryConnectedKnowledgePoints;
|
|
5
|
+
exports.buildGraphAnswerPlan = buildGraphAnswerPlan;
|
|
6
|
+
const ragPublicText_1 = require("./ragPublicText");
|
|
7
|
+
const graphClaimMatcher_1 = require("./graphClaimMatcher");
|
|
8
|
+
const graphAnswerQualityPolicy_1 = require("./graphAnswerQualityPolicy");
|
|
9
|
+
const ragEvidenceQuality_1 = require("./ragEvidenceQuality");
|
|
10
|
+
const answerTaskPlan_1 = require("./answerTaskPlan");
|
|
11
|
+
function normalize(value) {
|
|
12
|
+
return String(value || '').replace(/\s+/g, ' ').trim();
|
|
13
|
+
}
|
|
14
|
+
function isCompoundLearningDefinitionQuery(message) {
|
|
15
|
+
const normalized = normalize(message).toLowerCase();
|
|
16
|
+
return classifyIntent(normalized) === 'definition'
|
|
17
|
+
&& (/\b(?:learn|learning|study|knowledge\s+points?)\b/u.test(normalized)
|
|
18
|
+
|| /学习|知识点|学哪些|通过哪些/u.test(normalized));
|
|
19
|
+
}
|
|
20
|
+
function hasBalancedMathDelimiters(value) {
|
|
21
|
+
const source = String(value || '');
|
|
22
|
+
const displayCount = (source.match(/(?<!\\)\$\$/gu) || []).length;
|
|
23
|
+
if (displayCount % 2 !== 0) {
|
|
24
|
+
return false;
|
|
25
|
+
}
|
|
26
|
+
const inlineSource = source.replace(/(?<!\\)\$\$/gu, '');
|
|
27
|
+
return ((inlineSource.match(/(?<!\\)\$/gu) || []).length % 2) === 0;
|
|
28
|
+
}
|
|
29
|
+
function normalizedTopicWords(value) {
|
|
30
|
+
return normalize(value).toLowerCase().replace(/\b([a-z]{4,})s\b/gu, (word, base) => (/(?:ss|us|is)$/u.test(word) ? word : base));
|
|
31
|
+
}
|
|
32
|
+
function containsNamedChineseSubject(source, subject) {
|
|
33
|
+
let offset = source.indexOf(subject);
|
|
34
|
+
while (offset >= 0) {
|
|
35
|
+
if (offset === 0 || !/[无非不未]/u.test(source[offset - 1]))
|
|
36
|
+
return true;
|
|
37
|
+
offset = source.indexOf(subject, offset + 1);
|
|
38
|
+
}
|
|
39
|
+
return false;
|
|
40
|
+
}
|
|
41
|
+
function knowledgePointEstablishesSubject(point, subject) {
|
|
42
|
+
const topic = normalize(subject).replace(/^(?:a|an|the)\s+/iu, '').replace(/[.?!。?!]+$/gu, '').trim();
|
|
43
|
+
if (!topic)
|
|
44
|
+
return false;
|
|
45
|
+
const compact = (value) => normalizedTopicWords(value).replace(/[^\p{L}\p{N}]+/gu, '');
|
|
46
|
+
const aliases = [point.title, ...String(point.title || '').split(/[()()]/u)];
|
|
47
|
+
if (aliases.some(alias => compact(alias) === compact(topic)))
|
|
48
|
+
return true;
|
|
49
|
+
const source = normalizedTopicWords([
|
|
50
|
+
point.title, point.summary, point.evidenceSnippet,
|
|
51
|
+
...(point.matchedSpans || []).flatMap(span => [span.title, span.snippet]),
|
|
52
|
+
].filter(Boolean).join('\n'));
|
|
53
|
+
const normalizedTopic = normalizedTopicWords(topic);
|
|
54
|
+
if (/\p{Script=Han}/u.test(normalizedTopic)) {
|
|
55
|
+
// Negated entity names (for example 无缓冲通道) do not establish the positive entity.
|
|
56
|
+
return containsNamedChineseSubject(source, normalizedTopic);
|
|
57
|
+
}
|
|
58
|
+
const subjectFeatures = (0, graphClaimMatcher_1.semanticFeatures)(normalizedTopic);
|
|
59
|
+
const sourceFeatures = new Set((0, graphClaimMatcher_1.semanticFeatures)(source));
|
|
60
|
+
return subjectFeatures.length > 0 && subjectFeatures.every(feature => sourceFeatures.has(feature));
|
|
61
|
+
}
|
|
62
|
+
/** Source admission precedes RAG reads so citations, graph anchoring and composition share one decision. */
|
|
63
|
+
function selectQueryConnectedKnowledgePoints(message, knowledgePoints) {
|
|
64
|
+
const subjects = classifyIntent(message) === 'compare'
|
|
65
|
+
? comparisonQueryBranches(message)
|
|
66
|
+
: comparisonQueryBranches((0, answerTaskPlan_1.extractDefinitionQuerySubject)(message));
|
|
67
|
+
const requestedSubjects = subjects.filter(Boolean);
|
|
68
|
+
if (requestedSubjects.length === 0)
|
|
69
|
+
return knowledgePoints;
|
|
70
|
+
const subjectMatches = requestedSubjects.map(subject => knowledgePoints.filter(point => knowledgePointEstablishesSubject(point, subject)));
|
|
71
|
+
// An unmatched phrase may describe a comparison aspect or graph chain. Partial
|
|
72
|
+
// recognition must not discard that branch's graph support before intent-aware assembly.
|
|
73
|
+
if (subjectMatches.some(matches => matches.length === 0))
|
|
74
|
+
return knowledgePoints;
|
|
75
|
+
const selected = new Set(subjectMatches.flat());
|
|
76
|
+
return knowledgePoints.filter(point => selected.has(point));
|
|
77
|
+
}
|
|
78
|
+
function definitionTitleMatchesSubject(title, subject) {
|
|
79
|
+
const normalizeTitle = (value) => normalize(value)
|
|
80
|
+
.replace(/\s*\((?:mermaid|code|diagram|formula)\s+block\)\s*$/iu, '')
|
|
81
|
+
.replace(/[^\p{L}\p{N}\s_-]+/gu, ' ')
|
|
82
|
+
.replace(/\s+/gu, '')
|
|
83
|
+
.toLowerCase();
|
|
84
|
+
const normalizedSubject = normalizeTitle(subject);
|
|
85
|
+
const normalizedTitle = normalizeTitle(title);
|
|
86
|
+
return Boolean(normalizedSubject && normalizedTitle && (normalizedTitle === normalizedSubject
|
|
87
|
+
|| normalizedTitle.includes(normalizedSubject)));
|
|
88
|
+
}
|
|
89
|
+
function isDefinitionLearningSupportSpan(span, subject) {
|
|
90
|
+
const title = normalize(String(span.title || ''));
|
|
91
|
+
if (!title || /preamble|reference|参考文献|比较模型|comparison|mermaid|code|diagram/iu.test(title)) {
|
|
92
|
+
return false;
|
|
93
|
+
}
|
|
94
|
+
if (definitionTitleMatchesSubject(title, subject)) {
|
|
95
|
+
return true;
|
|
96
|
+
}
|
|
97
|
+
// A compound definition request may include a bounded learning bridge, but
|
|
98
|
+
// chapter titles such as applications, specifications, and comparisons are
|
|
99
|
+
// not definition evidence merely because their text contains a formula.
|
|
100
|
+
return /核心概念|数学|基础|key\s+concept|mathemat|prerequisite|前置|先修|learning\s+path|学习路径/iu.test(title);
|
|
101
|
+
}
|
|
102
|
+
function extractMathExpressions(value) {
|
|
103
|
+
const source = String(value || '');
|
|
104
|
+
const expressions = [];
|
|
105
|
+
for (const match of source.matchAll(/(?<!\\)\$\$([\s\S]*?)(?<!\\)\$\$/gu)) {
|
|
106
|
+
const expression = normalize(String(match[1] || '')).replace(/\s+/gu, '');
|
|
107
|
+
if (expression) {
|
|
108
|
+
expressions.push(expression);
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
const inlineSource = source.replace(/(?<!\\)\$\$([\s\S]*?)(?<!\\)\$\$/gu, '');
|
|
112
|
+
for (const match of inlineSource.matchAll(/(?<!\\)\$([^$\n]+?)(?<!\\)\$/gu)) {
|
|
113
|
+
const expression = normalize(String(match[1] || '')).replace(/\s+/gu, '');
|
|
114
|
+
if (expression) {
|
|
115
|
+
expressions.push(expression);
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
return Array.from(new Set(expressions));
|
|
119
|
+
}
|
|
120
|
+
function statementCarriesMathDelimiter(value) {
|
|
121
|
+
const normalized = normalize(value);
|
|
122
|
+
return /(?<!\\)\$\$[\s\S]+?(?<!\\)\$\$|(?<!\\)\$[^$\n]+?(?<!\\)\$/u.test(normalized);
|
|
123
|
+
}
|
|
124
|
+
function isCompoundDefinitionNoiseClause(value) {
|
|
125
|
+
const normalized = normalize(value);
|
|
126
|
+
if (!normalized) {
|
|
127
|
+
return true;
|
|
128
|
+
}
|
|
129
|
+
return /(?:常见用例|应用场景|关键技术规格|技术规格|性能指标|性能特征|比较数学模型|技术比较|流体容器技术比较|本节|本章|下表|参数\s*\(Parameter\)|preamble|reference|comparison|application|use\s+case|technical\s+specification|performance\s+characteristic|table|mermaid|diagram|code\s+block)/iu.test(normalized)
|
|
130
|
+
|| /```|\|\s*[^|]+\s*\|/u.test(normalized);
|
|
131
|
+
}
|
|
132
|
+
function isDefinitionNoiseTitle(value, message) {
|
|
133
|
+
const normalized = normalize(value);
|
|
134
|
+
if (!normalized) {
|
|
135
|
+
return true;
|
|
136
|
+
}
|
|
137
|
+
const compactTitle = (title) => normalizedTopicWords(title).replace(/[^\p{L}\p{N}]+/gu, '');
|
|
138
|
+
const subject = compactTitle((0, answerTaskPlan_1.extractDefinitionQuerySubject)(message));
|
|
139
|
+
if (subject && [normalized, ...normalized.split(/[()()]/u)].some(title => compactTitle(title) === subject)) {
|
|
140
|
+
return false;
|
|
141
|
+
}
|
|
142
|
+
// Metadata words must be words: "table" must not discard an Immutable/Stable source.
|
|
143
|
+
return /\b(?:preambles?|references?|comparisons?|compare|use\s+case|application\s+scenario|technical\s+specification|performance\s+(?:metric|characteristic)|related\s+technology|mermaid|code\s+block|tables?)\b|参考文献|参考资料|比较|对比|常见用例|应用场景|关键技术规格|技术规格|性能指标|性能特征|统计度量|相关技术|表格/iu.test(normalized.replace(/[_-]+/gu, ' '));
|
|
144
|
+
}
|
|
145
|
+
function isDefinitionNoiseClaim(value) {
|
|
146
|
+
const normalized = normalize(value);
|
|
147
|
+
return !normalized || /^\d+(?:\.\d+)*[.、)]?$/u.test(normalized);
|
|
148
|
+
}
|
|
149
|
+
function isCompoundDefinitionMathClause(value) {
|
|
150
|
+
const normalized = normalize(value);
|
|
151
|
+
return statementCarriesMathDelimiter(normalized)
|
|
152
|
+
|| /(?:径向分布|分布函数|数学|公式|方程|函数|描述|定义|radial\s+distribution|equation|formula|function|mathemat(?:ics|ical))/iu.test(normalized);
|
|
153
|
+
}
|
|
154
|
+
function selectCompoundDefinitionPublicClaimStatements(candidates) {
|
|
155
|
+
const eligibleCandidates = candidates
|
|
156
|
+
.filter((candidate) => !candidate.incompleteEnding)
|
|
157
|
+
.filter((candidate) => !isCompoundDefinitionNoiseClause(candidate.clause));
|
|
158
|
+
if (eligibleCandidates.length <= 0) {
|
|
159
|
+
return [];
|
|
160
|
+
}
|
|
161
|
+
// A compound definition has two bounded deliverables: the subject definition
|
|
162
|
+
// and a small mathematical/learning bridge. It must not inherit every clause
|
|
163
|
+
// from a full-document fragment merely because the subject is repeated in
|
|
164
|
+
// later application or comparison sections.
|
|
165
|
+
const selected = [];
|
|
166
|
+
const append = (candidate) => {
|
|
167
|
+
if (selected.length >= 4 || selected.includes(candidate)) {
|
|
168
|
+
return;
|
|
169
|
+
}
|
|
170
|
+
if (selected.some((entry) => normalize(entry.clause).toLowerCase() === normalize(candidate.clause).toLowerCase())) {
|
|
171
|
+
return;
|
|
172
|
+
}
|
|
173
|
+
selected.push(candidate);
|
|
174
|
+
};
|
|
175
|
+
const subjectCandidates = eligibleCandidates.filter((candidate) => candidate.anchorMatches > 0);
|
|
176
|
+
append(subjectCandidates[0] || eligibleCandidates[0]);
|
|
177
|
+
eligibleCandidates
|
|
178
|
+
.filter((candidate) => isCompoundDefinitionMathClause(candidate.clause))
|
|
179
|
+
.sort((left, right) => left.order - right.order)
|
|
180
|
+
.forEach(append);
|
|
181
|
+
return uniquePublicClaimStatements(selected.sort((left, right) => left.order - right.order));
|
|
182
|
+
}
|
|
183
|
+
function publicClaimAnchorFeatures(message, title) {
|
|
184
|
+
return new Set((0, graphClaimMatcher_1.semanticFeatures)(`${message} ${title || ''}`));
|
|
185
|
+
}
|
|
186
|
+
function comparisonQueryBranches(message) {
|
|
187
|
+
const normalizedMessage = normalize(message)
|
|
188
|
+
.toLowerCase()
|
|
189
|
+
.replace(/^(?:compare|contrast|difference\s+between)\s+/u, '')
|
|
190
|
+
.replace(/^(?:比较|对比)\s*/u, '');
|
|
191
|
+
return normalizedMessage
|
|
192
|
+
.split(/\s+(?:and|with|versus|vs\.?)\s+|\s*(?:与|和|跟)\s*/u)
|
|
193
|
+
.map((branch) => normalize(branch).replace(/^(?:a|an|the)\s+/iu, '').replace(/[.?!。?!]+$/gu, ''))
|
|
194
|
+
.filter(Boolean);
|
|
195
|
+
}
|
|
196
|
+
function comparisonIdentityMatchCount(text, requiredTerms) {
|
|
197
|
+
const normalized = normalize(text).toLowerCase();
|
|
198
|
+
const words = new Set(normalized.match(/[\p{L}\p{N}]+/gu) || []);
|
|
199
|
+
return Array.from(requiredTerms).filter(term => /\p{Script=Han}/u.test(term)
|
|
200
|
+
? containsNamedChineseSubject(normalized, term)
|
|
201
|
+
: words.has(term)).length;
|
|
202
|
+
}
|
|
203
|
+
function comparisonBranchFeatures(message) {
|
|
204
|
+
const branches = comparisonQueryBranches(message)
|
|
205
|
+
.map((branch) => new Set((0, graphClaimMatcher_1.semanticFeatures)(branch)))
|
|
206
|
+
.filter((features) => features.size > 0);
|
|
207
|
+
if (branches.length !== 2) {
|
|
208
|
+
return [];
|
|
209
|
+
}
|
|
210
|
+
const sharedFeatures = new Set(Array.from(branches[0]).filter((feature) => branches[1].has(feature)));
|
|
211
|
+
return branches.map((features) => {
|
|
212
|
+
const branchSpecificFeatures = new Set(Array.from(features).filter((feature) => !sharedFeatures.has(feature)));
|
|
213
|
+
return branchSpecificFeatures.size > 0 ? branchSpecificFeatures : features;
|
|
214
|
+
});
|
|
215
|
+
}
|
|
216
|
+
function comparisonBranchIdentityTerms(message) {
|
|
217
|
+
const branches = comparisonQueryBranches(message)
|
|
218
|
+
.map((branch) => new Set((branch.match(/[\p{L}\p{N}]+/gu) || [])
|
|
219
|
+
.map((term) => term.toLowerCase())
|
|
220
|
+
.filter(Boolean)))
|
|
221
|
+
.filter((terms) => terms.size > 0);
|
|
222
|
+
if (branches.length !== 2) {
|
|
223
|
+
return [];
|
|
224
|
+
}
|
|
225
|
+
const sharedTerms = new Set(Array.from(branches[0]).filter((term) => branches[1].has(term)));
|
|
226
|
+
return branches.map((terms) => {
|
|
227
|
+
const branchSpecificTerms = new Set(Array.from(terms).filter((term) => !sharedTerms.has(term)));
|
|
228
|
+
return branchSpecificTerms.size > 0 ? branchSpecificTerms : terms;
|
|
229
|
+
});
|
|
230
|
+
}
|
|
231
|
+
function isTitleDependentComparisonClause(clause) {
|
|
232
|
+
return /^(?:(?:it|its|they|their|this|these|such|the former|the latter)\b|(?:其|它|该|这些|这种|前者|后者))/iu.test(normalize(clause));
|
|
233
|
+
}
|
|
234
|
+
function rankQualityPublicClaimStatements(value, title, message) {
|
|
235
|
+
const rawEvidence = String(value || '');
|
|
236
|
+
if (!normalize(rawEvidence)) {
|
|
237
|
+
return [];
|
|
238
|
+
}
|
|
239
|
+
const normalizedEvidence = (0, ragPublicText_1.naturalizeRagPublicEvidenceClause)(rawEvidence);
|
|
240
|
+
const normalizedTitle = normalize(String(title || '').replace(/\s*\((?:mermaid|code|diagram)\s+block\)\s*$/iu, ''));
|
|
241
|
+
const titleMatchVariants = Array.from(new Set([
|
|
242
|
+
normalizedTitle,
|
|
243
|
+
normalizedTitle.replace(/^\d+(?:\.\d+)*[.、)]?\s*/u, ''),
|
|
244
|
+
].filter(Boolean)));
|
|
245
|
+
const anchorFeatures = publicClaimAnchorFeatures(message, normalizedTitle);
|
|
246
|
+
const compareBranches = comparisonBranchFeatures(message);
|
|
247
|
+
const compareBranchIdentities = comparisonBranchIdentityTerms(message);
|
|
248
|
+
const segmentSource = classifyIntent(message) === 'definition'
|
|
249
|
+
&& /(?:^|\r?\n)\s*#{1,6}\s+/u.test(rawEvidence)
|
|
250
|
+
? rawEvidence
|
|
251
|
+
: normalizedEvidence;
|
|
252
|
+
const candidates = [
|
|
253
|
+
// Segment before whitespace normalization so Markdown headings and
|
|
254
|
+
// display-math blocks remain independent evidence units. Normalizing
|
|
255
|
+
// first would merge an entire document section into one claim.
|
|
256
|
+
...(0, ragEvidenceQuality_1.segmentRagEvidenceClauses)(segmentSource),
|
|
257
|
+
...(0, ragPublicText_1.extractPublicMermaidEvidenceLabels)(value),
|
|
258
|
+
]
|
|
259
|
+
.map((clause) => (0, ragPublicText_1.naturalizeRagPublicEvidenceClause)(clause))
|
|
260
|
+
.map((clause) => {
|
|
261
|
+
const matchingTitle = titleMatchVariants.find((variant) => (clause.toLowerCase().startsWith(`${variant.toLowerCase()} `)));
|
|
262
|
+
if (!matchingTitle) {
|
|
263
|
+
return clause;
|
|
264
|
+
}
|
|
265
|
+
const remainder = clause.slice(matchingTitle.length).trim();
|
|
266
|
+
const repeatsTitle = remainder.toLowerCase().startsWith(`${matchingTitle.toLowerCase()} `)
|
|
267
|
+
|| remainder.toLowerCase().startsWith(matchingTitle.toLowerCase());
|
|
268
|
+
const titleWordCount = matchingTitle.split(/\s+/u).filter(Boolean).length;
|
|
269
|
+
const structuralHeading = /^\d+(?:\.\d+)*[.、)]?\s*/u.test(normalizedTitle)
|
|
270
|
+
|| /[::]/u.test(normalizedTitle)
|
|
271
|
+
|| (/[\u3400-\u9fff]/u.test(normalizedTitle) && normalizedTitle.length >= 8);
|
|
272
|
+
return structuralHeading
|
|
273
|
+
|| repeatsTitle
|
|
274
|
+
|| (titleWordCount > 1 && /^(?:a|an|the)\s+/iu.test(remainder))
|
|
275
|
+
? remainder
|
|
276
|
+
: clause;
|
|
277
|
+
})
|
|
278
|
+
.filter((clause) => (clause
|
|
279
|
+
&& !(0, ragPublicText_1.shouldRejectPublicEvidenceClause)(clause)
|
|
280
|
+
&& !(0, ragPublicText_1.shouldRejectCompareProcedureEvidenceClause)(clause, message)))
|
|
281
|
+
.map((clause, order) => {
|
|
282
|
+
const clauseFeatures = new Set((0, graphClaimMatcher_1.semanticFeatures)(clause));
|
|
283
|
+
const anchorMatches = Array.from(clauseFeatures)
|
|
284
|
+
.filter((feature) => anchorFeatures.has(feature))
|
|
285
|
+
.length;
|
|
286
|
+
const comparisonEvidenceText = isTitleDependentComparisonClause(clause) || anchorMatches > 0
|
|
287
|
+
? `${normalizedTitle} ${clause}`
|
|
288
|
+
: clause;
|
|
289
|
+
const comparisonEvidenceFeatures = new Set((0, graphClaimMatcher_1.semanticFeatures)(comparisonEvidenceText));
|
|
290
|
+
const comparisonBranchMatchCounts = (compareBranches.length === 2 || compareBranchIdentities.length === 2)
|
|
291
|
+
? [0, 1].map((branchIndex) => Math.max(compareBranches[branchIndex]
|
|
292
|
+
? Array.from(compareBranches[branchIndex])
|
|
293
|
+
.filter((feature) => comparisonEvidenceFeatures.has(feature))
|
|
294
|
+
.length
|
|
295
|
+
: 0, compareBranchIdentities[branchIndex]
|
|
296
|
+
? comparisonIdentityMatchCount(comparisonEvidenceText, compareBranchIdentities[branchIndex])
|
|
297
|
+
: 0))
|
|
298
|
+
: [];
|
|
299
|
+
const comparisonBranchCoverage = comparisonBranchMatchCounts
|
|
300
|
+
.filter((matchCount) => matchCount > 0)
|
|
301
|
+
.length;
|
|
302
|
+
const incompleteEnding = /\b(?:and|or|with|at|in|between|is|are|from|to)$/iu.test(clause)
|
|
303
|
+
|| /\b(?:vs|versus)\.?$/iu.test(clause)
|
|
304
|
+
|| /[::]$/u.test(clause)
|
|
305
|
+
|| /(?:以及|并且|其中|通常在|范围为|分别为|是|为)$/u.test(clause)
|
|
306
|
+
|| !hasBalancedMathDelimiters(clause);
|
|
307
|
+
return {
|
|
308
|
+
clause,
|
|
309
|
+
score: (0, ragEvidenceQuality_1.scoreRagEvidenceClause)(clause).score + Math.min(0.8, anchorMatches * 0.2) - (incompleteEnding ? 0.5 : 0),
|
|
310
|
+
anchorMatches,
|
|
311
|
+
comparisonBranchCoverage,
|
|
312
|
+
comparisonBranchMatchCounts,
|
|
313
|
+
incompleteEnding,
|
|
314
|
+
order,
|
|
315
|
+
};
|
|
316
|
+
});
|
|
317
|
+
// An adjacent sentence may describe an established operand without repeating
|
|
318
|
+
// its name. Require shared source content; a matching fragment title alone is insufficient.
|
|
319
|
+
for (let index = 1; index < candidates.length; index += 1) {
|
|
320
|
+
const candidate = candidates[index];
|
|
321
|
+
const previous = candidates[index - 1];
|
|
322
|
+
if (candidate.comparisonBranchCoverage === 0 && previous.comparisonBranchCoverage > 0
|
|
323
|
+
&& publicEvidenceClausesShareContext(candidate.clause, previous.clause)) {
|
|
324
|
+
candidate.comparisonBranchMatchCounts = [...previous.comparisonBranchMatchCounts];
|
|
325
|
+
candidate.comparisonBranchCoverage = previous.comparisonBranchCoverage;
|
|
326
|
+
}
|
|
327
|
+
}
|
|
328
|
+
return candidates.sort((left, right) => (Number(left.incompleteEnding) - Number(right.incompleteEnding)
|
|
329
|
+
|| right.comparisonBranchCoverage - left.comparisonBranchCoverage
|
|
330
|
+
|| right.anchorMatches - left.anchorMatches
|
|
331
|
+
|| right.score - left.score
|
|
332
|
+
|| left.order - right.order));
|
|
333
|
+
}
|
|
334
|
+
function normalizePublicClaimStatement(value) {
|
|
335
|
+
return value.replace(/(\d)\.\s+(?=\d)/gu, '$1.').trim();
|
|
336
|
+
}
|
|
337
|
+
function uniquePublicClaimStatements(candidates) {
|
|
338
|
+
const seen = new Set();
|
|
339
|
+
return candidates
|
|
340
|
+
.map((candidate) => normalizePublicClaimStatement(candidate.clause))
|
|
341
|
+
.filter((clause) => {
|
|
342
|
+
const key = normalize(clause).toLowerCase();
|
|
343
|
+
if (!key || seen.has(key)) {
|
|
344
|
+
return false;
|
|
345
|
+
}
|
|
346
|
+
seen.add(key);
|
|
347
|
+
return true;
|
|
348
|
+
});
|
|
349
|
+
}
|
|
350
|
+
function isComparisonDocumentaryClause(value) {
|
|
351
|
+
const normalized = normalize(value);
|
|
352
|
+
return /\b(?:preamble|technical\s+document|generated\s+from\s+(?:the\s+)?title|broad\s+background|documentary\s+context|source\s+anchoring)\b/iu.test(normalized)
|
|
353
|
+
|| /本文档|本技术文档|仅基于标题|遵从(?:您的)?指示|综合技术文档|上下文段落|后续分支|前置节点/u.test(normalized)
|
|
354
|
+
// Full-document retrieval deliberately reads scaffolding around a fact.
|
|
355
|
+
// It is useful for provenance and conflict discovery, but never a
|
|
356
|
+
// comparison claim in its own right.
|
|
357
|
+
|| /\b(?:scoped\s+comparison\s+document|full-document\s+[\w-]+\s+augmentation|opening\s+section\s+is\s+intentionally\s+separate|filler\s+paragraph|keeps?\s+the\s+remote\s+appendix\s+away|remote\s+appendix\s+away\s+from\s+the\s+matched\s+opening)\b/iu.test(normalized);
|
|
358
|
+
}
|
|
359
|
+
function comparisonClaimIdentityText(claim) {
|
|
360
|
+
const statement = normalize(claim.statement);
|
|
361
|
+
const leadingStatement = statement.split(/\b(?:records?|states?|notes?|reports?|indicates?|describes?|explains?|is|are|has|have|contains?|includes?|uses?|provides?)\b/iu)[0];
|
|
362
|
+
const sourcePaths = claim.evidenceRefs
|
|
363
|
+
.map((evidence) => normalize(evidence.sourcePath))
|
|
364
|
+
.filter(Boolean);
|
|
365
|
+
return [leadingStatement, ...sourcePaths].filter(Boolean).join(' ');
|
|
366
|
+
}
|
|
367
|
+
function comparisonClaimBranchMatchCounts(claim, semanticBranches, identityBranches) {
|
|
368
|
+
const identityText = comparisonClaimIdentityText(claim);
|
|
369
|
+
const features = new Set((0, graphClaimMatcher_1.semanticFeatures)(identityText));
|
|
370
|
+
return [0, 1].map((branchIndex) => Math.max(semanticBranches[branchIndex]
|
|
371
|
+
? Array.from(semanticBranches[branchIndex]).filter((feature) => features.has(feature)).length
|
|
372
|
+
: 0, identityBranches[branchIndex]
|
|
373
|
+
? comparisonIdentityMatchCount(identityText, identityBranches[branchIndex])
|
|
374
|
+
: 0));
|
|
375
|
+
}
|
|
376
|
+
function selectComparisonAnswerClaims(claims, message) {
|
|
377
|
+
const semanticBranches = comparisonBranchFeatures(message);
|
|
378
|
+
const identityBranches = comparisonBranchIdentityTerms(message);
|
|
379
|
+
if (semanticBranches.length !== 2 && identityBranches.length !== 2) {
|
|
380
|
+
return claims;
|
|
381
|
+
}
|
|
382
|
+
const branchMatchThresholds = [0, 1].map((branchIndex) => {
|
|
383
|
+
const identitySize = identityBranches[branchIndex]?.size || 0;
|
|
384
|
+
const semanticSize = semanticBranches[branchIndex]?.size || 0;
|
|
385
|
+
return Math.max(1, Math.min(2, identitySize || semanticSize || 1));
|
|
386
|
+
});
|
|
387
|
+
const candidates = claims.map((claim, index) => ({
|
|
388
|
+
claim,
|
|
389
|
+
index,
|
|
390
|
+
matchCounts: comparisonClaimBranchMatchCounts(claim, semanticBranches, identityBranches),
|
|
391
|
+
}));
|
|
392
|
+
const selected = new Set();
|
|
393
|
+
[0, 1].forEach((branchIndex) => {
|
|
394
|
+
candidates
|
|
395
|
+
.filter((candidate) => candidate.matchCounts[branchIndex] >= branchMatchThresholds[branchIndex])
|
|
396
|
+
.sort((left, right) => (right.matchCounts[branchIndex] - left.matchCounts[branchIndex]
|
|
397
|
+
|| Number(right.claim.role === 'contrast') - Number(left.claim.role === 'contrast')
|
|
398
|
+
|| right.claim.priority - left.claim.priority
|
|
399
|
+
|| left.index - right.index))
|
|
400
|
+
.slice(0, 4)
|
|
401
|
+
.forEach((candidate) => selected.add(candidate.claim));
|
|
402
|
+
});
|
|
403
|
+
// A relation-backed contrast claim can be semantically implicit (for example,
|
|
404
|
+
// "the two nodes differ") but is still valid evidence for a compare request.
|
|
405
|
+
candidates
|
|
406
|
+
.filter((candidate) => candidate.claim.role === 'contrast' && candidate.claim.supportingEdgeIds.length > 0)
|
|
407
|
+
.sort((left, right) => right.claim.priority - left.claim.priority || left.index - right.index)
|
|
408
|
+
.slice(0, 2)
|
|
409
|
+
.forEach((candidate) => selected.add(candidate.claim));
|
|
410
|
+
if (selected.size <= 0) {
|
|
411
|
+
return claims;
|
|
412
|
+
}
|
|
413
|
+
return candidates
|
|
414
|
+
.filter((candidate) => selected.has(candidate.claim))
|
|
415
|
+
.sort((left, right) => left.index - right.index)
|
|
416
|
+
.map((candidate) => candidate.claim);
|
|
417
|
+
}
|
|
418
|
+
function selectComparisonAlignedPublicClaimStatements(candidates) {
|
|
419
|
+
const eligibleCandidates = candidates.filter((candidate) => !isComparisonDocumentaryClause(candidate.clause));
|
|
420
|
+
const maximumComparisonBranchCoverage = eligibleCandidates.reduce((maximum, candidate) => Math.max(maximum, candidate.comparisonBranchCoverage), 0);
|
|
421
|
+
if (maximumComparisonBranchCoverage >= 2) {
|
|
422
|
+
return uniquePublicClaimStatements(eligibleCandidates
|
|
423
|
+
.filter((candidate) => candidate.comparisonBranchCoverage >= 2)
|
|
424
|
+
.slice(0, 8));
|
|
425
|
+
}
|
|
426
|
+
if (maximumComparisonBranchCoverage <= 0) {
|
|
427
|
+
return uniquePublicClaimStatements(eligibleCandidates.filter((candidate) => candidate.anchorMatches > 0).slice(0, 4));
|
|
428
|
+
}
|
|
429
|
+
// When no single clause names both operands (common for Mermaid labels or
|
|
430
|
+
// two separate source documents), retain the strongest bounded evidence for
|
|
431
|
+
// each branch. This prevents a high-scoring preamble/anchor clause from
|
|
432
|
+
// consuming the comparison answer budget while preserving one-sided facts.
|
|
433
|
+
const branchCandidates = [];
|
|
434
|
+
const appendBranchCandidate = (candidate) => {
|
|
435
|
+
if (branchCandidates.includes(candidate) || branchCandidates.length >= 8) {
|
|
436
|
+
return;
|
|
437
|
+
}
|
|
438
|
+
branchCandidates.push(candidate);
|
|
439
|
+
};
|
|
440
|
+
[0, 1].forEach((branchIndex) => {
|
|
441
|
+
eligibleCandidates
|
|
442
|
+
.filter((candidate) => Number(candidate.comparisonBranchMatchCounts[branchIndex] || 0) > 0)
|
|
443
|
+
.sort((left, right) => (Number(right.comparisonBranchMatchCounts[branchIndex] || 0) - Number(left.comparisonBranchMatchCounts[branchIndex] || 0)
|
|
444
|
+
|| right.anchorMatches - left.anchorMatches
|
|
445
|
+
|| right.score - left.score
|
|
446
|
+
|| left.order - right.order))
|
|
447
|
+
.slice(0, 4)
|
|
448
|
+
.forEach(appendBranchCandidate);
|
|
449
|
+
});
|
|
450
|
+
return uniquePublicClaimStatements(branchCandidates.sort((left, right) => left.order - right.order));
|
|
451
|
+
}
|
|
452
|
+
function isDiscourseContinuation(clause) {
|
|
453
|
+
return /^(?:(?:it|its|they|their|this|these|such|therefore|thus|hence|also|additionally|moreover|however|because|thereby|consequently|as a result)\b|(?:其|它|该|这些|这种|同时|此外|因此|所以|由此|并且|而且|其中|随后|进而))/iu.test(normalize(clause));
|
|
454
|
+
}
|
|
455
|
+
const CONTEXT_WORD_SEGMENTER = new Intl.Segmenter('zh', { granularity: 'word' });
|
|
456
|
+
const CHINESE_CONTEXT_FUNCTION_WORDS = new Set(['可以', '可能', '已经', '同一', '以及', '因为', '所以', '需要', '能够', '进行', '这种', '这个', '这些', '它们']);
|
|
457
|
+
function sourceContextFeatures(value) {
|
|
458
|
+
const features = new Set();
|
|
459
|
+
for (const part of CONTEXT_WORD_SEGMENTER.segment(value)) {
|
|
460
|
+
if (!part.isWordLike)
|
|
461
|
+
continue;
|
|
462
|
+
const word = normalizedTopicWords(part.segment);
|
|
463
|
+
if (/^(?:again|repeat|repeated|repeating|再次|重复|再度)$/u.test(word)) {
|
|
464
|
+
features.add('context:repeat');
|
|
465
|
+
}
|
|
466
|
+
else if (/\p{Script=Han}/u.test(word)) {
|
|
467
|
+
if (word.length < 2 || CHINESE_CONTEXT_FUNCTION_WORDS.has(word))
|
|
468
|
+
continue;
|
|
469
|
+
const concepts = (0, graphClaimMatcher_1.semanticFeatures)(word).filter(feature => feature.startsWith('concept:'));
|
|
470
|
+
if (concepts.length > 0)
|
|
471
|
+
concepts.forEach(feature => features.add(feature));
|
|
472
|
+
else
|
|
473
|
+
features.add(`han:${word}`);
|
|
474
|
+
}
|
|
475
|
+
else {
|
|
476
|
+
(0, graphClaimMatcher_1.semanticFeatures)(word).forEach(feature => features.add(feature));
|
|
477
|
+
}
|
|
478
|
+
}
|
|
479
|
+
return features;
|
|
480
|
+
}
|
|
481
|
+
function publicEvidenceClausesShareContext(left, right) {
|
|
482
|
+
if ((0, graphClaimMatcher_1.graphClaimSemanticSimilarity)(normalizedTopicWords(left), normalizedTopicWords(right)) >= 0.16)
|
|
483
|
+
return true;
|
|
484
|
+
const leftFeatures = sourceContextFeatures(left);
|
|
485
|
+
const rightFeatures = sourceContextFeatures(right);
|
|
486
|
+
const shorterSize = Math.min(leftFeatures.size, rightFeatures.size);
|
|
487
|
+
// A short continuation need not repeat the complete entity description.
|
|
488
|
+
// Word segmentation avoids treating an entire Chinese sentence as one identity token.
|
|
489
|
+
return shorterSize > 0 && Array.from(leftFeatures).filter(feature => rightFeatures.has(feature)).length / shorterSize >= 0.16;
|
|
490
|
+
}
|
|
491
|
+
function selectQueryConnectedPublicClaimStatements(value, title, message) {
|
|
492
|
+
const completeCandidates = rankQualityPublicClaimStatements(value, title, message)
|
|
493
|
+
.filter((candidate) => !candidate.incompleteEnding);
|
|
494
|
+
if (completeCandidates.length <= 0) {
|
|
495
|
+
return [];
|
|
496
|
+
}
|
|
497
|
+
const intent = classifyIntent(message);
|
|
498
|
+
if (intent === 'compare') {
|
|
499
|
+
return selectComparisonAlignedPublicClaimStatements(completeCandidates);
|
|
500
|
+
}
|
|
501
|
+
if (intent === 'procedure') {
|
|
502
|
+
return uniquePublicClaimStatements([...completeCandidates].sort((left, right) => left.order - right.order));
|
|
503
|
+
}
|
|
504
|
+
if (isCompoundLearningDefinitionQuery(message)) {
|
|
505
|
+
return selectCompoundDefinitionPublicClaimStatements(completeCandidates);
|
|
506
|
+
}
|
|
507
|
+
const sourceOrderedCandidates = [...completeCandidates].sort((left, right) => left.order - right.order);
|
|
508
|
+
const acceptedCandidates = sourceOrderedCandidates.filter((candidate) => candidate.anchorMatches > 0);
|
|
509
|
+
if (acceptedCandidates.length <= 0) {
|
|
510
|
+
acceptedCandidates.push(completeCandidates[0]);
|
|
511
|
+
}
|
|
512
|
+
let acceptedNewCandidate = true;
|
|
513
|
+
while (acceptedNewCandidate) {
|
|
514
|
+
acceptedNewCandidate = false;
|
|
515
|
+
sourceOrderedCandidates.forEach((candidate, index) => {
|
|
516
|
+
if (acceptedCandidates.includes(candidate)) {
|
|
517
|
+
return;
|
|
518
|
+
}
|
|
519
|
+
const precedingCandidate = sourceOrderedCandidates[index - 1];
|
|
520
|
+
const followsAcceptedEvidence = Boolean(precedingCandidate
|
|
521
|
+
&& acceptedCandidates.includes(precedingCandidate)
|
|
522
|
+
&& isDiscourseContinuation(candidate.clause));
|
|
523
|
+
if (acceptedCandidates.some(accepted => publicEvidenceClausesShareContext(candidate.clause, accepted.clause)) || followsAcceptedEvidence) {
|
|
524
|
+
acceptedCandidates.push(candidate);
|
|
525
|
+
acceptedNewCandidate = true;
|
|
526
|
+
}
|
|
527
|
+
});
|
|
528
|
+
}
|
|
529
|
+
const connectedCandidates = sourceOrderedCandidates.filter((candidate) => acceptedCandidates.includes(candidate));
|
|
530
|
+
if (classifyIntent(message) === 'definition') {
|
|
531
|
+
sourceOrderedCandidates
|
|
532
|
+
.filter((candidate) => statementCarriesMathDelimiter(candidate.clause))
|
|
533
|
+
.forEach((candidate) => {
|
|
534
|
+
if (!connectedCandidates.includes(candidate)) {
|
|
535
|
+
connectedCandidates.push(candidate);
|
|
536
|
+
}
|
|
537
|
+
});
|
|
538
|
+
}
|
|
539
|
+
return uniquePublicClaimStatements(connectedCandidates);
|
|
540
|
+
}
|
|
541
|
+
function selectContextualPublicClaimStatements(value, title, message) {
|
|
542
|
+
const completeCandidates = rankQualityPublicClaimStatements(value, title, message)
|
|
543
|
+
.filter((candidate) => !candidate.incompleteEnding);
|
|
544
|
+
if (classifyIntent(message) === 'compare') {
|
|
545
|
+
return selectComparisonAlignedPublicClaimStatements(completeCandidates);
|
|
546
|
+
}
|
|
547
|
+
return uniquePublicClaimStatements([...completeCandidates].sort((left, right) => left.order - right.order));
|
|
548
|
+
}
|
|
549
|
+
function isComparisonRelationClause(clause) {
|
|
550
|
+
return /\b(?:compare|comparison|contrast|difference|differences|compared|versus)\b|对比|比较|差异|区别/iu.test(normalize(clause));
|
|
551
|
+
}
|
|
552
|
+
function selectContrastRelationPublicClaimStatements(value, title, message) {
|
|
553
|
+
const completeCandidates = rankQualityPublicClaimStatements(value, title, message)
|
|
554
|
+
.filter((candidate) => !candidate.incompleteEnding);
|
|
555
|
+
if (completeCandidates.length <= 0) {
|
|
556
|
+
return [];
|
|
557
|
+
}
|
|
558
|
+
const branchAlignedStatements = new Set(selectComparisonAlignedPublicClaimStatements(completeCandidates)
|
|
559
|
+
.map((statement) => normalize(statement).toLowerCase()));
|
|
560
|
+
return uniquePublicClaimStatements(completeCandidates.filter((candidate) => (branchAlignedStatements.has(normalize(candidate.clause).toLowerCase())
|
|
561
|
+
|| isComparisonRelationClause(candidate.clause))));
|
|
562
|
+
}
|
|
563
|
+
function contextualizeComparisonClaimStatements(statements, title, message) {
|
|
564
|
+
const normalizedTitle = normalize(String(title || '').replace(/\s*\((?:mermaid|code|diagram)\s+block\)\s*$/iu, ''));
|
|
565
|
+
const comparisonBranches = comparisonBranchFeatures(message);
|
|
566
|
+
const comparisonBranchIdentities = comparisonBranchIdentityTerms(message);
|
|
567
|
+
if (!normalizedTitle || (comparisonBranches.length !== 2 && comparisonBranchIdentities.length !== 2)) {
|
|
568
|
+
return statements;
|
|
569
|
+
}
|
|
570
|
+
const titleFeatures = new Set((0, graphClaimMatcher_1.semanticFeatures)(normalizedTitle));
|
|
571
|
+
const completesComparisonBranch = (text, semanticTerms) => [0, 1].some((index) => {
|
|
572
|
+
const semanticBranch = comparisonBranches[index] || new Set();
|
|
573
|
+
const identityBranch = comparisonBranchIdentities[index] || new Set();
|
|
574
|
+
return (semanticBranch.size > 0
|
|
575
|
+
&& Array.from(semanticBranch).some((feature) => semanticTerms.has(feature))) || (identityBranch.size > 0
|
|
576
|
+
&& comparisonIdentityMatchCount(text, identityBranch) > 0);
|
|
577
|
+
});
|
|
578
|
+
const titleCompletesBranch = completesComparisonBranch(normalizedTitle, titleFeatures);
|
|
579
|
+
if (!titleCompletesBranch) {
|
|
580
|
+
return statements;
|
|
581
|
+
}
|
|
582
|
+
return statements.map((statement) => {
|
|
583
|
+
if (isComparisonRelationClause(statement)) {
|
|
584
|
+
return statement;
|
|
585
|
+
}
|
|
586
|
+
const statementFeatures = new Set((0, graphClaimMatcher_1.semanticFeatures)(statement));
|
|
587
|
+
const statementNamesBranch = completesComparisonBranch(statement, statementFeatures);
|
|
588
|
+
return statementNamesBranch ? statement : `${normalizedTitle}: ${statement}`;
|
|
589
|
+
});
|
|
590
|
+
}
|
|
591
|
+
function classifyIntent(message) {
|
|
592
|
+
const value = normalize(message).toLowerCase();
|
|
593
|
+
if ((0, answerTaskPlan_1.isComparisonRequest)(value))
|
|
594
|
+
return 'compare';
|
|
595
|
+
if (/\b(?:why|cause|mechanism|reason|consequence)\b/u.test(value) || /为什么|原因|机制|导致/u.test(value))
|
|
596
|
+
return 'causal';
|
|
597
|
+
if ((0, answerTaskPlan_1.isProcedureRequest)(value))
|
|
598
|
+
return 'procedure';
|
|
599
|
+
if (/\b(?:what is|explain|define)\b/u.test(value) || /什么是|解释/u.test(value))
|
|
600
|
+
return 'definition';
|
|
601
|
+
return 'generic';
|
|
602
|
+
}
|
|
603
|
+
function inferRole(title, text, relationKind) {
|
|
604
|
+
const value = `${title} ${text}`.toLowerCase();
|
|
605
|
+
if (/\b(?:compare|comparison|compared|contrast|versus)\b|对比|比较|相比/u.test(value))
|
|
606
|
+
return 'contrast';
|
|
607
|
+
if (relationKind === 'prerequisite')
|
|
608
|
+
return 'prerequisite';
|
|
609
|
+
if (relationKind === 'sequence')
|
|
610
|
+
return 'sequence';
|
|
611
|
+
if (relationKind === 'causal')
|
|
612
|
+
return 'causal_consequence';
|
|
613
|
+
if (relationKind === 'contrast')
|
|
614
|
+
return 'contrast';
|
|
615
|
+
if (relationKind === 'analogy')
|
|
616
|
+
return 'analogy';
|
|
617
|
+
if (relationKind === 'application')
|
|
618
|
+
return 'application';
|
|
619
|
+
if (/boundary|边界|隔开|separat/u.test(value))
|
|
620
|
+
return 'boundary';
|
|
621
|
+
if (/thermal|heat|mechanism|exchange|传热|热交换|机制/u.test(value))
|
|
622
|
+
return 'mechanism';
|
|
623
|
+
if (/compos|consist|made of|组成|构成/u.test(value))
|
|
624
|
+
return 'composition';
|
|
625
|
+
if (/property|attribute|feature|特征|属性/u.test(value))
|
|
626
|
+
return 'attribute';
|
|
627
|
+
return 'definition';
|
|
628
|
+
}
|
|
629
|
+
function fragmentRelationKind(fragment, graphContext) {
|
|
630
|
+
const edgeIds = new Set(fragment.relationEdgeIds || []);
|
|
631
|
+
return (graphContext?.knowledgePointRelations || [])
|
|
632
|
+
.find((relation) => edgeIds.has(relation.edgeId))?.relationKind;
|
|
633
|
+
}
|
|
634
|
+
function makeClaimsFromPublicStatements(params) {
|
|
635
|
+
return params.publicStatements.map((publicStatement, statementIndex) => makeClaimFromPublicStatement({
|
|
636
|
+
index: params.index + statementIndex,
|
|
637
|
+
role: params.resolveRole(publicStatement),
|
|
638
|
+
evidenceText: params.evidenceText,
|
|
639
|
+
publicStatement,
|
|
640
|
+
anchorAtomId: params.anchorAtomId,
|
|
641
|
+
atomId: params.atomId,
|
|
642
|
+
sourcePath: params.sourcePath,
|
|
643
|
+
evidenceId: params.evidenceId,
|
|
644
|
+
citationIds: params.citationIds,
|
|
645
|
+
edgeIds: params.edgeIds,
|
|
646
|
+
confidence: params.confidence,
|
|
647
|
+
}));
|
|
648
|
+
}
|
|
649
|
+
function makeClaimFromPublicStatement(params) {
|
|
650
|
+
return {
|
|
651
|
+
claimId: `graph_claim_${params.index + 1}`,
|
|
652
|
+
role: params.role,
|
|
653
|
+
required: false,
|
|
654
|
+
priority: params.role === 'definition' ? 100 : Math.round(params.confidence * 90),
|
|
655
|
+
statement: params.publicStatement,
|
|
656
|
+
subjectAtomId: params.anchorAtomId,
|
|
657
|
+
supportingAtomIds: params.atomId && params.atomId !== params.anchorAtomId ? [params.atomId] : [],
|
|
658
|
+
supportingEdgeIds: params.edgeIds || [],
|
|
659
|
+
evidenceRefs: [{
|
|
660
|
+
evidenceId: params.evidenceId,
|
|
661
|
+
atomId: params.atomId,
|
|
662
|
+
sourcePath: params.sourcePath,
|
|
663
|
+
citationIds: params.citationIds || [],
|
|
664
|
+
text: params.evidenceText,
|
|
665
|
+
}],
|
|
666
|
+
confidence: Number(Math.max(0, Math.min(1, params.confidence)).toFixed(4)),
|
|
667
|
+
};
|
|
668
|
+
}
|
|
669
|
+
function makeClaimsFromRagFragment(params) {
|
|
670
|
+
const evidenceText = String(params.fragment.text || '').trim();
|
|
671
|
+
const selectedStatements = params.fragment.role === 'direct_support'
|
|
672
|
+
? selectQueryConnectedPublicClaimStatements(evidenceText, params.fragment.title, params.message)
|
|
673
|
+
: params.relationKind === 'contrast' && classifyIntent(params.message) === 'compare'
|
|
674
|
+
? selectContrastRelationPublicClaimStatements(evidenceText, params.fragment.title, params.message)
|
|
675
|
+
: selectContextualPublicClaimStatements(evidenceText, params.fragment.title, params.message);
|
|
676
|
+
return makeClaimsFromPublicStatements({
|
|
677
|
+
publicStatements: contextualizeComparisonClaimStatements(selectedStatements, params.fragment.title, params.message),
|
|
678
|
+
index: params.startIndex,
|
|
679
|
+
resolveRole: (statement) => inferRole(params.fragment.title || '', statement, params.relationKind),
|
|
680
|
+
evidenceText,
|
|
681
|
+
anchorAtomId: params.anchorAtomId,
|
|
682
|
+
atomId: params.fragment.atomId,
|
|
683
|
+
sourcePath: params.fragment.sourcePath,
|
|
684
|
+
evidenceId: params.fragment.fragmentId,
|
|
685
|
+
citationIds: params.fragment.citationIds,
|
|
686
|
+
edgeIds: params.fragment.relationEdgeIds,
|
|
687
|
+
confidence: Number(params.fragment.score || 0.75),
|
|
688
|
+
});
|
|
689
|
+
}
|
|
690
|
+
function makeClaimsFromMatchedSpan(params) {
|
|
691
|
+
const evidenceText = String(params.span.snippet || '').trim();
|
|
692
|
+
return makeClaimsFromPublicStatements({
|
|
693
|
+
publicStatements: contextualizeComparisonClaimStatements(selectQueryConnectedPublicClaimStatements(evidenceText, params.span.title, params.message), params.span.title, params.message),
|
|
694
|
+
index: params.startIndex,
|
|
695
|
+
resolveRole: (statement) => (params.leadingSpan ? 'definition' : inferRole(params.span.title, statement)),
|
|
696
|
+
evidenceText,
|
|
697
|
+
anchorAtomId: params.anchorAtomId,
|
|
698
|
+
atomId: params.span.atomId,
|
|
699
|
+
sourcePath: params.span.sourcePath,
|
|
700
|
+
evidenceId: params.span.citation?.citationId || `span_${params.span.atomId}_${params.startIndex + 1}`,
|
|
701
|
+
citationIds: params.span.citation ? [params.span.citation.citationId] : [],
|
|
702
|
+
confidence: params.span.score,
|
|
703
|
+
});
|
|
704
|
+
}
|
|
705
|
+
function extractPredicateFact(statement) {
|
|
706
|
+
const normalizedStatement = normalize(statement).replace(/[.!?。!?]+$/u, '');
|
|
707
|
+
const englishMatch = normalizedStatement.match(/^(.+?)\s+(?:is|are|was|were|equals?|remains?|becomes?|uses?|contains?|runs?|stores?|points?\s+to|depends?\s+on|located\s+(?:in|at)|available\s+(?:at|in))\s+(.+)$/iu);
|
|
708
|
+
if (englishMatch) {
|
|
709
|
+
return {
|
|
710
|
+
subject: normalize(englishMatch[1]).toLowerCase(),
|
|
711
|
+
value: normalize(englishMatch[2]).toLowerCase(),
|
|
712
|
+
};
|
|
713
|
+
}
|
|
714
|
+
const chineseMatch = normalizedStatement.match(/^(.+?)(?:是|为|位于|使用|包含|依赖于|指向)(.+)$/u);
|
|
715
|
+
if (chineseMatch) {
|
|
716
|
+
return {
|
|
717
|
+
subject: normalize(chineseMatch[1]).toLowerCase(),
|
|
718
|
+
value: normalize(chineseMatch[2]).toLowerCase(),
|
|
719
|
+
};
|
|
720
|
+
}
|
|
721
|
+
return null;
|
|
722
|
+
}
|
|
723
|
+
function claimsStateDifferentPredicateValues(left, right) {
|
|
724
|
+
const leftFact = extractPredicateFact(left.statement);
|
|
725
|
+
const rightFact = extractPredicateFact(right.statement);
|
|
726
|
+
return Boolean(leftFact
|
|
727
|
+
&& rightFact
|
|
728
|
+
&& (leftFact.subject === rightFact.subject
|
|
729
|
+
|| (0, graphClaimMatcher_1.graphClaimSemanticSimilarity)(leftFact.subject, rightFact.subject) >= 0.72)
|
|
730
|
+
&& leftFact.value !== rightFact.value);
|
|
731
|
+
}
|
|
732
|
+
function claimsCoverDifferentComparisonBranches(left, right, comparisonBranches, comparisonBranchIdentities) {
|
|
733
|
+
if (comparisonBranches.length !== 2 && comparisonBranchIdentities.length !== 2) {
|
|
734
|
+
return false;
|
|
735
|
+
}
|
|
736
|
+
const collectClaimIdentity = (claim) => [
|
|
737
|
+
claim.statement,
|
|
738
|
+
...claim.evidenceRefs.map((evidence) => evidence.sourcePath),
|
|
739
|
+
].join(' ');
|
|
740
|
+
const leftIdentity = collectClaimIdentity(left);
|
|
741
|
+
const rightIdentity = collectClaimIdentity(right);
|
|
742
|
+
const leftFeatures = new Set((0, graphClaimMatcher_1.semanticFeatures)(leftIdentity));
|
|
743
|
+
const rightFeatures = new Set((0, graphClaimMatcher_1.semanticFeatures)(rightIdentity));
|
|
744
|
+
const branchMatches = (features, branch) => (Array.from(branch).some((feature) => features.has(feature)));
|
|
745
|
+
return [0, 1].some((index) => {
|
|
746
|
+
const semanticBranch = comparisonBranches[index] || new Set();
|
|
747
|
+
const identityBranch = comparisonBranchIdentities[index] || new Set();
|
|
748
|
+
return (semanticBranch.size > 0
|
|
749
|
+
&& branchMatches(leftFeatures, semanticBranch) !== branchMatches(rightFeatures, semanticBranch)) || (identityBranch.size > 0
|
|
750
|
+
&& (comparisonIdentityMatchCount(leftIdentity, identityBranch) > 0)
|
|
751
|
+
!== (comparisonIdentityMatchCount(rightIdentity, identityBranch) > 0));
|
|
752
|
+
});
|
|
753
|
+
}
|
|
754
|
+
function claimsRequireSeparateCoverage(left, right, comparisonBranches, comparisonBranchIdentities) {
|
|
755
|
+
return claimsStateDifferentPredicateValues(left, right)
|
|
756
|
+
|| claimsCoverDifferentComparisonBranches(left, right, comparisonBranches, comparisonBranchIdentities);
|
|
757
|
+
}
|
|
758
|
+
function buildGraphAnswerPlan(params) {
|
|
759
|
+
const anchor = params.knowledgePoints[0];
|
|
760
|
+
const anchorAtomId = params.graphContext?.anchorAtomId || anchor?.atomId || '';
|
|
761
|
+
const claims = [];
|
|
762
|
+
const redundantAtomIds = new Set();
|
|
763
|
+
const seen = new Set();
|
|
764
|
+
const seenCompoundMathExpressions = new Set();
|
|
765
|
+
const comparisonBranches = comparisonBranchFeatures(params.message);
|
|
766
|
+
const comparisonBranchIdentities = comparisonBranchIdentityTerms(params.message);
|
|
767
|
+
const definitionIntent = classifyIntent(params.message) === 'definition';
|
|
768
|
+
let nextClaimIndex = 0;
|
|
769
|
+
const append = (claim) => {
|
|
770
|
+
const key = normalize(claim.statement).toLowerCase();
|
|
771
|
+
if (!key || seen.has(key) || (definitionIntent && isDefinitionNoiseClaim(claim.statement)))
|
|
772
|
+
return;
|
|
773
|
+
if (isCompoundLearningDefinitionQuery(params.message)) {
|
|
774
|
+
const mathExpressions = extractMathExpressions(claim.statement);
|
|
775
|
+
if (mathExpressions.length > 0) {
|
|
776
|
+
const allExpressionsSeen = mathExpressions.every((expression) => seenCompoundMathExpressions.has(expression));
|
|
777
|
+
if (allExpressionsSeen) {
|
|
778
|
+
return;
|
|
779
|
+
}
|
|
780
|
+
mathExpressions.forEach((expression) => seenCompoundMathExpressions.add(expression));
|
|
781
|
+
}
|
|
782
|
+
}
|
|
783
|
+
const redundantClaim = claims.find((existing) => (existing.role === claim.role
|
|
784
|
+
&& (0, graphClaimMatcher_1.graphClaimSemanticSimilarity)(existing.statement, claim.statement) >= 0.72
|
|
785
|
+
&& (0, graphAnswerQualityPolicy_1.graphClaimsCanShareCoverage)(existing.statement, claim.statement)
|
|
786
|
+
&& !claimsRequireSeparateCoverage(existing, claim, comparisonBranches, comparisonBranchIdentities)));
|
|
787
|
+
if (redundantClaim) {
|
|
788
|
+
const redundantAtomId = claim.supportingAtomIds[0] || claim.evidenceRefs[0]?.atomId;
|
|
789
|
+
if (redundantAtomId)
|
|
790
|
+
redundantAtomIds.add(redundantAtomId);
|
|
791
|
+
return;
|
|
792
|
+
}
|
|
793
|
+
seen.add(key);
|
|
794
|
+
claims.push(claim);
|
|
795
|
+
};
|
|
796
|
+
const matchedSpans = (anchor?.matchedSpans || [])
|
|
797
|
+
.filter((span) => !definitionIntent || !isDefinitionNoiseTitle(String(span.title || ''), params.message))
|
|
798
|
+
.filter((span) => !isCompoundLearningDefinitionQuery(params.message)
|
|
799
|
+
|| isDefinitionLearningSupportSpan(span, (0, answerTaskPlan_1.extractDefinitionQuerySubject)(params.message)))
|
|
800
|
+
.slice(0, definitionIntent ? 8 : Number.MAX_SAFE_INTEGER);
|
|
801
|
+
matchedSpans.forEach((span, spanIndex) => {
|
|
802
|
+
const spanClaims = makeClaimsFromMatchedSpan({
|
|
803
|
+
startIndex: nextClaimIndex,
|
|
804
|
+
span,
|
|
805
|
+
anchorAtomId,
|
|
806
|
+
message: params.message,
|
|
807
|
+
leadingSpan: spanIndex === 0,
|
|
808
|
+
});
|
|
809
|
+
nextClaimIndex += spanClaims.length;
|
|
810
|
+
spanClaims.forEach(append);
|
|
811
|
+
});
|
|
812
|
+
const selectedSupportingAtomIds = new Set(params.graphContext?.supportingAtomIds || []);
|
|
813
|
+
const selectedGraphEdgeIds = new Set((params.graphContext?.knowledgePointRelations || []).map((relation) => relation.edgeId));
|
|
814
|
+
(params.ragContextPack?.fragments || [])
|
|
815
|
+
.filter((fragment) => fragment.role !== 'background')
|
|
816
|
+
.filter((fragment) => !definitionIntent || !isDefinitionNoiseTitle(String(fragment.title || ''), params.message))
|
|
817
|
+
.filter((fragment) => !isCompoundLearningDefinitionQuery(params.message)
|
|
818
|
+
|| isDefinitionLearningSupportSpan({
|
|
819
|
+
title: String(fragment.title || ''),
|
|
820
|
+
snippet: String(fragment.text || ''),
|
|
821
|
+
}, (0, answerTaskPlan_1.extractDefinitionQuerySubject)(params.message)))
|
|
822
|
+
.filter((fragment) => (fragment.role !== 'graph_neighbor_support'
|
|
823
|
+
|| selectedSupportingAtomIds.size <= 0
|
|
824
|
+
|| Boolean(fragment.atomId && selectedSupportingAtomIds.has(fragment.atomId))
|
|
825
|
+
|| (fragment.relationEdgeIds || []).some((edgeId) => selectedGraphEdgeIds.has(edgeId))))
|
|
826
|
+
.forEach((fragment) => {
|
|
827
|
+
const relationKind = fragmentRelationKind(fragment, params.graphContext);
|
|
828
|
+
const fragmentClaims = makeClaimsFromRagFragment({
|
|
829
|
+
startIndex: nextClaimIndex,
|
|
830
|
+
fragment,
|
|
831
|
+
relationKind,
|
|
832
|
+
anchorAtomId,
|
|
833
|
+
message: params.message,
|
|
834
|
+
});
|
|
835
|
+
nextClaimIndex += fragmentClaims.length;
|
|
836
|
+
fragmentClaims.forEach(append);
|
|
837
|
+
});
|
|
838
|
+
if (claims.length === 0 && anchor) {
|
|
839
|
+
const evidenceText = String(anchor.evidenceSnippet || anchor.summary || '').trim();
|
|
840
|
+
const fallbackClaims = makeClaimsFromPublicStatements({
|
|
841
|
+
publicStatements: contextualizeComparisonClaimStatements(selectQueryConnectedPublicClaimStatements(evidenceText, anchor.title, params.message), anchor.title, params.message),
|
|
842
|
+
index: nextClaimIndex,
|
|
843
|
+
resolveRole: () => 'definition',
|
|
844
|
+
evidenceText,
|
|
845
|
+
anchorAtomId,
|
|
846
|
+
atomId: anchor.atomId,
|
|
847
|
+
sourcePath: anchor.sourcePath || anchor.citation?.sourcePath || '',
|
|
848
|
+
evidenceId: anchor.citation?.citationId || `point_${anchor.atomId}`,
|
|
849
|
+
citationIds: anchor.citation ? [anchor.citation.citationId] : [],
|
|
850
|
+
confidence: anchor.score,
|
|
851
|
+
});
|
|
852
|
+
nextClaimIndex += fallbackClaims.length;
|
|
853
|
+
fallbackClaims.forEach(append);
|
|
854
|
+
}
|
|
855
|
+
const evidencedAtomIds = new Set(claims.flatMap((claim) => claim.supportingAtomIds));
|
|
856
|
+
const omittedCandidates = [
|
|
857
|
+
...Array.from(redundantAtomIds).map((atomId) => ({ atomId, reason: 'redundant' })),
|
|
858
|
+
...[
|
|
859
|
+
...(params.graphContext?.predecessorWindow || []),
|
|
860
|
+
...(params.graphContext?.successorWindow || []),
|
|
861
|
+
].map((node) => ({ atomId: node.atomId, reason: 'weak_evidence' })),
|
|
862
|
+
]
|
|
863
|
+
.filter((node) => node.atomId !== anchorAtomId && !evidencedAtomIds.has(node.atomId))
|
|
864
|
+
.filter((node, index, nodes) => nodes.findIndex((candidate) => candidate.atomId === node.atomId) === index);
|
|
865
|
+
const boundedClaims = classifyIntent(params.message) === 'compare'
|
|
866
|
+
? selectComparisonAnswerClaims(claims, params.message)
|
|
867
|
+
: claims;
|
|
868
|
+
const roleOrder = {
|
|
869
|
+
definition: 10,
|
|
870
|
+
composition: 20,
|
|
871
|
+
boundary: 30,
|
|
872
|
+
attribute: 40,
|
|
873
|
+
prerequisite: 50,
|
|
874
|
+
mechanism: 60,
|
|
875
|
+
causal_consequence: 70,
|
|
876
|
+
sequence: 80,
|
|
877
|
+
application: 90,
|
|
878
|
+
contrast: 100,
|
|
879
|
+
analogy: 110,
|
|
880
|
+
temporal_warning: 120,
|
|
881
|
+
};
|
|
882
|
+
const sortedClaims = boundedClaims.sort((left, right) => (roleOrder[left.role] - roleOrder[right.role]
|
|
883
|
+
|| right.priority - left.priority));
|
|
884
|
+
sortedClaims.forEach((claim) => {
|
|
885
|
+
// The upstream evidence pack is already bounded. At this boundary, novelty and
|
|
886
|
+
// confidence determine coverage; role cardinality does not imply redundancy.
|
|
887
|
+
claim.required = claim.confidence > 0.75;
|
|
888
|
+
});
|
|
889
|
+
if (!sortedClaims.some((claim) => claim.required) && sortedClaims.length > 0) {
|
|
890
|
+
sortedClaims[0].required = true;
|
|
891
|
+
}
|
|
892
|
+
const answerTaskPlan = (0, answerTaskPlan_1.buildAnswerTaskPlan)({
|
|
893
|
+
message: params.message,
|
|
894
|
+
knowledgePoints: params.knowledgePoints,
|
|
895
|
+
graphContext: params.graphContext,
|
|
896
|
+
ragContextPack: params.ragContextPack,
|
|
897
|
+
});
|
|
898
|
+
return {
|
|
899
|
+
intent: classifyIntent(params.message),
|
|
900
|
+
depth: sortedClaims.length <= 2 ? 'compact' : sortedClaims.length <= 7 ? 'standard' : 'deep',
|
|
901
|
+
anchorAtomId,
|
|
902
|
+
leadClaimId: sortedClaims[0]?.claimId || '',
|
|
903
|
+
claims: sortedClaims,
|
|
904
|
+
requiredRoles: Array.from(new Set(sortedClaims.filter((claim) => claim.required).map((claim) => claim.role))),
|
|
905
|
+
answerTaskPlan,
|
|
906
|
+
omittedCandidates,
|
|
907
|
+
};
|
|
908
|
+
}
|