test: harden AI analysis path
This commit is contained in:
@@ -85,14 +85,39 @@ function extractJsonFromText(value: string): unknown {
|
||||
return safeJsonParse(trimmed.slice(start, end + 1));
|
||||
}
|
||||
|
||||
function normalizeCitations(output: ElderAiAnalysisOutput, citations: AiCitation[]): ElderAiAnalysisOutput {
|
||||
function collectReferencedCitationIds(output: ElderAiAnalysisOutput): Set<string> {
|
||||
const referencedIds = new Set<string>();
|
||||
for (const finding of output.keyFindings) {
|
||||
for (const citationId of finding.citationIds) {
|
||||
referencedIds.add(citationId);
|
||||
}
|
||||
}
|
||||
for (const recommendation of output.recommendations) {
|
||||
for (const citationId of recommendation.citationIds) {
|
||||
referencedIds.add(citationId);
|
||||
}
|
||||
}
|
||||
return referencedIds;
|
||||
}
|
||||
|
||||
function normalizeCitations(output: ElderAiAnalysisOutput, citations: AiCitation[]): ElderAiAnalysisOutput | null {
|
||||
const allowed = new Map(citations.map((citation) => [citation.id, citation]));
|
||||
const merged = output.citations.filter((citation) => allowed.has(citation.id));
|
||||
const seenIds = new Set(merged.map((citation) => citation.id));
|
||||
const missing = citations.filter((citation) => !seenIds.has(citation.id));
|
||||
for (const citation of output.citations) {
|
||||
if (!allowed.has(citation.id)) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
const referencedIds = collectReferencedCitationIds(output);
|
||||
for (const citationId of referencedIds) {
|
||||
if (!allowed.has(citationId)) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
...output,
|
||||
citations: [...merged, ...missing],
|
||||
citations: citations.filter((citation) => referencedIds.has(citation.id)),
|
||||
};
|
||||
}
|
||||
|
||||
@@ -283,15 +308,23 @@ export async function generateElderAiAnalysis(
|
||||
return { success: false, reason: getBriefErrorReason("schema_validation_failed"), status: 502 };
|
||||
}
|
||||
|
||||
const normalizedOutput = normalizeCitations(
|
||||
knowledgeUnavailableReason
|
||||
? {
|
||||
...output,
|
||||
dataGaps: [...output.dataGaps, "知识库检索不可用,分析未使用内部知识库"],
|
||||
}
|
||||
: output,
|
||||
citations,
|
||||
);
|
||||
const outputWithDataGaps = knowledgeUnavailableReason
|
||||
? {
|
||||
...output,
|
||||
dataGaps: [...output.dataGaps, "知识库检索不可用,分析未使用内部知识库"],
|
||||
}
|
||||
: output;
|
||||
const normalizedOutput = normalizeCitations(outputWithDataGaps, citations);
|
||||
if (!normalizedOutput) {
|
||||
await saveFailedAnalysis({
|
||||
context,
|
||||
organizationId: residentContext.context.organizationId,
|
||||
elderId,
|
||||
dataScopes,
|
||||
category: "schema_validation_failed",
|
||||
});
|
||||
return { success: false, reason: getBriefErrorReason("schema_validation_failed"), status: 502 };
|
||||
}
|
||||
const database = getDatabase();
|
||||
const rows = await database
|
||||
.insert(elderAiAnalyses)
|
||||
|
||||
Reference in New Issue
Block a user