ARTICLE · INTELLIGENCE

战地情报 · 详情页

来自尧图项目组的一线实战观察与深度解析

JetBrains Air × Hermes Agent:ACP 连接配置与 Provider 过滤实战(TaoToken 统一 Key 接入)

JetBrains Air × Hermes Agent:ACP 连接配置与 Provider 过滤实战(TaoToken 统一 Key 接入)

1. JetBrains Air 接入 Hermes Agent 的真实场景与 ACP 是什么

如果你正在用 JetBrains Air 这类新一代 AI IDE,又想把自己已经配好的 Hermes Agent 直接塞进编辑器里当助手,那 ACP 就是那条必经之路。ACP 全称 Agent Client Protocol,是一个开放协议,让编辑器(客户端)和外部 AI Agent(服务端)用标准化的方式通信。传输层走 stdio,消息格式是 JSON-RPC 2.0,握手流程是 initialize → agentInfo → notifications/initialized → session/new。听起来像天书,其实你可以把它理解成"编辑器和 Agent 之间约定好的对讲机频道":Air 负责喊话,Hermes Agent 负责应答,双方按固定格式交换 JSON。

我这次的目标很明确:在 JetBrains Air 里把 Hermes Agent 接进来,并且只显示方舟 kl provider 的模型,不要出现 Anthropic、Copilot、MOA 这些无关的 provider。整个过程踩了两个大坑,一个是 acp.json 的路径不对,另一个是 Provider 过滤配置写成了字符串而不是列表。下面把完整链路拆开讲,每一步都能直接复制。

先说清楚适合谁看:你已经在本地跑通了 Hermes Agent(hermes-acp --version能出版本号),用的是 macOS,JetBrains Air 已经装好,并且你希望把 endpoint 和 Key 统一指向 TaoToken 的 API 通道,而不是散落在各个 provider 的原始地址里。如果你还没装 Hermes Agent,建议先把 Agent 本体跑起来再回来看这篇。

ACP 的核心价值在于解耦。以前每个编辑器接一个 Agent 都要写一套适配代码,现在只要 Agent 实现了 ACP 适配器,任何支持 ACP 的编辑器都能直接连。Hermes Agent 内置了hermes-acp这个适配器,不需要额外安装。Air 这边则通过一个叫fleet.plugins.ai.agent.genericacp的插件来发现和启动 ACP agent。两边对上暗号,就能在编辑器里直接调用 Agent 的模型选择、会话管理、图片输入等能力。

这里有个容易忽略的点:ACP 的握手是双向确认的。客户端发 initialize 时带上 protocolVersion 和 clientCapabilities,服务端返回 agentInfo、agentCapabilities 和 authMethods。只有 authMethods 里列出的认证方式被客户端识别,后续的 session/new 才能建立。所以如果你发现连上了但建不了会话,八成是 authMethods 没对上。

2. TaoToken 前置准备:统一 Key 与 endpoint 的接入思路

在动手改 acp.json 之前,先把 TaoToken 这一层理清楚。TaoToken 在这里扮演的是统一 API 通道的角色:你不需要为每个 provider 单独维护一套 Key 和 endpoint,而是把请求都指向https://taotoken.net/api,由它来路由。官网入口是https://taotoken.net/?utm_source=taotoken_aicg_blog_end,注册和拿 Key 都在这里完成。

具体操作分三步。第一步,登录后在控制台创建 API Key,路径是 console 页面下的 api-keys 管理。第二步,把 Key 写进 Hermes 的环境变量文件~/.hermes/.env,字段名用ARK_API_KEY(如果你沿用方舟的命名习惯)或者自定义一个,只要和 config.yaml 里的key_env对上即可。第三步,把 config.yaml 里 custom_providers 的base_url指向 TaoToken 的 API 通道。

这里要强调一个原则:endpoint 和 Key 要成对出现。你不能 base_url 指向 TaoToken,Key 却还是原来 provider 的 Key,那样鉴权一定失败。反过来也一样。统一指向 TaoToken 的好处是,后面你想换模型、换 provider,只改 config.yaml 里的 model 字段,不用动 acp.json,也不用重新在 Air 里配置。

如果你还没决定用哪种接入方式,可以先在模型对话页面验证一下 Key 是否可用,确认能正常返回再往下走。对于长期编码和 Agent 场景,Coding Plan 会更合适,因为它针对多轮会话和工具调用做了优化。接入文档里有完整的字段说明,遇到不确定的参数先去查文档,比在日志里猜要快得多。

需要提醒的是,TaoToken 是合规的 API 聚合通道,不是所谓的"中转"。它的作用是帮你把多个 provider 的调用统一到一个入口,减少配置分散带来的维护成本。你在 config.yaml 里写的 base_url 就是它的标准 API 地址,不要加任何额外路径后缀,除非文档明确说明。

准备好 Key 之后,先别急着改 Air 的配置。建议先用命令行验证一次 Hermes Agent 本身能不能通过 TaoToken 拿到模型列表。这一步能排除掉大部分"到底是 Agent 的问题还是编辑器的问题"的纠结。验证命令后面会给出。

3. 可复制配置:acp.json 与 config.yaml 完整片段

这一节是全文的核心,所有配置都能直接复制。先给 Air 的 ACP 配置文件,路径是~/Library/Application Support/JetBrains/Air/acp.json。注意不是acp-agents/installed.json,那个路径是错的,我一开始就是栽在这里。

{ "agent_servers": { "hermes-agent": { "command": "/Users/你的用户名/.hermes/hermes-agent/venv/bin/hermes-acp", "args": [], "env": {} } } }

字段说明:command是 hermes-acp 可执行文件的绝对路径,用which hermes-acp能查到;args是启动参数,一般留空;env是环境变量,如果你不想用.env文件,也可以在这里直接写 Key,但推荐用.env统一管理。type字段是可选的,不写也能识别。

然后是 Hermes 的主配置~/.hermes/config.yaml,重点是 custom_providers 和 model_catalog 两块:

model: default: ark-code-latest api_mode: codex_responses provider: kl context_length: 128000 custom_providers: - name: kl base_url: https://taotoken.net/api api_mode: codex_responses model: ark-code-latest key_env: ARK_API_KEY discover_models: false models: auto: name: "Auto - 智能调度模型" doubao-seed-evolving: name: "Doubao-Seed-Evolving (1M ctx, Coding/Agent)" doubao-seed-2.1-turbo: name: "Doubao-Seed-2.1-turbo (Coding/Agent/多形态)" doubao-seed-2.0-lite: name: "Doubao-Seed-2.0-lite (通用生产级, thinking)" doubao-seed-2.0-mini: name: "Doubao-Seed-2.0-mini (低时延, thinking)" deepseek-v4-flash: name: "DeepSeek-V4-Flash (Agent增强)" kimi-k3: name: "Kimi-K3 (1M ctx, 原生视觉理解)" glm-5.2: name: "GLM-5.2 (1M ctx, 智谱旗舰) 当前" kimi-k2.7-code: name: "Kimi-K2.7-Code (小鲜, 图片+视频输入)" minimax-m3: name: "MiniMax-M3 (编程/Agent顶尖)" deepseek-v4-pro: name: "DeepSeek-V4-Pro (thinking)" minimax-m2.7: name: "MiniMax-M2.7 (即将下线)" kimi-k2.6: name: "Kimi-K2.6 (即将下线, thinking)" model_catalog: enabled: true url: https://hermes-agent.nousresearch.com/docs/api/model-catalog.json ttl_hours: 1 excluded_providers: - anthropic - copilot - moa

关键点有三个。第一,base_url指向https://taotoken.net/api,这是 TaoToken 的标准 API 地址,不要加/v1之类的后缀。第二,discover_models: false很重要,因为方舟端点不支持/v1/models探测,设成 true 会导致模型列表被清空,只能靠下面声明的 models 列表。第三,excluded_providers必须是 YAML 列表,不能是字符串。

环境变量文件~/.hermes/.env:

ARK_API_KEY=你的TaoToken_API_Key

三件套齐了:Base URL 是https://taotoken.net/api,Key 是ARK_API_KEY对应的值,Model ID 是ark-code-latest。这三个字段在 config.yaml 里都能找到对应位置,改的时候一起改,别只改一个。

