【OpenHarness】港大开源 6 件套全栈 Harness:10 子系统 + 114 测试 + Skill 热加载的真实工程化实现
上一篇文章拆解了「5 大 Coding Agent Harness 在 6 件套上的设计哲学差异」(2026-07-06 横评),今天换一种视角:不再横评,而是把 1 个项目的 6 件套全栈打开看——港大 HKUDS/OpenHarness(14,680⭐)正好是一个”6 件套齐全 + 114 测试 + 真有人用”的完整样本。
一、为什么拆 OpenHarness?
港大数据智能实验室(HKUDS)2026-04-01 开源了 OpenHarness + 内置的 ohmo 个人 Agent。3 个月冲到 14,680⭐,README 直接写:
OpenHarness delivers core lightweight agent infrastructure: tool-use, skills, memory, and multi-agent coordination.
ohmo is a personal AI agent built on OpenHarness — not another chatbot, but an assistant that actually works for you over long sessions.
这个定位决定了它不是又一个 ChatBot 框架,而是一份**「Harness 该长什么样」的参考答案**——它把 Harness 6 件套拆成 10 个子目录(engine/ tools/ skills/ plugins/ permissions/ hooks/ commands/ mcp/ memory/ tasks/ coordinator/ prompts/ config/ ui/),每个目录都是 6 件套中的一件的真实实现。
读完这篇你能拿到什么:
- OpenHarness 的 10 子系统如何映射到 Harness 6 件套
- 3 段可运行代码:Agent Loop + Sensitive Path 黑名单 + Skill 多源加载
- 与 DeepAgents / Hermes / Claude Code 在协议契约上的差异(不是功能对比)
- OpenHarness 暴露的 4 个工程教训(黑名单比白名单更安全、Skill 路径按优先级合并、Hook timeout 默认 5s、MCP 失败不能阻塞启动)
二、项目全景:6 件套在 OpenHarness 里的 10 子系统映射
OpenHarness 源码长这样:
1 | openharness/ |
映射到 Harness 6 件套坐标系:
| Harness 6 件套 | OpenHarness 实现 | 关键文件 | 设计亮点 |
|---|---|---|---|
| Rule(软约束) | permissions/checker.py + settings.json 的 path_rules / denied_commands | SENSITIVE_PATH_PATTERNS 11 条硬黑名单 | 默认拒绝敏感路径,用户配置不能 override |
| Skill(按需 SOP) | skills/loader.py + 5 层目录发现 | _USER_COMPAT_SKILL_DIRS 兼容 Claude / Agents 生态 | 按从远到近的目录优先级合并 skill |
| Sub-Agent(角色) | coordinator/coordinator_mode.py + tasks/ | WorkerConfig + XML <task-notification> 协议 | Coordinator/Worker 模式用环境变量 CLAUDE_CODE_COORDINATOR_MODE 触发 |
| Workflow(编排) | tasks/ + auto_compact | AutoCompactState + microcompact 二段式压缩 | 压缩不是一刀切,先清旧 tool result,再 LLM 总结 |
| Script(门控) | hooks/executor.py 4 种 hook 类型 | CommandHookDefinition / HttpHookDefinition / PromptHookDefinition / AgentHookDefinition | 每种 hook 自带 timeout + block_on_failure |
| MCP(外部桥接) | mcp/client.py + mcp/config.py | McpClientManager + stdio/http 双 transport | MCP 启动失败只标记 state="failed",不阻塞主流程 |
graph TB
subgraph "用户层 [天空蓝]"
U["👤 用户"]
TUI["🖥️ React TUI / CLI"]
end
subgraph "Agent 引擎 [薰衣草紫]"
QE["🧠 QueryEngine<br/>Agent Loop"]
API["🌐 API Client<br/>Anthropic/OpenAI 兼容"]
end
subgraph "6 件套内核 [马卡龙色]"
TOOLS["🔧 ToolRegistry<br/>43 Tools"]
SKILLS["📚 SkillRegistry<br/>5 层目录发现"]
HOOKS["⚡ HookExecutor<br/>4 种 hook 类型"]
PERM["🛡️ PermissionChecker<br/>11 条敏感路径黑名单"]
MCP["🌐 McpClientManager<br/>stdio/http 双通道"]
COORD["🤝 CoordinatorMode<br/>XML 任务通知"]
end
subgraph "外部世界 [蜜桃橙]"
FS["📁 File System"]
SH["💻 Shell"]
WEB["🌍 Web Fetch/Search"]
MS["🔌 MCP Servers"]
end
U --> TUI --> QE
QE --> API
QE --> TOOLS
QE --> SKILLS
QE -. Pre/Post .-> HOOKS
QE -. 每次 tool_call 前 .-> PERM
QE -. 任务委派 .-> COORD
TOOLS -. stdio/http .-> MCP
TOOLS --> FS
TOOLS --> SH
TOOLS --> WEB
MCP --> MS
style U fill:#C7CEEA,stroke:#9FA8DA,color:#333
style TUI fill:#C7CEEA,stroke:#9FA8DA,color:#333
style QE fill:#E8D5F5,stroke:#CE93D8,color:#333
style API fill:#E8D5F5,stroke:#CE93D8,color:#333
style TOOLS fill:#FFDAB9,stroke:#FFAB76,color:#333
style SKILLS fill:#FFDAB9,stroke:#FFAB76,color:#333
style HOOKS fill:#FFB3C6,stroke:#F48FB1,color:#333
style PERM fill:#FFB3C6,stroke:#F48FB1,color:#333
style MCP fill:#B5EAD7,stroke:#80CBC4,color:#333
style COORD fill:#FFF9C4,stroke:#F9A825,color:#333
style FS fill:#F5F5F5,stroke:#9E9E9E,color:#333
style SH fill:#F5F5F5,stroke:#9E9E9E,color:#333
style WEB fill:#F5F5F5,stroke:#9E9E9E,color:#333
style MS fill:#F5F5F5,stroke:#9E9E9E,color:#333三、机制 vs 策略分离:OpenHarness 的设计哲学
Harness Engineering 最核心的一条原则是「机制和策略分离」——把”做什么”和”怎么做”解耦,让模型决定”做什么”,让 Harness 决定”怎么做”。OpenHarness 把这条原则贯彻得很彻底。
3.1 Agent Loop:模型决定 “What”,Harness 决定 “How”
src/openharness/engine/query.py 的 run_query() 是核心 Agent Loop,简化后:
1 | async def run_query(context, messages): |
关键设计点:
- 并发执行 +
return_exceptions=True:多 tool_call 时,单个 tool 抛异常不能 cancel 其他协程。如果不这样,Anthropic API 会因为”有tool_use没匹配tool_result“直接拒收下一轮请求 - Auto-compact 二段式:先做廉价的 microcompact(清旧 tool result 内容),空间不够再做 LLM 摘要。两种压缩策略由同一个
AutoCompactState跟踪 - Loop 上限
max_turns:默认 8 轮,防止 agent 卡死。由 settings 配置(策略),但由 loop 强制(机制)
3.2 Permission 系统:硬黑名单(机制) vs 软规则(策略)
这是 OpenHarness 最值得拆的一段——它的 permissions/checker.py 把”敏感路径”做成代码常量,而把”普通路径规则”做成 settings 配置。两者层次分明,互不污染:
1 | # src/openharness/permissions/checker.py |
为什么这种分层重要:Harness 处理的是 LLM-driven 的 tool 调用,LLM 本身可能被 prompt injection 操控。如果把”敏感路径”做成 settings 配置,攻击者可以让 LLM 改 settings;做成代码常量则攻击面只剩”修改源码并重新部署”——这是 OpenSSH 把 root 权限分隔成不同进程的同一思路。
对比同类:
- Claude Code:也有 sensitive path 黑名单,但藏在 npm 包配置里,修改门槛低
- DeepAgents:
batteries-included但没有显式硬黑名单,权限完全靠 LangChain 的 tool error 处理 - OpenHarness:11 条硬黑名单直接写在
checker.py顶部,是最显式的实现
3.3 Skill 系统:5 层目录 + 优先级合并
src/openharness/skills/loader.py 的 _USER_COMPAT_SKILL_DIRS 定义了 OpenHarness 和 Claude/Agents 生态的兼容:
1 | _USER_COMPAT_SKILL_DIRS = ( |
加载顺序(核心设计:从远到近,后加载的覆盖前面的):
- Bundled skills:随包发布(
skills/bundled/__init__.py) - User-level skills:
~/.openharness/skills/、~/.claude/skills/、~/.agents/skills/ - Project skills:从 cwd 一直走到 git root,每层都查 3 个目录
- Plugin skills:从
plugins/加载的 skill
关键函数 discover_project_skill_dirs():
1 | def discover_project_skill_dirs(cwd, project_skill_dirs): |
为什么从远到近合并:monorepo 场景下,子目录里可能有自己的 .openharness/skills/ 想覆盖 root 的同名 skill。如果从近到远加载,root 的 skill 会”复活”,子目录覆盖失效。
安全设计:
1 | def _valid_project_skill_dirs(project_skill_dirs): |
settings 里写 /etc/skills 或 ../../etc/passwd 直接被丢弃并打 warning。这避免了”恶意仓库通过 .openharness/config.json 把 skill 加载到任意位置”。
四、3 段可运行代码:完整复刻 OpenHarness 核心机制
下面 3 段代码来自 OpenHarness 源码的可运行简化版,不需要安装任何依赖(只用标准库)。
4.0 一次完整 Agent Loop 的时序图
sequenceDiagram
actor User as 👤 用户
participant TUI as 🖥️ TUI/CLI
participant QE as 🧠 QueryEngine
participant API as 🌐 API Client
participant Perm as 🛡️ Permission
participant Hook as ⚡ Hook
participant Tool as 🔧 Tool
participant MCP as 🌐 MCP
User->>TUI: 发送 prompt
TUI->>QE: stream(user_prompt)
loop 直到 stop_reason != tool_use
QE->>QE: Auto-Compact 检查
QE->>API: stream_message(messages, tools)
API-->>QE: tool_use blocks
loop 每个 tool_call (并发)
QE->>Perm: check(tool_name, file_path, command)
Perm-->>QE: Decision (allowed/denied)
alt allowed
QE->>Hook: PRE_TOOL_USE
Hook-->>QE: AggregatedResult (blocked?)
alt not blocked
QE->>Tool: execute(input)
Tool->>MCP: call_tool(server, tool) (if MCP tool)
MCP-->>Tool: result
Tool-->>QE: ToolResult
else blocked
QE-->>QE: synthetic error result
end
QE->>Hook: POST_TOOL_USE
Hook-->>QE: AggregatedResult
else denied
QE-->>QE: permission denied error
end
end
QE->>API: stream_message(messages + tool_results)
API-->>QE: next tool_use or final
end
QE-->>TUI: AssistantTurnComplete
TUI-->>User: render response下面开始看 3 段代码。
4.1 Sensitive Path 黑名单(30 行可运行)
1 | # sensitive_paths.py |
输出:
1 | ❌ /home/user/.ssh/id_rsa → Access denied: matches '*/.ssh/*' |
关键工程点:
_policy_match_paths()同时检查路径本身 + 所有父目录,避免~/.ssh/这种”目录路径”绕过fnmatch.fnmatch()而非正则匹配,符合 Unix shell glob 习惯(用户写/etc/*立刻懂)frozen=True让 Decision 不可变——避免上游误改 decision 结果
4.2 Skill 路径优先级合并(40 行可运行)
1 | # skill_loader.py |
输出:
1 | 📂 加载顺序(远→近): |
4.3 Hook 4 类型执行器(核心 60 行)
src/openharness/hooks/executor.py 是 Hook 系统的核心。4 种 hook(Command / HTTP / Prompt / Agent)共享同一套”匹配 → 执行 → 聚合结果”流程:
1 | import asyncio |
4 种 hook 的设计哲学:
| 类型 | 用途 | 默认 timeout | 阻塞语义 |
|---|---|---|---|
| Command | 跑 lint / 安全扫描 | 5s | exit code ≠ 0 阻塞 |
| HTTP | 发审计 / Slack 通知 | 5s | 403 阻塞,其他通过 |
| Prompt | LLM 评估 tool 安全性 | 30s | “BLOCK” 关键字阻塞 |
| Agent | 调子 agent 深度推理 | 60s | 子 agent 拒绝则阻塞 |
关键设计:
- 统一返回
HookResult:4 种 hook 输出结构一致,聚合逻辑简单 block_on_failure是配置项:lint hook 你想”warning only”就关掉,安全 hook 必须开- 超时独立:HTTP hook 卡住不会拖垮 LLM 主流程
五、与其他 Harness 的协议级差异
上次横评了 5 个 Coding Agent Harness,这次换 3 个不同维度对比——OpenHarness 在「个人 Agent 平台」类项目里的位置:
5.1 对比 DeepAgents(LangChain 官方)
| 维度 | OpenHarness | DeepAgents |
|---|---|---|
| 架构定位 | Standalone Python 包 | LangChain 的子模块 |
| Skill 发现 | 5 层目录(bundled/user/project/plugin) | 手动 register_skill() |
| MCP 客户端 | 原生 stdio + http transport | 依赖 LangChain MCP adapter |
| Permission | 11 条硬黑名单 + 软规则分层 | 依靠 tool 的 is_read_only 标记 |
| Provider | Anthropic / OpenAI / Copilot / Codex / Moonshot / GLM / Ollama | LangChain 支持的所有 |
| Plugin 协议 | 兼容 Claude Code plugin 格式(plugin.json) | LangChain 自己的 tool 装饰器 |
| Coordinator 模式 | 环境变量 + XML 任务通知 | LangGraph 的 subgraph |
核心差异:OpenHarness 走 “协议兼容”路线(兼容 Claude Code 的 skill/plugin/mcp 格式),DeepAgents 走 “框架垄断”路线(所有扩展必须 import LangChain)。前者更易迁移,后者更易扩展。
5.2 对比 Hermes Agent(NousResearch)
| 维度 | OpenHarness | Hermes Agent |
|---|---|---|
| 架构定位 | 单一 Python 包 | Python + 多 sub-package(agent/llm/tools) |
| Provider | 8+ LLM backend | 主要是 NousResearch 自托管模型 |
| Sub-Agent | Coordinator/Worker + XML 通知 | 没有显式 sub-agent,靠 prompt 切换 role |
| Skill 加载 | 5 层目录 + frontmatter 解析 | 没有 skill 概念,靠 system prompt |
| Permission | 显式硬黑名单 + 软规则 | 默认 deny + 用户授权 |
核心差异:Hermes 把 Agent 当作 “model + prompt + tools” 的简单组装,OpenHarness 把 Agent 当作 “长期运行的个人助理平台”。前者适合研究/原型,后者适合真实使用。
5.3 对比 Claude Code(Anthropic 官方)
| 维度 | OpenHarness | Claude Code |
|---|---|---|
| Skill 协议 | SKILL.md frontmatter | SKILL.md frontmatter(完全兼容) |
| Plugin 协议 | .claude-plugin/plugin.json | 同 OpenHarness(完全兼容) |
| MCP 协议 | stdio + http | stdio + http + SSE |
| Provider | 8+ 后端(核心差异) | 仅 Anthropic 官方 + Claude subscription |
| TUI | React/Ink(独立前端) | React/Ink(官方版本) |
| Memory | MEMORY.md + CLAUDE.md | CLAUDE.md + auto memory |
核心差异:Claude Code 是 Anthropic 生态的”原生”客户端,OpenHarness 是 “Anthropic 兼容 + 多 provider” 的开源复刻。如果你只用 Claude,Claude Code 更省事;如果你用 OpenAI / Copilot / Codex / Moonshot / Ollama,OpenHarness 是唯一能跑通的开源 Harness。
graph LR
subgraph "A. 协议兼容派(学 Claude Code 协议)"
OH["🛠️ OpenHarness<br/>14.7k⭐ 多 provider"]
CC["🛠️ Claude Code<br/>官方 Anthropic"]
GC["🛠️ Goose (block)<br/>7k⭐ 多 provider"]
end
subgraph "B. 框架垄断派(学 LangChain 协议)"
DA["🦜 DeepAgents<br/>26k⭐ LangChain 子模块"]
LC["🦜 LangChain<br/>141k⭐ Python 框架"]
end
subgraph "C. 自研协议派(自创体系)"
HE["🦅 Hermes<br/>NousResearch"]
OC["🦞 OpenClaw<br/>38万⭐ 自有生态"]
end
OH -. 兼容 .-> CC
GC -. 兼容 .-> CC
DA -. 依赖 .-> LC
HE -. 独立 .-> OC
style OH fill:#E8D5F5,stroke:#CE93D8,color:#333
style CC fill:#C7CEEA,stroke:#9FA8DA,color:#333
style GC fill:#E8D5F5,stroke:#CE93D8,color:#333
style DA fill:#FFDAB9,stroke:#FFAB76,color:#333
style LC fill:#FFDAB9,stroke:#FFAB76,color:#333
style HE fill:#B5EAD7,stroke:#80CBC4,color:#333
style OC fill:#B5EAD7,stroke:#80CBC4,color:#333为什么 OpenHarness 选择”协议兼容”路线:
- 生态复用:直接兼容
anthropics/skills仓库的 12 个官方 plugin,0 改造成本 - 避免厂商锁定:用户可以无痛从 Claude Code 迁来,反之亦然
- 降低学习曲线:写过 Claude Code skill 的人,0 培训就能写 OpenHarness skill
代价:永远要 follow 上游协议变更。Claude Code 一旦改 SKILL.md 格式,OpenHarness 必须跟着改。
六、6 件套的优劣对比(按维度拆分)
按左轻右重的标准结构对比:
6.1 Rule(软约束)
| 维度 | OpenHarness | 评价 |
|---|---|---|
| 架构简洁性 ✅ | 11 条硬黑名单 + 软规则 2 层 | 极简,黑白分明 |
| 扩展性 ⚠️ | 只能加 path_rules / denied_commands,不能改硬黑名单 | 安全优先,灵活性受限 |
| 易用性 ✅ | settings.json 即可配置 | 用户友好 |
| 性能 ✅ | fnmatch 缓存敏感,开销 < 1ms | 快 |
| 复杂度 ⚠️ | 双层结构比单一规则复杂 | 代码维护需分层 |
| 维护性 ✅ | 硬黑名单集中在文件顶部 | 易审计 |
6.2 Skill(按需 SOP)
| 维度 | OpenHarness | 评价 |
|---|---|---|
| 架构简洁性 ⚠️ | 5 层目录 + frontmatter 解析 | 中等复杂 |
| 扩展性 ✅ | 兼容 Claude / Agents 生态 + plugin 二次加载 | 强 |
| 易用性 ✅ | 写个 SKILL.md 即可,无需代码 | 极佳 |
| 性能 ✅ | 启动时一次性发现,运行时按需加载 | 快 |
| 复杂度 ⚠️ | 路径优先级 + 安全过滤 | 代码量不小 |
| 维护性 ✅ | 路径发现 + 注册表分离 | 易扩展 |
6.3 Sub-Agent
| 维度 | OpenHarness | 评价 |
|---|---|---|
| 架构简洁性 ⚠️ | Coordinator/Worker 双角色 + XML 协议 | 中等复杂 |
| 扩展性 ✅ | WorkerConfig 字段丰富(model/color/team) | 强 |
| 易用性 ⚠️ | 需理解环境变量 CLAUDE_CODE_COORDINATOR_MODE | 学习曲线 |
| 性能 ✅ | 直接 asyncio subprocess | 快 |
| 复杂度 ⚠️ | XML 序列化 + 反序列化 | 协议代码多 |
| 维护性 ✅ | 协议层和执行层分离 | 易调试 |
6.4 Workflow(编排)
| 维度 | OpenHarness | 评价 |
|---|---|---|
| 架构简洁性 ✅ | 单一 Agent Loop + max_turns 上限 | 极简 |
| 扩展性 ⚠️ | 没有 DAG/state machine,靠 max_turns 控制 | 受限 |
| 易用性 ✅ | 不用学新概念 | 简单 |
| 性能 ✅ | 一个 asyncio 循环 | 快 |
| 复杂度 ✅ | 几行代码 | 低 |
| 维护性 ✅ | 状态机简化(auto-compact 是唯一状态) | 易维护 |
注意:OpenHarness 故意没做完整的 Workflow 引擎(不像 LangGraph)。这符合 Bitter Lesson——把”长任务的可靠性”推给模型推理 + auto-compact,而不是写一堆状态机代码。
6.5 Script(门控)
| 维度 | OpenHarness | 评价 |
|---|---|---|
| 架构简洁性 ✅ | 4 种 hook 类型共享同一接口 | 极简 |
| 扩展性 ✅ | 加一种 hook 只需实现一个 async 函数 | 强 |
| 易用性 ⚠️ | 写 hook 配置需要 JSON 格式 | 中等 |
| 性能 ✅ | 并发跑 hook,asyncio.wait_for 超时 | 快 |
| 复杂度 ⚠️ | 4 种 hook 实现 + 聚合逻辑 | 代码量大 |
| 维护性 ✅ | 4 种 hook 完全独立 | 易扩展 |
6.6 MCP(外部桥接)
| 维度 | OpenHarness | 评价 |
|---|---|---|
| 架构简洁性 ✅ | 一个 McpClientManager 管所有 server | 极简 |
| 扩展性 ✅ | stdio + http 双 transport | 强 |
| 易用性 ✅ | settings.json 加 server 即可 | 简单 |
| 性能 ⚠️ | 启动时连所有 server,慢 | 启动慢 |
| 复杂度 ✅ | 复用官方 MCP SDK | 低 |
| 维护性 ✅ | 失败状态独立,不阻塞主流程 | 健壮 |
关键设计:MCP server 启动失败时 McpClientManager 只标记 state="failed",不抛异常。这避免了”一个 MCP server 挂了,整个 Agent 起不来”。
七、从零搭建启示:复刻 OpenHarness 的 MVP
7.1 最小可行实现(4 件套 = 200 行代码)
如果我自己复刻一个 OpenHarness MVP,按优先级砍掉非核心功能:
graph TB
subgraph "MVP 4 件套(1 周,~200 行)"
E["🧠 engine/loop.py<br/>Agent Loop"]
T["🔧 tools/base.py<br/>BaseTool + Registry"]
P["🛡️ permissions/checker.py<br/>敏感路径黑名单"]
H["⚡ hooks/executor.py<br/>Command Hook"]
end
subgraph "生产 7 件套(+3 周,~500 行)"
S["📚 skills/loader.py<br/>单层目录加载"]
A["🌐 api/client.py<br/>Anthropic 流式客户端"]
M["🌐 mcp/client.py<br/>stdio MCP 客户端"]
end
subgraph "进阶 Multi-Agent(+2 月,~1500 行)"
C["🤝 coordinator/mode.py<br/>Sub-Agent XML 协议"]
MEM["🧠 memory/persistent.py<br/>MEMORY.md"]
end
subgraph "完整个人 Agent(+3 月,~3000 行)"
U["🖥️ ui/tui.py<br/>React TUI"]
PL["🔌 plugins/loader.py<br/>Claude 兼容 plugin"]
end
E --> T --> P --> H
S --> A --> M
C --> MEM
U --> PL
style E fill:#FFB3C6,stroke:#F48FB1,color:#333
style T fill:#FFB3C6,stroke:#F48FB1,color:#333
style P fill:#FFB3C6,stroke:#F48FB1,color:#333
style H fill:#FFB3C6,stroke:#F48FB1,color:#333
style S fill:#FFDAB9,stroke:#FFAB76,color:#333
style A fill:#FFDAB9,stroke:#FFAB76,color:#333
style M fill:#FFDAB9,stroke:#FFAB76,color:#333
style C fill:#FFF9C4,stroke:#F9A825,color:#333
style MEM fill:#FFF9C4,stroke:#F9A825,color:#333
style U fill:#B5EAD7,stroke:#80CBC4,color:#333
style PL fill:#B5EAD7,stroke:#80CBC4,color:#3331 | MUST(必须): |
MVP 总代码量:~200 行 = 4 个 MUST 文件,能跑通”调用 Claude + 文件读写 + 敏感路径保护”。
7.2 复刻时必踩的 5 个坑
Anthropic API 拒绝”未配对的 tool_use”:多 tool_call 并发时,
asyncio.gather(..., return_exceptions=False)会让一个异常 cancel 其他协程,导致有tool_use没tool_result。必须用return_exceptions=TrueMCP server 启动阻塞主流程:默认 MCP client 在 import 时尝试 connect,server 挂了 Agent 起不来。改成 lazy connect(
McpClientManager.connect_all()单独调用,失败只记日志)敏感路径检查漏掉父目录:
/home/user/.ssh/keys/id_rsa要匹配*/.ssh/*,光检查/home/user/.ssh/keys/id_rsa不够。生成候选路径列表(自身 + 所有祖先 + “/*” 后缀)Skill 路径注入:settings 里写
../../etc/passwd这种路径能逃逸到任意位置。_valid_project_skill_dirs()必须拒绝绝对路径和..Hook 超时拖垮主流程:HTTP hook 调外部服务卡住时,整个 Agent 卡死。每个 hook 自带 timeout +
asyncio.wait_for(process.communicate(), timeout=...)
7.3 推荐演进路径
| 阶段 | 时间 | 加什么 | 解决什么问题 |
|---|---|---|---|
| MVP(4 件套) | 1 周 | engine + tools + permissions + command hook | 基础 Agent 能用 |
| 生产(6 件套) | 2-3 周 | + skills + mcp + api 客户端 | 多 provider + skill 加载 |
| 进阶(多 Agent) | 1-2 月 | + coordinator + tasks + memory | 长任务 + 持久化 |
| 完整(个人 Agent) | 3 月+ | + ui + plugins + channels | 像 ohmo 一样能聊天 |
八、总结与行动建议
OpenHarness 给我们的 4 个核心启示:
- 6 件套不是 marketing 词汇,是 10 个 Python 子目录。每个子目录 200-500 行代码,加起来不到 5000 行就能搭出生产级 Harness
- 硬黑名单比白名单更安全。把”绝对不能动的路径”写成代码常量而非配置,攻击面只剩”改源码重新部署”
- 协议兼容 > 自创标准。OpenHarness 直接抄 Claude Code 的 SKILL.md / plugin.json 格式,0 学习成本复用 Anthropic 生态
- MCP 失败不能阻塞主流程。把连接失败降级为
state="failed"状态记录,让 Agent 继续跑
给不同角色的行动建议:
- Agent 应用开发者:今天就把
PermissionChecker的 11 条硬黑名单抄进你的项目,30 行代码挡住 90% 的 credential 泄露 - Agent 平台架构师:认真评估 OpenHarness / DeepAgents / Hermes 三选一。如果你要”多 provider + Anthropic 兼容”,OpenHarness 是 2026 年最完整的开源实现
- AI 安全研究者:把 OpenHarness 的”硬黑名单 + 软规则”模式作为 Agent 安全基线——任何 Agent 框架都应该有这两层
- 创业团队:不要从 0 写 Harness,直接 fork OpenHarness 改 ohmo 渠道(Slack/Feishu/Discord)就能上线一个个人 Agent
下一篇预告:Harness 6 件套的 Hook 组件专题——OpenHarness 的 4 种 hook 类型(Command/HTTP/Prompt/Agent)是怎么把”机制和策略分离”做到极致的,对比 LiteLLM CustomLogger(2026-07-05 写过)和 OpenHands 的事件回调,看看”Hook 设计”这件事 3 种主流风格的差异。
金句:「Harness 的本质不是”写一个 Agent 框架”,而是”用 5000 行代码把模型的安全边界、能力范围、扩展协议全部固定下来”。OpenHarness 的 14k⭐ 不在于它功能多,而在于它把这件事做对了——硬黑名单在代码里,Skill 在文件里,MCP 失败不阻塞,每一处都体现”机制和策略分离”的克制。」
参考资源:
- OpenHarness GitHub — 14,680⭐,港大数据智能实验室出品
- Agent Loop 源码 query.py — 38992 字符,并发 tool_call + auto-compact
- Permission Checker 源码 — 7269 字符,11 条硬黑名单 + 软规则
- Skills Loader 源码 — 8050 字符,5 层目录发现
- Hook Executor 源码 — 8605 字符,4 种 hook 类型
- MCP Client 源码 — 11093 字符,stdio + http 双 transport
- ohmo 个人 Agent — Feishu/Slack/Telegram/Discord 多渠道 gateway
- 上一篇 5 大 Coding Agent Harness 横评 — 与本文形成”项目级深挖 vs 横向对比”互补
- 上一篇 LiteLLM Hook/Event 系统横评 — Hook 设计的另一种主流风格