fix(bom-costing): default omitted model confidence conservatively

This commit is contained in:
WuYongJie 2026-08-05 00:28:24 +08:00
parent 0e20ab41c5
commit 96a3abda1d
2 changed files with 42 additions and 1 deletions

View File

@ -338,8 +338,21 @@ export const aiOptionalPositiveNumberSchema = z.preprocess(
z.number().finite().positive().optional(),
)
const DEFAULT_AI_CONFIDENCE = 0.5
function parseAiConfidence(value: unknown) {
// Confidence is advisory metadata. Some otherwise complete model responses
// omit it, so retain the usable BOM with an explicitly conservative value
// instead of discarding and re-running the entire batch.
if (value === null || value === undefined) return DEFAULT_AI_CONFIDENCE
if (typeof value === 'string' && !value.trim()) {
return DEFAULT_AI_CONFIDENCE
}
return parseAiFiniteNumber(value)
}
export const aiConfidenceSchema = z.preprocess(
parseAiFiniteNumber,
parseAiConfidence,
z.number().finite().min(0).max(1),
)

View File

@ -677,6 +677,34 @@ describe('BOM 多模态分析器', () => {
})
})
it('模型省略置信度时使用保守默认值,不重跑完整 BOM 批次', async () => {
const structureGroup = analyzedStructureGroup()
const modelLine = JSON.parse(
JSON.stringify(structureGroup.bomLines[0]),
) as Record<string, unknown>
delete modelLine.confidence
const modelEvidence = modelLine.evidence as Array<Record<string, unknown>>
delete modelEvidence[0]?.confidence
mockFactExtractionThenAnalysis(snapshot, {
product: { category: '书桌' },
structureGroups: [{
...structureGroup,
bomLines: [modelLine, ...structureGroup.bomLines.slice(1)],
}],
skus: [analyzedSku('sku-white'), analyzedSku('sku-black')],
})
const result = await new BomCostingMultimodalAnalyzer().analyze({
sourceUrl: snapshot.detailUrl,
snapshot,
})
expect(result.skus[0]?.bomLines[0]).toMatchObject({
confidence: 0.5,
evidence: [{ confidence: 0.5 }],
})
})
it('模型返回其他不完整 SKU 信息时不暴露内部校验细节', async () => {
mockFactExtractionThenAnalysis(snapshot, {
product: { category: '书桌' },