Planner Autonomy: закрепить bounded inference и semantic bridges

This commit is contained in:
2026-05-01 12:53:21 +03:00
parent 3634404b1c
commit c7c85e9b42
26 changed files with 3496 additions and 55 deletions
@@ -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