@optima-chat/optima-agent 0.8.98 → 0.8.100

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (95) hide show
  1. package/.claude/settings.local.json +166 -0
  2. package/.claude/skills/.kb-skills-managed.json +9 -0
  3. package/.claude/skills/ingesting-sources/SKILL.md +67 -0
  4. package/.claude/skills/initializing-kb/SKILL.md +117 -0
  5. package/.claude/skills/kol-outreach/SKILL.md +194 -333
  6. package/.claude/skills/linting-the-wiki/SKILL.md +60 -0
  7. package/.claude/skills/querying-the-wiki/SKILL.md +59 -0
  8. package/.claude/skills/updating-related-pages/SKILL.md +65 -0
  9. package/dist/bin/bi-cli.js +0 -0
  10. package/dist/bin/browser-cli.js +0 -0
  11. package/dist/bin/comfy.d.ts +3 -0
  12. package/dist/bin/comfy.d.ts.map +1 -0
  13. package/dist/bin/comfy.js +3 -0
  14. package/dist/bin/comfy.js.map +1 -0
  15. package/dist/bin/commerce.js +0 -0
  16. package/dist/bin/gen.js +0 -0
  17. package/dist/bin/google-ads.js +0 -0
  18. package/dist/bin/growth.d.ts +3 -0
  19. package/dist/bin/growth.d.ts.map +1 -0
  20. package/dist/bin/growth.js +3 -0
  21. package/dist/bin/growth.js.map +1 -0
  22. package/dist/bin/logistics.js +0 -0
  23. package/dist/bin/optima.js +0 -0
  24. package/dist/bin/scout.js +0 -0
  25. package/dist/bin/sentinel.js +0 -0
  26. package/dist/bin/shopify.js +0 -0
  27. package/dist/bin/sync-kb-skills.d.ts +6 -0
  28. package/dist/bin/sync-kb-skills.d.ts.map +1 -0
  29. package/dist/bin/sync-kb-skills.js +65 -0
  30. package/dist/bin/sync-kb-skills.js.map +1 -0
  31. package/dist/src/hooks-loader.d.ts +6 -0
  32. package/dist/src/hooks-loader.d.ts.map +1 -0
  33. package/dist/src/hooks-loader.js +215 -0
  34. package/dist/src/hooks-loader.js.map +1 -0
  35. package/dist/src/ui/App.d.ts +6 -0
  36. package/dist/src/ui/App.d.ts.map +1 -0
  37. package/dist/src/ui/App.js +164 -0
  38. package/dist/src/ui/App.js.map +1 -0
  39. package/dist/src/ui/components/Composer.d.ts +10 -0
  40. package/dist/src/ui/components/Composer.d.ts.map +1 -0
  41. package/dist/src/ui/components/Composer.js +13 -0
  42. package/dist/src/ui/components/Composer.js.map +1 -0
  43. package/dist/src/ui/components/Header.d.ts +7 -0
  44. package/dist/src/ui/components/Header.d.ts.map +1 -0
  45. package/dist/src/ui/components/Header.js +7 -0
  46. package/dist/src/ui/components/Header.js.map +1 -0
  47. package/dist/src/ui/components/Message.d.ts +12 -0
  48. package/dist/src/ui/components/Message.d.ts.map +1 -0
  49. package/dist/src/ui/components/Message.js +21 -0
  50. package/dist/src/ui/components/Message.js.map +1 -0
  51. package/dist/src/ui/components/MessageList.d.ts +9 -0
  52. package/dist/src/ui/components/MessageList.d.ts.map +1 -0
  53. package/dist/src/ui/components/MessageList.js +18 -0
  54. package/dist/src/ui/components/MessageList.js.map +1 -0
  55. package/dist/src/ui/components/Spinner.d.ts +6 -0
  56. package/dist/src/ui/components/Spinner.d.ts.map +1 -0
  57. package/dist/src/ui/components/Spinner.js +7 -0
  58. package/dist/src/ui/components/Spinner.js.map +1 -0
  59. package/dist/src/ui/components/StatusBar.d.ts +11 -0
  60. package/dist/src/ui/components/StatusBar.d.ts.map +1 -0
  61. package/dist/src/ui/components/StatusBar.js +7 -0
  62. package/dist/src/ui/components/StatusBar.js.map +1 -0
  63. package/dist/src/ui/components/index.d.ts +7 -0
  64. package/dist/src/ui/components/index.d.ts.map +1 -0
  65. package/dist/src/ui/components/index.js +7 -0
  66. package/dist/src/ui/components/index.js.map +1 -0
  67. package/dist/src/validation/error-formatter.d.ts +21 -0
  68. package/dist/src/validation/error-formatter.d.ts.map +1 -0
  69. package/dist/src/validation/error-formatter.js +98 -0
  70. package/dist/src/validation/error-formatter.js.map +1 -0
  71. package/dist/src/validation/index.d.ts +10 -0
  72. package/dist/src/validation/index.d.ts.map +1 -0
  73. package/dist/src/validation/index.js +10 -0
  74. package/dist/src/validation/index.js.map +1 -0
  75. package/dist/src/validation/json-validator.d.ts +25 -0
  76. package/dist/src/validation/json-validator.d.ts.map +1 -0
  77. package/dist/src/validation/json-validator.js +173 -0
  78. package/dist/src/validation/json-validator.js.map +1 -0
  79. package/dist/src/validation/schema.d.ts +353 -0
  80. package/dist/src/validation/schema.d.ts.map +1 -0
  81. package/dist/src/validation/schema.js +57 -0
  82. package/dist/src/validation/schema.js.map +1 -0
  83. package/dist/src/validation/suggestions.d.ts +25 -0
  84. package/dist/src/validation/suggestions.d.ts.map +1 -0
  85. package/dist/src/validation/suggestions.js +144 -0
  86. package/dist/src/validation/suggestions.js.map +1 -0
  87. package/dist/src/validation/types.d.ts +40 -0
  88. package/dist/src/validation/types.d.ts.map +1 -0
  89. package/dist/src/validation/types.js +5 -0
  90. package/dist/src/validation/types.js.map +1 -0
  91. package/dist/src/validation/yaml-validator.d.ts +25 -0
  92. package/dist/src/validation/yaml-validator.d.ts.map +1 -0
  93. package/dist/src/validation/yaml-validator.js +177 -0
  94. package/dist/src/validation/yaml-validator.js.map +1 -0
  95. package/package.json +5 -3
