Astro Triage 流程解析:diagnose.md 的源码诊断工作流与 report.md 协作机制
本文聚焦 Astro 仓库 .agents/skills/triage/diagnose.md 定义的“诊断(Diagnose)”技能:它规定了一个已复现 Bug 在 Astro 源码中被定位根因的完整工作流——从读取复现报告、定位 packages/ 下的源文件、添加插桩日志,到还原现场并输出带置信度诊断结论的 report.md。读完本文,你将掌握这条 LLM Agent 诊断流水线的每个步骤约束、服务端管理规则,以及它与 reproduce/verify/fix 技能之间通过 report.md 传递上下文的协作机制。
1. 在 Triage 流水线中的定位
diagnose.md 不是独立文档,而是 triage 技能(SKILL.md)四步流水线中的第二步。整个流程为:
- Reproduce(reproduce.md):搭建最小复现项目,写出
report.md; - Diagnose(diagnose.md):在源码中定位根因,向
report.md追加诊断章节; - Verify(verify.md):判断行为是 Bug 还是有意设计;
- Fix(fix.md):实现最小修复并补测试、changeset。
diagnose.md 开头对两个硬约束的定义决定了它与其他技能的关系:
- CRITICAL(必须写报告):无论诊断成功与否——找不到根因、遇到报错、结论不确定——都必须在结束前读取并追加
report.md。原文强调“orchestrator 和下游技能依赖这个文件判断发生了什么”,如果诊断结束而没写报告,整条流水线会“静默失败”。 - SCOPE(只做诊断):完成本工作流即结束,不做更大范围的问题验证、不修复问题、不派生子任务/子代理。
另外,orchestrator 会根据诊断产出的置信度决定走向:confidence 为 low 时直接跳到 Output 结束 triage;medium/high 才进入 verify 阶段(见 SKILL.md Step 2)。因此诊断输出的置信度字段实际上是一条流程控制信号,而不只是描述性标签。
2. 前置变量(Prerequisites)
文档声明了四个贯穿全流程的变量,它们可以由 orchestrator 作为 args 传入,也可以在独立运行时从对话上下文推断:
| 变量 | 说明 | 仓库依据 |
|---|---|---|
triageDir |
复现项目所在目录(如 triage/issue-123),未传入时从先前对话推断 |
pnpm-workspace.yaml 将 triage/* 声明为 pnpm workspace 包,因此复现项目能直接链接到 monorepo 内的 astro 源码包 |
issueDetails |
GitHub API 的 issue 详情 payload;若上下文缺失,可运行 gh issue view ${issue_number} 从 GitHub 拉取 |
SKILL.md 中同样接受 issueTitle/issueBody 作为输入 |
report.md |
位于 triageDir 内、可能已存在的文件,包含之前所有技能写入的完整上下文 |
由 reproduce 技能创建,diagnose 只追加不覆盖 |
| Astro Compiler 源码 | withastro/compiler 仓库可能被克隆到仓库根的 .compiler/(已被 gitignore)。若存在则纳入诊断范围——有些 Bug 源于编译器而非 packages/(例如 HTML 解析、.astro 文件转换)。当堆栈或调查发现指向编译器行为时,应到 .compiler/ 中查相关源码 |
.gitignore 第 3 行的 /.compiler/ 确认该目录被刻意忽略;fix.md 进一步说明该克隆“仅作参考”(reference only),未接入 monorepo 依赖,无法端到端测试 |
从源码结构看,这一设计解决了 Astro 仓库诊断的特殊难点:.astro 文件先被编译器转成 JS 模板,再进入 packages/astro 的运行时/构建逻辑,一条渲染类 Bug 的堆栈可能跨越两个代码库。文档因此要求诊断者具备“先判断 Bug 属于哪一层”的意识。
3. Step 1:审阅复现结果(含提前退出)
诊断的第一步是读取 triageDir/report.md。文档给出了明确的提前退出(skip)规则:
- 若
report.md显示 Bug 未复现或被跳过(识别关键词:"could not reproduce"、"SKIP REASON"、"skipped: true"),则向report.md追加DIAGNOSIS SKIPPED: No reproduction并返回confidence: null,立即结束。
这条规则与下游 fix.md 的“低置信度路径”(confidence 为 low 或 null 时不尝试改代码,只留下失败测试和 // TRIAGE: 路标注释)严格对应——诊断阶段不猜测,修复阶段就不会基于猜测下手。
需要重新触发一次复现来亲眼看到报错时,标准命令是:
pnpm -C <triageDir> run build # 或 dev/preview
pnpm -C 的用法与 AGENTS.md 中的 monorepo 约定一致:在 packages/examples/triage 目录下执行项目本地脚本时必须带 -C。
4. Step 2:定位相关源文件
利用 Step 1 收集到的错误信息、堆栈和其他复现细节,圈定 packages/ 中可能涉及的源文件。
这一步之所以可行,依赖于 AGENTS.md 中明确的 dist→src 映射规则:
node_modules/astro/dist/...→packages/astro/src/...node_modules/@astrojs/react/...→packages/integrations/react/src/...
也就是说,复现项目(triageDir)跑的是本地 workspace 链接的 Astro 包,错误堆栈里出现的 dist/ 路径可以直接翻译回 packages/ 下的 TypeScript 源码进行阅读和插桩。这也是为什么 triage 复现项目要接进 pnpm workspace(triage/* 在 pnpm-workspace.yaml 中声明),而不是安装 npm 上发布的版本——只有源码版 Astro 才能被插桩调试。
5. Step 3:用插桩(Instrumentation)还原代码路径
这是 diagnose 技能的核心手段:向源码添加 console.log 来理解实际执行路径。文档给出的示例直接指向构建入口:
// In packages/astro/src/core/build/index.ts
console.log('[DEBUG] Building page:', pagePath);
console.log('[DEBUG] Props:', JSON.stringify(props, null, 2));
示例文件确实存在——packages/astro/src/core/build/index.ts 就是 astro build 的构建入口(导出 build 函数,负责 resolveConfig、createVite、路由清单与静态构建流程),在其中打日志可以观察每一页构建时的入参。
添加日志后,文档规定的完整循环是:
- 重新构建包(例如
pnpm -C packages/astro build)——这对应 AGENTS.md 的关键事实:“Edits to source files take effect after rebuilding the package viapnpm build”,改了src/不重新构建,复现项目里跑的还是旧的dist/; - 重跑复现(例如
pnpm -C <triageDir> build|dev|preview); - 观察调试输出。
迭代目标是用三个问题收敛:正在执行哪条代码路径?传入了什么数据?逻辑在哪里偏离了预期行为?
服务器管理规则(防“时间预算”耗尽)
文档用一整段约束 dev server 的生命周期管理,这些规则直接映射到仓库工具链的真实能力:
- 重启前必须先停旧服务器:
pnpm -C <triageDir> dev stop。这套dev --background / dev logs / dev status / dev stop子命令在 AGENTS.md 的 “Background Dev Servers” 一节有完整说明; - 失败两次即放弃:服务器连续两次起不来就停止重试,用已有数据写出诊断,不要在服务器重启上空转。这与 SKILL.md 的总则“Do not get stuck on infrastructure problems……bail out after 2 attempts”一致;
- 优先用
astro build:能用构建期复现就避免 dev/preview,从根上绕开服务器生命周期问题; - 永远不要用
&后台化:用pnpm -C <triageDir> dev --background,&在 CI 环境会挂起。
插桩后的现场还原
循环结束后有一条不可妥协的收尾规则:用 git checkout -- <file> 撤掉所有插桩。文档的原话是“Debug logs must not leak into downstream steps”——遗留的 console.log 会污染后续的 verify/fix 阶段,fix.md 的 Step 11 清理清单里也把“Debug code, console.logs”列为必须回滚项。诊断阶段对源码树是“借而不留”的关系。
6. Step 4:确定根因并书面化
当代码路径被理解后,诊断必须回答四个问题:
- 哪个文件包含 Bug;
- 代码做错了什么——具体的逻辑错误;
- 为什么表现为观察到的现象——错误如何外显;
- 修复应该怎么做——高层思路(注意:只给思路,不给补丁,实现属于 fix 技能)。
同时要求考虑三个延伸问题:这是不是近期变更引入的回归?是否影响其他相似用例?有没有需要留意的边界情况?
文档还有两条非常“工程文化”式的约束:
- 禁止以“删掉用户依赖”作为修复建议。原文:“Never suggest removing a user's dependency (adapters, framework integrations, features like MDX or DB) as a fix, those are things the user needs.” 修复必须在用户现有技术栈内成立。这条禁令在 fix.md Step 3 中被原样重申,说明它是整条 triage 流水线的一等约束;
- 语气校准(Tone calibration):根因描述要事实化、不戏剧化。除非证据真正支持,避免 “critical flaw”“fundamentally broken”“severe vulnerability” 之类措辞。文档给的例子很直白:“缺一个 null 检查就是缺一个 null 检查,不是‘渲染管线中的关键疏漏’。” 诊断的目标是帮维护者理解哪里错了并导向修复,而不是制造恐慌。
7. Step 5:写回 report.md(输出契约)
诊断结果以追加新章节的方式写入 report.md(不覆盖 reproduce 技能写的内容),章节必须包含:根因、带行号的影响文件、代码路径的详细解释、插桩结果,以及建议的修复方向——文档明确说这样做的目的是“help the fix skill work faster”。
报告同时承担了“最终 GitHub 评论原料”的角色(评论由下游 comment 技能生成),因此硬性要求包含四项:
- 根因解释(哪些文件、什么逻辑错了、为什么);
- 受影响文件路径及行号;
- 建议的修复方向;
- 置信度(
high/medium/low)与所有保留意见(caveats)。
这套“文件即交接”的模式在四个 triage 技能里完全对称:每个技能都声明 “MUST always read report.md and append to report.md before finishing”,reproduce.md 更直言 “Downstream skills will NOT have access to the original issue — report.md is their only source of context”。report.md 实际上是一条追加式日志(append-only log),把 issue 正文、环境、复现步骤、报错栈、诊断、验证与修复结果全部沉淀在 triageDir 中。由于 /triage/ 在 .gitignore 中被忽略(第 2 行),这些中间产物不会进入版本库,每个 issue 的 triage 目录天然隔离。
8. 评测如何验证这套工作流
diagnose 技能的行为由 live-model 评测守护。.agents/skills/triage/evals/evals.json 定义了三个用例,其中两个恰好覆盖了本文讲的两类分支:
- 用例 1(完整 dry-run):合成 Bug “
getTimeStat(0, 119999)打印1m 60s”。断言要求诊断“解释秒余数取整会产生 60”并给出 medium/high 置信度、验证结论为bug、修复保证分钟格式的秒位在 0–59 且 60 秒进位、回归测试断言getTimeStat(0, 119999)返回2m 0s、changeset 为'astro': patch。值得注意的是该用例中的getTimeStat并非虚构——它真实存在于构建计时工具 packages/astro/src/core/build/util.ts,且packages/astro/test/units/build/static-build.test.ts中有对应单测,说明评测集是围绕真实源码路径构造的; - 用例 2(提前退出):只在 Cloudflare Pages 上出现的 binding 问题,断言复现阶段直接以
host-specific分类跳过、写完整报告、不执行任何诊断/修复动作——这正是 reproduce.md 的 early-exit 机制; - 用例 3(intended-behavior):
Astro.url.hash在含 fragment 的 URL 下为空,期望验证阶段给出intended-behavior高置信结论(浏览器不把 fragment 发给服务器),不进入修复。
运行方式见 .agents/evals/README.md:先 pnpm eval:skills:validate 校验所有 manifest,再如 ANTHROPIC_API_KEY=... pnpm eval:skills -t "triage" 跑单个技能用例;每个用例消耗一次被测模型加一次评判模型的运行,评测在临时工作区中进行且结束后删除。
9. 小结:diagnose 技能的设计要点
把 diagnose.md 的约束汇总成一份可执行清单:
- 报告优先:无论成败,退出前必须追加
report.md(根因、文件+行号、修复方向、置信度); - 范围锁定:只诊断,不验证、不修复、不派生子代理;未复现则追加
DIAGNOSIS SKIPPED: No reproduction并以confidence: null返回; - 双层代码库意识:
packages/是主诊断范围,堆栈指向编译器行为(HTML 解析、.astro转换)时扩展到.compiler/; - 插桩—构建—重跑—观察四步循环,利用 workspace 链接让
triage/下的复现项目跑本地源码 Astro,用pnpm -C packages/astro build使插桩生效; - 服务器纪律:先
dev stop再起新服务、失败两次即罢手、优先build、禁用&; - 现场还原:
git checkout -- <file>撤掉全部console.log,不让调试痕迹流入下游; - 结论克制:根因描述事实化;禁止建议删除用户依赖;置信度字段直接决定流水线是否继续。
这套文档与 reproduce.md、verify.md、fix.md 共同构成 Astro 仓库内一套可被 LLM Agent 逐步执行的 Bug 分诊标准作业程序(SOP):report.md 是唯一上下文载体,triageDir 是唯一工作现场,而 diagnose 负责其中最关键的一环——把“能稳定复现的异常”翻译成“带行号的根因定位”。
atomcodeClaude Code 的开源替代方案。连接任意大模型,编辑代码,运行命令,自动验证 — 全自动执行。用 Rust 构建,极致性能。 | An open-source alternative to Claude Code. Connect any LLM, edit code, run commands, and verify changes — autonomously. Built in Rust for speed. Get StartedRust0622
Hy4-previewHy4 preview 是由腾讯混元团队研发的新一代混合专家(MoE)旗舰模型。模型总参数量 770B,每个 token 激活 49B,主干共包含78层,第一层采用标准 FFN,其余 77 层均为 MoE 结构,每层包含 256 个路由专家与 1 个共享专家,每个 token 激活 top-8 路由专家及共享专家。主干之外原生内置 1 层 MTP(总参数量 10B,激活 0.7B)以支持投机解码。Python00
GLM-5.3GLM-5.3 与 GLM-5.2 使用相同的基座模型——所有提升均来自后训练。与 GLM-5.2 相比,它在复杂编程和长程任务上的表现显著提升。Jinja00
GLM-5.3-FlashGLM-5.3-Flash (320B-A18B),是GLM-5系列的首个原生多模态模型。320B总参数,能力超过GLM-5.2Jinja00
Spark-X2.5-4BSpark-X2.5-4B 旨在让强大的 AI 更实用、更高效、更易获得。在广泛日常任务中表现强劲,涵盖对话、写作、翻译、推理、编码、工具调用以及智能体工作流,并在同等规模的开源模型中取得领先成绩。Spark-X2.5 将面向效率的架构与最高 1M tokens 的原生上下文窗口相结合,并支持 200 多种语言。Python00
Spark-X2.5-1.7BSpark-X2.5-1.7B 旨在让强大的 AI 更加实用、高效且易于获取。这些模型在广泛的日常任务中表现出色,涵盖对话、写作、翻译、推理、编程、工具调用和智能体工作流,并在同等规模的开源模型中取得领先结果。Spark-X2.5 将面向效率的架构与最高 1M tokens 的原生上下文窗口相结合,并支持 200 多种语言。Python00