# T549 — reply-generation-forbidden-filter

> Status: `done`  
> Milestone: `M3`  
> Kind: `feat`  
> Est. change: `~200 lines`

## Goal

完成後系統應：判定時只生成 `public_comment`，其餘 variant 按需生成；`forbidden[]` 命中自動重生一次，仍命中則**明確錯誤且不輸出**。

## Depends on

- T510、T548

## Inputs

- Spec：`../spec.md` §4.5、§5.5、§5.6、§9.3（RP-01～RP-03）
- 既有：`apps/backend/internal/module/ai/**`、active Persona（`internal/module/studio/**`）

## Outputs

### 程式變更（預期路徑）

| 路徑 | 動作 | 說明 |
|------|------|------|
| `apps/backend/internal/module/radar/usecase/reply_generate.go` | add | 組 prompt（原文＋ServiceProfile＋Persona）＋五種語氣指示 |
| `apps/backend/internal/module/radar/usecase/forbidden_filter.go` | add | 命中檢查（正規化比對）＋重生一次＋仍命中回錯誤 |
| `apps/backend/internal/module/radar/usecase/judge_persist.go` | edit | 入庫成功後生成 `public_comment` 一版 |
| `apps/backend/internal/logic/radar/createOpportunityReplyLogic.go` | edit | 按需生成指定 variant |

### 行為變更

- 首次判定只生成 1 版（成本控制）；查 replies 只會看到 `public_comment`（RP-01）。
- 計費：`ai_copy`／`source=radar.reply`，每個 variant 各計一次。
- forbidden 連兩次命中：回明確錯誤（非 `102000`），**不得把違規內容放進回應或寫庫**。

## Out of scope

- 送出（T550）
- AI 追蹤訊息（T588）

## Acceptance

- [x] RP-01：判定後僅存在 `public_comment`
- [x] RP-02：生成 `dm` 成功且 usage 記 `ai_copy`／`radar.reply`
- [x] RP-03：forbidden 含「保證錄取」→ 輸出不含該詞；連兩次命中回錯誤且無內容外洩
- [x] 指令：

```bash
cd apps/backend && go test ./internal/module/radar/... ./internal/logic/radar/... -count=1
```
