会话交接的艺术:为 learn-harness-engineering 构建“每次会话结束都留下干净状态“的 Harness 纪律
2026/9/23 1:33:37 网站建设 项目流程

【免费下载链接】learn-harness-engineering

Harness engineering beginner tutorial, from 0 to 1

项目地址:https://gitcode.com/gh_mirrors/le/learn-harness-engineering
点击查看免费下载

导读:本篇文章聚焦 learn-harness-engineering 课程中第十二讲《为什么每次会话都必须留下干净状态》的核心技术主题。你将学会如何把"清洁状态(clean state)"从一句口号变成可执行、可检查、可度量的 Harness 机制——包括五维退出检查清单、双模式清理策略、质量文档、幂等清理脚本,以及由 benchmark-runner.ts 与 cleanup-scanner.ts 构成的可运行参考实现,并结合 Project 06 完整工作环境 中的真实配置逐项落地。

问题:一个不干净的会话交接,会吃掉下一个会话的前 30 分钟

Agent 跑了一整个下午,改了 20 个文件,提交了代码,然后会话结束。下一个会话启动时立刻发现:构建坏了、测试红了、临时调试文件散落各处、功能清单和进度记录没有更新、进度完全不可见。新会话的前 30 分钟全部耗在"搞清楚上一个会话到底做了什么"上。

OpenAI 与 Anthropic 都明确表达了同一结论:长期可靠性取决于操作纪律,而不是单次运行的成功。每次会话结束时的状态质量,直接决定下一次会话的效率。这一讲要回答的问题就是:如何让每个会话在退出时留下干净状态,让下一个会话可以立刻开始干活。

为什么熵增是默认状态

Lehman 的软件演化定律指出:一个持续变更的系统,如果没有人主动管理,复杂性必然增加。对 AI 编码 agent 来说这尤其成立——每次会话都会引入变更,如果不在退出时清理,技术债务会指数级累积。

OpenAI 在 5 个月的 Codex 实验中观察到两个关键现象:

  1. 模式复制导致漂移:agent 会复制仓库中已有的模式,哪怕那些模式本身不一致或次优。第一个人放了一个杯子在公共区,第二个人心想"反正已经乱了"也放一个,一周后桌上堆满杯子——代码库的退化完全同构。
  2. 人工清理不可扩展:OpenAI 团队最初每周五花 20% 的工作时间手动清理 "AI slop",显然不可持续。

他们最终沉淀出三管齐下的系统性方案:

  1. 把"黄金规则"写进仓库:例如"优先使用共享工具包,不要手写 ad-hoc 辅助函数""不要瞎猜数据结构,查类型定义或使用类型安全的 SDK"。这些规则必须是具体的、机械的、可自动检查的。
  2. 建立周期性清理工作流:一组后台 Codex 任务定期扫描偏离规则的代码、更新质量评分、自动开定向重构 PR,大多数 PR 在一分钟内可审查并自动合并。
  3. 把人类品味捕获一次,持续执行:评审意见、重构 PR、用户报告的 bug,全部转化为文档更新,或直接编码进检查工具;文档不够用时,把规则提升为可自动检查的代码。

一句话总结:技术债是高息贷款,持续小额还款几乎总是好过攒成一次性爆雷。

清洁状态:远不止"代码能编译"

构建通过只是底线。完整的清洁状态由五个维度组成,缺一不可:

维度要求
构建npm run build通过,下一个会话不必先修别人的构建错误
测试所有测试通过,包括会话开始前就存在的旧测试;验证必须发生在 CI,不是"在我机器上能过"
进度以机器可读工件记录三类信息:已完成的子任务及通过标准、进行中但未完成的子任务及当前卡点、尚未开始的子任务
工件调试日志、临时文件、注释掉的代码、TODO 标记全部清理干净
启动标准启动路径可用:环境初始化、代码库加载、上下文获取、任务选择,任何一条断了,新会话都无法自行启动

原文档给出了两条互为镜像的流程图,值得原样保留作为会话内审阅的检查标准:

干净交接的检查流(功能工作完成 → 构建通过?→ 测试通过?→ 更新功能清单与进度 → 清理临时工件/调试代码 → 标准启动路径可用?→ 干净交接;任一环节失败则"先修好再退出"并回到构建):

正反两种会话结局的反馈回路(脏退出 → 下个会话先诊断 → 在乱仓库上继续改 → 更乱 → 更脏;干净退出 → 下个会话直接开写 → 无需救火 → 更稳定):

在五个维度里,进度记录与临时工件这两条最容易偷懒,也最伤下一个会话:好的进度记录可以减少 60%~80% 的会话启动诊断时间;而一堆console.log('debug')// 临时方案,回头改会显著增加下一个会话的认知负担。

