# External Query 独立主链升级计划 ## 摘要 把外部查询从通用 AdaptiveRAG 回答链里拆出来,做成一条独立的“外部事实链”:`规划 -> 取证 -> 验证 -> 呈现`。这次升级的目标是让 `route_mode=external` 后只依赖外部证据作答,不再落到 `industry_trend / external_trend` prompt,也不再混入 `project manifest / system snapshot / 内部检索上下文`。 ## 关键改动 ### 1. 外部链路边界固定 - 保留现有 `Query Route Gateway` 作为上游拍板;一旦 `effective_source_policy=external`,后续回答链默认只走外部主链,不再回到通用 scene prompt 选择。 - `ExternalQueryPlanner` 只负责产出结构化 `ExternalQueryPlan`;planner 的 fallback 改成保守模式: - 仅复用上游 `query_semantics` - 不再用硬性词语、对象词表或正则去猜 `query_kind / time_range / location` - 结构化 planner 失败时,默认 `query_kind=generic_web`、`answer_contract=summary_with_sources`、`search_queries=[原问题]` - `ExternalQueryGateway` 只做 provider 选择与证据收集;`verifier` 只判断证据是否足够支撑回答,不再承担语义重判或 internal/external 二次裁决。 ### 2. 能力选路替代固定顺序 - 在 `backend/app/services/external_query_service/` 内把 adapter 能力声明收紧为受控合同: - `provider_type`: `fact_api | live_search | doc_search` - `supported_query_kinds` - `supports_freshness` - `supports_page_fetch` - gateway 排序固定为“能力优先”而不是“注册顺序优先”: - `fact_api` 优先处理天气/汇率/股价等结构化事实 - `live_search` 处理实时网页事实 - `doc_search` 只处理文档/说明类问题 - 当前没有专用天气 API 时,天气类默认优先 `live_search`;MCP 文档型 adapter 不再先占坑。 - MCP 和 live search 都保留“搜索结果 -> 抓目标页正文 -> chunk 打分 -> best snippet”这套正文取证链。 ### 3. 外部证据与呈现彻底分层 - 在 `schemas.py` 新增 `ExternalContextPacket`,作为 external-only 的回答输入,字段固定为: - `question` - `plan` - `adapter_attempts` - `verified_evidence` - `verifier` - `uncertainties` - 新增 `ExternalAnswerPresenter`,替代 `_generate_answer()` 里对 external route 走通用 `_select_scene_prompt()`。 - presenter 只按 `answer_contract` 选 3 类 prompt,不再使用 `industry_trend / external_trend`: - `direct_fact` - `summary_with_sources` - `compare_with_sources` - presenter 默认只喂: - 用户问题 - 外部证据片段 - 来源摘要 - 时间/范围/不确定项 - presenter 明确禁止喂: - `manifest_text` - `project_context` - `system_snapshot` - `user_snapshot` - 内部 retrieval block ### 4. verifier 改成“证据校验器”,不是“语义裁判” - 把当前 `gateway.py` 里的 bundle 校验拆成独立 verifier 组件,职责只保留: - 外部来源合法性 - 目标对象一致性 - 时间范围一致性 - 多来源是否足以支撑回答 - 对象一致性继续走通用归一化和 bundle 评分,但不允许回退到硬编码对象词表、关键词探针或正则模式。 - 证据不足时统一返回 `clarify / insufficient_evidence`,不再误落到“行业趋势回答”这种假阳性路径。 ### 5. 与现有 AdaptiveRAG 的接缝 - `mixin_answer_retrieval.py` 保留 `_execute_external_query_gateway()`,但返回值从“retrieved rows + meta”扩成 `ExternalContextPacket` 所需信息。 - `mixin_deep_guard_answer.py` 在 `effective_source_policy=external` 时直接走 `ExternalAnswerPresenter`,绕过: - `_resolve_answer_scene_semantics()` 对 external 的 `industry_trend/external_trend` 映射 - `_generate_answer()` 里默认注入 manifest 的逻辑 - `Answer Presentation Policy` 保持现有客户可见合同: - 正文前缀仍是 `外部实时查询:` - 来源只显示中文化展示字段,不暴露内部 source label/raw metadata ## 主要接口与类型 - `ExternalQueryPlan` 保留现有字段,语义收紧为“planner 产出的结构化取证计划”,不再依赖正则推断补字段。 - 新增 `ExternalContextPacket` 作为 external presenter 的唯一输入。 - adapter 注册合同新增能力字段,供 gateway 统一排序。 - external presenter 输出仍是最终中文正文,不改上层 API 响应包络。 ## 测试与验收 - planner: - 结构化 planner 失败时,不使用正则或硬词表补 `weather/news/company` - fallback 仍能产出可执行 `search_queries` - gateway: - 天气 query 优先 `live_search` - 文档 query 优先 `mcp/doc_search` - MCP/live_search 都能抓目标页正文,而不是只用标题摘要 - verifier: - `湛江市 / 湛江` 这类归一化对象不再误杀 - 时间范围不匹配时明确 `insufficient_evidence` - 证据不足时返回 `clarify`,不生成假结论 - presenter: - external route 不再命中 `industry_trend / external_trend` - prompt 输入中不包含 `[M] 项目画像`、模块规模、知识图谱、system snapshot - 最终回答只基于外部 evidence 生成 - 端到端: - 从飞书入口发“湛江市未来五天的天气”,整条链应表现为: - `route_mode=external` - provider 选中 `live_search` - verifier 通过 - presenter 输出 `外部实时查询:...` - 回答内带来源摘要且无内部上下文污染 ## 假设与默认 - 这期只改“纯 external route”的主链,不在本期实现“外部事实 + 内部能力判断 + plan_mode 动作”的完整混合编排。 - 当前没有专用天气 API 时,实时公开事实默认靠 `live_search + 网页正文抓取` 承担。 - 若后续接入专用事实 API,只需要新增 adapter,不改 planner/verifier/presenter 的边界。 - 文档同步纳入本期:至少更新 `docs/architecture/RUNTIME_GUARDRAILS.md` 与 `docs/architecture/CODE_REFERENCE_INDEX.md`,明确 external-only 上下文和 presenter 边界。