# Plan: 商機工作收件匣與產品需求搜尋 > Status: `approved` > Status note: 使用者 2026-08-11「請幫我做」;視為批准 plan 並要求進入實作拆解 > Source: `docs/product/opportunity-inbox/spec.md`(approved 2026-08-11) > Requirements: `docs/product/opportunity-inbox/requirements.md`(approved 2026-08-11) > Baseline: `docs/product/brand-product-radar/` T700–T762 done > Last updated: `2026-08-11` ## 1. 交付策略 1. **保留已完成的產品雷達底座。** Opportunity、ProductMatch、主推產品、owner+來源去重、每日巡邏、mock/live repository 均原地擴充,不另建一套商機或抓取系統。 2. **先簡化操作,再擴大後端。** Phase A 只固定新增資料語意、跨層契約與舊資料映射;Phase B 先用 mock 驗證單一收件匣、完成/移除/復原、時間排序、DemandMap 與成本確認。使用者未確認這套操作前,不進入 Phase C 大量業務後端。 3. **先把免費流程做實,再接付費判定。** ReviewState、tombstone、DemandMap baseline、前處理與輕量排序先獨立可測;搜尋與 AI 深判最後才接入 CostPreview、CreditCeiling、有效判定快取及既有 usage meter。 4. **分數不互相偷換。** 既有 `intent_score` 與 `product_fit_score` 保留,新 `priority_score` 專門服務推薦排序;所有分頁排序由後端穩定執行,前端不只重排當頁。 5. **用漏斗與點數對帳驗收。** 每次巡邏必須能對上 hit → dedupe → prefilter → cache → judge → created/merged/tombstone/deferred,以及 search/judge 實際點數;部分完成不能顯示完整成功。 6. **準確度與成本同時是上線閘門。** 前十至少八筆具有真實需求與產品適配證據,且同候選同版本重試不重扣;任一未達標,先修召回、前處理、排序或冪等,不擴 P1 自動學習。 正式 tasks 每題預估約 100–200 行 production change;測試或文件可較小,但不得把 API、repository、worker、頁面與品質閘塞成單一大題。 ## 2. 里程碑 ### M0 — Phase A:契約、相容與測試地基 - **目標:** 固定 ReviewState、DemandMap、CostPreview、PriorityScore、Sweep funnel 的跨層 shape 與舊資料讀法;尚未完成的能力明確回 not-ready,不回成功空資料。 - **完成定義:** - 盤點並鎖定現有 OpportunityStatus、accept/dismiss、today/opportunities、ProductMatch、Sweep 與 usage 行為,建立不可退化的 baseline tests。 - 只從 `generate/api/radar.api` 擴充 review state、需求地圖、cost preview、列表 query、priority/evidence 與 Sweep funnel 契約,再執行 `make gen-api`;不手寫 handler/routes。 - goctl 產生的新 logic 在業務尚未完成前回明確 not-ready;不得回 `102000` 空物件或空陣列。 - 前端 domain types 與 RadarRepo 加入同一契約;mock/live 的方法簽名一致。 - 建立舊資料 fixture:accepted→completed、dismissed→removed(legacy_unknown)、其他→pending;舊 `sort=score|posted` 映射新排序。 - 定義 owner+source+product+input/map version+judge kind 的判定冪等測試鍵,但此里程碑不實作深判業務。 - **建議 task 區間:** **T800–T806** - **驗證:** spec 驗收 #1、#11、#12、#24 的契約/fixture 層;generated code 無手改;前後端 compile/build。 ### M1 — Phase B:單一商機收件匣(frontend + mock) - **目標:** 先讓第一次使用者只在一個頁面完成「看證據 → 完成或移除 → 查看/復原」;不接真 API。 - **完成定義:** - `/app/radar/opportunities` 成為唯一商機入口,預設 pending+today+recommended;`/app/radar/today` redirect 並保留可理解的 query。 - 導覽移除第二個商機結果入口;today/7d/all 只作為同頁時間範圍。 - 卡片首層只顯示問題、主推產品、一段最強證據、分數/時間、原文、完成、移除;回覆、CRM、其他產品與完整理由放詳情層。 - mock RadarRepo 支援 pending/completed/removed、移除原因、復原前狀態、legacy mapping、tombstone 再命中。 - 支援 recommended/newest/oldest/product_fit/demand_intent 與穩定分頁;未知 posted_at 在 newest/oldest 均置底。 - filter、sort、time scope、page 寫 URL;loading、error、各種 empty reason、操作失敗 rollback 均有明確狀態。 - 完成、移除、復原的 mock 用量保持 0;加入 CRM 成功後自動 completed,但單純完成不建 Contact。 - **建議 task 區間:** **T810–T819** - **驗證:** spec 驗收 #1–#5、#11、#12、#24;頁面 keyboard/mobile 走查;前端 test/build/lint/token check。 ### M2 — Phase B:需求地圖、探索成本與巡邏漏斗(frontend + mock) - **目標:** 在任何付費後端開發前,驗證使用者能看懂「系統準備找什麼、可能花幾點、結果為何被排除或延後」。 - **完成定義:** - `/app/radar/watches` 或其產品詳情提供 DemandMap 五類內容、來源 basis、完整度、AI/user/product origin 與 stale/待確認狀態。 - 免費 baseline、人工新增/啟停 phrase 與版本衝突用 mock 完整走通;產品只有名稱時明確 incomplete 並阻擋新巡邏。 - QueryPlan 摘要顯示 1–6 組使用者語言;純品牌/產品名不能單獨成查詢組。 - AI enrichment、手動 sweep/explore 與回覆按鈕在需要的位置顯示固定點數或範圍;變動成本先開 CostPreview 確認。 - mock CostPreview 覆蓋 platform/BYOK、過期、版本變動、餘額不足與 ceiling 小於固定成本。 - mock Sweep 結果顯示 hit、dedupe、prefilter pass/review/reject、cache、judge、created/merged/tombstone、deferred 與 credit breakdown。 - `complete / partial_budget / blocked_budget / failed` 的文案與下一步可辨識,不用成功空畫面代替。 - **建議 task 區間:** **T820–T829** - **驗證:** spec 驗收 #6、#7、#13–#15、#19、#20、#22;人工走查能在執行前回答最多花幾點,完成後回答點數花在哪裡。 - **硬閘門:** M1+M2 未取得使用者 UX 批准前,不得開始 M3–M6 的業務後端。 ### M3 — Phase C:ReviewState、軟移除與穩定列表 - **目標:** 把 Phase B 已批准的收件匣工作狀態接到可信 domain、repository 與 live API,保持既有業務狀態相容。 - **完成定義:** - Opportunity domain 加 ReviewState、previous state、移除欄位與 review audit;OpportunityStatus 保留原語意。 - memory/Mongo repository 實作 idempotent 狀態更新、append-only audit、duplicate target owner 驗證與 tombstone merge。 - 缺 review 欄位的舊文件讀時映射,不要求全庫回填;首次更新才持久化。 - accept 在 Contact binding 與 completed 之間維持一致成功邊界;舊 dismiss 保存 reason 或 legacy_unknown。 - list 支援 review state、time scope/from-to、新五種 sort、priority band 與 server-side 穩定 pagination;未知時間置底。 - `/today` 使用新列表語意提供相容結果;新 UI 不再依賴 today 專屬商業邏輯。 - migration/索引只加入必要查詢與 audit 支援,保留 owner+來源唯一性。 - **建議 task 區間:** **T830–T839** - **驗證:** domain/memory/Mongo parity、owner isolation、race 與 API integration;spec 驗收 #1–#5、#11、#12、#24。 ### M4 — Phase C:DemandMap、版本與 CostPreview - **目標:** 以產品真相建立可追溯需求地圖,並在 provider 執行前完成成本預估、版本確認與 ceiling gate。 - **完成定義:** - DemandMap domain 定義五類 phrase、basis、origin、enabled、input/map version、ready/incomplete/stale 不變量。 - baseline 只讀 Brand/Product 受眾、痛點、情境、能力、匹配與排除內容,免費建立;不得因開頁或排程自動呼叫 AI。 - input version 只對實質搜尋內容變更;名稱/圖片等顯示修改不改版。產品內容變更後,AI phrases 失效、user phrases 標待確認。 - memory/Mongo repository 支援 current map、舊版本解釋、optimistic version conflict 與 owner 隔離。 - QueryPlan 產生 1–6 組合法短詞與排除訊號,保存使用版本與摘要。 - CostPreview 對 enrichment、manual sweep/explore、批次判定與回覆回固定/範圍、key mode、餘額、期限及依據;取得 preview 不扣點。 - preview 使用時驗證 owner、action、context version、expiry、remaining credits 與 ceiling;不合格時 provider 不被呼叫。 - AI enrichment 走既有 meter 與新 `radar.demand_map` source,結構/basis 驗證成功才切 current map 並 commit;失敗保留舊 map 且不重複扣。 - **建議 task 區間:** **T840–T849** - **驗證:** spec 驗收 #6、#7、#13、#17、#20–#22;usage event 可與 action 對帳;BYOK credits=0。 ### M5 — Phase C:候選前處理、推薦排序與判定快取 - **目標:** 把昂貴 AI 判定縮到真正高價值或不確定候選,並讓推薦排序比產品名/關鍵字命中更貼近產品痛點。 - **完成定義:** - 建立可純測試的 candidate preprocessing:來源正規化/去重、基本有效性、產品排除、provider/廣告/公告/雜訊、DemandEvidence 抽取、產品證據與 pass/review/reject。 - 每個 reject/review 保存 machine reason 與 Sweep counter;reject 不進 AI,不建立高/中/低商機。 - 輕量 ranking 在 provider 呼叫前排序;review 只可低順位或在 ceiling 內抽樣深判,不能直接進 high。 - AI judge 輸出可驗證原文 excerpt、DemandMap/產品 basis、風險與 priority factors;缺欄位、找不到引文或 basis 不存在即失敗。 - `priority_score=45 pain fit + 25 demand intent + 15 evidence quality + 15 freshness`,並落實 high hard gates。 - JudgmentResult 以 owner+source+product+input/map version+kind 唯一;有效 accepted/rejected 都可復用,失敗不進快取。 - 逐筆 usage commit 與結果保存具冪等關聯;同候選跨 watch/query、HTTP 重試或 worker 重跑不重扣。 - 建立口語改寫、純名稱、同業銷售、公告、泛討論、未知時間、重複來源與真正求助的固定 fixture。 - **建議 task 區間:** **T850–T859** - **驗證:** table/fixture/AI structured output/concurrency tests;spec 驗收 #8–#12、#15–#18、#21、#23。 ### M6 — Phase C:巡邏整合、續跑、live UI 與完整點數對帳 - **目標:** 把 DemandMap、前處理、快取、深判與新收件匣接回每日/手動巡邏,完成真實可操作的 P0。 - **完成定義:** - `radar_sweep` 保存 system preview、DemandMap/QueryPlan 版本,按搜尋→前處理→cache→judge→persist 執行。 - API search 成功 0 hit 仍按既有規則計 search;前處理 reject 不計 AI;有效 rejected judge 正常計 AI;無效 AI 結果走失敗退點語意。 - 每完成一筆保存 guarded checkpoint;中斷後只續未完成唯一鍵。Redis lock value 保持 workerID,晚到 worker 不覆寫終態。 - CreditCeiling/方案額度用完後停止新 provider 呼叫,保留已完成結果並寫 deferred;watch 不刪除、不封存。 - removed tombstone 再命中只合併來源,Sweep 增加 tombstone count;不新建、不恢復、不重判。 - Sweep API 與 UI 可對帳所有 funnel counts、status、failed/deferred reason 與 search/demand map/judge/reply credits。 - Phase B 頁面切 live repository,mock/live contract tests 使用同一組 fixtures;loading、not-ready、conflict、quota、partial、empty reason 行為一致。 - 完成跨層流程:產品資料 → DemandMap → preview/ceiling → sweep → prefilter/judge → 收件匣排序 → 完成/移除/復原 → usage 對帳。 - **建議 task 區間:** **T860–T869** - **驗證:** spec 驗收 #1–#22、#24 的 live path;integration、race、restart tests;前後端全套 build/test。 ### M7 — Phase D:品質閘、成本回歸與安全上線 - **目標:** 以代表性資料證明搜尋更貼近產品且成本可控,再收斂相容路徑、文件與既有回歸。 - **完成定義:** - 建立人工標註集與可重跑 evaluation:有效需求、口語同義、產品不適合、provider、廣告、公告、泛討論、stale、unknown time、duplicate。 - recommended 前十至少八筆同時具備 DemandEvidence 與產品證據;provider/公告/純名稱命中不得為 high。 - 比較 baseline 關鍵字排序與新 pipeline 的 precision@10、AI judged ratio、每筆有效商機平均 credits;未達品質或成本閘不宣告完成。 - 多 worker、provider retry、HTTP retry、跨 watch/query、產品僅改名與產品實質改版的防重扣回歸全部通過。 - 回歸 generic radar、ProductMatch、主推產品、回覆、CRM、Scout、Outbox、usage soft caps、BYOK、通知與舊 Opportunity。 - `/today` redirect 與舊 API 相容性保留一個版本週期;文件記錄移除條件,不在本周期直接刪 API。 - 更新 testing/gaps/操作說明,明列 P1:批次操作、每產品 ceiling、移除原因調校、產品成本報表。 - 與 `brand-product-radar` T773–T775 共用 accuracy/race/regression 驗收資產;tasks 階段交叉引用一次實作,不複製兩套 fixture 與指令。 - **建議 task 區間:** **T870–T879** - **驗證:** spec 驗收 #16–#23、requirements 成功標準全表;全 repo test/build/lint/token check 綠。 ## 3. 依賴與硬閘門 ```text brand-product-radar T700–T762(已完成底座) └─► M0 Phase A 契約/相容 └─► M1 Phase B 收件匣 mock └─► M2 Phase B DemandMap/成本 mock ──【使用者 UX 批准】 ├─► M3 Phase C ReviewState/列表 └─► M4 Phase C DemandMap/CostPreview M3 ─┐ M4 ─┴─► M5 Phase C 前處理/排名/快取 └─► M6 Phase C sweep+live ──【P0 live 驗收】 └─► M7 Phase D 品質/成本/回歸 ``` | 關係 | 原因 | |------|------| | baseline → M0 | 新能力沿用現有 ProductMatch、主推產品、去重與 usage,不重做底座 | | M0 → M1/M2 | mock 與 UI 必須使用批准後的共用 shape、legacy mapping 與錯誤語意 | | M1+M2 → M3–M6 | **硬性:** 前端+mock 操作未批准,不大寫業務後端 | | M3 ↔ M4 | 可在 UX gate 後分開進行;兩者只透過已凍結契約相遇,不互相直接依賴 repository | | M3+M4 → M5 | 前處理結果需保存到商機/判定快取,且需要 DemandMap 版本與 basis | | M5 → M6 | worker 不可自己重寫另一套前處理、排序或計費判斷 | | M6 → M7 | accuracy 與成本只能用真 pipeline、真 funnel 語意驗證 | | T773–T775 ↔ M7 | 舊待辦與本計畫最後閘門範圍重疊;正式 tasks 只建立一次共用輸出並同步兩邊狀態 | ## 4. 風險與緩解 | 風險 | 緩解 | |------|------| | ReviewState 與既有 accepted/dismissed 混義 | M0 鎖 legacy mapping;M1 先驗證用語;M3 保持兩個狀態機獨立,accept 只做明確同步 | | 合併頁面反而塞更多資訊 | M1 首層固定七項資訊,其餘進詳情;以第一次使用者走查作 Phase B 硬閘 | | removed 後相同貼文再次出現 | M3 tombstone 沿用 owner+來源唯一鍵;M6 sweep 專測再命中只 merge | | 移除一次就污染後續搜尋 | P0 只保存原因,不立即 AI 調校;P1 批次建議仍需人工確認 | | DemandMap 成為第二份產品真相 | phrase 必須有 basis;baseline 由 Product 建立,AI/user 只作可辨識擴充;產品實質變更會改版 | | 產品改名造成重判重扣 | DemandInputVersion 排除顯示欄位;M4/M7 專測名稱修改不改版 | | 廣召回造成 AI 成本暴增 | M5 先免費前處理與 cache,再在 ceiling 內 shortlist;M7 比較 judged ratio 與每筆有效商機成本 | | 前處理過嚴造成完全找不到 | reject 與 review 分開;review 可低順位保留或抽樣深判;Sweep 保存每階段原因供抽查 | | CostPreview 顯示假精準 | 固定成本顯示確值,候選未知顯示 min/max 與依據;版本或餘額變動即失效重估 | | provider 成功但後續中斷造成雙扣 | M5 有效判定唯一鍵+usage 關聯;M6 逐筆 checkpoint,重跑只續未完成鍵 | | ceiling 競態被多 watch 突破 | owner 當日額度在 provider 前 guarded 檢查;M6/M7 多 worker race 測試 | | server 與前端排序不一致 | M0 共用 enum;M3 server-side stable sort;mock/live contract 使用相同 fixture | | goctl 產生空 logic 被當成功 | M0 每個新 logic 先明確 not-ready;只改 `.api` 後 gen,業務只在 logic/module | | 舊工作樹已有相關修改 | tasks 實作前逐檔比對現況,保留使用者變更;禁止用 reset/checkout 覆蓋,重疊無法安全拆分時停下回報 | ## 5. 刻意不做(本 plan 週期內) - 不實作 P1 的批次完成/移除、每產品 CreditCeiling、移除原因自動調校與產品成本儀表板。 - 不因每次完成或移除即時呼叫 AI,也不自動修改 Product、Brand 或 DemandMap。 - 不提供 Opportunity 不可逆硬刪除,不封鎖作者,不刪除來源內容。 - 不新增 usage meter、不修改 Free/Starter/Pro 售價,不重做 Stripe/BYOK/provider 抽象。 - 不重做 Threads API/crawler、排程器、CRM、Outbox、通知或成果歸因。 - 不用 priority score 取代既有 intent/product fit 歷史欄位。 - 不保證公開發文者有預算、身分真實或一定購買;只判斷公開需求與產品證據。 - 不在本週期移除舊 `/api/v1/radar/today` 或 dismiss API;只提供相容映射與 UI redirect。 ## 6. 驗證策略與指令 ### 6.1 前端里程碑 ```bash cd apps/web npm run test npm run build npm run lint npm run check:tokens ``` ### 6.2 API 契約或 generated code 變更 ```bash cd apps/backend make gen-api go test ./internal/logic/radar/... ./internal/module/radar/... -count=1 make build ``` ### 6.3 Phase C 整合與競態 ```bash cd apps/backend make test-integration go test ./internal/module/radar/... -race -count=1 go test ./... -count=1 ``` ### 6.4 Migration 驗證 只使用明確的獨立測試資料庫,不對預設開發資料庫執行: ```bash cd apps/backend RADAR_INBOX_TEST_MONGO_URL=mongodb://127.0.0.1:27017/thread_master_opportunity_inbox_test MONGO_URL="$RADAR_INBOX_TEST_MONGO_URL" make migrate-up MONGO_URL="$RADAR_INBOX_TEST_MONGO_URL" make init ``` ### 6.5 品質與成本閘 正式 task 需提供固定 evaluation 指令,至少輸出: ```text precision_at_10 high_without_required_evidence provider_or_announcement_in_high ai_judged_ratio credits_per_valid_opportunity duplicate_judgment_or_charge_count ``` 通過條件:`precision_at_10 >= 0.8`、兩種 high 違規數皆為 0、重複判定/扣點數為 0;AI judged ratio 與每筆有效商機 credits 先記 baseline,再由實際成本資料決定 P1 預算優化目標,不在 P0 捏造門檻。 ### 6.6 里程碑驗收摘要 | M | 額外驗收 | |---|----------| | M0 | 舊 fixture 可讀;新 logic 未完成不回成功空資料;goctl 產出乾淨 | | M1 | 一個頁面完成處理流程;today redirect;五種排序與 URL 狀態正確;操作不扣點 | | M2 | DemandMap 與 QueryPlan 看得懂;執行前知道成本範圍;partial/blocked 不裝完整成功;取得 UX 批准 | | M3 | review/business state 不混;tombstone 不重建;memory/Mongo/API 行為一致 | | M4 | input/map version 正確;preview 免費且會過期;provider 前 ceiling gate;enrichment 失敗不覆蓋 | | M5 | provider/公告/純名稱不進 high;reject 不問 AI;有效 accepted/rejected 都能防重復用 | | M6 | 中斷可續、budget partial 可對帳;mock/live 行為一致;完整 P0 流程可展示 | | M7 | precision@10 過 0.8;high 違規 0;duplicate charge 0;全 repo 回歸綠 | ## 7. Task 號段總表(供下一階段拆 task) | 區間 | Phase / Milestone | 內容 | |------|-------------------|------| | T800–T806 | A / M0 | baseline tests、goctl 契約、not-ready、FE types/repo、legacy/sort fixtures、冪等鍵契約 | | T810–T819 | B / M1 | 單一入口、收件匣 layout、review mock、完成/移除/復原、時間/排序、URL/empty/mobile tests | | T820–T829 | B / M2 | DemandMap mock、phrase editor、QueryPlan、CostPreview、付費確認、Sweep funnel/點數、UX gate tests | | T830–T839 | C / M3 | review domain/audit、memory/Mongo、migration、tombstone、accept/dismiss 相容、列表 API/排序 | | T840–T849 | C / M4 | DemandMap domain/repo、版本、baseline/QueryPlan、CostPreview、enrichment、meter/BYOK | | T850–T859 | C / M5 | preprocessor、evidence、disposition、priority scorer、AI judge、JudgmentResult cache、冪等計點、fixtures | | T860–T869 | C / M6 | sweep pipeline、checkpoint/resume、budget partial、counters、live repo/pages、跨層 P0 integration | | T870–T879 | D / M7 | 標註集、precision/成本 evaluation、race/retry、相容與全回歸、文件/rollout | 正式拆 tasks 時,每題必須包含 Goal、Depends on、Inputs、Outputs(實際路徑+行為)、Out of scope、Acceptance commands、對應 spec 驗收編號。M1/M2 tasks 只能做 frontend+mock;M3 之後才可寫業務後端。與 `brand-product-radar` T773–T775 重疊的測試輸出只能建立一份並由兩邊索引交叉引用。 ## 8. 批准 - [x] Plan 已批准(2026-08-11/使用者) - 批准後下一步:建立 `tasks/INDEX.md` 與各 T8xx task 文件;不在同一輪開始實作。