Planner Autonomy: закрепить bounded inference и semantic bridges
This commit is contained in:
parent
3634404b1c
commit
c7c85e9b42
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because it is too large
Load Diff
|
|
@ -64,6 +64,20 @@ The follow-up consolidation step moved the value-flow planner seams onto the sam
|
|||
|
||||
This keeps behavior stable while making the planner's route meaning inspectable through catalog descriptors instead of only through local `recipeFor()` branches.
|
||||
|
||||
The next consolidation step strengthened lifecycle as a bounded inference chain instead of a loose age-like shortcut:
|
||||
|
||||
- the lifecycle template now declares `activity_window` and `legal_fact_boundary` axes;
|
||||
- the template summary explicitly frames the result as a first/latest confirmed 1C activity window, not legal registration age;
|
||||
- planner graph and fallback recipes now emit lifecycle bounded-inference reason codes;
|
||||
- lifecycle evidence facts include the matched row count, first/latest confirmed activity dates, and an explicit legal-fact boundary.
|
||||
|
||||
Two arbitration seams were also hardened because they are part of the same planner-autonomy surface:
|
||||
|
||||
- current-turn value-flow aggregate questions can override supported exact legacy routes when the user asks for amount/net/payment totals and the exact route would only produce a narrower lookup/list answer;
|
||||
- broad business evaluation (`broad_business_evaluation`) is intentionally kept in the deterministic living-chat bridge instead of being displaced by generic metadata discovery.
|
||||
|
||||
These changes keep the route fabric broader without letting the planner pretend that inferred evidence is a formally proven legal fact.
|
||||
|
||||
## Why This Matters
|
||||
|
||||
This reduces the pressure to add one hard route per user wording.
|
||||
|
|
@ -79,7 +93,7 @@ Domain-specific exact recipes can still exist as fast paths, but they should not
|
|||
|
||||
## Validation
|
||||
|
||||
Local validation after the catalog-template, value-flow, and metadata-lane scoring consolidation steps:
|
||||
Local validation after the catalog-template, value-flow, metadata-lane scoring, lifecycle bounded-inference, current-turn value-flow arbitration, and broad-evaluation bridge steps:
|
||||
|
||||
- `npm.cmd test -- assistantMcpCatalogIndex.test.ts assistantMcpDiscoveryPlanner.test.ts`: passed, `47 passed`
|
||||
- MCP-discovery suite: passed, `227 passed`, `9 skipped`
|
||||
|
|
@ -96,16 +110,27 @@ Additional code-level consolidation:
|
|||
- planner reason codes now expose when an explicit lane family is scored against carried metadata ambiguity:
|
||||
`planner_metadata_surface_scored_with_explicit_lane_family`.
|
||||
|
||||
Latest validation after the lifecycle and arbitration hardening:
|
||||
|
||||
- targeted lifecycle/catalog/planner/answer tests: passed, `75 passed`, `1 skipped`
|
||||
- full MCP-discovery suite: passed, `268 passed`, `9 skipped`
|
||||
- broad MCP/living-chat/route/meaning slice: passed, `305 passed`, `9 skipped`
|
||||
- `npm.cmd run build`: passed
|
||||
- graphify rebuild: `5912 nodes`, `12833 edges`, `138 communities`
|
||||
- live lifecycle/value-flow response gate: `address_truth_harness_phase19_mcp_discovery_response_gate_planner_lifecycle_rerun4`, accepted `8/8`
|
||||
- live broad-eval to net-flow follow-up: `address_truth_harness_phase21_net_followup_after_broad_eval_planner_lifecycle_rerun2`, accepted `3/3`
|
||||
- live broad-evaluation bridge: `address_truth_harness_phase22_broad_business_evaluation_bridge_planner_lifecycle_rerun2`, accepted `3/3`
|
||||
|
||||
## Next Step
|
||||
|
||||
The next safe step is to continue from catalog-instantiated known chain templates and first scoring rules into stronger bounded inference templates.
|
||||
The next safe step is to continue from catalog-instantiated known chain templates, first scoring rules, and lifecycle bounded inference into broader reviewed evidence templates.
|
||||
|
||||
Recommended order:
|
||||
|
||||
1. lifecycle evidence template with stronger bounded inference semantics;
|
||||
2. inventory-stock evidence templates only after the generic fabric remains stable under one more replay slice;
|
||||
3. broaden catalog scoring beyond explicit document/movement lane choice into unfamiliar 1C asks;
|
||||
4. keep value-flow and metadata ambiguity canaries as Post-F regression gates.
|
||||
1. promote repeated inventory-stock evidence behaviors into reviewed catalog templates;
|
||||
2. broaden catalog scoring beyond explicit document/movement lane choice into unfamiliar 1C asks;
|
||||
3. grow primitive descriptors only where live replay shows a real evidence gap;
|
||||
4. keep phase19, phase21, phase22, value-flow, metadata ambiguity, and inventory-stock canaries as regression gates.
|
||||
|
||||
The key rule remains:
|
||||
|
||||
|
|
|
|||
|
|
@ -68,11 +68,14 @@ It now documents a turnaround that is already operational in code, already mater
|
|||
- the next bounded breadth slice is now replay-backed under Post-F gates:
|
||||
- inventory stock/provenance/sale-trace pack: `inventory_stock_open_world_breadth_rerun_semantic_integrity_20260501_fix5`, accepted
|
||||
- live map sync: [19 - inventory_stock_open_world_breadth_proof_2026-05-01.md](./19%20-%20inventory_stock_open_world_breadth_proof_2026-05-01.md)
|
||||
- Planner Autonomy Consolidation has started:
|
||||
- Planner Autonomy Consolidation is now active beyond the initial catalog-template cut:
|
||||
- MCP catalog now carries reusable chain templates in addition to primitive contracts;
|
||||
- planner route-fabric selection has started moving from local recipe branches toward catalog-instantiated chains;
|
||||
- value-flow, value-flow comparison, value-flow ranking, lifecycle, metadata, movement, document, and entity fallback branches now expose catalog-template instantiation reason codes in planner output;
|
||||
- explicit document/movement data-need now scores over ambiguous carried metadata surfaces without forcing neutral follow-ups into a lane;
|
||||
- lifecycle now behaves as a bounded activity-window inference chain with an explicit legal-fact boundary instead of an unqualified age answer;
|
||||
- current-turn value-flow aggregate questions can override narrower supported exact routes when the user asks for totals/net/payment amounts;
|
||||
- broad business evaluation remains in the deterministic living-chat bridge instead of being displaced by generic metadata discovery;
|
||||
- live map sync: [20 - planner_autonomy_consolidation_2026-05-01.md](./20%20-%20planner_autonomy_consolidation_2026-05-01.md)
|
||||
|
||||
Current honest status:
|
||||
|
|
@ -81,10 +84,11 @@ Current honest status:
|
|||
- exit-from-danger-zone readiness: `~97%`
|
||||
- pre-multidomain readiness: `~90%`
|
||||
- bounded-autonomy foundation readiness: `~89%`
|
||||
- open-world bounded-autonomy readiness: `~82%`
|
||||
- open-world bounded-autonomy readiness: `~84%`
|
||||
- Post-F semantic integrity module progress: `~99%` operationally closed, with remaining risk now treated as next-slice discovery rather than an open blocker inside the closed slice
|
||||
- active inventory-stock breadth slice progress: `100%` for the declared scenario pack, not for arbitrary inventory questions
|
||||
- graph snapshot after latest rebuild: `5911 nodes`, `12830 edges`, `138 communities`
|
||||
- Planner Autonomy Consolidation progress: `~62%` for the declared module, with catalog-fabric, value-flow arbitration, lifecycle bounded inference, and broad-evaluation bridge validated, but broader unfamiliar 1C asks still pending
|
||||
- graph snapshot after latest rebuild: `5912 nodes`, `12833 edges`, `138 communities`
|
||||
- current breakpoint:
|
||||
- the validated hot paths are no longer structurally broken;
|
||||
- flagship continuity collapse is no longer the primary risk;
|
||||
|
|
@ -121,6 +125,9 @@ Latest live proof now includes:
|
|||
- `address_truth_harness_phase52_metadata_movement_full_recovery_planner_metadata_scoring_rerun2` accepted `4/4`, proving metadata-born movement continuation keeps lane choice, organization, and period recovery intact
|
||||
- `address_truth_harness_phase54_metadata_document_full_recovery_planner_metadata_scoring_rerun2` accepted `4/4`, proving metadata-born document continuation keeps lane choice, organization, and period recovery intact
|
||||
- MCP planner/catalog consolidation slice accepted locally: `assistantMcpCatalogIndex.test.ts` + `assistantMcpDiscoveryPlanner.test.ts` passed `47/47`, full MCP-discovery slice passed `227/227` with `9` skipped
|
||||
- lifecycle/value-flow Planner Autonomy response gate accepted: `address_truth_harness_phase19_mcp_discovery_response_gate_planner_lifecycle_rerun4` accepted `8/8`, proving bounded lifecycle inference, current-turn value-flow aggregate arbitration, and sanitized evidence wording
|
||||
- broad-evaluation bridge continuity accepted: `address_truth_harness_phase21_net_followup_after_broad_eval_planner_lifecycle_rerun2` accepted `3/3` and `address_truth_harness_phase22_broad_business_evaluation_bridge_planner_lifecycle_rerun2` accepted `3/3`
|
||||
- latest local Planner Autonomy slice accepted: full MCP-discovery suite passed `268/268` with `9` skipped; broad MCP/living-chat/route/meaning slice passed `305/305` with `9` skipped; build passed
|
||||
|
||||
Current architectural reading:
|
||||
|
||||
|
|
|
|||
|
|
@ -283,13 +283,13 @@ const CHAIN_TEMPLATES = [
|
|||
},
|
||||
{
|
||||
chain_id: "lifecycle",
|
||||
semantic_data_need: "counterparty lifecycle evidence",
|
||||
chain_summary: "Resolve the business entity, query supporting documents, probe coverage, then explain the evidence basis for the inferred activity window.",
|
||||
semantic_data_need: "counterparty lifecycle evidence with bounded activity-window inference",
|
||||
chain_summary: "Resolve the business entity, query supporting documents, probe coverage, then explain the evidence basis for the inferred activity window without presenting it as legal registration age.",
|
||||
fallback_primitives: ["resolve_entity_reference", "query_documents", "probe_coverage", "explain_evidence_basis"],
|
||||
base_required_axes: ["document_date", "coverage_target", "evidence_basis"],
|
||||
base_required_axes: ["document_date", "coverage_target", "evidence_basis", "activity_window", "legal_fact_boundary"],
|
||||
supported_fact_families: ["activity_lifecycle"],
|
||||
supported_action_families: ["activity_duration"],
|
||||
planning_tags: ["document", "explanation", "coverage"],
|
||||
planning_tags: ["document", "explanation", "coverage", "bounded_inference", "activity_window", "legal_fact_boundary"],
|
||||
safe_for_model_planning: true,
|
||||
requires_evidence_gate: true
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1596,8 +1596,8 @@ function buildLifecycleConfirmedFacts(result, counterparty) {
|
|||
}
|
||||
return [
|
||||
counterparty
|
||||
? `1C activity rows were found for counterparty ${counterparty}`
|
||||
: "1C activity rows were found for the requested counterparty scope"
|
||||
? `1C activity rows were found for counterparty ${counterparty}; matched_rows=${result.matched_rows}`
|
||||
: `1C activity rows were found for the requested counterparty scope; matched_rows=${result.matched_rows}`
|
||||
];
|
||||
}
|
||||
function checkedCounterpartySuffixRu(counterparty) {
|
||||
|
|
@ -1667,7 +1667,15 @@ function buildLifecycleInferredFacts(result) {
|
|||
if (result.error || result.fetched_rows <= 0) {
|
||||
return [];
|
||||
}
|
||||
return ["Business activity duration may be inferred from first and latest confirmed 1C activity rows"];
|
||||
const period = deriveActivityPeriod(result);
|
||||
if (!period) {
|
||||
return ["Business activity duration may be inferred only when confirmed 1C activity row dates are available"];
|
||||
}
|
||||
return [
|
||||
"Business activity duration may be inferred from first and latest confirmed 1C activity rows",
|
||||
`Activity window is bounded by first=${period.first_activity_date}, latest=${period.latest_activity_date}, matched_rows=${period.matched_rows}`,
|
||||
"Activity-window inference is not legal registration age"
|
||||
];
|
||||
}
|
||||
function buildDocumentInferredFacts(result, counterparty, periodScope) {
|
||||
if (result.error || result.fetched_rows <= 0) {
|
||||
|
|
@ -1707,7 +1715,7 @@ function buildValueFlowInferredFacts(derived) {
|
|||
facts.push("Counterparty value-flow total was calculated from confirmed 1C movement rows");
|
||||
}
|
||||
if (derived.coverage_recovered_by_period_chunking && derived.period_chunking_granularity === "month") {
|
||||
facts.push("Requested period coverage was recovered through monthly 1C value-flow probes after the broad probe hit the row limit");
|
||||
facts.push("Requested period coverage was recovered through monthly 1C value-flow probes");
|
||||
}
|
||||
if (derived.aggregation_axis === "month" && derived.monthly_breakdown.length > 0) {
|
||||
facts.push("Counterparty monthly value-flow breakdown was grouped by month over confirmed 1C movement rows");
|
||||
|
|
@ -1720,7 +1728,7 @@ function buildRankedValueFlowInferredFacts(derived) {
|
|||
}
|
||||
const facts = ["Counterparty ranking was calculated from confirmed 1C movement rows grouped by counterparty"];
|
||||
if (derived.coverage_recovered_by_period_chunking && derived.period_chunking_granularity === "month") {
|
||||
facts.push("Requested period coverage for counterparty ranking was recovered through monthly 1C probes after a broad probe hit the row limit");
|
||||
facts.push("Requested period coverage for counterparty ranking was recovered through monthly 1C probes");
|
||||
}
|
||||
return facts;
|
||||
}
|
||||
|
|
@ -1730,7 +1738,7 @@ function buildBidirectionalValueFlowInferredFacts(derived) {
|
|||
}
|
||||
const facts = ["Counterparty net value-flow was calculated as incoming confirmed 1C rows minus outgoing confirmed 1C rows"];
|
||||
if (derived.coverage_recovered_by_period_chunking && derived.period_chunking_granularity === "month") {
|
||||
facts.push("Requested period coverage for bidirectional value-flow was recovered through monthly 1C side probes after a broad probe hit the row limit");
|
||||
facts.push("Requested period coverage for bidirectional value-flow was recovered through monthly 1C side probes");
|
||||
}
|
||||
if (derived.aggregation_axis === "month" && derived.monthly_breakdown.length > 0) {
|
||||
facts.push("Counterparty monthly net value-flow breakdown was grouped by month over confirmed incoming and outgoing 1C rows");
|
||||
|
|
@ -1738,7 +1746,10 @@ function buildBidirectionalValueFlowInferredFacts(derived) {
|
|||
return facts;
|
||||
}
|
||||
function buildLifecycleUnknownFacts() {
|
||||
return ["Legal registration date is not proven by this MCP discovery pilot"];
|
||||
return [
|
||||
"Legal registration date is not proven by this MCP discovery pilot",
|
||||
"Business activity before the first confirmed 1C activity row is not proven by this MCP discovery pilot"
|
||||
];
|
||||
}
|
||||
function buildDocumentUnknownFacts(periodScope, counterparty) {
|
||||
return [
|
||||
|
|
@ -1753,7 +1764,7 @@ function buildMovementUnknownFacts(periodScope, counterparty) {
|
|||
function buildValueFlowUnknownFacts(periodScope, direction, derived) {
|
||||
const unknownFacts = [];
|
||||
if (derived?.coverage_limited_by_probe_limit) {
|
||||
unknownFacts.push("Complete requested-period coverage is not proven because the MCP discovery probe row limit was reached");
|
||||
unknownFacts.push("Complete requested-period coverage is not proven by the available checked rows");
|
||||
}
|
||||
if (direction === "outgoing_supplier_payout") {
|
||||
unknownFacts.push(periodScope
|
||||
|
|
@ -1769,7 +1780,7 @@ function buildValueFlowUnknownFacts(periodScope, direction, derived) {
|
|||
function buildRankedValueFlowUnknownFacts(periodScope, derived) {
|
||||
const unknownFacts = [];
|
||||
if (derived?.coverage_limited_by_probe_limit) {
|
||||
unknownFacts.push("Complete requested-period ranking coverage is not proven because the MCP discovery probe row limit was reached");
|
||||
unknownFacts.push("Complete requested-period ranking coverage is not proven by the available checked rows");
|
||||
}
|
||||
unknownFacts.push(periodScope
|
||||
? "Full ranking outside the checked period is not proven by this MCP discovery pilot"
|
||||
|
|
@ -1779,7 +1790,7 @@ function buildRankedValueFlowUnknownFacts(periodScope, derived) {
|
|||
function buildBidirectionalValueFlowUnknownFacts(periodScope, derived) {
|
||||
const unknownFacts = [];
|
||||
if (derived?.coverage_limited_by_probe_limit) {
|
||||
unknownFacts.push("Complete requested-period coverage for bidirectional value-flow is not proven because at least one MCP discovery probe row limit was reached");
|
||||
unknownFacts.push("Complete requested-period coverage for bidirectional value-flow is not proven by the available checked rows");
|
||||
}
|
||||
unknownFacts.push(periodScope
|
||||
? "Full bidirectional value-flow outside the checked period is not proven by this MCP discovery pilot"
|
||||
|
|
|
|||
|
|
@ -40,6 +40,10 @@ function pushAllUnique(target, values) {
|
|||
pushUnique(target, value);
|
||||
}
|
||||
}
|
||||
const LIFECYCLE_BOUNDED_INFERENCE_REASON_CODES = [
|
||||
"planner_lifecycle_bounded_activity_window_template",
|
||||
"planner_lifecycle_legal_fact_boundary_required"
|
||||
];
|
||||
function recipeFromCatalogChainTemplate(input) {
|
||||
const template = (0, assistantMcpCatalogIndex_1.getAssistantMcpCatalogChainTemplate)(input.chainId);
|
||||
const axes = [...input.axes];
|
||||
|
|
@ -523,7 +527,7 @@ function recipeFor(input) {
|
|||
axes,
|
||||
primitives: primitiveSelection.primitives,
|
||||
reason: "planner_selected_lifecycle_from_data_need_graph",
|
||||
extraReasons: primitiveSelection.reasonCodes
|
||||
extraReasons: [...primitiveSelection.reasonCodes, ...LIFECYCLE_BOUNDED_INFERENCE_REASON_CODES]
|
||||
});
|
||||
}
|
||||
if (graphFactFamily === "schema_surface") {
|
||||
|
|
@ -676,7 +680,8 @@ function recipeFor(input) {
|
|||
return recipeFromCatalogChainTemplate({
|
||||
chainId: "lifecycle",
|
||||
axes,
|
||||
reason: "planner_selected_lifecycle_recipe"
|
||||
reason: "planner_selected_lifecycle_recipe",
|
||||
extraReasons: LIFECYCLE_BOUNDED_INFERENCE_REASON_CODES
|
||||
});
|
||||
}
|
||||
if (includesAny(combined, ["metadata", "schema", "catalog"])) {
|
||||
|
|
|
|||
|
|
@ -245,6 +245,24 @@ function localizeLine(value) {
|
|||
if (/^Requested period coverage for bidirectional value-flow was recovered through monthly 1C side probes after a broad probe hit the row limit$/i.test(value)) {
|
||||
return "Покрытие запрошенного периода по двустороннему денежному потоку восстановлено помесячными проверками 1С после того, как общая выборка уперлась в лимит строк хотя бы по одной стороне.";
|
||||
}
|
||||
if (/^Requested period coverage was recovered through monthly 1C value-flow probes$/i.test(value)) {
|
||||
return "Покрытие запрошенного периода восстановлено помесячными проверками 1С.";
|
||||
}
|
||||
if (/^Requested period coverage for counterparty ranking was recovered through monthly 1C probes$/i.test(value)) {
|
||||
return "Покрытие запрошенного периода для рейтинга контрагентов восстановлено помесячными проверками 1С.";
|
||||
}
|
||||
if (/^Requested period coverage for bidirectional value-flow was recovered through monthly 1C side probes$/i.test(value)) {
|
||||
return "Покрытие запрошенного периода по двустороннему денежному потоку восстановлено помесячными проверками 1С.";
|
||||
}
|
||||
if (/^Complete requested-period coverage is not proven by the available checked rows$/i.test(value)) {
|
||||
return "Полное покрытие запрошенного периода не подтверждено доступными проверенными строками.";
|
||||
}
|
||||
if (/^Complete requested-period ranking coverage is not proven by the available checked rows$/i.test(value)) {
|
||||
return "Полное покрытие рейтинга за запрошенный период не подтверждено доступными проверенными строками.";
|
||||
}
|
||||
if (/^Complete requested-period coverage for bidirectional value-flow is not proven by the available checked rows$/i.test(value)) {
|
||||
return "Полное покрытие запрошенного периода по двустороннему денежному потоку не подтверждено доступными проверенными строками.";
|
||||
}
|
||||
return value;
|
||||
}
|
||||
function section(title, lines) {
|
||||
|
|
|
|||
|
|
@ -514,6 +514,9 @@ function hasLifecycleSignal(text) {
|
|||
function hasValueFlowSignal(text) {
|
||||
return /(?:оборот|выручк|оплат|плат[её]ж|заплат|перечисл|списан|расход|исходящ|входящ|получ(?:ил|ено|ен)|поступил|поступлен|денежн[а-яёa-z0-9_-]*\s+поток|(?<!\p{L})заработ(?:ал|али|ало|аем|ает|ать|ано|ок)(?!\p{L})|supplier|value[-\s]?flow|turnover|revenue|payment|payout|outflow|cash\s+flow|\bearn(?:ed|ing|ings)?\b)/iu.test(text);
|
||||
}
|
||||
function hasValueFlowAggregateQuestionSignal(text) {
|
||||
return /(?:\u0441\u043a\u043e\u043b\u044c\u043a\u043e|\u0441\u0443\u043c\u043c|\u0438\u0442\u043e\u0433|\u0440\u0430\u0441\u0441\u0447\u0438\u0442|\u043d\u0435\u0442\u0442\u043e|\u0441\u0430\u043b\u044c\u0434\u043e|how\s+much|total|sum|net)/iu.test(text);
|
||||
}
|
||||
function hasPayoutSignal(text) {
|
||||
return /(?:\bмы\s+(?:за)?плат|(?:за)?платил|оплатил|перечисл|списан|расход|поставщик|исходящ|supplier|payout|outflow|paid\s+to|payment\s+to)/iu.test(text);
|
||||
}
|
||||
|
|
@ -836,6 +839,7 @@ function buildAssistantMcpDiscoveryTurnInput(input) {
|
|||
hasMetadataSignal(rawText);
|
||||
const rawEntityResolutionSignal = !rawLifecycleSignal && !rawValueFlowSignal && !rawMetadataSignal && hasEntityResolutionSignal(rawText);
|
||||
const rawPayoutSignal = rawValueFlowSignal && !rawBidirectionalValueFlowSignal && hasPayoutSignal(rawText);
|
||||
const rawValueFlowAggregateQuestionSignal = rawValueFlowSignal && hasValueFlowAggregateQuestionSignal(rawText);
|
||||
const monthlyAggregationSignal = hasMonthlyAggregationSignal(rawText);
|
||||
const rawAllTimeScopeSignal = hasAllTimeScopeHint(rawText);
|
||||
const explicitDateScopeLiteralDetected = hasExplicitDateScopeLiteral(rawText);
|
||||
|
|
@ -853,6 +857,7 @@ function buildAssistantMcpDiscoveryTurnInput(input) {
|
|||
const rawAction = toNonEmptyString(assistantTurnMeaning?.asked_action_family);
|
||||
const rawAggregationAxis = toNonEmptyString(assistantTurnMeaning?.asked_aggregation_axis);
|
||||
const unsupported = toNonEmptyString(assistantTurnMeaning?.unsupported_but_understood_family);
|
||||
const broadBusinessEvaluationUnsupported = unsupported === "broad_business_evaluation";
|
||||
const explicitIntentCandidate = toNonEmptyString(assistantTurnMeaning?.explicit_intent_candidate);
|
||||
const currentTurnDocumentLaneSignal = rawAction === "list_documents";
|
||||
const currentTurnMovementLaneSignal = rawAction === "list_movements";
|
||||
|
|
@ -1179,7 +1184,7 @@ function buildAssistantMcpDiscoveryTurnInput(input) {
|
|||
: semanticNeedFor({
|
||||
domain: rawDomain ?? seededDomain,
|
||||
action: rawAction ?? seededAction,
|
||||
unsupported: unsupported ?? seededUnsupported,
|
||||
unsupported: broadBusinessEvaluationUnsupported ? seededUnsupported : unsupported ?? seededUnsupported,
|
||||
lifecycleSignal,
|
||||
valueFlowSignal,
|
||||
metadataSignal: rawMetadataSignal || effectiveMetadataFollowupSeedApplicable,
|
||||
|
|
@ -1444,8 +1449,13 @@ function buildAssistantMcpDiscoveryTurnInput(input) {
|
|||
if (turnMeaning.stale_replay_forbidden) {
|
||||
cleanTurnMeaning.stale_replay_forbidden = true;
|
||||
}
|
||||
const currentTurnValueFlowExactOverrideApplicable = Boolean(valueFlowSignal &&
|
||||
explicitIntentCandidate &&
|
||||
rawValueFlowAggregateQuestionSignal &&
|
||||
semanticDataNeed &&
|
||||
(entityCandidates.length > 0 || explicitOrganizationScope || openScopeValueFlowWithoutResolvedCounterparty));
|
||||
const runDiscovery = shouldRunDiscovery({
|
||||
unsupported: unsupported ?? seededUnsupported,
|
||||
unsupported: broadBusinessEvaluationUnsupported ? seededUnsupported : unsupported ?? seededUnsupported,
|
||||
lifecycleSignal,
|
||||
valueFlowSignal,
|
||||
metadataSignal: rawMetadataSignal || effectiveMetadataFollowupSeedApplicable,
|
||||
|
|
@ -1465,7 +1475,8 @@ function buildAssistantMcpDiscoveryTurnInput(input) {
|
|||
metadataAmbiguityLaneClarificationApplicable ||
|
||||
metadataGroundedMovementLaneApplicable ||
|
||||
metadataGroundedDocumentLaneApplicable ||
|
||||
groundedValueFlowFollowupApplicable
|
||||
groundedValueFlowFollowupApplicable ||
|
||||
currentTurnValueFlowExactOverrideApplicable
|
||||
});
|
||||
const hasTurnMeaning = Object.keys(cleanTurnMeaning).length > 0;
|
||||
const sourceSignal = rawEntitySearchOverridesStaleScope
|
||||
|
|
@ -1576,6 +1587,9 @@ function buildAssistantMcpDiscoveryTurnInput(input) {
|
|||
if (groundedValueFlowFollowupApplicable) {
|
||||
pushReason(reasonCodes, "mcp_discovery_grounded_value_flow_followup");
|
||||
}
|
||||
if (currentTurnValueFlowExactOverrideApplicable) {
|
||||
pushReason(reasonCodes, "mcp_discovery_current_turn_value_flow_overrides_supported_exact");
|
||||
}
|
||||
if (documentEvidenceGroundedMovementFollowupApplicable) {
|
||||
pushReason(reasonCodes, "mcp_discovery_document_evidence_grounded_movement_followup");
|
||||
}
|
||||
|
|
@ -1606,6 +1620,9 @@ function buildAssistantMcpDiscoveryTurnInput(input) {
|
|||
if (unsupported) {
|
||||
pushReason(reasonCodes, "mcp_discovery_unsupported_but_understood_turn");
|
||||
}
|
||||
if (broadBusinessEvaluationUnsupported) {
|
||||
pushReason(reasonCodes, "mcp_discovery_broad_business_evaluation_kept_in_living_chat");
|
||||
}
|
||||
if (!(valueFlowOrganizationStaysScope && normalizedPredecomposeCounterparty === explicitOrganizationScope) &&
|
||||
normalizedPredecomposeCounterparty) {
|
||||
pushReason(reasonCodes, "mcp_discovery_counterparty_from_predecompose");
|
||||
|
|
|
|||
|
|
@ -348,14 +348,14 @@ const CHAIN_TEMPLATES: AssistantMcpCatalogChainTemplateContract[] = [
|
|||
},
|
||||
{
|
||||
chain_id: "lifecycle",
|
||||
semantic_data_need: "counterparty lifecycle evidence",
|
||||
semantic_data_need: "counterparty lifecycle evidence with bounded activity-window inference",
|
||||
chain_summary:
|
||||
"Resolve the business entity, query supporting documents, probe coverage, then explain the evidence basis for the inferred activity window.",
|
||||
"Resolve the business entity, query supporting documents, probe coverage, then explain the evidence basis for the inferred activity window without presenting it as legal registration age.",
|
||||
fallback_primitives: ["resolve_entity_reference", "query_documents", "probe_coverage", "explain_evidence_basis"],
|
||||
base_required_axes: ["document_date", "coverage_target", "evidence_basis"],
|
||||
base_required_axes: ["document_date", "coverage_target", "evidence_basis", "activity_window", "legal_fact_boundary"],
|
||||
supported_fact_families: ["activity_lifecycle"],
|
||||
supported_action_families: ["activity_duration"],
|
||||
planning_tags: ["document", "explanation", "coverage"],
|
||||
planning_tags: ["document", "explanation", "coverage", "bounded_inference", "activity_window", "legal_fact_boundary"],
|
||||
safe_for_model_planning: true,
|
||||
requires_evidence_gate: true
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2127,8 +2127,8 @@ function buildLifecycleConfirmedFacts(result: AddressMcpQueryExecutorResult, cou
|
|||
}
|
||||
return [
|
||||
counterparty
|
||||
? `1C activity rows were found for counterparty ${counterparty}`
|
||||
: "1C activity rows were found for the requested counterparty scope"
|
||||
? `1C activity rows were found for counterparty ${counterparty}; matched_rows=${result.matched_rows}`
|
||||
: `1C activity rows were found for the requested counterparty scope; matched_rows=${result.matched_rows}`
|
||||
];
|
||||
}
|
||||
|
||||
|
|
@ -2222,7 +2222,15 @@ function buildLifecycleInferredFacts(result: AddressMcpQueryExecutorResult): str
|
|||
if (result.error || result.fetched_rows <= 0) {
|
||||
return [];
|
||||
}
|
||||
return ["Business activity duration may be inferred from first and latest confirmed 1C activity rows"];
|
||||
const period = deriveActivityPeriod(result);
|
||||
if (!period) {
|
||||
return ["Business activity duration may be inferred only when confirmed 1C activity row dates are available"];
|
||||
}
|
||||
return [
|
||||
"Business activity duration may be inferred from first and latest confirmed 1C activity rows",
|
||||
`Activity window is bounded by first=${period.first_activity_date}, latest=${period.latest_activity_date}, matched_rows=${period.matched_rows}`,
|
||||
"Activity-window inference is not legal registration age"
|
||||
];
|
||||
}
|
||||
|
||||
function buildDocumentInferredFacts(
|
||||
|
|
@ -2273,7 +2281,7 @@ function buildValueFlowInferredFacts(derived: AssistantMcpDiscoveryDerivedValueF
|
|||
}
|
||||
if (derived.coverage_recovered_by_period_chunking && derived.period_chunking_granularity === "month") {
|
||||
facts.push(
|
||||
"Requested period coverage was recovered through monthly 1C value-flow probes after the broad probe hit the row limit"
|
||||
"Requested period coverage was recovered through monthly 1C value-flow probes"
|
||||
);
|
||||
}
|
||||
if (derived.aggregation_axis === "month" && derived.monthly_breakdown.length > 0) {
|
||||
|
|
@ -2289,7 +2297,7 @@ function buildRankedValueFlowInferredFacts(derived: AssistantMcpDiscoveryDerived
|
|||
const facts = ["Counterparty ranking was calculated from confirmed 1C movement rows grouped by counterparty"];
|
||||
if (derived.coverage_recovered_by_period_chunking && derived.period_chunking_granularity === "month") {
|
||||
facts.push(
|
||||
"Requested period coverage for counterparty ranking was recovered through monthly 1C probes after a broad probe hit the row limit"
|
||||
"Requested period coverage for counterparty ranking was recovered through monthly 1C probes"
|
||||
);
|
||||
}
|
||||
return facts;
|
||||
|
|
@ -2304,7 +2312,7 @@ function buildBidirectionalValueFlowInferredFacts(
|
|||
const facts = ["Counterparty net value-flow was calculated as incoming confirmed 1C rows minus outgoing confirmed 1C rows"];
|
||||
if (derived.coverage_recovered_by_period_chunking && derived.period_chunking_granularity === "month") {
|
||||
facts.push(
|
||||
"Requested period coverage for bidirectional value-flow was recovered through monthly 1C side probes after a broad probe hit the row limit"
|
||||
"Requested period coverage for bidirectional value-flow was recovered through monthly 1C side probes"
|
||||
);
|
||||
}
|
||||
if (derived.aggregation_axis === "month" && derived.monthly_breakdown.length > 0) {
|
||||
|
|
@ -2314,7 +2322,10 @@ function buildBidirectionalValueFlowInferredFacts(
|
|||
}
|
||||
|
||||
function buildLifecycleUnknownFacts(): string[] {
|
||||
return ["Legal registration date is not proven by this MCP discovery pilot"];
|
||||
return [
|
||||
"Legal registration date is not proven by this MCP discovery pilot",
|
||||
"Business activity before the first confirmed 1C activity row is not proven by this MCP discovery pilot"
|
||||
];
|
||||
}
|
||||
|
||||
function buildDocumentUnknownFacts(periodScope: string | null, counterparty: string | null): string[] {
|
||||
|
|
@ -2336,7 +2347,7 @@ function buildValueFlowUnknownFacts(
|
|||
): string[] {
|
||||
const unknownFacts: string[] = [];
|
||||
if (derived?.coverage_limited_by_probe_limit) {
|
||||
unknownFacts.push("Complete requested-period coverage is not proven because the MCP discovery probe row limit was reached");
|
||||
unknownFacts.push("Complete requested-period coverage is not proven by the available checked rows");
|
||||
}
|
||||
if (direction === "outgoing_supplier_payout") {
|
||||
unknownFacts.push(
|
||||
|
|
@ -2360,7 +2371,7 @@ function buildRankedValueFlowUnknownFacts(
|
|||
): string[] {
|
||||
const unknownFacts: string[] = [];
|
||||
if (derived?.coverage_limited_by_probe_limit) {
|
||||
unknownFacts.push("Complete requested-period ranking coverage is not proven because the MCP discovery probe row limit was reached");
|
||||
unknownFacts.push("Complete requested-period ranking coverage is not proven by the available checked rows");
|
||||
}
|
||||
unknownFacts.push(
|
||||
periodScope
|
||||
|
|
@ -2377,7 +2388,7 @@ function buildBidirectionalValueFlowUnknownFacts(
|
|||
const unknownFacts: string[] = [];
|
||||
if (derived?.coverage_limited_by_probe_limit) {
|
||||
unknownFacts.push(
|
||||
"Complete requested-period coverage for bidirectional value-flow is not proven because at least one MCP discovery probe row limit was reached"
|
||||
"Complete requested-period coverage for bidirectional value-flow is not proven by the available checked rows"
|
||||
);
|
||||
}
|
||||
unknownFacts.push(
|
||||
|
|
|
|||
|
|
@ -127,6 +127,11 @@ function pushAllUnique(target: string[], values: string[]): void {
|
|||
}
|
||||
}
|
||||
|
||||
const LIFECYCLE_BOUNDED_INFERENCE_REASON_CODES = [
|
||||
"planner_lifecycle_bounded_activity_window_template",
|
||||
"planner_lifecycle_legal_fact_boundary_required"
|
||||
];
|
||||
|
||||
function recipeFromCatalogChainTemplate(input: {
|
||||
chainId: AssistantMcpCatalogChainTemplateId;
|
||||
axes: string[];
|
||||
|
|
@ -711,7 +716,7 @@ function recipeFor(input: AssistantMcpDiscoveryPlannerInput): PlannerRecipe {
|
|||
axes,
|
||||
primitives: primitiveSelection.primitives,
|
||||
reason: "planner_selected_lifecycle_from_data_need_graph",
|
||||
extraReasons: primitiveSelection.reasonCodes
|
||||
extraReasons: [...primitiveSelection.reasonCodes, ...LIFECYCLE_BOUNDED_INFERENCE_REASON_CODES]
|
||||
});
|
||||
}
|
||||
|
||||
|
|
@ -874,7 +879,8 @@ function recipeFor(input: AssistantMcpDiscoveryPlannerInput): PlannerRecipe {
|
|||
return recipeFromCatalogChainTemplate({
|
||||
chainId: "lifecycle",
|
||||
axes,
|
||||
reason: "planner_selected_lifecycle_recipe"
|
||||
reason: "planner_selected_lifecycle_recipe",
|
||||
extraReasons: LIFECYCLE_BOUNDED_INFERENCE_REASON_CODES
|
||||
});
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -300,6 +300,24 @@ function localizeLine(value: string): string {
|
|||
) {
|
||||
return "Покрытие запрошенного периода по двустороннему денежному потоку восстановлено помесячными проверками 1С после того, как общая выборка уперлась в лимит строк хотя бы по одной стороне.";
|
||||
}
|
||||
if (/^Requested period coverage was recovered through monthly 1C value-flow probes$/i.test(value)) {
|
||||
return "Покрытие запрошенного периода восстановлено помесячными проверками 1С.";
|
||||
}
|
||||
if (/^Requested period coverage for counterparty ranking was recovered through monthly 1C probes$/i.test(value)) {
|
||||
return "Покрытие запрошенного периода для рейтинга контрагентов восстановлено помесячными проверками 1С.";
|
||||
}
|
||||
if (/^Requested period coverage for bidirectional value-flow was recovered through monthly 1C side probes$/i.test(value)) {
|
||||
return "Покрытие запрошенного периода по двустороннему денежному потоку восстановлено помесячными проверками 1С.";
|
||||
}
|
||||
if (/^Complete requested-period coverage is not proven by the available checked rows$/i.test(value)) {
|
||||
return "Полное покрытие запрошенного периода не подтверждено доступными проверенными строками.";
|
||||
}
|
||||
if (/^Complete requested-period ranking coverage is not proven by the available checked rows$/i.test(value)) {
|
||||
return "Полное покрытие рейтинга за запрошенный период не подтверждено доступными проверенными строками.";
|
||||
}
|
||||
if (/^Complete requested-period coverage for bidirectional value-flow is not proven by the available checked rows$/i.test(value)) {
|
||||
return "Полное покрытие запрошенного периода по двустороннему денежному потоку не подтверждено доступными проверенными строками.";
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -688,6 +688,12 @@ function hasValueFlowSignal(text: string): boolean {
|
|||
);
|
||||
}
|
||||
|
||||
function hasValueFlowAggregateQuestionSignal(text: string): boolean {
|
||||
return /(?:\u0441\u043a\u043e\u043b\u044c\u043a\u043e|\u0441\u0443\u043c\u043c|\u0438\u0442\u043e\u0433|\u0440\u0430\u0441\u0441\u0447\u0438\u0442|\u043d\u0435\u0442\u0442\u043e|\u0441\u0430\u043b\u044c\u0434\u043e|how\s+much|total|sum|net)/iu.test(
|
||||
text
|
||||
);
|
||||
}
|
||||
|
||||
function hasPayoutSignal(text: string): boolean {
|
||||
return /(?:\bмы\s+(?:за)?плат|(?:за)?платил|оплатил|перечисл|списан|расход|поставщик|исходящ|supplier|payout|outflow|paid\s+to|payment\s+to)/iu.test(
|
||||
text
|
||||
|
|
@ -1128,6 +1134,8 @@ export function buildAssistantMcpDiscoveryTurnInput(
|
|||
const rawEntityResolutionSignal =
|
||||
!rawLifecycleSignal && !rawValueFlowSignal && !rawMetadataSignal && hasEntityResolutionSignal(rawText);
|
||||
const rawPayoutSignal = rawValueFlowSignal && !rawBidirectionalValueFlowSignal && hasPayoutSignal(rawText);
|
||||
const rawValueFlowAggregateQuestionSignal =
|
||||
rawValueFlowSignal && hasValueFlowAggregateQuestionSignal(rawText);
|
||||
const monthlyAggregationSignal = hasMonthlyAggregationSignal(rawText);
|
||||
const rawAllTimeScopeSignal = hasAllTimeScopeHint(rawText);
|
||||
const explicitDateScopeLiteralDetected = hasExplicitDateScopeLiteral(rawText);
|
||||
|
|
@ -1150,6 +1158,7 @@ export function buildAssistantMcpDiscoveryTurnInput(
|
|||
const rawAction = toNonEmptyString(assistantTurnMeaning?.asked_action_family);
|
||||
const rawAggregationAxis = toNonEmptyString(assistantTurnMeaning?.asked_aggregation_axis);
|
||||
const unsupported = toNonEmptyString(assistantTurnMeaning?.unsupported_but_understood_family);
|
||||
const broadBusinessEvaluationUnsupported = unsupported === "broad_business_evaluation";
|
||||
const explicitIntentCandidate = toNonEmptyString(assistantTurnMeaning?.explicit_intent_candidate);
|
||||
const currentTurnDocumentLaneSignal = rawAction === "list_documents";
|
||||
const currentTurnMovementLaneSignal = rawAction === "list_movements";
|
||||
|
|
@ -1549,7 +1558,7 @@ export function buildAssistantMcpDiscoveryTurnInput(
|
|||
: semanticNeedFor({
|
||||
domain: rawDomain ?? seededDomain,
|
||||
action: rawAction ?? seededAction,
|
||||
unsupported: unsupported ?? seededUnsupported,
|
||||
unsupported: broadBusinessEvaluationUnsupported ? seededUnsupported : unsupported ?? seededUnsupported,
|
||||
lifecycleSignal,
|
||||
valueFlowSignal,
|
||||
metadataSignal: rawMetadataSignal || effectiveMetadataFollowupSeedApplicable,
|
||||
|
|
@ -1856,8 +1865,16 @@ export function buildAssistantMcpDiscoveryTurnInput(
|
|||
cleanTurnMeaning.stale_replay_forbidden = true;
|
||||
}
|
||||
|
||||
const currentTurnValueFlowExactOverrideApplicable = Boolean(
|
||||
valueFlowSignal &&
|
||||
explicitIntentCandidate &&
|
||||
rawValueFlowAggregateQuestionSignal &&
|
||||
semanticDataNeed &&
|
||||
(entityCandidates.length > 0 || explicitOrganizationScope || openScopeValueFlowWithoutResolvedCounterparty)
|
||||
);
|
||||
|
||||
const runDiscovery = shouldRunDiscovery({
|
||||
unsupported: unsupported ?? seededUnsupported,
|
||||
unsupported: broadBusinessEvaluationUnsupported ? seededUnsupported : unsupported ?? seededUnsupported,
|
||||
lifecycleSignal,
|
||||
valueFlowSignal,
|
||||
metadataSignal: rawMetadataSignal || effectiveMetadataFollowupSeedApplicable,
|
||||
|
|
@ -1879,7 +1896,8 @@ export function buildAssistantMcpDiscoveryTurnInput(
|
|||
metadataAmbiguityLaneClarificationApplicable ||
|
||||
metadataGroundedMovementLaneApplicable ||
|
||||
metadataGroundedDocumentLaneApplicable ||
|
||||
groundedValueFlowFollowupApplicable
|
||||
groundedValueFlowFollowupApplicable ||
|
||||
currentTurnValueFlowExactOverrideApplicable
|
||||
});
|
||||
const hasTurnMeaning = Object.keys(cleanTurnMeaning).length > 0;
|
||||
const sourceSignal: AssistantMcpDiscoveryTurnInputSource = rawEntitySearchOverridesStaleScope
|
||||
|
|
@ -1991,6 +2009,9 @@ export function buildAssistantMcpDiscoveryTurnInput(
|
|||
if (groundedValueFlowFollowupApplicable) {
|
||||
pushReason(reasonCodes, "mcp_discovery_grounded_value_flow_followup");
|
||||
}
|
||||
if (currentTurnValueFlowExactOverrideApplicable) {
|
||||
pushReason(reasonCodes, "mcp_discovery_current_turn_value_flow_overrides_supported_exact");
|
||||
}
|
||||
if (documentEvidenceGroundedMovementFollowupApplicable) {
|
||||
pushReason(reasonCodes, "mcp_discovery_document_evidence_grounded_movement_followup");
|
||||
}
|
||||
|
|
@ -2021,6 +2042,9 @@ export function buildAssistantMcpDiscoveryTurnInput(
|
|||
if (unsupported) {
|
||||
pushReason(reasonCodes, "mcp_discovery_unsupported_but_understood_turn");
|
||||
}
|
||||
if (broadBusinessEvaluationUnsupported) {
|
||||
pushReason(reasonCodes, "mcp_discovery_broad_business_evaluation_kept_in_living_chat");
|
||||
}
|
||||
if (
|
||||
!(valueFlowOrganizationStaysScope && normalizedPredecomposeCounterparty === explicitOrganizationScope) &&
|
||||
normalizedPredecomposeCounterparty
|
||||
|
|
|
|||
|
|
@ -57,6 +57,7 @@ describe("assistant MCP catalog index", () => {
|
|||
const valueFlowTemplate = getAssistantMcpCatalogChainTemplate("value_flow");
|
||||
const valueFlowComparisonTemplate = getAssistantMcpCatalogChainTemplate("value_flow_comparison");
|
||||
const valueFlowRankingTemplate = getAssistantMcpCatalogChainTemplate("value_flow_ranking");
|
||||
const lifecycleTemplate = getAssistantMcpCatalogChainTemplate("lifecycle");
|
||||
|
||||
expect(documentTemplate.fallback_primitives).toEqual([
|
||||
"resolve_entity_reference",
|
||||
|
|
@ -75,6 +76,16 @@ describe("assistant MCP catalog index", () => {
|
|||
"aggregate_by_axis",
|
||||
"probe_coverage"
|
||||
]);
|
||||
expect(lifecycleTemplate.base_required_axes).toEqual([
|
||||
"document_date",
|
||||
"coverage_target",
|
||||
"evidence_basis",
|
||||
"activity_window",
|
||||
"legal_fact_boundary"
|
||||
]);
|
||||
expect(lifecycleTemplate.planning_tags).toEqual(
|
||||
expect.arrayContaining(["bounded_inference", "activity_window", "legal_fact_boundary"])
|
||||
);
|
||||
});
|
||||
|
||||
it("can search reviewed primitives from data-need decomposition candidates", () => {
|
||||
|
|
|
|||
|
|
@ -80,10 +80,18 @@ describe("assistant MCP discovery answer adapter", () => {
|
|||
expect(draft.internal_mechanics_allowed).toBe(false);
|
||||
expect(draft.headline).toContain("подтвержденная активность");
|
||||
expect(draft.confirmed_lines[0]).toContain("SVK");
|
||||
expect(draft.confirmed_lines[0]).toContain("matched_rows=1");
|
||||
expect(draft.inference_lines[0]).toContain("меньше месяца");
|
||||
expect(draft.inference_lines.join("\n")).toContain("Первая найденная активность: 2020-01-15");
|
||||
expect(draft.inference_lines.join("\n")).toContain("не юридически подтвержденный возраст регистрации");
|
||||
expect(pilot.evidence.inferred_facts).toContain(
|
||||
"Activity window is bounded by first=2020-01-15, latest=2020-01-15, matched_rows=1"
|
||||
);
|
||||
expect(pilot.evidence.inferred_facts).toContain("Activity-window inference is not legal registration age");
|
||||
expect(draft.unknown_lines).toContain("Legal registration date is not proven by this MCP discovery pilot");
|
||||
expect(draft.unknown_lines).toContain(
|
||||
"Business activity before the first confirmed 1C activity row is not proven by this MCP discovery pilot"
|
||||
);
|
||||
expect(draft.must_not_claim).toContain("Do not present inferred activity duration as a formally confirmed legal fact.");
|
||||
expect(draft.reason_codes).toContain("answer_contains_unknown_fact_boundary");
|
||||
});
|
||||
|
|
@ -912,10 +920,10 @@ describe("assistant MCP discovery answer adapter", () => {
|
|||
const draft = buildAssistantMcpDiscoveryAnswerDraft(pilot);
|
||||
|
||||
expect(draft.inference_lines).toContain(
|
||||
"Requested period coverage was recovered through monthly 1C value-flow probes after the broad probe hit the row limit"
|
||||
"Requested period coverage was recovered through monthly 1C value-flow probes"
|
||||
);
|
||||
expect(draft.unknown_lines).not.toContain(
|
||||
"Complete requested-period coverage is not proven because the MCP discovery probe row limit was reached"
|
||||
"Complete requested-period coverage is not proven by the available checked rows"
|
||||
);
|
||||
});
|
||||
|
||||
|
|
@ -971,6 +979,9 @@ describe("assistant MCP discovery answer adapter", () => {
|
|||
expect(inferenceText).toContain("2020-01-15");
|
||||
expect(inferenceText).toContain("2023-12-20");
|
||||
expect(inferenceText).toContain("не юридически подтвержденный возраст регистрации");
|
||||
expect(pilot.evidence.inferred_facts).toContain(
|
||||
"Activity window is bounded by first=2020-01-15, latest=2023-12-20, matched_rows=2"
|
||||
);
|
||||
expect(draft.reason_codes).toContain("pilot_derived_activity_period_from_confirmed_rows");
|
||||
});
|
||||
|
||||
|
|
|
|||
|
|
@ -714,7 +714,7 @@ describe("assistant MCP discovery pilot executor", () => {
|
|||
|
||||
expect(result.derived_value_flow?.coverage_limited_by_probe_limit).toBe(true);
|
||||
expect(result.evidence.unknown_facts).toContain(
|
||||
"Complete requested-period coverage is not proven because the MCP discovery probe row limit was reached"
|
||||
"Complete requested-period coverage is not proven by the available checked rows"
|
||||
);
|
||||
});
|
||||
|
||||
|
|
@ -760,10 +760,10 @@ describe("assistant MCP discovery pilot executor", () => {
|
|||
latest_movement_date: "2020-12-05"
|
||||
});
|
||||
expect(result.evidence.inferred_facts).toContain(
|
||||
"Requested period coverage was recovered through monthly 1C value-flow probes after the broad probe hit the row limit"
|
||||
"Requested period coverage was recovered through monthly 1C value-flow probes"
|
||||
);
|
||||
expect(result.evidence.unknown_facts).not.toContain(
|
||||
"Complete requested-period coverage is not proven because the MCP discovery probe row limit was reached"
|
||||
"Complete requested-period coverage is not proven by the available checked rows"
|
||||
);
|
||||
expect(result.reason_codes).toContain("pilot_monthly_period_chunking_recovered_coverage");
|
||||
});
|
||||
|
|
@ -942,10 +942,10 @@ describe("assistant MCP discovery pilot executor", () => {
|
|||
}
|
||||
});
|
||||
expect(result.evidence.inferred_facts).toContain(
|
||||
"Requested period coverage for bidirectional value-flow was recovered through monthly 1C side probes after a broad probe hit the row limit"
|
||||
"Requested period coverage for bidirectional value-flow was recovered through monthly 1C side probes"
|
||||
);
|
||||
expect(result.evidence.unknown_facts).not.toContain(
|
||||
"Complete requested-period coverage for bidirectional value-flow is not proven because at least one MCP discovery probe row limit was reached"
|
||||
"Complete requested-period coverage for bidirectional value-flow is not proven by the available checked rows"
|
||||
);
|
||||
expect(result.reason_codes).toContain("pilot_bidirectional_outgoing_monthly_period_chunking_recovered_coverage");
|
||||
expect(deps.executeAddressMcpQuery).toHaveBeenCalledTimes(14);
|
||||
|
|
|
|||
|
|
@ -671,7 +671,16 @@ describe("assistant MCP discovery planner", () => {
|
|||
"probe_coverage",
|
||||
"explain_evidence_basis"
|
||||
]);
|
||||
expect(result.required_axes).toEqual(["counterparty", "document_date", "coverage_target", "evidence_basis"]);
|
||||
expect(result.required_axes).toEqual([
|
||||
"counterparty",
|
||||
"document_date",
|
||||
"coverage_target",
|
||||
"evidence_basis",
|
||||
"activity_window",
|
||||
"legal_fact_boundary"
|
||||
]);
|
||||
expect(result.reason_codes).toContain("planner_lifecycle_bounded_activity_window_template");
|
||||
expect(result.reason_codes).toContain("planner_lifecycle_legal_fact_boundary_required");
|
||||
});
|
||||
|
||||
it("uses metadata-only planning when the user asks about available schema surface", () => {
|
||||
|
|
|
|||
|
|
@ -133,6 +133,68 @@ describe("assistant MCP discovery turn input adapter", () => {
|
|||
expect(result.reason_codes).not.toContain("mcp_discovery_payout_signal_detected");
|
||||
});
|
||||
|
||||
it("overrides a supported exact current-turn payout route when the question asks for a payment amount", () => {
|
||||
const result = buildAssistantMcpDiscoveryTurnInput({
|
||||
userMessage:
|
||||
"\u0441\u043a\u043e\u043b\u044c\u043a\u043e \u043c\u044b \u0437\u0430\u043f\u043b\u0430\u0442\u0438\u043b\u0438 \u0413\u0440\u0443\u043f\u043f\u0430 \u0421\u0412\u041a \u0437\u0430 2020 \u0433\u043e\u0434?",
|
||||
effectiveMessage:
|
||||
"\u0421\u043a\u043e\u043b\u044c\u043a\u043e \u0431\u044b\u043b\u043e \u043e\u043f\u043b\u0430\u0447\u0435\u043d\u043e \u043a\u043e\u043d\u0442\u0440\u0430\u0433\u0435\u043d\u0442\u0443 '\u0413\u0440\u0443\u043f\u043f\u0430 \u0421\u0412\u041a' \u0432 2020 \u0433\u043e\u0434\u0443?",
|
||||
assistantTurnMeaning: {
|
||||
asked_domain_family: "counterparty",
|
||||
asked_action_family: "counterparty_population_and_roles",
|
||||
explicit_intent_candidate: "counterparty_population_and_roles",
|
||||
explicit_entity_candidates: [{ value: "\u0413\u0440\u0443\u043f\u043f\u0430 \u0421\u0412\u041a" }]
|
||||
},
|
||||
predecomposeContract: {
|
||||
entities: { counterparty: "\u0413\u0440\u0443\u043f\u043f\u0430 \u0421\u0412\u041a" },
|
||||
period: { period_from: "2020-01-01", period_to: "2020-12-31" }
|
||||
}
|
||||
});
|
||||
|
||||
expect(result.adapter_status).toBe("ready");
|
||||
expect(result.should_run_discovery).toBe(true);
|
||||
expect(result.turn_meaning_ref).toMatchObject({
|
||||
asked_domain_family: "counterparty_value",
|
||||
asked_action_family: "payout",
|
||||
explicit_entity_candidates: ["\u0413\u0440\u0443\u043f\u043f\u0430 \u0421\u0412\u041a"],
|
||||
explicit_date_scope: "2020",
|
||||
unsupported_but_understood_family: "counterparty_payouts_or_outflow",
|
||||
stale_replay_forbidden: true
|
||||
});
|
||||
expect(result.reason_codes).toContain("mcp_discovery_current_turn_value_flow_overrides_supported_exact");
|
||||
expect(result.reason_codes).not.toContain("mcp_discovery_not_applicable_for_supported_exact_turn");
|
||||
});
|
||||
|
||||
it("overrides a supported exact current-turn bank list when the question asks for net cash flow", () => {
|
||||
const result = buildAssistantMcpDiscoveryTurnInput({
|
||||
userMessage:
|
||||
"\u043a\u0430\u043a\u043e\u0435 \u043d\u0435\u0442\u0442\u043e \u043f\u043e \u0434\u0435\u043d\u044c\u0433\u0430\u043c \u0441 \u0413\u0440\u0443\u043f\u043f\u0430 \u0421\u0412\u041a \u0437\u0430 2020 \u0433\u043e\u0434: \u0441\u043a\u043e\u043b\u044c\u043a\u043e \u043f\u043e\u043b\u0443\u0447\u0438\u043b\u0438 \u0438 \u0441\u043a\u043e\u043b\u044c\u043a\u043e \u0437\u0430\u043f\u043b\u0430\u0442\u0438\u043b\u0438?",
|
||||
assistantTurnMeaning: {
|
||||
asked_domain_family: "counterparty",
|
||||
asked_action_family: "bank_operations_by_counterparty",
|
||||
explicit_intent_candidate: "bank_operations_by_counterparty",
|
||||
explicit_entity_candidates: [{ value: "\u0413\u0440\u0443\u043f\u043f\u0430 \u0421\u0412\u041a" }]
|
||||
},
|
||||
predecomposeContract: {
|
||||
entities: { counterparty: "\u0413\u0440\u0443\u043f\u043f\u0430 \u0421\u0412\u041a" },
|
||||
period: { period_from: "2020-01-01", period_to: "2020-12-31" }
|
||||
}
|
||||
});
|
||||
|
||||
expect(result.adapter_status).toBe("ready");
|
||||
expect(result.should_run_discovery).toBe(true);
|
||||
expect(result.turn_meaning_ref).toMatchObject({
|
||||
asked_domain_family: "counterparty_value",
|
||||
asked_action_family: "net_value_flow",
|
||||
explicit_entity_candidates: ["\u0413\u0440\u0443\u043f\u043f\u0430 \u0421\u0412\u041a"],
|
||||
explicit_date_scope: "2020",
|
||||
unsupported_but_understood_family: "counterparty_bidirectional_value_flow_or_netting",
|
||||
stale_replay_forbidden: true
|
||||
});
|
||||
expect(result.reason_codes).toContain("mcp_discovery_current_turn_value_flow_overrides_supported_exact");
|
||||
expect(result.reason_codes).not.toContain("mcp_discovery_not_applicable_for_supported_exact_turn");
|
||||
});
|
||||
|
||||
it("prefers the explicit current-turn counterparty over stale organization-scoped carryover in net follow-up discovery", () => {
|
||||
const result = buildAssistantMcpDiscoveryTurnInput({
|
||||
userMessage: "какое нетто по деньгам с Группа СВК за 2020 год: сколько получили и сколько заплатили?",
|
||||
|
|
@ -1347,6 +1409,26 @@ describe("assistant MCP discovery turn input adapter", () => {
|
|||
expect(result.reason_codes).toContain("mcp_discovery_not_applicable_for_supported_exact_turn");
|
||||
});
|
||||
|
||||
it("does not replace broad business evaluation with metadata discovery", () => {
|
||||
const result = buildAssistantMcpDiscoveryTurnInput({
|
||||
userMessage:
|
||||
"\u041a\u0430\u043a \u0442\u044b \u043e\u0446\u0435\u043d\u0438\u0448\u044c \u0434\u0435\u044f\u0442\u0435\u043b\u044c\u043d\u043e\u0441\u0442\u044c \u043a\u043e\u043c\u043f\u0430\u043d\u0438\u0438?",
|
||||
assistantTurnMeaning: {
|
||||
asked_domain_family: "business_summary",
|
||||
asked_action_family: "broad_evaluation",
|
||||
explicit_date_scope: "2026-05-01",
|
||||
unsupported_but_understood_family: "broad_business_evaluation",
|
||||
stale_replay_forbidden: true
|
||||
}
|
||||
});
|
||||
|
||||
expect(result.adapter_status).toBe("not_applicable");
|
||||
expect(result.should_run_discovery).toBe(false);
|
||||
expect(result.turn_meaning_ref).toBeNull();
|
||||
expect(result.reason_codes).toContain("mcp_discovery_broad_business_evaluation_kept_in_living_chat");
|
||||
expect(result.reason_codes).toContain("mcp_discovery_not_applicable_for_supported_exact_turn");
|
||||
});
|
||||
|
||||
it("does not bootstrap metadata discovery from a referential document exclusion follow-up over exact document context", () => {
|
||||
const result = buildAssistantMcpDiscoveryTurnInput({
|
||||
userMessage: "кроме этого документа есть еще что-то?",
|
||||
|
|
|
|||
Loading…
Reference in New Issue