Skip to content

Instantly share code, notes, and snippets.

Show Gist options
  • Select an option

  • Save knight42/3b3883b0db74e7a9a1ac8f80ac5485f9 to your computer and use it in GitHub Desktop.

Select an option

Save knight42/3b3883b0db74e7a9a1ac8f80ac5485f9 to your computer and use it in GitHub Desktop.
Hermes Agent v2026.4.8 architecture analysis in Chinese

Hermes Agent v2026.4.8 架构分析:从主循环到 OpenAI / Codex 接入

0. 分析范围

本文分析的是 Hermes Agent 最近一个 release:v2026.4.8(发布于 2026-04-08)。

我会先讲整体架构,再重点展开 OpenAI 相关处理:

  1. 怎么把 OpenAI / Codex 接进来
  2. 登录流程怎么跑
  3. refresh token 怎么转
  4. prompt 层做了哪些“专门为 GPT / Codex 调过”的处理
  5. 这些设计为什么合理,以及它们的 trade-off

1. 一句话总结这版 Hermes Agent 的架构

Hermes Agent 本质上是一个“统一 OpenAI 风格接口 + 多 provider 适配层 + 工具执行循环 + 多入口 UI”的 agent runtime。

它不是把每个 provider 当成完全独立系统来写,而是做了三层抽象:

  • 上层:CLI / Gateway / Cron / ACP 等入口
  • 中层:AIAgent 驱动的统一对话与工具循环
  • 下层:provider/runtime/auth adapter,把 OpenAI、OpenRouter、Anthropic、Nous、Codex、Copilot 等都压到相对统一的调用模型上

这也是它能同时支持:

  • CLI
  • Telegram / Discord / Slack / Signal / Matrix 等 gateway
  • Cron job
  • Subagent delegation
  • MCP / terminal / browser / file / memory 等工具

的关键原因:上层入口不同,但中层 agent loop 基本是同一个。


2. 总体架构图

+-------------------------------------------------------------+
|                        User Interfaces                       |
|-------------------------------------------------------------|
| CLI (cli.py) | Gateway (gateway/run.py) | Cron | ACP | API  |
+-----------------------------+-------------------------------+
                              |
                              v
+-------------------------------------------------------------+
|                      AIAgent (run_agent.py)                  |
|-------------------------------------------------------------|
| - build system prompt                                        |
| - manage conversation state                                  |
| - choose api_mode / provider                                 |
| - send request to model                                      |
| - parse tool calls / responses                               |
| - retry / recovery / fallback                                |
+-----------------------------+-------------------------------+
                              |
                              v
+-------------------------------------------------------------+
|                  Provider / Runtime Resolution               |
|-------------------------------------------------------------|
| hermes_cli/runtime_provider.py                               |
| hermes_cli/providers.py                                      |
| hermes_cli/auth.py                                           |
| agent/auxiliary_client.py                                    |
+-------------------+-------------------+---------------------+
                    |                   |                     |
                    v                   v                     v
           +----------------+  +----------------+   +------------------+
           | OpenRouter     |  | Anthropic      |   | OpenAI Codex     |
           | chat API       |  | Messages API   |   | Responses-style  |
           +----------------+  +----------------+   +------------------+
                    |                   |                     |
                    +---------+---------+---------------------+
                              |
                              v
+-------------------------------------------------------------+
|                      Tool Runtime Layer                      |
|-------------------------------------------------------------|
| terminal | file tools | browser | execute_code | delegate    |
| memory   | session_search | skill tools | cron | MCP         |
+-------------------------------------------------------------+

如果再压缩成一句话:

Hermes 的核心不是“某个模型”,而是 AIAgent + provider adapter + tool loop 这三个东西的组合。


3. 核心执行路径:Hermes 是怎么跑起来的

3.1 入口层:CLI / Gateway 只是“壳”

