ГЛОБАЛЬНЫЙ РЕФАКТОРИНГ АРХИТЕКТУРЫ - Этап 4.3: унификация контракта ответов и добавление теста формы Stage 4

This commit is contained in:
2026-04-12 01:17:56 +03:00
parent 522575c7cc
commit 88a7da4f0a
4 changed files with 403 additions and 167 deletions
+65 -60
View File
@@ -628,6 +628,30 @@ function formatList(items) {
}
return items.map((item) => `- ${item}`).join("\n");
}
function renderStage4ContractReply(input) {
const shortLine = ensureSentence(input.shortLine);
const checkedLines = dedupeNarrativeLines(input.checkedLines, 6);
const foundLines = dedupeNarrativeLines(input.foundLines, 6);
const unresolvedLines = dedupeNarrativeLines(input.unresolvedLines, 6);
const nextStepLines = dedupeNarrativeLines(input.nextStepLines, 5);
const contextLead = ensureSentence(String(input.contextLead ?? ""));
return sanitizeUserFacingReply([
`Коротко: ${shortLine}`,
contextLead || "",
`Что именно проверено:\n${formatList(checkedLines.length > 0
? checkedLines
: ["Подтвержденная опора собрана частично; для полного вывода нужен дополнительный проход."])}`,
`Что найдено:\n${formatList(foundLines.length > 0 ? foundLines : ["Явные отклонения по текущей опоре не подтверждены."])}`,
`Что пока не доказано:\n${formatList(unresolvedLines.length > 0
? unresolvedLines
: ["Существенных ограничений в текущем срезе не выявлено."])}`,
`${input.nextStepTitle}:\n${formatList(nextStepLines.length > 0
? nextStepLines
: ["Уточните период, объект или контрагента, чтобы продолжить проверку по 1С."])}`
]
.filter(Boolean)
.join("\n\n"));
}
function formatSafeItemLine(entity, sourceId, riskScore) {
const entityLabel = sanitizeUserText(String(entity ?? "")) ?? "Record";
const idRaw = String(sourceId ?? "").trim();
@@ -1699,24 +1723,21 @@ function buildBoundaryFallbackReply(input) {
"По этому запросу у меня нет надежного доменного покрытия, поэтому даю мягкий отказ вместо технического шаблона.",
"Сейчас у меня нет надежного доменного маршрута по этому запросу, поэтому даю мягкий отказ вместо шаблонной технички."
]);
return sanitizeUserFacingReply([
`Коротко: ${ensureSentence(heading)}`,
`Что именно проверено:\n${formatList([
return renderStage4ContractReply({
shortLine: heading,
checkedLines: [
"Проверен доступный контур 1С и возможность маршрутизации запроса.",
"Надежный доменный путь для текущей формулировки не подтвержден."
])}`,
`Что найдено:\n${formatList(nearbyCapabilities.length > 0
],
foundLines: nearbyCapabilities.length > 0
? nearbyCapabilities
: ["Близких поддерживаемых сценариев по текущей формулировке не найдено."])}`,
`Что пока не доказано:\n${formatList([
"Запрос не подтвержден в поддерживаемом контуре как надежный бухгалтерский сценарий."
])}`,
`Что могу сделать сейчас:\n${formatList(quickActionItems.length > 0
: ["Близких поддерживаемых сценариев по текущей формулировке не найдено."],
unresolvedLines: ["Запрос не подтвержден в поддерживаемом контуре как надежный бухгалтерский сценарий."],
nextStepLines: quickActionItems.length > 0
? quickActionItems
: [quickActionLine ?? "Переформулируйте вопрос через период, счет или объект, и я сразу продолжу проверку."])}`
]
.filter(Boolean)
.join("\n\n"));
: [quickActionLine ?? "Переформулируйте вопрос через период, счет или объект, и я сразу продолжу проверку."],
nextStepTitle: "Что могу сделать сейчас"
});
}
const clarificationHints = buildNaturalClarificationHints({
missingAnchors: input.missingAnchors,
@@ -1726,24 +1747,23 @@ function buildBoundaryFallbackReply(input) {
`Сейчас не могу надежно ответить по сценарию ${formatNarrativeDomainLabel(input.focusDomain)}: не хватает опоры.`,
`По сценарию ${formatNarrativeDomainLabel(input.focusDomain)} пока не хватает подтвержденной опоры для надежного вывода.`
]);
return sanitizeUserFacingReply([
`Коротко: ${ensureSentence(domainHeading)}`,
`Что именно проверено:\n${formatList([
return renderStage4ContractReply({
shortLine: domainHeading,
checkedLines: [
`Проверен сценарий ${formatNarrativeDomainLabel(input.focusDomain)} в текущем контуре 1С.`,
"Оценена достаточность подтвержденной опоры для прямого вывода."
])}`,
`Что найдено:\n${formatList(nearbyCapabilities.length > 0
],
foundLines: nearbyCapabilities.length > 0
? nearbyCapabilities.slice(0, 2)
: ["Подтвержденных близких сценариев в текущем проходе не найдено."])}`,
`Что пока не доказано:\n${formatList(clarificationHints.length > 0
: ["Подтвержденных близких сценариев в текущем проходе не найдено."],
unresolvedLines: clarificationHints.length > 0
? clarificationHints
: ["Без дополнительного ориентира (период, объект или контрагент) вывод останется частичным."])}`,
`Что могу сделать сейчас:\n${formatList(quickActionItems.length > 0
: ["Без дополнительного ориентира (период, объект или контрагент) вывод останется частичным."],
nextStepLines: quickActionItems.length > 0
? quickActionItems
: [quickActionLine ?? "Добавьте один уточняющий ориентир, и я продолжу проверку без смены контура."])}`
]
.filter(Boolean)
.join("\n\n"));
: [quickActionLine ?? "Добавьте один уточняющий ориентир, и я продолжу проверку без смены контура."],
nextStepTitle: "Что могу сделать сейчас"
});
}
function ensureSentence(value) {
const sanitized = sanitizeUserText(value) ?? String(value ?? "").trim();
@@ -3677,25 +3697,14 @@ function renderPolicyReply(structure, context) {
limitationLines
};
const enriched = enforceDomainWordingIsolation(enrichedBase, structure, context);
const checkedLines = dedupeNarrativeLines(enriched.evidenceLines, 6);
const foundLines = dedupeNarrativeLines([...enriched.brokenLines, ...enriched.whyLines], 6);
const unresolvedLines = dedupeNarrativeLines(enriched.limitationLines, 6);
const nextStepLines = dedupeNarrativeLines(enriched.checkLines, 5);
return sanitizeUserFacingReply([
`Коротко: ${enriched.shortLine}`,
`Что именно проверено:\n${formatList(checkedLines.length > 0
? checkedLines
: ["Подтвержденная опора собрана частично; для полного вывода нужен дополнительный проход."])}`,
`Что найдено:\n${formatList(foundLines.length > 0 ? foundLines : ["Явные отклонения по текущей опоре не подтверждены."])}`,
`Что пока не доказано:\n${formatList(unresolvedLines.length > 0
? unresolvedLines
: ["Существенных ограничений в текущем срезе не выявлено."])}`,
`Что проверить первым:\n${formatList(nextStepLines.length > 0
? nextStepLines
: ["Уточните период, объект или контрагента, чтобы продолжить проверку по 1С."])}`
]
.filter(Boolean)
.join("\n\n"));
return renderStage4ContractReply({
shortLine: enriched.shortLine,
checkedLines: enriched.evidenceLines,
foundLines: [...enriched.brokenLines, ...enriched.whyLines],
unresolvedLines: enriched.limitationLines,
nextStepLines: enriched.checkLines,
nextStepTitle: "Что проверить первым"
});
}
function shouldUseSoftPolicyReply(input) {
if (input.mode === "focused_grounded" || input.mode === "route_mismatch" || input.mode === "backend_error" || input.mode === "out_of_scope") {
@@ -3735,22 +3744,18 @@ function renderSoftPolicyReply(input) {
: input.mode === "no_grounded" || input.mode === "empty"
? "Сейчас подтвержденной опоры недостаточно для прямого вывода."
: "Есть рабочие сигналы, но часть вывода пока ограничена.";
return sanitizeUserFacingReply([
`Коротко: ${shortLine}`,
modeLine,
`Что именно проверено:\n${formatList(evidenceLines.length > 0
return renderStage4ContractReply({
shortLine,
checkedLines: evidenceLines.length > 0
? evidenceLines
: ["Подтвержденная опора собрана частично; для полного вывода нужна дополнительная проверка."])}`,
`Что найдено:\n${formatList(foundLines.length > 0 ? foundLines : ["Пока подтверждена только часть сигнала, без финальной фиксации причины."])}`,
`Что пока не доказано:\n${formatList(limitationLines.length > 0
? limitationLines
: ["Для полного вывода не хватает деталей по части требований."])}`,
`Что могу сделать сейчас:\n${formatList(actionLines.length > 0
: ["Подтвержденная опора собрана частично; для полного вывода нужна дополнительная проверка."],
foundLines: foundLines.length > 0 ? foundLines : ["Пока подтверждена только часть сигнала, без финальной фиксации причины."],
unresolvedLines: limitationLines.length > 0 ? [modeLine, ...limitationLines] : [modeLine, "Для полного вывода не хватает деталей по части требований."],
nextStepLines: actionLines.length > 0
? actionLines
: ["Уточните период, объект или контрагента, чтобы завершить проверку в следующем ходе."])}`
]
.filter(Boolean)
.join("\n\n"));
: ["Уточните период, объект или контрагента, чтобы завершить проверку в следующем ходе."],
nextStepTitle: "Что могу сделать сейчас"
});
}
function composeAssistantAnswerV11(input) {
const fallbackType = fallbackFromSummary(input.routeSummary);