74 lines
3.4 KiB
Markdown
74 lines
3.4 KiB
Markdown
|
|
# T514 — watch-keyword-suggest-ai
|
|||
|
|
|
|||
|
|
> Status: `done`(2026-07-31)
|
|||
|
|
> Milestone: `M1`
|
|||
|
|
> Kind: `feat`
|
|||
|
|
> Est. change: `~160 lines`
|
|||
|
|
|
|||
|
|
## Goal
|
|||
|
|
|
|||
|
|
完成後系統應:`POST /api/v1/radar/watches/suggest` 依服務檔案回關鍵字建議清單,計入 `ai_copy`/`source=radar.suggest`。
|
|||
|
|
|
|||
|
|
## Depends on
|
|||
|
|
|
|||
|
|
- T510、T513
|
|||
|
|
|
|||
|
|
## Inputs
|
|||
|
|
|
|||
|
|
- Spec:`../spec.md` §4.2.2、§5.5、§9.1(RW-03);§10 開放問題 7
|
|||
|
|
- 既有:`apps/backend/internal/module/ai/**`(provider 與 BYOK)、既有痛點關鍵字工具(scout brief 相關)
|
|||
|
|
|
|||
|
|
## Outputs
|
|||
|
|
|
|||
|
|
### 程式變更(預期路徑)
|
|||
|
|
|
|||
|
|
| 路徑 | 動作 | 說明 |
|
|||
|
|
|------|------|------|
|
|||
|
|
| `apps/backend/internal/module/radar/usecase/suggest.go` | add | 組 prompt:服務項目+地區+案例 → 建議 `terms[]`/`exclude_terms[]` |
|
|||
|
|
| `apps/backend/internal/logic/radar/suggestWatchTermsLogic.go` | edit | 呼叫 usecase、扣點、錯誤映射 |
|
|||
|
|
| `apps/backend/internal/module/radar/domain/suggest.go` | add | 建議項結構(term、理由、建議用途 include/exclude) |
|
|||
|
|
|
|||
|
|
### 行為變更
|
|||
|
|
|
|||
|
|
- 未建服務檔案時回明確錯誤(無依據不亂猜)。
|
|||
|
|
- 每則建議附一句理由,供使用者逐條採用;回傳不自動寫入 watch。
|
|||
|
|
- 計費:`ai_copy` meter,`source=radar.suggest`;BYOK 與 platform gate 行為沿用既有。
|
|||
|
|
|
|||
|
|
### 決策(收斂 spec §10 問題 7)
|
|||
|
|
|
|||
|
|
- 產生方式:**AI 為主,並把既有痛點關鍵字工具的輸出作為 prompt 素材**(不新建第二套關鍵字引擎)。
|
|||
|
|
|
|||
|
|
## Out of scope
|
|||
|
|
|
|||
|
|
- 前端採用互動(T516)
|
|||
|
|
- 依成效自動調整關鍵字(不在本輪)
|
|||
|
|
|
|||
|
|
## Acceptance
|
|||
|
|
|
|||
|
|
- [x] RW-03:有服務檔案 → 回建議清單且每項有理由
|
|||
|
|
- [x] usage 紀錄 `meter=ai_copy`、`source=radar.suggest`
|
|||
|
|
- [x] 指令:
|
|||
|
|
|
|||
|
|
```bash
|
|||
|
|
cd apps/backend && go test ./internal/module/radar/... -count=1
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
## Notes(2026-07-31)
|
|||
|
|
|
|||
|
|
| 路徑 | 動作 |
|
|||
|
|
|------|------|
|
|||
|
|
| `internal/module/radar/domain/suggest.go` | add(建議項結構、`CleanSuggestions`) |
|
|||
|
|
| `internal/module/radar/usecase/suggest.go` | add(prompt 組裝、AI 呼叫、解析) |
|
|||
|
|
| `internal/module/radar/usecase/billing.go` | add(`charge`:預留→結算/退點) |
|
|||
|
|
| `internal/logic/radar/suggest_watch_terms_logic.go` | edit |
|
|||
|
|
| `internal/svc/service_context.go` | edit(AI/Usage 注入+`radarPainTermBridge`) |
|
|||
|
|
| `internal/module/radar/usecase/suggest_test.go` | add(11 則) |
|
|||
|
|
|
|||
|
|
- **計費:`ai_copy` meter、`source=radar.suggest`**,測試直接讀用量事件驗證兩個值,因為 `radar.` 前綴是 P1 價格校準的唯一資料來源,標錯就無法歸因。
|
|||
|
|
- **AI 失敗一定退點**(`defer charge.Settle(ctx, &err)` 綁具名 error),有一則測試專門盯這件事。
|
|||
|
|
- **沒有服務檔案就不呼叫 AI**,直接回明確錯誤:沒有依據的建議只是猜測,但使用者會把它當成系統的判斷。
|
|||
|
|
- **沒有理由的建議項直接丟掉**,不補一句「AI 建議」湊數 —— 假理由比少一則更糟。
|
|||
|
|
- **解析容忍 code fence 與開場白**(只截第一個 `[` 到最後一個 `]`)。模型偶爾會加客套話,硬要求純 JSON 會讓功能在那些回應上整個壞掉。
|
|||
|
|
- **一則都解析不出來時回錯誤,不回空清單。** 空清單會被讀成「你的服務沒有關鍵字可監控」,那是錯的訊息。
|
|||
|
|
- **痛點素材沿用既有海巡產品的 `pain_points`**(`radarPainTermBridge`,只讀),拿不到只記 log 不讓請求失敗;沒有第二套關鍵字引擎。
|