ARCH: довести planner-selected entity chains и ambiguity follow-up
This commit is contained in:
@@ -1,5 +1,7 @@
|
||||
"use strict";
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.readAssistantMcpDiscoveryEntityResolutionStatus = readAssistantMcpDiscoveryEntityResolutionStatus;
|
||||
exports.readAssistantMcpDiscoveryEntityAmbiguityCandidates = readAssistantMcpDiscoveryEntityAmbiguityCandidates;
|
||||
exports.readAssistantMcpDiscoveryEntityCandidates = readAssistantMcpDiscoveryEntityCandidates;
|
||||
exports.readAssistantMcpDiscoveryPilotScope = readAssistantMcpDiscoveryPilotScope;
|
||||
exports.readAssistantMcpDiscoveryMetadataRouteFamily = readAssistantMcpDiscoveryMetadataRouteFamily;
|
||||
@@ -97,6 +99,16 @@ function readAssistantMcpDiscoveryDerivedEntityResolution(debug) {
|
||||
const pilot = toRecordObject(bridge?.pilot);
|
||||
return toRecordObject(pilot?.derived_entity_resolution);
|
||||
}
|
||||
function readAssistantMcpDiscoveryEntityResolutionStatus(debug, toNonEmptyString = fallbackToNonEmptyString) {
|
||||
return toNonEmptyString(readAssistantMcpDiscoveryDerivedEntityResolution(debug)?.resolution_status);
|
||||
}
|
||||
function readAssistantMcpDiscoveryEntityAmbiguityCandidates(debug, toNonEmptyString = fallbackToNonEmptyString) {
|
||||
const values = readAssistantMcpDiscoveryDerivedEntityResolution(debug)?.ambiguity_candidates;
|
||||
if (!Array.isArray(values)) {
|
||||
return [];
|
||||
}
|
||||
return values.map((item) => toNonEmptyString(item)).filter((item) => Boolean(item));
|
||||
}
|
||||
function collectAssistantMcpDiscoveryEntityCandidates(debug, toNonEmptyString = fallbackToNonEmptyString) {
|
||||
const result = [];
|
||||
const resolution = readAssistantMcpDiscoveryDerivedEntityResolution(debug);
|
||||
|
||||
+11
-1
@@ -27,6 +27,12 @@ function uniqueStrings(values) {
|
||||
}
|
||||
return result;
|
||||
}
|
||||
function formatNamedChoiceList(values) {
|
||||
return uniqueStrings(values)
|
||||
.slice(0, 6)
|
||||
.map((value, index) => `${index + 1}. ${value}`)
|
||||
.join("; ");
|
||||
}
|
||||
function isInternalMechanicsLine(value) {
|
||||
const text = value.toLowerCase();
|
||||
return (text.includes("primitive") ||
|
||||
@@ -273,6 +279,10 @@ function headlineFor(mode, pilot) {
|
||||
}
|
||||
function nextStepFor(mode, pilot) {
|
||||
if (isEntityResolutionPilot(pilot) && mode === "needs_clarification") {
|
||||
const ambiguityCandidates = pilot.derived_entity_resolution?.ambiguity_candidates ?? [];
|
||||
if (ambiguityCandidates.length > 0) {
|
||||
return `Уточните, какой именно контрагент нужен: ${formatNamedChoiceList(ambiguityCandidates)}. Можно ответить названием или номером варианта.`;
|
||||
}
|
||||
return "Уточните точное название контрагента или добавьте ИНН, и я продолжу уже по нужной сущности в 1С.";
|
||||
}
|
||||
if (isEntityResolutionPilot(pilot) && mode === "confirmed_with_bounded_inference") {
|
||||
@@ -449,7 +459,7 @@ function derivedEntityResolutionInferenceLine(pilot) {
|
||||
return "Сейчас подтверждено только заземление сущности по каталогу 1С; документы, движения и денежные показатели по ней еще не проверялись.";
|
||||
}
|
||||
if (resolution.resolution_status === "ambiguous" && resolution.ambiguity_candidates.length > 0) {
|
||||
return `В checked catalog slice есть несколько близких кандидатов: ${resolution.ambiguity_candidates.join(", ")}. Без уточнения нельзя честно выбрать одного контрагента для следующего шага.`;
|
||||
return `В каталоге 1С нашлось несколько близких кандидатов: ${formatNamedChoiceList(resolution.ambiguity_candidates)}. Без уточнения нельзя честно выбрать одного контрагента для следующего шага.`;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
+110
-16
@@ -655,6 +655,46 @@ function summarizeEntityResolutionRows(result) {
|
||||
}
|
||||
return `${result.fetched_rows} MCP catalog rows fetched for entity search`;
|
||||
}
|
||||
function entityResolutionFollowupStepLimitation() {
|
||||
return "Entity-resolution could not continue because the checked catalog search step did not return a confirmed slice";
|
||||
}
|
||||
function buildEntityResolutionResolveProbeResult(input) {
|
||||
if (!input.resolution) {
|
||||
return {
|
||||
primitive_id: "resolve_entity_reference",
|
||||
status: "ok",
|
||||
rows_received: input.queryResult.fetched_rows,
|
||||
rows_matched: 0,
|
||||
limitation: null
|
||||
};
|
||||
}
|
||||
if (input.resolution.resolution_status === "resolved") {
|
||||
return {
|
||||
primitive_id: "resolve_entity_reference",
|
||||
status: "ok",
|
||||
rows_received: input.queryResult.fetched_rows,
|
||||
rows_matched: 1,
|
||||
limitation: null
|
||||
};
|
||||
}
|
||||
return {
|
||||
primitive_id: "resolve_entity_reference",
|
||||
status: "ok",
|
||||
rows_received: input.queryResult.fetched_rows,
|
||||
rows_matched: 0,
|
||||
limitation: null
|
||||
};
|
||||
}
|
||||
function buildEntityResolutionCoverageProbeResult(input) {
|
||||
const resolved = input.resolution?.resolution_status === "resolved";
|
||||
return {
|
||||
primitive_id: "probe_coverage",
|
||||
status: "ok",
|
||||
rows_received: 1,
|
||||
rows_matched: resolved ? 1 : 0,
|
||||
limitation: null
|
||||
};
|
||||
}
|
||||
function metadataRowText(row, keys) {
|
||||
for (const key of keys) {
|
||||
const text = toNonEmptyString(row[key]);
|
||||
@@ -1644,28 +1684,82 @@ async function executeAssistantMcpDiscoveryPilot(planner, deps = DEFAULT_DEPS) {
|
||||
reason_codes: reasonCodes
|
||||
};
|
||||
}
|
||||
let derivedEntityResolution = null;
|
||||
for (const step of dryRun.execution_steps) {
|
||||
if (step.primitive_id !== "search_business_entity") {
|
||||
skippedPrimitives.push(step.primitive_id);
|
||||
probeResults.push(skippedProbeResult(step, "pilot_only_executes_search_business_entity"));
|
||||
if (step.primitive_id === "search_business_entity") {
|
||||
queryResult = await runtimeDeps.executeAddressMcpQuery({
|
||||
query: ENTITY_RESOLUTION_COUNTERPARTY_QUERY_TEMPLATE.replaceAll("__LIMIT__", String(ENTITY_RESOLUTION_COUNTERPARTY_LOOKUP_LIMIT)),
|
||||
limit: ENTITY_RESOLUTION_COUNTERPARTY_LOOKUP_LIMIT
|
||||
});
|
||||
pushUnique(executedPrimitives, step.primitive_id);
|
||||
probeResults.push(queryResultToProbeResult(step.primitive_id, queryResult));
|
||||
if (queryResult.error) {
|
||||
pushUnique(queryLimitations, queryResult.error);
|
||||
pushReason(reasonCodes, "pilot_search_business_entity_mcp_error");
|
||||
}
|
||||
else {
|
||||
pushReason(reasonCodes, "pilot_search_business_entity_mcp_executed");
|
||||
derivedEntityResolution = deriveEntityResolution(queryResult, requestedEntity);
|
||||
}
|
||||
continue;
|
||||
}
|
||||
queryResult = await runtimeDeps.executeAddressMcpQuery({
|
||||
query: ENTITY_RESOLUTION_COUNTERPARTY_QUERY_TEMPLATE.replaceAll("__LIMIT__", String(ENTITY_RESOLUTION_COUNTERPARTY_LOOKUP_LIMIT)),
|
||||
limit: ENTITY_RESOLUTION_COUNTERPARTY_LOOKUP_LIMIT
|
||||
});
|
||||
pushUnique(executedPrimitives, step.primitive_id);
|
||||
probeResults.push(queryResultToProbeResult(step.primitive_id, queryResult));
|
||||
if (queryResult.error) {
|
||||
pushUnique(queryLimitations, queryResult.error);
|
||||
pushReason(reasonCodes, "pilot_search_business_entity_mcp_error");
|
||||
if (step.primitive_id === "resolve_entity_reference") {
|
||||
if (!queryResult || queryResult.error) {
|
||||
skippedPrimitives.push(step.primitive_id);
|
||||
probeResults.push(skippedProbeResult(step, entityResolutionFollowupStepLimitation()));
|
||||
continue;
|
||||
}
|
||||
if (!derivedEntityResolution) {
|
||||
derivedEntityResolution = deriveEntityResolution(queryResult, requestedEntity);
|
||||
}
|
||||
pushUnique(executedPrimitives, step.primitive_id);
|
||||
probeResults.push(buildEntityResolutionResolveProbeResult({
|
||||
queryResult,
|
||||
resolution: derivedEntityResolution
|
||||
}));
|
||||
if (derivedEntityResolution?.resolution_status === "resolved") {
|
||||
pushReason(reasonCodes, "pilot_resolve_entity_reference_from_catalog_rows");
|
||||
}
|
||||
else if (derivedEntityResolution?.resolution_status === "ambiguous") {
|
||||
pushReason(reasonCodes, "pilot_resolve_entity_reference_requires_clarification");
|
||||
}
|
||||
else {
|
||||
pushReason(reasonCodes, "pilot_resolve_entity_reference_not_confirmed");
|
||||
}
|
||||
continue;
|
||||
}
|
||||
else {
|
||||
pushReason(reasonCodes, "pilot_search_business_entity_mcp_executed");
|
||||
if (step.primitive_id === "probe_coverage") {
|
||||
if (!queryResult || queryResult.error) {
|
||||
skippedPrimitives.push(step.primitive_id);
|
||||
probeResults.push(skippedProbeResult(step, entityResolutionFollowupStepLimitation()));
|
||||
continue;
|
||||
}
|
||||
if (!derivedEntityResolution) {
|
||||
derivedEntityResolution = deriveEntityResolution(queryResult, requestedEntity);
|
||||
}
|
||||
pushUnique(executedPrimitives, step.primitive_id);
|
||||
probeResults.push(buildEntityResolutionCoverageProbeResult({
|
||||
resolution: derivedEntityResolution
|
||||
}));
|
||||
pushReason(reasonCodes, "pilot_probe_coverage_executed_for_entity_resolution");
|
||||
if (derivedEntityResolution?.resolution_status === "resolved") {
|
||||
pushReason(reasonCodes, "pilot_entity_resolution_grounding_stable_for_downstream_probe");
|
||||
}
|
||||
else if (derivedEntityResolution?.resolution_status === "ambiguous") {
|
||||
pushReason(reasonCodes, "pilot_entity_resolution_coverage_requires_clarification");
|
||||
}
|
||||
else {
|
||||
pushReason(reasonCodes, "pilot_entity_resolution_coverage_not_confirmed");
|
||||
}
|
||||
continue;
|
||||
}
|
||||
skippedPrimitives.push(step.primitive_id);
|
||||
probeResults.push(skippedProbeResult(step, "pilot_entity_resolution_step_not_implemented"));
|
||||
}
|
||||
const sourceRowsSummary = queryResult ? summarizeEntityResolutionRows(queryResult) : null;
|
||||
const derivedEntityResolution = deriveEntityResolution(queryResult, requestedEntity);
|
||||
if (!derivedEntityResolution && queryResult && !queryResult.error) {
|
||||
derivedEntityResolution = deriveEntityResolution(queryResult, requestedEntity);
|
||||
}
|
||||
if (derivedEntityResolution?.resolution_status === "resolved") {
|
||||
pushReason(reasonCodes, "pilot_derived_entity_resolution_from_catalog_rows");
|
||||
}
|
||||
@@ -1683,7 +1777,7 @@ async function executeAssistantMcpDiscoveryPilot(planner, deps = DEFAULT_DEPS) {
|
||||
unknownFacts: buildEntityResolutionUnknownFacts(derivedEntityResolution, requestedEntity),
|
||||
sourceRowsSummary,
|
||||
queryLimitations,
|
||||
recommendedNextProbe: "resolve_entity_reference"
|
||||
recommendedNextProbe: null
|
||||
});
|
||||
return {
|
||||
schema_version: exports.ASSISTANT_MCP_DISCOVERY_PILOT_EXECUTOR_SCHEMA_VERSION,
|
||||
|
||||
+64
-4
@@ -138,11 +138,63 @@ function readDiscoveryTurnMeaning(entryPoint) {
|
||||
const turnInput = toRecordObject(entryPoint?.turn_input);
|
||||
return toRecordObject(turnInput?.turn_meaning_ref);
|
||||
}
|
||||
function readTruthAnswerShape(input) {
|
||||
const directShape = toRecordObject(input.addressRuntimeMeta?.answer_shape_contract);
|
||||
if (directShape) {
|
||||
return directShape;
|
||||
}
|
||||
const truthAnswerPolicy = toRecordObject(input.addressRuntimeMeta?.assistant_truth_answer_policy_v1);
|
||||
return toRecordObject(truthAnswerPolicy?.answer_shape);
|
||||
}
|
||||
function hasEffectivelyFactualAddressReply(input) {
|
||||
if (toNonEmptyString(input.currentReplyType) === "factual") {
|
||||
return true;
|
||||
}
|
||||
const truthAnswerShape = readTruthAnswerShape(input);
|
||||
return toNonEmptyString(truthAnswerShape?.reply_type) === "factual";
|
||||
}
|
||||
function readStateTransitionReasonCodes(input) {
|
||||
const directTransition = toRecordObject(input.addressRuntimeMeta?.assistant_state_transition_v1);
|
||||
const fallbackTransition = toRecordObject(input.addressRuntimeMeta?.state_transition_contract);
|
||||
const stateTransition = directTransition ?? fallbackTransition;
|
||||
if (!stateTransition || !Array.isArray(stateTransition.reason_codes)) {
|
||||
return [];
|
||||
}
|
||||
return stateTransition.reason_codes
|
||||
.map((item) => toNonEmptyString(item))
|
||||
.filter((item) => Boolean(item));
|
||||
}
|
||||
function hasRuntimeAdjustedExactReply(input, entryPoint) {
|
||||
if (!isDiscoveryReadyAddressCandidate(input, entryPoint)) {
|
||||
return false;
|
||||
}
|
||||
if (!hasEffectivelyFactualAddressReply(input)) {
|
||||
return false;
|
||||
}
|
||||
const truthGateStatus = toNonEmptyString(input.addressRuntimeMeta?.truth_gate_contract_status);
|
||||
const truthAnswerPolicy = toRecordObject(input.addressRuntimeMeta?.assistant_truth_answer_policy_v1);
|
||||
const truthGate = toRecordObject(truthAnswerPolicy?.truth_gate);
|
||||
const sourceTruthGateStatus = toNonEmptyString(truthGate?.source_truth_gate_status);
|
||||
const coverageStatus = toNonEmptyString(truthGate?.coverage_status);
|
||||
const groundingStatus = toNonEmptyString(truthGate?.grounding_status);
|
||||
const hasFullConfirmedTruth = truthGateStatus === "full_confirmed" ||
|
||||
sourceTruthGateStatus === "full_confirmed" ||
|
||||
(coverageStatus === "full" && groundingStatus === "grounded");
|
||||
if (!hasFullConfirmedTruth) {
|
||||
return false;
|
||||
}
|
||||
const truthAnswerShape = readTruthAnswerShape(input);
|
||||
const capabilityContractId = toNonEmptyString(truthAnswerShape?.capability_contract_id);
|
||||
if (!capabilityContractId) {
|
||||
return false;
|
||||
}
|
||||
return readStateTransitionReasonCodes(input).some((reason) => /^intent_adjusted_to_.+_followup_context$/i.test(reason));
|
||||
}
|
||||
function hasAlignedFactualAddressReply(input, entryPoint) {
|
||||
if (!isDiscoveryReadyAddressCandidate(input, entryPoint)) {
|
||||
return false;
|
||||
}
|
||||
if (toNonEmptyString(input.currentReplyType) !== "factual") {
|
||||
if (!hasEffectivelyFactualAddressReply(input)) {
|
||||
return false;
|
||||
}
|
||||
const detectedIntent = toNonEmptyString(input.addressRuntimeMeta?.detected_intent);
|
||||
@@ -152,7 +204,10 @@ function hasSemanticConflictWithDiscoveryTurnMeaning(input, entryPoint) {
|
||||
if (!isDiscoveryReadyAddressCandidate(input, entryPoint)) {
|
||||
return false;
|
||||
}
|
||||
if (toNonEmptyString(input.currentReplyType) !== "factual") {
|
||||
if (!hasEffectivelyFactualAddressReply(input)) {
|
||||
return false;
|
||||
}
|
||||
if (hasRuntimeAdjustedExactReply(input, entryPoint)) {
|
||||
return false;
|
||||
}
|
||||
const detectedIntent = toNonEmptyString(input.addressRuntimeMeta?.detected_intent);
|
||||
@@ -166,7 +221,7 @@ function hasSemanticConflictWithDiscoveryTurnMeaning(input, entryPoint) {
|
||||
return !isDetectedIntentAlignedWithTurnMeaning(detectedIntent, turnMeaning);
|
||||
}
|
||||
function hasMatchedFactualAddressContinuationTarget(input, entryPoint) {
|
||||
if (toNonEmptyString(input.currentReplyType) !== "factual") {
|
||||
if (!hasEffectivelyFactualAddressReply(input)) {
|
||||
return false;
|
||||
}
|
||||
if (hasSemanticConflictWithDiscoveryTurnMeaning(input, entryPoint)) {
|
||||
@@ -182,7 +237,7 @@ function hasFullConfirmedFactualAddressReply(input, entryPoint) {
|
||||
if (!isDiscoveryReadyAddressCandidate(input, entryPoint)) {
|
||||
return false;
|
||||
}
|
||||
if (toNonEmptyString(input.currentReplyType) !== "factual") {
|
||||
if (!hasEffectivelyFactualAddressReply(input)) {
|
||||
return false;
|
||||
}
|
||||
if (hasSemanticConflictWithDiscoveryTurnMeaning(input, entryPoint)) {
|
||||
@@ -214,6 +269,7 @@ function applyAssistantMcpDiscoveryResponsePolicy(input) {
|
||||
const semanticConflictWithDiscoveryTurnMeaning = hasSemanticConflictWithDiscoveryTurnMeaning(input, entryPoint);
|
||||
const matchedFactualAddressContinuationTarget = hasMatchedFactualAddressContinuationTarget(input, entryPoint);
|
||||
const fullConfirmedFactualAddressReply = hasFullConfirmedFactualAddressReply(input, entryPoint);
|
||||
const runtimeAdjustedExactReply = hasRuntimeAdjustedExactReply(input, entryPoint);
|
||||
if (!entryPoint) {
|
||||
pushReason(reasonCodes, "mcp_discovery_response_policy_no_entry_point");
|
||||
}
|
||||
@@ -241,6 +297,9 @@ function applyAssistantMcpDiscoveryResponsePolicy(input) {
|
||||
if (fullConfirmedFactualAddressReply) {
|
||||
pushReason(reasonCodes, "mcp_discovery_response_policy_keep_full_confirmed_factual_address_reply");
|
||||
}
|
||||
if (runtimeAdjustedExactReply) {
|
||||
pushReason(reasonCodes, "mcp_discovery_response_policy_keep_runtime_adjusted_exact_reply_over_stale_discovery_turn_meaning");
|
||||
}
|
||||
if (deterministicBroadBusinessEvaluationReply && candidate.candidate_status === "clarification_candidate") {
|
||||
pushReason(reasonCodes, "mcp_discovery_response_policy_keep_broad_business_summary_over_clarification_candidate");
|
||||
}
|
||||
@@ -261,6 +320,7 @@ function applyAssistantMcpDiscoveryResponsePolicy(input) {
|
||||
!alignedFactualAddressReply &&
|
||||
!matchedFactualAddressContinuationTarget &&
|
||||
!fullConfirmedFactualAddressReply &&
|
||||
!runtimeAdjustedExactReply &&
|
||||
!(deterministicBroadBusinessEvaluationReply && candidate.candidate_status === "clarification_candidate") &&
|
||||
ALLOWED_CANDIDATE_STATUSES.has(candidate.candidate_status) &&
|
||||
candidate.eligible_for_future_hot_runtime &&
|
||||
|
||||
+135
-6
@@ -241,12 +241,15 @@ function collectFollowupDiscoverySeed(followupContext) {
|
||||
? mapPilotScopeToFollowupMeaning(pilotScope)
|
||||
: mapAddressIntentToFollowupMeaning(previousIntent);
|
||||
const discoveryEntities = collectEntityCandidates(followupContext?.previous_discovery_entity_candidates);
|
||||
const entityResolutionStatus = toNonEmptyString(followupContext?.previous_discovery_entity_resolution_status);
|
||||
const entityResolutionAmbiguityCandidates = collectEntityCandidates(followupContext?.previous_discovery_entity_ambiguity_candidates);
|
||||
const ambiguityBlocksImplicitGrounding = pilotScope === "entity_resolution_search_v1" && entityResolutionStatus === "ambiguous";
|
||||
const counterparty = toNonEmptyString(previousFilters?.counterparty) ??
|
||||
toNonEmptyString(rootFilters?.counterparty) ??
|
||||
(toNonEmptyString(followupContext?.previous_anchor_type) === "counterparty"
|
||||
? toNonEmptyString(followupContext?.previous_anchor_value)
|
||||
: null) ??
|
||||
(discoveryEntities[0] ?? null);
|
||||
(ambiguityBlocksImplicitGrounding ? null : discoveryEntities[0] ?? null);
|
||||
const organization = toNonEmptyString(previousFilters?.organization) ??
|
||||
toNonEmptyString(rootFilters?.organization) ??
|
||||
(toNonEmptyString(followupContext?.previous_anchor_type) === "organization"
|
||||
@@ -260,7 +263,9 @@ function collectFollowupDiscoverySeed(followupContext) {
|
||||
action: mapped.action,
|
||||
unsupported: mapped.unsupported,
|
||||
counterparty,
|
||||
discoveryEntity: discoveryEntities[0] ?? null,
|
||||
discoveryEntity: ambiguityBlocksImplicitGrounding ? null : discoveryEntities[0] ?? null,
|
||||
entityResolutionStatus,
|
||||
entityResolutionAmbiguityCandidates,
|
||||
organization,
|
||||
dateScope,
|
||||
metadataRouteFamily: toNonEmptyString(followupContext?.previous_discovery_metadata_route_family),
|
||||
@@ -350,6 +355,93 @@ function rawEntityResolutionCandidate(text) {
|
||||
}
|
||||
return null;
|
||||
}
|
||||
function resolveEntityResolutionAmbiguityChoice(text, candidates) {
|
||||
const normalizedText = canonicalizeEntityResolutionCandidate(text);
|
||||
if (!normalizedText || candidates.length <= 0) {
|
||||
return null;
|
||||
}
|
||||
const exactMatch = candidates.find((candidate) => canonicalizeEntityResolutionCandidate(candidate) === normalizedText);
|
||||
if (exactMatch) {
|
||||
return exactMatch;
|
||||
}
|
||||
const includedMatches = candidates.filter((candidate) => {
|
||||
const normalizedCandidate = canonicalizeEntityResolutionCandidate(candidate);
|
||||
return normalizedCandidate.length > 0 && normalizedText.includes(normalizedCandidate);
|
||||
});
|
||||
if (includedMatches.length === 1) {
|
||||
return includedMatches[0];
|
||||
}
|
||||
const narrowedMatches = candidates.filter((candidate) => {
|
||||
const normalizedCandidate = canonicalizeEntityResolutionCandidate(candidate);
|
||||
return normalizedText.length >= 4 && normalizedCandidate.includes(normalizedText);
|
||||
});
|
||||
if (narrowedMatches.length === 1) {
|
||||
return narrowedMatches[0];
|
||||
}
|
||||
const normalizedLowerText = compactLower(text);
|
||||
const ordinalMatchers = [
|
||||
{
|
||||
index: 0,
|
||||
keywords: ["первый"],
|
||||
numericPatterns: [
|
||||
/(?:^|[^\p{L}\p{N}])(?:вариант|номер|№)?\s*1(?:$|[^\p{L}\p{N}])/iu,
|
||||
/^\s*1\s*$/u
|
||||
]
|
||||
},
|
||||
{
|
||||
index: 1,
|
||||
keywords: ["второй"],
|
||||
numericPatterns: [
|
||||
/(?:^|[^\p{L}\p{N}])(?:вариант|номер|№)?\s*2(?:$|[^\p{L}\p{N}])/iu,
|
||||
/^\s*2\s*$/u
|
||||
]
|
||||
},
|
||||
{
|
||||
index: 2,
|
||||
keywords: ["третий"],
|
||||
numericPatterns: [
|
||||
/(?:^|[^\p{L}\p{N}])(?:вариант|номер|№)?\s*3(?:$|[^\p{L}\p{N}])/iu,
|
||||
/^\s*3\s*$/u
|
||||
]
|
||||
},
|
||||
{
|
||||
index: 3,
|
||||
keywords: ["четвертый", "четвёртый"],
|
||||
numericPatterns: [
|
||||
/(?:^|[^\p{L}\p{N}])(?:вариант|номер|№)?\s*4(?:$|[^\p{L}\p{N}])/iu,
|
||||
/^\s*4\s*$/u
|
||||
]
|
||||
},
|
||||
{
|
||||
index: 4,
|
||||
keywords: ["пятый"],
|
||||
numericPatterns: [
|
||||
/(?:^|[^\p{L}\p{N}])(?:вариант|номер|№)?\s*5(?:$|[^\p{L}\p{N}])/iu,
|
||||
/^\s*5\s*$/u
|
||||
]
|
||||
},
|
||||
{
|
||||
index: 5,
|
||||
keywords: ["шестой"],
|
||||
numericPatterns: [
|
||||
/(?:^|[^\p{L}\p{N}])(?:вариант|номер|№)?\s*6(?:$|[^\p{L}\p{N}])/iu,
|
||||
/^\s*6\s*$/u
|
||||
]
|
||||
}
|
||||
];
|
||||
for (const matcher of ordinalMatchers) {
|
||||
if (matcher.index < candidates.length &&
|
||||
(matcher.keywords.some((keyword) => normalizedLowerText.includes(keyword)) ||
|
||||
matcher.numericPatterns.some((pattern) => pattern.test(normalizedLowerText)))) {
|
||||
return candidates[matcher.index];
|
||||
}
|
||||
}
|
||||
if (candidates.length > 0 &&
|
||||
(normalizedLowerText.includes("последний") || normalizedLowerText.includes("крайний"))) {
|
||||
return candidates[candidates.length - 1] ?? null;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
function metadataActionFromRawText(text) {
|
||||
if (/(?:\u043e\u0431\u044a\u0435\u043a\u0442(?:\u044b|\u0430|\u043e\u0432)?|objects?)/iu.test(text)) {
|
||||
return "inspect_surface";
|
||||
@@ -468,7 +560,11 @@ function buildAssistantMcpDiscoveryTurnInput(input) {
|
||||
const rawDateScope = collectDateScopeFromRawText(rawText);
|
||||
const rawMetadataScopeHint = rawMetadataSignal ? metadataScopeHintFromRawText(rawText) : null;
|
||||
const rawEntityCandidate = rawEntityResolutionSignal ? rawEntityResolutionCandidate(rawEntitySourceText) : null;
|
||||
const entityResolutionSignal = rawEntityResolutionSignal || Boolean(rawEntityCandidate);
|
||||
const entityResolutionClarificationCandidate = followupSeed.pilotScope === "entity_resolution_search_v1" &&
|
||||
followupSeed.entityResolutionStatus === "ambiguous"
|
||||
? resolveEntityResolutionAmbiguityChoice(rawEntitySourceText, followupSeed.entityResolutionAmbiguityCandidates)
|
||||
: null;
|
||||
const entityResolutionSignal = rawEntityResolutionSignal || Boolean(rawEntityCandidate) || Boolean(entityResolutionClarificationCandidate);
|
||||
const metadataDocumentHintSignal = hasDocumentEvidenceFollowupSignal(rawText) || hasPronounDocumentEvidenceFollowupSignal(rawText);
|
||||
const metadataMovementHintSignal = hasMovementEvidenceFollowupSignal(rawText) || hasPronounMovementEvidenceFollowupSignal(rawText);
|
||||
const rawDomain = toNonEmptyString(assistantTurnMeaning?.asked_domain_family);
|
||||
@@ -521,12 +617,26 @@ function buildAssistantMcpDiscoveryTurnInput(input) {
|
||||
!rawValueFlowSignal &&
|
||||
!rawMetadataSignal &&
|
||||
metadataDocumentHintSignal);
|
||||
const entityResolutionClarifiedDocumentFollowupApplicable = Boolean(followupSeed.pilotScope === "entity_resolution_search_v1" &&
|
||||
followupSeed.entityResolutionStatus === "ambiguous" &&
|
||||
entityResolutionClarificationCandidate &&
|
||||
!rawLifecycleSignal &&
|
||||
!rawValueFlowSignal &&
|
||||
!rawMetadataSignal &&
|
||||
metadataDocumentHintSignal);
|
||||
const entityResolutionGroundedMovementFollowupApplicable = Boolean(followupSeed.pilotScope === "entity_resolution_search_v1" &&
|
||||
(followupSeed.counterparty || followupSeed.discoveryEntity) &&
|
||||
!rawLifecycleSignal &&
|
||||
!rawValueFlowSignal &&
|
||||
!rawMetadataSignal &&
|
||||
metadataMovementHintSignal);
|
||||
const entityResolutionClarifiedMovementFollowupApplicable = Boolean(followupSeed.pilotScope === "entity_resolution_search_v1" &&
|
||||
followupSeed.entityResolutionStatus === "ambiguous" &&
|
||||
entityResolutionClarificationCandidate &&
|
||||
!rawLifecycleSignal &&
|
||||
!rawValueFlowSignal &&
|
||||
!rawMetadataSignal &&
|
||||
metadataMovementHintSignal);
|
||||
const groundedValueFlowFollowupApplicable = Boolean(rawValueFlowSignal &&
|
||||
!rawLifecycleSignal &&
|
||||
!rawMetadataSignal &&
|
||||
@@ -607,6 +717,7 @@ function buildAssistantMcpDiscoveryTurnInput(input) {
|
||||
const metadataGroundedDocumentLaneApplicable = metadataGroundedDocumentFollowupApplicable ||
|
||||
metadataAmbiguityResolvedDocumentFollowupApplicable ||
|
||||
entityResolutionGroundedDocumentFollowupApplicable ||
|
||||
entityResolutionClarifiedDocumentFollowupApplicable ||
|
||||
valueFlowGroundedDocumentFollowupApplicable ||
|
||||
movementEvidenceGroundedDocumentFollowupApplicable ||
|
||||
(metadataGroundedLaneContinuationApplicable && followupSeed.metadataRouteFamily === "document_evidence") ||
|
||||
@@ -614,6 +725,7 @@ function buildAssistantMcpDiscoveryTurnInput(input) {
|
||||
const metadataGroundedMovementLaneApplicable = metadataGroundedMovementFollowupApplicable ||
|
||||
metadataAmbiguityResolvedMovementFollowupApplicable ||
|
||||
entityResolutionGroundedMovementFollowupApplicable ||
|
||||
entityResolutionClarifiedMovementFollowupApplicable ||
|
||||
valueFlowGroundedMovementFollowupApplicable ||
|
||||
documentEvidenceGroundedMovementFollowupApplicable ||
|
||||
(metadataGroundedLaneContinuationApplicable && followupSeed.metadataRouteFamily === "movement_evidence") ||
|
||||
@@ -667,11 +779,14 @@ function buildAssistantMcpDiscoveryTurnInput(input) {
|
||||
lifecycleSignal,
|
||||
valueFlowSignal,
|
||||
metadataSignal: rawMetadataSignal || effectiveMetadataFollowupSeedApplicable,
|
||||
entityResolutionSignal
|
||||
entityResolutionSignal: entityResolutionSignal &&
|
||||
!metadataGroundedDocumentLaneApplicable &&
|
||||
!metadataGroundedMovementLaneApplicable
|
||||
});
|
||||
const groundedFollowupEntity = followupSeed.counterparty ?? followupSeed.discoveryEntity;
|
||||
const entityCandidates = entityResolutionSignal ? [] : [];
|
||||
if (entityResolutionSignal) {
|
||||
pushNormalizedEntityResolutionCandidate(entityCandidates, entityResolutionClarificationCandidate);
|
||||
pushNormalizedEntityResolutionCandidate(entityCandidates, rawEntityCandidate);
|
||||
for (const candidate of collectEntityCandidates(assistantTurnMeaning?.explicit_entity_candidates)) {
|
||||
pushNormalizedEntityResolutionCandidate(entityCandidates, candidate);
|
||||
@@ -814,12 +929,14 @@ function buildAssistantMcpDiscoveryTurnInput(input) {
|
||||
semanticDataNeed,
|
||||
explicitIntentCandidate,
|
||||
followupDiscoverySeedApplicable: followupDiscoverySeedApplicable ||
|
||||
Boolean(entityResolutionClarificationCandidate) ||
|
||||
effectiveMetadataFollowupSeedApplicable ||
|
||||
metadataAmbiguityLaneClarificationApplicable ||
|
||||
metadataGroundedMovementLaneApplicable ||
|
||||
metadataGroundedDocumentLaneApplicable ||
|
||||
groundedValueFlowFollowupApplicable,
|
||||
forceDiscoveryOverExplicitIntent: metadataAmbiguityLaneClarificationApplicable ||
|
||||
forceDiscoveryOverExplicitIntent: Boolean(entityResolutionClarificationCandidate) ||
|
||||
metadataAmbiguityLaneClarificationApplicable ||
|
||||
metadataGroundedMovementLaneApplicable ||
|
||||
metadataGroundedDocumentLaneApplicable ||
|
||||
groundedValueFlowFollowupApplicable
|
||||
@@ -827,7 +944,10 @@ function buildAssistantMcpDiscoveryTurnInput(input) {
|
||||
const hasTurnMeaning = Object.keys(cleanTurnMeaning).length > 0;
|
||||
const sourceSignal = assistantTurnMeaning
|
||||
? "assistant_turn_meaning"
|
||||
: followupDiscoverySeedApplicable || effectiveMetadataFollowupSeedApplicable || metadataAmbiguityLaneClarificationApplicable
|
||||
: followupDiscoverySeedApplicable ||
|
||||
Boolean(entityResolutionClarificationCandidate) ||
|
||||
effectiveMetadataFollowupSeedApplicable ||
|
||||
metadataAmbiguityLaneClarificationApplicable
|
||||
? "followup_context"
|
||||
: metadataGroundedMovementLaneApplicable
|
||||
? "followup_context"
|
||||
@@ -862,6 +982,9 @@ function buildAssistantMcpDiscoveryTurnInput(input) {
|
||||
if (rawEntityCandidate) {
|
||||
pushReason(reasonCodes, "mcp_discovery_entity_scope_from_raw_entity_search");
|
||||
}
|
||||
if (entityResolutionClarificationCandidate) {
|
||||
pushReason(reasonCodes, "mcp_discovery_entity_resolution_clarification_candidate_selected");
|
||||
}
|
||||
if (payoutSignal) {
|
||||
pushReason(reasonCodes, "mcp_discovery_payout_signal_detected");
|
||||
}
|
||||
@@ -892,9 +1015,15 @@ function buildAssistantMcpDiscoveryTurnInput(input) {
|
||||
if (entityResolutionGroundedDocumentFollowupApplicable) {
|
||||
pushReason(reasonCodes, "mcp_discovery_entity_resolution_grounded_document_followup");
|
||||
}
|
||||
if (entityResolutionClarifiedDocumentFollowupApplicable) {
|
||||
pushReason(reasonCodes, "mcp_discovery_entity_resolution_clarified_document_followup");
|
||||
}
|
||||
if (entityResolutionGroundedMovementFollowupApplicable) {
|
||||
pushReason(reasonCodes, "mcp_discovery_entity_resolution_grounded_movement_followup");
|
||||
}
|
||||
if (entityResolutionClarifiedMovementFollowupApplicable) {
|
||||
pushReason(reasonCodes, "mcp_discovery_entity_resolution_clarified_movement_followup");
|
||||
}
|
||||
if (valueFlowGroundedDocumentFollowupApplicable) {
|
||||
pushReason(reasonCodes, "mcp_discovery_value_flow_grounded_document_followup");
|
||||
}
|
||||
|
||||
@@ -503,7 +503,9 @@ function createAssistantTransitionPolicy(deps) {
|
||||
const sourceDiscoveryMetadataSelectedEntitySet = (0, assistantContinuityPolicy_1.readAssistantMcpDiscoveryMetadataSelectedEntitySet)(carryoverSourceDebug, deps.toNonEmptyString);
|
||||
const sourceDiscoveryMetadataAmbiguityDetected = (0, assistantContinuityPolicy_1.readAssistantMcpDiscoveryMetadataAmbiguityDetected)(carryoverSourceDebug);
|
||||
const sourceDiscoveryMetadataAmbiguityEntitySets = (0, assistantContinuityPolicy_1.readAssistantMcpDiscoveryMetadataAmbiguityEntitySets)(carryoverSourceDebug, deps.toNonEmptyString);
|
||||
const sourceDiscoveryEntityResolutionStatus = (0, assistantContinuityPolicy_1.readAssistantMcpDiscoveryEntityResolutionStatus)(carryoverSourceDebug, deps.toNonEmptyString);
|
||||
const sourceDiscoveryEntityCandidates = (0, assistantContinuityPolicy_1.readAssistantMcpDiscoveryEntityCandidates)(carryoverSourceDebug, deps.toNonEmptyString);
|
||||
const sourceDiscoveryEntityAmbiguityCandidates = (0, assistantContinuityPolicy_1.readAssistantMcpDiscoveryEntityAmbiguityCandidates)(carryoverSourceDebug, deps.toNonEmptyString);
|
||||
const llmExplicitIntent = deps.toNonEmptyString(llmPreDecomposeMeta?.predecomposeContract?.intent);
|
||||
const llmSelectedObjectScopeDetected = llmPreDecomposeMeta?.predecomposeContract?.semantics?.selected_object_scope_detected === true;
|
||||
const resolvedPrimaryIntent = deps.resolveAddressIntent(deps.repairAddressMojibake(String(userMessage ?? ""))).intent;
|
||||
@@ -738,7 +740,11 @@ function createAssistantTransitionPolicy(deps) {
|
||||
previous_anchor_type: previousAnchorType ?? undefined,
|
||||
previous_anchor_value: previousAnchor,
|
||||
previous_discovery_pilot_scope: sourceDiscoveryPilotScope ?? undefined,
|
||||
previous_discovery_entity_resolution_status: sourceDiscoveryEntityResolutionStatus ?? undefined,
|
||||
previous_discovery_entity_candidates: sourceDiscoveryEntityCandidates.length > 0 ? sourceDiscoveryEntityCandidates : undefined,
|
||||
previous_discovery_entity_ambiguity_candidates: sourceDiscoveryEntityAmbiguityCandidates.length > 0
|
||||
? sourceDiscoveryEntityAmbiguityCandidates
|
||||
: undefined,
|
||||
previous_discovery_metadata_route_family: sourceDiscoveryMetadataRouteFamily ?? undefined,
|
||||
previous_discovery_metadata_selected_entity_set: sourceDiscoveryMetadataSelectedEntitySet ?? undefined,
|
||||
previous_discovery_metadata_ambiguity_detected: sourceDiscoveryMetadataAmbiguityDetected || undefined,
|
||||
|
||||
Reference in New Issue
Block a user