8.2 KiB
External News Evidence 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: Make external freshness/news questions answerable with article-grade evidence, while keeping the current fail-closed external contract and without adding runtime keyword tables or business regex routing.
Architecture: Keep route_mode=external and the current verifier/presenter boundary, but strengthen the evidence acquisition stage. The next pass should expand search candidates into real article pages, normalize article metadata into structured evidence, separate attempted_sources from answer_evidence, and only let the presenter see customer-safe, article-backed sources.
Tech Stack: Python 3.11, FastAPI, PMFlow AdaptiveRAG, Pydantic, pytest, httpx, HTMLParser/JSON-LD metadata
Task 1: Lock The Next-Stage News Retrieval Contract With Tests
Files:
-
Modify:
backend/tests/test_mcp_external_query_bridge.py -
Modify:
backend/tests/test_external_answer_presenter_contract.py -
Modify:
backend/tests/test_adaptive_rag_mainline_contract.py -
Step 1: Add a failing test for live-search candidate expansion into article pages
- Simulate a search result that points to a category page plus one resolvable article page.
- Expect the adapter/gateway to keep only the article page as
verified_evidence.
-
Step 2: Add a failing test that
sources_usedonly includes answer evidence- Feed one article page and two discarded candidates.
- Expect customer-visible
sources_used/来源摘要to exclude discarded candidates.
-
Step 3: Add a failing test for absolute-date news output
- Feed
published_at. - Expect the presenter prompt/output contract to use
YYYY-MM-DDor日期未明确, never relative time like3天前.
- Feed
-
Step 4: Run the targeted tests to confirm they fail for the right reason
- Run:
PYTHONPATH=backend backend/venv/bin/python -m pytest backend/tests/test_mcp_external_query_bridge.py -v - Run:
PYTHONPATH=backend backend/venv/bin/python -m pytest backend/tests/test_external_answer_presenter_contract.py -v - Run:
PYTHONPATH=backend backend/venv/bin/python -m pytest backend/tests/test_adaptive_rag_mainline_contract.py -v
- Run:
Task 2: Add Article Expansion And Structured Candidate Hydration
Files:
-
Create:
backend/app/services/external_query_service/article_expander.py -
Modify:
backend/app/services/external_query_service/page_metadata.py -
Modify:
backend/app/services/external_query_service/adapters/live_search.py -
Modify:
backend/app/services/external_query_service/adapters/mcp.py -
Modify:
backend/app/services/external_query_service/schemas.py -
Modify:
backend/app/services/adaptive_rag_service/mixin_conversation_utils_a_p1.py -
Step 1: Add a focused article-expansion helper
- Accept one fetched page payload.
- Extract canonical/linked article candidates from structured HTML signals only (
link rel=canonical, JSON-LD, standard anchors), not business keyword heuristics.
-
Step 2: Extend evidence schemas for staged acquisition
- Add fields such as
evidence_stage,attempted_url,canonical_url,article_url, andselection_reason. - Keep
display_label/display_summary/page_kind/published_atas the customer-safe surface.
- Add fields such as
-
Step 3: Teach live-search hydration to do a second hop when first-page evidence is landing/category
- Fetch the top candidate page.
- If it is not article-grade, expand one bounded hop to article candidates and fetch those instead.
- Keep the fetch count bounded.
-
Step 4: Reuse the same bounded second-hop logic for MCP target URLs
- MCP search can still return landing/detail wrappers.
- Hydration should converge on article-grade evidence without changing upstream semantics.
-
Step 5: Re-run the targeted news retrieval tests
- Run:
PYTHONPATH=backend backend/venv/bin/python -m pytest backend/tests/test_mcp_external_query_bridge.py -v
- Run:
Task 3: Separate Attempted Sources From Answer Evidence End-To-End
Files:
-
Modify:
backend/app/services/external_query_service/gateway.py -
Modify:
backend/app/services/adaptive_rag_service/mixin_answer_retrieval.py -
Modify:
backend/app/services/adaptive_rag_service/mixin_answer_payload.py -
Modify:
backend/app/services/adaptive_rag_service/mixin_query_route_gateway.py -
Step 1: Persist
adapter_attemptsand discarded candidates as debug-only metadata- Do not expose them in customer-visible
sources_used.
- Do not expose them in customer-visible
-
Step 2: Make
execution.evidencerepresent only selected answer evidence- Keep attempted/discarded candidates in
meta. - Preserve current fail-closed semantics when no answer evidence remains.
- Keep attempted/discarded candidates in
-
Step 3: Build customer-visible sources from
display_labelonlysources_used/来源摘要should come from selected answer evidence.raw_source_labelstays admin/debug only.
-
Step 4: Re-run the answer payload and route-contract tests
- Run:
PYTHONPATH=backend backend/venv/bin/python -m pytest backend/tests/test_adaptive_rag_mainline_contract.py -v
- Run:
Task 4: Tighten The News Presentation Contract Without Relaxing Safety
Files:
-
Modify:
backend/app/services/external_query_service/presenter.py -
Modify:
backend/app/config/ai_prompts_catalog/part_scenes_06.py -
Modify:
backend/app/config/ai_prompts.py -
Modify:
backend/app/config/ai_prompt_registry.py -
Step 1: Keep
external_news_briefas the only news presenter contract- Do not fall back to generic summary style for news/freshness questions.
-
Step 2: Force date rendering through structured fields
- Use
published_at -> YYYY-MM-DD. - If absent, explicitly render
日期未明确.
- Use
-
Step 3: Keep the prompt surface customer-safe
- Evidence blocks may include
display_label,display_summary,title,published_at,target_url. - Never feed
URL[...],MCP[...], orraw_source_label.
- Evidence blocks may include
-
Step 4: Re-run presenter and prompt-registry tests
- Run:
PYTHONPATH=backend backend/venv/bin/python -m pytest backend/tests/test_external_answer_presenter_contract.py -v - Run:
PYTHONPATH=backend backend/venv/bin/python -m pytest backend/tests/test_ai_prompt_registry_contract.py -v
- Run:
Task 5: Verify On The Real Backend And Sync Runtime Docs
Files:
-
Modify:
CLAUDE.md -
Modify:
docs/architecture/ARCHITECTURE_INVENTORY.md -
Modify:
docs/architecture/CODE_REFERENCE_INDEX.md -
Modify:
docs/architecture/RUNTIME_GUARDRAILS.md -
Modify:
docs/architecture/DATAFLOW.md -
Step 1: Update docs for second-hop article expansion and answer-evidence separation
- Document that
newsgrounding now requires article-grade evidence. - Document that attempted sources and answer sources are different surfaces.
- Document that
-
Step 2: Run compile + targeted regression
- Run:
PYTHONPATH=backend /opt/homebrew/bin/python3 -m py_compile backend/app/services/external_query_service/article_expander.py backend/app/services/external_query_service/page_metadata.py backend/app/services/external_query_service/adapters/live_search.py backend/app/services/external_query_service/adapters/mcp.py backend/app/services/external_query_service/gateway.py backend/app/services/external_query_service/presenter.py backend/app/services/adaptive_rag_service/mixin_answer_retrieval.py backend/app/services/adaptive_rag_service/mixin_answer_payload.py backend/app/services/adaptive_rag_service/mixin_query_route_gateway.py
- Run:
-
Step 3: Rebuild the backend container so Python changes really load
- Run:
docker compose up -d --build backend
- Run:
-
Step 4: Live-check a real news query and capture the raw JSON
- Ask:
最近 AI 行业有哪些值得关注的新闻? - Expected:
- Best case: grounded answer with article-grade sources and customer-safe labels.
- Safe fallback: explicit
insufficient_evidence, but no machine labels and no landing-page hallucination.
- Ask:
-
Step 5: Record any remaining provider-gap separately
- If live-search still cannot surface article-grade pages reliably, document the exact gap before adding a new provider.