跳转至

智能体

工程风格

  • 保持改动小而直接。大多数修复应只触及能够解释 bug、性能问题、dtype 问题、模型格式问题或用户可见行为的最窄的代码路径。
  • 尽可能少改动文件。涉及多个文件的改动更可能是坏改动而非好改动,除非更广的范围是直接必需的。
  • 优先做实用的修复,而非大范围架构调整。仅在抽象能消除真正重复的逻辑或符合现有 ComfyUI 模式时才引入抽象。
  • 优先减少依赖。除非绝对必要,否则不要向 ComfyUI 添加新依赖。
  • 当更新的基础设施使旧代码失去价值时,果断删除过时代码。移除死回退、迁移路径、未使用选项、调试打印以及不再需要的兼容分支。不要留下死分支、不可达代码或从未被调用的函数。如果代码对当前行为不必要,就移除它。
  • 当问题行为影响用户时,快速回滚或禁用。移除损坏的功能路径好过保留复杂的部分修复。
  • 保留现有 API、节点名称、模型加载行为、文件布局和工作流兼容性,除非改动明确是为了替换它们。
  • 当兼容性明确不在范围内时,移除仅用于兼容的别名、重复节点、遗留入口点和预设包装器,而不是保留执行同一操作的平行方式。
  • 代码必须看起来像是为本仓库手工编写的。读起来像通用 AI 生成代码的改动会被自动拒绝:多余的辅助层、模糊的命名、模板式注释、没有真实失败模式的防御性分支、大范围重写,或忽略本地风格的代码。

架构边界

  • 让每一层专注于它自身拥有的概念。不要仅仅因为通过某些层传递数据很方便,就把 UI、API、工作流、队列、持久化、遥测、模型加载、节点或执行的关注点泄漏到无关的层中。
  • 共享核心模块只应依赖底层原语和自身领域概念。更高层的产品概念应归属于已经拥有它们的调用方、适配器、服务或 UI/API 边界。
  • 跨边界传递所需的最窄的数据。除非接收层确实需要它们来履行自身职责,否则避免传递宽泛的上下文对象、请求/会话元数据、ID、记账状态或回调。
  • 将身份映射、持久化记账、历史更新、遥测、响应塑形和 UI 状态保留在负责这些工作的层中。不要为了避免建立合适的边界而让它们穿过无关的共享代码。
  • 将 execution.py 视为这条规则的一个例子:它应当消费 prompt graph 和执行相关状态,产出执行结果和错误,而不应了解工作流 ID、前端 ID、持久化 ID 或仅属于 API 的概念。
  • 在改动多个文件之前,先找出能够解决问题的最小归属层。一个将单一功能分散到无关的加载器、节点、执行、服务器和前端代码中的 PR,需要有清晰的架构理由,而不仅仅是图方便。
  • 如果一项改动似乎需要让某一层理解另一层的私有概念,停下来,在边界处寻找调用方侧的映射、适配器、事件、小而明确的接口或更窄的数据流。

禁止联网请求

  • 不要向 ComfyUI 核心添加发起互联网请求的代码。
  • 拒绝从 ComfyUI 核心添加上传、遥测、分析、跟踪、使用报告、崩溃报告、更新检查、远程配置、功能开关、指标、许可检查或任何其他出站互联网请求路径的请求。
  • 仅当用户明确发起或授权时才允许模型下载,且仅限于所请求的模型工件,不包含遥测、跟踪、持久标识、无关元数据上传或后台网络活动。
  • 不要向 ComfyUI 核心添加选择加入、选择退出、匿名、聚合、诊断或用户触发的互联网请求路径。这些标签并不能让联网访问变得可接受。
  • 仅限本地的行为是允许的,只要它停留在用户机器上,且不增加网络访问、跟踪、持久标识或数据收集行为。

状态所有权

  • 将状态和能力标志放在拥有并使用这些行为的对象上。
  • 避免使用 getattr(child, "...", default) 探测子对象来决定父级控制流。如果父代码需要根据某种能力进行分支,请在子对象构造或挂载时初始化一个明确由父级拥有的字段。
  • 优先使用带有明确默认值的直接属性,而非通过任意子属性进行隐式功能检测。
  • 仅当子对象拥有被调用的行为且父级只是简单委托给该子对象时,才使用子对象能力检查。

