Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion adapters/afd_plugin/manifest.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -240,7 +240,7 @@ push:
# Knowledge service: declared but disabled until afd-plugin has its own
# calibration set and has run through a shadow period (rollout stage P3b).
knowledge_lifecycle:
enabled: false
enabled: true
mode: shadow
knowledge_dir: repos/afd-plugin
release:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,3 +36,4 @@ sources:
| 理解初始化、请求和 control metadata 流 | [architecture](architecture.md) | v0.26 当前运行实现 |
| 调查模型内 handoff | [model-integration](../model-integration/architecture.md) | 角色感知层构造和 metadata consumer |
| 调查 graph/DBO | [execution-platforms](../execution-platforms/architecture.md) | 平台 wrapper、capture 和 replay |
- [afd_plugin/v1/worker 审查规则:AFD 事务元数据、控制面发送与 runner 生命周期](rules.md)
76 changes: 76 additions & 0 deletions knowledge/repos/afd-plugin/components/attention-runtime/rules.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
---
title: "afd_plugin/v1/worker 审查规则:AFD 事务元数据、控制面发送与 runner 生命周期"
created: 2026-09-30
updated: 2026-09-30
type: rule
tags: [afd-plugin]
sources: []
---

# afd_plugin/v1/worker 审查规则:AFD 事务元数据、控制面发送与 runner 生命周期

## AFD-I34 — `install_afd_metadata_on_forward_context` 只在 pending 为空时新建事务,顺序和子 context 判定不能改

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Point the owner routes at the new rule pages

For reviews touching any of these four owners, Direct still routes to the sibling architecture.md files (for example, adapters/afd_plugin/manifest.yaml:116-163), whose quick-map status is unavailable and which do not link these new component rules. Because the review workflow opens only the returned exact route and then stops navigation, all 31 rules added here are skipped, despite the reported rule-bearing coverage increase; update the manifest routes to the corresponding rules.md pages or make the routed pages deliver these rules.

AGENTS.md reference: knowledge/AGENTS.md:L16-L16

Useful? React with 👍 / 👎.


- 方法内顺序固定为:ubatch 子 context 直接返回(不发送)→ `_afd_pending_metadata is None` 时才 `build_afd_metadata` → 写入 `additional_kwargs["afd_metadata"]` → 检查 `_afd_suppress_metadata_send` → 发送。调换顺序后,子 context 会重复发送,或者在 suppress 作用域里照样发送。
- 非 None 的 pending 会被原样复用,包括它的 `transaction_id` 和 `tokens_lens`。现有入口各自保证 pending 是新的:V1 靠 `_build_attention_metadata` 每步重新 stage;capture 的 ubatch 分支显式置 None;V2 靠 `_use_afd_execution_context` 在退出时恢复原值。新增前向入口要确认不会复用上一步留下的旧 metadata。
- `_is_ubatch_child_afd_context` 的判定条件是:`ubatch_slices is None`、`num_stages > 1`、`len(tokens_lens) == 1`。它依赖两件事:父 metadata 满足 `len(tokens_lens) == num_stages`(`build_afd_metadata` 多 stage 分支),子 metadata 只有一个 `tokens_lens` 元素且保留父 `num_stages`。改 metadata 字段布局或 wrapper 构造子 metadata 的方式时,要同步检查这个判定。
- FULL graph 且没有 `ubatch_slices` 时,DP 控制用 `_full_cudagraph_padded_tokens` 给出的 padded 数,不能换成真实 token 数。

<!-- kb:rule status=active since=init-9cae2d2ddaae -->

## AFD-I36 — V1/V2/NPU runner 必须初始化同一组 AFD 字段;NPU 直接依赖 `attention_metadata.py` 的私有 helper

- `AFDMetadataProviderMixin` 只声明字段,由子类 `__init__` 负责初始化。三个 runner 都各自设置 `_is_warmup`、`_afd_is_graph_capturing`、`_afd_is_graph_replaying`、`_afd_pending_metadata`、`_afd_suppress_metadata_send`、`_afd_transaction_counter = 0`,并要求 connector 带 `control_plane`。新增字段或新 runner 时,三处要一起改。
- `AFDNPUAttentionModelRunner` 不继承 mixin,而是直接 import `_forward_context_num_tokens`、`_full_cudagraph_padded_tokens`、`_resolve_world_ranks`、`build_ubatch_dp_metadata_list`。改这些下划线函数的签名或语义,等于改 NPU 的接口,需要同步 `afd_plugin/v1/worker/npu/attention_model_runner.py`。
- 每次调用 `build_afd_metadata` 都会通过 `_next_afd_transaction_id` 消耗一个事务号。不要在只需读取或检查 metadata 的地方调用它。

