模型架构、算子组合和精度格式持续变化,而负责导入、优化和执行模型的编译栈往往需要更长的适配周期。即使底层系统已经成熟,一个新模型也可能因为少数未知算子、动态形状约束或图变换错误而无法运行。AI 的价值不只是生成一段补丁,而是把故障归类、最小化复现、查找相似实现和验证候选改动串成一条受控流水线。
“首日支持”需要分层定义
团队容易把“模型可以导入”误认为“模型已经支持”。更实用的做法是把启用状态拆成四层:
| 层级 | 验收标准 | 常见阻塞点 |
|---|---|---|
| 可导入 | 编译器能够解析模型图 | 未注册算子、属性格式变化 |
| 可编译 | 图变换和代码生成能够完成 | 动态形状、布局或类型推导失败 |
| 结果正确 | 输出与参考实现处于允许误差内 | 降精度、融合规则、边界条件错误 |
| 性能可用 | 延迟、吞吐量和显存达到目标 | 回退执行、低效 kernel、重复数据搬运 |
“Day One”更合理的目标通常是:主路径能够编译和执行,有明确的正确性证据,并且所有回退都可以观测。极致性能可以继续迭代,但不能用性能优化掩盖结果错误。
这种分层也让 AI 更容易完成具体任务。相比“让这个模型跑起来”,下面这些目标更适合交给模型或代理:
- 根据编译日志识别首个根因,而不是重复解释后续级联错误。
- 从模型图中提取失败算子的输入类型、形状和属性。
- 在代码库中寻找语义相近的 lowering、shape inference 和测试。
- 生成候选补丁计划,并列出必须覆盖的边界条件。
- 比较参考后端与目标后端的输出,归纳误差模式。
给 AI 的不是一段报错,而是一份证据包
编译失败日志通常包含大量噪声。要让 AI 的输出可执行,应为每次失败保存结构化证据:
artifacts/
├── graph.json # 裁剪后的模型图或 IR
├── failure.txt # 完整编译错误与调用栈
├── environment.json # 编译器版本、设备、精度和参数
├── reference-output.npz # 参考后端输出
└── target-output.npz # 目标后端输出,若已能执行
其中最重要的是最小复现。一个包含数百个节点的完整模型,会让代理同时追踪过多假设;裁剪到一个算子或一个子图后,输入形状、精度和预期语义才能进入单元测试。
证据包还应记录静态形状与动态形状的差异。例如,一个算子在 [1, 128] 上成功,并不代表它能处理 [batch, sequence]。如果编译器支持回退执行,也要把回退节点列表作为产物保存,否则“运行成功”可能只是把大部分计算交还给解释器。
可以这样实践:让 AI 先生成补丁计划
下面是一个可改造的最小脚本。它读取 artifacts/graph.json 和 artifacts/failure.txt,调用兼容 OpenAI Chat Completions 格式的服务,并要求模型返回结构化的适配计划。运行前需要修改 AI_BASE_URL、AI_MODEL 和 AI_API_KEY;这是一种实践方案,不代表来源中指定了某个 API 或供应商。
# ai_triage.py
import json
import os
from pathlib import Path
from urllib.request import Request, urlopen
base_url = os.environ["AI_BASE_URL"].rstrip("/")
api_key = os.environ["AI_API_KEY"]
model = os.environ["AI_MODEL"]
graph = Path("artifacts/graph.json").read_text(encoding="utf-8")
failure = Path("artifacts/failure.txt").read_text(encoding="utf-8")
system_prompt = """You are a compiler enablement engineer.
Analyze the first root cause, not cascading errors.
Return JSON with these keys:
root_cause, missing_capability, related_code_searches,
proposed_changes, required_tests, correctness_risks, performance_risks.
Do not claim that a patch is correct without test evidence.
"""
payload = {
"model": model,
"temperature": 0,
"response_format": {"type": "json_object"},
"messages": [
{"role": "system", "content": system_prompt},
{
"role": "user",
"content": f"Compiler failure:\n{failure}\n\nModel graph or IR:\n{graph}",
},
],
}
request = Request(
f"{base_url}/chat/completions",
data=json.dumps(payload).encode("utf-8"),
headers={
"Authorization": f"Bearer {api_key}",
"Content-Type": "application/json",
},
method="POST",
)
with urlopen(request, timeout=120) as response:
result = json.load(response)
content = result["choices"][0]["message"]["content"]
plan = json.loads(content)
print(json.dumps(plan, ensure_ascii=False, indent=2))
可以这样运行:
export AI_BASE_URL="https://your-compatible-endpoint.example/v1"
export AI_MODEL="your-model-name"
export AI_API_KEY="replace-with-a-secret"
python ai_triage.py > patch-plan.json
python -m json.tool patch-plan.json
让 AI 先输出计划,而不是直接修改编译器,可以降低错误补丁进入代码库的概率。工程师可以检查它是否找到了正确的 IR 层级、是否混淆前端算子与后端 kernel,以及测试是否覆盖动态维度、空张量、广播和不同精度。
通过审查后,代理可以继续生成候选实现,但每个候选改动都应经过固定验证链:
# 命令名称需要替换为项目中的真实入口
./tools/import_model.sh fixtures/new-model
./tools/compile_model.sh fixtures/new-model --dump-fallbacks
./tools/compare_outputs.sh \
artifacts/reference-output.npz \
artifacts/target-output.npz \
--rtol 1e-4 --atol 1e-5
./tools/benchmark_model.sh fixtures/new-model --warmup 10 --runs 50
误差阈值不能机械复用。FP32、FP16、BF16 和量化模型需要不同标准;生成式模型还可能需要比较 logits、固定随机种子的 token 序列,或任务级指标,而不是只比较最终文本。
自动化边界:AI 提速,测试签字
AI 很适合压缩搜索时间,但不应成为正确性的证明。编译器改动通常位于共享路径上,一个为新模型生成的融合规则可能改变已有模型的数值结果。合并前至少要检查:
- 新增测试能否在没有完整模型权重的情况下稳定复现问题。
- 测试是否同时覆盖静态形状、代表性的动态形状和边界输入。
- 参考结果是否来自可信后端,并记录精度、设备和版本。
- 是否出现静默回退,以及回退对延迟和显存的影响。
- AI 服务是否会接触私有模型、权重、输入数据或内部源码。
- 生成代码引用的实现是否满足项目许可证和贡献要求。
更稳妥的落地顺序是从“只读代理”开始:让 AI 整理日志、定位代码和生成测试建议;随后开放受限分支上的补丁生成;等正确率和回滚机制得到验证后,再接入自动基准测试。这样,首日模型适配不再依赖工程师从海量日志中手工搜索,同时关键判断仍由可重复的测试、基准数据和代码审查完成。