Закрепить дату складского supplier-overlap follow-up
This commit is contained in:
parent
f0b84822d3
commit
fdd04abbde
|
|
@ -0,0 +1,80 @@
|
|||
{
|
||||
"schema_version": "domain_truth_harness_spec_v1",
|
||||
"scenario_id": "address_truth_harness_inventory_supplier_overlap_temporal_carryover",
|
||||
"domain": "inventory_supplier_overlap_temporal_carryover",
|
||||
"title": "Supplier-overlap follow-up keeps the inventory root date",
|
||||
"description": "Targeted replay for temporal carryover: after an inventory root slice for September 2021, colloquial supplier-overlap follow-ups with 'сейчас' / 'текущий товарный остаток' must stay on the confirmed root slice date instead of drifting to the wall-clock date.",
|
||||
"bindings": {},
|
||||
"steps": [
|
||||
{
|
||||
"step_id": "step_01_current_stock_anchor",
|
||||
"title": "Historical inventory root anchor",
|
||||
"question": "какие остатки на складе на сентябрь 2021",
|
||||
"allowed_reply_types": [
|
||||
"factual"
|
||||
],
|
||||
"expected_intents": [
|
||||
"inventory_on_hand_as_of_date"
|
||||
],
|
||||
"required_filters": {
|
||||
"as_of_date": "2021-09-30",
|
||||
"period_from": "2021-09-01",
|
||||
"period_to": "2021-09-30"
|
||||
},
|
||||
"required_answer_patterns_all": [
|
||||
"(?i)30\\.09\\.2021",
|
||||
"(?i)склад|остат"
|
||||
],
|
||||
"forbidden_answer_patterns": [
|
||||
"(?i)03\\.06\\.2026",
|
||||
"(?i)route_id|capability_id|runtime_|snapshot_items|answer_object|debug"
|
||||
]
|
||||
},
|
||||
{
|
||||
"step_id": "step_02_supplier_overlap_now",
|
||||
"title": "Supplier overlap inherits root date despite сейчас",
|
||||
"question": "У какого поставщика были куплены товары, которые сейчас лежат на складе Основной склад",
|
||||
"allowed_reply_types": [
|
||||
"factual"
|
||||
],
|
||||
"expected_intents": [
|
||||
"inventory_supplier_stock_overlap_as_of_date"
|
||||
],
|
||||
"required_filters": {
|
||||
"as_of_date": "2021-09-30"
|
||||
},
|
||||
"required_answer_patterns_all": [
|
||||
"(?i)30\\.09\\.2021",
|
||||
"(?i)поставщик|поставщиков|закупочн",
|
||||
"(?i)Следующий шаг|проверьте|партионн"
|
||||
],
|
||||
"forbidden_answer_patterns": [
|
||||
"(?i)03\\.06\\.2026",
|
||||
"(?i)route_id|capability_id|runtime_|snapshot_items|answer_object|debug"
|
||||
]
|
||||
},
|
||||
{
|
||||
"step_id": "step_03_supplier_residue_now",
|
||||
"title": "Current residue wording keeps root date",
|
||||
"question": "По какому поставщику проходит текущий товарный остаток на складе Основной склад",
|
||||
"allowed_reply_types": [
|
||||
"factual"
|
||||
],
|
||||
"expected_intents": [
|
||||
"inventory_supplier_stock_overlap_as_of_date"
|
||||
],
|
||||
"required_filters": {
|
||||
"as_of_date": "2021-09-30"
|
||||
},
|
||||
"required_answer_patterns_all": [
|
||||
"(?i)30\\.09\\.2021",
|
||||
"(?i)поставщик|поставщиков|закупочн",
|
||||
"(?i)Следующий шаг|проверьте|партионн"
|
||||
],
|
||||
"forbidden_answer_patterns": [
|
||||
"(?i)03\\.06\\.2026",
|
||||
"(?i)route_id|capability_id|runtime_|snapshot_items|answer_object|debug"
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
|
|
@ -534,6 +534,7 @@ 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 previousFilters = toRecordObject(followupContext?.previous_filters);
|
||||
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) ||
|
||||
|
|
@ -543,10 +544,11 @@ function shouldPreferRawFollowupMessage(userMessage, addressInputMessage, carryo
|
|||
const hasInventoryMarginRankingAccountCorrection = hasInventoryMarginRankingCarryover &&
|
||||
[rawMessage, canonicalMessage].some((message) => hasInventoryMarginRankingAccountCorrectionCue(message));
|
||||
const hasDocumentCarryover = previousIntent === "list_documents_by_counterparty" || previousIntent === "list_documents_by_contract";
|
||||
const hasCounterpartyCarryover = previousAnchorType === "counterparty" || Boolean(toNonEmptyString(previousFilters?.counterparty));
|
||||
if (mode === "unsupported" && intent === "unknown") {
|
||||
return true;
|
||||
}
|
||||
if (hasDocumentCarryover && hasReferentialDocumentExclusionFollowupCue) {
|
||||
if ((hasDocumentCarryover || hasCounterpartyCarryover) && hasReferentialDocumentExclusionFollowupCue) {
|
||||
return true;
|
||||
}
|
||||
if (hasSameDateFollowupSignal(rawMessage) && hasExplicitCurrentDateSignal(canonicalMessage)) {
|
||||
|
|
|
|||
|
|
@ -27,6 +27,25 @@ function createAssistantTransitionPolicy(deps) {
|
|||
return wantsFoundRows || account41Not01;
|
||||
});
|
||||
}
|
||||
function hasInventorySupplierOverlapRootFollowupSignal(userMessage, alternateMessage = null, sourceIntentHint = null, hasInventoryRootFrame = false) {
|
||||
if (!hasInventoryRootFrame ||
|
||||
(sourceIntentHint !== "inventory_on_hand_as_of_date" &&
|
||||
sourceIntentHint !== "inventory_supplier_stock_overlap_as_of_date")) {
|
||||
return false;
|
||||
}
|
||||
return [userMessage, alternateMessage]
|
||||
.filter((value) => deps.toNonEmptyString(value))
|
||||
.map((value) => normalizeFollowupText(value))
|
||||
.some((normalized) => {
|
||||
if (!normalized) {
|
||||
return false;
|
||||
}
|
||||
const hasSupplierCue = /(?:\u043f\u043e\u0441\u0442\u0430\u0432\u0449\u0438\u043a|supplier|vendor|\u0443\s+\u043a\u0430\u043a\u043e\u0433\u043e|\u0443\s+\u043a\u043e\u0433\u043e|\u043f\u043e\s+\u043a\u0430\u043a\u043e\u043c\u0443)/iu.test(normalized);
|
||||
const hasStockCue = /(?:\u043e\u0441\u0442\u0430\u0442|\u0441\u043a\u043b\u0430\u0434|\u0442\u043e\u0432\u0430\u0440|\u043f\u043e\u0437\u0438\u0446|\u043b\u0435\u0436|\u0432\u0438\u0441|\u0442\u0435\u043a\u0443\u0449)/iu.test(normalized);
|
||||
const hasPurchaseCue = /(?:\u043a\u0443\u043f\u0438\u043b|\u043a\u0443\u043f\u043b\u0435\u043d|\u043f\u043e\u043a\u0443\u043f|\u0437\u0430\u043a\u0443\u043f|\u043f\u043e\u0441\u0442\u0443\u043f\u043b|purchase|bought)/iu.test(normalized);
|
||||
return hasSupplierCue && (hasStockCue || hasPurchaseCue);
|
||||
});
|
||||
}
|
||||
function hasSamePeriodReferenceCue(...values) {
|
||||
return values
|
||||
.map((value) => normalizeFollowupText(value))
|
||||
|
|
@ -866,6 +885,7 @@ function createAssistantTransitionPolicy(deps) {
|
|||
const hasInventoryRootRestatementAlternate = deps.toNonEmptyString(alternateMessage)
|
||||
? hasInventoryRootRestatementLikeSignal(String(alternateMessage ?? ""), sourceIntentHint, Boolean(recentInventoryRootFrame))
|
||||
: false;
|
||||
const hasInventorySupplierOverlapRootFollowup = hasInventorySupplierOverlapRootFollowupSignal(userMessage, alternateMessage, sourceIntentHint, Boolean(recentInventoryRootFrame));
|
||||
const hasExplicitInventorySameDatePivotPrimary = hasExplicitInventorySameDatePivotSignal(userMessage);
|
||||
const hasExplicitInventorySameDatePivotAlternate = deps.toNonEmptyString(alternateMessage)
|
||||
? hasExplicitInventorySameDatePivotSignal(String(alternateMessage ?? ""))
|
||||
|
|
@ -882,6 +902,7 @@ function createAssistantTransitionPolicy(deps) {
|
|||
hasInventoryRootTemporalFollowupAlternate ||
|
||||
hasInventoryRootRestatementPrimary ||
|
||||
hasInventoryRootRestatementAlternate ||
|
||||
hasInventorySupplierOverlapRootFollowup ||
|
||||
inventoryPurchaseDateVatBridge ||
|
||||
explicitSummaryBundleReuseSignal ||
|
||||
Boolean(debtRoleSwapIntent) ||
|
||||
|
|
@ -907,6 +928,7 @@ function createAssistantTransitionPolicy(deps) {
|
|||
hasInventoryRootTemporalFollowupAlternate ||
|
||||
hasInventoryRootRestatementPrimary ||
|
||||
hasInventoryRootRestatementAlternate ||
|
||||
hasInventorySupplierOverlapRootFollowup ||
|
||||
inventoryPurchaseDateVatBridge ||
|
||||
explicitSummaryBundleReuseSignal ||
|
||||
Boolean(debtRoleSwapIntent) ||
|
||||
|
|
@ -938,6 +960,7 @@ function createAssistantTransitionPolicy(deps) {
|
|||
!hasInventoryRootTemporalFollowupAlternate &&
|
||||
!hasInventoryRootRestatementPrimary &&
|
||||
!hasInventoryRootRestatementAlternate &&
|
||||
!hasInventorySupplierOverlapRootFollowup &&
|
||||
!shortValueFlowRetargetPrimary &&
|
||||
!shortValueFlowRetargetAlternate &&
|
||||
!selectedCounterpartyDocumentFollowupSignal &&
|
||||
|
|
@ -955,6 +978,7 @@ function createAssistantTransitionPolicy(deps) {
|
|||
!hasInventoryRootTemporalFollowupAlternate &&
|
||||
!hasInventoryRootRestatementPrimary &&
|
||||
!hasInventoryRootRestatementAlternate &&
|
||||
!hasInventorySupplierOverlapRootFollowup &&
|
||||
!shortValueFlowRetargetPrimary &&
|
||||
!shortValueFlowRetargetAlternate &&
|
||||
!selectedCounterpartyDocumentFollowupSignal &&
|
||||
|
|
@ -1037,6 +1061,7 @@ function createAssistantTransitionPolicy(deps) {
|
|||
!hasInventoryRootTemporalFollowupAlternate &&
|
||||
!hasInventoryRootRestatementPrimary &&
|
||||
!hasInventoryRootRestatementAlternate &&
|
||||
!hasInventorySupplierOverlapRootFollowup &&
|
||||
!inventoryShortFollowupPrimary &&
|
||||
!inventoryShortFollowupAlternate &&
|
||||
!businessOverviewBoundaryFollowupPrimary &&
|
||||
|
|
@ -1104,6 +1129,7 @@ function createAssistantTransitionPolicy(deps) {
|
|||
inventoryMarginRankingFollowup ||
|
||||
explicitSummaryBundleReuseSignal ||
|
||||
hasInventoryRootTemporalFollowupPrimary ||
|
||||
hasInventorySupplierOverlapRootFollowup ||
|
||||
mcpDiscoveryOrganizationClarificationContinuation;
|
||||
hasAlternateFollowupSignal = deps.toNonEmptyString(alternateMessage)
|
||||
? deps.hasAddressFollowupContextSignal(alternateMessage) ||
|
||||
|
|
@ -1116,6 +1142,7 @@ function createAssistantTransitionPolicy(deps) {
|
|||
inventoryMarginRankingFollowup ||
|
||||
explicitSummaryBundleReuseSignal ||
|
||||
hasInventoryRootTemporalFollowupAlternate ||
|
||||
hasInventorySupplierOverlapRootFollowup ||
|
||||
mcpDiscoveryOrganizationClarificationContinuation
|
||||
: false;
|
||||
hasStrongFollowupReference =
|
||||
|
|
@ -1129,6 +1156,7 @@ function createAssistantTransitionPolicy(deps) {
|
|||
inventoryShortFollowupAlternate ||
|
||||
hasInventoryRootTemporalFollowupPrimary ||
|
||||
hasInventoryRootTemporalFollowupAlternate ||
|
||||
hasInventorySupplierOverlapRootFollowup ||
|
||||
inventoryPurchaseDateVatBridge ||
|
||||
explicitSummaryBundleReuseSignal ||
|
||||
Boolean(debtRoleSwapIntent) ||
|
||||
|
|
@ -1261,7 +1289,8 @@ function createAssistantTransitionPolicy(deps) {
|
|||
}
|
||||
const predecomposeIntent = deps.toNonEmptyString(llmPreDecomposeMeta?.predecomposeContract?.intent);
|
||||
const carryoverTargetIntentPreview = explicitIntent ?? predecomposeIntent ?? previousIntent;
|
||||
if (shouldClearStaleDateScopeForCounterpartyDocsRoot(carryoverTargetIntentPreview, sourceIntentHint, userMessage, alternateMessage)) {
|
||||
if (!selectedCounterpartyDocumentFollowupSignal &&
|
||||
shouldClearStaleDateScopeForCounterpartyDocsRoot(carryoverTargetIntentPreview, sourceIntentHint, userMessage, alternateMessage)) {
|
||||
delete previousFilters.as_of_date;
|
||||
delete previousFilters.period_from;
|
||||
delete previousFilters.period_to;
|
||||
|
|
@ -1280,6 +1309,7 @@ function createAssistantTransitionPolicy(deps) {
|
|||
inventoryShortFollowupAlternate ||
|
||||
hasInventoryRootTemporalFollowupPrimary ||
|
||||
hasInventoryRootTemporalFollowupAlternate ||
|
||||
hasInventorySupplierOverlapRootFollowup ||
|
||||
hasInventoryRootRestatementPrimary ||
|
||||
hasInventoryRootRestatementAlternate ||
|
||||
hasSelectedObjectInventorySignalPrimary ||
|
||||
|
|
|
|||
|
|
@ -765,6 +765,7 @@ function shouldPreferRawFollowupMessage(
|
|||
const previousIntent = toNonEmptyString(followupContext?.previous_intent);
|
||||
const rootIntent = toNonEmptyString(followupContext?.root_intent);
|
||||
const previousAnchorType = toNonEmptyString(followupContext?.previous_anchor_type);
|
||||
const previousFilters = toRecordObject(followupContext?.previous_filters);
|
||||
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
|
||||
);
|
||||
|
|
@ -781,12 +782,14 @@ function shouldPreferRawFollowupMessage(
|
|||
[rawMessage, canonicalMessage].some((message) => hasInventoryMarginRankingAccountCorrectionCue(message));
|
||||
const hasDocumentCarryover =
|
||||
previousIntent === "list_documents_by_counterparty" || previousIntent === "list_documents_by_contract";
|
||||
const hasCounterpartyCarryover =
|
||||
previousAnchorType === "counterparty" || Boolean(toNonEmptyString(previousFilters?.counterparty));
|
||||
|
||||
if (mode === "unsupported" && intent === "unknown") {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (hasDocumentCarryover && hasReferentialDocumentExclusionFollowupCue) {
|
||||
if ((hasDocumentCarryover || hasCounterpartyCarryover) && hasReferentialDocumentExclusionFollowupCue) {
|
||||
return true;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -72,6 +72,42 @@ export function createAssistantTransitionPolicy(deps) {
|
|||
});
|
||||
}
|
||||
|
||||
function hasInventorySupplierOverlapRootFollowupSignal(
|
||||
userMessage,
|
||||
alternateMessage = null,
|
||||
sourceIntentHint = null,
|
||||
hasInventoryRootFrame = false
|
||||
) {
|
||||
if (
|
||||
!hasInventoryRootFrame ||
|
||||
(sourceIntentHint !== "inventory_on_hand_as_of_date" &&
|
||||
sourceIntentHint !== "inventory_supplier_stock_overlap_as_of_date")
|
||||
) {
|
||||
return false;
|
||||
}
|
||||
return [userMessage, alternateMessage]
|
||||
.filter((value) => deps.toNonEmptyString(value))
|
||||
.map((value) => normalizeFollowupText(value))
|
||||
.some((normalized) => {
|
||||
if (!normalized) {
|
||||
return false;
|
||||
}
|
||||
const hasSupplierCue =
|
||||
/(?:\u043f\u043e\u0441\u0442\u0430\u0432\u0449\u0438\u043a|supplier|vendor|\u0443\s+\u043a\u0430\u043a\u043e\u0433\u043e|\u0443\s+\u043a\u043e\u0433\u043e|\u043f\u043e\s+\u043a\u0430\u043a\u043e\u043c\u0443)/iu.test(
|
||||
normalized
|
||||
);
|
||||
const hasStockCue =
|
||||
/(?:\u043e\u0441\u0442\u0430\u0442|\u0441\u043a\u043b\u0430\u0434|\u0442\u043e\u0432\u0430\u0440|\u043f\u043e\u0437\u0438\u0446|\u043b\u0435\u0436|\u0432\u0438\u0441|\u0442\u0435\u043a\u0443\u0449)/iu.test(
|
||||
normalized
|
||||
);
|
||||
const hasPurchaseCue =
|
||||
/(?:\u043a\u0443\u043f\u0438\u043b|\u043a\u0443\u043f\u043b\u0435\u043d|\u043f\u043e\u043a\u0443\u043f|\u0437\u0430\u043a\u0443\u043f|\u043f\u043e\u0441\u0442\u0443\u043f\u043b|purchase|bought)/iu.test(
|
||||
normalized
|
||||
);
|
||||
return hasSupplierCue && (hasStockCue || hasPurchaseCue);
|
||||
});
|
||||
}
|
||||
|
||||
function hasSamePeriodReferenceCue(...values) {
|
||||
return values
|
||||
.map((value) => normalizeFollowupText(value))
|
||||
|
|
@ -1180,6 +1216,12 @@ export function createAssistantTransitionPolicy(deps) {
|
|||
Boolean(recentInventoryRootFrame)
|
||||
)
|
||||
: false;
|
||||
const hasInventorySupplierOverlapRootFollowup = hasInventorySupplierOverlapRootFollowupSignal(
|
||||
userMessage,
|
||||
alternateMessage,
|
||||
sourceIntentHint,
|
||||
Boolean(recentInventoryRootFrame)
|
||||
);
|
||||
const hasExplicitInventorySameDatePivotPrimary = hasExplicitInventorySameDatePivotSignal(userMessage);
|
||||
const hasExplicitInventorySameDatePivotAlternate = deps.toNonEmptyString(alternateMessage)
|
||||
? hasExplicitInventorySameDatePivotSignal(String(alternateMessage ?? ""))
|
||||
|
|
@ -1197,6 +1239,7 @@ export function createAssistantTransitionPolicy(deps) {
|
|||
hasInventoryRootTemporalFollowupAlternate ||
|
||||
hasInventoryRootRestatementPrimary ||
|
||||
hasInventoryRootRestatementAlternate ||
|
||||
hasInventorySupplierOverlapRootFollowup ||
|
||||
inventoryPurchaseDateVatBridge ||
|
||||
explicitSummaryBundleReuseSignal ||
|
||||
Boolean(debtRoleSwapIntent) ||
|
||||
|
|
@ -1223,6 +1266,7 @@ export function createAssistantTransitionPolicy(deps) {
|
|||
hasInventoryRootTemporalFollowupAlternate ||
|
||||
hasInventoryRootRestatementPrimary ||
|
||||
hasInventoryRootRestatementAlternate ||
|
||||
hasInventorySupplierOverlapRootFollowup ||
|
||||
inventoryPurchaseDateVatBridge ||
|
||||
explicitSummaryBundleReuseSignal ||
|
||||
Boolean(debtRoleSwapIntent) ||
|
||||
|
|
@ -1259,6 +1303,7 @@ export function createAssistantTransitionPolicy(deps) {
|
|||
!hasInventoryRootTemporalFollowupAlternate &&
|
||||
!hasInventoryRootRestatementPrimary &&
|
||||
!hasInventoryRootRestatementAlternate &&
|
||||
!hasInventorySupplierOverlapRootFollowup &&
|
||||
!shortValueFlowRetargetPrimary &&
|
||||
!shortValueFlowRetargetAlternate &&
|
||||
!selectedCounterpartyDocumentFollowupSignal &&
|
||||
|
|
@ -1278,6 +1323,7 @@ export function createAssistantTransitionPolicy(deps) {
|
|||
!hasInventoryRootTemporalFollowupAlternate &&
|
||||
!hasInventoryRootRestatementPrimary &&
|
||||
!hasInventoryRootRestatementAlternate &&
|
||||
!hasInventorySupplierOverlapRootFollowup &&
|
||||
!shortValueFlowRetargetPrimary &&
|
||||
!shortValueFlowRetargetAlternate &&
|
||||
!selectedCounterpartyDocumentFollowupSignal &&
|
||||
|
|
@ -1412,6 +1458,7 @@ export function createAssistantTransitionPolicy(deps) {
|
|||
!hasInventoryRootTemporalFollowupAlternate &&
|
||||
!hasInventoryRootRestatementPrimary &&
|
||||
!hasInventoryRootRestatementAlternate &&
|
||||
!hasInventorySupplierOverlapRootFollowup &&
|
||||
!inventoryShortFollowupPrimary &&
|
||||
!inventoryShortFollowupAlternate &&
|
||||
!businessOverviewBoundaryFollowupPrimary &&
|
||||
|
|
@ -1483,6 +1530,7 @@ export function createAssistantTransitionPolicy(deps) {
|
|||
inventoryMarginRankingFollowup ||
|
||||
explicitSummaryBundleReuseSignal ||
|
||||
hasInventoryRootTemporalFollowupPrimary ||
|
||||
hasInventorySupplierOverlapRootFollowup ||
|
||||
mcpDiscoveryOrganizationClarificationContinuation;
|
||||
hasAlternateFollowupSignal = deps.toNonEmptyString(alternateMessage)
|
||||
? deps.hasAddressFollowupContextSignal(alternateMessage) ||
|
||||
|
|
@ -1495,6 +1543,7 @@ export function createAssistantTransitionPolicy(deps) {
|
|||
inventoryMarginRankingFollowup ||
|
||||
explicitSummaryBundleReuseSignal ||
|
||||
hasInventoryRootTemporalFollowupAlternate ||
|
||||
hasInventorySupplierOverlapRootFollowup ||
|
||||
mcpDiscoveryOrganizationClarificationContinuation
|
||||
: false;
|
||||
hasStrongFollowupReference =
|
||||
|
|
@ -1508,6 +1557,7 @@ export function createAssistantTransitionPolicy(deps) {
|
|||
inventoryShortFollowupAlternate ||
|
||||
hasInventoryRootTemporalFollowupPrimary ||
|
||||
hasInventoryRootTemporalFollowupAlternate ||
|
||||
hasInventorySupplierOverlapRootFollowup ||
|
||||
inventoryPurchaseDateVatBridge ||
|
||||
explicitSummaryBundleReuseSignal ||
|
||||
Boolean(debtRoleSwapIntent) ||
|
||||
|
|
@ -1728,6 +1778,7 @@ export function createAssistantTransitionPolicy(deps) {
|
|||
const predecomposeIntent = deps.toNonEmptyString(llmPreDecomposeMeta?.predecomposeContract?.intent);
|
||||
const carryoverTargetIntentPreview = explicitIntent ?? predecomposeIntent ?? previousIntent;
|
||||
if (
|
||||
!selectedCounterpartyDocumentFollowupSignal &&
|
||||
shouldClearStaleDateScopeForCounterpartyDocsRoot(
|
||||
carryoverTargetIntentPreview,
|
||||
sourceIntentHint,
|
||||
|
|
@ -1754,6 +1805,7 @@ export function createAssistantTransitionPolicy(deps) {
|
|||
inventoryShortFollowupAlternate ||
|
||||
hasInventoryRootTemporalFollowupPrimary ||
|
||||
hasInventoryRootTemporalFollowupAlternate ||
|
||||
hasInventorySupplierOverlapRootFollowup ||
|
||||
hasInventoryRootRestatementPrimary ||
|
||||
hasInventoryRootRestatementAlternate ||
|
||||
hasSelectedObjectInventorySignalPrimary ||
|
||||
|
|
|
|||
|
|
@ -153,6 +153,133 @@ describe("assistantTransitionPolicy", () => {
|
|||
expect(carryover?.followupContext?.root_intent).toBe("inventory_on_hand_as_of_date");
|
||||
});
|
||||
|
||||
it("carries inventory root date into supplier-overlap current-stock follow-up", () => {
|
||||
const organization = "\u041e\u041e\u041e \"\u0410\u043b\u044c\u0442\u0435\u0440\u043d\u0430\u0442\u0438\u0432\u0430 \u041f\u043b\u044e\u0441\"";
|
||||
const warehouse = "\u041e\u0441\u043d\u043e\u0432\u043d\u043e\u0439 \u0441\u043a\u043b\u0430\u0434";
|
||||
const rootFilters = {
|
||||
period_from: "2021-09-01",
|
||||
period_to: "2021-09-30",
|
||||
as_of_date: "2021-09-30",
|
||||
organization,
|
||||
warehouse
|
||||
};
|
||||
const policy = buildPolicy({
|
||||
findLastAddressAssistantItem: () => ({
|
||||
text: "\u041d\u0430 30.09.2021 \u043f\u043e \u0441\u043a\u043b\u0430\u0434\u0443 \u041e\u0441\u043d\u043e\u0432\u043d\u043e\u0439 \u0441\u043a\u043b\u0430\u0434 \u043f\u043e\u0434\u0442\u0432\u0435\u0440\u0436\u0434\u0435\u043d\u044b \u043e\u0441\u0442\u0430\u0442\u043a\u0438.",
|
||||
debug: {
|
||||
detected_intent: "inventory_on_hand_as_of_date",
|
||||
selected_recipe: "address_inventory_on_hand_as_of_date_v1",
|
||||
extracted_filters: rootFilters,
|
||||
anchor_type: "warehouse",
|
||||
anchor_value_resolved: warehouse
|
||||
}
|
||||
}),
|
||||
findRecentInventoryRootFrame: () => ({
|
||||
intent: "inventory_on_hand_as_of_date",
|
||||
filters: rootFilters,
|
||||
anchorType: "warehouse",
|
||||
anchorValue: warehouse,
|
||||
currentFrameKind: "inventory_root"
|
||||
}),
|
||||
hasAddressFollowupContextSignal: () => false,
|
||||
hasStandaloneAddressTopicSignal: () => true,
|
||||
resolveAddressIntent: () => ({ intent: "inventory_supplier_stock_overlap_as_of_date" }),
|
||||
resolveAddressIntentFamily: (intent: unknown) =>
|
||||
String(intent ?? "").startsWith("inventory_") ? "inventory" : null
|
||||
});
|
||||
|
||||
const carryover = policy.resolveAddressFollowupCarryoverContext(
|
||||
"\u0423 \u043a\u0430\u043a\u043e\u0433\u043e \u043f\u043e\u0441\u0442\u0430\u0432\u0449\u0438\u043a\u0430 \u0431\u044b\u043b\u0438 \u043a\u0443\u043f\u043b\u0435\u043d\u044b \u0442\u043e\u0432\u0430\u0440\u044b, \u043a\u043e\u0442\u043e\u0440\u044b\u0435 \u0441\u0435\u0439\u0447\u0430\u0441 \u043b\u0435\u0436\u0430\u0442 \u043d\u0430 \u0441\u043a\u043b\u0430\u0434\u0435 \u041e\u0441\u043d\u043e\u0432\u043d\u043e\u0439 \u0441\u043a\u043b\u0430\u0434",
|
||||
[],
|
||||
"\u041e\u043f\u0440\u0435\u0434\u0435\u043b\u0438\u0442\u044c \u043f\u043e\u0441\u0442\u0430\u0432\u0449\u0438\u043a\u0430 \u0442\u043e\u0432\u0430\u0440\u043e\u0432, \u043a\u043e\u0442\u043e\u0440\u044b\u0435 \u043d\u0430\u0445\u043e\u0434\u044f\u0442\u0441\u044f \u043d\u0430 \u0441\u043a\u043b\u0430\u0434\u0435 \u041e\u0441\u043d\u043e\u0432\u043d\u043e\u0439 \u0441\u043a\u043b\u0430\u0434 \u0432 \u0442\u0435\u043a\u0443\u0449\u0438\u0439 \u043c\u043e\u043c\u0435\u043d\u0442",
|
||||
{
|
||||
predecomposeContract: {
|
||||
intent: "inventory_supplier_stock_overlap_as_of_date"
|
||||
}
|
||||
},
|
||||
{
|
||||
session_context: {
|
||||
date_scope: rootFilters,
|
||||
organization_scope: organization,
|
||||
active_focus_object: {
|
||||
object_type: "organization",
|
||||
label: organization
|
||||
}
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
expect(carryover?.followupContext?.previous_intent).toBe("inventory_on_hand_as_of_date");
|
||||
expect(carryover?.followupContext?.target_intent).toBe("inventory_supplier_stock_overlap_as_of_date");
|
||||
expect(carryover?.followupContext?.previous_filters).toMatchObject(rootFilters);
|
||||
expect(carryover?.followupContext?.root_intent).toBe("inventory_on_hand_as_of_date");
|
||||
expect(carryover?.followupContext?.root_filters).toMatchObject(rootFilters);
|
||||
expect(carryover?.followupContext?.current_frame_kind).toBe("inventory_root");
|
||||
});
|
||||
|
||||
it("keeps supplier-overlap date on repeated current-residue supplier wording", () => {
|
||||
const organization = "\u041e\u041e\u041e \"\u0410\u043b\u044c\u0442\u0435\u0440\u043d\u0430\u0442\u0438\u0432\u0430 \u041f\u043b\u044e\u0441\"";
|
||||
const warehouse = "\u041e\u0441\u043d\u043e\u0432\u043d\u043e\u0439 \u0441\u043a\u043b\u0430\u0434";
|
||||
const rootFilters = {
|
||||
period_from: "2021-09-01",
|
||||
period_to: "2021-09-30",
|
||||
as_of_date: "2021-09-30",
|
||||
organization,
|
||||
warehouse
|
||||
};
|
||||
const policy = buildPolicy({
|
||||
findLastAddressAssistantItem: () => ({
|
||||
text: "\u041f\u043e \u0441\u043a\u043b\u0430\u0434\u0441\u043a\u043e\u043c\u0443 \u043e\u0441\u0442\u0430\u0442\u043a\u0443 \u043d\u0430 30.09.2021 \u043d\u0430\u0439\u0434\u0435\u043d\u044b \u043f\u043e\u0441\u0442\u0430\u0432\u0449\u0438\u043a\u0438.",
|
||||
debug: {
|
||||
detected_intent: "inventory_supplier_stock_overlap_as_of_date",
|
||||
selected_recipe: "address_inventory_supplier_stock_overlap_as_of_date_v1",
|
||||
extracted_filters: rootFilters,
|
||||
anchor_type: "warehouse",
|
||||
anchor_value_resolved: warehouse
|
||||
}
|
||||
}),
|
||||
findRecentInventoryRootFrame: () => ({
|
||||
intent: "inventory_on_hand_as_of_date",
|
||||
filters: rootFilters,
|
||||
anchorType: "warehouse",
|
||||
anchorValue: warehouse,
|
||||
currentFrameKind: "inventory_root"
|
||||
}),
|
||||
hasAddressFollowupContextSignal: () => false,
|
||||
hasStandaloneAddressTopicSignal: () => true,
|
||||
resolveAddressIntent: () => ({ intent: "inventory_supplier_stock_overlap_as_of_date" }),
|
||||
resolveAddressIntentFamily: (intent: unknown) =>
|
||||
String(intent ?? "").startsWith("inventory_") ? "inventory" : null
|
||||
});
|
||||
|
||||
const carryover = policy.resolveAddressFollowupCarryoverContext(
|
||||
"\u041f\u043e \u043a\u0430\u043a\u043e\u043c\u0443 \u043f\u043e\u0441\u0442\u0430\u0432\u0449\u0438\u043a\u0443 \u043f\u0440\u043e\u0445\u043e\u0434\u0438\u0442 \u0442\u0435\u043a\u0443\u0449\u0438\u0439 \u0442\u043e\u0432\u0430\u0440\u043d\u044b\u0439 \u043e\u0441\u0442\u0430\u0442\u043e\u043a \u043d\u0430 \u0441\u043a\u043b\u0430\u0434\u0435 \u041e\u0441\u043d\u043e\u0432\u043d\u043e\u0439 \u0441\u043a\u043b\u0430\u0434",
|
||||
[],
|
||||
"\u041e\u043f\u0440\u0435\u0434\u0435\u043b\u0438\u0442\u044c \u043f\u043e\u0441\u0442\u0430\u0432\u0449\u0438\u043a\u0430, \u0441 \u043a\u043e\u0442\u043e\u0440\u043e\u0433\u043e \u043f\u043e\u0441\u0442\u0443\u043f\u0438\u043b\u0438 \u0442\u043e\u0432\u0430\u0440\u044b, \u043e\u0431\u0440\u0430\u0437\u0443\u044e\u0449\u0438\u0435 \u0442\u0435\u043a\u0443\u0449\u0438\u0439 \u043e\u0441\u0442\u0430\u0442\u043e\u043a \u043d\u0430 \u0441\u043a\u043b\u0430\u0434\u0435 \u041e\u0441\u043d\u043e\u0432\u043d\u043e\u0439 \u0441\u043a\u043b\u0430\u0434",
|
||||
{
|
||||
predecomposeContract: {
|
||||
intent: "inventory_supplier_stock_overlap_as_of_date"
|
||||
}
|
||||
},
|
||||
{
|
||||
session_context: {
|
||||
date_scope: rootFilters,
|
||||
organization_scope: organization,
|
||||
active_focus_object: {
|
||||
object_type: "warehouse",
|
||||
label: warehouse
|
||||
}
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
expect(carryover?.followupContext?.previous_intent).toBe("inventory_supplier_stock_overlap_as_of_date");
|
||||
expect(carryover?.followupContext?.target_intent).toBe("inventory_supplier_stock_overlap_as_of_date");
|
||||
expect(carryover?.followupContext?.previous_filters).toMatchObject(rootFilters);
|
||||
expect(carryover?.followupContext?.root_intent).toBe("inventory_on_hand_as_of_date");
|
||||
expect(carryover?.followupContext?.current_frame_kind).toBe("inventory_root");
|
||||
});
|
||||
|
||||
it("builds continuation contract from extracted root carryover", () => {
|
||||
const policy = buildPolicy();
|
||||
|
||||
|
|
|
|||
|
|
@ -1,4 +1,40 @@
|
|||
[
|
||||
{
|
||||
"generation_id": "gen-ag06030918-32dc7c",
|
||||
"created_at": "2026-06-03T09:18:35+00:00",
|
||||
"mode": "saved_user_sessions",
|
||||
"title": "AGENT | Supplier-overlap follow-up keeps the inventory root date",
|
||||
"count": 3,
|
||||
"domain": "inventory_supplier_overlap_temporal_carryover",
|
||||
"questions": [
|
||||
"какие остатки на складе на сентябрь 2021",
|
||||
"У какого поставщика были куплены товары, которые сейчас лежат на складе Основной склад",
|
||||
"По какому поставщику проходит текущий товарный остаток на складе Основной склад"
|
||||
],
|
||||
"generated_by": "codex_agent",
|
||||
"saved_case_set_file": "assistant_autogen_saved_user_sessions_20260603091835_gen-ag06030918-32dc7c.json",
|
||||
"context": {
|
||||
"llm_provider": null,
|
||||
"model": null,
|
||||
"assistant_prompt_version": null,
|
||||
"decomposition_prompt_version": null,
|
||||
"prompt_fingerprint": null,
|
||||
"autogen_personality_id": null,
|
||||
"autogen_personality_prompt": null,
|
||||
"source_session_id": null,
|
||||
"saved_session_file": "assistant_saved_session_20260603091835_gen-ag06030918-32dc7c.json",
|
||||
"saved_case_set_kind": "agent_semantic_scenario",
|
||||
"agent_run": true,
|
||||
"agent_focus": "Targeted replay for temporal carryover: after an inventory root slice for September 2021, colloquial supplier-overlap follow-ups with 'сейчас' / 'текущий товарный остаток' must stay on the confirmed root slice date instead of drifting to the wall-clock date.",
|
||||
"architecture_phase": "turnaround_11",
|
||||
"source_spec_file": "X:\\1C\\NDC_1C\\docs\\orchestration\\address_truth_harness_inventory_supplier_overlap_temporal_carryover.json",
|
||||
"scenario_id": "address_truth_harness_inventory_supplier_overlap_temporal_carryover",
|
||||
"semantic_tags": [],
|
||||
"validation_status": "accepted_live_replay",
|
||||
"validated_run_dir": "artifacts\\domain_runs\\inventory_supplier_overlap_temporal_carryover_p2",
|
||||
"saved_after_validated_replay": true
|
||||
}
|
||||
},
|
||||
{
|
||||
"generation_id": "gen-ag06030841-d61275",
|
||||
"created_at": "2026-06-03T08:41:17+00:00",
|
||||
|
|
|
|||
|
|
@ -0,0 +1,133 @@
|
|||
{
|
||||
"saved_at": "2026-06-03T09:18:35+00:00",
|
||||
"generation_id": "gen-ag06030918-32dc7c",
|
||||
"mode": "saved_user_sessions",
|
||||
"title": "AGENT | Supplier-overlap follow-up keeps the inventory root date",
|
||||
"agent_run": true,
|
||||
"questions": [
|
||||
"какие остатки на складе на сентябрь 2021",
|
||||
"У какого поставщика были куплены товары, которые сейчас лежат на складе Основной склад",
|
||||
"По какому поставщику проходит текущий товарный остаток на складе Основной склад"
|
||||
],
|
||||
"metadata": {
|
||||
"assistant_prompt_version": null,
|
||||
"decomposition_prompt_version": null,
|
||||
"prompt_fingerprint": null,
|
||||
"agent_focus": "Targeted replay for temporal carryover: after an inventory root slice for September 2021, colloquial supplier-overlap follow-ups with 'сейчас' / 'текущий товарный остаток' must stay on the confirmed root slice date instead of drifting to the wall-clock date.",
|
||||
"architecture_phase": "turnaround_11",
|
||||
"source_spec_file": "X:\\1C\\NDC_1C\\docs\\orchestration\\address_truth_harness_inventory_supplier_overlap_temporal_carryover.json",
|
||||
"scenario_id": "address_truth_harness_inventory_supplier_overlap_temporal_carryover",
|
||||
"semantic_tags": [],
|
||||
"validation_status": "accepted_live_replay",
|
||||
"validated_run_dir": "artifacts\\domain_runs\\inventory_supplier_overlap_temporal_carryover_p2",
|
||||
"saved_after_validated_replay": true,
|
||||
"save_gate": {
|
||||
"schema_version": "agent_semantic_save_gate_v1",
|
||||
"validation_status": "accepted_live_replay",
|
||||
"validated_run_dir": "artifacts\\domain_runs\\inventory_supplier_overlap_temporal_carryover_p2",
|
||||
"final_status": "accepted",
|
||||
"review_overall_status": "pass",
|
||||
"business_overall_status": "pass",
|
||||
"steps_total": 3,
|
||||
"steps_passed": 3,
|
||||
"steps_failed": 0,
|
||||
"steps_with_business_failures": 0,
|
||||
"steps_with_business_warnings": 0,
|
||||
"acceptance_gate_passed": true,
|
||||
"effective_runtime": {
|
||||
"manifest_path": "artifacts\\domain_runs\\inventory_supplier_overlap_temporal_carryover_p2\\effective_runtime.json",
|
||||
"runner": "domain_truth_harness.run-live",
|
||||
"git_sha": "f0b84822d3e9283b5a8b823bd22ed571322a905f",
|
||||
"backend_url": "http://127.0.0.1:8787",
|
||||
"mcp_proxy_url": "http://127.0.0.1:6003",
|
||||
"llm_provider": "local",
|
||||
"llm_model": "unsloth/qwen3-30b-a3b-instruct-2507",
|
||||
"temperature": 0.8,
|
||||
"max_output_tokens": 900,
|
||||
"prompt_version": "normalizer_v2_0_2",
|
||||
"prompt_source": "file",
|
||||
"prompt_hash": "f36e36a5e491cd24511b380e0c0059cb01aea5e2af738fb9f01671cd291735c1",
|
||||
"prompt_registry_status": "pass"
|
||||
},
|
||||
"saved_after_validated_replay": true
|
||||
}
|
||||
},
|
||||
"source_session_id": null,
|
||||
"session": {
|
||||
"session_id": null,
|
||||
"mode": "agent_semantic_run",
|
||||
"items": [
|
||||
{
|
||||
"message_id": "agent-user-001",
|
||||
"role": "user",
|
||||
"text": "какие остатки на складе на сентябрь 2021",
|
||||
"created_at": "2026-06-03T09:18:35+00:00",
|
||||
"reply_type": null,
|
||||
"trace_id": null,
|
||||
"debug": null
|
||||
},
|
||||
{
|
||||
"message_id": "agent-user-002",
|
||||
"role": "user",
|
||||
"text": "У какого поставщика были куплены товары, которые сейчас лежат на складе Основной склад",
|
||||
"created_at": "2026-06-03T09:18:35+00:00",
|
||||
"reply_type": null,
|
||||
"trace_id": null,
|
||||
"debug": null
|
||||
},
|
||||
{
|
||||
"message_id": "agent-user-003",
|
||||
"role": "user",
|
||||
"text": "По какому поставщику проходит текущий товарный остаток на складе Основной склад",
|
||||
"created_at": "2026-06-03T09:18:35+00:00",
|
||||
"reply_type": null,
|
||||
"trace_id": null,
|
||||
"debug": null
|
||||
}
|
||||
],
|
||||
"agent_run": true,
|
||||
"metadata": {
|
||||
"assistant_prompt_version": null,
|
||||
"decomposition_prompt_version": null,
|
||||
"prompt_fingerprint": null,
|
||||
"agent_focus": "Targeted replay for temporal carryover: after an inventory root slice for September 2021, colloquial supplier-overlap follow-ups with 'сейчас' / 'текущий товарный остаток' must stay on the confirmed root slice date instead of drifting to the wall-clock date.",
|
||||
"architecture_phase": "turnaround_11",
|
||||
"source_spec_file": "X:\\1C\\NDC_1C\\docs\\orchestration\\address_truth_harness_inventory_supplier_overlap_temporal_carryover.json",
|
||||
"scenario_id": "address_truth_harness_inventory_supplier_overlap_temporal_carryover",
|
||||
"semantic_tags": [],
|
||||
"validation_status": "accepted_live_replay",
|
||||
"validated_run_dir": "artifacts\\domain_runs\\inventory_supplier_overlap_temporal_carryover_p2",
|
||||
"saved_after_validated_replay": true,
|
||||
"save_gate": {
|
||||
"schema_version": "agent_semantic_save_gate_v1",
|
||||
"validation_status": "accepted_live_replay",
|
||||
"validated_run_dir": "artifacts\\domain_runs\\inventory_supplier_overlap_temporal_carryover_p2",
|
||||
"final_status": "accepted",
|
||||
"review_overall_status": "pass",
|
||||
"business_overall_status": "pass",
|
||||
"steps_total": 3,
|
||||
"steps_passed": 3,
|
||||
"steps_failed": 0,
|
||||
"steps_with_business_failures": 0,
|
||||
"steps_with_business_warnings": 0,
|
||||
"acceptance_gate_passed": true,
|
||||
"effective_runtime": {
|
||||
"manifest_path": "artifacts\\domain_runs\\inventory_supplier_overlap_temporal_carryover_p2\\effective_runtime.json",
|
||||
"runner": "domain_truth_harness.run-live",
|
||||
"git_sha": "f0b84822d3e9283b5a8b823bd22ed571322a905f",
|
||||
"backend_url": "http://127.0.0.1:8787",
|
||||
"mcp_proxy_url": "http://127.0.0.1:6003",
|
||||
"llm_provider": "local",
|
||||
"llm_model": "unsloth/qwen3-30b-a3b-instruct-2507",
|
||||
"temperature": 0.8,
|
||||
"max_output_tokens": 900,
|
||||
"prompt_version": "normalizer_v2_0_2",
|
||||
"prompt_source": "file",
|
||||
"prompt_hash": "f36e36a5e491cd24511b380e0c0059cb01aea5e2af738fb9f01671cd291735c1",
|
||||
"prompt_registry_status": "pass"
|
||||
},
|
||||
"saved_after_validated_replay": true
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,34 @@
|
|||
{
|
||||
"suite_id": "assistant_saved_session_gen-ag06030918-32dc7c",
|
||||
"suite_version": "0.1.0",
|
||||
"schema_version": "assistant_saved_session_suite_v0_1",
|
||||
"generated_at": "2026-06-03T09:18:35+00:00",
|
||||
"generation_id": "gen-ag06030918-32dc7c",
|
||||
"mode": "saved_user_sessions",
|
||||
"title": "AGENT | Supplier-overlap follow-up keeps the inventory root date",
|
||||
"domain": "inventory_supplier_overlap_temporal_carryover",
|
||||
"scenario_count": 1,
|
||||
"case_ids": [
|
||||
"SAVED-001"
|
||||
],
|
||||
"cases": [
|
||||
{
|
||||
"case_id": "SAVED-001",
|
||||
"scenario_tag": "agent_saved_user_sessions",
|
||||
"title": "AGENT | Supplier-overlap follow-up keeps the inventory root date",
|
||||
"question_type": "followup",
|
||||
"broadness_level": "medium",
|
||||
"turns": [
|
||||
{
|
||||
"user_message": "какие остатки на складе на сентябрь 2021"
|
||||
},
|
||||
{
|
||||
"user_message": "У какого поставщика были куплены товары, которые сейчас лежат на складе Основной склад"
|
||||
},
|
||||
{
|
||||
"user_message": "По какому поставщику проходит текущий товарный остаток на складе Основной склад"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
Loading…
Reference in New Issue