Tasks: RAG 智慧問答整合
Input: specs/012-rag-chatbot-integration/
Prerequisites: plan.md ✅, spec.md ✅, research.md ✅, data-model.md ✅, contracts/ ✅
Tests: 每個 Phase 均包含測試任務(constitution §III 要求)
Format: [ID] [P?] [Story] Description
- [P]: 可並行執行(不同檔案、無相依)
- [Story]: 對應 spec.md 中的 User Story(US1/US2/US3)
Phase 1: Setup(基礎設施準備)
Purpose: 新增 Redis、環境變數、套件依賴,讓所有 User Story 的開發環境就緒
- [X] T001 在
docker-compose.yml中新增redisservice(image: redis:7-alpine,port6379:6379),並在backend和appservice 的depends_on加入redis - [X] T002 [P] 在
.env.example新增所有新增的環境變數:REDIS_URL,CHAT_SERVICE_URL,CHAT_SERVICE_API_KEY,EMBEDDING_MODEL_API,VECTOR_DB_NAME,VECTOR_DB_USER,VECTOR_DB_PASSWORD - [X] T003 [P] 在
pyproject.toml的scraperdependency group 新增 RAG SDK path dependency([tool.uv.sources]區塊設定rag-sdk = { path = "../rag-sdk", editable = true }) - [X] T004 [P] 在
frontend/package.json新增chatbot-plugin-ui套件依賴(local path 或 npm link),並在 frontend Docker container 中執行npm install
Checkpoint: docker compose up 啟動無誤,redis service 正常運行
Phase 2: Foundational(基礎層,阻擋所有 User Story 的前置作業)
Purpose: DB schema migration、ORM model、Domain Interface,所有 User Story 均依賴此 Phase
⚠️ CRITICAL: 所有 User Story 必須等此 Phase 完成後才能開始
- [X] T005 在
alembic/versions/新增 migration21_add_vectors_schema_and_article_chunks.py,建立vectorsschema、啟用vectorextension、建立vectors.article_chunkstable(含 ivfflat index),down migration 完整實作(見data-model.md) - [X] T006 [P] 在
models/article_chunk.py建立ArticleChunkSQLAlchemy ORM model(schema=vectors,欄位:id,article_id,chunk_index,content,embedding VECTOR(768),source_url,created_at,unique constraint(article_id, chunk_index)) - [X] T007 [P] 在
src/modules/intelligence/domain/services/rag_ingestion_service.py建立RagIngestionServiceABC(單一抽象方法ingest(self, article, full_text: str) -> None)
Checkpoint: make migrate 執行成功,vectors.article_chunks table 存在於 DB
Phase 3: US3 — 文章向量化(Pipeline 整合)(Priority: P1)
Goal: 文章完成分析後,自動向量化並存入 vectors.article_chunks,流程失敗不中斷現有 pipeline
Independent Test: 執行 make scrape SOURCE=rss LIMIT=1,完成後查詢 SELECT COUNT(*) FROM vectors.article_chunks 確認片段已寫入
Tests for US3
- [X] T008 [P] [US3] 在
src/tests/unit/modules/intelligence/application/test_rag_ingestion_handler.py建立RagIngestionHandler單元測試:mockIngestArticleForRagUseCase,驗證ArticleProcessedEvent觸發execute()呼叫;驗證拋出例外時 handler 不 re-raise、記錄 log 並 publishRagIngestionFailedEvent - [X] T009 [P] [US3] 在
src/tests/unit/infrastructure/intelligence/vector_store/test_rag_sdk_ingestion_impl.py建立RagSdkIngestionService單元測試:mockIngestProcessor,驗證ingest()以正確full_text與articles_column_values(含public_article_id、url、title、source、topic_id)呼叫 SDK - [X] T010 [US3] 在
src/tests/integration/test_rag_ingestion_pipeline.py建立 integration test(@pytest.mark.integration):建立 test Article,觸發 RAG ingestion use case,驗證冪等性(重複呼叫不產生重複片段)
Implementation for US3
- [X] T011 [US3] 在
src/infrastructure/intelligence/vector_store/__init__.py及src/infrastructure/intelligence/vector_store/rag_sdk_ingestion_impl.py實作RagSdkIngestionService(RagIngestionService),包裝IngestProcessor.ingest()(見contracts/rag-sdk.md);在src/modules/intelligence/application/use_cases/ingest_article_for_rag.py實作IngestArticleForRagUseCase(bot detection 過濾、full_text fallback 組裝) - [X] T012 [US3] 在
src/modules/intelligence/application/event_handlers/rag_ingestion_handler.py實作RagIngestionHandler:handle(self, event) -> None,包 OTel span(article.rag_ingest),失敗時logger.exception(...)不 re-raise、publishRagIngestionFailedEvent(符合 constitution §VIII Handler 命名規則) - [X] T013 [US3] 在
src/bootstrap.py中建立IngestProcessor(SDK 讀取環境變數)、RagSdkIngestionService、IngestArticleForRagUseCase、RagIngestionHandler、event_bus.subscribe(ArticleProcessedEvent, rag_handler.handle)
Checkpoint: US3 可獨立驗證 — 執行 make test(unit)與 make test-integration(integration),向量化流程測試通過
Phase 4: US1 — 文章問答(內嵌輸入欄)(Priority: P1)
Goal: 文章列表頁顯示 InlineQABarWrapper,使用者輸入問題後在下方看到回答(含 markdown 引用)
Independent Test: 瀏覽首頁,在 InlineQABar 輸入問題,確認系統回覆且回答以 markdown 格式呈現;驗證空白問題不送出請求
Tests for US1
- [X] T014 [P] [US1] 在
backend/tests/test_chat_service.py建立ChatService單元測試:mock Redis,驗證 guest/user/admin 三種身份的 rate limit 邏輯;驗證超過上限回傳RateLimitExceeded;驗證首次 guest 請求時設置__rag_gidcookie - [X] T015 [P] [US1] 在
backend/tests/test_chat_router.py建立POST /chat/completions路由測試:mockChatService,驗證 200 SSE 回應、429 rate limit 回應、X-Topic-Idheader 正確傳入 service - [X] T016 [P] [US1] 在
frontend/tests/unit/chat/InlineQABarWrapper.test.tsx建立InlineQABarWrapper單元測試(Vitest):mockuseChat,驗證AgentInput渲染、送出訊息後AnswerDisplay顯示 assistant 回答、空白輸入不觸發sendMessage
Implementation for US1
- [X] T017 [US1] 在
backend/services/chat_service.py實作ChatService:Redis rate limiting(guest cookie + user_id + IP fallback 三條路徑,admin bypass)、向外呼叫CHAT_SERVICE_URL/v1/chat/completions(注入topic_idextra field)、SSE response streaming(見contracts/chat-api.md) - [X] T018 [US1] 在
backend/routers/chat.py實作POST /chat/completionsFastAPI router:解析 JWT(optional)取 identity tier、呼叫ChatService、SSEStreamingResponse、429 exception handler、設置__rag_gidcookie(guest 首次) - [X] T019 [US1] 在
backend/main.py引入並include_routerchat router(無 prefix) - [X] T020 [P] [US1] 在
frontend/lib/chat-session.ts實作loadSession(),saveSession(messages: Message[]),clearSession()(使用sessionStorage,key:rag_chat_messages) - [X] T021 [P] [US1] 在
frontend/components/features/chat/AnswerDisplay.tsx實作顯示最新 assistant 訊息的元件(markdown 渲染含引用來源,含錯誤狀態與 loading 狀態) - [X] T022 [US1] 在
frontend/components/features/chat/InlineQABarWrapper.tsx實作 wrapper:useChat({ endpoint: '/api/proxy/chat/completions', streamAdapter: customAdapter, headers: { Auth, 'X-Topic-Id' } })、渲染AgentInput+AnswerDisplay+ quota 顯示;無 sessionStorage 持久化(in-memory only) - [X] T023 [P] [US1] 在
frontend/stories/InlineQABarWrapper.stories.tsx新增 Storybook story(default + loading + withAnswer + error 四個 variant,constitution §II 要求) - [X] T024 [US1] 在
frontend/app/page.tsx(首頁)加入<InlineQABarWrapper />,從useSession取得 token、從TopicContext取得topicId
Checkpoint: US1 可獨立驗證 — make test 後端測試通過;npm run test 前端測試通過;瀏覽首頁 InlineQABar 渲染正常
Phase 5: US2 — 浮動聊天機器人(右下角 FAB)(Priority: P2)
Goal: 所有頁面右下角顯示 FAB,展開後可多輪對話,同 browser session 保留歷史
Independent Test: 任意頁面點擊右下角 FAB,展開對話視窗;進行至少 2 輪追問,確認系統能銜接上下文;關閉再開啟視窗,確認對話紀錄保留(同 tab)
Tests for US2
- [X] T025 [P] [US2] 在
frontend/tests/unit/chat/FloatingChatbotWrapper.test.tsx建立FloatingChatbotWrapper單元測試(Vitest):mockuseChat,驗證 FAB 點擊展開/關閉、多輪訊息顯示、localStorage讀寫行為 - [ ] T026 [US2] 在
frontend/tests/integration/chat-flow.spec.ts建立 Playwright E2E 測試:FAB 展開、輸入問題、驗證串流回答顯示;換頁後回來驗證對話紀錄保留;rate limit 達上限後驗證 429 提示訊息顯示(不崩潰)
Implementation for US2
- [X] T027 [US2] 在
frontend/components/features/chat/FloatingChatbotWrapper.tsx實作 wrapper:useChat(含customAdapter解析 SSE sources 事件)、localStorage(userId標記)歷史持久化(loadFloatSession/saveFloatSessioninline 函式)、渲染FloatingChatbotPanel(messages,messageSources,onSend,isLoading,onNewChat,onAbortprops)、從useSession取 token、從TopicContext取topicId、on error 顯示 toast 錯誤(不崩潰頁面);未認證且非訪客模式時隱藏 - [X] T028 [P] [US2] 在
frontend/stories/FloatingChatbotWrapper.stories.tsx新增 Storybook story(default + conversation + loading + error 四個 variant,constitution §II 要求) - [X] T029 [US2] 在
frontend/app/layout-shell.tsx加入<FloatingChatbotWrapper />(ErrorBoundary 內側,session provider 可用)
Checkpoint: US2 可獨立驗證 — npm run test 前端測試通過;npm run test:e2e E2E 測試通過;瀏覽任意頁面 FAB 正常渲染
Phase 6: Polish & Cross-Cutting Concerns
Purpose: Observability、錯誤處理補強,跨 User Story 的收尾
- [X] T030 [P] 在
backend/routers/chat.py新增 OTel span(chat.completions),記錄identity_tier、rate_limit_remaining、topic_id、Chat Service 回應狀態(constitution §VI) - [X] T031 [P] 在
src/infrastructure/vector_store/vectorize_handler.py新增 OTel span(article.vectorize),記錄article_id、成功/失敗狀態(constitution §VI) - [X] T032 [P] 在
backend/services/chat_service.py補強 structured log:每次請求記錄identity_tier、rate_limit_counter、chat_service_status(structlog,constitution §VI) - [X] T033 在
frontend/components/features/rag/InlineQABarWrapper.tsx與FloatingChatbotWrapper.tsx補強錯誤邊界:useChat的onErrorcallback 顯示 user-friendly 提示(503→ 「問答服務暫時無法使用」;429→ 「已達每日上限」),確保不顯示技術錯誤訊息(spec SC-006) - [X] T034 [P] 更新
frontend/lib/providers/locales/en.json與zh-TW.json,新增rag.*i18n key(placeholder、error messages、empty state、assistantTitle)
Dependencies & Execution Order
Phase Dependencies
- Setup (Phase 1): 無依賴,立即開始
- Foundational (Phase 2): 依賴 Phase 1 完成
- US3 (Phase 3): 依賴 Phase 2 ─ 提供向量資料
- US1 (Phase 4): 依賴 Phase 2 ─ 後端 chat endpoint;可與 US3 並行開發(backend/frontend 不需要 US3 的向量資料即可建置和測試)
- US2 (Phase 5): 依賴 Phase 4(共用
chat-session.ts、useChat設定模式) - Polish (Phase 6): 依賴所有 User Story 完成
User Story Dependencies
- US3 (P1): Phase 2 完成後可開始,不依賴其他 US
- US1 (P1): Phase 2 完成後可開始,可與 US3 並行
- US2 (P2): 依賴 US1(共用
chat-session.ts、AnswerDisplay邏輯模式)
Within Each User Story
- 測試任務先寫(確認失敗)→ 實作 → 確認測試通過
- Models/Domain → Services → Router → Frontend → Integration
- 每個 Checkpoint 都可獨立驗證該 Story
Parallel Opportunities
- T008, T009 可並行(不同測試檔案)
- T011, T012 可在 T010(integration test 先寫確認失敗)後並行
- T014, T015, T016 可並行(測試檔案獨立)
- T017, T018 T017 先完成後 T018 依賴它
- T020, T021 可並行(不同 lib/component 檔案)
- T027, T028 可並行
- T030, T031, T032 可並行
Parallel Example: US1(Phase 4)
# Step 1 — 並行寫測試(全部先確認 FAIL):
Task T014: backend/tests/test_chat_service.py
Task T015: backend/tests/test_chat_router.py
Task T016: frontend/tests/unit/rag/InlineQABarWrapper.test.tsx
# Step 2 — 後端實作(T017 先,T018 依賴 T017):
Task T017: backend/services/chat_service.py
Task T019: backend/main.py ← 可與 T017 並行
→ Task T018: backend/routers/chat.py ← T017 完成後
# Step 3 — 前端實作(並行):
Task T020: frontend/lib/chat-session.ts
Task T021: frontend/components/features/rag/AnswerDisplay.tsx
→ Task T022: frontend/components/features/rag/InlineQABarWrapper.tsx ← T020, T021 完成後
Task T023: frontend/components/features/rag/InlineQABarWrapper.stories.tsx
# Step 4 — 整合:
Task T024: frontend/app/page.tsx(接入頁面)Implementation Strategy
MVP First(US3 + US1 Only)
- 完成 Phase 1: Setup
- 完成 Phase 2: Foundational(migrate、ORM model、domain interface)
- 完成 Phase 3: US3(向量化 pipeline)
- 完成 Phase 4: US1(InlineQABar)
- STOP & VALIDATE: 端對端問答流程可用
- 可上線驗收
Incremental Delivery
- Phase 1 + 2 → 基礎就緒
- Phase 3 → 向量資料開始累積
- Phase 4 → InlineQABar 可用(MVP!)
- Phase 5 → 浮動聊天機器人可用
- Phase 6 → 收尾
Notes
[P]= 不同檔案、無相依,可並行- US3 與 US1 後端可並行開發(US1 chat endpoint 不依賴向量資料存在)
- E2E 測試(T026)需要 US3 向量資料才能完整驗證,建議在 US3 完成後執行
make migrate須在docker compose exec job_service中執行(constitution §IV)- 所有
npm install須在 frontend Docker container 中執行(memory:feedback_npm_install_docker)