Skip to content

fix(pipeline): 候选方案与 confirm 前强校验用户 intent 资源覆盖 - #306

Closed
ruanzhengxin-zhuxing wants to merge 2 commits into
mainfrom
harness/b392d104
Closed

ruanzhengxin-zhuxing wants to merge 2 commits into
mainfrom
harness/b392d104

Conversation

@ruanzhengxin-zhuxing

Copy link
Copy Markdown
Collaborator

背景

Session 7be8bcd32aa44fd399f3e5bf7ec7db7d 等三个会话(UserId 1754580903499898)暴露同一个问题:intent_parsing 已经正确解析出用户声明的全部资源(ECS/NATGateway/EIP;VPC/VSwitch/ECS;NatGateway/SnatEntry/EIP),但最终候选模板与 confirm_and_select 选项只保留了 VPC/VSwitch,ECS/SLB/NAT/EIP 被静默丢弃,而候选名称仍然声称包含这些资源(例如「ECS+NAT公网方案」)。用户因此在不知情的情况下确认了不完整的方案。

AnalysisId: sarpt-auto-2026072900

根因

两处都缺少把 resource_intents 当作"必须交付清单"的机制:

  1. architecture_planning 没有任何 completion_guards,且 iac-aliyun-architecture SKILL 中 resource_intents 只是可选字段,「资源生命周期约束」章节只有禁止性规则(不要把 use_existing 设计成新建、不要引入 forbid 资源),没有"必须覆盖"这一条。模型少写资源时没有任何拦截。
  2. confirm_and_selectrepl / a2a / a2a_rich 三种 surface)同样没有 guard,选项 schema 里也没有任何资源字段,唯一的过滤条件是 failed == false。只要候选评估成功,缺资源的方案就会原样呈现给用户。

改动

1. 新增引擎级 completion guard require_resource_intent_coverage

src/iac_code/pipeline/engine/resource_intent_coverage.py(新增):

  • collect_resource_intents(context_snapshot, source_fields):从 context 快照按 normalize_product(casefold + 仅保留字母数字)归并 resource_intents,使 NAT Gateway / NATGateway / natgateway 视为同一产品。
  • validate_resource_intent_coverage(...):以 intent 中所有 action != forbid 的资源为必覆盖基准,逐个 candidate / option 校验。覆盖来源同时支持 list[str]products / covered_products)与 list[dict]resource_intents,其中 action == forbid 的条目不计入覆盖)。
  • 错误码:uncovered_resource_intent(未覆盖且未登记缺口)、unexpected_resource_intent_gap(登记了 intent 中不存在的缺口)、invalid_resource_intent_gap(缺口条目缺 product/reason)、forbidden_resource_intent_covered(forbid 资源被覆盖)、以及 invalid_* 系列配置/结构错误。

loader.py 把 guard key 加入 _SUPPORTED_COMPLETION_GUARD_KEYScomplete_step_tool.py 增加 _validate_resource_intent_coverage 分派、resource_intent_coverage_required 用户可见文案及其 i18n marker。

surface 兼容items_field 在 conclusion 中缺失时 guard 返回 None 跳过校验。这样 a2a_rich 的第二轮(_candidate_selection_response_schema 会剥离 options,只提交 selected_candidate_*)不会被误判失败。

2. pipeline.yaml 两处挂载 guard

  • architecture_planningsource_fields: [intent.resource_intents]items_field: candidatescovered_products_fields: [resource_intents, products]gaps_field: resource_intent_gaps
  • confirm_and_selectcontext_fields 补入 intent(原先没有 intent,无从校验);items_field: optionscovered_products_fields: [covered_products]
  • 三种 surface 的 option schema 均新增 covered_products(required)与 resource_intent_gapsrequired: [product, reason])。

