Hermes 集成

Hermes API Server 保姆级教程:鉴权、Responses、Runs 与上线验收

用 API-CANARY-741 完成 A01–A14:loopback 启动、401 负例、Capabilities、Chat、Responses、Runs、幂等、取消、CORS、并发与停用。

生态 连接 预计 70 分钟 更新 2026/9/9 核验 2026/9/9
本页目录
官方文档核验 · 尚未运行实测查看验证范围

已核读当前 API Server、Profiles、Environment Variables 与 Gateway 文档,并静态检查 A01–A14 验收表和运行卡;未启动 Hermes API、调用模型或暴露网络端口。

完成结果

学完后你会留下什么

一份填好的 A01–A14 API 验收表与运行卡,包含端口、鉴权、能力发现、响应、run ID、幂等冲突、CORS、并发和撤销证据。

参考版本
v0.21.1 / v2026.9.7(文档核读)
平台
macOS / Windows / Linux / Web
任务模式
Agent
权限
高权限
积分影响
适合谁
准备把 Hermes 接入内部 UI、服务端程序或 OpenAI-compatible 客户端,需要证明鉴权、状态、重试和停止都可控的开发者
开始前确认
  • 已完成 Profile P01–P12 隔离实验
  • Hermes 已配置可用 provider/model
  • 本机可使用 curl 和 jq
  • 首轮不连接生产系统或公开网页

API Server 把 Hermes 的模型、Memory、Skills 和终端工具交给另一个系统调用。一次 curl 返回答案只证明网络接通;上线还必须证明错误 key 被拒绝、长任务可追踪、重复请求不会重复执行、浏览器 Origin 被限制,以及管理员能停止入口。

本课只在独立 api-lab-741 Profile 和 127.0.0.1:8642 上测试,固定输入为 API-CANARY-741。完成 A01–A14 前不绑定 0.0.0.0,也不接真实前端。

本站验证范围: 本页于 2026-09-09 核读 v0.21.1 / v2026.9.7 当前官方文档;本站未启动 Hermes API、调用模型、测试真实 token 或开放端口。

下载两份材料

运行卡只记录 key 的保管位置、owner 和轮换时间,不记录 key 本身。命令使用当前 shell 的 API_SERVER_KEY;录屏和 CI 日志应屏蔽 header。

先决定是否需要 API

需求入口
人在终端里交互CLI / TUI
Telegram 等消息入口Messaging Gateway
固定周期任务Cron
OpenAI-compatible 客户端、内部服务主动调用API Server
Desktop 或远程 Dashboard 后端hermes serve,这是另一套 JSON-RPC/WebSocket 服务,默认 9119

旧教程经常把 hermes serve 和本页 API Server 混为一谈。OpenAI-compatible API Server 是 Gateway 中的一个 platform,默认端口 8642,通过 API_SERVER_ENABLED=true 后运行 hermes gateway 启动。

先按 Profiles P01–P12 建立独立 Profile。Profile 会隔离 Hermes 状态,但不会自动限制 local terminal backend 的宿主机文件权限;API 调用可能触发完整工具集,因此要先缩小 toolset 与 cwd。

A01:建立独立 Profile 与固定环境

hermes profile create api-lab-741 --clone \
  --description "Loopback API acceptance lab; no production writes"
hermes -p api-lab-741 doctor
hermes -p api-lab-741 dump

检查该 Profile 没有生产 Cron、消息渠道或不需要的写入 Skill。为它设置一个临时 terminal.cwd,只放无敏感测试文件。

A02:配置 loopback、key 与并发上限

~/.hermes/profiles/api-lab-741/.env 写入:

API_SERVER_ENABLED=true
API_SERVER_HOST=127.0.0.1
API_SERVER_PORT=8642
API_SERVER_KEY=<由密码管理器生成的随机测试值>
API_SERVER_MODEL_NAME=api-lab-741

在同一 Profile 的 config.yaml 设置首轮并发上限:

gateway:
  api_server:
    max_concurrent_runs: 1

API key 在 loopback 上也必填。环境变量优先于 config.yaml;避免两个位置写不同 key/port 后排查错误。CORS 此时保持未配置。

A03:前台启动并确认监听主体

hermes -p api-lab-741 gateway

启动日志应显示 API Server 监听 http://127.0.0.1:8642。另开终端检查端口对应进程和 Profile,不能只看到“8642 已占用”就假定是本次 Gateway。

A04:区分 liveness 与 readiness

curl -i http://127.0.0.1:8642/health
curl -i http://127.0.0.1:8642/health/detailed \
  -H "Authorization: Bearer $API_SERVER_KEY"

