Этап 4 / Волна 16: смысловая изоляция РБП и ОС, фиксы лайв-ответов и добивка экспорта

This commit is contained in:
2026-03-28 21:11:11 +03:00
parent 6123fafd5b
commit 7eb1410501
35 changed files with 4120 additions and 155 deletions
@@ -1,4 +1,4 @@
import type {
import type {
AssistantRequirement,
RequirementCoverageReport,
UnifiedRetrievalResult
@@ -146,6 +146,11 @@ function isVatAccount(value: string): boolean {
return prefix === "19" || prefix === "68";
}
function isFixedAssetAccount(value: string): boolean {
const prefix = normalizeAccountPrefix(value);
return prefix === "01" || prefix === "02" || prefix === "08";
}
function isCloseCostsAccount(value: string): boolean {
const prefix = normalizeAccountPrefix(value);
if (!prefix) {
@@ -161,11 +166,26 @@ function inferFollowupActiveDomain(input: {
routeSummary: RouteHintSummary | null;
previous: InvestigationStateWithProblemUnits;
}): string | null {
const corpus = `${input.userMessage} ${input.previous.focus.active_query_subject ?? ""}`.toLowerCase();
const messageCorpus = String(input.userMessage ?? "").toLowerCase();
const contextualCorpus = `${messageCorpus} ${input.previous.focus.active_query_subject ?? ""}`.toLowerCase();
const hasFixedAssetLexicalSignal =
/(?:амортиз|основн(ые|ых|ым)?\s+средств|(?:^|[^a-zа-яё])ос(?:$|[^a-zа-яё])|объект[а-яё]*\s+ос|fixed\s*asset|depreciat)/i.test(
messageCorpus
);
const hasFixedAssetAccountSignal =
input.focusAccounts.some((item) => isFixedAssetAccount(item)) &&
/(?:сч[её]т(?:а|у|ом|е)?\s*(?:01|02|08)|(?:01|02|08)(?:\.\d{2})?\s*\/\s*(?:01|02|08)(?:\.\d{2})?|\b0[128](?:\.\d{2})?\b)/i.test(
messageCorpus
);
if (hasFixedAssetLexicalSignal || hasFixedAssetAccountSignal) {
return "fixed_asset_amortization";
}
const hasSettlementSignal =
input.focusAccounts.some((item) => isSettlementAccount(item)) ||
/(60(?:\.\d{2})?|62(?:\.\d{2})?|оплат|расчет|расч[её]т|зачет|зач[её]т|аванс|долг|поставщ|покупат|settlement|payment|supplier|customer)/i.test(
corpus
/(?:60(?:\.\d{2})?|62(?:\.\d{2})?|оплат|расч[её]т|зач[её]т|аванс|долг|поставщ|покупат|settlement|payment|supplier|customer)/i.test(
messageCorpus
);
if (hasSettlementSignal) {
return "settlements_60_62";
@@ -173,18 +193,26 @@ function inferFollowupActiveDomain(input: {
const hasVatSignal =
input.focusAccounts.some((item) => isVatAccount(item)) ||
/(ндс|счет[\s-]?фактур|сч[её]т[\s-]?фактур|книг[аи]|vat|invoice|book|register)/i.test(corpus);
/(?:ндс|сч[её]т[\s-]?фактур|книг[аи]|vat|invoice|book|register)/i.test(messageCorpus);
if (hasVatSignal) {
return "vat_document_register_book";
}
const hasCloseSignal =
input.focusAccounts.some((item) => isCloseCostsAccount(item)) ||
/(закрыти|закрытие|месяц|затрат|распредел|списан|period\s*close|month\s*close|allocation|residual|cost)/i.test(corpus);
/(?:закрыти|месяц|затрат|распредел|списан|period\s*close|month\s*close|allocation|residual|cost)/i.test(messageCorpus);
if (hasCloseSignal) {
return "month_close_costs_20_44";
}
if (
/(?:60(?:\.\d{2})?|62(?:\.\d{2})?|оплат|расч[её]т|аванс|долг|settlement|payment)/i.test(contextualCorpus) &&
(input.previous.followup_context?.active_domain === "settlements_60_62" ||
input.previous.focus.domain === "settlements_60_62")
) {
return "settlements_60_62";
}
const routeDomain = deriveDomain(input.routeSummary);
if (routeDomain && routeDomain !== "no_route") {
return routeDomain;
@@ -450,7 +478,7 @@ export function updateInvestigationState(input: UpdateInvestigationStateInput):
const uncoveredRequirementIds = collectUncoveredRequirementIds(input.coverageReport);
const activeDomain = inferFollowupActiveDomain({
userMessage: input.userMessage,
focusAccounts: mergedFocusAccounts,
focusAccounts: focusFromMessage,
routeSummary: input.routeSummary,
previous
});
@@ -498,3 +526,4 @@ export function updateInvestigationState(input: UpdateInvestigationStateInput):
: {})
};
}