platform-assistant/scripts/clean_category.js
Sisyphus ab764bbc6c refactor: DeepSeek → MiniMax (M2.7 推理模型) 迁移
- notes-extractor 和 category-inference 都改用 MiniMax API
- endpoint: https://api.minimaxi.com/v1/chat/completions
- model: MiniMax-M2.7 (env 可覆盖 MINIMAX_MODEL)
- 提示词明确禁止 <think> 标签和解释文字
- 新增 scripts/backfill_category.js (单条请求 + 严格清洗)
- 新增 scripts/clean_category.js (清理 <think>/**加粗/解释行/破折号 等脏值)
- 生产 .env 已写入 MINIMAX_API_KEY
- 实际回填: 693/761 (91%) 已分配品类, 68 条空品类待后续处理
2026-07-01 11:31:17 +08:00

80 lines
2.9 KiB
JavaScript
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

const { PrismaClient } = require('@prisma/client')
const prisma = new PrismaClient()
const KNOWN_CATEGORIES = new Set([
'防火门', '配电箱', '电线电缆', '阀门', '钢材', '涂料', '混凝土',
'变压器', '水泵', '风机', '管材', '开关', '灯具', '仪表', '电机',
])
const EXPLANATION_PREFIX = /^(让我|用户|我需要|好的|现在|首先|分析|根据|对于|这条|这个|考虑|我们|这里|注意|提示|重要|关键|总结|综上|该|其|它|他们|这些|那些|由于|因为|所以|因此|需要|可能|应该|可以|建议|希望|要求|必须)/
function cleanCategory(raw) {
if (!raw) return ''
let s = String(raw).trim()
s = s.replace(/<think>[\s\S]*?<\/think>/g, '').trim()
s = s.replace(/\*\*/g, '').trim()
s = s.replace(/^["「『]+|["」』]+$/g, '').trim()
const lines = s.split('\n').map((l) => l.trim()).filter((l) => l)
for (const line of lines) {
if (EXPLANATION_PREFIX.test(line)) continue
if (line.length > 20) continue
if (line.includes(',') || line.includes('。') || line.includes(':') || line.includes(':')) continue
if (line.includes(' - ') || line.includes(' – ') || line.includes('--')) {
const parts = line.split(/\s*[-–]\s*/).map((p) => p.trim()).filter((p) => p)
for (const p of parts) {
if (KNOWN_CATEGORIES.has(p)) return p
const m = p.match(/^([\u4e00-\u9fa5]{2,6})$/)
if (m && !EXPLANATION_PREFIX.test(p)) return m[1]
}
continue
}
const cleaned = line.replace(/^[\d]+[\.、::]\s*/, '').trim()
if (!cleaned || cleaned.length > 10) continue
if (KNOWN_CATEGORIES.has(cleaned)) return cleaned
const m = cleaned.match(/^([\u4e00-\u9fa5]{2,6})$/)
if (m && !EXPLANATION_PREFIX.test(m[1])) return m[1]
}
return ''
}
async function main() {
const leads = await prisma.lead.findMany({
where: { NOT: { category: null } },
select: { id: true, category: true },
})
console.log(`共 ${leads.length} 条有品类的线索`)
let fixed = 0
let cleared = 0
for (const lead of leads) {
const cleaned = cleanCategory(lead.category)
if (cleaned !== lead.category) {
await prisma.lead.update({ where: { id: lead.id }, data: { category: cleaned } })
if (cleaned === '') cleared++
else fixed++
}
}
console.log(`修正: ${fixed}, 清空: ${cleared}`)
const remaining = await prisma.lead.count({ where: { OR: [{ category: null }, { category: '' }] } })
console.log(`剩余空品类: ${remaining}`)
const dist = await prisma.lead.groupBy({
by: ['category'],
where: { NOT: { category: '' } },
_count: true,
orderBy: { _count: { category: 'desc' } },
take: 30,
})
console.log('品类分布:')
dist.forEach((d) => console.log(` ${d.category}: ${d._count}`))
}
main()
.catch((e) => {
console.error(e)
process.exit(1)
})
.finally(() => prisma.$disconnect())