PMFlow/docs/superpowers/plans/2026-04-13-chat-external-fa...

160 lines
7.9 KiB
Markdown

# Chat External Fact QA Guardrails Implementation Plan
> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking.
**Goal:** Stop chat-mode Feishu Q&A from using internal PMFlow evidence to answer external latest/fact questions, and make factual pushback stay in natural chat instead of falling into AI PMO correction prose.
**Architecture:** Keep requirement-plan and draft-confirm chains unchanged. Implement a narrow chat-only policy inside Adaptive RAG that detects external factual chat questions, isolates retrieval from internal project noise, downgrades safely when no live external evidence exists, and keeps follow-up fact challenges in the same chat scene.
**Tech Stack:** FastAPI backend, Adaptive RAG mixins, PostgreSQL-backed QA records, control-plane routing, pytest.
---
### Task 1: Add Chat External-Fact Policy Flags
**Files:**
- Modify: `backend/app/services/adaptive_rag_service/mixin_answer_mainline.py`
- Modify: `backend/app/services/adaptive_rag_service/mixin_followup_anchor.py`
- Test: `backend/tests/test_adaptive_rag_mainline_contract.py`
- [ ] **Step 1: Write failing tests for chat-only external fact follow-up**
Add tests that prove:
- an external-trend/latest question is marked as chat external fact policy
- a follow-up like “不对吧,已经有 xxx 了” anchored to a prior `external_trend` / `industry_trend` answer stays in chat QA and does not inherit AI PMO correction behavior
- [ ] **Step 2: Resolve anchored prior answer scene from QA record**
Read the anchored `QARecord` via existing follow-up resolution and derive the prior prompt variant from `policy_version` so chat logic can tell whether the previous reply was `external_trend` or `industry_trend`.
- [ ] **Step 3: Add a narrow chat external-fact policy helper**
Implement a helper in `mixin_answer_mainline.py` that derives flags such as:
- `external_fact_chat`
- `requires_freshness`
- `chat_fact_challenge`
- `allow_internal_refine`
These flags must be derived from current scene + question + anchored prior QA record, not from requirement-plan state.
- [ ] **Step 4: Thread the policy flags through answer runtime**
Pass the new flags into retrieval/execution flow without changing requirement-plan ingress or workflow planner behavior.
- [ ] **Step 5: Run focused tests**
Run: `pytest backend/tests/test_adaptive_rag_mainline_contract.py -k "external or correction or followup" -v`
### Task 2: Isolate Retrieval for External Latest Questions
**Files:**
- Modify: `backend/app/services/adaptive_rag_service/mixin_multistep_flow.py`
- Modify: `backend/app/services/adaptive_rag_service/mixin_multistep_core.py`
- Modify: `backend/app/services/adaptive_rag_service/mixin_answer_retrieval.py`
- Test: `backend/tests/test_adaptive_rag_mainline_contract.py`
- [ ] **Step 1: Write failing tests for retrieval contamination**
Add regression tests that prove:
- `external_trend` / latest questions do not expand into `Project#104`, `#codex-plan*`, module paths, or feature catalog hints
- second-pass query refinement does not append internal entities/modules for this chat policy
- [ ] **Step 2: Disable internal second-pass hint expansion for external fact chat**
When `external_fact_chat=true`, skip internal refinement based on manifest, graph entities, project anchors, feature catalog, and symbol hints.
- [ ] **Step 3: Restrict evidentiary domains**
For external factual latest questions, internal `db / graph / pmflow / feishu / feature_catalog` sources must not be used as grounding for the external fact itself.
Allowed behavior:
- explicit external URL/doc evidence may ground the answer
- internal PMFlow context may only support “why this matters to your tenant/project”, never “what is the latest external fact”
- [ ] **Step 4: Preserve existing internal QA behavior**
Make sure non-external chat questions and PMFlow internal feature/status questions still use the current retrieval stack unchanged.
- [ ] **Step 5: Run focused tests**
Run: `pytest backend/tests/test_adaptive_rag_mainline_contract.py -k "external_trend or industry_trend or retrieval" -v`
### Task 3: Add Freshness Guard and Natural Chat Correction Style
**Files:**
- Modify: `backend/app/services/adaptive_rag_service/mixin_deep_guard_rules.py`
- Modify: `backend/app/services/adaptive_rag_service/mixin_answer_retrieval.py`
- Modify: `backend/app/services/adaptive_rag_service/mixin_deep_guard_answer.py`
- Modify: `backend/app/config/ai_prompts_catalog/part_scenes_06.py`
- Test: `backend/tests/test_adaptive_rag_mainline_contract.py`
- Test: `backend/tests/test_draft_confirm_handler.py`
- [ ] **Step 1: Write failing tests for unsafe “grounded latest” replies**
Cover these cases:
- no external evidence + question asks “最新/目前/截至现在” => answer must explicitly say no real-time data
- grounding status must not be `grounded`
- reply must not inject project IDs, internal routes, or AI config guidance unless user explicitly asked for tenant impact
- [ ] **Step 2: Add external freshness guard**
Extend guard logic so external factual latest questions are blocked or downgraded when there is no explicit external evidence. Static internal sources must not satisfy freshness requirements.
- [ ] **Step 3: Rewrite answer fallback shape**
When no live external evidence exists, answer should look like normal chat:
- first sentence admits lack of real-time data / inability to confirm latest
- second sentence may give bounded generic context
- optional final sentence can ask whether to check tenant config separately
Forbidden output in this policy:
- “结论 / 证据链 / 待确认清单” tribunal style
- treating internal tags or knowledge-graph entities as external market evidence
- [ ] **Step 4: Strengthen prompt constraints as secondary guard**
Update `PROMPT_EXTERNAL_TREND_QA` / `PROMPT_INDUSTRY_TREND_QA` so they explicitly say:
- internal PMFlow graph/feature/catalog sources are not external search results
- if freshness is required and no external evidence is provided, do not claim exact latest version/model status
- [ ] **Step 5: Review deep-routing behavior**
Do not let this path silently force low-first deep reasoning for fact challenges. If needed, keep the safe downgrade path on low tier now; if quality still falls short, introduce a dedicated external-fact answer function in a separate patch rather than raising all chat traffic.
- [ ] **Step 6: Run focused tests**
Run:
- `pytest backend/tests/test_adaptive_rag_mainline_contract.py -k "freshness or external or correction" -v`
- `pytest backend/tests/test_draft_confirm_handler.py -k "industry_trend or external_trend" -v`
### Task 4: Verify Runtime Safety and Sync Guardrail Docs
**Files:**
- Modify: `docs/architecture/RUNTIME_GUARDRAILS.md`
- Test: `backend/tests/test_adaptive_rag_mainline_contract.py`
- [ ] **Step 1: Add runtime guardrail note**
Document that chat-mode external factual latest questions cannot treat PMFlow internal graph/catalog/project evidence as external fact grounding, and anchored factual pushback must stay in chat unless explicitly tied to requirement/draft workflow.
- [ ] **Step 2: Run broader regression checks**
Run:
- `python -m py_compile backend/app`
- `pytest backend/tests/test_adaptive_rag_mainline_contract.py -v`
- [ ] **Step 3: Sanity-check that requirement-plan mainline is untouched**
Run a narrow requirement-plan regression slice:
- `pytest backend/tests/test_ai_pmo_ingress_mainline.py -k "requirement_plan or draft" -v`
- [ ] **Step 4: Commit**
```bash
git add backend/app/services/adaptive_rag_service backend/app/config/ai_prompts_catalog/part_scenes_06.py backend/tests/test_adaptive_rag_mainline_contract.py backend/tests/test_draft_confirm_handler.py docs/architecture/RUNTIME_GUARDRAILS.md docs/superpowers/plans/2026-04-13-chat-external-fact-qa-guardrails.md
git commit -m "fix: harden chat external fact QA guardrails"
```