@@ -1,371 +1,232 @@
1
1
  ---
2
2
  name: kol-outreach
3
- description: "KOL 建联全自动化:从 TikTok 发现潜在 KOL、提取 bio email、发送个性化 outreach、自动谈判价格与条款。当用户说'帮我找 KOL'、'{产品} KOL 建联'、'KOL 进展如何',或收到系统触发的 [KOL_INBOUND:...] 或 [KOL_INBOUND] 标记时,使用此 skill。"
3
+ description: |
4
+ KOL outreach automation: discover TikTok KOLs, extract emails, send personalized outreach, handle replies, negotiate deals.
5
+ Trigger: user says '帮我找 KOL', 'KOL 建联', 'KOL 进展', or message contains [KOL_INBOUND:{campaignId}] / [KOL_INBOUND].
4
6
  ---
5
7
 
6
- # KOL Outreach Skill
8
+ # KOL 建联
7
9
 
8
- skill 帮助商家在 TikTok 上端到端完成 KOL 建联:发现 → 触达 → 谈判 →
9
- 意向交付,运行时零打扰,所有决策参数在首次 onboarding 时固化。
10
+ 端到端 TikTok KOL 建联自动化:发现、触达、谈判、成交。
10
11
 
11
- ## 核心工作目录
12
+ ## 核心:~/kol-outreach/ 工作目录
12
13
 
13
- 所有状态都在 `~/kol-outreach/`,整个目录是一个 git repo。结构:
14
+ 所有状态都在 `~/kol-outreach/`(单个 git repo)。每次会话只做一件事。
14
15
 
15
- ```
16
- ~/kol-outreach/
17
- ├── BRAND.md # 商家品牌素材(跨 campaign)
18
- ├── MERCHANT_LIMITS.md # 跨 campaign 发信速率
19
- ├── CAMPAIGNS.md # campaign 索引
20
- ├── PROGRESS.md # 商家级里程碑日志
21
- └── campaigns/{campaignId}/
22
- ├── CONFIG.md # 本 campaign 的商品/预算/条款
23
- ├── KOLS.md # 本 campaign 的 KOL 索引
24
- ├── TEMPLATES.md # 邮件模板(4 类)
25
- ├── PROGRESS.md # 本 campaign 的事件日志
26
- ├── CONVERSATIONS/{username}.md # 每个 KOL 的对话历史
27
- └── assets/ # 商品图/素材
28
- ```
29
-
30
- ## 每次 session 的启动流程
31
-
32
- 按顺序执行这些检查,决定本次 session 的动作:
33
-
34
- 1. **检测系统触发**:用户消息是否包含 `[KOL_INBOUND:{campaignId}]` 或 `[KOL_INBOUND]`(无 campaignId)标记?
35
- - 是 → 走「KOL 回信处理」流程(见下)
36
- - 否 → 继续步骤 2
37
- 2. **检测首次使用**:`ls ~/kol-outreach/BRAND.md` 是否存在?
38
- - 不存在 → 走「Merchant onboarding」→ 接着走「Campaign onboarding」
39
- - 存在 → 继续步骤 3
40
- 3. **根据用户意图**分流:
41
- - 用户说"给 {新产品} 建联" → 走「Campaign onboarding」创建新 campaign
42
- - 用户说"开始建联 {campaign}" / "发送 outreach" → 走「发现 + 首发 outreach」
43
- - 用户说"KOL 进展" / "查看状态" → 走「查询进度」
44
-
45
- **单会话单任务原则**:每次 session 只执行一种操作,完成后 git commit 并
46
- 更新 PROGRESS.md。出错时可以精准回滚。
47
-
48
- ---
16
+ **每次会话启动**:
49
17
 
50
- ## 流程 A:Merchant onboarding(只发生一次)
18
+ 1. 检查 `~/kol-outreach/BRAND.md` 是否存在且已完整填写 — 不存在或未完成则走初始化
19
+ 2. 读 `CAMPAIGNS.md` — 了解所有 campaign 状态
20
+ 3. `git -C ~/kol-outreach log --oneline -5` — 最近变更
21
+ 4. 根据用户意图 + 文件状态决定本次操作
22
+
23
+ | 文件 | 层级 | 用途 |
24
+ |------|------|------|
25
+ | `BRAND.md` | merchant | 品牌素材、兜底话术、升级关键词 |
26
+ | `MERCHANT_LIMITS.md` | merchant | 日发信配额(跨 campaign 共享) |
27
+ | `CAMPAIGNS.md` | merchant | campaign 索引 |
28
+ | `PROGRESS.md` | merchant | 跨 campaign 关键事件 |
29
+ | `campaigns/{id}/CONFIG.md` | campaign | 商品、预算、条款、运行参数 |
30
+ | `campaigns/{id}/KOLS.md` | campaign | KOL 状态索引(status: `pending_outreach` / `awaiting_reply` / `in_progress` / `deal_reached` / `needs_merchant_followup` / `rejected` / `expired` / `send_error` / `no_contact`) |
31
+ | `campaigns/{id}/TEMPLATES.md` | campaign | 邮件模板(LLM 只填变量) |
32
+ | `campaigns/{id}/PROGRESS.md` | campaign | 本 campaign 事件日志 |
33
+ | `campaigns/{id}/CONVERSATIONS/{username}.md` | campaign | 每个 KOL 的完整对话 |
51
34
 
