---
name: baoyu-life-science-visit-card
description: |
  宝锐生物生命科学拜访卡 — 面向课题组（PI实验室）的访前作战卡。
  生命科学销售以课题组为商机单位，拜访卡从研究方向、组织架构、
  货号使用（我方+竞品）、测试历史、推荐产品方案等六个维度
  推送一站式访前情报。不含销售金额和应收帐款（ERP归口为客户级，无法拆分到课题组）。
  与诊断原料拜访卡互补：诊断原料面向企业客户，生命科学面向课题组（飞书商机=课题组）。
triggers:
  - 用户说"帮我做XX课题组的生命科学拜访卡"（标准触发词）
  - 用户要求"生命科学拜访卡""课题组拜访""PI拜访准备"
  - 用户提到某个课题组/PI姓名并要求准备拜访
  - 用户要求从CRM活动导出提取某个课题组的拜访情报
  - 用户说"帮我准备去见XX老师/XX教授/XX课题组"
  - 用户上传飞书活动导出并要求为某个课题组做拜访卡
  - ⚠️ 与诊断原料拜访卡区分：当用户说"帮我做XX的诊断原料拜访卡"时，加载 baoyu-visit-card 而非本技能
---

# 宝锐生物生命科学拜访卡

## 核心概念

| 概念 | 说明 |
|------|------|
| **商机单位** | 课题组（PI实验室），非公司 |
| **客户实体** | 大学/研究所（如"浙江大学""浙江清华长三角研究院"） |
| **商机名称格式** | 飞书商机中典型格式："PI姓名课题组"（如"岳欣课题组"） |
| **利益相关方** | PI（决策者）、实验室管理员（执行者）、研究生/博后（使用者） |

## 数据源

生命科学拜访卡的数据来自多个维度，按优先级：

| 数据源 | 获取方式 |
|--------|---------|
| 🔴 P0 | IMA知识库 | `ima-skill` knowledge-base，搜索课题组/PI/产品名 |
| 🔴 P0 | 飞书CRM活动导出 | 解析xlsx（zipfile+xml），搜索课题组名 |
| 🔴 P0 | ERP订单数据 | 解析xlsx，搜索课题组对应客户+货号 |
| 🔴 P0 | 飞书MCP商机 | `references/life-science-mcp-structure.md` — 视图 `_rz-opbvg`、三级模型、决策链 |
| 🟡 P1 | 生命科学客户清单 | 补充客户分级、区域信息 |
| 🟢 P2 | Web搜索 | PI研究方向、论文背景补充 |

### CRM活动导出结构

文件路径：`~/work/secure/CRM及ERP数据/CRM活动导出-销售管理*.xlsx`

列结构（2026-03版）：
| 列 | 字段 | 用途 |
|----|------|------|
| A | 跟进部门 | 南区/北区/浙江/广东 |
| B | 活动关联客户 | 客户名称（大学/研究所） |
| C | 活动关联客户(ID) | 飞书客户ID |
| D | 是否有效反馈 | 是/否 |
| E | 活动描述 | **核心情报**（测试结果/价格/竞品/技术细节） |
| F | 名称 | 活动标题 |
| G | 活动对应商机 | **课题组名**（如"岳欣课题组"） |
| H | 活动对应商机(ID) | 飞书商机ID |
| I | 活动关联联系人 | 联系人姓名 |
| J | 活动关联联系人(ID) | 飞书联系人ID |
| K | 活动参与人员 | 宝锐销售员 |
| L | 后续计划 | 下一步行动 |
| M | 创建时间 | 日期 |
| N | 创建者 | 销售员 |
| O | 活动链接 | 飞书项目链接 |

## 工作流

### Step 1: 信息收集

**1a. IMA知识库搜索（必做）**

搜索两个知识库中与课题组相关的产品信息、竞品对比、销售案例：
- 搜索课题组名/PI姓名/研究机构名
- 搜索相关技术关键词（qPCR/NGS/多重PCR等）
- 搜索匹配产品货号及竞品信息

**1b. CRM活动数据提取（必做）**

从飞书CRM活动导出中搜索课题组名（商机名称）：

