🤖HermesBlog
Hermes 实战教程 · 第4篇2026/8/9· Easy Understand Hermes Agent

实战:把 Hermes 当 Python 库用

Hermes 实战教程第4篇:Python 库。在代码里直接调用 Hermes。

实战:把 Hermes 当 Python 库用

实战:把 Hermes 当 Python 库用

嘿,朋友们!今天咱们来聊点硬核又好玩的东西——把 Hermes 当成一个库来用。别被“库”这个字吓到,其实意思就是:让你的外部程序(比如你自己的 Python 脚本、IDE、网页前端)直接驱动 Hermes 干活。

官方这次一口气给了我们三种协议,就像三把不同的钥匙,开同一扇门(AIAgent 核心)。选哪把,取决于你的“门框”长啥样。

三把钥匙,开同一扇门

协议 传输方式 适合谁 代码位置
ACP stdio 上的 JSON-RPC VS Code、Zed、JetBrains 等 IDE 插件 acp_adapter/
TUI Gateway stdio 或 WebSocket 上的 JSON-RPC 想要精细控制会话、审批、流式事件的自定义宿主 tui_gateway/server.py
API Server HTTP + SSE Open WebUI、LobeChat 等 OpenAI 兼容前端 gateway/platforms/api_server.py

核心思想:三种协议驱动的是同一个 AIAgent 核心,只是“线材”和“接口面板”不同。

钥匙一:ACP(Agent Client Protocol)

这是给 IDE 用的。VS Code、Zed 这些编辑器已经会说 ACP 这门“外语”,所以 Hermes 直接说同一种语言就行。

hermes acp              # 在 stdio 上启动 ACP 服务
hermes acp --check      # 检查依赖是否装好
hermes acp --setup      # 交互式配置提供商和模型

它支持会话创建、提示词提交、流式消息块、工具调用事件、权限请求,甚至**会话分叉(fork)**和取消。工具输出会被渲染成 IDE 能懂的 Diff/ToolCall 块。

钥匙二:TUI Gateway(最灵活)

这是 Ink TUI(hermes --tui)自己用的协议,但任何外部程序都可以通过 stdio 或 WebSocket 说同样的“方言”。

方法列表超丰富,挑几个眼熟的:

prompt.submit           session.create          session.list
session.activate        session.close           session.interrupt
config.set / get        command.dispatch        client.capabilities
gateway.capabilities    ping                    clarify.lock
terminal.resize         clipboard.paste         image.attach

注意:session.active_list、session.activate、session.close 这三个是进程内活会话控制,用于 TUI 的会话切换器。想找历史记录?用 session.list 或 /resume。

在同一个已认证网关内,恢复或激活一个活会话是再挂一个事件订阅者,而不是替换之前的连接:流式事件和终端事件会发给所有已连接的客户端,断开其中一个客户端也不会结束另一个客户端正在看的会话。原有的提交独占和忙碌输入策略照旧生效。已连接的客户端可以操纵该会话的子代理。但这并不让独立的网关进程写同一个会话,也不意味着所有者重启后提示词准入仍然持久。

建会话时就能指定模型

session.create 现在支持按会话传 model / provider 覆盖。如果这对组合提供商根本伺候不了(比如 model: gpt-5.5 配 provider: anthropic),网关会当场拒绝,报 JSON-RPC 错误码 -32602,而不是先建好会话、等第一轮才在提供商那边炸掉。错误里还会带上 model、provider 和最多五条 suggestions 供你参考。放心,这个检查是离线的,只拦 Hermes 明确知道“不属于这家”的名字,自定义端点、聚合器、以及目录还没收录的新模型都照常放行。

重写历史:prompt.submit 的“时光倒流”

这个新特性很酷——编辑/重写/重新生成本质上就是一个带“截断”的 prompt.submit。但因为是破坏性写入,网关要求你明确声明意图:

参数 含义
truncate_before_user_ordinal 从第几个用户轮次开始砍(从 0 数)。必须是整数,传布尔值会报 4004
truncate_before_row_id SQLite 行 ID,推荐用这个。和 ordinal 同时传会校验是否匹配(不匹配报 4030)
confirm_truncate 必须传!声明“我就是要重写,不是普通发送”
confirm_empty_truncate 如果砍完整个对话空了(ordinal 0),还得额外传这个
confirm_deep_truncate 如果砍掉的用户轮次超过一个(比“重新生成/编辑最后一轮”更深),先问过用户再传这个。本版本对未确认的深度截断只记日志、照常执行,老客户端不受影响;下个版本会直接拒绝,报 4033(error.data 带 archived_messages 和 archived_user_turns,且不写入任何东西)

