编译自动 Apply Patch 架构下的 AI 修复提交与工作区清理
关联模块:pipeline/ta_patch.py、pipeline/build.py、pipeline/commit.py、pipeline/fix.py、pipeline/test.py、flow.py、utils/submodule.py、agent/prompt.md、utils/config.py
1. 背景与目标
triton-ascend 项目发生架构调整(origin/3.7-master 分支):对上游代码的侵入式修改不再直接提交源码 ,而是以 patch 文件形式管理在 third_party/ascend/patch/ 下,每次编译时由 setup.py 自动 apply :
Patch 文件
内容
构建时是否 apply
triton-ascend-3.7.0.patch
上游源码的侵入式修改(16 个文件)
✅
triton-ascend-dev-3.7.0.patch
dev 变体的侵入式修改(1 个文件)
✅
npuir_adapter_to_llvm_23.patch
AscendNPU-IR 子模块的适配修改(9 个文件)
✅
triton-ascend-3.6.0.patch / -dev-3.6.0.patch
旧版本遗留
❌ 目录里还在,setup.py 已不引用
构建脚本的实际行为 (setup.py:1000-1067,apply_triton_ascend_patch() 在每次编译的 run() 中被调用,setup.py:496):
1. git checkout -- python/triton/runtime/autotuner.py # 先还原目标文件!
git apply triton-ascend-dev-3.7.0.patch
2. git checkout -- <16 个源码文件> # 先还原目标文件!
git apply triton-ascend-3.7.0.patch
3. git checkout -- <9 个子模块内文件> (cwd=子模块目录) # 先还原目标文件!
git apply --directory third_party/ascend/AscendNPU-IR npuir_adapter_to_llvm_23.patch
关键约束 :apply 之前构建脚本先对目标文件执行 git checkout --(还原到 HEAD)。这意味着对这 26 个被 patch 触碰的文件的修改无法以源码形式存活 ——直接留在源码里的修复会在下一次编译时被构建脚本静默丢弃再重新 apply patch。因此这些文件的修改必须以 patch 回流的方式提交(AI 修源码 → 工作流再生成进 .patch 文件 → 还原源码,见 §5),这是构建系统的强制要求,不是治理偏好。
工作流需要适配的变化点:
patch 集合变化 :npuir_adapter_to_llvm_23.patch 不匹配现有 glob triton-ascend-*.patch(ta_patch.py:34)
apply 责任转移 :编译时自动 apply —— 工作流不能再自己 git apply(重复 apply 会失败),但仍需要在合并后调整 patch (行号漂移)并提前知道 patch 触碰哪些文件
AI 修复提交改造 :编译过程中 AI 修复的代码,提交时必须跳过 patch 的改动 ——由 AI 分析改了哪些文件,单独提交 AI 的改动,提交信息由 AI 生成
提交后清理工作区 ,再进行其他流程(test / 下一步 / finalize)
主流程保持不变 :merge → resolve → build/test → commit 的主链路与原来一致
2. 现状与差异
#
环节
现状实现
新架构要求
适配
1
patch 发现
glob triton-ascend-*.patch(ta_patch.py:34)
3 个文件,其中 npuir 的命名不匹配 glob
配置化显式列表 + glob 兜底
2
patch 调整
AI patch_fix 模式调 hunk 位置(ta_patch.py:160-231)
仍需——合并后行号漂移,编译时 apply 失败即编译失败
保留 ,不变
3
patch 应用
工作流自己 git apply 后 diff 得 touched(ta_patch.py:79-98)
编译时项目自动 apply,工作流再 apply 会失败
删除 apply ,改为静态解析 touched
4
AI 修复提交
git add -A + 排除 touched(build.py:331-341、commit.py:67-77)
AI 分析改动文件清单,只提交 AI 改动
白名单式提交 (commit_plan)
5
提交信息
已有 AI 生成链(commit_message.txt → step_summary.md → 默认,build.py:360-397)
AI 生成提交信息
复用并强化 ,与文件清单同一次 AI 调用产出
6
清理工作区
仅 step 结束 revert(flow.py:200-207)
每次 AI 提交后清理,再继续其他流程
新增 restore_workspace,时机前移
7
submodule
只做整体提交(submodule.py),无 touched 排除
npu-ir 修改回流 patch,不直接提交子模块
submodule 只读,作为 patch 再生成中间状态;commit_submodule 退出 fix 主路径
8
fix 校验
validate_fix 只查路径前缀(fix.py:118-144)
touched 文件的修改标记为 patch 回流(不再一律拒绝)
校验规则增强
9
调整后提交
无独立时机——调整编辑挂在工作区,被 step 末 commit_step 或误扫进 AI fix 提交(build.py:331);build/test 失败则不提交
合并后行号漂移的调整成功后立即提交 ,再进 build
新增 commit_patch_adjustments(build 前独立提交)
10
模式门控
整个 patch 流程仅 ta_main 模式(flow.py:146、ta_patch.py:55)
编译自动 apply patch 是项目行为,与 merge mode 无关——upstream/ta_main 都需要调整
移除 ta_main 门控 ,全模式生效
3. 总体流程
主链路与原来一致,变化集中在 ta_patch 环节(apply → 静态解析)和 fix 循环内部(新增 commit_plan 白名单提交 + 每次提交后清理):
flowchart TD
P0["prepare → detect → plan → build baseline LLVM"]
P0 --> STEP
subgraph STEP["每个 step(循环)"]
direction TB
M["merge -X theirs"] --> R["resolve:AI 解冲突后立即提交"]
R --> AP["adjust_patches:AI 调整 hunk 位置<br/>直到 git apply --check 通过<br/>(工作流不再自己 apply)"]
AP --> PC["commit_patch_adjustments:<br/>调整成功后立即提交 .patch 文件<br/>(白名单:仅 patch 目录,build 前)"]
PC --> PS["parse_patches:静态解析 patch 头<br/>得出 touched 文件列表"]
PS --> FIX
subgraph FIX["build / test fix 循环"]
direction TB
FB{"编译 / 测试通过 ?"} -->|"false"| AF["ai_fix:AI 修复<br/>(AscendNPU-IR 文件可改,作为<br/>patch 再生成的中间产物)"]
AF --> RP["regenerate:子模块 diff<br/>机械更新 npuir_adapter_to_llvm_23.patch<br/>(其他修复正常提交)"]
RP --> CP["commit_plan:AI 分析改动清单 + 生成提交信息"]
CP --> WC["白名单提交:.patch 文件 + 非 touched 源码<br/>硬校验 staged ∩ touched = ∅"]
WC --> RW["restore_workspace:还原 touched 文件<br/>+ 清理工作区"]
RW --> FB
end
FIX -->|"全部通过"| CS["commit_step:提交剩余改动(兜底)<br/>(清理后 add -A 天然不含 patch 改动)"]
CS --> RV["restore_workspace(再次,保证下一步干净)"]
end
STEP --> FN["finalize → push PR"]
Loading
关键顺序保证 :先提交(白名单)再清理(还原 touched)——AI 的修复已落入提交,清理只会还原 patch 改动;下一次编译会重新自动 apply,清理不影响后续。
4. 适配点设计
4.1 patch 集合配置化
新增配置 TA_SOURCE_PATCHES(环境变量,逗号/空格分隔,相对于 third_party/ascend/patch/):
# config.py
source_patches : list [str ] = field (default_factory = lambda : [
"triton-ascend-3.7.0.patch" ,
"triton-ascend-dev-3.7.0.patch" ,
"npuir_adapter_to_llvm_23.patch" ,
])
默认值已对照构建脚本确认:setup.py 硬编码文件名 选择这三个 3.7.0 patch(3.6.0 的旧文件留在目录中但不 apply,构建无配置开关)
显式列表优先;未设置时回退到 glob 发现(triton-ascend-*.patch + npuir_*.patch)并打印 warning——注意 glob 会把遗留的 3.6.0 文件也卷进来,因此默认显式列表是主要路径 ,glob 仅是兜底
列表内不存在的文件:warning 跳过,不视为失败(防止版本演进后遗留文件名导致误报)
三个 patch 互不排斥,构建按固定顺序全部 apply(dev → main → npuir)
4.2 ta_patch 职责拆分:apply → 静态解析
模式范围变化(重要) :patch 管理不再局限于 ta_main 模式。新架构下"编译自动 apply patch"是 triton-ascend 项目的构建行为,与合并模式无关——upstream 模式合并上游代码后行号同样漂移,不调整 patch 编译必然失败。现有代码的 ta_main 门控(flow.py:146、ta_patch.py:55)需移除,adjust / commit / parse / regenerate 全模式生效。
ta_patch.py 改造为两个函数:
adjust_patches() — 保留现有 Phase 1/Phase 3:
逐个 git apply --check,失败则 AI patch_fix 调整(≤3 次),直到全部通过
任一 patch 调整失败 → ta_patch_ok=False → step 直接 FAIL(编译时 apply 必然失败,提前失败省时间)
结束执行 cleanup_temp_files
parse_patches(patch_files) -> PatchTouchInfo — 替代现有 Phase 2(apply + diff):
静态解析 patch 文件头,提取触碰文件列表,不执行 apply :
diff --git a/python/triton/compiler.py b/python/triton/compiler.py ← git diff 头
--- a/lib/foo.cpp ← unified 头(兜底)
+++ b/lib/foo.cpp
解析规则:优先 diff --git a/<path> b/<path>;无 git 头时回退 ---/+++ 行(统一路径 = +++ 侧去掉 b/ 前缀)
npuir patch 路径前缀规则 :npuir_adapter_to_llvm_23.patch 内是子模块相对路径(如 bishengir/include/...,构建时用 git apply --directory third_party/ascend/AscendNPU-IR 应用),解析后需统一前缀化为父仓库视角路径 third_party/ascend/AscendNPU-IR/<path>。识别依据:构建脚本对 npuir patch 使用 --directory(对应文件名在 TA_SOURCE_PATCHES 中的已知条目)
setup.py 硬编码清单交叉验证 :构建脚本的 patch_files(16)/dev_patch_files(1)/npuir_patch_files(9)三个列表是构建"还原+apply"的真实目标。解析结果与之比对:不一致时 warning 并取并集 (遗漏比多列更危险——漏掉的 patch 改动会混入提交)
路径归类:父仓库路径 vs third_party/ascend/AscendNPU-IR/ 前缀的子模块内路径(§4.5)
上下文语义变化:ta_patch_touched_files 从"apply 后实测 diff"变为"解析预期触碰"——更早可用(build 前),且不依赖工作区状态
格式验证 :三个 patch 均已确认为标准 git diff 格式(diff --git a/... b/... 头),静态解析可行
commit_patch_adjustments() — 调整成功后立即提交 (新增,build 之前):
合并上游代码导致行号漂移,adjust_patches 对 .patch 文件做的 hunk 位置调整是独立的、有价值的提交内容 ,应在调整验证通过后立刻固化为一次独立提交,而不是留到 step 末或混入 AI fix 提交:
时机 :adjust_patches 全部 git apply --check 通过后、build 之前(parse_patches 前后皆可,它不改文件)
内容 :仅 .patch 文件——白名单 = 工作区 diff 中位于 third_party/ascend/patch/ 的文件;硬校验 staged ⊆ patch 目录(调整步骤理论上只碰 patch 文件,多出即异常)
提交信息 :AI 生成(patch_fix 输出或 commit_plan)→ 兜底机械默认:[Sync](fix) Rebase ascend patches onto merged tree (<step_id>),body 列出本次调整的 patch 文件名
为什么不混入后续提交 :
调整若被 commit_fixes 的 git add -A(build.py:331)扫进 AI fix 提交,提交信息与实际内容不符
build/test 失败时 workflow 提前返回,挂在工作区的调整永不落盘 ,resume 也恢复不了——先行提交后调整始终保留
独立提交让 PR 历史可读:合并 → 解冲突 → patch 重定位 → AI 修复,各自成段
失败处理 :提交失败 → warning 不 fail(step 末 commit_step 的 git add -A 会兜底带上),但记录日志提醒
设计理由 (决策表):
方案
优点
缺点
结论
静态解析 patch 头
无需 apply、无副作用、build 前即可用、可重复执行
需处理多种 patch 格式
✅ 选用
继续自己 apply 再 diff
触碰文件列表绝对准确
与编译自动 apply 冲突(重复 apply 失败);apply 与编译之间的窗口不一致
❌
编译后再 diff 全仓库
无需解析
无法区分"patch 改动"与"AI 改动",违背核心诉求
❌
4.3 AI 修复提交:commit_plan 白名单模式
新增 AI mode commit_plan (agent/prompt.md + opencode_adapter 透传),在每次 AI fix 完成后、提交前调用。输入:
git status --porcelain、git diff --stat、git diff <未暂存文件>
解析出的 touched 文件列表(明确告知:这些文件的改动是 patch 的,不可提交 )
AI 自身的历史上下文(本次 fix 改了哪些文件)
输出(写入 step_dir):
commit_files.txt — 每行一个待提交文件路径(白名单 )
commit_message.txt — AI 生成的一行提交主题(复用 build.py:360-397 的读取链)
提交流程 (build.py:commit_fixes / commit.py:commit_step 共用新函数 commit_with_plan):
# 1. AI 产出白名单 + 提交信息(AI 失败则走兜底链)
# 2. 白名单过滤:剔除 touched 文件(硬校验,AI 列错也不提交)
files = [f for f in commit_files if f not in touched_set ]
# 3. git add -- <files>(非 -A;patch 文件本身的调整天然会被 AI 列入)
# 4. 校验 staged ∩ touched == ∅,否则报错并剔除
# 5. git commit -s -m <AI 信息>
兜底链 (AI 调用失败或产出非法时,保证流程不中断):
AI 产出 commit_files.txt + commit_message.txt → 白名单提交(首选)
AI 失败 → 回退现有逻辑:git add -A + 排除 touched 列表 + 默认信息
提交信息回退链不变:commit_message.txt → step_summary.md 首行 → 默认
fix 校验 (fix.py:validate_fix):保持原有路径前缀校验不变(AscendNPU-IR 文件天然在 third_party/ascend/ 前缀内);AI 修改列表记录为 last_fix_modified_files,其中落在 AscendNPU-IR touched 列表内的文件由 regenerate_and_reapply 回流到 npuir patch(§5.1),其余修复正常提交。
注意 :.patch 文件本身不在 touched 列表内(touched = patch 引用的源文件),因此 patch 文件的调整(patch_fix 产物)会被正常提交——这是有意为之,与项目"侵入式修改活在 patch 里"的治理模型一致。
4.4 提交后清理工作区
新增 restore_workspace() (ta_patch.py):
# 父仓库:还原 touched 源码文件到 HEAD(patch 改动被丢弃)
git checkout - - < touched files >
# 子模块:同样还原子模块内 touched 文件
# cleanup_temp_files:清除 .orig/.rej/__pycache__ 残留
时机 (相比现状前移):
时机
现状
新设计
每次 AI fix 提交后
不清理(patch 改动留到 step 末)
立即清理 ,再进入下一次 build/test 迭代
step 提交后(flow.py Step F)
revert_applied_patches
restore_workspace(替代)
为什么安全 :下一次编译会自动重新 apply patch,清理掉的 patch 改动会被构建系统恢复;而清理保证了每次 git add/merge 面对的是干净树。
连带简化 :清理在 commit_step 之前执行完毕后,commit_step 的 git add -A 天然不含 patch 改动——现有的 exclude_patch_files_from_index(commit.py:70-77、build.py:334-341)降级为防御性保留(防止清理失败时的兜底)。
4.5 Submodule 适配(npu-ir 修改回流 patch)
新政策 :工作流驱动的 npu-ir 修改不直接提交子模块,而是回流到 npuir_adapter_to_llvm_23.patch(父仓库提交,见 §5)。子模块工作树只是 patch 再生成的中间状态,step 结束时还原干净。
npuir_adapter_to_llvm_23.patch 触碰 third_party/ascend/AscendNPU-IR/ 子模块内的 9 个文件。静态解析出的路径需要按前缀归类:
# parse_patches 输出
touched = {
"parent" : ["python/triton/compiler.py" , ...], # 父仓库文件
"submodule" : ["bishengir/lib/..." , ...], # 子模块内相对路径(patch 原生格式)
}
各环节同步适配:
环节
父仓库
子模块(AscendNPU-IR)
AI 修复
非 touched 源码直接提交;touched 源码回流 patch
修改仅作为 patch 再生成的中间产物
patch 再生成
git diff HEAD -- files → triton-ascend-*.patch
git -C <submodule> diff HEAD -- files → npuir_adapter_to_llvm_23.patch(路径天然相对子模块,与 --directory apply 匹配)
白名单提交
.patch 文件 + 非 touched 源码
不直接提交 ——commit_submodule 退出 fix 提交主路径
清理
git checkout -- touched
子模块内 git checkout -- touched,不 reset 指针;step 结束时子模块恢复干净
提交顺序
先提交父仓库(含 .patch 更新)
无提交动作
保留项 :push_submodule 保留供存量场景(合并本身带入的子模块指针变化仍走 PR 推送流程);submodule_has_changes / commit_submodule 保留但不再被 fix 提交路径调用。
边界 :
子模块内 AI 修复 + patch 改动混在同文件 → §5 主机制天然覆盖(diff HEAD 同时含旧 hunk 与增量,合并进 patch 即完成)
子模块内新增/删除 文件 → 走 §5.3 兜底(git diff HEAD 需先 git add -N 意图添加,或二进制文件时人工处理)
5. AscendNPU-IR 修复回流 npuir patch
核心政策:架构调整后,工作流驱动的 npu-ir 修改一律回流到 npuir_adapter_to_llvm_23.patch,不直接提交子模块 ;AI 对其他代码(Ascend 后端等)的修复正常提交 ,不涉及 patch。提交树里永远只有 .patch 文件变化,npu-ir 源码级改动不出现。
5.1 主机制:fix-then-regenerate(修复走源码、提交走 patch)
AI 在打了 patch 的源码上 修复 AscendNPU-IR 文件(作为 patch 再生成的中间产物),编译/测试验证通过后,由工作流机械地把 diff 再生成进 npuir patch :
1. ai_fix:AI 修改 AscendNPU-IR 源码(prompt 告知这些修改将回流到 npuir patch)
2. 编译/测试验证通过
3. regenerate(机械、确定性,仅针对 AscendNPU-IR):
git -C third_party/ascend/AscendNPU-IR diff HEAD -- <files>
→ 合并进 npuir_adapter_to_llvm_23.patch
(diff 天然是子模块相对路径,与构建时 --directory 的 apply 方式一致)
4. 验证:对更新后的 npuir patch 执行 git apply --check(干净树上)
5. commit_plan → 白名单提交:npuir patch 文件 + 其他 AI 修复文件
6. restore_workspace:还原 npuir touched 源码 → 工作区干净
7. 下次编译自动 apply 新 patch 生效
关键点 :git diff HEAD 在打了 patch 的工作区上执行,输出天然 = 旧 patch 内容 + AI 增量修复,合并进 patch 文件即完成"修改提交到 patch"。无需区分旧 hunk 与新修复。
为什么只回流 npuir、其他正常提交 :triton-ascend patch 触碰的是上游 triton 文件,AI 修复受路径前缀校验限制(third_party/ascend/ 等),不会改动它们;Ascend 后端其他文件不被构建脚本 checkout,正常提交即可存活。只有 AscendNPU-IR 文件会被构建脚本 checkout+apply 冲掉,必须回流 patch。
为什么不直接让 AI 改 patch 文件 :AI 手写 unified diff hunk(行号/context/转义)极易出错且无法自检;在源码上修复 + 工作流机械再生成 + git apply --check 验证,全程可验证。
5.2 校验规则
阶段
规则
变化
ai_fix
保留原有路径前缀校验(third_party/ascend/ 等,AscendNPU-IR 天然在内);修改列表记录为 last_fix_modified_files
不变
regenerate
对 last_fix_modified_files 中所有 third_party/ascend/AscendNPU-IR/ 下的文件再生成(原 patch 未覆盖的文件追加新 section,touched 列表同步扩展);更新后 patch 必须 git apply --check 通过,失败 → 还原、修复作废重试
收窄
白名单提交
touched 源码硬性不入提交 (regenerate 失败时防止 patch 改动混入提交)
不变,仍是硬校验
5.3 兜底:hunk 级分离(regenerate 不可行时)
若因二进制文件、新增/删除文件等场景无法机械再生成,回退到确定性 hunk 级分离:
git checkout HEAD -- F # 还原到合并后基线
git apply --include=F <patch> # 恢复 patch 对 F 的改动
git diff F # 剩余 diff = 纯 AI 增量
→ 以该增量更新 patch 中对应 hunk;还原 F
5.4 方案对比
方案
优点
缺点
结论
fix-then-regenerate(仅 npuir)
机械、可验证(apply --check)、其他修复正常提交不打扰
新增/二进制文件需兜底
✅ 主机制
全部 touched 文件回流 patch
覆盖全
范围过大——上游 triton 文件 AI 本来不能改,徒增复杂度和风险
❌ 已收窄
AI 直接改 .patch 文件
无中间产物
AI 手写 hunk 脆弱、不可自检
❌ 降级为辅助
hunk 级分离
确定性
复杂;二进制/新删文件失效
✅ 兜底
直接提交子模块
与治理模型一致(旧)
构建 checkout 会丢、patch 与子模块双源漂移
❌ 新政策已废弃
6. 配置项汇总
配置
层级
默认值
说明
TA_SOURCE_PATCHES
env
3 个已知 patch 文件名
显式 patch 列表(新增)
TA_MERGE_MODE
env / CLI
upstream
不变;本设计的适配点在 ta_main 模式下生效
TA_MAX_RETRIES
env
10
不变,fix 循环上限
patch 调整次数
代码常量
_MAX_ADJUST_RETRIES = 3
不变
7. 风险与开放问题
#
风险/问题
影响
建议
1
3.7.0 与 dev-3.7.0 是否互斥
不确定构建实际 apply 哪个
✅ 已确认 :origin/3.7-master 的 setup.py 硬编码按序 apply 全部三个 3.7.0 patch,互不排斥;3.6.0 文件为遗留、不再引用
2
triton-ascend-3.6.0.patch / -dev-3.6.0.patch 遗留文件仍在 patch 目录
glob 兜底发现会把它们卷进 TA_SOURCE_PATCHES,导致对不上号的调整/校验
默认走显式列表;glob 兜底时按 setup.py 引用过滤;长期建议上游清理遗留文件
3
静态解析的格式覆盖:rename(rename from/to)、新文件(new file mode)、删除、二进制
解析遗漏 → touched 不完整 → patch 改动漏进提交
解析器覆盖 git diff 全格式;二进制文件额外记录警告(无法 hunk 分离);setup.py 清单交叉验证兜底
4
setup.py 硬编码的 patch_files 列表与 patch 实际内容漂移
构建还原/apply 的文件集合与工作流解析的 touched 不一致
parse_patches 以 patch 头部为准 + setup.py 清单交叉验证取并集;漂移时打印 warning 提醒人工核对
5
AI 修改 AscendNPU-IR 文件但 regenerate_and_reapply 失败(如新增/二进制文件)
修复无法进 npuir patch,被还原丢失、fix 循环空转
regenerate 后必须 git apply --check 验证;失败走 §5.3 兜底,再失败则还原文件并重试;prompt 明确告知"AscendNPU-IR 文件的修改将回流到 npuir patch"
6
每次 fix 多一次 commit_plan AI 调用
AI 成本上升、单步耗时增加
可配置 TA_AI_COMMIT_PLAN=false 跳过(走兜底链);或仅在 git status 非空时调用
7
构建系统 apply patch 的时机不可控(可能在子进程/临时目录)
工作流观测到的 touched 与实际不符
编译日志里 grep patch 应用记录做交叉验证(可选增强)
8
restore_workspace 使用 git checkout --,若清理时机与构建并发会丢改动
流程内串行执行,风险低
清理仅在提交后、下次 build 前执行,保持串行约束
9
移除 ta_main 门控后,upstream 模式的既有行为变化
patch 目录缺失/空时不应报错、原 upstream 流程回归风险
所有 patch 环节在"无 patch 文件"时优雅跳过(现有 ta_patch.py:62-67 逻辑保留);upstream 模式做一次完整回归
8. 文件变更清单
文件
变更
utils/config.py
新增 TA_SOURCE_PATCHES 配置
utils/context.py
touched 结构扩展(parent / submodule 分类)
pipeline/ta_patch.py
拆分为 adjust_patches + parse_patches;删除 apply 阶段;新增 restore_workspace、regenerate_and_reapply(仅 AscendNPU-IR diff 回流 npuir patch)、commit_patch_adjustments(调整后立即提交);移除 ta_main 门控
pipeline/build.py
commit_fixes 改用 commit_with_plan 白名单提交;fix 循环内插入 regenerate + 提交后调用 restore_workspace
pipeline/test.py
test fix 提交同样接入白名单 + regenerate + 清理
pipeline/commit.py
commit_step 接入白名单;清理后执行
pipeline/fix.py
validate_fix 增加 touched 标记 needs_patch_regen
utils/submodule.py
commit_submodule 退出 fix 提交主路径;保留 push_submodule 存量场景
agent/prompt.md
新增 commit_plan mode;fix 模式告知 touched 文件的修改将回流到 .patch 文件(改源码不直接生效)
flow.py
Step F 改用 restore_workspace;adjust 后插入 commit_patch_adjustments;fix 循环内插入 regenerate + commit_plan + 清理;patch 流程移除 ta_main 门控
编译自动 Apply Patch 架构下的 AI 修复提交与工作区清理
1. 背景与目标
triton-ascend 项目发生架构调整(
origin/3.7-master分支):对上游代码的侵入式修改不再直接提交源码,而是以 patch 文件形式管理在third_party/ascend/patch/下,每次编译时由setup.py自动 apply:triton-ascend-3.7.0.patchtriton-ascend-dev-3.7.0.patchnpuir_adapter_to_llvm_23.patchtriton-ascend-3.6.0.patch/-dev-3.6.0.patch构建脚本的实际行为(
setup.py:1000-1067,apply_triton_ascend_patch()在每次编译的run()中被调用,setup.py:496):关键约束:apply 之前构建脚本先对目标文件执行
git checkout --(还原到 HEAD)。这意味着对这 26 个被 patch 触碰的文件的修改无法以源码形式存活——直接留在源码里的修复会在下一次编译时被构建脚本静默丢弃再重新 apply patch。因此这些文件的修改必须以 patch 回流的方式提交(AI 修源码 → 工作流再生成进.patch文件 → 还原源码,见 §5),这是构建系统的强制要求,不是治理偏好。工作流需要适配的变化点:
npuir_adapter_to_llvm_23.patch不匹配现有 globtriton-ascend-*.patch(ta_patch.py:34)git apply(重复 apply 会失败),但仍需要在合并后调整 patch(行号漂移)并提前知道 patch 触碰哪些文件2. 现状与差异
triton-ascend-*.patch(ta_patch.py:34)patch_fix模式调 hunk 位置(ta_patch.py:160-231)git apply后 diff 得 touched(ta_patch.py:79-98)git add -A+ 排除 touched(build.py:331-341、commit.py:67-77)commit_message.txt→step_summary.md→ 默认,build.py:360-397)flow.py:200-207)restore_workspace,时机前移submodule.py),无 touched 排除commit_submodule退出 fix 主路径validate_fix只查路径前缀(fix.py:118-144)commit_step或误扫进 AI fix 提交(build.py:331);build/test 失败则不提交commit_patch_adjustments(build 前独立提交)flow.py:146、ta_patch.py:55)3. 总体流程
主链路与原来一致,变化集中在 ta_patch 环节(apply → 静态解析)和 fix 循环内部(新增 commit_plan 白名单提交 + 每次提交后清理):
flowchart TD P0["prepare → detect → plan → build baseline LLVM"] P0 --> STEP subgraph STEP["每个 step(循环)"] direction TB M["merge -X theirs"] --> R["resolve:AI 解冲突后立即提交"] R --> AP["adjust_patches:AI 调整 hunk 位置<br/>直到 git apply --check 通过<br/>(工作流不再自己 apply)"] AP --> PC["commit_patch_adjustments:<br/>调整成功后立即提交 .patch 文件<br/>(白名单:仅 patch 目录,build 前)"] PC --> PS["parse_patches:静态解析 patch 头<br/>得出 touched 文件列表"] PS --> FIX subgraph FIX["build / test fix 循环"] direction TB FB{"编译 / 测试通过 ?"} -->|"false"| AF["ai_fix:AI 修复<br/>(AscendNPU-IR 文件可改,作为<br/>patch 再生成的中间产物)"] AF --> RP["regenerate:子模块 diff<br/>机械更新 npuir_adapter_to_llvm_23.patch<br/>(其他修复正常提交)"] RP --> CP["commit_plan:AI 分析改动清单 + 生成提交信息"] CP --> WC["白名单提交:.patch 文件 + 非 touched 源码<br/>硬校验 staged ∩ touched = ∅"] WC --> RW["restore_workspace:还原 touched 文件<br/>+ 清理工作区"] RW --> FB end FIX -->|"全部通过"| CS["commit_step:提交剩余改动(兜底)<br/>(清理后 add -A 天然不含 patch 改动)"] CS --> RV["restore_workspace(再次,保证下一步干净)"] end STEP --> FN["finalize → push PR"]关键顺序保证:先提交(白名单)再清理(还原 touched)——AI 的修复已落入提交,清理只会还原 patch 改动;下一次编译会重新自动 apply,清理不影响后续。
4. 适配点设计
4.1 patch 集合配置化
新增配置
TA_SOURCE_PATCHES(环境变量,逗号/空格分隔,相对于third_party/ascend/patch/):triton-ascend-*.patch+npuir_*.patch)并打印 warning——注意 glob 会把遗留的 3.6.0 文件也卷进来,因此默认显式列表是主要路径,glob 仅是兜底4.2 ta_patch 职责拆分:apply → 静态解析
模式范围变化(重要):patch 管理不再局限于 ta_main 模式。新架构下"编译自动 apply patch"是 triton-ascend 项目的构建行为,与合并模式无关——upstream 模式合并上游代码后行号同样漂移,不调整 patch 编译必然失败。现有代码的 ta_main 门控(
flow.py:146、ta_patch.py:55)需移除,adjust / commit / parse / regenerate 全模式生效。ta_patch.py改造为两个函数:adjust_patches()— 保留现有 Phase 1/Phase 3:git apply --check,失败则 AIpatch_fix调整(≤3 次),直到全部通过ta_patch_ok=False→ step 直接 FAIL(编译时 apply 必然失败,提前失败省时间)cleanup_temp_filesparse_patches(patch_files) -> PatchTouchInfo— 替代现有 Phase 2(apply + diff):静态解析 patch 文件头,提取触碰文件列表,不执行 apply:
解析规则:优先
diff --git a/<path> b/<path>;无 git 头时回退---/+++行(统一路径 =+++侧去掉b/前缀)npuir patch 路径前缀规则:
npuir_adapter_to_llvm_23.patch内是子模块相对路径(如bishengir/include/...,构建时用git apply --directory third_party/ascend/AscendNPU-IR应用),解析后需统一前缀化为父仓库视角路径third_party/ascend/AscendNPU-IR/<path>。识别依据:构建脚本对 npuir patch 使用--directory(对应文件名在TA_SOURCE_PATCHES中的已知条目)setup.py 硬编码清单交叉验证:构建脚本的
patch_files(16)/dev_patch_files(1)/npuir_patch_files(9)三个列表是构建"还原+apply"的真实目标。解析结果与之比对:不一致时 warning 并取并集(遗漏比多列更危险——漏掉的 patch 改动会混入提交)路径归类:父仓库路径 vs
third_party/ascend/AscendNPU-IR/前缀的子模块内路径(§4.5)上下文语义变化:
ta_patch_touched_files从"apply 后实测 diff"变为"解析预期触碰"——更早可用(build 前),且不依赖工作区状态格式验证:三个 patch 均已确认为标准 git diff 格式(
diff --git a/... b/...头),静态解析可行commit_patch_adjustments()— 调整成功后立即提交(新增,build 之前):合并上游代码导致行号漂移,
adjust_patches对.patch文件做的 hunk 位置调整是独立的、有价值的提交内容,应在调整验证通过后立刻固化为一次独立提交,而不是留到 step 末或混入 AI fix 提交:adjust_patches全部git apply --check通过后、build之前(parse_patches前后皆可,它不改文件).patch文件——白名单 = 工作区 diff 中位于third_party/ascend/patch/的文件;硬校验 staged ⊆ patch 目录(调整步骤理论上只碰 patch 文件,多出即异常)patch_fix输出或commit_plan)→ 兜底机械默认:[Sync](fix) Rebase ascend patches onto merged tree (<step_id>),body 列出本次调整的 patch 文件名commit_fixes的git add -A(build.py:331)扫进 AI fix 提交,提交信息与实际内容不符commit_step的git add -A会兜底带上),但记录日志提醒设计理由(决策表):
4.3 AI 修复提交:commit_plan 白名单模式
新增 AI mode
commit_plan(agent/prompt.md+opencode_adapter透传),在每次 AI fix 完成后、提交前调用。输入:git status --porcelain、git diff --stat、git diff <未暂存文件>输出(写入 step_dir):
commit_files.txt— 每行一个待提交文件路径(白名单)commit_message.txt— AI 生成的一行提交主题(复用build.py:360-397的读取链)提交流程(
build.py:commit_fixes/commit.py:commit_step共用新函数commit_with_plan):兜底链(AI 调用失败或产出非法时,保证流程不中断):
commit_files.txt+commit_message.txt→ 白名单提交(首选)git add -A+ 排除 touched 列表 + 默认信息commit_message.txt→step_summary.md首行 → 默认fix 校验(
fix.py:validate_fix):保持原有路径前缀校验不变(AscendNPU-IR 文件天然在third_party/ascend/前缀内);AI 修改列表记录为last_fix_modified_files,其中落在 AscendNPU-IR touched 列表内的文件由regenerate_and_reapply回流到 npuir patch(§5.1),其余修复正常提交。注意:
.patch文件本身不在 touched 列表内(touched = patch 引用的源文件),因此 patch 文件的调整(patch_fix 产物)会被正常提交——这是有意为之,与项目"侵入式修改活在 patch 里"的治理模型一致。4.4 提交后清理工作区
新增
restore_workspace()(ta_patch.py):时机(相比现状前移):
revert_applied_patchesrestore_workspace(替代)为什么安全:下一次编译会自动重新 apply patch,清理掉的 patch 改动会被构建系统恢复;而清理保证了每次
git add/merge 面对的是干净树。连带简化:清理在
commit_step之前执行完毕后,commit_step的git add -A天然不含 patch 改动——现有的exclude_patch_files_from_index(commit.py:70-77、build.py:334-341)降级为防御性保留(防止清理失败时的兜底)。4.5 Submodule 适配(npu-ir 修改回流 patch)
新政策:工作流驱动的 npu-ir 修改不直接提交子模块,而是回流到
npuir_adapter_to_llvm_23.patch(父仓库提交,见 §5)。子模块工作树只是 patch 再生成的中间状态,step 结束时还原干净。npuir_adapter_to_llvm_23.patch触碰third_party/ascend/AscendNPU-IR/子模块内的 9 个文件。静态解析出的路径需要按前缀归类:各环节同步适配:
git diff HEAD -- files→ triton-ascend-*.patchgit -C <submodule> diff HEAD -- files→ npuir_adapter_to_llvm_23.patch(路径天然相对子模块,与--directoryapply 匹配).patch文件 + 非 touched 源码commit_submodule退出 fix 提交主路径git checkout -- touchedgit checkout -- touched,不 reset 指针;step 结束时子模块恢复干净保留项:
push_submodule保留供存量场景(合并本身带入的子模块指针变化仍走 PR 推送流程);submodule_has_changes/commit_submodule保留但不再被 fix 提交路径调用。边界:
git diff HEAD需先git add -N意图添加,或二进制文件时人工处理)5. AscendNPU-IR 修复回流 npuir patch
核心政策:架构调整后,工作流驱动的 npu-ir 修改一律回流到
npuir_adapter_to_llvm_23.patch,不直接提交子模块;AI 对其他代码(Ascend 后端等)的修复正常提交,不涉及 patch。提交树里永远只有.patch文件变化,npu-ir 源码级改动不出现。5.1 主机制:fix-then-regenerate(修复走源码、提交走 patch)
AI 在打了 patch 的源码上修复 AscendNPU-IR 文件(作为 patch 再生成的中间产物),编译/测试验证通过后,由工作流机械地把 diff 再生成进 npuir patch:
关键点:
git diff HEAD在打了 patch 的工作区上执行,输出天然 = 旧 patch 内容 + AI 增量修复,合并进 patch 文件即完成"修改提交到 patch"。无需区分旧 hunk 与新修复。为什么只回流 npuir、其他正常提交:triton-ascend patch 触碰的是上游 triton 文件,AI 修复受路径前缀校验限制(
third_party/ascend/等),不会改动它们;Ascend 后端其他文件不被构建脚本 checkout,正常提交即可存活。只有 AscendNPU-IR 文件会被构建脚本 checkout+apply 冲掉,必须回流 patch。为什么不直接让 AI 改 patch 文件:AI 手写 unified diff hunk(行号/context/转义)极易出错且无法自检;在源码上修复 + 工作流机械再生成 +
git apply --check验证,全程可验证。5.2 校验规则
third_party/ascend/等,AscendNPU-IR 天然在内);修改列表记录为last_fix_modified_fileslast_fix_modified_files中所有third_party/ascend/AscendNPU-IR/下的文件再生成(原 patch 未覆盖的文件追加新 section,touched 列表同步扩展);更新后 patch 必须git apply --check通过,失败 → 还原、修复作废重试5.3 兜底:hunk 级分离(regenerate 不可行时)
若因二进制文件、新增/删除文件等场景无法机械再生成,回退到确定性 hunk 级分离:
5.4 方案对比
6. 配置项汇总
TA_SOURCE_PATCHESTA_MERGE_MODEupstreamTA_MAX_RETRIES_MAX_ADJUST_RETRIES = 37. 风险与开放问题
3.7.0 与 dev-3.7.0 是否互斥不确定构建实际 apply 哪个origin/3.7-master的 setup.py 硬编码按序 apply 全部三个 3.7.0 patch,互不排斥;3.6.0 文件为遗留、不再引用triton-ascend-3.6.0.patch/-dev-3.6.0.patch遗留文件仍在 patch 目录TA_SOURCE_PATCHES,导致对不上号的调整/校验rename from/to)、新文件(new file mode)、删除、二进制patch_files列表与 patch 实际内容漂移regenerate_and_reapply失败(如新增/二进制文件)git apply --check验证;失败走 §5.3 兜底,再失败则还原文件并重试;prompt 明确告知"AscendNPU-IR 文件的修改将回流到 npuir patch"commit_planAI 调用TA_AI_COMMIT_PLAN=false跳过(走兜底链);或仅在git status非空时调用restore_workspace使用git checkout --,若清理时机与构建并发会丢改动ta_patch.py:62-67逻辑保留);upstream 模式做一次完整回归8. 文件变更清单
utils/config.pyTA_SOURCE_PATCHES配置utils/context.pypipeline/ta_patch.pyadjust_patches+parse_patches;删除 apply 阶段;新增restore_workspace、regenerate_and_reapply(仅 AscendNPU-IR diff 回流 npuir patch)、commit_patch_adjustments(调整后立即提交);移除 ta_main 门控pipeline/build.pycommit_fixes改用commit_with_plan白名单提交;fix 循环内插入 regenerate + 提交后调用restore_workspacepipeline/test.pypipeline/commit.pycommit_step接入白名单;清理后执行pipeline/fix.pyvalidate_fix增加 touched 标记needs_patch_regenutils/submodule.pycommit_submodule退出 fix 提交主路径;保留push_submodule存量场景agent/prompt.mdcommit_planmode;fix 模式告知 touched 文件的修改将回流到.patch文件(改源码不直接生效)flow.pyrestore_workspace;adjust 后插入commit_patch_adjustments;fix 循环内插入 regenerate + commit_plan + 清理;patch 流程移除 ta_main 门控