本文基于 Hermes Agent 官方架构文档、开发者文档和公开源码页面的静态调研整理,重点关注 Agent 模块中的上下文管理、上下文压缩、长期记忆、会话持久化与可扩展治理机制。
做 Agent 产品时,上下文治理很容易被简化成一句话:
历史太长了,需要压缩。
但 Hermes 的设计提醒我,上下文治理不是单点压缩策略,而是一条完整生命周期:
用户输入
↓
Agent Loop
↓
Prompt Builder 组装稳定上下文与临时上下文
↓
Memory / User Profile / Project Context / Tool Guidance 注入
↓
Context Engine 判断是否需要压缩
↓
Context Compressor 压缩中间历史,保护首尾和工具调用对
↓
模型响应
↓
工具调用、Memory 写入、Session DB 持久化
↓
下一轮继续
这意味着,真正的 Agent 上下文治理至少要同时处理五件事:
- 模型当前需要看到什么;
- 哪些内容应该稳定缓存;
- 哪些历史可以压缩;
- 压缩后如何追溯原文;
- 长期记忆和会话历史如何分工。
我的总体判断是:
Hermes 已经具备比较完整的上下文治理工程基础,但当前仍更偏“启发式压缩 + prompt 分层 + 单 ContextEngine + 单 MemoryProvider”的架构。它最有价值的启发,不是某个压缩参数,而是把上下文治理拆成可替换的 ContextEngine、MemoryProvider、Session Search 和 Prompt Assembly 多个边界。
上下文治理不是一个 Memory 模块
Hermes 的上下文治理并不集中在单独的 memory 模块里,而是分散在多个组件之间。
| 层级 | 作用 |
|---|---|
| Prompt 输入层 | 决定 system prompt、memory、user profile、skills、context files、tool guidance 如何进入模型 |
| 会话历史层 | 维护 conversation messages,保证模型消息格式和工具调用顺序 |
| 压缩治理层 | 判断何时压缩、压缩什么、保留什么、摘要如何生成 |
| 持久记忆层 | 管理 MEMORY、USER profile、外部 MemoryProvider 和 memory tools |
| 持久会话与检索层 | 保存完整历史,支持 session search、lineage 和全文检索 |
这对 Agent 产品设计很重要。
如果把上下文治理只理解成 memory,就会倾向于把所有问题都塞进长期记忆:用户偏好、当前任务进度、工具结果、历史错误、项目规则、代码片段、文件引用。
但这些信息的生命周期并不相同。
更合理的拆法是:
| 内容类型 | 更适合的位置 |
|---|---|
| 用户偏好、长期事实 | Memory |
| 当前任务状态 | Working context / Active task |
| 工具调用和原始结果 | Event log / Tool result store |
| 被压缩历史 | Session DB / Search |
| 项目规则和工具说明 | Stable prompt layer |
| 临时召回和当前轮提示 | Ephemeral layer |
Hermes 的价值正在于它没有把所有东西都叫做 memory,而是在 prompt、compression、memory provider、session storage 之间做了职责划分。
Prompt Assembly:稳定层和临时层分离
Hermes 的 prompt 设计里有一个重要原则:
稳定 system prompt 和 API-call-time 临时层分离。
稳定层通常包括:
- Agent identity;
- tool guidance;
- static instructions;
- frozen MEMORY;
- frozen USER;
- skills index;
- context files;
- platform / session 信息。
临时层则包括:
- ephemeral prompt;
- prefill;
- gateway overlays;
- recall;
- API-call-time dynamic context。
这个设计的好处是明显的:稳定信息更容易利用 prompt cache,临时信息可以按需注入。
但它也带来一个产品取舍:
Session 中途写入 memory 后,磁盘里的 memory 可能已经更新,但已经缓存的 system prompt 不一定立即改变。
这不是 bug,而是上下文一致性和 prompt cache 之间的取舍。
因此,做更精细的上下文治理时,需要把 memory 再拆成两类:
long-term memory
跨 session 持久化
适合稳定偏好、长期事实、项目约定
working memory
当前任务内可变
适合中间结论、临时约束、尚未确认的事实
长期 memory 不应该每轮随意改写 system prompt。当前任务内的新信息,更适合通过 ephemeral context、working memory 或 context delta 注入。
这和我在 Task Context 文章里提到的观点一致:上下文不应该只有“进 prompt / 不进 prompt”两个状态,而应该有层级、作用域、生命周期和可信度。
Context Files:项目上下文是潜在不可信输入
Hermes 会加载项目上下文文件,例如:
.hermes.md
HERMES.md
AGENTS.md
CLAUDE.md
.cursorrules
这些文件会被安全扫描,检测 prompt injection、Unicode 异常、credential exfiltration 等风险,并且会被截断到一定长度。
这个设计有一个很重要的产品含义:
项目上下文不是天然可信的系统指令。
很多 Agent 产品会把项目规则文件直接塞进 system prompt,默认它们是可靠指令。但在真实工作区里,项目文件可能来自仓库、第三方模板、用户粘贴、历史生成内容,甚至可能包含恶意 prompt injection。
因此,项目上下文应该带有 provenance 和 trust level。
一个更稳的上下文记录可以长这样:
context_item:
source: AGENTS.md
source_type: project_file
trust_level: project_policy
scope: repo
can_act_as_instruction: false
can_trigger_tool: false
expires_at: optional
Hermes 已经做了 context file security scan,但从产品架构角度,还可以进一步发展成完整的 Context Ledger。
ContextEngine:最重要的扩展点
Hermes 的 ContextEngine 是整个上下文治理里最值得关注的抽象。
它不是普通 summarizer,而是决定在接近 token limit 时如何控制上下文的策略层。内置 compressor 是默认实现,第三方 engine 可以通过插件替换。
这个边界很重要,因为它说明后续做上下文治理时,不应该优先硬改 Agent Loop。
更干净的路径是:
不要优先改 run_agent.py
↓
实现自定义 ContextEngine
↓
在 ContextEngine 内部做:
- token budget 统计
- 上下文分级
- 历史压缩
- 事实抽取
- 可逆引用
- 检索增强
- 压缩审计
↓
通过 context.engine 配置启用
这对产品可迭代性很重要。
如果上下文治理散落在 Agent Loop 里,后续想做 retrieval、memory ranking、compression audit、task context、context search,就会不断修改主循环。
如果 ContextEngine 是明确边界,那么上下文治理可以成为可替换策略,而不是 Agent Loop 的硬编码逻辑。
ContextCompressor:压缩不是简单摘要
Hermes 内置 ContextCompressor 的默认策略大致是:
threshold_percent = 0.50
protect_first_n = 3
protect_last_n = 20
summary_target_ratio = 0.20
abort_on_summary_failure = false
也就是说,上下文达到约 50% 时就开始压缩,保护开头和最近消息,把中间历史压缩成摘要。
但真正值得注意的不是这些参数,而是 compressor 还要维护很多协议不变量:
- 压缩旧 tool results;
- 压缩中间消息;
- 保护首尾消息;
- 对齐 tool call / tool result 边界;
- 剥离历史 multimodal screenshots;
- 截断 tool call 参数但保持 JSON 有效;
- 在摘要前做敏感信息 redaction;
- 处理摘要失败路径;
- 保留 session lineage。
这说明 Agent 上下文压缩不是普通聊天摘要。
普通聊天摘要只需要保留语义。Agent 压缩还必须保留执行协议、工具调用顺序、错误恢复线索和可追溯性。
尤其是 tool call pair,是硬约束:
assistant(tool_calls=[...])
必须跟随对应 tool result
tool result
必须能对应到前面的 assistant tool_call_id
压缩边界
不能切在 tool call group 中间
如果这个约束被破坏,模型 API 可能直接报错,或者 Agent 的执行历史会变得不可解释。
压缩摘要也需要语义约束
Hermes 的压缩摘要会标注 REFERENCE ONLY,强调摘要只是参考信息,不是新的系统指令。
这个细节非常重要。
压缩摘要有一个常见风险:
模型把旧摘要当成新指令。
如果摘要里包含“用户希望 X”“之前决定 Y”“应该继续 Z”,模型可能会把这些内容提升为当前轮的高优先级指令。随着多轮压缩,旧摘要可能逐渐改变任务方向。
更稳的方式是把摘要结构化:
{
"reference_only": true,
"active_task": "...",
"constraints": [],
"decisions": [],
"completed_steps": [],
"open_threads": [],
"files": [],
"tool_results": [],
"retrieval_anchors": []
}
结构化摘要有几个好处:
- 更容易验证;
- 更容易过滤;
- 更容易重排;
- 更容易审计;
- 更容易绑定 raw ref;
- 更不容易被模型误解成指令。
对 Agent 产品来说,摘要不是“把历史变短”,而是“把可执行状态、证据和引用压缩成可控结构”。
最大风险:静默丢失上下文
Hermes 当前最值得警惕的风险,是压缩失败后的行为。
原文调研里提到,abort_on_summary_failure 默认可能是 false。这意味着摘要失败时,系统可能插入 fallback marker,并继续丢弃中间历史。
对普通聊天来说,这可能只是回答质量下降。
但对代码任务、工具密集任务、长流程 Agent 来说,这可能导致关键上下文静默丢失:
- 错误日志;
- 文件 diff;
- 用户修正;
- 工具失败原因;
- 中间决策;
- 某个被截断的命令输出。
更稳的策略应该是:
compression:
enabled: true
threshold: 0.50
target_ratio: 0.20
protect_last_n: 20
abort_on_summary_failure: true
同时要保证 summary model 的 context window 不小于主模型。否则长任务里很容易出现“主模型能看到的内容,摘要模型反而无法处理”的失败路径。
在高可靠 Agent 产品里,宁可中止压缩并暴露风险,也不要无声丢弃历史。
MemoryProvider 与 Session Search 应该分工
Hermes 的 MemoryProvider 是长期记忆扩展点。它支持 system prompt block、prefetch、sync turn、on session end、on pre compress、on memory write 等 hook。
其中 on_pre_compress 特别关键,因为它可以在压缩发生前,把即将丢失的内容抽取到长期记忆或其他持久层。
但长期 memory 不应该承载所有历史。
Hermes 同时有 Session DB 和 session_search,用于保存完整消息历史、tool calls、reasoning、model、token、cost 等信息,并支持 FTS5 全文检索和 lineage。
这意味着它其实有两种长期上下文:
| 系统 | 更适合承载 |
|---|---|
| MemoryProvider / MEMORY / USER | 长期事实、偏好、稳定约束、用户画像 |
| Session DB / Session Search | 原始对话、工具结果、可追溯历史、压缩前证据 |
这个分工很重要。
如果把所有东西都写进 memory,memory 会很快变成垃圾场。更合理的策略是:
- 事实、偏好、长期约束进入 memory;
- 原始历史和工具证据进入 session DB;
- 当前任务状态进入 working memory 或 Task Context;
- 大型历史内容通过 session search 或 retrieval anchor 找回。
Prompt Caching 与上下文治理的张力
Hermes 对 prompt caching 的处理也很有启发。
上下文治理希望动态注入更多相关信息;prompt caching 则希望 system prompt 尽量稳定,不频繁变动。
Hermes 的解法是:
- 稳定身份、记忆快照、工具规则放 system prompt;
- 临时 recall、gateway overlays、prefill 放 API-call-time 层;
- 压缩主要改历史消息,不随意改稳定 system prompt;
- prompt cache breakpoints 优先服务稳定前缀。
这和我在缓存优化文章中的判断一致:
上下文治理不能破坏稳定前缀和 append-only transcript。
如果后续引入更复杂的 retrieval 或 memory ranking,不应该频繁重写 system prompt,而应该通过 ephemeral context、delta 或 checkpoint 注入。
否则上下文越“智能”,缓存越差,长任务成本和延迟越高。
Hermes 当前设计的优点
我认为 Hermes 在上下文治理上有五个明显优点。
第一,Prompt 层次比较清楚。
稳定 system prompt、memory snapshot、project context、ephemeral overlays 被区分开,比把所有内容混进一个大 system prompt 更稳。
第二,压缩是 Agent 内部的一等机制。
它不是简单裁剪历史,而是考虑 token 阈值、首尾保护、tool pair、旧 tool result、多模态裁剪、敏感信息、失败策略。
第三,ContextEngine 插件化。
这让后续可以在不重写 Agent Loop 的情况下替换上下文治理策略。
第四,MemoryProvider 插件化。
长期记忆、用户画像、pre-compress hook、turn sync 都有扩展点。
第五,会话持久化和全文检索比较完整。
SQLite、FTS5、lineage、session search 让压缩后的 session 仍然有机会追溯原始历史。
主要风险和缺口
Hermes 的设计虽然完整,但仍然有几个明显风险。
1. 压缩仍然是有损的
即使有摘要,压缩也不可避免损失细节。
尤其是工具密集型任务,关键上下文可能藏在旧 tool result、错误日志、文件 diff、用户修正、中间分析、被截断命令输出里。
因此,压缩摘要应该尽量结构化,并保留 retrieval anchors。
2. 摘要失败不能静默丢历史
如果摘要失败还继续 drop middle,会让 Agent 在不知情的情况下丢失关键上下文。
高可靠场景下,abort_on_summary_failure 应该默认打开。
3. Memory Snapshot 冻结会造成当前任务不可见
Mid-session memory write 不自动改变 cached system prompt,有利于缓存稳定,但可能让当前任务刚写入的记忆不可见。
因此需要 working memory 和 long-term memory 分层。
4. 单 ContextEngine / 单 MemoryProvider 限制组合能力
复杂上下文治理往往需要多个子系统:
- 摘要 engine;
- retrieval engine;
- vector memory;
- user profile memory;
- code workspace state;
- task planner state;
- safety policy。
如果只能激活一个 engine 或 provider,就需要在内部实现 composite router。
5. Provenance 不够显式
Hermes 已经区分 prompt layers,也做了 context file scan,但还需要更完整的上下文来源账本。
每条上下文都应该知道:
- 来自哪里;
- 何时生成;
- 可信度如何;
- 能否作为指令;
- 是否过期;
- 是否可持久化;
- 是否可压缩;
- 是否被用户确认。
建议:做 Context Governance Layer
如果要在 Hermes 这类 Agent 架构上继续增强上下文治理,我不会只做一个更强的 summarizer。
我会加一层:
Context Governance Layer
推荐架构是:
AIAgent
↓
PromptBuilder
↓
Context Governance Layer
├── ContextLedger # 上下文来源账本
├── ContextBudgeter # token 预算分配
├── ContextRanker # 相关性排序
├── CompressionPlanner # 决定压缩哪些内容
├── MemoryRouter # 哪些写入长期记忆
├── RetrievalRouter # 何时调用 session_search / memory / vector
├── SafetyFilter # prompt injection / secret / policy
└── CompressionAuditor # 压缩质量和可回溯性
↓
ContextEngine.compress()
↓
Model messages
最小可落地版本可以先做三件事。
第一,ContextLedger
给每条上下文打来源、scope、trust、expiry、token cost、compression state。
context_record:
id: ctx_...
source_type: user_message | assistant_message | tool_result | memory | project_file | summary
source_ref: ...
trust_level: system | developer | user | project | tool | model_generated
scope: global | profile | project | session | task | turn
expires_at: ...
compression_state: raw | summarized | dropped | pinned
can_act_as_instruction: false
retrieval_anchor: ...
第二,CompressionAuditor
每次压缩后记录:
- 丢弃了什么;
- 保留了什么;
- 摘要覆盖了什么;
- 哪些 raw ref 可以找回;
- 是否有 tool pair 被修正;
- 是否有 secret 被 redacted;
- 是否有摘要失败 fallback。
用户不一定每次都看这些信息,但系统必须能解释。
第三,Retrieval-aware Compressor
摘要里不仅保留自然语言,还要保留可检索锚点:
- session id;
- message id;
- tool result id;
- file path;
- commit hash;
- raw output ref;
- page / line range。
这样压缩后的上下文不是死摘要,而是可以回到原始证据。
测试清单
上下文治理不能只靠人工阅读效果,需要测试。
至少要覆盖:
- 压缩后没有 orphan tool result。
- 压缩后 assistant tool call 都有对应 tool result。
- 最近 N 轮不被压缩。
- system prompt 不被旧 summary 覆盖。
- memory 写入不会污染当前 cached system prompt。
- summary model 失败时不会静默丢上下文。
- 压缩摘要不包含 secret。
- session lineage 可以追溯原始消息。
- 中文 / CJK 内容可被 session search 检索。
- 多次压缩不会重复累积错误摘要。
这类测试反映了一个事实:Agent 上下文治理是系统工程,不是 prompt 文案优化。
对 Agent 产品的启发
Hermes 对 Agent PM 最有价值的启发有四点。
第一,上下文治理应该围绕 Execution Agent 的任务执行需要设计,而不是围绕“如何记住更多东西”设计。
第二,压缩、记忆、检索、缓存、安全、审计必须分层。把所有东西塞进 prompt 或 memory,都会让系统失控。
第三,ContextEngine 是很好的产品架构边界。它让上下文策略可以持续迭代,而不是绑死在 Agent Loop 里。
第四,可审计和可回溯会成为 Agent 产品信任基础。用户不只关心 Agent 做了什么,还会关心它为什么这样做、它看到了什么、它忘记了什么、它基于哪些证据行动。
结论
Hermes 的上下文治理已经不只是“长上下文压缩”,而是一套围绕 Prompt Assembly、ContextEngine、ContextCompressor、MemoryProvider、Session Search 和 Prompt Caching 的系统设计。
它当前仍有明显限制:压缩有损、摘要失败路径有风险、memory snapshot 冻结、单 engine/provider 限制组合能力、provenance 不够显式。
但它给出的架构方向是对的:
Agent 上下文治理的目标,不是让 prompt 更长,也不是让摘要更短,而是构建一个可审计、可回溯、可分级、可压缩、可检索、可撤销的 Context Governance System。
短期可以从 safer compression、ContextLedger、CompressionAuditor 做起;中期实现自定义 ContextEngine;长期把 memory、session search、project context、tool results 和 prompt cache 统一纳入治理。