52
- 1. 创建工作目录:
35
+ ## Session 路由
53
36
 
54
- ```bash
55
- mkdir -p ~/kol-outreach/campaigns
56
- cp -r $CLAUDE_SKILL_DIR/template/merchant/. ~/kol-outreach/
57
- cd ~/kol-outreach && git init && git add -A && git commit -m "init merchant workspace"
58
- ```
59
-
60
- 2. 会话式填 `BRAND.md`:
61
- - 询问 brand_name
62
- - brand_name 生成 brand_slug 建议(kebab-case)
63
- - 调 `scout outreach brand-slug check {slug}` 验证唯一性
64
- - 可用 确认
65
- - 被占用 → 使用 API 返回的 suggestions 让商家选或自己改
66
- - 成功后调 `scout outreach brand-slug claim` 锁定
67
- - 依次询问 brand_website / brand_social / default_contact_name / default_brand_pitch
68
- - 给出默认的兜底话术模板,让商家确认或改
69
- - 给出默认的双语 escalation_keywords,让商家确认或扩展
70
- - Edit `BRAND.md`
71
- - `git add BRAND.md && git commit -m "setup BRAND.md"`
72
-
73
- 3. 会话式填 `MERCHANT_LIMITS.md`:
74
- - 默认 `max_daily_sends: 50`,询问商家是否调整
75
- - 初始化 `sent_today: 0`,`date_utc: $(date -u +%Y-%m-%d)`
76
- - `git commit -m "setup MERCHANT_LIMITS.md"`
37
+ 按优先级依次判断:
38
+
39
+ | 条件 | 流程 |
40
+ |------|------|
41
+ | 消息含 `[KOL_INBOUND:{campaignId}]` 或 `[KOL_INBOUND]` | → 回信处理 |
42
+ | `~/kol-outreach/BRAND.md` 不存在 | → 商家初始化 → 创建 Campaign |
43
+ | 用户提到了新商品 + 该商品无对应 campaign | → 创建 Campaign |
44
+ | 用户要求发送 outreach / 开始建联 | → 发现 + Outreach |
45
+ | 用户问进展 / 状态 / 数据 | → 查询进度 |
46
+
47
+ **Campaign 定位**:用户指定了 campaign 用指定值;未指定时读 `CAMPAIGNS.md`——只有一个 active 就用它,多个则列出让商家选。
48
+
49
+ ## 商家初始化
50
+
51
+ 只在 `~/kol-outreach/BRAND.md` 不存在或未完整填写时执行。
52
+
53
+ 1. `cp -r $CLAUDE_SKILL_DIR/template/merchant/. ~/kol-outreach/` 并 `git init`
54
+ 2. 对话式填写 `BRAND.md`:
55
+ - `brand_name` → 生成 `brand_slug` 建议 → `scout outreach brand-slug check` 验证全局唯一 → 冲突则换 → `scout outreach brand-slug claim` 锁定
56
+ - 填完品牌基础、兜底话术、升级关键词(中英双语必填)、禁用词
57
+ 3. 填写 `MERCHANT_LIMITS.md`(默认 `max_daily_sends: 50`)
58
+ 4. `git add -A && git commit -m "merchant onboarding complete"`
59
+ 5. 继续创建第一个 campaign
60
+
61
+ ## 创建 Campaign
62
+
63
+ 1. 确定 `campaignId`:从商品标题生成 kebab-case slug,校验唯一且匹配 `^[a-z0-9][a-z0-9-]{0,29}$`
64
+ 2. `cp -r $CLAUDE_SKILL_DIR/template/campaign/. ~/kol-outreach/campaigns/{campaignId}/`
65
+ 3. 对话式填写 `CONFIG.md`,**逐段确认**:
66
+ - 商品信息 → KOL 画像 → 预算报价 → 合作条款 → 运行参数
67
+ - 预算校验:`max_price >= target_price >= first_offer`,`max_step <= max_price - first_offer`,`total_budget >= max_price`
68
+ - 不通过则说明原因、给建议、重新问(不退出)
69
+ 4. 更新 `CAMPAIGNS.md` 追加新行
70
+ 5. `git add -A && git commit -m "[{campaignId}] campaign ready"`
71
+ 6. 问商家是否立即开始发现 KOL
72
+
73
+ ## 发现 KOL + 首发 Outreach
74
+
75
+ **前置检查**(任一不过则告知商家并退出):
76
+ - campaign 状态为 `active`
77
+ - 本 campaign 在谈 KOL 数(`awaiting_reply` + `in_progress`)< `max_concurrent_kols`
78
+ - merchant 今日发信数(`MERCHANT_LIMITS.md`)< `max_daily_sends`
79
+ - UTC 跨天时先归档昨天计数、重置 `sent_today=0`
80
+
81
+ **发现**:
82
+
83
+ 1. `scout tiktok creators --follower-count <range> --country <region> --limit <N> --format json`
84
+ 2. 过滤:`engagement < 2%` 丢弃、已在 `KOLS.md` 丢弃、粉丝量不在区间丢弃
85
+ 3. 对每个候选:`scout tiktok creator-products @username --limit 5`,从 bio 提取 email
86
+ 4. 写入 `KOLS.md`:有 email → `pending_outreach`,无 email → `no_contact`
87
+ 5. `git commit -m "[{campaignId}] discovered N KOLs"`
88
+
89
+ **Outreach**:
90
+
91
+ 对每个 `pending_outreach` KOL(不超过本轮发送量):
92
+
93
+ 1. 读 `TEMPLATES.md` 的 `first_outreach_v1`
94
+ 2. 产出模板变量 JSON(`kol_first_name`, `opener_line`, `fit_reason`, `product_one_liner`)
95
+ 3. 渲染模板 → 终检(见「护栏」Layer 4)→ `scout outreach send`,JSON 必须包含:
96
+ `to`, `subject`, `body`, `from_merchant_id`, `from_campaign_id`, `from_brand_slug`, `from_brand_name`, `from_contact_name`
97
+ 4. 发送成功(`ok: true`)→ 写 `CONVERSATIONS/{username}.md`(Round 1)+ 更新 `KOLS.md` → `awaiting_reply` + `MERCHANT_LIMITS.md` sent_today +1
98
+ 5. 发送失败(`ok: false`)→ 标 `send_error`,不计入 sent_today,跳过该 KOL,不中断其他
99
+
100
+ 全部完成后:`git commit -m "[{campaignId}] outreach sent to N KOLs"` + 更新 PROGRESS.md
101
+
102
+ ## 处理 KOL 回信
103
+
104
+ 触发:消息含 `[KOL_INBOUND:{campaignId}]` 或 `[KOL_INBOUND]`。
105
+
106
+ 1. 从消息中提取 `From`, `Subject`, `Message-ID`, `In-Reply-To`, `Body`
107
+ 2. 定位 KOL:
108
+ - `campaignId` 已知 → 读该 campaign 的 `KOLS.md`,按 `from_email` 匹配
109
+ - `campaignId` 未知 → 遍历所有 `campaigns/*/KOLS.md`,按 `from_email` 匹配,多个取 `last_update` 最新
110
+ - 无匹配 → 写 PROGRESS.md + `sentinel report --condition-met false` + 退出
111
+ 3. 读 `CONFIG.md` + `BRAND.md` + `CONVERSATIONS/{username}.md`
112
+ 4. **预检**(见「护栏」Layer 2)→ 任一触发则发 `escalate_fallback_v1` 并退出
113
+ 5. 按「谈判策略」产出结构化决策 JSON
114
+ 6. **后验**(见「护栏」Layer 3)→ 不通过则降级为 escalate
115
+ 7. 渲染模板 → **终检**(见「护栏」Layer 4)→ `scout outreach send`
116
+ - 回信必须带 `in_reply_to`(KOL 的 `message_id`)和 `references`(`CONVERSATIONS` 里的完整 message-id chain)
117
+ 8. 写回 `CONVERSATIONS/{username}.md` + `KOLS.md` + `MERCHANT_LIMITS.md`
118
+ 9. 如果 `accept` → 发 `deal_confirmation_v1`,标 `deal_reached`
119
+ 10. `git commit -m "[{campaignId}] round N {action} @{username}"`
120
+ 11. `sentinel report --condition-met true --summary "..." --action-taken "..."`
121
+
122
+ ## 查询进度
123
+
124
+ - **多 campaign**:读每个 campaign 的 `KOLS.md`,按 status 分桶统计,输出 markdown 表格 + merchant 级 `sent_today` 信息
125
+ - **单 campaign**:读 `KOLS.md` 全表 + `PROGRESS.md` 最近 5 条。对每个 `needs_merchant_followup`,读 `CONVERSATIONS/{username}.md` 最后一轮,总结 KOL 诉求
126
+ - 只读不写,不 commit
127
+
128
+ ## 谈判策略
129
+
130
+ 处理 KOL 回信时,产出一个结构化决策 JSON:
131
+
132
+ ```json
133
+ {
134
+ "action": "counter_offer | accept | decline | clarify | escalate",
135
+ "new_price": null,
136
+ "final_price": null,
137
+ "concessions": [],
138
+ "template_id": "...",
139
+ "template_vars": {},
140
+ "reasoning": "..."
141
+ }
142
+ ```
77
143
 