3. prompt / SKILL 正向规则

  • iac-aliyun-architecture/SKILL.mdresource_intents 改为 candidate 的 required 字段;新增 resource_intent_gaps schema;给「资源生命周期约束」补上覆盖清单、缺口登记、以及"所有方案同缺口应先重新设计"三条正向规则;新增本次 badcase 反例(intent 为 ECS(create)+NATGateway(create)+EIP(create) 时只保留 VPC/VSwitch 属错误输出,方案名叫「ECS+NAT 公网方案」不构成覆盖证明)。
  • architecture_planning.md 增加「资源覆盖校对(提交前必做)」。
  • confirm_and_select.md / .a2a.md / .a2a.rich.md 增加「用户意图(资源覆盖基准)」{intent} 区块、covered_products / resource_intent_gaps 字段说明,以及部分满足需标注、不得把有缺口方案作为唯一推荐、全部方案同缺口需在 user_prompt 中如实披露。

4. i18n

新增用户可见文案 resource_intent_coverage_requiredmake translate 后为 zh/es/fr/de/ja/pt 六种语言补齐译文并编译。

测试

pytest tests/pipeline/engine/test_resource_intent_coverage.py tests/pipeline/engine/test_complete_step_tool.py tests/pipeline/selling/458 passed

  • test_resource_intent_coverage.py(新增 13 例):别名归一、非法来源、完整覆盖、逐项缺资源、缺口登记通过、缺口缺 reason 拒绝、未声明缺口、forbid 被覆盖、resource_intents 作覆盖来源、forbid 条目不计覆盖、items 非法/为空、无 intent 时跳过。
  • test_complete_step_tool.py(新增 6 例):badcase 复现(candidate 丢弃 ECS/NAT/EIP 时报 uncovered_resource_intent[ECS, 0])、别名 NAT Gateway 视为覆盖、已登记缺口放行、options 缺资源被拒、a2a_rich 仅提交选择结果时跳过校验。
  • test_resource_intent_coverage_guards.py(新增 3 例):两步 guard 字段值、intentconfirm_and_select.context_fields、各 surface option schema 字段契约。
  • test_terminal_ui_contract.py:更新 a2a_rich option required 集合。

全量分批串行执行合计约 14350 passed / 4 skipped。静态检查 ruff checkruff format --check(1082 files already formatted)、ty check src/ 均通过。

已知限制

  • 3 个存量失败点与本次改动无关:test_i18n.py::test_message_catalogs_pass_gnu_msgfmt_checks(zh 目录 {n} day{s} ago 等 5 条存量译文用 {s:.0},在改动前的 HEAD 上复现同样 5 处 msgfmt 错误)、test_prerequisites.py 中 infraguard 下载错误类型断言(依赖沙箱网络)、test_run_pipeline_contract_scenario.py::test_latest_pipeline_sidecar_reads_nested_session_layoutmax(st_mtime_ns) 在本环境 mtime 粒度过粗时 tie-break 不确定)。
  • 41f73c6 是前置的纯格式化提交:仓库 HEAD 在锁定的 ruff 0.15.10 下有 14 个文件存量格式漂移,导致 pre-commit 的 format 钩子每次都改文件并回滚,必须先单独落地格式化,业务提交才能通过钩子。该提交不含任何逻辑改动。

Aone: https://project.aone.alibaba-inc.com/v2/project/2169409/req/84817160

候选方案会静默丢弃用户 intent 中已正确解析的资源(如 ECS/NAT/EIP),
只保留 VPC/VSwitch,而方案名仍声称包含这些资源;confirm_and_select
也没有任何一致性校验,用户因此在不知情的情况下确认了不完整的方案。

根因有两处:
- architecture_planning 没有 completion_guards,且 SKILL 中
  resource_intents 是可选字段、只有禁止性规则,没有覆盖清单要求。
- confirm_and_select(三种 surface)同样没有 guard,选项 schema 里
  也没有资源字段,唯一过滤条件是 failed == false。

修复方式是新增引擎级 completion guard require_resource_intent_coverage:
以 intent.resource_intents 中所有非 forbid 资源为基准,校验每个 candidate
/ option 都覆盖到,或在 resource_intent_gaps 中显式声明缺口与原因;
forbid 资源被覆盖同样报错。guard 在 items_field 缺失时跳过,以兼容
a2a_rich 仅提交选择结果的第二轮。同时补齐 prompt/SKILL 的正向覆盖规则
与选项 schema 的 covered_products / resource_intent_gaps 字段。
@guima-why guima-why closed this Aug 25, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants