我把 Hermes Agent 源码扒了个底朝天:它不是“又一个 AI Agent”,而是在认真造一套代理操作系统
我原本以为,这又会是一个“README 气势磅礴,源码一看满地胶水”的 Agent 项目。
结果越看越不对劲。
不对劲的意思不是它写得离谱,而是它居然真的在拿工程化的方式,处理 Agent 这件事里最麻烦、最脏、也最容易被糊弄过去的问题。
一、为什么我会盯上 Hermes Agent
第一次看到 Hermes Agent,吸引我的不是“AI Agent”这四个字。现在这个赛道里,叫自己 Agent 的项目已经多到快能排队领号了,问题是很多项目扒开一看,核心思路基本都是:
- 大模型接一下
- 工具挂几个
- prompt 写很长
- 然后开始祈祷
说难听点,很多所谓 Agent,最后都只是一个会调函数的聊天机器人,穿了一件“自主智能”的外套。
但 Hermes Agent 不一样。它在 README 里写的是 self-improving AI agent。我看到这句话的时候,第一反应不是“哇,好厉害”,而是:
你最好真的别吹。
因为“自我进化”这四个字太容易写,太难落地。
你真要做这件事,就绕不开一整套工程问题:
- 记忆怎么存
- 会话怎么延续
- 工具怎么扩展
- 上下文太长了怎么活
- 终端、Telegram、Discord 这些入口怎么共用一个大脑
也正因为这样,我才更想拆它。
我不是想看它“能不能跑起来”,而是想验证一件更核心的事:
Hermes Agent 到底是在做一个炫 demo,还是在认真做一个 Agent Runtime?
结论先说:
它不是完美,但它确实不是玩票。
而且这个判断不是来自 README,而是来自我顺着源码一路往下啃的时候,看到它在很多细节上明显做过取舍、踩过坑、还愿意回头收拾烂摊子。这个味道,和那种“为了演示效果先糊出来”的项目,差别很大。
顺手提一句,我在读仓库过程里还先被 Git 教育了一下。
Windows 环境下因为 ownership 问题,仓库直接给了我一个 dubious ownership 提示。虽然只是个小插曲,但特别真实。那一刻我就知道:今天不是在看 PPT,我是在进现场。
二、先别急着吹,它真正值钱的地方到底是什么
如果让我用一句话概括 Hermes Agent 的核心价值,我会这么说:
Hermes Agent 不是在做“一个会调用工具的大模型壳子”,它是在做“一个能长期运行、跨入口复用、还能自己管理上下文和状态的代理操作系统”。
这句话听起来有点大,但我不是为了抬高它,而是因为它的源码结构确实在往这个方向收敛。
它的价值不在某个单独功能,而在整个闭环:
run_agent.py负责主循环,真正把模型调用、工具调度、中断、重试、fallback 串起来model_tools.py+tools/registry.py负责工具治理,让能力增长不必持续污染主循环agent/prompt_builder.py负责系统提示词分层拼装,而不是把所有上下文搅成一锅大杂烩agent/context_compressor.py负责上下文续命,让长对话不是“超长了就自求多福”hermes_state.py+gateway/负责长期会话和多平台入口,让代理不只是活在一个本地终端里
所以我会说,Hermes 真正厉害的地方,不是“功能很多”,而是它在认真回答一个问题:
一个 Agent,如果不是只活 10 分钟,而是要长期陪你工作,它的系统底座应该长什么样?
三、我读源码的第一感受:这项目是真的大,而且不是“装出来的大”
这个仓库不算那种“clone 下来 3 分钟读完”的体量。
我一开始最直观的感受就是:这文件长度,很有压迫感。
我简单看了下核心文件的行数:
run_agent.py:9376 行cli.py:8418 行gateway/run.py:7629 行tools/mcp_tool.py:1855 行tools/terminal_tool.py:1580 行agent/context_compressor.py:649 行agent/prompt_builder.py:866 行
老实说,第一眼看到 run_agent.py 的时候,我内心弹出的不是“优雅”,而是“兄弟,你经历过什么”。
但看下去之后我发现,这种“大”不是那种纯粹失控的大。
它更像是一个系统逐渐长大后,开始把真实复杂度都吸进来的结果。
另外我数了一下,tools/ 下通过 registry.register(...) 注册的工具一共有 51 个。
这不是“给模型挂几个函数玩玩”的规模了,而是真把工具系统当成了 Agent 栈的一部分。
所以我后面的阅读策略也很明确:不先抠细节,先抓主线。
我这次实际是按这个顺序读的:
- 先看
run_agent.py,确认主循环怎么运转 - 再看
model_tools.py和tools/registry.py,弄明白工具体系怎么组织 - 再啃
prompt_builder.py和context_compressor.py,看上下文治理有没有真东西 - 最后补
hermes_state.py、cli.py、gateway/run.py,理解它怎么把单机能力做成持续会话系统
这条线一通,项目骨架就差不多立起来了。
四、整体架构:它为什么没有写成一锅“万能糊糊”
先看 Hermes 最关键的一条依赖链:
tools/registry.py
↑
tools/*.py
↑
model_tools.py
↑
run_agent.py / cli.py / gateway/run.py
如果把这条链路画成一张更像“系统视角”的图,大概是这样:
flowchart TD
R[tools/registry.py<br/>工具注册中心]
T[tools/*.py<br/>内建工具 / 终端 / 文件 / 浏览器 / MCP]
M[model_tools.py<br/>工具发现 / schema 过滤 / dispatch]
A[run_agent.py<br/>AIAgent 主循环]
C[cli.py<br/>终端交互入口]
G[gateway/run.py<br/>多平台消息入口]
S[hermes_state.py<br/>SessionDB / FTS5 / 持久会话]
P[agent/prompt_builder.py<br/>系统提示词分层拼装]
X[agent/context_compressor.py<br/>上下文压缩与续命]
R --> M
T --> R
M --> A
P --> A
X --> A
A --> S
C --> A
G --> A
G --> S
这个结构不花哨,但我觉得它非常对。
为什么?
因为它做对了一个经常被低估的决定:
工具能力不应该反向绑死主循环。
说白了就是,run_agent.py 不需要知道每个工具的实现细节。
它只需要知道:
- 现在有哪些工具
- 哪些工具当前可用
- 模型该看到哪些 schema
- 工具调用来了以后,该交给谁执行
这套职责分开之后,整个系统立刻就顺了很多。
它至少带来三个很实在的好处:
- 扩展成本低:新工具主要是注册问题,不是侵入主循环的问题
- 平台复用自然:CLI、Gateway、Batch Runner 可以共享一套工具协议
- 演化空间更大:MCP 工具、插件工具、用户自定义工具,都可以走同一条路径
我读到这里时的第一反应是:
Hermes 至少知道,Agent 工程最怕的不是功能少,而是每加一个能力,都要去主循环里打一块补丁。
很多项目最后变难维护,不是因为“功能太多”,而是因为“功能增长路径不对”。Hermes 在这个点上明显更清醒。
五、技术栈看起来不炫,但选得挺老练
Hermes 的技术栈不属于那种“我一看就想发朋友圈说好先进”的类型。
它更像是那种你用久了以后会说一句:嗯,这套选型挺懂事。
1. Python:不是保守,而是合适
这种项目需要同时处理:
- 模型 API 编排
- 本地终端与文件系统
- 多平台消息适配
- 插件和动态导入
- 测试、脚本、运维小工具
这时候 Python 的优势不是性能,而是 生态密度高、工程阻力低、拼装速度快。
而 Agent Runtime 这类系统,真正的瓶颈通常不在 CPU,更多在:
- I/O
- API 延迟
- 网络抖动
- 上下文治理
- 状态一致性
所以从这个维度看,Python 非但不是妥协,反而是非常现实的选择。
2. SQLite + FTS5:这手我很喜欢
hermes_state.py 没有走“大系统就一定要上独立数据库服务”这条路,而是直接用了 SQLite,并且开了 WAL 和 FTS5。
这两个点一组合,我立刻就觉得作者是懂 Agent 真实使用场景的:
- WAL 适合多读少写
- FTS5 适合全文检索历史消息
- SQLite 部署成本低,不需要额外运维
对 Agent 产品来说,很多时候最好的数据库不是“理论上最强”的,而是“今天就能在个人机器、轻量服务、低成本容器里稳定跑起来”的。
Hermes 在这里明显偏务实派,我很认同。
3. prompt_toolkit:CLI 被当成产品,而不是附件
cli.py 里大面积使用 prompt_toolkit,这不是随手一选。
这说明作者很清楚,CLI 对这种工具不是“调试入口”,而是核心产品形态之一。
因为一旦你真把终端当产品,就会立刻遇到一堆脏问题:
- 多行输入怎么做
- slash command 怎么补全
- spinner 怎么不把终端刷花
- 审批弹窗怎么交互
- ANSI 输出怎么和事件循环兼容
这些问题不 glamorous,但特别考验诚意。
4. MCP 被吸进主体系,而不是单独开专区
tools/mcp_tool.py 不是简单做个“支持 MCP”的广告位,而是把 MCP 工具最后统一注册进现有 registry。
这一点非常关键,因为它意味着:
MCP 能力不是外挂,而是系统能力。
这类统一性,会直接决定项目未来能不能继续长。
六、源码主线一:AIAgent 主循环,才是系统真正的心脏
我先看的就是 AIAgent。
表面上,核心循环很朴素:
while api_call_count < self.max_iterations and self.iteration_budget.remaining > 0:
...
但如果只盯着这个 while,其实很容易误会 Hermes 的复杂度。
它真实的运行节奏,更接近下面这张图:
sequenceDiagram
participant U as User
participant A as AIAgent
participant P as Prompt Builder
participant L as LLM API
participant T as Tool Layer
participant C as Context Compressor
participant D as SessionDB
U->>A: user_message
A->>P: 构建或复用 system prompt
P-->>A: stable prompt
A->>C: 预检上下文长度
C-->>A: 压缩后消息或原消息
A->>L: messages + tools + system prompt
alt 模型返回 tool_calls
L-->>A: tool_calls
A->>T: 执行工具(串行或并发)
T-->>A: tool results
A->>D: 持久化消息与状态
A->>L: 带 tool results 再次请求
else 模型直接返回答案
L-->>A: final response
end
A->>D: 写入会话 / token / system prompt
A-->>U: final response
但真正有意思的,从来不是这个 while 本身,而是这个循环周围被塞进了哪些“真实世界复杂度”。
Hermes 在 run_conversation() 这条主线上,至少处理了这些事:
- 会话级系统 prompt 缓存
- 上下文预压缩
- 工具调用并发或串行选择
- 中断与恢复
- provider / api_mode 兼容
- memory prefetch
- hook 生命周期
- token 与成本统计
- fallback model 切换
换句话说,它不是一个“模型会不会调 function call”的教学样例,而是一个已经开始带运行时责任的会话引擎。
这块里我最想展开说两个点。
1. 它把“系统 prompt 稳定性”看得非常重
在 run_conversation() 里,系统 prompt 并不是每轮都重建,而是尽量复用:
if self._cached_system_prompt is None:
...
self._cached_system_prompt = self._build_system_prompt(system_message)
这事表面是性能优化,底层其实是在保护 prompt caching。
很多 Agent 项目爱搞“每轮都动态拼出一版最新 prompt”,看着好像很灵活,实际问题很大:
- cache 命中率下降
- token 成本上升
- 系统行为更漂
Hermes 的做法非常工程化:
宁愿牺牲一点“每轮都重新算”的灵活感,也要保住缓存收益和行为稳定性。
这就是典型的“产品做久了才会有的判断”。
因为你真跑过多轮会话,才会明白:便宜、稳定、可预测,比“看上去很智能”重要得多。
2. 它不是看见并发就猛踩油门
_execute_tool_calls() 里,Hermes 不会无脑把所有工具调用扔进线程池。
它会先判断这批调用是否适合并行。
这个细节特别关键。
因为 Agent 工具系统最大的坑,不是慢,而是乱:
- 读操作并发,通常问题不大
- 写操作并发,如果路径冲突,现场立刻变案发现场
Hermes 至少在设计上已经意识到这件事,所以它处理工具并发的思路,更像数据库并发控制,而不是“线程池先开了再说”。
我看到这里的时候,心里其实挺欣慰的。
因为这说明它不是把 Agent 当聊天程序来做,而是已经开始把它当运行时系统来做。
七、源码主线二:工具系统为什么读起来这么顺
如果说 run_agent.py 是心脏,那 tools/registry.py 就是中枢神经。
它的抽象非常直接:
class ToolRegistry:
def register(...)
def get_definitions(...)
def dispatch(...)
然后 model_tools.py 负责工具发现:
def _discover_tools():
_modules = [
"tools.web_tools",
"tools.terminal_tool",
"tools.file_tools",
...
]
这个设计我会给高分,不是因为它花,而是因为它克制。
1. “导入即注册”这事,看起来土,实际上很香
很多项目一新增工具,就要改四五个地方:
- schema 一份
- 路由一份
- 权限一份
- UI 展示一份
- 文档提示一份
最后改着改着,你已经忘了自己最初只是想加一个功能。
Hermes 没有把这件事做到零成本,但它至少把最关键的协议统一了:
先注册成标准工具,再进入系统治理。
这种顺序非常重要。
因为只要工具一开始就不是系统公民,后面所有管理都会越来越乱。
2. check_fn 是我特别喜欢的小钩子
注册工具时可以带 check_fn 和 requires_env。
这个设计的好处非常大:
工具的可用性,不必等到运行时报错才知道。
它可以在暴露 schema 给模型之前,就先做一层过滤。
这件事的价值被很多人低估了。对模型来说:
- 看见但调不了,很容易产生幻觉调用
- 压根看不见,反而是更干净的状态
而 Hermes 在 get_tool_definitions() 里甚至会进一步动态修 schema,把描述里那些会误导模型的 cross-reference 去掉。这个细节非常像“踩过坑以后认真补课”的产物。
3. MCP 也走同一条注册路径,这个很关键
discover_mcp_tools() 最终也是往 registry 里塞:
registry.register(
name=tool_name_prefixed,
toolset=toolset_name,
schema=schema,
handler=...
)
这意味着对上层来说,内建工具、MCP 工具、插件工具并不是三套世界观。
这点我特别认同。
真正好的扩展,不是“再外挂一个专区”,而是让新增能力和原生能力使用同一套治理协议。
做平台最怕“每接一个新能力,就造一个新宇宙”。Hermes 在这个点上做得很稳。
八、源码主线三:Prompt Builder 不是“拼字符串”,而是在治理上下文污染
我一开始看到 _build_system_prompt() 很长的时候,是有警惕心的。
因为 prompt builder 这种东西,特别容易长着长着就变成一锅玄学大补汤。
但 Hermes 这里比我预期更理性。它至少明确把系统 prompt 分了层:
# 1. Agent identity
# 2. User / gateway system prompt
# 3. Persistent memory
# 4. Skills guidance
# 5. Context files
# 6. Current date & time
# 7. Platform-specific formatting hint
这点看似平常,其实很重要。
因为它表明作者不是在“堆内容”,而是在 做上下文分层。
1. 它知道哪些内容应该稳定,哪些内容应该临时注入
比如 ephemeral_system_prompt 就不会进缓存系统 prompt。
这背后是一个很成熟的判断:
不是所有上下文都适合变成长期系统状态。
如果你把短期指令、插件临时上下文、当前运行期噪音,全塞进 system prompt,结果只会是:
- 更贵
- 更乱
- 更难 debug
Hermes 在这个边界上至少是有意识的。
2. AGENTS.md / SOUL.md 不是直接信任加载,而是先做注入扫描
这一点让我印象特别好。
agent/prompt_builder.py 里会扫描上下文文件里的可疑内容,比如:
_CONTEXT_THREAT_PATTERNS = [
(r'ignore\\s+(previous|all|above|prior)\\s+instructions', "prompt_injection"),
...
]
这说明作者已经意识到一个很多项目都忽略的问题:
本地上下文文件,不等于天然可信。
现在大家一谈 prompt injection,注意力往往都在:
- 网页抓取内容
- 用户输入
- RAG 文档
但实际工程里,目录下的 AGENTS.md、SOUL.md、.cursorrules 一样可能成为污染源。
Hermes 这手防线,不是很炫,但非常值得学。
3. 平台提示后置注入,这个抽象也挺漂亮
CLI、Telegram、WhatsApp、Signal 的格式提示被做成 PLATFORM_HINTS。
这意味着 Hermes 不是给每个平台复制一份大脑,而是:
给同一个大脑接不同的“输出外设”。
这个抽象简单,但很优雅。
九、源码主线四:ContextCompressor 是我最想夸的一块
如果让我选 Hermes 最让我眼前一亮的模块,我大概率会投给 agent/context_compressor.py。
因为很多 Agent 项目所谓“上下文压缩”,本质上就三步:
- 超了
- 总结一下
- 继续聊
这套做法的问题是,它经常只关心“省 token”,不关心“任务还能不能接得住”。
Hermes 这里不一样。
它的设计核心,我会概括成一句话:
不是为了少花 token 才压缩,而是为了让会话别半路失忆。
它的大致流程是:
1. 先裁旧工具输出
2. 保护头部消息
3. 按 token 预算保护尾部消息
4. 结构化总结中间区段
5. 修复 tool_call / tool_result 配对
如果把这件事从“源码实现”翻译成“读者一眼能懂”的图,它其实是这样的:
flowchart LR
A[原始长会话] --> B[裁掉旧工具输出]
B --> C[保护头部消息]
C --> D[按 token 预算保护尾部]
D --> E[抽取中间区段]
E --> F[生成结构化 handoff summary]
F --> G[修复 tool_call / tool_result 配对]
G --> H[得到可继续运行的压缩会话]
我最喜欢这里面三个点。
1. 不是“保最后 N 条消息”,而是按 token 预算保尾部
这个设计非常对。
因为 Agent 场景里,一条消息和一条消息的体积差异可以大得离谱:
- 有的只是两句自然语言
- 有的却是一坨工具输出
你这时候还执着于“最后保留 20 条消息”,基本等于拿尺子量云彩。
Hermes 直接改成 按 token 预算保护尾部,这说明它已经从“消息条数思维”切到“上下文容量思维”了。
2. 它写的不是摘要,而是交接单
压缩 summary 不是一句“之前我们干了很多事,请继续”。
它要求总结保留:
- Goal
- Constraints & Preferences
- Progress
- Decisions
- Files
- Next Steps
- Tools & Patterns
这不是普通摘要,这是 handoff 文档。
而这恰恰是长会话真正需要的东西。
因为长上下文最怕的,不是丢一句聊天内容,而是丢掉:
- 当前目标
- 已做决策
- 哪些文件已经动过
- 哪些坑已经踩过
说白了,Agent 压缩最怕的不是“忘词”,而是“断片”。Hermes 明显在努力避免这个问题。
3. 它连 tool_call / tool_result 配对完整性都考虑到了
这是最让我觉得“作者真在现场打过仗”的地方。
很多人做上下文压缩,只盯语义层面,完全不管消息协议结构会不会被压坏。
Hermes 不一样,它专门有逻辑去修复 orphaned tool pairs,避免压缩后消息序列失去合法性。
这意味着它考虑问题的视角是双层的:
- 语义不能断
- 协议也不能坏
我的评价很直接:
这已经不是“会做总结”的层次了,而是在拿运行时正确性的标准做上下文压缩。
十、源码主线五:SessionDB 让我第一次觉得“记忆”不是营销词
很多 Agent 项目也喜欢讲记忆。
但一落地,常见形态通常是:
- 拼一段字符串进 prompt
- 存个 JSON 文件
- 给功能起个很玄的名字
然后你再深挖,就会发现它离“可长期使用的会话系统”还差得远。
Hermes 的长期会话能力,真正落在 hermes_state.py。
它用了 SQLite,并且开了 FTS5:
CREATE VIRTUAL TABLE IF NOT EXISTS messages_fts USING fts5(
content,
content=messages,
content_rowid=id
);
再配合:
PRAGMA journal_mode=WAL
这两个选择拼在一起,味道就出来了:
- WAL 解决多入口并发读写
- FTS5 解决跨会话检索历史消息
最关键的是,这不是“为了显得高级而加数据库”,而是和 Agent 的真实使用方式高度匹配:
- 单机部署常见
- 多轮会话重要
- 搜索历史是刚需
- 不想为了一个个人 Agent 再维护一套复杂数据库服务
我尤其喜欢 search_sessions() 前面对 FTS5 查询语法做的清洗。
像引号、括号、点号、连字符这些细节,它都尽量兜住,避免用户一个查询把全文检索打炸。
这说明作者不是只想“有搜索功能”,而是想让搜索 真的能被长期使用。
一句话总结这块:
Hermes 的记忆系统至少不是口号派,它是真的在往“会话资产管理”上走。
十一、CLI 和 Gateway:同一个大脑,别搞成两套人格分裂系统
Hermes 另一个让我好感度很高的地方,是它没有把 CLI 版和消息平台版拆成两套互相平行的系统。
这件事说起来简单,做起来其实很难。
因为一旦你同时支持:
- 终端
- Telegram
- Discord
- Slack
最容易出现的情况就是:每个入口都长一套自己的逻辑,最后谁也不服谁。
Hermes 至少在努力避免这件事。
1. CLI 不是“调试入口”,它真的是产品形态
cli.py 这么大,不是因为作者手痒,而是因为它真的在解决很多 CLI 产品级问题:
- slash command 自动补全
- 多行输入
- spinner 显示
- approve / deny 交互
- clarify 问答
- status bar 宽度控制
/skin动态换主题
尤其 KawaiiSpinner 那块我看得挺开心。
它为了兼容 prompt_toolkit 的输出机制,故意避免 \033[K,改用空格覆盖。这个细节非常小,但特别说明问题:
作者真的用过,也真的被终端渲染问题折磨过。
2. Gateway 不是 webhook 转发器,而是会话与平台的编排层
gateway/run.py 也不是那种“收消息 -> 调模型 -> 回消息”的最简管道。
它要处理的事情比想象中多得多:
- 多平台 adapter 生命周期
- 会话路由
- slash command 统一分发
- 中断与审批
- 平台掉线后的后台重连
- delivery routing
我很喜欢 Hermes 的一个基本思路:
平台只是入口差异,不应该变成能力差异。
所以它才会让 CLI 和 Gateway 共享 COMMAND_REGISTRY,尽量共用一套命令定义,再根据平台能力做门控。这个设计在长期维护里会非常省命。
十二、我在源码里最想拍大腿的几个巧思
这部分我直接说我最有共鸣的点。
1. 为了 prompt cache,敢压制“动态拼 prompt 的冲动”
很多系统会下意识追求“实时、动态、每轮都不一样”。
Hermes 反过来,尽量让系统 prompt 稳定。
这背后的判断非常成熟:
缓存命中、成本控制、行为稳定,有时候比“理论上更灵活”更值钱。
2. 上下文文件先做注入检测
这一步不是锦上添花,而是信任边界意识。
很多项目在这个点上会天然信任本地文件,Hermes 至少没有这么天真。
3. 能力很多,但尽量用同一套协议收口
工具、会话、平台、MCP、插件,这些东西最怕各说各话。
Hermes 至少在努力用统一 registry、统一 command registry、统一 session 语义把它们拉到一张桌子上。
4. Profiles 设计非常实用
HERMES_HOME + profile override 这一套,我挺喜欢。
它不是简单做个命名空间,而是把:
- 配置
- sessions
- memory
- skills
- gateway
全都隔离开。
这相当于让同一套代码,可以长出多个互不打架的代理实例。
说得幽默一点,这已经不是“多用户支持”了,这更像给 Agent 做了一个可控的“多重人格系统”,而且人格之间还不串号。
十三、它的取舍我怎么看:有些地方很值,有些地方也确实开始吃力了
没有项目能全都要,Hermes 也不例外。
1. 它明显优先扩展性,而不是极简
所以你会看到一些核心文件非常大。
这带来的代价也很直观:
- 新读者阅读门槛高
- 局部修改时心智负担不小
但反过来看,如果没有这种集中的主控层,它现在也很难托住这么多能力。
2. 它明显优先“跑得稳”,而不是“看起来最优雅”
你会在主循环里看到很多现实世界兜底逻辑:
- provider 兼容
- fallback
- hook
- usage 统计
- prompt cache 特判
- 压缩边界处理
从“洁癖式架构审美”看,这确实不够轻盈。
但从“这是一个真在跑的系统”看,我觉得这反而是务实的。
3. 它接受适度中心化,但这个账迟早要继续还
run_agent.py、cli.py、gateway/run.py 这几个文件,现在已经不是“大”,而是有点“大得有存在感”了。
这类文件就像公司里的核心同事:
- 什么都懂
- 什么都能顶
- 一离职整个团队都冒汗
所以我理解它现在为什么还没拆完,但我也非常明确地认为:
这是 Hermes 下一阶段最该继续处理的技术债。
十四、从这个项目里,我学到了哪些可复用的东西
这一段对我来说其实最值钱。
因为看开源项目最爽的,不是“哇,这个函数写得好”,而是你能从中提炼出以后自己也能用的工程判断。
1. Agent 工程的本质不是“调模型”,而是“治理上下文”
如果上下文治理做不好,再强的模型也会逐轮失真。
Hermes 给我的最大启发就是:
Prompt、Memory、Session、Compression,本质上是一套系统,不是四个功能。
2. 工具体系一定要先统一协议,再谈功能爆炸
别急着加第十个工具、第十五个工具。
先想清楚:
- 怎么注册
- 怎么检查可用性
- 怎么裁 schema
- 怎么统一错误返回
这些东西没想清楚,功能越多,系统越乱。
3. 真正可用的 Agent,一定会处理很多“不性感的脏问题”
比如:
- 终端刷新乱码
- 平台线程差异
- 查询语法清洗
- 中断恢复
- 工具调用配对一致性
这些问题不酷,也不适合做海报,但它们决定了产品能不能活。
4. 记忆不是外挂,而是会话系统的一部分
如果“记忆”只是一段文本拼进 prompt,它大概率迟早失控。
Hermes 至少已经在往结构化、可检索、可持续演进的会话资产上靠,这个方向我非常认同。
十五、如果是我继续往下做,我会先改哪几刀
喜欢归喜欢,挑刺也得挑。
1. 继续拆 run_agent.py
这是我最明确的优化建议,没有之一。
run_agent.py 现在 9376 行,它当然还能工作,但“还能工作”和“适合继续长”是两回事。
我会优先把这些能力继续外提:
- provider 适配与请求构造
- tool loop 执行器
- retry / fallback 策略
- usage / cost 统计
- hook 生命周期
原因不是为了“代码更好看”,而是为了减少后续改动的回归风险。
2. Prompt Layer 可以再结构化一点
现在 _build_system_prompt() 虽然已经有层次,但本质仍然偏字符串拼装。
如果系统继续长,我会倾向把 prompt layer 做成更显式的数据结构,这样更利于:
- 调试每层来源
- 做增量对比
- 观测缓存命中变化
3. 上下文压缩可以补更多可观测性
ContextCompressor 现在已经很不错了,但如果继续打磨,我希望看到:
- 压缩前后 token 变化持久化
- summary 质量指标
- 被压缩后最容易丢失的信息类型
因为上下文压缩这事,最怕“看起来没问题”,结果悄悄丢关键信息。
4. 继续拆大文件,但别为了拆而拆
我不主张“看到大文件就兴奋地全拆掉”。
过度抽象一样会把系统搞得谁都看不懂。
所以我的建议不是大重构,而是:
沿着现有边界,做节制、可回滚的拆分。
十六、这个项目适合谁,不适合谁
我觉得 Hermes 很适合这些场景:
- 想做 长期运行 Agent 的个人开发者或小团队
- 想要 CLI + 消息平台双入口 的场景
- 想把 工具调用、记忆、上下文压缩 放在同一个系统里统一治理的人
- 想低运维成本部署,而不是一上来就堆基础设施的人
但它不一定适合:
- 只想做一个超轻量 demo
- 团队目前无法维护相对复杂的运行时系统
- 对“极简、单文件、完全可控”有强偏好的人
如果让我打个比方:
Hermes 更像一台配置很全的越野车,不是共享单车。
你只是下楼买瓶水,它确实有点重;
但你真准备跑长途、走烂路、拉装备,它的价值一下就出来了。
十七、最后的结论:为什么我愿意把这项目认真写一篇长文
读完 Hermes Agent,我最大的感受不是“学到了几个写法”,而是更确定了一件事:
一个好的 Agent 项目,真正难的从来不是把模型接上去,而是把能力、状态、上下文、入口、边界,收拢成一套长期可演化的系统。
Hermes 让我佩服的地方,不是它没有复杂度,而是它没有假装自己没有复杂度。
它在很认真地和这些问题正面交手:
- 工具怎么统一注册和治理
- 会话怎么长期保存和检索
- 提示词怎么分层,避免污染
- 上下文太长以后怎么不断片
- 多平台入口怎么共用一套大脑
如果一定要让我用一句最有个人态度的话收尾,我会这么说:
Hermes Agent 不是那种“看 README 时热血沸腾,看源码时血压升高”的项目。恰恰相反,它是我越往下读,越能感受到开发者在认真和真实复杂度掰手腕的项目。
而这种项目,才值得花时间读,值得花时间写,也值得你在自己的工程里,认真偷师几手。