1 · 机制逐条对照

这一章把四个核心问题拿出来,看两个系统各自怎么答。每一节的结构都是:问题是什么 → 两种解法 → 差异的根源。

1.1 上下文治理

问题

大语言模型有一个「上下文窗口」—— 一次能看多少文字,是有上限的。对话越长,占用越多,直到装不下。而且每一轮都要把全部历史重新发一遍,所以长上下文不只是「会满」,还是「每一轮都在烧钱」。

Claude Code:五级阶梯

① 工具结果预算 单个工具的输出超过配额就截断 ↓ 还是太长 ② 剪裁(snip) 把最老的工具结果替换成占位符 ↓ 还是太长 ③ 微压缩 只压缩工具结果,不动对话本身 ↓ 还是太长 ④ 上下文坍缩 把一段历史整体换成一段摘要 ↓ 还是太长 ⑤ 自动压缩 全量重写整个对话历史

五级的关键在于每一级的信息损失和执行代价都比上一级大。系统总是从最轻的手段开始,只有不够才升级。

此外还有一个 Anthropic 私有能力 cache_edits在服务端删除缓存里的某些内容,而本地消息列表完全不动。这让「压缩」可以做到不破坏提示词缓存的前缀 —— 这是外部开发者拿不到的能力。

Hermes:可插拔引擎

class ContextEngine(ABC):        # 490 行的抽象基类
    def should_compress(...)      # 抽象方法:现在该压缩了吗
    def compress(...)             # 抽象方法:怎么压缩
    def select_context(...)       # 抽象方法:这一轮发哪些消息

Hermes 不规定压缩策略,它规定的是「压缩器要长什么样」。内置一个叫 compressor 的默认实现,用户可以在配置里换成自己的。

而且它把动作拆成了两个正交的动词

做什么
select_context()
选择
每一轮都调用。决定「这一轮往模型发哪些消息」。不修改存储的历史
compress()
压缩
只在需要时调用。真正改写存储的历史,是不可逆的

源码里记录了一个真实的误用:有人为了让自己的引擎能每轮都介入,把 should_compress() 写死返回 True。结果是每一轮都真的执行一次不可逆压缩 —— 他想要的是「每轮选择」,用的却是「每轮销毁」。

这个故事说明:当一个接口被误用时,往往不是用户笨,而是接口没有把「读」和「写」分开。

差异的根源

Claude CodeHermes
形态固定的五级阶梯,写死在系统里一个抽象基类,策略可替换
可换吗不能。用户只能调参数能。换掉整个引擎
为什么只服务一种工作负载(编程),可以针对它做到极致优化;而且能用私有的 cache_edits要服务未知的、多样的工作负载,无法预先知道最优策略
代价换不了。你的场景如果不适合这五级,没有出路抽象层本身的成本 —— 490 行接口定义,还要保证任何实现都不破坏系统不变量

1.2 权限与安全

问题

智能体会执行命令、改文件、发网络请求。怎么防止它做出不可挽回的破坏?

Claude Code:10 步级联决策

每一次工具调用都走一条 10 步的判定链。最重要的设计是「绕过免疫」

步骤 1a-1c …… 步骤 1d-1g ★ 绕过免疫检查 这几步【无论用户开了什么模式都会执行】 包括最宽松的"跳过所有确认"模式 ↓ 步骤 2a 检查用户的权限模式 ↓ 步骤 2b-4 规则匹配、询问用户、执行

「绕过免疫」的意思是:有些检查不接受任何形式的豁免。

用户可以打开「不要再问我了」模式来跳过确认框,但跳不过 1d-1g 这几步。设计上把它们放在模式判断之前,就是为了让「跳过模式」这个开关在物理上够不着它们。

这比「在跳过逻辑里写 if 排除掉几项」更可靠 —— 后者依赖于每次改代码的人都记得维护那个排除列表。

Hermes:红线 + 环境隔离

第一层是 12 条硬编码红线HARDLINE_PATTERNS),配合一套相当精密的解析:

机制解决的问题
_CMDPOS 命令位置锚定只有出现在命令位置rm 才算命令。--title "rm -rf /" 里的不算
引号屏蔽引号里的内容是数据不是命令 —— 但 $() 是例外,它在双引号里仍会执行
Shell 载体识别bash -c "..."ssh host "..." 里面的内容要递归检查
去混淆r''mr\m$'\x72m' 都会被还原成 rm

