---
name: kol-outreach
description: |
  KOL outreach automation: discover TikTok KOLs, extract emails, send personalized outreach, handle replies, negotiate deals.
  Trigger: user says '帮我找 KOL', 'KOL 建联', 'KOL 进展', or message contains [KOL_INBOUND:{campaignId}] / [KOL_INBOUND].
---

# KOL 建联

端到端 TikTok KOL 建联自动化：发现、触达、谈判、成交。

## 核心：~/kol-outreach/ 工作目录

所有状态都在 `~/kol-outreach/`（单个 git repo）。每次会话只做一件事。

**每次会话启动**：

1. 检查 `~/kol-outreach/BRAND.md` 是否存在且已完整填写 — 不存在或未完成则走初始化
2. 读 `CAMPAIGNS.md` — 了解所有 campaign 状态
3. `git -C ~/kol-outreach log --oneline -5` — 最近变更
4. 根据用户意图 + 文件状态决定本次操作

| 文件 | 层级 | 用途 |
|------|------|------|
| `BRAND.md` | merchant | 品牌素材、兜底话术、升级关键词 |
| `MERCHANT_LIMITS.md` | merchant | 日发信配额（跨 campaign 共享） |
| `CAMPAIGNS.md` | merchant | campaign 索引 |
| `PROGRESS.md` | merchant | 跨 campaign 关键事件 |
| `campaigns/{id}/CONFIG.md` | campaign | 商品、预算、条款、运行参数 |
| `campaigns/{id}/KOLS.md` | campaign | KOL 状态索引（status: `pending_outreach` / `awaiting_reply` / `in_progress` / `deal_reached` / `needs_merchant_followup` / `rejected` / `expired` / `send_error` / `no_contact`） |
| `campaigns/{id}/TEMPLATES.md` | campaign | 邮件模板（LLM 只填变量） |
| `campaigns/{id}/PROGRESS.md` | campaign | 本 campaign 事件日志 |
| `campaigns/{id}/CONVERSATIONS/{username}.md` | campaign | 每个 KOL 的完整对话 |

## Session 路由

按优先级依次判断：

| 条件 | 流程 |
|------|------|
| 消息含 `[KOL_INBOUND:{campaignId}]` 或 `[KOL_INBOUND]` | → 回信处理 |
| `~/kol-outreach/BRAND.md` 不存在 | → 商家初始化 → 创建 Campaign |
| 用户提到了新商品 + 该商品无对应 campaign | → 创建 Campaign |
| 用户要求发送 outreach / 开始建联 | → 发现 + Outreach |
| 用户问进展 / 状态 / 数据 | → 查询进度 |

**Campaign 定位**：用户指定了 campaign 用指定值；未指定时读 `CAMPAIGNS.md`——只有一个 active 就用它，多个则列出让商家选。

## 商家初始化

只在 `~/kol-outreach/BRAND.md` 不存在或未完整填写时执行。

1. `cp -r $CLAUDE_SKILL_DIR/template/merchant/. ~/kol-outreach/` 并 `git init`
2. 对话式填写 `BRAND.md`：
   - `brand_name` → 生成 `brand_slug` 建议 → `scout outreach brand-slug check` 验证全局唯一 → 冲突则换 → `scout outreach brand-slug claim` 锁定
   - 填完品牌基础、兜底话术、升级关键词（中英双语必填）、禁用词
3. 填写 `MERCHANT_LIMITS.md`（默认 `max_daily_sends: 50`）
4. `git add -A && git commit -m "merchant onboarding complete"`
5. 继续创建第一个 campaign

## 创建 Campaign

1. 确定 `campaignId`：从商品标题生成 kebab-case slug，校验唯一且匹配 `^[a-z0-9][a-z0-9-]{0,29}$`
2. `cp -r $CLAUDE_SKILL_DIR/template/campaign/. ~/kol-outreach/campaigns/{campaignId}/`
3. 对话式填写 `CONFIG.md`，**逐段确认**：
   - 商品信息 → KOL 画像 → 预算报价 → 合作条款 → 运行参数
   - 预算校验：`max_price >= target_price >= first_offer`，`max_step <= max_price - first_offer`，`total_budget >= max_price`
   - 不通过则说明原因、给建议、重新问（不退出）
4. 更新 `CAMPAIGNS.md` 追加新行
5. `git add -A && git commit -m "[{campaignId}] campaign ready"`
6. 问商家是否立即开始发现 KOL