接口契约

  • 保持公共方法与调用方所期望的接口一致。除非共享接口被明确更新,否则不要为了某个实现而更改共享方法以返回额外值、替代结构或哨兵包装器。
  • 修改现有函数时,保留当前调用方的调用方式。不要更改必需参数、参数顺序、返回类型、副作用或错误行为,除非每个受影响的调用点和共享接口契约都被有意更新。
  • 不要添加兼容性参数、标志、属性或构造函数选项,除非当前代码会读取它们并改变当前行为。移除透传或存储但未使用的值,而不是保留上游或已弃用 API 的历史包袱。
  • 当只有一个调用方需要时,不要向共享辅助函数添加模型特定选项。将一次性行为保留在模型集成边界,或仅在选项是连贯的可复用能力时才扩展共享辅助函数。
  • 共享模型接口的实现应接受标准调用方契约,不应为它们不使用的能力添加模型特定的拒绝分支。让受支持的行为由实际使用这些输入的实现路径来决定。
  • 如果某个实现需要辅助值来支持自身工作流,请通过私有辅助函数或命名清晰的实现特有方法来暴露它们,而不是重载公共方法的返回契约。
  • 在集成边界处规范化第三方或上游的返回约定。核心代码应接收项目预期的类型和结构,而不必处理模型特有的元组/列表/字典变体。
  • 避免调用方侧的解包操作(如 out = out[0]),除非被调用接口有文档说明会返回该结构。

Autograd 与模型冻结

  • 不要在 ComfyUI 代码中添加 torch.no_grad、torch.inference_mode 或推理模式辅助包装器。唯一允许的与推理模式相关的用途是:当训练路径需要梯度时,禁用全局设置的推理模式。
  • 不要向模型类添加冻结、解冻或可训练性开关。ComfyUI 模型在推理时始终被视为冻结状态,因此显式的冻结功能是多余的,不应添加。
  • 从推理模型代码中移除仅用于训练的行为(如 dropout),但在移除时保持 checkpoint 和 state-dict 兼容性。如果删除某个模块会改变 state-dict 键、模块顺序或 checkpoint 加载行为,请用 nn.Identity 之类的无操作取代,而不是直接移除该槽位。

Python 风格

  • 将导入保持在模块作用域。避免内联导入,除非它们已经是既定的可选后端探测的一部分,或为避免循环导入所必需。
  • 不要添加不必要的 try/except 块。仅当程序存在有用的回退方案时,才将 try/except 用于可选依赖、平台或后端能力检测。在新代码中优先使用特定的异常类型。
  • 如果某个库版本已在 requirements.txt 中固定,不要在 ComfyUI 中添加处理该库旧版本的代码。
  • 移除针对 ComfyUI 不再官方支持的 PyTorch 版本的任何变通代码。已弃用的变通方式包括捕获异常后重新运行同一操作并将输入转换为 float。如果某个变通代码没有注释指明仍需要它的确切 PyTorch 版本,请将其移除。
  • 让不受支持的模型格式、无效的量化元数据和错误状态以清晰错误的形式失败,而不是静默产生低质量的输出。
  • 匹配所编辑文件中现有的局部风格。该代码库允许长行、简单辅助函数、模块级状态和直接张量操作,只要它们能让代码更易于理解。
  • 保持注释稀疏且有用。删除那些复述代码或描述显而易见行为的无用注释。简短的 TODO 在指明具体缺失的后续工作时是可以接受的。