```python
import zipfile, xml.etree.ElementTree as ET

def parse_crm_activities(xlsx_path, target_opp):
    """从CRM活动导出提取指定课题组的所有活动"""
    zf = zipfile.ZipFile(xlsx_path)
    ns = {'s': 'http://schemas.openxmlformats.org/spreadsheetml/2006/main'}
    
    # 读取共享字符串表
    ss_xml = zf.read('xl/sharedStrings.xml')
    ss_tree = ET.fromstring(ss_xml)
    strings = [''.join((t.text or '') for t in si.findall('.//s:t', ns)) 
               for si in ss_tree.findall('.//s:si', ns)]
    
    # 读取sheet（CRM导出通常为sheet1.xml）
    sheet_xml = zf.read('xl/worksheets/sheet1.xml')
    sheet_tree = ET.fromstring(sheet_xml)
    
    def cell_val(c):
        v = c.find('s:v', ns)
        t = c.get('t', '')
        val = v.text if v is not None else ''
        if t == 's' and val.isdigit():
            idx = int(val)
            if idx < len(strings):
                val = strings[idx]
        return val
    
    def col_letter(ref):
        return ''.join(ch for ch in ref if ch.isalpha())
    
    rows = sheet_tree.findall('.//s:row', ns)
    matches = []
    for row in rows[1:]:
        cells = {col_letter(c.get('r')): cell_val(c) for c in row.findall('s:c', ns)}
        opp_name = cells.get('G', '')
        if target_opp in opp_name:
            matches.append(cells)
    
    return matches
```

**1b. ERP订单数据提取**

搜索课题组对应客户名称的ERP订单记录，提取货号、金额、日期。

**1c. 生命科学客户清单**

文件：`~/Desktop/Hermes输出-工作类/hermas工作台数据库/客户清单/诊断原料&生命科学客户清单*.xls`

**1d. Web搜索（可选补充）**

搜索"PI姓名 研究方向 论文 单位"补充学术背景。

### Step 2: 数据提炼

从CRM活动中提取七个维度的情报：

**维度1 — 研究方向**
- 从活动描述中提取技术关键词（qPCR/多重PCR/冻干/NGS/LAMP/甲基化/SNP/数字PCR等）
- 从测试产品推断研究偏好（高GC？直扩？冻干？多重？）

**维度2 — 组织架构**
- PI姓名、职称
- 飞书商机链接（从活动链接字段提取飞书项目URL）
- 联系人名单（从CRM活动关联联系人和活动参与人员）

**维度3 — 我方货号使用**
- 正则提取宝锐货号：`M\\d+-\\d+|M\\d+|AS\\d+-\\d+|AS\\d+|MD\\d+|FE\\d+|FDE\\d+`
- 记录每个货号的首次出现日期、使用场景

**维度4 — 竞品货号**
- 搜索关键词：近岸/诺唯赞/Takara/Thermo/NEB/ABI/擎科/全式金/TOYOBO
- 从活动描述中提取竞品产品名和客户评价

**维度5 — 测试历史**
- 按时间线整理每次测试：日期/产品/结果/客户反馈
- 区分正面（通过/已采购/优于竞品）和负面（假阳/杂峰/污染/CT值偏高）

**维度6 — 建议推荐产品方案**
- 基于研究方向+当前产品+空白区推荐新产品
- 标注P0（立即推荐）/P1（下次拜访）/P2（中长期）
- ⚠️ 推荐理由中不得引用ERP金额（如"¥53,220"），仅用定性描述（如"已确认稳定采购"）
- 每条P0/P1推荐必须包含话术锚点（嵌入CRM客户原话或已知事实）

> ⚠️ 原维度7「应收帐款」已移除 — ERP归口为客户级（大学/研究所），无法精确拆分到课题组。拜访卡不展示销售金额和未回款金额。

### Step 3: 生成拜访卡HTML

保存到：`~/Desktop/Hermes输出-工作类/客户管理/拜访卡/{课题组名}_生命科学拜访卡_{YYYYMMDD}.html`

**HTML 必须纯静态**：所有数据在生成时写入 HTML，不使用 JS 动态渲染。模板中的 `V_DATA` + JS 仅用于模板说明。

### Step 4: 同步统一工作台（必做，不可遗漏）

生成拜访卡后，必须立即更新工作台的嵌入文件清单，否则工作台页面不显示新文件。

**目标文件**：`~/Desktop/Hermes输出-工作类/工具/宝锐统一工作平台.html`