重要:截断提交不会被“忙碌输入策略”吞掉。如果当前还有一轮在跑,普通的 prompt.submit 会被转向、改道或排队,但重写/编辑/重新生成会直接报 4009(session busy)——因为排队会把“砍历史”这个动作丢掉,变成在未编辑的轮次后面追加一条普通消息。宿主应该调 session.interrupt 然后重试同一个提交,直到成功。桌面版 App 已经自动这么做了,所以 Hermes 还在思考时你编辑消息,它会先停掉当前轮次,再从编辑后的提示词重新跑。

还有:截断后,prompt.submit 结果会带 survivor_user_row_ids——这是幸存轮次的新行 ID 列表。因为重写会重新插入保留的前缀,所以你之前缓存的所有 row ID 都作废了!记得从这个列表重新绑定,否则下次重写会报 4018。

另外,如果一次截断会归档超过一个用户轮次(比“重新生成/编辑最后一轮”更深),先问过用户再传 confirm_deep_truncate。本版本对未确认的深度截断只记日志、照常执行,老客户端不受影响;下个版本会直接拒绝,报 4033(error.data 带 archived_messages 和 archived_user_turns,且不写入任何东西)。

钥匙三:API Server(HTTP + SSE)

这个最“大众脸”——OpenAI 兼容的 HTTP 接口。Open WebUI、LobeChat、LibreChat 这些前端直接就能接,任何语言的 Web 客户端都能用。

浏览器扩展的控制器协议已被移除;想让 Hermes 驱动你已经在跑的浏览器,请改用 browser.cdp_url 或 /browser connect。

运行管理接口里还有个 POST /v1/runs/{id}/resolve-unknown,用来确认一次“重启后状态未知”的准入(admission)。它不是通用的状态修改:要原样传运行状态里给出的 admission_id 和整数 execution_generation,服务端会先绑定认证所有者、所选 profile、路径运行、API 主体、目标会话、准入和代次,再做 compare-and-set。确认后会把丢失的队头结算为 interrupted 并释放下一个 FIFO 项,不会把队头重新入队。用 features.run_unknown_resolution 做特性探测,因为旧执行模式不提供这个端点。

新的规范化 API 准入会在准入行里保留一个不透明的所有者作用域,因此只要该规范化准入仍然可用,适配器重启后带 key 和不带 key 的运行都能被授权。这不改变不带 key 请求的语义:相同的提交仍会创建不同的运行。升级前的不带 key 准入不含所有者作用域,重启后仍刻意不可解析——没有安全的凭据所有权可供回填。已有的带 key 记录保留其 replay-ledger 恢复路径。显式退役会话会移除规范化载荷,从而结束这条所有者恢复路径。重放预留和规范化准入仍使用各自独立的数据库,所以这个端点不提供全局的 exactly-once 保证。

事件流

不管哪把钥匙,事件都长差不多:message.delta、tool.start、gateway.ready、request.cancel…… 注意,审批、澄清、sudo/secret 这些“要你回答的问题”其实是网关发给客户端的 JSON-RPC 请求,不是事件。帧里带一个字符串 id,客户端用同样 id 的普通 JSON-RPC 响应来回答就行。

各种请求该怎么回?一张表说清

不同方法的“答案格式”不一样,别答错:

方法 响应格式
approval {choice}
clarify {answers},按问题 id 做键;传 {} 表示取消。clarify.lock 可以提前锁定某个答案,null 则跳过该问题
sudo、secret、vault.code、vault.unlock_prompt {value}
connection {settled_by, targets}(对应 manage_connections 卡片,每个 target 一个结果)
terminal.read、window.read、preview.act、tour {value}(JSON 文本)

还有个坑要避开:如果你这个宿主根本没实现某个方法,请直接回一个 JSON-RPC 错误 -32601。这样智能体会快速失败,而不是傻等到超时——对双方都省事。

WebSocket 客户端注意了:连接建立、收到 gateway.ready 之后,记得调一次 client.capabilities 并传 {"server_requests": true},声明“我能回答这些请求”。不调的话,网关会把你当成不支持服务端请求的老版本,所有审批/澄清/sudo 请求都会立刻失败(审批算撤回,不算拒绝),而不是傻等超时。stdio TUI、桌面版和 dashboard 都通过共享的 JsonRpcRequestChannel 自动声明了。

网关撤回一个问题时(超时、中断、或在别处已回答),会发 request.cancel { id, method, reason },外部宿主只清除匹配的那个待处理提示,别误伤。session.resume / session.activate 的结果里还会带 open_requests,重连的客户端可以重新渲染并继续回答。

小结

三种协议,一个核心。选 ACP 给 IDE,选 TUI Gateway 要精细控制,选 API Server 图省事兼容。从今天起,Hermes 不只是命令行工具,更是你程序里的一个“智能引擎”。动手试试吧!

📖 官方文档

本文根据 Hermes Agent 官方文档编写,原文见:官方文档 › developer-guide/programmatic-integration