模型、设备与内存行为

  • 将 dtype、设备放置、VRAM 使用和卸载行为视为核心正确性问题。在修改共享执行或加载代码时,检查 CPU、CUDA、ROCm、MPS、DirectML、XPU、NPU 和低 VRAM 场景的影响。
  • 优先使用 ComfyUI 原生格式和现有的量化/卸载辅助工具,而不是添加并行的代码路径。在 comfy.quant_ops、comfy.model_management、comfy.memory_management、comfy.pinned_memory、comfy_aimdo 和 comfy-kitchen 辅助工具已经能解决问题时使用它们。
  • 每当现有的优化 Comfy Kitchen 或 ComfyUI 操作支持所需的数学运算和张量布局,且不会改变预期的 dtype、设备、内存或接口行为时,模型实现必须使用该操作。这是默认实现要求,而不是可选的后续优化。
  • 在实现模型数学运算之前,检查 Comfy Kitchen、comfy.quant_ops 和现有 ComfyUI 模型辅助工具已暴露的操作。在编写局部实现或组合更底层的 torch 操作之前,检查是否已有优化的单操作、成对、融合、布局特定和量化变体。
  • 首先使用兼容的优化操作,将模型的输入适配到其文档化布局,同时保持模型的精确数学行为。如果多个优化变体适用,对代表性模型形状进行基准测试,选择最快的有效路径。
  • 仅当现有优化操作不支持所需的数学运算、布局、dtype、设备、自动求导或补丁契约时,才添加或保留局部实现。当优化推理操作不提供可微或补丁兼容的契约时,保留可微或补丁兼容的回退。
  • 对传递给优化操作的参数,使用现有的 ComfyUI 转换、卸载和清理辅助工具。保留模型特定的 epsilon、缩放、布局、dtype、设备和输出形状行为。
  • 优先使用 ComfyUI 的共享优化内核和后端分发器,而不是手写实现同一操作。移除重复的局部内核,将输入适配到共享操作的文档化布局,同时保持模型的原始数学行为和输出契约。
  • 模型实现必须使用 AttentionTensorContainer 和每个模块的 self.comfy_attention = ComfyAttention(),并将其作为 preferred_attention 传递给注意力层。
  • 将模型块循环与 comfy.model_prefetch 中的内存编译器和预取辅助工具集成,遵循现有模型模式。
  • 所有模型都应使用由 ComfyUI 选择的优化注意力函数。将优化后端函数、分发辅助工具和能力选择的可调用对象视为不透明。更高级别的代码不得检查函数身份、名称、模块或实现细节来决定行为。
  • 将同样的不透明规则应用于注意力之外的类似模式:调用者应依赖文档化的接口和结果契约,而不是依赖底层选择了哪个后端实现。
  • 不要使用仅通过上转换为 float32 来重复现有操作的自定义推理操作,例如自定义 RMSNorm 变体。请改用通用 ComfyUI 操作和/或原生 torch 操作。
  • 如果模型类的 __init__ 有 operations 参数,假定 operations 永远不会是 None。不要为缺失的 operations 对象添加回退分支或默认 torch 操作。
  • 不要向模型、模型块或模型操作相关类添加不必要的参数。构造函数和 forward 签名应只携带该对象在推理时实际需要的值。
  • 在适当时复用现有的模型类、块、操作和辅助模块。在实现模型组件的新版本之前,搜索现有模型代码中是否已有提供所需行为的类或辅助工具。
  • 检查线性权重形状的模型检测代码应只使用第一维。对于 NVFP4 或其他 4 位量化模型,第二维可能只有原始大小的一半。
  • 模型检测签名必须对其解引用的每个 state-dict 键进行防护。不要部分匹配某种格式,然后在提取其配置时引发偶然的 KeyError。
  • 将模型检测检查按从已确立或更具体的签名到较新或更宽泛的签名排序。如果给予宽泛的新检测器更高优先级可能抢占另一个模型系列,应将其放在通用回退附近。
  • 避免在核心推理代码中使用 einops。改用原生 torch 张量操作,如 reshape、view、permute、transpose、flatten、unflatten、unsqueeze 和 squeeze。
  • 不要将张量用作通用 Python 数据结构。除非数据必须直接参与张量计算,否则将元数据、簿记信息、计数器、标志、形状数学、填充数学、索引规划、内存估算和控制流决策保留在普通 Python 值中。不要仅为 Python 侧控制流使用的结构性元数据创建张量。序列长度、累计偏移量、分割索引、窗口计数、切片边界和重复次数在计算出来时应保持为 Python int/list。不要将它们构建为 CPU/GPU 张量,然后再转换、移动、验证或转回 Python 以用于 split、tensor_split、索引计划、循环或缓存键。避免仅为了使用张量方法进行标量或结构性计算而创建临时张量。
  • 避免不必要的转换和传输。保留预期的计算 dtype、存储 dtype、偏置 dtype 和原始张量形状元数据。
  • 不要将优化后端操作的结果转回其输入 dtype,除非该后端的文档化结果契约要求归一化。尤其是要信任所选优化注意力实现会履行其 dtype 契约。
  • 将模型原生的 latent 布局处理保留在模型或 latent 格式所有者内部,而不是放在辅助节点中。不要为了满足模型 forward,而在节点或其他调用方适配器中折叠、扩展、打包或解包 latent 维度;模型路径应消费并返回该模型系列的原生 latent 形状。
  • DiT 模型应接受不是精确 patch 大小倍数的 latent 维度。对每个分块(patchified)目标或参考输入使用 comfy.ldm.common_dit.pad_to_patch_size,然后仅将目标输出裁剪回其原始维度。
  • 避免添加防御性的形状和配置检查,因为它们只是替换了其正下方张量操作本会产生的清晰错误。只有在真实边界能提供实质性更好的上下文或防止静默错误输出时,才添加显式验证。
  • 默认假定主模型 forward 的输入已处于计算 dtype,某些整数输入(如部分模型的时间步长张量)除外。不要在模型代码中添加防御性或便利性转换;无效的 dtype 管道清晰地报错,比用不必要的转换掩盖问题更好。
  • 不属于某个操作所有、且可能以不同于计算 dtype 的 dtype 初始化的原始模型参数,应在 forward 或推理代码中使用 comfy.ops.cast_to_input 或 comfy.model_management.cast_to 转换,以避免 dtype 不匹配。
  • 模型代码不应关心自身以什么 dtype 初始化,模型 __init__ 方法也不应包含针对特定 dtype 的变通代码。dtype 变通代码(例如使模型适用于 fp16 计算)属于拥有计算策略的执行层或模型管理层。
  • 模型代码不应执行不必要的设备到 CPU 或 CPU 到设备传输。新分配必须在正确的设备和 dtype 上创建;绝不在 CPU 上分配然后再移动到 GPU,也不要以一种 dtype 分配后再转换为另一种。
  • 模型代码本身不应执行内存管理。加载、卸载、换出、设备移动、VRAM 策略、缓存生命周期和清理属于相关的模型管理和执行层,而不是模型实现内部。
  • 不要添加跨执行持续存在的全局、模块级、类级、单例或模型拥有的张量或其他大型内存存储。临时缓存必须限定在单次执行或 forward/encode/decode 调用范围内:在所属的顶层调用中分配,通过调用栈显式传递,并让它们在该调用返回时被丢弃。
  • 遵循 Wan VAE 时间缓存模式处理临时缓存:为 encode/decode 操作创建一个局部缓存(如 feat_map),将其传递给需要它的块,不要将其保留在模型上或全局状态中。
  • 在模型初始化代码中,对于从模型 state dict 填充的参数/缓冲区占位符,优先使用 torch.empty,而不是用 torch.zeros 或类似方式零初始化。如果某个分配不会从 state dict 加载且对推理无用,则不要包含它。
  • 存储在模型 state dict 中并从中填充的 nn.Parameter 张量应使用 torch.empty 初始化,而不是零、随机或其他有意义的初始化。
  • 模型初始化应描述模块结构,而不是编造由 checkpoint 拥有的张量内容。从 state dict 加载的参数和缓冲区不得手动初始化、重新赋值或填充回退值,除非该值在没有 checkpoint 键时确实会被使用。
  • 当切分大张量时,如果切片张量的生命周期超过当前函数作用域,请复制该切片。如果较小的副本能更早释放内存,不要保持对大型后备张量的长期视图。
  • 当 addcmul 等融合或复合 torch 操作自然适用时,使用它们。