CLI、Telegram、Discord 这些入口的职责,主要是:

  • 收用户输入
  • 做平台特定交互(例如 Telegram 命令、Slack thread、approval button)
  • 准备当前 session 的 provider / model / callbacks
  • 把任务交给 AIAgent

在代码结构上,这个分层非常明显:

  • CLI 主 orchestrator:cli.py
  • Gateway 主入口:gateway/run.py
  • 真实 agent loop:run_agent.py

所以 Hermes 的“产品表面”很多,但“内核”相对集中。

3.2 中枢:AIAgent

AIAgent 的初始化里就已经把几个关键决策做掉了:

  • 当前模型是什么
  • 当前 provider 是什么
  • 当前 base URL 是什么
  • 当前 API 模式是:
    • chat_completions
    • codex_responses
    • anthropic_messages

关键代码:

这里最值得注意的点是:

  1. 如果 provider 明确是 openai-codex,直接走 codex_responses
  2. 如果 base URL 看起来像 chatgpt.com/backend-api/codex,也会自动判成 codex_responses
  3. 更激进的一点:如果是“direct OpenAI URL”,即使原本是 chat_completions,也会被切到 codex_responses

这段逻辑说明 Hermes 在 v2026.4.8 的判断是:

“对于 OpenAI 新一代 GPT / Codex,工具型 agent 默认更适合 Responses 路线,而不是老式 chat completions。”

这不是表面兼容,而是产品层面的立场。

3.3 统一消息抽象,边界再做 provider-specific 变换

Hermes 的内部消息表示尽量统一。真正到 API 边界时,再做 provider-specific 转换。

最典型的是 system role -> developer role 的延迟转换:

  • 内部一直保留 system
  • 只有在 _build_api_kwargs() 里,如果模型命中 DEVELOPER_ROLE_MODELS(例如 gpt-5 / codex),才把第一条 system message 改成 developer

关键代码:

这个设计很干净:

  • 内部状态不被 provider 污染
  • 外部请求最大化贴合 provider 最佳实践

4. Prompt 架构:Hermes 不是只拼一个 system prompt

Hermes 的 prompt 不是单纯一块字符串,而是多个来源拼接:

  • 默认 agent identity
  • memory / user profile block
  • skill system prompt
  • context files(SOUL.md / AGENTS.md / 其他上下文)
  • model-specific guidance
  • API-call-time 的 ephemeral system prompt

关键 system prompt 组装逻辑在:

真正发请求前,又会额外做一次 API-call-time 拼接:

  • ephemeral_system_prompt 只在这次请求里拼上
  • 再插入 prefill messages
  • 再按 provider 特性做缓存或清洗

关键代码:

我觉得这是 Hermes 很成熟的一点:

它明确区分了两类 prompt:

  1. 持久 prompt(应该进入 session / cache / trajectory)
  2. 临时 prompt(只影响当前 API call,不污染长期状态)

这对 agent 系统非常重要。否则很多临时策略会污染后续回合,最后 prompt 越堆越脏。


5. OpenAI 处理的总思路:不是“接 OpenAI API”,而是“把 OpenAI 当一条运行时路线”

这一版里,OpenAI 相关处理分成两类:

5.1 普通 OpenAI-compatible endpoint

比如:

  • 自定义 OPENAI_BASE_URL
  • 一些 OpenAI-compatible 服务
  • 某些聚合器的 OpenAI 风格接口

它们会走统一的 OpenAI client 路线。

5.2 OpenAI Codex / ChatGPT OAuth 路线

这条路线不只是一个 API key,而是一整套:

  • provider overlay
  • OAuth 登录
  • access token / refresh token 管理
  • runtime credential resolution
  • Responses API / Codex backend 适配
  • 模型列表发现

Hermes 专门把它抽成了 openai-codex provider。

关键 provider overlay:

这里明确写了:

  • transport = codex_responses
  • auth_type = oauth_external
  • base_url_override = https://chatgpt.com/backend-api/codex

