ARCH: удержать contracts pivot после payments follow-up
This commit is contained in:
parent
7e1a2edadb
commit
d7ee95286d
|
|
@ -0,0 +1,64 @@
|
||||||
|
{
|
||||||
|
"schema_version": "domain_truth_harness_spec_v1",
|
||||||
|
"scenario_id": "address_truth_harness_phase76_contracts_to_payments_all_time",
|
||||||
|
"domain": "address_phase76_contracts_to_payments_all_time",
|
||||||
|
"title": "Phase 76 contracts to payments all-time continuity",
|
||||||
|
"description": "Replay for a human chain where the user opens documents by counterparty, pivots to contracts, then pivots again to payments via pronoun follow-up, and finally switches to all-time scope without renaming the counterparty.",
|
||||||
|
"bindings": {},
|
||||||
|
"steps": [
|
||||||
|
{
|
||||||
|
"step_id": "step_01_documents_by_counterparty",
|
||||||
|
"title": "Open documents for the counterparty",
|
||||||
|
"question": "Покажи документы по Жуковке 51.",
|
||||||
|
"allowed_reply_types": ["factual", "factual_with_explanation", "partial_coverage"],
|
||||||
|
"required_answer_patterns_all": [
|
||||||
|
"(?i)жуковк",
|
||||||
|
"(?i)документ|сч[её]т|акт|накладн|строк"
|
||||||
|
],
|
||||||
|
"criticality": "critical",
|
||||||
|
"semantic_tags": ["documents_by_counterparty", "pivot_seed", "integrity_guard"]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"step_id": "step_02_contracts_by_pronoun_followup",
|
||||||
|
"title": "Pivot to contracts",
|
||||||
|
"question": "А по нему договоры?",
|
||||||
|
"allowed_reply_types": ["factual", "factual_with_explanation", "partial_coverage"],
|
||||||
|
"required_answer_patterns_all": [
|
||||||
|
"(?i)жуковк|контрагент",
|
||||||
|
"(?i)договор|контракт|соглаш"
|
||||||
|
],
|
||||||
|
"criticality": "critical",
|
||||||
|
"semantic_tags": ["contracts_followup", "counterparty_pronoun_resolution", "integrity_guard"]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"step_id": "step_03_payments_after_contracts_pivot",
|
||||||
|
"title": "Pivot again to payments",
|
||||||
|
"question": "А по нему платежи?",
|
||||||
|
"allowed_reply_types": ["factual", "factual_with_explanation", "partial_coverage"],
|
||||||
|
"required_answer_patterns_all": [
|
||||||
|
"(?i)жуковк|контрагент",
|
||||||
|
"(?i)платеж|операц|банк|поступлен|списан"
|
||||||
|
],
|
||||||
|
"criticality": "critical",
|
||||||
|
"semantic_tags": ["payments_followup", "second_pivot", "integrity_guard"]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"step_id": "step_04_all_time_after_second_pivot",
|
||||||
|
"title": "Switch to all-time scope after the second pivot",
|
||||||
|
"question": "А за все время?",
|
||||||
|
"allowed_reply_types": ["factual", "factual_with_explanation", "partial_coverage"],
|
||||||
|
"required_answer_patterns_all": [
|
||||||
|
"(?i)жуковк|контрагент",
|
||||||
|
"(?i)платеж|операц|банк|поступлен|списан"
|
||||||
|
],
|
||||||
|
"forbidden_answer_patterns": [
|
||||||
|
"(?i)уточните .* контрагент",
|
||||||
|
"(?i)метадан",
|
||||||
|
"(?i)схем",
|
||||||
|
"(?i)объект[а-я]* 1с"
|
||||||
|
],
|
||||||
|
"criticality": "critical",
|
||||||
|
"semantic_tags": ["all_time_after_second_pivot", "payments_followup", "integrity_guard"]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,65 @@
|
||||||
|
{
|
||||||
|
"schema_version": "domain_truth_harness_spec_v1",
|
||||||
|
"scenario_id": "address_truth_harness_phase77_payments_to_contracts_year_switch",
|
||||||
|
"domain": "address_phase77_payments_to_contracts_year_switch",
|
||||||
|
"title": "Phase 77 payments to contracts year-switch continuity",
|
||||||
|
"description": "Replay for a human chain where the user opens documents by counterparty, pivots to payments, then pivots again to contracts via pronoun follow-up, and finally switches the year without renaming the counterparty.",
|
||||||
|
"bindings": {},
|
||||||
|
"steps": [
|
||||||
|
{
|
||||||
|
"step_id": "step_01_documents_by_counterparty",
|
||||||
|
"title": "Open documents for the counterparty",
|
||||||
|
"question": "Покажи документы по Жуковке 51.",
|
||||||
|
"allowed_reply_types": ["factual", "factual_with_explanation", "partial_coverage"],
|
||||||
|
"required_answer_patterns_all": [
|
||||||
|
"(?i)жуковк",
|
||||||
|
"(?i)документ|сч[её]т|акт|накладн|строк"
|
||||||
|
],
|
||||||
|
"criticality": "critical",
|
||||||
|
"semantic_tags": ["documents_by_counterparty", "pivot_seed", "integrity_guard"]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"step_id": "step_02_payments_by_pronoun_followup",
|
||||||
|
"title": "Pivot to payments",
|
||||||
|
"question": "А по нему платежи?",
|
||||||
|
"allowed_reply_types": ["factual", "factual_with_explanation", "partial_coverage"],
|
||||||
|
"required_answer_patterns_all": [
|
||||||
|
"(?i)жуковк|контрагент",
|
||||||
|
"(?i)платеж|операц|банк|поступлен|списан"
|
||||||
|
],
|
||||||
|
"criticality": "critical",
|
||||||
|
"semantic_tags": ["payments_followup", "counterparty_pronoun_resolution", "integrity_guard"]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"step_id": "step_03_contracts_after_payments_pivot",
|
||||||
|
"title": "Pivot again to contracts",
|
||||||
|
"question": "А по нему договоры?",
|
||||||
|
"allowed_reply_types": ["factual", "factual_with_explanation", "partial_coverage"],
|
||||||
|
"required_answer_patterns_all": [
|
||||||
|
"(?i)жуковк|контрагент",
|
||||||
|
"(?i)договор|контракт|соглаш"
|
||||||
|
],
|
||||||
|
"criticality": "critical",
|
||||||
|
"semantic_tags": ["contracts_followup", "second_pivot", "integrity_guard"]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"step_id": "step_04_year_switch_after_second_pivot",
|
||||||
|
"title": "Switch the year after the second pivot",
|
||||||
|
"question": "А за 2021?",
|
||||||
|
"allowed_reply_types": ["factual", "factual_with_explanation", "partial_coverage"],
|
||||||
|
"required_answer_patterns_all": [
|
||||||
|
"(?i)2021",
|
||||||
|
"(?i)жуковк|контрагент",
|
||||||
|
"(?i)договор|контракт|соглаш"
|
||||||
|
],
|
||||||
|
"forbidden_answer_patterns": [
|
||||||
|
"(?i)уточните .* контрагент",
|
||||||
|
"(?i)метадан",
|
||||||
|
"(?i)схем",
|
||||||
|
"(?i)объект[а-я]* 1с"
|
||||||
|
],
|
||||||
|
"criticality": "critical",
|
||||||
|
"semantic_tags": ["year_switch_after_second_pivot", "contracts_followup", "integrity_guard"]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
@ -554,6 +554,7 @@ function createAssistantTransitionPolicy(deps) {
|
||||||
!shortValueFlowRetargetPrimary &&
|
!shortValueFlowRetargetPrimary &&
|
||||||
!shortValueFlowRetargetAlternate &&
|
!shortValueFlowRetargetAlternate &&
|
||||||
!hasImplicitContinuationSignal &&
|
!hasImplicitContinuationSignal &&
|
||||||
|
!hasSuggestedIntentPivotSignal &&
|
||||||
!hasOrganizationClarificationContinuation &&
|
!hasOrganizationClarificationContinuation &&
|
||||||
!hasIndexReferenceSignal) {
|
!hasIndexReferenceSignal) {
|
||||||
return null;
|
return null;
|
||||||
|
|
@ -567,6 +568,7 @@ function createAssistantTransitionPolicy(deps) {
|
||||||
!shortValueFlowRetargetPrimary &&
|
!shortValueFlowRetargetPrimary &&
|
||||||
!shortValueFlowRetargetAlternate &&
|
!shortValueFlowRetargetAlternate &&
|
||||||
!hasImplicitContinuationSignal &&
|
!hasImplicitContinuationSignal &&
|
||||||
|
!hasSuggestedIntentPivotSignal &&
|
||||||
!hasOrganizationClarificationContinuation &&
|
!hasOrganizationClarificationContinuation &&
|
||||||
!hasIndexReferenceSignal) {
|
!hasIndexReferenceSignal) {
|
||||||
return null;
|
return null;
|
||||||
|
|
@ -683,6 +685,7 @@ function createAssistantTransitionPolicy(deps) {
|
||||||
}
|
}
|
||||||
hasPrimaryFollowupSignal =
|
hasPrimaryFollowupSignal =
|
||||||
deps.hasAddressFollowupContextSignal(userMessage) ||
|
deps.hasAddressFollowupContextSignal(userMessage) ||
|
||||||
|
hasSuggestedIntentPivotSignal ||
|
||||||
Boolean(debtRoleSwapPrimary) ||
|
Boolean(debtRoleSwapPrimary) ||
|
||||||
shortValueFlowRetargetPrimary ||
|
shortValueFlowRetargetPrimary ||
|
||||||
inventoryShortFollowupPrimary ||
|
inventoryShortFollowupPrimary ||
|
||||||
|
|
@ -690,6 +693,7 @@ function createAssistantTransitionPolicy(deps) {
|
||||||
hasInventoryRootTemporalFollowupPrimary;
|
hasInventoryRootTemporalFollowupPrimary;
|
||||||
hasAlternateFollowupSignal = deps.toNonEmptyString(alternateMessage)
|
hasAlternateFollowupSignal = deps.toNonEmptyString(alternateMessage)
|
||||||
? deps.hasAddressFollowupContextSignal(alternateMessage) ||
|
? deps.hasAddressFollowupContextSignal(alternateMessage) ||
|
||||||
|
hasSuggestedIntentPivotSignal ||
|
||||||
Boolean(debtRoleSwapAlternate) ||
|
Boolean(debtRoleSwapAlternate) ||
|
||||||
shortValueFlowRetargetAlternate ||
|
shortValueFlowRetargetAlternate ||
|
||||||
inventoryShortFollowupAlternate ||
|
inventoryShortFollowupAlternate ||
|
||||||
|
|
@ -700,6 +704,7 @@ function createAssistantTransitionPolicy(deps) {
|
||||||
hasPrimaryIndexReferenceSignal ||
|
hasPrimaryIndexReferenceSignal ||
|
||||||
hasAlternateIndexReferenceSignal ||
|
hasAlternateIndexReferenceSignal ||
|
||||||
hasOrganizationClarificationContinuation ||
|
hasOrganizationClarificationContinuation ||
|
||||||
|
hasSuggestedIntentPivotSignal ||
|
||||||
hasImplicitContinuationSignal ||
|
hasImplicitContinuationSignal ||
|
||||||
inventoryShortFollowupPrimary ||
|
inventoryShortFollowupPrimary ||
|
||||||
inventoryShortFollowupAlternate ||
|
inventoryShortFollowupAlternate ||
|
||||||
|
|
|
||||||
|
|
@ -751,6 +751,7 @@ export function createAssistantTransitionPolicy(deps) {
|
||||||
!shortValueFlowRetargetPrimary &&
|
!shortValueFlowRetargetPrimary &&
|
||||||
!shortValueFlowRetargetAlternate &&
|
!shortValueFlowRetargetAlternate &&
|
||||||
!hasImplicitContinuationSignal &&
|
!hasImplicitContinuationSignal &&
|
||||||
|
!hasSuggestedIntentPivotSignal &&
|
||||||
!hasOrganizationClarificationContinuation &&
|
!hasOrganizationClarificationContinuation &&
|
||||||
!hasIndexReferenceSignal
|
!hasIndexReferenceSignal
|
||||||
) {
|
) {
|
||||||
|
|
@ -766,6 +767,7 @@ export function createAssistantTransitionPolicy(deps) {
|
||||||
!shortValueFlowRetargetPrimary &&
|
!shortValueFlowRetargetPrimary &&
|
||||||
!shortValueFlowRetargetAlternate &&
|
!shortValueFlowRetargetAlternate &&
|
||||||
!hasImplicitContinuationSignal &&
|
!hasImplicitContinuationSignal &&
|
||||||
|
!hasSuggestedIntentPivotSignal &&
|
||||||
!hasOrganizationClarificationContinuation &&
|
!hasOrganizationClarificationContinuation &&
|
||||||
!hasIndexReferenceSignal
|
!hasIndexReferenceSignal
|
||||||
) {
|
) {
|
||||||
|
|
@ -944,6 +946,7 @@ export function createAssistantTransitionPolicy(deps) {
|
||||||
}
|
}
|
||||||
hasPrimaryFollowupSignal =
|
hasPrimaryFollowupSignal =
|
||||||
deps.hasAddressFollowupContextSignal(userMessage) ||
|
deps.hasAddressFollowupContextSignal(userMessage) ||
|
||||||
|
hasSuggestedIntentPivotSignal ||
|
||||||
Boolean(debtRoleSwapPrimary) ||
|
Boolean(debtRoleSwapPrimary) ||
|
||||||
shortValueFlowRetargetPrimary ||
|
shortValueFlowRetargetPrimary ||
|
||||||
inventoryShortFollowupPrimary ||
|
inventoryShortFollowupPrimary ||
|
||||||
|
|
@ -951,6 +954,7 @@ export function createAssistantTransitionPolicy(deps) {
|
||||||
hasInventoryRootTemporalFollowupPrimary;
|
hasInventoryRootTemporalFollowupPrimary;
|
||||||
hasAlternateFollowupSignal = deps.toNonEmptyString(alternateMessage)
|
hasAlternateFollowupSignal = deps.toNonEmptyString(alternateMessage)
|
||||||
? deps.hasAddressFollowupContextSignal(alternateMessage) ||
|
? deps.hasAddressFollowupContextSignal(alternateMessage) ||
|
||||||
|
hasSuggestedIntentPivotSignal ||
|
||||||
Boolean(debtRoleSwapAlternate) ||
|
Boolean(debtRoleSwapAlternate) ||
|
||||||
shortValueFlowRetargetAlternate ||
|
shortValueFlowRetargetAlternate ||
|
||||||
inventoryShortFollowupAlternate ||
|
inventoryShortFollowupAlternate ||
|
||||||
|
|
@ -961,6 +965,7 @@ export function createAssistantTransitionPolicy(deps) {
|
||||||
hasPrimaryIndexReferenceSignal ||
|
hasPrimaryIndexReferenceSignal ||
|
||||||
hasAlternateIndexReferenceSignal ||
|
hasAlternateIndexReferenceSignal ||
|
||||||
hasOrganizationClarificationContinuation ||
|
hasOrganizationClarificationContinuation ||
|
||||||
|
hasSuggestedIntentPivotSignal ||
|
||||||
hasImplicitContinuationSignal ||
|
hasImplicitContinuationSignal ||
|
||||||
inventoryShortFollowupPrimary ||
|
inventoryShortFollowupPrimary ||
|
||||||
inventoryShortFollowupAlternate ||
|
inventoryShortFollowupAlternate ||
|
||||||
|
|
|
||||||
|
|
@ -737,6 +737,52 @@ describe("assistantTransitionPolicy", () => {
|
||||||
expect(contract.decision_reasons).toContain("suggested_intent_followup_pivot");
|
expect(contract.decision_reasons).toContain("suggested_intent_followup_pivot");
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it("switches from payments to suggested contracts on a pronoun follow-up even when generic follow-up signal is weak", () => {
|
||||||
|
const policy = buildPolicy({
|
||||||
|
findLastAddressAssistantItem: () => ({
|
||||||
|
text: "Собран список банковских операций по контрагенту ТСЖ \\Жуковка 51\\.",
|
||||||
|
debug: {
|
||||||
|
detected_intent: "bank_operations_by_counterparty",
|
||||||
|
extracted_filters: {
|
||||||
|
counterparty: "ТСЖ \\Жуковка 51\\"
|
||||||
|
},
|
||||||
|
anchor_type: "counterparty",
|
||||||
|
anchor_value_resolved: "ТСЖ \\Жуковка 51\\"
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
buildAddressFollowupOffer: () => ({
|
||||||
|
enabled: true,
|
||||||
|
source_intent: "bank_operations_by_counterparty",
|
||||||
|
suggested_intents: ["list_documents_by_counterparty", "list_contracts_by_counterparty"]
|
||||||
|
}),
|
||||||
|
hasAddressFollowupContextSignal: () => false,
|
||||||
|
hasReferentialPointer: () => true
|
||||||
|
});
|
||||||
|
|
||||||
|
const carryover = policy.resolveAddressFollowupCarryoverContext("А по нему договоры?", [], null, null, null);
|
||||||
|
|
||||||
|
expect(carryover?.followupContext?.previous_intent).toBe("list_contracts_by_counterparty");
|
||||||
|
expect(carryover?.followupContext?.target_intent).toBe("list_contracts_by_counterparty");
|
||||||
|
expect(carryover?.followupContext?.previous_anchor_type).toBe("counterparty");
|
||||||
|
expect(carryover?.hasSuggestedIntentPivotSignal).toBe(true);
|
||||||
|
expect(carryover?.followupSelectionMode).toBe("switch_to_suggested_intent");
|
||||||
|
|
||||||
|
const contract = policy.buildAddressDialogContinuationContractV2(
|
||||||
|
"А по нему договоры?",
|
||||||
|
"Покажи договоры, связанные с указанным объектом",
|
||||||
|
carryover,
|
||||||
|
{
|
||||||
|
predecomposeContract: {
|
||||||
|
intent: "unknown"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(contract.decision).toBe("switch_to_suggested");
|
||||||
|
expect(contract.target_intent).toBe("list_contracts_by_counterparty");
|
||||||
|
expect(contract.decision_reasons).toContain("suggested_intent_followup_pivot");
|
||||||
|
});
|
||||||
|
|
||||||
it("keeps root-scoped carryover for foreign accounting pivot over inventory drilldown", () => {
|
it("keeps root-scoped carryover for foreign accounting pivot over inventory drilldown", () => {
|
||||||
const policy = buildPolicy({
|
const policy = buildPolicy({
|
||||||
findLastAddressAssistantItem: () => ({
|
findLastAddressAssistantItem: () => ({
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue