fix(bom-costing): prefer product-wide explicit thickness facts
This commit is contained in:
parent
d630a73433
commit
2e3168bb99
@ -385,66 +385,168 @@ describe('BOM 逐图事实提取', () => {
|
||||
)
|
||||
})
|
||||
|
||||
it('优先读取卡尺板厚,并把图片比例反推限制为对应部件的低可信预估', async () => {
|
||||
const detailUrl = 'https://img.example.com/caliper-detail.jpg'
|
||||
const detailSnapshot: MarketDetailSnapshot = {
|
||||
it('全商品任一图片有明确板厚时,舍弃其他图片产生的目测候选', async () => {
|
||||
const markedMainUrl = 'https://img.example.com/marked-thickness-main.jpg'
|
||||
const visualDetailUrl = 'https://img.example.com/visual-thickness-detail.jpg'
|
||||
const markedSnapshot: MarketDetailSnapshot = {
|
||||
...snapshot,
|
||||
mainImages: [],
|
||||
mainImages: [markedMainUrl],
|
||||
skuImages: [],
|
||||
detailImages: [detailUrl],
|
||||
detailImages: [visualDetailUrl],
|
||||
skus: snapshot.skus.map((sku) => ({ ...sku, imageUrl: null })),
|
||||
}
|
||||
let request: ReturnType<typeof readBatchRequest> | null = null
|
||||
jest.spyOn(global, 'fetch').mockImplementation(async (_input, init) => {
|
||||
request = readBatchRequest(init)
|
||||
return createStreamingResponse(JSON.stringify({
|
||||
images: [{
|
||||
imageId: request.imageIds[0],
|
||||
observedTexts: ['纯实木隔板', '电子卡尺读数'],
|
||||
images: request.imageIds.map((imageId, index) => index === 0
|
||||
? {
|
||||
imageId,
|
||||
observedTexts: ['柜体板厚 18mm'],
|
||||
facts: [{
|
||||
kind: 'DIMENSION',
|
||||
subject: '实木隔板板厚',
|
||||
value: '电子卡尺显示 19.5 mm',
|
||||
subject: '柜体板材明确板厚标注',
|
||||
value: '板厚 18 mm',
|
||||
relatedSkuIds: [],
|
||||
basis: 'SOURCE_TEXT',
|
||||
note: '右上区域电子卡尺显示屏',
|
||||
confidence: 0.96,
|
||||
}, {
|
||||
note: '主图右下角参数标注',
|
||||
confidence: 0.97,
|
||||
}],
|
||||
}
|
||||
: {
|
||||
imageId,
|
||||
observedTexts: [],
|
||||
facts: [{
|
||||
kind: 'DIMENSION',
|
||||
subject: '未标注侧板板厚',
|
||||
value: '约 18 mm',
|
||||
value: '约 17 mm',
|
||||
relatedSkuIds: [],
|
||||
basis: '比例估算',
|
||||
note: '以同图已标注柜体宽度为参照,仅估算侧板,不适用于背板',
|
||||
note: '以同图已标注柜体宽度为参照,仅估算侧板',
|
||||
confidence: 0.88,
|
||||
}],
|
||||
}),
|
||||
}))
|
||||
})
|
||||
|
||||
const output = await extractBomCostingImageFacts({
|
||||
config: providerConfig,
|
||||
snapshot: markedSnapshot,
|
||||
})
|
||||
|
||||
expect(request?.systemPrompt).toContain('电子卡尺')
|
||||
expect(request?.systemPrompt).toContain('不得扩展成所有门板、背板、侧板或层板')
|
||||
expect(request?.prompt.boardThicknessEvidenceOrder).toHaveLength(4)
|
||||
expect(request?.imageDetails).toEqual([
|
||||
{ url: markedMainUrl, detail: 'auto' },
|
||||
{ url: visualDetailUrl, detail: 'high' },
|
||||
])
|
||||
expect(output.snapshot.imageFacts.flatMap((image) => image.facts)).toEqual([
|
||||
expect.objectContaining({
|
||||
subject: '柜体板材明确板厚标注',
|
||||
basis: 'SOURCE_TEXT',
|
||||
confidence: 0.97,
|
||||
}),
|
||||
])
|
||||
})
|
||||
|
||||
it('全部图片都没有明确板厚时,才保留图片比例目测候选', async () => {
|
||||
const detailUrl = 'https://img.example.com/visual-only-thickness.jpg'
|
||||
const visualOnlySnapshot: MarketDetailSnapshot = {
|
||||
...snapshot,
|
||||
mainImages: [],
|
||||
skuImages: [],
|
||||
detailImages: [detailUrl],
|
||||
skus: snapshot.skus.map((sku) => ({ ...sku, imageUrl: null })),
|
||||
}
|
||||
jest.spyOn(global, 'fetch').mockImplementation(async (_input, init) => {
|
||||
const request = readBatchRequest(init)
|
||||
return createStreamingResponse(JSON.stringify({
|
||||
images: [{
|
||||
imageId: request.imageIds[0],
|
||||
observedTexts: [],
|
||||
facts: [{
|
||||
kind: 'DIMENSION',
|
||||
subject: '侧板板厚',
|
||||
value: '约 18 mm',
|
||||
relatedSkuIds: [],
|
||||
basis: 'VISUAL_ESTIMATE',
|
||||
note: '以同图已标注柜体宽度为参照,存在透视误差',
|
||||
confidence: 0.82,
|
||||
}],
|
||||
}],
|
||||
}))
|
||||
})
|
||||
|
||||
const output = await extractBomCostingImageFacts({
|
||||
config: providerConfig,
|
||||
snapshot: detailSnapshot,
|
||||
snapshot: visualOnlySnapshot,
|
||||
})
|
||||
|
||||
expect(request?.systemPrompt).toContain('电子卡尺')
|
||||
expect(request?.systemPrompt).toContain('不得扩展成所有门板、背板、侧板或层板')
|
||||
expect(request?.prompt.boardThicknessEvidenceOrder).toHaveLength(3)
|
||||
expect(request?.imageDetails).toEqual([{ url: detailUrl, detail: 'high' }])
|
||||
expect(output.snapshot.imageFacts[0]?.facts).toEqual([
|
||||
expect.objectContaining({
|
||||
subject: '实木隔板板厚',
|
||||
basis: 'SOURCE_TEXT',
|
||||
confidence: 0.96,
|
||||
}),
|
||||
expect.objectContaining({
|
||||
subject: '未标注侧板板厚',
|
||||
subject: '侧板板厚',
|
||||
basis: 'VISUAL_ESTIMATE',
|
||||
confidence: 0.45,
|
||||
}),
|
||||
])
|
||||
})
|
||||
|
||||
it('仍有图片分析失败时不启用目测板厚,保留任务继续使用历史规格兜底', async () => {
|
||||
const analyzedUrl = 'https://img.example.com/visual-candidate.jpg'
|
||||
const failedUrl = 'https://img.example.com/unread-thickness-page.jpg'
|
||||
const incompleteSnapshot: MarketDetailSnapshot = {
|
||||
...snapshot,
|
||||
mainImages: [],
|
||||
skuImages: [],
|
||||
detailImages: [analyzedUrl, failedUrl],
|
||||
skus: snapshot.skus.map((sku) => ({ ...sku, imageUrl: null })),
|
||||
}
|
||||
|
||||
const output = await extractBomCostingImageFacts({
|
||||
config: providerConfig,
|
||||
snapshot: incompleteSnapshot,
|
||||
reuseExistingFactsOnly: true,
|
||||
existingSnapshot: {
|
||||
schemaVersion: BOM_COSTING_FACT_SCHEMA_VERSION,
|
||||
extractedAt: '2026-08-10T00:00:00.000Z',
|
||||
imageFacts: [{
|
||||
imageId: 'OLD_1',
|
||||
sourceImageUrl: analyzedUrl,
|
||||
kind: 'DETAIL',
|
||||
label: '旧详情图 1',
|
||||
externalSkuIds: [],
|
||||
observedTexts: [],
|
||||
facts: [{
|
||||
factId: 'visual-thickness',
|
||||
kind: 'DIMENSION',
|
||||
subject: '侧板板厚',
|
||||
value: '约 18 mm',
|
||||
relatedSkuIds: [],
|
||||
basis: 'VISUAL_ESTIMATE',
|
||||
note: '按图片比例估算',
|
||||
confidence: 0.45,
|
||||
}],
|
||||
}],
|
||||
failedImages: [{
|
||||
imageId: 'OLD_2',
|
||||
sourceImageUrl: failedUrl,
|
||||
kind: 'DETAIL',
|
||||
label: '旧详情图 2',
|
||||
externalSkuIds: [],
|
||||
reason: 'REQUEST_FAILED',
|
||||
message: '图片分析服务暂时不可用',
|
||||
attempts: 2,
|
||||
}],
|
||||
},
|
||||
})
|
||||
|
||||
expect(output.snapshot.imageFacts[0]?.facts).toEqual([])
|
||||
expect(output.snapshot.failedImages).toEqual([
|
||||
expect.objectContaining({ sourceImageUrl: failedUrl }),
|
||||
])
|
||||
})
|
||||
|
||||
it('图片结果缺失时会用更严格的契约自动重试一次,而不是立即终止核算', async () => {
|
||||
const singleImageSnapshot: MarketDetailSnapshot = {
|
||||
...snapshot,
|
||||
|
||||
@ -38,11 +38,11 @@ const FACT_EXTRACTION_SYSTEM_PROMPT = `你是家具竞品资料事实提取员
|
||||
硬规则:
|
||||
1. 只输出一个 JSON 对象,不要 Markdown、解释、代码围栏或额外文字。
|
||||
2. 输入中的每个 imageId 必须返回且只返回一次;即使图片没有成本信息,也要返回空 facts。
|
||||
3. 本阶段不生成 BOM、不推断隐藏部件、不估算价格。唯一允许的受控估算是:没有任何明确板厚读数时,依据同一部件、同一张图片或明确连续长图中的已知尺寸参照,低可信估算该部件的板厚。
|
||||
3. 本阶段不生成 BOM、不推断隐藏部件、不估算价格。你可以记录“板厚视觉估算候选”,但系统只会在主图、SKU 图和全部详情图均成功分析,且没有任何明确板厚标注或测量读数时启用它;只要整条商品资料里存在明确板厚,或仍有图片分析失败,所有视觉估算候选都会被舍弃。
|
||||
4. 图片直接可见的形状、部件、结构或机构使用 basis=IMAGE;尺寸标注、卡尺/标尺读数、材质说明、功能说明、参数表等图片文字使用 basis=SOURCE_TEXT;按图片比例得到的近似板厚使用 basis=VISUAL_ESTIMATE。
|
||||
5. 板厚是必须专项检查的证据:逐张放大检查电子卡尺、游标卡尺、直尺、卷尺、厚度规、剖面、板边和尺寸线。显示屏或刻度即使旋转、较小或位于局部红框,也要先按正确方向读取,不得因不显眼而直接判定“无板厚”。
|
||||
6. 卡尺或标尺有可读数值时,输出 kind=DIMENSION、basis=SOURCE_TEXT;subject 必须写清被测部件(例如“实木隔板板厚”),value 保留仪器读数和单位,note 写明卡尺/标尺位置。该读数只属于被测部件,不得扩展成所有门板、背板、侧板或层板的共同厚度。
|
||||
7. 某个部件没有明确板厚读数时,只有存在可核验的尺寸参照和清楚的同部件板边时才允许反推:输出 kind=DIMENSION、basis=VISUAL_ESTIMATE,value 必须使用“约 X mm”,note 必须写出所用参照尺寸、对应部件和误差原因,confidence 不得高于 0.45。不得用手指、人物或无固定尺寸的物体作唯一比例尺。
|
||||
7. 某张图没有明确板厚读数时,只有存在可核验的尺寸参照和清楚的同部件板边时才允许输出“视觉估算候选”:输出 kind=DIMENSION、basis=VISUAL_ESTIMATE,value 必须使用“约 X mm”,note 必须写出所用参照尺寸、对应部件和误差原因,confidence 不得高于 0.45。不得用手指、人物或无固定尺寸的物体作唯一比例尺;后续是否采用由系统在汇总全部图片后统一决定。
|
||||
8. 没有明确读数,也没有可核验的比例参照时,不得编造具体板厚;可以记录板件存在、材质和“板厚不可判断”,但不要返回虚构数值。
|
||||
9. 必须检查局部小图、红框区域、剖面图、折叠或运动示意、产品参数图和包装说明,不得把它们当成装饰。
|
||||
10. 尺寸、重量及单位全部保留在 value 原文中。多维尺寸能确认长宽高含义时拆成多个事实,无法确认轴向时保留完整原文,不得强行压成单一数值。
|
||||
@ -371,8 +371,9 @@ function buildFactBatchUserText(
|
||||
optionalFields: '可选字段没有证据时省略或返回 null,不得填写 optional 等说明文字',
|
||||
},
|
||||
boardThicknessEvidenceOrder: [
|
||||
'优先:读取卡尺、标尺、尺寸线或参数文字中的明确板厚,并只绑定被测部件',
|
||||
'其次:仅在同一部件存在已知尺寸参照时,按图片比例输出约值,basis=VISUAL_ESTIMATE,confidence<=0.45',
|
||||
'第一步:汇总主图、SKU 图和全部详情图中的板厚标注、参数文字、卡尺及标尺读数',
|
||||
'第二步:只要整条商品资料存在任意明确板厚,优先采用明确数据并舍弃全部 VISUAL_ESTIMATE 候选',
|
||||
'第三步:仅当全部图片均成功分析且都没有明确板厚时,才启用基于已知尺寸参照的 VISUAL_ESTIMATE,confidence<=0.45',
|
||||
'最后:没有可核验参照时不输出具体厚度,由后续公司历史资料层进行低可信兜底',
|
||||
],
|
||||
productTruth: {
|
||||
@ -634,6 +635,38 @@ function describeFactImageFailure(
|
||||
}
|
||||
}
|
||||
|
||||
function isExplicitBoardThicknessFact(
|
||||
fact: BomCostingSourceFactSnapshot['imageFacts'][number]['facts'][number],
|
||||
) {
|
||||
if (fact.kind !== 'DIMENSION' || fact.basis === 'VISUAL_ESTIMATE') {
|
||||
return false
|
||||
}
|
||||
const context = [
|
||||
fact.subject,
|
||||
fact.value,
|
||||
fact.note ?? '',
|
||||
].join(' ').normalize('NFKC')
|
||||
const explicitThicknessLanguage = (
|
||||
/板厚|板材厚度|厚度|卡尺|厚度规|板边厚薄|加厚/i.test(context) ||
|
||||
/\d+(?:\.\d+)?\s*(?:mm|毫米|cm|厘米|厘)\s*(?:厚|实木板|板材|板)/i.test(context) ||
|
||||
/(?:隔板|层板|门板|背板|侧板|顶板|底板|面板)\s*(?:为|约|[::])?\s*\d+(?:\.\d+)?\s*(?:mm|毫米|cm|厘米|厘)/i.test(
|
||||
context,
|
||||
)
|
||||
)
|
||||
if (!explicitThicknessLanguage) return false
|
||||
|
||||
return Array.from(
|
||||
context.matchAll(/(\d+(?:\.\d+)?)\s*(mm|毫米|cm|厘米|厘)/gi),
|
||||
).some((match) => {
|
||||
const numeric = Number(match[1])
|
||||
if (!Number.isFinite(numeric) || numeric <= 0) return false
|
||||
const millimeters = /cm|厘米/i.test(match[2] ?? '')
|
||||
? numeric * 10
|
||||
: numeric
|
||||
return millimeters <= 80
|
||||
})
|
||||
}
|
||||
|
||||
function createFactSnapshot(
|
||||
images: BomCostingMultimodalInputImage[],
|
||||
completedByIdentity: Map<
|
||||
@ -643,18 +676,35 @@ function createFactSnapshot(
|
||||
failedByIdentity: Map<string, BomCostingFailedImageFact>,
|
||||
extractedAt = new Date().toISOString(),
|
||||
) {
|
||||
return bomCostingSourceFactSnapshotSchema.parse({
|
||||
schemaVersion: BOM_COSTING_FACT_SCHEMA_VERSION,
|
||||
imageFacts: images
|
||||
const imageFacts = images
|
||||
.map((image) =>
|
||||
completedByIdentity.get(buildBomCostingImageIdentity(image.url)),
|
||||
)
|
||||
.filter(Boolean),
|
||||
failedImages: images
|
||||
.filter((image): image is NonNullable<typeof image> => Boolean(image))
|
||||
const hasExplicitBoardThickness = imageFacts.some((image) =>
|
||||
image.facts.some(isExplicitBoardThicknessFact),
|
||||
)
|
||||
const failedImages = images
|
||||
.map((image) =>
|
||||
failedByIdentity.get(buildBomCostingImageIdentity(image.url)),
|
||||
)
|
||||
.filter(Boolean),
|
||||
.filter((image): image is NonNullable<typeof image> => Boolean(image))
|
||||
const canUseVisualThicknessEstimate = (
|
||||
!hasExplicitBoardThickness &&
|
||||
failedImages.length === 0 &&
|
||||
imageFacts.length === images.length
|
||||
)
|
||||
return bomCostingSourceFactSnapshotSchema.parse({
|
||||
schemaVersion: BOM_COSTING_FACT_SCHEMA_VERSION,
|
||||
imageFacts: canUseVisualThicknessEstimate
|
||||
? imageFacts
|
||||
: imageFacts.map((image) => ({
|
||||
...image,
|
||||
facts: image.facts.filter(
|
||||
(fact) => fact.basis !== 'VISUAL_ESTIMATE',
|
||||
),
|
||||
})),
|
||||
failedImages,
|
||||
extractedAt,
|
||||
})
|
||||
}
|
||||
|
||||
@ -72,7 +72,7 @@ const SYSTEM_PROMPT = `你是家具产品结构与成本工程师。你的任务
|
||||
9. BOM 分类只能是 BOARD、STEEL、ACCESSORY、PACKAGING、PROCESSING、LABOR。
|
||||
10. evidence 必须说明依据;引用图片时只能照抄逐图事实中的 url(这是系统图片引用,不得编造、改写或填写其他图片地址)。
|
||||
11. confidence 必须在 0 到 1 之间。
|
||||
12. BOARD 的 lengthMm/widthMm 表示单块板件长宽,heightMm 只能表示板厚。板厚证据必须按部件绑定并按以下顺序使用:① 卡尺、标尺、尺寸线或参数文字的明确读数最优,填写 heightMm 和 materialSpec,evidence 保留 SOURCE_TEXT/IMAGE;② 输入事实 basis=VISUAL_ESTIMATE 时,可以填写约值,但 materialSpec 必须明确写“约 Xmm(图片比例估算)”,evidence.basis 必须改为 STRUCTURAL_INFERENCE,confidence 不得高于 0.45,note 必须保留参照尺寸、对应部件和误差原因;③ 两者都没有时省略 heightMm,并写明“板厚待历史规格低可信预估”。一块隔板的卡尺读数不得复制给门板、背板、侧板或其他未测部件,也不能把整机高度或部件外形高度填成板厚。若图片没有直接标出单块长宽,但 SKU 元数据或图片事实已经确认整机长宽高与柜体、桌体、层板等装配结构,应把主要板件拆成少量可计价行,依据装配关系保守推导单块长宽和数量;这类行的 evidence.basis 必须是 STRUCTURAL_INFERENCE,confidence 不得高于 0.45,note 必须写明“由整机尺寸与结构推导,待复核”。系统会在后续按基础资料库整板尺寸做裁切排版,模型不要自行填写板价。
|
||||
12. BOARD 的 lengthMm/widthMm 表示单块板件长宽,heightMm 只能表示板厚。系统已先汇总主图、SKU 图和全部详情图:只要整条商品资料存在明确板厚,VISUAL_ESTIMATE 候选就不会进入当前 facts;因此必须优先使用 facts 中卡尺、标尺、尺寸线或参数文字的明确读数,填写 heightMm 和 materialSpec,evidence 保留 SOURCE_TEXT/IMAGE。只有全商品没有任何明确板厚时,facts 才可能保留 basis=VISUAL_ESTIMATE 的约值;此时 materialSpec 必须明确写“约 Xmm(图片比例估算)”,evidence.basis 必须改为 STRUCTURAL_INFERENCE,confidence 不得高于 0.45,note 必须保留参照尺寸、对应部件和误差原因。两者都没有时省略 heightMm,并写明“板厚待历史规格低可信预估”。明确标注的适用范围仍以原文为准:只标隔板时不得复制给门板、背板、侧板或其他未标部件;标明“柜体通用板厚”时才可用于对应柜体板件。不能把整机高度或部件外形高度填成板厚。若图片没有直接标出单块长宽,但 SKU 元数据或图片事实已经确认整机长宽高与柜体、桌体、层板等装配结构,应把主要板件拆成少量可计价行,依据装配关系保守推导单块长宽和数量;这类行的 evidence.basis 必须是 STRUCTURAL_INFERENCE,confidence 不得高于 0.45,note 必须写明“由整机尺寸与结构推导,待复核”。系统会在后续按基础资料库整板尺寸做裁切排版,模型不要自行填写板价。
|
||||
13. PACKAGING 必须按纸箱、泡沫、珍珠棉、护角、打包带等实际包材逐行拆分;packagingKind 必须与部件一致,尺寸表示单件包材规格,quantity 表示单套产品实际用量。没有直接包装证据时,根据整机尺寸、重量、易损部位和运输保护需要给出保守的最小包装方案,basis 使用 STRUCTURAL_INFERENCE,confidence 不得高于 0.55,并明确待确认项。
|
||||
14. materialName 使用可用于资料库匹配的材种或材料名称,例如“橡胶木”“刨花板”“碳钢”“瓦楞纸箱”,不要把颜色、营销等级或整段商品描述当作材种。SKU 元数据明确为 FAS 级橡胶木、实木、无镜等时,不能默认替换为另一种材质或保留不应存在的镜子部件。
|
||||
15. 每条 evidence 必须设置 basis:图片直接可见为 IMAGE;图片标注、详情文字或商品文案为 SOURCE_TEXT;SKU 名称、规格或属性为 SKU_METADATA;由装配或机构功能必需性推断的隐藏部件为 STRUCTURAL_INFERENCE。
|
||||
@ -95,7 +95,7 @@ const COMPACT_COSTING_SYSTEM_PROMPT = `你是家具产品成本工程师。当
|
||||
1. 必须至少有一条材料项(BOARD、STEEL 或 ACCESSORY)、一条 PROCESSING、一条 LABOR 和一条 PACKAGING。
|
||||
2. 每条必须包含 externalLineId、category、partName、quantity、unit、evidence、confidence。不得输出 estimatedUnitPrice 或 suggestedLossFactor;正式价格和损耗由后续公司数据规则层计算。
|
||||
3. 公司基础资料库和历史 BOM 本地镜像是唯一正式价格来源。materialName 只写可匹配的材料,例如“橡胶木”“不锈钢”“银镜”“万向轮”“瓦楞纸箱”,不要把营销文案当材料。
|
||||
4. 板材有明确“单块板件”长宽时使用“㎡”并填写 lengthMm、widthMm。板厚按部件独立处理:卡尺、标尺、尺寸线或参数文字的明确读数优先,填写 heightMm 和 materialSpec,并保留 SOURCE_TEXT/IMAGE 证据;facts 中 basis=VISUAL_ESTIMATE 的约值是次级证据,可以填写 heightMm,但 materialSpec 必须写“约 Xmm(图片比例估算)”,evidence.basis 必须使用 STRUCTURAL_INFERENCE,confidence 不得高于 0.45,note 必须保留参照尺寸、部件范围与误差原因;没有这两类证据时省略 heightMm。某一隔板的读数不得套用到门板、背板、侧板或其他板件,不得把整机高度填成板厚。若图片没有直接标出单块长宽,但 SKU 元数据或图片事实已经确认整机长宽高与柜体、桌体、层板等装配结构,应把主要板件拆成少量可计价行,依据装配关系保守推导单块长宽和数量;这类行的 evidence.basis 必须是 STRUCTURAL_INFERENCE,confidence 不得高于 0.45,note 必须写明“由整机尺寸与结构推导,待复核”。只有连整机尺寸或结构都不足时,才使用“件”或“套”并保持成本待补。系统会在后续按整板尺寸与裁切利用率计算成本,模型不要自行填写板价。
|
||||
4. 板材有明确“单块板件”长宽时使用“㎡”并填写 lengthMm、widthMm。系统已先汇总主图、SKU 图和全部详情图:只要整条商品资料存在明确板厚,VISUAL_ESTIMATE 候选就不会进入 facts;必须优先使用卡尺、标尺、尺寸线或参数文字的明确读数,填写 heightMm 和 materialSpec,并保留 SOURCE_TEXT/IMAGE 证据。只有全商品没有任何明确板厚时,facts 才可能保留 basis=VISUAL_ESTIMATE 的约值;此时可以填写 heightMm,但 materialSpec 必须写“约 Xmm(图片比例估算)”,evidence.basis 必须使用 STRUCTURAL_INFERENCE,confidence 不得高于 0.45,note 必须保留参照尺寸、部件范围与误差原因。没有这两类证据时省略 heightMm。明确标注的适用范围以原文为准:只标隔板时不得套用到门板、背板、侧板或其他板件;标明柜体通用板厚时才可用于对应柜体板件。不得把整机高度填成板厚。若图片没有直接标出单块长宽,但 SKU 元数据或图片事实已经确认整机长宽高与柜体、桌体、层板等装配结构,应把主要板件拆成少量可计价行,依据装配关系保守推导单块长宽和数量;这类行的 evidence.basis 必须是 STRUCTURAL_INFERENCE,confidence 不得高于 0.45,note 必须写明“由整机尺寸与结构推导,待复核”。只有连整机尺寸或结构都不足时,才使用“件”或“套”并保持成本待补。系统会在后续按整板尺寸与裁切利用率计算成本,模型不要自行填写板价。
|
||||
5. 只写与该 SKU 已确认结构直接相关的部件。隐藏五金、人工、包材可以作最小必要推断,但 evidence.basis 必须是 STRUCTURAL_INFERENCE,且 evidence.confidence 和该行 confidence 均不得高于 0.55。
|
||||
6. evidence.imageUrl 只能使用输入 facts 中的 imageRef,不能编造地址。所有尺寸用毫米;没有证据的可选尺寸字段直接省略。
|
||||
7. SKU 之间不允许组合、拆分、补造或遗漏。若材料、尺寸、是否含镜、部件或包装有差异,必须在本 SKU 的 BOM 中体现。
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user