也就是说,在 Hermes 看来,OpenAI Codex 不是普通 openai_chat,而是一个 transport 不同、auth 也不同的 provider。

这点很关键。


6. OpenAI / Codex 是怎么接入运行时的

6.1 Provider 解析层

当用户选中 openai-codex,runtime provider 会把它解析成:

  • provider = openai-codex
  • api_mode = codex_responses
  • base_url = https://chatgpt.com/backend-api/codex(或 override)
  • api_key = 通过 resolve_codex_runtime_credentials() 取到的 access token

关键代码:

这一步的意义是:

上层根本不用知道 “OpenAI Codex token 怎么来的”。 它只需要拿到一组已经可以跑的 runtime credentials。

6.2 AIAgent 初始化时接 provider router

如果没有显式传 API key / base_url,AIAgent 会走统一 provider router:

  • resolve_provider_client(self.provider or "auto", model=self.model, raw_codex=True)

关键代码:

这段值得注意的地方有两个:

  1. raw_codex=True

    • 表示主 agent loop 需要直接接触 Codex Responses 风格 client
    • 不是只拿一个抽象过的 chat client
  2. 如果用户明确选了某个非 OpenRouter provider,但没凭证,Hermes 会 fail fast

    • 不会偷偷回退到 OpenRouter
    • 这避免“用户明明以为自己在用 Codex,实际上却跑到别家 provider 去了”

这类 fail-fast 行为在 agent 产品里很重要,因为 provider 切错,语义、费用、工具行为都可能变。


7. OpenAI 登录流程:这版 Hermes 的重点不是 API key,而是 Codex OAuth

这是你特别关心的部分。

Hermes 在 v2026.4.8 对 OpenAI 的“官方”接入重点,并不是传统 OPENAI_API_KEY,而是 openai-codex 这条 OAuth 流程。

7.1 登录入口

用户走 hermes modelhermes auth 时,如果选了 OpenAI Codex,会进入 _login_openai_codex()

关键代码:

7.2 登录流程分三条支路

_login_openai_codex() 不是无脑重新登录,它先做三层检查:

  1. 先看 Hermes 自己 auth store 里有没有可用凭证
  2. 再看 ~/.codex/auth.json 里有没有 Codex CLI 的凭证可导入
  3. 都没有,再跑 fresh device code flow

这是很实用的工程设计,因为它同时兼顾:

  • 首次登录用户
  • 已登录过 Hermes 的用户
  • 先用过 Codex CLI,后来想接 Hermes 的用户

7.3 Device Code 流程怎么跑

真正的 OAuth 设备码流程在 _codex_device_code_login()

关键代码:

它的步骤很标准:

Step 1. 申请 device code

向:

  • https://auth.openai.com/api/accounts/deviceauth/usercode

发请求,拿:

  • user_code
  • device_auth_id
  • interval

代码位置: https://github.com/NousResearch/hermes-agent/blob/86960cdbb0148145890e2ee90b4e157fa899f6e1/hermes_cli/auth.py#L2538-L2567

Step 2. 引导用户网页授权

终端里提示用户去:

  • https://auth.openai.com/codex/device

输入 user_code

代码位置: https://github.com/NousResearch/hermes-agent/blob/86960cdbb0148145890e2ee90b4e157fa899f6e1/hermes_cli/auth.py#L2569-L2575

Step 3. 轮询拿 authorization code

向:

  • https://auth.openai.com/api/accounts/deviceauth/token

轮询,直到拿到:

  • authorization_code
  • code_verifier

代码位置: https://github.com/NousResearch/hermes-agent/blob/86960cdbb0148145890e2ee90b4e157fa899f6e1/hermes_cli/auth.py#L2577-L2621

Step 4. 用 authorization code 换 token

最后向 CODEX_OAUTH_TOKEN_URL 做 authorization code exchange,拿到:

  • access_token
  • refresh_token

代码位置: https://github.com/NousResearch/hermes-agent/blob/86960cdbb0148145890e2ee90b4e157fa899f6e1/hermes_cli/auth.py#L2623-L2665