六个核心概念

  • 清洁状态(Clean state):会话退出时必须同时满足五条件——构建通过、测试通过、进度已记录、无过时工件、启动路径可用。缺一个都不算"做完"。
  • 会话完整性(Session integrity):类比数据库事务——要么全部完成并留下清洁状态,要么回滚到上一个一致状态,不存在"做了一半但还行"的中间地带。
  • 质量文档(Quality document):持续记录每个模块质量评分的活动工件,是追踪代码库在变强还是变弱的仪表盘,而非一次性评估。
  • 清理循环(Cleanup loop):定期执行的维护会话,系统性降低代码库熵。属于常规保养(像定期换机油),不属于紧急修复。
  • Harness 简化(Harness simplification):随着模型能力提升,定期移除不再必要的组件——今天必须的约束,三个月后可能只是开销。
  • 幂等清理(Idempotent cleanup):清理操作无论执行多少次结果都一样,保证失败重试时依然安全。

"以后再清理"等于永远不清理

最常见的心理陷阱是"这次来不及了,下次再弄"。但下次的 agent 不知道你留下了什么,它必须花大量时间推断"哪些代码是有意的、哪些是临时的"。更糟的是,新会话有自己的任务目标,它不会清理旧账,而是直接在混乱之上开始新工作,再引入更多混乱——这是熵增的正反馈循环。

原文档给出的 12 周实测对比(使用 agent 持续开发的项目,两组配置完全相同,唯一变量是有无清理策略):

无清洁策略

时间构建通过率测试通过率新会话启动时间
第 1 周100%100%5 分钟
第 4 周95%92%15 分钟
第 8 周82%78%35 分钟
第 12 周68%61%60+ 分钟

有清洁策略

时间构建通过率测试通过率新会话启动时间
第 1 周100%100%5 分钟
第 12 周97%95%9 分钟

12 周后两组构建通过率相差 29 个百分点,测试通过率相差 34 个百分点,新会话启动时间相差 85%。这些数字来自原文档所述的实际观察,并非理论推演。

怎么做:六步落地清洁状态

1. 把清洁状态写进"完成"的定义

在 Harness 里明确定义:会话完成 = 任务通过验证 AND 清洁状态检查通过。在 CLAUDE.md 或 AGENTS.md 中写下退出检查清单:

## Session Exit Checklist - [ ] Build passes (npm run build) - [ ] All tests pass (npm test) - [ ] Feature list updated - [ ] No debug code remaining (console.log, debugger, TODO) - [ ] Standard startup path available (npm run dev)

仓库给出了一个远超五行的生产级范例:projects/project-06/solution/clean-state-checklist.md 把检查项展开为八个板块——Build、Architecture、Runtime、Logging、Data Integrity、Performance、Repository、Scripts——总计约 40 个可勾选项,例如:

  • Build 板块要求npm run check无类型错误、npm run build成功、无未使用变量/导入告警;
  • Architecture 板块要求 renderer 代码(src/renderer/)不得导入fs/path、服务代码不得出现 Electron IPC、所有 IPC channel 定义在src/shared/types.ts
  • Repository 板块要求feature_list.json反映真实功能状态、session-handoff.md在会话结束时更新、claude-progress.md记录当前状态;
  • Scripts 板块要求cleanup-scanner.sh报告无过时工件、benchmark.sh跑完全部任务套件、init.sh通过全部验证。

为什么必须有功能清单(feature list):功能清单是一份机器可读文件,记录每个功能项的三列信息——这个功能做什么、用什么命令验证、当前状态(未开始/进行中/已阻塞/已通过)。调度器靠它选下一个工作,验证器靠它判断做完没有,交接器靠它生成进度报告。没有清单,agent 会用自己那套(几乎必然更低)的标准判断"完成"。仓库中的真实样例见 projects/project-06/solution/feature_list.json:每个条目包含idnamedescriptionstatuspass/in-progress/blocked/not-started)、evidencetestedAt时间戳,例如window-launch的 evidence 写明"main.ts 创建 1200x800 BrowserWindow,contextIsolation=true、nodeIntegration=false",让下个会话无需重新考古。

2. 双模式清理策略

把清理拆成两种模式配合使用:

  • 即时清理(每个会话结束时):清掉本次会话创建的临时文件、更新功能清单状态、确保构建和测试全绿。原则是"用完就清",像引用计数一样,谁产生的垃圾谁负责。
  • 定期清理(每周一次):全面系统扫描,处理累积的结构性问题、更新质量文档、跑基准测试检测漂移。原则是定期全身体检,不让小问题拖成大病。

原文档的 cleanup-loop.md 给出了清理循环的任务清单:扫描过时文档、扫描结构违规、更新质量评分、开定向清理 PR、清理后重跑固定基准切片。这五步正是"定期清理"的落地骨架。

3. 维护质量文档

质量文档是持续更新的评分文件,新会话一打开就知道每个模块的健康状况,并优先处理评分最低的模块。模板:

# Quality Document ## User Authentication Module (Quality: A) - Verification passing: Yes - Agent understandable: Yes - Test stability: Stable - Architecture boundaries: Compliant - Code conventions: Followed ## Payment Module (Quality: C) - Verification passing: Partial (payment callback untested) - Agent understandable: Difficult (logic spread across 3 files) - Test stability: Unstable (2 flaky tests) - Architecture boundaries: Violations present - Code conventions: Partially followed

质量文档本质上是 Harness 可观测性的一部分——它把 agent 的运行结果在代码库层面变得可见(对应第十一讲的主题)。

4. 定期简化 Harness

Harness 里每个组件的存在都源于模型在某个方面还无法独立完成;模型能力演进后,这些前提会过时。Anthropic 的实验是直接证据:最初为了 Sonnet 4.5 而引入的 sprint 拆分机制,在 Opus 4.6 能自主做工作分解后就成了多余开销,移除后 builder agent 反而能连续工作两小时以上不跑偏。但 evaluator 是反例——即便 Opus 4.6 能力更强,当任务逼近模型能力边界时,evaluator 仍能抓住缺失功能和 stub 实现。结论是:要不要保留某组件,取决于任务难度与模型能力的相对位置,而不是一刀切

推荐做法:每月挑一个 Harness 组件,暂时禁用,跑基准任务。结果不退化就永久移除;退化就恢复或换更轻量的替代。更深层的原则是:模型变强,Harness 中有趣的组合没有减少,而是在位移——旧问题被模型吸收,新能力边界又打开新的设计空间。

5. 清理操作必须幂等

清理失败时你会重跑一遍,因此清理脚本必须幂等:执行一次与执行一百次结果相同。

# Idempotent cleanup operations rm -f /tmp/debug-*.log # -f ensures no error when files don't exist git checkout -- .env.local # Restore to known state npm run test # Verify cleanup didn't break anything

6. 高吞吐量下调整合并哲学

当 agent 每天开出 3.5 个甚至更多 PR 时,减少阻塞型合并门禁是正确选择:PR 应当短命,测试 flake 用后续运行修正,而不是无限期卡住进度。判断标准是修 bug 的平均成本 vs 等待人工审查的平均成本——当前者低于后者时,快合并 + 快修复优于慢确认。注意前提:这条规则只在高产出环境成立,低吞吐环境里快速合并没有意义。

参考实现一:benchmark-runner.ts —— 用固定基准切片检测漂移

原文档配套代码 benchmark-runner.ts 提供了一个可直接运行的基准执行器,用于支撑"清理后重跑固定基准切片"与练习 2 的对照实验。它模拟执行一组基准任务并输出对比报告,核心结构如下:

  • 任务定义BenchmarkTask接口包含idnamecategorypassCriteria(通过标准数组)、expectedDurationMs(期望耗时)与模拟的actualDurationMs/actualPass。内置 8 条任务,覆盖四类场景:文档导入管道(markdown/PDF 导入)、Q&A 管道(含"无相关文档时的优雅降级")、安全(并发用户隔离、API 限流)与可靠性(重启后会话连续性)。
  • 执行模拟executeBenchmark()把每条任务映射为BenchmarkResult,统计criteriaPassed/criteriaTotal、计算durationDelta(实际-期望耗时差)。
  • 报告输出:表格列出每条任务的 ID/名称/类别/通过与否/标准达成数/期望与真实耗时/偏差;对失败任务单独输出failureReason(例如 "PDF text extraction failed on page 3 -- encoding issue"、"Model hallucinated a citation instead of saying 'no results'"、跨用户数据泄露 + 超 SLA);最后按类别聚合通过率并给出总体通过率。

运行方式(仓库内相对路径):

npx tsx docs/en/lectures/lecture-12-why-every-session-must-leave-a-clean-state/code/benchmark-runner.ts

这套切片的用法契合第十二讲的核心实践:把关键路径编码为带通过标准与期望耗时的固定任务集,每次清理循环后重跑,任何actualPass: false或耗时漂移都是"代码库在退化"的早期信号。配套的 benchmark-comparison-template.md 给出了两种 Harness 配置的对照模板(completion rate、average retries、bugs caught before human review),并提示回答两个问题:哪个 Harness 改变了结果?哪个 Harness 改变了获得结果的成本?

参考实现二:cleanup-scanner.ts —— 过期工件与违规扫描器

配套的 cleanup-scanner.ts 是"即时清理"与"定期清理"共用的扫描器,用 Node 原生fs/path实现,无需第三方依赖。它把扫描项组织为八类检查,每项带严重级别(critical/warning/info)与描述:

类别检查项严重级
Stale Artifacts*.tmp/*.bak/*.swp/~临时文件warning
Stale Artifacts源码目录(src/lib/app/)中的.log调试文件warning
Dead Codesrc/lib/app.ts/.tsx/.js文件前 50 行内的TODO:/FIXME:/HACK:/XXX:标记info
Structural Violations缺少.gitignorewarning
Structural Violationssrc/distsrc/build等编译产物混入源码critical
Structural Violationssrc/lib/app/test下的嵌套node_modulescritical
Session CleanlinessWIP.md/IN_PROGRESS.md/scratch.ts/debug.ts/temp.ts等未完成会话痕迹info
Session Cleanliness空的src/lib/app/test/docs目录info
Configuration源码中的.env/.env.local/.env.production/.env.staging(可能含密钥)critical

扫描器实现要点:findFiles()递归遍历时限制深度(depth > 4即停止)并跳过node_modules/.git/dist/build/.next/coveragescanForPatterns()只读 TS/TSX/JS 文件且只看前 50 行,避免把"正文里提到 TODO"误报。最终输出表格报告与汇总(Critical/Warnings/Info/Clean 计数),若存在 critical 问题会打印 ACTION REQUIRED;全部干净则输出 "Project is in a clean state."。

# 默认扫描当前目录 npx tsx docs/en/lectures/lecture-12-why-every-session-must-leave-a-clean-state/code/cleanup-scanner.ts # 或指定目录 npx tsx docs/en/lectures/lecture-12-why-every-session-must-leave-a-clean-state/code/cleanup-scanner.ts /path/to/project

注意:这是仓库为第十二讲提供的教学参考实现(模拟执行 + 启发式扫描),实际项目落地时建议把同样逻辑写入 CI 或项目脚本。

生产级对照:Project 06 的完整 Harness 面

第十二讲不是孤立的理论——它直接支撑了 Project 06(搭建一套完整的 agent 工作环境,Capstone 项目)。该项目的 solution 目录把本讲所有概念都变成了真实文件:

  • clean-state-checklist.md:上文已拆解的生产级退出检查清单;
  • feature_list.json:机器可读功能清单,每个功能带 status/evidence/testedAt;
  • cleanup-scanner.sh:面向应用数据目录的真实一致性扫描器,检查五类问题——孤儿内容文件(有 content 无 metadata)、悬空 chunk 文件(有 chunks 无 index 条目)、缺失内容文件(有 metadata 无 content)、不一致元数据(status=indexed 但无 chunk 文件)、过期 Q&A 引用(历史记录引用已删除文档)。脚本输出 CLEAN 或 ISSUES FOUND,后者给出推荐动作(用应用内 Reset 清空数据 → 从data/sample-documents/重新导入 → 重跑扫描验证);
  • 另有 benchmark.sh、check-architecture.sh 支撑构建/架构/性能维度,以及 session-handoff.md 承载交接报告。

与之对照,starter 目录刻意只保留基础 AGENTS.md,没有feature_list.json、没有session-handoff.md、没有清洁状态清单、也没有基准脚本——这正是第十二讲要展示的"弱 Harness"基线。你可以用两个脚本bash scripts/benchmark.shbash scripts/cleanup-scanner.sh跑出证据,对比质量文档里的评分差异。

核心要点

  • 清洁状态是会话完成的必要条件——它是"完成"定义的一部分,不是可选的额外家务。
  • 五个维度缺一不可:构建、测试、进度、工件、启动,每一条都要在退出时显式检查,不能靠"感觉应该没问题"。
  • 功能清单让 agent 知道"做完"的标准——没有清单,agent 用自己的标准判断完成,那个标准几乎一定更低。
  • 质量文档让代码库健康可追踪——知道哪里在退化才能主动修复;不知道问题在哪,就只能等它爆发。
  • 定期简化 Harness:每月挑一个组件禁用后跑基准,结果不退化就永久移除。
  • "以后再清理"等于永远不清理:熵增是默认方向,只有主动清理能对抗它。每次多花五分钟,是长期回报最高的投资。

练习建议

  1. 设计清洁状态检查表:为你的代码库设计涵盖五个维度的会话退出检查表,连续 5 个会话坚持执行,记录每个维度的违反次数。
  2. 基准对比实验:用固定任务集分别跑"要求清洁状态"与"不要求"两种 Harness,比较完成率、重试次数与漏网 bug 数(可直接复用 benchmark-runner.ts 和 benchmark-comparison-template.md)。
  3. Harness 简化实践:禁用某个 Harness 组件跑基准,决定保留、移除还是替换。
  4. 质量文档入门:为 3~5 个核心模块打分(A/B/C/D)并标注扣分原因,连续 4 周每周更新,观察质量走势。

【免费下载链接】learn-harness-engineering

Harness engineering beginner tutorial, from 0 to 1

项目地址:https://gitcode.com/gh_mirrors/le/learn-harness-engineering
点击查看免费下载

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询