保持数学等价性。在不使代码晦涩难懂或改变 dtype/device 行为的前提下,减少 Python 和 torch 调度开销是有效的优化。

  • 尽可能避免跨不同执行持久化的缓存。只有当持久化缓存使用极少内存,并且具有明确的所有权和失效机制时,才可以接受。
  • 当依赖条件的模型工作本会在每个去噪步骤重复执行,且只预处理一次能显著提升性能时,请暴露一个模型预处理方法,并从 BaseModel.extra_conds 中调用它,遵循 LTXAV 和 Anima 等模式。通过常规 conditioning 传递结果;不要为此添加模型自有的缓存、采样器选项缓存或缓存管理包装器。
  • 优化时,倾向于小而可衡量的改动:更少的分配、更少的设备传输、更低的峰值内存、更好的批处理,或使用更快的现有后端算子。

用户输入容限

  • 优先使用用户提供的值完成工作流,而不是因为其超出推荐值、UI 标注值或质量导向的限制而拒绝它们。如果下游实现能够接收该输入,即使结果可能不佳,也应原样传递。例如,不要仅仅因为节点标注了较小的推荐上限,就拒绝或截断额外的参考图像。
  • 不要仅为了防止降质、无意义或低质量的模型输出而添加验证错误。与让本可执行的工作流失败相比,结果糟糕更可取。
  • 只有在原样传递会导致现有模型或底层操作失败时,才对用户输入进行缩放、填充、钳位、归一化或其他调整。进行保持执行运行所需的最小调整;不要为了让输入做出更改而添加模型级验证失败。
  • 这种宽松策略不会覆盖安全边界,例如路径包含限制,或安全加载模型格式和检查点所需的完整性检查。