7.4 一个很重要的设计:Hermes 不再把自己和 ~/.codex 绑死

这一版有个非常明确的设计取舍:

Hermes 会读取 / 导入 ~/.codex/auth.json,但不会把它当长期唯一真相源。

它会把凭证保存进 Hermes 自己的 auth store。

代码里写得很直白:

  • tokens stored in ~/.hermes/auth.json
  • 不再直接写回 ~/.codex/

关键代码:

这是对的,因为 Codex CLI / VS Code 插件 / Hermes 如果共用一个 refresh token,会互相踩。


8. Refresh token 流程:这是 OpenAI 接入里最容易出坑,也最值得这版写的地方

这一块其实是 v2026.4.8 对 OpenAI 处理最“像产品工程”的部分。

8.1 纯刷新函数:refresh_codex_oauth_pure

核心刷新函数在:

它做的事很直接:

  • refresh_tokenCODEX_OAUTH_TOKEN_URL
  • grant_type=refresh_token
  • 带上 client_id
  • 成功后拿新的 access_token
  • 如果返回了新的 refresh_token,就旋转过去

这里有两个关键点:

  1. 函数名叫 pure

    • 意味着它本身不直接改全局 auth state
    • 只是返回刷新结果
    • 真正落盘由外层控制
  2. 它显式处理 refresh_token_reused

    • 这是 Codex / ChatGPT OAuth 场景里很现实的问题
    • 如果另一个客户端消费掉了 refresh token,这里会直接报需要重新登录

对应代码: https://github.com/NousResearch/hermes-agent/blob/86960cdbb0148145890e2ee90b4e157fa899f6e1/hermes_cli/auth.py#L1074-L1081

8.2 运行时凭证解析时自动刷新

resolve_codex_runtime_credentials() 会在 access token 即将过期时自动刷新。

关键代码:

这里的流程是:

  1. 先读 Hermes 自己 auth store
  2. 如果完全没有,再尝试从 ~/.codex/auth.json 迁移
  3. 检查 access token 是否快过期
  4. 如果快过期,在锁里重读并刷新
  5. 返回 runtime 可用的:
    • provider
    • base_url
    • api_key(其实是 access token)
    • last_refresh

这里的锁也很关键:

它不是简单刷新,而是“在锁里重读再刷新”,避免多个 Hermes 进程并发刷新同一份 token。

8.3 Credential pool 还做了二次同步

如果启用了 credential pool,Hermes 还会处理一个更麻烦的现实问题:

“另一个客户端刷新了 token,导致当前 pool 里的 refresh token 过期。”

为此它加了 _sync_codex_entry_from_cli()

  • 比较 pool entry 里的 refresh token 和 ~/.codex/auth.json 的 refresh token
  • 不一致就把最新那对 token 同步回来

关键代码:

同时 _refresh_entry()openai-codex 分支也会用 refresh_codex_oauth_pure() 刷新并旋转 refresh token:

这个设计很实用:

Hermes 已经接受了一个现实——OpenAI Codex OAuth token 并不是“单机单进程永远自洽”的,外部客户端会参与状态变化。

所以它没有假设“自己的 token store 永远是唯一真相”,而是加了同步与迁移逻辑。

这是成熟系统才会做的事。

8.4 真到请求阶段,401 还会再救一次

即使前面都做了刷新,真正发请求时如果拿到 401,Hermes 还是会再尝试一次 Codex auth refresh,然后重试请求。

关键代码:

这说明它把 token 失效处理成三层防线:

  1. 进入 runtime 前预刷新
  2. credential pool 内刷新 / 同步
  3. 请求 401 后最后一次即时刷新

这套链路挺扎实。


9. OpenAI 模型发现:不是写死,而是“API 拉取 + 本地缓存 + 前向兼容”