**操作**：在 `EMBED.lifeSciVisits` 数组的第一个条目上方插入新条目（倒序，最新在上）：
```javascript
lifeSciVisits:[
  {name:"{新课题组名} ({MMDD})",path:"../客户管理/拜访卡/{文件名}.html"},  // ← 新增
  {name:"陈昆松课题组 (0707)",path:"../客户管理/拜访卡/陈昆松课题组_生命科学拜访卡_20260707.html"}
],
```

使用 `patch` 工具精准插入，不重写整个文件。更新后验证 JS 语法：
```bash
sed -n '/<script>/,/<\/script>/p' ...工作平台.html | sed '1d;$d' > /tmp/_check.js && node --check /tmp/_check.js
```

## HTML模板规范

### 配色（白蓝科技风 — 统一品牌规范）
```css
--pri:#1a73e8;--pri-dark:#1557b0;--pri-light:#e8f0fe;
--ok:#0d904f;--ok-light:#e6f4ea;
--warn:#e37400;--warn-light:#fef7e0;
--bad:#d93025;--bad-light:#fce8e6;
--bg:#f5f7fa;--card-bg:#fff;--border:#e0e0e0;
--text:#202124;--sub:#5f6368;--muted:#9e9e9e;
```

### 页面结构（单页多卡片布局）

```
┌─────────────────────────────────────┐
│ 顶栏：BIORI BIOTECH + 课题组名 + 日期 │
├─────────────────────────────────────┤
│ KPI行：研究方向/合作阶段/客户等级/销售员 │
├──────────────────┬──────────────────┤
│ ① 研究方向卡片    │ ② 组织架构卡片    │
│ (技术关键词标签)  │ (PI+联系人+       │
│                  │  飞书链接)       │
├──────────────────┴──────────────────┤
│ ③ 我方货号使用表（货号/名称/用途/最近使用）│
├─────────────────────────────────────┤
│ ④ 竞品信息表（竞品品牌/产品/客户评价）  │
├─────────────────────────────────────┤
│ ⑤ 测试历史时间线（日期/产品/结果/反馈）│
├─────────────────────────────────────┤
│ ⑥ 推荐产品方案（P0/P1/P2 + 推荐理由 · 含CRM话术锚点） │
└─────────────────────────────────────┘
```
> ⚠️ 不包含应收帐款卡片 — ERP归口暂无法精确到课题组级别。

### 顶栏

使用宝锐统一顶栏规范：
```html
<div style="position:fixed;top:0;left:0;right:0;height:48px;background:#fff;border-bottom:1px solid var(--border);z-index:1000;display:flex;align-items:center;padding:0 20px;">
  <a href="http://127.0.0.1:8765/工具/宝锐统一工作平台.html" style="text-decoration:none;display:flex;align-items:center;gap:8px;">
    <img src="http://127.0.0.1:8765/logo_transparent.png" height="28" alt="BIORI">
    <span style="font-size:14px;font-weight:700;color:#333;">BIORI BIOTECH</span>
  </a>
  <span style="margin-left:16px;font-size:13px;color:var(--sub);">生命科学拜访卡</span>
  <span style="margin-left:auto;font-size:12px;color:var(--muted);" id="pageDate"></span>
</div>
<div style="margin-top:64px;"></div>
```

### KPI卡片行

4列：研究方向标签 | 合作阶段 | 客户等级 | 负责销售

### 各卡片样式

- 使用 `.bior-card` 统一卡片类（白底+圆角8px+阴影）
- 卡片标题：15px bold，`border-bottom:2px solid var(--pri-light)`
- 表格：`.bior-table`，表头 `background:var(--pri-light)`

### 技术标签

```css
.tag-tech { background:#e8f0fe; color:#1a73e8; padding:2px 8px; border-radius:12px; font-size:12px; margin:2px; display:inline-block; }
```

### 优先级标记

```css
.tag-p0 { background:#fce8e6; color:#d93025; }
.tag-p1 { background:#fef7e0; color:#e37400; }
.tag-p2 { background:#e6f4ea; color:#0d904f; }
```

## 输出路径

`~/Desktop/Hermes输出-工作类/客户管理/拜访卡/{课题组名}_生命科学拜访卡_{YYYYMMDD}.html`

## 与诊断原料拜访卡的区别

