ГЛОБАЛЬНЫЙ РЕФАКТОРИНГ АРХИТЕКТУРЫ - Рефакторинг этапов 2.12.23: декомпозиция deep-turn пайплайна ассистента в runtime-адаптеры
This commit is contained in:
@@ -0,0 +1,159 @@
|
||||
import { FEATURE_ASSISTANT_EVIDENCE_ENRICHMENT_V1 } from "../config";
|
||||
import type { AnswerGroundingCheck, RequirementCoverageReport, UnifiedRetrievalResult } from "../types/assistant";
|
||||
import {
|
||||
ANSWER_STRUCTURE_SCHEMA_VERSION,
|
||||
type AnswerStructureV11,
|
||||
type EvidenceLimitationReasonCode
|
||||
} from "../types/stage1Contracts";
|
||||
|
||||
export interface BuildAssistantAnswerStructureV11Input {
|
||||
assistantReply: string;
|
||||
coverageReport: RequirementCoverageReport;
|
||||
groundingCheck: AnswerGroundingCheck;
|
||||
retrievalResults: UnifiedRetrievalResult[];
|
||||
options?: {
|
||||
enableEvidenceEnrichment?: boolean;
|
||||
};
|
||||
}
|
||||
|
||||
const EVIDENCE_LIMITATION_REASON_CODE_SET: ReadonlySet<EvidenceLimitationReasonCode> = new Set([
|
||||
"snapshot_only",
|
||||
"heuristic_inference",
|
||||
"missing_mechanism",
|
||||
"weak_source_mapping",
|
||||
"insufficient_detail",
|
||||
"unknown"
|
||||
]);
|
||||
|
||||
function summarizeUnique(values: Array<string | null | undefined>, limit = 6): string[] {
|
||||
return Array.from(new Set(values.map((item) => String(item ?? "").trim()).filter(Boolean))).slice(0, limit);
|
||||
}
|
||||
|
||||
function isEvidenceLimitationReasonCode(value: string): value is EvidenceLimitationReasonCode {
|
||||
return EVIDENCE_LIMITATION_REASON_CODE_SET.has(value as EvidenceLimitationReasonCode);
|
||||
}
|
||||
|
||||
function firstNonEmptyLine(text: string): string {
|
||||
const line = String(text ?? "")
|
||||
.split("\n")
|
||||
.map((item) => item.trim())
|
||||
.find((item) => item.length > 0);
|
||||
return (line ?? String(text ?? "")).slice(0, 220);
|
||||
}
|
||||
|
||||
function buildClaimEvidenceLinks(
|
||||
retrievalResults: UnifiedRetrievalResult[]
|
||||
): NonNullable<AnswerStructureV11["evidence_block"]["claim_evidence_links"]> {
|
||||
const byClaim = new Map<string, string[]>();
|
||||
for (const result of retrievalResults) {
|
||||
for (const evidence of result.evidence) {
|
||||
const claimRef = String(evidence.claim_ref ?? "").trim();
|
||||
if (!claimRef) {
|
||||
continue;
|
||||
}
|
||||
const evidenceId = String(evidence.evidence_id ?? "").trim();
|
||||
if (!evidenceId) {
|
||||
continue;
|
||||
}
|
||||
const current = byClaim.get(claimRef) ?? [];
|
||||
current.push(evidenceId);
|
||||
byClaim.set(claimRef, current);
|
||||
}
|
||||
}
|
||||
return Array.from(byClaim.entries())
|
||||
.slice(0, 10)
|
||||
.map(([claimRef, evidenceIds]) => ({
|
||||
claim_ref: claimRef,
|
||||
evidence_ids: summarizeUnique(evidenceIds, 10)
|
||||
}));
|
||||
}
|
||||
|
||||
export function buildAssistantAnswerStructureV11(input: BuildAssistantAnswerStructureV11Input): AnswerStructureV11 {
|
||||
const evidenceIds = summarizeUnique(
|
||||
input.retrievalResults.flatMap((item) => item.evidence.map((evidence) => evidence.evidence_id)),
|
||||
10
|
||||
);
|
||||
const mechanismNotes = summarizeUnique(
|
||||
input.retrievalResults.flatMap((item) =>
|
||||
item.evidence
|
||||
.map((evidence) => evidence.mechanism_note)
|
||||
.filter((note): note is string => typeof note === "string" && note.trim().length > 0)
|
||||
),
|
||||
6
|
||||
);
|
||||
const sourceRefs = summarizeUnique(
|
||||
input.retrievalResults.flatMap((item) =>
|
||||
item.evidence
|
||||
.map((evidence) => evidence.source_ref?.canonical_ref)
|
||||
.filter((value): value is string => typeof value === "string" && value.trim().length > 0)
|
||||
),
|
||||
8
|
||||
);
|
||||
const limitationReasonCodes: EvidenceLimitationReasonCode[] = summarizeUnique(
|
||||
input.retrievalResults.flatMap((item) =>
|
||||
item.evidence.flatMap((evidence) => {
|
||||
const code = evidence.limitation?.reason_code;
|
||||
return typeof code === "string" && code.trim().length > 0 ? [code] : [];
|
||||
})
|
||||
),
|
||||
8
|
||||
).filter(isEvidenceLimitationReasonCode);
|
||||
const claimEvidenceLinks = buildClaimEvidenceLinks(input.retrievalResults);
|
||||
const limitations = summarizeUnique(
|
||||
[...input.retrievalResults.flatMap((item) => item.limitations), ...input.groundingCheck.reasons],
|
||||
8
|
||||
);
|
||||
const clarificationQuestions = input.coverageReport.clarification_needed_for.map(
|
||||
(item) => `Уточните требование ${item}.`
|
||||
);
|
||||
const recommendedActions = summarizeUnique(
|
||||
[
|
||||
...input.coverageReport.requirements_uncovered.map((item) => `Проверить непокрытое требование ${item}.`),
|
||||
...input.coverageReport.requirements_partially_covered.map(
|
||||
(item) => `Доуточнить частично покрытое требование ${item}.`
|
||||
)
|
||||
],
|
||||
6
|
||||
);
|
||||
const mechanismStatus: AnswerStructureV11["mechanism_block"]["status"] =
|
||||
mechanismNotes.length === 0
|
||||
? "unresolved"
|
||||
: limitationReasonCodes.includes("missing_mechanism") || limitationReasonCodes.includes("heuristic_inference")
|
||||
? "limited"
|
||||
: "grounded";
|
||||
const enableEvidenceEnrichment =
|
||||
input.options?.enableEvidenceEnrichment ?? FEATURE_ASSISTANT_EVIDENCE_ENRICHMENT_V1;
|
||||
|
||||
return {
|
||||
schema_version: ANSWER_STRUCTURE_SCHEMA_VERSION,
|
||||
answer_summary: firstNonEmptyLine(input.assistantReply),
|
||||
direct_answer: input.assistantReply,
|
||||
mechanism_block: {
|
||||
status: mechanismStatus,
|
||||
mechanism_notes: mechanismNotes,
|
||||
limitation_reason_codes: limitationReasonCodes
|
||||
},
|
||||
evidence_block: {
|
||||
evidence_ids: evidenceIds,
|
||||
source_refs: sourceRefs,
|
||||
mechanism_notes: mechanismNotes,
|
||||
coverage_note:
|
||||
input.coverageReport.requirements_total === input.coverageReport.requirements_covered
|
||||
? "coverage_full_or_near_full"
|
||||
: "coverage_partial_or_limited",
|
||||
...(enableEvidenceEnrichment && claimEvidenceLinks.length > 0
|
||||
? {
|
||||
claim_evidence_links: claimEvidenceLinks
|
||||
}
|
||||
: {})
|
||||
},
|
||||
uncertainty_block: {
|
||||
open_uncertainties: input.groundingCheck.missing_requirements,
|
||||
limitations
|
||||
},
|
||||
next_step_block: {
|
||||
recommended_actions: recommendedActions,
|
||||
clarification_questions: clarificationQuestions
|
||||
}
|
||||
};
|
||||
}
|
||||
Reference in New Issue
Block a user