78
- 4. 告诉商家:商家级配置完成,现在为第一个商品创建 campaign,进入流程 B。
144
+ **决策框架**:
79
145
 
80
- ---
146
+ | KOL 回应 | 判断 | action |
147
+ |----------|------|--------|
148
+ | KOL 报价 ≤ `first_offer` | 低于预期,直接接受 | `accept` |
149
+ | KOL 报价 ≤ `target_price` | 在目标范围内,可接受 | `accept` |
150
+ | KOL 报价 ≤ `max_price` | 超出目标但可谈,渐进加价 | `counter_offer` |
151
+ | KOL 报价 > `max_price` | 超预算,回一次低价试探 | `counter_offer`(报 `target_price`) |
152
+ | KOL 连续 2 轮报价 > `max_price` | 已试探过,差距太大 | `decline` |
153
+ | KOL 对之前的 counter 再次还价,且 ≤ `max_price` | 拉锯中,评估是否值得让步 | `counter_offer` 或 `accept` |
154
+ | KOL 明确拒绝合作 | 尊重意愿 | `decline` |
155
+ | KOL 问非价格问题(产品细节、时间线等) | 信息补充 | `clarify` |
156
+ | KOL 提到超出授权范围的话题 | 不要硬答 | `escalate` |
81
157
 
82
- ## 流程 B:Campaign onboarding(每次新建 campaign)
158
+ **出价规则**:
83
159
 
84
- 1. 确定 campaignId:
85
- - 询问商家要为哪个商品建联(可列出他的 Optima 店铺商品作为选项)
86
- - 从商品标题生成 kebab-case slug 建议("Titanium Travel Tumbler" → "titanium-tumbler")
87
- - 校验:与 `CAMPAIGNS.md` 现有 campaign 不重复;匹配 `^[a-z0-9][a-z0-9-]{0,29}$`
88
- - 告诉商家建议值,让他确认或改
160
+ - 首次 counter:从 `first_offer` 开始
161
+ - 每轮加价不超过 `max_step`
162
+ - 绝不超过 `max_price`
163
+ - concession 时优先用 `allowed_uses` 里的授权项(转发、二创等)作为附加价值,而非直接加钱
164
+ - 本 campaign 已承诺预算(`deal_reached` 的 offer 总和)+ 本次报价不能超 `total_budget`
89
165
 
90
- 2. 创建 campaign 目录:
166
+ **tone**:
91
167
 
