Закрепить расширенный AGENT-прогон качества 1С-ассистента

This commit is contained in:
2026-05-23 09:07:24 +03:00
parent 50d938b8f1
commit e38223e33e
10 changed files with 647 additions and 50 deletions
@@ -549,7 +549,7 @@ function classifyBankOperationSemanticBucket(row: ComposeStageRow): BankOperatio
function bankOperationSemanticBucketLabel(bucket: BankOperationSemanticBucket): string {
if (bucket === "commission") {
return "комиссии и банковое обслуживание";
return "комиссии и банковское обслуживание";
}
if (bucket === "deposit_or_credit") {
return "депозиты, кредиты или проценты";
@@ -4940,6 +4940,7 @@ function composeFactualReplyBody(
)
.slice(0, Math.min(rows.length, 5));
const semanticSummary = summarizeBankOperationSemantics(rows);
const compactRoleAnswer = /(?:клиент|поставщик|финансов|роль|коротко)/iu.test(String(options.userMessage ?? ""));
const compactEvidenceRows = visibleRows.map((row, index) => {
const direction = bankOperationDirectionLabel(bankOperationDirection(row));
const amount = formatMoneyRub(row.amount ?? 0);
@@ -4955,13 +4956,17 @@ function composeFactualReplyBody(
`Коротко: найдено банковских операций${counterparty ? ` по ${counterparty}` : " по контрагенту"}${rows.length}.`,
summarizeBankOperationDirections(rows),
roleBoundary ?? "Показываю подтвержденные банковские операции из текущего среза.",
bankOperationEvidenceLine(rows, preferredBankEvidenceDirection(options.userMessage)),
...(semanticSummary ? [semanticSummary] : []),
"Примеры строк 1С:",
...compactEvidenceRows,
"Следующий шаг: могу отдельно разложить назначения платежа, договоры или отделить банковский контур от клиентского/поставщицкого."
...(semanticSummary ? [semanticSummary] : [])
];
if (rows.length > visibleRows.length) {
if (!compactRoleAnswer) {
lines.push(
bankOperationEvidenceLine(rows, preferredBankEvidenceDirection(options.userMessage)),
"Примеры строк 1С:",
...compactEvidenceRows
);
}
lines.push("Следующий шаг: могу отдельно разложить назначения платежа, договоры или отделить банковский контур от клиентского/поставщицкого.");
if (!compactRoleAnswer && rows.length > visibleRows.length) {
lines.push(`Показаны первые ${visibleRows.length} из ${rows.length}; полный список остается в подтвержденном срезе.`);
}
return {
@@ -1392,26 +1392,18 @@ function derivedValueFlowConfirmedLine(pilot: AssistantMcpDiscoveryPilotExecutio
const organization = organizationScope ? ` по организации ${organizationScope}` : "";
const counterparty = flow.counterparty ? ` по контрагенту ${flow.counterparty}` : "";
const period = flow.period_scope ? ` за период ${flow.period_scope}` : " в проверенном окне";
const movementLabel =
flow.value_flow_direction === "outgoing_supplier_payout"
? "исходящих платежей/списаний"
: "входящих денежных поступлений";
const totalLabel =
flow.value_flow_direction === "outgoing_supplier_payout"
? "сумма исходящих платежей/списаний составляет"
: "сумма входящих денежных поступлений составляет";
? "исходящие платежи/списания"
: "входящие денежные поступления";
const caveat =
flow.value_flow_direction === "outgoing_supplier_payout"
? "Это расчет по найденным строкам 1С, а не подтверждение полного объема платежей вне проверенного окна."
: "Это расчет по найденным строкам 1С, а не подтверждение полного объема поступлений вне проверенного окна.";
const dates =
flow.first_movement_date && flow.latest_movement_date
? ` Первая найденная дата движения: ${flow.first_movement_date}; последняя: ${flow.latest_movement_date}.`
: "";
? "Граница: это проверенный денежный срез по найденным списаниям в 1С, не внешний аудит всех платежей вне этого окна."
: "Граница: это проверенный денежный срез по найденным поступлениям в 1С, не внешний аудит всех поступлений вне этого окна.";
const limitCaveat = flow.coverage_limited_by_probe_limit
? " Лимит строк проверки достигнут; полный запрошенный период может быть покрыт не полностью."
? " Проверка уперлась в лимит строк, поэтому полный период может быть покрыт не полностью."
: "";
return `По найденным строкам ${movementLabel} в 1С${counterparty}${period} ${totalLabel} ${flow.total_amount_human_ru} Учтено строк с суммой: ${flow.rows_with_amount} из ${flow.rows_matched}.${dates}${limitCaveat} ${caveat}`;
return `${totalLabel}${counterparty || organization}${period}: ${flow.total_amount_human_ru}.${limitCaveat} ${caveat}`;
}
function derivedValueFlowMonthlyLines(pilot: AssistantMcpDiscoveryPilotExecutionContract): string[] {
@@ -2109,7 +2101,7 @@ export function buildAssistantMcpDiscoveryAnswerDraft(
: pilot.derived_ranked_value_flow && derivedValueLine
? [derivedValueLine]
: derivedValueLine
? [...pilot.evidence.confirmed_facts, derivedValueLine, ...monthlyConfirmedLines]
? [derivedValueLine, ...monthlyConfirmedLines]
: valueFlowZeroResultConfirmedLine(pilot)
? [valueFlowZeroResultConfirmedLine(pilot)!]
: derivedEntityResolutionLine
@@ -602,14 +602,12 @@ function businessOverviewInventoryLine(overview: Record<string, unknown>): strin
}
const amount = moneyText(inventory.total_amount_human_ru);
const rows = Number(inventory.rows_matched);
const quantity = Number(inventory.total_quantity);
if (!amount && !Number.isFinite(rows)) {
return null;
}
const pieces = [
Number.isFinite(rows) ? `${rows} позиций` : null,
amount ? `на ${sentenceAmount(amount) ?? amount}` : null,
Number.isFinite(quantity) && quantity > 0 ? `количество ${quantity}` : null
amount ? `на ${sentenceAmount(amount) ?? amount}` : null
].filter((item): item is string => Boolean(item));
return pieces.length > 0 ? `Склад: ${pieces.join(", ")}.` : null;
}
@@ -1098,7 +1096,7 @@ function buildCompactBusinessOverviewReply(
if (customerName && customerAmount) {
lines.push(
topCustomerLooksFinancial
? `- крупнейший входящий источник: ${customerName}${sentenceAmount(customerAmount) ?? customerAmount}; это похоже на финансовый контур, не на обычную клиентскую выручку;`
? `- крупнейший входящий источник: ${customerName}${sentenceAmount(customerAmount) ?? customerAmount}; это похоже на финансовый контур, не на обычную клиентскую выручку;${nonFinancialCustomer ? ` крупнейший небанковский входящий контрагент: ${nonFinancialCustomer};` : ""}`
: `- крупнейший источник денег: ${customerName}${sentenceAmount(customerAmount) ?? customerAmount}; это сигнал концентрации на крупном заказчике;`
);
}