ARCH: усилить semantic integrity и добавить human AGENT сценарии
This commit is contained in:
+22
-13
@@ -5,6 +5,7 @@ exports.cloneAddressNavigationState = cloneAddressNavigationState;
|
||||
exports.normalizeAddressNavigationState = normalizeAddressNavigationState;
|
||||
exports.evolveAddressNavigationStateWithAssistantItem = evolveAddressNavigationStateWithAssistantItem;
|
||||
const nanoid_1 = require("nanoid");
|
||||
const assistantContinuityPolicy_1 = require("./assistantContinuityPolicy");
|
||||
const addressNavigation_1 = require("../types/addressNavigation");
|
||||
const MAX_RESULT_SETS = 40;
|
||||
const MAX_NAVIGATION_EVENTS = 120;
|
||||
@@ -242,24 +243,32 @@ function resolveNavigationAction(debug, hasFocusObject) {
|
||||
}
|
||||
return hasFocusObject ? "drilldown" : "open";
|
||||
}
|
||||
function buildFocusObjectFromDebug(debug, resultSetId, createdAt) {
|
||||
const extractedFilters = toObject(debug.extracted_filters) ?? {};
|
||||
const rawValue = toNonEmptyString(debug.anchor_value_resolved) ??
|
||||
toNonEmptyString(debug.anchor_value_raw) ??
|
||||
toNonEmptyString(extractedFilters.item);
|
||||
if (!rawValue) {
|
||||
return null;
|
||||
}
|
||||
const objectType = toAddressFocusObjectType(debug.anchor_type);
|
||||
const canonicalType = objectType === "unknown" ? inferDisplayEntityType(toAddressIntent(debug.detected_intent)) : objectType;
|
||||
function buildFocusObject(objectType, label, resultSetId, createdAt) {
|
||||
return {
|
||||
object_type: canonicalType,
|
||||
object_id: `${canonicalType}:${rawValue}`.toLowerCase(),
|
||||
label: rawValue,
|
||||
object_type: objectType,
|
||||
object_id: `${objectType}:${label}`.toLowerCase(),
|
||||
label,
|
||||
provenance_result_set_id: resultSetId,
|
||||
selected_at: createdAt
|
||||
};
|
||||
}
|
||||
function buildFocusObjectFromDebug(debug, resultSetId, createdAt) {
|
||||
const extractedFilters = toObject(debug.extracted_filters) ?? {};
|
||||
const objectType = toAddressFocusObjectType(debug.anchor_type);
|
||||
const canonicalType = objectType === "unknown" ? inferDisplayEntityType(toAddressIntent(debug.detected_intent)) : objectType;
|
||||
if (canonicalType === "item") {
|
||||
const item = (0, assistantContinuityPolicy_1.readAddressDebugItem)(debug, toNonEmptyString);
|
||||
return item ? buildFocusObject(canonicalType, item, resultSetId, createdAt) : null;
|
||||
}
|
||||
if (canonicalType === "counterparty" && debug.mcp_discovery_response_applied === true) {
|
||||
const counterparty = (0, assistantContinuityPolicy_1.readAddressDebugCounterparty)(debug, toNonEmptyString);
|
||||
return counterparty ? buildFocusObject(canonicalType, counterparty, resultSetId, createdAt) : null;
|
||||
}
|
||||
const rawValue = toNonEmptyString(debug.anchor_value_resolved) ??
|
||||
toNonEmptyString(debug.anchor_value_raw) ??
|
||||
toNonEmptyString(extractedFilters.item);
|
||||
return rawValue ? buildFocusObject(canonicalType, rawValue, resultSetId, createdAt) : null;
|
||||
}
|
||||
function capResultSets(resultSets) {
|
||||
if (resultSets.length <= MAX_RESULT_SETS) {
|
||||
return resultSets;
|
||||
|
||||
@@ -0,0 +1,73 @@
|
||||
"use strict";
|
||||
var __importDefault = (this && this.__importDefault) || function (mod) {
|
||||
return (mod && mod.__esModule) ? mod : { "default": mod };
|
||||
};
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.repairAddressMojibakeText = repairAddressMojibakeText;
|
||||
exports.normalizeRussianComparableText = normalizeRussianComparableText;
|
||||
const iconv_lite_1 = __importDefault(require("iconv-lite"));
|
||||
function compactWhitespace(value) {
|
||||
return value.replace(/\s+/g, " ").trim();
|
||||
}
|
||||
function textMojibakeScore(value) {
|
||||
const source = String(value ?? "");
|
||||
const cyrillic = (source.match(/[\u0400-\u04ff]/g) ?? []).length;
|
||||
const latin = (source.match(/[A-Za-z]/g) ?? []).length;
|
||||
const replacement = (source.match(/[�]/g) ?? []).length;
|
||||
const pairMarkers = (source.match(/(?:Р.|С.|Ð.|Ñ.)/g) ?? []).length;
|
||||
const doubleEncodedMarkers = (source.match(/(?:Р“[Р-џ]|Р’[Р-џ]|Ã.|Â.)/gu) ?? []).length;
|
||||
return cyrillic + latin - replacement * 3 - pairMarkers * 2 - doubleEncodedMarkers * 2;
|
||||
}
|
||||
function looksLikeAddressMojibake(value) {
|
||||
const source = String(value ?? "");
|
||||
if (!source.trim()) {
|
||||
return false;
|
||||
}
|
||||
if (/[�]/.test(source)) {
|
||||
return true;
|
||||
}
|
||||
if ((source.match(/(?:Р.|С.|Ð.|Ñ.)/g) ?? []).length >= 2) {
|
||||
return true;
|
||||
}
|
||||
if ((source.match(/(?:Р“[Р-џ]|Р’[Р-џ]|Ã.|Â.)/gu) ?? []).length >= 2) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
function repairAddressMojibakeText(value) {
|
||||
const source = String(value ?? "");
|
||||
if (!looksLikeAddressMojibake(source)) {
|
||||
return source;
|
||||
}
|
||||
let candidate = source;
|
||||
for (let pass = 0; pass < 3; pass += 1) {
|
||||
let improved = false;
|
||||
try {
|
||||
const fromWin1251 = iconv_lite_1.default.encode(candidate, "win1251").toString("utf8");
|
||||
if (textMojibakeScore(fromWin1251) > textMojibakeScore(candidate)) {
|
||||
candidate = fromWin1251;
|
||||
improved = true;
|
||||
}
|
||||
}
|
||||
catch {
|
||||
// Ignore decode failures and keep the current candidate.
|
||||
}
|
||||
try {
|
||||
const fromLatin1 = Buffer.from(candidate, "latin1").toString("utf8");
|
||||
if (textMojibakeScore(fromLatin1) > textMojibakeScore(candidate)) {
|
||||
candidate = fromLatin1;
|
||||
improved = true;
|
||||
}
|
||||
}
|
||||
catch {
|
||||
// Ignore decode failures and keep the current candidate.
|
||||
}
|
||||
if (!improved) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
return candidate;
|
||||
}
|
||||
function normalizeRussianComparableText(value) {
|
||||
return compactWhitespace(repairAddressMojibakeText(String(value ?? "")).toLowerCase()).replace(/ё/g, "е");
|
||||
}
|
||||
+5
@@ -77,12 +77,17 @@ function shouldPreferRawFollowupMessage(userMessage, addressInputMessage, carryo
|
||||
const previousIntent = toNonEmptyString(followupContext?.previous_intent);
|
||||
const rootIntent = toNonEmptyString(followupContext?.root_intent);
|
||||
const previousAnchorType = toNonEmptyString(followupContext?.previous_anchor_type);
|
||||
const hasReferentialDocumentExclusionFollowupCue = /(?:\u043a\u0440\u043e\u043c\u0435|\u043f\u043e\u043c\u0438\u043c\u043e)\s+(?:\u044d\u0442\u043e\u0433\u043e|\u044d\u0442\u043e\u0439|\u044d\u0442\u043e\u0442|\u044d\u0442\u0443|\u044d\u0442\u0438\u0445)(?:\s+(?:\u0434\u043e\u043a\u0443\u043c\u0435\u043d\u0442\u0430|\u0434\u043e\u0433\u043e\u0432\u043e\u0440\u0430|\u043a\u043e\u043d\u0442\u0440\u0430\u0433\u0435\u043d\u0442\u0430))?/iu.test(rawMessage);
|
||||
const hasInventoryItemCarryover = previousAnchorType === "item" && isInventorySelectedObjectOrRootIntent(previousIntent);
|
||||
const hasInventoryFrameCarryover = isInventorySelectedObjectOrRootIntent(previousIntent) ||
|
||||
isInventorySelectedObjectOrRootIntent(rootIntent);
|
||||
const hasDocumentCarryover = previousIntent === "list_documents_by_counterparty" || previousIntent === "list_documents_by_contract";
|
||||
if (mode === "unsupported" && intent === "unknown") {
|
||||
return true;
|
||||
}
|
||||
if (hasDocumentCarryover && hasReferentialDocumentExclusionFollowupCue) {
|
||||
return true;
|
||||
}
|
||||
if (hasSameDateFollowupSignal(rawMessage) && hasExplicitCurrentDateSignal(canonicalMessage)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -51,6 +51,7 @@ exports.resolveAssistantContinuitySnapshot = resolveAssistantContinuitySnapshot;
|
||||
exports.resolveAssistantOrganizationAuthority = resolveAssistantOrganizationAuthority;
|
||||
exports.resolveOrganizationClarificationContinuation = resolveOrganizationClarificationContinuation;
|
||||
const assistantOrganizationMatcher_1 = require("./assistantOrganizationMatcher");
|
||||
const addressTextRepair_1 = require("./addressTextRepair");
|
||||
function fallbackToNonEmptyString(value) {
|
||||
if (value === null || value === undefined) {
|
||||
return null;
|
||||
@@ -365,14 +366,45 @@ function readAddressDebugItem(debug, toNonEmptyString = fallbackToNonEmptyString
|
||||
? toNonEmptyString(debug?.anchor_value_resolved) ?? toNonEmptyString(debug?.anchor_value_raw)
|
||||
: null));
|
||||
}
|
||||
function readAddressDebugCounterparty(debug, toNonEmptyString = fallbackToNonEmptyString) {
|
||||
const extractedFilters = readAddressDebugFilters(debug);
|
||||
if (toNonEmptyString(extractedFilters?.counterparty)) {
|
||||
return toNonEmptyString(extractedFilters?.counterparty);
|
||||
function isReferentialCounterpartyPlaceholder(value) {
|
||||
if (!value) {
|
||||
return false;
|
||||
}
|
||||
if (String(debug?.anchor_type ?? "") === "counterparty") {
|
||||
return toNonEmptyString(debug?.anchor_value_resolved) ?? toNonEmptyString(debug?.anchor_value_raw);
|
||||
return new Set([
|
||||
"он",
|
||||
"она",
|
||||
"оно",
|
||||
"они",
|
||||
"ему",
|
||||
"ней",
|
||||
"нему",
|
||||
"ним",
|
||||
"ними",
|
||||
"его",
|
||||
"ее",
|
||||
"их",
|
||||
"этому",
|
||||
"этой",
|
||||
"этом",
|
||||
"этим",
|
||||
"эта",
|
||||
"этот",
|
||||
"эти"
|
||||
]).has((0, addressTextRepair_1.normalizeRussianComparableText)(value));
|
||||
}
|
||||
function normalizeCounterpartyCandidate(value, toNonEmptyString) {
|
||||
const text = toNonEmptyString(value);
|
||||
if (!text || isReferentialCounterpartyPlaceholder(text)) {
|
||||
return null;
|
||||
}
|
||||
return text;
|
||||
}
|
||||
function sameCounterpartyCandidate(left, right) {
|
||||
return Boolean(left &&
|
||||
right &&
|
||||
(0, addressTextRepair_1.normalizeRussianComparableText)(left) === (0, addressTextRepair_1.normalizeRussianComparableText)(right));
|
||||
}
|
||||
function readGroundedDiscoveryCounterparty(debug, toNonEmptyString = fallbackToNonEmptyString) {
|
||||
const discoveryPilotScope = readAssistantMcpDiscoveryPilotScope(debug, toNonEmptyString);
|
||||
const suppressDiscoveryEntityCarryover = discoveryPilotScope === "metadata_inspection_v1" ||
|
||||
readAssistantMcpDiscoveryLoopSubjectResolutionOptional(debug);
|
||||
@@ -381,12 +413,27 @@ function readAddressDebugCounterparty(debug, toNonEmptyString = fallbackToNonEmp
|
||||
}
|
||||
const discoveryEntities = collectAssistantMcpDiscoveryEntityCandidates(debug, toNonEmptyString);
|
||||
for (const entity of discoveryEntities) {
|
||||
const text = toNonEmptyString(entity);
|
||||
if (text) {
|
||||
return text;
|
||||
const normalized = normalizeCounterpartyCandidate(entity, toNonEmptyString);
|
||||
if (normalized) {
|
||||
return normalized;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
return normalizeCounterpartyCandidate(readAssistantMcpDiscoveryLoopMetadataScopeHint(debug, toNonEmptyString), toNonEmptyString);
|
||||
}
|
||||
function readAddressDebugCounterparty(debug, toNonEmptyString = fallbackToNonEmptyString) {
|
||||
const extractedFilters = readAddressDebugFilters(debug);
|
||||
const extractedCounterparty = normalizeCounterpartyCandidate(extractedFilters?.counterparty, toNonEmptyString);
|
||||
const anchorCounterparty = String(debug?.anchor_type ?? "") === "counterparty"
|
||||
? normalizeCounterpartyCandidate(toNonEmptyString(debug?.anchor_value_resolved) ?? toNonEmptyString(debug?.anchor_value_raw), toNonEmptyString)
|
||||
: null;
|
||||
const groundedDiscoveryCounterparty = readGroundedDiscoveryCounterparty(debug, toNonEmptyString);
|
||||
if (hasGroundedDiscoveryBusinessAnswer(debug, toNonEmptyString) && groundedDiscoveryCounterparty) {
|
||||
if (!extractedCounterparty || !sameCounterpartyCandidate(extractedCounterparty, groundedDiscoveryCounterparty)) {
|
||||
return groundedDiscoveryCounterparty;
|
||||
}
|
||||
return extractedCounterparty;
|
||||
}
|
||||
return extractedCounterparty ?? anchorCounterparty ?? groundedDiscoveryCounterparty;
|
||||
}
|
||||
function readAddressDebugIntent(debug, toNonEmptyString = fallbackToNonEmptyString) {
|
||||
const detectedIntent = toNonEmptyString(debug?.detected_intent);
|
||||
@@ -431,8 +478,16 @@ function readAddressDebugTemporalScope(debug, toNonEmptyString = fallbackToNonEm
|
||||
}
|
||||
function resolveAddressDebugAnchorContext(debug, toNonEmptyString = fallbackToNonEmptyString) {
|
||||
const explicitAnchorType = toNonEmptyString(debug?.anchor_type);
|
||||
const explicitAnchorValue = toNonEmptyString(debug?.anchor_value_resolved) ?? toNonEmptyString(debug?.anchor_value_raw);
|
||||
if (explicitAnchorType || explicitAnchorValue) {
|
||||
const explicitAnchorValueRaw = toNonEmptyString(debug?.anchor_value_resolved) ?? toNonEmptyString(debug?.anchor_value_raw);
|
||||
const explicitAnchorValue = explicitAnchorType === "counterparty"
|
||||
? normalizeCounterpartyCandidate(explicitAnchorValueRaw, toNonEmptyString)
|
||||
: explicitAnchorValueRaw;
|
||||
const groundedDiscoveryCounterparty = readGroundedDiscoveryCounterparty(debug, toNonEmptyString);
|
||||
const shouldPreferDiscoveryCounterparty = explicitAnchorType === "counterparty" &&
|
||||
Boolean(groundedDiscoveryCounterparty &&
|
||||
hasGroundedDiscoveryBusinessAnswer(debug, toNonEmptyString) &&
|
||||
(!explicitAnchorValue || !sameCounterpartyCandidate(explicitAnchorValue, groundedDiscoveryCounterparty)));
|
||||
if ((explicitAnchorType || explicitAnchorValue) && !shouldPreferDiscoveryCounterparty) {
|
||||
return {
|
||||
anchorType: explicitAnchorType,
|
||||
anchorValue: explicitAnchorValue
|
||||
@@ -446,8 +501,11 @@ function resolveAddressDebugAnchorContext(debug, toNonEmptyString = fallbackToNo
|
||||
anchorValue: item
|
||||
};
|
||||
}
|
||||
const counterparty = toNonEmptyString(extractedFilters?.counterparty);
|
||||
if (counterparty) {
|
||||
const counterparty = normalizeCounterpartyCandidate(extractedFilters?.counterparty, toNonEmptyString);
|
||||
if (counterparty &&
|
||||
!(groundedDiscoveryCounterparty &&
|
||||
hasGroundedDiscoveryBusinessAnswer(debug, toNonEmptyString) &&
|
||||
!sameCounterpartyCandidate(counterparty, groundedDiscoveryCounterparty))) {
|
||||
return {
|
||||
anchorType: "counterparty",
|
||||
anchorValue: counterparty
|
||||
@@ -512,7 +570,9 @@ function resolveAddressDebugCarryoverFilters(debug, toNonEmptyString = fallbackT
|
||||
Boolean(discoveryDateScope.asOfDate || discoveryDateScope.periodFrom || discoveryDateScope.periodTo);
|
||||
const counterparty = readAddressDebugCounterparty(debug, toNonEmptyString);
|
||||
const organization = readAddressDebugOrganization(debug, toNonEmptyString);
|
||||
if (counterparty && !toNonEmptyString(nextFilters.counterparty)) {
|
||||
const preferGroundedDiscoveryCounterparty = hasGroundedDiscoveryBusinessAnswer(debug, toNonEmptyString) && Boolean(counterparty);
|
||||
const existingCounterparty = normalizeCounterpartyCandidate(nextFilters.counterparty, toNonEmptyString);
|
||||
if (counterparty && (preferGroundedDiscoveryCounterparty || !existingCounterparty)) {
|
||||
nextFilters.counterparty = counterparty;
|
||||
}
|
||||
if (organization && !toNonEmptyString(nextFilters.organization)) {
|
||||
|
||||
+55
-12
@@ -3,6 +3,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.ASSISTANT_MCP_DISCOVERY_TURN_INPUT_SCHEMA_VERSION = void 0;
|
||||
exports.buildAssistantMcpDiscoveryTurnInput = buildAssistantMcpDiscoveryTurnInput;
|
||||
const assistantMcpDiscoveryDataNeedGraph_1 = require("./assistantMcpDiscoveryDataNeedGraph");
|
||||
const addressTextRepair_1 = require("./addressTextRepair");
|
||||
exports.ASSISTANT_MCP_DISCOVERY_TURN_INPUT_SCHEMA_VERSION = "assistant_mcp_discovery_turn_input_v1";
|
||||
function toRecordObject(value) {
|
||||
if (!value || typeof value !== "object" || Array.isArray(value)) {
|
||||
@@ -38,7 +39,32 @@ function pushUnique(target, value) {
|
||||
}
|
||||
}
|
||||
function isReferentialEntityPlaceholder(value) {
|
||||
return /^(?:\u043d\u0435\u043c\u0443|\u043d\u0435\u0439|\u043d\u0438\u043c|\u043d\u0438\u043c\u0438|\u0435\u0433\u043e|\u0435\u0435|\u0435\u0451|\u0438\u0445|\u044d\u0442\u043e\u043c\u0443|\u044d\u0442\u043e\u0439|\u044d\u0442\u0438\u043c|\u044d\u0442\u0438\u043c\u0438|\u044d\u0442\u043e\u043c)$/iu.test(value.trim());
|
||||
return new Set([
|
||||
"он",
|
||||
"она",
|
||||
"оно",
|
||||
"они",
|
||||
"ему",
|
||||
"ней",
|
||||
"нему",
|
||||
"ним",
|
||||
"ними",
|
||||
"его",
|
||||
"ее",
|
||||
"их",
|
||||
"этому",
|
||||
"этой",
|
||||
"этим",
|
||||
"этими",
|
||||
"этом"
|
||||
]).has((0, addressTextRepair_1.normalizeRussianComparableText)(value));
|
||||
}
|
||||
function normalizeFollowupCounterpartyCandidate(value) {
|
||||
const text = candidateValue(value);
|
||||
if (!text || isReferentialEntityPlaceholder(text)) {
|
||||
return null;
|
||||
}
|
||||
return text;
|
||||
}
|
||||
function pushScopedEntityCandidate(target, value, groundedFollowupEntity) {
|
||||
const text = candidateValue(value);
|
||||
@@ -347,14 +373,22 @@ function collectFollowupDiscoverySeed(followupContext) {
|
||||
const entityResolutionAmbiguityCandidates = collectEntityCandidates(followupContext?.previous_discovery_entity_ambiguity_candidates);
|
||||
const ambiguityBlocksImplicitGrounding = effectivePilotScope === "entity_resolution_search_v1" && entityResolutionStatus === "ambiguous";
|
||||
const metadataPilotCarriesScopeOnly = effectivePilotScope === "metadata_inspection_v1" || loopSubjectResolutionOptional;
|
||||
const normalizedDiscoveryEntities = discoveryEntities
|
||||
.map((entity) => normalizeFollowupCounterpartyCandidate(entity))
|
||||
.filter((entity) => Boolean(entity));
|
||||
const groundedDiscoveryCounterparty = ambiguityBlocksImplicitGrounding || metadataPilotCarriesScopeOnly
|
||||
? null
|
||||
: normalizedDiscoveryEntities[0] ?? normalizeFollowupCounterpartyCandidate(loopMetadataScopeHint);
|
||||
const metadataScopeHint = loopMetadataScopeHint ??
|
||||
(loopSubjectResolutionOptional ? discoveryEntities[0] ?? null : null);
|
||||
const counterparty = toNonEmptyString(previousFilters?.counterparty) ??
|
||||
toNonEmptyString(rootFilters?.counterparty) ??
|
||||
(toNonEmptyString(followupContext?.previous_anchor_type) === "counterparty"
|
||||
? toNonEmptyString(followupContext?.previous_anchor_value)
|
||||
: null) ??
|
||||
(ambiguityBlocksImplicitGrounding || metadataPilotCarriesScopeOnly ? null : discoveryEntities[0] ?? null);
|
||||
(loopSubjectResolutionOptional ? normalizedDiscoveryEntities[0] ?? null : null);
|
||||
const previousFiltersCounterparty = normalizeFollowupCounterpartyCandidate(previousFilters?.counterparty);
|
||||
const rootFiltersCounterparty = normalizeFollowupCounterpartyCandidate(rootFilters?.counterparty);
|
||||
const previousAnchorCounterparty = toNonEmptyString(followupContext?.previous_anchor_type) === "counterparty"
|
||||
? normalizeFollowupCounterpartyCandidate(followupContext?.previous_anchor_value)
|
||||
: null;
|
||||
const counterparty = groundedDiscoveryCounterparty
|
||||
? groundedDiscoveryCounterparty
|
||||
: previousFiltersCounterparty ?? rootFiltersCounterparty ?? previousAnchorCounterparty;
|
||||
const organization = toNonEmptyString(previousFilters?.organization) ??
|
||||
toNonEmptyString(rootFilters?.organization) ??
|
||||
(toNonEmptyString(followupContext?.previous_anchor_type) === "organization"
|
||||
@@ -372,7 +406,7 @@ function collectFollowupDiscoverySeed(followupContext) {
|
||||
loopPendingAxes,
|
||||
loopProvidedAxes,
|
||||
counterparty,
|
||||
discoveryEntity: ambiguityBlocksImplicitGrounding || loopSubjectResolutionOptional ? null : discoveryEntities[0] ?? null,
|
||||
discoveryEntity: ambiguityBlocksImplicitGrounding || loopSubjectResolutionOptional ? null : normalizedDiscoveryEntities[0] ?? null,
|
||||
entityResolutionStatus,
|
||||
entityResolutionAmbiguityCandidates,
|
||||
rankingNeed: toNonEmptyString(followupContext?.previous_discovery_ranking_need),
|
||||
@@ -472,6 +506,9 @@ function hasMetadataSignal(text) {
|
||||
return (/(?:\u043e\u0431\u044a\u0435\u043a\u0442(?:\u044b|\u0430|\u043e\u0432)?|\u0440\u0435\u0433\u0438\u0441\u0442\u0440\u044b|\u0434\u043e\u043a\u0443\u043c\u0435\u043d\u0442\u044b|\u0441\u043f\u0440\u0430\u0432\u043e\u0447\u043d\u0438\u043a\u0438|\u043f\u043e\u043b(?:\u0435|\u044f)|objects?|registers?|documents?|catalogs?|fields?)/iu.test(text) &&
|
||||
/(?:\u0435\u0441\u0442\u044c|\u043a\u0430\u043a\u0438\u0435|\u0434\u043e\u0441\u0442\u0443\u043f\u043d|\u0432\s+1\u0441|1\u0441|available|exist|which)/iu.test(text));
|
||||
}
|
||||
function hasReferentialDocumentExclusionFollowupSignal(text) {
|
||||
return /(?:\u043a\u0440\u043e\u043c\u0435|\u043f\u043e\u043c\u0438\u043c\u043e)\s+(?:\u044d\u0442\u043e\u0433\u043e|\u044d\u0442\u043e\u0439|\u044d\u0442\u043e\u0442|\u044d\u0442\u0443|\u044d\u0442\u0438\u0445)(?:\s+(?:\u0434\u043e\u043a\u0443\u043c\u0435\u043d\u0442\u0430|\u0434\u043e\u0433\u043e\u0432\u043e\u0440\u0430|\u043a\u043e\u043d\u0442\u0440\u0430\u0433\u0435\u043d\u0442\u0430))?/iu.test(text);
|
||||
}
|
||||
function hasMetadataObjectHint(text) {
|
||||
return /(?:\u043e\u0431\u044a\u0435\u043a\u0442(?:\u044b|\u0430|\u043e\u0432)?|\u0440\u0435\u0433\u0438\u0441\u0442\u0440(?:\u044b)?|\u0434\u043e\u043a\u0443\u043c\u0435\u043d\u0442(?:\u044b)?|\u0441\u043f\u0440\u0430\u0432\u043e\u0447\u043d\u0438\u043a(?:\u0438)?|\u043f\u043e\u043b(?:\u0435|\u044f)|objects?|registers?|documents?|catalogs?|fields?)/iu.test(text);
|
||||
}
|
||||
@@ -732,14 +769,20 @@ function buildAssistantMcpDiscoveryTurnInput(input) {
|
||||
const reasonCodes = [];
|
||||
const rawUserText = toNonEmptyString(input.userMessage);
|
||||
const rawEffectiveText = toNonEmptyString(input.effectiveMessage);
|
||||
const rawSignalSourceText = `${rawUserText ?? ""} ${rawEffectiveText ?? ""}`.trim();
|
||||
const rawEntitySourceText = rawUserText ?? rawEffectiveText ?? rawSignalSourceText;
|
||||
const repairedUserText = rawUserText ? (0, addressTextRepair_1.repairAddressMojibakeText)(rawUserText) : null;
|
||||
const repairedEffectiveText = rawEffectiveText ? (0, addressTextRepair_1.repairAddressMojibakeText)(rawEffectiveText) : null;
|
||||
const rawSignalSourceText = `${repairedUserText ?? rawUserText ?? ""} ${repairedEffectiveText ?? rawEffectiveText ?? ""}`.trim();
|
||||
const rawEntitySourceText = repairedUserText ?? rawUserText ?? repairedEffectiveText ?? rawEffectiveText ?? rawSignalSourceText;
|
||||
const rawText = compactLower(rawSignalSourceText);
|
||||
const rawReferentialDocumentExclusionSignal = hasReferentialDocumentExclusionFollowupSignal(repairedUserText ?? rawUserText ?? "");
|
||||
const rawLifecycleSignal = hasLifecycleSignal(rawText);
|
||||
const rawBidirectionalValueFlowSignal = !rawLifecycleSignal && hasBidirectionalValueFlowSignal(rawText);
|
||||
const rawValueFlowSignal = !rawLifecycleSignal &&
|
||||
(hasValueFlowSignal(rawText) || hasValueRankingSignal(rawText) || rawBidirectionalValueFlowSignal);
|
||||
const rawMetadataSignal = !rawLifecycleSignal && !rawValueFlowSignal && hasMetadataSignal(rawText);
|
||||
const rawMetadataSignal = !rawLifecycleSignal &&
|
||||
!rawValueFlowSignal &&
|
||||
!rawReferentialDocumentExclusionSignal &&
|
||||
hasMetadataSignal(rawText);
|
||||
const rawEntityResolutionSignal = !rawLifecycleSignal && !rawValueFlowSignal && !rawMetadataSignal && hasEntityResolutionSignal(rawText);
|
||||
const rawPayoutSignal = rawValueFlowSignal && !rawBidirectionalValueFlowSignal && hasPayoutSignal(rawText);
|
||||
const monthlyAggregationSignal = hasMonthlyAggregationSignal(rawText);
|
||||
|
||||
@@ -495,12 +495,35 @@ function createAssistantRoutePolicy(deps) {
|
||||
!effectiveAddressFollowupSignal &&
|
||||
resolvedModeDetection.mode === "unsupported" &&
|
||||
resolvedIntentResolution.intent === "unknown");
|
||||
const groundedValueFlowFollowupContextDetected = Boolean(followupContext &&
|
||||
[
|
||||
"counterparty_value_flow_query_movements_v1",
|
||||
"counterparty_supplier_payout_query_movements_v1",
|
||||
"counterparty_bidirectional_value_flow_query_movements_v1"
|
||||
].includes(String(toNonEmptyString(followupContext?.previous_discovery_pilot_scope) ?? "")) &&
|
||||
!dangerOrCoercionSignal &&
|
||||
(toNonEmptyString(assistantTurnMeaning?.asked_domain_family) === "counterparty_value" ||
|
||||
[
|
||||
"turnover",
|
||||
"payout",
|
||||
"net_value_flow"
|
||||
].includes(String(toNonEmptyString(assistantTurnMeaning?.asked_action_family) ?? "")) ||
|
||||
/(?:нетто|сальдо|сколько\s+мы\s+(?:получили|заплатили)|incoming|outgoing)/iu.test(analyticsSample)));
|
||||
const baseToolGatePreservesAddressLane = Boolean(baseToolGate?.runAddressLane &&
|
||||
["address_intent_resolver_detected", "address_mode_classifier_detected", "address_signal_detected", "llm_canonical_data_signal_detected"].includes(String(baseToolGate?.reason ?? "")));
|
||||
[
|
||||
"address_intent_resolver_detected",
|
||||
"address_mode_classifier_detected",
|
||||
"address_signal_detected",
|
||||
"llm_canonical_data_signal_detected"
|
||||
].includes(String(baseToolGate?.reason ?? ""))) ||
|
||||
Boolean(baseToolGate?.runAddressLane &&
|
||||
String(baseToolGate?.reason ?? "") === "followup_context_detected" &&
|
||||
groundedValueFlowFollowupContextDetected);
|
||||
const nonDomainQueryIndexed = Boolean(!llmFirstAddressCandidate &&
|
||||
deterministicNonDomainGuard &&
|
||||
(llmFirstUnsupportedCandidate || llmContractMode === null) &&
|
||||
!baseToolGatePreservesAddressLane &&
|
||||
!groundedValueFlowFollowupContextDetected &&
|
||||
!protectedInventoryShortFollowup &&
|
||||
!organizationClarificationContinuationDetected);
|
||||
const lastAddressAssistantDebug = sessionItems
|
||||
@@ -664,9 +687,11 @@ function createAssistantRoutePolicy(deps) {
|
||||
const unsupportedCurrentTurnMeaningBoundary = Boolean(assistantTurnMeaning?.unsupported_but_understood_family &&
|
||||
assistantTurnMeaning?.stale_replay_forbidden === true &&
|
||||
!turnMeaningIntentCandidate &&
|
||||
!aggregateBusinessAnalyticsSignal &&
|
||||
!dataScopeMetaQuery &&
|
||||
!capabilityMetaQuery &&
|
||||
!dangerOrCoercionSignal &&
|
||||
!groundedValueFlowFollowupContextDetected &&
|
||||
!organizationClarificationContinuationDetected);
|
||||
if (unsupportedCurrentTurnMeaningBoundary) {
|
||||
return {
|
||||
|
||||
Reference in New Issue
Block a user