## 发现 KOL + 首发 Outreach

**前置检查**（任一不过则告知商家并退出）：
- campaign 状态为 `active`
- 本 campaign 在谈 KOL 数（`awaiting_reply` + `in_progress`）< `max_concurrent_kols`
- merchant 今日发信数（`MERCHANT_LIMITS.md`）< `max_daily_sends`
  - UTC 跨天时先归档昨天计数、重置 `sent_today=0`

**发现**：

1. `scout tiktok creators --follower-count <range> --country <region> --limit <N> --format json`
2. 过滤：`engagement < 2%` 丢弃、已在 `KOLS.md` 丢弃、粉丝量不在区间丢弃
3. 对每个候选：`scout tiktok creator-products @username --limit 5`，从 bio 提取 email
4. 写入 `KOLS.md`：有 email → `pending_outreach`，无 email → `no_contact`
5. `git commit -m "[{campaignId}] discovered N KOLs"`

**Outreach**：

对每个 `pending_outreach` KOL（不超过本轮发送量）：

1. 读 `TEMPLATES.md` 的 `first_outreach_v1`
2. 产出模板变量 JSON（`kol_first_name`, `opener_line`, `fit_reason`, `product_one_liner`）
3. 渲染模板 → 终检（见「护栏」Layer 4）→ `scout outreach send`，JSON 必须包含：
   `to`, `subject`, `body`, `from_merchant_id`, `from_campaign_id`, `from_brand_slug`, `from_brand_name`, `from_contact_name`
4. 发送成功（`ok: true`）→ 写 `CONVERSATIONS/{username}.md`（Round 1）+ 更新 `KOLS.md` → `awaiting_reply` + `MERCHANT_LIMITS.md` sent_today +1
5. 发送失败（`ok: false`）→ 标 `send_error`，不计入 sent_today，跳过该 KOL，不中断其他

全部完成后：`git commit -m "[{campaignId}] outreach sent to N KOLs"` + 更新 PROGRESS.md

## 处理 KOL 回信

触发：消息含 `[KOL_INBOUND:{campaignId}]` 或 `[KOL_INBOUND]`。

1. 从消息中提取 `From`, `Subject`, `Message-ID`, `In-Reply-To`, `Body`
2. 定位 KOL：
   - `campaignId` 已知 → 读该 campaign 的 `KOLS.md`，按 `from_email` 匹配
   - `campaignId` 未知 → 遍历所有 `campaigns/*/KOLS.md`，按 `from_email` 匹配，多个取 `last_update` 最新
   - 无匹配 → 写 PROGRESS.md + `sentinel report --condition-met false` + 退出
3. 读 `CONFIG.md` + `BRAND.md` + `CONVERSATIONS/{username}.md`
4. **预检**（见「护栏」Layer 2）→ 任一触发则发 `escalate_fallback_v1` 并退出
5. 按「谈判策略」产出结构化决策 JSON
6. **后验**（见「护栏」Layer 3）→ 不通过则降级为 escalate
7. 渲染模板 → **终检**（见「护栏」Layer 4）→ `scout outreach send`
   - 回信必须带 `in_reply_to`（KOL 的 `message_id`）和 `references`（`CONVERSATIONS` 里的完整 message-id chain）
8. 写回 `CONVERSATIONS/{username}.md` + `KOLS.md` + `MERCHANT_LIMITS.md`
9. 如果 `accept` → 发 `deal_confirmation_v1`，标 `deal_reached`
10. `git commit -m "[{campaignId}] round N {action} @{username}"`
11. `sentinel report --condition-met true --summary "..." --action-taken "..."`

## 查询进度

- **多 campaign**：读每个 campaign 的 `KOLS.md`，按 status 分桶统计，输出 markdown 表格 + merchant 级 `sent_today` 信息
- **单 campaign**：读 `KOLS.md` 全表 + `PROGRESS.md` 最近 5 条。对每个 `needs_merchant_followup`，读 `CONVERSATIONS/{username}.md` 最后一轮，总结 KOL 诉求
- 只读不写，不 commit

## 谈判策略

处理 KOL 回信时，产出一个结构化决策 JSON：

```json
{
  "action": "counter_offer | accept | decline | clarify | escalate",
  "new_price": null,
  "final_price": null,
  "concessions": [],
  "template_id": "...",
  "template_vars": {},
  "reasoning": "..."
}
```

