Precise Card Parse(精准名片解析 · 增强版)
相对 paste-card-parse 的升级版:同样支持粘贴文本 / 图片 OCR,但采用
清洗 → 行分类 → 标签提取 → 候选打分 → 冲突消解 管线,提升准确率与效率,并输出置信度。
目标字段:姓名、电话、邮箱、公司(可选职位、地址)。
何时启用(优先于旧 Skill)
- 生产录入、智能录入、线索落库前的结构化
- 用户要求「更准 / 更完整 / 更高效率」的名片解析
- OCR 噪声多、带「手机:」「邮箱:」标签、单行备注混排
旧 Skill paste-card-parse 仅作入门;新实现对齐本 Skill。
硬约束
- 单次扫描优先:正文只遍历行/token 一遍完成分类与候选收集(O(n)),禁止对全文反复跑多套无关正则。
- 标签优先于启发式:出现「姓名/手机/电话/邮箱/公司/单位」等标签时,标签值置信度最高。
- 每字段可多候选,只输出最优 + confidence:禁止静默拼接脏字符串。
- 清洗必做:零宽字符、全角、杂空白(可复用
cleanPasteText)。 - 图片:OCR → 纠错 → 同一解析器;OCR 空文必须失败提示。
- 填表默认 fill-empty;成功反馈含命中字段与低置信警告。
- 正则无全局状态污染;手机/邮箱用局部字面量。
- 与仓库
parseContactText共存时:封装为parseCardPrecise,可内部先调旧函数作 baseline,再以本管线结果覆盖高置信字段。
标准工作流
Precise Card Parse
- [ ] cleanPasteText(或等价预处理)
- [ ] 实现 lineClassify + extractByLabel
- [ ] 收集候选 → score → pickBest → ParsedCardPrecise
- [ ] OCR 路径:ocr → fixOcrNoise → 同上
- [ ] apply + emitParseSignal(含低置信提示)
- [ ] 用 fixtures 回归(见 reference)
管线总览
raw / OCR text
│
▼
clean (全角/零宽/换行)
│
▼
split lines + soft-split(;|;|空格分隔标签段)
│
▼
classify each segment ──► label | phone | email | company | title | name_cand | noise
│
▼
extractByLabel(最高优先级)
│
▼
score candidates(规则分)
│
▼
pickBest + conflict resolve
│
▼
ParsedCardPrecise { fields, confidence, hitFields, warnings }
Step 1:增强输出模型
export type CardFieldKey = 'name' | 'phone' | 'email' | 'company' | 'title' | 'address';
export type FieldHit = {
value: string;
confidence: number; // 0~1
evidence: 'label' | 'pattern' | 'heuristic' | 'ocr-fix';
};
export type ParsedCardPrecise = {
name: string;
phone: string;
email: string;
company: string;
title: string;
address: string;
raw: string;
cleaned: string;
source: 'text' | 'image';
fields: Partial<Record<CardFieldKey, FieldHit>>;
hitFields: CardFieldKey[];
warnings: string[]; // 如「姓名置信度偏低」
};
Step 2:清洗(效率:一遍字符串替换)
优先复用 src/utils/localCrmTools/pasteClean.ts → cleanPasteText。
额外建议:统一中文冒号 :→:(便于标签解析);去掉首尾引号。
Step 3:标签提取(准确率关键)
const LABEL_MAP: Array<{ keys: string[]; field: CardFieldKey }> = [
{ keys: ['姓名', '名字', '联系人', 'name'], field: 'name' },
{ keys: ['手机', '电话', '联系电话', 'mobile', 'tel', 'phone'], field: 'phone' },
{ keys: ['邮箱', '邮件', 'email', 'mail'], field: 'email' },
{ keys: ['公司', '单位', '企业', '机构', 'company'], field: 'company' },
{ keys: ['职位', '职务', 'title'], field: 'title' },
{ keys: ['地址', 'addr', 'address'], field: 'address' },
];
// 匹配:^标签\s*[::]\s*(.+)$
// confidence: 0.92 ~ 0.98
同一字段多标签命中时取更长且更像该字段的值(电话需通过 normalizePhone 校验)。
Step 4:无标签启发式(打分,勿盲取)
| 字段 | 高分信号 | 降分 / 排除 |
|------|----------|-------------|
| phone | 11 位 1[3-9]…;标签附近 | 连续年份、订单号过长数字 |
| email | 含 @ 且域名合法 | 缺 . 的残缺串 |
| company | 后缀 有限公司/股份/集团/研究所;长度 6–40 | 纯职位词、纯姓名 |
| name | 2–4 汉字;或「姓+职位」拆分 | 落在公司行内的子串;含「公司」 |
| title | 词表命中且行短 | 整行即公司名 |
姓名防误吸(比旧版重要):
- 已判定为 company 的行,禁止再当 name
- 禁止用「全文第一个 2–4 汉字」若该匹配落在公司名内部
- 优先独立行 / 标签值 /「X经理」去职位后剩余
公司防误吸:
- 关键词命中后仍要
scoreCompany:后缀权重大于「科技/投资」孤词 - 多候选取 score 最高,而非单纯最长(避免把整段备注当公司)
Step 5:OCR 噪声轻纠(图片路径)
在解析前对 OCR 文做低成本替换(仅高把握):
| 噪声 | 处理 |
|------|------|
| 邮箱中空格 name @ xx.com | 去 @ 两侧空格 |
| 常见 l/I/1 混在域名 | 仅当原串不像邮箱、修正后像邮箱时采用 |
| 全角 @ . | 转半角 |
| 手机号中间杂 . | 按手机模式抽取时忽略分隔符 |
纠错成功的字段 evidence: 'ocr-fix',confidence 略降(如 0.8)。
Step 6:冲突消解与效率
function pickBest(cands: FieldHit[]): FieldHit | undefined {
return cands.slice().sort((a, b) => b.confidence - a.confidence)[0];
}
- 电话:手机优先于座机;多个手机取标签命中或全文首次合法号
- 邮箱:多个取标签或顶级域名更常见者
warnings:任一输出字段confidence < 0.65时推入警告- 早停:清洗后为空直接返回;已标签凑齐 name+phone+email+company 可跳过重度启发式(仍可轻量补 title)
Step 7:填表与反馈
function emitParseSignal(card: ParsedCardPrecise) {
const hit = card.hitFields.join('/'); // 映射中文后再 Toast
if (!card.hitFields.length) {
ElMessage.warning('未识别到有效字段');
return;
}
const low = card.warnings.length ? `;注意:${card.warnings[0]}` : '';
ElMessage.success(`解析成功:${hit}${low}`);
}
与旧实现迁移
| 项目 | paste-card-parse | precise-card-parse(本 Skill) | |------|------------------|--------------------------------| | 策略 | 单次正则 + 最长公司行 | 标签 + 打分 + 消解 | | 输出 | 平坦字符串 | 带 confidence / warnings | | 姓名 | 易误吸公司内汉字 | 显式排除公司行 | | 效率 | 多次全文 match | 一行分类为主 | | OCR | 直解析 | 纠错后再解析 |
新页面直接按本 Skill 实现;旧页可渐进把 parseContactText 包进 parseCardPrecise。
反模式
| 禁止 | 原因 | |------|------| | 只用最长含「科技」的行当公司 | 备注整段误判 | | 全文第一个 2–4 汉字当姓名 | 从公司名切开「上海」等 | | 每字段各扫一遍超大正则库 | 浪费、难维护 | | 忽略标签行 | 准确率上限被锁死 | | 低置信仍 success 且无 warnings | 脏数据入库 |
Agent 交付检查
- [ ] 带标签备注样例:四要素全中且 confidence≥0.9
- [ ] 无标签混排样例:电话+邮箱稳定
- [ ] 公司名内不含被拆出的伪姓名
- [ ] OCR 空格邮箱可修好或给出 warning
- [ ] fixtures 中样例通过(见 reference)
- [ ] 反馈含命中字段;低置信有提示
附加资源
- 规则、打分、样例集:reference.md
- 参考实现:templates/parseCardPrecise.ts.md
- Vue 接入:templates/vue-precise-card.md
Scan to join WeChat group