32 KiB
32 KiB
Spec: 商機工作收件匣與產品需求搜尋
Status:
approvedStatus note: 使用者 2026-08-11「批准」 Source:docs/product/opportunity-inbox/requirements.md(approved 2026-08-11) Parent specs:docs/product/demand-radar/spec.md、docs/product/brand-product-radar/spec.mdLast updated:2026-08-12
1. 摘要
本規格把現有「今日商機」與「全部結果」合併成一個商機工作收件匣,以獨立的 review_state 管理待處理、已完成與已移除,不改變既有加入 CRM、判定合格或拒絕等業務狀態。移除是可復原的軟移除,且保留來源 tombstone,確保下一次巡邏不會把同一內容重新建立成新商機。
搜尋改為「產品真相 → 需求地圖 → 多組使用者語言 → 去重與前處理 → 輕量排序 → AI 深度判定」。預設推薦分數以產品痛點適配為主,另支援來源發布時間等排序。所有付費動作都沿用既有 meter、平台代付與 BYOK 規則,加入執行前預估、每次巡邏成本上限、階段用量明細,以及同候選同產品輸入版本不重複判定與扣點的契約。
2. 名詞與實體
| 名詞 | 定義 |
|---|---|
| OpportunityStatus | 既有業務/判定狀態:judging / qualified / rejected / accepted / dismissed;accepted 仍表示已加入 CRM |
| ReviewState | 新增的工作收件匣狀態:pending / completed / removed,與 OpportunityStatus 分開 |
| Soft removal | 將 ReviewState 設為 removed 並保存原因、時間與操作者;資料與來源識別仍保留 |
| Source tombstone | 已移除商機保留的 owner+來源唯一識別;後續命中只合併來源,不建立新卡、不自動恢復 |
| DemandMap | 一個產品可檢查的需求地圖:困擾語言、情境、期望結果、解法意圖與排除訊號 |
| DemandInputVersion | 由受眾、痛點、情境、能力、匹配與排除內容決定的產品搜尋輸入版本;純名稱或顯示欄位修改不改版 |
| DemandMapVersion | 需求地圖每次有效儲存的版本,供搜尋計畫與判定防重使用 |
| QueryPlan | 從目前 DemandMap 產生、符合來源短詞限制的多組搜尋語言與排除語言 |
| CandidateDisposition | 前處理結果:pass / review / reject;reject 不進 AI 深判,review 可低順位保留或抽樣深判 |
| DemandEvidence | 候選原文中表達問題、求助、求推薦、比較或想達成結果的短片段 |
| PriorityScore | priority_score,0–100;決定收件匣推薦順序,與既有 intent_score、product_fit_score 分開 |
| Billable action | 會使用平台代付搜尋或 AI provider 的動作;純 UI、規則前處理與已保存結果讀取不是付費動作 |
| CostPreview | 付費動作執行前的點數估計,含固定最低值、可能最高值、BYOK 模式及估計依據 |
| CreditCeiling | 單次手動工作或每日自動巡邏最多可新增消耗的平台點數;不得超額後再通知 |
3. 狀態機與不變量
3.1 工作收件匣狀態
pending --> completed : 使用者標示完成,或成功加入 CRM
completed --> pending : 使用者恢復待處理
pending --> removed : 使用者移除並選原因
completed --> removed : 使用者移除並選原因
removed --> pending|completed : 復原到移除前狀態
- 每筆 Opportunity 恰有一個 ReviewState。
completed只代表使用者已處理,不代表適合/成交/加入 CRM,不形成負向訓練訊號。removed必須保存removal_reason;other必須另有非空白說明。原因 enum:pain_mismatch / provider_or_ad / stale / already_solved / duplicate / other / legacy_unknown。- 進入
removed時保存previous_review_state、removed_at、removed_by;復原回到previous_review_state,缺值時回pending,並保存最近一次移除紀錄供稽核。 - 更新成目前相同狀態是 idempotent success,不重複寫操作、不產生用量。
- ReviewState 不得直接改寫 OpportunityStatus。既有
accept成功後仍設accepted、綁 Contact,另外把 ReviewState 設為completed。 - 舊
dismiss行為保留相容,但改為設定 OpportunityStatus=dismissed且 ReviewState=removed;reason 空白的舊呼叫保存legacy_unknown。
3.2 舊資料讀取規則
缺 review_state 的既有 Opportunity 依下列順序讀取:
status=accepted -> completed
status=dismissed -> removed(removal_reason=legacy_unknown)
其他 -> pending
- 第一次狀態寫入時持久化映射結果;列表讀取不可要求一次性全庫遷移才可用。
dismissed舊資料復原時只恢復 ReviewState;既有 OpportunityStatus 保持dismissed,避免竄改歷史業務狀態。若需要重新成為 qualified,仍走既有人工 override。
3.3 去重與 tombstone
- 沿用「同 owner+同來源內容識別只一筆 Opportunity」。來源識別優先用正規化 external ID,其次正規化 permalink。
- 查到既有
removed商機時,只合併新的 watch/term/產品匹配來源與最近命中時間;不得建立新 Opportunity、不得自動改回 pending、不得重複深判。 duplicate移除原因可保存duplicate_of_opportunity_id;被指向的主卡不存在或不屬於同 owner 時拒絕操作。- tombstone 沿用既有資料保留政策;P0 不提供不可逆硬刪除 API。
4. 搜尋、前處理與排名契約
4.1 DemandMap 結構
每個產品目前版本只有一份生效 DemandMap:
DemandMap = {
product_id, demand_input_version, map_version, state,
pain_phrases[], scenario_phrases[], desired_outcomes[],
solution_signals[], exclusion_signals[],
source_basis[], custom_phrases[], ai_enriched_at?, updated_at
}
state為ready / incomplete / stale。- 每個 phrase 保存
text、kind、basis_kind、basis_text、origin=product|user|ai、enabled;不得只保存無法追溯來源的模型輸出。 - 系統從 Brand/Product 真相建立免費 baseline;不得為了開頁或每日巡邏自動呼叫 AI。
- 使用者手動編輯與啟停 phrase 不扣點,每次有效儲存增加 map_version。
- AI enrichment 是明確的手動付費動作;成功後新增 AI phrases 並保存來源依據,不覆寫使用者自訂內容。
- 產品受眾、痛點、情境、能力、匹配或排除內容實質變更時,DemandInputVersion 改變;舊 AI enrichment 失效,免費 baseline 以新產品真相重建,使用者自訂內容保留但標示待確認。
- 純品牌/產品名稱、圖片或顯示順序修改不改 DemandInputVersion,也不允許重新扣同候選深判點數。
- baseline 缺少痛點且缺少情境/期望結果時為
incomplete;可保存但不能啟動新的產品巡邏,畫面引導補產品資料。
4.2 QueryPlan
- 只從 enabled DemandMap phrases 產生符合 Threads 短詞契約的 1–6 組查詢;每組至少包含困擾/情境/期望結果之一,產品名不得單獨成組。
- solution signals 用於提升求助、求推薦、比較與購買意圖召回;exclusion signals 傳給既有排除流程。
- 同義表達分散在不同查詢組,結果在 owner 層統一去重;增加 query 組數不得繞過單次 CostPreview 與 CreditCeiling。
- 每次 Sweep 保存實際使用的 demand_input_version、map_version 與 query plan 摘要,使結果可重現。
- 使用者手動修改 watch terms 時仍可使用,但 UI 顯示它們與 DemandMap 的關聯;純產品名 term 只能輔助召回,不能提供排名證據。
4.3 候選前處理
候選依序經過下列不呼叫 AI provider 的階段;每階段保存計數與 machine reason:
- 正規化與去重: 正規化 external ID/permalink,同一來源只保留資訊最完整的一筆。
- 基本有效性: 必須有可用文字與來源;語言不支援、時間未知不直接 reject,但留下風險。
- 硬排除: 命中產品排除訊號,或明確為同業供給、廣告、品牌公告、抽獎/互追雜訊時 disposition=
reject。 - 證據抽取: 從原文擷取 DemandEvidence;找不到任何問題、求助、比較、求推薦或期望結果訊號時 disposition 至少降為
review。 - 產品證據: 用 DemandMap 對困擾、情境、期望結果做輕量比對;產品名/品牌名命中不計分。
- 分流: 同時具備 DemandEvidence 與至少一項產品證據為
pass;只有其中一項為review;兩者皆無為reject。
- reject 候選不進 AI 深度判定,也不建立高/中/低商機;Sweep 保留原因計數,不保存作者敏感資訊的額外副本。
- review 候選可按輕量分數進入低順位結果或在 CreditCeiling 內抽樣深判;不得標為高順位。
- 已存在且已有同 DemandInputVersion+DemandMapVersion 有效判定的候選直接復用,不再呼叫 AI。
4.4 AI 深度判定
- 只對
pass的高價值候選及部分review不確定候選執行,順序以輕量產品證據、需求意圖與新鮮度排列。 - 輸出必須包含結構化 DemandEvidence、產品適配證據、排除風險與各分數;所有正向重要理由都須引用候選原文片段及 DemandMap/產品依據。
- AI 不得用品牌名、產品名或搜尋詞本身補成痛點證據;不得推測作者未公開的身分、預算或敏感屬性。
- 結構不完整、證據無法在原文找到或產品依據不存在時,該判定失敗且不得保存為有效結果;工作可在剩餘 CreditCeiling 內繼續下一筆。
- 判定結果為不適合但 provider 已成功完成有效結構化工作,仍正常計點並保存拒絕原因,避免日後重判重扣。
4.5 推薦分數與硬門檻
priority_score = pain_fit(0..45)
+ demand_intent(0..25)
+ evidence_quality(0..15)
+ freshness(0..15)
| 因子 | 行為定義 |
|---|---|
pain_fit |
原文對 Product 痛點、情境或期望結果的適配;至少一項有可引用證據才可大於 0 |
demand_intent |
是否正在求助、求推薦、比較方案、表達想改善或準備採取行動 |
evidence_quality |
引文是否直接、完整且同時支持需求與產品適配;只有推測時為 0 |
freshness |
沿用來源 posted_at 時效衰減;未知時間為 0,不得視為最新 |
priority_band = high if score >= 75 and hard gates pass
review if score 45..74 and hard gates pass
low otherwise but has basic demand + product evidence
高順位 hard gates:
- 至少一段 DemandEvidence。
- 至少一項痛點、情境或期望結果的產品證據。
- 非 provider/廣告/公告/雜訊,且沒有產品硬排除。
- 不能只靠品牌名、產品名或 matched term。
- 來源與引文可回看;證據不足只能進 review/low。
排序 enum:
| sort | 排序 |
|---|---|
recommended |
priority_score desc → product_fit_score desc → posted_at desc → id asc |
newest |
已知 posted_at desc → priority_score desc;未知時間置底 |
oldest |
已知 posted_at asc → priority_score desc;未知時間置底 |
product_fit |
product_fit_score desc → priority_score desc → posted_at desc |
demand_intent |
demand_intent desc → priority_score desc → posted_at desc |
- 所有分頁排序必須穩定,最後以 id tie-break;禁止前端只對單頁資料重排。
- 舊
sort=score相容映射為recommended,舊sort=posted相容映射為newest。
5. 扣點與配額契約
5.1 免費與付費邊界
| 動作 | 平台點數 |
|---|---|
| 開頁、列表、詳情、篩選、排序 | 0 |
| 完成、移除、復原、選移除原因 | 0 |
| baseline DemandMap 建立、手動編輯、QueryPlan 規則產生 | 0 |
| 去重、硬排除、證據抽取、輕量排序 | 0 |
| API path 搜尋 | 沿用既有 web_search 單次成本與 radar.* source |
| crawler path 搜尋 | 沿用既有 crawler/BYOK 計量行為,不臆造平台搜尋點數 |
| DemandMap AI enrichment | 沿用既有 AI meter,以獨立 radar.demand_map source 歸因 |
| AI 深度判定 | 沿用既有 AI research/判定 meter,以 radar.judge source 歸因 |
| 回覆生成 | Retain 既有 meter 與按需生成規則 |
- 不新增第五種 usage meter;
radar.*source 只用於歸因。 - BYOK 的平台 credits 一律為 0,但 count、source、工作統計仍增加。
5.2 CostPreview 與執行確認
CostPreview 回傳:
{
action, key_mode,
fixed_credits, min_credits, max_credits,
search_calls, max_ai_candidates,
remaining_credits, estimate_basis, expires_at
}
- 取得 preview 本身不扣點。
- 手動搜尋/巡邏、AI enrichment、批次深判與回覆生成在 UI 觸發前取得 preview;固定成本顯示確值,候選數未知時顯示範圍,不假裝精準。
- 使用者確認時送出
preview_id與credit_ceiling。preview 過期、產品/query plan 改版或目前餘額不足時,執行前拒絕並要求重新確認。 - 單筆既有固定成本的動作仍可由既有用量 metadata 顯示;若沒有變動成本,不必多一個確認步驟,但按鈕附近必須標示點數。
5.3 實際計點、防重與失敗
- provider 呼叫前通過會員額度、meter soft cap、preview 及 CreditCeiling gate。
- provider 未被呼叫,或呼叫未產生既有計量規則認定的有效工作,不 commit 點數。
- 搜尋 provider 成功返回(即使 0 hit)後,commit 搜尋點數。
- AI 成功返回可驗證的完整判定後,commit 該候選判定點數;結果 rejected 仍計點。
- AI 輸出結構無效或不可驗證時,沿用既有失敗退點語意,不把失敗結果加入有效判定快取。
- 有效判定唯一鍵為 owner+來源識別+product_id+demand_input_version+map_version+judge_kind;命中即復用,重試、跨 watch 或跨 query 不再扣。
- Job 中斷重跑時,逐筆跳過已 commit 且已有有效結果的唯一鍵;不得只靠整批成功旗標防重。
- 達 CreditCeiling 或方案點數上限時停止新的 provider 呼叫,已完成結果保留,剩餘候選記
budget_deferred_count;不得把工作標成完整成功。
5.4 自動巡邏成本上限
- P0 沿用方案既有雷達與 AI 配額作預設 CreditCeiling,不新增每產品自訂額度;自訂額度列 P1。
- 每日排程不逐次詢問,但開始前仍建立系統 CostPreview 快照供對帳。
- 多個 watch 共用 owner 當日剩餘額度;排程不得因先處理某個 watch 而突破會員方案上限。
- 點數不足不刪除、不封存 watch;該 Sweep 顯示
partial_budget或blocked_budget,並保留未判定數量。
6. 使用者流程(行為)
6.1 第一次進入商機收件匣
- 使用者進
/app/radar/opportunities,預設review_state=pending、time_scope=today、sort=recommended。 - 頁首只顯示待決定/已處理/已移除、時間範圍與排序;品牌、產品、意向、匹配狀態收進「更多篩選」。
- 卡片首層顯示問題摘要、適合產品、單段最強證據、priority score/band、posted_at 與三個明確決策:「加入名單並追蹤/只標示已處理/不適合」。
- 「加入名單並追蹤」成功後 OpportunityStatus=
accepted且 ReviewState=completed,顯示前往該 Contact 的入口;「只標示已處理」不建立 Contact。 - 使用者展開完整判定後仍可直接加入名單或只標示已處理,不必關閉詳情回卡片操作。
- 無結果時依 no watch、尚未巡邏、全部被前處理排除、點數不足、巡邏失敗、篩選後為空分別提示,不回一個無解釋的空白頁。
6.2 完成、移除與復原
- 按「只標示已處理」立即將卡片移出 pending,但不建立 Contact;操作失敗則回復原畫面並顯示後端訊息。
- 按移除先選原因;
other需輸入說明。成功後顯示可復原提示,不觸發 AI。 - 已完成/已移除 tab 使用同一列表與排序契約;復原後依 previous_review_state 回到對應 tab。
- 加入 CRM 成功後 ReviewState 自動 completed,並回傳 Contact 入口;只標示已處理不會自動建立 Contact。兩種整理動作皆不扣點。
6.3 檢查需求地圖與手動探索
- 使用者從雷達設定或收件匣次要入口選 Brand/Product,查看免費 baseline DemandMap 與資料完整度。
- 可免費編輯、啟停 phrases;儲存後 preview 顯示下一次探索使用的新 map version。
- 若選 AI enrichment,先顯示 CostPreview,確認後執行;失敗不覆寫目前有效地圖。
- 手動探索前顯示 QueryPlan 摘要、預估搜尋次數、最多 AI 候選與點數範圍;使用者確認 ceiling 後才排程。
- 完成頁顯示 hit、dedupe、prefilter reject/review/pass、AI judge、created/merged、budget deferred 與實際點數。
6.4 每日巡邏
- worker 驗證 watch、產品與 DemandMap;incomplete 時阻擋並通知補產品資料。
- 保存系統 preview 與版本,執行 QueryPlan 搜尋。
- 依 §4.3 前處理,先復用已有判定,再依輕量順位及剩餘 ceiling 選 AI 深判候選。
- 每完成一個可計點階段即記錄可續跑進度;中斷後從未完成候選繼續。
- 建立/合併 Opportunity 時保存 priority factors、證據、版本與 ReviewState=pending;命中 removed tombstone 只合併來源。
- 完成時寫完整 funnel 與點數;部分完成必須標 partial 並顯示未處理數。
7. 介面契約
7.1 共通規則與 Auth
- 所有
/api/v1/radar/**只接受會員Authorization: Bearer <member JWT>;owner 隔離沿用既有安全語意。 - AI provider token/BYOK key 只供內部 provider 使用,不得出現在 CostPreview、Sweep 或 Opportunity response。
- 成功 envelope:
code=102000、message、data、error;失敗不得回成功空資料。 - 列表 request
page / pageSize,responsepagination + list。 - 所有時間均為 UTC unix nanoseconds
int64。 - 後端契約只由
generate/api/*.api修改並以 goctl 生成 handler/route;業務行為落在 logic/module。
7.2 HTTP / API
| Method | Path | 用途 | 主要 request/response |
|---|---|---|---|
| GET | /api/v1/radar/opportunities |
單一收件匣列表 | query 加 review_state、time_scope、sort;item 加 review、priority、evidence |
| GET | /api/v1/radar/opportunities/:id |
商機詳情 | 回完整 review audit、priority factors、DemandEvidence 與產品依據 |
| PUT | /api/v1/radar/opportunities/:id/review-state |
完成、移除、復原 | req state、removal_reason?、removal_note?、duplicate_of_opportunity_id?;回 Opportunity |
| POST | /api/v1/radar/opportunities/:id/accept |
加入 CRM | Retain;成功時另設 review_state=completed |
| POST | /api/v1/radar/opportunities/:id/dismiss |
舊客戶相容 | Retain;映射 review_state=removed,保存 reason 或 legacy_unknown |
| GET | /api/v1/radar/products/:productId/demand-map |
讀目前需求地圖 | 回完整度、input/map version、phrases 與 stale 資訊 |
| PUT | /api/v1/radar/products/:productId/demand-map |
免費人工調整 | req enabled/custom phrases 與基礎版本;衝突時要求重讀 |
| POST | /api/v1/radar/products/:productId/demand-map/enrich |
AI 擴充 | req preview_id、credit_ceiling;回新 map 與 credits_used |
| GET | /api/v1/radar/cost-preview |
付費動作預估 | query action、watch/product/opportunity context;回 CostPreview |
| POST | /api/v1/radar/watches/:id/sweep |
手動巡邏 | req 加 preview_id、credit_ceiling;回 job/sweep 識別 |
| POST | /api/v1/radar/explore |
手動探索 | req 加 map version、preview、ceiling;回 funnel 統計與實際耗點 |
| GET | /api/v1/radar/sweeps |
巡邏紀錄 | item 加版本、funnel counts、credit breakdown、budget state |
| GET | /api/v1/radar/today |
舊頁相容 | Retain 暫時;只回 pending+today,同新列表排序,之後導覽移除 |
OpportunityPublic 增量:
{
review_state, previous_review_state?, removal_reason?, removal_note?,
removed_at?, removed_by?, last_matched_at?,
priority_score, priority_band,
priority_factors: { pain_fit, demand_intent, evidence_quality, freshness },
demand_evidence[], demand_input_version?, demand_map_version?
}
RadarSweepPublic 增量:
{
demand_input_version?, demand_map_version?,
hit_count, deduped_count,
prefilter_pass_count, prefilter_review_count, prefilter_rejected_count,
cached_judgment_count, judged_count,
created_count, merged_count, tombstone_matched_count,
budget_deferred_count,
status: complete|partial_budget|blocked_budget|failed,
credits_used,
credit_breakdown: { search, demand_map, judge, reply }
}
7.3 列表 query 行為
| query | 值 | 預設/說明 |
|---|---|---|
review_state |
pending/completed/removed | pending |
time_scope |
today/7d/all | today;與 from/to 不可同時使用 |
sort |
recommended/newest/oldest/product_fit/demand_intent | recommended |
brand_id、product_id |
owner 可見識別 | 沿用既有 product match filter |
priority_band |
high/review/low | 空表示全部 |
from、to |
unix ns UTC | 以 posted_at 篩選;未知時間只在 all 且無 from/to 時出現 |
time_scope=today以會員顯示時區的當地 00:00 換算成 UTC ns 範圍;資料儲存仍只用 UTC ns。- time scope、filter、sort、page 寫入 URL,重新整理與返回頁面保持相同工作位置。
7.4 前端頁面 / 導覽
| 路由 | 目的 | 主要操作 |
|---|---|---|
/app/radar/opportunities |
唯一商機工作收件匣 | review tab、時間、排序、產品篩選、完成、移除、復原、詳情 |
/app/radar/today |
舊連結相容 | redirect 到 opportunities 的 pending+today,不再顯示第二套頁面 |
/app/radar/watches |
雷達與需求地圖入口 | 查看完整度、編輯 map、AI enrichment、預覽成本、觸發巡邏 |
/app/usage |
用量追溯 | 沿用既有 meter,按 radar.* source 查看消耗 |
- 導覽只保留一個「商機」入口,不同頁面不得重複出現同一工作清單。
- mock/live 實作同一 repository 介面、排序、狀態、錯誤與空結果。
- 沿用 Harbor Desk 與既有 UI 元件;不新增 UI framework。
7.5 Job / 背景任務
| Template type | Worker | 觸發 | 成功結果 |
|---|---|---|---|
radar_sweep |
既有 radar worker | 每日排程、首巡、手動巡 | 按新 funnel 執行,guarded 更新完整或 partial 狀態與點數 |
radar_demand_map_enrich |
radar worker | 使用者確認付費 enrichment | 產生可驗證 phrases;成功才切換 map version 與 commit 用量 |
- Redis lock value 保持
workerID;只有持有者可續期/釋放。 - Job status 必須 guarded 更新;被取消或已終態的 job 不得被晚到 worker 覆寫。
- CostPreview 不是 Job,不得占用工作佇列或扣點。
7.6 錯誤語意
| 情境 | 行為 |
|---|---|
| 非 owner 讀寫商機、產品或需求地圖 | not found/forbidden 的既有安全語意,不洩漏存在性 |
| review state/removal reason/sort 不合法 | validation error;不做部分更新 |
| removed 未帶原因或 other 未帶 note | validation error;保持原狀態 |
| duplicate 指向不存在、自己或其他 owner | validation error/not found;保持原狀態 |
| DemandMap 基礎版本過期 | conflict;回目前版本摘要,要求重讀,不覆寫 |
| DemandMap incomplete | 新巡邏 blocked;回缺少哪些產品資訊,不回成功空資料 |
| preview 過期或 context 已改版 | conflict;要求重新預估與確認 |
| ceiling 小於固定成本 | 執行前 validation/quota error;provider 不被呼叫、不扣點 |
| 點數於批次中耗盡 | Sweep partial_budget;回已完成與 deferred counts |
| 全部候選被前處理排除 | Sweep complete 且 created=0;回各 reject reason counts,非無說明空成功 |
| provider 失敗 | 沿用 retry/退點規則;Sweep failed 或 partial,watch 不被刪除 |
8. 與現況對照
| 能力 | Retain | Replace | Remove | 備註 |
|---|---|---|---|---|
| Opportunity 與 owner+來源去重 | ✓ | 加 tombstone 行為,不重建第二套實體 | ||
intent_score、product_fit_score |
✓ | 保留顯示;新推薦排序用獨立 priority score | ||
| ProductMatch 與主推產品 | ✓ | DemandMap 提供更好的證據來源 | ||
accepted 加入 CRM |
✓ | 成功後同步 review completed | ||
dismissed 終態 |
相容讀寫 | ✓ | 新 UI 改用可復原 review removed | |
RadarTodayPage 獨立工作頁 |
✓ | 舊 URL redirect 到單一收件匣 | ||
RadarOpportunitiesPage |
✓ | 改成唯一收件匣,不再只是封存查詢頁 | ||
| watch 手工 include/exclude terms | ✓ | QueryPlan 與 DemandMap 提供可追溯建議 | ||
| 目前短詞建議 | 相容 | ✓ | 升級為五類 DemandMap,不只回平面 terms | |
| 基本 substring exclude/分類 | ✓ | ✓ | 成為完整、可觀測的免費前處理 funnel | |
| 五問與產品適配 AI | ✓ | ✓ | 只處理 shortlist,輸出可驗證證據並防重扣 | |
| 既有四 meter、BYOK、方案配額 | ✓ | 只加 radar.* source 與預估/明細 |
||
| 今日/全部兩套導覽 | ✓ | 時間改為同頁 scope | ||
| hard delete 商機 | ✓ | P0 明確不提供 |
9. 非功能
- 安全/權限: Brand、Product、DemandMap、Opportunity、CostPreview、Sweep 全部以 owner 隔離;不得回傳 provider token、搜尋 session 或他人存在性。
- 一致性: review state 更新、移除 audit 與 tombstone 必須同一成功邊界;accept 的 CRM binding 與 review completed 不得出現成功訊息但狀態未更新。
- 冪等性: review state、provider 判定與用量 commit 各自有穩定冪等鍵;HTTP 重送及 Job 重跑不重複扣點。
- 可觀測性: 每 Sweep 保存版本、funnel counts、各階段原因、點數 breakdown、partial/blocked 原因;日誌不得含 token 或完整敏感設定。
- 效能邊界: 列表必須 server-side filter/sort/page;前處理應在 provider 深判前完成。候選與 AI 最大數受現有搜尋 limit、方案配額與 CreditCeiling 三重上限。
- 可解釋性: 正向高順位理由必須能回指公開原文與產品/DemandMap 依據;找不到原文片段不得保存為高順位證據。
- 相容性: 缺新欄位的舊資料可讀;舊 sort、today、accept、dismiss 呼叫有明確映射,不要求客戶端同日切換。
10. 資料保存(邏輯層)
| 實體 | 關鍵欄位 | 生命週期 |
|---|---|---|
| Opportunity | review state、previous state、removal fields、priority factors、demand evidence、版本、last matched | 既有 owner+來源唯一;removed 保留 tombstone;不硬刪 |
| OpportunityReviewEvent | opportunity、from/to、reason/note、duplicate target、actor、at | append-only;完成、移除、復原皆留紀錄 |
| DemandMap | product、input/map version、state、五類 phrases、basis、origin | 一產品一個 current;舊版本保留供既有判定解釋與防重 |
| JudgmentResult | owner、來源、product、input/map version、kind、結果、usage event | 唯一鍵防重;有效結果可復用;失敗不當有效快取 |
| CostPreview | owner、action、context version、估計、ceiling、expiry | 短期保存;只可由同 owner 使用一次啟動工作,不扣點 |
| RadarSweep | query/版本、funnel counts、status、credit breakdown、deferred | 保留供成本、續跑與品質分析;時間 UTC ns |
| UsageEvent | 既有 meter、radar.* source、key mode、credits、count |
Retain;不新增 meter;與 Sweep 可對帳 |
11. 驗收場景(Given / When / Then)
- Given 既有 qualified 商機沒有 review_state,When 開啟收件匣,Then 它出現在 pending,且讀取不扣點。
- Given pending 商機,When 使用者標完成,Then 它移到 completed、未建立 Contact、可免費恢復 pending。
- Given pending 商機,When 以 pain_mismatch 移除,Then 它進 removed、保存原因;下次同來源命中只更新來源資訊,不新建、不重判、不扣判定點。
- Given removed 商機先前為 completed,When 復原,Then 回 completed;移除 audit 仍可查。
- Given 使用者按加入 CRM,When Contact binding 成功,Then OpportunityStatus=accepted 且 ReviewState=completed;只成功其中一半不得回成功。
- Given 產品只有名稱、沒有痛點與情境,When 建 DemandMap,Then state=incomplete 且新巡邏被擋下,系統指出缺少資料。
- Given 產品痛點有多種使用者口語,When QueryPlan 產生,Then 查詢含困擾/情境/結果語言,產品名不單獨成組。
- Given 候選是同業廣告,When 前處理,Then disposition=reject、不呼叫 AI judge,Sweep reject count 增加。
- Given 候選只有產品名命中,When 排名,Then pain fit 與 evidence quality 不因名稱得分,不能進 high。
- Given 候選用不同說法表達同一產品痛點且引用可驗證,When 深判,Then 保存原文 evidence 與產品 basis,可依 priority 排到純關鍵字結果前。
- Given 三筆相同 priority score,When 分頁查 recommended,Then 依 product fit、posted_at、id 穩定排序,跨頁無重複或跳筆。
- Given posted_at 未知,When sort newest 或 oldest,Then 該筆都在已知時間之後,不冒充最新或最舊。
- Given 手動探索尚未確認,When 取得 CostPreview,Then credits 不變;確認 ceiling 後才可啟動 provider 呼叫。
- Given API 搜尋成功但 0 hit,When Sweep 完成,Then 搜尋點數如實計入、AI judge 為 0,畫面說明 no hit。
- Given 前處理排除全部候選,When Sweep 完成,Then AI judge 點數為 0,並顯示各排除原因而非空白成功。
- Given AI 完成結構有效但判定不適合,When 結果保存,Then 該次正常計點並快取 rejected 結果,重跑不再扣。
- Given AI 回傳無法驗證的證據,When 判定失敗,Then 不建立有效 JudgmentResult、不 commit 該判定點,Sweep 顯示失敗。
- Given Job 在第五筆後中斷,When 重跑,Then 前五筆從有效唯一鍵復用,僅未完成候選可能扣點。
- Given CreditCeiling 在批次中用完,When 尚有候選,Then 停止新 provider 呼叫、保存已完成結果、status=partial_budget 並回 deferred count。
- Given BYOK 會員執行相同流程,When 完成,Then 平台 credits_used=0,count、source 與 funnel 統計仍可查。
- Given 產品只改名稱,When 同候選再命中,Then DemandInputVersion 不變,不允許重新扣點深判。
- Given 產品痛點實質修改,When DemandMap 更新,Then input version 改變、舊 AI phrases 失效、使用者 custom phrases 待確認,重新深判前需新的 CostPreview。
- Given 人工標註代表性資料,When 取 recommended 前十,Then 至少八筆同時具備需求與產品適配證據,且 provider/公告/純名稱命中不在 high。
- Given 使用者從舊
/app/radar/today進入,When 頁面載入,Then redirect 到 opportunities pending+today,沒有第二份狀態或列表邏輯。
12. 開放規格問題
- 無阻擋 P0 的開放問題。每產品自訂 CreditCeiling、批次操作與移除回饋調校均依 requirements 保持 P1,不在 P0 偷渡。
13. 批准
- Spec 已批准(2026-08-11/使用者)
- 批准後下一步:建立
plan.md,依 Phase B mock 收件匣 → 契約 → Phase C live → Phase D 搜尋與品質閘拆里程碑;本輪不建立 tasks、不修改產品程式。