thread-master/docs/product/scout-topic-quality/plan.md

263 lines
14 KiB
Markdown
Raw Permalink Normal View History

2026-08-13 02:22:24 +00:00
# Plan: 話題海巡品質、分數排序與批次分頁
> Status: `approved`
> Source: `docs/product/scout-topic-quality/spec.md`
> Last updated: `2026-08-10`
## 1. 交付策略
先以自動化案例鎖住現有的話題產詞、網址正規化與其他 Scout 模式,避免新工作覆蓋目前工作樹中的有效修正。接著遵守 Phase B → Phase C 順序,先讓前端與 mock 具備獨立 run、雙層分頁及貼文時間語意再由 goctl 產生後端契約,逐層完成 run persistence、搜尋管線與 worker 狀態。
後端不在原始候選階段截斷,而是每輪完成 canonical 去重、歷史去重與關聯性判定後才計算 target。完成結果以 run 為不可混同的邊界,由後端固定 score 排序並分頁;前端不再載入全量 posts 自行組批次。最後接上 live repository使用同一組 mock/live 契約案例、品質題組及全專案測試完成驗收。
實作期間保留目前未提交的 Scout 改動,不回復、不覆寫與本功能無關的使用者變更。所有後端 HTTP 變更一律先改 `generate/api/*.api` 再執行 `make gen-api`;生成的 handlerroutes 僅由 goctl 維護,業務實作只落在 `internal/logic/**``internal/module/**`
## 2. 里程碑
### M0 — 基線與可重現品質題組
- **目標:**把目前已存在的產詞、關聯性與 shortcode 去重行為固定成回歸基線,並建立本次規格所需的代表題組與分頁 fixtures。
- **完成定義:**
- 現有「鬼滅之刃」、「外包/後端工程師」與網址 alias 案例持續通過。
- 加入至少 30 組人工標註題組的資料格式與首批固定 fixtures題組能標出必要概念群、直接相關與離題候選。
- 建立 score同分時間排序 fixtures涵蓋已知時間、未知時間、相同時間與相同貼文網址別名。
- 記錄開始實作前的 backend Scout 測試、web 單元測試與 build 結果;若既有失敗,與本工作造成的回歸分開列示。
- **建議 task 區間:** T001T004
- **驗證:**
```bash
cd apps/backend
go test ./internal/module/scout/... -count=1
cd ../web
npm test -- src/lib/threadsTerm.test.ts
npm run build
```
### M1 — Phase B前端 repository 契約、mock 與操作行為
- **目標:**先在 mock 模式完成使用者能驗收的 run 清單、批次結果、時間顯示與獨立分頁,不依賴尚未存在的 live API。
- **完成定義:**
- 前端 domain 定義 `ScoutRun`、不足原因與通用 page response`ScoutPost` 增加 run ID。
- Scout repository 增加 `listRuns`、`listRunPosts`、`removeRun`mock 與 live 介面形狀一致;舊 `listPosts` 暫時保留。
- mock 每次執行建立新 run相同 theme 的兩次執行不合併、不搬動舊結果。
- Scout 頁面先由新 repository 讀取批次與單批結果,不再使用全量 `listPosts``runGroups``pageSlice` 作主路徑。
- 批次 pager 預設 10、結果 pager 預設 8兩個頁碼獨立切換批次只重設結果頁。
- 結果列與明細顯示貼文時間未知掃入時間另行標示score 主排序pending 不影響顯示順序。
- 新批次完成只顯示提示,不自動切換目前選取或頁碼;刪除後頁碼收斂到有效頁。
- **建議 task 區間:** T010T018
- **驗證:**
```bash
cd apps/web
npm test -- src/pages/ScoutPage.test.tsx
npm test -- src/data
npm run lint
npm run build
```
### M2 — Phase C 契約地基goctl API 與 domain
- **目標:**依已批准 Spec 建立 run、分頁結果與 scan response 契約,並產生可編譯的 go-zero 程式骨架。
- **完成定義:**
- 在 Scout API 定義 `ScoutRunPublic`、run list、run posts page、run delete、pagination 與 `run_id`
- scan response 同時含 job 與新建 run舊 posts/theme 端點保留相容。
- domain 加入 Run、狀態、計數、不足原因與 guarded transition 所需輸入;所有時間為 UTC unix nanoseconds。
- API 經 `make gen-api` 生成;不手寫 handler 或 routes。
- 生成後 gateway、worker 及既有 Scout logic 可編譯,新增 logic 尚未實作時不得回成功空資料。
- **建議 task 區間:** T020T025
- **驗證:**
```bash
cd apps/backend
make gen-api
go test ./internal/logic/scout/... ./internal/module/scout/... -count=1
go build .
go build ./cmd/worker
```
### M3 — Run persistence、穩定查詢與 legacy 相容
- **目標:**讓 memory 與 Mongo repository 都能保存獨立 run、穩定分頁結果及完整批次統計。
- **完成定義:**
- repository 支援建立 run、guarded 更新狀態統計、owner-scoped run 分頁、單 run posts 分頁及終態 run 刪除。
- run 排序固定為 `created_at DESC, id DESC`
- run posts 依 `score DESC`;同分已知時間優先,再依 `posted_at DESC, created_at DESC, id DESC`
- ownercanonical identity 維持唯一;新掃描遇到歷史貼文不覆寫其 run、時間或 outreach 狀態。
- 批次 total、eligible 與 pending 從完整批次維護,不從目前頁推算。
- legacy posts 以穩定 synthetic run 呈現、分頁及整組刪除;不虛構無法還原的多次執行。
- Mongo 查詢具備符合 owner、run、時間與唯一性的索引memory 與 Mongo contract tests 使用相同案例。
- **建議 task 區間:** T030T037
- **驗證:**
```bash
cd apps/backend
go test ./internal/module/scout/repository/... ./internal/module/scout/usecase/... -count=1
go test ./internal/logic/scout/... -count=1
```
### M4 — 合格數驅動的搜尋、關聯性與原子發佈
- **目標:**把「找得夠多」改成以合格結果計數,同時維持關聯性硬門檻與可診斷的不足原因。
- **完成定義:**
- 候選管線拆成可測的 normalize → canonical dedupe → historical dedupe → relevance/classification → eligible 階段。
- 話題簽章從原始 intent 與批准搜尋詞建立;泛用錨點不算主題證據,必要實體及多概念群不能被廣義詞繞過。
- target 在每階段過濾後計算;不得在 raw hit 數達標時提前 cap。
- 依 initial fan-out、同源加量、單一批准詞的有限 intent 擴展、既有次來源補量;達標立即停止,最多檢視 320 個原始候選。
- 計算 searched、duplicate、irrelevant、eligible、shortfall 與原因;單一來源失敗可降級,全來源失敗則整批失敗。
- 所有模式結果依 score 排序posted_at 僅作同分 tie-breaker軟時效只降分、不淘汰。
- 結果先暫存為該 run 不可見集合,完整成功後才發佈;失敗 run 不暴露半套 results。
- **建議 task 區間:** T040T049
- **驗證:**
```bash
cd apps/backend
go test ./internal/module/scout/usecase/... -count=1
go test ./internal/module/scout/... -run 'Target|Relevance|Dedupe|PostedAt|Run' -count=1
```
### M4 補強 — 真實稽核後的召回與去重修正
首輪品質 gate 通過後,針對「實際結果仍偏少、單一核命中不夠貼題」再做一次資料流稽核:
- 候選只有通過完整關聯性與分類門檻後才佔用 local identity低品質的先到摘要不會鎖死後續較完整摘要。
- 自然語句意圖先抽出真正主題核,不把產生器的所有變體(外包/接案/徵人等)錯誤合併成同時必要條件。
- activity 只有泛用錨點(例如「推薦」)時全部拒絕,不再把錨點當成主題證據。
- 多概念批准詞的 token 不會被誤當成另一個核心的 alias只有單一批准詞才能明確授予短 alias。
- API provider 沒有 offset 時,單一批准詞在同源加量仍重複,會依原始 intent 追加最多四組短查詢;每筆仍經同一個 topic signature gate。
- 批次清單顯示 `created_at`,讓跨頁批次辨識不依賴狀態或結果數。
這些補強不降低關聯性門檻,只改善被錯誤去重與單一 query 第一頁限制造成的召回損失。
### M5 — Jobworker 生命週期與進度
- **目標:**讓 run 與 `scout_scan` job 一對一、狀態 guarded 且失敗可收斂。
- **完成定義:**
- POST scan 先建立 run再排程 jobjob `RefID` 與 immutable payload 都帶同一 run ID。
- 排程失敗時 run guarded 轉 failed不得留下永遠 queued 的批次。
- worker 驗證 RefIDpayloadowner 一致後才執行,依序 guarded 更新 running、succeededfailedcancelled。
- 進度摘要包含搜尋階段與目前 eligibletarget不記錄 token 或敏感 payload。
- job 更新沿用既有 guarded 語意Redis lock value 維持 workerID。
- retry 或重複 claim 不會重複發佈結果、搬動舊貼文或讓終態倒退。
- **建議 task 區間:** T050T055
- **驗證:**
```bash
cd apps/backend
go test ./internal/module/job/... ./internal/module/scout/... -count=1
go test ./cmd/worker -count=1
make test-integration
```
### M6 — Live repository 串接與前後端整合
- **目標:**讓 Phase B 已驗收的操作改用真 APImock 與 live 保持同一契約。
- **完成定義:**
- live repository 正確送出 `page``pageSize`、run ID 與 filters映射 `pagination`、run counters、shortfall reasons、posted_at 與 created_at。
- Scout 主頁只取得目前 run page 與目前 result page不再因 job revision 下載全部 posts。
- 其他仍依賴舊 `listPosts` 的頁面維持相容並通過測試。
- 401404409503 使用既有錯誤顯示,不將失敗轉成成功空列表。
- live contract tests 驗證 URL、query、envelope mapping 與 unix ns 欄位;前端不以 JavaScript number 精度重新排序 ns 時間。
- **建議 task 區間:** T060T066
- **驗證:**
```bash
cd apps/web
npm test -- src/data/live/m5Repos.test.ts src/pages/ScoutPage.test.tsx
npm run lint
npm run build
```
### M7 — 品質門檻、全域回歸與文件收尾
- **目標:**以需求成功標準證明「相關、足量、按時間、可分頁、不重複」均成立。
- **完成定義:**
- 30 組人工標註題組的 precision@10 平均至少 0.8;有足量合格候選的題組至少 90% 達成 target。
- 規格中的 17 個 GivenWhenThen 場景都有自動化證據,或明確對應的人工驗收記錄。
- backend 全測試、integration gate、web 全測試、lint 與 build 通過。
- 檢查 goctl 生成差異,確認沒有手寫 handlerroutes也沒有改動 `old/`
- 更新相關產品文件中的現況描述tasks INDEX 全部反映實際狀態;不把尚未達標項目標成 done。
- **建議 task 區間:** T070T075
- **驗證:**
```bash
cd apps/backend
make test
make test-integration
make build
cd ../web
npm test
npm run lint
npm run build
cd ../..
git diff --check
git status --short
```
## 3. 依賴
```text
M0 → M1 → M2 → M3 → M4 → M5 → M6 → M7
└──────────────→ M6
```
- M0 先建立保護網,所有後續里程碑都依賴它。
- M1 先固定 Phase B 操作與 mock 契約M2 的 API 形狀必須與其一致。
- M3 提供 M4 的歷史去重、暫存/發佈與分頁能力。
- M4 完成後M5 才能正確把搜尋統計與終態寫回 run。
- M6 同時依賴 M2 的 API 契約與 M5 的完整 live 行為。
- M7 只能在所有前後端路徑接通後開始宣告成功標準。
## 4. 風險與緩解
| 風險 | 緩解 |
|------|------|
| 目前工作樹已有未提交的 Scout 修正,生成或重構時可能被覆蓋 | M0 先記錄基線;每個 task 只碰輸出範圍,執行 goctl 後逐檔檢查差異,不回復使用者既有變更。 |
| `theme_key` 曾同時扮演話題與批次,舊資料無法還原每次執行 | 新資料強制 run ID舊資料只建立一個穩定 synthetic run不虛構歷史。 |
| ownercanonical identity 唯一規則可能與既有 permalink IDupsert 行為衝突 | persistence contract tests 先覆蓋「新 run 不覆寫舊 post」在搜尋階段查 seen identity保存端再以唯一約束防競爭。 |
| 多文件結果寫入中途失敗會暴露半套批次 | 以 run 可見狀態隔離暫存結果;列表只讀成功發佈的 membership失敗路徑清理或保留不可見資料供重試。 |
| offset pagination 遇到背景新增資料會漂移 | 完成 run 的 membership 與排序鍵固定;批次列表不自動刷新,提示使用者主動回到最新頁。 |
| unix nanoseconds 超過 JavaScript safe integer前端重排可能不穩 | score同分時間排序在後端完成使用唯一 ID 決勝;前端只格式化與顯示,不再按 ns 數值重排分頁結果。 |
| 高關聯門檻使窄題目無法湊足 target | 三階段有限補量並呈現 shortfall品質門檻優先不降低核心概念要求。 |
| 人工題組過度貼合既有範例 | 30 組涵蓋不同領域並分離固定回歸 fixture 與驗收 fixture避免只對兩個已知查詢最佳化。 |
| 新 run API 破壞 Today 或其他舊呼叫者 | 保留舊 posts endpoint 與 repository 方法直到相依頁面另有批准的遷移計畫M6 明確跑全前端測試。 |
| goctl 生成大量無關差異 | 固定使用專案內 goctl home 與 make target生成後只接受契約導致的差異業務不寫 handlerroutes。 |
## 5. 刻意不做(本 plan 週期內)
- 不改造 Radar 商機判定、CRM 狀態或 Radar 列表。
- 不新增第三個外部搜尋/爬取來源。
- 不降低主題關聯性門檻來湊 target也不做無限補抓。
- 不自動發送 Threads 回覆。
- 不刪除所有歷史 Mongo 重複資料;只確保讀取與新寫入不再重複呈現。
- 不移除舊 `GET /scout/posts` 或強迫其他頁面同時遷移。
- 不重新設計 Harbor Desk 全站視覺或導覽。
- 不在本週期加入個人化學習、互動量趨勢或跨批次分析。
## 6. 全域驗證指令
```bash
cd apps/backend
make gen-api
make test
make test-integration
make build
cd ../web
npm test
npm run lint
npm run build
cd ../..
git diff --check
git status --short
```
需要 Mongo 行為的 repository contract 以可控 test database既有測試容器執行外部搜尋來源一律使用 fake provider不以真實 provider token 或不穩定網路作為 CI 成功條件。
## 7. 批准
- [x] Plan 已批准2026-08-09使用者