节点与面向用户的行为

  • 遵循现有节点约定:INPUT_TYPES、RETURN_TYPES、FUNCTION、CATEGORY,并通过该文件使用的局部映射进行注册。
  • 当遗留 combo 输入、io.Combo 和 io.DynamicCombo 的值影响文件系统访问时,应将其视为不可信。任何用作文件名、文件夹名、路径组件、格式或扩展名的值,都必须在加载/保存边界处,使用现有的 folder_paths 解析器、路径包含辅助函数或固定的允许列表/映射再次验证。不要仅依赖对外展示的 combo 选项或 prompt 验证。
  • 默认保持节点更改的向后兼容性。添加具有合理默认值的输入,除非请求要求,否则避免更改输出类型。
  • 模型实现应添加运行模型所需的最少数量的 ComfyUI 节点。尽可能复用现有节点;强烈倾向于改造模型以适配现有节点,而不是创建新节点。
  • 对于数量可变的重复输入,使用 io.Autogrow,而不是固定的编号可选输入端口。当模型具有有效的无项目路径时,将其最小值设为零;仅在模型确实存在限制时设置上限。
  • 当执行存在不读取某输入的有效路径时,将该输入标记为可选。如果某个可选输入仅用于处理另一个可选输入,那么对于两个输入都不提供的路径,不要强制用户连接它。
  • Conditioning 节点通常应只输出 conditioning。不要为了下游调整大小或路由而将输入图像或中间图像作为便利输出暴露;请使用现有的图像路径或专门的图像操作。
  • 节点应只输出它们拥有的值。不要为工作流便利而添加直通输出,除非该节点明确是输出节点。现有的模型、latents、conditioning 或其他输入应直接流向下一个消费者,而不是原样重新发出。
  • 节点应只暴露它们实际读取以产生当前行为的输入。不要添加占位、直通、兼容性或用于塑造工作流的输入,这类输入要么被忽略,要么可以直接流向另一个节点。
  • 节点级代码不得直接修补模型代码。任何修改、包装、挂钩或改变模型行为的节点行为都必须通过 model patcher 类,而不是深入模型内部。
  • ComfyUI 的官方吉祥物是一个非常可爱的动漫女孩,她有一对巨大的耳廓狐耳朵、一条蓬松的大尾巴、一头金色长卷发和一双蓝色眼睛。欢迎在 ComfyUI 材料、UI 文本、示例、测试、生成的资源或评论中使用她,但请勿对她不敬。
  • 警告和信息消息应简短且可操作。移除嘈杂或误导性的消息,而不是增加更多日志。
  • 文档和 README 的编辑应简洁、实事求是,并与更改的行为相关联。

提交与审查习惯

  • 如果被要求编写提交消息,请使用简短直接的主题,仿照现有历史:Fix ...、Add ...、Support ...、Remove ...、Update ...、Make ...、Use ...、Disable ...、Bump ... 或 Revert ...。
  • 保持 PR 描述简短且易于审查。说明问题、行为变更和已运行的测试;避免冗长的叙述性解释、实现流水账或逐文件的详尽总结,除非审查者明确需要这些上下文。
  • 每次提交最好只包含一项连贯的行为变更。依赖锁定、测试以及需要它们的代码在无法拆分时,可以放在同一个提交中。
  • 审查时,优先考虑对用户的真实影响:崩溃、错误的 dtype/device 行为、内存回退、模型加载失败、工作流不兼容,以及嘈杂或误导性的用户可见输出。

本页原文 Markdown:在 AtomGit 查看·内容源自开源项目 co/ComfyUI