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

263 lines
14 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.

# 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使用者