/health 是公开 liveness,预期 200 与 {"status":"ok"};它不运行 readiness 检查。/health/detailed 是鉴权后的有界状态,检查 active Profile 的 config、state DB、model、disk、Gateway、active runs 等,且不返回 key、路径、命令或原始错误。

Detailed readiness 即使 degraded 也使用 HTTP 200。自动监控必须解析顶层 statusreadiness.checks,不能只看状态码。

A05:做三次鉴权负例

curl -i http://127.0.0.1:8642/v1/models
curl -i http://127.0.0.1:8642/v1/models \
  -H "Authorization: Bearer definitely-wrong"
curl -i http://127.0.0.1:8642/v1/models \
  -H "Authorization: Bearer $API_SERVER_KEY"

缺失与错误 bearer 必须是 401,正确 key 才能读取模型。若错误 key 得到业务数据,立即停止 Gateway,不继续后续测试。

A06:先发现能力,再写客户端

curl -sS http://127.0.0.1:8642/v1/capabilities \
  -H "Authorization: Bearer $API_SERVER_KEY" | jq .
curl -sS http://127.0.0.1:8642/v1/models \
  -H "Authorization: Bearer $API_SERVER_KEY" | jq .
curl -sS http://127.0.0.1:8642/v1/toolsets \
  -H "Authorization: Bearer $API_SERVER_KEY" | jq .

Capabilities 是客户端的契约入口:确认 chat_completionsresponses_apirun_submissionrun_statusrun_events_sserun_stop 等实际 feature,再决定展示哪些按钮。/v1/models 只广告稳定 alias,不是 provider 全量目录;Hermes-aware UI 才使用 /api/model/options

在运行卡抄录 API platform 最终解析出的 toolsets。它们可能包含 terminal/file 等完整能力,不能因请求长得像 OpenAI API 就把它当成纯文本模型。

A07:验证无状态 Chat Completions

curl -sS http://127.0.0.1:8642/v1/chat/completions \
  -H "Authorization: Bearer $API_SERVER_KEY" \
  -H "Content-Type: application/json" \
  -d '{"model":"api-lab-741","messages":[{"role":"user","content":"Reply with exactly API-CANARY-741 and nothing else."}],"stream":false}' | jq .

记录 HTTP 状态、response id、实际 model、唯一输出和 usage。Chat Completions 是无状态接口,每次都应携带完整 messages。裸 model 默认可能被当稳定 alias 并回退到 Gateway 默认;只有显式 provider 或启用 direct_model_requests 才能让 OpenAI-compatible 请求直接选择其他模型。

A08:验证 Responses 的服务端多轮

第一次请求:

curl -sS http://127.0.0.1:8642/v1/responses \
  -H "Authorization: Bearer $API_SERVER_KEY" \
  -H "Content-Type: application/json" \
  -d '{"model":"api-lab-741","input":"Remember only for this conversation: API-CANARY-741.","store":true}' | jq .

保存实际 resp_...,第二次用 previous_response_id 询问金丝雀。服务端会重建包含 tool calls/results 的链路,并把多轮记录为同一 Session。也可用固定 conversation 名让服务端自动链到该 conversation 最新 response。

Stored responses 在 SQLite 中跨 Gateway 重启保留,但最多 100 条并按 LRU 淘汰。不要把它当无限期业务数据库。DELETE /v1/responses/{id} 后再 GET 应按真实结果记录。

A09:创建可追踪 Run

为本次实验生成一个唯一、不可复用的幂等键,例如 api-lab-741-<uuid>

curl -i -X POST http://127.0.0.1:8642/v1/runs \
  -H "Authorization: Bearer $API_SERVER_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: api-lab-741-<uuid>" \
  -d '{"input":"Reply with exactly API-CANARY-741 and nothing else.","session_id":"api-lab-741-session"}'

创建返回 run_id 与 started。用精确 ID 轮询:

curl -sS http://127.0.0.1:8642/v1/runs/<run-id> \
  -H "Authorization: Bearer $API_SERVER_KEY" | jq .

追到 completedfailedcancelled,再记录 output 与 usage。POST 接受不等于执行完成。

A10:证明相同重试不会创建第二个 Run

同一 Idempotency-Key 和完全相同 JSON 重发 A09。预期 HTTP 202、相同 run_id,并包含 Idempotency-Replayed: true。该 reservation 在启动工作前持久保存,Gateway 重启和 terminal state 后仍能重放;key 按认证 Profile/credential 隔离,并在最后状态更新后保留 24 小时。

随后用同一 key 改一个字符,预期 HTTP 409 与 idempotency_key_conflict。无 Idempotency-Key 的每次 POST 都创建新 run,因此客户端超时重试必须自己携带唯一键。

A11:验证 Events 与 Stop

创建一个足够长但无副作用的测试 run,然后:

curl -N http://127.0.0.1:8642/v1/runs/<run-id>/events \
  -H "Authorization: Bearer $API_SERVER_KEY"
curl -sS -X POST http://127.0.0.1:8642/v1/runs/<run-id>/stop \
  -H "Authorization: Bearer $API_SERVER_KEY"

Stop 先返回 stopping,只有 executor 退出后状态才变为 cancelled。不能在收到 stopping 时释放同一业务资源或宣告取消完成。SSE 断开不会取消 run;未消费的 event buffer 五分钟后过期,但仍可轮询状态、停止或审批。

A12:验证 CORS allowlist 正反例

只有浏览器必须直连时才配置:

API_SERVER_CORS_ORIGINS=http://localhost:3000

重启同一 Profile Gateway 后分别发送 allowlisted 与陌生 Origin 的 preflight:

curl -i -X OPTIONS http://127.0.0.1:8642/v1/runs \
  -H "Origin: http://localhost:3000" \
  -H "Access-Control-Request-Method: POST" \
  -H "Access-Control-Request-Headers: authorization,content-type,idempotency-key"
curl -i -X OPTIONS http://127.0.0.1:8642/v1/runs \
  -H "Origin: https://untrusted.invalid" \
  -H "Access-Control-Request-Method: POST"

只有允许 Origin 应得到匹配的 Access-Control-Allow-Origin。CORS 管浏览器读取,不是鉴权;服务端脚本不受浏览器 CORS 保护,仍必须验证 bearer。

A13:验证并发上限和 Profile 路由

max_concurrent_runs: 1 时保持一个长 run 运行,再提交第二个,预期 HTTP 429 Too many concurrent runs (max 1)。客户端应退避,不能无界立即重试;0 会禁用上限,不适合作为首轮配置。

若启用 gateway.multiplex_profiles,共享 listener 通过 /p/<profile>/... 路由。每个 named Profile 必须使用自己的 API_SERVER_KEY;default key 对 named prefix 应返回 401。Run ID 也按 Profile 隔离,在另一个 Profile 查询返回 404。先在单 Profile 完成 A01–A12,再增加这组矩阵。

A14:撤销入口并复查端口

  1. 停止 api-lab-741 Gateway。
  2. 确认 8642 不再监听,/health 不可达。
  3. 从 Profile 配置移除 API_SERVER_ENABLED 和测试 CORS。
  4. 轮换或销毁测试 key。
  5. Export Profile,记录归档 SHA-256。
  6. 删除临时 Profile,再确认 default active。

如果对接系统已经保存 base URL/key,还要先撤销调用方配置,避免它持续产生 401 与重试流量。

上线必须填写的责任表

问题必须有明确答案
谁能调用服务身份、owner、key 轮换与离职交接
Agent 能做什么Profile、cwd、Toolsets、Skills、审批规则
如何重试Idempotency-Key 生命周期与 409/429 处理
如何观察liveness、detailed readiness、run terminal state、usage
如何停止stop 的最终 cancelled、Gateway 停止、key 撤销
浏览器是否直连无需则关闭 CORS;需要则精确 Origin

完成检查

  • A01–A14 均有真实状态码、ID、JSON 字段或端口证据。
  • 缺 key 与错误 key 都被拒绝,详细 readiness 没有泄露 Secret。
  • Chat、Responses、Runs 三种接口的状态模型没有混用。
  • 相同幂等请求返回同一 run,不同 payload 返回 409。
  • Stop 被追踪到最终 cancelled,第二个并发请求得到 429。
  • 非 allowlist Origin 没有得到跨域读取许可。
  • Gateway 已停止,端口关闭,测试 key 与 Profile 已撤销。

需要把 API 对接 Open WebUI 时,应沿用同一份 A01–A14 表,而不是只填写 base URL。需要定时执行时,转到 Cron 生命周期实验,不要让外部客户端自己模拟 scheduler。

官方资料

版本和参数,以这些来源为准

本文按实际任务重写,快速变化的信息仍应在操作前回到官方页面核对。

常见问题

继续操作前,先确认这些边界

`/health` 返回 200 是否代表 API 可以接流量?

不代表。它只是公开的低成本 liveness;`/health/detailed` 需要 bearer token,并且即使 readiness degraded 也返回 200,必须读取 JSON 中的 status 与 checks。

Chat Completions、Responses 与 Runs 怎么选?

无状态兼容客户端用 Chat Completions;需要服务端多轮链路用 Responses 的 previous_response_id 或 conversation;需要可分离的进度、轮询、取消和审批用 Runs。

重试 POST /v1/runs 会不会重复执行?

携带同一 Idempotency-Key 与相同 JSON 时会返回原 run_id;同键不同 payload 返回 409。无该 header 的每次请求都会创建新 run。

继续学习

按当前任务继续推进