Stage 3 Wave 1: lifecycle-слой встроен в pipeline
This commit is contained in:
@@ -10,7 +10,7 @@ import type { RouteHintSummary } from "../types/normalizer";
|
||||
import type { AnswerStructureV11, EvidenceConfidence, EvidenceItem, EvidenceLimitationReasonCode } from "../types/stage1Contracts";
|
||||
import type { ProblemUnit, ProblemUnitSummary, ProblemUnitType } from "../types/stage2ProblemUnits";
|
||||
|
||||
type ProblemAnswerMode = "stage1_policy_v11" | "stage2_problem_centric_v1";
|
||||
type ProblemAnswerMode = "stage1_policy_v11" | "stage2_problem_centric_v1" | "stage3_lifecycle_aware_v1";
|
||||
|
||||
interface ComposeAnswerInput {
|
||||
userMessage: string;
|
||||
@@ -21,6 +21,7 @@ interface ComposeAnswerInput {
|
||||
groundingCheck: AnswerGroundingCheck;
|
||||
enableAnswerPolicyV11?: boolean;
|
||||
enableProblemCentricAnswerV1?: boolean;
|
||||
enableLifecycleAnswerV1?: boolean;
|
||||
}
|
||||
|
||||
interface ComposeAnswerOutput {
|
||||
@@ -382,6 +383,61 @@ function formatAffectedScope(unit: ProblemUnit): string {
|
||||
return scopeParts.join("; ");
|
||||
}
|
||||
|
||||
function formatLifecycleScope(unit: ProblemUnit): string | null {
|
||||
if (!unit.lifecycle_domain) {
|
||||
return null;
|
||||
}
|
||||
const parts: string[] = [`domain=${unit.lifecycle_domain}`];
|
||||
if (unit.current_lifecycle_state) {
|
||||
parts.push(`current=${unit.current_lifecycle_state}`);
|
||||
}
|
||||
if (unit.expected_lifecycle_state) {
|
||||
parts.push(`expected=${unit.expected_lifecycle_state}`);
|
||||
}
|
||||
if (unit.lifecycle_defect_type) {
|
||||
parts.push(`defect=${unit.lifecycle_defect_type}`);
|
||||
}
|
||||
if (unit.missing_transition) {
|
||||
parts.push(`missing_transition=${unit.missing_transition}`);
|
||||
}
|
||||
if (unit.invalid_transition) {
|
||||
parts.push(`invalid_transition=${unit.invalid_transition}`);
|
||||
}
|
||||
if (unit.stale_duration) {
|
||||
parts.push(`stale_duration=${unit.stale_duration}`);
|
||||
}
|
||||
return parts.join(", ");
|
||||
}
|
||||
|
||||
function rankProblemUnitsForAnswer(units: ProblemUnit[], lifecycleAnswerEnabled: boolean): ProblemUnit[] {
|
||||
if (!lifecycleAnswerEnabled) {
|
||||
return units.slice().sort((left, right) => {
|
||||
const severityDiff = right.severity.score - left.severity.score;
|
||||
if (severityDiff !== 0) return severityDiff;
|
||||
return right.confidence.score - left.confidence.score;
|
||||
});
|
||||
}
|
||||
return units.slice().sort((left, right) => {
|
||||
const lifecycleRankDiff = (right.lifecycle_ranking_score ?? 0) - (left.lifecycle_ranking_score ?? 0);
|
||||
if (lifecycleRankDiff !== 0) return lifecycleRankDiff;
|
||||
const lifecycleConfidenceDiff = (right.lifecycle_confidence?.score ?? 0) - (left.lifecycle_confidence?.score ?? 0);
|
||||
if (lifecycleConfidenceDiff !== 0) return lifecycleConfidenceDiff;
|
||||
const severityDiff = right.severity.score - left.severity.score;
|
||||
if (severityDiff !== 0) return severityDiff;
|
||||
return right.confidence.score - left.confidence.score;
|
||||
});
|
||||
}
|
||||
|
||||
function hasLifecycleResolution(units: ProblemUnit[]): boolean {
|
||||
return units.some(
|
||||
(unit) =>
|
||||
Boolean(unit.lifecycle_domain) &&
|
||||
Boolean(unit.current_lifecycle_state) &&
|
||||
Boolean(unit.expected_lifecycle_state) &&
|
||||
Boolean(unit.lifecycle_defect_type)
|
||||
);
|
||||
}
|
||||
|
||||
function buildProblemCentricActions(input: {
|
||||
units: ProblemUnit[];
|
||||
mode: PolicyMode;
|
||||
@@ -406,6 +462,17 @@ function buildProblemCentricActions(input: {
|
||||
if (unitTypes.has("lifecycle_anomaly_node")) {
|
||||
actions.push("Проверьте lifecycle объекта: ожидаемый этап не должен оставаться в partially_linked состоянии.");
|
||||
}
|
||||
for (const unit of input.units) {
|
||||
if (unit.lifecycle_defect_type === "stale_active_state") {
|
||||
actions.push("Проверьте, почему объект завис: ожидаемый переход не должен оставаться в активной стадии.");
|
||||
}
|
||||
if (unit.lifecycle_defect_type === "misclosed_state") {
|
||||
actions.push("Проверьте закрывающий документ и проводки: закрытие может быть формальным, но некорректным по пути.");
|
||||
}
|
||||
if (unit.lifecycle_defect_type === "cross_branch_state_conflict") {
|
||||
actions.push("Сверьте бухгалтерскую и смежную ветки (например, НДС/расчеты): обнаружен межконтурный конфликт состояния.");
|
||||
}
|
||||
}
|
||||
|
||||
if (input.mode === "clarification_required") {
|
||||
if (input.missingAnchors.period) {
|
||||
@@ -825,7 +892,14 @@ function buildProblemCentricAnswerSummary(input: {
|
||||
mode: PolicyMode;
|
||||
weakUnits: boolean;
|
||||
summary: ProblemUnitSummary | null;
|
||||
lifecycleEnriched: boolean;
|
||||
}): string {
|
||||
if (input.lifecycleEnriched && input.summary?.lifecycle_enriched_units && input.summary.lifecycle_enriched_units > 0) {
|
||||
if (input.mode === "clarification_required") {
|
||||
return "Выявлены lifecycle-дефекты, но для надежного вывода требуется уточнение предметных якорей.";
|
||||
}
|
||||
return `Сформирован lifecycle-aware problem срез: выделено ${input.summary.lifecycle_enriched_units} lifecycle-узлов с приоритетом по дефектам перехода.`;
|
||||
}
|
||||
if (input.mode === "clarification_required") {
|
||||
return "Выявлены проблемные кластеры, но для надежного вывода требуется предметное уточнение фокуса.";
|
||||
}
|
||||
@@ -842,17 +916,31 @@ function buildProblemCentricDirectAnswer(input: {
|
||||
mode: PolicyMode;
|
||||
units: ProblemUnit[];
|
||||
weakUnits: boolean;
|
||||
lifecycleAnswerEnabled: boolean;
|
||||
}): string {
|
||||
const lead =
|
||||
input.mode === "clarification_required"
|
||||
? "Обнаружены проблемные зоны, но без уточнения якорей сильный factual-вывод преждевременен."
|
||||
: input.weakUnits
|
||||
? "Выделены проблемные зоны с ограниченной надежностью; вывод дан в ограниченном режиме."
|
||||
: "Выделены ключевые проблемные зоны и их влияние на учетный контур.";
|
||||
: input.lifecycleAnswerEnabled && hasLifecycleResolution(input.units)
|
||||
? "Выделены lifecycle-проблемы: определены текущие/ожидаемые стадии и тип нарушения перехода."
|
||||
: "Выделены ключевые проблемные зоны и их влияние на учетный контур.";
|
||||
|
||||
const unitLines = input.units.map((unit) => {
|
||||
const scope = formatAffectedScope(unit);
|
||||
return `- ${unit.title}: ${unit.business_defect_class}; ${scope}; severity=${unit.severity.grade}, confidence=${unit.confidence.grade}.`;
|
||||
const lifecycleScope = input.lifecycleAnswerEnabled ? formatLifecycleScope(unit) : null;
|
||||
const lifecycleInterpretation = input.lifecycleAnswerEnabled ? unit.business_lifecycle_interpretation : null;
|
||||
const segments = [
|
||||
`${unit.title}: ${unit.business_defect_class}`,
|
||||
scope,
|
||||
lifecycleScope,
|
||||
lifecycleInterpretation,
|
||||
`severity=${unit.severity.grade}`,
|
||||
`confidence=${unit.confidence.grade}`,
|
||||
unit.lifecycle_confidence ? `lifecycle_confidence=${unit.lifecycle_confidence.grade}` : null
|
||||
].filter((item): item is string => Boolean(item));
|
||||
return `- ${segments.join("; ")}.`;
|
||||
});
|
||||
|
||||
if (unitLines.length === 0) {
|
||||
@@ -873,8 +961,10 @@ function buildProblemCentricAnswerStructure(input: {
|
||||
retrievalResults: UnifiedRetrievalResult[];
|
||||
missingAnchors: MissingAnchors;
|
||||
coverageReport: RequirementCoverageReport;
|
||||
lifecycleAnswerEnabled: boolean;
|
||||
}): AnswerStructureV11 {
|
||||
const weakUnits = input.selectedUnits.every((item) => item.confidence.grade === "low");
|
||||
const lifecycleEnriched = input.lifecycleAnswerEnabled && hasLifecycleResolution(input.selectedUnits);
|
||||
const unitMechanismNotes = uniqueStrings(
|
||||
input.selectedUnits
|
||||
.map((item) => item.mechanism_summary)
|
||||
@@ -932,12 +1022,14 @@ function buildProblemCentricAnswerStructure(input: {
|
||||
answer_summary: buildProblemCentricAnswerSummary({
|
||||
mode: input.mode,
|
||||
weakUnits,
|
||||
summary: input.problemSummary
|
||||
summary: input.problemSummary,
|
||||
lifecycleEnriched
|
||||
}),
|
||||
direct_answer: buildProblemCentricDirectAnswer({
|
||||
mode: input.mode,
|
||||
units: input.selectedUnits,
|
||||
weakUnits
|
||||
weakUnits,
|
||||
lifecycleAnswerEnabled: input.lifecycleAnswerEnabled
|
||||
}),
|
||||
mechanism_block: {
|
||||
status: mechanismStatus,
|
||||
@@ -1059,10 +1151,11 @@ function composeAssistantAnswerV11(input: ComposeAnswerInput): ComposeAnswerOutp
|
||||
.filter((item): item is string => typeof item === "string" && item.trim().length > 0),
|
||||
6
|
||||
);
|
||||
const lifecycleAnswerEnabled = Boolean(input.enableLifecycleAnswerV1);
|
||||
const problemUnits = flattenProblemUnits(input.retrievalResults);
|
||||
const problemUnitSummary = selectProblemUnitSummary(input.retrievalResults);
|
||||
const problemHeavyUnits = problemUnits.filter((item) => PROBLEM_HEAVY_TYPES.has(item.problem_unit_type));
|
||||
const selectedProblemUnits = problemHeavyUnits.slice(0, 4);
|
||||
const selectedProblemUnits = rankProblemUnitsForAnswer(problemHeavyUnits, lifecycleAnswerEnabled).slice(0, 4);
|
||||
const claimEvidenceLinks = buildClaimEvidenceLinks(input.retrievalResults);
|
||||
const aggregateEvidenceConfidence = aggregateConfidence(input.retrievalResults, evidenceItems);
|
||||
const lowConfidenceSignals = evidenceItems.filter((item) => item.confidence === "low").length;
|
||||
@@ -1138,9 +1231,12 @@ function composeAssistantAnswerV11(input: ComposeAnswerInput): ComposeAnswerOutp
|
||||
groundingCheck: input.groundingCheck,
|
||||
retrievalResults: input.retrievalResults,
|
||||
missingAnchors,
|
||||
coverageReport: input.coverageReport
|
||||
coverageReport: input.coverageReport,
|
||||
lifecycleAnswerEnabled
|
||||
});
|
||||
|
||||
const lifecycleModeActive = lifecycleAnswerEnabled && hasLifecycleResolution(selectedProblemUnits);
|
||||
|
||||
return {
|
||||
assistant_reply: renderPolicyReply(problemCentricStructure),
|
||||
fallback_type: decision.fallback_type,
|
||||
@@ -1148,7 +1244,7 @@ function composeAssistantAnswerV11(input: ComposeAnswerInput): ComposeAnswerOutp
|
||||
answer_structure_v11: problemCentricStructure,
|
||||
problem_centric_answer_applied: true,
|
||||
problem_units_used_count: selectedProblemUnits.length,
|
||||
problem_answer_mode: "stage2_problem_centric_v1",
|
||||
problem_answer_mode: lifecycleModeActive ? "stage3_lifecycle_aware_v1" : "stage2_problem_centric_v1",
|
||||
problem_unit_ids_used: selectedProblemUnits.map((item) => item.problem_unit_id)
|
||||
};
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user