| 维度 | 诊断原料拜访卡 | 生命科学拜访卡 |
|------|-------------|-------------|
| 商机单位 | 公司 | 课题组（PI实验室） |
| 数据重点 | 决策链/竞争矩阵/NMPA | 研究方向/测试历史/货号使用 |
| 组织架构 | 企业层级图 | PI+联系人（飞书链接） |
| 产品 | 诊断原料（酶/预混液） | 生命科学试剂（PCR mix/酶/NGS） |
| 应收款 | 公司级（ERP直接对应） | ❌ 不展示（ERP无法拆分到课题组） |

## 注意事项

- 课题组名可能包含PI姓名（如"岳欣课题组"），搜索时用模糊匹配
- 一个大学/研究所可能对应多个课题组，按课题组分别处理
- 活动描述的[图片]标记无法提取，仅提取文字部分
- 飞书链接提取后保留完整URL，方便销售直接跳转
- 货号提取正则：`[A-Z]+\\d+(-\\d+)*`（匹配M216-3、AS44-001902等）
- 所有生成HTML必须通过 `node --check` 语法验证
- **HTML优先静态填充**：拜访卡数据全部在生成时写入HTML（纯静态），模板中的 `V_DATA`+JS动态渲染仅用于模板说明，实际产出应为不含JS渲染逻辑的静态HTML
- **❌ 不显示ERP金额和应收帐款**：ERP数据归口为客户级（大学/研究所），无法精确拆分到课题组级别。拜访卡仅展示CRM来源的定性采购记录，不含销售金额。

## Pitfalls（踩坑记录）

### Pitfall 1: CRM XLSX rels 命名空间

CRM活动导出的 `xl/_rels/workbook.xml.rels` 使用 **package** 命名空间，不是 officeDocument：
```python
# ✅ 正确
rns = {'r': 'http://schemas.openxmlformats.org/package/2006/relationships'}
# ❌ 错误 — 飞书CRM导出返回空结果
rns = {'r': 'http://schemas.openxmlformats.org/officeDocument/2006/relationships'}
```

对于CRM导出（单Sheet），直接读 `xl/worksheets/sheet1.xml` 即可，无需 workbook.xml+rels 联动解析。

### Pitfall 2: 生命科学部门识别

CRM中生命科学销售部门是**区域名**，不是"生命科学"：
- `南区`、`北区`、`浙江`、`广东` = 生命科学销售部
- `诊断原料销售拓展部`、`诊断原料大客户销售部` = 诊断原料销售部

搜索时用区域名集合 `{'南区', '北区', '浙江', '广东'}` 匹配 A 列。

### Pitfall 3: ERP数据是客户级，非课题组级 ⚠️ 不要展示金额

ERP订单数据的 `A列（客户）` 是大学/研究所级别（如"浙江大学"），无法自动拆分到具体课题组。

**最终方案（2026-07-07 确定）：不在拜访卡中展示任何ERP金额和应收帐款。**

原因：即使按销售员过滤（如吴云/王慧洁名下¥279,925），一人同时管理100+个课题组，过滤精度仅80-90%，不可用于正式拜访卡。脚注标注 `ERP数据归口暂无法精确区分课题组级别`。

**采购记录仅从CRM提取**（定性）：从活动描述中正则提取"下单X支""买X包"等采购事件，作为定性参考。货号使用表只保留 货号/产品名/CRM采购记录/状态，不设金额列。

> ❌ 已废弃的方法：按销售员过滤ERP → 精度不足，数据归口有问题。

### Pitfall 4: ERP列结构（19-22列）

与CRM不同，ERP导出有更多列：
| 列 | 字段 | 用途 |
|----|------|------|
| A | 客户名称 | 搜索目标大学/研究所 |
| D | 价税合计 | 销售额（排除赠品N列=是） |
| F | 单据类型 | 测试销售订单/标准销售订单 |
| I | 物料名称 | 产品描述（中文名） |
| T | 货号# | 宝锐产品编码（如 BR1D303） |
| W | 区域# | 华东区/华北区等 |

### Pitfall 5: 话术必须嵌入客户原话锚点

推荐产品方案中的"话术锚点"不能是泛泛的销售话术，必须从CRM数据中提取真实锚点：
- ✅ "上次BR1D303批次问题我们做了根因分析，新批次做了严格QC"
- ✅ "你们现在用的诺唯赞定量是q711对吧？我们的FastAmpli在同等条件下…"
- ❌ "我们的产品质量很好，建议您尝试一下"（无锚点的泛化话术）