<!-- kb:rule status=active since=init-9cae2d2ddaae -->

## AFD-I37 — connector 生命周期:rendezvous 放在 `load_model` 最后,`close()` 放在 `finally`

- `init_afd_connector()` 是 Attention 和 FFN 之间的阻塞集合操作,同时充当“双方都加载完权重”的屏障。它必须在 `super().load_model()` 和 V1 的 `_install_afd_ubatch_wrapper()` 之后、内存 profiling 和首次前向之前执行,并保留 `is_initialized` 守卫(FFN 侧用同一个守卫)。不要把它挪回 `__init__` 或 wrapper 安装之前。
- `shutdown` 里的 `self.connector.close()` 必须放在 `finally` 中,这样 native `super().shutdown()` 抛异常时仍会关闭。V2 还用嵌套 `finally` 保证 profiler 停止失败时不跳过后续清理。
- V2 `__init__` 在 `super().__init__` 之后出错时,会关闭已创建的 connector、调用 `super().shutdown()`,然后重新抛出。新增的初始化步骤要放进这个 `try` 里。

<!-- kb:rule status=active since=init-9cae2d2ddaae -->

## AFD-I38 — 模块级符号替换的作用域要尽量窄,并在 `finally` 中恢复

- 目前有三处替换,各自只在一个窗口内生效:`_use_afd_ubatch_wrapper_during_load` 只在 `super().load_model` 期间把 `gpu_model_runner.UBatchWrapper` 换成 `AFDUBatchWrapper`;`AFDAttentionWorker.init_device` 只在 `super().init_device()` 期间替换 `native_runner_module.GPUModelRunner`;V2 只在 `capture_model` 内替换 `v2_cudagraph_utils.prepare_inputs_to_capture`。这些替换依赖上游在调用时按模块属性查找符号。改动时要保留 try/finally 恢复,不要扩大替换窗口。
- `init_device` 必须在调用 `super().init_device()` 之前,用 `get_afd_model_config(...)` 替换 `vllm_config.model_config` 和 `self.model_config`。否则 runner 会用非 AFD 的模型身份构造。
- `_install_afd_ubatch_wrapper` 有两个分支:模型已经是 `AFDUBatchWrapper`,或需要先 unwrap 原生 `UBatchWrapper`。两个分支都必须调用 `configure_afd_context_provider(self.install_afd_metadata_on_forward_context)`。新增分支漏掉这一步,ubatch 前向就拿不到 AFD metadata。

<!-- kb:rule status=active since=init-9cae2d2ddaae -->

## AFD-I39 — `_determine_batch_execution_and_padding` 的 `args` 元组必须和 `_batch_execution_values` 的名称表逐位对齐

- `args` 按位置构造,再由 `_batch_execution_values` 用 `zip(..., strict=False)` 映射成名字。如果上游签名插入或调换了参数,或者这里元组的顺序变了,映射会静默错位(例如把 `num_reqs` 当成 `num_tokens`),不会报错。改其中一处时,要同时改另一处,并对照上游签名核对。
- DP=1 时,`_should_ubatch_single_rank` 必须保留三道守卫:`num_tokens < num_ubatches`、`check_ubatch_thresholds`,以及基于 padded `batch_descriptor.num_tokens` 的 `is_last_ubatch_empty`。DP>1 分支要保留“第一个 ubatch 不能为空”的检查。
- ubatch 只支持 `num_ubatches == 2`,runner 和 worker 都通过 `fail_if_unsupported_ubatching` 检查。
- `_afd_is_graph_replaying` 在这里由 `_is_warmup`、`_afd_is_graph_capturing` 和 `CUDAGraphMode.FULL` 算出,所以调用前这两个标志必须已经设置正确。

<!-- kb:rule status=active since=init-9cae2d2ddaae -->

## AFD-I40 — V1 `_build_attention_metadata`:先 stage AFD 事务,再临时关闭 `supports_update_block_table`

- `_afd_pending_metadata` 必须在调用 `super()._build_attention_metadata` 之前,用 `ubatch_slices` 和未 padding 的 `num_tokens` 构建好。这样 `_model_forward` 里的 install 会复用这个事务,而不是按 context 的 token 数另建一个。
- 有多个 ubatch 时,要遍历每个 ubatch 的 builder 并关闭 `supports_update_block_table`。只记录原本为 True 的 builder(按 `id` 去重),并在 `finally` 中恢复。漏掉恢复会让之后所有步骤都失去 block-table 快速更新。
- 这是针对 vLLM PR #48659 的临时绕过。升级 pinned vLLM 时应评估能否删除,不要扩大它的作用范围。

<!-- kb:rule status=active since=init-9cae2d2ddaae -->

## AFD-I42 — V2 FULL replay hook 只作用于单个 manager 实例、不可重入,且只能在 `_use_afd_execution_context` 内使用

- hook 只在 `cudagraph_mode == CUDAGraphMode.FULL_DECODE_ONLY` 且 `cudagraph_manager` 存在时启用。它用 `MethodType` 覆盖单个 manager 实例的 `run_fullgraph`,用 `_AFD_FULLGRAPH_HOOK_MARKER` 防止嵌套。退出时恢复之前的实例覆盖,或者 `del` 掉实例属性。不要改成修改类属性。
- replay 的 metadata 先用真实的 `real_tokens`(`scheduler_output.total_num_scheduled_tokens`)构建,再把 `tokens_lens` 改成 padded 的 `desc.num_tokens`;DP 控制同样用 padded 数。`tokens_unpadded_lens` 保持真实值,两者不要合并成一个。
- hook 自己只恢复 `_afd_is_graph_replaying`。`_afd_pending_metadata`、`_afd_suppress_metadata_send`、`_is_warmup`、`_afd_is_graph_capturing` 都靠外层 `_use_afd_execution_context` 的 `finally` 恢复,所以不能在这个作用域之外单独使用 hook。

<!-- kb:rule status=active since=init-9cae2d2ddaae -->

## AFD-I43 — NPU Async CAM stage 构建必须把 builder 0 留给全批次,并保持单 stage 的 AFD 事务

- 全批次 metadata 已由 builder 0 构建,并在 stage 执行期间一直存活。因此 Async CAM stage 构建时用 `ASYNC_MOE_STAGE_METADATA_BUILDER_OFFSET + ubid` 选 builder。native DBO(`is_async_moe_stage_build=False`)的 offset 保持为 0。改动 offset 或 builder 的选择方式,会让 stage metadata 覆盖全批次 metadata 正在使用的可复用缓冲。
- `isolate_deepseek_attention_builder_inputs` 只对 async stage 构建调用,而且不能省略,因为多个 stage 的 metadata 会同时存活。
- async 路径必须置 `self.ubatch_slices = None`,并以单 stage(`build` 时传 `None`)构建 `_afd_pending_metadata`。否则 `_model_forward` 会把 stage 切片写进 `forward_context.ubatch_slices`,走上 DBO 路径。

<!-- kb:rule status=active since=init-9cae2d2ddaae -->
1 change: 1 addition & 0 deletions knowledge/repos/afd-plugin/components/connectors/_index.md
Original file line number Diff line number Diff line change
Expand Up @@ -36,3 +36,4 @@ sources:
| 理解 factory、拓扑、payload 和生命周期 | [architecture](architecture.md) | 三类 connector 当前合同 |
| 核对 role runtime 如何消费 control/work item | [Attention](../attention-runtime/architecture.md) / [FFN](../ffn-runtime/architecture.md) | caller 生命周期 |
| 核对平台支持组合 | [execution-platforms](../execution-platforms/architecture.md) | graph/DBO/quantization 矩阵 |
- [NPU connector(CAM async / CAMP2P)review 规则](rules.md)
92 changes: 92 additions & 0 deletions knowledge/repos/afd-plugin/components/connectors/rules.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
---
title: "NPU connector(CAM async / CAMP2P)review 规则"
created: 2026-09-30
updated: 2026-09-30
type: rule
tags: [afd-plugin]
sources: []
---

# NPU connector(CAM async / CAMP2P)review 规则

## AFD-I58 — async CAM FFN 侧:`token_nums_rankid_layeridx` 必须原样回传,真实层号和 token 数只来自 CAM 元数据

- `afd_async_dispatch_recv` 返回的 `batch_info` 存为 `states.token_nums_rankid_layeridx`,`send_ffn_output` 必须把它原样传给 `afd_async_combine_send`。
- 其中 `[0]` 是整个 rank 的总 token 数,而本次 chunk 的 token 数来自 `group_list.sum()`。不能为了对齐 `num_tokens` 去切片或重建这个张量。
- `recv_ffn_work_item` 依赖的布局是:`[0]` 为总 token 数,`[2]` 为 layer_idx。算子布局变化时要同步修改这里。
- FFN 侧调用 `recv_attn_output` 时传的是 `layer_idx=0` 和容量大小的 `batch_size`,之后 `states.layer_idx`/`states.batch_size` 不会再更新。下游必须使用 `AFDAsyncFFNWorkItem.layer_idx`/`num_tokens` 或 `metadata.layer_idx`,不能读 `states` 里的这两个值。
- `hidden_states` 和 `states.dynamic_scales` 必须切到同一个 `num_tokens`。
- 三个标量目前通过一次 `torch.stack(...).cpu()` 读取。新增的 host 读值应并入这次读取,不要再加一次 D2H 同步。

<!-- kb:rule status=active since=init-9cae2d2ddaae -->

## AFD-I59 — async CAM:零 token 的 FFN work item 也必须 combine-send,并且用浮点占位张量

- 每个 `AFDAsyncFFNWorkItem` 都要走一次 `send_ffn_work_item_output`。`num_tokens == 0` 时如果跳过发送,Attention 侧的 `afd_async_combine_recv` 会一直收不到数据。
- 零 token 分支发送的是形状 `(1, hidden)`、dtype 为 `self.activation_dtype` 的全零张量。
- 这里不能复用 `recv_output.hidden_states`:开启 `dynamicQuant` 时它是 int8,CAM combine-send 不接受。
- 该方法可能返回替换后的 `ffn_output`。调用方如果还要继续处理,必须用返回值,而不是原来的输入。
- `_send_ffn_output_payload` 只发送 `AFDF2ATransferPayload.routed_output`。给 payload 新增的字段不会经 CAM 回传,需要单独处理。

<!-- kb:rule status=active since=init-9cae2d2ddaae -->

## AFD-I62 — `connector_extra_config` 白名单、`from_mapping`、`to_mapping` 与 dataclass 字段必须同步修改

- `from_mapping` 用 `_AFD_ASYNC_EXTRA_CONFIG_FIELDS` 和 `_CAMP2P_EXTRA_CONFIG_FIELDS` 拒绝未知 key。新字段如果只加到 dataclass、没加进白名单,用户配置会直接报 unknown field。
- 外部 key 名属于对外契约:
- `dynamicQuant` 对应属性 `dynamic_quant`;
- `ATTN_RANKS_PER_DP_CONFIG_KEY` 必须与白名单里的 `attn_ranks_per_dp` 一致。
- `to_mapping` 会省略值为 `None` 的可选字段。新增字段要保证 `from_mapping(x.to_mapping()) == x`。
- 新字段要经过 `coerce_extra_*` 系列函数做类型校验,不能直接用 `raw.get(...)` 的结果。

<!-- kb:rule status=active since=init-9cae2d2ddaae -->

## AFD-I63 — CAMP2P:进程组创建顺序和 ubatch→HCCL group 映射

- 所有 rank 都在同一个 `tcp://host:port` 上 rendezvous,所以各 rank 执行 `init_afd_connector` 的调用序列必须一致:
1. 先按 ubatch 创建 HCCL 组 `afd`、`afd1`、…,world 大小为 ffn + attn;
2. 再由 FFN 单独创建 `afd_moe`;
3. 最后由参与者创建 gloo 组 `p2p`。
- 改变顺序、改组名,或者只在一种角色上加组,都会让 rendezvous 卡住。Attention 和 FFN 两侧的 `parallel_config.num_ubatches` 必须相同。
- 算子只接收三个组名。`_get_group_ep` 把 ubatch 0/1/2 映射到 `hccl_comm_name`/`hccl_comm_name2`/`hccl_comm_name3`:
- 只有一个 ubatch 时,ubatch 1 退回组 0;
- 缺第三组时,ubatch 2 直接报错;
- ubatch ≥3 会静默落回组 0。
要支持更多 ubatch,必须同时修改 custom op 签名和 `_get_group_ep`。
- 命名陷阱:属性 `self.hccl_comm_name1` 是 `afd_moe` 组(写入 `cam_p2p_ep_name`),而 `_get_group_ep` 的形参 `hccl_comm_name1` 是主 `afd` 组。调用时第一个参数必须是 `self.hccl_comm_name`。