如果用户进入 OpenAI Codex 的 model selection,Hermes 会:

  1. 确保已经登录
  2. 拿到当前可用 token
  3. 通过 get_codex_model_ids() 获取模型列表

关键代码:

模型拉取逻辑在:

它会请求:

  • https://chatgpt.com/backend-api/codex/models?client_version=1.0.0

然后过滤:

  • supported_in_api == False 的模型
  • visibility 为 hidden 的模型
  • 再按 priority 排序

更有意思的是,它还会加一层“synthetic forward-compat models”,例如:

  • 如果旧模板模型存在,也能把某些新 slug 补出来

对应代码: https://github.com/NousResearch/hermes-agent/blob/86960cdbb0148145890e2ee90b4e157fa899f6e1/hermes_cli/codex_models.py#L23-L52

这说明作者不想把模型目录完全交给服务端即时返回,而是希望 UI 在模型升级过渡期也尽量稳。


10. Prompt 对 OpenAI / GPT / Codex 做了什么“特殊处理”

这是另一个重点。

我觉得 v2026.4.8 的一个明显特征是:

它不再把 GPT / Codex 当作“和别的模型一样只是换个 provider”,而是专门给它们写了行为矫正 prompt。

10.1 专门的 GPT / Codex 执行纪律 prompt

agent/prompt_builder.py 里有一个很显眼的常量:

  • OPENAI_MODEL_EXECUTION_GUIDANCE

关键代码:

这个 guidance 不是泛泛而谈,而是明显针对 GPT / Codex 常见 failure mode:

  • 工具调用半途而废
  • 缺前置检查
  • 不验证结果就宣称完成
  • 缺上下文时靠猜
  • 本来该调用工具却直接用语言糊过去

它里面甚至把规则写成半结构化块:

  • <tool_persistence>
  • <mandatory_tool_use>
  • <act_dont_ask>
  • <prerequisite_checks>
  • <verification>
  • <missing_context>

这其实很像“给模型上操作系统纪律”。

10.2 为什么这一招有效

原因很简单:

GPT / Codex 类模型往往不是不会用工具,而是:

  • 容易过早收敛
  • 容易把“计划”说成“已完成”
  • 容易在信息缺失时做语言补完

Hermes 这里的做法,不是改 decoder,而是把 agent 成功所需的执行纪律直接前置进 prompt。

这是一种很现实、也很有效的工程策略。

10.3 system -> developer role

前面提过,这版对 GPT-5 / Codex 还专门做了 developer role 转换。

关键代码:

这是一个很细,但很值钱的兼容层:

内部依然统一用 system role,只有对 OpenAI 新模型出站时才变成 developer role。

这避免 Hermes 内部逻辑到处都要分叉。

10.4 Prompt cache 相关处理

严格说,prompt caching 的主优化对象在这版更多是 Claude / Anthropic 路线,而不是 OpenAI。

但对 OpenAI 一样有两个重要处理:

  1. ephemeral_system_prompt 不进入持久 system prompt
  2. plugin / 外部 recall 等动态信息尽量不污染稳定 cache prefix

关键代码:

这对 OpenAI 的实际意义是:

即使没有 Anthropic 式 cache_control,Hermes 也在努力保持 prompt 结构稳定,减少“临时上下文把主指令结构搞脏”的问题。

10.5 tool_call 字段清洗

Codex Responses 路线和普通 chat completion 路线在 tool call 字段上并不完全一样。

Hermes 会保留一些 Codex 专用字段(如 call_id, response_item_id)用于内部兼容,但对严格 provider 又会清洗掉它们。

关键代码:

这再次体现了 Hermes 的总体策略:

内部表示 richer,出站 payload provider-specific。


11. OpenAI 路线为什么默认偏向 Responses / Codex transport

我觉得这是这一版最值得强调的架构判断之一。

AIAgent.__init__() 里,Hermes 明确写了:

  • Direct OpenAI sessions use the Responses API path
  • GPT-5.x tool calls with reasoning are rejected on /v1/chat/completions
  • Hermes is a tool-using client by default

