跨 Harness 可移植性模型
跨 Harness 可移植性模型
学习目标
读完本章后,你将能够:
- 理解”工作流层 vs 执行表面”的架构分离原则
- 识别哪些组件可以跨 Harness 共享、哪些需要适配
- 设计可移植的 SKILL.md 格式
- 评估适配层的厚度与边界
前置知识
1. 核心架构原则
1.1 工作流层 vs 执行表面
跨 Harness 架构的核心思想是将持久的行为逻辑与Harness 特定的加载和执行机制分离:
原则:共享行为属于 skills/、rules/、hooks/、scripts/ 和 mcp-configs/。适配层应该保持薄。每个 Harness 的加载和强制执行行为不同,但共享的工作流描述应该保持一致。
1.2 可移植性矩阵
| 表面 | 共享源 | Harness 适配 | 可移植性 |
|---|---|---|---|
| Skills | skills/*/SKILL.md | 各 Harness 的打包格式 | 高 — 主要是指令文本,格式一致 |
| Rules | rules/, AGENTS.md | 安装路径、文件格式 | 中 — 支持但各 Harness 格式不完全相同 |
| Hooks | hooks/hooks.json, scripts/ | 原生钩子 vs 指令驱动 | 中 — Claude/OpenCode 有原生钩子,Codex 是指令驱动 |
| MCPs | .mcp.json, mcp-configs/ | 各 Harness 的 MCP 导入 | 高 — MCP 是标准协议 |
| Commands | commands/, CLI 脚本 | 斜杠命令 vs CLI 入口点 | 中 — 命令语义各 Harness 不同 |
| Sessions | 编排脚本, 状态存储 | TUI/daemon, tmux/worktree | 低 — 仍在 Alpha 阶段 |
2. 便携单元设计
2.1 SKILL.md 作为最便携的单元
一个良好的跨 Harness 技能应该:
- 使用 YAML frontmatter 声明
name、description和origin - 描述何时使用该技能
- 声明所需工具或连接器,但不嵌入密钥
- 保持示例与仓库相对或通用
- 避免 Harness 特定的命令假设,除非该部分有明确标签
可移植技能示例结构:
---name: tdd-workflowdescription: 测试驱动开发工作流,要求 80%+ 覆盖率origin: ECC---
# Test-Driven Development Workflow
## When to Activate- Writing new features- Fixing bugs- Refactoring code
## Core Principles1. Tests BEFORE Code2. Minimum 80% coverage3. Unit + Integration + E2E tests
## Workflow Steps...(通用步骤,不绑定特定 Harness 命令)...同一个源技能可以安装到多个 Harness,因为它主要是指令、约束和工作流形状。
2.2 什么保持不变
| 组件 | 为什么可移植 |
|---|---|
| SKILL.md 文本 | 主要是自然语言指令,不依赖运行时 |
| MCP 配置 | MCP 是标准协议,各 Harness 实现相同 |
| 规则文本 | 编码风格、安全指南是通用知识 |
| Node.js 脚本 | 跨平台 Node.js 运行时保证兼容性 |
2.3 什么需要适配
| 组件 | 适配需求 | 示例 |
|---|---|---|
| Hooks | Claude 有原生钩子执行,Codex 只能指令驱动 | hooks.json → AGENTS.md 中的说明文字 |
| Commands | 各 Harness 的命令加载机制不同 | Claude 斜杠命令 → Codex 内置命令 |
| 安装路径 | 各 Harness 的插件根目录不同 | ~/.claude/plugins/ vs .agents/ |
| 会话管理 | 各 Harness 的会话生命周期不同 | Claude 的 Stop 钩子 vs Codex 的会话结束检测 |
3. 适配层模式
3.1 指令驱动回退
当目标 Harness 不支持原生钩子时,钩子行为退化为指令描述:
关键:适配层不应该复制行为逻辑。共享逻辑保持在 scripts/ 中,适配层只负责加载和触发方式。
3.2 薄适配原则
适配层应该:
- 只翻译加载机制,不翻译行为
- 保持共享行为在源位置(
skills/、hooks/) - 避免在适配层中嵌入业务逻辑
- 测试源技能与 Harness 元数据分别验证
厚的适配层反模式:
# ❌ 坏:在适配层复制逻辑# codex-adapter.js 中重写整个 TDD 工作流# ✅ 好:适配层只负责加载# codex-setup.md 中引用 skills/tdd-workflow/SKILL.md4. Operator Shell 边界
4.1 Hermes 模式
Operator Shell(如 Hermes)不是公共运行时,而是消费 ECC 资产的壳:
- 将选定的 ECC 技能导入 Operator 技能目录
- 使用 ECC 的 MCP 约定访问工具
- 通过可复用的 ECC 模式路由聊天、CLI、定时任务和交接工作流
- 将重复的本地 Operator 工作提炼为已净化的 ECC 技能
4.2 应该交付什么
| 交付 | 不交付 |
|---|---|
| 净化的设置文档 | OAuth Token 或 API Key |
| 仓库相对演示提示 | 原始的本地 Operator 记忆 |
| 通用 Operator 技能 | 个人工作空间记忆 |
| 不依赖私有凭证的示例 | 私有数据集 |
| 未经审查的本地自动化包 |
5. 陷阱与对策
5.1 Harness 特定假设导致不可移植
问题:技能中使用了 Claude 特定的命令(如 /compact),在 Codex 中无效。
对策:将 Harness 特定内容放在有明确标签的独立区块中,主流程使用通用描述。
5.2 适配层过厚
问题:为每个 Harness 维护一份行为逻辑的副本,导致不一致。
对策:适配层只翻译加载机制。行为逻辑始终引用共享源文件。定期运行适配一致性检查。
5.3 钩子语义差异
问题:Claude 的 PreToolUse 钩子可以阻塞工具调用,但 Codex 没有等效的阻塞机制。
对策:
- 在支持原生钩子的 Harness 中使用阻塞钩子
- 在不支持的 Harness 中使用指令描述 + 事后检查
- 明确记录哪些钩子有回退、哪些没有
6. 设计取舍
为什么不做 Harness 无关的运行时?
构建一个统一的、Harness 无关的运行时(类似一个”超级插件”在所有 Harness 上运行)看似理想,但:
- 各 Harness 的插件 API 差异太大
- 维护统一运行时的成本超过收益
- 薄适配层更简单、更容易调试
为什么 SKILL.md 是最便携的单元?
因为 SKILL.md 本质上是自然语言指令,不依赖任何运行时。它只需要被加载到 Agent 的上下文中即可生效。相比之下,钩子需要原生执行支持,命令需要特定的加载机制。
共享源码 vs 分发产物
ECC 选择共享源码(skills/、rules/ 目录中的 Markdown 文件)作为分发单位,而非编译后的分发产物。这使得:
- 社区可以直接阅读和贡献
- 每个 Harness 的适配可以透明地审计
- 技能可以在不同版本间平滑演进
参考来源
- Claude Code Plugins 文档
- OpenAI Codex CLI — AGENTS.md 协议
- Model Context Protocol — 标准 MCP 协议定义