Уточнить subject-оси Autonomy Core 1С
This commit is contained in:
parent
4d1b25cca7
commit
624e0cdf26
|
|
@ -37,12 +37,29 @@ function uniqueStrings(values) {
|
||||||
function isExplicitDate(value) {
|
function isExplicitDate(value) {
|
||||||
return Boolean(value && /^\d{4}-\d{2}-\d{2}$/.test(value));
|
return Boolean(value && /^\d{4}-\d{2}-\d{2}$/.test(value));
|
||||||
}
|
}
|
||||||
function providedAxesFromMeaning(meaning, graph) {
|
const SUBJECT_AXIS_SET = new Set([
|
||||||
|
"counterparty",
|
||||||
|
"business_entity",
|
||||||
|
"item",
|
||||||
|
"supplier",
|
||||||
|
"buyer",
|
||||||
|
"document",
|
||||||
|
"contract"
|
||||||
|
]);
|
||||||
|
function providedAxesFromMeaning(meaning, graph, requiredAxes) {
|
||||||
const result = [];
|
const result = [];
|
||||||
if ((meaning?.explicit_entity_candidates?.length ?? 0) > 0) {
|
if ((meaning?.explicit_entity_candidates?.length ?? 0) > 0) {
|
||||||
result.push("counterparty");
|
result.push("counterparty");
|
||||||
result.push("business_entity");
|
result.push("business_entity");
|
||||||
}
|
}
|
||||||
|
if ((graph?.subject_candidates.length ?? 0) > 0) {
|
||||||
|
result.push("business_entity");
|
||||||
|
for (const axis of requiredAxes) {
|
||||||
|
if (SUBJECT_AXIS_SET.has(axis)) {
|
||||||
|
result.push(axis);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
if (toNonEmptyString(meaning?.explicit_organization_scope)) {
|
if (toNonEmptyString(meaning?.explicit_organization_scope)) {
|
||||||
result.push("organization");
|
result.push("organization");
|
||||||
}
|
}
|
||||||
|
|
@ -126,7 +143,7 @@ function buildAssistantEvidencePlanner(input) {
|
||||||
const plan = input.discoveryPlan;
|
const plan = input.discoveryPlan;
|
||||||
const turnMeaning = plan.turn_meaning_ref;
|
const turnMeaning = plan.turn_meaning_ref;
|
||||||
const requiredAxes = uniqueStrings(plan.required_axes);
|
const requiredAxes = uniqueStrings(plan.required_axes);
|
||||||
const providedAxes = providedAxesFromMeaning(turnMeaning, graph);
|
const providedAxes = providedAxesFromMeaning(turnMeaning, graph, requiredAxes);
|
||||||
const graphClarificationGaps = uniqueStrings(graph?.clarification_gaps ?? []);
|
const graphClarificationGaps = uniqueStrings(graph?.clarification_gaps ?? []);
|
||||||
const additionalAxisGaps = uniqueStrings(input.additionalMissingAxes ?? []).filter((axis) => !providedAxes.includes(axis) && (requiredAxes.includes(axis) || USER_ACTIONABLE_AXIS_SET.has(axis)));
|
const additionalAxisGaps = uniqueStrings(input.additionalMissingAxes ?? []).filter((axis) => !providedAxes.includes(axis) && (requiredAxes.includes(axis) || USER_ACTIONABLE_AXIS_SET.has(axis)));
|
||||||
const axisGaps = uniqueStrings([...additionalAxisGaps, ...missingAxes(requiredAxes, providedAxes)]);
|
const axisGaps = uniqueStrings([...additionalAxisGaps, ...missingAxes(requiredAxes, providedAxes)]);
|
||||||
|
|
|
||||||
|
|
@ -178,6 +178,23 @@ function hasOpenScopeOneSidedValueTotalHintUtf8Safe(rawUtterance, action) {
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
function inferredOpenScopeOneSidedValueAction(rawUtterance) {
|
||||||
|
if (hasOpenScopeOneSidedValueTotalHintUtf8Safe(rawUtterance, "turnover") ||
|
||||||
|
hasOpenScopeOneSidedValueTotalHint(rawUtterance, "turnover")) {
|
||||||
|
return "turnover";
|
||||||
|
}
|
||||||
|
if (hasOpenScopeOneSidedValueTotalHintUtf8Safe(rawUtterance, "payout") ||
|
||||||
|
hasOpenScopeOneSidedValueTotalHint(rawUtterance, "payout")) {
|
||||||
|
return "payout";
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
function hasDetailedBusinessOverviewMoneyBreakdownHint(rawUtterance) {
|
||||||
|
if (!rawUtterance) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return /(?:\u0440\u0430\u0441\u043a\u0440\u043e\p{L}*|\u043f\u043e\u0434\u0440\u043e\u0431\p{L}*|\u0440\u0430\u0437\u0431\u0435\p{L}*|\u0440\u0430\u0437\u0432\u0435\u0440\u043d\p{L}*|\u043a\u0442\u043e\s+\u0433\u043b\u0430\u0432\p{L}*|\u0433\u043b\u0430\u0432\p{L}*\s+\u043a\u043b\u0438\u0435\u043d\p{L}*|\u0433\u043b\u0430\u0432\p{L}*\s+\u043f\u043e\u0441\u0442\u0430\u0432\p{L}*|\u0447\u0438\u0441\u0442\p{L}*\s+\u0434\u0435\u043d\u0435\u0436\p{L}*\s+\u043f\u043e\u0442\u043e\p{L}*|\u043f\u043e\u043b\u0443\u0447\p{L}*[\s\S]{0,80}\u0437\u0430\u043f\u043b\u0430\u0442\p{L}*|\u0432\u0445\u043e\u0434\p{L}*[\s\S]{0,80}\u0438\u0441\u0445\u043e\u0434\p{L}*|detail|breakdown|drill\s*down|main\s+customer|main\s+supplier)/iu.test(rawUtterance);
|
||||||
|
}
|
||||||
function supportsOrganizationScopedOpenTotal(action) {
|
function supportsOrganizationScopedOpenTotal(action) {
|
||||||
return action === "turnover" || action === "payout";
|
return action === "turnover" || action === "payout";
|
||||||
}
|
}
|
||||||
|
|
@ -393,7 +410,7 @@ function buildAssistantMcpDiscoveryDataNeedGraph(input) {
|
||||||
const semanticDataNeed = lower(input.semanticDataNeed);
|
const semanticDataNeed = lower(input.semanticDataNeed);
|
||||||
const turnMeaning = input.turnMeaning ?? null;
|
const turnMeaning = input.turnMeaning ?? null;
|
||||||
const domain = lower(turnMeaning?.asked_domain_family);
|
const domain = lower(turnMeaning?.asked_domain_family);
|
||||||
const action = lower(turnMeaning?.asked_action_family);
|
const rawAction = lower(turnMeaning?.asked_action_family);
|
||||||
const unsupported = lower(turnMeaning?.unsupported_but_understood_family);
|
const unsupported = lower(turnMeaning?.unsupported_but_understood_family);
|
||||||
const rawUtterance = lower(input.rawUtterance);
|
const rawUtterance = lower(input.rawUtterance);
|
||||||
const rawQuestionSignal = lower([input.rawUtterance, turnMeaning?.raw_message, turnMeaning?.effective_message].join(" "));
|
const rawQuestionSignal = lower([input.rawUtterance, turnMeaning?.raw_message, turnMeaning?.effective_message].join(" "));
|
||||||
|
|
@ -406,12 +423,19 @@ function buildAssistantMcpDiscoveryDataNeedGraph(input) {
|
||||||
const subjectCandidates = (turnMeaning?.explicit_entity_candidates ?? [])
|
const subjectCandidates = (turnMeaning?.explicit_entity_candidates ?? [])
|
||||||
.map((item) => toNonEmptyString(item))
|
.map((item) => toNonEmptyString(item))
|
||||||
.filter((item) => Boolean(item));
|
.filter((item) => Boolean(item));
|
||||||
const businessFactFamily = businessFactFamilyFor({
|
const initialBusinessFactFamily = businessFactFamilyFor({
|
||||||
semanticDataNeed,
|
semanticDataNeed,
|
||||||
domain,
|
domain,
|
||||||
action,
|
action: rawAction,
|
||||||
unsupported
|
unsupported
|
||||||
});
|
});
|
||||||
|
const oneSidedValueAction = inferredOpenScopeOneSidedValueAction(rawQuestionSignal);
|
||||||
|
const detailedBusinessOverviewMoneyBreakdownHint = hasDetailedBusinessOverviewMoneyBreakdownHint(rawQuestionSignal);
|
||||||
|
const oneSidedBusinessOverviewValueFlowOverride = initialBusinessFactFamily === "business_overview" &&
|
||||||
|
Boolean(oneSidedValueAction) &&
|
||||||
|
!detailedBusinessOverviewMoneyBreakdownHint;
|
||||||
|
const action = oneSidedBusinessOverviewValueFlowOverride ? oneSidedValueAction : rawAction;
|
||||||
|
const businessFactFamily = oneSidedBusinessOverviewValueFlowOverride ? "value_flow" : initialBusinessFactFamily;
|
||||||
const aggregationNeed = aggregationNeedFor(aggregationAxis);
|
const aggregationNeed = aggregationNeedFor(aggregationAxis);
|
||||||
const comparisonNeed = comparisonNeedFor(action);
|
const comparisonNeed = comparisonNeedFor(action);
|
||||||
const allTimeScopeHint = hasAllTimeScopeHint(rawUtterance);
|
const allTimeScopeHint = hasAllTimeScopeHint(rawUtterance);
|
||||||
|
|
@ -529,6 +553,9 @@ function buildAssistantMcpDiscoveryDataNeedGraph(input) {
|
||||||
if (directBusinessOverviewMoneyAnswerHint) {
|
if (directBusinessOverviewMoneyAnswerHint) {
|
||||||
pushReason(reasonCodes, "data_need_graph_business_overview_direct_money_answer");
|
pushReason(reasonCodes, "data_need_graph_business_overview_direct_money_answer");
|
||||||
}
|
}
|
||||||
|
if (oneSidedBusinessOverviewValueFlowOverride) {
|
||||||
|
pushReason(reasonCodes, "data_need_graph_business_overview_one_sided_money_total_routed_to_value_flow");
|
||||||
|
}
|
||||||
if (clarificationGaps.includes("organization")) {
|
if (clarificationGaps.includes("organization")) {
|
||||||
pushReason(reasonCodes, "data_need_graph_open_scope_total_needs_organization");
|
pushReason(reasonCodes, "data_need_graph_open_scope_total_needs_organization");
|
||||||
}
|
}
|
||||||
|
|
@ -542,7 +569,7 @@ function buildAssistantMcpDiscoveryDataNeedGraph(input) {
|
||||||
metadata_scope_hint: metadataScopeHint,
|
metadata_scope_hint: metadataScopeHint,
|
||||||
subject_resolution_optional: subjectResolutionOptional || undefined,
|
subject_resolution_optional: subjectResolutionOptional || undefined,
|
||||||
business_fact_family: businessFactFamily,
|
business_fact_family: businessFactFamily,
|
||||||
action_family: toNonEmptyString(turnMeaning?.asked_action_family),
|
action_family: toNonEmptyString(action),
|
||||||
aggregation_need: aggregationNeed,
|
aggregation_need: aggregationNeed,
|
||||||
time_scope_need: timeScopeNeed,
|
time_scope_need: timeScopeNeed,
|
||||||
comparison_need: comparisonNeed,
|
comparison_need: comparisonNeed,
|
||||||
|
|
|
||||||
|
|
@ -109,10 +109,16 @@ function hasReasonCode(graph, reasonCode) {
|
||||||
function aggregationAxis(meaning) {
|
function aggregationAxis(meaning) {
|
||||||
return toNonEmptyString(meaning?.asked_aggregation_axis)?.toLowerCase() ?? null;
|
return toNonEmptyString(meaning?.asked_aggregation_axis)?.toLowerCase() ?? null;
|
||||||
}
|
}
|
||||||
function addScopeAxes(axes, meaning) {
|
function addScopeAxes(axes, meaning, graph) {
|
||||||
if (hasEntity(meaning)) {
|
if (hasEntity(meaning)) {
|
||||||
pushUnique(axes, "counterparty");
|
pushUnique(axes, "counterparty");
|
||||||
}
|
}
|
||||||
|
if (hasSubjectCandidates(graph)) {
|
||||||
|
const factFamily = lower(graph?.business_fact_family);
|
||||||
|
if (factFamily === "value_flow" || factFamily === "activity_lifecycle") {
|
||||||
|
pushUnique(axes, "counterparty");
|
||||||
|
}
|
||||||
|
}
|
||||||
if (toNonEmptyString(meaning?.explicit_organization_scope)) {
|
if (toNonEmptyString(meaning?.explicit_organization_scope)) {
|
||||||
pushUnique(axes, "organization");
|
pushUnique(axes, "organization");
|
||||||
}
|
}
|
||||||
|
|
@ -498,7 +504,7 @@ function recipeFor(input) {
|
||||||
const combined = `${domain} ${action} ${unsupported}`.trim();
|
const combined = `${domain} ${action} ${unsupported}`.trim();
|
||||||
const axes = [];
|
const axes = [];
|
||||||
const requestedAggregationAxis = aggregationAxis(meaning);
|
const requestedAggregationAxis = aggregationAxis(meaning);
|
||||||
addScopeAxes(axes, meaning);
|
addScopeAxes(axes, meaning, dataNeedGraph);
|
||||||
addMetadataScopeAxis(axes, meaning);
|
addMetadataScopeAxis(axes, meaning);
|
||||||
addTimeScopeAxes(axes, dataNeedGraph);
|
addTimeScopeAxes(axes, dataNeedGraph);
|
||||||
if (graphClarificationGaps.includes("lane_family_choice")) {
|
if (graphClarificationGaps.includes("lane_family_choice")) {
|
||||||
|
|
|
||||||
|
|
@ -117,15 +117,34 @@ function isExplicitDate(value: string | null): boolean {
|
||||||
return Boolean(value && /^\d{4}-\d{2}-\d{2}$/.test(value));
|
return Boolean(value && /^\d{4}-\d{2}-\d{2}$/.test(value));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const SUBJECT_AXIS_SET = new Set([
|
||||||
|
"counterparty",
|
||||||
|
"business_entity",
|
||||||
|
"item",
|
||||||
|
"supplier",
|
||||||
|
"buyer",
|
||||||
|
"document",
|
||||||
|
"contract"
|
||||||
|
]);
|
||||||
|
|
||||||
function providedAxesFromMeaning(
|
function providedAxesFromMeaning(
|
||||||
meaning: AssistantMcpDiscoveryTurnMeaningRef | null,
|
meaning: AssistantMcpDiscoveryTurnMeaningRef | null,
|
||||||
graph: AssistantMcpDiscoveryDataNeedGraphContract | null
|
graph: AssistantMcpDiscoveryDataNeedGraphContract | null,
|
||||||
|
requiredAxes: string[]
|
||||||
): string[] {
|
): string[] {
|
||||||
const result: string[] = [];
|
const result: string[] = [];
|
||||||
if ((meaning?.explicit_entity_candidates?.length ?? 0) > 0) {
|
if ((meaning?.explicit_entity_candidates?.length ?? 0) > 0) {
|
||||||
result.push("counterparty");
|
result.push("counterparty");
|
||||||
result.push("business_entity");
|
result.push("business_entity");
|
||||||
}
|
}
|
||||||
|
if ((graph?.subject_candidates.length ?? 0) > 0) {
|
||||||
|
result.push("business_entity");
|
||||||
|
for (const axis of requiredAxes) {
|
||||||
|
if (SUBJECT_AXIS_SET.has(axis)) {
|
||||||
|
result.push(axis);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
if (toNonEmptyString(meaning?.explicit_organization_scope)) {
|
if (toNonEmptyString(meaning?.explicit_organization_scope)) {
|
||||||
result.push("organization");
|
result.push("organization");
|
||||||
}
|
}
|
||||||
|
|
@ -223,7 +242,7 @@ export function buildAssistantEvidencePlanner(
|
||||||
const plan = input.discoveryPlan;
|
const plan = input.discoveryPlan;
|
||||||
const turnMeaning = plan.turn_meaning_ref;
|
const turnMeaning = plan.turn_meaning_ref;
|
||||||
const requiredAxes = uniqueStrings(plan.required_axes);
|
const requiredAxes = uniqueStrings(plan.required_axes);
|
||||||
const providedAxes = providedAxesFromMeaning(turnMeaning, graph);
|
const providedAxes = providedAxesFromMeaning(turnMeaning, graph, requiredAxes);
|
||||||
const graphClarificationGaps = uniqueStrings(graph?.clarification_gaps ?? []);
|
const graphClarificationGaps = uniqueStrings(graph?.clarification_gaps ?? []);
|
||||||
const additionalAxisGaps = uniqueStrings(input.additionalMissingAxes ?? []).filter(
|
const additionalAxisGaps = uniqueStrings(input.additionalMissingAxes ?? []).filter(
|
||||||
(axis) => !providedAxes.includes(axis) && (requiredAxes.includes(axis) || USER_ACTIONABLE_AXIS_SET.has(axis)),
|
(axis) => !providedAxes.includes(axis) && (requiredAxes.includes(axis) || USER_ACTIONABLE_AXIS_SET.has(axis)),
|
||||||
|
|
|
||||||
|
|
@ -267,6 +267,31 @@ function hasOpenScopeOneSidedValueTotalHintUtf8Safe(rawUtterance: string, action
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function inferredOpenScopeOneSidedValueAction(rawUtterance: string): "turnover" | "payout" | null {
|
||||||
|
if (
|
||||||
|
hasOpenScopeOneSidedValueTotalHintUtf8Safe(rawUtterance, "turnover") ||
|
||||||
|
hasOpenScopeOneSidedValueTotalHint(rawUtterance, "turnover")
|
||||||
|
) {
|
||||||
|
return "turnover";
|
||||||
|
}
|
||||||
|
if (
|
||||||
|
hasOpenScopeOneSidedValueTotalHintUtf8Safe(rawUtterance, "payout") ||
|
||||||
|
hasOpenScopeOneSidedValueTotalHint(rawUtterance, "payout")
|
||||||
|
) {
|
||||||
|
return "payout";
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
function hasDetailedBusinessOverviewMoneyBreakdownHint(rawUtterance: string): boolean {
|
||||||
|
if (!rawUtterance) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return /(?:\u0440\u0430\u0441\u043a\u0440\u043e\p{L}*|\u043f\u043e\u0434\u0440\u043e\u0431\p{L}*|\u0440\u0430\u0437\u0431\u0435\p{L}*|\u0440\u0430\u0437\u0432\u0435\u0440\u043d\p{L}*|\u043a\u0442\u043e\s+\u0433\u043b\u0430\u0432\p{L}*|\u0433\u043b\u0430\u0432\p{L}*\s+\u043a\u043b\u0438\u0435\u043d\p{L}*|\u0433\u043b\u0430\u0432\p{L}*\s+\u043f\u043e\u0441\u0442\u0430\u0432\p{L}*|\u0447\u0438\u0441\u0442\p{L}*\s+\u0434\u0435\u043d\u0435\u0436\p{L}*\s+\u043f\u043e\u0442\u043e\p{L}*|\u043f\u043e\u043b\u0443\u0447\p{L}*[\s\S]{0,80}\u0437\u0430\u043f\u043b\u0430\u0442\p{L}*|\u0432\u0445\u043e\u0434\p{L}*[\s\S]{0,80}\u0438\u0441\u0445\u043e\u0434\p{L}*|detail|breakdown|drill\s*down|main\s+customer|main\s+supplier)/iu.test(
|
||||||
|
rawUtterance
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
function supportsOrganizationScopedOpenTotal(action: string): boolean {
|
function supportsOrganizationScopedOpenTotal(action: string): boolean {
|
||||||
return action === "turnover" || action === "payout";
|
return action === "turnover" || action === "payout";
|
||||||
}
|
}
|
||||||
|
|
@ -522,7 +547,7 @@ export function buildAssistantMcpDiscoveryDataNeedGraph(
|
||||||
const semanticDataNeed = lower(input.semanticDataNeed);
|
const semanticDataNeed = lower(input.semanticDataNeed);
|
||||||
const turnMeaning = input.turnMeaning ?? null;
|
const turnMeaning = input.turnMeaning ?? null;
|
||||||
const domain = lower(turnMeaning?.asked_domain_family);
|
const domain = lower(turnMeaning?.asked_domain_family);
|
||||||
const action = lower(turnMeaning?.asked_action_family);
|
const rawAction = lower(turnMeaning?.asked_action_family);
|
||||||
const unsupported = lower(turnMeaning?.unsupported_but_understood_family);
|
const unsupported = lower(turnMeaning?.unsupported_but_understood_family);
|
||||||
const rawUtterance = lower(input.rawUtterance);
|
const rawUtterance = lower(input.rawUtterance);
|
||||||
const rawQuestionSignal = lower([input.rawUtterance, turnMeaning?.raw_message, turnMeaning?.effective_message].join(" "));
|
const rawQuestionSignal = lower([input.rawUtterance, turnMeaning?.raw_message, turnMeaning?.effective_message].join(" "));
|
||||||
|
|
@ -535,12 +560,20 @@ export function buildAssistantMcpDiscoveryDataNeedGraph(
|
||||||
const subjectCandidates = (turnMeaning?.explicit_entity_candidates ?? [])
|
const subjectCandidates = (turnMeaning?.explicit_entity_candidates ?? [])
|
||||||
.map((item) => toNonEmptyString(item))
|
.map((item) => toNonEmptyString(item))
|
||||||
.filter((item): item is string => Boolean(item));
|
.filter((item): item is string => Boolean(item));
|
||||||
const businessFactFamily = businessFactFamilyFor({
|
const initialBusinessFactFamily = businessFactFamilyFor({
|
||||||
semanticDataNeed,
|
semanticDataNeed,
|
||||||
domain,
|
domain,
|
||||||
action,
|
action: rawAction,
|
||||||
unsupported
|
unsupported
|
||||||
});
|
});
|
||||||
|
const oneSidedValueAction = inferredOpenScopeOneSidedValueAction(rawQuestionSignal);
|
||||||
|
const detailedBusinessOverviewMoneyBreakdownHint = hasDetailedBusinessOverviewMoneyBreakdownHint(rawQuestionSignal);
|
||||||
|
const oneSidedBusinessOverviewValueFlowOverride =
|
||||||
|
initialBusinessFactFamily === "business_overview" &&
|
||||||
|
Boolean(oneSidedValueAction) &&
|
||||||
|
!detailedBusinessOverviewMoneyBreakdownHint;
|
||||||
|
const action = oneSidedBusinessOverviewValueFlowOverride ? oneSidedValueAction! : rawAction;
|
||||||
|
const businessFactFamily = oneSidedBusinessOverviewValueFlowOverride ? "value_flow" : initialBusinessFactFamily;
|
||||||
const aggregationNeed = aggregationNeedFor(aggregationAxis);
|
const aggregationNeed = aggregationNeedFor(aggregationAxis);
|
||||||
const comparisonNeed = comparisonNeedFor(action);
|
const comparisonNeed = comparisonNeedFor(action);
|
||||||
const allTimeScopeHint = hasAllTimeScopeHint(rawUtterance);
|
const allTimeScopeHint = hasAllTimeScopeHint(rawUtterance);
|
||||||
|
|
@ -669,6 +702,9 @@ export function buildAssistantMcpDiscoveryDataNeedGraph(
|
||||||
if (directBusinessOverviewMoneyAnswerHint) {
|
if (directBusinessOverviewMoneyAnswerHint) {
|
||||||
pushReason(reasonCodes, "data_need_graph_business_overview_direct_money_answer");
|
pushReason(reasonCodes, "data_need_graph_business_overview_direct_money_answer");
|
||||||
}
|
}
|
||||||
|
if (oneSidedBusinessOverviewValueFlowOverride) {
|
||||||
|
pushReason(reasonCodes, "data_need_graph_business_overview_one_sided_money_total_routed_to_value_flow");
|
||||||
|
}
|
||||||
if (clarificationGaps.includes("organization")) {
|
if (clarificationGaps.includes("organization")) {
|
||||||
pushReason(reasonCodes, "data_need_graph_open_scope_total_needs_organization");
|
pushReason(reasonCodes, "data_need_graph_open_scope_total_needs_organization");
|
||||||
}
|
}
|
||||||
|
|
@ -683,7 +719,7 @@ export function buildAssistantMcpDiscoveryDataNeedGraph(
|
||||||
metadata_scope_hint: metadataScopeHint,
|
metadata_scope_hint: metadataScopeHint,
|
||||||
subject_resolution_optional: subjectResolutionOptional || undefined,
|
subject_resolution_optional: subjectResolutionOptional || undefined,
|
||||||
business_fact_family: businessFactFamily,
|
business_fact_family: businessFactFamily,
|
||||||
action_family: toNonEmptyString(turnMeaning?.asked_action_family),
|
action_family: toNonEmptyString(action),
|
||||||
aggregation_need: aggregationNeed,
|
aggregation_need: aggregationNeed,
|
||||||
time_scope_need: timeScopeNeed,
|
time_scope_need: timeScopeNeed,
|
||||||
comparison_need: comparisonNeed,
|
comparison_need: comparisonNeed,
|
||||||
|
|
|
||||||
|
|
@ -253,10 +253,20 @@ function aggregationAxis(meaning: AssistantMcpDiscoveryTurnMeaningRef | null | u
|
||||||
return toNonEmptyString(meaning?.asked_aggregation_axis)?.toLowerCase() ?? null;
|
return toNonEmptyString(meaning?.asked_aggregation_axis)?.toLowerCase() ?? null;
|
||||||
}
|
}
|
||||||
|
|
||||||
function addScopeAxes(axes: string[], meaning: AssistantMcpDiscoveryTurnMeaningRef | null | undefined): void {
|
function addScopeAxes(
|
||||||
|
axes: string[],
|
||||||
|
meaning: AssistantMcpDiscoveryTurnMeaningRef | null | undefined,
|
||||||
|
graph: AssistantMcpDiscoveryDataNeedGraphContract | null | undefined
|
||||||
|
): void {
|
||||||
if (hasEntity(meaning)) {
|
if (hasEntity(meaning)) {
|
||||||
pushUnique(axes, "counterparty");
|
pushUnique(axes, "counterparty");
|
||||||
}
|
}
|
||||||
|
if (hasSubjectCandidates(graph)) {
|
||||||
|
const factFamily = lower(graph?.business_fact_family);
|
||||||
|
if (factFamily === "value_flow" || factFamily === "activity_lifecycle") {
|
||||||
|
pushUnique(axes, "counterparty");
|
||||||
|
}
|
||||||
|
}
|
||||||
if (toNonEmptyString(meaning?.explicit_organization_scope)) {
|
if (toNonEmptyString(meaning?.explicit_organization_scope)) {
|
||||||
pushUnique(axes, "organization");
|
pushUnique(axes, "organization");
|
||||||
}
|
}
|
||||||
|
|
@ -741,7 +751,7 @@ function recipeFor(input: AssistantMcpDiscoveryPlannerInput): PlannerRecipe {
|
||||||
const combined = `${domain} ${action} ${unsupported}`.trim();
|
const combined = `${domain} ${action} ${unsupported}`.trim();
|
||||||
const axes: string[] = [];
|
const axes: string[] = [];
|
||||||
const requestedAggregationAxis = aggregationAxis(meaning);
|
const requestedAggregationAxis = aggregationAxis(meaning);
|
||||||
addScopeAxes(axes, meaning);
|
addScopeAxes(axes, meaning, dataNeedGraph);
|
||||||
addMetadataScopeAxis(axes, meaning);
|
addMetadataScopeAxis(axes, meaning);
|
||||||
addTimeScopeAxes(axes, dataNeedGraph);
|
addTimeScopeAxes(axes, dataNeedGraph);
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -336,6 +336,37 @@ describe("assistant MCP discovery data need graph", () => {
|
||||||
]);
|
]);
|
||||||
expect(result.reason_codes).toContain("data_need_graph_open_scope_total_without_subject");
|
expect(result.reason_codes).toContain("data_need_graph_open_scope_total_without_subject");
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it("routes broad-overview one-sided incoming totals to value-flow instead of company overview", () => {
|
||||||
|
const result = buildAssistantMcpDiscoveryDataNeedGraph({
|
||||||
|
semanticDataNeed: "business overview evidence with bounded analyst interpretation",
|
||||||
|
rawUtterance:
|
||||||
|
"А всего сколько денег пришло в ООО Альтернатива Плюс за 2020, без топов и без контрагентов?",
|
||||||
|
turnMeaning: {
|
||||||
|
asked_domain_family: "business_overview",
|
||||||
|
asked_action_family: "broad_evaluation",
|
||||||
|
unsupported_but_understood_family: "broad_business_evaluation",
|
||||||
|
explicit_date_scope: "2020",
|
||||||
|
explicit_organization_scope: "ООО Альтернатива Плюс"
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(result.business_fact_family).toBe("value_flow");
|
||||||
|
expect(result.action_family).toBe("turnover");
|
||||||
|
expect(result.comparison_need).toBeNull();
|
||||||
|
expect(result.ranking_need).toBeNull();
|
||||||
|
expect(result.clarification_gaps).toEqual([]);
|
||||||
|
expect(result.decomposition_candidates).toEqual([
|
||||||
|
"collect_scoped_movements",
|
||||||
|
"aggregate_checked_amounts",
|
||||||
|
"probe_coverage"
|
||||||
|
]);
|
||||||
|
expect(result.reason_codes).toContain("data_need_graph_open_scope_total_without_subject");
|
||||||
|
expect(result.reason_codes).toContain(
|
||||||
|
"data_need_graph_business_overview_one_sided_money_total_routed_to_value_flow"
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
it("treats a generic incoming total as an understood open-scope ask that still needs organization", () => {
|
it("treats a generic incoming total as an understood open-scope ask that still needs organization", () => {
|
||||||
const result = buildAssistantMcpDiscoveryDataNeedGraph({
|
const result = buildAssistantMcpDiscoveryDataNeedGraph({
|
||||||
semanticDataNeed: "counterparty value-flow evidence",
|
semanticDataNeed: "counterparty value-flow evidence",
|
||||||
|
|
|
||||||
|
|
@ -157,6 +157,68 @@ describe("assistant MCP discovery planner", () => {
|
||||||
expect(result.evidence_plan.evidence_axes.missing_axes).not.toContain("all_time_scope");
|
expect(result.evidence_plan.evidence_axes.missing_axes).not.toContain("all_time_scope");
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it("uses graph subject candidates to satisfy the selected subject evidence axis", () => {
|
||||||
|
const result = planAssistantMcpDiscovery({
|
||||||
|
dataNeedGraph: {
|
||||||
|
schema_version: "assistant_data_need_graph_v1",
|
||||||
|
policy_owner: "assistantMcpDiscoveryDataNeedGraph",
|
||||||
|
subject_candidates: ["SVK"],
|
||||||
|
business_fact_family: "value_flow",
|
||||||
|
action_family: "net_value_flow",
|
||||||
|
aggregation_need: null,
|
||||||
|
time_scope_need: "explicit_period",
|
||||||
|
comparison_need: "incoming_vs_outgoing",
|
||||||
|
ranking_need: null,
|
||||||
|
proof_expectation: "coverage_checked_fact",
|
||||||
|
clarification_gaps: [],
|
||||||
|
decomposition_candidates: [],
|
||||||
|
forbidden_overclaim_flags: ["no_raw_model_claims"],
|
||||||
|
reason_codes: ["data_need_graph_built"]
|
||||||
|
},
|
||||||
|
turnMeaning: {
|
||||||
|
asked_action_family: "net_value_flow",
|
||||||
|
explicit_date_scope: "2020"
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(result.selected_chain_id).toBe("value_flow_comparison");
|
||||||
|
expect(result.evidence_plan.evidence_axes.required_axes).toContain("counterparty");
|
||||||
|
expect(result.evidence_plan.evidence_axes.provided_axes).toContain("business_entity");
|
||||||
|
expect(result.evidence_plan.evidence_axes.provided_axes).toContain("counterparty");
|
||||||
|
expect(result.evidence_plan.evidence_axes.missing_axes).not.toContain("counterparty");
|
||||||
|
});
|
||||||
|
|
||||||
|
it("does not coerce inventory item subjects into counterparty evidence axes", () => {
|
||||||
|
const result = planAssistantMcpDiscovery({
|
||||||
|
dataNeedGraph: {
|
||||||
|
schema_version: "assistant_data_need_graph_v1",
|
||||||
|
policy_owner: "assistantMcpDiscoveryDataNeedGraph",
|
||||||
|
subject_candidates: ["Столешница 600"],
|
||||||
|
business_fact_family: "inventory_purchase_provenance",
|
||||||
|
action_family: "purchase_provenance",
|
||||||
|
aggregation_need: null,
|
||||||
|
time_scope_need: null,
|
||||||
|
comparison_need: null,
|
||||||
|
ranking_need: null,
|
||||||
|
proof_expectation: "coverage_checked_fact",
|
||||||
|
clarification_gaps: [],
|
||||||
|
decomposition_candidates: [],
|
||||||
|
forbidden_overclaim_flags: ["no_raw_model_claims"],
|
||||||
|
reason_codes: ["data_need_graph_built"]
|
||||||
|
},
|
||||||
|
turnMeaning: {
|
||||||
|
asked_action_family: "purchase_provenance"
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(result.selected_chain_id).toBe("inventory_purchase_provenance");
|
||||||
|
expect(result.evidence_plan.evidence_axes.required_axes).toContain("item");
|
||||||
|
expect(result.evidence_plan.evidence_axes.provided_axes).toContain("business_entity");
|
||||||
|
expect(result.evidence_plan.evidence_axes.provided_axes).toContain("item");
|
||||||
|
expect(result.evidence_plan.evidence_axes.provided_axes).not.toContain("counterparty");
|
||||||
|
expect(result.evidence_plan.evidence_axes.missing_axes).not.toContain("item");
|
||||||
|
});
|
||||||
|
|
||||||
it("keeps representative graph-selected chains aligned with top catalog template matches", () => {
|
it("keeps representative graph-selected chains aligned with top catalog template matches", () => {
|
||||||
const graph = (
|
const graph = (
|
||||||
businessFactFamily: string,
|
businessFactFamily: string,
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue