noteconnection 1.8.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/README.md +257 -1803
- package/dist/src/agent_workspace.contract.parity.test.js +105 -421
- package/dist/src/agent_workspace.frontend.test.js +410 -11
- 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/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 +265 -2
- 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 +44 -0
- package/dist/src/export/WorkspaceExportBundle.test.js +61 -0
- package/dist/src/foundation.release.evidence.contract.test.js +127 -9
- package/dist/src/foundation.sqlite.runtime.contract.test.js +6 -0
- package/dist/src/frontend/README.md +257 -1803
- package/dist/src/frontend/agent_workspace.js +665 -7
- package/dist/src/frontend/index.html +31 -2
- package/dist/src/frontend/knowledge_projection_contract.js +230 -0
- package/dist/src/frontend/knowledge_projection_store.js +214 -0
- package/dist/src/frontend/libs/path_core.js +158 -42
- package/dist/src/frontend/locales/en.json +32 -0
- package/dist/src/frontend/locales/zh.json +32 -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/path.html +6 -2
- package/dist/src/frontend/simulationWorker.js +1 -1
- package/dist/src/frontend/source_manager.js +110 -4
- package/dist/src/frontend/storage_provider.js +661 -71
- package/dist/src/frontend/styles.css +107 -13
- package/dist/src/frontend/workspace_panes.js +111 -7
- package/dist/src/frontend.runtime.assets.contract.test.js +81 -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 +22 -0
- package/dist/src/indexing/IndexLifecycle.test.js +24 -0
- package/dist/src/knowledge.api.contract.test.js +4 -2
- 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 +4687 -3892
- package/dist/src/learning/KnowledgeLearningPlatform.persistence.test.js +418 -0
- package/dist/src/learning/KnowledgeLearningPlatform.test.js +243 -6
- package/dist/src/learning/KnowledgeWorkspaceConversationRegression.js +25 -1
- package/dist/src/learning/KnowledgeWorkspaceConversationRegression.test.js +13 -5
- 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 +1191 -250
- package/dist/src/learning/answerReleaseReview.test.js +1293 -7
- package/dist/src/learning/answerTaskPlan.js +473 -0
- package/dist/src/learning/conversationComposer.js +627 -386
- package/dist/src/learning/conversationComposer.test.js +1403 -0
- package/dist/src/learning/evidenceContextAssembler.js +312 -120
- 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 +3 -13
- package/dist/src/learning/graphContextAssembler.test.js +2 -2
- 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 +1 -0
- package/dist/src/learning/mobileAnswerProjection.js +104 -0
- package/dist/src/learning/mobileAnswerProjection.test.js +102 -0
- package/dist/src/learning/queryBackend.js +34 -10
- package/dist/src/learning/queryBackend.test.js +21 -0
- package/dist/src/learning/ragContextPack.js +115 -16
- package/dist/src/learning/ragContextPack.test.js +151 -0
- package/dist/src/learning/ragEvidenceQuality.js +96 -0
- package/dist/src/learning/ragEvidenceQuality.test.js +29 -0
- package/dist/src/learning/ragPublicText.js +67 -4
- package/dist/src/learning/ragPublicText.test.js +57 -0
- package/dist/src/learning/ragSufficiencyJudge.js +11 -1
- package/dist/src/learning/ragSufficiencyProviderJudge.js +16 -26
- package/dist/src/learning/ragSufficiencyProviderJudge.test.js +51 -0
- package/dist/src/learning/requestNormalization.js +106 -0
- package/dist/src/learning/requestNormalization.test.js +101 -0
- package/dist/src/learning/store.js +116 -12
- package/dist/src/learning/store.test.js +299 -0
- package/dist/src/learning/vectorAccelerationAdapter.js +22 -6
- package/dist/src/learning/vectorAccelerationAdapter.test.js +30 -0
- package/dist/src/learning/workspaceHydration.js +168 -0
- package/dist/src/learning/workspaceHydration.test.js +30 -0
- package/dist/src/license.policy.contract.test.js +9 -4
- package/dist/src/markdown/MarkdownGateway.js +4 -0
- package/dist/src/markdown/MarkdownGateway.test.js +59 -0
- package/dist/src/middleware/SseResponseWriter.js +122 -0
- package/dist/src/middleware/SseResponseWriter.test.js +54 -0
- package/dist/src/middleware/auth.js +28 -7
- package/dist/src/middleware/auth.test.js +33 -0
- package/dist/src/middleware/index.js +2 -1
- 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/ContentGenerator.js +4 -9
- package/dist/src/notemd/FileProcessor.js +4 -9
- package/dist/src/notemd/NotemdService.js +12 -17
- package/dist/src/notemd/Translator.js +4 -9
- package/dist/src/notemd/progressReporter.js +9 -0
- package/dist/src/notemd.server.integration.test.js +119 -4
- package/dist/src/notemd.workflow.pipeline.test.js +1 -1
- package/dist/src/pathbridge.handshake.contract.test.js +29 -0
- package/dist/src/pathbridge.host-adapter.contract.test.js +133 -0
- package/dist/src/pathcore.bundle.contract.test.js +8 -0
- package/dist/src/platform/ExportProfile.js +28 -0
- package/dist/src/platform/MobileBudget.js +11 -0
- package/dist/src/platform/PlatformCapabilities.js +14 -0
- package/dist/src/platform/PlatformCapabilities.test.js +6 -0
- package/dist/src/query_backend.external_http.integration.test.js +2 -2
- package/dist/src/release.godot.mirror.contract.test.js +28 -4
- package/dist/src/resources/ResourceRegistry.js +40 -0
- package/dist/src/resources/ResourceRegistry.test.js +36 -0
- package/dist/src/routes/index.js +0 -4
- package/dist/src/routes/knowledge.js +46 -33
- package/dist/src/routes/knowledgePayload.js +185 -0
- package/dist/src/routes/knowledgePayload.test.js +53 -0
- package/dist/src/routes/markdown.js +8 -0
- package/dist/src/routes/notemd.js +990 -234
- package/dist/src/routes/registry.contract.test.js +5 -2
- package/dist/src/routes/registry.shadow.contract.test.js +22 -0
- package/dist/src/routes/render.js +14 -51
- package/dist/src/runtime.capabilities.test.js +18 -2
- package/dist/src/server.js +566 -1090
- package/dist/src/server.migration.test.js +417 -15
- 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 +6 -3
- package/dist/src/sidecar.supply.readiness.contract.test.js +11 -0
- package/dist/src/simulation.worker.offline.test.js +104 -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 +8 -0
- package/dist/src/tauri.window.evidence.execution.test.js +68 -0
- package/dist/src/utils/RuntimePaths.js +6 -0
- package/dist/src/utils/RuntimePaths.test.js +27 -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/workspace/WorkspaceRegistry.js +13 -0
- package/package.json +32 -10
- package/dist/src/routes/data.js +0 -267
- package/dist/src/routes/diagnostics.js +0 -51
|
@@ -1,7 +1,11 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.reviewAnswerRelease = reviewAnswerRelease;
|
|
4
|
+
const graphAnswerCoverage_1 = require("./graphAnswerCoverage");
|
|
5
|
+
const graphAnswerFacts_1 = require("./graphAnswerFacts");
|
|
4
6
|
const ragPublicText_1 = require("./ragPublicText");
|
|
7
|
+
const ragEvidenceQuality_1 = require("./ragEvidenceQuality");
|
|
8
|
+
const answerTaskPlan_1 = require("./answerTaskPlan");
|
|
5
9
|
const INTERNAL_DIAGNOSTIC_FRAGMENTS = [
|
|
6
10
|
'No scoped knowledge points matched',
|
|
7
11
|
'retrieval_candidates_below_threshold',
|
|
@@ -41,8 +45,6 @@ const STRUCTURED_UNIT_ALIASES = {
|
|
|
41
45
|
年: 'year',
|
|
42
46
|
};
|
|
43
47
|
const STRUCTURED_FACT_PATTERN = /(-?\d{1,4}(?:,\d{3})*(?:\.\d+)?)(?:\s*(kg\/m(?:³|3)|gpa|mpa|kpa|pa|km\/h|m\/s|km|cm|mm|ml|mb|gb|tb|kw|mw|%|percent|percentage|years?|yrs?|yr|year|°c|℃|℉|年))?/giu;
|
|
44
|
-
const ANSWER_RELEASE_PUBLIC_CHAR_LIMIT = 900;
|
|
45
|
-
const ANSWER_RELEASE_SENTENCE_BUDGET = 6;
|
|
46
48
|
const DEFINITION_EVIDENCE_HIGHLIGHT_LIMIT = 2;
|
|
47
49
|
const RAG_CLAIM_CITATION_SUPPORT_MIN_FEATURES = 2;
|
|
48
50
|
const RAG_CLAIM_CITATION_SUPPORT_MIN_COVERAGE = 0.78;
|
|
@@ -95,6 +97,31 @@ const STRUCTURED_ANCHOR_STOPWORDS = new Set([
|
|
|
95
97
|
'和',
|
|
96
98
|
'的',
|
|
97
99
|
]);
|
|
100
|
+
const QUERY_SUBJECT_STOPWORDS = new Set([
|
|
101
|
+
'a',
|
|
102
|
+
'an',
|
|
103
|
+
'the',
|
|
104
|
+
'and',
|
|
105
|
+
'are',
|
|
106
|
+
'be',
|
|
107
|
+
'definition',
|
|
108
|
+
'define',
|
|
109
|
+
'does',
|
|
110
|
+
'how',
|
|
111
|
+
'is',
|
|
112
|
+
'meaning',
|
|
113
|
+
'of',
|
|
114
|
+
'what',
|
|
115
|
+
'which',
|
|
116
|
+
'who',
|
|
117
|
+
'with',
|
|
118
|
+
'我',
|
|
119
|
+
'应该',
|
|
120
|
+
'通过',
|
|
121
|
+
'哪些',
|
|
122
|
+
'知识点',
|
|
123
|
+
'学习',
|
|
124
|
+
]);
|
|
98
125
|
const POLARITY_SENTENCE_SPLIT_PATTERN = /[.!?\u3002\uFF01\uFF1F;\n\r]+/u;
|
|
99
126
|
const ENGLISH_POLARITY_NEGATION_PATTERN = /\b(?:not|never|no|none|cannot|is not|are not|was not|were not|do not|does not|did not|can not|could not|should not|would not|will not)\b/i;
|
|
100
127
|
const CHINESE_POLARITY_NEGATION_PATTERN = /不是|并非|并不|没有|不能|无法/u;
|
|
@@ -108,12 +135,38 @@ const STATE_FRAME_SKIP_VALUE_PATTERN = /\b(?:prerequisite|before|after|depends?\
|
|
|
108
135
|
function normalizeWhitespace(value) {
|
|
109
136
|
return String(value || '').replace(/\s+/g, ' ').trim();
|
|
110
137
|
}
|
|
138
|
+
function normalizePublicAnswerSurface(value) {
|
|
139
|
+
return (0, answerTaskPlan_1.formatDisplayMathBlocks)(String(value || '')
|
|
140
|
+
.replace(/\r\n?/gu, '\n')
|
|
141
|
+
.replace(/[ \t]+\n/gu, '\n')
|
|
142
|
+
.replace(/\n{3,}/gu, '\n\n')
|
|
143
|
+
.trim());
|
|
144
|
+
}
|
|
145
|
+
function isCompoundLearningDefinitionQuery(message) {
|
|
146
|
+
const normalized = normalizeWhitespace(String(message || '')).toLowerCase();
|
|
147
|
+
return isDefinitionIntentQuery(normalized)
|
|
148
|
+
&& (/\b(?:learn|learning|study|knowledge\s+points?)\b/u.test(normalized)
|
|
149
|
+
|| /学习|知识点|学哪些|通过哪些/u.test(normalized));
|
|
150
|
+
}
|
|
111
151
|
function escapeRegExp(value) {
|
|
112
152
|
return String(value || '').replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
|
|
113
153
|
}
|
|
114
154
|
function containsCjk(value) {
|
|
115
155
|
return /[\u3400-\u9fff]/u.test(String(value || ''));
|
|
116
156
|
}
|
|
157
|
+
function useChineseAnswerLanguage(context, additionalSignals = []) {
|
|
158
|
+
if (context.answerLanguage === 'zh') {
|
|
159
|
+
return true;
|
|
160
|
+
}
|
|
161
|
+
if (context.answerLanguage === 'en') {
|
|
162
|
+
return false;
|
|
163
|
+
}
|
|
164
|
+
return containsCjk([
|
|
165
|
+
context.message,
|
|
166
|
+
context.draftAnswer,
|
|
167
|
+
...additionalSignals,
|
|
168
|
+
].filter(Boolean).join(' '));
|
|
169
|
+
}
|
|
117
170
|
function resolveScopeLabel(scope) {
|
|
118
171
|
if (scope.workspaceId) {
|
|
119
172
|
return String(scope.workspaceId).trim();
|
|
@@ -129,11 +182,11 @@ function resolveScopeLabel(scope) {
|
|
|
129
182
|
}
|
|
130
183
|
return '';
|
|
131
184
|
}
|
|
132
|
-
function buildFriendlyScopeFailureHint(scope) {
|
|
185
|
+
function buildFriendlyScopeFailureHint(scope, useChinese) {
|
|
133
186
|
const scopeLabel = resolveScopeLabel(scope);
|
|
134
187
|
const readinessStatus = String(scope.readiness?.status || '').trim();
|
|
135
188
|
const missReason = String(scope.missDiagnostics?.reason || '').trim();
|
|
136
|
-
if (
|
|
189
|
+
if (useChinese) {
|
|
137
190
|
if (readinessStatus === 'empty_store') {
|
|
138
191
|
return scopeLabel
|
|
139
192
|
? `当前范围“${scopeLabel}”还没有可检索知识。`
|
|
@@ -192,10 +245,11 @@ function buildFriendlyScopeFailureHint(scope) {
|
|
|
192
245
|
? `I could not ground the answer inside the current scope "${scopeLabel}".`
|
|
193
246
|
: 'I could not ground the answer inside the current scope.';
|
|
194
247
|
}
|
|
195
|
-
function buildAbstentionAnswer(
|
|
196
|
-
const normalizedMessage = normalizeWhitespace(message);
|
|
197
|
-
const
|
|
198
|
-
|
|
248
|
+
function buildAbstentionAnswer(context) {
|
|
249
|
+
const normalizedMessage = normalizeWhitespace(context.message);
|
|
250
|
+
const useChinese = useChineseAnswerLanguage(context);
|
|
251
|
+
const hint = buildFriendlyScopeFailureHint(context.usedScope, useChinese);
|
|
252
|
+
if (useChinese) {
|
|
199
253
|
return normalizeWhitespace(normalizedMessage
|
|
200
254
|
? `${hint} 我暂时不能对“${normalizedMessage}”给出有依据的回答。请换个说法、放宽范围,或补充相关笔记。`
|
|
201
255
|
: `${hint} 请换个说法、放宽范围,或补充相关笔记。`);
|
|
@@ -205,6 +259,20 @@ function buildAbstentionAnswer(message, scope) {
|
|
|
205
259
|
: `${hint} Refine the wording, widen the scope, or add the missing note.`);
|
|
206
260
|
}
|
|
207
261
|
function buildGroundedRevisionAnswer(context) {
|
|
262
|
+
const conflictPlanClaims = (context.graphAnswerPlan?.claims || [])
|
|
263
|
+
.filter((claim) => claim.required)
|
|
264
|
+
.map((claim) => String(claim.statement || '').trim())
|
|
265
|
+
.filter(Boolean);
|
|
266
|
+
const hasConflictEvidence = context.ragSufficiencyReview?.degradationState === 'conflict'
|
|
267
|
+
|| (context.ragSufficiencyReview?.reasons || []).some((reason) => String(reason || '').includes('conflict_evidence_present'));
|
|
268
|
+
if (hasConflictEvidence && conflictPlanClaims.length > 1) {
|
|
269
|
+
const useChinese = useChineseAnswerLanguage(context, conflictPlanClaims);
|
|
270
|
+
const sentences = [];
|
|
271
|
+
conflictPlanClaims.forEach((claim) => appendRevisionAnswerSentence(sentences, claim, useChinese));
|
|
272
|
+
if (sentences.length > 0) {
|
|
273
|
+
return sentences.join(useChinese ? '' : ' ');
|
|
274
|
+
}
|
|
275
|
+
}
|
|
208
276
|
const ragGroundedAnswer = buildRagGroundedRevisionAnswer(context);
|
|
209
277
|
if (ragGroundedAnswer) {
|
|
210
278
|
return ragGroundedAnswer;
|
|
@@ -223,12 +291,11 @@ function buildGroundedRevisionAnswer(context) {
|
|
|
223
291
|
const baseAnswer = summary && title && !summaryAlreadyCarriesTitle
|
|
224
292
|
? `${title}: ${summary}`
|
|
225
293
|
: (summary || title || normalizeWhitespace(context.draftAnswer));
|
|
226
|
-
const useChinese =
|
|
227
|
-
context.message,
|
|
294
|
+
const useChinese = useChineseAnswerLanguage(context, [
|
|
228
295
|
title,
|
|
229
296
|
summary,
|
|
230
297
|
context.graphContext?.anchorTitle || '',
|
|
231
|
-
]
|
|
298
|
+
]);
|
|
232
299
|
return expandAnswerWithGraphContext(baseAnswer, context, useChinese);
|
|
233
300
|
}
|
|
234
301
|
function stripTerminalSentencePunctuation(value) {
|
|
@@ -322,41 +389,12 @@ function removeLeadingEvidenceTitle(value, title) {
|
|
|
322
389
|
const escapedTitle = escapeRegExp(normalizedTitle);
|
|
323
390
|
return normalizeWhitespace(normalized.replace(new RegExp(`^#{0,6}\\s*${escapedTitle}\\s*[::-]?\\s*`, 'iu'), ''));
|
|
324
391
|
}
|
|
325
|
-
function isPublicEvidenceClauseBoundary(value, index) {
|
|
326
|
-
const char = value[index];
|
|
327
|
-
if (char === '\n' || char === '\r' || char === ';' || char === ';') {
|
|
328
|
-
return true;
|
|
329
|
-
}
|
|
330
|
-
if (char === '.' && /\d/u.test(value[index - 1] || '') && /\d/u.test(value[index + 1] || '')) {
|
|
331
|
-
return false;
|
|
332
|
-
}
|
|
333
|
-
return /[.!?。!?]/u.test(char);
|
|
334
|
-
}
|
|
335
|
-
function splitPublicEvidenceClauses(value) {
|
|
336
|
-
const source = String(value || '');
|
|
337
|
-
if (!source) {
|
|
338
|
-
return [];
|
|
339
|
-
}
|
|
340
|
-
const clauses = [];
|
|
341
|
-
let start = 0;
|
|
342
|
-
for (let index = 0; index < source.length; index += 1) {
|
|
343
|
-
if (!isPublicEvidenceClauseBoundary(source, index)) {
|
|
344
|
-
continue;
|
|
345
|
-
}
|
|
346
|
-
clauses.push(source.slice(start, index));
|
|
347
|
-
start = index + 1;
|
|
348
|
-
}
|
|
349
|
-
if (start < source.length) {
|
|
350
|
-
clauses.push(source.slice(start));
|
|
351
|
-
}
|
|
352
|
-
return clauses;
|
|
353
|
-
}
|
|
354
392
|
function selectPublicEvidenceClause(snippet, title) {
|
|
355
393
|
const cleaned = removeLeadingEvidenceTitle(stripMarkdownScaffolding(snippet), title);
|
|
356
394
|
if (!cleaned) {
|
|
357
395
|
return '';
|
|
358
396
|
}
|
|
359
|
-
const clauses =
|
|
397
|
+
const clauses = (0, ragEvidenceQuality_1.segmentRagEvidenceClauses)(cleaned)
|
|
360
398
|
.map((clause) => normalizeWhitespace(clause))
|
|
361
399
|
.map((clause) => (0, ragPublicText_1.naturalizeRagPublicEvidenceClause)(clause))
|
|
362
400
|
.filter((clause) => (clause.length >= 8
|
|
@@ -365,9 +403,66 @@ function selectPublicEvidenceClause(snippet, title) {
|
|
|
365
403
|
&& !isPromptArtifactClause(clause)
|
|
366
404
|
&& !/^[::\-–—]+$/u.test(clause)));
|
|
367
405
|
const selected = clauses[0] || cleaned;
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
406
|
+
if (!hasBalancedPublicMathDelimiters(selected)) {
|
|
407
|
+
return '';
|
|
408
|
+
}
|
|
409
|
+
return selectCompletePublicEvidenceBoundary(selected);
|
|
410
|
+
}
|
|
411
|
+
const PUBLIC_EVIDENCE_MAX_CHARS = 480;
|
|
412
|
+
const PUBLIC_EVIDENCE_HARD_MAX_CHARS = 720;
|
|
413
|
+
function hasBalancedPublicMathDelimiters(value) {
|
|
414
|
+
const source = String(value || '');
|
|
415
|
+
const displayMathCount = (source.match(/(?<!\\)\$\$/gu) || []).length;
|
|
416
|
+
if (displayMathCount % 2 !== 0) {
|
|
417
|
+
return false;
|
|
418
|
+
}
|
|
419
|
+
const inlineSource = source.replace(/(?<!\\)\$\$/gu, '');
|
|
420
|
+
const inlineMathCount = (inlineSource.match(/(?<!\\)\$/gu) || []).length;
|
|
421
|
+
return inlineMathCount % 2 === 0;
|
|
422
|
+
}
|
|
423
|
+
function selectCompletePublicEvidenceBoundary(value) {
|
|
424
|
+
const normalized = normalizeWhitespace(value);
|
|
425
|
+
if (normalized.length <= PUBLIC_EVIDENCE_MAX_CHARS) {
|
|
426
|
+
return normalized;
|
|
427
|
+
}
|
|
428
|
+
let inlineMathOpen = false;
|
|
429
|
+
let displayMathOpen = false;
|
|
430
|
+
const boundaries = [];
|
|
431
|
+
for (let index = 0; index < normalized.length; index += 1) {
|
|
432
|
+
const character = normalized[index];
|
|
433
|
+
if (character === '\\') {
|
|
434
|
+
index += 1;
|
|
435
|
+
continue;
|
|
436
|
+
}
|
|
437
|
+
if (character === '$') {
|
|
438
|
+
if (normalized[index + 1] === '$') {
|
|
439
|
+
displayMathOpen = !displayMathOpen;
|
|
440
|
+
index += 1;
|
|
441
|
+
}
|
|
442
|
+
else if (!displayMathOpen) {
|
|
443
|
+
inlineMathOpen = !inlineMathOpen;
|
|
444
|
+
}
|
|
445
|
+
continue;
|
|
446
|
+
}
|
|
447
|
+
if (!inlineMathOpen
|
|
448
|
+
&& !displayMathOpen
|
|
449
|
+
&& /[.!?\u3002\uFF01\uFF1F]/u.test(character)
|
|
450
|
+
&& !(character === '.'
|
|
451
|
+
&& /\d/u.test(normalized[index - 1] || '')
|
|
452
|
+
&& /\d/u.test(normalized[index + 1] || ''))
|
|
453
|
+
&& (index + 1 >= normalized.length || /\s/u.test(normalized[index + 1]))) {
|
|
454
|
+
boundaries.push(index + 1);
|
|
455
|
+
}
|
|
456
|
+
}
|
|
457
|
+
const boundedBoundary = boundaries
|
|
458
|
+
.filter((boundary) => boundary <= PUBLIC_EVIDENCE_MAX_CHARS)
|
|
459
|
+
.pop()
|
|
460
|
+
|| boundaries.find((boundary) => boundary <= PUBLIC_EVIDENCE_HARD_MAX_CHARS);
|
|
461
|
+
if (!boundedBoundary) {
|
|
462
|
+
return '';
|
|
463
|
+
}
|
|
464
|
+
const complete = normalizeWhitespace(normalized.slice(0, boundedBoundary));
|
|
465
|
+
return hasBalancedPublicMathDelimiters(complete) ? complete : '';
|
|
371
466
|
}
|
|
372
467
|
function collectDefinitionEvidenceHighlights(context) {
|
|
373
468
|
const leadingPoint = context.knowledgePoints[0];
|
|
@@ -433,39 +528,6 @@ function buildRevisionGraphConnectionSentence(context, useChinese) {
|
|
|
433
528
|
}
|
|
434
529
|
return `The strongest graph path runs through ${pathTitles.join(' -> ')}`;
|
|
435
530
|
}
|
|
436
|
-
function normalizeRevisionGraphComparableTitle(value) {
|
|
437
|
-
return normalizeDefinitionEvidenceTitle(value).toLowerCase();
|
|
438
|
-
}
|
|
439
|
-
function collectRevisionGraphWindowTitles(graphContext, windowKey, limit) {
|
|
440
|
-
if (!graphContext || !Array.isArray(graphContext[windowKey])) {
|
|
441
|
-
return [];
|
|
442
|
-
}
|
|
443
|
-
const anchorProfile = graphContext.anchorGraphProfile && typeof graphContext.anchorGraphProfile === 'object'
|
|
444
|
-
? graphContext.anchorGraphProfile
|
|
445
|
-
: null;
|
|
446
|
-
const anchorAtomId = normalizeWhitespace(String(graphContext.anchorAtomId || anchorProfile?.atomId || '').trim());
|
|
447
|
-
const anchorTitle = normalizeRevisionGraphComparableTitle(graphContext.anchorTitle || anchorProfile?.title || '');
|
|
448
|
-
const seen = new Set();
|
|
449
|
-
const titles = [];
|
|
450
|
-
for (const node of graphContext[windowKey] || []) {
|
|
451
|
-
const atomId = normalizeWhitespace(String(node && node.atomId || '').trim());
|
|
452
|
-
const title = normalizeDefinitionEvidenceTitle(node && node.title);
|
|
453
|
-
const comparableTitle = normalizeRevisionGraphComparableTitle(title);
|
|
454
|
-
if (!title || (atomId && atomId === anchorAtomId) || (comparableTitle && comparableTitle === anchorTitle)) {
|
|
455
|
-
continue;
|
|
456
|
-
}
|
|
457
|
-
const key = comparableTitle || atomId;
|
|
458
|
-
if (seen.has(key)) {
|
|
459
|
-
continue;
|
|
460
|
-
}
|
|
461
|
-
seen.add(key);
|
|
462
|
-
titles.push(title);
|
|
463
|
-
if (titles.length >= limit) {
|
|
464
|
-
break;
|
|
465
|
-
}
|
|
466
|
-
}
|
|
467
|
-
return titles;
|
|
468
|
-
}
|
|
469
531
|
function buildRevisionGraphProfileSentence(context, useChinese) {
|
|
470
532
|
const graphContext = context.graphContext;
|
|
471
533
|
if (!graphContext) {
|
|
@@ -474,48 +536,15 @@ function buildRevisionGraphProfileSentence(context, useChinese) {
|
|
|
474
536
|
const anchorProfile = graphContext.anchorGraphProfile && typeof graphContext.anchorGraphProfile === 'object'
|
|
475
537
|
? graphContext.anchorGraphProfile
|
|
476
538
|
: null;
|
|
477
|
-
const
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
if (Number.isFinite(inDegree)) {
|
|
484
|
-
degreeParts.push(useChinese ? `入度为 ${inDegree}` : `${inDegree} incoming`);
|
|
485
|
-
}
|
|
486
|
-
if (Number.isFinite(outDegree)) {
|
|
487
|
-
degreeParts.push(useChinese ? `出度为 ${outDegree}` : `${outDegree} outgoing`);
|
|
488
|
-
}
|
|
489
|
-
if (degreeParts.length <= 0 && predecessorTitles.length <= 0 && successorTitles.length <= 0) {
|
|
539
|
+
const facts = (0, graphAnswerFacts_1.collectGraphAnswerFacts)(graphContext, {
|
|
540
|
+
anchorAtomId: graphContext.anchorAtomId || anchorProfile?.atomId || '',
|
|
541
|
+
anchorTitle: graphContext.anchorTitle || anchorProfile?.title || '',
|
|
542
|
+
normalizeTitle: normalizeDefinitionEvidenceTitle,
|
|
543
|
+
});
|
|
544
|
+
if (!facts) {
|
|
490
545
|
return '';
|
|
491
546
|
}
|
|
492
|
-
|
|
493
|
-
const fragments = [];
|
|
494
|
-
if (degreeParts.length > 0) {
|
|
495
|
-
fragments.push(`${anchorTitle || '当前锚点'}在当前图中的${degreeParts.join(',')}`);
|
|
496
|
-
}
|
|
497
|
-
if (predecessorTitles.length > 0) {
|
|
498
|
-
fragments.push(`紧邻前置节点包括 ${predecessorTitles.join('、')}`);
|
|
499
|
-
}
|
|
500
|
-
if (successorTitles.length > 0) {
|
|
501
|
-
fragments.push(`后续分支包括 ${successorTitles.join('、')}`);
|
|
502
|
-
}
|
|
503
|
-
return fragments.join(',');
|
|
504
|
-
}
|
|
505
|
-
const fragments = [];
|
|
506
|
-
if (degreeParts.length > 0) {
|
|
507
|
-
fragments.push(`${anchorTitle || 'The current anchor'} has ${degreeParts.join(' and ')} links in the current graph`);
|
|
508
|
-
}
|
|
509
|
-
if (predecessorTitles.length > 0 && successorTitles.length > 0) {
|
|
510
|
-
fragments.push(`its immediate predecessors include ${predecessorTitles.join(', ')}, and likely next nodes include ${successorTitles.join(', ')}`);
|
|
511
|
-
}
|
|
512
|
-
else if (predecessorTitles.length > 0) {
|
|
513
|
-
fragments.push(`its immediate predecessors include ${predecessorTitles.join(', ')}`);
|
|
514
|
-
}
|
|
515
|
-
else if (successorTitles.length > 0) {
|
|
516
|
-
fragments.push(`its likely next nodes include ${successorTitles.join(', ')}`);
|
|
517
|
-
}
|
|
518
|
-
return fragments.join('; ');
|
|
547
|
+
return (0, graphAnswerFacts_1.formatGraphAnswerProfileSentence)(facts, useChinese);
|
|
519
548
|
}
|
|
520
549
|
function expandAnswerWithGraphContext(baseAnswer, context, useChinese, extraSentences = []) {
|
|
521
550
|
const sentences = [];
|
|
@@ -523,13 +552,13 @@ function expandAnswerWithGraphContext(baseAnswer, context, useChinese, extraSent
|
|
|
523
552
|
extraSentences.forEach((sentence) => appendRevisionAnswerSentence(sentences, sentence, useChinese));
|
|
524
553
|
appendRevisionAnswerSentence(sentences, buildRevisionGraphConnectionSentence(context, useChinese), useChinese);
|
|
525
554
|
appendRevisionAnswerSentence(sentences, buildRevisionGraphProfileSentence(context, useChinese), useChinese);
|
|
526
|
-
return sentences.
|
|
555
|
+
return sentences.join(useChinese ? '' : ' ');
|
|
527
556
|
}
|
|
528
557
|
function expandRagGroundedAnswer(baseAnswer, useChinese, evidenceSentences = []) {
|
|
529
558
|
const sentences = [];
|
|
530
559
|
appendRevisionAnswerSentence(sentences, baseAnswer, useChinese);
|
|
531
560
|
evidenceSentences.forEach((sentence) => appendRevisionAnswerSentence(sentences, sentence, useChinese));
|
|
532
|
-
return sentences.
|
|
561
|
+
return sentences.join(useChinese ? '' : ' ');
|
|
533
562
|
}
|
|
534
563
|
function hasUsableRagEvidenceContext(context) {
|
|
535
564
|
const pack = context.ragContextPack;
|
|
@@ -651,7 +680,7 @@ function splitRagPublicEvidenceClauses(fragment, options = {}) {
|
|
|
651
680
|
if (!cleaned) {
|
|
652
681
|
return [];
|
|
653
682
|
}
|
|
654
|
-
const clauses =
|
|
683
|
+
const clauses = (0, ragEvidenceQuality_1.segmentRagEvidenceClauses)(cleaned)
|
|
655
684
|
.map((clause) => normalizeWhitespace(clause))
|
|
656
685
|
.map((clause) => (0, ragPublicText_1.naturalizeRagPublicEvidenceClause)(clause))
|
|
657
686
|
.filter((clause) => (clause.length >= 8
|
|
@@ -678,6 +707,7 @@ function collectRagPublicEvidenceClauses(context, roles, limit, excludedClauses
|
|
|
678
707
|
clause,
|
|
679
708
|
queryTerms: collectRagPublicClauseQueryTerms(clause, queryTerms),
|
|
680
709
|
headingQueryTerms,
|
|
710
|
+
qualityScore: (0, ragEvidenceQuality_1.scoreRagEvidenceClause)(clause).score,
|
|
681
711
|
order,
|
|
682
712
|
});
|
|
683
713
|
order += 1;
|
|
@@ -698,10 +728,13 @@ function collectRagPublicEvidenceClauses(context, roles, limit, excludedClauses
|
|
|
698
728
|
if (rightScore !== leftScore) {
|
|
699
729
|
return rightScore - leftScore;
|
|
700
730
|
}
|
|
731
|
+
if (right.qualityScore !== left.qualityScore) {
|
|
732
|
+
return right.qualityScore - left.qualityScore;
|
|
733
|
+
}
|
|
701
734
|
return left.order - right.order;
|
|
702
735
|
});
|
|
703
736
|
rankedCandidates.forEach((candidate) => {
|
|
704
|
-
if (clauses.length >= limit || ragClauseAlreadyCovered(candidate.clause, selected)) {
|
|
737
|
+
if ((limit !== undefined && clauses.length >= limit) || ragClauseAlreadyCovered(candidate.clause, selected)) {
|
|
705
738
|
return;
|
|
706
739
|
}
|
|
707
740
|
selected.push(candidate.clause);
|
|
@@ -709,33 +742,6 @@ function collectRagPublicEvidenceClauses(context, roles, limit, excludedClauses
|
|
|
709
742
|
});
|
|
710
743
|
return clauses;
|
|
711
744
|
}
|
|
712
|
-
function ragRevisionDirectClauseLimit(profile) {
|
|
713
|
-
if (profile === 'compare') {
|
|
714
|
-
return 1;
|
|
715
|
-
}
|
|
716
|
-
if (profile === 'how_to') {
|
|
717
|
-
return 3;
|
|
718
|
-
}
|
|
719
|
-
if (profile === 'causal') {
|
|
720
|
-
return 2;
|
|
721
|
-
}
|
|
722
|
-
return 1;
|
|
723
|
-
}
|
|
724
|
-
function ragRevisionDocumentClauseLimit(profile) {
|
|
725
|
-
if (profile === 'compare') {
|
|
726
|
-
return 2;
|
|
727
|
-
}
|
|
728
|
-
if (profile === 'how_to' || profile === 'causal') {
|
|
729
|
-
return 2;
|
|
730
|
-
}
|
|
731
|
-
return 2;
|
|
732
|
-
}
|
|
733
|
-
function ragRevisionGraphClauseLimit(profile) {
|
|
734
|
-
if (profile === 'compare' || profile === 'how_to' || profile === 'causal') {
|
|
735
|
-
return 2;
|
|
736
|
-
}
|
|
737
|
-
return 1;
|
|
738
|
-
}
|
|
739
745
|
function ragRevisionDocumentClauseOptions(profile, queryTerms) {
|
|
740
746
|
if (profile === 'compare') {
|
|
741
747
|
return {
|
|
@@ -775,36 +781,39 @@ function ragRevisionGraphClauseOptions(profile, queryTerms) {
|
|
|
775
781
|
minimumLeafHeadingTermCount: queryTerms.length >= 3 ? queryTerms.length : 0,
|
|
776
782
|
};
|
|
777
783
|
}
|
|
784
|
+
function hasPlannedGraphAnswerClaims(context) {
|
|
785
|
+
return (context.graphAnswerPlan?.claims || []).some((claim) => (normalizeWhitespace(String(claim.statement || '')).length > 0));
|
|
786
|
+
}
|
|
778
787
|
function buildRagGroundedRevisionAnswer(context) {
|
|
779
|
-
if (!hasUsableRagEvidenceContext(context)) {
|
|
788
|
+
if (hasPlannedGraphAnswerClaims(context) || !hasUsableRagEvidenceContext(context)) {
|
|
780
789
|
return '';
|
|
781
790
|
}
|
|
782
|
-
const useChinese =
|
|
783
|
-
context.message,
|
|
784
|
-
context.draftAnswer,
|
|
791
|
+
const useChinese = useChineseAnswerLanguage(context, [
|
|
785
792
|
context.graphContext?.anchorTitle || '',
|
|
786
793
|
...(context.ragContextPack?.fragments || []).slice(0, 4).map((fragment) => fragment.text),
|
|
787
|
-
]
|
|
794
|
+
]);
|
|
788
795
|
const queryTerms = extractRagPublicQueryTerms(context.message);
|
|
789
796
|
const profile = resolveRagPublicAnswerProfile(context.message);
|
|
790
797
|
const isCompareQuery = profile === 'compare';
|
|
798
|
+
const rejectAnswerControlClause = ragPublicText_1.shouldRejectPublicEvidenceClause;
|
|
791
799
|
const rejectCompareProcedureClause = isCompareQuery
|
|
792
|
-
? (clause) => (
|
|
793
|
-
|
|
794
|
-
|
|
800
|
+
? (clause) => (rejectAnswerControlClause(clause)
|
|
801
|
+
|| (0, ragPublicText_1.shouldRejectCompareProcedureEvidenceClause)(clause, context.message))
|
|
802
|
+
: rejectAnswerControlClause;
|
|
803
|
+
const directClauses = collectRagPublicEvidenceClauses(context, new Set(['direct_support']), undefined, [], {
|
|
795
804
|
preserveLeadingHeading: isCompareQuery,
|
|
796
805
|
rejectClause: rejectCompareProcedureClause,
|
|
797
806
|
});
|
|
798
|
-
const documentClauses = collectRagPublicEvidenceClauses(context, new Set(['parent_context', 'adjacent_context']),
|
|
807
|
+
const documentClauses = collectRagPublicEvidenceClauses(context, new Set(['parent_context', 'adjacent_context']), undefined, directClauses, {
|
|
799
808
|
...ragRevisionDocumentClauseOptions(profile, queryTerms),
|
|
800
809
|
rejectClause: rejectCompareProcedureClause,
|
|
801
810
|
});
|
|
802
811
|
const hasConflictEvidence = context.ragSufficiencyReview?.degradationState === 'conflict'
|
|
803
812
|
|| (context.ragSufficiencyReview?.reasons || []).some((reason) => String(reason || '').includes('conflict_evidence_present'));
|
|
804
813
|
const conflictClauses = hasConflictEvidence
|
|
805
|
-
? collectRagPublicEvidenceClauses(context, new Set(['conflict']),
|
|
814
|
+
? collectRagPublicEvidenceClauses(context, new Set(['conflict']), undefined, [...directClauses, ...documentClauses])
|
|
806
815
|
: [];
|
|
807
|
-
const graphClauses = collectRagPublicEvidenceClauses(context, new Set(['graph_neighbor_support']),
|
|
816
|
+
const graphClauses = collectRagPublicEvidenceClauses(context, new Set(['graph_neighbor_support']), undefined, [...directClauses, ...documentClauses, ...conflictClauses], {
|
|
808
817
|
...ragRevisionGraphClauseOptions(profile, queryTerms),
|
|
809
818
|
rejectClause: rejectCompareProcedureClause,
|
|
810
819
|
});
|
|
@@ -816,11 +825,8 @@ function buildRagGroundedRevisionAnswer(context) {
|
|
|
816
825
|
if (!baseAnswer) {
|
|
817
826
|
return '';
|
|
818
827
|
}
|
|
819
|
-
const additionalDirectClauses = profile === 'compare'
|
|
820
|
-
? []
|
|
821
|
-
: directClauses.slice(1);
|
|
822
828
|
const extraSentences = [
|
|
823
|
-
...
|
|
829
|
+
...directClauses.slice(1),
|
|
824
830
|
...conflictClauses,
|
|
825
831
|
...documentClauses,
|
|
826
832
|
...graphClauses,
|
|
@@ -873,12 +879,7 @@ function buildDefinitionIntentRevisionAnswer(context, supportFrame) {
|
|
|
873
879
|
const subject = normalizeWhitespace(String(leadingPoint?.title || supportFrame.subject || ''));
|
|
874
880
|
const value = stripTerminalSentencePunctuation(supportFrame.value);
|
|
875
881
|
const normalizedSurface = normalizeWhitespace(String(supportFrame.surface || ''));
|
|
876
|
-
const useChinese =
|
|
877
|
-
context.message,
|
|
878
|
-
subject,
|
|
879
|
-
value,
|
|
880
|
-
normalizedSurface,
|
|
881
|
-
].join(' '));
|
|
882
|
+
const useChinese = useChineseAnswerLanguage(context, [subject, value, normalizedSurface]);
|
|
882
883
|
if (!useChinese && normalizedSurface) {
|
|
883
884
|
const canonicalizedSurface = (subject
|
|
884
885
|
&& supportFrame.subject
|
|
@@ -912,33 +913,18 @@ function buildDefinitionIntentRevisionAnswer(context, supportFrame) {
|
|
|
912
913
|
]);
|
|
913
914
|
}
|
|
914
915
|
function buildReleasedPublicAnswer(context, draftAnswer) {
|
|
915
|
-
if (
|
|
916
|
-
|
|
917
|
-
|
|
918
|
-
|
|
919
|
-
|
|
920
|
-
return ragGroundedAnswer;
|
|
916
|
+
if (isDefinitionIntentQuery(context.message)) {
|
|
917
|
+
const ragGroundedAnswer = buildRagGroundedRevisionAnswer(context);
|
|
918
|
+
if (ragGroundedAnswer && !normalizeWhitespace(draftAnswer).includes(normalizeWhitespace(ragGroundedAnswer))) {
|
|
919
|
+
return normalizeWhitespace(`${draftAnswer} ${ragGroundedAnswer}`);
|
|
920
|
+
}
|
|
921
921
|
}
|
|
922
|
-
|
|
923
|
-
context.message,
|
|
924
|
-
draftAnswer,
|
|
925
|
-
context.knowledgePoints[0]?.title || '',
|
|
926
|
-
context.graphContext?.anchorTitle || '',
|
|
927
|
-
].join(' '));
|
|
928
|
-
return expandAnswerWithGraphContext(draftAnswer, context, useChinese, [
|
|
929
|
-
buildDefinitionAugmentationSentence(context, useChinese),
|
|
930
|
-
buildDefinitionEvidenceHighlightSentence(context, useChinese),
|
|
931
|
-
]);
|
|
922
|
+
return draftAnswer;
|
|
932
923
|
}
|
|
933
924
|
function buildGraphOrderRevisionAnswer(context, conflict) {
|
|
934
925
|
const earlierTitle = normalizeWhitespace(conflict.evidence.earlierTitle);
|
|
935
926
|
const laterTitle = normalizeWhitespace(conflict.evidence.laterTitle);
|
|
936
|
-
const useChinese =
|
|
937
|
-
context.message,
|
|
938
|
-
context.draftAnswer,
|
|
939
|
-
earlierTitle,
|
|
940
|
-
laterTitle,
|
|
941
|
-
].join(' '));
|
|
927
|
+
const useChinese = useChineseAnswerLanguage(context, [earlierTitle, laterTitle]);
|
|
942
928
|
if (useChinese) {
|
|
943
929
|
return conflict.evidence.relationKind === 'prerequisite'
|
|
944
930
|
? `${earlierTitle}是${laterTitle}的前置条件。`
|
|
@@ -951,12 +937,7 @@ function buildGraphOrderRevisionAnswer(context, conflict) {
|
|
|
951
937
|
function buildGraphCausalRevisionAnswer(context, conflict) {
|
|
952
938
|
const causeTitle = normalizeWhitespace(conflict.evidence.causeTitle);
|
|
953
939
|
const effectTitle = normalizeWhitespace(conflict.evidence.effectTitle);
|
|
954
|
-
const useChinese =
|
|
955
|
-
context.message,
|
|
956
|
-
context.draftAnswer,
|
|
957
|
-
causeTitle,
|
|
958
|
-
effectTitle,
|
|
959
|
-
].join(' '));
|
|
940
|
+
const useChinese = useChineseAnswerLanguage(context, [causeTitle, effectTitle]);
|
|
960
941
|
if (useChinese) {
|
|
961
942
|
return `${causeTitle}导致${effectTitle}。`;
|
|
962
943
|
}
|
|
@@ -965,12 +946,7 @@ function buildGraphCausalRevisionAnswer(context, conflict) {
|
|
|
965
946
|
function buildGraphComparisonRevisionAnswer(context, conflict) {
|
|
966
947
|
const leftTitle = normalizeWhitespace(conflict.evidence.leftTitle);
|
|
967
948
|
const rightTitle = normalizeWhitespace(conflict.evidence.rightTitle);
|
|
968
|
-
const useChinese =
|
|
969
|
-
context.message,
|
|
970
|
-
context.draftAnswer,
|
|
971
|
-
leftTitle,
|
|
972
|
-
rightTitle,
|
|
973
|
-
].join(' '));
|
|
949
|
+
const useChinese = useChineseAnswerLanguage(context, [leftTitle, rightTitle]);
|
|
974
950
|
if (useChinese) {
|
|
975
951
|
return conflict.evidence.relationKind === 'contrast'
|
|
976
952
|
? `${leftTitle}与${rightTitle}不同。`
|
|
@@ -995,13 +971,11 @@ function buildTemporalValidityRevisionAnswer(context, conflict) {
|
|
|
995
971
|
|| context.knowledgePoints[0]?.title
|
|
996
972
|
|| conflict.invalidKnowledgePointTitles[0]
|
|
997
973
|
|| '');
|
|
998
|
-
const useChinese =
|
|
999
|
-
context.message,
|
|
1000
|
-
context.draftAnswer,
|
|
974
|
+
const useChinese = useChineseAnswerLanguage(context, [
|
|
1001
975
|
anchorTitle,
|
|
1002
976
|
...conflict.invalidKnowledgePointTitles,
|
|
1003
977
|
...conflict.warningReasons,
|
|
1004
|
-
]
|
|
978
|
+
]);
|
|
1005
979
|
if (useChinese) {
|
|
1006
980
|
if (anchorTitle) {
|
|
1007
981
|
return `关于${anchorTitle}的当前命中证据带有时序警告,我不能把它直接当作当前结论发布。`;
|
|
@@ -1036,12 +1010,7 @@ function buildStructuredComparisonRevisionAnswer(context, conflict) {
|
|
|
1036
1010
|
if (!correctedLeft || !correctedRight) {
|
|
1037
1011
|
return buildGroundedRevisionAnswer(context);
|
|
1038
1012
|
}
|
|
1039
|
-
const useChinese =
|
|
1040
|
-
context.message,
|
|
1041
|
-
context.draftAnswer,
|
|
1042
|
-
correctedLeft,
|
|
1043
|
-
correctedRight,
|
|
1044
|
-
].join(' '));
|
|
1013
|
+
const useChinese = useChineseAnswerLanguage(context, [correctedLeft, correctedRight]);
|
|
1045
1014
|
if (useChinese) {
|
|
1046
1015
|
return `${correctedLeft}高于${correctedRight}。`;
|
|
1047
1016
|
}
|
|
@@ -1057,6 +1026,7 @@ function buildSupportCandidates(context) {
|
|
|
1057
1026
|
candidates.push({
|
|
1058
1027
|
label: title,
|
|
1059
1028
|
text,
|
|
1029
|
+
sourceText: String(fragment.text || ''),
|
|
1060
1030
|
});
|
|
1061
1031
|
}
|
|
1062
1032
|
});
|
|
@@ -1069,6 +1039,7 @@ function buildSupportCandidates(context) {
|
|
|
1069
1039
|
candidates.push({
|
|
1070
1040
|
label: title,
|
|
1071
1041
|
text,
|
|
1042
|
+
sourceText: String(citation.snippet || ''),
|
|
1072
1043
|
});
|
|
1073
1044
|
}
|
|
1074
1045
|
});
|
|
@@ -1080,11 +1051,35 @@ function buildSupportCandidates(context) {
|
|
|
1080
1051
|
candidates.push({
|
|
1081
1052
|
label: title,
|
|
1082
1053
|
text,
|
|
1054
|
+
sourceText: String(point.evidenceSnippet || point.summary || ''),
|
|
1083
1055
|
});
|
|
1084
1056
|
}
|
|
1085
1057
|
});
|
|
1086
1058
|
return candidates;
|
|
1087
1059
|
}
|
|
1060
|
+
function removeSupportedSourceLabels(context, answer) {
|
|
1061
|
+
const sourceClausesByLabel = new Map();
|
|
1062
|
+
const clauseKey = (text) => stripTerminalSentencePunctuation(normalizeWhitespace((0, ragPublicText_1.naturalizeRagPublicEvidenceClause)(text)));
|
|
1063
|
+
for (const source of buildSupportCandidates(context)) {
|
|
1064
|
+
const label = normalizeWhitespace(source.label).toLowerCase();
|
|
1065
|
+
const clauses = sourceClausesByLabel.get(label) || new Set();
|
|
1066
|
+
for (const clause of (0, ragEvidenceQuality_1.segmentRagEvidenceClauses)(source.sourceText))
|
|
1067
|
+
clauses.add(clauseKey(clause));
|
|
1068
|
+
sourceClausesByLabel.set(label, clauses);
|
|
1069
|
+
}
|
|
1070
|
+
return (0, ragEvidenceQuality_1.segmentRagEvidenceClauses)(answer).map(clause => {
|
|
1071
|
+
for (const separator of clause.matchAll(/[::]/gu)) {
|
|
1072
|
+
const label = clauseKey(clause.slice(0, separator.index)).toLowerCase();
|
|
1073
|
+
const sourceClauses = sourceClausesByLabel.get(label);
|
|
1074
|
+
const body = clause.slice(separator.index + 1).trim();
|
|
1075
|
+
// A source prefix is presentation only when the complete remaining
|
|
1076
|
+
// clause is quoted from that source. Labels cannot legitimize a changed assertion.
|
|
1077
|
+
if (body && sourceClauses?.has(clauseKey(body)))
|
|
1078
|
+
return body;
|
|
1079
|
+
}
|
|
1080
|
+
return clause;
|
|
1081
|
+
}).join('\n');
|
|
1082
|
+
}
|
|
1088
1083
|
function isGraphOrderSupportedRelationKind(value) {
|
|
1089
1084
|
return value === 'prerequisite' || value === 'sequence';
|
|
1090
1085
|
}
|
|
@@ -1248,11 +1243,18 @@ function buildPolarityComparableFeatures(value) {
|
|
|
1248
1243
|
.filter((feature) => feature.length >= 2)
|
|
1249
1244
|
.filter((feature) => !STRUCTURED_ANCHOR_STOPWORDS.has(feature));
|
|
1250
1245
|
}
|
|
1251
|
-
function
|
|
1246
|
+
function stripLearningRoutePresentation(value) {
|
|
1252
1247
|
return String(value || '')
|
|
1248
|
+
.replace(/(?:^|\n)\s*#{1,6}\s*(?:建议学习路径|推荐学习路径|Suggested learning path)\s*:?[^\n]*\n[\s\S]*$/imu, ' ')
|
|
1249
|
+
.replace(/(?:建议学习路径|推荐学习路径|Suggested learning path)\s*[::][\s\S]*?(?=。|\.$|$)/giu, ' ')
|
|
1250
|
+
.trim();
|
|
1251
|
+
}
|
|
1252
|
+
function extractPolaritySentences(value, responseMode = 'slim') {
|
|
1253
|
+
return stripLearningRoutePresentation(value)
|
|
1253
1254
|
.split(POLARITY_SENTENCE_SPLIT_PATTERN)
|
|
1254
1255
|
.map((sentence) => normalizeWhitespace(sentence))
|
|
1255
1256
|
.filter((sentence) => sentence.length >= 8)
|
|
1257
|
+
.filter((sentence) => responseMode !== 'full' || !isMarkdownLabelOnlySentence(sentence))
|
|
1256
1258
|
.map((sentence) => ({
|
|
1257
1259
|
surface: sentence,
|
|
1258
1260
|
polarity: classifySentencePolarity(sentence),
|
|
@@ -1260,6 +1262,13 @@ function extractPolaritySentences(value) {
|
|
|
1260
1262
|
}))
|
|
1261
1263
|
.filter((sentence) => sentence.comparableFeatures.length >= 2);
|
|
1262
1264
|
}
|
|
1265
|
+
function isMarkdownLabelOnlySentence(value) {
|
|
1266
|
+
const normalized = normalizeWhitespace(String(value || ''));
|
|
1267
|
+
if (!normalized) {
|
|
1268
|
+
return true;
|
|
1269
|
+
}
|
|
1270
|
+
return /^(?:#{1,6}\s*|[|]\s*|[-*+]\s+)?(?:\*\*)?[^.!?\u3002\uFF01\uFF1F]{2,}(?:\*\*)?[::]\s*$/u.test(normalized);
|
|
1271
|
+
}
|
|
1263
1272
|
function computePolarityFeatureOverlap(answerSentence, supportSentence) {
|
|
1264
1273
|
if (answerSentence.comparableFeatures.length <= 0 || supportSentence.comparableFeatures.length <= 0) {
|
|
1265
1274
|
return 0;
|
|
@@ -1825,7 +1834,7 @@ function extractStateFrames(value) {
|
|
|
1825
1834
|
connectorKind: 'definition',
|
|
1826
1835
|
},
|
|
1827
1836
|
];
|
|
1828
|
-
return
|
|
1837
|
+
return stripLearningRoutePresentation(value)
|
|
1829
1838
|
.split(POLARITY_SENTENCE_SPLIT_PATTERN)
|
|
1830
1839
|
.map((sentence) => normalizeWhitespace(sentence))
|
|
1831
1840
|
.filter((sentence) => sentence.length >= 8 || (containsCjk(sentence) && sentence.length >= 5))
|
|
@@ -1861,6 +1870,9 @@ function buildSubjectFrame(surface, subject, tail) {
|
|
|
1861
1870
|
tailFeatures,
|
|
1862
1871
|
};
|
|
1863
1872
|
}
|
|
1873
|
+
function collapseDisplayMathForSemanticParsing(value) {
|
|
1874
|
+
return String(value || '').replace(/\s*(?<!\\)\$\$([\s\S]*?)(?<!\\)\$\$\s*/gu, (_match, expression) => ` $$${normalizeWhitespace(expression)}$$ `);
|
|
1875
|
+
}
|
|
1864
1876
|
function buildAttributeFrame(surface, subject, value) {
|
|
1865
1877
|
const normalizedSurface = normalizeWhitespace(surface);
|
|
1866
1878
|
const normalizedSubject = normalizeAttributeFrameSubject(subject);
|
|
@@ -1889,7 +1901,7 @@ function extractSubjectFrames(value) {
|
|
|
1889
1901
|
/^(.{1,80}?)\s+(?:is|are|was|were|remains|stays|equals|means|refers to|belongs to|has|contains)\s+(.+)$/iu,
|
|
1890
1902
|
/^(.{1,24}?)(?:是|为|等于|指|属于|有|包含)(.+)$/u,
|
|
1891
1903
|
];
|
|
1892
|
-
return
|
|
1904
|
+
return collapseDisplayMathForSemanticParsing(stripLearningRoutePresentation(value))
|
|
1893
1905
|
.split(POLARITY_SENTENCE_SPLIT_PATTERN)
|
|
1894
1906
|
.map((sentence) => normalizeWhitespace(sentence))
|
|
1895
1907
|
.filter((sentence) => sentence.length >= 8 || (containsCjk(sentence) && sentence.length >= 5))
|
|
@@ -1900,7 +1912,11 @@ function extractSubjectFrames(value) {
|
|
|
1900
1912
|
continue;
|
|
1901
1913
|
}
|
|
1902
1914
|
const frame = buildSubjectFrame(sentence, String(match[1] || ''), String(match[2] || ''));
|
|
1903
|
-
return frame
|
|
1915
|
+
return frame
|
|
1916
|
+
&& !isStandaloneVariableDefinitionClaim(frame.surface)
|
|
1917
|
+
&& !/(?<!\\)\$[^$\n]+?(?<!\\)\$/u.test(frame.subject)
|
|
1918
|
+
? [frame]
|
|
1919
|
+
: [];
|
|
1904
1920
|
}
|
|
1905
1921
|
return [];
|
|
1906
1922
|
});
|
|
@@ -1910,7 +1926,7 @@ function extractAttributeFrames(value) {
|
|
|
1910
1926
|
/^(.{1,80}?)\s+(?:has|have|had|features?)\s+(.+)$/iu,
|
|
1911
1927
|
/^(.{1,24}?)(?:有|具有|带有)(.+)$/u,
|
|
1912
1928
|
];
|
|
1913
|
-
return
|
|
1929
|
+
return stripLearningRoutePresentation(value)
|
|
1914
1930
|
.split(POLARITY_SENTENCE_SPLIT_PATTERN)
|
|
1915
1931
|
.map((sentence) => normalizeWhitespace(sentence))
|
|
1916
1932
|
.filter((sentence) => sentence.length >= 8 || (containsCjk(sentence) && sentence.length >= 5))
|
|
@@ -2027,7 +2043,7 @@ function extractContainmentFrames(value) {
|
|
|
2027
2043
|
/^(.{1,80}?)\s+(?:is|are|was|were)\s+filled\s+with\s+(.+)$/iu,
|
|
2028
2044
|
/^(.{1,24}?)(?:装有|盛有|含有|包含)(.+)$/u,
|
|
2029
2045
|
];
|
|
2030
|
-
return
|
|
2046
|
+
return stripLearningRoutePresentation(value)
|
|
2031
2047
|
.split(POLARITY_SENTENCE_SPLIT_PATTERN)
|
|
2032
2048
|
.map((sentence) => normalizeWhitespace(sentence))
|
|
2033
2049
|
.filter((sentence) => sentence.length >= 8 || (containsCjk(sentence) && sentence.length >= 5))
|
|
@@ -2054,7 +2070,7 @@ function extractPurposeFrames(value) {
|
|
|
2054
2070
|
/^(.{1,24}?)(?:可)?用来(.+)$/u,
|
|
2055
2071
|
/^(.{1,24}?)(?:被)?用作(.+)$/u,
|
|
2056
2072
|
];
|
|
2057
|
-
return
|
|
2073
|
+
return stripLearningRoutePresentation(value)
|
|
2058
2074
|
.split(POLARITY_SENTENCE_SPLIT_PATTERN)
|
|
2059
2075
|
.map((sentence) => normalizeWhitespace(sentence))
|
|
2060
2076
|
.filter((sentence) => sentence.length >= 8 || (containsCjk(sentence) && sentence.length >= 5))
|
|
@@ -2097,7 +2113,7 @@ function extractDependencyFrames(value) {
|
|
|
2097
2113
|
dependencyIndex: 1,
|
|
2098
2114
|
},
|
|
2099
2115
|
];
|
|
2100
|
-
return
|
|
2116
|
+
return stripLearningRoutePresentation(value)
|
|
2101
2117
|
.split(POLARITY_SENTENCE_SPLIT_PATTERN)
|
|
2102
2118
|
.map((sentence) => normalizeWhitespace(sentence))
|
|
2103
2119
|
.filter((sentence) => sentence.length >= 8 || (containsCjk(sentence) && sentence.length >= 5))
|
|
@@ -2149,7 +2165,7 @@ function extractLocationFrames(value) {
|
|
|
2149
2165
|
/^(.{1,80}?)\s+lies?\s+(?:in|within)\s+(.+)$/iu,
|
|
2150
2166
|
/^(.{1,24}?)(?:位于|位於|坐落于|坐落於|处于|處於)(.+)$/u,
|
|
2151
2167
|
];
|
|
2152
|
-
return
|
|
2168
|
+
return stripLearningRoutePresentation(value)
|
|
2153
2169
|
.split(POLARITY_SENTENCE_SPLIT_PATTERN)
|
|
2154
2170
|
.map((sentence) => normalizeWhitespace(sentence))
|
|
2155
2171
|
.filter((sentence) => sentence.length >= 8 || (containsCjk(sentence) && sentence.length >= 5))
|
|
@@ -2175,7 +2191,7 @@ function extractCompositionFrames(value) {
|
|
|
2175
2191
|
/^(.{1,24}?)(?:是)?由(.+?)(?:组成|构成|構成)(?:的.+)?$/u,
|
|
2176
2192
|
/^(.{1,24}?)(?:被定义为|定义为|被定義為|定義為).+?由(.+?)(?:组成|构成|構成)(?:的.+)?$/u,
|
|
2177
2193
|
];
|
|
2178
|
-
return
|
|
2194
|
+
return stripLearningRoutePresentation(value)
|
|
2179
2195
|
.split(POLARITY_SENTENCE_SPLIT_PATTERN)
|
|
2180
2196
|
.map((sentence) => normalizeWhitespace(sentence))
|
|
2181
2197
|
.filter((sentence) => sentence.length >= 8 || (containsCjk(sentence) && sentence.length >= 5))
|
|
@@ -2192,10 +2208,54 @@ function extractCompositionFrames(value) {
|
|
|
2192
2208
|
});
|
|
2193
2209
|
}
|
|
2194
2210
|
function subjectFramesShareTail(answerFrame, supportFrame) {
|
|
2211
|
+
const answerQualifiers = collectScopeQualifierTokens(`${answerFrame.subject} ${answerFrame.tail}`);
|
|
2212
|
+
const supportQualifiers = collectScopeQualifierTokens(`${supportFrame.subject} ${supportFrame.tail}`);
|
|
2213
|
+
if (answerQualifiers.size > 0
|
|
2214
|
+
&& supportQualifiers.size > 0
|
|
2215
|
+
&& !Array.from(answerQualifiers).some((qualifier) => supportQualifiers.has(qualifier))) {
|
|
2216
|
+
return false;
|
|
2217
|
+
}
|
|
2195
2218
|
const overlapRatio = computeFeatureOverlapRatio(answerFrame.tailFeatures, supportFrame.tailFeatures);
|
|
2196
2219
|
const jaccard = computeFeatureJaccard(answerFrame.tailFeatures, supportFrame.tailFeatures);
|
|
2197
2220
|
return overlapRatio >= 0.75 || jaccard >= 0.7;
|
|
2198
2221
|
}
|
|
2222
|
+
const SUBJECT_SCOPE_QUALIFIER_FEATURES = new Set([
|
|
2223
|
+
'staging', 'production', 'development', 'dev', 'test', 'testing', 'qa', 'sandbox',
|
|
2224
|
+
'windows', 'android', 'ios', 'linux', 'macos', 'mac',
|
|
2225
|
+
'current', 'planned', 'historical', 'history', 'legacy', 'future', 'past',
|
|
2226
|
+
'生产', '预发布', '开发', '测试', '版本',
|
|
2227
|
+
]);
|
|
2228
|
+
function collectScopeQualifierTokens(value) {
|
|
2229
|
+
const normalized = normalizeWhitespace(value).toLowerCase();
|
|
2230
|
+
const qualifiers = normalized.match(/\b(?:staging|production|development|dev|test|testing|qa|sandbox|windows|android|ios|linux|macos|mac|current|planned|historical|history|legacy|future|past)\b|\b(?:version|v)\s*\d+(?:\.\d+)*\b|(?:生产|预发布|开发|测试|当前|计划|历史|旧版|版本\s*\d+(?:\.\d+)*)/gu) || [];
|
|
2231
|
+
return new Set(qualifiers.map((qualifier) => {
|
|
2232
|
+
const normalizedQualifier = normalizeWhitespace(qualifier);
|
|
2233
|
+
const versionMatch = normalizedQualifier.match(/^(?:version|v)\s*(\d+(?:\.\d+)*)$/iu);
|
|
2234
|
+
if (!versionMatch) {
|
|
2235
|
+
return normalizedQualifier;
|
|
2236
|
+
}
|
|
2237
|
+
const components = String(versionMatch[1] || '').split('.');
|
|
2238
|
+
while (components.length > 1 && components[components.length - 1] === '0') {
|
|
2239
|
+
components.pop();
|
|
2240
|
+
}
|
|
2241
|
+
return `version ${components.join('.')}`;
|
|
2242
|
+
}));
|
|
2243
|
+
}
|
|
2244
|
+
function subjectFramesShareQualifiedIdentity(answerFrame, supportFrame) {
|
|
2245
|
+
if (!subjectFramesShareTail(answerFrame, supportFrame)) {
|
|
2246
|
+
return false;
|
|
2247
|
+
}
|
|
2248
|
+
const answerQualifiers = collectScopeQualifierTokens(`${answerFrame.subject} ${answerFrame.tail}`);
|
|
2249
|
+
const supportQualifiers = collectScopeQualifierTokens(`${supportFrame.subject} ${supportFrame.tail}`);
|
|
2250
|
+
if (answerQualifiers.size <= 0
|
|
2251
|
+
|| !Array.from(answerQualifiers).some((qualifier) => supportQualifiers.has(qualifier))) {
|
|
2252
|
+
return false;
|
|
2253
|
+
}
|
|
2254
|
+
const answerFeatures = new Set(answerFrame.subjectFeatures.filter((feature) => !SUBJECT_SCOPE_QUALIFIER_FEATURES.has(feature)));
|
|
2255
|
+
const supportFeatures = new Set(supportFrame.subjectFeatures.filter((feature) => !SUBJECT_SCOPE_QUALIFIER_FEATURES.has(feature)));
|
|
2256
|
+
const sharedCoreFeatureCount = Array.from(answerFeatures).filter((feature) => supportFeatures.has(feature)).length;
|
|
2257
|
+
return sharedCoreFeatureCount >= 2;
|
|
2258
|
+
}
|
|
2199
2259
|
function subjectFramesEquivalent(answerFrame, supportFrame) {
|
|
2200
2260
|
const normalizedAnswerSubject = normalizeWhitespace(answerFrame.subject).toLowerCase();
|
|
2201
2261
|
const normalizedSupportSubject = normalizeWhitespace(supportFrame.subject).toLowerCase();
|
|
@@ -2205,7 +2265,8 @@ function subjectFramesEquivalent(answerFrame, supportFrame) {
|
|
|
2205
2265
|
if (normalizedAnswerSubject === normalizedSupportSubject) {
|
|
2206
2266
|
return true;
|
|
2207
2267
|
}
|
|
2208
|
-
return computeFeatureOverlapRatio(answerFrame.subjectFeatures, supportFrame.subjectFeatures) >= 0.75
|
|
2268
|
+
return computeFeatureOverlapRatio(answerFrame.subjectFeatures, supportFrame.subjectFeatures) >= 0.75
|
|
2269
|
+
|| subjectFramesShareQualifiedIdentity(answerFrame, supportFrame);
|
|
2209
2270
|
}
|
|
2210
2271
|
function computeSubjectFrameTailOverlap(answerFrame, supportFrame) {
|
|
2211
2272
|
return computeFeatureOverlapRatio(answerFrame.tailFeatures, supportFrame.tailFeatures);
|
|
@@ -2579,7 +2640,7 @@ function evaluateGroundingAlignment(context) {
|
|
|
2579
2640
|
const scored = candidates
|
|
2580
2641
|
.map((candidate) => ({
|
|
2581
2642
|
label: candidate.label,
|
|
2582
|
-
score: computeGroundingAlignmentScore(context.draftAnswer, candidate.text),
|
|
2643
|
+
score: computeGroundingAlignmentScore(stripLearningRoutePresentation(context.draftAnswer), candidate.text),
|
|
2583
2644
|
}))
|
|
2584
2645
|
.sort((left, right) => right.score - left.score);
|
|
2585
2646
|
const best = scored[0] || { label: '', score: 0 };
|
|
@@ -2617,7 +2678,8 @@ function ragAnswerRoleIsCovered(context, role) {
|
|
|
2617
2678
|
if (!supportText) {
|
|
2618
2679
|
return true;
|
|
2619
2680
|
}
|
|
2620
|
-
return
|
|
2681
|
+
return splitDraftAnswerClaims(context.draftAnswer)
|
|
2682
|
+
.some((claim) => computeGroundingAlignmentScore(claim, supportText) >= 0.22);
|
|
2621
2683
|
}
|
|
2622
2684
|
const HOW_TO_PROFILE_SIGNAL_RULES = [
|
|
2623
2685
|
{
|
|
@@ -2734,6 +2796,446 @@ function buildRagAnswerCompletenessMessage(result) {
|
|
|
2734
2796
|
}
|
|
2735
2797
|
return `Draft answer missed required RAG evidence roles: ${result.missingRoles.join(', ') || 'none'}; missing profile signals: ${result.missingProfileSignals.join(', ') || 'none'}.`;
|
|
2736
2798
|
}
|
|
2799
|
+
function preservePlannedGraphAnswerClaims(answer, plan) {
|
|
2800
|
+
if (!plan) {
|
|
2801
|
+
return normalizePublicAnswerSurface(answer);
|
|
2802
|
+
}
|
|
2803
|
+
const plannedStatements = (plan.claims || [])
|
|
2804
|
+
.map((claim) => (0, ragPublicText_1.naturalizeRagPublicEvidenceClause)(String(claim.statement || '')))
|
|
2805
|
+
.filter((statement) => (statement
|
|
2806
|
+
&& hasBalancedPublicMathDelimiters(statement)
|
|
2807
|
+
&& !(0, ragPublicText_1.shouldRejectPublicEvidenceClause)(statement)));
|
|
2808
|
+
if (plannedStatements.length <= 0) {
|
|
2809
|
+
return normalizeWhitespace(answer);
|
|
2810
|
+
}
|
|
2811
|
+
const normalizedAnswer = normalizeWhitespace(answer);
|
|
2812
|
+
const orderedPlanText = plannedStatements.join('\n\n');
|
|
2813
|
+
const supplementalClauses = (0, ragEvidenceQuality_1.segmentRagEvidenceClauses)((0, ragPublicText_1.naturalizeRagPublicEvidenceClause)(normalizedAnswer))
|
|
2814
|
+
.map((clause) => normalizeWhitespace(clause))
|
|
2815
|
+
.filter((clause) => (clause
|
|
2816
|
+
&& hasBalancedPublicMathDelimiters(clause)
|
|
2817
|
+
&& !(0, ragPublicText_1.shouldRejectPublicEvidenceClause)(clause)))
|
|
2818
|
+
.filter((clause) => plan?.intent !== 'definition' || (!/\b(?:compared?\s+with|versus|vs\.?|plastic\s+cup|metal\s+cup|对比|比较|差异|区别)\b/iu.test(clause)
|
|
2819
|
+
&& !/\b(?:remain(?:s)?\s+(?:internal|outside)|public\s+(?:definition|answer)\s+budget|extra\s+sentence|beyond\s+the\s+public)\b/iu.test(clause)))
|
|
2820
|
+
.filter((clause) => !plannedStatements.some((plannedStatement) => {
|
|
2821
|
+
const clauseKey = clause.toLowerCase();
|
|
2822
|
+
const plannedKey = plannedStatement.toLowerCase();
|
|
2823
|
+
return clauseKey === plannedKey
|
|
2824
|
+
|| clauseKey.includes(plannedKey)
|
|
2825
|
+
|| plannedKey.includes(clauseKey);
|
|
2826
|
+
}));
|
|
2827
|
+
const supplementalText = plan?.intent === 'definition'
|
|
2828
|
+
? ''
|
|
2829
|
+
: supplementalClauses.join('\n\n');
|
|
2830
|
+
const useChinese = /[\u3400-\u9fff]/u.test(`${answer} ${plan.answerTaskPlan?.primarySubject || ''}`);
|
|
2831
|
+
return (0, answerTaskPlan_1.formatTaskAwareAnswer)([orderedPlanText, supplementalText], plan.answerTaskPlan, useChinese);
|
|
2832
|
+
}
|
|
2833
|
+
function isIncompletePublicGraphClaim(value) {
|
|
2834
|
+
const normalized = normalizeWhitespace(value);
|
|
2835
|
+
if (normalized.length < (containsCjk(normalized) ? 5 : 16) || /^\d+(?:\.\d+)*[.\u3001)]?$/u.test(normalized)) {
|
|
2836
|
+
return true;
|
|
2837
|
+
}
|
|
2838
|
+
const endsWithTerminalPunctuation = /[.!?\u3002\uFF01\uFF1F]$/u.test(normalized);
|
|
2839
|
+
const endsWithBalancedMath = /(?<!\\)(?:\$\$[\s\S]+?\$\$|\$[^$\n]+\$)$/u.test(normalized);
|
|
2840
|
+
if (!endsWithTerminalPunctuation && !endsWithBalancedMath) {
|
|
2841
|
+
return true;
|
|
2842
|
+
}
|
|
2843
|
+
if (/[::]$|(?:and|or|with|between|from|to|is|are|以及|并且|其中|通常在|范围为|分别为|性能)$/iu.test(normalized)) {
|
|
2844
|
+
return true;
|
|
2845
|
+
}
|
|
2846
|
+
return false;
|
|
2847
|
+
}
|
|
2848
|
+
function isDefinitionComparisonOrArtifactClaim(value) {
|
|
2849
|
+
const normalized = normalizeWhitespace(value);
|
|
2850
|
+
return /\b(?:compare|comparison|contrast|versus|vs\.?|plastic\s+cup|metal\s+cup|ceramic\s+(?:mug|cup)|technologies|context\s+paragraph|local\s+window|technical\s+document|subsequent\s+branch)\b/iu.test(normalized)
|
|
2851
|
+
|| /\b(?:validates?\s+(?:the\s+)?source|source\s+anchoring|same\s+clause\s+appears\s+in\s+multiple\s+sections)\b/iu.test(normalized)
|
|
2852
|
+
|| /相关技术|比较|对比|塑料杯|金属杯|陶瓷杯|流体容器技术比较|上下文段落|后续分支|本技术文档|技术规格|性能特征|核心概念|我们将从|我们将|本节|本章/u.test(normalized);
|
|
2853
|
+
}
|
|
2854
|
+
function isDefinitionDisambiguationContextStatement(value) {
|
|
2855
|
+
const normalized = normalizeWhitespace(value);
|
|
2856
|
+
if (!normalized) {
|
|
2857
|
+
return false;
|
|
2858
|
+
}
|
|
2859
|
+
const sectionReference = /\b(?:(?:target|selected|matched|active|current|applicable)\s+(?:section|occurrence|version|record|entry|context)|(?:first|second|later|earlier|specific|corresponding)\s+(?:occurrence|section|match|entry))\b/iu.test(normalized);
|
|
2860
|
+
const authoritySignal = /\b(?:controls?|determines?|applies?|guides?|answers?|answer|selected|active|authoritative|canonical|use|used)\b/iu.test(normalized);
|
|
2861
|
+
return sectionReference && authoritySignal;
|
|
2862
|
+
}
|
|
2863
|
+
function isFullDocumentDefinitionNoiseStatement(value) {
|
|
2864
|
+
const normalized = normalizeWhitespace(value);
|
|
2865
|
+
return !normalized
|
|
2866
|
+
|| /^\d+(?:\.\d+)*[.、)]?$/u.test(normalized)
|
|
2867
|
+
|| /常见用例|应用场景|关键技术规格|技术规格|性能指标|性能特征|统计度量|量化分析|静水压力|环向应力|威布尔|机械可靠性|simple\s+optical\s+lens|use\s+case|application\s+scenario|technical\s+specification|performance\s+(?:metric|characteristic)|quantitative\s+analysis|hydrostatic\s+pressure|hoop\s+stress|weibull|mechanical\s+reliability/iu.test(normalized);
|
|
2868
|
+
}
|
|
2869
|
+
function extractNormalizedPublicMathExpressions(value) {
|
|
2870
|
+
const source = String(value || '');
|
|
2871
|
+
const expressions = [];
|
|
2872
|
+
const displayPattern = /(?<!\\)\$\$([\s\S]*?)(?<!\\)\$\$/gu;
|
|
2873
|
+
for (const match of source.matchAll(displayPattern)) {
|
|
2874
|
+
const expression = normalizeWhitespace(match[1]).replace(/\s+/gu, '');
|
|
2875
|
+
if (expression) {
|
|
2876
|
+
expressions.push(expression);
|
|
2877
|
+
}
|
|
2878
|
+
}
|
|
2879
|
+
const inlineSource = source.replace(displayPattern, '');
|
|
2880
|
+
const inlinePattern = /(?<!\\)\$([^$\n]+?)(?<!\\)\$/gu;
|
|
2881
|
+
for (const match of inlineSource.matchAll(inlinePattern)) {
|
|
2882
|
+
const expression = normalizeWhitespace(match[1]).replace(/\s+/gu, '');
|
|
2883
|
+
if (expression) {
|
|
2884
|
+
expressions.push(expression);
|
|
2885
|
+
}
|
|
2886
|
+
}
|
|
2887
|
+
return Array.from(new Set(expressions));
|
|
2888
|
+
}
|
|
2889
|
+
function isFormulaOnlyPublicGraphClaim(value) {
|
|
2890
|
+
const source = String(value || '');
|
|
2891
|
+
if (extractNormalizedPublicMathExpressions(source).length <= 0) {
|
|
2892
|
+
return false;
|
|
2893
|
+
}
|
|
2894
|
+
const withoutDisplayMath = source.replace(/(?<!\\)\$\$[\s\S]*?(?<!\\)\$\$/gu, '');
|
|
2895
|
+
const prose = withoutDisplayMath
|
|
2896
|
+
.replace(/(?<!\\)\$[^$\n]+?(?<!\\)\$/gu, '')
|
|
2897
|
+
.replace(/[()[\]{}.,;:!?]/gu, '')
|
|
2898
|
+
.trim();
|
|
2899
|
+
return prose.length <= 0;
|
|
2900
|
+
}
|
|
2901
|
+
function formulaOnlyPublicClaimIsAlreadyCovered(candidate, selectedClaims) {
|
|
2902
|
+
if (!isFormulaOnlyPublicGraphClaim(candidate.statement)) {
|
|
2903
|
+
return false;
|
|
2904
|
+
}
|
|
2905
|
+
const candidateExpressions = extractNormalizedPublicMathExpressions(candidate.statement);
|
|
2906
|
+
return candidateExpressions.length > 0 && selectedClaims.some((selectedClaim) => {
|
|
2907
|
+
const selectedExpressions = extractNormalizedPublicMathExpressions(selectedClaim.statement);
|
|
2908
|
+
return candidateExpressions.every((expression) => selectedExpressions.includes(expression));
|
|
2909
|
+
});
|
|
2910
|
+
}
|
|
2911
|
+
function isStandaloneVariableDefinitionClaim(value) {
|
|
2912
|
+
const source = normalizeWhitespace(String(value || ''));
|
|
2913
|
+
const expressions = extractNormalizedPublicMathExpressions(source);
|
|
2914
|
+
if (expressions.length !== 1 || /(?<!\\)\$\$[\s\S]*?(?<!\\)\$\$/u.test(source)) {
|
|
2915
|
+
return false;
|
|
2916
|
+
}
|
|
2917
|
+
const prose = source
|
|
2918
|
+
.replace(/(?<!\\)\$[^$\n]+?(?<!\\)\$/gu, '')
|
|
2919
|
+
.replace(/[()[\]{}.,;:!?]/gu, '')
|
|
2920
|
+
.trim();
|
|
2921
|
+
return /^(?:is|are|was|were|means?|denotes?|represents?|分别是|是|为|表示|指)/iu.test(prose)
|
|
2922
|
+
|| /(?:^|\s)(?:is|are|means?|denotes?|represents?|分别是|是|为|表示|指)(?:\s|$)/iu.test(prose);
|
|
2923
|
+
}
|
|
2924
|
+
function isStandaloneVariableGlossaryClaim(value) {
|
|
2925
|
+
const source = normalizeWhitespace(String(value || ''));
|
|
2926
|
+
const expressions = extractNormalizedPublicMathExpressions(source);
|
|
2927
|
+
if (expressions.length < 2 || expressions.some((expression) => expression.includes('='))) {
|
|
2928
|
+
return false;
|
|
2929
|
+
}
|
|
2930
|
+
const variableDefinitions = source.match(/(?<!\\)\$[^$\n]+?(?<!\\)\$\s*(?:is|are|means?|denotes?|represents?|是|为|表示|指)/giu) || [];
|
|
2931
|
+
if (variableDefinitions.length < 2) {
|
|
2932
|
+
return false;
|
|
2933
|
+
}
|
|
2934
|
+
const prose = source
|
|
2935
|
+
.replace(/(?<!\\)\$[^$\n]+?(?<!\\)\$/gu, ' ')
|
|
2936
|
+
.trim();
|
|
2937
|
+
return /^(?:where|in which|here|variables?|the variables|其中|其中的|分别|各变量)/iu.test(prose)
|
|
2938
|
+
|| variableDefinitions.length >= 3;
|
|
2939
|
+
}
|
|
2940
|
+
function definitionQueryMatchesVariableGlossary(glossary, message) {
|
|
2941
|
+
const normalizedMessage = normalizeWhitespace(String(message || ''))
|
|
2942
|
+
.replace(/^(?:what\s+is|what'?s|what\s+are|define|definition\s+of|meaning\s+of|什么是|指的是什么|定义|是什么意思)\s*/iu, '')
|
|
2943
|
+
.replace(/[??!!。.;;]+$/u, '')
|
|
2944
|
+
.trim()
|
|
2945
|
+
.toLowerCase();
|
|
2946
|
+
if (!normalizedMessage) {
|
|
2947
|
+
return false;
|
|
2948
|
+
}
|
|
2949
|
+
if (/\b(?:cfl|courant(?:-friedrichs-lewy)?|mesh(?:ing)?|grid|numerical\s+simulation|characteristic\s+velocity|finite\s+(?:volume|element))\b/iu.test(normalizedMessage)
|
|
2950
|
+
|| /库朗|数值模拟|网格|特征速度|有限元|有限体积|稳定性/u.test(normalizedMessage)) {
|
|
2951
|
+
return true;
|
|
2952
|
+
}
|
|
2953
|
+
const source = normalizeWhitespace(String(glossary || ''))
|
|
2954
|
+
.toLowerCase()
|
|
2955
|
+
.replace(/\\/gu, '');
|
|
2956
|
+
const queryTokens = normalizedMessage.match(/[\p{L}\p{N}]+/gu) || [];
|
|
2957
|
+
const stopwords = new Set([
|
|
2958
|
+
'a', 'an', 'the', 'and', 'are', 'be', 'definition', 'define', 'does', 'how',
|
|
2959
|
+
'is', 'meaning', 'of', 'or', 'what', 'which', 'who', 'with', '这', '个', '是', '的',
|
|
2960
|
+
]);
|
|
2961
|
+
return queryTokens.some((token) => !stopwords.has(token) && source.includes(token));
|
|
2962
|
+
}
|
|
2963
|
+
function isUnrelatedVariableGlossaryClaim(value, message) {
|
|
2964
|
+
return isStandaloneVariableGlossaryClaim(value)
|
|
2965
|
+
&& !definitionQueryMatchesVariableGlossary(value, message);
|
|
2966
|
+
}
|
|
2967
|
+
function publicClaimCarriesCompleteMathContext(value) {
|
|
2968
|
+
const source = normalizeWhitespace(String(value || ''));
|
|
2969
|
+
if (!source
|
|
2970
|
+
|| isStandaloneVariableDefinitionClaim(source)
|
|
2971
|
+
|| isStandaloneVariableGlossaryClaim(source)) {
|
|
2972
|
+
return false;
|
|
2973
|
+
}
|
|
2974
|
+
const expressions = extractNormalizedPublicMathExpressions(source);
|
|
2975
|
+
return expressions.some((expression) => expression.includes('='));
|
|
2976
|
+
}
|
|
2977
|
+
function publicClaimMathKeys(value) {
|
|
2978
|
+
return extractNormalizedPublicMathExpressions(value)
|
|
2979
|
+
.filter((expression) => expression.includes('='));
|
|
2980
|
+
}
|
|
2981
|
+
function extractConflictPredicate(value) {
|
|
2982
|
+
const normalized = normalizeWhitespace(String(value || '')).replace(/[.!?。!?]+$/u, '');
|
|
2983
|
+
const englishMatch = normalized.match(/^(.+?)\s+(?:is|are|was|were|equals?|remains?|contains?|uses?)\s+(.+)$/iu);
|
|
2984
|
+
if (englishMatch) {
|
|
2985
|
+
return {
|
|
2986
|
+
subject: normalizeWhitespace(englishMatch[1]).toLowerCase(),
|
|
2987
|
+
value: normalizeWhitespace(englishMatch[2]).toLowerCase(),
|
|
2988
|
+
};
|
|
2989
|
+
}
|
|
2990
|
+
const chineseMatch = normalized.match(/^(.+?)(?:是|为|包含|使用|位于)(.+)$/u);
|
|
2991
|
+
if (chineseMatch) {
|
|
2992
|
+
return {
|
|
2993
|
+
subject: normalizeWhitespace(chineseMatch[1]).toLowerCase(),
|
|
2994
|
+
value: normalizeWhitespace(chineseMatch[2]).toLowerCase(),
|
|
2995
|
+
};
|
|
2996
|
+
}
|
|
2997
|
+
return null;
|
|
2998
|
+
}
|
|
2999
|
+
function selectConflictDefinitionClaims(candidates) {
|
|
3000
|
+
const requiredCandidates = candidates.filter(({ claim }) => claim.required);
|
|
3001
|
+
const conflictCandidates = requiredCandidates.filter(({ claim }, candidateIndex) => {
|
|
3002
|
+
const left = extractConflictPredicate(claim.statement);
|
|
3003
|
+
if (!left) {
|
|
3004
|
+
return false;
|
|
3005
|
+
}
|
|
3006
|
+
return requiredCandidates.some(({ claim: otherClaim }, otherIndex) => {
|
|
3007
|
+
if (candidateIndex === otherIndex) {
|
|
3008
|
+
return false;
|
|
3009
|
+
}
|
|
3010
|
+
const right = extractConflictPredicate(otherClaim.statement);
|
|
3011
|
+
return Boolean(right && right.subject === left.subject && right.value !== left.value);
|
|
3012
|
+
});
|
|
3013
|
+
});
|
|
3014
|
+
return conflictCandidates.length >= 2 ? conflictCandidates : [];
|
|
3015
|
+
}
|
|
3016
|
+
function findContextualFormulaClaim(formulaOnlyCandidate, candidates) {
|
|
3017
|
+
if (!isFormulaOnlyPublicGraphClaim(formulaOnlyCandidate.statement)) {
|
|
3018
|
+
return null;
|
|
3019
|
+
}
|
|
3020
|
+
const candidateExpressions = extractNormalizedPublicMathExpressions(formulaOnlyCandidate.statement);
|
|
3021
|
+
if (candidateExpressions.length <= 0) {
|
|
3022
|
+
return null;
|
|
3023
|
+
}
|
|
3024
|
+
return candidates.find((candidate) => {
|
|
3025
|
+
if (candidate.claimId === formulaOnlyCandidate.claimId || isFormulaOnlyPublicGraphClaim(candidate.statement)) {
|
|
3026
|
+
return false;
|
|
3027
|
+
}
|
|
3028
|
+
const contextualExpressions = extractNormalizedPublicMathExpressions(candidate.statement);
|
|
3029
|
+
return candidateExpressions.every((expression) => contextualExpressions.includes(expression));
|
|
3030
|
+
}) || null;
|
|
3031
|
+
}
|
|
3032
|
+
function selectPublicGraphPlanStatements(plan, ragContextPack, message) {
|
|
3033
|
+
const claims = Array.isArray(plan?.claims)
|
|
3034
|
+
? plan.claims.filter((claim) => hasBalancedPublicMathDelimiters(String(claim.statement || '')))
|
|
3035
|
+
: [];
|
|
3036
|
+
if (plan?.intent !== 'definition') {
|
|
3037
|
+
return claims;
|
|
3038
|
+
}
|
|
3039
|
+
const eligibleClaims = claims
|
|
3040
|
+
.filter((claim) => claim.role !== 'contrast' && claim.role !== 'analogy')
|
|
3041
|
+
.filter((claim) => !/\b(?:compared?\s+with|versus|vs\.?|plastic\s+cup|metal\s+cup|对比|比较|差异|区别)\b/iu.test(String(claim.statement || '')))
|
|
3042
|
+
.filter((claim) => !/\b(?:remain(?:s)?\s+(?:internal|outside)|public\s+(?:definition|answer)\s+budget|extra\s+sentence|beyond\s+the\s+public)\b/iu.test(String(claim.statement || '')))
|
|
3043
|
+
.filter((claim) => !isIncompletePublicGraphClaim(String(claim.statement || '')))
|
|
3044
|
+
.filter((claim) => !isDefinitionComparisonOrArtifactClaim(String(claim.statement || '')))
|
|
3045
|
+
.filter((claim) => !isFullDocumentDefinitionNoiseStatement(String(claim.statement || '')))
|
|
3046
|
+
.filter((claim) => !isUnrelatedVariableGlossaryClaim(String(claim.statement || ''), String(message || '')));
|
|
3047
|
+
const selectedCandidates = [];
|
|
3048
|
+
const selectedClaimIds = new Set();
|
|
3049
|
+
const appendCandidate = (candidate) => {
|
|
3050
|
+
if (selectedClaimIds.has(candidate.claim.claimId)) {
|
|
3051
|
+
return;
|
|
3052
|
+
}
|
|
3053
|
+
if (formulaOnlyPublicClaimIsAlreadyCovered(candidate.claim, selectedCandidates.map((selectedCandidate) => selectedCandidate.claim))) {
|
|
3054
|
+
return;
|
|
3055
|
+
}
|
|
3056
|
+
selectedCandidates.push(candidate);
|
|
3057
|
+
selectedClaimIds.add(candidate.claim.claimId);
|
|
3058
|
+
};
|
|
3059
|
+
eligibleClaims.forEach((claim, index) => {
|
|
3060
|
+
const contextualClaim = findContextualFormulaClaim(claim, eligibleClaims);
|
|
3061
|
+
appendCandidate({ claim: contextualClaim || claim, index });
|
|
3062
|
+
});
|
|
3063
|
+
let selected = selectedCandidates
|
|
3064
|
+
.sort((left, right) => left.index - right.index)
|
|
3065
|
+
.map(({ claim }) => claim);
|
|
3066
|
+
if (ragContextPack?.fragments?.some((fragment) => fragment.role === 'conflict')) {
|
|
3067
|
+
const conflictClaims = selectConflictDefinitionClaims(selectedCandidates);
|
|
3068
|
+
if (conflictClaims.length >= 2) {
|
|
3069
|
+
return conflictClaims
|
|
3070
|
+
.sort((left, right) => left.index - right.index)
|
|
3071
|
+
.map(({ claim }) => claim);
|
|
3072
|
+
}
|
|
3073
|
+
}
|
|
3074
|
+
if (ragContextPack?.sourceBoundary === 'full_document' && selected.length > 0) {
|
|
3075
|
+
const selectedClaimIds = new Set();
|
|
3076
|
+
const boundedCandidates = [];
|
|
3077
|
+
const appendBoundedCandidate = (candidate) => {
|
|
3078
|
+
if (boundedCandidates.length >= 5 || selectedClaimIds.has(candidate.claim.claimId)) {
|
|
3079
|
+
return;
|
|
3080
|
+
}
|
|
3081
|
+
if (formulaOnlyPublicClaimIsAlreadyCovered(candidate.claim, boundedCandidates.map((entry) => entry.claim))) {
|
|
3082
|
+
return;
|
|
3083
|
+
}
|
|
3084
|
+
boundedCandidates.push(candidate);
|
|
3085
|
+
selectedClaimIds.add(candidate.claim.claimId);
|
|
3086
|
+
};
|
|
3087
|
+
const indexedSelected = selected.map((claim, index) => ({ claim, index }));
|
|
3088
|
+
const definitionCandidates = indexedSelected.filter((entry) => (entry.claim.role === 'definition'
|
|
3089
|
+
&& !isFormulaOnlyPublicGraphClaim(entry.claim.statement)
|
|
3090
|
+
&& !isStandaloneVariableDefinitionClaim(entry.claim.statement)));
|
|
3091
|
+
const leadingDefinition = definitionCandidates.find(entry => extractNormalizedPublicMathExpressions(entry.claim.statement).length === 0)
|
|
3092
|
+
|| definitionCandidates[0];
|
|
3093
|
+
if (leadingDefinition) {
|
|
3094
|
+
appendBoundedCandidate(leadingDefinition);
|
|
3095
|
+
}
|
|
3096
|
+
indexedSelected
|
|
3097
|
+
.filter((entry) => isDefinitionDisambiguationContextStatement(entry.claim.statement))
|
|
3098
|
+
.sort((left, right) => right.claim.priority - left.claim.priority || left.index - right.index)
|
|
3099
|
+
.slice(0, 1)
|
|
3100
|
+
.forEach(appendBoundedCandidate);
|
|
3101
|
+
if (isCompoundLearningDefinitionQuery(String(message || ''))) {
|
|
3102
|
+
indexedSelected
|
|
3103
|
+
.filter((entry) => extractNormalizedPublicMathExpressions(entry.claim.statement).length > 0)
|
|
3104
|
+
.filter((entry) => !publicClaimCarriesCompleteMathContext(entry.claim.statement))
|
|
3105
|
+
.filter((entry) => !isStandaloneVariableDefinitionClaim(entry.claim.statement))
|
|
3106
|
+
.sort((left, right) => right.claim.priority - left.claim.priority || left.index - right.index)
|
|
3107
|
+
.slice(0, 1)
|
|
3108
|
+
.forEach(appendBoundedCandidate);
|
|
3109
|
+
}
|
|
3110
|
+
indexedSelected
|
|
3111
|
+
.filter((entry) => ['composition', 'boundary', 'attribute'].includes(entry.claim.role))
|
|
3112
|
+
.filter((entry) => !isUnrelatedVariableGlossaryClaim(entry.claim.statement, String(message || '')))
|
|
3113
|
+
.slice(0, 2)
|
|
3114
|
+
.forEach(appendBoundedCandidate);
|
|
3115
|
+
// Full-document definition evidence often contains a variable glossary
|
|
3116
|
+
// after each equation. Prefer complete equation contexts across distinct
|
|
3117
|
+
// formulas; standalone `$x$ is ...` clauses are useful only as fallback
|
|
3118
|
+
// when no contextual equation is available.
|
|
3119
|
+
const selectedMathKeys = new Set();
|
|
3120
|
+
let selectedMathContextCount = 0;
|
|
3121
|
+
indexedSelected
|
|
3122
|
+
.filter((entry) => publicClaimCarriesCompleteMathContext(entry.claim.statement))
|
|
3123
|
+
.sort((left, right) => (Number(right.claim.role === 'definition')
|
|
3124
|
+
- Number(left.claim.role === 'definition')
|
|
3125
|
+
|| right.claim.priority - left.claim.priority
|
|
3126
|
+
|| left.index - right.index))
|
|
3127
|
+
.forEach((entry) => {
|
|
3128
|
+
if (selectedMathContextCount >= 2) {
|
|
3129
|
+
return;
|
|
3130
|
+
}
|
|
3131
|
+
const mathKeys = publicClaimMathKeys(entry.claim.statement);
|
|
3132
|
+
if (mathKeys.length <= 0 || mathKeys.some((key) => !selectedMathKeys.has(key))) {
|
|
3133
|
+
appendBoundedCandidate(entry);
|
|
3134
|
+
mathKeys.forEach((key) => selectedMathKeys.add(key));
|
|
3135
|
+
selectedMathContextCount += 1;
|
|
3136
|
+
}
|
|
3137
|
+
});
|
|
3138
|
+
if (boundedCandidates.length <= 1) {
|
|
3139
|
+
indexedSelected
|
|
3140
|
+
.filter((entry) => !isStandaloneVariableDefinitionClaim(entry.claim.statement))
|
|
3141
|
+
.filter((entry) => !isUnrelatedVariableGlossaryClaim(entry.claim.statement, String(message || '')))
|
|
3142
|
+
.filter((entry) => extractNormalizedPublicMathExpressions(entry.claim.statement).length > 0)
|
|
3143
|
+
.forEach((entry) => appendBoundedCandidate(entry));
|
|
3144
|
+
}
|
|
3145
|
+
if (boundedCandidates.length <= 2) {
|
|
3146
|
+
indexedSelected
|
|
3147
|
+
.filter((entry) => entry.claim.role === 'mechanism')
|
|
3148
|
+
.filter((entry) => !isStandaloneVariableDefinitionClaim(entry.claim.statement))
|
|
3149
|
+
.filter((entry) => !isUnrelatedVariableGlossaryClaim(entry.claim.statement, String(message || '')))
|
|
3150
|
+
.filter((entry) => extractNormalizedPublicMathExpressions(entry.claim.statement).length <= 0)
|
|
3151
|
+
.forEach(appendBoundedCandidate);
|
|
3152
|
+
}
|
|
3153
|
+
indexedSelected
|
|
3154
|
+
.filter((entry) => extractNormalizedPublicMathExpressions(entry.claim.statement).length <= 0)
|
|
3155
|
+
.filter((entry) => !isStandaloneVariableDefinitionClaim(entry.claim.statement))
|
|
3156
|
+
.filter((entry) => !isUnrelatedVariableGlossaryClaim(entry.claim.statement, String(message || '')))
|
|
3157
|
+
.filter((entry) => ['composition', 'boundary', 'attribute'].includes(entry.claim.role))
|
|
3158
|
+
.filter((entry) => !isFullDocumentDefinitionNoiseStatement(entry.claim.statement))
|
|
3159
|
+
.forEach(appendBoundedCandidate);
|
|
3160
|
+
// Extraction may classify several independent supported facts as definitions.
|
|
3161
|
+
// Keep those complements within the same cap instead of silently dropping them by role.
|
|
3162
|
+
indexedSelected
|
|
3163
|
+
.filter((entry) => entry.claim.required && entry.claim.role === 'definition')
|
|
3164
|
+
.filter((entry) => !isStandaloneVariableDefinitionClaim(entry.claim.statement))
|
|
3165
|
+
.filter((entry) => !isUnrelatedVariableGlossaryClaim(entry.claim.statement, String(message || '')))
|
|
3166
|
+
.forEach(appendBoundedCandidate);
|
|
3167
|
+
selected = boundedCandidates
|
|
3168
|
+
.sort((left, right) => left.index - right.index)
|
|
3169
|
+
.map(({ claim }) => claim);
|
|
3170
|
+
}
|
|
3171
|
+
return selected;
|
|
3172
|
+
}
|
|
3173
|
+
function projectPublicGraphAnswerPlan(plan, ragContextPack, message) {
|
|
3174
|
+
if (!plan) {
|
|
3175
|
+
return null;
|
|
3176
|
+
}
|
|
3177
|
+
if (plan.intent !== 'definition') {
|
|
3178
|
+
return plan;
|
|
3179
|
+
}
|
|
3180
|
+
const claims = selectPublicGraphPlanStatements(plan, ragContextPack, message);
|
|
3181
|
+
return {
|
|
3182
|
+
...plan,
|
|
3183
|
+
depth: claims.length <= 2 ? 'compact' : 'standard',
|
|
3184
|
+
leadClaimId: claims[0]?.claimId || '',
|
|
3185
|
+
claims,
|
|
3186
|
+
requiredRoles: Array.from(new Set(claims.map((claim) => claim.role))),
|
|
3187
|
+
};
|
|
3188
|
+
}
|
|
3189
|
+
function createEmptyPublicGraphAnswerPlan(plan) {
|
|
3190
|
+
if (!plan) {
|
|
3191
|
+
return null;
|
|
3192
|
+
}
|
|
3193
|
+
return {
|
|
3194
|
+
...plan,
|
|
3195
|
+
depth: 'compact',
|
|
3196
|
+
leadClaimId: '',
|
|
3197
|
+
claims: [],
|
|
3198
|
+
requiredRoles: [],
|
|
3199
|
+
};
|
|
3200
|
+
}
|
|
3201
|
+
function isVerifiedRagConflictDisclosurePlan(context, plan) {
|
|
3202
|
+
const fragments = context.ragContextPack?.fragments || [];
|
|
3203
|
+
const conflictFragmentIds = new Set(fragments
|
|
3204
|
+
.filter((fragment) => fragment.role === 'conflict')
|
|
3205
|
+
.map((fragment) => normalizeWhitespace(String(fragment.fragmentId || '')))
|
|
3206
|
+
.filter(Boolean));
|
|
3207
|
+
if (conflictFragmentIds.size <= 0
|
|
3208
|
+
|| context.ragSufficiencyReview?.degradationState !== 'conflict'
|
|
3209
|
+
|| !plan
|
|
3210
|
+
|| plan.claims.length < 2) {
|
|
3211
|
+
return false;
|
|
3212
|
+
}
|
|
3213
|
+
const conflictClaims = plan.claims.filter((claim) => ((claim.evidenceRefs || []).some((evidence) => conflictFragmentIds.has(normalizeWhitespace(String(evidence.evidenceId || ''))))));
|
|
3214
|
+
const nonConflictClaims = plan.claims.filter((claim) => ((claim.evidenceRefs || []).some((evidence) => !conflictFragmentIds.has(normalizeWhitespace(String(evidence.evidenceId || ''))))));
|
|
3215
|
+
return conflictClaims.length > 0
|
|
3216
|
+
&& nonConflictClaims.length > 0
|
|
3217
|
+
&& plan.claims.every((claim) => (hasBalancedPublicMathDelimiters(String(claim.statement || ''))
|
|
3218
|
+
&& (claim.evidenceRefs || []).some((evidence) => normalizeWhitespace(String(evidence.text || '')).length > 0)));
|
|
3219
|
+
}
|
|
3220
|
+
function discloseRagEvidenceConflicts(context, answer) {
|
|
3221
|
+
const conflicts = (context.ragContextPack?.fragments || []).filter(fragment => (fragment.role === 'conflict' && !fragment.truncated && fragment.citationIds.length > 0));
|
|
3222
|
+
if (conflicts.length === 0)
|
|
3223
|
+
return answer;
|
|
3224
|
+
const useChinese = useChineseAnswerLanguage(context);
|
|
3225
|
+
let publicAnswer = answer;
|
|
3226
|
+
for (const fragment of conflicts) {
|
|
3227
|
+
const heading = fragment.text.split('\n', 1)[0];
|
|
3228
|
+
if (heading.startsWith('Conflicting evidence for ')) {
|
|
3229
|
+
publicAnswer = publicAnswer.split(heading).join(useChinese ? '来源中的冲突记录:' : 'Conflicting source observations:');
|
|
3230
|
+
}
|
|
3231
|
+
}
|
|
3232
|
+
// Claim projection can discard supplemental text. The release boundary owns
|
|
3233
|
+
// this conclusion so slim/full and every transport expose the same uncertainty.
|
|
3234
|
+
const notice = useChinese
|
|
3235
|
+
? '来源中的观测存在冲突,现有证据无法确定哪一项正确。'
|
|
3236
|
+
: 'The source observations conflict; the available evidence does not establish which is correct.';
|
|
3237
|
+
return publicAnswer.startsWith(notice) ? publicAnswer : `${notice}\n\n${publicAnswer}`;
|
|
3238
|
+
}
|
|
2737
3239
|
function collectCitationBackedRagFragments(context) {
|
|
2738
3240
|
if (!hasUsableRagEvidenceContext(context)) {
|
|
2739
3241
|
return [];
|
|
@@ -2748,10 +3250,14 @@ function claimLooksLikeReleaseScaffolding(claim) {
|
|
|
2748
3250
|
return true;
|
|
2749
3251
|
}
|
|
2750
3252
|
return (/\b(?:grounded by|key evidence|citations?|rag context|retrieval|planner)\b/iu.test(normalizedClaim)
|
|
3253
|
+
|| /^(?:建议学习路径|推荐学习路径|Suggested learning path)\s*[::]/iu.test(normalizedClaim)
|
|
3254
|
+
|| /\b(?:available evidence|evidence coverage)\s+is\s+still\s+partial\b/iu.test(normalizedClaim)
|
|
3255
|
+
|| /当前证据覆盖仍然有限|回答只使用已命中的材料|回答只陈述已有材料能够支持的内容/u.test(normalizedClaim)
|
|
3256
|
+
|| /^(?:当前图中的关键路径是|The strongest graph path runs through)(?:\s|$)/iu.test(normalizedClaim)
|
|
2751
3257
|
|| INTERNAL_DIAGNOSTIC_FRAGMENTS.some((fragment) => normalizedClaim.includes(fragment)));
|
|
2752
3258
|
}
|
|
2753
3259
|
function splitDraftAnswerClaims(answer) {
|
|
2754
|
-
return stripMarkdownScaffolding(answer)
|
|
3260
|
+
return stripMarkdownScaffolding(stripLearningRoutePresentation(answer))
|
|
2755
3261
|
.split(POLARITY_SENTENCE_SPLIT_PATTERN)
|
|
2756
3262
|
.map((claim) => normalizeWhitespace(claim))
|
|
2757
3263
|
.filter((claim) => (claim.length >= 8
|
|
@@ -2830,6 +3336,12 @@ function evaluateRagClaimCitationSupport(context) {
|
|
|
2830
3336
|
}
|
|
2831
3337
|
const claims = splitDraftAnswerClaims(context.draftAnswer).filter((claim) => (collectClaimSupportFeatures(claim).length >= RAG_CLAIM_CITATION_SUPPORT_MIN_FEATURES));
|
|
2832
3338
|
const citationBackedFragments = collectCitationBackedRagFragments(context);
|
|
3339
|
+
const citationBackedPlanEvidence = (context.graphAnswerPlan?.claims || [])
|
|
3340
|
+
.flatMap((claim) => claim.evidenceRefs || [])
|
|
3341
|
+
.filter((evidence) => (normalizeWhitespace(String(evidence.text || '')).length > 0
|
|
3342
|
+
&& Array.isArray(evidence.citationIds)
|
|
3343
|
+
&& evidence.citationIds.some((citationId) => normalizeWhitespace(String(citationId || '')).length > 0)));
|
|
3344
|
+
const citationBackedEvidenceCount = citationBackedFragments.length + citationBackedPlanEvidence.length;
|
|
2833
3345
|
if (claims.length <= 0) {
|
|
2834
3346
|
return {
|
|
2835
3347
|
passed: true,
|
|
@@ -2840,7 +3352,7 @@ function evaluateRagClaimCitationSupport(context) {
|
|
|
2840
3352
|
citationBackedFragmentCount: citationBackedFragments.length,
|
|
2841
3353
|
};
|
|
2842
3354
|
}
|
|
2843
|
-
if (
|
|
3355
|
+
if (citationBackedEvidenceCount <= 0) {
|
|
2844
3356
|
return {
|
|
2845
3357
|
passed: false,
|
|
2846
3358
|
applicable: true,
|
|
@@ -2855,6 +3367,7 @@ function evaluateRagClaimCitationSupport(context) {
|
|
|
2855
3367
|
normalizeWhitespace(String(fragment.title || '').trim()),
|
|
2856
3368
|
normalizeWhitespace(String(fragment.text || '').trim()),
|
|
2857
3369
|
].filter(Boolean).join(' '))
|
|
3370
|
+
.concat(citationBackedPlanEvidence.map((evidence) => normalizeWhitespace(evidence.text)))
|
|
2858
3371
|
.filter(Boolean)
|
|
2859
3372
|
.join(' ');
|
|
2860
3373
|
const supportFeatures = new Set(collectClaimSupportFeatures(supportText));
|
|
@@ -2879,7 +3392,7 @@ function evaluateRagClaimCitationSupport(context) {
|
|
|
2879
3392
|
supportedClaimCount,
|
|
2880
3393
|
weakClaims,
|
|
2881
3394
|
unsupportedClaims,
|
|
2882
|
-
citationBackedFragmentCount:
|
|
3395
|
+
citationBackedFragmentCount: citationBackedEvidenceCount,
|
|
2883
3396
|
};
|
|
2884
3397
|
}
|
|
2885
3398
|
function buildRagClaimCitationSupportMessage(result) {
|
|
@@ -2920,6 +3433,120 @@ function isMetaDocumentaryAnswer(answer) {
|
|
|
2920
3433
|
return (ENGLISH_META_DOCUMENTARY_PATTERN.test(normalizedAnswer)
|
|
2921
3434
|
|| CHINESE_META_DOCUMENTARY_PATTERN.test(normalizedAnswer));
|
|
2922
3435
|
}
|
|
3436
|
+
function normalizeQuerySubject(value) {
|
|
3437
|
+
return normalizeWhitespace(String(value || ''))
|
|
3438
|
+
.replace(/^["'“”‘’`]+|["'“”‘’`]+$/gu, '')
|
|
3439
|
+
.replace(/^(?:a|an|the)\s+/iu, '')
|
|
3440
|
+
.replace(/[??!!。.;;:,,]+$/u, '')
|
|
3441
|
+
.trim();
|
|
3442
|
+
}
|
|
3443
|
+
function stripDefinitionQuestionPredicate(value) {
|
|
3444
|
+
return normalizeWhitespace(value)
|
|
3445
|
+
.replace(/^(?:(?:a|an|the)\s+)?(?:prerequisite|precondition|requirement|dependency|dependencies)\s+(?:for|of)\s+/iu, '')
|
|
3446
|
+
.replace(/\s+(?:made\s+of|composed\s+of|consists?\s+of|used\s+for|used\s+to|designed\s+for|designed\s+to|intended\s+for|located\s+in|found\s+in|contains?|includes?|depends?\s+on)\b.*$/iu, '')
|
|
3447
|
+
.trim();
|
|
3448
|
+
}
|
|
3449
|
+
function querySubjectFeatures(value) {
|
|
3450
|
+
return collectLexicalFeatures(value)
|
|
3451
|
+
.map((feature) => String(feature || '').trim().toLowerCase())
|
|
3452
|
+
.filter((feature) => feature.length > 0 && !QUERY_SUBJECT_STOPWORDS.has(feature));
|
|
3453
|
+
}
|
|
3454
|
+
function compactSubject(value) {
|
|
3455
|
+
return normalizeQuerySubject(value)
|
|
3456
|
+
.toLowerCase()
|
|
3457
|
+
.replace(/[^\p{L}\p{N}]+/gu, '');
|
|
3458
|
+
}
|
|
3459
|
+
function scoreQuerySubjectAgainstLabel(subject, label) {
|
|
3460
|
+
const normalizedSubject = normalizeQuerySubject(subject);
|
|
3461
|
+
const normalizedLabel = normalizeQuerySubject(label);
|
|
3462
|
+
if (!normalizedSubject || !normalizedLabel) {
|
|
3463
|
+
return 0;
|
|
3464
|
+
}
|
|
3465
|
+
const subjectCompact = compactSubject(normalizedSubject);
|
|
3466
|
+
const labelCompact = compactSubject(normalizedLabel);
|
|
3467
|
+
if (!subjectCompact || !labelCompact) {
|
|
3468
|
+
return 0;
|
|
3469
|
+
}
|
|
3470
|
+
if (subjectCompact === labelCompact) {
|
|
3471
|
+
return 1;
|
|
3472
|
+
}
|
|
3473
|
+
// Parenthetical bilingual titles and qualified headings are valid aliases.
|
|
3474
|
+
if (labelCompact.includes(subjectCompact)) {
|
|
3475
|
+
return 0.95;
|
|
3476
|
+
}
|
|
3477
|
+
const subjectFeatures = querySubjectFeatures(normalizedSubject);
|
|
3478
|
+
const labelFeatures = new Set(querySubjectFeatures(normalizedLabel));
|
|
3479
|
+
if (subjectFeatures.length <= 0 || labelFeatures.size <= 0) {
|
|
3480
|
+
return 0;
|
|
3481
|
+
}
|
|
3482
|
+
const overlap = subjectFeatures.filter((feature) => labelFeatures.has(feature)).length;
|
|
3483
|
+
return Number((overlap / subjectFeatures.length).toFixed(4));
|
|
3484
|
+
}
|
|
3485
|
+
function collectQuerySubjectEvidenceLabels(context) {
|
|
3486
|
+
const labels = [
|
|
3487
|
+
...(context.ragContextPack?.fragments || [])
|
|
3488
|
+
.filter((fragment) => fragment.role !== 'graph_neighbor_support' && fragment.role !== 'background')
|
|
3489
|
+
.flatMap((fragment) => [fragment.title || '', fragment.sourcePath || '', fragment.documentId || '']),
|
|
3490
|
+
...context.citations.flatMap((citation) => [citation.title || '', citation.sourcePath || '', citation.documentId || '']),
|
|
3491
|
+
...context.knowledgePoints.map((point) => point.title || ''),
|
|
3492
|
+
]
|
|
3493
|
+
.map((label) => normalizeQuerySubject(String(label || '')))
|
|
3494
|
+
.filter(Boolean);
|
|
3495
|
+
return Array.from(new Set(labels));
|
|
3496
|
+
}
|
|
3497
|
+
function evaluateQuerySubjectAlignment(context) {
|
|
3498
|
+
const subject = isDefinitionIntentQuery(context.message)
|
|
3499
|
+
? normalizeQuerySubject(stripDefinitionQuestionPredicate((0, answerTaskPlan_1.extractDefinitionQuerySubject)(context.message)))
|
|
3500
|
+
: '';
|
|
3501
|
+
if (!subject || querySubjectFeatures(subject).length <= 0) {
|
|
3502
|
+
return {
|
|
3503
|
+
passed: true,
|
|
3504
|
+
applicable: false,
|
|
3505
|
+
subject,
|
|
3506
|
+
subjectFeatures: querySubjectFeatures(subject),
|
|
3507
|
+
matchedLabels: [],
|
|
3508
|
+
bestScore: 0,
|
|
3509
|
+
};
|
|
3510
|
+
}
|
|
3511
|
+
const labels = collectQuerySubjectEvidenceLabels(context);
|
|
3512
|
+
const scoredLabels = labels
|
|
3513
|
+
.map((label) => ({ label, score: scoreQuerySubjectAgainstLabel(subject, label) }))
|
|
3514
|
+
.filter((entry) => entry.score > 0)
|
|
3515
|
+
.sort((left, right) => right.score - left.score);
|
|
3516
|
+
const attributeQuery = /^(.+?)\s+(?:for|of)\s+(.+)$/iu.exec(subject);
|
|
3517
|
+
if ((scoredLabels[0]?.score || 0) < 0.9 && attributeQuery
|
|
3518
|
+
&& labels.some(label => scoreQuerySubjectAgainstLabel(attributeQuery[2], label) >= 0.9)) {
|
|
3519
|
+
// A title can name an entity while its body states the requested attribute.
|
|
3520
|
+
// Require both witnesses; sharing an attribute with a different entity is insufficient.
|
|
3521
|
+
const attributeSubject = `${attributeQuery[2]} ${attributeQuery[1]}`;
|
|
3522
|
+
for (const candidate of buildSupportCandidates(context)) {
|
|
3523
|
+
for (const frame of extractSubjectFrames(candidate.text)) {
|
|
3524
|
+
const score = scoreQuerySubjectAgainstLabel(attributeSubject, frame.subject);
|
|
3525
|
+
if (score >= 0.9)
|
|
3526
|
+
scoredLabels.push({ label: `${candidate.label}: ${frame.subject}`, score });
|
|
3527
|
+
}
|
|
3528
|
+
}
|
|
3529
|
+
scoredLabels.sort((left, right) => right.score - left.score);
|
|
3530
|
+
}
|
|
3531
|
+
const bestScore = scoredLabels[0]?.score || 0;
|
|
3532
|
+
return {
|
|
3533
|
+
passed: bestScore >= 0.9,
|
|
3534
|
+
applicable: true,
|
|
3535
|
+
subject,
|
|
3536
|
+
subjectFeatures: querySubjectFeatures(subject),
|
|
3537
|
+
matchedLabels: scoredLabels.filter((entry) => entry.score >= 0.9).map((entry) => entry.label).slice(0, 3),
|
|
3538
|
+
bestScore,
|
|
3539
|
+
};
|
|
3540
|
+
}
|
|
3541
|
+
function buildQuerySubjectAlignmentMessage(result) {
|
|
3542
|
+
if (!result.applicable) {
|
|
3543
|
+
return 'The query did not expose a stable definition subject, so subject alignment stayed conservative.';
|
|
3544
|
+
}
|
|
3545
|
+
if (result.passed) {
|
|
3546
|
+
return `Evidence titles aligned with the requested subject "${result.subject}" (best match ${Math.round(result.bestScore * 100)}%).`;
|
|
3547
|
+
}
|
|
3548
|
+
return `Evidence titles did not establish the requested subject "${result.subject}" (best match ${Math.round(result.bestScore * 100)}%); unrelated entities must not be released as the answer.`;
|
|
3549
|
+
}
|
|
2923
3550
|
function evaluateQueryIntentAlignment(context) {
|
|
2924
3551
|
if (!isDefinitionIntentQuery(context.message)) {
|
|
2925
3552
|
return {
|
|
@@ -2945,7 +3572,10 @@ function evaluateQueryIntentAlignment(context) {
|
|
|
2945
3572
|
};
|
|
2946
3573
|
}
|
|
2947
3574
|
return {
|
|
2948
|
-
|
|
3575
|
+
// Full mode intentionally emits a report preface and section headers;
|
|
3576
|
+
// those are allowed as long as the answer also contains the grounded
|
|
3577
|
+
// definition frame. Slim mode keeps the stricter documentary guard.
|
|
3578
|
+
passed: context.responseMode === 'full' || !isMetaDocumentaryAnswer(context.draftAnswer),
|
|
2949
3579
|
applicable: true,
|
|
2950
3580
|
comparableFrameCount: supportFrames.length,
|
|
2951
3581
|
supportFrame: primarySupportFrame,
|
|
@@ -3284,7 +3914,7 @@ function evaluateStructuredComparisonConsistency(context) {
|
|
|
3284
3914
|
};
|
|
3285
3915
|
}
|
|
3286
3916
|
function evaluateSubjectConsistency(context) {
|
|
3287
|
-
const answerFrames = extractSubjectFrames(context.draftAnswer);
|
|
3917
|
+
const answerFrames = extractSubjectFrames(removeSupportedSourceLabels(context, context.draftAnswer));
|
|
3288
3918
|
if (answerFrames.length <= 0) {
|
|
3289
3919
|
return {
|
|
3290
3920
|
passed: true,
|
|
@@ -3638,7 +4268,15 @@ function evaluateStateConsistency(context) {
|
|
|
3638
4268
|
};
|
|
3639
4269
|
}
|
|
3640
4270
|
function evaluatePolarityConsistency(context) {
|
|
3641
|
-
const
|
|
4271
|
+
const sourceHeadings = new Set([
|
|
4272
|
+
...context.knowledgePoints.map(point => point.title),
|
|
4273
|
+
...context.citations.map(citation => citation.title),
|
|
4274
|
+
...(context.ragContextPack?.fragments || []).flatMap(fragment => [fragment.title || '', ...(fragment.headingPath || [])]),
|
|
4275
|
+
].map(title => normalizeWhitespace(title).toLowerCase()).filter(Boolean));
|
|
4276
|
+
// A known source title is identity/presentation, not a positive assertion.
|
|
4277
|
+
// Unknown headings remain subject to the same contradiction checks as prose.
|
|
4278
|
+
const omitSourceTitleHeadings = (text) => text.replace(/^ {0,3}#{1,6}[\t ]+(.+?)(?:[\t ]+#+[\t ]*)?$/gmu, (heading, title) => (sourceHeadings.has(normalizeWhitespace(title).toLowerCase()) ? '' : heading));
|
|
4279
|
+
const answerSentences = extractPolaritySentences(omitSourceTitleHeadings(removeSupportedSourceLabels(context, context.draftAnswer)), context.responseMode);
|
|
3642
4280
|
if (answerSentences.length <= 0) {
|
|
3643
4281
|
return {
|
|
3644
4282
|
passed: true,
|
|
@@ -3646,7 +4284,7 @@ function evaluatePolarityConsistency(context) {
|
|
|
3646
4284
|
conflicts: [],
|
|
3647
4285
|
};
|
|
3648
4286
|
}
|
|
3649
|
-
const supportSentences = buildSupportCandidates(context).flatMap((candidate) => (extractPolaritySentences(candidate.text).map((sentence) => ({
|
|
4287
|
+
const supportSentences = buildSupportCandidates(context).flatMap((candidate) => (extractPolaritySentences(omitSourceTitleHeadings(candidate.text), context.responseMode).map((sentence) => ({
|
|
3650
4288
|
...sentence,
|
|
3651
4289
|
label: candidate.label,
|
|
3652
4290
|
}))));
|
|
@@ -3812,20 +4450,55 @@ function evaluateTemporalValidityConsistency(context) {
|
|
|
3812
4450
|
},
|
|
3813
4451
|
};
|
|
3814
4452
|
}
|
|
3815
|
-
function checkPublicSurfaceContraction(answer) {
|
|
3816
|
-
|
|
3817
|
-
|
|
4453
|
+
function checkPublicSurfaceContraction(answer, responseMode = 'slim') {
|
|
4454
|
+
// Learning-route bullets are an explicit typed deliverable, not internal
|
|
4455
|
+
// diagnostics. Exclude that section from the legacy contraction heuristic.
|
|
4456
|
+
const normalizedAnswer = stripLearningRoutePresentation(String(answer || ''));
|
|
4457
|
+
const forbiddenScaffolding = (/\bGrounded by\b/i.test(normalizedAnswer)
|
|
4458
|
+
|| /\bKey evidence\b/i.test(normalizedAnswer)
|
|
4459
|
+
|| /\bCitations?:\b/i.test(normalizedAnswer));
|
|
4460
|
+
if (forbiddenScaffolding) {
|
|
3818
4461
|
return false;
|
|
3819
4462
|
}
|
|
3820
|
-
return
|
|
3821
|
-
|
|
3822
|
-
|
|
3823
|
-
|
|
4463
|
+
return responseMode === 'full' || !/\n\s*[-*]\s+/u.test(normalizedAnswer);
|
|
4464
|
+
}
|
|
4465
|
+
function finalPublicAnswerPassesSemanticSafety(context, answer) {
|
|
4466
|
+
const finalContext = {
|
|
4467
|
+
...context,
|
|
4468
|
+
draftAnswer: String(answer || '').replace(/\r?\n/gu, ' '),
|
|
4469
|
+
};
|
|
4470
|
+
const checks = [
|
|
4471
|
+
['structured', evaluateStructuredConsistency(finalContext).passed],
|
|
4472
|
+
['structuredComparison', evaluateStructuredComparisonConsistency(finalContext).passed],
|
|
4473
|
+
['attribute', evaluateAttributeConsistency(finalContext).passed],
|
|
4474
|
+
['containment', evaluateContainmentConsistency(finalContext).passed],
|
|
4475
|
+
['composition', evaluateCompositionConsistency(finalContext).passed],
|
|
4476
|
+
['purpose', evaluatePurposeConsistency(finalContext).passed],
|
|
4477
|
+
['dependency', evaluateDependencyConsistency(finalContext).passed],
|
|
4478
|
+
['location', evaluateLocationConsistency(finalContext).passed],
|
|
4479
|
+
['subject', evaluateSubjectConsistency(finalContext).passed],
|
|
4480
|
+
['state', evaluateStateConsistency(finalContext).passed],
|
|
4481
|
+
['polarity', evaluatePolarityConsistency(finalContext).passed],
|
|
4482
|
+
['graphCausal', evaluateGraphCausalConsistency(finalContext).passed],
|
|
4483
|
+
['graphOrder', evaluateGraphOrderConsistency(finalContext).passed],
|
|
4484
|
+
['graphComparison', evaluateGraphComparisonConsistency(finalContext).passed],
|
|
4485
|
+
];
|
|
4486
|
+
return checks.every(([, passed]) => passed);
|
|
4487
|
+
}
|
|
4488
|
+
function finalPublicAnswerPassesReleaseContract(context, answer, requireSemanticSafety, responseMode = 'slim') {
|
|
4489
|
+
return hasBalancedPublicMathDelimiters(answer)
|
|
4490
|
+
&& checkPublicSurfaceContraction(answer, responseMode)
|
|
4491
|
+
&& (!requireSemanticSafety || finalPublicAnswerPassesSemanticSafety(context, answer));
|
|
3824
4492
|
}
|
|
3825
|
-
function buildDecision(groundedEvidenceAvailable, groundingAlignmentPassed, queryIntentAlignmentPassed, structuredConsistencyPassed, structuredComparisonConsistencyPassed, attributeConsistencyPassed, containmentConsistencyPassed, compositionConsistencyPassed, purposeConsistencyPassed, dependencyConsistencyPassed, locationConsistencyPassed, subjectConsistencyPassed, stateConsistencyPassed, polarityConsistencyPassed, graphCausalConsistencyPassed, graphOrderConsistencyPassed, graphComparisonConsistencyPassed, temporalValidityConsistencyPassed, ragAnswerCompletenessPassed, ragClaimCitationSupportPassed, leakedInternalFragments, publicSurfaceContracted) {
|
|
4493
|
+
function buildDecision(groundedEvidenceAvailable, groundingAlignmentPassed, queryIntentAlignmentPassed, querySubjectAlignmentPassed, structuredConsistencyPassed, structuredComparisonConsistencyPassed, attributeConsistencyPassed, containmentConsistencyPassed, compositionConsistencyPassed, purposeConsistencyPassed, dependencyConsistencyPassed, locationConsistencyPassed, subjectConsistencyPassed, stateConsistencyPassed, polarityConsistencyPassed, graphCausalConsistencyPassed, graphOrderConsistencyPassed, graphComparisonConsistencyPassed, temporalValidityConsistencyPassed, ragAnswerCompletenessPassed, ragClaimCitationSupportPassed, graphAnswerPlanCoveragePassed, leakedInternalFragments, publicSurfaceContracted) {
|
|
3826
4494
|
if (!groundedEvidenceAvailable) {
|
|
3827
4495
|
return 'abstain';
|
|
3828
4496
|
}
|
|
4497
|
+
// A grounded answer for the wrong entity is unsafe to revise into shape:
|
|
4498
|
+
// the only valid public outcome is an explicit abstention.
|
|
4499
|
+
if (!querySubjectAlignmentPassed) {
|
|
4500
|
+
return 'abstain';
|
|
4501
|
+
}
|
|
3829
4502
|
if (!groundingAlignmentPassed
|
|
3830
4503
|
|| !queryIntentAlignmentPassed
|
|
3831
4504
|
|| !structuredConsistencyPassed
|
|
@@ -3845,6 +4518,7 @@ function buildDecision(groundedEvidenceAvailable, groundingAlignmentPassed, quer
|
|
|
3845
4518
|
|| !temporalValidityConsistencyPassed
|
|
3846
4519
|
|| !ragAnswerCompletenessPassed
|
|
3847
4520
|
|| !ragClaimCitationSupportPassed
|
|
4521
|
+
|| !graphAnswerPlanCoveragePassed
|
|
3848
4522
|
|| leakedInternalFragments.length > 0
|
|
3849
4523
|
|| !publicSurfaceContracted) {
|
|
3850
4524
|
return 'revise';
|
|
@@ -3862,8 +4536,64 @@ function buildReason(decision, groundedEvidenceAvailable) {
|
|
|
3862
4536
|
? 'Draft answer was downgraded even though some evidence existed.'
|
|
3863
4537
|
: 'Draft answer lacked grounded evidence, so the public answer was downgraded to a concise abstention.';
|
|
3864
4538
|
}
|
|
4539
|
+
function evaluateLearningRouteSubjectAlignment(plan) {
|
|
4540
|
+
const taskPlan = plan?.answerTaskPlan;
|
|
4541
|
+
const route = taskPlan?.learningRoute || [];
|
|
4542
|
+
if (!taskPlan?.subtasks.some((subtask) => subtask.kind === 'learning_route')) {
|
|
4543
|
+
return true;
|
|
4544
|
+
}
|
|
4545
|
+
const subjectKey = normalizeWhitespace(taskPlan.primarySubject)
|
|
4546
|
+
.replace(/[^\p{L}\p{N}]+/gu, '')
|
|
4547
|
+
.toLowerCase();
|
|
4548
|
+
if (!subjectKey || route.length < 3) {
|
|
4549
|
+
return false;
|
|
4550
|
+
}
|
|
4551
|
+
return route.some((node) => {
|
|
4552
|
+
const titleKey = normalizeWhitespace(node.title)
|
|
4553
|
+
.replace(/[^\p{L}\p{N}]+/gu, '')
|
|
4554
|
+
.toLowerCase();
|
|
4555
|
+
return node.role === 'core' && Boolean(titleKey && (titleKey === subjectKey
|
|
4556
|
+
|| titleKey.includes(subjectKey)
|
|
4557
|
+
|| subjectKey.includes(titleKey)));
|
|
4558
|
+
});
|
|
4559
|
+
}
|
|
4560
|
+
function evaluateLearningRouteOrderEvidence(plan) {
|
|
4561
|
+
const taskPlan = plan?.answerTaskPlan;
|
|
4562
|
+
const route = taskPlan?.learningRoute || [];
|
|
4563
|
+
if (!taskPlan?.subtasks.some((subtask) => subtask.kind === 'learning_route')) {
|
|
4564
|
+
return true;
|
|
4565
|
+
}
|
|
4566
|
+
const explicitOrderingPresent = route.some((node) => (node.orderingBasis === 'explicit_prerequisite'
|
|
4567
|
+
|| node.orderingBasis === 'explicit_sequence'));
|
|
4568
|
+
return route.length >= 3
|
|
4569
|
+
&& route.every((node, index) => node.order === index + 1 && node.evidenceRefs.length > 0)
|
|
4570
|
+
&& route.some((node) => node.role === 'core')
|
|
4571
|
+
&& (!explicitOrderingPresent || route.some((node) => node.orderingBasis === 'explicit_prerequisite'));
|
|
4572
|
+
}
|
|
4573
|
+
function buildAnswerTaskCoverageMessage(result) {
|
|
4574
|
+
if (!result.applicable) {
|
|
4575
|
+
return 'No compound answer-task contract was active for this request.';
|
|
4576
|
+
}
|
|
4577
|
+
if (result.passed) {
|
|
4578
|
+
return `The public answer covered all ${result.coveredSubtaskIds.length} required answer deliverable(s).`;
|
|
4579
|
+
}
|
|
4580
|
+
return `The public answer is missing required deliverable(s): ${result.missingRequiredSubtaskIds.join(', ')}.`;
|
|
4581
|
+
}
|
|
4582
|
+
function appendAnswerTaskDeliverables(answer, plan, answerLanguage) {
|
|
4583
|
+
const normalizedAnswer = String(answer || '').replace(/\r\n?/gu, '\n').trim();
|
|
4584
|
+
if (!plan || !plan.subtasks.some((subtask) => subtask.kind === 'learning_route') || plan.learningRoute.length <= 0) {
|
|
4585
|
+
return normalizedAnswer;
|
|
4586
|
+
}
|
|
4587
|
+
const useChinese = answerLanguage === 'zh'
|
|
4588
|
+
|| (answerLanguage !== 'en' && /[\u3400-\u9fff]/u.test(`${normalizedAnswer} ${plan.primarySubject}`));
|
|
4589
|
+
const routeText = (0, answerTaskPlan_1.formatLearningRouteAnswer)(plan, useChinese);
|
|
4590
|
+
if (!routeText || normalizedAnswer.includes(routeText)) {
|
|
4591
|
+
return normalizedAnswer;
|
|
4592
|
+
}
|
|
4593
|
+
return (0, answerTaskPlan_1.formatTaskAwareAnswer)([normalizedAnswer], plan, useChinese);
|
|
4594
|
+
}
|
|
3865
4595
|
function reviewAnswerRelease(context) {
|
|
3866
|
-
const draftAnswer =
|
|
4596
|
+
const draftAnswer = normalizePublicAnswerSurface(context.draftAnswer);
|
|
3867
4597
|
const groundedEvidenceAvailable = context.knowledgePoints.length > 0
|
|
3868
4598
|
|| context.citations.length > 0
|
|
3869
4599
|
|| hasUsableRagEvidenceContext(context);
|
|
@@ -3889,6 +4619,19 @@ function reviewAnswerRelease(context) {
|
|
|
3889
4619
|
comparableFrameCount: 0,
|
|
3890
4620
|
supportFrame: null,
|
|
3891
4621
|
};
|
|
4622
|
+
const querySubjectAlignment = groundedEvidenceAvailable
|
|
4623
|
+
? evaluateQuerySubjectAlignment({
|
|
4624
|
+
...context,
|
|
4625
|
+
draftAnswer,
|
|
4626
|
+
})
|
|
4627
|
+
: {
|
|
4628
|
+
passed: true,
|
|
4629
|
+
applicable: false,
|
|
4630
|
+
subject: '',
|
|
4631
|
+
subjectFeatures: [],
|
|
4632
|
+
matchedLabels: [],
|
|
4633
|
+
bestScore: 0,
|
|
4634
|
+
};
|
|
3892
4635
|
const structuredConsistency = groundedEvidenceAvailable
|
|
3893
4636
|
? evaluateStructuredConsistency({
|
|
3894
4637
|
...context,
|
|
@@ -4066,23 +4809,34 @@ function reviewAnswerRelease(context) {
|
|
|
4066
4809
|
unsupportedClaims: [],
|
|
4067
4810
|
citationBackedFragmentCount: 0,
|
|
4068
4811
|
};
|
|
4069
|
-
const
|
|
4812
|
+
const draftAnswerTaskCoverage = groundedEvidenceAvailable
|
|
4813
|
+
? (0, answerTaskPlan_1.reviewAnswerTaskCoverage)(draftAnswer, context.graphAnswerPlan?.answerTaskPlan)
|
|
4814
|
+
: (0, answerTaskPlan_1.reviewAnswerTaskCoverage)('', null);
|
|
4815
|
+
const publicGraphAnswerPlan = projectPublicGraphAnswerPlan(context.graphAnswerPlan, context.ragContextPack, context.message);
|
|
4816
|
+
const verifiedRagConflictDisclosurePlan = isVerifiedRagConflictDisclosurePlan(context, publicGraphAnswerPlan);
|
|
4817
|
+
const definitionProjectionIntegrityPassed = !(context.graphAnswerPlan?.intent === 'definition'
|
|
4818
|
+
&& (context.graphAnswerPlan.claims || []).some((claim) => claim.required)
|
|
4819
|
+
&& (publicGraphAnswerPlan?.claims || []).length <= 0);
|
|
4820
|
+
const draftGraphAnswerPlanCoverage = groundedEvidenceAvailable
|
|
4821
|
+
? (0, graphAnswerCoverage_1.reviewGraphAnswerCoverage)(draftAnswer, publicGraphAnswerPlan)
|
|
4822
|
+
: (0, graphAnswerCoverage_1.reviewGraphAnswerCoverage)('', null);
|
|
4823
|
+
const publicSurfaceContracted = checkPublicSurfaceContraction(draftAnswer, context.responseMode);
|
|
4070
4824
|
const graphSupportCount = context.graphContext
|
|
4071
4825
|
? ((Array.isArray(context.graphContext.relationSummaries) ? context.graphContext.relationSummaries.length : 0)
|
|
4072
4826
|
+ (Array.isArray(context.graphContext.connectionPaths) ? context.graphContext.connectionPaths.length : 0)
|
|
4073
4827
|
+ (Array.isArray(context.graphContext.supportingAtomIds) ? context.graphContext.supportingAtomIds.length : 0))
|
|
4074
4828
|
: 0;
|
|
4075
4829
|
const graphSupportSufficient = context.knowledgePoints.length <= 0 || graphSupportCount > 0 || Boolean(context.graphContext?.anchorAtomId);
|
|
4076
|
-
const
|
|
4830
|
+
const draftDecision = buildDecision(groundedEvidenceAvailable, groundingAlignment.passed, queryIntentAlignment.passed, querySubjectAlignment.passed, structuredConsistency.passed, structuredComparisonConsistency.passed, attributeConsistency.passed, containmentConsistency.passed, compositionConsistency.passed, purposeConsistency.passed, dependencyConsistency.passed, locationConsistency.passed, subjectConsistency.passed, stateConsistency.passed, polarityConsistency.passed, graphCausalConsistency.passed, graphOrderConsistency.passed, graphComparisonConsistency.passed, temporalValidityConsistency.passed, ragAnswerCompleteness.passed, ragClaimCitationSupport.passed, draftGraphAnswerPlanCoverage.passed, leakedInternalFragments, publicSurfaceContracted);
|
|
4077
4831
|
const primaryGraphCausalConflict = graphCausalConsistency.conflicts[0];
|
|
4078
4832
|
const primaryGraphOrderConflict = graphOrderConsistency.conflicts[0];
|
|
4079
4833
|
const primaryGraphComparisonConflict = graphComparisonConsistency.conflicts[0];
|
|
4080
4834
|
const primaryStructuredComparisonConflict = structuredComparisonConsistency.conflicts[0];
|
|
4081
4835
|
const primaryTemporalValidityConflict = temporalValidityConsistency.conflict;
|
|
4082
|
-
|
|
4083
|
-
? buildAbstentionAnswer(context
|
|
4084
|
-
:
|
|
4085
|
-
? (primaryTemporalValidityConflict
|
|
4836
|
+
let revisedPublicAnswer = draftDecision === 'abstain'
|
|
4837
|
+
? buildAbstentionAnswer(context)
|
|
4838
|
+
: draftDecision === 'revise'
|
|
4839
|
+
? appendAnswerTaskDeliverables((primaryTemporalValidityConflict
|
|
4086
4840
|
? buildTemporalValidityRevisionAnswer(context, primaryTemporalValidityConflict)
|
|
4087
4841
|
: primaryGraphCausalConflict
|
|
4088
4842
|
? buildGraphCausalRevisionAnswer(context, primaryGraphCausalConflict)
|
|
@@ -4094,8 +4848,133 @@ function reviewAnswerRelease(context) {
|
|
|
4094
4848
|
? buildStructuredComparisonRevisionAnswer(context, primaryStructuredComparisonConflict)
|
|
4095
4849
|
: (!queryIntentAlignment.passed && queryIntentAlignment.supportFrame)
|
|
4096
4850
|
? buildDefinitionIntentRevisionAnswer(context, queryIntentAlignment.supportFrame)
|
|
4097
|
-
: buildGroundedRevisionAnswer(context))
|
|
4098
|
-
: buildReleasedPublicAnswer(context, draftAnswer));
|
|
4851
|
+
: buildGroundedRevisionAnswer(context)), context.graphAnswerPlan?.answerTaskPlan, context.answerLanguage)
|
|
4852
|
+
: appendAnswerTaskDeliverables(buildReleasedPublicAnswer(context, draftAnswer), context.graphAnswerPlan?.answerTaskPlan, context.answerLanguage);
|
|
4853
|
+
// Every claim-level consistency gate is a semantic safety boundary. A plan may
|
|
4854
|
+
// improve coverage, but must never overwrite a correction from any of them.
|
|
4855
|
+
const requiresSafetyCorrection = !verifiedRagConflictDisclosurePlan && [
|
|
4856
|
+
querySubjectAlignment.passed,
|
|
4857
|
+
groundingAlignment.passed,
|
|
4858
|
+
structuredConsistency.passed,
|
|
4859
|
+
structuredComparisonConsistency.passed,
|
|
4860
|
+
attributeConsistency.passed,
|
|
4861
|
+
containmentConsistency.passed,
|
|
4862
|
+
compositionConsistency.passed,
|
|
4863
|
+
purposeConsistency.passed,
|
|
4864
|
+
dependencyConsistency.passed,
|
|
4865
|
+
locationConsistency.passed,
|
|
4866
|
+
subjectConsistency.passed,
|
|
4867
|
+
stateConsistency.passed,
|
|
4868
|
+
polarityConsistency.passed,
|
|
4869
|
+
graphCausalConsistency.passed,
|
|
4870
|
+
graphOrderConsistency.passed,
|
|
4871
|
+
graphComparisonConsistency.passed,
|
|
4872
|
+
temporalValidityConsistency.passed,
|
|
4873
|
+
].some((passed) => !passed);
|
|
4874
|
+
if (context.responseMode === 'full'
|
|
4875
|
+
&& !requiresSafetyCorrection
|
|
4876
|
+
&& leakedInternalFragments.length <= 0
|
|
4877
|
+
&& hasBalancedPublicMathDelimiters(draftAnswer)) {
|
|
4878
|
+
// Full mode owns the report surface. A non-safety gate (for example a
|
|
4879
|
+
// slim projection coverage heuristic) must not collapse it back to a
|
|
4880
|
+
// single definition sentence.
|
|
4881
|
+
revisedPublicAnswer = draftAnswer;
|
|
4882
|
+
}
|
|
4883
|
+
const candidatePublicPlanCoverage = groundedEvidenceAvailable
|
|
4884
|
+
? (0, graphAnswerCoverage_1.reviewGraphAnswerCoverage)(revisedPublicAnswer, publicGraphAnswerPlan)
|
|
4885
|
+
: (0, graphAnswerCoverage_1.reviewGraphAnswerCoverage)('', null);
|
|
4886
|
+
const requiresDefinitionProjection = Boolean(publicGraphAnswerPlan?.intent === 'definition'
|
|
4887
|
+
&& (!candidatePublicPlanCoverage.passed
|
|
4888
|
+
|| !hasBalancedPublicMathDelimiters(revisedPublicAnswer)
|
|
4889
|
+
|| isDefinitionComparisonOrArtifactClaim(revisedPublicAnswer)));
|
|
4890
|
+
const shouldPreservePublicPlan = Boolean(publicGraphAnswerPlan
|
|
4891
|
+
&& definitionProjectionIntegrityPassed
|
|
4892
|
+
&& !requiresSafetyCorrection
|
|
4893
|
+
&& context.responseMode !== 'full'
|
|
4894
|
+
&& (requiresDefinitionProjection
|
|
4895
|
+
|| (context.ragContextPack?.fragments || []).length > 0));
|
|
4896
|
+
let effectivePublicGraphAnswerPlan = requiresSafetyCorrection
|
|
4897
|
+
? createEmptyPublicGraphAnswerPlan(context.graphAnswerPlan)
|
|
4898
|
+
: publicGraphAnswerPlan;
|
|
4899
|
+
let decision = definitionProjectionIntegrityPassed
|
|
4900
|
+
? draftDecision
|
|
4901
|
+
: 'abstain';
|
|
4902
|
+
if (decision === 'release'
|
|
4903
|
+
&& draftAnswerTaskCoverage.applicable
|
|
4904
|
+
&& !draftAnswerTaskCoverage.passed) {
|
|
4905
|
+
decision = 'revise';
|
|
4906
|
+
}
|
|
4907
|
+
let publicAnswer = context.responseMode === 'full'
|
|
4908
|
+
? revisedPublicAnswer
|
|
4909
|
+
: preservePlannedGraphAnswerClaims(revisedPublicAnswer, shouldPreservePublicPlan ? effectivePublicGraphAnswerPlan : null);
|
|
4910
|
+
if (decision !== 'abstain') {
|
|
4911
|
+
publicAnswer = appendAnswerTaskDeliverables(publicAnswer, context.graphAnswerPlan?.answerTaskPlan, context.answerLanguage);
|
|
4912
|
+
}
|
|
4913
|
+
let graphAnswerPlanCoverage = groundedEvidenceAvailable
|
|
4914
|
+
? (0, graphAnswerCoverage_1.reviewGraphAnswerCoverage)(publicAnswer, effectivePublicGraphAnswerPlan)
|
|
4915
|
+
: (0, graphAnswerCoverage_1.reviewGraphAnswerCoverage)('', null);
|
|
4916
|
+
if (!definitionProjectionIntegrityPassed) {
|
|
4917
|
+
publicAnswer = buildAbstentionAnswer(context);
|
|
4918
|
+
effectivePublicGraphAnswerPlan = createEmptyPublicGraphAnswerPlan(context.graphAnswerPlan);
|
|
4919
|
+
graphAnswerPlanCoverage = (0, graphAnswerCoverage_1.reviewGraphAnswerCoverage)(publicAnswer, effectivePublicGraphAnswerPlan);
|
|
4920
|
+
}
|
|
4921
|
+
if (groundedEvidenceAvailable
|
|
4922
|
+
&& effectivePublicGraphAnswerPlan
|
|
4923
|
+
&& graphAnswerPlanCoverage.applicable
|
|
4924
|
+
&& !graphAnswerPlanCoverage.passed) {
|
|
4925
|
+
if (context.responseMode === 'full') {
|
|
4926
|
+
decision = decision === 'abstain' ? 'abstain' : 'revise';
|
|
4927
|
+
}
|
|
4928
|
+
else {
|
|
4929
|
+
const coveragePreservingAnswer = preservePlannedGraphAnswerClaims('', effectivePublicGraphAnswerPlan);
|
|
4930
|
+
const coveragePreservingReview = (0, graphAnswerCoverage_1.reviewGraphAnswerCoverage)(coveragePreservingAnswer, effectivePublicGraphAnswerPlan);
|
|
4931
|
+
if (coveragePreservingReview.passed) {
|
|
4932
|
+
publicAnswer = coveragePreservingAnswer;
|
|
4933
|
+
graphAnswerPlanCoverage = coveragePreservingReview;
|
|
4934
|
+
decision = 'revise';
|
|
4935
|
+
}
|
|
4936
|
+
else {
|
|
4937
|
+
publicAnswer = buildAbstentionAnswer(context);
|
|
4938
|
+
graphAnswerPlanCoverage = (0, graphAnswerCoverage_1.reviewGraphAnswerCoverage)(publicAnswer, effectivePublicGraphAnswerPlan);
|
|
4939
|
+
decision = 'abstain';
|
|
4940
|
+
}
|
|
4941
|
+
}
|
|
4942
|
+
}
|
|
4943
|
+
if (decision !== 'abstain') {
|
|
4944
|
+
publicAnswer = discloseRagEvidenceConflicts(context, publicAnswer);
|
|
4945
|
+
}
|
|
4946
|
+
if (decision !== 'abstain'
|
|
4947
|
+
&& !finalPublicAnswerPassesReleaseContract(context, publicAnswer, Boolean(shouldPreservePublicPlan
|
|
4948
|
+
&& effectivePublicGraphAnswerPlan
|
|
4949
|
+
&& effectivePublicGraphAnswerPlan.claims.length > 0
|
|
4950
|
+
&& !verifiedRagConflictDisclosurePlan), context.responseMode)) {
|
|
4951
|
+
const correctedAnswer = normalizeWhitespace(revisedPublicAnswer);
|
|
4952
|
+
effectivePublicGraphAnswerPlan = createEmptyPublicGraphAnswerPlan(context.graphAnswerPlan);
|
|
4953
|
+
if (correctedAnswer && finalPublicAnswerPassesReleaseContract(context, correctedAnswer, false, context.responseMode)) {
|
|
4954
|
+
publicAnswer = correctedAnswer;
|
|
4955
|
+
graphAnswerPlanCoverage = (0, graphAnswerCoverage_1.reviewGraphAnswerCoverage)(publicAnswer, effectivePublicGraphAnswerPlan);
|
|
4956
|
+
decision = 'revise';
|
|
4957
|
+
}
|
|
4958
|
+
else {
|
|
4959
|
+
publicAnswer = buildAbstentionAnswer(context);
|
|
4960
|
+
graphAnswerPlanCoverage = (0, graphAnswerCoverage_1.reviewGraphAnswerCoverage)(publicAnswer, effectivePublicGraphAnswerPlan);
|
|
4961
|
+
decision = 'abstain';
|
|
4962
|
+
}
|
|
4963
|
+
}
|
|
4964
|
+
const answerTaskCoverage = groundedEvidenceAvailable
|
|
4965
|
+
? (0, answerTaskPlan_1.reviewAnswerTaskCoverage)(publicAnswer, context.graphAnswerPlan?.answerTaskPlan)
|
|
4966
|
+
: (0, answerTaskPlan_1.reviewAnswerTaskCoverage)('', null);
|
|
4967
|
+
const learningRouteSubjectAlignment = groundedEvidenceAvailable
|
|
4968
|
+
? evaluateLearningRouteSubjectAlignment(context.graphAnswerPlan)
|
|
4969
|
+
: true;
|
|
4970
|
+
const learningRouteOrderEvidence = groundedEvidenceAvailable
|
|
4971
|
+
? evaluateLearningRouteOrderEvidence(context.graphAnswerPlan)
|
|
4972
|
+
: true;
|
|
4973
|
+
if (decision === 'release'
|
|
4974
|
+
&& answerTaskCoverage.applicable
|
|
4975
|
+
&& (!answerTaskCoverage.passed || !learningRouteSubjectAlignment || !learningRouteOrderEvidence)) {
|
|
4976
|
+
decision = 'revise';
|
|
4977
|
+
}
|
|
4099
4978
|
const abstentionHygienePassed = decision !== 'abstain'
|
|
4100
4979
|
|| (collectLeakedInternalFragments(publicAnswer).length <= 0
|
|
4101
4980
|
&& !/\bretrieval\b/i.test(publicAnswer)
|
|
@@ -4137,6 +5016,13 @@ function reviewAnswerRelease(context) {
|
|
|
4137
5016
|
: 'No grounded definition frame was available, so intent alignment stayed conservative.')
|
|
4138
5017
|
: 'No evidence was available, so definition-intent alignment was not evaluated.',
|
|
4139
5018
|
},
|
|
5019
|
+
{
|
|
5020
|
+
gateId: 'query_subject_alignment',
|
|
5021
|
+
passed: querySubjectAlignment.passed,
|
|
5022
|
+
message: groundedEvidenceAvailable
|
|
5023
|
+
? buildQuerySubjectAlignmentMessage(querySubjectAlignment)
|
|
5024
|
+
: 'No evidence was available, so query-subject alignment was not evaluated.',
|
|
5025
|
+
},
|
|
4140
5026
|
{
|
|
4141
5027
|
gateId: 'claim_structured_consistency',
|
|
4142
5028
|
passed: structuredConsistency.passed,
|
|
@@ -4280,6 +5166,56 @@ function reviewAnswerRelease(context) {
|
|
|
4280
5166
|
passed: ragClaimCitationSupport.passed,
|
|
4281
5167
|
message: buildRagClaimCitationSupportMessage(ragClaimCitationSupport),
|
|
4282
5168
|
},
|
|
5169
|
+
{
|
|
5170
|
+
gateId: 'graph_answer_plan_coverage',
|
|
5171
|
+
passed: graphAnswerPlanCoverage.passed,
|
|
5172
|
+
message: !graphAnswerPlanCoverage.applicable
|
|
5173
|
+
? 'No required graph-answer claims were active for this answer.'
|
|
5174
|
+
: graphAnswerPlanCoverage.passed
|
|
5175
|
+
? `The public answer covered all ${graphAnswerPlanCoverage.requiredClaimIds.length} required graph-answer claim(s).`
|
|
5176
|
+
: `The public answer omitted required graph-answer claims: ${graphAnswerPlanCoverage.missingRequiredClaimIds.join(', ')}.`,
|
|
5177
|
+
},
|
|
5178
|
+
{
|
|
5179
|
+
gateId: 'subtask_coverage',
|
|
5180
|
+
passed: draftAnswerTaskCoverage.passed && answerTaskCoverage.passed,
|
|
5181
|
+
message: draftAnswerTaskCoverage.passed
|
|
5182
|
+
? buildAnswerTaskCoverageMessage(answerTaskCoverage)
|
|
5183
|
+
: `Draft answer ${buildAnswerTaskCoverageMessage(draftAnswerTaskCoverage).replace(/^The public answer /u, '').replace(/^公共回答/u, '')}`,
|
|
5184
|
+
},
|
|
5185
|
+
{
|
|
5186
|
+
gateId: 'learning_route_subject_alignment',
|
|
5187
|
+
passed: learningRouteSubjectAlignment,
|
|
5188
|
+
message: learningRouteSubjectAlignment
|
|
5189
|
+
? 'Learning-route nodes retained the requested subject as the core anchor.'
|
|
5190
|
+
: 'Learning-route nodes did not establish the requested subject as a core anchor.',
|
|
5191
|
+
},
|
|
5192
|
+
{
|
|
5193
|
+
gateId: 'learning_route_order_evidence',
|
|
5194
|
+
passed: learningRouteOrderEvidence,
|
|
5195
|
+
message: learningRouteOrderEvidence
|
|
5196
|
+
? 'Learning-route order is sequential and every node retains evidence references; explicit prerequisite order is used when available.'
|
|
5197
|
+
: 'Learning-route order or evidence references were insufficient for release; unsupported prerequisite order was not inferred.',
|
|
5198
|
+
},
|
|
5199
|
+
{
|
|
5200
|
+
gateId: 'deliverable_completeness',
|
|
5201
|
+
passed: draftAnswerTaskCoverage.passed
|
|
5202
|
+
&& answerTaskCoverage.passed
|
|
5203
|
+
&& learningRouteSubjectAlignment
|
|
5204
|
+
&& learningRouteOrderEvidence,
|
|
5205
|
+
message: draftAnswerTaskCoverage.passed
|
|
5206
|
+
&& answerTaskCoverage.passed
|
|
5207
|
+
&& learningRouteSubjectAlignment
|
|
5208
|
+
&& learningRouteOrderEvidence
|
|
5209
|
+
? 'All required answer deliverables were present and evidence-bounded.'
|
|
5210
|
+
: 'At least one required answer deliverable was missing or not evidence-bounded.',
|
|
5211
|
+
},
|
|
5212
|
+
{
|
|
5213
|
+
gateId: 'definition_projection_integrity',
|
|
5214
|
+
passed: definitionProjectionIntegrityPassed,
|
|
5215
|
+
message: definitionProjectionIntegrityPassed
|
|
5216
|
+
? 'Definition-plan projection retained at least one eligible required public claim when projection was required.'
|
|
5217
|
+
: 'Required definition claims projected to zero eligible public claims, so the answer was downgraded to abstention.',
|
|
5218
|
+
},
|
|
4283
5219
|
{
|
|
4284
5220
|
gateId: 'public_surface_contraction',
|
|
4285
5221
|
passed: publicSurfaceContracted,
|
|
@@ -4311,6 +5247,11 @@ function reviewAnswerRelease(context) {
|
|
|
4311
5247
|
revised: publicAnswer !== draftAnswer,
|
|
4312
5248
|
originalAnswer: draftAnswer,
|
|
4313
5249
|
publicAnswer,
|
|
5250
|
+
publicGraphAnswerPlan: effectivePublicGraphAnswerPlan || undefined,
|
|
5251
|
+
auditGraphAnswerPlan: context.graphAnswerPlan || undefined,
|
|
5252
|
+
graphAnswerCoverage: graphAnswerPlanCoverage,
|
|
5253
|
+
answerTaskCoverage,
|
|
5254
|
+
draftAnswerTaskCoverage,
|
|
4314
5255
|
reason: buildReason(decision, groundedEvidenceAvailable),
|
|
4315
5256
|
failedGateIds,
|
|
4316
5257
|
leakedInternalFragments,
|