第二层是执行环境:7 种后端(本机 / Docker / Modal / Vercel / Daytona / Singularity / SSH)。这是唯一真正的硬边界 —— 前面所有规则都是「猜测这条命令危不危险」,只有隔离是「就算危险也出不去」。

必须诚实说明:2026 年 4 月的第三方审计(约 36.4 万行代码)发现 Hermes 有 4 个「严重」、9 个「高」级别的架构问题,头号问题是默认后端是本机、无沙箱。也就是说默认安装等于给模型一个完整权限的终端。

那 5,802 行红线不能替代隔离。它拦的是「一眼看去就是灾难」的命令。

差异的根源

Claude CodeHermes
主要手段问人 —— 决策链的终点是弹确认框规则 + 隔离 —— 因为常常没人可问
模式数量6 种权限模式,用户按场景切换按 Profile / 触发源配置
不可豁免的部分绕过免疫的 1d-1g 步12 条红线
隔离有沙箱能力,但主要靠权限层7 种可选环境,但默认不隔离

这是「用户在不在场」这条主线最直接的体现。

Claude Code 可以把最难的判断交给人 —— 因为人就在那儿。
Hermes 必须自己判断 —— 所以它需要 5,802 行规则去逼近人的判断力,而这必然做不到,所以还需要隔离层兜底。

1.3 多智能体

Claude Code:三种形态

形态特点
普通子智能体全新的上下文,独立执行一个任务
分叉(fork)继承父智能体的完整上下文。用了 4 个技巧做到与父级字节级一致的 API 请求前缀,从而共享提示词缓存
工作流确定性的编排脚本,决定谁在什么时候跑

那 4 个分叉缓存技巧值得单独说:为了让子智能体的请求前缀和父级逐字节相同,系统必须保证系统提示词、工具定义、工具顺序、消息序列化方式全都完全一致。任何一个字节不同,整个缓存就失效,成本翻数倍。

Hermes:委派 + 看板

MAX_DEPTH = 1                          # 只允许一层
_DEFAULT_MAX_CONCURRENT_CHILDREN = 10  # 最多 10 个并发
_RECENT_SUBAGENTS_CAP = 200            # 历史记录上限

加上运行时控制:interrupt_subagent(中断)/ steer_subagent(注入指令)/ set_spawn_paused(停止派生但让现有的跑完)。

另一种模式是看板:多个对等的智能体共享一块任务板,各自认领。配合 kanban_heartbeat 心跳 —— 认领了但死掉的任务会自动回到待认领。

差异的根源

Claude CodeHermes
核心关注成本 —— 怎么让子智能体也命中缓存控制 —— 怎么让 10 个无人看管的子智能体不失控
深度有分叉,层级由工作流脚本决定硬限制一层
运行中干预用户 Ctrl-C三个专门的接口
对等协作看板模式

1.4 扩展机制

Claude CodeHermes
扩展点技能 · 插件 · MCP · 15 种钩子事件 · 记忆目录技能 · 插件 · MCP · 抽象基类矩阵
抽象基类较少 —— 大部分能力是内置的大量 —— 平台 / 记忆 / 上下文 / 模型 / 环境 / 定时,全都是可替换的
编译期89 个特性开关 + 死代码消除。关掉的功能物理上不进二进制文件运行时加载,无编译期
技能数量随版本内置81 个,跨 15 个类别

两个都用的模式:渐进式披露

这是两个系统独立得出的相同结论,也是本文最值得记住的一条。

目录常驻,内容按需。81 个技能全展开是 6 万 token;只放描述目录是 5 KB。模型看到目录,判断需要哪个,再去读那一个的完整内容。

两个系统在技能、工具、MCP、记忆四个地方都用了这个模式。这不是巧合 —— 它是「上下文有限且昂贵」这个物理约束的必然产物。

1.5 一页速查表

维度Claude CodeHermes
目标形态单一场景做到极致任意场景都能跑
上下文固定五级阶梯 + 私有缓存编辑可插拔引擎(ABC)
权限10 步级联 + 绕过免疫12 条红线 + 7 种环境
多智能体分叉 + 缓存共享委派(深度≤1)+ 看板
扩展钩子事件 + 编译期开关抽象基类矩阵
入口终端(4 种启动形式)22 平台 + CLI + webhook + 定时
最大投入人机界面(输入框 347 KB)自主运行(委派 5,071 行)
失败时用户看到并处理事件系统 + 告警 + 自动重试
共同点渐进式披露 · 分层安全 · 上下文是最稀缺资源 · fail-closed 默认