第五章 上下文管理 — 记忆与压缩
AI Agent 面临的核心工程挑战之一:上下文窗口有限,但对话越来越长。Hermes 的解决方案是三层架构——上下文引擎(可插拔)、压缩器(三区策略)、记忆管理器(多 Provider)。
5.1 上下文引擎架构
agent/context_engine.py 定义了可插拔的上下文管理接口:
核心接口
class ContextEngine:
last_prompt_tokens: int # 上次 prompt token 数
last_completion_tokens: int # 上次 completion token 数
threshold_tokens: int # 压缩触发阈值
def should_compress(self) -> bool:
"""是否需要压缩"""
def compress(self, messages) -> List[Dict]:
"""执行压缩,返回压缩后的消息列表"""插件系统允许第三方实现自定义压缩策略,只需放到 plugins/context_engine/<name>/ 目录下。
5.2 上下文压缩器 — 三区策略
agent/context_compressor.py 实现了经典的三区压缩算法:
压缩流程
结构化摘要模板
压缩后的摘要遵循固定格式:
[CONTEXT COMPACTION — REFERENCE ONLY]
## Resolved Questions
- 用户偏好 Python 而非 JavaScript
- 项目使用 uv 管理依赖
## Pending Questions
- 部署目标尚未确认
## Active Task
- 正在实现用户认证模块
## Remaining Work
- 编写测试
- 更新文档关键设计:摘要中保留了 _prev_summary,每次压缩都在前一次摘要的基础上迭代更新,避免信息丢失。
5.3 工具结果智能摘要
不是所有工具结果都同等重要。压缩器为每种工具定制了摘要策略:
| 工具类型 | 摘要内容 | 丢弃内容 |
|---|---|---|
terminal | 退出码、输出行数、错误信息 | 完整 stdout/stderr |
file_read | 文件路径、大小、行数 | 文件完整内容 |
file_write | 文件路径、写入行数 | 写入的内容 |
web_search | 搜索查询、结果数量 | 搜索结果详情 |
web_extract | URL、内容大小 | 完整网页内容 |
browser | URL、操作类型 | 页面完整 DOM |
execute_code | 代码预览(前几行)、输出行数 | 完整代码和输出 |
JSON 安全截断
工具参数中的大 JSON 字符串会被安全截断:
def _json_safe_truncate(s: str, max_chars: int) -> str:
"""尝试解析 JSON,缩小字符串值,重新序列化"""
try:
obj = json.loads(s)
# 递归缩小所有字符串值
_shrink_strings(obj, max_chars)
return json.dumps(obj, ensure_ascii=False)
except json.JSONDecodeError:
return s # 非 JSON 原样返回5.4 会话持久化 — SessionDB
hermes_state.py 实现了基于 SQLite 的会话存储,支持全文搜索。
数据模型
关键特性
WAL 模式(Write-Ahead Logging):
self._conn.execute("PRAGMA journal_mode=WAL")允许多个读者 + 一个写者并发操作,不会阻塞。
写入冲突处理:
def _execute_with_retry(self, sql, params, max_retries=3):
for attempt in range(max_retries):
try:
return self._conn.execute(sql, params)
except sqlite3.OperationalError as e:
if "locked" in str(e) and attempt < max_retries - 1:
time.sleep(random.uniform(0.05, 0.2)) # 随机抖动
continue
raise会话链(Session Chaining):压缩后创建新 session,通过 parent_session_id 链接,保留完整历史。
FTS5 全文搜索:
CREATE VIRTUAL TABLE messages_fts USING fts5(content, session_id);
-- 搜索: "工具调用" 相关的对话
SELECT session_id, snippet(messages_fts) FROM messages_fts
WHERE messages_fts MATCH '工具调用' ORDER BY rank;5.5 记忆管理器 — 多 Provider 架构
agent/memory_manager.py 协调多个记忆 Provider:
Provider 接口
class MemoryProvider(ABC):
def prefetch(self, query: str) -> str:
"""对话开始前,根据用户消息预取相关记忆"""
def sync_turn(self, user: str, assistant: str) -> None:
"""每轮对话后,同步新信息到记忆"""
def get_tool_schemas(self) -> List[Dict]:
"""返回记忆相关的工具 schema(如 memory_save)"""
def handle_tool_call(self, name: str, args: Dict) -> str:
"""处理记忆工具调用"""故障隔离
每个 Provider 独立运行,一个失败不影响其他:
for provider in self._providers:
try:
result = provider.prefetch(query)
combined += result
except Exception as e:
logger.warning("Provider %s failed: %s", provider.name, e)
# 继续尝试下一个 Provider5.6 内置记忆 Provider
MEMORY.md — 持久化事实
存储在 ~/.hermes/memories/MEMORY.md,记录跨会话的持久事实:
# Memory
## 用户偏好
- 偏好 Python,不喜欢 JavaScript
- 喜欢用 Rich 库做终端输出
- 偏好中文交流
## 项目约定
- 使用 uv 管理依赖
- 测试用 pytest + xdist
## 纠正记录
- 不要用 simple_term_menu,tmux 下有 bugUSER.md — 用户画像
存储在 ~/.hermes/memories/USER.md:
# User Profile
- 角色:后端工程师,10 年 Go 经验
- 项目:hermes-agent AI agent 开发
- 时区:UTC+8上下文隔离
记忆通过 <memory-context> 标签注入,防止被当作用户输入:
<memory-context>
这里是记忆内容,不会被视为用户发送的消息
</memory-context>记忆使用规则
- 只记事实,不记录任务进度或临时状态
- 声明式格式:"用户偏好 X"而非"总是做 X"
- 用户偏好 > 纠正 > 约定 的优先级
5.7 外部记忆集成
Honcho
Honcho 是生产级记忆后端,提供辩证式用户建模:
- 通过对话自动构建用户认知模型
- 支持多用户、多会话隔离
- 适合长期运行的生产环境
Mem0
基于向量检索的记忆系统:
- 自动从对话中提取关键信息
- 语义搜索而非关键词匹配
- 适合需要模糊检索的场景
集成方式
在 config.yaml 中配置:
memory:
provider: honcho # 或 mem0
honcho:
api_key: "..."
endpoint: "https://..."源码导航:
- 上下文引擎 →
agent/context_engine.py - 压缩器 →
agent/context_compressor.py - 会话存储 →
hermes_state.py - 记忆管理 →
agent/memory_manager.py、agent/memory_provider.py
下一章:第六章 技能系统