每条P0/P1推荐都需包含：①理由段（数据支持）②话术锚点段（嵌入CRM中客户说过的原话/已知事实）

### Pitfall 6: 工作台同步

**新版工作台（v2精简版，2026-07-07上线）已无 EMBED 文件清单，无需手动同步。** 旧版作战室和旧工作台仍使用 `EMBED.lifeSciVisits` 数组。

### Pitfall 7: 客户清单为 .xls 二进制格式

客户清单（如 `诊断原料&生命科学客户清单20260630.xls`）是旧版 `.xls` 格式（Composite Document File V2），不能用 `zipfile` 解析。必须用 `xlrd`：

```bash
pip3 install xlrd
```

```python
import xlrd
wb = xlrd.open_workbook('xxx.xls')
```

`execute_code` 沙箱可能没有 xlrd，此时改用 `terminal` 执行。`zipfile.BadZipFile` 报错时就是遇到了 .xls 格式。

### Pitfall 8: 综合仪表盘生命科学团队归属

综合管理仪表盘 `allSalesData` 中，生命科学销售员的 `dept` 字段需按区域拆分（不是笼统的"生命科学部"）。

**铁律来源**：客户清单 Sheet 6「生命科学销售部」的区域列。

| dept 值 | 销售员 |
|---------|--------|
| 生命科学-浙江 | 吴云、王慧洁、刘静文、袁殿果、侯海瑜、叶信福 |
| 生命科学-广东 | 韩远怀、黄建华、王诗香 |

CRM 数据可辅助确认：王慧洁（CRM 浙江1653条）、刘静文（CRM 浙江732条）。客户清单中未出现的销售员（如刘斌、陈彩虹）保留为"生命科学部"。

### Pitfall 10: ERP销售订单格式版本差异（2026-07-07新增）

ERP销售订单导出存在两种列结构，差异极大。格式检测：检查G列值是否含`-`（日期格式）。详见 `references/erp-crm-format-detection.md`。

### Pitfall 9: 飞书CRM导出格式版本差异（2026-07-07新增）

飞书CRM活动导出存在两种列结构，自动检测方法：

| 列 | 旧格式（20260328导出） | 新格式（20260701导出） |
|----|---------------------|---------------------|
| A | 跟进部门 | 活动关联客户 |
| B | 活动关联客户 | 活动关联客户(ID) |
| C | 活动关联客户(ID) | 活动对应销售订单 |
| E | 活动描述 | 名称 |
| F | 名称 | 活动描述 |
| G | 活动对应商机 | 所属空间 |
| H | 活动对应商机(ID) | 创建时间 |
| I | 活动关联联系人 | 创建者 |
| K | 活动参与人员 | — (无此列) |
| M | 创建时间 | — |
| N | 创建者 | — |

**检测代码**：
```python
is_old = bool(cells.get('K'))  # 旧格式有K列（活动参与人员），新格式没有
if is_old:
client = cells.get('B', '')  # 旧格式：B=客户名
date = cells.get('M', '')    # 旧格式：M=日期
sales = cells.get('K', '') or cells.get('N', '')
desc = cells.get('E', '')
opp = cells.get('G', '')     # 旧格式：G=商机名
else:
client = cells.get('A', '')  # 新格式：A=客户名
date = cells.get('H', '')    # 新格式：H=日期
sales = cells.get('I', '')   # 新格式：仅I=创建者
desc = cells.get('F', '')
opp = ''                     # 新格式无商机列
```

**关键差异**：
- 新格式**无跟进部门**列 → 用"所属空间"或留空
- 新格式**无商机**列 → 无法按课题组筛选，需要用客户名匹配
- 新格式**无活动参与人员** → 只有创建者
- 新格式XLSX使用 `sheet2.xml` 而非 `sheet1.xml` → 需自动检测

**XLSX Sheet自动检测**：
```python
sheet_names = [f for f in zf.namelist() if f.startswith('xl/worksheets/sheet') and f.endswith('.xml')]
sheet_names.sort()
sheet_file = 'xl/worksheets/sheet2.xml' if 'xl/worksheets/sheet2.xml' in sheet_names else sheet_names[0]
```