<!-- kb:rule status=active since=init-9cae2d2ddaae -->

## AFD-I64 — CAMP2P DP 元数据组:发送目的地与接收 src 公式必须互为逆映射

- 发送侧:前 `min_size` 个 Attention rank 以 `p2p_rank = role_rank + min(ffn_size, attention_size)` 向 `dp_metadata_destinations` 发送。
- 接收侧:FFN 以 `src = p2p_rank % min_size + ffn_size` 接收。
- 这组公式依赖 `attention_size >= ffn_size`,此时 `min_size == ffn_size`。放宽 `build_camp2p_topology` 里的这项校验,src 公式和 destinations 就不再对应。
- `participates_in_p2p_group` 选出的 rank 数必须等于 `p2p_world_size = ffn_size + min_size`,否则 gloo 组 rendezvous 会挂住。
- 这个组用的是 gloo backend,`send_dp_metadata_list`/`recv_dp_metadata_list` 的 wire 张量必须留在 CPU。
- 非发送者 rank 调用 `send_dp_metadata_list` 会静默返回。

<!-- kb:rule status=active since=init-9cae2d2ddaae -->

## AFD-I65 — CAMP2P custom op 通过 forward context 传递状态,同一 context 内只能有一个未完成的 send→recv

- `send_attn_output` 在调用 `torch.ops.vllm.afd_camp2p_send_attn_output` 之前,写入新的 `forward_context.cam_afdtransfer_state` 和 `ubatch_idx`。
- op impl 用 `ubatch_idx` 选组,并把 A2E 的 `outputs[3]` 写回 `atten_batch_size`。`recv_ffn_output_impl` 依赖这个值。
- 每个 forward context 只有一个状态槽位。同一 context 内第二次 `send_attn_output` 会覆盖前一次的 `atten_batch_size`,之后再 recv 前一个 ubatch 就会用错数据。如果要让多个 ubatch 重叠,需要按 ubatch 分开存状态。
- op 本身不接收 ubatch 参数,所以 `recv_ffn_output` 必须先设置 `get_forward_context().ubatch_idx`,再调用 op。
- 修改 op 签名时,要同步修改 `*_impl`、`*_fake_impl`、`send_annotations`/`recv_annotations` 和所有调用处。
- 注册受 `_CAMP2P_CUSTOM_OPS_REGISTERED` 保护,并且会吞掉重复注册错误。如果同名 op 已经注册过,改签名不会有显式报错。
- `send_attn_output` 里新增的 host 侧校验,要像现有 shape 校验一样在 `torch.compiler.is_compiling()` 下跳过,以保留 `FULL_DECODE_ONLY` ACL graph 路径。

<!-- kb:rule status=active since=init-9cae2d2ddaae -->

## AFD-I66 — CAMP2P ids 通道:Attention 的 `compute_gate` 必须与 FFN 的 `recv_input_ids` 成对选择

- Attention 传入 `input_ids` 时使用 `compute_gate=1`。FFN 必须用 `recv_attn_output(recv_input_ids=True)` 选择同一种算子模式。
- 模式必须由调用方显式声明,不能改成按返回张量的形状推断,因为真实的单 token 层和算子占位张量无法区分。
- 模式 0 下 A2E 的 `outputs[1]` 是未初始化内存,只能在 `compute_gate_mode == 1` 时读取。
- `prepare_token_id_transfer` 的约束:ids 数必须恰好等于 `metadata.total_tokens`,列数为 `num_experts_per_tok`,scales 全为零(这个通道只传 token 身份,不传路由权重)。
- `received_token_ids` 的约束:收到的数量必须 ≥ FFN 实际计算的 token 数,然后截断到该值。
- 放宽这两处校验,按 token 选专家的 router 会给错误的 token 选专家。

<!-- kb:rule status=active since=init-9cae2d2ddaae -->
Original file line number Diff line number Diff line change
Expand Up @@ -36,3 +36,4 @@ sources:
| 理解 daemon、step selection 和 shutdown | [architecture](architecture.md) | control-plane 与 connector-driven 两条路径 |
| 审查 EngineCore 适配 | [compatibility](../compatibility/rules.md) | patch 和 non-AFD 分支门禁 |
| 调查 FFN 模型计算 | [model-integration](../model-integration/architecture.md) | role-aware 构造和权重边界 |
- [FFN 角色运行时(worker / model runner)审查规则](rules.md)
Loading
Loading