14 KiB
Three-Mode Interaction And Context Upgrade 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: 让 PMFlow 的三类用户可见模式在同一套交互骨架下可见、可控、可纠偏:planmode(requirement_plan/draft) 先做顶层状态显化,query.answer 再按 internal / external 给出显式来源合同与上下文续写提示。
Architecture: 先锁共享基线,不改语义边界:保留 plan_mode gate -> non-plan query.answer -> internal/external split 的现有层级,只把后端已经存在的 route_decision / routing_snapshot / session_state / requirement plan status 暴露给前端。前端新增一套轻量状态条和回答元信息卡,查询侧挂在 FloatingQAChat,planmode 侧挂在 Inbox 与 AI PMO handoff 结果,不另起一套聊天壳。
Tech Stack: FastAPI, SQLAlchemy 2.0, PMFlow AdaptiveRAG, AI PMO kernel, React 18, TypeScript, Vitest, pytest
Baseline File Map
Shared Query Contract
- Modify:
backend/app/api/rag_ask_contract.py- 保持
/rag/ask现有包络不变,补齐给前台可消费的route_decision / clarification_code / clarification_candidates / answer_mode / grounding_status / policy_version / citations / sources_used / evidence_pack规范化输出。
- 保持
- Modify:
backend/app/services/adaptive_rag_service/mixin_qa_session_list_ops_p1.py- 让会话详情把每轮回答的 route / evidence / answer mode 元信息带出来,不再只有
question / answer / sources。
- 让会话详情把每轮回答的 route / evidence / answer mode 元信息带出来,不再只有
- Modify:
frontend/src/api/aiSteward.ts- 前端类型补齐 richer QA payload,并新增
getQASessionState(sessionId)读取last_subject / pending_clarification / rewrite_history。
- 前端类型补齐 richer QA payload,并新增
- Modify:
backend/tests/test_ai_pmo_qa_http_mainline_e2e.py- 锁
/rag/askHTTP 响应合同。
- 锁
- Modify:
backend/tests/test_ai_pmo_ingress_mainline.py- 锁 QA session detail / state replay 合同。
Query UI Shell
- Create:
frontend/src/components/ai-steward/QAConversationStatusBar.tsx- 显示当前对象、当前来源模式、待澄清状态、是否仍在继承上一轮上下文。
- Create:
frontend/src/components/ai-steward/QAResponseMetaCard.tsx- 每条回答下显示
内部/外部/待澄清、grounding 状态、来源摘要、推荐下一步。
- 每条回答下显示
- Modify:
frontend/src/components/ai-steward/FloatingQAChat.tsx- 接入 status bar、message-level meta card、route clarify 快捷动作、session state 拉取与“清空上下文/新话题”入口。
- Create:
frontend/src/components/ai-steward/FloatingQAChat.test.tsx- 锁状态条、route clarify 动作、元信息卡渲染。
Plan Mode Surface
- Modify:
backend/app/api/ai_pmo_api/helpers_inbox_payload.py- 给 inbox work item summary 增加 planmode 相关轻量字段,例如
plan_mode / plan_status / requirement_plan_context_summary。
- 给 inbox work item summary 增加 planmode 相关轻量字段,例如
- Modify:
backend/app/api/ai_pmo_api/routes_core.py- 统一
process-text与work-items输出里的 planmode 相关字段,避免前端靠reply_template_key猜。
- 统一
- Modify:
frontend/src/api/aiPmo.ts- 补齐 AI PMO result / work item summary 的 planmode 类型。
- Modify:
frontend/src/types/inbox.ts- 让 inbox item view model 能承载
plan_status / plan_mode / requirement_plan_context_summary。
- 让 inbox item view model 能承载
- Modify:
frontend/src/components/inbox/InboxListItem.tsx- 在详情卡里显式展示“当前需求模式状态 / 下一步动作 / 草稿待确认”。
- Modify:
frontend/src/pages/inbox/InboxPage.tsx- 把 mounted run / work item 映射到统一的 planmode 视图文案。
- Create:
frontend/src/components/inbox/InboxListItem.test.tsx- 锁 planmode 状态渲染与 CTA 文案。
Docs And Inventory
- Modify:
CLAUDE.md - Modify:
docs/architecture/ARCHITECTURE_INVENTORY.md - Modify:
docs/architecture/CODE_REFERENCE_INDEX.md - Modify:
docs/architecture/RUNTIME_GUARDRAILS.md
Task 1: Lock The Shared QA Contract
Files:
-
Modify:
backend/tests/test_ai_pmo_qa_http_mainline_e2e.py -
Modify:
backend/tests/test_ai_pmo_ingress_mainline.py -
Modify:
frontend/src/api/aiSteward.ts -
Step 1: Write the failing
/rag/askcontract test
Add an HTTP contract assertion that /rag/ask returns:
assert payload["data"]["route_decision"]["effective_source_policy"] == "internal"
assert payload["data"]["clarification_code"] is None
assert payload["data"]["answer_mode"] == "fast_path"
assert payload["data"]["evidence_pack"]["summary"]["conclusion"] == "当前主链状态正常。"
- Step 2: Write the failing session detail/state test
Add sqlite-backed tests that prove:
detail = await service.get_qa_session_detail(session_id=session_id, user_id=101)
assert detail["messages"][0]["route_decision"]["effective_source_policy"] == "external"
assert detail["messages"][0]["grounding_status"] == "insufficient_evidence"
state = await service.get_session_state_replay(session_id=session_id, user_id=101)
assert state["last_subject"] == "Alpha项目"
assert state["pending_clarification"]["kind"] == "query_route_selection"
- Step 3: Run the focused backend tests to confirm they fail
Run:
cd /Users/yuuuu/Documents/AIProjectManger && backend/venv/bin/python -m pytest backend/tests/test_ai_pmo_qa_http_mainline_e2e.py -k "rag_ask" -v
cd /Users/yuuuu/Documents/AIProjectManger && backend/venv/bin/python -m pytest backend/tests/test_ai_pmo_ingress_mainline.py -k "qa_session_detail or session_state_replay" -v
Expected: FAIL because the richer QA/session contract is not exposed yet.
Task 2: Implement The Shared QA Metadata Baseline
Files:
-
Modify:
backend/app/api/rag_ask_contract.py -
Modify:
backend/app/services/adaptive_rag_service/mixin_qa_session_list_ops_p1.py -
Modify:
frontend/src/api/aiSteward.ts -
Test:
backend/tests/test_ai_pmo_qa_http_mainline_e2e.py -
Test:
backend/tests/test_ai_pmo_ingress_mainline.py -
Step 1: Normalize
/rag/askpayload fields
Keep the existing response envelope, but explicitly preserve:
payload["route_decision"] = dict(payload.get("route_decision") or {})
payload["clarification_code"] = str(payload.get("clarification_code") or "").strip() or None
payload["clarification_candidates"] = _normalize_dict_list(payload.get("clarification_candidates"))
- Step 2: Extend QA session detail payload
When serializing session messages, add:
"route_decision": row.routing_snapshot.get("query_route") if isinstance(row.routing_snapshot, dict) else {},
"answer_mode": row.answer_mode,
"grounding_status": row.grounding_status,
"policy_version": row.policy_version,
"citations": row.citations or [],
"sources_used": row.sources_used or [],
"evidence_pack": row.evidence_pack or {},
- Step 3: Update front-end request/response types
Extend QAAskResponse and QASessionMessage to accept:
route_decision?: Record<string, unknown> | null;
clarification_code?: string | null;
clarification_candidates?: Array<Record<string, unknown>>;
answer_mode?: string | null;
grounding_status?: string | null;
policy_version?: string | null;
citations?: Array<Record<string, unknown>>;
sources_used?: Array<Record<string, unknown>>;
evidence_pack?: Record<string, unknown> | null;
Also add:
async getQASessionState(sessionId: number): Promise<QASessionStateReplay>
- Step 4: Re-run the focused backend tests
Run:
cd /Users/yuuuu/Documents/AIProjectManger && backend/venv/bin/python -m pytest backend/tests/test_ai_pmo_qa_http_mainline_e2e.py -k "rag_ask" -v
cd /Users/yuuuu/Documents/AIProjectManger && backend/venv/bin/python -m pytest backend/tests/test_ai_pmo_ingress_mainline.py -k "qa_session_detail or session_state_replay" -v
Expected: PASS
Task 3: Build The Query Status Shell
Files:
-
Create:
frontend/src/components/ai-steward/QAConversationStatusBar.tsx -
Create:
frontend/src/components/ai-steward/QAResponseMetaCard.tsx -
Modify:
frontend/src/components/ai-steward/FloatingQAChat.tsx -
Create:
frontend/src/components/ai-steward/FloatingQAChat.test.tsx -
Test:
frontend/src/components/ai-steward/FloatingQAChat.test.tsx -
Step 1: Write the failing front-end tests
Cover these cases:
it("shows current subject and source mode in the status bar", ...)
it("renders route clarify actions when clarification_code=query_route_clarify", ...)
it("renders answer meta card with grounding and source summary", ...)
- Step 2: Run the front-end test to verify it fails
Run:
cd /Users/yuuuu/Documents/AIProjectManger/frontend && npx vitest run src/components/ai-steward/FloatingQAChat.test.tsx
Expected: FAIL because the new status shell does not exist yet.
- Step 3: Implement the session-level status bar
Render a compact bar above the message list with:
<QAConversationStatusBar
currentSubject={sessionState.last_subject}
effectiveSourcePolicy={lastRouteDecision?.effective_source_policy}
pendingClarification={sessionState.pending_clarification}
onClearContext={startNewChat}
/>
- Step 4: Implement the per-answer meta card
For each message, render a compact card that surfaces:
<QAResponseMetaCard
routeDecision={msg.route_decision}
groundingStatus={msg.grounding_status}
sourcesUsed={msg.sources_used}
evidencePack={msg.evidence_pack}
/>
- Step 5: Wire route clarify actions
When the last answer returns clarification_code === "query_route_clarify", show two quick actions that submit the existing protocol text:
onChooseInternal={() => runQuestionRequest({ prompt: "B" })}
onChooseExternal={() => runQuestionRequest({ prompt: "A" })}
- Step 6: Re-run the front-end test
Run:
cd /Users/yuuuu/Documents/AIProjectManger/frontend && npx vitest run src/components/ai-steward/FloatingQAChat.test.tsx
Expected: PASS
Task 4: Surface Plan Mode In AI PMO And Inbox
Files:
-
Modify:
backend/tests/test_ai_pmo_ingress_mainline.py -
Modify:
backend/app/api/ai_pmo_api/helpers_inbox_payload.py -
Modify:
backend/app/api/ai_pmo_api/routes_core.py -
Modify:
frontend/src/api/aiPmo.ts -
Modify:
frontend/src/types/inbox.ts -
Modify:
frontend/src/components/inbox/InboxListItem.tsx -
Modify:
frontend/src/pages/inbox/InboxPage.tsx -
Create:
frontend/src/components/inbox/InboxListItem.test.tsx -
Test:
frontend/src/components/inbox/InboxListItem.test.tsx -
Step 1: Write the failing backend test for planmode summary fields
Add coverage that proves work item summary/detail exposes:
assert item["plan_mode"] is True
assert item["plan_status"] == "collecting"
assert item["requirement_plan_context_summary"]["title"] == "换季搭配推荐"
- Step 2: Run the targeted backend test to verify it fails
Run:
cd /Users/yuuuu/Documents/AIProjectManger && backend/venv/bin/python -m pytest backend/tests/test_ai_pmo_ingress_mainline.py -k "plan_mode or requirement_plan_context_summary" -v
Expected: FAIL because inbox/process-text payloads do not expose these fields yet.
- Step 3: Serialize planmode summary fields in AI PMO payloads
Normalize a small summary shape instead of leaking full runtime packet:
"plan_mode": bool(summary.get("plan_mode")),
"plan_status": normalized_plan_status,
"requirement_plan_context_summary": {
"title": card.get("title"),
"goal": card.get("goal"),
"missing_info": card.get("missing_info", [])[:3],
}
- Step 4: Update front-end inbox types and rendering
Show:
<Badge variant="outline">{item.source_work_item?.plan_status || "draft_pending_confirm"}</Badge>
<div>当前模式:需求模式</div>
<div>下一步:补信息 / 选 A-B-C / 草稿确认</div>
Keep the existing draft actions intact.
- Step 5: Write and run the front-end inbox test
Run:
cd /Users/yuuuu/Documents/AIProjectManger/frontend && npx vitest run src/components/inbox/InboxListItem.test.tsx
Expected: FAIL first, then PASS after the new planmode summary UI is wired.
Task 5: Verification And Docs Sync
Files:
-
Modify:
CLAUDE.md -
Modify:
docs/architecture/ARCHITECTURE_INVENTORY.md -
Modify:
docs/architecture/CODE_REFERENCE_INDEX.md -
Modify:
docs/architecture/RUNTIME_GUARDRAILS.md -
Step 1: Update docs for the new three-mode front-end contract
Record:
-
plan_modeis still the top-level gate -
internal / externalremain submodes underquery.answer -
FloatingQAChatnow shows context/source status instead of only answer text -
inbox exposes requirement-plan summary state
-
Step 2: Run focused backend verification
Run:
cd /Users/yuuuu/Documents/AIProjectManger && backend/venv/bin/python -m pytest \
backend/tests/test_ai_pmo_qa_http_mainline_e2e.py \
backend/tests/test_ai_pmo_ingress_mainline.py \
backend/tests/test_adaptive_rag_mainline_contract.py -v
Expected: PASS
- Step 3: Run backend compile verification
Run:
cd /Users/yuuuu/Documents/AIProjectManger && backend/venv/bin/python -m py_compile \
backend/app/api/rag_ask_contract.py \
backend/app/services/adaptive_rag_service/mixin_qa_session_list_ops_p1.py \
backend/app/api/ai_pmo_api/helpers_inbox_payload.py \
backend/app/api/ai_pmo_api/routes_core.py
Expected: exit 0
- Step 4: Run front-end verification
Run:
cd /Users/yuuuu/Documents/AIProjectManger/frontend && npx vitest run \
src/components/ai-steward/FloatingQAChat.test.tsx \
src/components/inbox/InboxListItem.test.tsx
cd /Users/yuuuu/Documents/AIProjectManger/frontend && npx tsc --noEmit
cd /Users/yuuuu/Documents/AIProjectManger/frontend && npm run build
Expected: PASS