92
- ```bash
93
- mkdir -p ~/kol-outreach/campaigns/{campaignId}
94
- cp -r $CLAUDE_SKILL_DIR/template/campaign/. ~/kol-outreach/campaigns/{campaignId}/
95
- cd ~/kol-outreach && git add -A && git commit -m "init campaign {campaignId}"
96
- ```
168
+ - 专业友好,不卑不亢
169
+ - 不要用 "unfortunately" / "sadly" — 直接说 "our budget is..."
170
+ - 每封回信开头用一句话承接 KOL 上一封的内容(`acknowledge_line`)
171
+ - 永远不要透露 `max_price`、`total_budget`、或谈判策略
97
172
 
98
- 3. 分段会话式填 campaign `CONFIG.md`:
99
- - 3.1 Campaign 元信息(填 campaignId、创建时间、status=active)
100
- - 3.2 商品段(ID/URL/标题/卖点/受众/样品)
101
- - 3.3 目标 KOL 画像
102
- - 3.4 预算与报价
103
- - **校验**:`max_price >= target_price >= first_offer`、
104
- `max_step <= max_price - first_offer`、`total_budget >= max_price`
105
- - 任一不过 → 说明原因 + 建议调整 + 重新问,不要跳过
106
- - 3.5 合作条款边界(保留时长/使用授权/付款节点/样品政策)
107
- - 3.6 兜底话术覆写(默认留空继承 BRAND.md)+ 运行参数(max_rounds/max_days/max_concurrent_kols/覆写的 escalation_keywords/forbidden_phrases)
108
- - 每段填完 → Edit CONFIG.md → `git commit -m "[{campaignId}] setup CONFIG section N"`
173
+ ## 护栏
109
174
 
110
- 4. 追加 `CAMPAIGNS.md`(新增一行,status=active)
175
+ 四层防线。任一层失败都**不问商家**——自动降级为发兜底话术 + 标记 `needs_merchant_followup`。
111
176
 
112
- 5. `git commit -m "[{campaignId}] campaign ready"`
177
+ ### Layer 1 Onboarding 校验
113
178
 
114
- 6. 告诉商家 campaign 已就绪,问他是否立即开始发现 KOL。
179
+ 写 `BRAND.md` / `CONFIG.md` 时执行:
180
+ - 所有必填字段非空
181
+ - `brand_slug` 全局唯一(通过 `scout outreach brand-slug check`)
182
+ - `max_price >= target_price >= first_offer`
183
+ - `max_step <= max_price - first_offer`
184
+ - `total_budget >= max_price`
185
+ - `escalation_keywords` 中英双语都有
186
+ - 不通过 → 继续追问,不退出 onboarding
115
187
 
116
- ---
188
+ ### Layer 2 — 回信预检(调 LLM 前)
117
189
 
118
- ## 流程 C:发现 KOL + 首发 Outreach
119
-
120
- 触发:用户说"给 {campaign} 开始建联" / "为 {campaign} 发送 outreach"。
121
- 如果商家没指定 campaign,读 `CAMPAIGNS.md`:
122
- - 只有一个 active → 用它
123
- - 多个 active → 列出让商家选
124
-
125
- 1. 读配置:
126
- - `CONFIG.md`(目标画像/运行参数/预算)
127
- - `KOLS.md`(已存在 KOL 列表)
128
- - `BRAND.md`(后面生成 outreach 需要)
129
- - `MERCHANT_LIMITS.md`(今日发信数)
130
-
131
- 2. 并行上限预检:
132
- - `in_flight = KOLS.md 中 status ∈ {awaiting_reply, in_progress} 的行数`
133
- - `available_slots = max_concurrent_kols - in_flight`
134
- - `available_slots <= 0` → 告诉商家"当前 {in_flight} 个 KOL 在谈,已达并行上限。等 KOL 进入终态后再继续。"退出
135
- - `target_new = min(CONFIG.target_kols_this_round, available_slots)`
136
-
137
- 3. 日发信上限预检(merchant 级):
138
- - 读 MERCHANT_LIMITS.md 的 sent_today 和 max_daily_sends
139
- - 如果当前 UTC 日期 ≠ MERCHANT_LIMITS.md 的 date_utc:归档昨天计数到历史表,sent_today=0,写回
140
- - `daily_budget = max_daily_sends - sent_today`
141
- - `daily_budget <= 0` → 告诉商家"今日所有 campaign 合计发信配额已用完",退出
142
- - `本轮发送量 = min(target_new, daily_budget)`
143
-
144
- 4. 调 `scout tiktok creators` 发现 KOL:
145
-
146
- ```bash
147
- scout tiktok creators \
148
- --follower-count "<CONFIG.powder_count_range>" \
149
- --country "<CONFIG.region>" \
150
- --limit <本轮发送量 * 3> \
151
- --format json
152
- ```
153
-
154
- (×3 给过滤留空间)
155
-
156
- 5. 过滤:
157
- - engagement_rate < 2% 丢弃
158
- - 已在 KOLS.md 的 username 丢弃
159
- - 粉丝量不在目标区间的丢弃
160
-
161
- 6. 对每个候选 KOL:
162
- - 调 `scout tiktok creator-products @username --limit 5` 获取最近视频(用于生成开场白)
163
- - 如果 scout 的 creators 响应 **没有返回 bio**,也从这里拉(见 probe Task 0.2)
164
- - 用正则从 bio 抠 email:
165
-
166
- ```bash
167
- python3 -c "import re, sys; print(re.findall(r'[\\w.+-]+@[\\w-]+\\.[\\w.-]+', sys.stdin.read())[0] if re.search(r'[\\w.+-]+@[\\w-]+\\.[\\w.-]+', sys.stdin.read()) else '')" <<< "$BIO"
168
- ```
169
-
170
- - 有 email → 追加到 KOLS.md,status=pending_outreach
171
- - 没 email → 追加到 KOLS.md,status=no_contact
172
- - `git commit -m "[{campaignId}] discovered N KOLs"`
173
-
174
- 7. 对每个 status=pending_outreach 的 KOL(限制本轮发送量):
175
- - 读 `TEMPLATES.md` 的 first_outreach_v1 骨架
176
- - **LLM 决策**(结构化 JSON 输出):
177
- - 输入:CONFIG 商品段 + BRAND.md(覆盖后) + KOL 最近视频 + template 变量列表
178
- - 输出:
179
- ```json
180
- {
181
- "kol_first_name": "...",
182
- "opener_line": "...",
183
- "fit_reason": "...",
184
- "product_one_liner": "..."
185
- }
186
- ```
187
- - 你只决定变量值,**不要直接写 email body**
188
- - 模板渲染:把变量代入 first_outreach_v1 → 最终 body
189
- - **Layer 4 内容终检**:
190
- - body 不含 CONFIG.forbidden_phrases(fallback 到 BRAND.md default)
191
- - body 长度 ≤ 2000 字符
192
- - 任一不过 → 跳过这个 KOL,写 KOLS.md status=send_error,记 PROGRESS.md
193
- - Edit `CONVERSATIONS/{username}.md` 追加 Round 1 (outreach sent) 段
194
- - 调 `scout outreach send`:
195
-
196
- ```bash
197
- echo '{
198
- "to": "kol_email",
199
- "subject": "Collaboration opportunity with <brand_name>",
200
- "body": "<rendered body>",
201
- "from_merchant_id": "<merchantId>",
202
- "from_campaign_id": "<campaignId>",
203
- "from_brand_slug": "<BRAND.brand_slug>",
204
- "from_brand_name": "<BRAND.brand_name>",
205
- "from_contact_name": "<effective contact_name>"
206
- }' | scout outreach send
207
- ```
208
-
209
- - 解析响应,拿 message_id 写回 CONVERSATIONS/{username}.md 的 Message-ID chain
210
- - Edit KOLS.md:status: pending_outreach → awaiting_reply,offer=first_offer
211
- - 读 MERCHANT_LIMITS.md,sent_today += 1,写回
212
- - `git commit -m "[{campaignId}] outreach sent to @{username}"`
213
-
214
- 8. 追加本 campaign 的 PROGRESS.md;重要事件同步追加 merchant PROGRESS.md
215
-
216
- 9. 告诉商家本次发送结果。
190
+ - `rounds >= max_rounds` `needs_merchant_followup`
191
+ - `now - started >= max_days` → 标 `expired`,**不发邮件**,直接退出
192
+ - KOL 回信命中 `escalation_keywords`(中英双语匹配) `escalate_fallback_v1`
193
+ - `in_reply_to` 与 conversation 最后一条 inbound 的 `message_id` 不一致 → 发 `escalate_fallback_v1`
217
194
 
218
- ---
195
+ ### Layer 3 — 决策后验(产出 JSON 后)
219
196
 
220
- ## 流程 D:KOL 回信处理(由 `[KOL_INBOUND:{campaignId}]` 或 `[KOL_INBOUND]` 触发)
221
-
222
- 1. 解析 prompt:
223
- - 如果包含 `[KOL_INBOUND:{campaignId}]` 直接知道 campaignId,同时抠 email metadata(From / Subject / Message-ID / In-Reply-To / References / Body)
224
- - 如果包含 `[KOL_INBOUND]` + `merchant_id: {X}` → campaignId 未知,抠 merchant_id 和 email metadata;campaignId 在步骤 3 中搜索确定
225
- (`merchant_id` 仅用于日志和审计。campaign 查找基于 `~/kol-outreach/campaigns/*/KOLS.md` 遍历,因为工作目录已隐含了 merchant 身份。)
226
-
227
- 2. 校验 campaign 存在(仅 campaignId 已知时):
228
- - `ls ~/kol-outreach/campaigns/{campaignId}/CONFIG.md`
229
- - 不存在 → 写 merchant PROGRESS.md 异常记录 + `sentinel report --condition-met false --summary "unknown campaign"` + 退出
230
-
231
- 3. 按 from_email 查找 KOL:
232
- - **campaignId 已知**(from step 1):只读该 campaign 的 KOLS.md
233
- - 找到 → 继续步骤 4
234
- - 找不到 → orphan_inbound:写 campaign PROGRESS.md + sentinel report false + 退出
235
- - **campaignId 未知**(fallback):遍历 `~/kol-outreach/campaigns/*/KOLS.md`
236
- - 唯一匹配 → 确定 campaignId,继续步骤 4
237
- - 多个匹配 → 选 **KOLS.md 中该 email 对应条目的 `last_update` 字段**最新的那个 campaign
238
- - 无匹配 → orphan_inbound:写 merchant PROGRESS.md + sentinel report false + 退出
239
-
240
- ※ 同一 campaign 内如果同一 email 出现多条记录(例如 KOL 换 username 后被重新添加),取 status 非终态(非 rejected / expired / no_contact)的活跃记录。
241
-
242
- 4. 读本 campaign 的 `CONFIG.md`、`BRAND.md`、`CONVERSATIONS/{username}.md`
243
-
244
- 5. **Layer 2 预检**(收到回信后、调 LLM 前):
245
- - 当前 KOL `rounds >= CONFIG.max_rounds` → escalate
246
- - 对话时长 `>= CONFIG.max_days` → expired
247
- - KOL 最新回信 body 匹配 escalation_keywords(先查 CONFIG 覆写,再 fallback 到 BRAND 默认) → escalate
248
- - Idempotency:最后一条 inbound round 的 message_id ≠ 本次 in_reply_to → escalate
249
-
250
- 6. **如果 Layer 2 触发**:
251
- - 用 `escalate_fallback_v1` 模板渲染一封兜底回复
252
- - 调 `scout outreach send` 发出去(带 in_reply_to / references)
253
- - 更新 KOLS.md:status → needs_merchant_followup
254
- - git commit
255
- - `sentinel report --condition-met true --summary "layer2 escalate" --action-taken "..."`
256
- - 退出
257
-
258
- 7. **LLM 结构化决策**(每一轮都重新调一次 LLM,全量对话历史放进上下文):
259
- 输入:
260
- - campaign CONFIG.md 全文
261
- - BRAND.md 全文
262
- - CONVERSATIONS/{username}.md 全文
263
- - 最新 KOL 回信
264
- - TEMPLATES.md 里 counter_offer_v1 / deal_confirmation_v1 / escalate_fallback_v1 的变量列表
265
-
266
- 输出(结构化 JSON):
267
- ```json
268
- {
269
- "action": "counter_offer | accept | decline | clarify | escalate",
270
- "new_price": 200,
271
- "final_price": 250,
272
- "concessions": ["video_retention_90d"],
273
- "template_id": "counter_offer_v1",
274
- "template_vars": { },
275
- "reasoning": "..."
276
- }
277
- ```
278
-
279
- 8. **Layer 3 后验**(基于本 campaign CONFIG 的预算和条款,不跨 campaign):
280
- - `action ∈ {counter_offer, accept, decline, clarify, escalate}`
281
- - 如果 `counter_offer`:
282
- - `CONFIG.first_offer ≤ new_price ≤ CONFIG.max_price`
283
- - `new_price - previous_offer ≤ CONFIG.max_step`
284
- - 如果 `accept`:
285
- - `CONFIG.first_offer ≤ final_price ≤ CONFIG.max_price`
286
- - `committed_so_far (本 campaign KOLS.md 中 deal_reached 的 offer 总和) + final_price ≤ CONFIG.total_budget`
287
- - `concessions ⊆ CONFIG.allowed_uses ∪ {'video_retention_N'}`
288
- - `template_id ∈ 本 campaign TEMPLATES.md 的模板列表`
289
- - 任一不过 → 降级为 escalate(走 Layer 2 失败分支)
290
-
291
- 9. 模板渲染:套对应模板 + template_vars → 最终 body
292
-
293
- 10. **Layer 4 终检**:
294
- - body 不含 forbidden_phrases(CONFIG 覆写优先,fallback BRAND)
295
- - body 长度 ≤ 2000 字符
296
- - 不过 → 降级为 escalate
297
-
298
- 11. Edit `CONVERSATIONS/{username}.md` 追加 Round N (response sent),包括完整 LLM decision JSON 供审计
299
-
300
- 12. 调 `scout outreach send`:
301
- - 带 `in_reply_to = KOL 的 message_id`
302
- - 带 `references = 原 references 追加 KOL 的 message_id`
303
- - 拿到新 message_id 写回 CONVERSATIONS
304
-
305
- 13. Edit `KOLS.md`:更新 rounds、current_offer、last_update、status
306
- - 如果 action == accept:status → deal_reached
307
- - 否则 status → awaiting_reply
308
-
309
- 14. 读 `MERCHANT_LIMITS.md`,sent_today += 1,写回
310
-
311
- 15. 如果 action == accept:
312
- - 额外调 `scout outreach send` 发 deal_confirmation_v1 的 recap 给**商家注册邮箱**
313
- - 在 merchant PROGRESS.md 记录 `[campaignId] DEAL REACHED with @username ($final_price)`
314
-
315
- 16. `git commit -m "[{campaignId}] round N {action} with @{username}"`
316
-
317
- 17. 追加 campaign PROGRESS.md
318
-
319
- 18. `sentinel report --condition-met true --summary "..." --action-taken "..."`
197
+ - `action` 必须在白名单内
198
+ - `counter_offer`: `first_offer <= new_price <= max_price`,`new_price - previous_offer <= max_step`
199
+ - `accept`: `first_offer <= final_price <= max_price`,`committed + final_price <= total_budget`
200
+ - `concessions allowed_terms`
201
+ - `template_id` 存在于 `TEMPLATES.md`
202
+ - 任一不通过 降级 `escalate`,不重试
320
203
 
321
- ---
204
+ ### Layer 4 — 内容终检(发送前)
322
205
 
323
- ## 流程 E:查询进度
206
+ - body 不含 `forbidden_phrases`
207
+ - body 长度 ≤ 2000 字符
208
+ - 不通过 → 不发送,降级 `escalate`
324
209
 
325
- 触发:商家说"KOL 进展如何" / "查看 {X} 状态"。
210
+ ## 允许的命令
326
211
 
327
- 1. `CAMPAIGNS.md`
328
- 2. 解析范围:
329
- - 商家指定 campaign → 只汇报该 campaign
330
- - 未指定 + 只有一个 active → 只汇报该 campaign
331
- - 未指定 + 多个 active → 先汇报跨 campaign 摘要,再提示可说"查看 {X}"
212
+ - `scout tiktok creators` / `scout tiktok creator-products`
213
+ - `scout outreach send` / `scout outreach brand-slug check|claim`
214
+ - `sentinel report`
332
215
 
333
- 3. **跨 campaign 摘要**:
334
- - 对每个 campaign 读 KOLS.md 统计 in_flight / deal_reached / needs_merchant_followup
335
- - 读 CONFIG 的 total_budget,算已承诺预算
336
- - 读 MERCHANT_LIMITS.md 今日 sent
337
- - 汇报表格(markdown)
216
+ ## 常见错误
338
217
 
339
- 4. **单 campaign 详情**:
340
- - 读 KOLS.md 按 status 分桶统计
341
- - 对每个 needs_merchant_followup:读 CONVERSATIONS/{username}.md 最后一轮,总结 KOL 的诉求
342
- - 读 campaign PROGRESS.md 最近 5 条
343
- - 汇报(markdown 表格 + 待办列表)
218
+ 自由撰写邮件正文 ✅ 只产出模板变量 JSON,用 TEMPLATES.md 渲染
344
219
 
345
- ---
220
+ ❌ 一次 session 混做多个流程 → ✅ 单会话单任务
346
221
 
347
- ## 核心规则 / 必须遵守
222
+ 每一小步都 git commit → ✅ 每个逻辑动作完成后 commit 一次(发现、outreach、单个回信处理)
348
223
 
349
- - **git commit 节奏**:每个原子操作后立即 commit(一个 KOL 的一次发送 / 一个 CONFIG 段 / 一次状态更新),不要批量 commit
350
- - **LLM 只填变量,不写 body**:所有对外邮件必须走模板渲染;直接生成 body 的 bypass 护栏
351
- - **运行时零打扰**:任何护栏不过都**不要**问商家,降级为发 escalate_fallback + 标记 needs_merchant_followup
352
- - **单会话单任务**:一次 session 只做一种动作(onboarding / 发 outreach / 处理回信 / 查询),完成后 git commit 并退出
353
- - **文件路径**:所有路径都在 `~/kol-outreach/` 之下;运行前先 `cd ~/kol-outreach`
354
- - **幂等性**:处理回信前检查 `in_reply_to` 是否等于最后一条 outbound 的 message_id;不等则 escalate(防止重复投递)
224
+ 护栏失败后重试 LLM 直接降级为 escalate
355
225
 
356
- ## 错误处理要点
226
+ 透露 max_price / total_budget / 谈判策略 → ✅ 只透露当前报价
357
227
 
358
- | 故障 | 处理 |
359
- |---|---|
360
- | `scout outreach send` 返回 `ok: false` | Edit KOLS.md status=send_error,记 PROGRESS.md,不中断其他 KOL 的处理 |
361
- | LLM 结构化输出格式错误 | 重试 1 次;仍失败降级为 escalate |
362
- | git commit 失败 | 说明冲突原因,修复后重试;不要擅自 reset |
363
- | 当前 UTC 日期跨天 | 读 MERCHANT_LIMITS.md 时先归档昨天计数再继续 |
228
+ expired KOL 还发邮件 → ✅ 标 expired 直接退出,不发信
364
229
 
365
- ## 相关工具
230
+ 修改不属于当前任务的 campaign 状态 → ✅ 只操作当前 campaign
366
231
 
367
- - `scout tiktok creators` 发现 KOL
368
- - `scout tiktok creator-products` — 获取 KOL 最近内容用于个性化
369
- - `scout outreach send` — 发送邮件(内部调 Resend)
370
- - `scout outreach brand-slug check|claim` — 验证/锁定 brand_slug
371
- - `sentinel report` — 回信处理结束时汇报结果(仅在 `[KOL_INBOUND]` 触发的会话里)
232
+ scout outreach send 失败就中断全部 ✅ 标 send_error 跳过该 KOL,继续其他
@@ -0,0 +1,60 @@
1
+ ---
2
+ name: linting-the-wiki
3
+ description: "检查知识库健康度。当用户说'检查知识库'、'知识库有什么问题'时使用。"
4
+ ---
5
+
6
+ # Linting The Wiki
7
+
8
+ 检查 wiki 随时间推移后的完整性和可维护性。
9
+
10
+ ## 启动 SOP
11
+
12
+ 1. `ls ~/kb/REGISTRY.md`
13
+ - 不存在 → 告诉用户"还没有知识库,需要先创建一个",引导使用 initializing-kb
14
+ 2. 读 `~/kb/REGISTRY.md` — 获取所有 active 的 KB 列表
15
+ 3. 确定目标 KB:
16
+ - 用户明确指定了 KB slug 或主题 → 直接用
17
+ - 用户说"全部 lint" → 遍历所有 active KB
18
+ - 只有一个 active KB → 用它
19
+ - 多个 active → 列出让用户选
20
+ 4. 读 `~/kb/<slug>/AGENTS.md` 了解该 KB 的契约
21
+
22
+ ## Lint 流程
23
+
24
+ 1. 检查 `~/kb/<slug>/index.md`、关键 wiki 目录和一组有代表性的页面
25
+ 2. 先识别结构性问题,再决定是否动手修改
26
+ 3. 按类型归类问题:链接、覆盖度、过时、矛盾、重复、页面规模或索引问题
27
+ 4. 除非用户要求更大范围修复,否则只应用小而明确的修补
28
+ 5. 清楚报告仍未解决的问题
29
+ 6. 追加 `~/kb/PROGRESS.md`
30
+ 7. `cd ~/kb && git add -A && git commit -m "[<slug>] lint: <发现N个问题,修复M个>"`
31
+
32
+ ## 要检查什么
33
+
34
+ - 没有入链或出链的页面
35
+ - 被反复提及但没有独立页面的实体或概念
36
+ - 本应并入 overview 的 analysis 页面,或反过来的情况
37
+ - 新来源进入后仍未更新的旧综述
38
+ - `index.md` 与实际长期页面之间的漂移
39
+ - 为了清晰起见应被拆分的大页面
40
+ - 由于缺少来源而无法回答、但明显值得继续调查的问题
41
+
42
+ ## 修复边界
43
+
44
+ 安全的 lint 修复通常包括:
45
+
46
+ - 增加缺失链接
47
+ - 更新 `index.md`
48
+ - 澄清过时标签
49
+ - 就地标出矛盾
50
+
51
+ 不要把 lint 变成无边界重写。需要更深层次调整时,明确说出来并停下,除非用户明确要求继续。
52
+
53
+ ## 最小交付标准
54
+
55
+ 一次成功的 lint 至少应留下:
56
+
57
+ - 一份简短但具体的问题清单
58
+ - 已经完成的有边界修复
59
+ - 已修复问题与剩余债务之间的清晰区分
60
+ - 需要通过新来源或 web search 继续补足的空白点