Harmonist六大Hook阶段深度解析:从sessionStart到stop的全生命周期管控
【免费下载链接】harmonistPortable AI agent orchestration with mechanical protocol enforcement. 186 agents, zero runtime dependencies.项目地址: https://gitcode.com/gh_mirrors/ha/harmonist
Harmonist 是一款零运行时依赖的 AI 代理编排工具,内置 186 个代理,其核心亮点是用 IDE 级Hook 钩子把 AGENTS.md 里"必须执行"的协议从口头约定变成机械门禁。本文带你深度解析 Harmonist 的六大 Hook 阶段——sessionStart、afterFileEdit、subagentStart、subagentStop、beforeShellExecution 和 stop,看懂它们如何从会话开始到结束实现全生命周期管控,确保 AI 跳过评审、忘记更新记忆时根本无法"草率收工"。
一、为什么需要机械式协议执行?
传统 AI 编程助手有个结构性难题:你可以告诉它"改完代码必须跑 QA""每个任务结束必须更新交接文档",但它可能忘记、跳过、甚至幻觉式地声称"已评审"。
Harmonist 的解法非常直接——把协议变成磁盘上的状态机。Cursor 暴露了 hook 系统,Harmonist 将真实脚本挂到代理生命周期事件上,观察 AI 的每一步操作,在stop事件时如果发现协议违规,就返回followup_message让 Cursor 重新打开代理轮次,直到补齐缺失的步骤。
没有这些 Hook,协议只是建议;有了它们,代理在评审跑完、记忆更新之前无法结束任何改代码的回答。
完整配置注册在 hooks/hooks.json,六个阶段全部通过跨平台 Python 入口 hooks/scripts/hook_runner.py 分发,POSIX 参考实现保留在 hooks/scripts/ 目录中。
二、六大Hook阶段总览
| 阶段 | 触发时机 | 核心职责 |
|---|---|---|
sessionStart | 会话开始 | 初始化 correlation_id,注入最近记忆条目,预警历史事故 |
afterFileEdit | 每次文件写入 | 记录所有写入到会话状态,供 stop 门禁核查 |
subagentStart | 派发子代理 | 解析AGENT: <slug>标记、并发上限、委派上下文检查 |
subagentStop | 子代理结束 | 关闭调用记录,为评审者"记分" |
beforeShellExecution | 执行 shell 命令前 | HITL 人工确认门禁,拦截危险命令 |
stop | 代理试图结束时 | 最终门禁:核查评审、记忆更新,不达标就拒绝放行 |
每个阶段对应 hooks/hooks.json 中的一条命令,例如stop阶段额外配置了"loop_limit": 3,限制重试次数。
三、sessionStart:给会话"上户口"
会话一开始,sessionStart阶段会做四件关键的事(实现见 hook_runner.py):
- 重置状态并生成 correlation_id:格式为
<unix时间戳><pid后4位>,天然防冲突。这个 ID 关联本次任务的所有记忆条目,LLM 只能读取、无法伪造。 - 注入项目上下文:注入 AGENTS.md 的不变量摘要、最近 3 条
session-handoff.md状态条目和 3 条decisions.md决策记录,让代理"带着记忆上岗"。 - 预警历史事故:如果上次会话有"协议耗尽"(PROTOCOL-EXHAUSTED)事故,会弹出醒目横幅,提醒先调查再动代码。
- 滥用审计与陈旧提醒:若
PROTOCOL-SKIP跳过率超过阈值(默认 >25% 且至少 5 次),会显示滥用警告;仓库地图陈旧时也会提示刷新。
四、afterFileEdit:每一次写入都留痕
代理每写一个文件,afterFileEdit阶段就把它记入.cursor/hooks/.state/session.json。
它有几个精巧的判定逻辑:
- 记忆文件单独追踪:写入
session-handoff.md、decisions.md、patterns.md等记忆文件时,记入memory_updates而非普通writes,直接放行。 - 跳过构建产物:
node_modules/、dist/、.git/等路径不计入门禁。 - 只读代理写文件即违规:如果当前有标记为
readonly的子代理在运行(比如只读评审者),任何文件写入都会被记录为readonly_violation,成为 stop 门禁的扣分项。
这就是 stop 门禁能"算账"的数据来源——没有这里的留痕,后面一切核查都是空谈。
五、subagentStart / subagentStop:子代理的"门禁卡"
Cursor 不暴露子代理的"身份",只有类型(generalPurpose、shell等)。Harmonist 因此定义了一个小型契约:
每个子代理 prompt 的第一行必须是
AGENT: <slug>,slug 对应 agents/ 目录中的文件名。例如派发 QA 评审时第一行写AGENT: qa-verifier。
subagentStart阶段(hook_runner.py)据此执行三道防线:
- 并发上限:默认最多 3 个子代理并行(
max_concurrent_subagents),超出直接返回{"permission": "deny"},防止无限制扇出耗尽内存。900 秒内没有 stop 事件的僵尸调用会被自动忽略,不会永久锁死新派发。 - 委派上下文门禁(可选开启):如果去掉
AGENT:标记后 prompt 不足 80 字符,判定为"薄委派"并拒绝——因为子代理看不到主会话对话,只凭一句"验证一下代码"必然返工。 - 能力范围检查:解析该代理 frontmatter 中的
readonly标记,为 afterFileEdit 的违规判定做准备。
subagentStop阶段按 LIFO 顺序关闭最近的调用记录;如果 slug 在评审者名单(reviewer_slugs:qa-verifier、security-reviewer、code-quality-auditor 等 6 个)中,就把这个"信用"记入reviewers_seen。
六、beforeShellExecution:危险命令的人工确认(HITL)
这是唯一带"人在回路"的阶段。每条 shell 命令执行前都会与一份危险命令模式库比对,命中即暂停等待人类确认(默认ask,可配置为deny):
- 递归强删根目录 / 家目录 / 通配符(
rm -rf /、rm -rf "$HOME") git push --force、git reset --harddd写入裸设备、mkfs格式化- fork 炸弹、
curl | sh远程执行 DROP TABLE/TRUNCATE DATABASE等破坏性 SQL
它刻意"调校到只拦灾难级操作"——像rm -rf node_modules这种日常命令不会被暂停,避免门禁变成噪音。
七、stop:最终的"放行"大门
stop是整套系统的核心(hook_runner.py)。当代理试图结束回答时,门禁会逐项核算:
直接放行的情况:
- 本任务没有任何代码写入(纯问答)
- 最终消息包含
PROTOCOL-SKIP: <原因>(仅限真正琐碎的改动,会被记录审计) - 只改了文档、配置等"琐碎路径"(轻量模式,默认开启)
必须全部满足才能放行:
- ✅ 至少调用过一个评审者(
require_any_reviewer) - ✅ 必须调用过
qa-verifier(require_qa_verifier) - ✅
session-handoff.md中存在与当前 correlation_id 匹配的交接条目(采用行锚定匹配,防止-1误匹配-10) - ✅ 无只读代理写入违规
- ✅ 可选:回归测试真实通过(
require_regression_passed)/ 受影响的测试已被运行(require_affected_tests,基于仓库地图计算爆炸半径) - ✅ 记忆文件通过 schema 校验(超时视为未通过,失败关闭)
不满足时怎么办?返回followup_message,精确列出缺失步骤、已修改文件、已见评审者和当前 correlation_id,并要求代理按模板补齐。重试达到loop_limit(默认 3 次)仍未满足,任务被强制关闭为 PROTOCOL-VIOLATED:事故写入incidents.json,下个会话开场就弹横幅警示。
整个设计遵循"失败关闭(fail-closed)"原则——stop 阶段内部出错时宁可拒绝放行,也不让回答在无人核查下悄悄结束。
八、动手验证:运行 Hook 测试套件
所有 Hook 都有不依赖 Cursor 的纯集成测试,通过管道注入合成 JSON 模拟各阶段调用:
bash hooks/tests/run-hook-tests.sh覆盖 8 个经典场景:纯问答、裸写入、完整协议跑通、缺少 qa-verifier、PROTOCOL-SKIP 逃逸、忽略路径、缺失 AGENT 标记、备选字段名。想深入某个阶段的行为,直接看对应的 shell 实现,如 hooks/scripts/gate-stop.sh、hooks/scripts/seed-session.sh。
九、上手须知:两个容易踩的坑
- 一个项目只开一个 Cursor 窗口:六个 Hook 共享同一状态文件,双窗口会互相重置会话状态,导致门禁判定错乱。
loop_limit存在于两个文件:hooks/hooks.json 里的是 Cursor 的重试上限,.cursor/hooks/config.json里的是门禁自身 fail-closed 的判定上限,修改时务必同步。- 需要 Python 3.9+:Python 缺失时 Hook 静默失效(门禁关闭);版本过低则会"大声降级"——
stop阶段明确告知无法验证协议。 - 定位问题看两个文件:会话状态
.cursor/hooks/.state/session.json、活动日志.cursor/hooks/.state/activity.log(自动在 1 MiB 处轮换)。评审跑了却没记分?十有八九是子代理 prompt 少了AGENT: <slug>第一行。
十、总结:从提示词到状态机的跨越
Harmonist 六大 Hook 阶段构成一条完整的管控链:sessionStart 立规矩 → afterFileEdit 记流水 → subagentStart/Stop 管身份与并发 → beforeShellExecution 拦危险 → stop 收总账。
它不引入运行时、不建数据库、不锁定供应商,只是 markdown + 标准库 Python + bash 组成的文件级状态机,就实现了"AI 无法绕过评审交付代码改动"这一企业级治理能力。对个人开发者而言,它把"提醒 AI 遵守协议"升级成了"AI 客观上无法跳过协议"——这正是机械式执行的精髓。更多细节可参阅 hooks/README.md 与 AGENTS.template.md。
【免费下载链接】harmonistPortable AI agent orchestration with mechanical protocol enforcement. 186 agents, zero runtime dependencies.项目地址: https://gitcode.com/gh_mirrors/ha/harmonist
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考