thread-master/backend/docs/scan-placement-plan.md

512 lines
17 KiB
Markdown
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.

# 海巡獲客計畫:知識圖譜 + 雙軌爬取 + 島民交接
> 在既有「背景 Job + 島民交接」上,新增 Topic Knowledge GraphBrave 驅動)與**雙維度 Tag**(相關 + 近期)+ **雙軌海巡**,強化流程 B 的痛點發現、關鍵字精準度與**產品-痛點匹配**驗證。
## 北極星
海巡找到的貼文/留言,**你的產品是否真的解得了那個問題**(可置入、可回覆、可追蹤)。
流程 B 主線:
```text
Brave 知識圖譜擴散(周邊延伸)
→ 衍生雙維度搜尋 tag相關詞 + 近期求助詞)
→ 使用者手動勾選
→ 每個 tag 雙軌爬 Threads相關軌 + 近期軌7d 優先 / 30d 補充)
→ productFitScore 篩選
→ 島民協助撰寫獲客留言
```
## 已拍板決策
| 項目 | 決策 |
|------|------|
| 圖譜深度 | **3 層、範圍廣**:核心 → 成因/症狀 → 相鄰情境 |
| 進海巡的 tag | **使用者手動勾選**(圖譜 UI 多選;島民可 toggle不預設全選 |
| 近期窗口 | **7 天內為重點**;不足時補充至 **30 天**;超過 30 天排除 |
| Brave 預算 | 中等,每輪知識擴展 **1015 次查詢**(不足可 supplemental 1 輪) |
| 痛點 tag 候選 | 圖譜衍生 **≥12 候選**,其中痛點/求助類 **≥8** |
| 流程 A | 保留 `style-8d` 捷徑matrix + 留言收集疊加於 Phase 2 |
---
## 根因診斷(舊系統痛點少、關鍵字不準)
對照舊 Next.js[`lib/ai/prompts/research-map-placement.ts`](../../lib/ai/prompts/research-map-placement.ts)、[`lib/services/scan-tasks.ts`](../../lib/services/scan-tasks.ts)、[`lib/ai/analyze-topic.ts`](../../lib/ai/analyze-topic.ts)
| 現象 | 根因 | 新系統對策 |
|------|------|------------|
| 痛點只抓到 12 個 | Placement 壓 `suggestedTags`**24**`PLACEMENT_QUERY_MAX = 8` | 圖譜衍生 ≥8 痛點 tag + supplemental 補充迴圈 |
| 關鍵字不夠精準 | AI 憑種子詞推測,無外部知識 | Brave `knowledge_expand` 建 TKG節點附 `evidence[]` |
| 只有「最相關」 | Recency 只是加分,無獨立近期軌 | **Tag 層**分 `relevance` / `recency`**Crawl 層**雙軌必跑 |
| 沒有周邊延伸 | Brave 只做 `site:threads.net` | `knowledge_expand` 做領域知識(成因、懷孕、換季…)再衍生 tag |
新後端原則見 [`AGENTS.md`](../AGENTS.md)**複製模式,不複製舊業務**——移植 Playwright/過濾規則,用 Mongo + Job 重建。
---
## 架構總覽
```mermaid
flowchart TB
subgraph input [輸入]
Seed["種子詞"]
ProductBrief["product_brief"]
Persona["人設"]
end
subgraph tkg [知識圖譜]
ExpandJob["expand-graph job"]
BraveK["Brave knowledge_expand"]
TKG["topic_knowledge_graphs"]
end
subgraph derive [Tag衍生]
DeriveFn["deriveSearchTagsFromGraph"]
RelQ["relevanceQueries"]
RecQ["recencyQueries"]
end
subgraph select [使用者選擇]
GraphUI["圖譜 UI 勾選節點/tag"]
end
subgraph scan [海巡]
ScanJob["scan job 每tag雙軌"]
Posts["scan_posts"]
end
subgraph outcome [驗收]
Fit["productFitScore"]
Outreach["outreach + 島民留言"]
end
Seed --> ExpandJob
ProductBrief --> ExpandJob
Persona --> ExpandJob
ExpandJob --> BraveK --> TKG
TKG --> DeriveFn
DeriveFn --> RelQ
DeriveFn --> RecQ
RelQ --> GraphUI
RecQ --> GraphUI
GraphUI --> ScanJob --> Posts --> Fit --> Outreach
```
---
## Tag 產生完整流水線
Tag **不是** AI 一次吐 24 個,而是五段流水線產出:
```mermaid
flowchart LR
S["1 種子詞+brief"] --> A["2 AI核心地圖"]
A --> B["3 Brave knowledge_expand"]
B --> G["4 合成TKG三層"]
G --> D["5 deriveSearchTagsFromGraph"]
D --> R["relevanceQueries"]
D --> C["recencyQueries"]
```
| 步驟 | 做什麼 | 產出 |
|------|--------|------|
| 1 | 讀 `seed_query`、`product_brief`、`target_audience` | 輸入包 |
| 2 | AI 產核心 questions/pillars/exclusions | 研究地圖骨架 |
| 3 | Brave 1015 次**一般網搜**(非 threadsOnly | snippets → 候選節點 |
| 4 | AI 合成 TKGL0/L1/L2+ `productFitScore` + `evidence[]` | `topic_knowledge_graphs` |
| 5 | 每節點壓成 28 字真人搜尋詞,分兩套 | `derivedTags` |
### 雙維度 Tag相關 + 近期都要)
每個圖譜節點衍生:
| 維度 | 用途 | 寫法範例 |
|------|------|----------|
| **`relevanceQueries`** | 相關軌:短詞、高命中 | `敏感肌`、`屏障受損` |
| **`recencyQueries`** | 近期軌:求助語境 + 時間窗 | `敏感肌 請問`、`換季泛紅 推薦` |
- `recencyQueries` 在 Brave `threads_discover` 時加 `after:{7天前日期}`(參考舊 [`scan-web-discover.ts`](../../lib/services/scan-web-discover.ts) `buildPlacementKeywordQueries`
- 候選總量:**≥12 tag**(痛點/求助類 **≥8****使用者勾選後才 crawl**
---
## 痛點 Tag 保底機制
解決「只抓到一兩個痛點」:
```text
expand-graph 完成 → deriveSearchTagsFromGraph
IF 痛點/求助類 tag 數 < 8:
→ supplemental_round最多 1 次Brave +5 查詢)
→ 追加查詢例:{seed} 困擾、{seed} 求助、{L2節點} 請問、{seed} 推薦
→ AI 補節點 + 補 derivedTags
IF 仍 < 8:
→ job 標 warningUI + 島民提示「可重跑 expand 或手動加種子詞」
```
| 指標 | 舊系統 | 新系統 |
|------|--------|--------|
| Placement suggestedTags | 24 | 不沿用此上限 |
| 搜尋任務上限 | 8 | 候選 ≥12實 crawl = 勾選數 |
| 痛點類最低 | 無保證 | **≥8**(含 supplemental |
---
## Topic Knowledge GraphTKG
### Mongo collection`topic_knowledge_graphs`
`persona_id` + `seed_query`
```json
{
"seed": "敏感肌",
"nodes": [
{
"id": "n1",
"label": "敏感肌",
"nodeKind": "pain",
"type": "core",
"layer": 0,
"placementValue": "high",
"productFitScore": 95,
"selectedForScan": false,
"evidence": [],
"derivedTags": {
"relevance": ["敏感肌"],
"recency": ["敏感肌 請問", "敏感肌 推薦"]
}
},
{
"id": "n2",
"label": "懷孕嗅覺敏感",
"nodeKind": "cause",
"type": "cause",
"layer": 2,
"relation": "可能成因",
"placementValue": "medium",
"productFitScore": 40,
"selectedForScan": false,
"evidence": [{ "url": "...", "snippet": "..." }],
"derivedTags": {
"relevance": ["懷孕皮膚癢", "嗅覺敏感"],
"recency": ["懷孕 皮膚 癢 請益"]
}
},
{
"id": "n3",
"label": "屏障修復原理",
"nodeKind": "knowledge",
"type": "mechanism",
"layer": 1,
"productFitScore": 70,
"selectedForScan": false,
"derivedTags": {
"relevance": ["屏障受損"],
"recency": ["屏障受損 怎麼辦"]
}
}
],
"edges": [
{ "from": "n1", "to": "n2", "relation": "可能因" },
{ "from": "n1", "to": "n3", "relation": "機制" }
],
"braveSources": [{ "query": "敏感肌 懷孕 原因", "snippet": "...", "url": "..." }],
"painTagCount": 9,
"generatedAt": 0
}
```
### 三層擴散
```text
L0 核心:敏感肌
L1 直接相關:屏障受損、換季泛紅、刺癢
L2 周邊情境:懷孕荷爾蒙、嗅覺敏感、壓力熬夜、換洗臉產品過敏 …
```
### 節點語意
| 欄位 | 說明 |
|------|------|
| `nodeKind` | `pain`(痛點/求助)、`knowledge`(科普延伸)、`cause`、`symptom` |
| `placementValue` | 建議優先級,**不決定是否海巡** |
| `selectedForScan` | 使用者勾選後 `true`,才進 `scan` payload |
| `productFitScore` | 依 `product_brief`:產品解不解得了 |
| `derivedTags` | `relevance` + `recency` 兩套查詢詞 |
| `evidence[]` | L1/L2 必填Brave snippet 可追溯) |
- `knowledge` 節點:延伸話題/科普靈感,**預設不勾選**;若 snippet 含求助語境可升級為 `pain`
- `knowledge` 不強制進 placement crawl除非使用者勾選且 `productFitScore` 達標
---
## Brave 雙模式
| 模式 | `threadsOnly` | 用途 |
|------|---------------|------|
| `knowledge_expand` | `false` | 建 TKG找成因/周邊/知識 |
| `threads_discover` | `true` | 海巡時找 Threads 貼文 |
### L0/L1 查詢模板plan_queries上限 15/輪)
```text
{seed} 常見原因
{seed} 什麼情況會
{seed} 初期 症狀
{seed} 怎麼改善 困擾
{seed} 求助 推薦
```
### L2 周邊擴散查詢池(從 brief/受眾推導)
```text
{seed} 懷孕 相關
{seed} 壓力 熬夜
{seed} 換產品 過敏
{seed} 與 {受眾場景} 的關係
{L1節點} 原因
{L1節點} 困擾
```
Brave 回傳 title/snippet/url → AI 萃取節點與邊 → 寫入 TKG。實作`internal/library/knowledge/` + Brave adapter`BRAVE_SEARCH_API_KEY`;參考舊 [`lib/services/web-search.ts`](../../lib/services/web-search.ts))。
---
## 完整範例:敏感肌 Walkthrough
**輸入**
- 種子詞:`敏感肌`
- product_brief溫和修護、無香料、適合敏感/屏障受損肌
**Brave knowledge_expand節錄**
| 查詢 | snippet 線索 | 圖譜節點 |
|------|--------------|----------|
| `敏感肌 常見原因` | 屏障受損、過度清潔 | L1 symptom `屏障受損` |
| `敏感肌 懷孕` | 荷爾蒙、嗅覺/皮膚變敏感 | L2 cause `懷孕嗅覺敏感` |
| `換季 皮膚 泛紅` | 季節性刺激 | L1 symptom `換季泛紅` |
**衍生 tag候選勾選前不 crawl**
| 節點 | relevanceQuery | recencyQuery | productFit |
|------|----------------|--------------|------------|
| 敏感肌 | `敏感肌` | `敏感肌 請問` | 95 |
| 屏障受損 | `屏障受損` | `屏障受損 推薦` | 90 |
| 換季泛紅 | `換季泛紅` | `換季泛紅 請問` | 88 |
| 懷孕皮膚癢 | `懷孕皮膚癢` | `懷孕 皮膚 癢 請益` | 視產品而定 |
**使用者**:勾選 productFit 高的 4 個節點(可不勾懷孕若產品不適用)
**startScan**:每個勾選節點的 relevance + recency 詞都跑雙軌
| 軌道 | 行為 | 本例預期 |
|------|------|----------|
| 相關軌 | sort=relevance, limit≈12 | 高互動痛點貼文 |
| 近期軌 | 7d 優先,不足補 30d | 一週內求助帖 |
**合併** → gold / recent / relevant → `productFitScore` → 獲客台 → 島民 `generateOutreachReply` + fill
---
## 近期窗口
| 窗口 | 天數 | 行為 |
|------|------|------|
| **重點** | 7 天內 | 優先爬取、優先顯示、排序最高 |
| **補充** | 830 天 | 7 天內不足時才補,排序較低 |
| **排除** | >30 天 | 不進海巡與獲客清單 |
策略:
1. 每個勾選 tag 的**近期軌**先抓滿 7 天名額
2. 全輪痛點貼文不足目標時,自動放寬至 30 天
3. 獲客台預設篩「7 天內」,可切「含 30 天內補充」
---
## 雙軌海巡Tag + Crawl + UI 三層對齊)
**近期軌不是相關軌的副產品**——每個勾選 tag 的 relevance 與 recency 查詢都**必跑**。
| 層級 | 相關 | 近期 |
|------|------|------|
| **Tag** | `derivedTags.relevance` 短詞高命中 | `derivedTags.recency` 求助語境 + after 日期 |
| **Crawl** | 相關軌 sort=relevance, limit≈12 | 近期軌 7d 滿額 → 30d 補 |
| **UI** | 可篩 `priority=relevant` | 預設 7d + `priority=gold` 置頂 |
合併優先級:
1. 兩軌皆有 → `gold`
2. 僅近期軌 → `recent`
3. 僅相關軌 → `relevant`
過濾:移植 `hasPlacementIntent`、`looksLikeCasualChat`(舊 [`lib/topic-anchor.ts`](../../lib/topic-anchor.ts)、[`lib/scan-recency.ts`](../../lib/scan-recency.ts))。
### scan_posts 擴充欄位
- `placement_score`、`priority`gold/recent/relevant
- `product_fit_score`、`solved_by_product`
- `posted_at`、`search_tag`、`query_dimension`relevance/recency
- `graph_node_id`
- `replies[]`(可選,`scrape_replies: true`
---
## 產品匹配驗收
每篇海巡結果:
- **`productFitScore`**:痛點 vs `product_brief`
- **`solvedByProduct`**:獲客留言是否對應產品能力(生成時強制檢查)
獲客台 UI
- 預設排序7 天內 + 產品能解決
- 標示:可置入 / 需人工 / 超出產品範圍
- 獲客留言:**島民 fill 全文,不自動送出**
---
## 島民交接
### job.result.handoff
```json
{
"handoff": {
"flow": "placement",
"persona_id": "...",
"pain_tag_count": 9,
"summary": "12 候選 tag → 勾選 6 節點 → 38 篇;痛點 10核心 6 + 周邊 47 天內 8 篇",
"pain_breakdown": { "core": 6, "peripheral": 4, "recent_7d": 8 },
"top_peripheral_hits": ["懷孕皮膚癢", "換季泛紅"],
"next_route": "/personas/:id/outreach",
"needs_supplemental_expand": false,
"connection_required": false
}
}
```
JobMonitor → `islanderHandoffStore`[`buildIslanderContext`](../web/src/lib/islander/buildIslanderContext.ts) 注入【近期海巡交接】。
### Custom actions
| Action | 用途 |
|--------|------|
| `expandKnowledgeGraph` | 觸發 `expand-graph``supplemental=true` 補充迴圈 |
| `toggleGraphNode` | 勾選/取消節點 |
| `startScan` | `dual_track=true`,只爬 `selectedForScan` 節點 |
| `generateOutreachReply` | 產獲客留言 |
| `applyDraft` | fill 留言欄位 |
### 對話路徑(流程 B
```text
「幫我找敏感肌的痛點」
→ expand-graphBrave knowledge_expand + AI 合成 TKG
→ IF pain_tag_count < 8 → 島民:「要再補一輪 Brave 嗎?」→ supplemental_round
→ 研究頁:圖譜 + 雙維度 tag + productFitScore
→ 使用者手動勾選節點
→ startScan每詞雙軌相關 + 近期7d/30d
→ outreach → highlight gold/recent → generateOutreachReply + fill
```
---
## Job 模板
| Template | Steps | worker |
|----------|-------|--------|
| `expand-graph` | plan_queries → brave_knowledge → ai_synth → derive_tags → [supplemental?] → persist_tkg | go |
| `scan` | session → crawl_dual_track → replies? → store → filter → ai_fit → persist | node + go |
| `style-8d` | (既有) | node + go |
執行順序:**expand-graph → 勾選 tag → scan**。
---
## API 草案
```text
POST /api/v1/personas/:id/knowledge-graph/expand # ?supplemental=true
GET /api/v1/personas/:id/knowledge-graph
PATCH /api/v1/personas/:id/knowledge-graph/nodes # selectedForScan
POST /api/v1/personas/:id/scan-jobs # graph_id, selected_node_ids, dual_track
GET /api/v1/personas/:id/scan-posts # recent_7d, product_fit_min, priority
POST /api/v1/personas/:id/outreach-drafts/generate
```
Internal worker`POST /workers/scan-posts/batch`、Brave/AI 內部端點。
---
## 前端頁面
| 路徑 | 用途 | 島民 label |
|------|------|------------|
| `/personas/:id/research` | 圖譜、雙維度 tag、勾選 | 加入海巡、Brave 再擴展 |
| `/personas/:id/outreach` | 獲客貼文 + 留言 | 獲客留言、標記已處理 |
| `/personas/:id/matrix` | 流程 APhase 2 | 草稿內容 |
研究頁每節點展示:`relevanceQueries`、`recencyQueries`、`productFitScore`、勾選框、上次命中數。
---
## 實作分期
### Phase 0a — 知識圖譜 + Tag 流水線
- [ ] Go Brave adapter`knowledge_expand` / `threads_discover`
- [ ] `expand-graph` jobplan_queries → brave → ai_synth → derive_tags
- [ ] `supplemental_round`(痛點 tag < 8
- [ ] Mongo `topic_knowledge_graphs` `derivedTags`、`painTagCount`
- [ ] `deriveSearchTagsFromGraph`relevance + recency 雙陣列
- [ ] API expand / get / patch nodes
### Phase 0b — 島民 handoff
- [ ] handoff `pain_tag_count`、`needs_supplemental_expand`
- [ ] JobMonitor bridge
- [ ] custom actions + `ai.islander.system.md` 海巡專章
### Phase 1 — 雙軌 scan + 流程 B
- [ ] Node `crawl_dual_track` tag 相關+近期7d/30d
- [ ] `productFitScore` + outreach UI
- [ ] **驗收**敏感肌 L2懷孕等)→ 候選痛點 tag 8 勾選後貼文痛點 8 7d 5
### Phase 2 — 流程 A
- [ ] matrix + 留言收集 + 島民 fill
### Phase 3 — 自動化
- [ ] job_schedulesBrave 熔斷Meta API 發留言
---
## 風險
| 議題 | 對策 |
|------|------|
| Brave 幻覺 | 節點必須有 `evidence[]` |
| 圖譜跑題 | exclusions + `productFitScore` |
| 查詢爆炸 | Brave 15/supplemental 5衍生 20只爬勾選 |
| 醫療敏感 | `disclaimer`留言不自動發 |
| 周邊節點產品不符 | productFit 預設不勾獲客台標 |
---
## 參考
- 舊海巡[`lib/services/scan.ts`](../../lib/services/scan.ts)
- 舊網搜[`lib/services/scan-web-discover.ts`](../../lib/services/scan-web-discover.ts)
- 舊研究地圖[`lib/ai/analyze-topic.ts`](../../lib/ai/analyze-topic.ts)
- Job 系統[`docs/job-system-plan.md`](./job-system-plan.md)
- 島民[`internal/library/prompt/files/ai.islander.system.md`](../internal/library/prompt/files/ai.islander.system.md)
- 既有 8D[`worker/style-8d-worker.ts`](../worker/style-8d-worker.ts)