ARCH: приоритизировать discovery-period в planner follow-up
This commit is contained in:
parent
bd58ab490f
commit
dca49ef4e1
|
|
@ -0,0 +1,115 @@
|
||||||
|
{
|
||||||
|
"schema_version": "domain_truth_harness_spec_v1",
|
||||||
|
"scenario_id": "address_truth_harness_phase32_planner_selected_chain_end_to_end",
|
||||||
|
"domain": "address_phase32_planner_selected_chain_end_to_end",
|
||||||
|
"title": "Phase 32 planner-selected chain end-to-end replay",
|
||||||
|
"description": "Targeted AGENT replay for closing Big Block C: a grounded 1C counterparty must survive planner-selected pivots across incoming value-flow, outgoing payouts, net flow, document evidence, and movement evidence without forcing the user to restate the resolved name.",
|
||||||
|
"bindings": {},
|
||||||
|
"steps": [
|
||||||
|
{
|
||||||
|
"step_id": "step_01_resolve_counterparty_alias",
|
||||||
|
"title": "Entity resolution grounds the checked 1C counterparty from a loose alias",
|
||||||
|
"question": "найди в 1С контрагента СВК",
|
||||||
|
"allowed_reply_types": ["factual", "factual_with_explanation", "partial_coverage"],
|
||||||
|
"required_answer_patterns_all": ["(?i)свк", "(?i)контрагент"],
|
||||||
|
"required_answer_patterns_any": [
|
||||||
|
"(?i)группа\\s+свк",
|
||||||
|
"(?i)каталог",
|
||||||
|
"(?i)найден",
|
||||||
|
"(?i)наиболее вероятн"
|
||||||
|
],
|
||||||
|
"forbidden_answer_patterns": [
|
||||||
|
"(?i)получили",
|
||||||
|
"(?i)заплатили",
|
||||||
|
"(?i)нетто",
|
||||||
|
"(?i)оборот",
|
||||||
|
"(?i)выручк",
|
||||||
|
"(?i)сумм(а|ы)"
|
||||||
|
],
|
||||||
|
"criticality": "critical",
|
||||||
|
"semantic_tags": ["entity_resolution", "alias_grounding", "followup_anchor"]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"step_id": "step_02_incoming_by_resolved_entity",
|
||||||
|
"title": "Incoming value-flow follow-up reuses the resolved counterparty anchor",
|
||||||
|
"question": "сколько получили по нему за 2020 год",
|
||||||
|
"allowed_reply_types": ["factual_with_explanation", "partial_coverage"],
|
||||||
|
"required_answer_patterns_all": ["(?i)2020", "(?i)получил|входящ|поступ", "(?i)руб"],
|
||||||
|
"required_answer_patterns_any": ["(?i)группа\\s+свк", "(?i)свк"],
|
||||||
|
"forbidden_answer_patterns": [
|
||||||
|
"(?i)не найден контрагент",
|
||||||
|
"(?i)уточните, какого контрагента",
|
||||||
|
"(?i)по какому контрагенту"
|
||||||
|
],
|
||||||
|
"criticality": "critical",
|
||||||
|
"semantic_tags": ["entity_resolution", "incoming_value_flow", "followup_reuse"]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"step_id": "step_03_payout_switch_by_resolved_entity",
|
||||||
|
"title": "Outgoing payment follow-up keeps the same grounded counterparty and checked year",
|
||||||
|
"question": "а теперь сколько заплатили?",
|
||||||
|
"allowed_reply_types": ["factual_with_explanation", "partial_coverage"],
|
||||||
|
"required_answer_patterns_all": ["(?i)2020", "(?i)заплатил|исходящ|списан|платеж", "(?i)руб"],
|
||||||
|
"required_answer_patterns_any": ["(?i)группа\\s+свк", "(?i)свк"],
|
||||||
|
"forbidden_answer_patterns": [
|
||||||
|
"(?i)не найден контрагент",
|
||||||
|
"(?i)уточните, какого контрагента",
|
||||||
|
"(?i)по какому контрагенту",
|
||||||
|
"(?i)за какой год"
|
||||||
|
],
|
||||||
|
"criticality": "critical",
|
||||||
|
"semantic_tags": ["entity_resolution", "payout_switch", "followup_reuse", "date_carryover"]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"step_id": "step_04_net_after_payout",
|
||||||
|
"title": "Net-flow follow-up reuses the same grounded counterparty and checked year after payout",
|
||||||
|
"question": "а какое нетто?",
|
||||||
|
"allowed_reply_types": ["factual_with_explanation", "partial_coverage"],
|
||||||
|
"required_answer_patterns_all": ["(?i)2020", "(?i)нетто|сальдо", "(?i)руб"],
|
||||||
|
"required_answer_patterns_any": ["(?i)получ", "(?i)заплат", "(?i)группа\\s+свк", "(?i)свк"],
|
||||||
|
"forbidden_answer_patterns": [
|
||||||
|
"(?i)не найден контрагент",
|
||||||
|
"(?i)уточните, какого контрагента",
|
||||||
|
"(?i)по какому контрагенту"
|
||||||
|
],
|
||||||
|
"criticality": "critical",
|
||||||
|
"semantic_tags": ["entity_resolution", "net_value_flow", "followup_reuse"]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"step_id": "step_05_documents_after_net",
|
||||||
|
"title": "Document evidence follow-up keeps the grounded counterparty after the net answer",
|
||||||
|
"question": "а по документам?",
|
||||||
|
"allowed_reply_types": ["factual", "factual_with_explanation", "partial_coverage"],
|
||||||
|
"required_answer_patterns_all": ["(?i)документ|счет|накладн|акт"],
|
||||||
|
"required_answer_patterns_any": ["(?i)группа\\s+свк", "(?i)свк", "(?i)2020"],
|
||||||
|
"forbidden_answer_patterns": [
|
||||||
|
"(?i)не найден контрагент",
|
||||||
|
"(?i)уточните, какого контрагента",
|
||||||
|
"(?i)по какому контрагенту",
|
||||||
|
"(?i)сколько получили",
|
||||||
|
"(?i)сколько заплатили",
|
||||||
|
"(?i)нетто"
|
||||||
|
],
|
||||||
|
"criticality": "critical",
|
||||||
|
"semantic_tags": ["entity_resolution", "document_evidence", "value_flow_pivot", "followup_reuse"]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"step_id": "step_06_movements_after_documents",
|
||||||
|
"title": "Movement evidence follow-up keeps the grounded counterparty after the document answer",
|
||||||
|
"question": "а по движениям?",
|
||||||
|
"allowed_reply_types": ["factual", "factual_with_explanation", "partial_coverage"],
|
||||||
|
"required_answer_patterns_all": ["(?i)движени|операц|платеж|списан|поступ"],
|
||||||
|
"required_answer_patterns_any": ["(?i)группа\\s+свк", "(?i)свк", "(?i)2020"],
|
||||||
|
"forbidden_answer_patterns": [
|
||||||
|
"(?i)не найден контрагент",
|
||||||
|
"(?i)уточните, какого контрагента",
|
||||||
|
"(?i)по какому контрагенту",
|
||||||
|
"(?i)сколько получили",
|
||||||
|
"(?i)сколько заплатили",
|
||||||
|
"(?i)нетто"
|
||||||
|
],
|
||||||
|
"criticality": "critical",
|
||||||
|
"semantic_tags": ["entity_resolution", "movement_evidence", "document_pivot", "followup_reuse"]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
@ -425,6 +425,8 @@ function resolveAddressDebugCarryoverFilters(debug, toNonEmptyString = fallbackT
|
||||||
const extractedFilters = readAddressDebugFilters(debug);
|
const extractedFilters = readAddressDebugFilters(debug);
|
||||||
const nextFilters = extractedFilters ? { ...extractedFilters } : {};
|
const nextFilters = extractedFilters ? { ...extractedFilters } : {};
|
||||||
const discoveryDateScope = readDiscoveryDateScopeFilters(debug, toNonEmptyString);
|
const discoveryDateScope = readDiscoveryDateScopeFilters(debug, toNonEmptyString);
|
||||||
|
const preferGroundedDiscoveryDateScope = hasGroundedDiscoveryBusinessAnswer(debug, toNonEmptyString) &&
|
||||||
|
Boolean(discoveryDateScope.asOfDate || discoveryDateScope.periodFrom || discoveryDateScope.periodTo);
|
||||||
const counterparty = readAddressDebugCounterparty(debug, toNonEmptyString);
|
const counterparty = readAddressDebugCounterparty(debug, toNonEmptyString);
|
||||||
const organization = readAddressDebugOrganization(debug, toNonEmptyString);
|
const organization = readAddressDebugOrganization(debug, toNonEmptyString);
|
||||||
if (counterparty && !toNonEmptyString(nextFilters.counterparty)) {
|
if (counterparty && !toNonEmptyString(nextFilters.counterparty)) {
|
||||||
|
|
@ -433,14 +435,19 @@ function resolveAddressDebugCarryoverFilters(debug, toNonEmptyString = fallbackT
|
||||||
if (organization && !toNonEmptyString(nextFilters.organization)) {
|
if (organization && !toNonEmptyString(nextFilters.organization)) {
|
||||||
nextFilters.organization = organization;
|
nextFilters.organization = organization;
|
||||||
}
|
}
|
||||||
if (discoveryDateScope.asOfDate && !toNonEmptyString(nextFilters.as_of_date)) {
|
if (discoveryDateScope.asOfDate && (preferGroundedDiscoveryDateScope || !toNonEmptyString(nextFilters.as_of_date))) {
|
||||||
nextFilters.as_of_date = discoveryDateScope.asOfDate;
|
nextFilters.as_of_date = discoveryDateScope.asOfDate;
|
||||||
|
delete nextFilters.period_from;
|
||||||
|
delete nextFilters.period_to;
|
||||||
}
|
}
|
||||||
if (discoveryDateScope.periodFrom && !toNonEmptyString(nextFilters.period_from)) {
|
if (discoveryDateScope.periodFrom &&
|
||||||
|
(preferGroundedDiscoveryDateScope || !toNonEmptyString(nextFilters.period_from))) {
|
||||||
nextFilters.period_from = discoveryDateScope.periodFrom;
|
nextFilters.period_from = discoveryDateScope.periodFrom;
|
||||||
}
|
}
|
||||||
if (discoveryDateScope.periodTo && !toNonEmptyString(nextFilters.period_to)) {
|
if (discoveryDateScope.periodTo &&
|
||||||
|
(preferGroundedDiscoveryDateScope || !toNonEmptyString(nextFilters.period_to))) {
|
||||||
nextFilters.period_to = discoveryDateScope.periodTo;
|
nextFilters.period_to = discoveryDateScope.periodTo;
|
||||||
|
delete nextFilters.as_of_date;
|
||||||
}
|
}
|
||||||
const inventoryRootFrame = buildInventoryRootFrameFromAddressDebug(debug, toNonEmptyString);
|
const inventoryRootFrame = buildInventoryRootFrameFromAddressDebug(debug, toNonEmptyString);
|
||||||
const rootFilters = inventoryRootFrame?.filters && typeof inventoryRootFrame.filters === "object"
|
const rootFilters = inventoryRootFrame?.filters && typeof inventoryRootFrame.filters === "object"
|
||||||
|
|
|
||||||
|
|
@ -622,6 +622,9 @@ export function resolveAddressDebugCarryoverFilters(
|
||||||
const extractedFilters = readAddressDebugFilters(debug);
|
const extractedFilters = readAddressDebugFilters(debug);
|
||||||
const nextFilters = extractedFilters ? { ...extractedFilters } : {};
|
const nextFilters = extractedFilters ? { ...extractedFilters } : {};
|
||||||
const discoveryDateScope = readDiscoveryDateScopeFilters(debug, toNonEmptyString);
|
const discoveryDateScope = readDiscoveryDateScopeFilters(debug, toNonEmptyString);
|
||||||
|
const preferGroundedDiscoveryDateScope =
|
||||||
|
hasGroundedDiscoveryBusinessAnswer(debug, toNonEmptyString) &&
|
||||||
|
Boolean(discoveryDateScope.asOfDate || discoveryDateScope.periodFrom || discoveryDateScope.periodTo);
|
||||||
const counterparty = readAddressDebugCounterparty(debug, toNonEmptyString);
|
const counterparty = readAddressDebugCounterparty(debug, toNonEmptyString);
|
||||||
const organization = readAddressDebugOrganization(debug, toNonEmptyString);
|
const organization = readAddressDebugOrganization(debug, toNonEmptyString);
|
||||||
if (counterparty && !toNonEmptyString(nextFilters.counterparty)) {
|
if (counterparty && !toNonEmptyString(nextFilters.counterparty)) {
|
||||||
|
|
@ -630,14 +633,23 @@ export function resolveAddressDebugCarryoverFilters(
|
||||||
if (organization && !toNonEmptyString(nextFilters.organization)) {
|
if (organization && !toNonEmptyString(nextFilters.organization)) {
|
||||||
nextFilters.organization = organization;
|
nextFilters.organization = organization;
|
||||||
}
|
}
|
||||||
if (discoveryDateScope.asOfDate && !toNonEmptyString(nextFilters.as_of_date)) {
|
if (discoveryDateScope.asOfDate && (preferGroundedDiscoveryDateScope || !toNonEmptyString(nextFilters.as_of_date))) {
|
||||||
nextFilters.as_of_date = discoveryDateScope.asOfDate;
|
nextFilters.as_of_date = discoveryDateScope.asOfDate;
|
||||||
|
delete nextFilters.period_from;
|
||||||
|
delete nextFilters.period_to;
|
||||||
}
|
}
|
||||||
if (discoveryDateScope.periodFrom && !toNonEmptyString(nextFilters.period_from)) {
|
if (
|
||||||
|
discoveryDateScope.periodFrom &&
|
||||||
|
(preferGroundedDiscoveryDateScope || !toNonEmptyString(nextFilters.period_from))
|
||||||
|
) {
|
||||||
nextFilters.period_from = discoveryDateScope.periodFrom;
|
nextFilters.period_from = discoveryDateScope.periodFrom;
|
||||||
}
|
}
|
||||||
if (discoveryDateScope.periodTo && !toNonEmptyString(nextFilters.period_to)) {
|
if (
|
||||||
|
discoveryDateScope.periodTo &&
|
||||||
|
(preferGroundedDiscoveryDateScope || !toNonEmptyString(nextFilters.period_to))
|
||||||
|
) {
|
||||||
nextFilters.period_to = discoveryDateScope.periodTo;
|
nextFilters.period_to = discoveryDateScope.periodTo;
|
||||||
|
delete nextFilters.as_of_date;
|
||||||
}
|
}
|
||||||
const inventoryRootFrame = buildInventoryRootFrameFromAddressDebug(debug, toNonEmptyString);
|
const inventoryRootFrame = buildInventoryRootFrameFromAddressDebug(debug, toNonEmptyString);
|
||||||
const rootFilters =
|
const rootFilters =
|
||||||
|
|
|
||||||
|
|
@ -192,6 +192,44 @@ describe("assistantContinuityPolicy organization authority", () => {
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it("prefers grounded discovery date scope over stale exact-route date filters in carryover", () => {
|
||||||
|
const debug = {
|
||||||
|
execution_lane: "address_query",
|
||||||
|
extracted_filters: {
|
||||||
|
counterparty: "Группа СВК",
|
||||||
|
period_to: "2026-04-22"
|
||||||
|
},
|
||||||
|
mcp_discovery_response_applied: true,
|
||||||
|
assistant_mcp_discovery_entry_point_v1: {
|
||||||
|
schema_version: "assistant_mcp_discovery_runtime_entry_point_v1",
|
||||||
|
entry_status: "bridge_executed",
|
||||||
|
turn_input: {
|
||||||
|
turn_meaning_ref: {
|
||||||
|
asked_action_family: "payout",
|
||||||
|
explicit_entity_candidates: ["Группа СВК"],
|
||||||
|
explicit_date_scope: "2020"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
bridge: {
|
||||||
|
bridge_status: "answer_draft_ready",
|
||||||
|
business_fact_answer_allowed: true,
|
||||||
|
pilot: {
|
||||||
|
pilot_scope: "counterparty_supplier_payout_query_movements_v1"
|
||||||
|
},
|
||||||
|
answer_draft: {
|
||||||
|
answer_mode: "confirmed_with_bounded_inference"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
expect(resolveAddressDebugCarryoverFilters(debug)).toEqual({
|
||||||
|
counterparty: "Группа СВК",
|
||||||
|
period_from: "2020-01-01",
|
||||||
|
period_to: "2020-12-31"
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
it("prefers the resolved entity from grounded entity-resolution discovery for counterparty carryover", () => {
|
it("prefers the resolved entity from grounded entity-resolution discovery for counterparty carryover", () => {
|
||||||
const debug = {
|
const debug = {
|
||||||
execution_lane: "living_chat",
|
execution_lane: "living_chat",
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue