# Post Project 計劃(文案任務 Flow A 砍掉重做) > 取代 `backend/docs/threads-automation-gap-plan.md` 中文案自動營運閉環的舊版 Flow A。Flow B(海巡獲客:brand / placement_topic / outreach_drafts / PatrolPage)不在本計劃範圍。 ## 1. 動機 舊版文案任務(Flow A)流程過重: - 一個任務跑 5 個 worker(analyze → expand-graph → scan-viral → matrix → copy-draft),加上 refill-publish-inventory 自動補庫存旁路。 - 中間產 research map、knowledge graph、scan_posts、相似帳號 / 受眾樣本等中介資料,前端被 pipeline 狀態綁住。 - 使用者真正要的是: > 單純 po 文、相似貼文模仿、發想主題、AI 找網路資料、矩陣產 N 篇、定時發送、可選人設、可貼參考貼文讓 AI 學風格。 本計劃把 Flow A 整包砍掉重做,換成線性可操作的「發文計畫(Post Project)」。 ## 2. 範圍 ### 砍 - Persona 文案流(copy_mission / copy_draft 全部) - refill-publish-inventory worker 與其 publish_inventory policy 接面 - 5 個文案 worker(analyze-copy-mission / expand-copy-mission-graph / generate-copy-matrix / generate-copy-draft / refill-publish-inventory) - `library/copymission/`、`library/matrix/copy_generate.go`、`library/viral/mission_*.go`、`library/viral/research_map.go` - prompt files `matrix_copy.*` - 前端 MissionsPage + `pages/missions/` - `copy_missions` / `copy_drafts` 兩個 MongoDB collection(drop) ### 不動 - Flow B(brand / placement_topic outreach_drafts、PatrolPage 海巡獲客) - `outreach_drafts` model / library/outreach / `generate-outreach-draft` worker - `knowledge_graph` model(brand / placement_topic 共用) - `scan_post` 通用 viral 採樓能力(移除 `CopyMissionID` 欄位即可) - `publish_queue` / `publish_analytics` 通用發布與量測 - scheduler / reaper / sweeper / token-sweeper / refresh-threads-token 通用 worker - Persona model / API(沿用) ### 新增 - 兩個 collection:`post_projects` / `post_drafts` - 一個 worker:`go-generate-post-matrix` - 三個同步 LLM endpoint:research-topics / analyze-style / generate-matrix - 一條線性的前端 wizard 頁面 - 三份 prompt files(post_matrix_generate / analyze_style / research_topics) ## 3. 資料模型 ### post_projects | 欄位 | 型態 | 說明 | |------|------|------| | `_id` | ObjectID | | | `tenant_id` | string | | | `owner_uid` | string | | | `persona_id` | ObjectID | 關聯 personas | | `label` | string | 使用者自訂專案名 | | `topic` | string | 使用者選定 / 編輯後的主題描述 | | `web_research_summary` | string | research-topics 回傳的摘要 | | `web_research_refs` | []object | `[{url, title, snippet}]` | | `style_profile` | object | analyze-style 回的結構化風格 | | `style_summary` | string | 給前端顯示的風格短述 | | `style_references` | []string | 使用者貼進來的參考貼文原文 | | `status` | string | `topic_set` / `style_set` / `matrix_generating` / `drafts_ready` / `archived` | | `matrix_draft_count` | int | 矩陣產生的草稿數 | | `created_at` / `updated_at` | int64 | unix nanoseconds(UTC+0) | `style_profile` 建議結構: ```json { "tone": "親切、口語、像跟朋友聊天", "length": "短至中(80-180 字)", "structure": ["hook", "故事", "takeaway", "互動問句"], "emoji_usage": "少量,平均 1-2 顆,放結尾", "hook_style": "提問或反差句", "avoid": ["過度口號", "廉價 emoji 疊字"] } ``` ### post_drafts | 欄位 | 型態 | 說明 | |------|------|------| | `_id` | ObjectID | | | `tenant_id` | string | | | `project_id` | ObjectID | 關聯 post_project | | `persona_id` | ObjectID | | | `topic` | string | snapshot 主題(生成時) | | `angle` | string | AI 給該篇的角度標籤 | | `hook` | string | AI 給的 hook | | `text` | string | 草稿本文 | | `rationale` | string | AI 說為何這樣寫 | | `sources` | []object | `[{url, title}]` 引用網路資料 | | `status` | string | `draft` / `scheduled` / `published` | | `publish_queue_id` | ObjectID? | 排入佇列後回填 | | `scheduled_at` | int64? | 排程時間 | | `published_post_id` | string? | 發布後 Threads post id | | `created_at` / `updated_at` | int64 | unix nanoseconds | ### publish_queue.queue_item 改欄位 - `copy_mission_id` / `copy_draft_id` → `post_project_id` / `post_draft_id`(可空,舊資料不再解析) - 既有 outreach 排程不受影響(Flow B 不靠這兩欄) ### publish_inventory policy - 移除 `copy_mission_id` 欄位(refill worker 已砍) - publish_inventory 對 outreach 是否仍有用需確認;若只服務文案 refill 則整個 model 一併砍。階段 1 解耦時若發現 inventory 僅供 refill,連 collection 一起 drop。 ## 4. API 新 `generate/api/post_project.api`,import 至 `gateway.api`。 ``` Prefix: /api/v1/personas (scope) 與 /api/v1/post-projects (資源) POST /api/v1/personas/:personaId/post-projects GET /api/v1/personas/:personaId/post-projects GET /api/v1/post-projects/:projectId PATCH /api/v1/post-projects/:projectId (label / topic / status) DELETE /api/v1/post-projects/:projectId POST /api/v1/post-projects/:projectId/research-topics sync: { seed } -> { suggestions[], refs[] } POST /api/v1/post-projects/:projectId/analyze-style sync: { references:[{text}] } -> { style_profile, style_summary } POST /api/v1/post-projects/:projectId/generate-matrix async job: enqueue generate-post-matrix POST /api/v1/post-projects/:projectId/drafts/:draftId/regenerate async job: 重生單篇 GET /api/v1/post-projects/:projectId/drafts PATCH /api/v1/post-projects/:projectId/drafts/:draftId (text / angle / sources) DELETE /api/v1/post-projects/:projectId/drafts/:draftId POST /api/v1/post-projects/:projectId/drafts/schedule body: [{draft_id, send_at}] -> publish_queue.Create ``` Envelope:`code/message/data/error`;列表用 `page/pageSize` + `pagination/list`。 ## 5. Worker `internal/worker/job/generate_post_matrix.go` - `workerType = "go-generate-post-matrix"` - `step = "post_matrix_generate"` - 讀 `project.topic` + `project.style_profile` + `persona` + `project.web_research_refs` - 組 prompt → LLM 一次吐 N 篇(N=5 預設) - 寫 N 個 `post_drafts` doc - 更新 `project.status = drafts_ready`、`matrix_draft_count = N` - 過程中 `RefreshRunLock` + `Heartbeat`,不做 cancel 自行設狀態 regenerate 走同一個 worker 同一 template,payload 帶 `target_draft_id`: - 有 → 刪舊 draft / 重生一篇覆蓋 - 無 → 全新矩陣 ## 6. Library 新 `internal/library/post_matrix/`(取代 `library/copymission/`): - `analyze_style.go`:貼文 → LLM → 吐 `style_profile` JSON + `style_summary` - `research_topics.go`:seed → 內部 websearch(`node-tool-keyword-search` 或 `library/viral/crawler_search`) → LLM 摘要 + 主題候選 - `generate.go`:組 prompt → LLM → N 篇 post_drafts prompt files(`internal/library/prompt/files/`): - `post_matrix_generate.system.md` / `.user.md` - `analyze_style.system.md` / `.user.md` - `research_topics.system.md` / `.user.md` ## 7. 前端 ### 7.1 新頁面 `web/src/pages/PostProjectsPage.tsx` 取代 MissionsPage,5-step wizard(沿用 `PageTitle` / `Card` / `Field` / `Input` / `Textarea` / `Button` / `Badge`): 1. **人設**:下拉選 Persona 2. **主題**: - Textarea 輸入 seed 或想法 - 按鈕「給我主題靈感」→ `research-topics`(同步) → 顯示 3-5 張主題卡(含摘要 + 來源) - 點卡片自動填 Textarea,或手動編輯 - 「再查一次」換一批 3. **風格**(可選): - Textarea 貼 1~N 篇參考貼文(空行分隔) - 按鈕「分析風格」→ `analyze-style`(同步) → 顯示 `style_profile` 條列 - 可重貼、重新分析 4. **矩陣**: - 按鈕「產生 5 篇草稿」→ `generate-matrix` job → polling 進度 - 顯示 N 張草稿卡(Hook / Text / Angle / Sources) - 每張可:編輯 / 刪除 / 重生單篇 5. **排程**: - 勾選草稿 → 展開 datetime-local 輸入 - 「排入佇列」→ `drafts/schedule` - 顯示排程清單(沿用 publish_queue 顯示元件) ### 7.2 路由 / 導覽 - `App.tsx`:`case "posts": return `,加進 `validPages` / `accountRequiredPages` - `acAssets.ts` `navApps`:把原 `missions` 換成 `posts`(label「發文計畫」,sublabel `POST`) - `mobileNavKeys` 同步替換 - `AcIcon`:`mission` 換 `posts` SVG(鉛筆 + 對話框 stack 線條稿) - `validPages` / `accountRequiredPages` 移除 `"missions"` ### 7.3 API client `api/haixun.ts`: - 刪 types `CopyMission`、`ContentInboxItem`、`CopyMissionID` 等相關 - 刪 endpoints `copyMissions` / `createCopyMission` / `inspireCopyMission` / `startCopyMissionAnalyze` / `startCopyMissionMatrix` / `scheduleMissionDrafts` / persona copy-drafts 系列 - 新 types `PostProject` / `PostDraft` / `StyleProfile` - 新 endpoints `createPostProject` / `listPostProjects` / `researchTopics` / `analyzeStyle` / `generateMatrix` / `regenerateDraft` / `listPostDrafts` / `updatePostDraft` / `deletePostDraft` / `schedulePostDrafts` - 保留 outreach_drafts endpoints(Flow B) ## 8. Infra / deploy `deploy/docker-compose.yml`: - 刪 `worker-copy-mission-analyze` / `worker-copy-mission-expand-graph` / `worker-copy-matrix` / `worker-copy-draft` / `worker-refill-publish-inventory` 五個 service - 新 `worker-post-matrix`:`HAIXUN_JOB_WORKER_TYPE=go-generate-post-matrix` / `HAIXUN_JOB_WORKER_ID=go-generate-post-matrix` - 保留:`worker-scan-viral`、`worker-outreach-draft`、其餘通用 worker ## 9. Migration `internal/bootstrap/init.go` 加 idempotent migration block: - `db.RunCommand({dropIndexes: "publish_queue"})` 必要時重 EnsureIndexes - 寫一個 `tool` subcommand(沿用 `backend/cmd/tool`)或 init runtime 做一次性: - drop `copy_missions` collection(忽略 not exist) - drop `copy_drafts` collection(忽略 not exist) - rename `publish_queue.copy_mission_id` → `post_project_id`、`copy_draft_id` → `post_draft_id`(對存量資料,若有人 queue 還沒發) - 移除 `publish_inventory.policy.copy_mission_id` 欄位(或整 drop `publish_inventories` collection,階段 1 確認後決定) - 新 EnsureIndexes `post_projects`(tenant+owner+status)、`post_drafts`(project_id+status) 執行:首次 `make prod-install` 跑 init,後續 `make prod-rebuild` 即可。 ## 10. 執行階段 | 階段 | 動作 | 驗證 | |------|------|------| | **1. 後端解耦** | scan_viral 拔 CopyMission/CopyDraft 依賴;刪 refill_publish_inventory.go;service_context 對應移除;publish_queue / publish_inventory entity 改欄位 | `go test ./...` Flow B 仍綠 | | **2. 刪除 Flow A 後端** | api / handler / logic / model / worker template / library / prompt 全砍;`make gen-api` 重產 | `go test ./...` 通過 | | **3. 前端清 Flow A** | MissionsPage / missions 目錄 / App.tsx / Layout / acAssets / haixun.ts 清 off | `make web-build` 通過 | | **4. Infra 清理** | docker-compose.yml 刪 5 個 worker service | `docker compose config` 過 | | **5. 新後端實作** | post_project / post_draft model + usecase + repository;post_project.api + generate_post_matrix.go worker + library/post_matrix + prompt files;job template 註冊 | `go test ./...` 通過 | | **6. 新前端** | PostProjectsPage + 5-step wizard;AcIcon 新 icon | `make web-build` 通過 | | **7. DB migration** | bootstrap init drop / rename / EnsureIndexes | `docker run init` 過 | | **8. 煙霧測試** | 開 project → 靈感 → 風格 → 矩陣 → 排程 → sweeper 發;Flow B outreach 仍可;JobsPage 顯示新 type | 部署在 dev 環境實測 | 每階段結束跑 `go mod tidy && make fmt && go test ./... && make web-build`。 ## 11. 風險 / 開放問題 1. **websearch 串接**:`library/viral/crawler_search.go` 與 `node-tool-keyword-search` 服務是否適合 research-topics 用?實作時驗證呼叫 path;不順則直接打內部 node-tool-keyword-search HTTP。 2. **publish_queue rename 對存量資料**:先做 null-tolerant(舊資料視為已發 / 忽略),降低遷移負擔;正式上線前可一次性 rename script。 3. **publish_inventory 是否完全沒用**:階段 1 確認;若 Flow B 不依賴,整包砍。 4. **style_profile 形態**:採 JSON 結構化(語氣 / 長度 / 結構 / emoji / hook_style / avoid),方便前端顯示;prompt 需校調測試。 5. **矩陣批次數**:MVP 固定 N=5;後續可加 `count` query 參數,受 persona 配額上限保護。 6. **regenerate 是否保留 sources**:MVP 整篇重來,不保留;後續可加「保留 sources」選項。 ## 12. 不在本計劃範圍 - Flow B 重做(若日後需要另開計劃) - Persona model 改造(沿用) - publish_queue_sweeper 行為修改(沿用) - A/B 測試、自動成效優化(屬另一個閉環計劃)