如果你用的是 Cline MCP 或者 Codex 的 auth.json,思路是一样的:Base URL 填 TaoToken 的 API 地址,Key 填 TaoToken 的 Key,Model ID 填你要用的模型。三件套缺一不可,缺哪个都会在鉴权或模型列表阶段报错。

4. 验证请求:一次最小 ACP 握手确认连接与过滤生效

配置写完,先别急着开 Air。用命令行做一次最小 ACP 握手,确认 hermes-acp 本身能正常响应。这条命令直接管道输入 initialize 请求:

echo '{"jsonrpc":"2.0","id":1,"method":"initialize","params":{"protocolVersion":1,"clientCapabilities":{}}}' | hermes-acp

正常返回应该包含 agentInfo 和 authMethods,类似这样:

{ "jsonrpc": "2.0", "id": 1, "result": { "agentCapabilities": { "loadSession": true, "promptCapabilities": {"image": true}, "sessionCapabilities": {"fork": {}, "list": {}, "resume": {}} }, "agentInfo": { "name": "hermes-agent", "version": "0.20.0" }, "authMethods": [ { "id": "custom", "name": "custom runtime credentials", "description": "Authenticate Hermes using the currently configured custom runtime credentials." }, { "id": "hermes-setup", "name": "Configure Hermes provider", "type": "terminal", "args": ["--setup"] } ], "protocolVersion": 1 } }

看到agentInfo.name是hermes-agent,说明适配器正常。接下来验证 Provider 过滤是否生效。先重启 Hermes serve 进程让 config.yaml 生效:

kill <serve_pid>

Hermes 桌面应用会自动重启 serve 进程。然后查 Air 日志确认连接:

grep -i "acp\|hermes\|agent" ~/Library/Logs/JetBrains/Air/air.log | tail -10

正常日志会显示ACP client connected、Initialize from air (protocol v1)、Caching 2 auth methods、New session等字样。如果看到No agents connected,说明 acp.json 路径或内容有问题,回到第 3 节检查。

验证模型列表过滤,可以查 Hermes 的模型选择 API。先拿到 serve 进程的端口和 token:

ps aux | grep "[h]ermes.*serve" lsof -p <serve_pid> -iTCP -sTCP:LISTEN | grep python TOKEN=$(ps eww <serve_pid> | tr ' ' '\n' | grep HERMES_DASHBOARD_SESSION_TOKEN | cut -d= -f2) curl -s -H "Authorization: Bearer $TOKEN" http://127.0.0.1:<port>/api/model/options

返回的 provider 列表里应该只有custom:kl,Anthropic、Copilot、MOA 都不见了。如果还看到它们,说明 excluded_providers 没生效,大概率是写成了字符串。用 Python 检查一下类型:

import yaml with open("/Users/你的用户名/.hermes/config.yaml") as f: cfg = yaml.safe_load(f) print(type(cfg["model_catalog"]["excluded_providers"]))

输出应该是<class 'list'>。如果是<class 'str'>,说明被hermes config set存成了字符串,需要手动改回列表。

最后在 Air 里打开模型选择器,应该只看到 kl provider 的 14 个模型(13 个语言模型 + auto 智能调度)。到这一步,连接和过滤都验证通过了。

5. 本篇常见错排查:401、local proxy failed、reading choices、OAuth

配置过程中最容易撞上的几类报错,逐个拆解。

401 Unauthorized。这个基本是 Key 的问题。先确认~/.hermes/.env里的ARK_API_KEY和 config.yaml 里key_env指向的字段名一致。然后确认这个 Key 是 TaoToken 控制台创建的,不是原来 provider 的 Key。如果 base_url 指向 TaoToken 但 Key 用的是别家的,必然 401。还有一种情况是 Key 有空格或换行,用cat -A ~/.hermes/.env检查一下有没有隐藏字符。

local proxy failed。这个报错通常出现在 base_url 写错的时候。比如你写成了https://taotoken.net/api/v1,多了一层路径,请求就打到不存在的端点上了。TaoToken 的标准 API 地址就是https://taotoken.net/api,不要自作主张加后缀。另外检查一下有没有在系统层面配了什么本地转发,如果有,先关掉再试。