关键代码:

这句话其实已经说明了他们的产品判断:

Hermes 不是一个“纯聊天壳”,而是一个默认 heavily tool-using 的 agent。

既然如此,就应该优先走更适合 reasoning + tool use 的 transport,而不是为了 API 兼容保守地死守 chat completions。

这是对 agent 产品定位非常清楚的体现。


12. 我对这版 OpenAI 架构的评价

12.1 优点

优点一:把 OpenAI 当“运行时系统”而不是“一个 API key”

很多项目接 OpenAI,只做:

  • base_url
  • api_key
  • chat.completions.create()

Hermes 这版显然已经超过这个层次。它考虑的是:

  • provider overlay
  • runtime credential resolution
  • OAuth 生命周期
  • token rotation
  • model catalog discovery
  • tool/reasoning transport 匹配
  • prompt 行为矫正

这是 agent 产品应有的深度。

优点二:显式处理 refresh token reuse / rotation

这点非常关键。

只要系统和 Codex CLI / VS Code / 其他 profile 共存,就迟早会撞上 refresh token rotation 问题。

Hermes 没回避这个问题,而是:

  • 单独 auth store
  • import 但不共用
  • refresh 失败时给出明确错误
  • pool 里再同步一次

这套设计说明作者确实踩过坑。

优点三:prompt 对 GPT / Codex 做了定向优化

很多 agent 框架的毛病是“同一套 system prompt 喂所有模型”。

Hermes v2026.4.8 明显不这么想。

它承认不同模型家族有不同 failure mode,于是:

  • Google 系模型加一套
  • GPT / Codex 加一套
  • developer role 再额外适配

这是更务实的路线。

12.2 代价 / trade-off

代价一:复杂度明显上升

一旦你支持:

  • OpenAI-compatible endpoint
  • ChatGPT / Codex OAuth
  • direct OpenAI URL -> Responses 自动切换
  • credential pool + token sync

代码复杂度肯定上来。

Hermes 的做法是把复杂度主要集中在:

  • hermes_cli/auth.py
  • hermes_cli/runtime_provider.py
  • run_agent.py
  • agent/auxiliary_client.py

这算是“集中复杂”,还可以接受。

代价二:OpenAI 相关逻辑已经不是一处

如果以后继续扩张,OpenAI 相关逻辑会分散在:

  • auth
  • provider overlay
  • runtime resolution
  • auxiliary client
  • main agent loop
  • prompt builder

这对维护者是个挑战。

如果我来继续演进,我会考虑再抽一层更明确的 openai_runtime.py / codex_runtime.py,把现在散落的 Codex transport 规则再聚一聚。

但以 v2026.4.8 的状态来说,它已经比很多 agent 项目整洁了。


13. 最后总结

如果只看最近这个 release,我对 Hermes Agent 的判断是:

它已经不是一个“把 LLM API 套在工具调用上”的简单 agent 了,而是在往“多 provider、多 auth 形态、多 transport 的统一 agent runtime”演化。

而 OpenAI 这条线,恰好最能体现它的架构成熟度。

因为在 OpenAI 这里,Hermes 不只是做了:

  • 接 model
  • 发请求

而是把完整链路都打通了:

  • provider overlay
  • runtime credential resolution
  • device code login
  • auth store 隔离
  • refresh token rotation
  • 401 后再刷新
  • Codex model discovery
  • GPT / Codex prompt discipline
  • developer role 边界转换
  • Responses transport 默认化

如果你要我用一句中文概括它对 OpenAI 的处理:

“这版 Hermes 不是把 OpenAI 当成一个 endpoint,而是把它当成一个有自己认证生命周期、传输语义和行为偏差的 agent substrate 来认真适配。”

这是我觉得最值钱的地方。


14. 关键参考链接索引

Release

架构主干

OpenAI / Codex provider 接入

登录 / token / refresh

Prompt 特化

模型发现

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment