1 · 机制逐条对照
这一章把四个核心问题拿出来,看两个系统各自怎么答。每一节的结构都是:问题是什么 → 两种解法 → 差异的根源。
1.1 上下文治理
问题
大语言模型有一个「上下文窗口」—— 一次能看多少文字,是有上限的。对话越长,占用越多,直到装不下。而且每一轮都要把全部历史重新发一遍,所以长上下文不只是「会满」,还是「每一轮都在烧钱」。
Claude Code:五级阶梯
五级的关键在于每一级的信息损失和执行代价都比上一级大。系统总是从最轻的手段开始,只有不够才升级。
此外还有一个 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 Code | Hermes | |
|---|---|---|
| 形态 | 固定的五级阶梯,写死在系统里 | 一个抽象基类,策略可替换 |
| 可换吗 | 不能。用户只能调参数 | 能。换掉整个引擎 |
| 为什么 | 只服务一种工作负载(编程),可以针对它做到极致优化;而且能用私有的 cache_edits | 要服务未知的、多样的工作负载,无法预先知道最优策略 |
| 代价 | 换不了。你的场景如果不适合这五级,没有出路 | 抽象层本身的成本 —— 490 行接口定义,还要保证任何实现都不破坏系统不变量 |
1.2 权限与安全
问题
智能体会执行命令、改文件、发网络请求。怎么防止它做出不可挽回的破坏?
Claude Code:10 步级联决策
每一次工具调用都走一条 10 步的判定链。最重要的设计是「绕过免疫」:
「绕过免疫」的意思是:有些检查不接受任何形式的豁免。
用户可以打开「不要再问我了」模式来跳过确认框,但跳不过 1d-1g 这几步。设计上把它们放在模式判断之前,就是为了让「跳过模式」这个开关在物理上够不着它们。
这比「在跳过逻辑里写 if 排除掉几项」更可靠 —— 后者依赖于每次改代码的人都记得维护那个排除列表。
Hermes:红线 + 环境隔离
第一层是 12 条硬编码红线(HARDLINE_PATTERNS),配合一套相当精密的解析:
| 机制 | 解决的问题 |
|---|---|
_CMDPOS 命令位置锚定 | 只有出现在命令位置的 rm 才算命令。--title "rm -rf /" 里的不算 |
| 引号屏蔽 | 引号里的内容是数据不是命令 —— 但 $() 是例外,它在双引号里仍会执行 |
| Shell 载体识别 | bash -c "..."、ssh host "..." 里面的内容要递归检查 |
| 去混淆 | r''m、r\m、$'\x72m' 都会被还原成 rm |
第二层是执行环境:7 种后端(本机 / Docker / Modal / Vercel / Daytona / Singularity / SSH)。这是唯一真正的硬边界 —— 前面所有规则都是「猜测这条命令危不危险」,只有隔离是「就算危险也出不去」。
必须诚实说明:2026 年 4 月的第三方审计(约 36.4 万行代码)发现 Hermes 有 4 个「严重」、9 个「高」级别的架构问题,头号问题是默认后端是本机、无沙箱。也就是说默认安装等于给模型一个完整权限的终端。
那 5,802 行红线不能替代隔离。它拦的是「一眼看去就是灾难」的命令。
差异的根源
| Claude Code | Hermes | |
|---|---|---|
| 主要手段 | 问人 —— 决策链的终点是弹确认框 | 规则 + 隔离 —— 因为常常没人可问 |
| 模式数量 | 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 Code | Hermes | |
|---|---|---|
| 核心关注 | 成本 —— 怎么让子智能体也命中缓存 | 控制 —— 怎么让 10 个无人看管的子智能体不失控 |
| 深度 | 有分叉,层级由工作流脚本决定 | 硬限制一层 |
| 运行中干预 | 用户 Ctrl-C | 三个专门的接口 |
| 对等协作 | 无 | 看板模式 |
1.4 扩展机制
| Claude Code | Hermes | |
|---|---|---|
| 扩展点 | 技能 · 插件 · MCP · 15 种钩子事件 · 记忆目录 | 技能 · 插件 · MCP · 抽象基类矩阵 |
| 抽象基类 | 较少 —— 大部分能力是内置的 | 大量 —— 平台 / 记忆 / 上下文 / 模型 / 环境 / 定时,全都是可替换的 |
| 编译期 | 89 个特性开关 + 死代码消除。关掉的功能物理上不进二进制文件 | 运行时加载,无编译期 |
| 技能数量 | 随版本内置 | 81 个,跨 15 个类别 |
两个都用的模式:渐进式披露
这是两个系统独立得出的相同结论,也是本文最值得记住的一条。
目录常驻,内容按需。81 个技能全展开是 6 万 token;只放描述目录是 5 KB。模型看到目录,判断需要哪个,再去读那一个的完整内容。
两个系统在技能、工具、MCP、记忆四个地方都用了这个模式。这不是巧合 —— 它是「上下文有限且昂贵」这个物理约束的必然产物。
1.5 一页速查表
| 维度 | Claude Code | Hermes |
|---|---|---|
| 目标形态 | 单一场景做到极致 | 任意场景都能跑 |
| 上下文 | 固定五级阶梯 + 私有缓存编辑 | 可插拔引擎(ABC) |
| 权限 | 10 步级联 + 绕过免疫 | 12 条红线 + 7 种环境 |
| 多智能体 | 分叉 + 缓存共享 | 委派(深度≤1)+ 看板 |
| 扩展 | 钩子事件 + 编译期开关 | 抽象基类矩阵 |
| 入口 | 终端(4 种启动形式) | 22 平台 + CLI + webhook + 定时 |
| 最大投入 | 人机界面(输入框 347 KB) | 自主运行(委派 5,071 行) |
| 失败时 | 用户看到并处理 | 事件系统 + 告警 + 自动重试 |
| 共同点 | 渐进式披露 · 分层安全 · 上下文是最稀缺资源 · fail-closed 默认 | |
1 · Mechanism by Mechanism
This chapter takes four core problems and looks at how each system answers them. Every section follows the same shape: what the problem is → the two solutions → where the difference comes from.
1.1 Context Management
The problem
A large language model has a “context window” — there is a hard limit on how much text it can look at in one go. The longer the conversation, the more of it is used up, until nothing more fits. And every turn re-sends the entire history, so long context isn't just “it will fill up” — it's “every turn burns money.”
Claude Code: a five-rung ladder
The point of the five rungs is that each rung loses more information and costs more to run than the one before it. The system always starts with the gentlest tool and only escalates when that isn't enough.
On top of this there's a private Anthropic capability, cache_edits: delete specific content from the cache server-side while the local message list stays completely untouched. This lets “compaction” happen without breaking the prompt-cache prefix — a capability outside developers can't get.
Hermes: a pluggable engine
class ContextEngine(ABC): # a 490-line abstract base class
def should_compress(...) # abstract: is it time to compress?
def compress(...) # abstract: how to compress
def select_context(...) # abstract: which messages to send this turn
Hermes doesn't prescribe a compaction strategy; what it prescribes is “what a compressor has to look like.” It ships a default implementation called compressor, and you can swap in your own through config.
It also splits the action into two orthogonal verbs:
| What it does | |
|---|---|
select_context()Select | Called every turn. Decides “which messages go to the model this turn.” Does not modify the stored history |
compress()Compress | Called only when needed. Actually rewrites the stored history; irreversible |
The source records a real misuse: someone wanted their engine to step in every turn, so they hard-coded should_compress() to return True. The result was a genuine, irreversible compaction on every single turn — they wanted “select every turn” and got “destroy every turn.”
The lesson: when an interface gets misused, it's usually not that the user is dumb — it's that the interface didn't separate “read” from “write.”
Where the difference comes from
| Claude Code | Hermes | |
|---|---|---|
| Shape | A fixed five-rung ladder, hard-wired into the system | One abstract base class; the strategy is swappable |
| Swappable? | No. Users can only tune parameters | Yes. Replace the whole engine |
| Why | It serves one workload (coding), so it can be tuned to the limit for it — and it gets to use the private cache_edits | It has to serve unknown, varied workloads, so the optimal strategy can't be known in advance |
| Cost | Can't be swapped. If your scenario doesn't fit these five rungs, there's no way out | The cost of the abstraction itself — a 490-line interface definition, plus guaranteeing that no implementation can break the system's invariants |
1.2 Permissions and Safety
The problem
An agent runs commands, edits files, and makes network requests. How do you stop it from doing irreversible damage?
Claude Code: a 10-step cascade
Every tool call goes through a 10-step decision chain. The most important design element is “bypass immunity”:
“Bypass immunity” means: certain checks accept no exemption of any kind.
The user can turn on a “stop asking me” mode to skip the confirmation prompts, but they cannot skip steps 1d-1g. Those steps sit before the mode check by design, precisely so that the “skip” switch physically can't reach them.
That's more reliable than “add an if inside the skip logic to exclude a few items” — the latter depends on everyone who ever touches the code remembering to maintain that exclusion list.
Hermes: red lines + environment isolation
The first layer is 12 hard-coded red lines (HARDLINE_PATTERNS), backed by a fairly sophisticated parser:
| Mechanism | Problem it solves |
|---|---|
_CMDPOS command-position anchoring | Only when it sits in command position does rm count as a command. The one inside --title "rm -rf /" doesn't |
| Quote masking | What's inside quotes is data, not a command — except $(), which still executes inside double quotes |
| Shell-carrier detection | The contents of bash -c "..." and ssh host "..." get checked recursively |
| De-obfuscation | r''m, r\m, and $'\x72m' all get normalized back to rm |
The second layer is the execution environment: 7 backends (local / Docker / Modal / Vercel / Daytona / Singularity / SSH). This is the only true hard boundary — every rule above is “guessing whether this command is dangerous”; only isolation is “even if it is, it can't get out.”
In fairness, this has to be said: a third-party audit in April 2026 (roughly 364,000 lines of code) found Hermes had 4 “critical” and 9 “high” severity architectural issues, the number one being that the default backend is the local machine, with no sandbox. In other words, a default install hands the model a terminal with full permissions.
Those 5,802 lines of red lines are not a substitute for isolation. What they catch are the commands that are obviously a disaster at a glance.
Where the difference comes from
| Claude Code | Hermes | |
|---|---|---|
| Primary means | Ask a human — the decision chain ends in a confirmation prompt | Rules + isolation — because often there's nobody to ask |
| Number of modes | 6 permission modes; the user switches by scenario | Configured per profile / trigger source |
| The non-exemptable part | Bypass-immune steps 1d-1g | 12 red lines |
| Isolation | Has sandbox capability, but leans mainly on the permission layer | 7 optional environments, but no isolation by default |
This is the most direct expression of the “is the user present” theme.
Claude Code can hand the hardest judgment calls to a human — because the human is right there.
Hermes has to make the call itself — so it needs 5,802 lines of rules to approximate human judgment, which can never fully succeed, which is why it also needs an isolation layer as the backstop.
1.3 Multi-Agent
Claude Code: three forms
| Form | Characteristics |
|---|---|
| Plain subagent | A fresh context; runs one task independently |
| Fork | Inherits the parent agent's full context. Uses 4 tricks to produce an API request prefix byte-identical to the parent's, so it shares the prompt cache |
| Workflow | A deterministic orchestration script that decides who runs when |
Those 4 fork-caching tricks deserve a separate mention: for the subagent's request prefix to be identical to the parent's byte for byte, the system has to guarantee that the system prompt, tool definitions, tool ordering, and message serialization are all exactly the same. A single differing byte invalidates the whole cache and multiplies the cost several times over.
Hermes: delegation + kanban
MAX_DEPTH = 1 # only one level allowed
_DEFAULT_MAX_CONCURRENT_CHILDREN = 10 # at most 10 concurrent
_RECENT_SUBAGENTS_CAP = 200 # cap on the history record
Plus runtime controls: interrupt_subagent (interrupt) / steer_subagent (inject instructions) / set_spawn_paused (stop spawning but let the existing ones finish).
The other mode is kanban: several peer agents share one task board and each claims work from it. Paired with the kanban_heartbeat heartbeat — a task that was claimed and then died automatically goes back to unclaimed.
Where the difference comes from
| Claude Code | Hermes | |
|---|---|---|
| Core concern | Cost — how to get subagents to hit the cache too | Control — how to keep 10 unsupervised subagents from running wild |
| Depth | Has forks; the hierarchy is set by the workflow script | Hard-limited to one level |
| Mid-run intervention | The user hits Ctrl-C | Three dedicated interfaces |
| Peer collaboration | None | Kanban mode |
1.4 Extension Mechanisms
| Claude Code | Hermes | |
|---|---|---|
| Extension points | Skills · plugins · MCP · 15 hook events · memory directory | Skills · plugins · MCP · a matrix of abstract base classes |
| Abstract base classes | Few — most capabilities are built in | Many — platform / memory / context / model / environment / scheduling are all replaceable |
| Compile time | 89 feature flags + dead-code elimination. A disabled feature is physically absent from the binary | Loaded at runtime; there is no compile step |
| Number of skills | Built in, varies by release | 81, across 15 categories |
A pattern both use: progressive disclosure
This is a conclusion the two systems reached independently, and the single most worthwhile thing in this article to remember.
The catalog stays resident; the contents load on demand. All 81 skills fully expanded is 60,000 tokens; a catalog of descriptions alone is 5 KB. The model sees the catalog, decides which one it needs, and then reads that one in full.
Both systems use this pattern in four places: skills, tools, MCP, and memory. That's no coincidence — it's the inevitable product of a physical constraint: context is finite and expensive.
1.5 One-Page Cheat Sheet
| Dimension | Claude Code | Hermes |
|---|---|---|
| Target shape | One scenario, pushed to the limit | Runs in any scenario |
| Context | Fixed five-rung ladder + private cache edits | Pluggable engine (ABC) |
| Permissions | 10-step cascade + bypass immunity | 12 red lines + 7 environments |
| Multi-agent | Fork + shared cache | Delegation (depth ≤ 1) + kanban |
| Extension | Hook events + compile-time flags | Matrix of abstract base classes |
| Entry points | Terminal (4 launch forms) | 22 platforms + CLI + webhook + cron |
| Biggest investment | The human interface (a 347 KB input box) | Autonomous operation (5,071 lines of delegation) |
| On failure | The user sees it and deals with it | Event system + alerting + automatic retry |
| In common | Progressive disclosure · layered safety · context is the scarcest resource · fail-closed by default | |