reading choices 报错。这个一般出现在模型返回格式不符合预期的时候。常见原因是api_mode设错了。方舟的 codex_responses 模式要求api_mode: codex_responses,如果你写成了chat_completions,返回结构对不上,解析就会失败。检查 config.yaml 里 model 和 custom_providers 两处的 api_mode 是否一致。

OAuth 相关报错。Hermes 桌面应用如果之前登录过 Anthropic 或 Copilot 的 OAuth,这些 provider 会被标记为"已配置",即使你设了explicit_only=True也会出现在列表里。这就是为什么必须用excluded_providers显式排除。如果你看到 OAuth token 过期的报错,要么重新登录,要么直接在 excluded_providers 里把对应 provider 排除掉,眼不见为净。

模型列表为空。如果discover_models: true且端点不支持/v1/models,列表会被清空。方舟端点就是这种情况,所以必须设discover_models: false,然后靠 models 字段手动声明。声明的时候注意缩进,YAML 对缩进敏感,少一个空格就解析失败。

acp.json 不生效。再强调一次路径:~/Library/Application Support/JetBrains/Air/acp.json。不是acp-agents/installed.json。如果你不确定 Air 读的是哪个文件,可以用javap反编译插件字节码找线索:

jar xf /Applications/Air.app/Contents/app/code-cache/<hash>/fleet.plugins.ai.agent.genericacp.workspace.jar javap -p -c fleet/plugins/ai/agent/genericacp/workspace/GenericAcpConfigWatcherKt.class | grep "String"

字节码里会出现acp.json not present这样的字符串常量,顺着就能找到正确路径。这个方法有点硬核,但比在文档里翻要快。

hermes config set 的 YAML 类型陷阱。这个坑我踩过两次。hermes config set model_catalog.excluded_providers '["anthropic", "copilot", "moa"]'会把值存成字符串,而不是列表。正确做法是用 Python 的 yaml.dump 直接写入:

import yaml config_path = "/Users/你的用户名/.hermes/config.yaml" with open(config_path) as f: cfg = yaml.safe_load(f) cfg["model_catalog"]["excluded_providers"] = ["anthropic", "copilot", "moa"] with open(config_path, "w") as f: yaml.dump(cfg, f, default_flow_style=False, allow_unicode=True, sort_keys=False)

写完再读回来验证类型是 list。这个坑和之前 custom_providers 的问题是同一个根源:CLI 对嵌套列表类型会存成 str。

6. 语义一致 CTA:把 Key 和 endpoint 统一到 TaoToken

配置跑通之后,日常使用中最省心的做法就是把所有请求都统一走 TaoToken 的 API 通道。这样你换模型、加 provider、调参数,都只改 config.yaml 一处,acp.json 完全不用动。Air 那边只认 hermes-acp 这个可执行文件,至于 hermes-acp 背后连的是哪个 provider,Air 不关心。

如果你还没拿 Key,去 API Keys 页面创建一个,然后按第 3 节的配置写进.env和 config.yaml。接入文档里有完整的字段对照表,遇到不确定的参数先查文档。想先验证模型能不能正常返回,可以在模型对话页面发一条测试消息,确认通了再往编辑器里接。对于长期编码和 Agent 场景,Coding Plan 的多轮会话优化会更适合,尤其是你要在 Air 里跑复杂的重构任务时。

统一到 TaoToken 之后,你的配置就变成了一个稳定的三件套:Base URL 是https://taotoken.net/api,Key 是 TaoToken 控制台创建的 Key,Model ID 是ark-code-latest(或你声明的其他模型)。这三个字段在 config.yaml 里各出现一次,改的时候一起改。Air 的 acp.json 只负责告诉编辑器去哪里找 hermes-acp,不涉及任何鉴权信息,所以 Key 轮换的时候你只需要更新.env,不用碰 Air 的配置。

最后留一个实用技巧:把~/.hermes/config.yaml和~/Library/Application Support/JetBrains/Air/acp.json这两个文件加入你的 dotfiles 备份。下次换机器或者重装 Air,直接恢复这两个文件,再补一个.env里的 Key,五分钟就能把整套环境重建起来。比重新走一遍反编译找路径的流程要快得多。

RELATED READING

延伸阅读

更多一线实战笔记与深度复盘,助您持续精进