
ECC 智能体钩子系统架构解析如何为 AI 编码 Agent 装上会记忆、能拦截、可审计的行为护栏【免费下载链接】ECCThe agent harness performance optimization system. Skills, instincts, memory, security, and research-first development for Claude Code, Codex, Opencode, Cursor and beyond.项目地址: https://gitcode.com/GitHub_Trending/ev/ECCECCEverything Claude Code是一个面向 Claude Code、Codex、OpenCode、Cursor 等 AI 编码智能体的运行时性能优化与安全增强系统。它解决的根本问题是Agent 会失忆、会越权、会失控——而传统提示词工程对此无能为力。本文从源码层面拆解它的钩子拦截、记忆持久化、会话归一化与控制面设计。从一次失控会话倒推Agent 的失忆、越权与失控是怎么发生的先看一个真实场景。你在 Claude Code 里让它改一个模块它在第 40 次工具调用后触发了上下文压缩compaction之前的约束约定被压缩成一段摘要——然后它忘了禁止动 linter 配置这条规则顺手把 ESLint 关掉了。又或者它在 tmux 里跑起一个npm run dev长驻进程把终端日志淹没你还不知道它干了什么。这三个缺陷分别对应三个机制层面的根因失忆会话上下文是易失的压缩与重启都会丢状态且没有跨会话的持久层。越权Agent 的工具调用只受模型自觉约束没有代码级的强制拦截点。失控一次长任务的中间过程不可观测出了事也无法回放审计。ECC 的回应不是写更多提示词而是把整条事件管线做成可编程的在工具执行前、执行后、会话开始、压缩前、会话结束这些时点插入 JavaScript 钩子用退出码当裁判用 JSON schema 当契约。它的本质是把希望 Agent 自觉转化为用代码强制。钩子拦截图的第一性原理PreToolUse 的阻断语义与退出码即裁判机制Claude Code 原生提供了一套生命周期事件PreToolUse、PostToolUse、Stop、SessionStart、SessionEnd、PreCompact。ECC 做的事情是把每个事件点都变成一道可编程的闸门。{ PreToolUse: [ { matcher: Bash|Write|Edit|MultiEdit, hooks: [ { id: pre:config-protection, command: node scripts/hooks/config-protection.js standard,strict, timeout: 5 } ] } ] }这段配置声明了一条规则任何写操作执行前先跑config-protection脚本。脚本发现目标是 ESLint、Prettier 等配置文件时直接拒绝执行——它拦截的不是写文件这个动作而是削弱代码质量约束这个意图。与之并列的还有gateguard-fact-force每个文件第一次编辑必须先调查 import、数据结构再放行、mcp-health-checkMCP 服务不健康就阻断调用。整套钩子图的裁判语义只有两条却足够表达复杂的策略退出码含义配套输出典型用途0放行stderr 输出 → 仅警告tmux 提醒、compaction 建议2阻断stderr 输出 → 拦截并说明原因配置保护、GateGuard、MCP 健康检查其他异常记录日志钩子自身崩溃不误伤主流程为什么只依赖退出码因为这是钩子宿主协议里唯一无歧义、跨语言、零依赖的裁判信号。脚本可以用 Node、Python 甚至 Shell 编写只要遵守0 放行、2 阻断的约定就能无缝接入。PostToolUse 侧则由一个同步调度器posttooluse-dispatcher.js把多个后置钩子合并进单个进程执行同时保留每个钩子独立的超时与开关控制避免 30 个脚本各自 fork 进程带来的启动开销。用户请求 → Agent 选工具 → PreToolUse 闸门 → 工具执行 → PostToolUse 汇总 │ │ │ │ │ exit 2 阻断 exit 0 记录/格式化 │ │ │ │ └──────── 放行后进入执行 ────────────────────────┘Bootstrap 解析机制一段内联代码如何在 7 种安装形态下定位自身根目录钩子配置里有一个值得单独拆解的细节几乎每个钩子命令都以一段node -e ...内联脚本开头而不是直接写脚本路径。为什么因为 ECC 可以以插件eccecc、marketplace、npm 包、手动安装等至少 7 种形态落盘钩子宿主Claude Code执行钩子时的工作目录并不等于插件根目录。如果写死相对路径换个安装方式就全盘失效。于是它内联了一个小型解析器resolveEccRoot()按顺序探测读取CLAUDE_PLUGIN_ROOT环境变量命中即返回。依次尝试~/.claude及其plugins/ecc、plugins/marketplaces/ecc、plugins/everything-claude-code等 5 个候选路径。遍历plugins/cache下按名称分组的缓存目录逐层寻找可解析根。全部失败则回退到~/.claude本身。这段自举逻辑的巧妙之处在于解析器与被解析的钩子运行在同一个进程里先定位根目录再把真正要执行的脚本注入process.argv拼接执行。用户装完插件后无需手工改任何绝对路径钩子图是可搬运的。配合ECC_HOOK_PROFILEstandard|strict、ECC_DISABLED_HOOKS这类环境变量开关运维者可以在不触碰hooks.json的前提下按需启停单个钩子——策略与实现彻底解耦。记忆持久化的三段式生命周期SessionStart、PreCompact、SessionEnd 如何对抗上下文压缩失忆问题的解法是把会话状态外置到文件系统。ECC 的记忆钩子组定义了三个关键时点SessionStart加载有界的历史上下文默认上限 8000 字符可用ECC_SESSION_START_MAX_CHARS调低并探测项目包管理器与工作区状态。PreCompact在上下文压缩发生之前把当前会话状态持久化——压缩会丢文件不会。SessionEnd转录元数据可用时写入会话结束摘要。会话开始 ──► SessionStart ──► 工作循环 ──► PreCompact ──► 压缩 ──► 恢复 │ │ │ │ │ │ 载入历史 工具调用 │ 落盘状态快照 │ │ │ └──► observe-runner 记录意图/结果 │ │ └──► suggest-compact约 50 次调用提醒 └─────────────┴──── SessionEnd 写终结摘要 ──┘中间还有一个常驻的观察者observe-runner在每次工具调用前后分别记录意图与结果喂给 continuous-learning 信号流。这些信号并不直接注入上下文那会撑爆 token而是沉淀为后续模式提取与记忆入库的原料。记忆是写时持久、读时有界的——写入廉价读取受控这是它区别于把整个历史塞回上下文的朴素做法的关键取舍。记忆契约 ecc.memory.v1schema 约束如何把不可信记忆变成可控数据记忆要跨会话、跨 harness 复用就必须有统一形状。schemas/memory.schema.json定义了ecc.memory.v1每份记忆文档必须声明kindcontext、decision、fact、handoff、lesson、note、preference、runbook 八选一、scopeproject / team / user、trust、status与sourceHarness、targetHarnesses。{ schema: ecc.memory.v1, kind: lesson, scope: project, trust: unreviewed, status: active, sourceHarness: claude, targetHarnesses: [claude, codex], tags: [migration, schema], body: … }schema 里有两个设计信号值得注意。其一trust的枚举值只有unreviewed一个——schema 注释写得直白被回忆的记忆只是上下文不是可执行指令受治理的真相会被提升为项目规范文档离开记忆库。这是对记忆污染风险的显式防御记忆默认低可信只有人工/流程确认后才升级为正式规范。其二status支持rejected与superseded让坏记忆可以被标注作废而不是物理删除——保证审计链完整。记忆系统通过memory.js、memory-mcp.mjs等脚本提供查询与写入入口整体遵循回忆即上下文、约束即数据的范式。会话适配层 ecc.session.v1异构 harness 的状态归一化机制一个更宏大的问题是Claude Code 的会话历史、tmux 编排的工作树会话、未来的 Codex/OpenCode 后端格式各不相同。ECC 用一个会话适配器契约docs/SESSION-ADAPTER-CONTRACT.md把一切归一为ecc.session.v1快照{ schemaVersion: ecc.session.v1, adapterId: dmux-tmux, session: { id: workflow-visual-proof, kind: orchestrated, state: active }, workers: [ { id: seed-check, state: running, health: healthy, branch: feature/seed-check, worktree: /tmp/worktree, runtime: { kind: tmux-pane, command: codex, pid: 1234 } } ], aggregates: { workerCount: 1, states: { running: 1 } } }每个源都通过一个 adapter如dmux-tmux、claude-history转换成这个形状。收益是双向的上层session-inspect、loop-status、未来的 HUD只依赖契约不关心底层是 tmux 还是原生历史文件底层接入新 harness 时只需新增一个 adapter无需改任何消费方。契约即边界——这正是它能在 7 类 harness 上保持可移植的原因。分档安装与 Rust 控制面性能优化系统如何向操作系统演进ECC 的仓库形态暴露了它的演进路线manifests/install-profiles.json把 26 个模块rules-core、hooks-runtime、framework-language、security、ito-compute……组合成 6 个安装档位档位是否含 hooks-runtime定位minimal否低上下文占用的纯规则/命令集opencode否OpenCode 专用基线钩子按需选装core是最小可运行基线developer是工程场景默认档security是安全加固档full是全模块覆盖机器学习到供应链域钩子运行时是否默认开启这一维度的刻意区分反映了一个权衡钩子是能力也是成本——它们占启动时间、占 token、可能误伤流程所以默认档位必须按上下文预算与防护强度双轴设计而不是一刀切全装。向上延伸ecc2/目录用 Rust 重写了控制面原型SQLite 会话存储、cargo run -- dashboard终端仪表盘、后台守护进程以及一个有意思的harness-eval机制——候选配置用 SHA-256 寻址、不可变引用证据晋升需要满足min-samples 2、min-mean-delta 0.05、min-win-rate 0.5三重复合门限且整个评估只读、不联网、不碰运行中会话。这套以证据换晋升、以回滚保安全的流程把对 Agent 行为的治理从事后打补丁推进到上线前可证明。设计哲学约束即自由契约即边界回看 ECC 的整体架构能提炼出三条贯穿始终的设计主张约束即自由退出码、schema、模块清单这些限制恰恰让策略可组合、可替换、可审计——自由不是无限提示词而是被约束托底的探索空间。记忆要分层易失的上下文、低可信的记忆库、受治理的规范文档三者严格区分宁可丢细节也不让坏数据污染决策。契约先于实现ecc.memory.v1、ecc.session.v1、钩子退出码协议都是先定契约再写实现这让 7 类 harness 与未来控制面可以并行演进而不互相绑架。对一个 Agent 系统来说最危险的从来不是模型不够聪明而是它聪明得不可控。ECC 用一层薄而硬的工程约束把失控的概率压到可接受区间——这种用确定性兜底不确定性的思路值得每一个把 Agent 接进生产流程的团队借鉴。【免费下载链接】ECCThe agent harness performance optimization system. Skills, instincts, memory, security, and research-first development for Claude Code, Codex, Opencode, Cursor and beyond.项目地址: https://gitcode.com/GitHub_Trending/ev/ECC创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考