你家的 AI 助手会做梦吗?从 Hermes Agent 学到的 7 个架构设计最佳实践
📋 摘要: 不管用什么框架,好的设计理念是通用的。本文从一个开源 AI Agent 项目的开发指南出发,聊聊那些值得每个 AI 项目抄作业的架构实践。
🎬 引言
你家的 AI 助手会做梦吗?💭
如果你的 AI 助手用的是 OpenClaw,答案是——真的会。我们之前研究过 OpenClaw 的 “Dreaming” 功能,Agent 会在空闲时自动整理记忆、生成摘要,就像人在睡眠中巩固记忆一样。🌙
但今天我想聊的不是 OpenClaw 怎么做梦,而是我最近在研究另一个优秀的开源 AI Agent 项目 —— Hermes Agent 时,从它的 AGENTS.md 开发指南里挖到的宝藏。💎
作为一个基于 Python 的开源 AI 智能体框架,Hermes 专注于 CLI 和消息平台(Telegram、Discord、Slack 等)的集成。它的代码量不算特别大,但开发指南写得异常扎实。读完之后我最大的感受是:
好的架构设计,不是用了多先进的技术栈,而是把简单的事情做得足够好。✨
下面这些设计原则,不管你在用什么框架、写什么 AI 项目,都可以直接抄作业。📝
📌 一、中央命令注册表 —— 一处定义,处处同步
❓ 问题
假设你要开发一个支持多平台的 AI Agent,用户可以在 CLI 里输入 /help,在 Telegram 里发送 /help,在 Discord 里用 /help……
最常见的做法是什么?每个平台各写一份命令定义。
|
|
|
|
|---|---|---|
|
|
cli/commands.py |
|
|
|
telegram/handlers.py |
|
|
|
discord/slash_cmds.py |
|
|
|
slack/bolt_handlers.py |
|
|
|
autocomplete/registry.json |
|
改一个命令的 help 文案,要改 5 个地方。漏了一个,用户就会在不同平台看到不一致的提示。😫
🧪 Hermes 的做法
Hermes 搞了一个 COMMAND_REGISTRY,所有命令在一个地方定义:
💡 以下为简化示意代码,展示核心思想。Hermes 实际使用
CommandDef对象列表而非字典。
# 简化示意:所有命令在一个地方定义
COMMAND_REGISTRY = [
CommandDef("help", "Show available commands", "Info",
aliases=("h",), args_hint=""),
CommandDef("memory", "Manage agent memory", "Configuration",
aliases=("mem",), args_hint="<action>"),
# ... 更多命令
]
每个
CommandDef包含命令名、描述、分类、别名、参数提示、适用平台等字段。CLI 菜单、Telegram 命令列表、Discord 应用命令、Slack 路由、自动补全——全部从这个单一列表自动派生。
💡 核心思想
Single Source of Truth(单一数据源)
改一个地方,处处生效。这不是什么高深的设计模式,但很多项目就是做不到——因为”先快速跑起来”的时候,顺手就在每个平台各写了一份。等技术债积累到一定程度,再想重构就难了。🔧
我们的启发: 当你需要扩展自定义命令时,第一件事不是去对应平台的 handler 里加代码,而是先问自己——能不能把它加到中央注册表里?🤔
🔧 二、工具自动发现 —— 注册即生效
📦 常见做法
很多 AI Agent 项目里,添加工具需要同时做几件事:
# tools/__init__.py —— 手动维护导入列表
from .weather import get_weather
from .calendar import get_events
from .search import web_search
from .email import send_email
from .github import create_issue
# 每新增一个工具,这里加一行 import
# 然后下面还要注册……
TOOL_REGISTRY = [
get_weather,
get_events,
web_search,
send_email,
create_issue,
# 每新增一个工具,这里再加一行
]
两个地方都要改,漏一个工具就”消失”了。🫥
🌟 Hermes 的做法
工具文件本身就负责注册:
# tools/weather.py — 简化示意
# 实际 Hermes 使用 registry.register() 函数调用,handler 返回 JSON 字符串
import json
from tools.registry import registry
defcheck_requirements():
returnTrue# 检查 API Key 等环境
defget_weather(location: str) -> str:
return json.dumps({"location": location, "temperature": 22})
registry.register(
name="weather",
toolset="core",
schema={"name": "weather", "description": "Get weather", "parameters": {...}},
handler=lambda args, **kw: get_weather(args.get("location")),
check_fn=check_requirements,
)
文件放对位置、调用 register(),完事。 框架在启动时自动扫描 tools/ 目录,发现注册调用就完成了加载。注册、分发、可用性检查、错误封装——全部由 registry 处理。
工具文件本身?只写业务逻辑。✅
💡 核心思想
关注点分离:框架负责”怎么管”,开发者只写”做什么”。
这和 Django 的 apps.py、Spring 的 @Component 扫描是一个道理。好的框架让你少写”胶水代码”,把精力留给真正的业务逻辑。🎯
🎨 三、纯数据驱动的主题引擎 —— 零代码定制
Hermes 有一个 Skin/主题引擎,用 YAML 定义皮肤。从 banner 颜色到 spinner 表情到 prompt 符号,全部是数据,不是代码。
# skins/cyberpunk.yaml — 用户自定义皮肤
name:cyberpunk
description:Neon-soakedterminaltheme
colors:
banner_border:"#FF00FF"
banner_title:"#00FFFF"
banner_accent:"#FF1493"
spinner:
thinking_verbs:["jackingin","decrypting","uploading"]
wings:
-["⟨⚡","⚡⟩"]
branding:
agent_name:"Cyber Agent"
response_label:" ⚡ Cyber "
tool_prefix:"▏"
💡 以上是用户自定义皮肤的 YAML 示例(来自原文)。激活只需
/skin cyberpunk或在 config.yaml 里设display.skin: cyberpunk。
换一个皮肤?换一个 YAML 文件就行。零代码改动。 🎭
💡 核心思想
配置化 > 代码化:把可变的东西抽成数据。
如果你在设计一个系统,问自己:“这个部分将来会不会经常改?” 如果答案是”会”,那就把它抽成配置——YAML、JSON、环境变量都行。把变化关在数据层,代码层保持稳定。🔒
这个道理放之四海而皆准:
-
按钮颜色不要硬编码在 CSS 里,用 CSS 变量 🎨 -
API endpoint 不要散落在各个文件里,用配置文件集中管理 🔗 -
AI 的 system prompt 不要写死在代码里,用模板引擎 📋
⚠️ 四、Known Pitfalls —— 踩过的坑就是最好的文档
Hermes 的 AGENTS.md 里有一个非常实在的部分:Known Pitfalls(已知坑)。
不是那种空洞的”最佳实践”,而是明明白白告诉你:
## Known Pitfalls
- ❌ DO NOT hardcode `~/.hermes` paths
- Use `get_hermes_home()` instead
- Fixed in PR #247
- ❌ DO NOT use `simple_term_menu`
- Causes crashes on macOS with Python 3.12+
- Fixed in PR #312, replaced with `questionary`
- ❌ DO NOT rebuild system prompt mid-conversation
- Breaks prompt caching, costs 3x more
甚至标注了是哪个 PR 修的这个坑。📎
💡 核心思想
经验沉淀比文档模板更有价值。
很多项目的文档只告诉你”应该怎么做”,却不告诉你”千万别怎么做”。但后者往往更有价值——因为”应该怎么做”可以猜,”千万别怎么做”只有踩过坑才知道。💪
我们自己也踩过不少坑:
-
🌙 Dreaming 功能污染日志:Agent 在后台做梦时产生的日志混入了正常会话日志,排查问题的时候一头雾水 -
🤖 Main Agent 越位干活:本该交给 subagent 处理的任务,main agent 自己冲上去干了,导致职责混乱
把这些踩过的坑写进文档,比写一百页”架构规范”都有用。📖
🔒 五、Prompt Caching 保护策略 —— 明确”不能做什么”
Hermes 对 Prompt Caching 有明确的保护规定:
## Prompt Caching Rules
- DO NOT modify context mid-conversation
- DO NOT change the toolset during a session
- DO NOT rebuild the system prompt after initialization
为什么?因为 cache-breaking 会导致成本大幅上升。💰
LLM 的 prompt caching 机制依赖于 prompt 的前缀不变。如果你中途改了 system prompt、增删了工具定义、或者在上下文里插入了大段内容导致缓存失效——下一次请求就要重新计算所有 token,成本可能是原来的 3-5 倍。
💡 核心思想
规则不只是告诉你”能做什么”,更重要的是”不能做什么”。
这在我们的 AI 项目中同样适用:
-
不要在对话中途换 model(缓存失效 + context 丢失)🚫 -
不要动态增删 tool definitions(触发重新解析)🔄 -
不要在 system prompt 里塞会变化的内容(比如当前时间戳——放 user message 里)⏰
“不能做什么”的清单,往往比”能做什么”的清单更重要。 📌
🏗️ 六、多实例 Profile 系统 —— 隔离的艺术
Hermes 从一开始就设计了 Profile 系统。每个 profile 是独立的:
~/.hermes/
├── profiles/
│ ├── work/
│ │ ├── config.yaml
│ │ ├── api_keys.json
│ │ ├── memory/
│ │ └── sessions/
│ ├── personal/
│ │ ├── config.yaml
│ │ ├── api_keys.json
│ │ ├── memory/
│ │ └── sessions/
│ └── experiment/
│ ├── config.yaml
│ ├── api_keys.json
│ ├── memory/
│ └── sessions/
└── hermes.yaml # 全局配置
每个 profile 有自己的配置、API Key、记忆、会话。所有路径通过 get_hermes_home() 获取,绝不硬编码。 📁
💡 核心思想
从一开始就设计隔离,比后来补救容易得多。
很多项目开始时只有一个”默认实例”,跑得好好的。直到有一天用户说”我想同时跑两个不同的 agent”——然后发现路径全硬编码了、状态全混在一起了、配置文件互相覆盖……😵
隔离不是过度设计,是远见。 🔭
🧪 七、测试纪律 —— ~3000 个测试的安全网
Hermes 项目有大约 3000 个测试用例。
不是什么 fancy 的自动化测试框架,就是朴素的纪律:
# 每次 push 之前
pytest tests/ -v
# 测试文件使用隔离的 temp dir
# 不污染开发者工作区
# 不依赖外部服务(用 mock)
3000 个测试听起来很多,但对于一个多平台、多工具的 Agent 项目来说,这意味着:
-
✅ 改了 CLI 的命令解析?Telegram 的 handler 不会被意外破坏 -
✅ 更新了 weather 工具?email 工具的单元测试不会受影响 -
✅ 重构了 profile 系统?所有已有功能依然在测试覆盖下
💡 核心思想
测试不是 QA 的工作,是长期项目的安全网。 🛡️
对于个人项目或者小团队,不要求你也写 3000 个测试。但至少要做到:
-
改了核心逻辑,跑一遍测试再 push 🏃 -
测试用临时目录,别污染工作区 📂 -
测试要能本地跑,别依赖 CI 环境 🖥️
🎯 总结:最值得立刻借鉴的 4 点
回过头来看,从 Hermes 项目学到的架构设计中,我认为最值得立刻应用到任何 AI Agent 项目的是这 4 点:
|
|
|
|
|---|---|---|
|
|
中央命令注册表 |
|
|
|
工具自动发现 |
|
|
|
Known Pitfalls 文档 |
|
|
|
Prompt Caching 保护 |
|
好的架构设计从来不是关于用了什么花哨的技术栈。它关于:
-
把可变的东西抽成数据 📊 -
把重复的事情自动化 🤖 -
把踩过的坑记录下来 📝 -
从一开始就设计隔离 🏰
Hermes 把这些简单的事情做好了,所以它成了一个可靠的、可维护的、可生长的项目。🌱
🔗 Hermes Agent 项目:详见项目仓库(GitHub 搜索 “hermes agent”)
💬 欢迎在评论区交流——你在做 AI Agent 项目时,踩过哪些坑?又学到了哪些好的架构实践?
📤 如果你觉得这篇文章有帮助,欢迎转发给同样在折腾 AI Agent 的朋友。
夜雨聆风