# PR #3095 完整报告

- 仓库：`radixark/miles`
- 标题：refactor(rollout): rename shared filter output
- 合并时间：2026-09-07 13:56
- 原文链接：http://prhub.com.cn/radixark/miles/pull/3095

---

# 执行摘要

- 一句话：将共享的 rollout 过滤结果类型从 DynamicFilterOutput 重命名为 FilterOutput。
- 推荐动作：该 PR 是一个清晰、小范围的代码清理。核心变更集中在单个类型定义和其别名上，并通过系统性更新确保了命名一致性。对于理解 rollout 过滤器模块的开发者，此 PR 值得快速浏览以了解命名约定的演进。

# 功能与动机

Common predicates and configured dynamic filters return the same result type, so `DynamicFilterOutput` describes the wrong ownership boundary. `FilterOutput` is now the primary name while the old import remains compatible. ( 源自 PR body)

# 实现拆解

1. **核心类型重命名**：在 `miles/rollout/filter_hub/base_types.py` 中，将 `@dataclass class DynamicFilterOutput` 重命名为 `class FilterOutput`，并添加 `DynamicFilterOutput = FilterOutput` 作为兼容别名。同时将 `call_dynamic_filter` 函数内部所有实例化和类型检查更新为使用 `FilterOutput`。
2. **生产代码导入更新**：在 `miles/rollout/filter_hub/dynamic_sampling_filters.py` 中，将导入语句从 `DynamicFilterOutput` 更新为 `FilterOutput`，并更新所有返回 `DynamicFilterOutput(...)` 的实例化调用。
3. **测试文件同步迁移**：更新 `tests/fast/rollout/test_fully_async_rollout.py` 和 `tests/fast/rollout/inference_rollout/integration/utils.py` 中的导入和实例化调用。
4. **新增兼容性测试**：在 `tests/fast/rollout/test_filters.py` 中添加 `test_dynamic_filter_output_is_a_compatibility_alias` 测试，断言 `DynamicFilterOutput is FilterOutput` 为真，确保别名正确性。
5. **文档更新**：同步更新 `docs/user-guide/customization.md` 和 `docs/diffusion/user-guide/customization.md` 中的示例代码和注释，使用新名称 `FilterOutput`。

关键文件：
- `miles/rollout/filter_hub/base_types.py`（模块 过滤器类型；类别 source；类型 core-logic；符号 FilterOutput, DynamicFilterOutput）: 核心变更文件，定义了新的 `FilterOutput` 类型和 `DynamicFilterOutput` 别名，是整个重命名的入口和定义点。
- `tests/fast/rollout/test_filters.py`（模块 过滤器测试；类别 test；类型 test-coverage；符号 test_dynamic_filter_output_is_a_compatibility_alias）: 新增的测试文件，专门验证 `DynamicFilterOutput` 作为 `FilterOutput` 别名的兼容性，是确保重构安全性的关键保障。
- `miles/rollout/filter_hub/dynamic_sampling_filters.py`（模块 动态采样过滤器；类别 source；类型 dependency-wiring）: 生产代码中的主要调用点，将导入和所有实例化调用更新为新名称 `FilterOutput`。

关键符号：call_dynamic_filter, check_reward_nonzero_std, check_no_aborted, filter_by_reward

## 关键源码片段

### `miles/rollout/filter_hub/base_types.py`

核心变更文件，定义了新的 `FilterOutput` 类型和 `DynamicFilterOutput` 别名，是整个重命名的入口和定义点。

```python
# miles/rollout/filter_hub/base_types.py
from collections import defaultdict
from dataclasses import dataclass


# 核心变更：将类名从 DynamicFilterOutput 改为 FilterOutput，
# 因为它现在是所有过滤谓词（公共和动态）共享的返回类型。
@dataclass
class FilterOutput:
    keep: bool
    reason: str | None = None


# 保留旧名称作为别名，确保向后兼容性。
# 旧代码的导入和 isinstance 检查将继续工作。
DynamicFilterOutput = FilterOutput


def call_dynamic_filter(fn, *args, **kwargs):
    if fn is None:
        return FilterOutput(keep=True)

    output = fn(*args, **kwargs)

    # compatibility for legacy version
    # 此处的类型检查现在使用新的名称 FilterOutput
    if not isinstance(output, FilterOutput):
        output = FilterOutput(keep=output)

    return output

```

# 评论区精华

Review 中无实质性技术讨论。仅有一个来自 `claude[bot]` 的自动评论，说明该仓库配置为手动代码审查，并提供了启用自动化审查的指引。`yueming-yuan` 直接批准了 PR。核心决策（保留旧名称作为别名以维持兼容性）在 PR body 中已明确说明，未在评论区引发争议。

- 暂无高价值评论线程

# 风险与影响

- 风险：风险极低。主要风险点在于：
 1. **兼容性风险**：通过保留 `DynamicFilterOutput = FilterOutput` 别名，确保了旧代码导入和 `isinstance` 检查行为不变，已由新测试覆盖。
 2. **文档遗漏风险**：变更已覆盖主要用户指南文档。
 3. **命名一致性**：内部使用的 `MetricGatherer` 类名和 `_dynamic_filter_drop_reason_count` 属性未变更，这是刻意保留的内部实现细节，不构成风险。
 - 影响：对用户和系统影响很小。这是一个纯粹的内部重构，不改变任何功能行为。对团队而言，它提升了代码的清晰度和可维护性，使过滤器的返回类型名称与其用途更加匹配。
 - 风险标记：命名一致性

# 关联脉络

- 暂无明显关联 PR