**决策框架**：

| KOL 回应 | 判断 | action |
|----------|------|--------|
| KOL 报价 ≤ `first_offer` | 低于预期，直接接受 | `accept` |
| KOL 报价 ≤ `target_price` | 在目标范围内，可接受 | `accept` |
| KOL 报价 ≤ `max_price` | 超出目标但可谈，渐进加价 | `counter_offer` |
| KOL 报价 > `max_price` | 超预算，回一次低价试探 | `counter_offer`（报 `target_price`） |
| KOL 连续 2 轮报价 > `max_price` | 已试探过，差距太大 | `decline` |
| KOL 对之前的 counter 再次还价，且 ≤ `max_price` | 拉锯中，评估是否值得让步 | `counter_offer` 或 `accept` |
| KOL 明确拒绝合作 | 尊重意愿 | `decline` |
| KOL 问非价格问题（产品细节、时间线等） | 信息补充 | `clarify` |
| KOL 提到超出授权范围的话题 | 不要硬答 | `escalate` |

**出价规则**：

- 首次 counter：从 `first_offer` 开始
- 每轮加价不超过 `max_step`
- 绝不超过 `max_price`
- 加 concession 时优先用 `allowed_uses` 里的授权项（转发、二创等）作为附加价值，而非直接加钱
- 本 campaign 已承诺预算（`deal_reached` 的 offer 总和）+ 本次报价不能超 `total_budget`

**tone**：

- 专业友好，不卑不亢
- 不要用 "unfortunately" / "sadly" — 直接说 "our budget is..."
- 每封回信开头用一句话承接 KOL 上一封的内容（`acknowledge_line`）
- 永远不要透露 `max_price`、`total_budget`、或谈判策略

## 护栏

四层防线。任一层失败都**不问商家**——自动降级为发兜底话术 + 标记 `needs_merchant_followup`。

### Layer 1 — Onboarding 校验

写 `BRAND.md` / `CONFIG.md` 时执行：
- 所有必填字段非空
- `brand_slug` 全局唯一（通过 `scout outreach brand-slug check`）
- `max_price >= target_price >= first_offer`
- `max_step <= max_price - first_offer`
- `total_budget >= max_price`
- `escalation_keywords` 中英双语都有
- 不通过 → 继续追问，不退出 onboarding

### Layer 2 — 回信预检（调 LLM 前）

- `rounds >= max_rounds` → `needs_merchant_followup`
- `now - started >= max_days` → 标 `expired`，**不发邮件**，直接退出
- KOL 回信命中 `escalation_keywords`（中英双语匹配） → 发 `escalate_fallback_v1`
- `in_reply_to` 与 conversation 最后一条 inbound 的 `message_id` 不一致 → 发 `escalate_fallback_v1`

### Layer 3 — 决策后验（产出 JSON 后）

- `action` 必须在白名单内
- `counter_offer`: `first_offer <= new_price <= max_price`，`new_price - previous_offer <= max_step`
- `accept`: `first_offer <= final_price <= max_price`，`committed + final_price <= total_budget`
- `concessions ⊆ allowed_terms`
- `template_id` 存在于 `TEMPLATES.md`
- 任一不通过 → 降级 `escalate`，不重试

### Layer 4 — 内容终检（发送前）

- body 不含 `forbidden_phrases`
- body 长度 ≤ 2000 字符
- 不通过 → 不发送，降级 `escalate`

## 允许的命令

- `scout tiktok creators` / `scout tiktok creator-products`
- `scout outreach send` / `scout outreach brand-slug check|claim`
- `sentinel report`

## 常见错误

❌ 自由撰写邮件正文 → ✅ 只产出模板变量 JSON，用 TEMPLATES.md 渲染

❌ 一次 session 混做多个流程 → ✅ 单会话单任务

❌ 每一小步都 git commit → ✅ 每个逻辑动作完成后 commit 一次（发现、outreach、单个回信处理）

❌ 护栏失败后重试 LLM → ✅ 直接降级为 escalate

❌ 透露 max_price / total_budget / 谈判策略 → ✅ 只透露当前报价

❌ expired 的 KOL 还发邮件 → ✅ 标 expired 直接退出，不发信

❌ 修改不属于当前任务的 campaign 状态 → ✅ 只操作当前 campaign

❌ scout outreach send 失败就中断全部 → ✅ 标 send_error 跳过该 KOL，继续其他
