Этап 4 / Волна 10: корректировка settlement-кейса — доменная фиксация синтеза, честное покрытие, удержание фокуса / Этап 4 / Волна 11: бизнес-якоря, доменное заземление и устранение утечки дебага

This commit is contained in:
2026-03-28 02:17:19 +03:00
parent 914843a8ba
commit a06e575be4
367 changed files with 432257 additions and 3627 deletions
+114 -4
View File
@@ -75,6 +75,78 @@ function collectOpenUncertainties(coverageReport, retrievalResults) {
const limitationNotes = retrievalResults.flatMap((result) => result.limitations).slice(0, 6);
return capStrings([...requirementNotes, ...limitationNotes], stage1Contracts_1.INVESTIGATION_MAX_UNCERTAINTIES);
}
function normalizeAccountPrefix(value) {
const account = String(value ?? "").trim();
if (!account) {
return null;
}
const match = account.match(/^(\d{2})/);
return match?.[1] ?? null;
}
function isSettlementAccount(value) {
const prefix = normalizeAccountPrefix(value);
return prefix === "60" || prefix === "62" || prefix === "51" || prefix === "76";
}
function isVatAccount(value) {
const prefix = normalizeAccountPrefix(value);
return prefix === "19" || prefix === "68";
}
function isCloseCostsAccount(value) {
const prefix = normalizeAccountPrefix(value);
if (!prefix) {
return false;
}
const account = Number(prefix);
return (account >= 20 && account <= 44) || prefix === "97";
}
function inferFollowupActiveDomain(input) {
const corpus = `${input.userMessage} ${input.previous.focus.active_query_subject ?? ""}`.toLowerCase();
const hasSettlementSignal = input.focusAccounts.some((item) => isSettlementAccount(item)) ||
/(60(?:\.\d{2})?|62(?:\.\d{2})?|оплат|расчет|расч[её]т|зачет|зач[её]т|аванс|долг|поставщ|покупат|settlement|payment|supplier|customer)/i.test(corpus);
if (hasSettlementSignal) {
return "settlements_60_62";
}
const hasVatSignal = input.focusAccounts.some((item) => isVatAccount(item)) ||
/(ндс|счет[\s-]?фактур|сч[её]т[\s-]?фактур|книг[аи]|vat|invoice|book|register)/i.test(corpus);
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);
if (hasCloseSignal) {
return "month_close_costs_20_44";
}
const routeDomain = deriveDomain(input.routeSummary);
if (routeDomain && routeDomain !== "no_route") {
return routeDomain;
}
return input.previous.followup_context?.active_domain ?? input.previous.focus.domain ?? null;
}
function collectUncoveredRequirementIds(coverageReport) {
return capStrings([
...coverageReport.requirements_uncovered,
...coverageReport.requirements_partially_covered,
...coverageReport.clarification_needed_for,
...coverageReport.out_of_scope_requirements
], stage1Contracts_1.INVESTIGATION_MAX_REQUIREMENT_LINKS);
}
function collectEvidenceSummary(retrievalResults) {
const lines = retrievalResults.map((result) => {
const requirementRef = result.requirement_ids[0] ?? result.fragment_id;
return `${requirementRef}:${result.status}:${result.route}`;
});
return capStrings(lines, 6);
}
function settlementFocusActions(activeDomain) {
if (activeDomain !== "settlements_60_62") {
return [];
}
return [
"Проверьте договор и объект расчетов по платежу.",
"Сверьте регистр расчетов и привязку платежа к закрывающему документу.",
"Проверьте зачет аванса или взаимозачет по связке 60/62."
];
}
function normalizeEntityBacklinks(values) {
const result = [];
const seen = new Set();
@@ -180,7 +252,27 @@ function cloneInvestigationState(state) {
followup_context: state.followup_context
? {
...state.followup_context,
referenced_requirement_ids: [...state.followup_context.referenced_requirement_ids]
referenced_requirement_ids: [...state.followup_context.referenced_requirement_ids],
...(state.followup_context.active_requirement_ids
? {
active_requirement_ids: [...state.followup_context.active_requirement_ids]
}
: {}),
...(state.followup_context.uncovered_requirement_ids
? {
uncovered_requirement_ids: [...state.followup_context.uncovered_requirement_ids]
}
: {}),
...(state.followup_context.settlement_next_actions
? {
settlement_next_actions: [...state.followup_context.settlement_next_actions]
}
: {}),
...(state.followup_context.evidence_summary
? {
evidence_summary: [...state.followup_context.evidence_summary]
}
: {})
}
: null
};
@@ -222,9 +314,21 @@ function createEmptyInvestigationState(sessionId, timestamp = new Date().toISOSt
function updateInvestigationState(input) {
const previous = input.previous;
const focusFromMessage = capStrings(detectAccounts(input.userMessage), stage1Contracts_1.INVESTIGATION_MAX_PRIMARY_ACCOUNTS);
const mergedFocusAccounts = capStrings([...focusFromMessage, ...previous.focus.primary_accounts], stage1Contracts_1.INVESTIGATION_MAX_PRIMARY_ACCOUNTS);
const requirementIds = capStrings(input.requirements.map((item) => item.requirement_id), stage1Contracts_1.INVESTIGATION_MAX_REQUIREMENT_LINKS);
const mainRequirement = input.requirements[0]?.requirement_text ?? input.userMessage;
const problemUnitState = updateProblemUnitState(previous, input.retrievalResults);
const uncoveredRequirementIds = collectUncoveredRequirementIds(input.coverageReport);
const activeDomain = inferFollowupActiveDomain({
userMessage: input.userMessage,
focusAccounts: mergedFocusAccounts,
routeSummary: input.routeSummary,
previous
});
const focusDomain = activeDomain ?? deriveDomain(input.routeSummary) ?? previous.focus.domain;
const settlementNextActions = settlementFocusActions(activeDomain);
const lastProblemUnitId = problemUnitState?.active_problem_units[0] ?? null;
const evidenceSummary = collectEvidenceSummary(input.retrievalResults);
return {
schema_version: stage1Contracts_1.INVESTIGATION_STATE_SCHEMA_VERSION,
session_id: previous.session_id,
@@ -233,9 +337,9 @@ function updateInvestigationState(input) {
updated_at: input.timestamp,
question_id: input.questionId,
focus: {
domain: deriveDomain(input.routeSummary) ?? previous.focus.domain,
domain: focusDomain,
period: detectPeriod(input.userMessage) ?? previous.focus.period,
primary_accounts: capStrings([...focusFromMessage, ...previous.focus.primary_accounts], stage1Contracts_1.INVESTIGATION_MAX_PRIMARY_ACCOUNTS),
primary_accounts: mergedFocusAccounts,
active_query_subject: mainRequirement.slice(0, 180)
},
narrowing_status: deriveNarrowingStatus(input.routeSummary, input.coverageReport),
@@ -245,7 +349,13 @@ function updateInvestigationState(input) {
followup_context: {
previous_question_id: previous.question_id,
last_user_message: input.userMessage.slice(0, 240),
referenced_requirement_ids: requirementIds
referenced_requirement_ids: requirementIds,
active_domain: activeDomain,
active_requirement_ids: requirementIds,
uncovered_requirement_ids: uncoveredRequirementIds,
last_problem_unit_id: lastProblemUnitId,
settlement_next_actions: settlementNextActions,
evidence_summary: evidenceSummary
},
query_mode_hint: deriveQueryModeHint(input.routeSummary),
...(problemUnitState