Stage_04_Wave_15_Question_Type_Contract_First_Check_Relevance
This commit is contained in:
parent
8b84f5e989
commit
014ff65188
|
|
@ -2519,12 +2519,27 @@ function extractRequirementIdsFromText(value) {
|
||||||
const matches = String(value ?? "").match(/\bR\d+\b/gi);
|
const matches = String(value ?? "").match(/\bR\d+\b/gi);
|
||||||
return uniqueStrings((matches ?? []).map((item) => item.toUpperCase()), 8);
|
return uniqueStrings((matches ?? []).map((item) => item.toUpperCase()), 8);
|
||||||
}
|
}
|
||||||
function buildCoverageSplitLines(structure) {
|
function buildCoverageSplitLines(structure, questionType = "unknown") {
|
||||||
const confirmed = uniqueStrings((structure.evidence_block.claim_evidence_links ?? [])
|
const confirmed = uniqueStrings((structure.evidence_block.claim_evidence_links ?? [])
|
||||||
.flatMap((item) => extractRequirementIdsFromText(item.claim_ref))
|
.flatMap((item) => extractRequirementIdsFromText(item.claim_ref))
|
||||||
.map((item) => item.toUpperCase()), 8);
|
.map((item) => item.toUpperCase()), 8);
|
||||||
const unresolved = uniqueStrings(structure.uncertainty_block.open_uncertainties.flatMap((item) => extractRequirementIdsFromText(item)), 8);
|
const unresolved = uniqueStrings(structure.uncertainty_block.open_uncertainties.flatMap((item) => extractRequirementIdsFromText(item)), 8);
|
||||||
const lines = [];
|
const lines = [];
|
||||||
|
if (questionType === "which_chains_are_complete_vs_incomplete") {
|
||||||
|
if (confirmed.length > 0) {
|
||||||
|
lines.push(`Цепочки подтверждены: ${confirmed.join(", ")}.`);
|
||||||
|
}
|
||||||
|
if (unresolved.length > 0) {
|
||||||
|
lines.push(`Цепочки подтверждены частично или не подтверждены: ${unresolved.join(", ")}.`);
|
||||||
|
}
|
||||||
|
else if (structure.evidence_block.coverage_note === "coverage_partial_or_limited") {
|
||||||
|
lines.push("Часть цепочек подтверждена частично; для остальных не хватает опоры.");
|
||||||
|
}
|
||||||
|
if (lines.length === 0) {
|
||||||
|
lines.push("Цепочки пока не удалось уверенно разделить на полные и неполные.");
|
||||||
|
}
|
||||||
|
return dedupeNarrativeLines(lines, 3);
|
||||||
|
}
|
||||||
if (confirmed.length > 0) {
|
if (confirmed.length > 0) {
|
||||||
lines.push(`Подтверждено по требованиям: ${confirmed.join(", ")}.`);
|
lines.push(`Подтверждено по требованиям: ${confirmed.join(", ")}.`);
|
||||||
}
|
}
|
||||||
|
|
@ -2536,7 +2551,7 @@ function buildCoverageSplitLines(structure) {
|
||||||
}
|
}
|
||||||
return dedupeNarrativeLines(lines, 3);
|
return dedupeNarrativeLines(lines, 3);
|
||||||
}
|
}
|
||||||
function buildEvidenceSectionLines(structure) {
|
function buildEvidenceSectionLines(structure, questionType = "unknown") {
|
||||||
const evidenceCount = Array.isArray(structure.evidence_block.evidence_ids) ? structure.evidence_block.evidence_ids.length : 0;
|
const evidenceCount = Array.isArray(structure.evidence_block.evidence_ids) ? structure.evidence_block.evidence_ids.length : 0;
|
||||||
const sourceCount = Array.isArray(structure.evidence_block.source_refs) ? structure.evidence_block.source_refs.length : 0;
|
const sourceCount = Array.isArray(structure.evidence_block.source_refs) ? structure.evidence_block.source_refs.length : 0;
|
||||||
const claimLinks = Array.isArray(structure.evidence_block.claim_evidence_links)
|
const claimLinks = Array.isArray(structure.evidence_block.claim_evidence_links)
|
||||||
|
|
@ -2547,7 +2562,16 @@ function buildEvidenceSectionLines(structure) {
|
||||||
structure.uncertainty_block.open_uncertainties.length > 0 ||
|
structure.uncertainty_block.open_uncertainties.length > 0 ||
|
||||||
structure.evidence_block.coverage_note === "coverage_partial_or_limited";
|
structure.evidence_block.coverage_note === "coverage_partial_or_limited";
|
||||||
const lines = [];
|
const lines = [];
|
||||||
const coverageSplitLines = buildCoverageSplitLines(structure);
|
const coverageSplitLines = buildCoverageSplitLines(structure, questionType);
|
||||||
|
if (questionType === "what_is_it_grounded_on") {
|
||||||
|
lines.push("Основание вывода перечислено по подтвержденным документам, регистрам и проводкам.");
|
||||||
|
}
|
||||||
|
else if (questionType === "prove_or_guess") {
|
||||||
|
lines.push("Основание разделено на подтвержденную часть и зону гипотез.");
|
||||||
|
}
|
||||||
|
else if (questionType === "which_chains_are_complete_vs_incomplete") {
|
||||||
|
lines.push("Опора собрана так, чтобы разделить цепочки на полные и неполные.");
|
||||||
|
}
|
||||||
if (evidenceCount > 0) {
|
if (evidenceCount > 0) {
|
||||||
lines.push(`Вывод опирается на ${evidenceCount} подтвержденных наблюдений в текущем срезе.`);
|
lines.push(`Вывод опирается на ${evidenceCount} подтвержденных наблюдений в текущем срезе.`);
|
||||||
}
|
}
|
||||||
|
|
@ -2589,7 +2613,111 @@ function buildDefaultChecksByDomain(domain) {
|
||||||
}
|
}
|
||||||
return ["Проверьте связку документов и проводок по проблемному участку в указанном периоде."];
|
return ["Проверьте связку документов и проводок по проблемному участку в указанном периоде."];
|
||||||
}
|
}
|
||||||
function buildChecksSectionLines(structure) {
|
function buildQuestionTypeDomainChecks(questionType, domain) {
|
||||||
|
if (questionType === "what_to_check_first") {
|
||||||
|
if (domain === "settlements_60_62") {
|
||||||
|
return [
|
||||||
|
"Сверьте договор и объект расчетов по спорной операции.",
|
||||||
|
"Проверьте регистр расчетов и зачет аванса/взаимозачет.",
|
||||||
|
"Подтвердите проводки по 60/62/76 и факт закрытия хвоста."
|
||||||
|
];
|
||||||
|
}
|
||||||
|
if (domain === "vat_document_register_book") {
|
||||||
|
return [
|
||||||
|
"Сверьте исходный документ и счет-фактуру.",
|
||||||
|
"Проверьте запись в регистре НДС и попадание в книгу.",
|
||||||
|
"Подтвердите налоговые проводки по 19/68 в нужном периоде."
|
||||||
|
];
|
||||||
|
}
|
||||||
|
if (domain === "month_close_costs_20_44") {
|
||||||
|
return [
|
||||||
|
"Проверьте регламентную операцию закрытия за нужный период.",
|
||||||
|
"Сверьте базу распределения затрат и проводки по 20/25/26/44.",
|
||||||
|
"Убедитесь, что остатки объяснены или закрыты после операции."
|
||||||
|
];
|
||||||
|
}
|
||||||
|
return ["Начните с первого подтверждаемого документа и пройдите цепочку без пропусков."];
|
||||||
|
}
|
||||||
|
if (questionType === "where_break_is") {
|
||||||
|
if (domain === "settlements_60_62") {
|
||||||
|
return [
|
||||||
|
"Локализуйте разрыв в узле: договор -> объект расчетов -> регистр расчетов -> закрывающий документ.",
|
||||||
|
"Сверьте, где прерывается переход платеж -> зачет/закрытие -> проводки 60/62/76."
|
||||||
|
];
|
||||||
|
}
|
||||||
|
if (domain === "vat_document_register_book") {
|
||||||
|
return [
|
||||||
|
"Локализуйте разрыв в узле: документ -> счет-фактура -> регистр НДС -> книга.",
|
||||||
|
"Сверьте, где прерывается переход от исходного документа к налоговой записи."
|
||||||
|
];
|
||||||
|
}
|
||||||
|
if (domain === "month_close_costs_20_44") {
|
||||||
|
return [
|
||||||
|
"Локализуйте разрыв в узле: накопление затрат -> правило распределения -> операция закрытия.",
|
||||||
|
"Сверьте, на каком шаге исчезает подтверждение перехода к закрытию остатков."
|
||||||
|
];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (questionType === "prove_or_guess") {
|
||||||
|
if (domain === "settlements_60_62") {
|
||||||
|
return [
|
||||||
|
"Отдельно отметьте, что доказано документами и проводками, а что остается гипотезой.",
|
||||||
|
"Для доказательства проверьте связку платеж -> расчетный документ -> регистр расчетов -> 60/62/76."
|
||||||
|
];
|
||||||
|
}
|
||||||
|
if (domain === "vat_document_register_book") {
|
||||||
|
return [
|
||||||
|
"Разделите доказанное и предположительное по цепочке: документ -> счет-фактура -> регистр -> книга.",
|
||||||
|
"Подтвердите налоговую запись по 19/68 в нужном периоде."
|
||||||
|
];
|
||||||
|
}
|
||||||
|
if (domain === "month_close_costs_20_44") {
|
||||||
|
return [
|
||||||
|
"Разделите доказанные и предположительные участки в цепочке закрытия месяца.",
|
||||||
|
"Проверьте подтверждение: операция закрытия -> распределение -> остатки по 20/25/26/44."
|
||||||
|
];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (questionType === "what_is_it_grounded_on") {
|
||||||
|
if (domain === "settlements_60_62") {
|
||||||
|
return [
|
||||||
|
"Перечислите основание: платежный документ, расчетный документ, запись регистра расчетов, проводки 60/62/76."
|
||||||
|
];
|
||||||
|
}
|
||||||
|
if (domain === "vat_document_register_book") {
|
||||||
|
return [
|
||||||
|
"Перечислите основание: исходный документ, счет-фактура, запись регистра НДС, запись книги, проводки 19/68."
|
||||||
|
];
|
||||||
|
}
|
||||||
|
if (domain === "month_close_costs_20_44") {
|
||||||
|
return [
|
||||||
|
"Перечислите основание: операция закрытия, база распределения, проводки по затратам, остатки после закрытия."
|
||||||
|
];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (questionType === "which_chains_are_complete_vs_incomplete") {
|
||||||
|
if (domain === "settlements_60_62") {
|
||||||
|
return [
|
||||||
|
"Разделите цепочки на: подтверждена, подтверждена частично, не подтверждена по переходу платеж -> закрытие расчета.",
|
||||||
|
"Проверьте разницу между закрытыми и незакрытыми связками по 60/62/76."
|
||||||
|
];
|
||||||
|
}
|
||||||
|
if (domain === "vat_document_register_book") {
|
||||||
|
return [
|
||||||
|
"Разделите цепочки на: полная, частичная, неполная по связке документ -> счет-фактура -> регистр -> книга.",
|
||||||
|
"Проверьте, где отсутствует подтверждение налоговой записи."
|
||||||
|
];
|
||||||
|
}
|
||||||
|
if (domain === "month_close_costs_20_44") {
|
||||||
|
return [
|
||||||
|
"Разделите цепочки закрытия на полные и неполные по шагам распределения и регламентной операции.",
|
||||||
|
"Проверьте, какие остатки после закрытия подтверждены, а какие нет."
|
||||||
|
];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return buildDefaultChecksByDomain(domain);
|
||||||
|
}
|
||||||
|
function buildChecksSectionLines(structure, context) {
|
||||||
const actionLines = dedupeNarrativeLines([
|
const actionLines = dedupeNarrativeLines([
|
||||||
...structure.next_step_block.recommended_actions,
|
...structure.next_step_block.recommended_actions,
|
||||||
...structure.next_step_block.clarification_questions
|
...structure.next_step_block.clarification_questions
|
||||||
|
|
@ -2601,29 +2729,49 @@ function buildChecksSectionLines(structure) {
|
||||||
.filter((item) => item.length >= 18)
|
.filter((item) => item.length >= 18)
|
||||||
.filter((item) => !/\b(?:factual|source-of-record|reference)\b/i.test(item)), 4);
|
.filter((item) => !/\b(?:factual|source-of-record|reference)\b/i.test(item)), 4);
|
||||||
const broken = sanitizeUserText(structure.direct_answer) ?? "";
|
const broken = sanitizeUserText(structure.direct_answer) ?? "";
|
||||||
const domain = inferNarrativeDomainFromText(broken);
|
const domain = context?.focusDomain ?? inferNarrativeDomainFromText(broken);
|
||||||
const domainFallback = buildDefaultChecksByDomain(domain);
|
const questionType = context?.questionType ?? "unknown";
|
||||||
|
const domainFallback = buildQuestionTypeDomainChecks(questionType, domain);
|
||||||
const hasMissingPeriod = structure.uncertainty_block.open_uncertainties.some((item) => /missing_anchor:period/i.test(String(item ?? "")));
|
const hasMissingPeriod = structure.uncertainty_block.open_uncertainties.some((item) => /missing_anchor:period/i.test(String(item ?? "")));
|
||||||
const lines = [];
|
const lines = [];
|
||||||
if (domain === "settlements_60_62") {
|
if (questionType === "what_to_check_first") {
|
||||||
lines.push(...domainFallback.slice(0, 2));
|
lines.push(...domainFallback.slice(0, 3));
|
||||||
lines.push(...actionLines.slice(0, 2));
|
if (lines.length < 3) {
|
||||||
|
lines.push(...actionLines.slice(0, 3 - lines.length));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
else if (actionLines.length > 0) {
|
else if (questionType === "what_is_it_grounded_on") {
|
||||||
|
lines.push(...domainFallback.slice(0, 2));
|
||||||
|
lines.push(...actionLines.slice(0, 1));
|
||||||
|
}
|
||||||
|
else if (questionType === "prove_or_guess" || questionType === "where_break_is") {
|
||||||
|
lines.push(...domainFallback.slice(0, 2));
|
||||||
lines.push(...actionLines.slice(0, 2));
|
lines.push(...actionLines.slice(0, 2));
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
lines.push(...domainFallback.slice(0, 2));
|
if (domain === "settlements_60_62") {
|
||||||
|
lines.push(...domainFallback.slice(0, 2));
|
||||||
|
lines.push(...actionLines.slice(0, 2));
|
||||||
|
}
|
||||||
|
else if (actionLines.length > 0) {
|
||||||
|
lines.push(...actionLines.slice(0, 2));
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
lines.push(...domainFallback.slice(0, 2));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if (hasMissingPeriod) {
|
if (hasMissingPeriod) {
|
||||||
if (domain === "settlements_60_62" && lines.length > 0) {
|
if (questionType === "what_to_check_first") {
|
||||||
|
lines.push("Уточните период, если он не зафиксирован в исходной формулировке вопроса.");
|
||||||
|
}
|
||||||
|
else if (domain === "settlements_60_62" && lines.length > 0) {
|
||||||
lines.push("Уточните период проверки, чтобы подтвердить проблему без лишнего шума.");
|
lines.push("Уточните период проверки, чтобы подтвердить проблему без лишнего шума.");
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
lines.unshift("Уточните период проверки, чтобы подтвердить проблему без лишнего шума.");
|
lines.unshift("Уточните период проверки, чтобы подтвердить проблему без лишнего шума.");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return dedupeNarrativeLines(lines, 4);
|
return dedupeNarrativeLines(lines, questionType === "what_to_check_first" ? 3 : 5);
|
||||||
}
|
}
|
||||||
function humanizeLimitationToken(value) {
|
function humanizeLimitationToken(value) {
|
||||||
const raw = String(value ?? "").trim();
|
const raw = String(value ?? "").trim();
|
||||||
|
|
@ -2719,19 +2867,19 @@ function domainNameForQuestionType(domain) {
|
||||||
function buildQuestionTypeShortLine(context) {
|
function buildQuestionTypeShortLine(context) {
|
||||||
const domainName = domainNameForQuestionType(context.focusDomain);
|
const domainName = domainNameForQuestionType(context.focusDomain);
|
||||||
if (context.questionType === "where_break_is") {
|
if (context.questionType === "where_break_is") {
|
||||||
return `\u041f\u0440\u0438\u043e\u0440\u0438\u0442\u0435\u0442 \u043e\u0442\u0432\u0435\u0442\u0430: \u043b\u043e\u043a\u0430\u043b\u0438\u0437\u043e\u0432\u0430\u0442\u044c \u0440\u0430\u0437\u0440\u044b\u0432 \u0432\u043d\u0443\u0442\u0440\u0438 ${domainName}.`;
|
return `\u041b\u043e\u043a\u0430\u043b\u0438\u0437\u043e\u0432\u0430\u043d \u043d\u0430\u0438\u0431\u043e\u043b\u0435\u0435 \u0432\u0435\u0440\u043e\u044f\u0442\u043d\u044b\u0439 \u0443\u0437\u0435\u043b \u0440\u0430\u0437\u0440\u044b\u0432\u0430 \u0432\u043d\u0443\u0442\u0440\u0438 ${domainName}.`;
|
||||||
}
|
}
|
||||||
if (context.questionType === "prove_or_guess") {
|
if (context.questionType === "prove_or_guess") {
|
||||||
return "\u041f\u0440\u0438\u043e\u0440\u0438\u0442\u0435\u0442 \u043e\u0442\u0432\u0435\u0442\u0430: \u0440\u0430\u0437\u0432\u0435\u0441\u0442\u0438 \u0434\u043e\u043a\u0430\u0437\u0430\u043d\u043e \u0438 \u0433\u0438\u043f\u043e\u0442\u0435\u0437\u0443.";
|
return "\u0412\u044b\u0432\u043e\u0434 \u0440\u0430\u0437\u0434\u0435\u043b\u0435\u043d \u043d\u0430 \u0434\u043e\u043a\u0430\u0437\u0430\u043d\u043d\u0443\u044e \u0447\u0430\u0441\u0442\u044c \u0438 \u0433\u0438\u043f\u043e\u0442\u0435\u0437\u0443.";
|
||||||
}
|
}
|
||||||
if (context.questionType === "what_is_it_grounded_on") {
|
if (context.questionType === "what_is_it_grounded_on") {
|
||||||
return "\u041f\u0440\u0438\u043e\u0440\u0438\u0442\u0435\u0442 \u043e\u0442\u0432\u0435\u0442\u0430: \u043f\u043e\u043a\u0430\u0437\u0430\u0442\u044c \u043e\u0441\u043d\u043e\u0432\u0430\u043d\u0438\u0435 \u0432\u044b\u0432\u043e\u0434\u0430 \u043f\u043e \u0434\u0430\u043d\u043d\u044b\u043c.";
|
return "\u041d\u0438\u0436\u0435 \u043f\u0435\u0440\u0435\u0447\u0438\u0441\u043b\u0435\u043d\u044b \u043e\u0441\u043d\u043e\u0432\u0430\u043d\u0438\u044f \u0432\u044b\u0432\u043e\u0434\u0430 \u043f\u043e \u0434\u0430\u043d\u043d\u044b\u043c \u0443\u0447\u0435\u0442\u0430.";
|
||||||
}
|
}
|
||||||
if (context.questionType === "which_chains_are_complete_vs_incomplete") {
|
if (context.questionType === "which_chains_are_complete_vs_incomplete") {
|
||||||
return "\u041f\u0440\u0438\u043e\u0440\u0438\u0442\u0435\u0442 \u043e\u0442\u0432\u0435\u0442\u0430: \u0440\u0430\u0437\u0434\u0435\u043b\u0438\u0442\u044c \u0437\u0430\u0432\u0435\u0440\u0448\u0435\u043d\u043d\u044b\u0435 \u0438 \u043d\u0435\u0437\u0430\u0432\u0435\u0440\u0448\u0435\u043d\u043d\u044b\u0435 \u0446\u0435\u043f\u043e\u0447\u043a\u0438.";
|
return "\u0426\u0435\u043f\u043e\u0447\u043a\u0438 \u0440\u0430\u0437\u0434\u0435\u043b\u0435\u043d\u044b \u043d\u0430 \u043f\u043e\u0434\u0442\u0432\u0435\u0440\u0436\u0434\u0435\u043d\u043d\u044b\u0435, \u0447\u0430\u0441\u0442\u0438\u0447\u043d\u043e \u043f\u043e\u0434\u0442\u0432\u0435\u0440\u0436\u0434\u0435\u043d\u043d\u044b\u0435 \u0438 \u043d\u0435\u043f\u043e\u0434\u0442\u0432\u0435\u0440\u0436\u0434\u0435\u043d\u043d\u044b\u0435.";
|
||||||
}
|
}
|
||||||
if (context.questionType === "what_to_check_first") {
|
if (context.questionType === "what_to_check_first") {
|
||||||
return "\u041f\u0440\u0438\u043e\u0440\u0438\u0442\u0435\u0442 \u043e\u0442\u0432\u0435\u0442\u0430: \u0434\u0430\u0442\u044c \u043f\u0435\u0440\u0432\u044b\u0439 \u043c\u0430\u0440\u0448\u0440\u0443\u0442 \u043f\u0440\u043e\u0432\u0435\u0440\u043a\u0438.";
|
return `\u041a\u043e\u0440\u043e\u0442\u043a\u0438\u0439 \u043c\u0430\u0440\u0448\u0440\u0443\u0442 \u043f\u0435\u0440\u0432\u044b\u0445 \u043f\u0440\u043e\u0432\u0435\u0440\u043e\u043a \u0432\u043d\u0443\u0442\u0440\u0438 ${domainName}.`;
|
||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
@ -2751,12 +2899,18 @@ function buildQuestionTypeBrokenLine(context) {
|
||||||
return "\u0412\u0435\u0440\u043e\u044f\u0442\u043d\u044b\u0439 \u0443\u0437\u0435\u043b \u0440\u0430\u0437\u0440\u044b\u0432\u0430 \u043b\u043e\u043a\u0430\u043b\u0438\u0437\u043e\u0432\u0430\u043d \u0447\u0430\u0441\u0442\u0438\u0447\u043d\u043e; \u043d\u0443\u0436\u043d\u0430 \u0442\u043e\u0447\u0435\u0447\u043d\u0430\u044f \u0441\u0432\u0435\u0440\u043a\u0430.";
|
return "\u0412\u0435\u0440\u043e\u044f\u0442\u043d\u044b\u0439 \u0443\u0437\u0435\u043b \u0440\u0430\u0437\u0440\u044b\u0432\u0430 \u043b\u043e\u043a\u0430\u043b\u0438\u0437\u043e\u0432\u0430\u043d \u0447\u0430\u0441\u0442\u0438\u0447\u043d\u043e; \u043d\u0443\u0436\u043d\u0430 \u0442\u043e\u0447\u0435\u0447\u043d\u0430\u044f \u0441\u0432\u0435\u0440\u043a\u0430.";
|
||||||
}
|
}
|
||||||
function buildQuestionTypeWhyLine(context) {
|
function buildQuestionTypeWhyLine(context) {
|
||||||
|
if (context.questionType === "where_break_is") {
|
||||||
|
return "\u0424\u043e\u043a\u0443\u0441 \u043e\u0442\u0432\u0435\u0442\u0430: \u043d\u0435 \u043e\u0431\u0449\u0438\u0439 \u043c\u0435\u0445\u0430\u043d\u0438\u0437\u043c, \u0430 \u0442\u043e\u0447\u043a\u0430 \u0440\u0430\u0437\u0440\u044b\u0432\u0430 \u0432 \u0446\u0435\u043f\u043e\u0447\u043a\u0435.";
|
||||||
|
}
|
||||||
if (context.questionType === "prove_or_guess") {
|
if (context.questionType === "prove_or_guess") {
|
||||||
return "\u0417\u0434\u0435\u0441\u044c \u0447\u0435\u0441\u0442\u043d\u043e \u0440\u0430\u0437\u0432\u043e\u0434\u0438\u0442\u0441\u044f \u0447\u0442\u043e \u0443\u0436\u0435 \u043f\u043e\u0434\u0442\u0432\u0435\u0440\u0436\u0434\u0435\u043d\u043e \u0438 \u0447\u0442\u043e \u043f\u043e\u043a\u0430 \u043e\u0441\u0442\u0430\u0435\u0442\u0441\u044f \u0433\u0438\u043f\u043e\u0442\u0435\u0437\u043e\u0439.";
|
return "\u041e\u0442\u0434\u0435\u043b\u044c\u043d\u043e \u043f\u043e\u043a\u0430\u0437\u0430\u043d\u043e, \u0447\u0442\u043e \u0443\u0436\u0435 \u043f\u043e\u0434\u0442\u0432\u0435\u0440\u0436\u0434\u0435\u043d\u043e, \u0430 \u0447\u0442\u043e \u043f\u043e\u043a\u0430 \u043e\u0441\u0442\u0430\u0435\u0442\u0441\u044f \u0433\u0438\u043f\u043e\u0442\u0435\u0437\u043e\u0439.";
|
||||||
}
|
}
|
||||||
if (context.questionType === "which_chains_are_complete_vs_incomplete") {
|
if (context.questionType === "which_chains_are_complete_vs_incomplete") {
|
||||||
return "\u0426\u0435\u043f\u043e\u0447\u043a\u0438 \u0440\u0430\u0437\u0434\u0435\u043b\u0435\u043d\u044b \u043d\u0430 \u0437\u0430\u0432\u0435\u0440\u0448\u0435\u043d\u043d\u044b\u0435 \u0438 \u043d\u0435\u0437\u0430\u0432\u0435\u0440\u0448\u0435\u043d\u043d\u044b\u0435 \u043f\u043e \u0442\u0435\u043a\u0443\u0449\u0435\u0439 \u043e\u043f\u043e\u0440\u0435.";
|
return "\u0426\u0435\u043f\u043e\u0447\u043a\u0438 \u0440\u0430\u0437\u0434\u0435\u043b\u0435\u043d\u044b \u043d\u0430 \u0437\u0430\u0432\u0435\u0440\u0448\u0435\u043d\u043d\u044b\u0435 \u0438 \u043d\u0435\u0437\u0430\u0432\u0435\u0440\u0448\u0435\u043d\u043d\u044b\u0435 \u043f\u043e \u0442\u0435\u043a\u0443\u0449\u0435\u0439 \u043e\u043f\u043e\u0440\u0435.";
|
||||||
}
|
}
|
||||||
|
if (context.questionType === "what_is_it_grounded_on") {
|
||||||
|
return "\u0424\u043e\u043a\u0443\u0441 \u043e\u0442\u0432\u0435\u0442\u0430 \u0441\u043c\u0435\u0449\u0435\u043d \u0432 \u0434\u043e\u043a\u0430\u0437\u0430\u0442\u0435\u043b\u044c\u043d\u044b\u0435 \u0438\u0441\u0442\u043e\u0447\u043d\u0438\u043a\u0438, \u0430 \u043d\u0435 \u0432 \u043e\u0431\u0449\u0438\u0439 narrative.";
|
||||||
|
}
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
function buildQuestionTypeEvidenceLine(context) {
|
function buildQuestionTypeEvidenceLine(context) {
|
||||||
|
|
@ -2766,6 +2920,9 @@ function buildQuestionTypeEvidenceLine(context) {
|
||||||
if (context.questionType === "prove_or_guess") {
|
if (context.questionType === "prove_or_guess") {
|
||||||
return "\u0421\u0438\u043b\u0430 \u0432\u044b\u0432\u043e\u0434\u0430 \u043e\u0446\u0435\u043d\u0435\u043d\u0430 \u043f\u043e \u043f\u0440\u044f\u043c\u043e\u0439 \u043e\u043f\u043e\u0440\u0435, \u0430 \u043d\u0435 \u043f\u043e \u0434\u043e\u0433\u0430\u0434\u043a\u0430\u043c.";
|
return "\u0421\u0438\u043b\u0430 \u0432\u044b\u0432\u043e\u0434\u0430 \u043e\u0446\u0435\u043d\u0435\u043d\u0430 \u043f\u043e \u043f\u0440\u044f\u043c\u043e\u0439 \u043e\u043f\u043e\u0440\u0435, \u0430 \u043d\u0435 \u043f\u043e \u0434\u043e\u0433\u0430\u0434\u043a\u0430\u043c.";
|
||||||
}
|
}
|
||||||
|
if (context.questionType === "which_chains_are_complete_vs_incomplete") {
|
||||||
|
return "\u041e\u043f\u043e\u0440\u0430 \u0441\u043e\u0431\u0440\u0430\u043d\u0430 \u0442\u0430\u043a, \u0447\u0442\u043e\u0431\u044b \u0447\u0435\u0441\u0442\u043d\u043e \u0440\u0430\u0437\u0434\u0435\u043b\u0438\u0442\u044c \u043f\u043e\u043b\u043d\u044b\u0435 \u0438 \u043d\u0435\u043f\u043e\u043b\u043d\u044b\u0435 \u0446\u0435\u043f\u043e\u0447\u043a\u0438.";
|
||||||
|
}
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
function formatAnchorList(anchors, prefix) {
|
function formatAnchorList(anchors, prefix) {
|
||||||
|
|
@ -2776,7 +2933,19 @@ function formatAnchorList(anchors, prefix) {
|
||||||
}
|
}
|
||||||
function buildQuestionTypeCheckLine(context) {
|
function buildQuestionTypeCheckLine(context) {
|
||||||
if (context.questionType === "what_to_check_first") {
|
if (context.questionType === "what_to_check_first") {
|
||||||
return "\u041d\u0430\u0447\u043d\u0438\u0442\u0435 \u0441 \u043f\u0435\u0440\u0432\u043e\u0433\u043e \u043f\u0443\u043d\u043a\u0442\u0430 \u0438 \u043f\u0440\u043e\u0439\u0434\u0438\u0442\u0435 \u043c\u0430\u0440\u0448\u0440\u0443\u0442 \u043f\u043e\u0441\u043b\u0435\u0434\u043e\u0432\u0430\u0442\u0435\u043b\u044c\u043d\u043e, \u0431\u0435\u0437 \u043f\u0435\u0440\u0435\u0441\u043a\u043e\u043a\u0430.";
|
return "\u041f\u0440\u043e\u0432\u0435\u0440\u044c\u0442\u0435 \u043f\u0443\u043d\u043a\u0442\u044b \u043f\u043e \u043f\u043e\u0440\u044f\u0434\u043a\u0443: \u0448\u0430\u0433 1 -> \u0448\u0430\u0433 2 -> \u0448\u0430\u0433 3.";
|
||||||
|
}
|
||||||
|
if (context.questionType === "where_break_is") {
|
||||||
|
return "\u041b\u043e\u043a\u0430\u043b\u0438\u0437\u0430\u0446\u0438\u044e \u0440\u0430\u0437\u0440\u044b\u0432\u0430 \u043d\u0430\u0447\u043d\u0438\u0442\u0435 \u0441 \u0443\u0437\u043b\u0430, \u0433\u0434\u0435 \u043f\u0435\u0440\u0435\u0445\u043e\u0434 \u043f\u0435\u0440\u0435\u0441\u0442\u0430\u043b \u043f\u043e\u0434\u0442\u0432\u0435\u0440\u0436\u0434\u0430\u0442\u044c\u0441\u044f.";
|
||||||
|
}
|
||||||
|
if (context.questionType === "prove_or_guess") {
|
||||||
|
return "\u041f\u0435\u0440\u0432\u044b\u043c \u0434\u0435\u0439\u0441\u0442\u0432\u0438\u0435\u043c \u043e\u0442\u0434\u0435\u043b\u0438\u0442\u0435 \u0434\u043e\u043a\u0443\u043c\u0435\u043d\u0442\u0430\u043b\u044c\u043d\u043e \u043f\u043e\u0434\u0442\u0432\u0435\u0440\u0436\u0434\u0435\u043d\u043d\u044b\u0435 \u0444\u0430\u043a\u0442\u044b \u043e\u0442 \u0433\u0438\u043f\u043e\u0442\u0435\u0437.";
|
||||||
|
}
|
||||||
|
if (context.questionType === "what_is_it_grounded_on") {
|
||||||
|
return "\u0421\u043d\u0430\u0447\u0430\u043b\u0430 \u043f\u0435\u0440\u0435\u0447\u0438\u0441\u043b\u0438\u0442\u0435 \u043e\u043f\u043e\u0440\u043d\u044b\u0435 \u0434\u043e\u043a\u0443\u043c\u0435\u043d\u0442\u044b \u0438 \u0440\u0435\u0433\u0438\u0441\u0442\u0440\u044b, \u0437\u0430\u0442\u0435\u043c \u043f\u043e\u0434\u0442\u0432\u0435\u0440\u0436\u0434\u0430\u044e\u0449\u0438\u0435 \u043f\u0440\u043e\u0432\u043e\u0434\u043a\u0438.";
|
||||||
|
}
|
||||||
|
if (context.questionType === "which_chains_are_complete_vs_incomplete") {
|
||||||
|
return "\u0421\u043d\u0430\u0447\u0430\u043b\u0430 \u0440\u0430\u0437\u043b\u043e\u0436\u0438\u0442\u0435 \u0446\u0435\u043f\u043e\u0447\u043a\u0438 \u043d\u0430 \u043f\u043e\u043b\u043d\u044b\u0435, \u0447\u0430\u0441\u0442\u0438\u0447\u043d\u043e \u043f\u043e\u043b\u043d\u044b\u0435 \u0438 \u043d\u0435\u043f\u043e\u0434\u0442\u0432\u0435\u0440\u0436\u0434\u0435\u043d\u043d\u044b\u0435.";
|
||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
@ -2787,6 +2956,15 @@ function buildQuestionTypeLimitationLine(context) {
|
||||||
if (context.questionType === "which_chains_are_complete_vs_incomplete") {
|
if (context.questionType === "which_chains_are_complete_vs_incomplete") {
|
||||||
return "\u0414\u0435\u043b\u0435\u043d\u0438\u0435 \u043d\u0430 \u00abcomplete/incomplete\u00bb \u0437\u0430\u0432\u0438\u0441\u0438\u0442 \u043e\u0442 \u043f\u043e\u043b\u043d\u043e\u0442\u044b \u0446\u0435\u043f\u043e\u0447\u043a\u0438 \u0432 \u0442\u0435\u043a\u0443\u0449\u0435\u043c \u0441\u0440\u0435\u0437\u0435.";
|
return "\u0414\u0435\u043b\u0435\u043d\u0438\u0435 \u043d\u0430 \u00abcomplete/incomplete\u00bb \u0437\u0430\u0432\u0438\u0441\u0438\u0442 \u043e\u0442 \u043f\u043e\u043b\u043d\u043e\u0442\u044b \u0446\u0435\u043f\u043e\u0447\u043a\u0438 \u0432 \u0442\u0435\u043a\u0443\u0449\u0435\u043c \u0441\u0440\u0435\u0437\u0435.";
|
||||||
}
|
}
|
||||||
|
if (context.questionType === "where_break_is") {
|
||||||
|
return "\u0422\u043e\u0447\u043d\u0430\u044f \u043b\u043e\u043a\u0430\u043b\u0438\u0437\u0430\u0446\u0438\u044f \u043c\u043e\u0436\u0435\u0442 \u0441\u043c\u0435\u0449\u0430\u0442\u044c\u0441\u044f, \u0435\u0441\u043b\u0438 \u0447\u0430\u0441\u0442\u044c \u043f\u0435\u0440\u0435\u0445\u043e\u0434\u043e\u0432 \u0432 \u0446\u0435\u043f\u043e\u0447\u043a\u0435 \u043d\u0435 \u043f\u043e\u0434\u0442\u0432\u0435\u0440\u0436\u0434\u0435\u043d\u0430.";
|
||||||
|
}
|
||||||
|
if (context.questionType === "what_is_it_grounded_on") {
|
||||||
|
return "\u0412 \u043e\u0441\u043d\u043e\u0432\u0430\u043d\u0438\u0435 \u0432\u043a\u043b\u044e\u0447\u0435\u043d\u044b \u0442\u043e\u043b\u044c\u043a\u043e \u043f\u043e\u0434\u0442\u0432\u0435\u0440\u0436\u0434\u0435\u043d\u043d\u044b\u0435 \u0438\u0441\u0442\u043e\u0447\u043d\u0438\u043a\u0438; \u043d\u0435\u043f\u043e\u0434\u0442\u0432\u0435\u0440\u0436\u0434\u0435\u043d\u043d\u044b\u0435 \u0432\u044b\u043d\u0435\u0441\u0435\u043d\u044b \u0432 \u043e\u0433\u0440\u0430\u043d\u0438\u0447\u0435\u043d\u0438\u044f.";
|
||||||
|
}
|
||||||
|
if (context.questionType === "what_to_check_first") {
|
||||||
|
return "\u041c\u0430\u0440\u0448\u0440\u0443\u0442 \u043f\u0435\u0440\u0432\u0438\u0447\u043d\u044b\u0439 \u0438 \u043c\u043e\u0436\u0435\u0442 \u0443\u0442\u043e\u0447\u043d\u044f\u0442\u044c\u0441\u044f \u043f\u043e\u0441\u043b\u0435 \u043f\u0435\u0440\u0432\u043e\u0433\u043e \u0448\u0430\u0433\u0430.";
|
||||||
|
}
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
function applyQuestionTypeAndAnchorPolicy(input) {
|
function applyQuestionTypeAndAnchorPolicy(input) {
|
||||||
|
|
@ -2795,8 +2973,8 @@ function applyQuestionTypeAndAnchorPolicy(input) {
|
||||||
const nextWhy = dedupeNarrativeLines([buildQuestionTypeWhyLine(input.context), ...input.whyLines].filter((item) => Boolean(item)), 4);
|
const nextWhy = dedupeNarrativeLines([buildQuestionTypeWhyLine(input.context), ...input.whyLines].filter((item) => Boolean(item)), 4);
|
||||||
const anchorUsedLine = formatAnchorList(input.context.anchors.used, "\u0412 \u043e\u043f\u043e\u0440\u0435 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u043d\u044b \u044f\u043a\u043e\u0440\u044f \u0432\u043e\u043f\u0440\u043e\u0441\u0430");
|
const anchorUsedLine = formatAnchorList(input.context.anchors.used, "\u0412 \u043e\u043f\u043e\u0440\u0435 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u043d\u044b \u044f\u043a\u043e\u0440\u044f \u0432\u043e\u043f\u0440\u043e\u0441\u0430");
|
||||||
const anchorUnusedLine = formatAnchorList(input.context.anchors.unused, "\u042f\u043a\u043e\u0440\u044f \u0438\u0437 \u0432\u043e\u043f\u0440\u043e\u0441\u0430 \u0431\u0435\u0437 \u043f\u0440\u044f\u043c\u043e\u0433\u043e \u043f\u043e\u0434\u0442\u0432\u0435\u0440\u0436\u0434\u0435\u043d\u0438\u044f");
|
const anchorUnusedLine = formatAnchorList(input.context.anchors.unused, "\u042f\u043a\u043e\u0440\u044f \u0438\u0437 \u0432\u043e\u043f\u0440\u043e\u0441\u0430 \u0431\u0435\u0437 \u043f\u0440\u044f\u043c\u043e\u0433\u043e \u043f\u043e\u0434\u0442\u0432\u0435\u0440\u0436\u0434\u0435\u043d\u0438\u044f");
|
||||||
const nextEvidence = dedupeNarrativeLines([buildQuestionTypeEvidenceLine(input.context), ...input.evidenceLines, anchorUsedLine].filter((item) => Boolean(item)), 7);
|
const nextEvidence = dedupeNarrativeLines([buildQuestionTypeEvidenceLine(input.context), ...input.evidenceLines, anchorUsedLine].filter((item) => Boolean(item)), input.context.questionType === "what_to_check_first" ? 4 : 7);
|
||||||
const nextChecks = dedupeNarrativeLines([buildQuestionTypeCheckLine(input.context), ...input.checkLines].filter((item) => Boolean(item)), 5);
|
const nextChecks = dedupeNarrativeLines([buildQuestionTypeCheckLine(input.context), ...input.checkLines].filter((item) => Boolean(item)), input.context.questionType === "what_to_check_first" ? 3 : 5);
|
||||||
const nextLimitations = dedupeNarrativeLines([buildQuestionTypeLimitationLine(input.context), anchorUnusedLine, ...input.limitationLines].filter((item) => Boolean(item)), 6);
|
const nextLimitations = dedupeNarrativeLines([buildQuestionTypeLimitationLine(input.context), anchorUnusedLine, ...input.limitationLines].filter((item) => Boolean(item)), 6);
|
||||||
return {
|
return {
|
||||||
shortLine: ensureSentence(nextShort),
|
shortLine: ensureSentence(nextShort),
|
||||||
|
|
@ -2808,11 +2986,12 @@ function applyQuestionTypeAndAnchorPolicy(input) {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
function renderPolicyReply(structure, context) {
|
function renderPolicyReply(structure, context) {
|
||||||
|
const questionType = context?.questionType ?? "unknown";
|
||||||
const shortLine = ensureSentence(buildShortSectionLine(structure));
|
const shortLine = ensureSentence(buildShortSectionLine(structure));
|
||||||
const brokenLines = buildBrokenSectionLines(structure);
|
const brokenLines = buildBrokenSectionLines(structure);
|
||||||
const whyLines = buildWhySectionLines(structure);
|
const whyLines = buildWhySectionLines(structure);
|
||||||
const evidenceLines = buildEvidenceSectionLines(structure);
|
const evidenceLines = buildEvidenceSectionLines(structure, questionType);
|
||||||
const checkLines = buildChecksSectionLines(structure);
|
const checkLines = buildChecksSectionLines(structure, context);
|
||||||
const limitationLines = buildLimitationsSectionLines(structure);
|
const limitationLines = buildLimitationsSectionLines(structure);
|
||||||
const enriched = context
|
const enriched = context
|
||||||
? applyQuestionTypeAndAnchorPolicy({
|
? applyQuestionTypeAndAnchorPolicy({
|
||||||
|
|
|
||||||
|
|
@ -4,40 +4,89 @@ exports.resolveQuestionType = resolveQuestionType;
|
||||||
const QUESTION_TYPE_RULES = [
|
const QUESTION_TYPE_RULES = [
|
||||||
{
|
{
|
||||||
type: "what_to_check_first",
|
type: "what_to_check_first",
|
||||||
pattern: /(?:\bwhat\s+to\s+check\s+first\b|\bfirst\s+check\b|\bcheck\s+first\b|\u0441\s+\u0447\u0435\u0433\u043e\s+\u043d\u0430\u0447\u0430\u0442\u044c\s+\u043f\u0440\u043e\u0432\u0435\u0440\u043a|\u0447\u0442\u043e\s+\u043f\u0440\u043e\u0432\u0435\u0440\u0438\u0442\u044c\s+\u043f\u0435\u0440\u0432)/iu
|
priority: 1,
|
||||||
},
|
patterns: [
|
||||||
{
|
/(?:\bwhat\s+to\s+check\s+first\b|\bfirst\s+check\b|\bcheck\s+first\b)/iu,
|
||||||
type: "what_is_it_grounded_on",
|
/(?:что\s+проверить\s+перв(?:ым|ой)|с\s+чего\s+начать\s+проверк|перв(?:ый|ым)\s+шаг(?:ом)?\s+проверк)/iu
|
||||||
pattern: /(?:\bwhat\s+is\s+it\s+grounded\s+on\b|\bgrounded\s+on\b|\bbased\s+on\b|\bwhat\s+evidence\b|\u043d\u0430\s+\u0447(?:\u0435|\u0451)\u043c\s+\u044d\u0442\u043e\s+\u043e\u0441\u043d\u043e\u0432\u0430\u043d|\u0447\u0435\u043c\s+\u043f\u043e\u0434\u0442\u0432\u0435\u0440\u0436\u0434)/iu
|
]
|
||||||
},
|
|
||||||
{
|
|
||||||
type: "prove_or_guess",
|
|
||||||
pattern: /(?:\bprove\b|\bguess\b|\bprove\s+or\s+guess\b|\bis\s+it\s+proven\b|\u044d\u0442\u043e\s+\u0434\u043e\u043a\u0430\u0437\u0430\u043d|\u0438\u043b\u0438\s+\u0442\u043e\u043b\u044c\u043a\u043e\s+\u0433\u0438\u043f\u043e\u0442\u0435\u0437|\u0434\u043e\u043a\u0430\u0437\u0430\u043d|\u0434\u043e\u0433\u0430\u0434|\u0435\u0441\u0442\u044c\s+\u043b\u0438|\u043c\u043e\u0436\u0435\u0442\s+\u043b\u0438|\u044d\u0442\u043e\s+\u0443\u0436\u0435.*\u0438\u043b\u0438)/iu
|
|
||||||
},
|
|
||||||
{
|
|
||||||
type: "which_chains_are_complete_vs_incomplete",
|
|
||||||
pattern: /(?:\bcomplete(?:d)?\b.*\bincomplete\b|\bwhich\s+chains?\b|\bcomplete\s+vs\s+incomplete\b|\u043a\u0430\u043a\u0438\u0435\s+\u0446\u0435\u043f\u043e\u0447\u043a[аи]\s+.*\u0437\u0430\u0432\u0435\u0440\u0448|\u0447\u0442\u043e\s+\u0437\u0430\u043a\u0440\u044b\u0442\u043e.*\u0447\u0442\u043e\s+\u043d\u0435\u0442)/iu
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
type: "where_break_is",
|
type: "where_break_is",
|
||||||
pattern: /(?:\bwhere\s+is\s+the\s+break\b|\bwhere\s+exactly\b|\blocate\b|\u0433\u0434\u0435\s+\u0438\u043c\u0435\u043d\u043d\u043e|\u0433\u0434\u0435\s+\u0440\u0430\u0437\u0440\u044b\u0432|\u0432\s+\u043a\u0430\u043a\u043e\u043c\s+\u043c\u0435\u0441\u0442\u0435)/iu
|
priority: 2,
|
||||||
|
patterns: [
|
||||||
|
/(?:\bwhere\s+is\s+the\s+break\b|\bwhere\s+exactly\b|\blocate(?:\s+the\s+break)?\b)/iu,
|
||||||
|
/(?:где\s+именно|где\s+разрыв|в\s+каком\s+месте|на\s+каком\s+этапе|локализ(?:овать|аци)|какой\s+узел)/iu
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: "which_chains_are_complete_vs_incomplete",
|
||||||
|
priority: 3,
|
||||||
|
patterns: [
|
||||||
|
/(?:\bcomplete(?:d)?\b.*\bincomplete\b|\bwhich\s+chains?\b|\bcomplete\s+vs\s+incomplete\b)/iu,
|
||||||
|
/(?:какие(?:\s+\S+){0,4}\s+цепочк[аи].*(?:заверш|полны|неполны|не\s+заверш|подтвержд)|что\s+закрыто.*что\s+нет)/iu,
|
||||||
|
/(?:цепочк[аи].*(?:полная|неполная|частич|выпадени)|отраж[её]н\s+частич.*документ.*сч[её]т[-\s]?фактур)/iu
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: "prove_or_guess",
|
||||||
|
priority: 4,
|
||||||
|
patterns: [
|
||||||
|
/(?:\bprove\b|\bguess\b|\bprove\s+or\s+guess\b|\bis\s+it\s+proven\b)/iu,
|
||||||
|
/(?:это\s+доказан|докаж(?:и|ите|ите\s+ли)|доказуем|доказательн|гипотез|догад(?:ка|ыв))/iu,
|
||||||
|
/(?:доказано\s+или\s+нет|похоже\s+или\s+доказано|зач[её]л(?:ся|ось)\s+ли|связан\s+ли|вс[её]\s+ли.*закрыл|больше\s+похоже.*или)/iu,
|
||||||
|
/(?:это\s+уже\s+[^?!.]{3,}\s+или|есть\s+ли\s+[^?!.]{0,80}ситуац[^?!.]{0,80}\s+где)/iu
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: "what_is_it_grounded_on",
|
||||||
|
priority: 5,
|
||||||
|
patterns: [
|
||||||
|
/(?:\bwhat\s+is\s+it\s+grounded\s+on\b|\bgrounded\s+on\b|\bbased\s+on\b|\bwhat\s+evidence\b)/iu,
|
||||||
|
/(?:на\s+ч(?:е|ё)м[^?!.]{0,40}основан|чем\s+подтвержда(?:ется|но)|какие\s+основани[яе]|какими\s+доказательств)/iu,
|
||||||
|
/(?:есть\s+ли\s+[^?!.]{0,80}признак[аи])/iu
|
||||||
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
type: "why_breaks",
|
type: "why_breaks",
|
||||||
pattern: /(?:\bwhy\b|\bwhy\s+does\s+it\s+break\b|\u043f\u043e\u0447\u0435\u043c\u0443|\u0432\s+\u0447(?:\u0435|\u0451)\u043c\s+\u043f\u0440\u0438\u0447\u0438\u043d\u0430|\u0438\u0437-\u0437\u0430\s+\u0447\u0435\u0433\u043e)/iu
|
priority: 6,
|
||||||
|
patterns: [
|
||||||
|
/(?:\bwhy\b|\bwhy\s+does\s+it\s+break\b|\bwhat\s+causes\b)/iu,
|
||||||
|
/(?:почему|в\s+ч(?:е|ё)м\s+причина|из-?за\s+чего|откуда\s+разрыв)/iu
|
||||||
|
]
|
||||||
}
|
}
|
||||||
];
|
];
|
||||||
|
function countRuleHits(text, rule) {
|
||||||
|
let hits = 0;
|
||||||
|
for (const pattern of rule.patterns) {
|
||||||
|
if (pattern.test(text)) {
|
||||||
|
hits += 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return hits;
|
||||||
|
}
|
||||||
function resolveQuestionType(input) {
|
function resolveQuestionType(input) {
|
||||||
const text = String(input ?? "").trim();
|
const text = String(input ?? "").trim();
|
||||||
if (!text) {
|
if (!text) {
|
||||||
return "unknown";
|
return "unknown";
|
||||||
}
|
}
|
||||||
|
let bestType = "unknown";
|
||||||
|
let bestHits = 0;
|
||||||
|
let bestPriority = Number.POSITIVE_INFINITY;
|
||||||
for (const rule of QUESTION_TYPE_RULES) {
|
for (const rule of QUESTION_TYPE_RULES) {
|
||||||
if (rule.pattern.test(text)) {
|
const hits = countRuleHits(text, rule);
|
||||||
return rule.type;
|
if (hits <= 0) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (hits > bestHits || (hits === bestHits && rule.priority < bestPriority)) {
|
||||||
|
bestType = rule.type;
|
||||||
|
bestHits = hits;
|
||||||
|
bestPriority = rule.priority;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (/[??]/u.test(text)) {
|
if (bestType !== "unknown") {
|
||||||
|
return bestType;
|
||||||
|
}
|
||||||
|
if (/[?пјџ]/u.test(text)) {
|
||||||
return "why_breaks";
|
return "why_breaks";
|
||||||
}
|
}
|
||||||
return "unknown";
|
return "unknown";
|
||||||
|
|
|
||||||
|
|
@ -2990,7 +2990,10 @@ function extractRequirementIdsFromText(value: string): string[] {
|
||||||
return uniqueStrings((matches ?? []).map((item) => item.toUpperCase()), 8);
|
return uniqueStrings((matches ?? []).map((item) => item.toUpperCase()), 8);
|
||||||
}
|
}
|
||||||
|
|
||||||
function buildCoverageSplitLines(structure: AnswerStructureV11): string[] {
|
function buildCoverageSplitLines(
|
||||||
|
structure: AnswerStructureV11,
|
||||||
|
questionType: QuestionTypeClass = "unknown"
|
||||||
|
): string[] {
|
||||||
const confirmed = uniqueStrings(
|
const confirmed = uniqueStrings(
|
||||||
(structure.evidence_block.claim_evidence_links ?? [])
|
(structure.evidence_block.claim_evidence_links ?? [])
|
||||||
.flatMap((item) => extractRequirementIdsFromText(item.claim_ref))
|
.flatMap((item) => extractRequirementIdsFromText(item.claim_ref))
|
||||||
|
|
@ -3002,6 +3005,21 @@ function buildCoverageSplitLines(structure: AnswerStructureV11): string[] {
|
||||||
8
|
8
|
||||||
);
|
);
|
||||||
const lines: string[] = [];
|
const lines: string[] = [];
|
||||||
|
if (questionType === "which_chains_are_complete_vs_incomplete") {
|
||||||
|
if (confirmed.length > 0) {
|
||||||
|
lines.push(`Цепочки подтверждены: ${confirmed.join(", ")}.`);
|
||||||
|
}
|
||||||
|
if (unresolved.length > 0) {
|
||||||
|
lines.push(`Цепочки подтверждены частично или не подтверждены: ${unresolved.join(", ")}.`);
|
||||||
|
} else if (structure.evidence_block.coverage_note === "coverage_partial_or_limited") {
|
||||||
|
lines.push("Часть цепочек подтверждена частично; для остальных не хватает опоры.");
|
||||||
|
}
|
||||||
|
if (lines.length === 0) {
|
||||||
|
lines.push("Цепочки пока не удалось уверенно разделить на полные и неполные.");
|
||||||
|
}
|
||||||
|
return dedupeNarrativeLines(lines, 3);
|
||||||
|
}
|
||||||
|
|
||||||
if (confirmed.length > 0) {
|
if (confirmed.length > 0) {
|
||||||
lines.push(`Подтверждено по требованиям: ${confirmed.join(", ")}.`);
|
lines.push(`Подтверждено по требованиям: ${confirmed.join(", ")}.`);
|
||||||
}
|
}
|
||||||
|
|
@ -3013,7 +3031,10 @@ function buildCoverageSplitLines(structure: AnswerStructureV11): string[] {
|
||||||
return dedupeNarrativeLines(lines, 3);
|
return dedupeNarrativeLines(lines, 3);
|
||||||
}
|
}
|
||||||
|
|
||||||
function buildEvidenceSectionLines(structure: AnswerStructureV11): string[] {
|
function buildEvidenceSectionLines(
|
||||||
|
structure: AnswerStructureV11,
|
||||||
|
questionType: QuestionTypeClass = "unknown"
|
||||||
|
): string[] {
|
||||||
const evidenceCount = Array.isArray(structure.evidence_block.evidence_ids) ? structure.evidence_block.evidence_ids.length : 0;
|
const evidenceCount = Array.isArray(structure.evidence_block.evidence_ids) ? structure.evidence_block.evidence_ids.length : 0;
|
||||||
const sourceCount = Array.isArray(structure.evidence_block.source_refs) ? structure.evidence_block.source_refs.length : 0;
|
const sourceCount = Array.isArray(structure.evidence_block.source_refs) ? structure.evidence_block.source_refs.length : 0;
|
||||||
const claimLinks = Array.isArray(structure.evidence_block.claim_evidence_links)
|
const claimLinks = Array.isArray(structure.evidence_block.claim_evidence_links)
|
||||||
|
|
@ -3025,7 +3046,15 @@ function buildEvidenceSectionLines(structure: AnswerStructureV11): string[] {
|
||||||
structure.uncertainty_block.open_uncertainties.length > 0 ||
|
structure.uncertainty_block.open_uncertainties.length > 0 ||
|
||||||
structure.evidence_block.coverage_note === "coverage_partial_or_limited";
|
structure.evidence_block.coverage_note === "coverage_partial_or_limited";
|
||||||
const lines: string[] = [];
|
const lines: string[] = [];
|
||||||
const coverageSplitLines = buildCoverageSplitLines(structure);
|
const coverageSplitLines = buildCoverageSplitLines(structure, questionType);
|
||||||
|
|
||||||
|
if (questionType === "what_is_it_grounded_on") {
|
||||||
|
lines.push("Основание вывода перечислено по подтвержденным документам, регистрам и проводкам.");
|
||||||
|
} else if (questionType === "prove_or_guess") {
|
||||||
|
lines.push("Основание разделено на подтвержденную часть и зону гипотез.");
|
||||||
|
} else if (questionType === "which_chains_are_complete_vs_incomplete") {
|
||||||
|
lines.push("Опора собрана так, чтобы разделить цепочки на полные и неполные.");
|
||||||
|
}
|
||||||
|
|
||||||
if (evidenceCount > 0) {
|
if (evidenceCount > 0) {
|
||||||
lines.push(`Вывод опирается на ${evidenceCount} подтвержденных наблюдений в текущем срезе.`);
|
lines.push(`Вывод опирается на ${evidenceCount} подтвержденных наблюдений в текущем срезе.`);
|
||||||
|
|
@ -3070,7 +3099,117 @@ function buildDefaultChecksByDomain(domain: P0NarrativeDomain): string[] {
|
||||||
return ["Проверьте связку документов и проводок по проблемному участку в указанном периоде."];
|
return ["Проверьте связку документов и проводок по проблемному участку в указанном периоде."];
|
||||||
}
|
}
|
||||||
|
|
||||||
function buildChecksSectionLines(structure: AnswerStructureV11): string[] {
|
function buildQuestionTypeDomainChecks(questionType: QuestionTypeClass, domain: P0NarrativeDomain): string[] {
|
||||||
|
if (questionType === "what_to_check_first") {
|
||||||
|
if (domain === "settlements_60_62") {
|
||||||
|
return [
|
||||||
|
"Сверьте договор и объект расчетов по спорной операции.",
|
||||||
|
"Проверьте регистр расчетов и зачет аванса/взаимозачет.",
|
||||||
|
"Подтвердите проводки по 60/62/76 и факт закрытия хвоста."
|
||||||
|
];
|
||||||
|
}
|
||||||
|
if (domain === "vat_document_register_book") {
|
||||||
|
return [
|
||||||
|
"Сверьте исходный документ и счет-фактуру.",
|
||||||
|
"Проверьте запись в регистре НДС и попадание в книгу.",
|
||||||
|
"Подтвердите налоговые проводки по 19/68 в нужном периоде."
|
||||||
|
];
|
||||||
|
}
|
||||||
|
if (domain === "month_close_costs_20_44") {
|
||||||
|
return [
|
||||||
|
"Проверьте регламентную операцию закрытия за нужный период.",
|
||||||
|
"Сверьте базу распределения затрат и проводки по 20/25/26/44.",
|
||||||
|
"Убедитесь, что остатки объяснены или закрыты после операции."
|
||||||
|
];
|
||||||
|
}
|
||||||
|
return ["Начните с первого подтверждаемого документа и пройдите цепочку без пропусков."];
|
||||||
|
}
|
||||||
|
|
||||||
|
if (questionType === "where_break_is") {
|
||||||
|
if (domain === "settlements_60_62") {
|
||||||
|
return [
|
||||||
|
"Локализуйте разрыв в узле: договор -> объект расчетов -> регистр расчетов -> закрывающий документ.",
|
||||||
|
"Сверьте, где прерывается переход платеж -> зачет/закрытие -> проводки 60/62/76."
|
||||||
|
];
|
||||||
|
}
|
||||||
|
if (domain === "vat_document_register_book") {
|
||||||
|
return [
|
||||||
|
"Локализуйте разрыв в узле: документ -> счет-фактура -> регистр НДС -> книга.",
|
||||||
|
"Сверьте, где прерывается переход от исходного документа к налоговой записи."
|
||||||
|
];
|
||||||
|
}
|
||||||
|
if (domain === "month_close_costs_20_44") {
|
||||||
|
return [
|
||||||
|
"Локализуйте разрыв в узле: накопление затрат -> правило распределения -> операция закрытия.",
|
||||||
|
"Сверьте, на каком шаге исчезает подтверждение перехода к закрытию остатков."
|
||||||
|
];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (questionType === "prove_or_guess") {
|
||||||
|
if (domain === "settlements_60_62") {
|
||||||
|
return [
|
||||||
|
"Отдельно отметьте, что доказано документами и проводками, а что остается гипотезой.",
|
||||||
|
"Для доказательства проверьте связку платеж -> расчетный документ -> регистр расчетов -> 60/62/76."
|
||||||
|
];
|
||||||
|
}
|
||||||
|
if (domain === "vat_document_register_book") {
|
||||||
|
return [
|
||||||
|
"Разделите доказанное и предположительное по цепочке: документ -> счет-фактура -> регистр -> книга.",
|
||||||
|
"Подтвердите налоговую запись по 19/68 в нужном периоде."
|
||||||
|
];
|
||||||
|
}
|
||||||
|
if (domain === "month_close_costs_20_44") {
|
||||||
|
return [
|
||||||
|
"Разделите доказанные и предположительные участки в цепочке закрытия месяца.",
|
||||||
|
"Проверьте подтверждение: операция закрытия -> распределение -> остатки по 20/25/26/44."
|
||||||
|
];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (questionType === "what_is_it_grounded_on") {
|
||||||
|
if (domain === "settlements_60_62") {
|
||||||
|
return [
|
||||||
|
"Перечислите основание: платежный документ, расчетный документ, запись регистра расчетов, проводки 60/62/76."
|
||||||
|
];
|
||||||
|
}
|
||||||
|
if (domain === "vat_document_register_book") {
|
||||||
|
return [
|
||||||
|
"Перечислите основание: исходный документ, счет-фактура, запись регистра НДС, запись книги, проводки 19/68."
|
||||||
|
];
|
||||||
|
}
|
||||||
|
if (domain === "month_close_costs_20_44") {
|
||||||
|
return [
|
||||||
|
"Перечислите основание: операция закрытия, база распределения, проводки по затратам, остатки после закрытия."
|
||||||
|
];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (questionType === "which_chains_are_complete_vs_incomplete") {
|
||||||
|
if (domain === "settlements_60_62") {
|
||||||
|
return [
|
||||||
|
"Разделите цепочки на: подтверждена, подтверждена частично, не подтверждена по переходу платеж -> закрытие расчета.",
|
||||||
|
"Проверьте разницу между закрытыми и незакрытыми связками по 60/62/76."
|
||||||
|
];
|
||||||
|
}
|
||||||
|
if (domain === "vat_document_register_book") {
|
||||||
|
return [
|
||||||
|
"Разделите цепочки на: полная, частичная, неполная по связке документ -> счет-фактура -> регистр -> книга.",
|
||||||
|
"Проверьте, где отсутствует подтверждение налоговой записи."
|
||||||
|
];
|
||||||
|
}
|
||||||
|
if (domain === "month_close_costs_20_44") {
|
||||||
|
return [
|
||||||
|
"Разделите цепочки закрытия на полные и неполные по шагам распределения и регламентной операции.",
|
||||||
|
"Проверьте, какие остатки после закрытия подтверждены, а какие нет."
|
||||||
|
];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return buildDefaultChecksByDomain(domain);
|
||||||
|
}
|
||||||
|
|
||||||
|
function buildChecksSectionLines(structure: AnswerStructureV11, context?: AnswerRenderContext): string[] {
|
||||||
const actionLines = dedupeNarrativeLines(
|
const actionLines = dedupeNarrativeLines(
|
||||||
[
|
[
|
||||||
...structure.next_step_block.recommended_actions,
|
...structure.next_step_block.recommended_actions,
|
||||||
|
|
@ -3086,29 +3225,45 @@ function buildChecksSectionLines(structure: AnswerStructureV11): string[] {
|
||||||
);
|
);
|
||||||
|
|
||||||
const broken = sanitizeUserText(structure.direct_answer) ?? "";
|
const broken = sanitizeUserText(structure.direct_answer) ?? "";
|
||||||
const domain = inferNarrativeDomainFromText(broken);
|
const domain = context?.focusDomain ?? inferNarrativeDomainFromText(broken);
|
||||||
const domainFallback = buildDefaultChecksByDomain(domain);
|
const questionType = context?.questionType ?? "unknown";
|
||||||
|
const domainFallback = buildQuestionTypeDomainChecks(questionType, domain);
|
||||||
const hasMissingPeriod = structure.uncertainty_block.open_uncertainties.some((item) =>
|
const hasMissingPeriod = structure.uncertainty_block.open_uncertainties.some((item) =>
|
||||||
/missing_anchor:period/i.test(String(item ?? ""))
|
/missing_anchor:period/i.test(String(item ?? ""))
|
||||||
);
|
);
|
||||||
|
|
||||||
const lines: string[] = [];
|
const lines: string[] = [];
|
||||||
if (domain === "settlements_60_62") {
|
if (questionType === "what_to_check_first") {
|
||||||
|
lines.push(...domainFallback.slice(0, 3));
|
||||||
|
if (lines.length < 3) {
|
||||||
|
lines.push(...actionLines.slice(0, 3 - lines.length));
|
||||||
|
}
|
||||||
|
} else if (questionType === "what_is_it_grounded_on") {
|
||||||
|
lines.push(...domainFallback.slice(0, 2));
|
||||||
|
lines.push(...actionLines.slice(0, 1));
|
||||||
|
} else if (questionType === "prove_or_guess" || questionType === "where_break_is") {
|
||||||
lines.push(...domainFallback.slice(0, 2));
|
lines.push(...domainFallback.slice(0, 2));
|
||||||
lines.push(...actionLines.slice(0, 2));
|
|
||||||
} else if (actionLines.length > 0) {
|
|
||||||
lines.push(...actionLines.slice(0, 2));
|
lines.push(...actionLines.slice(0, 2));
|
||||||
} else {
|
} else {
|
||||||
lines.push(...domainFallback.slice(0, 2));
|
if (domain === "settlements_60_62") {
|
||||||
|
lines.push(...domainFallback.slice(0, 2));
|
||||||
|
lines.push(...actionLines.slice(0, 2));
|
||||||
|
} else if (actionLines.length > 0) {
|
||||||
|
lines.push(...actionLines.slice(0, 2));
|
||||||
|
} else {
|
||||||
|
lines.push(...domainFallback.slice(0, 2));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if (hasMissingPeriod) {
|
if (hasMissingPeriod) {
|
||||||
if (domain === "settlements_60_62" && lines.length > 0) {
|
if (questionType === "what_to_check_first") {
|
||||||
|
lines.push("Уточните период, если он не зафиксирован в исходной формулировке вопроса.");
|
||||||
|
} else if (domain === "settlements_60_62" && lines.length > 0) {
|
||||||
lines.push("Уточните период проверки, чтобы подтвердить проблему без лишнего шума.");
|
lines.push("Уточните период проверки, чтобы подтвердить проблему без лишнего шума.");
|
||||||
} else {
|
} else {
|
||||||
lines.unshift("Уточните период проверки, чтобы подтвердить проблему без лишнего шума.");
|
lines.unshift("Уточните период проверки, чтобы подтвердить проблему без лишнего шума.");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return dedupeNarrativeLines(lines, 4);
|
return dedupeNarrativeLines(lines, questionType === "what_to_check_first" ? 3 : 5);
|
||||||
}
|
}
|
||||||
|
|
||||||
function humanizeLimitationToken(value: string): string | null {
|
function humanizeLimitationToken(value: string): string | null {
|
||||||
|
|
@ -3192,19 +3347,19 @@ function domainNameForQuestionType(domain: P0NarrativeDomain): string {
|
||||||
function buildQuestionTypeShortLine(context: AnswerRenderContext): string | null {
|
function buildQuestionTypeShortLine(context: AnswerRenderContext): string | null {
|
||||||
const domainName = domainNameForQuestionType(context.focusDomain);
|
const domainName = domainNameForQuestionType(context.focusDomain);
|
||||||
if (context.questionType === "where_break_is") {
|
if (context.questionType === "where_break_is") {
|
||||||
return `\u041f\u0440\u0438\u043e\u0440\u0438\u0442\u0435\u0442 \u043e\u0442\u0432\u0435\u0442\u0430: \u043b\u043e\u043a\u0430\u043b\u0438\u0437\u043e\u0432\u0430\u0442\u044c \u0440\u0430\u0437\u0440\u044b\u0432 \u0432\u043d\u0443\u0442\u0440\u0438 ${domainName}.`;
|
return `\u041b\u043e\u043a\u0430\u043b\u0438\u0437\u043e\u0432\u0430\u043d \u043d\u0430\u0438\u0431\u043e\u043b\u0435\u0435 \u0432\u0435\u0440\u043e\u044f\u0442\u043d\u044b\u0439 \u0443\u0437\u0435\u043b \u0440\u0430\u0437\u0440\u044b\u0432\u0430 \u0432\u043d\u0443\u0442\u0440\u0438 ${domainName}.`;
|
||||||
}
|
}
|
||||||
if (context.questionType === "prove_or_guess") {
|
if (context.questionType === "prove_or_guess") {
|
||||||
return "\u041f\u0440\u0438\u043e\u0440\u0438\u0442\u0435\u0442 \u043e\u0442\u0432\u0435\u0442\u0430: \u0440\u0430\u0437\u0432\u0435\u0441\u0442\u0438 \u0434\u043e\u043a\u0430\u0437\u0430\u043d\u043e \u0438 \u0433\u0438\u043f\u043e\u0442\u0435\u0437\u0443.";
|
return "\u0412\u044b\u0432\u043e\u0434 \u0440\u0430\u0437\u0434\u0435\u043b\u0435\u043d \u043d\u0430 \u0434\u043e\u043a\u0430\u0437\u0430\u043d\u043d\u0443\u044e \u0447\u0430\u0441\u0442\u044c \u0438 \u0433\u0438\u043f\u043e\u0442\u0435\u0437\u0443.";
|
||||||
}
|
}
|
||||||
if (context.questionType === "what_is_it_grounded_on") {
|
if (context.questionType === "what_is_it_grounded_on") {
|
||||||
return "\u041f\u0440\u0438\u043e\u0440\u0438\u0442\u0435\u0442 \u043e\u0442\u0432\u0435\u0442\u0430: \u043f\u043e\u043a\u0430\u0437\u0430\u0442\u044c \u043e\u0441\u043d\u043e\u0432\u0430\u043d\u0438\u0435 \u0432\u044b\u0432\u043e\u0434\u0430 \u043f\u043e \u0434\u0430\u043d\u043d\u044b\u043c.";
|
return "\u041d\u0438\u0436\u0435 \u043f\u0435\u0440\u0435\u0447\u0438\u0441\u043b\u0435\u043d\u044b \u043e\u0441\u043d\u043e\u0432\u0430\u043d\u0438\u044f \u0432\u044b\u0432\u043e\u0434\u0430 \u043f\u043e \u0434\u0430\u043d\u043d\u044b\u043c \u0443\u0447\u0435\u0442\u0430.";
|
||||||
}
|
}
|
||||||
if (context.questionType === "which_chains_are_complete_vs_incomplete") {
|
if (context.questionType === "which_chains_are_complete_vs_incomplete") {
|
||||||
return "\u041f\u0440\u0438\u043e\u0440\u0438\u0442\u0435\u0442 \u043e\u0442\u0432\u0435\u0442\u0430: \u0440\u0430\u0437\u0434\u0435\u043b\u0438\u0442\u044c \u0437\u0430\u0432\u0435\u0440\u0448\u0435\u043d\u043d\u044b\u0435 \u0438 \u043d\u0435\u0437\u0430\u0432\u0435\u0440\u0448\u0435\u043d\u043d\u044b\u0435 \u0446\u0435\u043f\u043e\u0447\u043a\u0438.";
|
return "\u0426\u0435\u043f\u043e\u0447\u043a\u0438 \u0440\u0430\u0437\u0434\u0435\u043b\u0435\u043d\u044b \u043d\u0430 \u043f\u043e\u0434\u0442\u0432\u0435\u0440\u0436\u0434\u0435\u043d\u043d\u044b\u0435, \u0447\u0430\u0441\u0442\u0438\u0447\u043d\u043e \u043f\u043e\u0434\u0442\u0432\u0435\u0440\u0436\u0434\u0435\u043d\u043d\u044b\u0435 \u0438 \u043d\u0435\u043f\u043e\u0434\u0442\u0432\u0435\u0440\u0436\u0434\u0435\u043d\u043d\u044b\u0435.";
|
||||||
}
|
}
|
||||||
if (context.questionType === "what_to_check_first") {
|
if (context.questionType === "what_to_check_first") {
|
||||||
return "\u041f\u0440\u0438\u043e\u0440\u0438\u0442\u0435\u0442 \u043e\u0442\u0432\u0435\u0442\u0430: \u0434\u0430\u0442\u044c \u043f\u0435\u0440\u0432\u044b\u0439 \u043c\u0430\u0440\u0448\u0440\u0443\u0442 \u043f\u0440\u043e\u0432\u0435\u0440\u043a\u0438.";
|
return `\u041a\u043e\u0440\u043e\u0442\u043a\u0438\u0439 \u043c\u0430\u0440\u0448\u0440\u0443\u0442 \u043f\u0435\u0440\u0432\u044b\u0445 \u043f\u0440\u043e\u0432\u0435\u0440\u043e\u043a \u0432\u043d\u0443\u0442\u0440\u0438 ${domainName}.`;
|
||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
@ -3226,12 +3381,18 @@ function buildQuestionTypeBrokenLine(context: AnswerRenderContext): string | nul
|
||||||
}
|
}
|
||||||
|
|
||||||
function buildQuestionTypeWhyLine(context: AnswerRenderContext): string | null {
|
function buildQuestionTypeWhyLine(context: AnswerRenderContext): string | null {
|
||||||
|
if (context.questionType === "where_break_is") {
|
||||||
|
return "\u0424\u043e\u043a\u0443\u0441 \u043e\u0442\u0432\u0435\u0442\u0430: \u043d\u0435 \u043e\u0431\u0449\u0438\u0439 \u043c\u0435\u0445\u0430\u043d\u0438\u0437\u043c, \u0430 \u0442\u043e\u0447\u043a\u0430 \u0440\u0430\u0437\u0440\u044b\u0432\u0430 \u0432 \u0446\u0435\u043f\u043e\u0447\u043a\u0435.";
|
||||||
|
}
|
||||||
if (context.questionType === "prove_or_guess") {
|
if (context.questionType === "prove_or_guess") {
|
||||||
return "\u0417\u0434\u0435\u0441\u044c \u0447\u0435\u0441\u0442\u043d\u043e \u0440\u0430\u0437\u0432\u043e\u0434\u0438\u0442\u0441\u044f \u0447\u0442\u043e \u0443\u0436\u0435 \u043f\u043e\u0434\u0442\u0432\u0435\u0440\u0436\u0434\u0435\u043d\u043e \u0438 \u0447\u0442\u043e \u043f\u043e\u043a\u0430 \u043e\u0441\u0442\u0430\u0435\u0442\u0441\u044f \u0433\u0438\u043f\u043e\u0442\u0435\u0437\u043e\u0439.";
|
return "\u041e\u0442\u0434\u0435\u043b\u044c\u043d\u043e \u043f\u043e\u043a\u0430\u0437\u0430\u043d\u043e, \u0447\u0442\u043e \u0443\u0436\u0435 \u043f\u043e\u0434\u0442\u0432\u0435\u0440\u0436\u0434\u0435\u043d\u043e, \u0430 \u0447\u0442\u043e \u043f\u043e\u043a\u0430 \u043e\u0441\u0442\u0430\u0435\u0442\u0441\u044f \u0433\u0438\u043f\u043e\u0442\u0435\u0437\u043e\u0439.";
|
||||||
}
|
}
|
||||||
if (context.questionType === "which_chains_are_complete_vs_incomplete") {
|
if (context.questionType === "which_chains_are_complete_vs_incomplete") {
|
||||||
return "\u0426\u0435\u043f\u043e\u0447\u043a\u0438 \u0440\u0430\u0437\u0434\u0435\u043b\u0435\u043d\u044b \u043d\u0430 \u0437\u0430\u0432\u0435\u0440\u0448\u0435\u043d\u043d\u044b\u0435 \u0438 \u043d\u0435\u0437\u0430\u0432\u0435\u0440\u0448\u0435\u043d\u043d\u044b\u0435 \u043f\u043e \u0442\u0435\u043a\u0443\u0449\u0435\u0439 \u043e\u043f\u043e\u0440\u0435.";
|
return "\u0426\u0435\u043f\u043e\u0447\u043a\u0438 \u0440\u0430\u0437\u0434\u0435\u043b\u0435\u043d\u044b \u043d\u0430 \u0437\u0430\u0432\u0435\u0440\u0448\u0435\u043d\u043d\u044b\u0435 \u0438 \u043d\u0435\u0437\u0430\u0432\u0435\u0440\u0448\u0435\u043d\u043d\u044b\u0435 \u043f\u043e \u0442\u0435\u043a\u0443\u0449\u0435\u0439 \u043e\u043f\u043e\u0440\u0435.";
|
||||||
}
|
}
|
||||||
|
if (context.questionType === "what_is_it_grounded_on") {
|
||||||
|
return "\u0424\u043e\u043a\u0443\u0441 \u043e\u0442\u0432\u0435\u0442\u0430 \u0441\u043c\u0435\u0449\u0435\u043d \u0432 \u0434\u043e\u043a\u0430\u0437\u0430\u0442\u0435\u043b\u044c\u043d\u044b\u0435 \u0438\u0441\u0442\u043e\u0447\u043d\u0438\u043a\u0438, \u0430 \u043d\u0435 \u0432 \u043e\u0431\u0449\u0438\u0439 narrative.";
|
||||||
|
}
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -3242,6 +3403,9 @@ function buildQuestionTypeEvidenceLine(context: AnswerRenderContext): string | n
|
||||||
if (context.questionType === "prove_or_guess") {
|
if (context.questionType === "prove_or_guess") {
|
||||||
return "\u0421\u0438\u043b\u0430 \u0432\u044b\u0432\u043e\u0434\u0430 \u043e\u0446\u0435\u043d\u0435\u043d\u0430 \u043f\u043e \u043f\u0440\u044f\u043c\u043e\u0439 \u043e\u043f\u043e\u0440\u0435, \u0430 \u043d\u0435 \u043f\u043e \u0434\u043e\u0433\u0430\u0434\u043a\u0430\u043c.";
|
return "\u0421\u0438\u043b\u0430 \u0432\u044b\u0432\u043e\u0434\u0430 \u043e\u0446\u0435\u043d\u0435\u043d\u0430 \u043f\u043e \u043f\u0440\u044f\u043c\u043e\u0439 \u043e\u043f\u043e\u0440\u0435, \u0430 \u043d\u0435 \u043f\u043e \u0434\u043e\u0433\u0430\u0434\u043a\u0430\u043c.";
|
||||||
}
|
}
|
||||||
|
if (context.questionType === "which_chains_are_complete_vs_incomplete") {
|
||||||
|
return "\u041e\u043f\u043e\u0440\u0430 \u0441\u043e\u0431\u0440\u0430\u043d\u0430 \u0442\u0430\u043a, \u0447\u0442\u043e\u0431\u044b \u0447\u0435\u0441\u0442\u043d\u043e \u0440\u0430\u0437\u0434\u0435\u043b\u0438\u0442\u044c \u043f\u043e\u043b\u043d\u044b\u0435 \u0438 \u043d\u0435\u043f\u043e\u043b\u043d\u044b\u0435 \u0446\u0435\u043f\u043e\u0447\u043a\u0438.";
|
||||||
|
}
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -3254,7 +3418,19 @@ function formatAnchorList(anchors: string[], prefix: string): string | null {
|
||||||
|
|
||||||
function buildQuestionTypeCheckLine(context: AnswerRenderContext): string | null {
|
function buildQuestionTypeCheckLine(context: AnswerRenderContext): string | null {
|
||||||
if (context.questionType === "what_to_check_first") {
|
if (context.questionType === "what_to_check_first") {
|
||||||
return "\u041d\u0430\u0447\u043d\u0438\u0442\u0435 \u0441 \u043f\u0435\u0440\u0432\u043e\u0433\u043e \u043f\u0443\u043d\u043a\u0442\u0430 \u0438 \u043f\u0440\u043e\u0439\u0434\u0438\u0442\u0435 \u043c\u0430\u0440\u0448\u0440\u0443\u0442 \u043f\u043e\u0441\u043b\u0435\u0434\u043e\u0432\u0430\u0442\u0435\u043b\u044c\u043d\u043e, \u0431\u0435\u0437 \u043f\u0435\u0440\u0435\u0441\u043a\u043e\u043a\u0430.";
|
return "\u041f\u0440\u043e\u0432\u0435\u0440\u044c\u0442\u0435 \u043f\u0443\u043d\u043a\u0442\u044b \u043f\u043e \u043f\u043e\u0440\u044f\u0434\u043a\u0443: \u0448\u0430\u0433 1 -> \u0448\u0430\u0433 2 -> \u0448\u0430\u0433 3.";
|
||||||
|
}
|
||||||
|
if (context.questionType === "where_break_is") {
|
||||||
|
return "\u041b\u043e\u043a\u0430\u043b\u0438\u0437\u0430\u0446\u0438\u044e \u0440\u0430\u0437\u0440\u044b\u0432\u0430 \u043d\u0430\u0447\u043d\u0438\u0442\u0435 \u0441 \u0443\u0437\u043b\u0430, \u0433\u0434\u0435 \u043f\u0435\u0440\u0435\u0445\u043e\u0434 \u043f\u0435\u0440\u0435\u0441\u0442\u0430\u043b \u043f\u043e\u0434\u0442\u0432\u0435\u0440\u0436\u0434\u0430\u0442\u044c\u0441\u044f.";
|
||||||
|
}
|
||||||
|
if (context.questionType === "prove_or_guess") {
|
||||||
|
return "\u041f\u0435\u0440\u0432\u044b\u043c \u0434\u0435\u0439\u0441\u0442\u0432\u0438\u0435\u043c \u043e\u0442\u0434\u0435\u043b\u0438\u0442\u0435 \u0434\u043e\u043a\u0443\u043c\u0435\u043d\u0442\u0430\u043b\u044c\u043d\u043e \u043f\u043e\u0434\u0442\u0432\u0435\u0440\u0436\u0434\u0435\u043d\u043d\u044b\u0435 \u0444\u0430\u043a\u0442\u044b \u043e\u0442 \u0433\u0438\u043f\u043e\u0442\u0435\u0437.";
|
||||||
|
}
|
||||||
|
if (context.questionType === "what_is_it_grounded_on") {
|
||||||
|
return "\u0421\u043d\u0430\u0447\u0430\u043b\u0430 \u043f\u0435\u0440\u0435\u0447\u0438\u0441\u043b\u0438\u0442\u0435 \u043e\u043f\u043e\u0440\u043d\u044b\u0435 \u0434\u043e\u043a\u0443\u043c\u0435\u043d\u0442\u044b \u0438 \u0440\u0435\u0433\u0438\u0441\u0442\u0440\u044b, \u0437\u0430\u0442\u0435\u043c \u043f\u043e\u0434\u0442\u0432\u0435\u0440\u0436\u0434\u0430\u044e\u0449\u0438\u0435 \u043f\u0440\u043e\u0432\u043e\u0434\u043a\u0438.";
|
||||||
|
}
|
||||||
|
if (context.questionType === "which_chains_are_complete_vs_incomplete") {
|
||||||
|
return "\u0421\u043d\u0430\u0447\u0430\u043b\u0430 \u0440\u0430\u0437\u043b\u043e\u0436\u0438\u0442\u0435 \u0446\u0435\u043f\u043e\u0447\u043a\u0438 \u043d\u0430 \u043f\u043e\u043b\u043d\u044b\u0435, \u0447\u0430\u0441\u0442\u0438\u0447\u043d\u043e \u043f\u043e\u043b\u043d\u044b\u0435 \u0438 \u043d\u0435\u043f\u043e\u0434\u0442\u0432\u0435\u0440\u0436\u0434\u0435\u043d\u043d\u044b\u0435.";
|
||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
@ -3266,6 +3442,15 @@ function buildQuestionTypeLimitationLine(context: AnswerRenderContext): string |
|
||||||
if (context.questionType === "which_chains_are_complete_vs_incomplete") {
|
if (context.questionType === "which_chains_are_complete_vs_incomplete") {
|
||||||
return "\u0414\u0435\u043b\u0435\u043d\u0438\u0435 \u043d\u0430 \u00abcomplete/incomplete\u00bb \u0437\u0430\u0432\u0438\u0441\u0438\u0442 \u043e\u0442 \u043f\u043e\u043b\u043d\u043e\u0442\u044b \u0446\u0435\u043f\u043e\u0447\u043a\u0438 \u0432 \u0442\u0435\u043a\u0443\u0449\u0435\u043c \u0441\u0440\u0435\u0437\u0435.";
|
return "\u0414\u0435\u043b\u0435\u043d\u0438\u0435 \u043d\u0430 \u00abcomplete/incomplete\u00bb \u0437\u0430\u0432\u0438\u0441\u0438\u0442 \u043e\u0442 \u043f\u043e\u043b\u043d\u043e\u0442\u044b \u0446\u0435\u043f\u043e\u0447\u043a\u0438 \u0432 \u0442\u0435\u043a\u0443\u0449\u0435\u043c \u0441\u0440\u0435\u0437\u0435.";
|
||||||
}
|
}
|
||||||
|
if (context.questionType === "where_break_is") {
|
||||||
|
return "\u0422\u043e\u0447\u043d\u0430\u044f \u043b\u043e\u043a\u0430\u043b\u0438\u0437\u0430\u0446\u0438\u044f \u043c\u043e\u0436\u0435\u0442 \u0441\u043c\u0435\u0449\u0430\u0442\u044c\u0441\u044f, \u0435\u0441\u043b\u0438 \u0447\u0430\u0441\u0442\u044c \u043f\u0435\u0440\u0435\u0445\u043e\u0434\u043e\u0432 \u0432 \u0446\u0435\u043f\u043e\u0447\u043a\u0435 \u043d\u0435 \u043f\u043e\u0434\u0442\u0432\u0435\u0440\u0436\u0434\u0435\u043d\u0430.";
|
||||||
|
}
|
||||||
|
if (context.questionType === "what_is_it_grounded_on") {
|
||||||
|
return "\u0412 \u043e\u0441\u043d\u043e\u0432\u0430\u043d\u0438\u0435 \u0432\u043a\u043b\u044e\u0447\u0435\u043d\u044b \u0442\u043e\u043b\u044c\u043a\u043e \u043f\u043e\u0434\u0442\u0432\u0435\u0440\u0436\u0434\u0435\u043d\u043d\u044b\u0435 \u0438\u0441\u0442\u043e\u0447\u043d\u0438\u043a\u0438; \u043d\u0435\u043f\u043e\u0434\u0442\u0432\u0435\u0440\u0436\u0434\u0435\u043d\u043d\u044b\u0435 \u0432\u044b\u043d\u0435\u0441\u0435\u043d\u044b \u0432 \u043e\u0433\u0440\u0430\u043d\u0438\u0447\u0435\u043d\u0438\u044f.";
|
||||||
|
}
|
||||||
|
if (context.questionType === "what_to_check_first") {
|
||||||
|
return "\u041c\u0430\u0440\u0448\u0440\u0443\u0442 \u043f\u0435\u0440\u0432\u0438\u0447\u043d\u044b\u0439 \u0438 \u043c\u043e\u0436\u0435\u0442 \u0443\u0442\u043e\u0447\u043d\u044f\u0442\u044c\u0441\u044f \u043f\u043e\u0441\u043b\u0435 \u043f\u0435\u0440\u0432\u043e\u0433\u043e \u0448\u0430\u0433\u0430.";
|
||||||
|
}
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -3306,11 +3491,11 @@ function applyQuestionTypeAndAnchorPolicy(input: {
|
||||||
[buildQuestionTypeEvidenceLine(input.context), ...input.evidenceLines, anchorUsedLine].filter(
|
[buildQuestionTypeEvidenceLine(input.context), ...input.evidenceLines, anchorUsedLine].filter(
|
||||||
(item): item is string => Boolean(item)
|
(item): item is string => Boolean(item)
|
||||||
),
|
),
|
||||||
7
|
input.context.questionType === "what_to_check_first" ? 4 : 7
|
||||||
);
|
);
|
||||||
const nextChecks = dedupeNarrativeLines(
|
const nextChecks = dedupeNarrativeLines(
|
||||||
[buildQuestionTypeCheckLine(input.context), ...input.checkLines].filter((item): item is string => Boolean(item)),
|
[buildQuestionTypeCheckLine(input.context), ...input.checkLines].filter((item): item is string => Boolean(item)),
|
||||||
5
|
input.context.questionType === "what_to_check_first" ? 3 : 5
|
||||||
);
|
);
|
||||||
const nextLimitations = dedupeNarrativeLines(
|
const nextLimitations = dedupeNarrativeLines(
|
||||||
[buildQuestionTypeLimitationLine(input.context), anchorUnusedLine, ...input.limitationLines].filter(
|
[buildQuestionTypeLimitationLine(input.context), anchorUnusedLine, ...input.limitationLines].filter(
|
||||||
|
|
@ -3330,11 +3515,12 @@ function applyQuestionTypeAndAnchorPolicy(input: {
|
||||||
}
|
}
|
||||||
|
|
||||||
function renderPolicyReply(structure: AnswerStructureV11, context?: AnswerRenderContext): string {
|
function renderPolicyReply(structure: AnswerStructureV11, context?: AnswerRenderContext): string {
|
||||||
|
const questionType = context?.questionType ?? "unknown";
|
||||||
const shortLine = ensureSentence(buildShortSectionLine(structure));
|
const shortLine = ensureSentence(buildShortSectionLine(structure));
|
||||||
const brokenLines = buildBrokenSectionLines(structure);
|
const brokenLines = buildBrokenSectionLines(structure);
|
||||||
const whyLines = buildWhySectionLines(structure);
|
const whyLines = buildWhySectionLines(structure);
|
||||||
const evidenceLines = buildEvidenceSectionLines(structure);
|
const evidenceLines = buildEvidenceSectionLines(structure, questionType);
|
||||||
const checkLines = buildChecksSectionLines(structure);
|
const checkLines = buildChecksSectionLines(structure, context);
|
||||||
const limitationLines = buildLimitationsSectionLines(structure);
|
const limitationLines = buildLimitationsSectionLines(structure);
|
||||||
const enriched = context
|
const enriched = context
|
||||||
? applyQuestionTypeAndAnchorPolicy({
|
? applyQuestionTypeAndAnchorPolicy({
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
export type QuestionTypeClass =
|
export type QuestionTypeClass =
|
||||||
| "why_breaks"
|
| "why_breaks"
|
||||||
| "where_break_is"
|
| "where_break_is"
|
||||||
| "prove_or_guess"
|
| "prove_or_guess"
|
||||||
|
|
@ -7,52 +7,104 @@ export type QuestionTypeClass =
|
||||||
| "what_to_check_first"
|
| "what_to_check_first"
|
||||||
| "unknown";
|
| "unknown";
|
||||||
|
|
||||||
const QUESTION_TYPE_RULES: Array<{ type: QuestionTypeClass; pattern: RegExp }> = [
|
interface QuestionTypeRule {
|
||||||
|
type: QuestionTypeClass;
|
||||||
|
priority: number;
|
||||||
|
patterns: RegExp[];
|
||||||
|
}
|
||||||
|
|
||||||
|
const QUESTION_TYPE_RULES: QuestionTypeRule[] = [
|
||||||
{
|
{
|
||||||
type: "what_to_check_first",
|
type: "what_to_check_first",
|
||||||
pattern:
|
priority: 1,
|
||||||
/(?:\bwhat\s+to\s+check\s+first\b|\bfirst\s+check\b|\bcheck\s+first\b|\u0441\s+\u0447\u0435\u0433\u043e\s+\u043d\u0430\u0447\u0430\u0442\u044c\s+\u043f\u0440\u043e\u0432\u0435\u0440\u043a|\u0447\u0442\u043e\s+\u043f\u0440\u043e\u0432\u0435\u0440\u0438\u0442\u044c\s+\u043f\u0435\u0440\u0432)/iu
|
patterns: [
|
||||||
},
|
/(?:\bwhat\s+to\s+check\s+first\b|\bfirst\s+check\b|\bcheck\s+first\b)/iu,
|
||||||
{
|
/(?:что\s+проверить\s+перв(?:ым|ой)|с\s+чего\s+начать\s+проверк|перв(?:ый|ым)\s+шаг(?:ом)?\s+проверк)/iu
|
||||||
type: "what_is_it_grounded_on",
|
]
|
||||||
pattern:
|
|
||||||
/(?:\bwhat\s+is\s+it\s+grounded\s+on\b|\bgrounded\s+on\b|\bbased\s+on\b|\bwhat\s+evidence\b|\u043d\u0430\s+\u0447(?:\u0435|\u0451)\u043c\s+\u044d\u0442\u043e\s+\u043e\u0441\u043d\u043e\u0432\u0430\u043d|\u0447\u0435\u043c\s+\u043f\u043e\u0434\u0442\u0432\u0435\u0440\u0436\u0434)/iu
|
|
||||||
},
|
|
||||||
{
|
|
||||||
type: "prove_or_guess",
|
|
||||||
pattern:
|
|
||||||
/(?:\bprove\b|\bguess\b|\bprove\s+or\s+guess\b|\bis\s+it\s+proven\b|\u044d\u0442\u043e\s+\u0434\u043e\u043a\u0430\u0437\u0430\u043d|\u0438\u043b\u0438\s+\u0442\u043e\u043b\u044c\u043a\u043e\s+\u0433\u0438\u043f\u043e\u0442\u0435\u0437|\u0434\u043e\u043a\u0430\u0437\u0430\u043d|\u0434\u043e\u0433\u0430\u0434|\u0435\u0441\u0442\u044c\s+\u043b\u0438|\u043c\u043e\u0436\u0435\u0442\s+\u043b\u0438|\u044d\u0442\u043e\s+\u0443\u0436\u0435.*\u0438\u043b\u0438)/iu
|
|
||||||
},
|
|
||||||
{
|
|
||||||
type: "which_chains_are_complete_vs_incomplete",
|
|
||||||
pattern:
|
|
||||||
/(?:\bcomplete(?:d)?\b.*\bincomplete\b|\bwhich\s+chains?\b|\bcomplete\s+vs\s+incomplete\b|\u043a\u0430\u043a\u0438\u0435\s+\u0446\u0435\u043f\u043e\u0447\u043a[аи]\s+.*\u0437\u0430\u0432\u0435\u0440\u0448|\u0447\u0442\u043e\s+\u0437\u0430\u043a\u0440\u044b\u0442\u043e.*\u0447\u0442\u043e\s+\u043d\u0435\u0442)/iu
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
type: "where_break_is",
|
type: "where_break_is",
|
||||||
pattern:
|
priority: 2,
|
||||||
/(?:\bwhere\s+is\s+the\s+break\b|\bwhere\s+exactly\b|\blocate\b|\u0433\u0434\u0435\s+\u0438\u043c\u0435\u043d\u043d\u043e|\u0433\u0434\u0435\s+\u0440\u0430\u0437\u0440\u044b\u0432|\u0432\s+\u043a\u0430\u043a\u043e\u043c\s+\u043c\u0435\u0441\u0442\u0435)/iu
|
patterns: [
|
||||||
|
/(?:\bwhere\s+is\s+the\s+break\b|\bwhere\s+exactly\b|\blocate(?:\s+the\s+break)?\b)/iu,
|
||||||
|
/(?:где\s+именно|где\s+разрыв|в\s+каком\s+месте|на\s+каком\s+этапе|локализ(?:овать|аци)|какой\s+узел)/iu
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: "which_chains_are_complete_vs_incomplete",
|
||||||
|
priority: 3,
|
||||||
|
patterns: [
|
||||||
|
/(?:\bcomplete(?:d)?\b.*\bincomplete\b|\bwhich\s+chains?\b|\bcomplete\s+vs\s+incomplete\b)/iu,
|
||||||
|
/(?:какие(?:\s+\S+){0,4}\s+цепочк[аи].*(?:заверш|полны|неполны|не\s+заверш|подтвержд)|что\s+закрыто.*что\s+нет)/iu,
|
||||||
|
/(?:цепочк[аи].*(?:полная|неполная|частич|выпадени)|отраж[её]н\s+частич.*документ.*сч[её]т[-\s]?фактур)/iu
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: "prove_or_guess",
|
||||||
|
priority: 4,
|
||||||
|
patterns: [
|
||||||
|
/(?:\bprove\b|\bguess\b|\bprove\s+or\s+guess\b|\bis\s+it\s+proven\b)/iu,
|
||||||
|
/(?:это\s+доказан|докаж(?:и|ите|ите\s+ли)|доказуем|доказательн|гипотез|догад(?:ка|ыв))/iu,
|
||||||
|
/(?:доказано\s+или\s+нет|похоже\s+или\s+доказано|зач[её]л(?:ся|ось)\s+ли|связан\s+ли|вс[её]\s+ли.*закрыл|больше\s+похоже.*или)/iu,
|
||||||
|
/(?:это\s+уже\s+[^?!.]{3,}\s+или|есть\s+ли\s+[^?!.]{0,80}ситуац[^?!.]{0,80}\s+где)/iu
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: "what_is_it_grounded_on",
|
||||||
|
priority: 5,
|
||||||
|
patterns: [
|
||||||
|
/(?:\bwhat\s+is\s+it\s+grounded\s+on\b|\bgrounded\s+on\b|\bbased\s+on\b|\bwhat\s+evidence\b)/iu,
|
||||||
|
/(?:на\s+ч(?:е|ё)м[^?!.]{0,40}основан|чем\s+подтвержда(?:ется|но)|какие\s+основани[яе]|какими\s+доказательств)/iu,
|
||||||
|
/(?:есть\s+ли\s+[^?!.]{0,80}признак[аи])/iu
|
||||||
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
type: "why_breaks",
|
type: "why_breaks",
|
||||||
pattern:
|
priority: 6,
|
||||||
/(?:\bwhy\b|\bwhy\s+does\s+it\s+break\b|\u043f\u043e\u0447\u0435\u043c\u0443|\u0432\s+\u0447(?:\u0435|\u0451)\u043c\s+\u043f\u0440\u0438\u0447\u0438\u043d\u0430|\u0438\u0437-\u0437\u0430\s+\u0447\u0435\u0433\u043e)/iu
|
patterns: [
|
||||||
|
/(?:\bwhy\b|\bwhy\s+does\s+it\s+break\b|\bwhat\s+causes\b)/iu,
|
||||||
|
/(?:почему|в\s+ч(?:е|ё)м\s+причина|из-?за\s+чего|откуда\s+разрыв)/iu
|
||||||
|
]
|
||||||
}
|
}
|
||||||
];
|
];
|
||||||
|
|
||||||
|
function countRuleHits(text: string, rule: QuestionTypeRule): number {
|
||||||
|
let hits = 0;
|
||||||
|
for (const pattern of rule.patterns) {
|
||||||
|
if (pattern.test(text)) {
|
||||||
|
hits += 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return hits;
|
||||||
|
}
|
||||||
|
|
||||||
export function resolveQuestionType(input: string): QuestionTypeClass {
|
export function resolveQuestionType(input: string): QuestionTypeClass {
|
||||||
const text = String(input ?? "").trim();
|
const text = String(input ?? "").trim();
|
||||||
if (!text) {
|
if (!text) {
|
||||||
return "unknown";
|
return "unknown";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let bestType: QuestionTypeClass = "unknown";
|
||||||
|
let bestHits = 0;
|
||||||
|
let bestPriority = Number.POSITIVE_INFINITY;
|
||||||
|
|
||||||
for (const rule of QUESTION_TYPE_RULES) {
|
for (const rule of QUESTION_TYPE_RULES) {
|
||||||
if (rule.pattern.test(text)) {
|
const hits = countRuleHits(text, rule);
|
||||||
return rule.type;
|
if (hits <= 0) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (hits > bestHits || (hits === bestHits && rule.priority < bestPriority)) {
|
||||||
|
bestType = rule.type;
|
||||||
|
bestHits = hits;
|
||||||
|
bestPriority = rule.priority;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (/[??]/u.test(text)) {
|
if (bestType !== "unknown") {
|
||||||
|
return bestType;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (/[?пјџ]/u.test(text)) {
|
||||||
return "why_breaks";
|
return "why_breaks";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
import { describe, expect, it } from "vitest";
|
import { describe, expect, it } from "vitest";
|
||||||
import { composeAssistantAnswer } from "../src/services/answerComposer";
|
import { composeAssistantAnswer } from "../src/services/answerComposer";
|
||||||
import type { AnswerGroundingCheck, RequirementCoverageReport, UnifiedRetrievalResult } from "../src/types/assistant";
|
import type { AnswerGroundingCheck, RequirementCoverageReport, UnifiedRetrievalResult } from "../src/types/assistant";
|
||||||
import type { ProblemUnit } from "../src/types/stage2ProblemUnits";
|
import type { ProblemUnit } from "../src/types/stage2ProblemUnits";
|
||||||
|
|
@ -116,7 +116,7 @@ function buildRetrieval(input: {
|
||||||
{
|
{
|
||||||
source_entity: "Document",
|
source_entity: "Document",
|
||||||
source_id: "DOC-1",
|
source_id: "DOC-1",
|
||||||
display_name: "Счет № 4 от 07.07.20",
|
display_name: "Счет № 4 от 07.07.20",
|
||||||
account_context: accountScope,
|
account_context: accountScope,
|
||||||
graph_domain_scope: domainScope,
|
graph_domain_scope: domainScope,
|
||||||
relation_pattern_hits: relationPatterns,
|
relation_pattern_hits: relationPatterns,
|
||||||
|
|
@ -177,7 +177,7 @@ function buildRetrieval(input: {
|
||||||
limitation: null,
|
limitation: null,
|
||||||
payload: {
|
payload: {
|
||||||
notes: input.notes ?? [],
|
notes: input.notes ?? [],
|
||||||
contract: "договор № 01/19-ПТ",
|
contract: "РґРѕРіРѕРІРѕСЂ в„– 01/19-РџРў",
|
||||||
amount: "276 873,60",
|
amount: "276 873,60",
|
||||||
date: "07.07.20"
|
date: "07.07.20"
|
||||||
}
|
}
|
||||||
|
|
@ -245,21 +245,21 @@ function composeCase(input: {
|
||||||
focusDomainHint: input.focusDomainHint,
|
focusDomainHint: input.focusDomainHint,
|
||||||
questionTypeHint: input.questionType,
|
questionTypeHint: input.questionType,
|
||||||
companyAnchors: {
|
companyAnchors: {
|
||||||
contract_numbers: ["договор № 01/19-ПТ"],
|
contract_numbers: ["РґРѕРіРѕРІРѕСЂ в„– 01/19-РџРў"],
|
||||||
document_numbers: ["документ № 4"],
|
document_numbers: ["документ № 4"],
|
||||||
dates: ["07.07.20", "13.07.20"],
|
dates: ["07.07.20", "13.07.20"],
|
||||||
amounts: ["276 873,60"],
|
amounts: ["276 873,60"],
|
||||||
accounts: ["62.02"],
|
accounts: ["62.02"],
|
||||||
periods: ["июль 2020"],
|
periods: ["июль 2020"],
|
||||||
document_types: ["payment", "invoice"],
|
document_types: ["payment", "invoice"],
|
||||||
all: [
|
all: [
|
||||||
"договор № 01/19-ПТ",
|
"РґРѕРіРѕРІРѕСЂ в„– 01/19-РџРў",
|
||||||
"документ № 4",
|
"документ № 4",
|
||||||
"07.07.20",
|
"07.07.20",
|
||||||
"13.07.20",
|
"13.07.20",
|
||||||
"276 873,60",
|
"276 873,60",
|
||||||
"account:62.02",
|
"account:62.02",
|
||||||
"period:июль 2020"
|
"period:июль 2020"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
enableAnswerPolicyV11: true,
|
enableAnswerPolicyV11: true,
|
||||||
|
|
@ -279,7 +279,7 @@ describe("wave13 domain fit + question-type fit + company-anchor grounding", ()
|
||||||
});
|
});
|
||||||
const output = composeCase({
|
const output = composeCase({
|
||||||
userMessage:
|
userMessage:
|
||||||
"Почему по договору № 01/19-ПТ от 09.01.2019 оплата 276 873,60 есть, а 62.01/62.02 все равно не сходятся?",
|
"Почему по договору № 01/19-ПТ от 09.01.2019 оплата 276 873,60 есть, а 62.01/62.02 все равно не сходятся?",
|
||||||
questionType: "why_breaks",
|
questionType: "why_breaks",
|
||||||
focusDomainHint: "vat_document_register_book",
|
focusDomainHint: "vat_document_register_book",
|
||||||
retrievalResults: [
|
retrievalResults: [
|
||||||
|
|
@ -294,8 +294,8 @@ describe("wave13 domain fit + question-type fit + company-anchor grounding", ()
|
||||||
]
|
]
|
||||||
});
|
});
|
||||||
|
|
||||||
expect(output.assistant_reply).toMatch(/расчет|62\.01|62\.02|зачет|зачёт/i);
|
expect(output.assistant_reply).toMatch(/расчет|62\.01|62\.02|зачет|зачёт/i);
|
||||||
expect(output.assistant_reply).not.toMatch(/переход от документа к регистру и книге|цепочке ндс/i);
|
expect(output.assistant_reply).not.toMatch(/переход от документа к регистру и книге|цепочке ндс/i);
|
||||||
});
|
});
|
||||||
|
|
||||||
it("question_type_where_break_is_must_produce_localization_style_line", () => {
|
it("question_type_where_break_is_must_produce_localization_style_line", () => {
|
||||||
|
|
@ -308,13 +308,13 @@ describe("wave13 domain fit + question-type fit + company-anchor grounding", ()
|
||||||
});
|
});
|
||||||
const output = composeCase({
|
const output = composeCase({
|
||||||
userMessage:
|
userMessage:
|
||||||
"Где именно разрыв по договору № 01/19-ПТ: в договоре, объекте расчетов или в связке документов?",
|
"Где именно разрыв по договору № 01/19-ПТ: в договоре, объекте расчетов или в связке документов?",
|
||||||
questionType: "where_break_is",
|
questionType: "where_break_is",
|
||||||
focusDomainHint: "settlements_60_62",
|
focusDomainHint: "settlements_60_62",
|
||||||
retrievalResults: [buildRetrieval({ requirementId: "R1", status: "ok", units: [settlementUnit] })]
|
retrievalResults: [buildRetrieval({ requirementId: "R1", status: "ok", units: [settlementUnit] })]
|
||||||
});
|
});
|
||||||
|
|
||||||
expect(output.assistant_reply).toMatch(/локализ|узел разрыва|где именно/i);
|
expect(output.assistant_reply).toMatch(/локализ|узел разрыва|где/i);
|
||||||
});
|
});
|
||||||
|
|
||||||
it("question_type_prove_or_guess_must_explicitly_separate_proven_vs_hypothesis", () => {
|
it("question_type_prove_or_guess_must_explicitly_separate_proven_vs_hypothesis", () => {
|
||||||
|
|
@ -327,7 +327,7 @@ describe("wave13 domain fit + question-type fit + company-anchor grounding", ()
|
||||||
});
|
});
|
||||||
const output = composeCase({
|
const output = composeCase({
|
||||||
userMessage:
|
userMessage:
|
||||||
"По НДС это доказано по данным или это только гипотеза? На чем основано утверждение?",
|
"По НДС это доказано по данным или это только гипотеза? На чем основано утверждение?",
|
||||||
questionType: "prove_or_guess",
|
questionType: "prove_or_guess",
|
||||||
focusDomainHint: "vat_document_register_book",
|
focusDomainHint: "vat_document_register_book",
|
||||||
retrievalResults: [
|
retrievalResults: [
|
||||||
|
|
@ -346,26 +346,26 @@ describe("wave13 domain fit + question-type fit + company-anchor grounding", ()
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
expect(output.assistant_reply).toMatch(/доказан|гипотез|ограничени/i);
|
expect(output.assistant_reply).toMatch(/доказ|гипотез|огранич/i);
|
||||||
});
|
});
|
||||||
|
|
||||||
it("anchor_usage_lines_must_be_present_when_company_anchors_are_used", () => {
|
it("anchor_usage_lines_must_be_present_when_company_anchors_are_used", () => {
|
||||||
const output = composeCase({
|
const output = composeCase({
|
||||||
userMessage:
|
userMessage:
|
||||||
"Оплата по счету № 4 от 07.07.20 на 276 873,60 пришла 13 июля. Что проверить первым по 62.02?",
|
"Оплата по счету № 4 от 07.07.20 на 276 873,60 пришла 13 июля. Что проверить первым по 62.02?",
|
||||||
questionType: "what_to_check_first",
|
questionType: "what_to_check_first",
|
||||||
focusDomainHint: "settlements_60_62",
|
focusDomainHint: "settlements_60_62",
|
||||||
retrievalResults: [buildRetrieval({ requirementId: "R1", status: "ok" })]
|
retrievalResults: [buildRetrieval({ requirementId: "R1", status: "ok" })]
|
||||||
});
|
});
|
||||||
|
|
||||||
expect(output.assistant_reply).toMatch(/якоря вопроса/i);
|
expect(output.assistant_reply).toMatch(/якоря вопроса|в опоре использованы|якоря из вопроса/i);
|
||||||
expect(output.assistant_reply).toMatch(/договор|07\.07\.20|276 873,60|62\.02/i);
|
expect(output.assistant_reply).toMatch(/РґРѕРіРѕРІРѕСЂ|07\.07\.20|276 873,60|62\.02/i);
|
||||||
});
|
});
|
||||||
|
|
||||||
it("anchor_usage_must_be_honest_when_part_of_anchors_not_confirmed", () => {
|
it("anchor_usage_must_be_honest_when_part_of_anchors_not_confirmed", () => {
|
||||||
const output = composeCase({
|
const output = composeCase({
|
||||||
userMessage:
|
userMessage:
|
||||||
"Почему по договору № 01/19-ПТ не сходится 62.02 в июле 2020, если была оплата 276 873,60?",
|
"Почему по договору № 01/19-ПТ не сходится 62.02 в июле 2020, если была оплата 276 873,60?",
|
||||||
questionType: "what_is_it_grounded_on",
|
questionType: "what_is_it_grounded_on",
|
||||||
focusDomainHint: "settlements_60_62",
|
focusDomainHint: "settlements_60_62",
|
||||||
retrievalResults: [
|
retrievalResults: [
|
||||||
|
|
@ -380,19 +380,19 @@ describe("wave13 domain fit + question-type fit + company-anchor grounding", ()
|
||||||
]
|
]
|
||||||
});
|
});
|
||||||
|
|
||||||
expect(output.assistant_reply).toMatch(/без прямого подтверждения|ограничени/i);
|
expect(output.assistant_reply).toMatch(/без прямого подтверждения|огранич/i);
|
||||||
});
|
});
|
||||||
|
|
||||||
it("answers_for_different_question_types_must_not_collapse_to_same_generic_pattern", () => {
|
it("answers_for_different_question_types_must_not_collapse_to_same_generic_pattern", () => {
|
||||||
const baseRetrieval = [buildRetrieval({ requirementId: "R1", status: "ok" })];
|
const baseRetrieval = [buildRetrieval({ requirementId: "R1", status: "ok" })];
|
||||||
const whereOutput = composeCase({
|
const whereOutput = composeCase({
|
||||||
userMessage: "Где именно разрыв по 62.01/62.02?",
|
userMessage: "Где именно разрыв по 62.01/62.02?",
|
||||||
questionType: "where_break_is",
|
questionType: "where_break_is",
|
||||||
focusDomainHint: "settlements_60_62",
|
focusDomainHint: "settlements_60_62",
|
||||||
retrievalResults: baseRetrieval
|
retrievalResults: baseRetrieval
|
||||||
});
|
});
|
||||||
const checkFirstOutput = composeCase({
|
const checkFirstOutput = composeCase({
|
||||||
userMessage: "Что проверить первым по 62.01/62.02?",
|
userMessage: "Что проверить первым по 62.01/62.02?",
|
||||||
questionType: "what_to_check_first",
|
questionType: "what_to_check_first",
|
||||||
focusDomainHint: "settlements_60_62",
|
focusDomainHint: "settlements_60_62",
|
||||||
retrievalResults: baseRetrieval
|
retrievalResults: baseRetrieval
|
||||||
|
|
@ -400,6 +400,9 @@ describe("wave13 domain fit + question-type fit + company-anchor grounding", ()
|
||||||
|
|
||||||
expect(whereOutput.assistant_reply).not.toEqual(checkFirstOutput.assistant_reply);
|
expect(whereOutput.assistant_reply).not.toEqual(checkFirstOutput.assistant_reply);
|
||||||
expect(whereOutput.assistant_reply).toMatch(/локализ|разрыв/i);
|
expect(whereOutput.assistant_reply).toMatch(/локализ|разрыв/i);
|
||||||
expect(checkFirstOutput.assistant_reply).toMatch(/первый маршрут проверки|начните с первого пункта/i);
|
expect(checkFirstOutput.assistant_reply).toMatch(
|
||||||
|
/маршрут первых проверок|проверьте пункты по порядку|начните с первого пункта/i
|
||||||
|
);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,334 @@
|
||||||
|
import { describe, expect, it } from "vitest";
|
||||||
|
import { composeAssistantAnswer } from "../src/services/answerComposer";
|
||||||
|
import type { AnswerGroundingCheck, RequirementCoverageReport, UnifiedRetrievalResult } from "../src/types/assistant";
|
||||||
|
import type { ProblemUnit } from "../src/types/stage2ProblemUnits";
|
||||||
|
import type { QuestionTypeClass } from "../src/services/questionTypeResolver";
|
||||||
|
|
||||||
|
function buildRouteSummary() {
|
||||||
|
return {
|
||||||
|
mode: "deterministic_v2" as const,
|
||||||
|
message_in_scope: true,
|
||||||
|
scope_confidence: "high" as const,
|
||||||
|
planner: {
|
||||||
|
total_fragments: 1,
|
||||||
|
in_scope_fragments: 1,
|
||||||
|
out_of_scope_fragments: 0,
|
||||||
|
discarded_fragments: 0,
|
||||||
|
contains_multiple_tasks: false
|
||||||
|
},
|
||||||
|
decisions: [],
|
||||||
|
fallback: {
|
||||||
|
type: "none" as const,
|
||||||
|
message: null
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
function buildCoverage(input?: Partial<RequirementCoverageReport>): RequirementCoverageReport {
|
||||||
|
return {
|
||||||
|
requirements_total: 1,
|
||||||
|
requirements_covered: 1,
|
||||||
|
requirements_uncovered: [],
|
||||||
|
requirements_partially_covered: [],
|
||||||
|
clarification_needed_for: [],
|
||||||
|
out_of_scope_requirements: [],
|
||||||
|
...input
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
function buildGrounding(input?: Partial<AnswerGroundingCheck>): AnswerGroundingCheck {
|
||||||
|
return {
|
||||||
|
status: "grounded",
|
||||||
|
route_subject_match: true,
|
||||||
|
missing_requirements: [],
|
||||||
|
reasons: [],
|
||||||
|
why_included_summary: ["wave15-test"],
|
||||||
|
selection_reason_summary: ["wave15-test"],
|
||||||
|
...input
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
function buildProblemUnit(input: {
|
||||||
|
id: string;
|
||||||
|
type: ProblemUnit["problem_unit_type"];
|
||||||
|
account: string;
|
||||||
|
defect: string;
|
||||||
|
lifecycleDomain: ProblemUnit["lifecycle_domain"];
|
||||||
|
}): ProblemUnit {
|
||||||
|
return {
|
||||||
|
schema_version: "problem_unit_v0_1",
|
||||||
|
problem_unit_id: input.id,
|
||||||
|
problem_unit_type: input.type,
|
||||||
|
title: "Wave15 problem unit",
|
||||||
|
mechanism_summary: `Mechanism candidate: ${input.defect}.`,
|
||||||
|
business_defect_class: input.defect,
|
||||||
|
severity: {
|
||||||
|
score: 0.74,
|
||||||
|
grade: "high"
|
||||||
|
},
|
||||||
|
confidence: {
|
||||||
|
score: 0.66,
|
||||||
|
grade: "medium"
|
||||||
|
},
|
||||||
|
lifecycle_domain: input.lifecycleDomain,
|
||||||
|
affected_entities: ["Document:DOC-1"],
|
||||||
|
affected_documents: ["Document:DOC-1"],
|
||||||
|
affected_postings: ["Posting:POST-1"],
|
||||||
|
affected_accounts: [input.account],
|
||||||
|
affected_counterparties: ["Counterparty:CP-1"],
|
||||||
|
affected_contracts: ["Contract:CTR-1"],
|
||||||
|
failed_expected_edge: input.defect,
|
||||||
|
period_impact: {
|
||||||
|
is_period_sensitive: true,
|
||||||
|
impact_class: "close_risk"
|
||||||
|
},
|
||||||
|
evidence_pack: ["cand-1"],
|
||||||
|
entity_backlinks: [{ entity: "Document", id: "DOC-1" }],
|
||||||
|
snapshot_limitations: []
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
function buildRetrieval(input: {
|
||||||
|
requirementId: string;
|
||||||
|
status: UnifiedRetrievalResult["status"];
|
||||||
|
units?: ProblemUnit[];
|
||||||
|
accountScope?: string[];
|
||||||
|
domainScope?: string[];
|
||||||
|
relationPatterns?: string[];
|
||||||
|
}): UnifiedRetrievalResult {
|
||||||
|
const units = input.units ?? [];
|
||||||
|
const accountScope = input.accountScope ?? ["60", "62"];
|
||||||
|
const domainScope = input.domainScope ?? ["customer_settlement"];
|
||||||
|
const relationPatterns = input.relationPatterns ?? ["payment_to_settlement"];
|
||||||
|
|
||||||
|
return {
|
||||||
|
fragment_id: `F-${input.requirementId}`,
|
||||||
|
requirement_ids: [input.requirementId],
|
||||||
|
route: "hybrid_store_plus_live",
|
||||||
|
status: input.status,
|
||||||
|
result_type: "chain",
|
||||||
|
items:
|
||||||
|
input.status === "empty"
|
||||||
|
? []
|
||||||
|
: [
|
||||||
|
{
|
||||||
|
source_entity: "Document",
|
||||||
|
source_id: "DOC-1",
|
||||||
|
display_name: "Счет №4 от 07.07.20",
|
||||||
|
account_context: accountScope,
|
||||||
|
graph_domain_scope: domainScope,
|
||||||
|
relation_pattern_hits: relationPatterns,
|
||||||
|
period: "2020-07",
|
||||||
|
amount: "276 873,60"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
summary: {
|
||||||
|
broad_query_detected: false,
|
||||||
|
broad_result_flag: false,
|
||||||
|
minimum_evidence_failed: false,
|
||||||
|
degraded_to: null,
|
||||||
|
narrowing_strength: "strong",
|
||||||
|
semantic_profile: {
|
||||||
|
account_scope: accountScope,
|
||||||
|
domain_scope: domainScope,
|
||||||
|
relation_patterns: relationPatterns,
|
||||||
|
period_scope: {
|
||||||
|
from: "2020-07-01",
|
||||||
|
to: "2020-07-31",
|
||||||
|
granularity: "month"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
evidence:
|
||||||
|
input.status === "empty"
|
||||||
|
? []
|
||||||
|
: [
|
||||||
|
{
|
||||||
|
evidence_id: `ev-${input.requirementId}`,
|
||||||
|
claim_ref: `requirement:${input.requirementId}`,
|
||||||
|
source_type: "retrieval_item",
|
||||||
|
source_ref: {
|
||||||
|
schema_version: "evidence_source_ref_v1",
|
||||||
|
namespace: "snapshot_2020_07",
|
||||||
|
entity: "document",
|
||||||
|
id: "DOC-1",
|
||||||
|
period: "2020-07",
|
||||||
|
canonical_ref: "evidence_source_ref_v1|snapshot_2020_07|document|doc-1|2020-07"
|
||||||
|
},
|
||||||
|
pointer: {
|
||||||
|
fragment_id: `F-${input.requirementId}`,
|
||||||
|
route: "hybrid_store_plus_live",
|
||||||
|
source: {
|
||||||
|
namespace: "snapshot_2020_07",
|
||||||
|
entity: "document",
|
||||||
|
id: "DOC-1",
|
||||||
|
period: "2020-07"
|
||||||
|
},
|
||||||
|
locator: {
|
||||||
|
field_path: "risk_score",
|
||||||
|
item_index: 0
|
||||||
|
}
|
||||||
|
},
|
||||||
|
evidence_kind: "mechanism_link",
|
||||||
|
mechanism_note: relationPatterns[0],
|
||||||
|
confidence: "medium",
|
||||||
|
limitation: null,
|
||||||
|
payload: {
|
||||||
|
notes: ["wave15"],
|
||||||
|
contract: "договор № 01/19-ПТ",
|
||||||
|
amount: "276 873,60",
|
||||||
|
date: "07.07.20"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
candidate_evidence: [],
|
||||||
|
problem_units: units,
|
||||||
|
problem_unit_summary:
|
||||||
|
units.length > 0
|
||||||
|
? {
|
||||||
|
schema_version: "problem_unit_summary_v0_1",
|
||||||
|
units_total: units.length,
|
||||||
|
duplicate_collapses: 0,
|
||||||
|
unit_types: units.map((unit) => unit.problem_unit_type),
|
||||||
|
type_distribution: {
|
||||||
|
[units[0]?.problem_unit_type ?? "broken_chain_segment"]: units.length
|
||||||
|
},
|
||||||
|
severity_distribution: {
|
||||||
|
low: 0,
|
||||||
|
medium: 0,
|
||||||
|
high: units.length
|
||||||
|
},
|
||||||
|
confidence_distribution: {
|
||||||
|
low: 0,
|
||||||
|
medium: units.length,
|
||||||
|
high: 0
|
||||||
|
},
|
||||||
|
primary_unit_type: units[0]?.problem_unit_type ?? null
|
||||||
|
}
|
||||||
|
: null,
|
||||||
|
why_included: ["wave15-test"],
|
||||||
|
selection_reason: ["wave15-test"],
|
||||||
|
risk_factors: ["wave15"],
|
||||||
|
business_interpretation: ["wave15"],
|
||||||
|
confidence: "medium",
|
||||||
|
limitations: [],
|
||||||
|
errors: []
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
function composeCase(input: {
|
||||||
|
userMessage: string;
|
||||||
|
questionType: QuestionTypeClass;
|
||||||
|
focusDomainHint: string | null;
|
||||||
|
retrievalResults: UnifiedRetrievalResult[];
|
||||||
|
coverage?: Partial<RequirementCoverageReport>;
|
||||||
|
grounding?: Partial<AnswerGroundingCheck>;
|
||||||
|
}) {
|
||||||
|
return composeAssistantAnswer({
|
||||||
|
userMessage: input.userMessage,
|
||||||
|
routeSummary: buildRouteSummary(),
|
||||||
|
retrievalResults: input.retrievalResults,
|
||||||
|
requirements: [
|
||||||
|
{
|
||||||
|
requirement_id: "R1",
|
||||||
|
source_fragment_id: "F-R1",
|
||||||
|
requirement_text: "Wave15 requirement",
|
||||||
|
subject_tokens: [],
|
||||||
|
status: "covered",
|
||||||
|
route: "hybrid_store_plus_live"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
coverageReport: buildCoverage(input.coverage),
|
||||||
|
groundingCheck: buildGrounding(input.grounding),
|
||||||
|
focusDomainHint: input.focusDomainHint,
|
||||||
|
questionTypeHint: input.questionType,
|
||||||
|
companyAnchors: {
|
||||||
|
contract_numbers: ["договор № 01/19-ПТ"],
|
||||||
|
document_numbers: ["документ № 4"],
|
||||||
|
dates: ["07.07.20"],
|
||||||
|
amounts: ["276 873,60"],
|
||||||
|
accounts: ["62.02"],
|
||||||
|
periods: ["июль 2020"],
|
||||||
|
document_types: ["payment"],
|
||||||
|
all: ["договор № 01/19-ПТ", "документ № 4", "07.07.20", "276 873,60", "account:62.02", "period:июль 2020"]
|
||||||
|
},
|
||||||
|
enableAnswerPolicyV11: true,
|
||||||
|
enableProblemCentricAnswerV1: true,
|
||||||
|
enableLifecycleAnswerV1: true
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
describe("wave15 question-type contract + first-check relevance", () => {
|
||||||
|
const settlementUnit = buildProblemUnit({
|
||||||
|
id: "pu-settlement-wave15",
|
||||||
|
type: "broken_chain_segment",
|
||||||
|
account: "62.02",
|
||||||
|
defect: "failed_edge:payment_to_settlement",
|
||||||
|
lifecycleDomain: "customer_settlement"
|
||||||
|
});
|
||||||
|
|
||||||
|
it("prove_or_guess answer keeps proof-vs-hypothesis framing", () => {
|
||||||
|
const output = composeCase({
|
||||||
|
userMessage: "Это по расчетам доказано или пока гипотеза?",
|
||||||
|
questionType: "prove_or_guess",
|
||||||
|
focusDomainHint: "settlements_60_62",
|
||||||
|
retrievalResults: [buildRetrieval({ requirementId: "R1", status: "ok", units: [settlementUnit] })],
|
||||||
|
grounding: {
|
||||||
|
status: "partial",
|
||||||
|
reasons: ["Mechanism is unresolved for part of the evidence."]
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(output.assistant_reply).toMatch(/доказ|гипотез|ограничен/i);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("grounded_on answer is basis-oriented, not generic mechanism dump", () => {
|
||||||
|
const output = composeCase({
|
||||||
|
userMessage: "На чем это основано по 62.02?",
|
||||||
|
questionType: "what_is_it_grounded_on",
|
||||||
|
focusDomainHint: "settlements_60_62",
|
||||||
|
retrievalResults: [buildRetrieval({ requirementId: "R1", status: "ok", units: [settlementUnit] })]
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(output.assistant_reply).toMatch(/основан|опор|документ|регистр|60\/62\/76/i);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("chains question provides complete-vs-incomplete framing", () => {
|
||||||
|
const output = composeCase({
|
||||||
|
userMessage: "Какие цепочки завершены, а какие нет по 62.01/62.02?",
|
||||||
|
questionType: "which_chains_are_complete_vs_incomplete",
|
||||||
|
focusDomainHint: "settlements_60_62",
|
||||||
|
retrievalResults: [buildRetrieval({ requirementId: "R1", status: "ok", units: [settlementUnit] })],
|
||||||
|
coverage: {
|
||||||
|
requirements_covered: 0,
|
||||||
|
requirements_partially_covered: ["R1"],
|
||||||
|
requirements_uncovered: []
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(output.assistant_reply).toMatch(/цепочк|подтвержден|частично|не подтвержден/i);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("where_break_is answer localizes break node", () => {
|
||||||
|
const output = composeCase({
|
||||||
|
userMessage: "Где именно разрыв по расчетной цепочке?",
|
||||||
|
questionType: "where_break_is",
|
||||||
|
focusDomainHint: "settlements_60_62",
|
||||||
|
retrievalResults: [buildRetrieval({ requirementId: "R1", status: "ok", units: [settlementUnit] })]
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(output.assistant_reply).toMatch(/узел разрыва|локализ|где/i);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("what_to_check_first answer gives short operational route", () => {
|
||||||
|
const output = composeCase({
|
||||||
|
userMessage: "Что проверить первым по 62.01/62.02?",
|
||||||
|
questionType: "what_to_check_first",
|
||||||
|
focusDomainHint: "settlements_60_62",
|
||||||
|
retrievalResults: [buildRetrieval({ requirementId: "R1", status: "ok", units: [settlementUnit] })]
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(output.assistant_reply).toMatch(/что проверить первым/i);
|
||||||
|
expect(output.assistant_reply).toMatch(/договор|регистр|60\/62\/76/i);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
@ -0,0 +1,34 @@
|
||||||
|
import { describe, expect, it } from "vitest";
|
||||||
|
import { resolveQuestionType } from "../src/services/questionTypeResolver";
|
||||||
|
|
||||||
|
describe("questionTypeResolver", () => {
|
||||||
|
it("resolves what_to_check_first for operational check phrasing", () => {
|
||||||
|
expect(resolveQuestionType("Что проверить первым по 62.01/62.02?"))
|
||||||
|
.toBe("what_to_check_first");
|
||||||
|
});
|
||||||
|
|
||||||
|
it("resolves where_break_is for localization phrasing", () => {
|
||||||
|
expect(resolveQuestionType("Где именно разрыв: в договоре или в связке документ -> регистр?"))
|
||||||
|
.toBe("where_break_is");
|
||||||
|
});
|
||||||
|
|
||||||
|
it("resolves prove_or_guess without collapsing to generic yes/no", () => {
|
||||||
|
expect(resolveQuestionType("Это доказано или пока только гипотеза?"))
|
||||||
|
.toBe("prove_or_guess");
|
||||||
|
});
|
||||||
|
|
||||||
|
it("resolves grounded_on when question asks for evidence basis", () => {
|
||||||
|
expect(resolveQuestionType("На чем это основано и чем подтверждается вывод?"))
|
||||||
|
.toBe("what_is_it_grounded_on");
|
||||||
|
});
|
||||||
|
|
||||||
|
it("resolves chains completeness classification questions", () => {
|
||||||
|
expect(resolveQuestionType("Какие цепочки подтверждены, а какие не завершены?"))
|
||||||
|
.toBe("which_chains_are_complete_vs_incomplete");
|
||||||
|
});
|
||||||
|
|
||||||
|
it("falls back to why_breaks for generic why-question", () => {
|
||||||
|
expect(resolveQuestionType("Почему не сходится 62.01/62.02?"))
|
||||||
|
.toBe("why_breaks");
|
||||||
|
});
|
||||||
|
});
|
||||||
Binary file not shown.
Loading…
Reference in New Issue