Исправить компактные follow-up ответы по денежному итогу 1С
This commit is contained in:
parent
91a9dfa3a7
commit
f7314b0212
|
|
@ -0,0 +1,141 @@
|
||||||
|
{
|
||||||
|
"schema_version": "domain_truth_harness_spec_v1",
|
||||||
|
"scenario_id": "agent_cashflow_compact_display_variants_20260523",
|
||||||
|
"domain": "autonomy_business_answer_contract",
|
||||||
|
"title": "AGENT | Cashflow compact display variants",
|
||||||
|
"description": "Targeted AGENT replay for compact cashflow display modifiers: no counterparties, no breakdown, one-line and only-total follow-ups must preserve the 2020 company cashflow context.",
|
||||||
|
"bindings": {},
|
||||||
|
"steps": [
|
||||||
|
{
|
||||||
|
"step_id": "step_01_explicit_overview_anchor",
|
||||||
|
"title": "Create 2020 business overview context",
|
||||||
|
"question": "Теперь дай взрослый обзор за 2020 по компании: входящие, исходящие, нетто, топы, но банк в топах отдельно объясни как финансовый поток.",
|
||||||
|
"allowed_reply_types": ["partial_coverage", "factual_with_explanation", "factual"],
|
||||||
|
"required_answer_patterns_all": [
|
||||||
|
"47[\\s.]*628[\\s.]*853",
|
||||||
|
"43[\\s.]*763[\\s.]*351",
|
||||||
|
"3[\\s.]*865[\\s.]*501",
|
||||||
|
"12[\\s.]*792[\\s.]*194",
|
||||||
|
"12[\\s.]*093[\\s.]*465"
|
||||||
|
],
|
||||||
|
"forbidden_answer_patterns": ["runtime_", "planner_", "query_movements", "primitive"],
|
||||||
|
"criticality": "high",
|
||||||
|
"semantic_tags": ["business_overview", "context_anchor"]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"step_id": "step_02_no_counterparties",
|
||||||
|
"title": "No-counterparties means no breakdown, not exclusion",
|
||||||
|
"question": "сколько заработали деньгами без контрагентов?",
|
||||||
|
"allowed_reply_types": ["partial_coverage", "factual_with_explanation", "factual"],
|
||||||
|
"required_answer_patterns_all": [
|
||||||
|
"2020",
|
||||||
|
"47[\\s.]*628[\\s.]*853",
|
||||||
|
"43[\\s.]*763[\\s.]*351",
|
||||||
|
"3[\\s.]*865[\\s.]*501"
|
||||||
|
],
|
||||||
|
"forbidden_answer_patterns": [
|
||||||
|
"2026-05-23",
|
||||||
|
"получили\\s+0\\s*руб",
|
||||||
|
"нетто\\s+0\\s*руб",
|
||||||
|
"с исключением",
|
||||||
|
"исключением крупнейших",
|
||||||
|
"Комитет государственных услуг",
|
||||||
|
"Группа СВК",
|
||||||
|
"СБЕРБАНК",
|
||||||
|
"Что проверить дальше",
|
||||||
|
"runtime_",
|
||||||
|
"planner_",
|
||||||
|
"query_movements",
|
||||||
|
"primitive"
|
||||||
|
],
|
||||||
|
"criticality": "critical",
|
||||||
|
"semantic_tags": ["no_counterparty_breakdown", "display_modifier", "temporal_carryover"]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"step_id": "step_03_only_total",
|
||||||
|
"title": "Only total keeps compact cashflow",
|
||||||
|
"question": "только итог: пришло, ушло, нетто",
|
||||||
|
"allowed_reply_types": ["partial_coverage", "factual_with_explanation", "factual"],
|
||||||
|
"required_answer_patterns_all": [
|
||||||
|
"2020",
|
||||||
|
"47[\\s.]*628[\\s.]*853",
|
||||||
|
"43[\\s.]*763[\\s.]*351",
|
||||||
|
"3[\\s.]*865[\\s.]*501"
|
||||||
|
],
|
||||||
|
"forbidden_answer_patterns": [
|
||||||
|
"2026-05-23",
|
||||||
|
"получили\\s+0\\s*руб",
|
||||||
|
"нетто\\s+0\\s*руб",
|
||||||
|
"Комитет государственных услуг",
|
||||||
|
"Группа СВК",
|
||||||
|
"СБЕРБАНК",
|
||||||
|
"Что проверить дальше",
|
||||||
|
"runtime_",
|
||||||
|
"planner_",
|
||||||
|
"query_movements",
|
||||||
|
"primitive"
|
||||||
|
],
|
||||||
|
"criticality": "critical",
|
||||||
|
"semantic_tags": ["only_total", "display_modifier", "temporal_carryover"]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"step_id": "step_04_one_line_no_breakdown",
|
||||||
|
"title": "One-line no-breakdown stays cashflow",
|
||||||
|
"question": "дай одной строкой деньги без разбивки",
|
||||||
|
"allowed_reply_types": ["partial_coverage", "factual_with_explanation", "factual"],
|
||||||
|
"required_answer_patterns_all": [
|
||||||
|
"2020",
|
||||||
|
"47[\\s.]*628[\\s.]*853",
|
||||||
|
"43[\\s.]*763[\\s.]*351",
|
||||||
|
"3[\\s.]*865[\\s.]*501"
|
||||||
|
],
|
||||||
|
"forbidden_answer_patterns": [
|
||||||
|
"2026-05-23",
|
||||||
|
"получили\\s+0\\s*руб",
|
||||||
|
"нетто\\s+0\\s*руб",
|
||||||
|
"Комитет государственных услуг",
|
||||||
|
"Группа СВК",
|
||||||
|
"СБЕРБАНК",
|
||||||
|
"Что проверить дальше",
|
||||||
|
"runtime_",
|
||||||
|
"planner_",
|
||||||
|
"query_movements",
|
||||||
|
"primitive"
|
||||||
|
],
|
||||||
|
"criticality": "critical",
|
||||||
|
"semantic_tags": ["one_line", "no_breakdown", "display_modifier"]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"step_id": "step_05_no_details",
|
||||||
|
"title": "No-details wording remains direct totals",
|
||||||
|
"question": "сколько получили и заплатили без детализации",
|
||||||
|
"allowed_reply_types": ["partial_coverage", "factual_with_explanation", "factual"],
|
||||||
|
"required_answer_patterns_all": [
|
||||||
|
"2020",
|
||||||
|
"47[\\s.]*628[\\s.]*853",
|
||||||
|
"43[\\s.]*763[\\s.]*351",
|
||||||
|
"3[\\s.]*865[\\s.]*501"
|
||||||
|
],
|
||||||
|
"forbidden_answer_patterns": [
|
||||||
|
"2026-05-23",
|
||||||
|
"получили\\s+0\\s*руб",
|
||||||
|
"нетто\\s+0\\s*руб",
|
||||||
|
"Комитет государственных услуг",
|
||||||
|
"Группа СВК",
|
||||||
|
"СБЕРБАНК",
|
||||||
|
"Что проверить дальше",
|
||||||
|
"runtime_",
|
||||||
|
"planner_",
|
||||||
|
"query_movements",
|
||||||
|
"primitive"
|
||||||
|
],
|
||||||
|
"criticality": "critical",
|
||||||
|
"semantic_tags": ["no_details", "display_modifier", "direct_totals"]
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"acceptance": {
|
||||||
|
"min_score": 80,
|
||||||
|
"max_unresolved_p0": 0,
|
||||||
|
"require_all_critical_steps_pass": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -51,6 +51,106 @@ function mergeOrganizationIntoDiscoveryFollowupContext(followupContext, organiza
|
||||||
}
|
}
|
||||||
return base;
|
return base;
|
||||||
}
|
}
|
||||||
|
function compactLower(value) {
|
||||||
|
return String(value ?? "")
|
||||||
|
.toLowerCase()
|
||||||
|
.replace(/\s+/g, " ")
|
||||||
|
.trim();
|
||||||
|
}
|
||||||
|
function hasCompactCashflowFollowupSignal(text) {
|
||||||
|
const value = compactLower(text);
|
||||||
|
if (!value) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return /(?:\u0431\u0435\u0437\s+\u043a\u043e\u043d\u0442\u0440\u0430\u0433\u0435\u043d\u0442\p{L}*|\u0431\u0435\u0437\s+\u0440\u0430\u0437\u0431\u0438\u0432\p{L}*|\u0431\u0435\u0437\s+\u0440\u0430\u0437\u0440\u0435\u0437\p{L}*|\u0431\u0435\u0437\s+\u0434\u0435\u0442\u0430\u043b\p{L}*|\u043e\u0434\u043d\u043e\u0439\s+\u0441\u0442\u0440\u043e\u043a\p{L}*|\u0442\u043e\u043b\u044c\u043a\u043e\s+\u0438\u0442\u043e\u0433|\u043f\u0440\u0438\u0448\p{L}*[\s\S]{0,80}\u0443\u0448\p{L}*[\s\S]{0,80}\u043d\u0435\u0442\u0442\u043e|\u043f\u043e\u043b\u0443\u0447\p{L}*[\s\S]{0,80}\u0437\u0430\u043f\u043b\u0430\u0442\p{L}*[\s\S]{0,80}\u0431\u0435\u0437\s+\u0434\u0435\u0442\u0430\u043b)/iu.test(value);
|
||||||
|
}
|
||||||
|
function dateScopeToFilterWindow(dateScope) {
|
||||||
|
if (!dateScope) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
if (/^(?:19|20)\d{2}$/.test(dateScope)) {
|
||||||
|
return {
|
||||||
|
period_from: `${dateScope}-01-01`,
|
||||||
|
period_to: `${dateScope}-12-31`
|
||||||
|
};
|
||||||
|
}
|
||||||
|
if (/^(?:19|20)\d{2}-\d{2}-\d{2}$/.test(dateScope)) {
|
||||||
|
return { as_of_date: dateScope };
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
function looksLikeReliableOrganizationScope(value) {
|
||||||
|
const text = compactLower(value);
|
||||||
|
if (!text) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (/(?:\u0440\u0430\u0437\u0431\u0438\u0432|\u0440\u0430\u0437\u0440\u0435\u0437|\u0432\u0445\u043e\u0434\u044f\u0449|\u0438\u0441\u0445\u043e\u0434\u044f\u0449|\u043d\u0435\u0442\u0442\u043e|\u0442\u043e\u043f)/iu.test(text)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return /(?:^|[\s"'\u00ab])(?:\u043e\u043e\u043e|\u0438\u043f|\u043f\u0430\u043e|\u0430\u043e|\u0437\u0430\u043e)(?:[\s"'\u00bb]|$)|\u043e\u0431\u0449\u0435\u0441\u0442\u0432\p{L}*\s+\u0441\s+\u043e\u0433\u0440\u0430\u043d\u0438\u0447\p{L}*\s+\u043e\u0442\u0432\p{L}*|\b(?:llc|inc|corp)\b/iu.test(text);
|
||||||
|
}
|
||||||
|
function inferBusinessOverviewDiscoveryContextFromSessionItems(sessionItems, toNonEmptyString) {
|
||||||
|
for (let index = sessionItems.length - 1; index >= 0; index -= 1) {
|
||||||
|
const item = toRecordObject(sessionItems[index]);
|
||||||
|
if (toNonEmptyString(item?.role) !== "assistant") {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
const debug = toRecordObject(item?.debug);
|
||||||
|
const entryPoint = toRecordObject(debug?.assistant_mcp_discovery_entry_point_v1);
|
||||||
|
const turnInput = toRecordObject(entryPoint?.turn_input);
|
||||||
|
const dataNeedGraph = toRecordObject(turnInput?.data_need_graph);
|
||||||
|
if (toNonEmptyString(dataNeedGraph?.business_fact_family) !== "business_overview") {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
const turnMeaningRef = toRecordObject(turnInput?.turn_meaning_ref);
|
||||||
|
const filterWindow = dateScopeToFilterWindow(toNonEmptyString(turnMeaningRef?.explicit_date_scope));
|
||||||
|
if (!filterWindow) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
const previousFilters = { ...filterWindow };
|
||||||
|
const organization = toNonEmptyString(turnMeaningRef?.explicit_organization_scope);
|
||||||
|
if (looksLikeReliableOrganizationScope(organization)) {
|
||||||
|
previousFilters.organization = organization;
|
||||||
|
}
|
||||||
|
const rankingNeed = toNonEmptyString(dataNeedGraph?.ranking_need);
|
||||||
|
return {
|
||||||
|
previous_discovery_pilot_scope: "business_overview_route_template_v1",
|
||||||
|
previous_filters: previousFilters,
|
||||||
|
root_filters: { ...previousFilters },
|
||||||
|
previous_seeded_ranking_need: rankingNeed,
|
||||||
|
previous_intent: "business_overview",
|
||||||
|
root_intent: "business_overview"
|
||||||
|
};
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
function mergeBusinessOverviewDateContextForCompactCashflow(input) {
|
||||||
|
if (!hasCompactCashflowFollowupSignal(input.userMessage)) {
|
||||||
|
return input.followupContext;
|
||||||
|
}
|
||||||
|
const existingFilters = toRecordObject(input.followupContext?.previous_filters);
|
||||||
|
if (input.toNonEmptyString(existingFilters?.period_from) ||
|
||||||
|
input.toNonEmptyString(existingFilters?.period_to) ||
|
||||||
|
input.toNonEmptyString(existingFilters?.as_of_date)) {
|
||||||
|
return input.followupContext;
|
||||||
|
}
|
||||||
|
const inferred = inferBusinessOverviewDiscoveryContextFromSessionItems(input.sessionItems, input.toNonEmptyString);
|
||||||
|
if (!inferred) {
|
||||||
|
return input.followupContext;
|
||||||
|
}
|
||||||
|
return {
|
||||||
|
...inferred,
|
||||||
|
...(input.followupContext ?? {}),
|
||||||
|
previous_filters: {
|
||||||
|
...(toRecordObject(inferred.previous_filters) ?? {}),
|
||||||
|
...(toRecordObject(input.followupContext?.previous_filters) ?? {})
|
||||||
|
},
|
||||||
|
root_filters: {
|
||||||
|
...(toRecordObject(inferred.root_filters) ?? {}),
|
||||||
|
...(toRecordObject(input.followupContext?.root_filters) ?? {})
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
function hasSelectedObjectInventorySignal(text) {
|
function hasSelectedObjectInventorySignal(text) {
|
||||||
return /(?:по\s+выбранному\s+объекту|по\s+выбранной\s+позиции|по\s+этой\s+позиции|по\s+этому\s+товару|по\s+ним|selected\s+object)/iu.test(String(text ?? ""));
|
return /(?:по\s+выбранному\s+объекту|по\s+выбранной\s+позиции|по\s+этой\s+позиции|по\s+этому\s+товару|по\s+ним|selected\s+object)/iu.test(String(text ?? ""));
|
||||||
}
|
}
|
||||||
|
|
@ -215,9 +315,15 @@ async function buildAssistantAddressOrchestrationRuntime(input) {
|
||||||
const orchestrationContract = toRecordObject(orchestrationDecision.orchestrationContract);
|
const orchestrationContract = toRecordObject(orchestrationDecision.orchestrationContract);
|
||||||
const predecomposeContract = toRecordObject(addressPreDecompose.predecomposeContract);
|
const predecomposeContract = toRecordObject(addressPreDecompose.predecomposeContract);
|
||||||
const explicitPredecomposeOrganization = predecomposeOrganizationName(predecomposeContract, input.toNonEmptyString);
|
const explicitPredecomposeOrganization = predecomposeOrganizationName(predecomposeContract, input.toNonEmptyString);
|
||||||
const discoveryFollowupContext = mergeOrganizationIntoDiscoveryFollowupContext(followupContext, explicitPredecomposeOrganization
|
const discoveryFollowupContextWithOrganization = mergeOrganizationIntoDiscoveryFollowupContext(followupContext, explicitPredecomposeOrganization
|
||||||
? null
|
? null
|
||||||
: sessionOrganizationName(input.sessionOrganizationScope ?? null, input.toNonEmptyString));
|
: sessionOrganizationName(input.sessionOrganizationScope ?? null, input.toNonEmptyString));
|
||||||
|
const discoveryFollowupContext = mergeBusinessOverviewDateContextForCompactCashflow({
|
||||||
|
userMessage: input.userMessage,
|
||||||
|
followupContext: discoveryFollowupContextWithOrganization,
|
||||||
|
sessionItems: input.sessionItems,
|
||||||
|
toNonEmptyString: input.toNonEmptyString
|
||||||
|
});
|
||||||
const dialogContinuationContract = input.buildAddressDialogContinuationContractV2(input.userMessage, addressInputMessage, carryover, addressPreDecompose);
|
const dialogContinuationContract = input.buildAddressDialogContinuationContractV2(input.userMessage, addressInputMessage, carryover, addressPreDecompose);
|
||||||
const runDiscoveryEntryPoint = input.runMcpDiscoveryRuntimeEntryPoint ?? assistantMcpDiscoveryRuntimeEntryPoint_1.runAssistantMcpDiscoveryRuntimeEntryPoint;
|
const runDiscoveryEntryPoint = input.runMcpDiscoveryRuntimeEntryPoint ?? assistantMcpDiscoveryRuntimeEntryPoint_1.runAssistantMcpDiscoveryRuntimeEntryPoint;
|
||||||
let mcpDiscoveryRuntimeEntryPoint = null;
|
let mcpDiscoveryRuntimeEntryPoint = null;
|
||||||
|
|
|
||||||
|
|
@ -118,7 +118,7 @@ function hasBusinessOverviewDirectMoneyAnswerHint(input) {
|
||||||
if (/(?:\u043f\u043e\s+\u0434\u0435\u043d\p{L}*|\u0434\u0435\u043d\p{L}*)[\s\S]{0,80}(?:\u043f\u043b\u044e\u0441|\u043c\u0438\u043d\u0443\u0441|\u043d\u0435\u0442\u0442\u043e|\u043f\u0440\u0438\u0448\p{L}*|\u0443\u0448\p{L}*|\u043f\u043e\u043b\u0443\u0447\p{L}*|\u0437\u0430\u043f\u043b\u0430\u0442\p{L}*)|(?:\u043f\u043b\u044e\u0441|\u043c\u0438\u043d\u0443\u0441|\u043d\u0435\u0442\u0442\u043e)[\s\S]{0,80}(?:\u043f\u043e\s+\u0434\u0435\u043d\p{L}*|\u0434\u0435\u043d\p{L}*)/iu.test(text)) {
|
if (/(?:\u043f\u043e\s+\u0434\u0435\u043d\p{L}*|\u0434\u0435\u043d\p{L}*)[\s\S]{0,80}(?:\u043f\u043b\u044e\u0441|\u043c\u0438\u043d\u0443\u0441|\u043d\u0435\u0442\u0442\u043e|\u043f\u0440\u0438\u0448\p{L}*|\u0443\u0448\p{L}*|\u043f\u043e\u043b\u0443\u0447\p{L}*|\u0437\u0430\u043f\u043b\u0430\u0442\p{L}*)|(?:\u043f\u043b\u044e\u0441|\u043c\u0438\u043d\u0443\u0441|\u043d\u0435\u0442\u0442\u043e)[\s\S]{0,80}(?:\u043f\u043e\s+\u0434\u0435\u043d\p{L}*|\u0434\u0435\u043d\p{L}*)/iu.test(text)) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
if (/(?:\u043d\u0435\s+\u043e\u0431\u0437\u043e\u0440|\u043f\u0440\u043e\u0441\u0442\u043e\s+\u0434\u0435\u043d\p{L}+|\u0431\u0435\u0437\s+\u0442\u043e\u043f(?:\u043e\u0432|\u0430)?\b|\u0434\u0435\u043d\p{L}{0,20}\s+\u043d\u0435\u0442\u0442\u043e|\u043f\u0440\u0438\u0448\u043b\p{L}*[\s\S]{0,80}\u0443\u0448\u043b\p{L}*[\s\S]{0,80}\u043d\u0435\u0442\u0442\u043e|\u0432\u0445\u043e\u0434\u044f\u0449\p{L}*[\s\S]{0,80}\u0438\u0441\u0445\u043e\u0434\u044f\u0449\p{L}*[\s\S]{0,80}\u043d\u0435\u0442\u0442\u043e)/iu.test(text)) {
|
if (/(?:\u043d\u0435\s+\u043e\u0431\u0437\u043e\u0440|\u043f\u0440\u043e\u0441\u0442\u043e\s+\u0434\u0435\u043d\p{L}+|\u0431\u0435\u0437\s+\u0442\u043e\u043f(?:\u043e\u0432|\u0430)?\b|\u0431\u0435\u0437\s+\u043a\u043e\u043d\u0442\u0440\u0430\u0433\u0435\u043d\u0442\p{L}*|\u0431\u0435\u0437\s+\u0440\u0430\u0437\u0431\u0438\u0432\p{L}*|\u0431\u0435\u0437\s+\u0434\u0435\u0442\u0430\u043b\p{L}*|\u043e\u0434\u043d\u043e\u0439\s+\u0441\u0442\u0440\u043e\u043a\p{L}*|\u0442\u043e\u043b\u044c\u043a\u043e\s+\u0438\u0442\u043e\u0433|\u0434\u0435\u043d\p{L}{0,20}\s+\u043d\u0435\u0442\u0442\u043e|\u043f\u0440\u0438\u0448\u043b\p{L}*[\s\S]{0,80}\u0443\u0448\u043b\p{L}*[\s\S]{0,80}\u043d\u0435\u0442\u0442\u043e|\u0432\u0445\u043e\u0434\u044f\u0449\p{L}*[\s\S]{0,80}\u0438\u0441\u0445\u043e\u0434\u044f\u0449\p{L}*[\s\S]{0,80}\u043d\u0435\u0442\u0442\u043e)/iu.test(text)) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
return /(?:\u0441\u043a\u043e\u043b\u044c\u043a\u043e|\u0441\u043a\u043e\u043a\w*|how\s+much)[\s\S]{0,120}(?:\u0437\u0430\u0440\u0430\u0431\u043e\u0442|\u0432\u044b\u0440\u0443\u0447|\u0434\u0435\u043d\p{L}*|\u043f\u043e\u043b\u0443\u0447|\u043f\u043e\u0441\u0442\u0443\u043f\p{L}*)|(?:\u0437\u0430\u0440\u0430\u0431\u043e\u0442|\u0432\u044b\u0440\u0443\u0447)[\s\S]{0,120}(?:\u0441\u043a\u043e\u043b\u044c\u043a\u043e|\u0441\u043a\u043e\u043a\w*|\u0432\u0441\u0435\u0433\u043e|\u0432\u043e\u043e\u0431\u0449\u0435|(?:19|20)\d{2}|all\s+time)|(?:\u043a\u0430\u043a\u043e\u0439|\u043a\u0430\u043a\u0430\u044f|\u043a\u0430\u043a\u0438\u0435|which|what)[\s\S]{0,80}(?:\u0441\u0430\u043c\p{L}*|top|best|most)[\s\S]{0,80}(?:\u0434\u043e\u0445\u043e\u0434\u043d|\u0432\u044b\u0440\u0443\u0447|\u043e\u0431\u043e\u0440\u043e\u0442|revenue|turnover)[\s\S]{0,40}(?:\u0433\u043e\u0434|year)/iu.test(text);
|
return /(?:\u0441\u043a\u043e\u043b\u044c\u043a\u043e|\u0441\u043a\u043e\u043a\w*|how\s+much)[\s\S]{0,120}(?:\u0437\u0430\u0440\u0430\u0431\u043e\u0442|\u0432\u044b\u0440\u0443\u0447|\u0434\u0435\u043d\p{L}*|\u043f\u043e\u043b\u0443\u0447|\u043f\u043e\u0441\u0442\u0443\u043f\p{L}*)|(?:\u0437\u0430\u0440\u0430\u0431\u043e\u0442|\u0432\u044b\u0440\u0443\u0447)[\s\S]{0,120}(?:\u0441\u043a\u043e\u043b\u044c\u043a\u043e|\u0441\u043a\u043e\u043a\w*|\u0432\u0441\u0435\u0433\u043e|\u0432\u043e\u043e\u0431\u0449\u0435|(?:19|20)\d{2}|all\s+time)|(?:\u043a\u0430\u043a\u043e\u0439|\u043a\u0430\u043a\u0430\u044f|\u043a\u0430\u043a\u0438\u0435|which|what)[\s\S]{0,80}(?:\u0441\u0430\u043c\p{L}*|top|best|most)[\s\S]{0,80}(?:\u0434\u043e\u0445\u043e\u0434\u043d|\u0432\u044b\u0440\u0443\u0447|\u043e\u0431\u043e\u0440\u043e\u0442|revenue|turnover)[\s\S]{0,40}(?:\u0433\u043e\u0434|year)/iu.test(text);
|
||||||
|
|
|
||||||
|
|
@ -47,7 +47,7 @@ function requestsCompactCashflowAnswer(turnMeaning, graph) {
|
||||||
if (/(?:\u043f\u043e\s+\u0434\u0435\u043d\p{L}*|\u0434\u0435\u043d\p{L}*)[\s\S]{0,80}(?:\u043f\u043b\u044e\u0441|\u043c\u0438\u043d\u0443\u0441|\u043d\u0435\u0442\u0442\u043e|\u043f\u0440\u0438\u0448\p{L}*|\u0443\u0448\p{L}*|\u043f\u043e\u043b\u0443\u0447\p{L}*|\u0437\u0430\u043f\u043b\u0430\u0442\p{L}*)|(?:\u043f\u043b\u044e\u0441|\u043c\u0438\u043d\u0443\u0441|\u043d\u0435\u0442\u0442\u043e)[\s\S]{0,80}(?:\u043f\u043e\s+\u0434\u0435\u043d\p{L}*|\u0434\u0435\u043d\p{L}*)/iu.test(text)) {
|
if (/(?:\u043f\u043e\s+\u0434\u0435\u043d\p{L}*|\u0434\u0435\u043d\p{L}*)[\s\S]{0,80}(?:\u043f\u043b\u044e\u0441|\u043c\u0438\u043d\u0443\u0441|\u043d\u0435\u0442\u0442\u043e|\u043f\u0440\u0438\u0448\p{L}*|\u0443\u0448\p{L}*|\u043f\u043e\u043b\u0443\u0447\p{L}*|\u0437\u0430\u043f\u043b\u0430\u0442\p{L}*)|(?:\u043f\u043b\u044e\u0441|\u043c\u0438\u043d\u0443\u0441|\u043d\u0435\u0442\u0442\u043e)[\s\S]{0,80}(?:\u043f\u043e\s+\u0434\u0435\u043d\p{L}*|\u0434\u0435\u043d\p{L}*)/iu.test(text)) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
return /(?:\u043d\u0435\s+\u043e\u0431\u0437\u043e\u0440|\u043f\u0440\u043e\u0441\u0442\u043e\s+\u0434\u0435\u043d\p{L}+|\u0431\u0435\u0437\s+\u0442\u043e\u043f(?:\u043e\u0432|\u0430)?\b|\u0434\u0435\u043d\p{L}{0,20}\s+\u043d\u0435\u0442\u0442\u043e|\u043f\u043e\u043b\u0443\u0447\p{L}*[\s\S]{0,80}\u0437\u0430\u043f\u043b\u0430\u0442\p{L}*[\s\S]{0,80}\u043d\u0435\u0442\u0442\u043e|\u043f\u0440\u0438\u0448\u043b\p{L}*[\s\S]{0,80}\u0443\u0448\u043b\p{L}*[\s\S]{0,80}\u043d\u0435\u0442\u0442\u043e|(?:\u0441\u043a\u043e\u043b\u044c\u043a\u043e|\u0441\u043a\u043e\u043a\w*)[\s\S]{0,120}(?:\u0434\u0435\u043d\p{L}*|\u0437\u0430\u0440\u0430\u0431\u043e\u0442|\u043f\u043e\u043b\u0443\u0447|\u043f\u0440\u0438\u0448\u043b))/iu.test(text);
|
return /(?:\u043d\u0435\s+\u043e\u0431\u0437\u043e\u0440|\u043f\u0440\u043e\u0441\u0442\u043e\s+\u0434\u0435\u043d\p{L}+|\u0431\u0435\u0437\s+\u0442\u043e\u043f(?:\u043e\u0432|\u0430)?\b|\u0431\u0435\u0437\s+\u043a\u043e\u043d\u0442\u0440\u0430\u0433\u0435\u043d\u0442\p{L}*|\u0431\u0435\u0437\s+\u0440\u0430\u0437\u0431\u0438\u0432\p{L}*|\u0431\u0435\u0437\s+\u0440\u0430\u0437\u0440\u0435\u0437\p{L}*|\u0431\u0435\u0437\s+\u0434\u0435\u0442\u0430\u043b\p{L}*|\u043e\u0434\u043d\u043e\u0439\s+\u0441\u0442\u0440\u043e\u043a\p{L}*|\u0442\u043e\u043b\u044c\u043a\u043e\s+\u0438\u0442\u043e\u0433|\u0434\u0435\u043d\p{L}{0,20}\s+\u043d\u0435\u0442\u0442\u043e|\u043f\u043e\u043b\u0443\u0447\p{L}*[\s\S]{0,80}\u0437\u0430\u043f\u043b\u0430\u0442\p{L}*[\s\S]{0,80}\u043d\u0435\u0442\u0442\u043e|\u043f\u0440\u0438\u0448\u043b\p{L}*[\s\S]{0,80}\u0443\u0448\u043b\p{L}*[\s\S]{0,80}\u043d\u0435\u0442\u0442\u043e|(?:\u0441\u043a\u043e\u043b\u044c\u043a\u043e|\u0441\u043a\u043e\u043a\w*)[\s\S]{0,120}(?:\u0434\u0435\u043d\p{L}*|\u0437\u0430\u0440\u0430\u0431\u043e\u0442|\u043f\u043e\u043b\u0443\u0447|\u043f\u0440\u0438\u0448\u043b))/iu.test(text);
|
||||||
}
|
}
|
||||||
function requestsCashflowPolarityAnswer(turnMeaning, graph) {
|
function requestsCashflowPolarityAnswer(turnMeaning, graph) {
|
||||||
const text = normalizeQuestionText([
|
const text = normalizeQuestionText([
|
||||||
|
|
|
||||||
|
|
@ -274,6 +274,17 @@ function collectEntityCandidates(value) {
|
||||||
}
|
}
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
function collectReasonCodes(value) {
|
||||||
|
const result = [];
|
||||||
|
if (!Array.isArray(value)) {
|
||||||
|
const reason = toNonEmptyString(value);
|
||||||
|
return reason ? [reason] : result;
|
||||||
|
}
|
||||||
|
for (const item of value) {
|
||||||
|
pushUnique(result, item);
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
function collectPredecomposeEntities(predecompose) {
|
function collectPredecomposeEntities(predecompose) {
|
||||||
const entities = toRecordObject(predecompose?.entities);
|
const entities = toRecordObject(predecompose?.entities);
|
||||||
const organization = toNonEmptyString(entities?.organization);
|
const organization = toNonEmptyString(entities?.organization);
|
||||||
|
|
@ -1276,6 +1287,8 @@ function buildAssistantMcpDiscoveryTurnInput(input) {
|
||||||
const rawAction = toNonEmptyString(assistantTurnMeaning?.asked_action_family);
|
const rawAction = toNonEmptyString(assistantTurnMeaning?.asked_action_family);
|
||||||
const rawAggregationAxis = toNonEmptyString(assistantTurnMeaning?.asked_aggregation_axis);
|
const rawAggregationAxis = toNonEmptyString(assistantTurnMeaning?.asked_aggregation_axis);
|
||||||
const unsupported = toNonEmptyString(assistantTurnMeaning?.unsupported_but_understood_family);
|
const unsupported = toNonEmptyString(assistantTurnMeaning?.unsupported_but_understood_family);
|
||||||
|
const assistantTurnMeaningReasonCodes = collectReasonCodes(assistantTurnMeaning?.reason_codes);
|
||||||
|
const compactCashflowDisplayFollowupSignal = assistantTurnMeaningReasonCodes.includes("compact_cashflow_display_current_turn_signal");
|
||||||
const broadBusinessEvaluationUnsupported = unsupported === "broad_business_evaluation";
|
const broadBusinessEvaluationUnsupported = unsupported === "broad_business_evaluation";
|
||||||
const seededBusinessOverviewSignal = broadBusinessEvaluationUnsupported ||
|
const seededBusinessOverviewSignal = broadBusinessEvaluationUnsupported ||
|
||||||
rawDomain === "business_summary" ||
|
rawDomain === "business_summary" ||
|
||||||
|
|
@ -1909,6 +1922,7 @@ function buildAssistantMcpDiscoveryTurnInput(input) {
|
||||||
(valueFlowOrganizationStaysScope && (Boolean(followupSeed.rankingNeed) || bidirectionalValueFlowSignal))));
|
(valueFlowOrganizationStaysScope && (Boolean(followupSeed.rankingNeed) || bidirectionalValueFlowSignal))));
|
||||||
const suppressNegatedTaxOnlyDateScope = Boolean(businessOverviewSignal && negatedTaxDateScopeOnlySignal);
|
const suppressNegatedTaxOnlyDateScope = Boolean(businessOverviewSignal && negatedTaxDateScopeOnlySignal);
|
||||||
const topicSwitchSuppressesFollowupScope = Boolean(rawTopicSwitchSignal &&
|
const topicSwitchSuppressesFollowupScope = Boolean(rawTopicSwitchSignal &&
|
||||||
|
!compactCashflowDisplayFollowupSignal &&
|
||||||
(rawMetadataSignal ||
|
(rawMetadataSignal ||
|
||||||
businessOverviewSignal ||
|
businessOverviewSignal ||
|
||||||
rawEntitySearchOverridesStaleScope ||
|
rawEntitySearchOverridesStaleScope ||
|
||||||
|
|
|
||||||
|
|
@ -139,14 +139,15 @@ function hasCompactOrganizationCashflowDisplaySignal(text) {
|
||||||
if (!normalized) {
|
if (!normalized) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
const hasCompactCue = /(?:\u043a\u043e\u0440\u043e\u0442\u043a\w*|\u043d\u0435\s+\u043e\u0431\u0437\u043e\u0440|\u043f\u0440\u043e\u0441\u0442\u043e\s+\u0434\u0435\u043d\p{L}*|\u0431\u0435\u0437\s+\u0442\u043e\u043f(?:\u043e\u0432|\u0430)?\b|\u0431\u0435\u0437\s+\u0440\u0430\u0437\u0431\u0438\u0432\p{L}*)/iu.test(normalized);
|
const hasCompactCue = /(?:\u043a\u043e\u0440\u043e\u0442\u043a\w*|\u043e\u0434\u043d\u043e\u0439\s+\u0441\u0442\u0440\u043e\u043a\p{L}*|\u0442\u043e\u043b\u044c\u043a\u043e\s+\u0438\u0442\u043e\u0433\p{L}*|\u043d\u0435\s+\u043e\u0431\u0437\u043e\u0440|\u043f\u0440\u043e\u0441\u0442\u043e\s+\u0434\u0435\u043d\p{L}*|\u0431\u0435\u0437\s+\u0442\u043e\u043f(?:\u043e\u0432|\u0430)?\b|\u0431\u0435\u0437\s+\u0441\u043f\u0438\u0441(?:\u043a\p{L}*)?|\u0431\u0435\u0437\s+\u0434\u0435\u0442\u0430\u043b\p{L}*|\u0431\u0435\u0437\s+\u0440\u0430\u0437\u0431\u0438\u0432\p{L}*|\u0431\u0435\u0437\s+\u0440\u0430\u0437\u0440\u0435\u0437\p{L}*|\u0431\u0435\u0437\s+\u043a\u043e\u043d\u0442\u0440\u0430\u0433\u0435\u043d\p{L}*)/iu.test(normalized);
|
||||||
if (!hasCompactCue) {
|
if (!hasCompactCue) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
const hasMoneyCue = /(?:\u0434\u0435\u043d\p{L}*|\u0434\u0435\u043d\u0435\u0436\p{L}*|\u043f\u0440\u0438\u0448\p{L}*|\u0443\u0448\p{L}*|\u043f\u043e\u043b\u0443\u0447\p{L}*|\u0437\u0430\u043f\u043b\u0430\u0442\p{L}*|\u0441\u043f\u0438\u0441\u0430\p{L}*|\u043d\u0435\u0442\u0442\u043e|cash|money|incoming|outgoing|net)/iu.test(normalized);
|
const hasMoneyCue = /(?:\u0434\u0435\u043d\p{L}*|\u0434\u0435\u043d\u0435\u0436\p{L}*|\u043f\u0440\u0438\u0448\p{L}*|\u0443\u0448\p{L}*|\u043f\u043e\u043b\u0443\u0447\p{L}*|\u0437\u0430\u043f\u043b\u0430\u0442\p{L}*|\u0441\u043f\u0438\u0441\u0430\p{L}*|\u043d\u0435\u0442\u0442\u043e|cash|money|incoming|outgoing|net)/iu.test(normalized);
|
||||||
const hasOrganizationEarningsCue = /(?:\u0441\u043a\u043e\u043b\u044c\u043a\u043e|\u0441\u043a\u043e\u043a\w*|\u0437\u0430\u0440\u0430\u0431\u043e\u0442\p{L}*|\u043f\u0440\u0438\u0448\p{L}*|\u0443\u0448\p{L}*|\u043f\u043e\u043b\u0443\u0447\p{L}*|\u0437\u0430\u043f\u043b\u0430\u0442\p{L}*|\u0434\u0435\u043d\u0435\u0436\p{L}*\s+\u043d\u0435\u0442\u0442\u043e|how\s+much|earned|received|paid)/iu.test(normalized);
|
const hasOrganizationEarningsCue = /(?:\u0434\u0430\u0439|\u043f\u043e\u043a\u0430\u0436\p{L}*|\u0438\u0442\u043e\u0433\p{L}*|\u0441\u043a\u043e\u043b\u044c\u043a\u043e|\u0441\u043a\u043e\u043a\w*|\u0437\u0430\u0440\u0430\u0431\u043e\u0442\p{L}*|\u043f\u0440\u0438\u0448\p{L}*|\u0443\u0448\p{L}*|\u043f\u043e\u043b\u0443\u0447\p{L}*|\u0437\u0430\u043f\u043b\u0430\u0442\p{L}*|\u0434\u0435\u043d\u0435\u0436\p{L}*\s+\u043d\u0435\u0442\u0442\u043e|how\s+much|show|give|earned|received|paid)/iu.test(normalized);
|
||||||
const hasExplicitExclusionCue = /(?:\u0438\u0441\u043a\u043b\u044e\u0447\p{L}*|\u0443\u0431\u0435\u0440\p{L}*|\u043a\u0440\u043e\u043c\u0435|exclude|excluding|without)[\s\S]{0,80}(?:\u043a\u0440\u0443\u043f\u043d\p{L}*|\u043a\u043e\u043d\u0442\u0440\u0430\u0433\u0435\u043d\p{L}*|\u043a\u043b\u0438\u0435\u043d\u0442\p{L}*|\u043f\u043e\u0441\u0442\u0430\u0432\u0449\u0438\u043a\p{L}*|\u043e\u043f\u0435\u0440\u0430\u0446\p{L}*|counterpart|customer|supplier|operation)/iu.test(normalized);
|
const hasExplicitExclusionCue = /(?:\u0438\u0441\u043a\u043b\u044e\u0447\p{L}*|\u0443\u0431\u0435\u0440\p{L}*|\u043a\u0440\u043e\u043c\u0435|exclude|excluding|without)[\s\S]{0,80}(?:\u043a\u0440\u0443\u043f\u043d\p{L}*|\u043a\u043e\u043d\u0442\u0440\u0430\u0433\u0435\u043d\p{L}*|\u043a\u043b\u0438\u0435\u043d\u0442\p{L}*|\u043f\u043e\u0441\u0442\u0430\u0432\u0449\u0438\u043a\p{L}*|\u043e\u043f\u0435\u0440\u0430\u0446\p{L}*|counterpart|customer|supplier|operation)/iu.test(normalized);
|
||||||
return hasMoneyCue && hasOrganizationEarningsCue && !hasExplicitExclusionCue;
|
const hasSpecificCounterpartyScope = detectScopedCounterpartyEntity(normalized) !== null;
|
||||||
|
return hasMoneyCue && hasOrganizationEarningsCue && !hasExplicitExclusionCue && !hasSpecificCounterpartyScope;
|
||||||
}
|
}
|
||||||
function detectScopedCounterpartyEntity(text) {
|
function detectScopedCounterpartyEntity(text) {
|
||||||
const patterns = [
|
const patterns = [
|
||||||
|
|
@ -366,8 +367,8 @@ function createAssistantTurnMeaningPolicy(deps = {}) {
|
||||||
const joinedText = fallbackCompactWhitespace(`${rawText} ${effectiveText}`);
|
const joinedText = fallbackCompactWhitespace(`${rawText} ${effectiveText}`);
|
||||||
const compactOrganizationCashflowDisplay = hasCompactOrganizationCashflowDisplaySignal(rawText);
|
const compactOrganizationCashflowDisplay = hasCompactOrganizationCashflowDisplaySignal(rawText);
|
||||||
const supportedIntent = compactOrganizationCashflowDisplay ? null : detectSupportedIntent(joinedText, deps);
|
const supportedIntent = compactOrganizationCashflowDisplay ? null : detectSupportedIntent(joinedText, deps);
|
||||||
const counterpartyBidirectionalValueFlow = detectCounterpartyBidirectionalValueFlowFamily(joinedText);
|
const counterpartyBidirectionalValueFlow = compactOrganizationCashflowDisplay ? null : detectCounterpartyBidirectionalValueFlowFamily(joinedText);
|
||||||
const counterpartyTurnover = detectCounterpartyTurnoverFamily(joinedText);
|
const counterpartyTurnover = compactOrganizationCashflowDisplay ? null : detectCounterpartyTurnoverFamily(joinedText);
|
||||||
const selectedObjectInventoryExact = hasSelectedObjectInventoryExactSignal(joinedText);
|
const selectedObjectInventoryExact = hasSelectedObjectInventoryExactSignal(joinedText);
|
||||||
const broadBusinessEvaluation = compactOrganizationCashflowDisplay
|
const broadBusinessEvaluation = compactOrganizationCashflowDisplay
|
||||||
? { family: "broad_business_evaluation" }
|
? { family: "broad_business_evaluation" }
|
||||||
|
|
|
||||||
|
|
@ -138,6 +138,122 @@ function mergeOrganizationIntoDiscoveryFollowupContext(
|
||||||
return base;
|
return base;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function compactLower(value: unknown): string {
|
||||||
|
return String(value ?? "")
|
||||||
|
.toLowerCase()
|
||||||
|
.replace(/\s+/g, " ")
|
||||||
|
.trim();
|
||||||
|
}
|
||||||
|
|
||||||
|
function hasCompactCashflowFollowupSignal(text: string | null): boolean {
|
||||||
|
const value = compactLower(text);
|
||||||
|
if (!value) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return /(?:\u0431\u0435\u0437\s+\u043a\u043e\u043d\u0442\u0440\u0430\u0433\u0435\u043d\u0442\p{L}*|\u0431\u0435\u0437\s+\u0440\u0430\u0437\u0431\u0438\u0432\p{L}*|\u0431\u0435\u0437\s+\u0440\u0430\u0437\u0440\u0435\u0437\p{L}*|\u0431\u0435\u0437\s+\u0434\u0435\u0442\u0430\u043b\p{L}*|\u043e\u0434\u043d\u043e\u0439\s+\u0441\u0442\u0440\u043e\u043a\p{L}*|\u0442\u043e\u043b\u044c\u043a\u043e\s+\u0438\u0442\u043e\u0433|\u043f\u0440\u0438\u0448\p{L}*[\s\S]{0,80}\u0443\u0448\p{L}*[\s\S]{0,80}\u043d\u0435\u0442\u0442\u043e|\u043f\u043e\u043b\u0443\u0447\p{L}*[\s\S]{0,80}\u0437\u0430\u043f\u043b\u0430\u0442\p{L}*[\s\S]{0,80}\u0431\u0435\u0437\s+\u0434\u0435\u0442\u0430\u043b)/iu.test(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
function dateScopeToFilterWindow(dateScope: string | null): Record<string, string> | null {
|
||||||
|
if (!dateScope) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
if (/^(?:19|20)\d{2}$/.test(dateScope)) {
|
||||||
|
return {
|
||||||
|
period_from: `${dateScope}-01-01`,
|
||||||
|
period_to: `${dateScope}-12-31`
|
||||||
|
};
|
||||||
|
}
|
||||||
|
if (/^(?:19|20)\d{2}-\d{2}-\d{2}$/.test(dateScope)) {
|
||||||
|
return { as_of_date: dateScope };
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
function looksLikeReliableOrganizationScope(value: string | null): boolean {
|
||||||
|
const text = compactLower(value);
|
||||||
|
if (!text) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (/(?:\u0440\u0430\u0437\u0431\u0438\u0432|\u0440\u0430\u0437\u0440\u0435\u0437|\u0432\u0445\u043e\u0434\u044f\u0449|\u0438\u0441\u0445\u043e\u0434\u044f\u0449|\u043d\u0435\u0442\u0442\u043e|\u0442\u043e\u043f)/iu.test(text)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return /(?:^|[\s"'\u00ab])(?:\u043e\u043e\u043e|\u0438\u043f|\u043f\u0430\u043e|\u0430\u043e|\u0437\u0430\u043e)(?:[\s"'\u00bb]|$)|\u043e\u0431\u0449\u0435\u0441\u0442\u0432\p{L}*\s+\u0441\s+\u043e\u0433\u0440\u0430\u043d\u0438\u0447\p{L}*\s+\u043e\u0442\u0432\p{L}*|\b(?:llc|inc|corp)\b/iu.test(text);
|
||||||
|
}
|
||||||
|
|
||||||
|
function inferBusinessOverviewDiscoveryContextFromSessionItems(
|
||||||
|
sessionItems: unknown[],
|
||||||
|
toNonEmptyString: BuildAssistantAddressOrchestrationRuntimeInput["toNonEmptyString"]
|
||||||
|
): Record<string, unknown> | null {
|
||||||
|
for (let index = sessionItems.length - 1; index >= 0; index -= 1) {
|
||||||
|
const item = toRecordObject(sessionItems[index]);
|
||||||
|
if (toNonEmptyString(item?.role) !== "assistant") {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
const debug = toRecordObject(item?.debug);
|
||||||
|
const entryPoint = toRecordObject(debug?.assistant_mcp_discovery_entry_point_v1);
|
||||||
|
const turnInput = toRecordObject(entryPoint?.turn_input);
|
||||||
|
const dataNeedGraph = toRecordObject(turnInput?.data_need_graph);
|
||||||
|
if (toNonEmptyString(dataNeedGraph?.business_fact_family) !== "business_overview") {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
const turnMeaningRef = toRecordObject(turnInput?.turn_meaning_ref);
|
||||||
|
const filterWindow = dateScopeToFilterWindow(toNonEmptyString(turnMeaningRef?.explicit_date_scope));
|
||||||
|
if (!filterWindow) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
const previousFilters: Record<string, unknown> = { ...filterWindow };
|
||||||
|
const organization = toNonEmptyString(turnMeaningRef?.explicit_organization_scope);
|
||||||
|
if (looksLikeReliableOrganizationScope(organization)) {
|
||||||
|
previousFilters.organization = organization;
|
||||||
|
}
|
||||||
|
const rankingNeed = toNonEmptyString(dataNeedGraph?.ranking_need);
|
||||||
|
return {
|
||||||
|
previous_discovery_pilot_scope: "business_overview_route_template_v1",
|
||||||
|
previous_filters: previousFilters,
|
||||||
|
root_filters: { ...previousFilters },
|
||||||
|
previous_seeded_ranking_need: rankingNeed,
|
||||||
|
previous_intent: "business_overview",
|
||||||
|
root_intent: "business_overview"
|
||||||
|
};
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
function mergeBusinessOverviewDateContextForCompactCashflow(input: {
|
||||||
|
userMessage: string;
|
||||||
|
followupContext: Record<string, unknown> | null;
|
||||||
|
sessionItems: unknown[];
|
||||||
|
toNonEmptyString: BuildAssistantAddressOrchestrationRuntimeInput["toNonEmptyString"];
|
||||||
|
}): Record<string, unknown> | null {
|
||||||
|
if (!hasCompactCashflowFollowupSignal(input.userMessage)) {
|
||||||
|
return input.followupContext;
|
||||||
|
}
|
||||||
|
const existingFilters = toRecordObject(input.followupContext?.previous_filters);
|
||||||
|
if (
|
||||||
|
input.toNonEmptyString(existingFilters?.period_from) ||
|
||||||
|
input.toNonEmptyString(existingFilters?.period_to) ||
|
||||||
|
input.toNonEmptyString(existingFilters?.as_of_date)
|
||||||
|
) {
|
||||||
|
return input.followupContext;
|
||||||
|
}
|
||||||
|
const inferred = inferBusinessOverviewDiscoveryContextFromSessionItems(input.sessionItems, input.toNonEmptyString);
|
||||||
|
if (!inferred) {
|
||||||
|
return input.followupContext;
|
||||||
|
}
|
||||||
|
return {
|
||||||
|
...inferred,
|
||||||
|
...(input.followupContext ?? {}),
|
||||||
|
previous_filters: {
|
||||||
|
...(toRecordObject(inferred.previous_filters) ?? {}),
|
||||||
|
...(toRecordObject(input.followupContext?.previous_filters) ?? {})
|
||||||
|
},
|
||||||
|
root_filters: {
|
||||||
|
...(toRecordObject(inferred.root_filters) ?? {}),
|
||||||
|
...(toRecordObject(input.followupContext?.root_filters) ?? {})
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
function hasSelectedObjectInventorySignal(text: string | null): boolean {
|
function hasSelectedObjectInventorySignal(text: string | null): boolean {
|
||||||
return /(?:по\s+выбранному\s+объекту|по\s+выбранной\s+позиции|по\s+этой\s+позиции|по\s+этому\s+товару|по\s+ним|selected\s+object)/iu.test(
|
return /(?:по\s+выбранному\s+объекту|по\s+выбранной\s+позиции|по\s+этой\s+позиции|по\s+этому\s+товару|по\s+ним|selected\s+object)/iu.test(
|
||||||
String(text ?? "")
|
String(text ?? "")
|
||||||
|
|
@ -397,12 +513,18 @@ export async function buildAssistantAddressOrchestrationRuntime(
|
||||||
const orchestrationContract = toRecordObject(orchestrationDecision.orchestrationContract);
|
const orchestrationContract = toRecordObject(orchestrationDecision.orchestrationContract);
|
||||||
const predecomposeContract = toRecordObject(addressPreDecompose.predecomposeContract);
|
const predecomposeContract = toRecordObject(addressPreDecompose.predecomposeContract);
|
||||||
const explicitPredecomposeOrganization = predecomposeOrganizationName(predecomposeContract, input.toNonEmptyString);
|
const explicitPredecomposeOrganization = predecomposeOrganizationName(predecomposeContract, input.toNonEmptyString);
|
||||||
const discoveryFollowupContext = mergeOrganizationIntoDiscoveryFollowupContext(
|
const discoveryFollowupContextWithOrganization = mergeOrganizationIntoDiscoveryFollowupContext(
|
||||||
followupContext,
|
followupContext,
|
||||||
explicitPredecomposeOrganization
|
explicitPredecomposeOrganization
|
||||||
? null
|
? null
|
||||||
: sessionOrganizationName(input.sessionOrganizationScope ?? null, input.toNonEmptyString)
|
: sessionOrganizationName(input.sessionOrganizationScope ?? null, input.toNonEmptyString)
|
||||||
);
|
);
|
||||||
|
const discoveryFollowupContext = mergeBusinessOverviewDateContextForCompactCashflow({
|
||||||
|
userMessage: input.userMessage,
|
||||||
|
followupContext: discoveryFollowupContextWithOrganization,
|
||||||
|
sessionItems: input.sessionItems,
|
||||||
|
toNonEmptyString: input.toNonEmptyString
|
||||||
|
});
|
||||||
const dialogContinuationContract = input.buildAddressDialogContinuationContractV2(
|
const dialogContinuationContract = input.buildAddressDialogContinuationContractV2(
|
||||||
input.userMessage,
|
input.userMessage,
|
||||||
addressInputMessage,
|
addressInputMessage,
|
||||||
|
|
|
||||||
|
|
@ -184,7 +184,7 @@ function hasBusinessOverviewDirectMoneyAnswerHint(input: {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
if (
|
if (
|
||||||
/(?:\u043d\u0435\s+\u043e\u0431\u0437\u043e\u0440|\u043f\u0440\u043e\u0441\u0442\u043e\s+\u0434\u0435\u043d\p{L}+|\u0431\u0435\u0437\s+\u0442\u043e\u043f(?:\u043e\u0432|\u0430)?\b|\u0434\u0435\u043d\p{L}{0,20}\s+\u043d\u0435\u0442\u0442\u043e|\u043f\u0440\u0438\u0448\u043b\p{L}*[\s\S]{0,80}\u0443\u0448\u043b\p{L}*[\s\S]{0,80}\u043d\u0435\u0442\u0442\u043e|\u0432\u0445\u043e\u0434\u044f\u0449\p{L}*[\s\S]{0,80}\u0438\u0441\u0445\u043e\u0434\u044f\u0449\p{L}*[\s\S]{0,80}\u043d\u0435\u0442\u0442\u043e)/iu.test(text)
|
/(?:\u043d\u0435\s+\u043e\u0431\u0437\u043e\u0440|\u043f\u0440\u043e\u0441\u0442\u043e\s+\u0434\u0435\u043d\p{L}+|\u0431\u0435\u0437\s+\u0442\u043e\u043f(?:\u043e\u0432|\u0430)?\b|\u0431\u0435\u0437\s+\u043a\u043e\u043d\u0442\u0440\u0430\u0433\u0435\u043d\u0442\p{L}*|\u0431\u0435\u0437\s+\u0440\u0430\u0437\u0431\u0438\u0432\p{L}*|\u0431\u0435\u0437\s+\u0434\u0435\u0442\u0430\u043b\p{L}*|\u043e\u0434\u043d\u043e\u0439\s+\u0441\u0442\u0440\u043e\u043a\p{L}*|\u0442\u043e\u043b\u044c\u043a\u043e\s+\u0438\u0442\u043e\u0433|\u0434\u0435\u043d\p{L}{0,20}\s+\u043d\u0435\u0442\u0442\u043e|\u043f\u0440\u0438\u0448\u043b\p{L}*[\s\S]{0,80}\u0443\u0448\u043b\p{L}*[\s\S]{0,80}\u043d\u0435\u0442\u0442\u043e|\u0432\u0445\u043e\u0434\u044f\u0449\p{L}*[\s\S]{0,80}\u0438\u0441\u0445\u043e\u0434\u044f\u0449\p{L}*[\s\S]{0,80}\u043d\u0435\u0442\u0442\u043e)/iu.test(text)
|
||||||
) {
|
) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -80,7 +80,7 @@ function requestsCompactCashflowAnswer(
|
||||||
) {
|
) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
return /(?:\u043d\u0435\s+\u043e\u0431\u0437\u043e\u0440|\u043f\u0440\u043e\u0441\u0442\u043e\s+\u0434\u0435\u043d\p{L}+|\u0431\u0435\u0437\s+\u0442\u043e\u043f(?:\u043e\u0432|\u0430)?\b|\u0434\u0435\u043d\p{L}{0,20}\s+\u043d\u0435\u0442\u0442\u043e|\u043f\u043e\u043b\u0443\u0447\p{L}*[\s\S]{0,80}\u0437\u0430\u043f\u043b\u0430\u0442\p{L}*[\s\S]{0,80}\u043d\u0435\u0442\u0442\u043e|\u043f\u0440\u0438\u0448\u043b\p{L}*[\s\S]{0,80}\u0443\u0448\u043b\p{L}*[\s\S]{0,80}\u043d\u0435\u0442\u0442\u043e|(?:\u0441\u043a\u043e\u043b\u044c\u043a\u043e|\u0441\u043a\u043e\u043a\w*)[\s\S]{0,120}(?:\u0434\u0435\u043d\p{L}*|\u0437\u0430\u0440\u0430\u0431\u043e\u0442|\u043f\u043e\u043b\u0443\u0447|\u043f\u0440\u0438\u0448\u043b))/iu.test(
|
return /(?:\u043d\u0435\s+\u043e\u0431\u0437\u043e\u0440|\u043f\u0440\u043e\u0441\u0442\u043e\s+\u0434\u0435\u043d\p{L}+|\u0431\u0435\u0437\s+\u0442\u043e\u043f(?:\u043e\u0432|\u0430)?\b|\u0431\u0435\u0437\s+\u043a\u043e\u043d\u0442\u0440\u0430\u0433\u0435\u043d\u0442\p{L}*|\u0431\u0435\u0437\s+\u0440\u0430\u0437\u0431\u0438\u0432\p{L}*|\u0431\u0435\u0437\s+\u0440\u0430\u0437\u0440\u0435\u0437\p{L}*|\u0431\u0435\u0437\s+\u0434\u0435\u0442\u0430\u043b\p{L}*|\u043e\u0434\u043d\u043e\u0439\s+\u0441\u0442\u0440\u043e\u043a\p{L}*|\u0442\u043e\u043b\u044c\u043a\u043e\s+\u0438\u0442\u043e\u0433|\u0434\u0435\u043d\p{L}{0,20}\s+\u043d\u0435\u0442\u0442\u043e|\u043f\u043e\u043b\u0443\u0447\p{L}*[\s\S]{0,80}\u0437\u0430\u043f\u043b\u0430\u0442\p{L}*[\s\S]{0,80}\u043d\u0435\u0442\u0442\u043e|\u043f\u0440\u0438\u0448\u043b\p{L}*[\s\S]{0,80}\u0443\u0448\u043b\p{L}*[\s\S]{0,80}\u043d\u0435\u0442\u0442\u043e|(?:\u0441\u043a\u043e\u043b\u044c\u043a\u043e|\u0441\u043a\u043e\u043a\w*)[\s\S]{0,120}(?:\u0434\u0435\u043d\p{L}*|\u0437\u0430\u0440\u0430\u0431\u043e\u0442|\u043f\u043e\u043b\u0443\u0447|\u043f\u0440\u0438\u0448\u043b))/iu.test(
|
||||||
text
|
text
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -353,6 +353,18 @@ function collectEntityCandidates(value: unknown): string[] {
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function collectReasonCodes(value: unknown): string[] {
|
||||||
|
const result: string[] = [];
|
||||||
|
if (!Array.isArray(value)) {
|
||||||
|
const reason = toNonEmptyString(value);
|
||||||
|
return reason ? [reason] : result;
|
||||||
|
}
|
||||||
|
for (const item of value) {
|
||||||
|
pushUnique(result, item);
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
function collectPredecomposeEntities(predecompose: Record<string, unknown> | null): {
|
function collectPredecomposeEntities(predecompose: Record<string, unknown> | null): {
|
||||||
counterparty: string | null;
|
counterparty: string | null;
|
||||||
organization: string | null;
|
organization: string | null;
|
||||||
|
|
@ -1782,6 +1794,10 @@ export function buildAssistantMcpDiscoveryTurnInput(
|
||||||
const rawAction = toNonEmptyString(assistantTurnMeaning?.asked_action_family);
|
const rawAction = toNonEmptyString(assistantTurnMeaning?.asked_action_family);
|
||||||
const rawAggregationAxis = toNonEmptyString(assistantTurnMeaning?.asked_aggregation_axis);
|
const rawAggregationAxis = toNonEmptyString(assistantTurnMeaning?.asked_aggregation_axis);
|
||||||
const unsupported = toNonEmptyString(assistantTurnMeaning?.unsupported_but_understood_family);
|
const unsupported = toNonEmptyString(assistantTurnMeaning?.unsupported_but_understood_family);
|
||||||
|
const assistantTurnMeaningReasonCodes = collectReasonCodes(assistantTurnMeaning?.reason_codes);
|
||||||
|
const compactCashflowDisplayFollowupSignal = assistantTurnMeaningReasonCodes.includes(
|
||||||
|
"compact_cashflow_display_current_turn_signal"
|
||||||
|
);
|
||||||
const broadBusinessEvaluationUnsupported = unsupported === "broad_business_evaluation";
|
const broadBusinessEvaluationUnsupported = unsupported === "broad_business_evaluation";
|
||||||
const seededBusinessOverviewSignal =
|
const seededBusinessOverviewSignal =
|
||||||
broadBusinessEvaluationUnsupported ||
|
broadBusinessEvaluationUnsupported ||
|
||||||
|
|
@ -2553,6 +2569,7 @@ export function buildAssistantMcpDiscoveryTurnInput(
|
||||||
const suppressNegatedTaxOnlyDateScope = Boolean(businessOverviewSignal && negatedTaxDateScopeOnlySignal);
|
const suppressNegatedTaxOnlyDateScope = Boolean(businessOverviewSignal && negatedTaxDateScopeOnlySignal);
|
||||||
const topicSwitchSuppressesFollowupScope = Boolean(
|
const topicSwitchSuppressesFollowupScope = Boolean(
|
||||||
rawTopicSwitchSignal &&
|
rawTopicSwitchSignal &&
|
||||||
|
!compactCashflowDisplayFollowupSignal &&
|
||||||
(rawMetadataSignal ||
|
(rawMetadataSignal ||
|
||||||
businessOverviewSignal ||
|
businessOverviewSignal ||
|
||||||
rawEntitySearchOverridesStaleScope ||
|
rawEntitySearchOverridesStaleScope ||
|
||||||
|
|
|
||||||
|
|
@ -145,7 +145,7 @@ function hasCompactOrganizationCashflowDisplaySignal(text) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
const hasCompactCue =
|
const hasCompactCue =
|
||||||
/(?:\u043a\u043e\u0440\u043e\u0442\u043a\w*|\u043d\u0435\s+\u043e\u0431\u0437\u043e\u0440|\u043f\u0440\u043e\u0441\u0442\u043e\s+\u0434\u0435\u043d\p{L}*|\u0431\u0435\u0437\s+\u0442\u043e\u043f(?:\u043e\u0432|\u0430)?\b|\u0431\u0435\u0437\s+\u0440\u0430\u0437\u0431\u0438\u0432\p{L}*)/iu.test(
|
/(?:\u043a\u043e\u0440\u043e\u0442\u043a\w*|\u043e\u0434\u043d\u043e\u0439\s+\u0441\u0442\u0440\u043e\u043a\p{L}*|\u0442\u043e\u043b\u044c\u043a\u043e\s+\u0438\u0442\u043e\u0433\p{L}*|\u043d\u0435\s+\u043e\u0431\u0437\u043e\u0440|\u043f\u0440\u043e\u0441\u0442\u043e\s+\u0434\u0435\u043d\p{L}*|\u0431\u0435\u0437\s+\u0442\u043e\u043f(?:\u043e\u0432|\u0430)?\b|\u0431\u0435\u0437\s+\u0441\u043f\u0438\u0441(?:\u043a\p{L}*)?|\u0431\u0435\u0437\s+\u0434\u0435\u0442\u0430\u043b\p{L}*|\u0431\u0435\u0437\s+\u0440\u0430\u0437\u0431\u0438\u0432\p{L}*|\u0431\u0435\u0437\s+\u0440\u0430\u0437\u0440\u0435\u0437\p{L}*|\u0431\u0435\u0437\s+\u043a\u043e\u043d\u0442\u0440\u0430\u0433\u0435\u043d\p{L}*)/iu.test(
|
||||||
normalized
|
normalized
|
||||||
);
|
);
|
||||||
if (!hasCompactCue) {
|
if (!hasCompactCue) {
|
||||||
|
|
@ -156,14 +156,15 @@ function hasCompactOrganizationCashflowDisplaySignal(text) {
|
||||||
normalized
|
normalized
|
||||||
);
|
);
|
||||||
const hasOrganizationEarningsCue =
|
const hasOrganizationEarningsCue =
|
||||||
/(?:\u0441\u043a\u043e\u043b\u044c\u043a\u043e|\u0441\u043a\u043e\u043a\w*|\u0437\u0430\u0440\u0430\u0431\u043e\u0442\p{L}*|\u043f\u0440\u0438\u0448\p{L}*|\u0443\u0448\p{L}*|\u043f\u043e\u043b\u0443\u0447\p{L}*|\u0437\u0430\u043f\u043b\u0430\u0442\p{L}*|\u0434\u0435\u043d\u0435\u0436\p{L}*\s+\u043d\u0435\u0442\u0442\u043e|how\s+much|earned|received|paid)/iu.test(
|
/(?:\u0434\u0430\u0439|\u043f\u043e\u043a\u0430\u0436\p{L}*|\u0438\u0442\u043e\u0433\p{L}*|\u0441\u043a\u043e\u043b\u044c\u043a\u043e|\u0441\u043a\u043e\u043a\w*|\u0437\u0430\u0440\u0430\u0431\u043e\u0442\p{L}*|\u043f\u0440\u0438\u0448\p{L}*|\u0443\u0448\p{L}*|\u043f\u043e\u043b\u0443\u0447\p{L}*|\u0437\u0430\u043f\u043b\u0430\u0442\p{L}*|\u0434\u0435\u043d\u0435\u0436\p{L}*\s+\u043d\u0435\u0442\u0442\u043e|how\s+much|show|give|earned|received|paid)/iu.test(
|
||||||
normalized
|
normalized
|
||||||
);
|
);
|
||||||
const hasExplicitExclusionCue =
|
const hasExplicitExclusionCue =
|
||||||
/(?:\u0438\u0441\u043a\u043b\u044e\u0447\p{L}*|\u0443\u0431\u0435\u0440\p{L}*|\u043a\u0440\u043e\u043c\u0435|exclude|excluding|without)[\s\S]{0,80}(?:\u043a\u0440\u0443\u043f\u043d\p{L}*|\u043a\u043e\u043d\u0442\u0440\u0430\u0433\u0435\u043d\p{L}*|\u043a\u043b\u0438\u0435\u043d\u0442\p{L}*|\u043f\u043e\u0441\u0442\u0430\u0432\u0449\u0438\u043a\p{L}*|\u043e\u043f\u0435\u0440\u0430\u0446\p{L}*|counterpart|customer|supplier|operation)/iu.test(
|
/(?:\u0438\u0441\u043a\u043b\u044e\u0447\p{L}*|\u0443\u0431\u0435\u0440\p{L}*|\u043a\u0440\u043e\u043c\u0435|exclude|excluding|without)[\s\S]{0,80}(?:\u043a\u0440\u0443\u043f\u043d\p{L}*|\u043a\u043e\u043d\u0442\u0440\u0430\u0433\u0435\u043d\p{L}*|\u043a\u043b\u0438\u0435\u043d\u0442\p{L}*|\u043f\u043e\u0441\u0442\u0430\u0432\u0449\u0438\u043a\p{L}*|\u043e\u043f\u0435\u0440\u0430\u0446\p{L}*|counterpart|customer|supplier|operation)/iu.test(
|
||||||
normalized
|
normalized
|
||||||
);
|
);
|
||||||
return hasMoneyCue && hasOrganizationEarningsCue && !hasExplicitExclusionCue;
|
const hasSpecificCounterpartyScope = detectScopedCounterpartyEntity(normalized) !== null;
|
||||||
|
return hasMoneyCue && hasOrganizationEarningsCue && !hasExplicitExclusionCue && !hasSpecificCounterpartyScope;
|
||||||
}
|
}
|
||||||
|
|
||||||
function detectScopedCounterpartyEntity(text) {
|
function detectScopedCounterpartyEntity(text) {
|
||||||
|
|
@ -485,8 +486,8 @@ export function createAssistantTurnMeaningPolicy(deps = {}) {
|
||||||
const joinedText = fallbackCompactWhitespace(`${rawText} ${effectiveText}`);
|
const joinedText = fallbackCompactWhitespace(`${rawText} ${effectiveText}`);
|
||||||
const compactOrganizationCashflowDisplay = hasCompactOrganizationCashflowDisplaySignal(rawText);
|
const compactOrganizationCashflowDisplay = hasCompactOrganizationCashflowDisplaySignal(rawText);
|
||||||
const supportedIntent = compactOrganizationCashflowDisplay ? null : detectSupportedIntent(joinedText, deps);
|
const supportedIntent = compactOrganizationCashflowDisplay ? null : detectSupportedIntent(joinedText, deps);
|
||||||
const counterpartyBidirectionalValueFlow = detectCounterpartyBidirectionalValueFlowFamily(joinedText);
|
const counterpartyBidirectionalValueFlow = compactOrganizationCashflowDisplay ? null : detectCounterpartyBidirectionalValueFlowFamily(joinedText);
|
||||||
const counterpartyTurnover = detectCounterpartyTurnoverFamily(joinedText);
|
const counterpartyTurnover = compactOrganizationCashflowDisplay ? null : detectCounterpartyTurnoverFamily(joinedText);
|
||||||
const selectedObjectInventoryExact = hasSelectedObjectInventoryExactSignal(joinedText);
|
const selectedObjectInventoryExact = hasSelectedObjectInventoryExactSignal(joinedText);
|
||||||
const broadBusinessEvaluation =
|
const broadBusinessEvaluation =
|
||||||
compactOrganizationCashflowDisplay
|
compactOrganizationCashflowDisplay
|
||||||
|
|
|
||||||
|
|
@ -224,6 +224,89 @@ describe("assistant address orchestration runtime adapter", () => {
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it("rebuilds business overview period carryover for compact cashflow follow-up when address carryover is absent", async () => {
|
||||||
|
const runMcpDiscoveryRuntimeEntryPoint = vi.fn(async () => ({
|
||||||
|
schema_version: "assistant_mcp_discovery_runtime_entry_point_v1",
|
||||||
|
policy_owner: "assistantMcpDiscoveryRuntimeEntryPoint",
|
||||||
|
entry_status: "bridge_executed",
|
||||||
|
hot_runtime_wired: false,
|
||||||
|
discovery_attempted: true
|
||||||
|
}));
|
||||||
|
const input = buildInput({
|
||||||
|
userMessage:
|
||||||
|
"\u0434\u0430\u0439 \u043e\u0434\u043d\u043e\u0439 \u0441\u0442\u0440\u043e\u043a\u043e\u0439 \u0434\u0435\u043d\u044c\u0433\u0438 \u0431\u0435\u0437 \u0440\u0430\u0437\u0431\u0438\u0432\u043a\u0438",
|
||||||
|
sessionItems: [
|
||||||
|
{
|
||||||
|
role: "assistant",
|
||||||
|
debug: {
|
||||||
|
assistant_mcp_discovery_entry_point_v1: {
|
||||||
|
turn_input: {
|
||||||
|
data_need_graph: {
|
||||||
|
business_fact_family: "business_overview",
|
||||||
|
ranking_need: "top_desc"
|
||||||
|
},
|
||||||
|
turn_meaning_ref: {
|
||||||
|
explicit_date_scope: "2020",
|
||||||
|
explicit_organization_scope:
|
||||||
|
"\u041e\u041e\u041e \u0410\u043b\u044c\u0442\u0435\u0440\u043d\u0430\u0442\u0438\u0432\u0430 \u041f\u043b\u044e\u0441"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
runAddressLlmPreDecompose: vi.fn(async () => ({
|
||||||
|
attempted: true,
|
||||||
|
applied: false,
|
||||||
|
effectiveMessage:
|
||||||
|
"\u0434\u0430\u0439 \u043e\u0434\u043d\u043e\u0439 \u0441\u0442\u0440\u043e\u043a\u043e\u0439 \u0434\u0435\u043d\u044c\u0433\u0438 \u0431\u0435\u0437 \u0440\u0430\u0437\u0431\u0438\u0432\u043a\u0438",
|
||||||
|
reason: "raw_kept",
|
||||||
|
predecomposeContract: {
|
||||||
|
mode: "unsupported",
|
||||||
|
intent: "unknown",
|
||||||
|
period: {}
|
||||||
|
}
|
||||||
|
})),
|
||||||
|
resolveAddressFollowupCarryoverContext: vi.fn(() => null),
|
||||||
|
resolveAssistantOrchestrationDecision: vi.fn(() => ({
|
||||||
|
runAddressLane: false,
|
||||||
|
livingMode: "chat",
|
||||||
|
livingReason: "unsupported_current_turn_meaning_boundary",
|
||||||
|
toolGateDecision: "skip_address_lane",
|
||||||
|
toolGateReason: "unsupported_current_turn_meaning_boundary",
|
||||||
|
orchestrationContract: {
|
||||||
|
schema_version: "assistant_orchestration_contract_v1",
|
||||||
|
assistant_turn_meaning: {
|
||||||
|
schema_version: "assistant_turn_meaning_v1",
|
||||||
|
asked_domain_family: "business_summary",
|
||||||
|
asked_action_family: "broad_evaluation",
|
||||||
|
unsupported_but_understood_family: "broad_business_evaluation"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})),
|
||||||
|
runMcpDiscoveryRuntimeEntryPoint
|
||||||
|
});
|
||||||
|
|
||||||
|
await buildAssistantAddressOrchestrationRuntime(input);
|
||||||
|
|
||||||
|
expect(runMcpDiscoveryRuntimeEntryPoint).toHaveBeenCalledWith(
|
||||||
|
expect.objectContaining({
|
||||||
|
followupContext: expect.objectContaining({
|
||||||
|
previous_discovery_pilot_scope: "business_overview_route_template_v1",
|
||||||
|
previous_seeded_ranking_need: "top_desc",
|
||||||
|
previous_filters: expect.objectContaining({
|
||||||
|
period_from: "2020-01-01",
|
||||||
|
period_to: "2020-12-31"
|
||||||
|
}),
|
||||||
|
root_filters: expect.objectContaining({
|
||||||
|
period_from: "2020-01-01",
|
||||||
|
period_to: "2020-12-31"
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
it("passes grounded discovery follow-up carryover into MCP discovery entry point for a short year switch", async () => {
|
it("passes grounded discovery follow-up carryover into MCP discovery entry point for a short year switch", async () => {
|
||||||
const runMcpDiscoveryRuntimeEntryPoint = vi.fn(async () => ({
|
const runMcpDiscoveryRuntimeEntryPoint = vi.fn(async () => ({
|
||||||
schema_version: "assistant_mcp_discovery_runtime_entry_point_v1",
|
schema_version: "assistant_mcp_discovery_runtime_entry_point_v1",
|
||||||
|
|
|
||||||
|
|
@ -704,6 +704,80 @@ describe("assistant MCP discovery response candidate", () => {
|
||||||
expect(candidate.reply_text).not.toContain("wide overview");
|
expect(candidate.reply_text).not.toContain("wide overview");
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it("uses compact cashflow output for one-line no-breakdown wording", () => {
|
||||||
|
const candidate = buildAssistantMcpDiscoveryResponseCandidate(
|
||||||
|
entryPoint({
|
||||||
|
turn_input: {
|
||||||
|
adapter_status: "ready",
|
||||||
|
turn_meaning_ref: {
|
||||||
|
raw_message:
|
||||||
|
"\u0434\u0430\u0439 \u043e\u0434\u043d\u043e\u0439 \u0441\u0442\u0440\u043e\u043a\u043e\u0439 \u0434\u0435\u043d\u044c\u0433\u0438 \u0431\u0435\u0437 \u0440\u0430\u0437\u0431\u0438\u0432\u043a\u0438",
|
||||||
|
asked_action_family: "broad_evaluation",
|
||||||
|
unsupported_but_understood_family: "broad_business_evaluation",
|
||||||
|
explicit_date_scope: "2020"
|
||||||
|
},
|
||||||
|
data_need_graph: {
|
||||||
|
business_fact_family: "business_overview",
|
||||||
|
ranking_need: null,
|
||||||
|
reason_codes: [
|
||||||
|
"data_need_graph_family_business_overview",
|
||||||
|
"data_need_graph_business_overview_direct_money_answer"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
bridge: {
|
||||||
|
bridge_status: "answer_draft_ready",
|
||||||
|
user_facing_response_allowed: true,
|
||||||
|
business_fact_answer_allowed: true,
|
||||||
|
requires_user_clarification: false,
|
||||||
|
pilot: {
|
||||||
|
pilot_scope: "business_overview_route_template_v1",
|
||||||
|
derived_business_overview: {
|
||||||
|
period_scope: "2020",
|
||||||
|
incoming_customer_revenue: {
|
||||||
|
total_amount_human_ru: "47 628 853,03 \u0440\u0443\u0431."
|
||||||
|
},
|
||||||
|
outgoing_supplier_payout: {
|
||||||
|
total_amount_human_ru: "43 763 351,53 \u0440\u0443\u0431."
|
||||||
|
},
|
||||||
|
net_amount_human_ru: "3 865 501,50 \u0440\u0443\u0431.",
|
||||||
|
net_direction: "net_incoming",
|
||||||
|
top_customers: [
|
||||||
|
{
|
||||||
|
axis_value: "\u0421\u0411\u0415\u0420\u0411\u0410\u041d\u041a, \u041f\u0410\u041e",
|
||||||
|
total_amount_human_ru: "12 792 194,31 \u0440\u0443\u0431."
|
||||||
|
}
|
||||||
|
],
|
||||||
|
top_suppliers: [
|
||||||
|
{
|
||||||
|
axis_value: "\u0414\u0435\u043f\u0430\u0440\u0442\u0430\u043c\u0435\u043d\u0442",
|
||||||
|
total_amount_human_ru: "9 612 904,90 \u0440\u0443\u0431."
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
answer_draft: {
|
||||||
|
answer_mode: "confirmed_with_bounded_inference",
|
||||||
|
headline: "wide overview should not leak",
|
||||||
|
confirmed_lines: [],
|
||||||
|
inference_lines: [],
|
||||||
|
unknown_lines: [],
|
||||||
|
limitation_lines: [],
|
||||||
|
next_step_line: "\u0427\u0442\u043e \u043f\u0440\u043e\u0432\u0435\u0440\u0438\u0442\u044c \u0434\u0430\u043b\u044c\u0448\u0435"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(candidate.reply_text).toContain("\u0437\u0430 2020");
|
||||||
|
expect(candidate.reply_text).toContain("47 628 853,03");
|
||||||
|
expect(candidate.reply_text).toContain("43 763 351,53");
|
||||||
|
expect(candidate.reply_text).toContain("3 865 501,50");
|
||||||
|
expect(candidate.reply_text).not.toContain("\u0421\u0411\u0415\u0420\u0411\u0410\u041d\u041a");
|
||||||
|
expect(candidate.reply_text).not.toContain("\u0427\u0442\u043e \u043f\u0440\u043e\u0432\u0435\u0440\u0438\u0442\u044c");
|
||||||
|
expect(candidate.reply_text).not.toContain("wide overview");
|
||||||
|
});
|
||||||
|
|
||||||
it("labels organization-scoped bidirectional value-flow continuations as company scope", () => {
|
it("labels organization-scoped bidirectional value-flow continuations as company scope", () => {
|
||||||
const candidate = buildAssistantMcpDiscoveryResponseCandidate(
|
const candidate = buildAssistantMcpDiscoveryResponseCandidate(
|
||||||
entryPoint({
|
entryPoint({
|
||||||
|
|
|
||||||
|
|
@ -2380,6 +2380,47 @@ describe("assistant MCP discovery turn input adapter", () => {
|
||||||
expect(result.data_need_graph?.reason_codes).toContain("data_need_graph_business_overview_direct_money_answer");
|
expect(result.data_need_graph?.reason_codes).toContain("data_need_graph_business_overview_direct_money_answer");
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it("preserves previous overview period for compact cashflow display follow-ups", () => {
|
||||||
|
const orgName = "\u041e\u041e\u041e \u0410\u043b\u044c\u0442\u0435\u0440\u043d\u0430\u0442\u0438\u0432\u0430 \u041f\u043b\u044e\u0441";
|
||||||
|
const result = buildAssistantMcpDiscoveryTurnInput({
|
||||||
|
userMessage:
|
||||||
|
"\u0434\u0430\u0439 \u043e\u0434\u043d\u043e\u0439 \u0441\u0442\u0440\u043e\u043a\u043e\u0439 \u0434\u0435\u043d\u044c\u0433\u0438 \u0431\u0435\u0437 \u0440\u0430\u0437\u0431\u0438\u0432\u043a\u0438",
|
||||||
|
assistantTurnMeaning: {
|
||||||
|
asked_domain_family: "business_summary",
|
||||||
|
asked_action_family: "broad_evaluation",
|
||||||
|
unsupported_but_understood_family: "broad_business_evaluation",
|
||||||
|
stale_replay_forbidden: true,
|
||||||
|
reason_codes: ["compact_cashflow_display_current_turn_signal"]
|
||||||
|
},
|
||||||
|
followupContext: {
|
||||||
|
previous_discovery_pilot_scope: "business_overview_route_template_v1",
|
||||||
|
previous_filters: {
|
||||||
|
organization: orgName,
|
||||||
|
period_from: "2020-01-01",
|
||||||
|
period_to: "2020-12-31"
|
||||||
|
},
|
||||||
|
previous_seeded_ranking_need: "top_desc"
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(result.adapter_status).toBe("ready");
|
||||||
|
expect(result.should_run_discovery).toBe(true);
|
||||||
|
expect(result.data_need_graph?.business_fact_family).toBe("business_overview");
|
||||||
|
expect(result.data_need_graph?.ranking_need).toBeNull();
|
||||||
|
expect(result.turn_meaning_ref).toMatchObject({
|
||||||
|
asked_domain_family: "business_overview",
|
||||||
|
asked_action_family: "broad_evaluation",
|
||||||
|
explicit_organization_scope: orgName,
|
||||||
|
explicit_date_scope: "2020",
|
||||||
|
unsupported_but_understood_family: "broad_business_evaluation",
|
||||||
|
stale_replay_forbidden: true
|
||||||
|
});
|
||||||
|
expect(result.reason_codes).toContain("mcp_discovery_date_scope_from_followup_context");
|
||||||
|
expect(result.reason_codes).not.toContain("mcp_discovery_topic_switch_suppressed_followup_scope");
|
||||||
|
expect(result.data_need_graph?.reason_codes).toContain("data_need_graph_business_overview_direct_money_answer");
|
||||||
|
expect(result.data_need_graph?.reason_codes).not.toContain("data_need_graph_all_time_scope_hint");
|
||||||
|
});
|
||||||
|
|
||||||
it("routes organization-level profit and margin wording to business overview instead of exact value recipes", () => {
|
it("routes organization-level profit and margin wording to business overview instead of exact value recipes", () => {
|
||||||
const orgName = "\u041e\u041e\u041e \u0410\u043b\u044c\u0442\u0435\u0440\u043d\u0430\u0442\u0438\u0432\u0430 \u041f\u043b\u044e\u0441";
|
const orgName = "\u041e\u041e\u041e \u0410\u043b\u044c\u0442\u0435\u0440\u043d\u0430\u0442\u0438\u0432\u0430 \u041f\u043b\u044e\u0441";
|
||||||
const result = buildAssistantMcpDiscoveryTurnInput({
|
const result = buildAssistantMcpDiscoveryTurnInput({
|
||||||
|
|
|
||||||
|
|
@ -216,6 +216,32 @@ describe("assistantTurnMeaningPolicy", () => {
|
||||||
expect(meaning.reason_codes).not.toContain("counterparty_turnover_current_turn_signal");
|
expect(meaning.reason_codes).not.toContain("counterparty_turnover_current_turn_signal");
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it.each([
|
||||||
|
"\u0441\u043a\u043e\u043b\u044c\u043a\u043e \u0437\u0430\u0440\u0430\u0431\u043e\u0442\u0430\u043b\u0438 \u0434\u0435\u043d\u044c\u0433\u0430\u043c\u0438 \u0431\u0435\u0437 \u043a\u043e\u043d\u0442\u0440\u0430\u0433\u0435\u043d\u0442\u043e\u0432?",
|
||||||
|
"\u0442\u043e\u043b\u044c\u043a\u043e \u0438\u0442\u043e\u0433: \u043f\u0440\u0438\u0448\u043b\u043e, \u0443\u0448\u043b\u043e, \u043d\u0435\u0442\u0442\u043e",
|
||||||
|
"\u0434\u0430\u0439 \u043e\u0434\u043d\u043e\u0439 \u0441\u0442\u0440\u043e\u043a\u043e\u0439 \u0434\u0435\u043d\u044c\u0433\u0438 \u0431\u0435\u0437 \u0440\u0430\u0437\u0431\u0438\u0432\u043a\u0438",
|
||||||
|
"\u0441\u043a\u043e\u043b\u044c\u043a\u043e \u043f\u043e\u043b\u0443\u0447\u0438\u043b\u0438 \u0438 \u0437\u0430\u043f\u043b\u0430\u0442\u0438\u043b\u0438 \u0431\u0435\u0437 \u0434\u0435\u0442\u0430\u043b\u0438\u0437\u0430\u0446\u0438\u0438"
|
||||||
|
])("treats compact cashflow wording '%s' as business-summary display request", (rawUserMessage) => {
|
||||||
|
const policy = buildPolicy({
|
||||||
|
resolveAddressIntent: () => ({ intent: "customer_revenue_and_payments", confidence: "high" })
|
||||||
|
});
|
||||||
|
|
||||||
|
const meaning = policy.resolveAssistantTurnMeaning({
|
||||||
|
rawUserMessage,
|
||||||
|
effectiveAddressUserMessage:
|
||||||
|
"\u041e\u043f\u0440\u0435\u0434\u0435\u043b\u0438\u0442\u044c \u043e\u0431\u043e\u0440\u043e\u0442 \u0438 \u0432\u044b\u0440\u0443\u0447\u043a\u0443 \u043f\u043e \u043a\u043e\u043c\u043f\u0430\u043d\u0438\u0438 \u0431\u0435\u0437 \u0440\u0430\u0437\u0440\u0435\u0437\u0430 \u043f\u043e \u043a\u043e\u043d\u0442\u0440\u0430\u0433\u0435\u043d\u0442\u0430\u043c."
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(meaning.explicit_intent_candidate).toBeNull();
|
||||||
|
expect(meaning.asked_domain_family).toBe("business_summary");
|
||||||
|
expect(meaning.asked_action_family).toBe("broad_evaluation");
|
||||||
|
expect(meaning.explicit_entity_candidates).toEqual([]);
|
||||||
|
expect(meaning.unsupported_but_understood_family).toBe("broad_business_evaluation");
|
||||||
|
expect(meaning.stale_replay_forbidden).toBe(true);
|
||||||
|
expect(meaning.reason_codes).toContain("compact_cashflow_display_current_turn_signal");
|
||||||
|
expect(meaning.reason_codes).not.toContain("counterparty_turnover_current_turn_signal");
|
||||||
|
});
|
||||||
|
|
||||||
it("treats organization-level earnings and best-year wording as business overview", () => {
|
it("treats organization-level earnings and best-year wording as business overview", () => {
|
||||||
const policy = buildPolicy({
|
const policy = buildPolicy({
|
||||||
resolveAddressIntent: () => ({ intent: "customer_revenue_and_payments", confidence: "high" })
|
resolveAddressIntent: () => ({ intent: "customer_revenue_and_payments", confidence: "high" })
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,53 @@
|
||||||
[
|
[
|
||||||
|
{
|
||||||
|
"generation_id": "gen-ag05231427-70915a",
|
||||||
|
"created_at": "2026-05-23T14:27:55+00:00",
|
||||||
|
"mode": "saved_user_sessions",
|
||||||
|
"title": "AGENT | Cashflow compact display variants",
|
||||||
|
"count": 5,
|
||||||
|
"domain": "autonomy_business_answer_contract",
|
||||||
|
"questions": [
|
||||||
|
"Теперь дай взрослый обзор за 2020 по компании: входящие, исходящие, нетто, топы, но банк в топах отдельно объясни как финансовый поток.",
|
||||||
|
"сколько заработали деньгами без контрагентов?",
|
||||||
|
"только итог: пришло, ушло, нетто",
|
||||||
|
"дай одной строкой деньги без разбивки",
|
||||||
|
"сколько получили и заплатили без детализации"
|
||||||
|
],
|
||||||
|
"generated_by": "codex_agent",
|
||||||
|
"saved_case_set_file": "assistant_autogen_saved_user_sessions_20260523142755_gen-ag05231427-70915a.json",
|
||||||
|
"context": {
|
||||||
|
"llm_provider": null,
|
||||||
|
"model": null,
|
||||||
|
"assistant_prompt_version": null,
|
||||||
|
"decomposition_prompt_version": null,
|
||||||
|
"prompt_fingerprint": null,
|
||||||
|
"autogen_personality_id": null,
|
||||||
|
"autogen_personality_prompt": null,
|
||||||
|
"source_session_id": null,
|
||||||
|
"saved_session_file": "assistant_saved_session_20260523142755_gen-ag05231427-70915a.json",
|
||||||
|
"saved_case_set_kind": "agent_semantic_scenario",
|
||||||
|
"agent_run": true,
|
||||||
|
"agent_focus": "Targeted AGENT replay for compact cashflow display modifiers: no counterparties, no breakdown, one-line and only-total follow-ups must preserve the 2020 company cashflow context.",
|
||||||
|
"architecture_phase": "turnaround_11",
|
||||||
|
"source_spec_file": "X:\\1C\\NDC_1C\\docs\\orchestration\\agent_cashflow_compact_display_variants_20260523.json",
|
||||||
|
"scenario_id": "agent_cashflow_compact_display_variants_20260523",
|
||||||
|
"semantic_tags": [
|
||||||
|
"business_overview",
|
||||||
|
"context_anchor",
|
||||||
|
"direct_totals",
|
||||||
|
"display_modifier",
|
||||||
|
"no_breakdown",
|
||||||
|
"no_counterparty_breakdown",
|
||||||
|
"no_details",
|
||||||
|
"one_line",
|
||||||
|
"only_total",
|
||||||
|
"temporal_carryover"
|
||||||
|
],
|
||||||
|
"validation_status": "accepted_live_replay",
|
||||||
|
"validated_run_dir": "artifacts\\domain_runs\\agent_cashflow_compact_display_variants_live5",
|
||||||
|
"saved_after_validated_replay": true
|
||||||
|
}
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"generation_id": "gen-ag05231310-3d45fe",
|
"generation_id": "gen-ag05231310-3d45fe",
|
||||||
"created_at": "2026-05-23T13:10:25+00:00",
|
"created_at": "2026-05-23T13:10:25+00:00",
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,145 @@
|
||||||
|
{
|
||||||
|
"saved_at": "2026-05-23T14:27:55+00:00",
|
||||||
|
"generation_id": "gen-ag05231427-70915a",
|
||||||
|
"mode": "saved_user_sessions",
|
||||||
|
"title": "AGENT | Cashflow compact display variants",
|
||||||
|
"agent_run": true,
|
||||||
|
"questions": [
|
||||||
|
"Теперь дай взрослый обзор за 2020 по компании: входящие, исходящие, нетто, топы, но банк в топах отдельно объясни как финансовый поток.",
|
||||||
|
"сколько заработали деньгами без контрагентов?",
|
||||||
|
"только итог: пришло, ушло, нетто",
|
||||||
|
"дай одной строкой деньги без разбивки",
|
||||||
|
"сколько получили и заплатили без детализации"
|
||||||
|
],
|
||||||
|
"metadata": {
|
||||||
|
"assistant_prompt_version": null,
|
||||||
|
"decomposition_prompt_version": null,
|
||||||
|
"prompt_fingerprint": null,
|
||||||
|
"agent_focus": "Targeted AGENT replay for compact cashflow display modifiers: no counterparties, no breakdown, one-line and only-total follow-ups must preserve the 2020 company cashflow context.",
|
||||||
|
"architecture_phase": "turnaround_11",
|
||||||
|
"source_spec_file": "X:\\1C\\NDC_1C\\docs\\orchestration\\agent_cashflow_compact_display_variants_20260523.json",
|
||||||
|
"scenario_id": "agent_cashflow_compact_display_variants_20260523",
|
||||||
|
"semantic_tags": [
|
||||||
|
"business_overview",
|
||||||
|
"context_anchor",
|
||||||
|
"direct_totals",
|
||||||
|
"display_modifier",
|
||||||
|
"no_breakdown",
|
||||||
|
"no_counterparty_breakdown",
|
||||||
|
"no_details",
|
||||||
|
"one_line",
|
||||||
|
"only_total",
|
||||||
|
"temporal_carryover"
|
||||||
|
],
|
||||||
|
"validation_status": "accepted_live_replay",
|
||||||
|
"validated_run_dir": "artifacts\\domain_runs\\agent_cashflow_compact_display_variants_live5",
|
||||||
|
"saved_after_validated_replay": true,
|
||||||
|
"save_gate": {
|
||||||
|
"schema_version": "agent_semantic_save_gate_v1",
|
||||||
|
"validation_status": "accepted_live_replay",
|
||||||
|
"validated_run_dir": "artifacts\\domain_runs\\agent_cashflow_compact_display_variants_live5",
|
||||||
|
"final_status": "accepted",
|
||||||
|
"review_overall_status": "pass",
|
||||||
|
"business_overall_status": "pass",
|
||||||
|
"steps_total": 5,
|
||||||
|
"steps_passed": 5,
|
||||||
|
"steps_failed": 0,
|
||||||
|
"steps_with_business_failures": 0,
|
||||||
|
"steps_with_business_warnings": 0,
|
||||||
|
"acceptance_gate_passed": true,
|
||||||
|
"saved_after_validated_replay": true
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"source_session_id": null,
|
||||||
|
"session": {
|
||||||
|
"session_id": null,
|
||||||
|
"mode": "agent_semantic_run",
|
||||||
|
"items": [
|
||||||
|
{
|
||||||
|
"message_id": "agent-user-001",
|
||||||
|
"role": "user",
|
||||||
|
"text": "Теперь дай взрослый обзор за 2020 по компании: входящие, исходящие, нетто, топы, но банк в топах отдельно объясни как финансовый поток.",
|
||||||
|
"created_at": "2026-05-23T14:27:55+00:00",
|
||||||
|
"reply_type": null,
|
||||||
|
"trace_id": null,
|
||||||
|
"debug": null
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"message_id": "agent-user-002",
|
||||||
|
"role": "user",
|
||||||
|
"text": "сколько заработали деньгами без контрагентов?",
|
||||||
|
"created_at": "2026-05-23T14:27:55+00:00",
|
||||||
|
"reply_type": null,
|
||||||
|
"trace_id": null,
|
||||||
|
"debug": null
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"message_id": "agent-user-003",
|
||||||
|
"role": "user",
|
||||||
|
"text": "только итог: пришло, ушло, нетто",
|
||||||
|
"created_at": "2026-05-23T14:27:55+00:00",
|
||||||
|
"reply_type": null,
|
||||||
|
"trace_id": null,
|
||||||
|
"debug": null
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"message_id": "agent-user-004",
|
||||||
|
"role": "user",
|
||||||
|
"text": "дай одной строкой деньги без разбивки",
|
||||||
|
"created_at": "2026-05-23T14:27:55+00:00",
|
||||||
|
"reply_type": null,
|
||||||
|
"trace_id": null,
|
||||||
|
"debug": null
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"message_id": "agent-user-005",
|
||||||
|
"role": "user",
|
||||||
|
"text": "сколько получили и заплатили без детализации",
|
||||||
|
"created_at": "2026-05-23T14:27:55+00:00",
|
||||||
|
"reply_type": null,
|
||||||
|
"trace_id": null,
|
||||||
|
"debug": null
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"agent_run": true,
|
||||||
|
"metadata": {
|
||||||
|
"assistant_prompt_version": null,
|
||||||
|
"decomposition_prompt_version": null,
|
||||||
|
"prompt_fingerprint": null,
|
||||||
|
"agent_focus": "Targeted AGENT replay for compact cashflow display modifiers: no counterparties, no breakdown, one-line and only-total follow-ups must preserve the 2020 company cashflow context.",
|
||||||
|
"architecture_phase": "turnaround_11",
|
||||||
|
"source_spec_file": "X:\\1C\\NDC_1C\\docs\\orchestration\\agent_cashflow_compact_display_variants_20260523.json",
|
||||||
|
"scenario_id": "agent_cashflow_compact_display_variants_20260523",
|
||||||
|
"semantic_tags": [
|
||||||
|
"business_overview",
|
||||||
|
"context_anchor",
|
||||||
|
"direct_totals",
|
||||||
|
"display_modifier",
|
||||||
|
"no_breakdown",
|
||||||
|
"no_counterparty_breakdown",
|
||||||
|
"no_details",
|
||||||
|
"one_line",
|
||||||
|
"only_total",
|
||||||
|
"temporal_carryover"
|
||||||
|
],
|
||||||
|
"validation_status": "accepted_live_replay",
|
||||||
|
"validated_run_dir": "artifacts\\domain_runs\\agent_cashflow_compact_display_variants_live5",
|
||||||
|
"saved_after_validated_replay": true,
|
||||||
|
"save_gate": {
|
||||||
|
"schema_version": "agent_semantic_save_gate_v1",
|
||||||
|
"validation_status": "accepted_live_replay",
|
||||||
|
"validated_run_dir": "artifacts\\domain_runs\\agent_cashflow_compact_display_variants_live5",
|
||||||
|
"final_status": "accepted",
|
||||||
|
"review_overall_status": "pass",
|
||||||
|
"business_overall_status": "pass",
|
||||||
|
"steps_total": 5,
|
||||||
|
"steps_passed": 5,
|
||||||
|
"steps_failed": 0,
|
||||||
|
"steps_with_business_failures": 0,
|
||||||
|
"steps_with_business_warnings": 0,
|
||||||
|
"acceptance_gate_passed": true,
|
||||||
|
"saved_after_validated_replay": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,40 @@
|
||||||
|
{
|
||||||
|
"suite_id": "assistant_saved_session_gen-ag05231427-70915a",
|
||||||
|
"suite_version": "0.1.0",
|
||||||
|
"schema_version": "assistant_saved_session_suite_v0_1",
|
||||||
|
"generated_at": "2026-05-23T14:27:55+00:00",
|
||||||
|
"generation_id": "gen-ag05231427-70915a",
|
||||||
|
"mode": "saved_user_sessions",
|
||||||
|
"title": "AGENT | Cashflow compact display variants",
|
||||||
|
"domain": "autonomy_business_answer_contract",
|
||||||
|
"scenario_count": 1,
|
||||||
|
"case_ids": [
|
||||||
|
"SAVED-001"
|
||||||
|
],
|
||||||
|
"cases": [
|
||||||
|
{
|
||||||
|
"case_id": "SAVED-001",
|
||||||
|
"scenario_tag": "agent_saved_user_sessions",
|
||||||
|
"title": "AGENT | Cashflow compact display variants",
|
||||||
|
"question_type": "followup",
|
||||||
|
"broadness_level": "medium",
|
||||||
|
"turns": [
|
||||||
|
{
|
||||||
|
"user_message": "Теперь дай взрослый обзор за 2020 по компании: входящие, исходящие, нетто, топы, но банк в топах отдельно объясни как финансовый поток."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"user_message": "сколько заработали деньгами без контрагентов?"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"user_message": "только итог: пришло, ушло, нетто"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"user_message": "дай одной строкой деньги без разбивки"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"user_message": "сколько получили и заплатили без детализации"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
Loading…
Reference in New Issue