本地测试通过、CI流水线却失败,先不要让AI重写业务代码。更有效的起点是固定失败提交,找到第一处真正失败的命令,再比较本地与CI的运行条件:依赖版本、系统、时区、环境变量、缓存和并行顺序。AI可以帮助整理线索,但每个原因都应对应一个可证实或排除的检查。
本文按“证据包—环境差异—单个假设—复测”的顺序展开,并用报表日期跨时区的例子说明:一次重跑变绿,并不能证明问题已修复。需要持续处理项目文件时,可把脱敏日志、流水线配置和相关代码放入墨刀AI客户端的本地项目中分析。

先找到失败阶段,别只盯最后一行
“Process completed with exit code 1”说明进程失败,不说明原因。安装依赖失败、构建失败、断言失败和部署权限不足,分别需要不同资料。把完整日志不加区分地交给AI,容易让它抓住末尾的连锁报错,而忽略前面的第一处异常。
| 失败阶段 | 优先找的证据 | 先不要做什么 |
|---|---|---|
| 依赖安装 | 运行时版本、锁文件、实际安装命令和下载错误 | 不要先修改业务断言 |
| 编译或打包 | 第一条编译错误、文件路径、导入名称和构建参数 | 不要用删除整个功能来换取通过 |
| 测试执行 | 失败测试、期望/实际值、测试前置数据与时序 | 不要直接把期望值改成错误输出 |
| 部署或发布 | 目标环境、权限范围、服务响应与部署产物 | 不要把凭证原文粘贴到任务中 |
先找到第一个失败作业,再定位其中最早有解释力的错误。一个构建工具报错后,后续“产物不存在”“无法上传报告”可能只是结果,应当跟随原错误处理,而不是分别修三次。
给AI一份对应到同次运行的材料
建议在项目内建立一个临时诊断目录,保存运行编号、提交标识、失败命令、相关日志、流水线配置和复现说明。日志可以裁到错误前后必要范围,但要保留文件名、错误类型、时间顺序和关键堆栈。屏蔽Token、Cookie、真实用户信息及业务机密,不要把关键路径一并涂掉而失去定位依据。
ci-diagnosis/
run-context.md # 提交、作业、命令、运行环境
failing-step.log # 同一次失败的脱敏日志
local-result.txt # 本地同命令的实际结果
hypotheses.md # 假设与验证记录
工作区代码如果已经比失败提交新了几个版本,就不能把本地结果直接和旧流水线比较。先在独立副本或隔离工作区使用对应提交;保留尚未提交的个人工作,再进行版本切换。客户端读取本地文件,是材料分析的一部分;远端日志仍应通过团队已有平台取得,不应假设它能直接读取所有CI平台。
本地与CI差在哪一项
排查时可先写一张只记录差异的表:运行时和包管理器的版本是否一致,是否都按锁文件安装,操作系统的路径大小写规则是否不同,时区和语言设置是否一致,测试是否并行,是否复用了旧缓存,以及相关环境变量是否存在。环境变量通常只需要比较“有无”和配置来源,不需要展示真实值。
看到差异后,仍要建立因果关系。例如日志显示导入路径找不到文件,路径大小写才是优先假设;如果失败的是日期断言,就优先核对日期解析、时区和当前时间。不能因为本地和CI系统不同,就把所有问题统称为“环境问题”。

报表日期:为什么同一时间会得到两天
设一份运营报表按北京时间归档。接口返回时间2026-10-04T18:30:00Z,其北京时间已经是10月5日。下面的代码取的是运行环境的本地日期:
const instant = new Date('2026-10-04T18:30:00Z');
console.log(instant.getDate());
当运行环境为Asia/Shanghai时,这个值是5;在UTC环境下则是4。如果本地机器位于北京时间,测试可能一直通过,CI默认时区改变后便失败。原因并不是“CI慢”,而是代码把机器的本地时区误当成业务规定的时区。
验证时,把同一段脚本分别放在明确的两个时区下运行。Node.js环境可在启动进程前设置TZ,例如Linux/macOS终端中分别执行TZ=UTC node date-check.mjs和TZ=Asia/Shanghai node date-check.mjs;PowerShell可设置$env:TZ='UTC'后再执行。记录实际输出,不仅记录“已检查时区”。
修复应体现业务约定:报表归档日明确使用Asia/Shanghai,而非简单把服务器时区改成开发者时区。下面的提取方式让时区出现在代码中,避免机器设置决定结果:
const parts = new Intl.DateTimeFormat('en-CA', {
timeZone: 'Asia/Shanghai',
year: 'numeric', month: '2-digit', day: '2-digit'
}).formatToParts(instant);
const value = Object.fromEntries(parts.map(p => [p.type, p.value]));
const businessDate = `${value.year}-${value.month}-${value.day}`;
console.log(businessDate); // 2026-10-05
这里用组成字段拼接日期,避免依赖语言环境恰好输出某种分隔符。随后应补午夜前后、月底和跨年的断言;业务若使用其它地区时区,还需要考虑对应的夏令时规则。把时间规则写清楚,比盲目给测试增加等待更有效。
让每个猜测都带一项验证
可以向AI下达这样的诊断任务:
请分析 ci-diagnosis/ 中同一次CI失败,不修改业务代码、测试或配置文件。
允许创建或更新指定的诊断记录 hypotheses.md。
先确认失败阶段、命令和第一处有解释力的错误。
结合流水线配置、报表日期代码和相关测试,列出最多3个优先假设。
每个假设写清:支持证据、缺少的证据、如何验证、什么结果能排除它。
不要因为本地通过就认定业务正确;不要跳过测试或提高超时来掩盖失败。
涉及时间、随机数、并行执行时,明确哪些条件需要固定。
将结论写进 hypotheses.md,区分已证实和待验证。
例如,时区假设可以通过固定同一时间戳、切换运行时区验证;依赖变化可以通过干净目录和锁文件安装比较;共享状态竞争可以通过单个测试与整组并行运行对照。先操作最能区分原因的验证,不必把所有可能性都跑一遍。
确认故障来自依赖升级后,可继续梳理版本范围、锁文件与peer依赖,把关联包分批迁移和验证。
偶发失败要保留它再次出现的条件
间歇性失败经常被“重跑成功”掩盖。记录同一提交下成功与失败的运行编号、测试顺序、耗时、重试次数和资源条件,才能判断是否存在时间依赖或共享状态。可以在已知环境下重复执行受影响测试,但要写明重复次数和实际失败次数,不把几次成功当成可靠性保证。
如果测试依赖当前日期,应固定测试时钟;依赖随机输入,应记录种子与生成器版本;等待异步操作,应等明确完成条件,而不是任意延长休眠。隔离故障测试必须是有负责人和恢复计划的临时措施,不能作为已解决的证据。
什么时候可以结束这次排查
结束条件应包括:原失败条件能够解释,修复差异足够小,原失败命令在对应环境通过,相关回归没有新增问题。对于上面的日期问题,还要确认UTC和Asia/Shanghai两种环境都得到同一业务日期。若远端流水线尚未运行,只能说本地验证完成,不能写“CI已恢复”。
GitHub的CI失败诊断教程展示了让AI结合日志与代码缩小问题范围的方法。实际使用中,可以让墨刀AI客户端分析本地日志与代码并维护排查记录,再回到团队流水线完成验证;需要补充代码背景时,先查清相关业务调用链,不要让不完整上下文替你决定修改范围。