docs(i18n): translate all docs into Simplified Chinese (zh-Hans) (#31942)
Translates the full English docs corpus (335 files) into Simplified Chinese under website/i18n/zh-Hans/. Combined with PR #31895 (cross- locale link fix), the 简体中文 locale toggle now serves a complete Chinese site with working cross-page navigation. Pipeline: - Claude Sonnet 4.6 via OpenRouter, 8-way concurrent - Preserves frontmatter keys, code blocks, MDX/JSX, link URLs, brand names, and technical jargon (prompt/token/hook/MCP/ACP/etc.) - Translates only frontmatter title/description and prose - Two largest files (configuration.md 93KB, research-paper-writing.md 107KB) retried with 64K max_tokens after initial fence-drift - 3 manual post-fixes for MDX edge cases the model didn't escape: < in optional-skills-catalog table, double-quotes in an alt= tag, and a bare URL adjacent to a full-width period Cost: ~$30 total (Sonnet 4.6 input $3/M + output $15/M). Verified `npm run build` succeeds for both en and zh-Hans locales, no double-prefixed /docs/zh-Hans/docs/ URLs in rendered output, all in-page navigation resolves correctly. Translations are machine-generated and may need human review on specific pages — but they're an enormous improvement over the previous state (3 zh-Hans pages out of 335).
This commit is contained in:
parent
ffe11c14ec
commit
76135b329d
@ -0,0 +1,184 @@
|
||||
---
|
||||
sidebar_position: 2
|
||||
title: "ACP 内部机制"
|
||||
description: "ACP 适配器的工作原理:生命周期、会话、事件桥接、审批流程与工具渲染"
|
||||
---
|
||||
|
||||
# ACP 内部机制
|
||||
|
||||
ACP 适配器将 Hermes 的同步 `AIAgent` 封装为异步 JSON-RPC stdio 服务器。
|
||||
|
||||
关键实现文件:
|
||||
|
||||
- `acp_adapter/entry.py`
|
||||
- `acp_adapter/server.py`
|
||||
- `acp_adapter/session.py`
|
||||
- `acp_adapter/events.py`
|
||||
- `acp_adapter/permissions.py`
|
||||
- `acp_adapter/tools.py`
|
||||
- `acp_adapter/auth.py`
|
||||
- `acp_registry/agent.json`
|
||||
|
||||
## 启动流程
|
||||
|
||||
```text
|
||||
hermes acp / hermes-acp / python -m acp_adapter
|
||||
-> acp_adapter.entry.main()
|
||||
-> parse --version / --check / --setup before server startup
|
||||
-> load ~/.hermes/.env
|
||||
-> configure stderr logging
|
||||
-> construct HermesACPAgent
|
||||
-> acp.run_agent(agent, use_unstable_protocol=True)
|
||||
```
|
||||
|
||||
Zed ACP Registry 路径通过 `uvx --from 'hermes-agent[acp]==<version>' hermes-acp` 启动同一适配器,指向 `hermes-agent` PyPI 发布包。
|
||||
|
||||
stdout 保留用于 ACP JSON-RPC 传输。人类可读的日志输出至 stderr。
|
||||
|
||||
## 主要组件
|
||||
|
||||
### `HermesACPAgent`
|
||||
|
||||
`acp_adapter/server.py` 实现 ACP agent 协议。
|
||||
|
||||
职责:
|
||||
|
||||
- 初始化 / 认证
|
||||
- 新建/加载/恢复/fork/列出/取消会话方法
|
||||
- prompt(提示词)执行
|
||||
- 会话模型切换
|
||||
- 将同步 AIAgent 回调接入 ACP 异步通知
|
||||
|
||||
### `SessionManager`
|
||||
|
||||
`acp_adapter/session.py` 跟踪活跃的 ACP 会话。
|
||||
|
||||
每个会话存储:
|
||||
|
||||
- `session_id`
|
||||
- `agent`
|
||||
- `cwd`
|
||||
- `model`
|
||||
- `history`
|
||||
- `cancel_event`
|
||||
|
||||
管理器线程安全,支持:
|
||||
|
||||
- create
|
||||
- get
|
||||
- remove
|
||||
- fork
|
||||
- list
|
||||
- cleanup
|
||||
- cwd 更新
|
||||
|
||||
### 事件桥接
|
||||
|
||||
`acp_adapter/events.py` 将 AIAgent 回调转换为 ACP `session_update` 事件。
|
||||
|
||||
已桥接的回调:
|
||||
|
||||
- `tool_progress_callback`
|
||||
- `thinking_callback`(当前在 ACP 桥接中设置为 `None`——推理内容通过 `step_callback` 转发)
|
||||
- `step_callback`
|
||||
|
||||
由于 `AIAgent` 在工作线程中运行,而 ACP I/O 位于主事件循环,桥接使用:
|
||||
|
||||
```python
|
||||
asyncio.run_coroutine_threadsafe(...)
|
||||
```
|
||||
|
||||
### 权限桥接
|
||||
|
||||
`acp_adapter/permissions.py` 将危险终端审批 prompt 适配为 ACP 权限请求。
|
||||
|
||||
映射关系:
|
||||
|
||||
- `allow_once` -> Hermes `once`
|
||||
- `allow_always` -> Hermes `always`
|
||||
- 拒绝选项 -> Hermes `deny`
|
||||
|
||||
超时和桥接失败默认拒绝。
|
||||
|
||||
### 工具渲染辅助
|
||||
|
||||
`acp_adapter/tools.py` 将 Hermes 工具映射到 ACP 工具类型,并构建面向编辑器的内容。
|
||||
|
||||
示例:
|
||||
|
||||
- `patch` / `write_file` -> 文件 diff
|
||||
- `terminal` -> shell 命令文本
|
||||
- `read_file` / `search_files` -> 文本预览
|
||||
- 大型结果 -> 截断文本块(保障 UI 安全)
|
||||
|
||||
## 会话生命周期
|
||||
|
||||
```text
|
||||
new_session(cwd)
|
||||
-> create SessionState
|
||||
-> create AIAgent(platform="acp", enabled_toolsets=["hermes-acp"])
|
||||
-> bind task_id/session_id to cwd override
|
||||
|
||||
prompt(..., session_id)
|
||||
-> extract text from ACP content blocks
|
||||
-> reset cancel event
|
||||
-> install callbacks + approval bridge
|
||||
-> run AIAgent in ThreadPoolExecutor
|
||||
-> update session history
|
||||
-> emit final agent message chunk
|
||||
```
|
||||
|
||||
### 取消
|
||||
|
||||
`cancel(session_id)`:
|
||||
|
||||
- 设置会话取消事件
|
||||
- 在可用时调用 `agent.interrupt()`
|
||||
- 使 prompt 响应返回 `stop_reason="cancelled"`
|
||||
|
||||
### Fork
|
||||
|
||||
`fork_session()` 将消息历史深拷贝至新的活跃会话,在保留对话状态的同时为 fork 分配独立的 session ID 和 cwd。
|
||||
|
||||
## Provider/认证行为
|
||||
|
||||
ACP 不实现自己的认证存储。
|
||||
|
||||
而是复用 Hermes 的运行时解析器:
|
||||
|
||||
- `acp_adapter/auth.py`
|
||||
- `hermes_cli/runtime_provider.py`
|
||||
|
||||
因此 ACP 通告并使用当前配置的 Hermes provider/凭据。它还始终通告一个终端 setup 认证方法(`hermes-setup`,参数 `--setup`),以便首次运行的 registry 客户端在启动正常 ACP 会话前可以打开 Hermes 的交互式模型/provider 配置。
|
||||
|
||||
## 工作目录绑定
|
||||
|
||||
ACP 会话携带编辑器 cwd。
|
||||
|
||||
会话管理器通过任务作用域的终端/文件覆盖将该 cwd 绑定到 ACP session ID,使文件和终端工具相对于编辑器工作区运行。
|
||||
|
||||
## 重复同名工具调用
|
||||
|
||||
事件桥接按工具名称以 FIFO 队列跟踪工具 ID,而非每个名称仅保留一个 ID。这对以下场景至关重要:
|
||||
|
||||
- 并行同名调用
|
||||
- 单步内重复同名调用
|
||||
|
||||
若不使用 FIFO 队列,完成事件将附加到错误的工具调用上。
|
||||
|
||||
## 审批回调恢复
|
||||
|
||||
ACP 在 prompt 执行期间临时在终端工具上安装审批回调,执行完成后恢复之前的回调。这避免了将 ACP 会话特定的审批处理器永久全局安装。
|
||||
|
||||
## 当前限制
|
||||
|
||||
- ACP 会话持久化至共享的 `~/.hermes/state.db`(SessionDB),在进程重启后透明恢复;它们会出现在 `session_search` 中
|
||||
- 非文本 prompt 块在请求文本提取时当前被忽略
|
||||
- 编辑器特定的 UX 因 ACP 客户端实现而异
|
||||
|
||||
## 相关文件
|
||||
|
||||
- `tests/acp/` — ACP 测试套件
|
||||
- `toolsets.py` — `hermes-acp` toolset 定义
|
||||
- `hermes_cli/main.py` — `hermes acp` CLI 子命令
|
||||
- `pyproject.toml` — `[acp]` 可选依赖 + `hermes-acp` 脚本
|
||||
@ -0,0 +1,688 @@
|
||||
---
|
||||
sidebar_position: 9
|
||||
---
|
||||
|
||||
# 添加平台适配器
|
||||
|
||||
本指南介绍如何向 Hermes gateway 添加新的消息平台。平台适配器将 Hermes 连接到外部消息服务(Telegram、Discord、WeCom 等),使用户可以通过该服务与 agent 交互。
|
||||
|
||||
:::tip
|
||||
添加平台有两种方式:
|
||||
- **Plugin**(推荐用于社区/第三方):将 plugin 目录放入 `~/.hermes/plugins/` — 无需修改任何核心代码。参见下方 [Plugin 路径](#plugin-path-recommended)。
|
||||
- **内置**:需修改代码、配置和文档共 20+ 个文件。参见下方 [内置清单](#step-by-step-checklist)。
|
||||
:::
|
||||
|
||||
## 架构概览
|
||||
|
||||
```
|
||||
用户 ↔ 消息平台 ↔ 平台适配器 ↔ Gateway Runner ↔ AIAgent
|
||||
```
|
||||
|
||||
每个适配器都继承自 `gateway/platforms/base.py` 中的 `BasePlatformAdapter`,并实现以下方法:
|
||||
|
||||
- **`connect()`** — 建立连接(WebSocket、长轮询、HTTP 服务器等)*(抽象方法)*
|
||||
- **`disconnect()`** — 清理关闭 *(抽象方法)*
|
||||
- **`send()`** — 向聊天发送文本消息 *(抽象方法)*
|
||||
- **`send_typing()`** — 显示正在输入指示器(可选覆盖)
|
||||
- **`get_chat_info()`** — 返回聊天元数据(可选覆盖)
|
||||
|
||||
适配器接收入站消息后,通过 `self.handle_message(event)` 转发,基类将其路由到 gateway runner。
|
||||
|
||||
## Plugin 路径(推荐){#plugin-path-recommended}
|
||||
|
||||
Plugin 系统允许你在不修改任何 Hermes 核心代码的情况下添加平台适配器。你的 plugin 是一个包含两个文件的目录:
|
||||
|
||||
```
|
||||
~/.hermes/plugins/my-platform/
|
||||
PLUGIN.yaml # Plugin 元数据
|
||||
adapter.py # 适配器类 + register() 入口点
|
||||
```
|
||||
|
||||
### PLUGIN.yaml
|
||||
|
||||
Plugin 元数据。`requires_env` 和 `optional_env` 块会自动填充 `hermes config` UI 条目(参见下方[在 hermes config 中暴露环境变量](#surfacing-env-vars-in-hermes-config))。
|
||||
|
||||
```yaml
|
||||
name: my-platform
|
||||
label: My Platform
|
||||
kind: platform
|
||||
version: 1.0.0
|
||||
description: My custom messaging platform adapter
|
||||
author: Your Name
|
||||
requires_env:
|
||||
- MY_PLATFORM_TOKEN # 裸字符串有效
|
||||
- name: MY_PLATFORM_CHANNEL # 或使用富字典以获得更好的 UX
|
||||
description: "Channel to join"
|
||||
prompt: "Channel"
|
||||
password: false
|
||||
optional_env:
|
||||
- name: MY_PLATFORM_HOME_CHANNEL
|
||||
description: "Default channel for cron delivery"
|
||||
password: false
|
||||
```
|
||||
|
||||
### adapter.py
|
||||
|
||||
```python
|
||||
import os
|
||||
from gateway.platforms.base import (
|
||||
BasePlatformAdapter, SendResult, MessageEvent, MessageType,
|
||||
)
|
||||
from gateway.config import Platform, PlatformConfig
|
||||
|
||||
|
||||
class MyPlatformAdapter(BasePlatformAdapter):
|
||||
def __init__(self, config: PlatformConfig):
|
||||
super().__init__(config, Platform("my_platform"))
|
||||
extra = config.extra or {}
|
||||
self.token = os.getenv("MY_PLATFORM_TOKEN") or extra.get("token", "")
|
||||
|
||||
async def connect(self) -> bool:
|
||||
# 连接到平台 API,启动监听器
|
||||
self._mark_connected()
|
||||
return True
|
||||
|
||||
async def disconnect(self) -> None:
|
||||
self._mark_disconnected()
|
||||
|
||||
async def send(self, chat_id, content, reply_to=None, metadata=None):
|
||||
# 通过平台 API 发送消息
|
||||
return SendResult(success=True, message_id="...")
|
||||
|
||||
async def get_chat_info(self, chat_id):
|
||||
return {"name": chat_id, "type": "dm"}
|
||||
|
||||
|
||||
def check_requirements() -> bool:
|
||||
return bool(os.getenv("MY_PLATFORM_TOKEN"))
|
||||
|
||||
|
||||
def validate_config(config) -> bool:
|
||||
extra = getattr(config, "extra", {}) or {}
|
||||
return bool(os.getenv("MY_PLATFORM_TOKEN") or extra.get("token"))
|
||||
|
||||
|
||||
def _env_enablement() -> dict | None:
|
||||
token = os.getenv("MY_PLATFORM_TOKEN", "").strip()
|
||||
channel = os.getenv("MY_PLATFORM_CHANNEL", "").strip()
|
||||
if not (token and channel):
|
||||
return None
|
||||
seed = {"token": token, "channel": channel}
|
||||
home = os.getenv("MY_PLATFORM_HOME_CHANNEL")
|
||||
if home:
|
||||
seed["home_channel"] = {"chat_id": home, "name": "Home"}
|
||||
return seed
|
||||
|
||||
|
||||
def register(ctx):
|
||||
"""Plugin 入口点 — 由 Hermes plugin 系统调用。"""
|
||||
ctx.register_platform(
|
||||
name="my_platform",
|
||||
label="My Platform",
|
||||
adapter_factory=lambda cfg: MyPlatformAdapter(cfg),
|
||||
check_fn=check_requirements,
|
||||
validate_config=validate_config,
|
||||
required_env=["MY_PLATFORM_TOKEN"],
|
||||
install_hint="pip install my-platform-sdk",
|
||||
# 环境变量驱动的自动配置 — 在适配器构建前从环境变量
|
||||
# 填充 PlatformConfig.extra。参见下方"环境变量驱动的自动配置"章节。
|
||||
env_enablement_fn=_env_enablement,
|
||||
# Cron 主频道投递支持。允许 deliver=my_platform 的 cron 任务
|
||||
# 无需编辑 cron/scheduler.py 即可路由。参见下方"Cron 投递"章节。
|
||||
cron_deliver_env_var="MY_PLATFORM_HOME_CHANNEL",
|
||||
# 每平台用户授权环境变量
|
||||
allowed_users_env="MY_PLATFORM_ALLOWED_USERS",
|
||||
allow_all_env="MY_PLATFORM_ALLOW_ALL_USERS",
|
||||
# 智能分块的消息长度限制(0 = 无限制)
|
||||
max_message_length=4000,
|
||||
# 注入系统 prompt(提示词)的 LLM 指导
|
||||
platform_hint=(
|
||||
"You are chatting via My Platform. "
|
||||
"It supports markdown formatting."
|
||||
),
|
||||
# 显示
|
||||
emoji="💬",
|
||||
)
|
||||
|
||||
# 可选:注册平台专属工具
|
||||
ctx.register_tool(
|
||||
name="my_platform_search",
|
||||
toolset="my_platform",
|
||||
schema={...},
|
||||
handler=my_search_handler,
|
||||
)
|
||||
```
|
||||
|
||||
### 配置
|
||||
|
||||
用户在 `config.yaml` 中配置平台:
|
||||
|
||||
```yaml
|
||||
gateway:
|
||||
platforms:
|
||||
my_platform:
|
||||
enabled: true
|
||||
extra:
|
||||
token: "..."
|
||||
channel: "#general"
|
||||
```
|
||||
|
||||
或通过环境变量(适配器在 `__init__` 中读取)。
|
||||
|
||||
### Plugin 系统自动处理的内容
|
||||
|
||||
调用 `ctx.register_platform()` 时,以下集成点将自动处理 — 无需修改核心代码:
|
||||
|
||||
| 集成点 | 工作方式 |
|
||||
|---|---|
|
||||
| Gateway 适配器创建 | 在内置 if/elif 链之前检查注册表 |
|
||||
| 配置解析 | `Platform._missing_()` 接受任意平台名称 |
|
||||
| 已连接平台验证 | 调用注册表中的 `validate_config()` |
|
||||
| 用户授权 | 检查 `allowed_users_env` / `allow_all_env` |
|
||||
| 仅环境变量自动启用 | `env_enablement_fn` 填充 `PlatformConfig.extra` + `home_channel` |
|
||||
| YAML 配置桥接 | `apply_yaml_config_fn` 将 `config.yaml` 键转换为环境变量/extras |
|
||||
| Cron 投递 | `cron_deliver_env_var` 使 `deliver=<name>` 生效 |
|
||||
| `hermes config` UI 条目 | `plugin.yaml` 中的 `requires_env` / `optional_env` 自动填充 |
|
||||
| send_message 工具 | 通过实时 gateway 适配器路由 |
|
||||
| Webhook 跨平台投递 | 检查注册表中的已知平台 |
|
||||
| `/update` 命令访问 | `allow_update_command` 标志 |
|
||||
| 频道目录 | Plugin 平台包含在枚举中 |
|
||||
| 系统 prompt 提示 | `platform_hint` 注入 LLM 上下文 |
|
||||
| 消息分块 | `max_message_length` 用于智能分割 |
|
||||
| PII 脱敏 | `pii_safe` 标志 |
|
||||
| `hermes status` | 显示带 `(plugin)` 标签的 plugin 平台 |
|
||||
| `hermes gateway setup` | Plugin 平台出现在设置菜单中 |
|
||||
| `hermes tools` / `hermes skills` | Plugin 平台出现在每平台配置中 |
|
||||
| Token 锁(多配置文件) | 在 `connect()` 中使用 `acquire_scoped_lock()` |
|
||||
| 孤立配置警告 | Plugin 缺失时输出描述性日志 |
|
||||
|
||||
## 环境变量驱动的自动配置
|
||||
|
||||
大多数用户通过将环境变量写入 `~/.hermes/.env` 来配置平台,而不是编辑 `config.yaml`。`env_enablement_fn` hook 允许你的 plugin 在适配器构建**之前**读取这些环境变量,使 `hermes gateway status`、`get_connected_platforms()` 和 cron 投递无需实例化平台 SDK 即可看到正确状态。
|
||||
|
||||
```python
|
||||
def _env_enablement() -> dict | None:
|
||||
"""从环境变量填充 PlatformConfig.extra。
|
||||
|
||||
在 load_gateway_config() 期间由平台注册表调用。
|
||||
当平台未完成最低配置时返回 None — 调用方将跳过自动启用。
|
||||
返回字典以填充 extras。
|
||||
|
||||
特殊键 'home_channel' 会被提取并成为 PlatformConfig 上的
|
||||
HomeChannel dataclass;其他所有键合并到 PlatformConfig.extra 中。
|
||||
"""
|
||||
token = os.getenv("MY_PLATFORM_TOKEN", "").strip()
|
||||
channel = os.getenv("MY_PLATFORM_CHANNEL", "").strip()
|
||||
if not (token and channel):
|
||||
return None
|
||||
seed = {"token": token, "channel": channel}
|
||||
home = os.getenv("MY_PLATFORM_HOME_CHANNEL")
|
||||
if home:
|
||||
seed["home_channel"] = {
|
||||
"chat_id": home,
|
||||
"name": os.getenv("MY_PLATFORM_HOME_CHANNEL_NAME", "Home"),
|
||||
}
|
||||
return seed
|
||||
|
||||
|
||||
def register(ctx):
|
||||
ctx.register_platform(
|
||||
name="my_platform",
|
||||
label="My Platform",
|
||||
adapter_factory=lambda cfg: MyPlatformAdapter(cfg),
|
||||
check_fn=check_requirements,
|
||||
validate_config=validate_config,
|
||||
env_enablement_fn=_env_enablement,
|
||||
# ... 其他字段
|
||||
)
|
||||
```
|
||||
|
||||
|
||||
## YAML→env 配置桥接
|
||||
|
||||
部分用户更倾向于设置 `config.yaml` 键(`my_platform.require_mention`、`my_platform.allowed_channels` 等)而非环境变量。`apply_yaml_config_fn` hook 允许你的 plugin 自行处理这一转换,而无需强制核心 `gateway/config.py` 了解你平台的 YAML schema。
|
||||
|
||||
```python
|
||||
import os
|
||||
|
||||
def _apply_yaml_config(yaml_cfg: dict, platform_cfg: dict) -> dict | None:
|
||||
"""将 config.yaml 中的 `my_platform:` 键转换为环境变量/extras。
|
||||
|
||||
yaml_cfg — 完整的顶层解析后 config.yaml 字典
|
||||
platform_cfg — 平台自身的子字典(yaml_cfg.get("my_platform", {}))
|
||||
|
||||
可直接修改 os.environ(使用 `not os.getenv(...)` 守卫以保持
|
||||
环境变量 > YAML 的优先级),也可返回字典合并到 PlatformConfig.extra 中。
|
||||
返回 None 或 {} 表示无额外内容。
|
||||
"""
|
||||
if "require_mention" in platform_cfg and not os.getenv("MY_PLATFORM_REQUIRE_MENTION"):
|
||||
os.environ["MY_PLATFORM_REQUIRE_MENTION"] = str(platform_cfg["require_mention"]).lower()
|
||||
allowed = platform_cfg.get("allowed_channels")
|
||||
if allowed is not None and not os.getenv("MY_PLATFORM_ALLOWED_CHANNELS"):
|
||||
if isinstance(allowed, list):
|
||||
allowed = ",".join(str(v) for v in allowed)
|
||||
os.environ["MY_PLATFORM_ALLOWED_CHANNELS"] = str(allowed)
|
||||
return None # 无需合并到 PlatformConfig.extra 的额外内容
|
||||
|
||||
def register(ctx):
|
||||
ctx.register_platform(
|
||||
name="my_platform",
|
||||
...,
|
||||
apply_yaml_config_fn=_apply_yaml_config,
|
||||
)
|
||||
```
|
||||
|
||||
该 hook 在 `load_gateway_config()` 期间,于通用共享键循环(处理 `unauthorized_dm_behavior`、`notice_delivery`、`reply_prefix`、`require_mention` 等公共键)之后、`_apply_env_overrides()` 之前调用,因此你的 plugin 只需桥接**平台专属**键。
|
||||
|
||||
hook 内抛出的异常会被捕获并以 debug 级别记录 — 行为异常的 plugin 不会中止 gateway 配置加载。
|
||||
|
||||
|
||||
## Cron 投递
|
||||
|
||||
要让 `deliver=my_platform` 的 cron 任务路由到已配置的主频道,将 `cron_deliver_env_var` 设置为持有默认聊天/房间/频道 ID 的环境变量名:
|
||||
|
||||
```python
|
||||
ctx.register_platform(
|
||||
name="my_platform",
|
||||
...
|
||||
cron_deliver_env_var="MY_PLATFORM_HOME_CHANNEL",
|
||||
)
|
||||
```
|
||||
|
||||
调度器在解析 `deliver=my_platform` 任务的主目标时会读取此环境变量,并将该平台视为 `_KNOWN_DELIVERY_PLATFORMS` 风格检查中的有效 cron 目标。如果你的 `env_enablement_fn` 填充了 `home_channel` 字典(见上文),则优先使用该值 — `cron_deliver_env_var` 是在环境变量填充之前运行的 cron 任务的回退方案。
|
||||
|
||||
### 进程外 cron 投递
|
||||
|
||||
`cron_deliver_env_var` 使你的平台成为可识别的 `deliver=` 目标。要在 cron 任务运行于独立进程(即 `hermes cron run` 与 `hermes gateway` 分离)时使实际发送成功,需注册 `standalone_sender_fn`:
|
||||
|
||||
```python
|
||||
async def _standalone_send(
|
||||
pconfig,
|
||||
chat_id,
|
||||
message,
|
||||
*,
|
||||
thread_id=None,
|
||||
media_files=None,
|
||||
force_document=False,
|
||||
):
|
||||
"""建立临时连接/获取新 token,发送消息,然后关闭。"""
|
||||
# ... 建立连接,发送消息,返回结果 ...
|
||||
return {"success": True, "message_id": "..."}
|
||||
# 或 {"error": "..."}
|
||||
|
||||
ctx.register_platform(
|
||||
name="my_platform",
|
||||
...
|
||||
cron_deliver_env_var="MY_PLATFORM_HOME_CHANNEL",
|
||||
standalone_sender_fn=_standalone_send,
|
||||
)
|
||||
```
|
||||
|
||||
为何需要此 hook:内置平台(Telegram、Discord、Slack 等)在 `tools/send_message_tool.py` 中内置了直接 REST 辅助函数,使 cron 无需在同一进程中持有 gateway 即可投递。Plugin 平台历史上依赖 `_gateway_runner_ref()`,该函数在 gateway 进程外返回 `None`,因此若没有 `standalone_sender_fn`,cron 端发送会失败并报 `No live adapter for platform '<name>'`。
|
||||
|
||||
该函数接收与实时适配器相同的 `pconfig` 和 `chat_id`,以及可选的 `thread_id`、`media_files` 和 `force_document` 关键字参数。返回 `{"success": True, "message_id": ...}` 视为成功投递;返回 `{"error": "..."}` 会将消息记录到 cron 的 `delivery_errors` 中。函数内抛出的异常由调度器捕获并报告为 `Plugin standalone send failed: <reason>`。参考实现位于 `plugins/platforms/{irc,teams,google_chat}/adapter.py`。
|
||||
|
||||
## 在 `hermes config` 中暴露环境变量 {#surfacing-env-vars-in-hermes-config}
|
||||
|
||||
`hermes_cli/config.py` 在导入时扫描 `plugins/platforms/*/plugin.yaml`,并从 `requires_env` 和(可选的)`optional_env` 块自动填充 `OPTIONAL_ENV_VARS`。使用富字典形式可提供完整的描述、prompt、password 标志和 URL — CLI 设置 UI 会自动识别。
|
||||
|
||||
```yaml
|
||||
# plugins/platforms/my_platform/plugin.yaml
|
||||
name: my_platform-platform
|
||||
label: My Platform
|
||||
kind: platform
|
||||
version: 1.0.0
|
||||
description: >
|
||||
My Platform gateway adapter for Hermes Agent.
|
||||
author: Your Name
|
||||
requires_env:
|
||||
- name: MY_PLATFORM_TOKEN
|
||||
description: "Bot API token from the My Platform console"
|
||||
prompt: "My Platform bot token"
|
||||
url: "https://my-platform.example.com/bots"
|
||||
password: true
|
||||
- name: MY_PLATFORM_CHANNEL
|
||||
description: "Channel to join (e.g. #hermes)"
|
||||
prompt: "Channel"
|
||||
password: false
|
||||
optional_env:
|
||||
- name: MY_PLATFORM_HOME_CHANNEL
|
||||
description: "Default channel for cron delivery (defaults to MY_PLATFORM_CHANNEL)"
|
||||
prompt: "Home channel (or empty)"
|
||||
password: false
|
||||
- name: MY_PLATFORM_ALLOWED_USERS
|
||||
description: "Comma-separated user IDs allowed to talk to the bot"
|
||||
prompt: "Allowed users (comma-separated)"
|
||||
password: false
|
||||
```
|
||||
|
||||
**支持的字典键:** `name`(必填)、`description`、`prompt`、`url`、`password`(布尔值;当省略时根据 `*_TOKEN` / `*_SECRET` / `*_KEY` / `*_PASSWORD` / `*_JSON` 后缀自动检测)、`category`(默认为 `"messaging"`)。
|
||||
|
||||
裸字符串条目(`- MY_PLATFORM_TOKEN`)仍然有效 — 会根据 plugin 的 `label` 自动生成通用描述。如果 `OPTIONAL_ENV_VARS` 中已存在同名变量的硬编码条目,则以硬编码为准(向后兼容);plugin.yaml 形式作为回退。
|
||||
|
||||
## 平台专属慢速 LLM 用户体验
|
||||
|
||||
某些平台存在约束,影响慢速 LLM 响应的呈现方式:
|
||||
|
||||
- **LINE** 发出单次使用的*回复 token*,在入站事件后约 60 秒过期。使用该 token 回复是免费的;回退到计费的 Push API 则不然。如果 LLM 在截止时间前未完成,选择是"消耗付费 Push 配额"或"在回复 token 过期前用它做些更聪明的事"。
|
||||
- **WhatsApp** 在 24 小时不活跃后将会话标记为非活跃,此后只接受模板消息。
|
||||
- **SMS** 没有正在输入指示器或渐进式更新的概念 — 长响应看起来就像 bot 离线了。
|
||||
|
||||
这些是 `BasePlatformAdapter` 无法预判的真实约束。Plugin 接口有意为适配器在基础输入循环之上叠加平台专属 UX 留出空间,而无需扩展 kwarg 列表。
|
||||
|
||||
### 模式:子类化 `_keep_typing` 以叠加飞行中 UX
|
||||
|
||||
`BasePlatformAdapter._keep_typing` 是正在输入指示器的心跳 — 它在 LLM 生成时作为后台任务运行,响应投递后被取消。要在某个阈值时叠加平台专属行为(例如在 45 秒时发送"仍在思考"气泡),在你的适配器中覆盖 `_keep_typing`,在 `super()._keep_typing()` 旁边调度你自己的任务,并在 `finally` 中清理:
|
||||
|
||||
```python
|
||||
class LineAdapter(BasePlatformAdapter):
|
||||
async def _keep_typing(self, chat_id: str, *args, **kwargs) -> None:
|
||||
if self.slow_response_threshold <= 0:
|
||||
await super()._keep_typing(chat_id, *args, **kwargs)
|
||||
return
|
||||
|
||||
async def _fire_at_threshold() -> None:
|
||||
try:
|
||||
await asyncio.sleep(self.slow_response_threshold)
|
||||
except asyncio.CancelledError:
|
||||
raise
|
||||
# 平台专属操作 — 对于 LINE,使用缓存的回复 token 发送
|
||||
# Template Buttons "获取答案"气泡,用户可通过 postback
|
||||
# 回调中的新(免费)回复 token 稍后获取缓存的响应。
|
||||
await self._send_slow_response_button(chat_id)
|
||||
|
||||
side_task = asyncio.create_task(_fire_at_threshold())
|
||||
try:
|
||||
await super()._keep_typing(chat_id, *args, **kwargs)
|
||||
finally:
|
||||
if not side_task.done():
|
||||
side_task.cancel()
|
||||
try:
|
||||
await side_task
|
||||
except (asyncio.CancelledError, Exception):
|
||||
pass
|
||||
```
|
||||
|
||||
关键点:
|
||||
|
||||
- **始终 `await super()._keep_typing(...)`。** 输入心跳本身有独立价值 — 不要替换它,而是在其上叠加。
|
||||
- **在 `finally` 中清理副任务。** 当 LLM 完成(或 `/stop` 取消运行)时,gateway 会取消输入任务。你的副任务也必须响应该取消,否则它会残留并可能在响应已投递后触发。
|
||||
- **配合 `interrupt_session_activity`** 在用户发出 `/stop` 时解决任何孤立 UX 状态。对于 LINE,这意味着将 postback 缓存条目从 `PENDING` 转换为 `ERROR`,使持久的"获取答案"按钮投递"运行已中断"消息而非循环。
|
||||
|
||||
### 模式:子类化 `send` 以通过缓存路由而非立即发送
|
||||
|
||||
如果你的慢速响应 UX 缓存响应以供稍后检索(LINE 的 postback 流程),你的 `send` 覆盖需要识别三种模式:
|
||||
|
||||
1. **此聊天存在待处理的 postback** → 将响应缓存在 request_id 下,不发送任何可见内容。
|
||||
2. **系统忙碌确认**(`⚡ Interrupting`、`⏳ Queued`、`⏩ Steered`)→ 绕过缓存直接发送,使用户看到 gateway 对其输入的响应。
|
||||
3. **正常响应** → 按常规通过回复 token 或 Push 发送。
|
||||
|
||||
```python
|
||||
async def send(self, chat_id: str, content: str, **kw) -> SendResult:
|
||||
if _is_system_bypass(content):
|
||||
return await self._send_text_chunks(chat_id, content, force_push=False)
|
||||
pending_rid = self._pending_buttons.get(chat_id)
|
||||
if pending_rid:
|
||||
self._cache.set_ready(pending_rid, content)
|
||||
return SendResult(success=True, message_id=pending_rid)
|
||||
return await self._send_text_chunks(chat_id, content, force_push=False)
|
||||
```
|
||||
|
||||
`_SYSTEM_BYPASS_PREFIXES` 是 gateway 自身的忙碌确认前缀(`⚡`、`⏳`、`⏩`、`💾`)。无论缓存 UX 状态如何,始终让这些前缀可见地通过。
|
||||
|
||||
### 何时适用此模式
|
||||
|
||||
在以下情况使用输入循环覆盖方式:
|
||||
|
||||
- 平台的出站 API 存在硬性时间窗口约束(单次使用回复 token、过期的粘性会话等),**且**
|
||||
- 在该平台上*可见的飞行中气泡*是可接受的 UX。
|
||||
|
||||
在以下情况使用更简单的 `slow_response_threshold = 0` 始终 Push 路径:
|
||||
|
||||
- 平台没有有意义的免费与付费区别,**或**
|
||||
- 用户社区更倾向于"加载中……加载中……完成"的静默后响应,而非交互式中间气泡。
|
||||
|
||||
LINE 两者都支持:阈值默认为 45 秒用于免费 postback 获取,`LINE_SLOW_RESPONSE_THRESHOLD=0` 恢复为"始终 Push 回退"。
|
||||
|
||||
### 参考实现
|
||||
|
||||
完整的 LINE postback 实现参见 `plugins/platforms/line/adapter.py` — 包含 `RequestCache` 状态机(`PENDING → READY → DELIVERED`,以及 `/stop` 的 `ERROR`)、在阈值时触发 Template Buttons 气泡的 `_keep_typing` 覆盖、通过缓存路由的 `send` 覆盖,以及解决孤立 PENDING 条目的 `interrupt_session_activity` 覆盖。
|
||||
|
||||
### 参考实现(Plugin 路径)
|
||||
|
||||
完整的工作示例参见仓库中的 `plugins/platforms/irc/` — 一个无外部依赖的完整异步 IRC 适配器。`plugins/platforms/teams/` 涵盖 Bot Framework / Adaptive Cards,`plugins/platforms/google_chat/` 涵盖基于 OAuth 的 REST API,`plugins/platforms/line/` 涵盖带平台专属慢速 LLM UX 的 webhook 驱动消息 API。
|
||||
|
||||
---
|
||||
|
||||
## 分步清单(内置路径){#step-by-step-checklist}
|
||||
|
||||
:::note
|
||||
此清单用于将平台直接添加到 Hermes 核心代码库 — 通常由核心贡献者为官方支持的平台执行。社区/第三方平台应使用上方的 [Plugin 路径](#plugin-path-recommended)。
|
||||
:::
|
||||
|
||||
### 1. Platform 枚举
|
||||
|
||||
在 `gateway/config.py` 的 `Platform` 枚举中添加你的平台:
|
||||
|
||||
```python
|
||||
class Platform(str, Enum):
|
||||
# ... 现有平台 ...
|
||||
NEWPLAT = "newplat"
|
||||
```
|
||||
|
||||
### 2. 适配器文件
|
||||
|
||||
创建 `gateway/platforms/newplat.py`:
|
||||
|
||||
```python
|
||||
from gateway.config import Platform, PlatformConfig
|
||||
from gateway.platforms.base import (
|
||||
BasePlatformAdapter, MessageEvent, MessageType, SendResult,
|
||||
)
|
||||
|
||||
def check_newplat_requirements() -> bool:
|
||||
"""如果依赖可用则返回 True。"""
|
||||
return SOME_SDK_AVAILABLE
|
||||
|
||||
class NewPlatAdapter(BasePlatformAdapter):
|
||||
def __init__(self, config: PlatformConfig):
|
||||
super().__init__(config, Platform.NEWPLAT)
|
||||
# 从 config.extra 字典读取配置
|
||||
extra = config.extra or {}
|
||||
self._api_key = extra.get("api_key") or os.getenv("NEWPLAT_API_KEY", "")
|
||||
|
||||
async def connect(self) -> bool:
|
||||
# 建立连接,启动轮询/webhook
|
||||
self._mark_connected()
|
||||
return True
|
||||
|
||||
async def disconnect(self) -> None:
|
||||
self._running = False
|
||||
self._mark_disconnected()
|
||||
|
||||
async def send(self, chat_id, content, reply_to=None, metadata=None):
|
||||
# 通过平台 API 发送消息
|
||||
return SendResult(success=True, message_id="...")
|
||||
|
||||
async def get_chat_info(self, chat_id):
|
||||
return {"name": chat_id, "type": "dm"}
|
||||
```
|
||||
|
||||
对于入站消息,构建 `MessageEvent` 并调用 `self.handle_message(event)`:
|
||||
|
||||
```python
|
||||
source = self.build_source(
|
||||
chat_id=chat_id,
|
||||
chat_name=name,
|
||||
chat_type="dm", # 或 "group"
|
||||
user_id=user_id,
|
||||
user_name=user_name,
|
||||
)
|
||||
event = MessageEvent(
|
||||
text=content,
|
||||
message_type=MessageType.TEXT,
|
||||
source=source,
|
||||
message_id=msg_id,
|
||||
)
|
||||
await self.handle_message(event)
|
||||
```
|
||||
|
||||
### 3. Gateway 配置(`gateway/config.py`)
|
||||
|
||||
三个接触点:
|
||||
|
||||
1. **`get_connected_platforms()`** — 添加对你平台所需凭据的检查
|
||||
2. **`load_gateway_config()`** — 添加 token 环境变量映射条目:`Platform.NEWPLAT: "NEWPLAT_TOKEN"`
|
||||
3. **`_apply_env_overrides()`** — 将所有 `NEWPLAT_*` 环境变量映射到配置
|
||||
|
||||
### 4. Gateway Runner(`gateway/run.py`)
|
||||
|
||||
五个接触点:
|
||||
|
||||
1. **`_create_adapter()`** — 添加 `elif platform == Platform.NEWPLAT:` 分支
|
||||
2. **`_is_user_authorized()` allowed_users 映射** — `Platform.NEWPLAT: "NEWPLAT_ALLOWED_USERS"`
|
||||
3. **`_is_user_authorized()` allow_all 映射** — `Platform.NEWPLAT: "NEWPLAT_ALLOW_ALL_USERS"`
|
||||
4. **早期环境检查 `_any_allowlist` 元组** — 添加 `"NEWPLAT_ALLOWED_USERS"`
|
||||
5. **早期环境检查 `_allow_all` 元组** — 添加 `"NEWPLAT_ALLOW_ALL_USERS"`
|
||||
6. **`_UPDATE_ALLOWED_PLATFORMS` frozenset** — 添加 `Platform.NEWPLAT`
|
||||
|
||||
### 5. 跨平台投递
|
||||
|
||||
1. **`gateway/platforms/webhook.py`** — 将 `"newplat"` 添加到投递类型元组
|
||||
2. **`cron/scheduler.py`** — 添加到 `_KNOWN_DELIVERY_PLATFORMS` frozenset 和 `_deliver_result()` 平台映射
|
||||
|
||||
### 6. CLI 集成
|
||||
|
||||
1. **`hermes_cli/config.py`** — 将所有 `NEWPLAT_*` 变量添加到 `_EXTRA_ENV_KEYS`
|
||||
2. **`hermes_cli/gateway.py`** — 在 `_PLATFORMS` 列表中添加条目,包含 key、label、emoji、token_var、setup_instructions 和 vars
|
||||
3. **`hermes_cli/platforms.py`** — 添加带 label 和 default_toolset 的 `PlatformInfo` 条目(供 `skills_config` 和 `tools_config` TUI 使用)
|
||||
4. **`hermes_cli/setup.py`** — 添加 `_setup_newplat()` 函数(可委托给 `gateway.py`)并将元组添加到消息平台列表
|
||||
5. **`hermes_cli/status.py`** — 添加平台检测条目:`"NewPlat": ("NEWPLAT_TOKEN", "NEWPLAT_HOME_CHANNEL")`
|
||||
6. **`hermes_cli/dump.py`** — 将 `"newplat": "NEWPLAT_TOKEN"` 添加到平台检测字典
|
||||
|
||||
### 7. 工具
|
||||
|
||||
1. **`tools/send_message_tool.py`** — 将 `"newplat": Platform.NEWPLAT` 添加到平台映射
|
||||
2. **`tools/cronjob_tools.py`** — 将 `newplat` 添加到投递目标描述字符串
|
||||
|
||||
### 8. Toolset
|
||||
|
||||
1. **`toolsets.py`** — 添加带 `_HERMES_CORE_TOOLS` 的 `"hermes-newplat"` toolset 定义
|
||||
2. **`toolsets.py`** — 将 `"hermes-newplat"` 添加到 `"hermes-gateway"` 的 includes 列表
|
||||
|
||||
### 9. 可选:平台提示
|
||||
|
||||
**`agent/prompt_builder.py`** — 如果你的平台有特定渲染限制(不支持 markdown、消息长度限制等),在 `_PLATFORM_HINTS` 字典中添加条目。这会将平台专属指导注入系统 prompt:
|
||||
|
||||
```python
|
||||
_PLATFORM_HINTS = {
|
||||
# ...
|
||||
"newplat": (
|
||||
"You are chatting via NewPlat. It supports markdown formatting "
|
||||
"but has a 4000-character message limit."
|
||||
),
|
||||
}
|
||||
```
|
||||
|
||||
并非所有平台都需要提示 — 仅在 agent 行为应有所不同时添加。
|
||||
|
||||
### 10. 测试
|
||||
|
||||
创建 `tests/gateway/test_newplat.py`,覆盖:
|
||||
|
||||
- 从配置构建适配器
|
||||
- 消息事件构建
|
||||
- 发送方法(mock 外部 API)
|
||||
- 平台专属功能(加密、路由等)
|
||||
|
||||
### 11. 文档
|
||||
|
||||
| 文件 | 需添加内容 |
|
||||
|------|-------------|
|
||||
| `website/docs/user-guide/messaging/newplat.md` | 完整的平台设置页面 |
|
||||
| `website/docs/user-guide/messaging/index.md` | 平台对比表、架构图、toolset 表、安全章节、下一步链接 |
|
||||
| `website/docs/reference/environment-variables.md` | 所有 NEWPLAT_* 环境变量 |
|
||||
| `website/docs/reference/toolsets-reference.md` | hermes-newplat toolset |
|
||||
| `website/docs/integrations/index.md` | 平台链接 |
|
||||
| `website/sidebars.ts` | 文档页面的侧边栏条目 |
|
||||
| `website/docs/developer-guide/architecture.md` | 适配器数量 + 列表 |
|
||||
| `website/docs/developer-guide/gateway-internals.md` | 适配器文件列表 |
|
||||
|
||||
## 一致性审计
|
||||
|
||||
在将新平台 PR 标记为完成之前,对照已有平台进行一致性审计:
|
||||
|
||||
```bash
|
||||
# 查找所有提及参考平台的 .py 文件
|
||||
search_files "bluebubbles" output_mode="files_only" file_glob="*.py"
|
||||
|
||||
# 查找所有提及新平台的 .py 文件
|
||||
search_files "newplat" output_mode="files_only" file_glob="*.py"
|
||||
|
||||
# 在第一个集合中但不在第二个集合中的文件是潜在的遗漏点
|
||||
```
|
||||
|
||||
对 `.md` 和 `.ts` 文件重复上述操作。逐一排查每个遗漏点 — 是平台枚举(需要更新)还是平台专属引用(可跳过)?
|
||||
|
||||
## 常见模式
|
||||
|
||||
### 长轮询适配器
|
||||
|
||||
如果你的适配器使用长轮询(如 Telegram 或 Weixin),使用轮询循环任务:
|
||||
|
||||
```python
|
||||
async def connect(self):
|
||||
self._poll_task = asyncio.create_task(self._poll_loop())
|
||||
self._mark_connected()
|
||||
|
||||
async def _poll_loop(self):
|
||||
while self._running:
|
||||
messages = await self._fetch_updates()
|
||||
for msg in messages:
|
||||
await self.handle_message(self._build_event(msg))
|
||||
```
|
||||
|
||||
### 回调/Webhook 适配器
|
||||
|
||||
如果平台将消息推送到你的端点(如 WeCom 回调),运行 HTTP 服务器:
|
||||
|
||||
```python
|
||||
async def connect(self):
|
||||
self._app = web.Application()
|
||||
self._app.router.add_post("/callback", self._handle_callback)
|
||||
# ... 启动 aiohttp 服务器
|
||||
self._mark_connected()
|
||||
|
||||
async def _handle_callback(self, request):
|
||||
event = self._build_event(await request.text())
|
||||
await self._message_queue.put(event)
|
||||
return web.Response(text="success") # 立即确认
|
||||
```
|
||||
|
||||
对于有严格响应截止时间的平台(例如 WeCom 的 5 秒限制),始终立即确认,稍后通过 API 主动投递 agent 的回复。Agent 会话运行 3–30 分钟 — 在回调响应窗口内内联回复是不可行的。
|
||||
|
||||
### Token 锁
|
||||
|
||||
如果适配器持有带唯一凭据的持久连接,添加作用域锁以防止两个配置文件使用相同凭据:
|
||||
|
||||
```python
|
||||
from gateway.status import acquire_scoped_lock, release_scoped_lock
|
||||
|
||||
async def connect(self):
|
||||
if not acquire_scoped_lock("newplat", self._token):
|
||||
logger.error("Token already in use by another profile")
|
||||
return False
|
||||
# ... 连接
|
||||
|
||||
async def disconnect(self):
|
||||
release_scoped_lock("newplat", self._token)
|
||||
```
|
||||
|
||||
## 参考实现
|
||||
|
||||
| 适配器 | 模式 | 复杂度 | 适合参考的场景 |
|
||||
|---------|---------|------------|-------------------|
|
||||
| `bluebubbles.py` | REST + webhook | 中 | 简单 REST API 集成 |
|
||||
| `weixin.py` | 长轮询 + CDN | 高 | 媒体处理、加密 |
|
||||
| `wecom_callback.py` | 回调/webhook | 中 | HTTP 服务器、AES 加密、多应用 |
|
||||
| `telegram.py` | 长轮询 + Bot API | 高 | 支持群组、线程的全功能适配器 |
|
||||
@ -0,0 +1,459 @@
|
||||
---
|
||||
sidebar_position: 5
|
||||
title: "添加 Provider"
|
||||
description: "如何向 Hermes Agent 添加新的推理 provider——认证、运行时解析、CLI 流程、适配器、测试与文档"
|
||||
---
|
||||
|
||||
# 添加 Provider
|
||||
|
||||
Hermes 已经可以通过自定义 provider 路径与任何 OpenAI 兼容的端点通信。除非你需要为某个服务提供一流的用户体验,否则不要添加内置 provider:
|
||||
|
||||
- provider 专属的认证或 token 刷新
|
||||
- 精选的模型目录
|
||||
- setup / `hermes model` 菜单条目
|
||||
- 用于 `provider:model` 语法的 provider 别名
|
||||
- 需要适配器的非 OpenAI API 格式
|
||||
|
||||
如果该 provider 只是"另一个 OpenAI 兼容的 base URL 和 API key",一个命名的自定义 provider 可能就足够了。
|
||||
|
||||
## 心智模型
|
||||
|
||||
内置 provider 需要在几个层面保持一致:
|
||||
|
||||
1. `hermes_cli/auth.py` 决定如何查找凭据。
|
||||
2. `hermes_cli/runtime_provider.py` 将其转换为运行时数据:
|
||||
- `provider`
|
||||
- `api_mode`
|
||||
- `base_url`
|
||||
- `api_key`
|
||||
- `source`
|
||||
3. `run_agent.py` 使用 `api_mode` 决定如何构建和发送请求。
|
||||
4. `hermes_cli/models.py` 和 `hermes_cli/main.py` 使 provider 在 CLI 中可见。(`hermes_cli/setup.py` 自动委托给 `main.py`——无需在此处做任何修改。)
|
||||
5. `agent/auxiliary_client.py` 和 `agent/model_metadata.py` 保持辅助任务和 token 预算正常运作。
|
||||
|
||||
核心抽象是 `api_mode`。
|
||||
|
||||
- 大多数 provider 使用 `chat_completions`。
|
||||
- Codex 使用 `codex_responses`。
|
||||
- Anthropic 使用 `anthropic_messages`。
|
||||
- 新的非 OpenAI 协议通常意味着需要添加新的适配器和新的 `api_mode` 分支。
|
||||
|
||||
## 首先选择实现路径
|
||||
|
||||
### 路径 A——OpenAI 兼容 provider
|
||||
|
||||
当 provider 接受标准 chat-completions 风格的请求时使用此路径。
|
||||
|
||||
典型工作:
|
||||
|
||||
- 添加认证元数据
|
||||
- 添加模型目录 / 别名
|
||||
- 添加运行时解析
|
||||
- 添加 CLI 菜单接线
|
||||
- 添加辅助模型默认值
|
||||
- 添加测试和用户文档
|
||||
|
||||
通常不需要新的适配器或新的 `api_mode`。
|
||||
|
||||
### 路径 B——原生 provider
|
||||
|
||||
当 provider 的行为与 OpenAI chat completions 不同时使用此路径。
|
||||
|
||||
当前代码库中的示例:
|
||||
|
||||
- `codex_responses`
|
||||
- `anthropic_messages`
|
||||
|
||||
此路径包含路径 A 的所有内容,另加:
|
||||
|
||||
- `agent/` 中的 provider 适配器
|
||||
- `run_agent.py` 中用于请求构建、分发、用量提取、中断处理和响应规范化的分支
|
||||
- 适配器测试
|
||||
|
||||
## 文件清单
|
||||
|
||||
### 每个内置 provider 都必须修改
|
||||
|
||||
1. `hermes_cli/auth.py`
|
||||
2. `hermes_cli/models.py`
|
||||
3. `hermes_cli/runtime_provider.py`
|
||||
4. `hermes_cli/main.py`
|
||||
5. `agent/auxiliary_client.py`
|
||||
6. `agent/model_metadata.py`
|
||||
7. 测试
|
||||
8. `website/docs/` 下的用户文档
|
||||
|
||||
:::tip
|
||||
`hermes_cli/setup.py` **无需**修改。setup 向导将 provider/model 选择委托给 `main.py` 中的 `select_provider_and_model()`——在那里添加的任何 provider 都会自动出现在 `hermes setup` 中。
|
||||
:::
|
||||
|
||||
### 原生 / 非 OpenAI provider 额外需要
|
||||
|
||||
10. `agent/<provider>_adapter.py`
|
||||
11. `run_agent.py`
|
||||
12. 如果需要 provider SDK,则修改 `pyproject.toml`
|
||||
|
||||
## 快速路径:简单 API key provider
|
||||
|
||||
如果你的 provider 只是一个使用单个 API key 进行认证的 OpenAI 兼容端点,则无需修改 `auth.py`、`runtime_provider.py`、`main.py` 或下面完整清单中的任何其他文件。
|
||||
|
||||
你只需要:
|
||||
|
||||
1. 在 `plugins/model-providers/<your-provider>/` 下创建一个插件目录,包含:
|
||||
- `__init__.py`——在模块级别调用 `register_provider(profile)`
|
||||
- `plugin.yaml`——清单文件(name、kind: model-provider、version、description)
|
||||
2. 就这些。Provider 插件在任何代码首次调用 `get_provider_profile()` 或 `list_providers()` 时自动加载——捆绑插件(本仓库)和位于 `$HERMES_HOME/plugins/model-providers/` 的用户插件都会被加载。
|
||||
|
||||
当你添加一个插件并调用 `register_provider()` 时,以下内容会自动接线:
|
||||
|
||||
1. `auth.py` 中的 `PROVIDER_REGISTRY` 条目(凭据解析、环境变量查找)
|
||||
2. `api_mode` 设置为 `chat_completions`
|
||||
3. `base_url` 从配置或声明的环境变量中获取
|
||||
4. 按优先级顺序检查 `env_vars` 以获取 API key
|
||||
5. 为该 provider 注册 `fallback_models` 列表
|
||||
6. `--provider` CLI 标志接受该 provider id
|
||||
7. `hermes model` 菜单包含该 provider
|
||||
8. `hermes setup` 向导自动委托给 `main.py`
|
||||
9. `provider:model` 别名语法正常工作
|
||||
10. 运行时解析器返回正确的 `base_url` 和 `api_key`
|
||||
11. `--provider <name>` CLI 标志接受该 provider id
|
||||
12. 回退模型激活可以干净地切换到该 provider
|
||||
|
||||
位于 `$HERMES_HOME/plugins/model-providers/<name>/` 的用户插件会覆盖同名的捆绑插件(`register_provider()` 中后写者获胜)——因此第三方可以在不编辑本仓库的情况下对任何内置 profile 进行 monkey-patch 或替换。
|
||||
|
||||
参见 `plugins/model-providers/nvidia/` 或 `plugins/model-providers/gmi/` 作为模板,以及完整的 [Model Provider Plugin 指南](/developer-guide/model-provider-plugin),了解字段参考、hook 用法和端到端示例。
|
||||
|
||||
## 完整路径:OAuth 和复杂 provider
|
||||
|
||||
当你的 provider 需要以下任何内容时,使用下面的完整清单:
|
||||
|
||||
- OAuth 或 token 刷新(Nous Portal、Codex、Google Gemini、Qwen Portal、Copilot)
|
||||
- 需要新适配器的非 OpenAI API 格式(Anthropic Messages、Codex Responses)
|
||||
- 自定义端点检测或多区域探测(z.ai、Kimi)
|
||||
- 精选的静态模型目录或实时 `/models` 获取
|
||||
- 带有特定认证流程的 provider 专属 `hermes model` 菜单条目
|
||||
|
||||
## 第 1 步:选择一个规范的 provider id
|
||||
|
||||
选择一个 provider id 并在所有地方使用它。
|
||||
|
||||
代码库中的示例:
|
||||
|
||||
- `openai-codex`
|
||||
- `kimi-coding`
|
||||
- `minimax-cn`
|
||||
|
||||
该 id 应出现在:
|
||||
|
||||
- `hermes_cli/auth.py` 中的 `PROVIDER_REGISTRY`
|
||||
- `hermes_cli/models.py` 中的 `_PROVIDER_LABELS`
|
||||
- `hermes_cli/auth.py` 和 `hermes_cli/models.py` 中的 `_PROVIDER_ALIASES`
|
||||
- `hermes_cli/main.py` 中的 CLI `--provider` 选项
|
||||
- setup / 模型选择分支
|
||||
- 辅助模型默认值
|
||||
- 测试
|
||||
|
||||
如果这些文件之间的 id 不一致,provider 会感觉只接了一半线:认证可能正常,而 `/model`、setup 或运行时解析会静默地遗漏它。
|
||||
|
||||
## 第 2 步:在 `hermes_cli/auth.py` 中添加认证元数据
|
||||
|
||||
对于 API key provider,在 `PROVIDER_REGISTRY` 中添加一个 `ProviderConfig` 条目,包含:
|
||||
|
||||
- `id`
|
||||
- `name`
|
||||
- `auth_type="api_key"`
|
||||
- `inference_base_url`
|
||||
- `api_key_env_vars`
|
||||
- 可选的 `base_url_env_var`
|
||||
|
||||
同时在 `_PROVIDER_ALIASES` 中添加别名。
|
||||
|
||||
使用现有 provider 作为模板:
|
||||
|
||||
- 简单 API key 路径:Z.AI、MiniMax
|
||||
- 带端点检测的 API key 路径:Kimi、Z.AI
|
||||
- 原生 token 解析:Anthropic
|
||||
- OAuth / auth-store 路径:Nous、OpenAI Codex
|
||||
|
||||
需要在此回答的问题:
|
||||
|
||||
- Hermes 应该检查哪些环境变量,按什么优先级顺序?
|
||||
- provider 是否需要 base URL 覆盖?
|
||||
- 是否需要端点探测或 token 刷新?
|
||||
- 当凭据缺失时,认证错误应该显示什么?
|
||||
|
||||
如果 provider 需要的不仅仅是"查找 API key",请添加专用的凭据解析器,而不是将逻辑塞进不相关的分支。
|
||||
|
||||
## 第 3 步:在 `hermes_cli/models.py` 中添加模型目录和别名
|
||||
|
||||
更新 provider 目录,使 provider 在菜单和 `provider:model` 语法中正常工作。
|
||||
|
||||
典型修改:
|
||||
|
||||
- `_PROVIDER_MODELS`
|
||||
- `_PROVIDER_LABELS`
|
||||
- `_PROVIDER_ALIASES`
|
||||
- `list_available_providers()` 中的 provider 显示顺序
|
||||
- 如果 provider 支持实时 `/models` 获取,则修改 `provider_model_ids()`
|
||||
|
||||
如果 provider 提供实时模型列表,优先使用它,并将 `_PROVIDER_MODELS` 保留为静态回退。
|
||||
|
||||
此文件也是使以下输入正常工作的关键:
|
||||
|
||||
```text
|
||||
anthropic:claude-sonnet-4-6
|
||||
kimi:model-name
|
||||
```
|
||||
|
||||
如果此处缺少别名,provider 可能认证正常,但在 `/model` 解析中仍然失败。
|
||||
|
||||
## 第 4 步:在 `hermes_cli/runtime_provider.py` 中解析运行时数据
|
||||
|
||||
`resolve_runtime_provider()` 是 CLI、gateway(网关)、cron、ACP 和辅助客户端共用的路径。
|
||||
|
||||
添加一个分支,至少返回包含以下内容的字典:
|
||||
|
||||
```python
|
||||
{
|
||||
"provider": "your-provider",
|
||||
"api_mode": "chat_completions", # or your native mode
|
||||
"base_url": "https://...",
|
||||
"api_key": "...",
|
||||
"source": "env|portal|auth-store|explicit",
|
||||
"requested_provider": requested_provider,
|
||||
}
|
||||
```
|
||||
|
||||
如果 provider 与 OpenAI 兼容,`api_mode` 通常应保持为 `chat_completions`。
|
||||
|
||||
注意 API key 优先级。Hermes 已经包含避免将 OpenRouter key 泄露给无关端点的逻辑。新 provider 应同样明确地指定哪个 key 对应哪个 base URL。
|
||||
|
||||
## 第 5 步:在 `hermes_cli/main.py` 中接线 CLI
|
||||
|
||||
在交互式 `hermes model` 流程中出现之前,provider 是不可发现的。
|
||||
|
||||
在 `hermes_cli/main.py` 中更新以下内容:
|
||||
|
||||
- `provider_labels` 字典
|
||||
- `select_provider_and_model()` 中的 `providers` 列表
|
||||
- provider 分发(`if selected_provider == ...`)
|
||||
- `--provider` 参数选项
|
||||
- 如果 provider 支持登录/登出流程,则更新相应选项
|
||||
- 一个 `_model_flow_<provider>()` 函数,或者如果适用则复用 `_model_flow_api_key_provider()`
|
||||
|
||||
:::tip
|
||||
`hermes_cli/setup.py` 无需修改——它调用 `main.py` 中的 `select_provider_and_model()`,因此你的新 provider 会自动出现在 `hermes model` 和 `hermes setup` 中。
|
||||
:::
|
||||
|
||||
## 第 6 步:保持辅助调用正常工作
|
||||
|
||||
这里有两个文件需要关注:
|
||||
|
||||
### `agent/auxiliary_client.py`
|
||||
|
||||
如果这是一个直接 API key provider,在 `_API_KEY_PROVIDER_AUX_MODELS` 中添加一个廉价/快速的默认辅助模型。
|
||||
|
||||
辅助任务包括:
|
||||
|
||||
- 视觉摘要
|
||||
- 网页提取摘要
|
||||
- 上下文压缩摘要
|
||||
- 会话搜索摘要
|
||||
- 记忆刷新
|
||||
|
||||
如果 provider 没有合理的辅助默认值,辅助任务可能会严重回退,或意外使用昂贵的主模型。
|
||||
|
||||
### `agent/model_metadata.py`
|
||||
|
||||
为 provider 的模型添加上下文长度,以保持 token 预算、压缩阈值和限制的合理性。
|
||||
|
||||
## 第 7 步:如果 provider 是原生的,添加适配器和 `run_agent.py` 支持
|
||||
|
||||
如果 provider 不是普通的 chat completions,将 provider 专属逻辑隔离在 `agent/<provider>_adapter.py` 中。
|
||||
|
||||
保持 `run_agent.py` 专注于编排。它应该调用适配器辅助函数,而不是在整个文件中内联构建 provider 请求载荷。
|
||||
|
||||
原生 provider 通常需要在以下地方进行工作:
|
||||
|
||||
### 新适配器文件
|
||||
|
||||
典型职责:
|
||||
|
||||
- 构建 SDK / HTTP 客户端
|
||||
- 解析 token
|
||||
- 将 OpenAI 风格的对话消息转换为 provider 的请求格式
|
||||
- 如有需要,转换工具 schema
|
||||
- 将 provider 响应规范化为 `run_agent.py` 期望的格式
|
||||
- 提取用量和 finish-reason 数据
|
||||
|
||||
### `run_agent.py`
|
||||
|
||||
搜索 `api_mode` 并审计每个切换点。至少验证:
|
||||
|
||||
- `__init__` 选择了新的 `api_mode`
|
||||
- 客户端构建对该 provider 有效
|
||||
- `_build_api_kwargs()` 知道如何格式化请求
|
||||
- `_interruptible_api_call()` 分发到正确的客户端调用
|
||||
- 中断 / 客户端重建路径正常工作
|
||||
- 响应验证接受该 provider 的格式
|
||||
- finish-reason 提取正确
|
||||
- token 用量提取正确
|
||||
- 回退模型激活可以干净地切换到新 provider
|
||||
- 摘要生成和记忆刷新路径仍然正常工作
|
||||
|
||||
同时在 `run_agent.py` 中搜索 `self.client.`。任何假设标准 OpenAI 客户端存在的代码路径,在原生 provider 使用不同客户端对象或 `self.client = None` 时都可能中断。
|
||||
|
||||
### Prompt 缓存和 provider 专属请求字段
|
||||
|
||||
Prompt(提示词)缓存和 provider 专属的调节项很容易出现回归。
|
||||
|
||||
代码库中已有的示例:
|
||||
|
||||
- Anthropic 有原生的 prompt 缓存路径
|
||||
- OpenRouter 获得 provider 路由字段
|
||||
- 并非每个 provider 都应该接收每个请求端选项
|
||||
|
||||
添加原生 provider 时,仔细检查 Hermes 只向该 provider 发送它实际理解的字段。
|
||||
|
||||
## 第 8 步:测试
|
||||
|
||||
至少修改保护 provider 接线的测试。
|
||||
|
||||
常见位置:
|
||||
|
||||
- `tests/test_runtime_provider_resolution.py`
|
||||
- `tests/test_cli_provider_resolution.py`
|
||||
- `tests/test_cli_model_command.py`
|
||||
- `tests/test_setup_model_selection.py`
|
||||
- `tests/test_provider_parity.py`
|
||||
- `tests/test_run_agent.py`
|
||||
- 原生 provider 的 `tests/test_<provider>_adapter.py`
|
||||
|
||||
对于仅文档示例,确切的文件集可能不同。重点是覆盖:
|
||||
|
||||
- 认证解析
|
||||
- CLI 菜单 / provider 选择
|
||||
- 运行时 provider 解析
|
||||
- agent 执行路径
|
||||
- `provider:model` 解析
|
||||
- 任何适配器专属的消息转换
|
||||
|
||||
使用禁用 xdist 的方式运行测试:
|
||||
|
||||
```bash
|
||||
source venv/bin/activate
|
||||
python -m pytest tests/test_runtime_provider_resolution.py tests/test_cli_provider_resolution.py tests/test_cli_model_command.py tests/test_setup_model_selection.py -n0 -q
|
||||
```
|
||||
|
||||
对于更深层的修改,在推送前运行完整测试套件:
|
||||
|
||||
```bash
|
||||
source venv/bin/activate
|
||||
python -m pytest tests/ -n0 -q
|
||||
```
|
||||
|
||||
## 第 9 步:实时验证
|
||||
|
||||
测试通过后,运行真实的冒烟测试。
|
||||
|
||||
```bash
|
||||
source venv/bin/activate
|
||||
python -m hermes_cli.main chat -q "Say hello" --provider your-provider --model your-model
|
||||
```
|
||||
|
||||
如果你修改了菜单,也测试交互式流程:
|
||||
|
||||
```bash
|
||||
source venv/bin/activate
|
||||
python -m hermes_cli.main model
|
||||
python -m hermes_cli.main setup
|
||||
```
|
||||
|
||||
对于原生 provider,至少也验证一次工具调用,而不仅仅是纯文本响应。
|
||||
|
||||
## 第 10 步:更新用户文档
|
||||
|
||||
如果该 provider 打算作为一流选项发布,也更新用户文档:
|
||||
|
||||
- `website/docs/getting-started/quickstart.md`
|
||||
- `website/docs/user-guide/configuration.md`
|
||||
- `website/docs/reference/environment-variables.md`
|
||||
|
||||
开发者可以完美地接线 provider,但仍然让用户无法发现所需的环境变量或 setup 流程。
|
||||
|
||||
## OpenAI 兼容 provider 清单
|
||||
|
||||
如果 provider 是标准 chat completions,使用此清单。
|
||||
|
||||
- [ ] 在 `hermes_cli/auth.py` 中添加 `ProviderConfig`
|
||||
- [ ] 在 `hermes_cli/auth.py` 和 `hermes_cli/models.py` 中添加别名
|
||||
- [ ] 在 `hermes_cli/models.py` 中添加模型目录
|
||||
- [ ] 在 `hermes_cli/runtime_provider.py` 中添加运行时分支
|
||||
- [ ] 在 `hermes_cli/main.py` 中添加 CLI 接线(setup.py 自动继承)
|
||||
- [ ] 在 `agent/auxiliary_client.py` 中添加辅助模型
|
||||
- [ ] 在 `agent/model_metadata.py` 中添加上下文长度
|
||||
- [ ] 更新运行时 / CLI 测试
|
||||
- [ ] 更新用户文档
|
||||
|
||||
## 原生 provider 清单
|
||||
|
||||
当 provider 需要新的协议路径时使用此清单。
|
||||
|
||||
- [ ] OpenAI 兼容清单中的所有内容
|
||||
- [ ] 在 `agent/<provider>_adapter.py` 中添加适配器
|
||||
- [ ] 在 `run_agent.py` 中支持新的 `api_mode`
|
||||
- [ ] 中断 / 重建路径正常工作
|
||||
- [ ] 用量和 finish-reason 提取正常工作
|
||||
- [ ] 回退路径正常工作
|
||||
- [ ] 添加适配器测试
|
||||
- [ ] 实时冒烟测试通过
|
||||
|
||||
## 常见陷阱
|
||||
|
||||
### 1. 将 provider 添加到 auth 但未添加到模型解析
|
||||
|
||||
这会导致凭据解析正确,而 `/model` 和 `provider:model` 输入失败。
|
||||
|
||||
### 2. 忘记 `config["model"]` 可以是字符串或字典
|
||||
|
||||
大量 provider 选择代码必须对两种形式进行规范化。
|
||||
|
||||
### 3. 假设必须使用内置 provider
|
||||
|
||||
如果该服务只是 OpenAI 兼容的,自定义 provider 可能已经以更少的维护成本解决了用户问题。
|
||||
|
||||
### 4. 忘记辅助路径
|
||||
|
||||
主聊天路径可能正常工作,而摘要、记忆刷新或视觉辅助失败,因为辅助路由从未更新。
|
||||
|
||||
### 5. 原生 provider 分支隐藏在 `run_agent.py` 中
|
||||
|
||||
搜索 `api_mode` 和 `self.client.`。不要假设显而易见的请求路径是唯一的。
|
||||
|
||||
### 6. 将 OpenRouter 专属字段发送给其他 provider
|
||||
|
||||
provider 路由等字段只属于支持它们的 provider。
|
||||
|
||||
### 7. 更新了 `hermes model` 但未更新 `hermes setup`
|
||||
|
||||
两个流程都需要了解该 provider。
|
||||
|
||||
## 实现时的好搜索目标
|
||||
|
||||
如果你在寻找 provider 涉及的所有位置,搜索以下符号:
|
||||
|
||||
- `PROVIDER_REGISTRY`
|
||||
- `_PROVIDER_ALIASES`
|
||||
- `_PROVIDER_MODELS`
|
||||
- `resolve_runtime_provider`
|
||||
- `_model_flow_`
|
||||
- `select_provider_and_model`
|
||||
- `api_mode`
|
||||
- `_API_KEY_PROVIDER_AUX_MODELS`
|
||||
- `self.client.`
|
||||
|
||||
## 相关文档
|
||||
|
||||
- [Provider 运行时解析](./provider-runtime.md)
|
||||
- [架构](./architecture.md)
|
||||
- [贡献指南](./contributing.md)
|
||||
@ -0,0 +1,209 @@
|
||||
---
|
||||
sidebar_position: 2
|
||||
title: "添加工具"
|
||||
description: "如何向 Hermes Agent 添加新工具——schema、handler、注册与 toolset"
|
||||
---
|
||||
|
||||
# 添加工具
|
||||
|
||||
在编写工具之前,先问自己:**这是否应该是一个 [skill](creating-skills.md)?**
|
||||
|
||||
:::warning 仅限内置核心工具
|
||||
本页面用于向仓库本身添加 **Hermes 内置工具**。
|
||||
如果你想要个人专用、项目本地或其他自定义工具,而不修改 Hermes 核心,请使用插件方式:
|
||||
|
||||
- [插件](/user-guide/features/plugins)
|
||||
- [构建 Hermes 插件](/guides/build-a-hermes-plugin)
|
||||
|
||||
大多数自定义工具创建场景默认使用插件。只有当你明确希望在 `tools/` 和 `toolsets.py` 中发布新的内置工具时,才遵循本页面。
|
||||
:::
|
||||
|
||||
以下情况应创建 **Skill**:该能力可以通过指令 + shell 命令 + 现有工具来实现(如 arXiv 搜索、git 工作流、Docker 管理、PDF 处理)。
|
||||
|
||||
以下情况应创建 **Tool**:需要与 API 密钥进行端到端集成、自定义处理逻辑、二进制数据处理或流式传输(如浏览器自动化、TTS、视觉分析)。
|
||||
|
||||
## 概述
|
||||
|
||||
添加一个工具涉及 **2 个文件**:
|
||||
|
||||
1. **`tools/your_tool.py`** — handler、schema、check 函数、`registry.register()` 调用
|
||||
2. **`toolsets.py`** — 将工具名称添加到 `_HERMES_CORE_TOOLS`(或特定 toolset)
|
||||
|
||||
任何包含顶层 `registry.register()` 调用的 `tools/*.py` 文件都会在启动时被自动发现——无需手动维护导入列表。
|
||||
|
||||
## 第一步:创建内置工具文件
|
||||
|
||||
每个工具文件遵循相同的结构:
|
||||
|
||||
```python
|
||||
# tools/weather_tool.py
|
||||
"""Weather Tool -- look up current weather for a location."""
|
||||
|
||||
import json
|
||||
import os
|
||||
import logging
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
# --- Availability check ---
|
||||
|
||||
def check_weather_requirements() -> bool:
|
||||
"""Return True if the tool's dependencies are available."""
|
||||
return bool(os.getenv("WEATHER_API_KEY"))
|
||||
|
||||
|
||||
# --- Handler ---
|
||||
|
||||
def weather_tool(location: str, units: str = "metric") -> str:
|
||||
"""Fetch weather for a location. Returns JSON string."""
|
||||
api_key = os.getenv("WEATHER_API_KEY")
|
||||
if not api_key:
|
||||
return json.dumps({"error": "WEATHER_API_KEY not configured"})
|
||||
try:
|
||||
# ... call weather API ...
|
||||
return json.dumps({"location": location, "temp": 22, "units": units})
|
||||
except Exception as e:
|
||||
return json.dumps({"error": str(e)})
|
||||
|
||||
|
||||
# --- Schema ---
|
||||
|
||||
WEATHER_SCHEMA = {
|
||||
"name": "weather",
|
||||
"description": "Get current weather for a location.",
|
||||
"parameters": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"location": {
|
||||
"type": "string",
|
||||
"description": "City name or coordinates (e.g. 'London' or '51.5,-0.1')"
|
||||
},
|
||||
"units": {
|
||||
"type": "string",
|
||||
"enum": ["metric", "imperial"],
|
||||
"description": "Temperature units (default: metric)",
|
||||
"default": "metric"
|
||||
}
|
||||
},
|
||||
"required": ["location"]
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
# --- Registration ---
|
||||
|
||||
from tools.registry import registry
|
||||
|
||||
registry.register(
|
||||
name="weather",
|
||||
toolset="weather",
|
||||
schema=WEATHER_SCHEMA,
|
||||
handler=lambda args, **kw: weather_tool(
|
||||
location=args.get("location", ""),
|
||||
units=args.get("units", "metric")),
|
||||
check_fn=check_weather_requirements,
|
||||
requires_env=["WEATHER_API_KEY"],
|
||||
)
|
||||
```
|
||||
|
||||
### 关键规则
|
||||
|
||||
:::danger 重要
|
||||
- Handler **必须**返回 JSON 字符串(通过 `json.dumps()`),不得返回原始 dict
|
||||
- 错误**必须**以 `{"error": "message"}` 形式返回,不得抛出异常
|
||||
- `check_fn` 在构建工具定义时被调用——若返回 `False`,该工具将被静默排除
|
||||
- `handler` 接收 `(args: dict, **kwargs)`,其中 `args` 是 LLM 的工具调用参数
|
||||
:::
|
||||
|
||||
## 第二步:将内置工具添加到 Toolset
|
||||
|
||||
在 `toolsets.py` 中添加工具名称:
|
||||
|
||||
```python
|
||||
# If it should be available on all platforms (CLI + messaging):
|
||||
_HERMES_CORE_TOOLS = [
|
||||
...
|
||||
"weather", # <-- add here
|
||||
]
|
||||
|
||||
# Or create a new standalone toolset:
|
||||
"weather": {
|
||||
"description": "Weather lookup tools",
|
||||
"tools": ["weather"],
|
||||
"includes": []
|
||||
},
|
||||
```
|
||||
|
||||
## ~~第三步:添加发现导入~~(不再需要)
|
||||
|
||||
包含顶层 `registry.register()` 调用的工具模块会由 `tools/registry.py` 中的 `discover_builtin_tools()` 自动发现。无需手动维护导入列表——只需在 `tools/` 中创建文件,启动时即可自动加载。
|
||||
|
||||
## 异步 Handler
|
||||
|
||||
如果你的 handler 需要异步代码,使用 `is_async=True` 标记:
|
||||
|
||||
```python
|
||||
async def weather_tool_async(location: str) -> str:
|
||||
async with aiohttp.ClientSession() as session:
|
||||
...
|
||||
return json.dumps(result)
|
||||
|
||||
registry.register(
|
||||
name="weather",
|
||||
toolset="weather",
|
||||
schema=WEATHER_SCHEMA,
|
||||
handler=lambda args, **kw: weather_tool_async(args.get("location", "")),
|
||||
check_fn=check_weather_requirements,
|
||||
is_async=True, # registry calls _run_async() automatically
|
||||
)
|
||||
```
|
||||
|
||||
registry 会透明地处理异步桥接——你无需自己调用 `asyncio.run()`。
|
||||
|
||||
## 需要 task_id 的 Handler
|
||||
|
||||
管理每个会话状态的工具通过 `**kwargs` 接收 `task_id`:
|
||||
|
||||
```python
|
||||
def _handle_weather(args, **kw):
|
||||
task_id = kw.get("task_id")
|
||||
return weather_tool(args.get("location", ""), task_id=task_id)
|
||||
|
||||
registry.register(
|
||||
name="weather",
|
||||
...
|
||||
handler=_handle_weather,
|
||||
)
|
||||
```
|
||||
|
||||
## Agent 循环拦截工具
|
||||
|
||||
某些工具(`todo`、`memory`、`session_search`、`delegate_task`)需要访问每个会话的 agent 状态。这些工具在到达 registry 之前会被 `run_agent.py` 拦截。registry 仍然保存它们的 schema,但如果绕过拦截,`dispatch()` 会返回一个回退错误。
|
||||
|
||||
## 可选:Setup Wizard 集成
|
||||
|
||||
如果你的工具需要 API 密钥,将其添加到 `hermes_cli/config.py`:
|
||||
|
||||
```python
|
||||
OPTIONAL_ENV_VARS = {
|
||||
...
|
||||
"WEATHER_API_KEY": {
|
||||
"description": "Weather API key for weather lookup",
|
||||
"prompt": "Weather API key",
|
||||
"url": "https://weatherapi.com/",
|
||||
"tools": ["weather"],
|
||||
"password": True,
|
||||
},
|
||||
}
|
||||
```
|
||||
|
||||
## 检查清单
|
||||
|
||||
- [ ] 已创建包含 handler、schema、check 函数和注册调用的工具文件
|
||||
- [ ] 已在 `toolsets.py` 中添加到适当的 toolset
|
||||
- [ ] 已确认该工具确实应为内置/核心工具而非插件
|
||||
- [ ] Handler 返回 JSON 字符串,错误以 `{"error": "..."}` 形式返回
|
||||
- [ ] 可选:已将 API 密钥添加到 `hermes_cli/config.py` 的 `OPTIONAL_ENV_VARS`
|
||||
- [ ] 可选:已添加到 `toolset_distributions.py` 以支持批量处理
|
||||
- [ ] 已通过 `hermes chat -q "Use the weather tool for London"` 测试
|
||||
@ -0,0 +1,239 @@
|
||||
---
|
||||
sidebar_position: 3
|
||||
title: "Agent Loop 内部机制"
|
||||
description: "AIAgent 执行流程、API 模式、工具、回调及回退行为的详细说明"
|
||||
---
|
||||
|
||||
# Agent Loop 内部机制
|
||||
|
||||
核心编排引擎是 `run_agent.py` 中的 `AIAgent` 类——这是一个大型文件(15k+ 行),负责处理从 prompt(提示词)组装到工具分发再到 provider 故障转移的所有逻辑。
|
||||
|
||||
## 核心职责
|
||||
|
||||
`AIAgent` 负责:
|
||||
|
||||
- 通过 `prompt_builder.py` 组装有效的系统 prompt 和工具 schema
|
||||
- 选择正确的 provider/API 模式(`chat_completions`、`codex_responses`、`anthropic_messages`)
|
||||
- 发起支持取消操作的可中断模型调用
|
||||
- 执行工具调用(顺序执行或通过线程池并发执行)
|
||||
- 以 OpenAI 消息格式维护对话历史
|
||||
- 处理压缩、重试和回退模型切换
|
||||
- 跨父 agent 和子 agent 追踪迭代预算
|
||||
- 在上下文丢失前将持久化内存刷写到磁盘
|
||||
|
||||
## 两个入口点
|
||||
|
||||
```python
|
||||
# 简单接口——返回最终响应字符串
|
||||
response = agent.chat("Fix the bug in main.py")
|
||||
|
||||
# 完整接口——返回包含消息、元数据、用量统计的 dict
|
||||
result = agent.run_conversation(
|
||||
user_message="Fix the bug in main.py",
|
||||
system_message=None, # 省略时自动构建
|
||||
conversation_history=None, # 省略时自动从 session 加载
|
||||
task_id="task_abc123"
|
||||
)
|
||||
```
|
||||
|
||||
`chat()` 是对 `run_conversation()` 的轻量封装,从结果 dict 中提取 `final_response` 字段。
|
||||
|
||||
## API 模式
|
||||
|
||||
Hermes 支持三种 API 执行模式,通过 provider 选择、显式参数和 base URL 启发式规则来确定:
|
||||
|
||||
| API 模式 | 用途 | 客户端类型 |
|
||||
|----------|------|-----------|
|
||||
| `chat_completions` | 兼容 OpenAI 的端点(OpenRouter、自定义及大多数 provider) | `openai.OpenAI` |
|
||||
| `codex_responses` | OpenAI Codex / Responses API | `openai.OpenAI`(使用 Responses 格式) |
|
||||
| `anthropic_messages` | 原生 Anthropic Messages API | 通过适配器使用 `anthropic.Anthropic` |
|
||||
|
||||
模式决定了消息的格式化方式、工具调用的结构、响应的解析方式,以及缓存/流式传输的工作方式。三种模式在 API 调用前后均收敛到相同的内部消息格式(OpenAI 风格的 `role`/`content`/`tool_calls` dict)。
|
||||
|
||||
**模式解析顺序:**
|
||||
1. 显式 `api_mode` 构造函数参数(最高优先级)
|
||||
2. Provider 特定检测(例如 `anthropic` provider → `anthropic_messages`)
|
||||
3. Base URL 启发式规则(例如 `api.anthropic.com` → `anthropic_messages`)
|
||||
4. 默认:`chat_completions`
|
||||
|
||||
## 单轮生命周期
|
||||
|
||||
agent loop 的每次迭代按以下顺序执行:
|
||||
|
||||
```text
|
||||
run_conversation()
|
||||
1. 若未提供则生成 task_id
|
||||
2. 将用户消息追加到对话历史
|
||||
3. 构建或复用已缓存的系统 prompt(prompt_builder.py)
|
||||
4. 检查是否需要预检压缩(上下文超过 50%)
|
||||
5. 从对话历史构建 API 消息
|
||||
- chat_completions:直接使用 OpenAI 格式
|
||||
- codex_responses:转换为 Responses API 输入项
|
||||
- anthropic_messages:通过 anthropic_adapter.py 转换
|
||||
6. 注入临时 prompt 层(预算警告、上下文压力提示)
|
||||
7. 若使用 Anthropic,应用 prompt 缓存标记
|
||||
8. 发起可中断的 API 调用(_interruptible_api_call)
|
||||
9. 解析响应:
|
||||
- 若有 tool_calls:执行工具,追加结果,回到步骤 5
|
||||
- 若为文本响应:持久化 session,按需刷写内存,返回
|
||||
```
|
||||
|
||||
### 消息格式
|
||||
|
||||
所有消息在内部均使用兼容 OpenAI 的格式:
|
||||
|
||||
```python
|
||||
{"role": "system", "content": "..."}
|
||||
{"role": "user", "content": "..."}
|
||||
{"role": "assistant", "content": "...", "tool_calls": [...]}
|
||||
{"role": "tool", "tool_call_id": "...", "content": "..."}
|
||||
```
|
||||
|
||||
推理内容(来自支持扩展思考的模型)存储在 `assistant_msg["reasoning"]` 中,并可选择通过 `reasoning_callback` 展示。
|
||||
|
||||
### 消息交替规则
|
||||
|
||||
agent loop 强制执行严格的消息角色交替规则:
|
||||
|
||||
- 系统消息之后:`User → Assistant → User → Assistant → ...`
|
||||
- 工具调用期间:`Assistant(含 tool_calls)→ Tool → Tool → ... → Assistant`
|
||||
- **不允许**连续出现两条 assistant 消息
|
||||
- **不允许**连续出现两条 user 消息
|
||||
- **只有** `tool` 角色可以连续出现(并行工具结果)
|
||||
|
||||
Provider 会验证这些序列,并拒绝格式错误的历史记录。
|
||||
|
||||
## 可中断的 API 调用
|
||||
|
||||
API 请求被封装在 `_interruptible_api_call()` 中,该方法在后台线程中执行实际的 HTTP 调用,同时监听中断事件:
|
||||
|
||||
```text
|
||||
┌────────────────────────────────────────────────────┐
|
||||
│ 主线程 API 线程 │
|
||||
│ │
|
||||
│ 等待: HTTP POST │
|
||||
│ - 响应就绪 ───▶ 发送至 provider │
|
||||
│ - 中断事件 │
|
||||
│ - 超时 │
|
||||
└────────────────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
当发生中断(用户发送新消息、`/stop` 命令或信号)时:
|
||||
- API 线程被放弃(响应被丢弃)
|
||||
- agent 可以处理新输入或干净地关闭
|
||||
- 不会将部分响应注入对话历史
|
||||
|
||||
## 工具执行
|
||||
|
||||
### 顺序执行与并发执行
|
||||
|
||||
当模型返回工具调用时:
|
||||
|
||||
- **单个工具调用** → 直接在主线程中执行
|
||||
- **多个工具调用** → 通过 `ThreadPoolExecutor` 并发执行
|
||||
- 例外:标记为交互式的工具(如 `clarify`)强制顺序执行
|
||||
- 无论完成顺序如何,结果均按原始工具调用顺序重新插入
|
||||
|
||||
### 执行流程
|
||||
|
||||
```text
|
||||
for each tool_call in response.tool_calls:
|
||||
1. 从 tools/registry.py 解析处理器
|
||||
2. 触发 pre_tool_call 插件 hook
|
||||
3. 检查是否为危险命令(tools/approval.py)
|
||||
- 若危险:调用 approval_callback,等待用户确认
|
||||
4. 使用参数 + task_id 执行处理器
|
||||
5. 触发 post_tool_call 插件 hook
|
||||
6. 将 {"role": "tool", "content": result} 追加到历史
|
||||
```
|
||||
|
||||
### Agent 级工具
|
||||
|
||||
部分工具在到达 `handle_function_call()` 之前,由 `run_agent.py` *提前*拦截:
|
||||
|
||||
| 工具 | 拦截原因 |
|
||||
|------|---------|
|
||||
| `todo` | 读写 agent 本地任务状态 |
|
||||
| `memory` | 向持久化内存文件写入内容(有字符限制) |
|
||||
| `session_search` | 通过 agent 的 session DB 查询 session 历史 |
|
||||
| `delegate_task` | 以隔离上下文生成子 agent |
|
||||
|
||||
这些工具直接修改 agent 状态,并返回合成的工具结果,不经过注册表。
|
||||
|
||||
## 回调接口
|
||||
|
||||
`AIAgent` 支持平台特定的回调,用于在 CLI、gateway 和 ACP 集成中实现实时进度展示:
|
||||
|
||||
| 回调 | 触发时机 | 使用方 |
|
||||
|------|---------|--------|
|
||||
| `tool_progress_callback` | 每次工具执行前后 | CLI spinner、gateway 进度消息 |
|
||||
| `thinking_callback` | 模型开始/停止思考时 | CLI "thinking..." 指示器 |
|
||||
| `reasoning_callback` | 模型返回推理内容时 | CLI 推理展示、gateway 推理块 |
|
||||
| `clarify_callback` | 调用 `clarify` 工具时 | CLI 输入提示、gateway 交互消息 |
|
||||
| `step_callback` | 每次完整 agent 轮次结束后 | Gateway 步骤追踪、ACP 进度 |
|
||||
| `stream_delta_callback` | 每个流式 token(启用时) | CLI 流式展示 |
|
||||
| `tool_gen_callback` | 从流中解析出工具调用时 | CLI spinner 中的工具预览 |
|
||||
| `status_callback` | 状态变更时(思考、执行等) | ACP 状态更新 |
|
||||
|
||||
## 预算与回退行为
|
||||
|
||||
### 迭代预算
|
||||
|
||||
agent 通过 `IterationBudget` 追踪迭代次数:
|
||||
|
||||
- 默认:90 次迭代(可通过 `agent.max_turns` 配置)
|
||||
- 每个 agent 拥有独立预算。子 agent 获得独立预算,上限为 `delegation.max_iterations`(默认 50)——父 agent 与子 agent 的总迭代次数可超过父 agent 的上限
|
||||
- 达到 100% 时,agent 停止并返回已完成工作的摘要
|
||||
|
||||
### 回退模型
|
||||
|
||||
当主模型失败时(429 限流、5xx 服务器错误、401/403 鉴权错误):
|
||||
|
||||
1. 检查配置中的 `fallback_providers` 列表
|
||||
2. 按顺序尝试每个回退 provider
|
||||
3. 成功后,使用新 provider 继续对话
|
||||
4. 遇到 401/403 时,在故障转移前尝试刷新凭据
|
||||
|
||||
回退系统也独立覆盖辅助任务——视觉、压缩和网页提取各自拥有独立的回退链,可通过 `auxiliary.*` 配置节进行配置。
|
||||
|
||||
## 压缩与持久化
|
||||
|
||||
### 压缩触发时机
|
||||
|
||||
- **预检**(API 调用前):对话超过模型上下文窗口的 50%
|
||||
- **Gateway 自动压缩**:对话超过 85%(更激进,在轮次之间运行)
|
||||
|
||||
### 压缩过程
|
||||
|
||||
1. 首先将内存刷写到磁盘(防止数据丢失)
|
||||
2. 将中间对话轮次摘要为紧凑的摘要内容
|
||||
3. 保留最后 N 条消息完整不变(`compression.protect_last_n`,默认:20)
|
||||
4. 工具调用/结果消息对保持完整(不拆分)
|
||||
5. 生成新的 session 血缘 ID(压缩会创建一个"子" session)
|
||||
|
||||
### Session 持久化
|
||||
|
||||
每轮结束后:
|
||||
- 消息保存到 session 存储(通过 `hermes_state.py` 使用 SQLite)
|
||||
- 内存变更刷写到 `MEMORY.md` / `USER.md`
|
||||
- 可通过 `/resume` 或 `hermes chat --resume` 恢复 session
|
||||
|
||||
## 关键源文件
|
||||
|
||||
| 文件 | 用途 |
|
||||
|------|------|
|
||||
| `run_agent.py` | AIAgent 类——完整的 agent loop |
|
||||
| `agent/prompt_builder.py` | 从内存、技能、上下文文件和个性组装系统 prompt |
|
||||
| `agent/context_engine.py` | ContextEngine ABC——可插拔的上下文管理 |
|
||||
| `agent/context_compressor.py` | 默认引擎——有损摘要算法 |
|
||||
| `agent/prompt_caching.py` | Anthropic prompt 缓存标记和缓存指标 |
|
||||
| `agent/auxiliary_client.py` | 用于辅助任务的辅助 LLM 客户端(视觉、摘要) |
|
||||
| `model_tools.py` | 工具 schema 集合,`handle_function_call()` 分发 |
|
||||
|
||||
## 相关文档
|
||||
|
||||
- [Provider 运行时解析](./provider-runtime.md)
|
||||
- [Prompt 组装](./prompt-assembly.md)
|
||||
- [上下文压缩与 Prompt 缓存](./context-compression-and-caching.md)
|
||||
- [工具运行时](./tools-runtime.md)
|
||||
- [架构概览](./architecture.md)
|
||||
@ -0,0 +1,277 @@
|
||||
---
|
||||
sidebar_position: 1
|
||||
title: "架构"
|
||||
description: "Hermes Agent 内部结构——主要子系统、执行路径、数据流及延伸阅读指引"
|
||||
---
|
||||
|
||||
# 架构
|
||||
|
||||
本页是 Hermes Agent 内部结构的顶层导图。用它在代码库中定位自己,然后深入各子系统专项文档了解实现细节。
|
||||
|
||||
## 系统概览
|
||||
|
||||
```text
|
||||
┌─────────────────────────────────────────────────────────────────────┐
|
||||
│ Entry Points │
|
||||
│ │
|
||||
│ CLI (cli.py) Gateway (gateway/run.py) ACP (acp_adapter/) │
|
||||
│ Batch Runner API Server Python Library │
|
||||
└──────────┬──────────────┬───────────────────────┬───────────────────┘
|
||||
│ │ │
|
||||
▼ ▼ ▼
|
||||
┌─────────────────────────────────────────────────────────────────────┐
|
||||
│ AIAgent (run_agent.py) │
|
||||
│ │
|
||||
│ ┌──────────────┐ ┌──────────────┐ ┌──────────────┐ │
|
||||
│ │ Prompt │ │ Provider │ │ Tool │ │
|
||||
│ │ Builder │ │ Resolution │ │ Dispatch │ │
|
||||
│ │ (prompt_ │ │ (runtime_ │ │ (model_ │ │
|
||||
│ │ builder.py) │ │ provider.py)│ │ tools.py) │ │
|
||||
│ └──────┬───────┘ └──────┬───────┘ └──────┬───────┘ │
|
||||
│ │ │ │ │
|
||||
│ ┌──────┴───────┐ ┌──────┴───────┐ ┌──────┴───────┐ │
|
||||
│ │ Compression │ │ 3 API Modes │ │ Tool Registry│ │
|
||||
│ │ & Caching │ │ chat_compl. │ │ (registry.py)│ │
|
||||
│ │ │ │ codex_resp. │ │ 70+ tools │ │
|
||||
│ │ │ │ anthropic │ │ 28 toolsets │ │
|
||||
│ └──────────────┘ └──────────────┘ └──────────────┘ │
|
||||
└─────────┴─────────────────┴─────────────────┴───────────────────────┘
|
||||
│ │
|
||||
▼ ▼
|
||||
┌───────────────────┐ ┌──────────────────────┐
|
||||
│ Session Storage │ │ Tool Backends │
|
||||
│ (SQLite + FTS5) │ │ Terminal (7 backends) │
|
||||
│ hermes_state.py │ │ Browser (5 backends) │
|
||||
│ gateway/session.py│ │ Web (4 backends) │
|
||||
└───────────────────┘ │ MCP (dynamic) │
|
||||
│ File, Vision, etc. │
|
||||
└──────────────────────┘
|
||||
```
|
||||
|
||||
## 目录结构
|
||||
|
||||
```text
|
||||
hermes-agent/
|
||||
├── run_agent.py # AIAgent — 核心对话循环(大文件)
|
||||
├── cli.py # HermesCLI — 交互式终端 UI(大文件)
|
||||
├── model_tools.py # 工具发现、schema 收集、分发
|
||||
├── toolsets.py # 工具分组与平台预设
|
||||
├── hermes_state.py # 带 FTS5 的 SQLite 会话/状态数据库
|
||||
├── hermes_constants.py # HERMES_HOME、感知 profile 的路径
|
||||
├── batch_runner.py # 批量轨迹生成
|
||||
│
|
||||
├── agent/ # Agent 内部模块
|
||||
│ ├── prompt_builder.py # 系统 prompt 组装
|
||||
│ ├── context_engine.py # ContextEngine ABC(可插拔)
|
||||
│ ├── context_compressor.py # 默认引擎——有损摘要压缩
|
||||
│ ├── prompt_caching.py # Anthropic prompt 缓存
|
||||
│ ├── auxiliary_client.py # 辅助 LLM,用于旁路任务(视觉、摘要)
|
||||
│ ├── model_metadata.py # 模型上下文长度、token 估算
|
||||
│ ├── models_dev.py # models.dev 注册表集成
|
||||
│ ├── anthropic_adapter.py # Anthropic Messages API 格式转换
|
||||
│ ├── display.py # KawaiiSpinner、工具预览格式化
|
||||
│ ├── skill_commands.py # Skill 斜杠命令
|
||||
│ ├── memory_manager.py # 记忆管理器编排
|
||||
│ ├── memory_provider.py # 记忆提供者 ABC
|
||||
│ └── trajectory.py # 轨迹保存辅助函数
|
||||
│
|
||||
├── hermes_cli/ # CLI 子命令与设置
|
||||
│ ├── main.py # 入口点——所有 `hermes` 子命令(大文件)
|
||||
│ ├── config.py # DEFAULT_CONFIG、OPTIONAL_ENV_VARS、迁移
|
||||
│ ├── commands.py # COMMAND_REGISTRY——斜杠命令中央定义
|
||||
│ ├── auth.py # PROVIDER_REGISTRY、凭据解析
|
||||
│ ├── runtime_provider.py # Provider → api_mode + 凭据
|
||||
│ ├── models.py # 模型目录、provider 模型列表
|
||||
│ ├── model_switch.py # /model 命令逻辑(CLI + gateway 共用)
|
||||
│ ├── setup.py # 交互式设置向导(大文件)
|
||||
│ ├── skin_engine.py # CLI 主题引擎
|
||||
│ ├── skills_config.py # hermes skills——按平台启用/禁用
|
||||
│ ├── skills_hub.py # /skills 斜杠命令
|
||||
│ ├── tools_config.py # hermes tools——按平台启用/禁用
|
||||
│ ├── plugins.py # PluginManager——发现、加载、hook
|
||||
│ ├── callbacks.py # 终端回调(clarify、sudo、approval)
|
||||
│ └── gateway.py # hermes gateway 启动/停止
|
||||
│
|
||||
├── tools/ # 工具实现(每个工具一个文件)
|
||||
│ ├── registry.py # 中央工具注册表
|
||||
│ ├── approval.py # 危险命令检测
|
||||
│ ├── terminal_tool.py # 终端编排
|
||||
│ ├── process_registry.py # 后台进程管理
|
||||
│ ├── file_tools.py # read_file、write_file、patch、search_files
|
||||
│ ├── web_tools.py # web_search、web_extract
|
||||
│ ├── browser_tool.py # 10 个浏览器自动化工具
|
||||
│ ├── code_execution_tool.py # execute_code 沙箱
|
||||
│ ├── delegate_tool.py # 子 agent 委托
|
||||
│ ├── mcp_tool.py # MCP 客户端(大文件)
|
||||
│ ├── credential_files.py # 基于文件的凭据透传
|
||||
│ ├── env_passthrough.py # 沙箱环境变量透传
|
||||
│ ├── ansi_strip.py # ANSI 转义字符剥离
|
||||
│ └── environments/ # 终端后端(local、docker、ssh、modal、daytona、singularity)
|
||||
│
|
||||
├── gateway/ # 消息平台 gateway
|
||||
│ ├── run.py # GatewayRunner——消息分发(大文件)
|
||||
│ ├── session.py # SessionStore——对话持久化
|
||||
│ ├── delivery.py # 出站消息投递
|
||||
│ ├── pairing.py # DM 配对授权
|
||||
│ ├── hooks.py # Hook 发现与生命周期事件
|
||||
│ ├── mirror.py # 跨会话消息镜像
|
||||
│ ├── status.py # Token 锁、profile 范围的进程追踪
|
||||
│ ├── builtin_hooks/ # 始终注册的 hook 扩展点(当前无内置)
|
||||
│ └── platforms/ # 20 个适配器:telegram、discord、slack、whatsapp、
|
||||
│ # signal、matrix、mattermost、email、sms、
|
||||
│ # dingtalk、feishu、wecom、wecom_callback、weixin、
|
||||
│ # bluebubbles、qqbot、homeassistant、webhook、api_server、
|
||||
│ # yuanbao
|
||||
│
|
||||
├── acp_adapter/ # ACP 服务器(VS Code / Zed / JetBrains)
|
||||
├── cron/ # 调度器(jobs.py、scheduler.py)
|
||||
├── plugins/memory/ # 记忆提供者插件
|
||||
├── plugins/context_engine/ # 上下文引擎插件
|
||||
├── skills/ # 内置 skill(始终可用)
|
||||
├── optional-skills/ # 官方可选 skill(需显式安装)
|
||||
├── website/ # Docusaurus 文档站点
|
||||
└── tests/ # Pytest 测试套件(3,000+ 个测试)
|
||||
```
|
||||
|
||||
## 数据流
|
||||
|
||||
### CLI 会话
|
||||
|
||||
```text
|
||||
用户输入 → HermesCLI.process_input()
|
||||
→ AIAgent.run_conversation()
|
||||
→ prompt_builder.build_system_prompt()
|
||||
→ runtime_provider.resolve_runtime_provider()
|
||||
→ API 调用(chat_completions / codex_responses / anthropic_messages)
|
||||
→ tool_calls? → model_tools.handle_function_call() → 循环
|
||||
→ 最终响应 → 显示 → 保存至 SessionDB
|
||||
```
|
||||
|
||||
### Gateway 消息
|
||||
|
||||
```text
|
||||
平台事件 → Adapter.on_message() → MessageEvent
|
||||
→ GatewayRunner._handle_message()
|
||||
→ 授权用户
|
||||
→ 解析会话 key
|
||||
→ 创建带会话历史的 AIAgent
|
||||
→ AIAgent.run_conversation()
|
||||
→ 通过适配器回传响应
|
||||
```
|
||||
|
||||
### Cron 任务
|
||||
|
||||
```text
|
||||
调度器触发 → 从 jobs.json 加载到期任务
|
||||
→ 创建全新 AIAgent(无历史)
|
||||
→ 将附加的 skill 注入为上下文
|
||||
→ 运行任务 prompt
|
||||
→ 向目标平台投递响应
|
||||
→ 更新任务状态与 next_run
|
||||
```
|
||||
|
||||
## 推荐阅读顺序
|
||||
|
||||
如果你是第一次接触代码库:
|
||||
|
||||
1. **本页** — 整体定位
|
||||
2. **[Agent 循环内部机制](./agent-loop.md)** — AIAgent 的工作原理
|
||||
3. **[Prompt 组装](./prompt-assembly.md)** — 系统 prompt 的构建过程
|
||||
4. **[Provider 运行时解析](./provider-runtime.md)** — provider 的选择方式
|
||||
5. **[添加 Provider](./adding-providers.md)** — 新增 provider 的实践指南
|
||||
6. **[工具运行时](./tools-runtime.md)** — 工具注册表、分发、环境
|
||||
7. **[会话存储](./session-storage.md)** — SQLite schema、FTS5、会话血缘
|
||||
8. **[Gateway 内部机制](./gateway-internals.md)** — 消息平台 gateway
|
||||
9. **[上下文压缩与 Prompt 缓存](./context-compression-and-caching.md)** — 压缩与缓存
|
||||
10. **[ACP 内部机制](./acp-internals.md)** — IDE 集成
|
||||
|
||||
## 主要子系统
|
||||
|
||||
### Agent 循环
|
||||
|
||||
同步编排引擎(`run_agent.py` 中的 `AIAgent`)。负责 provider 选择、prompt 构建、工具执行、重试、回退、回调、压缩和持久化。支持三种 API 模式以适配不同 provider 后端。
|
||||
|
||||
→ [Agent 循环内部机制](./agent-loop.md)
|
||||
|
||||
### Prompt 系统
|
||||
|
||||
在对话生命周期中构建和维护 prompt:
|
||||
|
||||
- **`prompt_builder.py`** — 从以下来源组装系统 prompt:个性(SOUL.md)、记忆(MEMORY.md、USER.md)、skill、上下文文件(AGENTS.md、.hermes.md)、工具使用指引以及模型专项指令
|
||||
- **`prompt_caching.py`** — 为前缀缓存应用 Anthropic 缓存断点
|
||||
- **`context_compressor.py`** — 当上下文超出阈值时对中间对话轮次进行摘要
|
||||
|
||||
→ [Prompt 组装](./prompt-assembly.md),[上下文压缩与 Prompt 缓存](./context-compression-and-caching.md)
|
||||
|
||||
### Provider 解析
|
||||
|
||||
CLI、gateway、cron、ACP 及辅助调用共用的运行时解析器。将 `(provider, model)` 元组映射为 `(api_mode, api_key, base_url)`。支持 18+ 个 provider、OAuth 流程、凭据池和别名解析。
|
||||
|
||||
→ [Provider 运行时解析](./provider-runtime.md)
|
||||
|
||||
### 工具系统
|
||||
|
||||
中央工具注册表(`tools/registry.py`),包含约 28 个 toolset 中的 70+ 个已注册工具。每个工具文件在导入时自行注册。注册表负责 schema 收集、分发、可用性检查和错误包装。终端工具支持 7 种后端(local、Docker、SSH、Daytona、Modal、Singularity、Vercel Sandbox)。
|
||||
|
||||
→ [工具运行时](./tools-runtime.md)
|
||||
|
||||
### 会话持久化
|
||||
|
||||
基于 SQLite 的会话存储,带 FTS5 全文检索。会话具有血缘追踪(跨压缩的父/子关系)、按平台隔离,以及带竞争处理的原子写入。
|
||||
|
||||
→ [会话存储](./session-storage.md)
|
||||
|
||||
### 消息 Gateway
|
||||
|
||||
长驻进程,包含 20 个平台适配器、统一会话路由、用户授权(白名单 + DM 配对)、斜杠命令分发、hook 系统、cron 触发和后台维护。
|
||||
|
||||
→ [Gateway 内部机制](./gateway-internals.md)
|
||||
|
||||
### 插件系统
|
||||
|
||||
三种发现来源:`~/.hermes/plugins/`(用户级)、`.hermes/plugins/`(项目级)和 pip entry point。插件通过上下文 API 注册工具、hook 和 CLI 命令。存在两种专用插件类型:记忆提供者(`plugins/memory/`)和上下文引擎(`plugins/context_engine/`)。两者均为单选——每种同时只能激活一个,通过 `hermes plugins` 或 `config.yaml` 配置。
|
||||
|
||||
→ [插件指南](/guides/build-a-hermes-plugin),[记忆提供者插件](./memory-provider-plugin.md)
|
||||
|
||||
### Cron
|
||||
|
||||
一等公民的 agent 任务(非 shell 任务)。任务以 JSON 存储,支持多种调度格式,可附加 skill 和脚本,并可向任意平台投递。
|
||||
|
||||
→ [Cron 内部机制](./cron-internals.md)
|
||||
|
||||
### ACP 集成
|
||||
|
||||
通过 stdio/JSON-RPC 将 Hermes 作为编辑器原生 agent 暴露给 VS Code、Zed 和 JetBrains。
|
||||
|
||||
→ [ACP 内部机制](./acp-internals.md)
|
||||
|
||||
### 轨迹
|
||||
|
||||
从 agent 会话生成 ShareGPT 格式的轨迹,用于训练数据生成。
|
||||
|
||||
→ [轨迹与训练格式](./trajectory-format.md)
|
||||
|
||||
## 设计原则
|
||||
|
||||
| 原则 | 实践含义 |
|
||||
|------|---------|
|
||||
| **Prompt 稳定性** | 系统 prompt 在对话中途不会改变。除用户显式操作(`/model`)外,不进行破坏缓存的变更。 |
|
||||
| **可观测执行** | 每次工具调用均通过回调对用户可见。CLI(spinner)和 gateway(聊天消息)中均有进度更新。 |
|
||||
| **可中断** | API 调用和工具执行可被用户输入或信号在执行中途取消。 |
|
||||
| **平台无关的核心** | 单一 AIAgent 类同时服务于 CLI、gateway、ACP、批处理和 API 服务器。平台差异存在于入口点,而非 agent 内部。 |
|
||||
| **松耦合** | 可选子系统(MCP、插件、记忆提供者、RL 环境)使用注册表模式和 check_fn 门控,而非硬依赖。 |
|
||||
| **Profile 隔离** | 每个 profile(`hermes -p <name>`)拥有独立的 HERMES_HOME、配置、记忆、会话和 gateway PID。多个 profile 可并发运行。 |
|
||||
|
||||
## 文件依赖链
|
||||
|
||||
```text
|
||||
tools/registry.py (无依赖——被所有工具文件导入)
|
||||
↑
|
||||
tools/*.py (每个文件在导入时调用 registry.register())
|
||||
↑
|
||||
model_tools.py (导入 tools/registry 并触发工具发现)
|
||||
↑
|
||||
run_agent.py, cli.py, batch_runner.py, environments/
|
||||
```
|
||||
|
||||
这条依赖链意味着工具注册发生在导入时,早于任何 agent 实例的创建。任何在顶层调用 `registry.register()` 的 `tools/*.py` 文件都会被自动发现——无需手动维护导入列表。
|
||||
@ -0,0 +1,160 @@
|
||||
# Browser CDP Supervisor — 设计文档
|
||||
|
||||
**状态:** 已发布(PR 14540)
|
||||
**最后更新:** 2026-04-23
|
||||
**作者:** @teknium1
|
||||
|
||||
## 问题
|
||||
|
||||
原生 JS 对话框(`alert`/`confirm`/`prompt`/`beforeunload`)和 iframe 是我们浏览器工具中最大的两个缺口:
|
||||
|
||||
1. **对话框会阻塞 JS 线程。** 页面上的任何操作都会挂起,直到对话框被处理。在此工作之前,agent 无法感知对话框是否已打开——后续的工具调用会挂起或抛出不透明的错误。
|
||||
2. **iframe 不可见。** Agent 可以在 DOM 快照中看到 iframe 节点,但无法在其中点击、输入或执行 eval——尤其是运行在独立 Chromium 进程中的跨域(OOPIF)iframe。
|
||||
|
||||
[PR #12550](https://github.com/NousResearch/hermes-agent/pull/12550) 提出了一个无状态的 `browser_dialog` 包装器。该方案无法解决检测问题——它只是在 agent 已经(通过症状)知道对话框已打开时,提供了一个更简洁的 CDP 调用。已作为被取代方案关闭。
|
||||
|
||||
## 后端能力矩阵(2026-04-23 实测验证)
|
||||
|
||||
使用一次性探测脚本,针对一个在主框架和同源 srcdoc iframe 中触发 alert 的 data-URL 页面,以及一个跨域 `https://example.com` iframe 进行测试:
|
||||
|
||||
| 后端 | 对话框检测 | 对话框响应 | 框架树 | OOPIF `Runtime.evaluate`(通过 `browser_cdp(frame_id=...)`) |
|
||||
|---|---|---|---|---|
|
||||
| 本地 Chrome(`--remote-debugging-port`)/ `/browser connect` | ✓ | ✓ 完整流程 | ✓ | ✓ |
|
||||
| Browserbase | ✓(通过 bridge) | ✓ 完整流程(通过 bridge) | ✓ | ✓(`document.title = "Example Domain"` 已在真实跨域 iframe 上验证) |
|
||||
| Camofox | ✗ 无 CDP(仅 REST) | ✗ | 通过 DOM 快照部分支持 | ✗ |
|
||||
|
||||
**Browserbase 响应的工作原理。** Browserbase 的 CDP 代理在内部使用 Playwright,并在约 10ms 内自动关闭原生对话框,因此 `Page.handleJavaScriptDialog` 无法跟上。为解决此问题,supervisor 通过 `Page.addScriptToEvaluateOnNewDocument` 注入一个 bridge 脚本,将 `window.alert`/`confirm`/`prompt` 覆盖为向魔法主机(`hermes-dialog-bridge.invalid`)发起的同步 XHR。`Fetch.enable` 在这些 XHR 触达网络之前将其拦截——对话框变成 supervisor 捕获的 `Fetch.requestPaused` 事件,`respond_to_dialog` 通过 `Fetch.fulfillRequest` 以 JSON 响应体完成请求,注入的脚本对其进行解码。
|
||||
|
||||
最终效果:从页面角度看,`prompt()` 仍然返回 agent 提供的字符串。从 agent 角度看,无论哪种方式,都是同一套 `browser_dialog(action=...)` API。已针对真实 Browserbase 会话进行端到端测试——4/4(alert/prompt/confirm-accept/confirm-dismiss)全部通过,包括值回传到页面 JS 的验证。
|
||||
|
||||
Camofox 在本 PR 中暂不支持;计划在 `jo-inc/camofox-browser` 提交上游 issue,请求添加对话框轮询端点。
|
||||
|
||||
## 架构
|
||||
|
||||
### CDPSupervisor
|
||||
|
||||
每个 Hermes `task_id` 对应一个在后台守护线程中运行的 `asyncio.Task`。持有一个到后端 CDP 端点的持久 WebSocket 连接。维护:
|
||||
|
||||
- **对话框队列** — `List[PendingDialog]`,包含 `{id, type, message, default_prompt, session_id, opened_at}`
|
||||
- **框架树** — `Dict[frame_id, FrameInfo]`,包含父子关系、URL、origin,以及是否为跨域子会话
|
||||
- **会话映射** — `Dict[session_id, SessionInfo]`,供交互工具将操作路由到正确的已附加会话以执行 OOPIF 操作
|
||||
- **近期控制台错误** — 最近 50 条的环形缓冲区(用于 PR 2 诊断)
|
||||
|
||||
附加时订阅:
|
||||
- `Page.enable` — `javascriptDialogOpening`、`frameAttached`、`frameNavigated`、`frameDetached`
|
||||
- `Runtime.enable` — `executionContextCreated`、`consoleAPICalled`、`exceptionThrown`
|
||||
- `Target.setAutoAttach {autoAttach: true, flatten: true}` — 暴露子 OOPIF target;supervisor 在每个上启用 `Page`+`Runtime`
|
||||
|
||||
通过快照锁实现线程安全的状态访问;工具处理器(同步)读取冻结快照,无需 await。
|
||||
|
||||
### 生命周期
|
||||
|
||||
- **启动:** `SupervisorRegistry.get_or_start(task_id, cdp_url)` — 由 `browser_navigate`、Browserbase 会话创建、`/browser connect` 调用。幂等。
|
||||
- **停止:** 会话拆除或 `/browser disconnect`。取消 asyncio task,关闭 WebSocket,丢弃状态。
|
||||
- **重新绑定:** 若 CDP URL 变更(用户重新连接到新的 Chrome),停止旧 supervisor 并重新启动——绝不跨端点复用状态。
|
||||
|
||||
### 对话框策略
|
||||
|
||||
通过 `config.yaml` 中的 `browser.dialog_policy` 配置:
|
||||
|
||||
- **`must_respond`**(默认)— 捕获,在 `browser_snapshot` 中呈现,等待显式的 `browser_dialog(action=...)` 调用。在 300s 安全超时后若无响应,则自动关闭并记录日志。防止有缺陷的 agent 永久挂起。
|
||||
- `auto_dismiss` — 记录并立即关闭;agent 事后通过 `browser_snapshot` 内的 `browser_state` 查看。
|
||||
- `auto_accept` — 记录并接受(适用于用户希望干净导航离开时的 `beforeunload`)。
|
||||
|
||||
策略按 task 配置;v1 不支持按对话框覆盖。
|
||||
|
||||
## Agent 接口(PR 1)
|
||||
|
||||
### 一个新工具
|
||||
|
||||
```
|
||||
browser_dialog(action, prompt_text=None, dialog_id=None)
|
||||
```
|
||||
|
||||
- `action="accept"` / `"dismiss"` → 响应指定的或唯一待处理的对话框(必填)
|
||||
- `prompt_text=...` → 向 `prompt()` 对话框提供的文本
|
||||
- `dialog_id=...` → 当多个对话框排队时用于消歧(罕见)
|
||||
|
||||
该工具仅用于响应。Agent 在调用前从 `browser_snapshot` 输出中读取待处理对话框。
|
||||
|
||||
### `browser_snapshot` 扩展
|
||||
|
||||
当 supervisor 已附加时,在现有快照输出中新增三个可选字段:
|
||||
|
||||
```json
|
||||
{
|
||||
"pending_dialogs": [
|
||||
{"id": "d-1", "type": "alert", "message": "Hello", "opened_at": 1650000000.0}
|
||||
],
|
||||
"recent_dialogs": [
|
||||
{"id": "d-1", "type": "alert", "message": "...", "opened_at": 1650000000.0,
|
||||
"closed_at": 1650000000.1, "closed_by": "remote"}
|
||||
],
|
||||
"frame_tree": {
|
||||
"top": {"frame_id": "FRAME_A", "url": "https://example.com/", "origin": "https://example.com"},
|
||||
"children": [
|
||||
{"frame_id": "FRAME_B", "url": "about:srcdoc", "is_oopif": false},
|
||||
{"frame_id": "FRAME_C", "url": "https://ads.example.net/", "is_oopif": true, "session_id": "SID_C"}
|
||||
],
|
||||
"truncated": false
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
- **`pending_dialogs`**:当前阻塞页面 JS 线程的对话框。Agent 必须调用 `browser_dialog(action=...)` 进行响应。在 Browserbase 上为空,因为其 CDP 代理会在约 10ms 内自动关闭对话框。
|
||||
|
||||
- **`recent_dialogs`**:最近关闭的最多 20 个对话框的环形缓冲区,带有 `closed_by` 标签——`"agent"`(我们响应了)、`"auto_policy"`(本地 auto_dismiss/auto_accept)、`"watchdog"`(must_respond 超时触发)或 `"remote"`(浏览器/后端主动关闭,例如 Browserbase)。这是 Browserbase 上的 agent 仍能了解发生了什么的方式。
|
||||
|
||||
- **`frame_tree`**:框架结构,包括跨域(OOPIF)子框架。上限为 30 条 + OOPIF 深度 2,以限制广告密集页面上的快照大小。当达到限制时,`truncated: true` 会出现;需要完整树的 agent 可使用 `browser_cdp` 配合 `Page.getFrameTree`。
|
||||
|
||||
以上均不新增工具 schema 接口——agent 从其已请求的快照中读取。
|
||||
|
||||
### 可用性门控
|
||||
|
||||
两个接口均通过 `_browser_cdp_check` 进行门控(supervisor 只能在 CDP 端点可达时运行)。在 Camofox / 无后端会话中,对话框工具被隐藏,快照省略新字段——不产生 schema 膨胀。
|
||||
|
||||
## 跨域 iframe 交互
|
||||
|
||||
在对话框检测工作的基础上,`browser_cdp(frame_id=...)` 通过 supervisor 已连接的 WebSocket,使用 OOPIF 的子 `sessionId` 路由 CDP 调用(尤其是 `Runtime.evaluate`)。Agent 从 `browser_snapshot.frame_tree.children[]` 中 `is_oopif=true` 的条目获取 frame_id,并将其传递给 `browser_cdp`。对于同源 iframe(无专用 CDP 会话),agent 改用顶层 `Runtime.evaluate` 中的 `contentWindow`/`contentDocument`——当 `frame_id` 属于非 OOPIF 时,supervisor 会返回指向该回退方案的错误。
|
||||
|
||||
在 Browserbase 上,这是 iframe 交互的**唯一**可靠路径——无状态 CDP 连接(每次 `browser_cdp` 调用时打开)会遭遇签名 URL 过期,而 supervisor 的长连接则保持有效会话。
|
||||
|
||||
## Camofox(后续跟进)
|
||||
|
||||
计划向 `jo-inc/camofox-browser` 提交 issue,添加:
|
||||
- 每个会话的 Playwright `page.on('dialog', handler)`
|
||||
- `GET /tabs/:tabId/dialogs` 轮询端点
|
||||
- `POST /tabs/:tabId/dialogs/:id` 用于接受/关闭
|
||||
- 框架树内省端点
|
||||
|
||||
## 涉及文件(PR 1)
|
||||
|
||||
### 新增
|
||||
|
||||
- `tools/browser_supervisor.py` — `CDPSupervisor`、`SupervisorRegistry`、`PendingDialog`、`FrameInfo`
|
||||
- `tools/browser_dialog_tool.py` — `browser_dialog` 工具处理器
|
||||
- `tests/tools/test_browser_supervisor.py` — 模拟 CDP WebSocket 服务器 + 生命周期/状态测试
|
||||
- `website/docs/developer-guide/browser-supervisor.md` — 本文件
|
||||
|
||||
### 修改
|
||||
|
||||
- `toolsets.py` — 在 `browser`、`hermes-acp`、`hermes-api-server`、核心工具集中注册 `browser_dialog`(通过 CDP 可达性门控)
|
||||
- `tools/browser_tool.py`
|
||||
- `browser_navigate` 启动钩子:若 CDP URL 可解析,调用 `SupervisorRegistry.get_or_start(task_id, cdp_url)`
|
||||
- `browser_snapshot`(约第 1536 行):将 supervisor 状态合并到返回载荷
|
||||
- `/browser connect` 处理器:以新端点重启 supervisor
|
||||
- `_cleanup_browser_session` 中的会话拆除钩子
|
||||
- `hermes_cli/config.py` — 向 `DEFAULT_CONFIG` 添加 `browser.dialog_policy` 和 `browser.dialog_timeout_s`
|
||||
- 文档:`website/docs/user-guide/features/browser.md`、`website/docs/reference/tools-reference.md`、`website/docs/reference/toolsets-reference.md`
|
||||
|
||||
## 非目标
|
||||
|
||||
- Camofox 的检测/交互(上游缺口;单独跟踪)
|
||||
- 向用户实时流式传输对话框/框架事件(需要 gateway 钩子)
|
||||
- 跨会话持久化对话框历史(仅内存)
|
||||
- 按 iframe 配置对话框策略(agent 可通过 `dialog_id` 表达)
|
||||
- 替换 `browser_cdp`——它作为长尾场景(cookies、viewport、网络限速)的逃生舱口继续保留
|
||||
|
||||
## 测试
|
||||
|
||||
单元测试使用 asyncio 模拟 CDP 服务器,该服务器实现了足够的协议子集,以覆盖所有状态转换:附加、启用、导航、对话框触发、对话框关闭、框架附加/分离、子 target 附加、会话拆除。真实后端端到端测试(Browserbase + 本地 Chromium 系浏览器)为手动执行——通过 `/browser connect` 连接到实时 Chromium 系浏览器,并运行上述对话框/框架测试用例。
|
||||
@ -0,0 +1,326 @@
|
||||
---
|
||||
title: 上下文压缩与缓存
|
||||
description: Hermes Agent 如何通过双重压缩系统和 Anthropic prompt 缓存高效管理上下文窗口。
|
||||
---
|
||||
|
||||
# 上下文压缩与缓存
|
||||
|
||||
Hermes Agent 使用双重压缩系统和 Anthropic prompt(提示词)缓存,在长对话中高效管理上下文窗口用量。
|
||||
|
||||
源文件:`agent/context_engine.py`(ABC)、`agent/context_compressor.py`(默认引擎)、
|
||||
`agent/prompt_caching.py`、`gateway/run.py`(会话清理)、`run_agent.py`(搜索 `_compress_context`)
|
||||
|
||||
|
||||
## 可插拔上下文引擎
|
||||
|
||||
上下文管理基于 `ContextEngine` ABC(`agent/context_engine.py`)构建。内置的 `ContextCompressor` 是默认实现,但插件可以用其他引擎替换它(例如无损上下文管理)。
|
||||
|
||||
```yaml
|
||||
context:
|
||||
engine: "compressor" # default — built-in lossy summarization
|
||||
engine: "lcm" # example — plugin providing lossless context
|
||||
```
|
||||
|
||||
引擎负责:
|
||||
- 决定何时触发压缩(`should_compress()`)
|
||||
- 执行压缩(`compress()`)
|
||||
- 可选地暴露 agent 可调用的工具(例如 `lcm_grep`)
|
||||
- 追踪 API 响应中的 token 用量
|
||||
|
||||
通过 `config.yaml` 中的 `context.engine` 进行配置驱动选择。解析顺序:
|
||||
1. 检查 `plugins/context_engine/<name>/` 目录
|
||||
2. 检查通用插件系统(`register_context_engine()`)
|
||||
3. 回退到内置 `ContextCompressor`
|
||||
|
||||
插件引擎**永远不会自动激活**——用户必须在 `context.engine` 中显式设置插件名称。默认的 `"compressor"` 始终使用内置实现。
|
||||
|
||||
通过 `hermes plugins` → Provider Plugins → Context Engine 进行配置,或直接编辑 `config.yaml`。
|
||||
|
||||
关于构建上下文引擎插件,请参阅 [Context Engine 插件](/developer-guide/context-engine-plugin)。
|
||||
|
||||
## 双重压缩系统
|
||||
|
||||
Hermes 有两个独立运行的压缩层:
|
||||
|
||||
```
|
||||
┌──────────────────────────┐
|
||||
Incoming message │ Gateway Session Hygiene │ Fires at 85% of context
|
||||
─────────────────► │ (pre-agent, rough est.) │ Safety net for large sessions
|
||||
└─────────────┬────────────┘
|
||||
│
|
||||
▼
|
||||
┌──────────────────────────┐
|
||||
│ Agent ContextCompressor │ Fires at 50% of context (default)
|
||||
│ (in-loop, real tokens) │ Normal context management
|
||||
└──────────────────────────┘
|
||||
```
|
||||
|
||||
### 1. Gateway 会话清理(85% 阈值)
|
||||
|
||||
位于 `gateway/run.py`(搜索 `Session hygiene: auto-compress`)。这是一个**安全网**,在 agent 处理消息之前运行。它防止会话在两次交互之间增长过大时(例如 Telegram/Discord 中的隔夜积累)导致 API 失败。
|
||||
|
||||
- **阈值**:固定为模型上下文长度的 85%
|
||||
- **Token 来源**:优先使用上一轮 API 实际报告的 token 数;回退到基于字符的粗略估算(`estimate_messages_tokens_rough`)
|
||||
- **触发条件**:仅当 `len(history) >= 4` 且压缩已启用时
|
||||
- **目的**:捕获逃过 agent 自身压缩器的会话
|
||||
|
||||
Gateway 清理阈值有意高于 agent 压缩器的阈值。将其设置为 50%(与 agent 相同)会导致长 gateway 会话在每一轮都过早触发压缩。
|
||||
|
||||
### 2. Agent ContextCompressor(50% 阈值,可配置)
|
||||
|
||||
位于 `agent/context_compressor.py`。这是**主要压缩系统**,在 agent 的工具循环内运行,可访问准确的 API 报告 token 数。
|
||||
|
||||
|
||||
## 配置
|
||||
|
||||
所有压缩设置从 `config.yaml` 的 `compression` 键读取:
|
||||
|
||||
```yaml
|
||||
compression:
|
||||
enabled: true # Enable/disable compression (default: true)
|
||||
threshold: 0.50 # Fraction of context window (default: 0.50 = 50%)
|
||||
target_ratio: 0.20 # How much of threshold to keep as tail (default: 0.20)
|
||||
protect_last_n: 20 # Minimum protected tail messages (default: 20)
|
||||
|
||||
# Summarization model/provider configured under auxiliary:
|
||||
auxiliary:
|
||||
compression:
|
||||
model: null # Override model for summaries (default: auto-detect)
|
||||
provider: auto # Provider: "auto", "openrouter", "nous", "main", etc.
|
||||
base_url: null # Custom OpenAI-compatible endpoint
|
||||
```
|
||||
|
||||
### 参数详情
|
||||
|
||||
| 参数 | 默认值 | 范围 | 描述 |
|
||||
|-----------|---------|-------|-------------|
|
||||
| `threshold` | `0.50` | 0.0-1.0 | 当 prompt token 数 ≥ `threshold × context_length` 时触发压缩 |
|
||||
| `target_ratio` | `0.20` | 0.10-0.80 | 控制尾部保护 token 预算:`threshold_tokens × target_ratio` |
|
||||
| `protect_last_n` | `20` | ≥1 | 始终保留的最近消息最小数量 |
|
||||
| `protect_first_n` | `3` | (硬编码)| 系统提示词 + 首次交互始终保留 |
|
||||
|
||||
### 计算值(200K 上下文模型,默认参数)
|
||||
|
||||
```
|
||||
context_length = 200,000
|
||||
threshold_tokens = 200,000 × 0.50 = 100,000
|
||||
tail_token_budget = 100,000 × 0.20 = 20,000
|
||||
max_summary_tokens = min(200,000 × 0.05, 12,000) = 10,000
|
||||
```
|
||||
|
||||
|
||||
## 压缩算法
|
||||
|
||||
`ContextCompressor.compress()` 方法遵循 4 阶段算法:
|
||||
|
||||
### 阶段 1:清除旧工具结果(廉价,无需 LLM 调用)
|
||||
|
||||
保护尾部之外的旧工具结果(>200 字符)将被替换为:
|
||||
```
|
||||
[Old tool output cleared to save context space]
|
||||
```
|
||||
|
||||
这是一个廉价的预处理步骤,可从冗长的工具输出(文件内容、终端输出、搜索结果)中节省大量 token。
|
||||
|
||||
### 阶段 2:确定边界
|
||||
|
||||
```
|
||||
┌─────────────────────────────────────────────────────────────┐
|
||||
│ Message list │
|
||||
│ │
|
||||
│ [0..2] ← protect_first_n (system + first exchange) │
|
||||
│ [3..N] ← middle turns → SUMMARIZED │
|
||||
│ [N..end] ← tail (by token budget OR protect_last_n) │
|
||||
│ │
|
||||
└─────────────────────────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
尾部保护基于 **token 预算**:从末尾向前遍历,累积 token 直到预算耗尽。如果预算保护的消息数少于固定的 `protect_last_n`,则回退到该固定数量。
|
||||
|
||||
边界对齐以避免拆分 tool_call/tool_result 组。`_align_boundary_backward()` 方法会跳过连续的工具结果,找到父级 assistant 消息,保持组的完整性。
|
||||
|
||||
### 阶段 3:生成结构化摘要
|
||||
|
||||
:::warning 摘要模型上下文长度
|
||||
摘要模型的上下文窗口必须**至少与主 agent 模型一样大**。整个中间部分通过单次 `call_llm(task="compression")` 调用发送给摘要模型。如果摘要模型的上下文更小,API 将返回上下文长度错误——`_generate_summary()` 会捕获该错误,记录警告并返回 `None`。压缩器随后会**在没有摘要的情况下丢弃中间轮次**,静默丢失对话上下文。这是压缩质量下降最常见的原因。
|
||||
:::
|
||||
|
||||
中间轮次使用辅助 LLM 以结构化模板进行摘要:
|
||||
|
||||
```
|
||||
## Goal
|
||||
[What the user is trying to accomplish]
|
||||
|
||||
## Constraints & Preferences
|
||||
[User preferences, coding style, constraints, important decisions]
|
||||
|
||||
## Progress
|
||||
### Done
|
||||
[Completed work — specific file paths, commands run, results]
|
||||
### In Progress
|
||||
[Work currently underway]
|
||||
### Blocked
|
||||
[Any blockers or issues encountered]
|
||||
|
||||
## Key Decisions
|
||||
[Important technical decisions and why]
|
||||
|
||||
## Relevant Files
|
||||
[Files read, modified, or created — with brief note on each]
|
||||
|
||||
## Next Steps
|
||||
[What needs to happen next]
|
||||
|
||||
## Critical Context
|
||||
[Specific values, error messages, configuration details]
|
||||
```
|
||||
|
||||
摘要预算随被压缩内容的量动态调整:
|
||||
- 公式:`content_tokens × 0.20`(`_SUMMARY_RATIO` 常量)
|
||||
- 最小值:2,000 token
|
||||
- 最大值:`min(context_length × 0.05, 12,000)` token
|
||||
|
||||
### 阶段 4:组装压缩后的消息
|
||||
|
||||
压缩后的消息列表为:
|
||||
1. 头部消息(首次压缩时在系统提示词后追加一条说明)
|
||||
2. 摘要消息(角色经过选择以避免连续相同角色违规)
|
||||
3. 尾部消息(未修改)
|
||||
|
||||
`_sanitize_tool_pairs()` 清理孤立的 tool_call/tool_result 对:
|
||||
- 引用已删除调用的工具结果 → 删除
|
||||
- 结果已被删除的工具调用 → 注入存根结果
|
||||
|
||||
### 迭代重压缩
|
||||
|
||||
在后续压缩中,前一次摘要会连同指令一起传递给 LLM,要求其**更新**摘要而非从头摘要。这在多次压缩中保留了信息——条目从"进行中"移至"已完成",新进展被添加,过时信息被删除。
|
||||
|
||||
压缩器实例上的 `_previous_summary` 字段存储最后一次摘要文本以供此用途。
|
||||
|
||||
|
||||
## 压缩前后示例
|
||||
|
||||
### 压缩前(45 条消息,约 95K token)
|
||||
|
||||
```
|
||||
[0] system: "You are a helpful assistant..." (system prompt)
|
||||
[1] user: "Help me set up a FastAPI project"
|
||||
[2] assistant: <tool_call> terminal: mkdir project </tool_call>
|
||||
[3] tool: "directory created"
|
||||
[4] assistant: <tool_call> write_file: main.py </tool_call>
|
||||
[5] tool: "file written (2.3KB)"
|
||||
... 30 more turns of file editing, testing, debugging ...
|
||||
[38] assistant: <tool_call> terminal: pytest </tool_call>
|
||||
[39] tool: "8 passed, 2 failed\n..." (5KB output)
|
||||
[40] user: "Fix the failing tests"
|
||||
[41] assistant: <tool_call> read_file: tests/test_api.py </tool_call>
|
||||
[42] tool: "import pytest\n..." (3KB)
|
||||
[43] assistant: "I see the issue with the test fixtures..."
|
||||
[44] user: "Great, also add error handling"
|
||||
```
|
||||
|
||||
### 压缩后(25 条消息,约 45K token)
|
||||
|
||||
```
|
||||
[0] system: "You are a helpful assistant...
|
||||
[Note: Some earlier conversation turns have been compacted...]"
|
||||
[1] user: "Help me set up a FastAPI project"
|
||||
[2] assistant: "[CONTEXT COMPACTION] Earlier turns were compacted...
|
||||
|
||||
## Goal
|
||||
Set up a FastAPI project with tests and error handling
|
||||
|
||||
## Progress
|
||||
### Done
|
||||
- Created project structure: main.py, tests/, requirements.txt
|
||||
- Implemented 5 API endpoints in main.py
|
||||
- Wrote 10 test cases in tests/test_api.py
|
||||
- 8/10 tests passing
|
||||
|
||||
### In Progress
|
||||
- Fixing 2 failing tests (test_create_user, test_delete_user)
|
||||
|
||||
## Relevant Files
|
||||
- main.py — FastAPI app with 5 endpoints
|
||||
- tests/test_api.py — 10 test cases
|
||||
- requirements.txt — fastapi, pytest, httpx
|
||||
|
||||
## Next Steps
|
||||
- Fix failing test fixtures
|
||||
- Add error handling"
|
||||
[3] user: "Fix the failing tests"
|
||||
[4] assistant: <tool_call> read_file: tests/test_api.py </tool_call>
|
||||
[5] tool: "import pytest\n..."
|
||||
[6] assistant: "I see the issue with the test fixtures..."
|
||||
[7] user: "Great, also add error handling"
|
||||
```
|
||||
|
||||
|
||||
## Prompt 缓存(Anthropic)
|
||||
|
||||
来源:`agent/prompt_caching.py`
|
||||
|
||||
通过缓存对话前缀,在多轮对话中将输入 token 成本降低约 75%。使用 Anthropic 的 `cache_control` 断点。
|
||||
|
||||
### 策略:system_and_3
|
||||
|
||||
Anthropic 每次请求最多允许 4 个 `cache_control` 断点。Hermes 使用"system_and_3"策略:
|
||||
|
||||
```
|
||||
Breakpoint 1: System prompt (stable across all turns)
|
||||
Breakpoint 2: 3rd-to-last non-system message ─┐
|
||||
Breakpoint 3: 2nd-to-last non-system message ├─ Rolling window
|
||||
Breakpoint 4: Last non-system message ─┘
|
||||
```
|
||||
|
||||
### 工作原理
|
||||
|
||||
`apply_anthropic_cache_control()` 深拷贝消息并注入 `cache_control` 标记:
|
||||
|
||||
```python
|
||||
# Cache marker format
|
||||
marker = {"type": "ephemeral"}
|
||||
# Or for 1-hour TTL:
|
||||
marker = {"type": "ephemeral", "ttl": "1h"}
|
||||
```
|
||||
|
||||
标记根据内容类型以不同方式应用:
|
||||
|
||||
| 内容类型 | 标记位置 |
|
||||
|-------------|-------------------|
|
||||
| 字符串内容 | 转换为 `[{"type": "text", "text": ..., "cache_control": ...}]` |
|
||||
| 列表内容 | 添加到最后一个元素的字典中 |
|
||||
| None/空 | 作为 `msg["cache_control"]` 添加 |
|
||||
| 工具消息 | 作为 `msg["cache_control"]` 添加(仅限原生 Anthropic) |
|
||||
|
||||
### 缓存感知设计模式
|
||||
|
||||
1. **稳定的系统提示词**:系统提示词是断点 1,在所有轮次中缓存。避免在对话中途修改它(压缩仅在首次压缩时追加一条说明)。
|
||||
|
||||
2. **消息顺序很重要**:缓存命中需要前缀匹配。在中间添加或删除消息会使其后所有内容的缓存失效。
|
||||
|
||||
3. **压缩与缓存的交互**:压缩后,被压缩区域的缓存失效,但系统提示词缓存保留。滚动 3 消息窗口在 1-2 轮内重新建立缓存。
|
||||
|
||||
4. **TTL 选择**:默认为 `5m`(5 分钟)。对于用户在轮次之间有较长间隔的长时间会话,使用 `1h`。
|
||||
|
||||
### 启用 Prompt 缓存
|
||||
|
||||
满足以下条件时,prompt 缓存自动启用:
|
||||
- 模型为 Anthropic Claude 模型(通过模型名称检测)
|
||||
- 提供商支持 `cache_control`(原生 Anthropic API 或 OpenRouter)
|
||||
|
||||
```yaml
|
||||
# config.yaml — TTL is configurable (must be "5m" or "1h")
|
||||
prompt_caching:
|
||||
cache_ttl: "5m"
|
||||
```
|
||||
|
||||
CLI 在启动时显示缓存状态:
|
||||
```
|
||||
💾 Prompt caching: ENABLED (Claude via OpenRouter, 5m TTL)
|
||||
```
|
||||
|
||||
|
||||
## 上下文压力警告
|
||||
|
||||
中间上下文压力警告已被移除(参见 `run_agent.py` 中的迭代预算块,其中注明:"No intermediate pressure warnings — they caused models to 'give up' prematurely on complex tasks")。压缩在 prompt token 达到配置的 `compression.threshold`(默认 50%)时触发,无需事先警告步骤;gateway 会话清理作为二级安全网在模型上下文窗口的 85% 处触发。
|
||||
@ -0,0 +1,193 @@
|
||||
---
|
||||
sidebar_position: 9
|
||||
title: "Context Engine 插件"
|
||||
description: "如何构建替换内置 ContextCompressor 的 context engine 插件"
|
||||
---
|
||||
|
||||
# 构建 Context Engine 插件
|
||||
|
||||
Context engine 插件用于替换内置的 `ContextCompressor`,以实现管理对话上下文的替代策略。例如,无损上下文管理(LCM)引擎通过构建知识 DAG 来替代有损摘要。
|
||||
|
||||
## 工作原理
|
||||
|
||||
Agent 的上下文管理基于 `ContextEngine` ABC(`agent/context_engine.py`)构建。内置的 `ContextCompressor` 是默认实现。插件引擎必须实现相同的接口。
|
||||
|
||||
同一时间只能有**一个** context engine 处于激活状态。选择由配置驱动:
|
||||
|
||||
```yaml
|
||||
# config.yaml
|
||||
context:
|
||||
engine: "compressor" # 默认内置
|
||||
engine: "lcm" # 激活名为 "lcm" 的插件引擎
|
||||
```
|
||||
|
||||
插件引擎**永远不会自动激活** — 用户必须显式将 `context.engine` 设置为插件名称。
|
||||
|
||||
## 目录结构
|
||||
|
||||
每个 context engine 位于 `plugins/context_engine/<name>/`:
|
||||
|
||||
```
|
||||
plugins/context_engine/lcm/
|
||||
├── __init__.py # 导出 ContextEngine 子类
|
||||
├── plugin.yaml # 元数据(name、description、version)
|
||||
└── ... # 引擎所需的其他模块
|
||||
```
|
||||
|
||||
## ContextEngine ABC
|
||||
|
||||
你的引擎必须实现以下**必需**方法:
|
||||
|
||||
```python
|
||||
from agent.context_engine import ContextEngine
|
||||
|
||||
class LCMEngine(ContextEngine):
|
||||
|
||||
@property
|
||||
def name(self) -> str:
|
||||
"""短标识符,例如 'lcm'。必须与 config.yaml 中的值匹配。"""
|
||||
return "lcm"
|
||||
|
||||
def update_from_response(self, usage: dict) -> None:
|
||||
"""每次 LLM 调用后,以 usage dict 为参数调用。
|
||||
|
||||
从响应中更新 self.last_prompt_tokens、self.last_completion_tokens、
|
||||
self.last_total_tokens。
|
||||
"""
|
||||
|
||||
def should_compress(self, prompt_tokens: int = None) -> bool:
|
||||
"""若本轮应触发压缩则返回 True。"""
|
||||
|
||||
def compress(self, messages: list, current_tokens: int = None,
|
||||
focus_topic: str = None) -> list:
|
||||
"""压缩消息列表并返回新的(可能更短的)列表。
|
||||
|
||||
返回的列表必须是有效的 OpenAI 格式消息序列。
|
||||
|
||||
``focus_topic`` 是来自手动 ``/compress <focus>`` 的可选主题字符串;
|
||||
支持引导式压缩的引擎应优先保留与其相关的信息,其他引擎可忽略。
|
||||
"""
|
||||
```
|
||||
|
||||
### 引擎必须维护的类属性
|
||||
|
||||
Agent 直接读取这些属性用于显示和日志记录:
|
||||
|
||||
```python
|
||||
last_prompt_tokens: int = 0
|
||||
last_completion_tokens: int = 0
|
||||
last_total_tokens: int = 0
|
||||
threshold_tokens: int = 0 # 触发压缩的阈值
|
||||
context_length: int = 0 # 模型的完整上下文窗口
|
||||
compression_count: int = 0 # compress() 已运行的次数
|
||||
```
|
||||
|
||||
### 可选方法
|
||||
|
||||
这些方法在 ABC 中有合理的默认实现,按需覆盖:
|
||||
|
||||
| 方法 | 默认行为 | 何时覆盖 |
|
||||
|--------|---------|--------------|
|
||||
| `on_session_start(session_id, **kwargs)` | 空操作 | 需要加载持久化状态(DAG、DB)时 |
|
||||
| `on_session_end(session_id, messages)` | 空操作 | 需要刷新状态、关闭连接时 |
|
||||
| `on_session_reset()` | 重置 token 计数器 | 有需要清除的会话级状态时 |
|
||||
| `update_model(model, context_length, ...)` | 更新 context_length 和阈值 | 需要在切换模型时重新计算预算时 |
|
||||
| `get_tool_schemas()` | 返回 `[]` | 引擎提供 agent 可调用的工具时(例如 `lcm_grep`) |
|
||||
| `handle_tool_call(name, args, **kwargs)` | 返回错误 JSON | 实现工具处理器时 |
|
||||
| `should_compress_preflight(messages)` | 返回 `False` | 可在 API 调用前进行低成本预估时 |
|
||||
| `get_status()` | 标准 token/阈值字典 | 有自定义指标需要暴露时 |
|
||||
|
||||
## 引擎工具
|
||||
|
||||
Context engine 可以暴露 agent 直接调用的工具。从 `get_tool_schemas()` 返回 schema,并在 `handle_tool_call()` 中处理调用:
|
||||
|
||||
```python
|
||||
def get_tool_schemas(self):
|
||||
return [{
|
||||
"name": "lcm_grep",
|
||||
"description": "Search the context knowledge graph",
|
||||
"parameters": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"query": {"type": "string", "description": "Search query"}
|
||||
},
|
||||
"required": ["query"],
|
||||
},
|
||||
}]
|
||||
|
||||
def handle_tool_call(self, name, args, **kwargs):
|
||||
if name == "lcm_grep":
|
||||
results = self._search_dag(args["query"])
|
||||
return json.dumps({"results": results})
|
||||
return json.dumps({"error": f"Unknown tool: {name}"})
|
||||
```
|
||||
|
||||
引擎工具在启动时注入到 agent 的工具列表中并自动分发 — 无需注册到注册表。
|
||||
|
||||
## 注册
|
||||
|
||||
### 通过目录(推荐)
|
||||
|
||||
将引擎放置于 `plugins/context_engine/<name>/`。`__init__.py` 必须导出一个 `ContextEngine` 子类。发现系统会自动找到并实例化它。
|
||||
|
||||
### 通过通用插件系统
|
||||
|
||||
通用插件也可以注册 context engine:
|
||||
|
||||
```python
|
||||
def register(ctx):
|
||||
engine = LCMEngine(context_length=200000)
|
||||
ctx.register_context_engine(engine)
|
||||
```
|
||||
|
||||
只能注册一个引擎。第二个尝试注册的插件将被拒绝并发出警告。
|
||||
|
||||
## 生命周期
|
||||
|
||||
```
|
||||
1. 引擎实例化(插件加载或目录发现)
|
||||
2. on_session_start() — 对话开始
|
||||
3. update_from_response() — 每次 API 调用后
|
||||
4. should_compress() — 每轮检查
|
||||
5. compress() — 当 should_compress() 返回 True 时调用
|
||||
6. on_session_end() — 会话边界(CLI 退出、/reset、gateway 过期)
|
||||
```
|
||||
|
||||
`on_session_reset()` 在 `/new` 或 `/reset` 时调用,用于清除会话级状态而不完全关闭。
|
||||
|
||||
## 配置
|
||||
|
||||
用户通过 `hermes plugins` → Provider Plugins → Context Engine 选择引擎,或直接编辑 `config.yaml`:
|
||||
|
||||
```yaml
|
||||
context:
|
||||
engine: "lcm" # 必须与引擎的 name 属性匹配
|
||||
```
|
||||
|
||||
`compression` 配置块(`compression.threshold`、`compression.protect_last_n` 等)专属于内置的 `ContextCompressor`。如有需要,你的引擎应定义自己的配置格式,并在初始化期间从 `config.yaml` 读取。
|
||||
|
||||
## 测试
|
||||
|
||||
```python
|
||||
from agent.context_engine import ContextEngine
|
||||
|
||||
def test_engine_satisfies_abc():
|
||||
engine = YourEngine(context_length=200000)
|
||||
assert isinstance(engine, ContextEngine)
|
||||
assert engine.name == "your-name"
|
||||
|
||||
def test_compress_returns_valid_messages():
|
||||
engine = YourEngine(context_length=200000)
|
||||
msgs = [{"role": "user", "content": "hello"}]
|
||||
result = engine.compress(msgs)
|
||||
assert isinstance(result, list)
|
||||
assert all("role" in m for m in result)
|
||||
```
|
||||
|
||||
完整的 ABC 契约测试套件请参见 `tests/agent/test_context_engine.py`。
|
||||
|
||||
## 另请参阅
|
||||
|
||||
- [上下文压缩与缓存](/developer-guide/context-compression-and-caching) — 内置压缩器的工作原理
|
||||
- [Memory Provider 插件](/developer-guide/memory-provider-plugin) — 类似的单选插件系统(用于内存)
|
||||
- [插件](/user-guide/features/plugins) — 通用插件系统概述
|
||||
@ -0,0 +1,243 @@
|
||||
---
|
||||
sidebar_position: 4
|
||||
title: "贡献指南"
|
||||
description: "如何为 Hermes Agent 做贡献 — 开发环境配置、代码风格、PR 流程"
|
||||
---
|
||||
|
||||
# 贡献指南
|
||||
|
||||
感谢您为 Hermes Agent 做贡献!本指南涵盖开发环境配置、代码库结构说明以及 PR 合并流程。
|
||||
|
||||
## 贡献优先级
|
||||
|
||||
我们按以下顺序评估贡献价值:
|
||||
|
||||
1. **Bug 修复** — 崩溃、错误行为、数据丢失
|
||||
2. **跨平台兼容性** — macOS、不同 Linux 发行版、WSL2
|
||||
3. **安全加固** — shell 注入、prompt(提示词)注入、路径穿越
|
||||
4. **性能与健壮性** — 重试逻辑、错误处理、优雅降级
|
||||
5. **新 skill** — 具有广泛用途的 skill(参见 [创建 Skill](creating-skills.md))
|
||||
6. **新工具** — 极少需要;大多数能力应以 skill 形式实现
|
||||
7. **文档** — 修正、说明、新示例
|
||||
|
||||
## 常见贡献路径
|
||||
|
||||
- 构建自定义/本地工具而不修改 Hermes 核心?从 [构建 Hermes 插件](../guides/build-a-hermes-plugin.md) 开始
|
||||
- 为 Hermes 本身构建新的内置核心工具?从 [添加工具](./adding-tools.md) 开始
|
||||
- 构建新的 skill?从 [创建 Skill](./creating-skills.md) 开始
|
||||
- 构建新的推理提供商?从 [添加提供商](./adding-providers.md) 开始
|
||||
|
||||
## 开发环境配置
|
||||
|
||||
### 前置要求
|
||||
|
||||
| 要求 | 说明 |
|
||||
|-------------|-------|
|
||||
| **Git** | 需支持 `--recurse-submodules`,并安装 `git-lfs` 扩展 |
|
||||
| **Python 3.11+** | 若未安装,uv 会自动安装 |
|
||||
| **uv** | 高速 Python 包管理器([安装](https://docs.astral.sh/uv/)) |
|
||||
| **Node.js 20+** | 可选 — 浏览器工具和 WhatsApp bridge 需要(与根目录 `package.json` engines 字段一致) |
|
||||
|
||||
### 克隆与安装
|
||||
|
||||
```bash
|
||||
git clone --recurse-submodules https://github.com/NousResearch/hermes-agent.git
|
||||
cd hermes-agent
|
||||
|
||||
# 使用 Python 3.11 创建虚拟环境
|
||||
uv venv venv --python 3.11
|
||||
export VIRTUAL_ENV="$(pwd)/venv"
|
||||
|
||||
# 安装所有扩展(messaging、cron、CLI 菜单、开发工具)
|
||||
uv pip install -e ".[all,dev]"
|
||||
|
||||
# 可选:浏览器工具
|
||||
npm install
|
||||
```
|
||||
|
||||
### 配置开发环境
|
||||
|
||||
```bash
|
||||
mkdir -p ~/.hermes/{cron,sessions,logs,memories,skills}
|
||||
cp cli-config.yaml.example ~/.hermes/config.yaml
|
||||
touch ~/.hermes/.env
|
||||
|
||||
# 至少添加一个 LLM 提供商密钥:
|
||||
echo 'OPENROUTER_API_KEY=sk-or-v1-your-key' >> ~/.hermes/.env
|
||||
```
|
||||
|
||||
### 运行
|
||||
|
||||
```bash
|
||||
# 创建全局访问的符号链接
|
||||
mkdir -p ~/.local/bin
|
||||
ln -sf "$(pwd)/venv/bin/hermes" ~/.local/bin/hermes
|
||||
|
||||
# 验证
|
||||
hermes doctor
|
||||
hermes chat -q "Hello"
|
||||
```
|
||||
|
||||
### 运行测试
|
||||
|
||||
```bash
|
||||
pytest tests/ -v
|
||||
```
|
||||
|
||||
## 代码风格
|
||||
|
||||
- **PEP 8**,允许合理例外(不强制限制行长度)
|
||||
- **注释**:仅在解释非显而易见的意图、权衡取舍或 API 特殊行为时添加
|
||||
- **错误处理**:捕获具体异常。对于意外错误,使用 `logger.warning()`/`logger.error()` 并设置 `exc_info=True`
|
||||
- **跨平台**:不得假设 Unix 环境(见下文)
|
||||
- **Profile 安全路径**:不得硬编码 `~/.hermes` — 代码路径使用 `hermes_constants` 中的 `get_hermes_home()`,面向用户的消息使用 `display_hermes_home()`。完整规则参见 [AGENTS.md](https://github.com/NousResearch/hermes-agent/blob/main/AGENTS.md#profiles-multi-instance-support)。
|
||||
|
||||
## 跨平台兼容性
|
||||
|
||||
Hermes 官方支持 **Linux、macOS、WSL2 以及原生 Windows(早期 beta — 通过 PowerShell 安装)**。原生 Windows 使用 [Git for Windows](https://git-scm.com/download/win) 提供的 Git Bash 执行 shell 命令。部分功能依赖 POSIX 内核原语,已做条件限制:dashboard 内嵌的 PTY 终端面板(`/chat` 标签页)仅支持 WSL2。原生 Windows 路径较新且迭代较快 — 如果您主要在 Windows 上开发,请做好遇到并修复粗糙边缘的准备。
|
||||
|
||||
贡献代码时,请遵守以下规则:
|
||||
|
||||
- **不得添加未加保护的 `signal.SIGKILL` 引用。** Windows 上未定义该信号。请通过 `gateway.status.terminate_pid(pid, force=True)`(集中式原语,Windows 上执行 `taskkill /T /F`,POSIX 上发送 SIGKILL)路由,或使用 `getattr(signal, "SIGKILL", signal.SIGTERM)` 回退。
|
||||
- **在 `os.kill(pid, 0)` 探测时同时捕获 `OSError` 和 `ProcessLookupError`。** Windows 对已消失的 PID 抛出 `OSError`(WinError 87,"参数不正确"),而非 `ProcessLookupError`。
|
||||
- **不得强制终端使用 POSIX 语义。** `os.setsid`、`os.killpg`、`os.getpgid`、`os.fork` 在 Windows 上均会抛出异常 — 使用 `if sys.platform != "win32":` 或 `if os.name != "nt":` 进行条件判断。
|
||||
- **打开文件时显式指定 `encoding="utf-8"`。** Windows 上 Python 默认使用系统区域设置(通常为 cp1252),处理非拉丁字符时会出现乱码或崩溃。
|
||||
- **使用 `pathlib.Path` / `os.path.join`,不得手动用 `/` 拼接路径。** 这对我们构造后传给子进程的字符串尤为重要,而非 OS 返回给我们的字符串。
|
||||
|
||||
关键模式:
|
||||
|
||||
### 1. `termios` 和 `fcntl` 仅适用于 Unix
|
||||
|
||||
始终同时捕获 `ImportError` 和 `NotImplementedError`:
|
||||
|
||||
```python
|
||||
try:
|
||||
from simple_term_menu import TerminalMenu
|
||||
menu = TerminalMenu(options)
|
||||
idx = menu.show()
|
||||
except (ImportError, NotImplementedError):
|
||||
# 回退:编号菜单
|
||||
for i, opt in enumerate(options):
|
||||
print(f" {i+1}. {opt}")
|
||||
idx = int(input("Choice: ")) - 1
|
||||
```
|
||||
|
||||
### 2. 文件编码
|
||||
|
||||
某些环境可能以非 UTF-8 编码保存 `.env` 文件:
|
||||
|
||||
```python
|
||||
try:
|
||||
load_dotenv(env_path)
|
||||
except UnicodeDecodeError:
|
||||
load_dotenv(env_path, encoding="latin-1")
|
||||
```
|
||||
|
||||
### 3. 进程管理
|
||||
|
||||
`os.setsid()`、`os.killpg()` 以及信号处理在各平台间存在差异:
|
||||
|
||||
```python
|
||||
import platform
|
||||
if platform.system() != "Windows":
|
||||
kwargs["preexec_fn"] = os.setsid
|
||||
```
|
||||
|
||||
### 4. 路径分隔符
|
||||
|
||||
使用 `pathlib.Path` 代替用 `/` 进行字符串拼接。
|
||||
|
||||
## 安全注意事项
|
||||
|
||||
Hermes 拥有终端访问权限,安全至关重要。
|
||||
|
||||
### 现有保护措施
|
||||
|
||||
| 层级 | 实现方式 |
|
||||
|-------|---------------|
|
||||
| **sudo 密码管道** | 使用 `shlex.quote()` 防止 shell 注入 |
|
||||
| **危险命令检测** | `tools/approval.py` 中的正则表达式模式,配合用户审批流程 |
|
||||
| **Cron prompt 注入** | 扫描器阻断指令覆盖模式 |
|
||||
| **写入拒绝列表** | 受保护路径通过 `os.path.realpath()` 解析,防止符号链接绕过 |
|
||||
| **Skill 守卫** | 对 hub 安装的 skill 进行安全扫描 |
|
||||
| **代码执行沙箱** | 子进程运行时剥离 API 密钥 |
|
||||
| **容器加固** | Docker:删除所有 capability,禁止权限提升,限制 PID 数量 |
|
||||
|
||||
### 贡献安全敏感代码
|
||||
|
||||
- 将用户输入插入 shell 命令时,始终使用 `shlex.quote()`
|
||||
- 访问控制检查前,使用 `os.path.realpath()` 解析符号链接
|
||||
- 不得记录密钥信息
|
||||
- 在工具执行周围捕获宽泛异常
|
||||
- 若您的变更涉及文件路径或进程,请在所有平台上测试
|
||||
|
||||
## Pull Request 流程
|
||||
|
||||
### 分支命名
|
||||
|
||||
```
|
||||
fix/description # Bug 修复
|
||||
feat/description # 新功能
|
||||
docs/description # 文档
|
||||
test/description # 测试
|
||||
refactor/description # 代码重构
|
||||
```
|
||||
|
||||
### 提交前检查
|
||||
|
||||
1. **运行测试**:`pytest tests/ -v`
|
||||
2. **手动测试**:运行 `hermes` 并验证您修改的代码路径
|
||||
3. **检查跨平台影响**:考虑 macOS 和不同 Linux 发行版
|
||||
4. **保持 PR 聚焦**:每个 PR 只包含一个逻辑变更
|
||||
|
||||
### PR 描述
|
||||
|
||||
请包含:
|
||||
- **变更内容**及**变更原因**
|
||||
- **测试方法**
|
||||
- **测试平台**
|
||||
- 关联 issue 引用
|
||||
|
||||
### Commit 消息
|
||||
|
||||
我们使用 [Conventional Commits](https://www.conventionalcommits.org/):
|
||||
|
||||
```
|
||||
<type>(<scope>): <description>
|
||||
```
|
||||
|
||||
| 类型 | 适用场景 |
|
||||
|------|---------|
|
||||
| `fix` | Bug 修复 |
|
||||
| `feat` | 新功能 |
|
||||
| `docs` | 文档 |
|
||||
| `test` | 测试 |
|
||||
| `refactor` | 代码重构 |
|
||||
| `chore` | 构建、CI、依赖更新 |
|
||||
|
||||
Scope 范围:`cli`、`gateway`、`tools`、`skills`、`agent`、`install`、`whatsapp`、`security`
|
||||
|
||||
示例:
|
||||
```
|
||||
fix(cli): prevent crash in save_config_value when model is a string
|
||||
feat(gateway): add WhatsApp multi-user session isolation
|
||||
fix(security): prevent shell injection in sudo password piping
|
||||
```
|
||||
|
||||
## 报告问题
|
||||
|
||||
- 使用 [GitHub Issues](https://github.com/NousResearch/hermes-agent/issues)
|
||||
- 请包含:操作系统、Python 版本、Hermes 版本(`hermes version`)、完整错误堆栈
|
||||
- 包含复现步骤
|
||||
- 创建前请检查是否已有重复 issue
|
||||
- 安全漏洞请私下报告
|
||||
|
||||
## 社区
|
||||
|
||||
- **Discord**:[discord.gg/NousResearch](https://discord.gg/NousResearch)
|
||||
- **GitHub Discussions**:用于设计提案和架构讨论
|
||||
- **Skills Hub**:上传专业 skill 并与社区共享
|
||||
|
||||
## 许可证
|
||||
|
||||
提交贡献即表示您同意您的贡献将以 [MIT 许可证](https://github.com/NousResearch/hermes-agent/blob/main/LICENSE) 授权。
|
||||
@ -0,0 +1,375 @@
|
||||
---
|
||||
sidebar_position: 3
|
||||
title: "创建 Skill"
|
||||
description: "如何为 Hermes Agent 创建 skill——SKILL.md 格式、规范与发布"
|
||||
---
|
||||
|
||||
# 创建 Skill
|
||||
|
||||
Skill 是为 Hermes Agent 添加新能力的首选方式。与 tool 相比,skill 更易于创建,无需修改 agent 代码,且可与社区共享。
|
||||
|
||||
## 应该创建 Skill 还是 Tool?
|
||||
|
||||
以下情况创建 **Skill**:
|
||||
- 该能力可通过指令 + shell 命令 + 现有 tool 来实现
|
||||
- 封装了 agent 可通过 `terminal` 或 `web_extract` 调用的外部 CLI 或 API
|
||||
- 不需要将自定义 Python 集成或 API key 管理内置到 agent 中
|
||||
- 示例:arXiv 搜索、git 工作流、Docker 管理、PDF 处理、通过 CLI 工具发送邮件
|
||||
|
||||
以下情况创建 **Tool**:
|
||||
- 需要与 API key、认证流程或多组件配置进行端到端集成
|
||||
- 需要每次精确执行的自定义处理逻辑
|
||||
- 处理二进制数据、流式传输或实时事件
|
||||
- 示例:浏览器自动化、TTS、视觉分析
|
||||
|
||||
## Skill 目录结构
|
||||
|
||||
内置 skill 位于 `skills/` 目录下,按类别组织。官方可选 skill 在 `optional-skills/` 中使用相同结构:
|
||||
|
||||
```text
|
||||
skills/
|
||||
├── research/
|
||||
│ └── arxiv/
|
||||
│ ├── SKILL.md # 必需:主要指令
|
||||
│ └── scripts/ # 可选:辅助脚本
|
||||
│ └── search_arxiv.py
|
||||
├── productivity/
|
||||
│ └── ocr-and-documents/
|
||||
│ ├── SKILL.md
|
||||
│ ├── scripts/
|
||||
│ └── references/
|
||||
└── ...
|
||||
```
|
||||
|
||||
## SKILL.md 格式
|
||||
|
||||
```markdown
|
||||
---
|
||||
name: my-skill
|
||||
description: Brief description (shown in skill search results)
|
||||
version: 1.0.0
|
||||
author: Your Name
|
||||
license: MIT
|
||||
platforms: [macos, linux] # Optional — restrict to specific OS platforms
|
||||
# Valid: macos, linux, windows
|
||||
# Omit to load on all platforms (default)
|
||||
metadata:
|
||||
hermes:
|
||||
tags: [Category, Subcategory, Keywords]
|
||||
related_skills: [other-skill-name]
|
||||
requires_toolsets: [web] # Optional — only show when these toolsets are active
|
||||
requires_tools: [web_search] # Optional — only show when these tools are available
|
||||
fallback_for_toolsets: [browser] # Optional — hide when these toolsets are active
|
||||
fallback_for_tools: [browser_navigate] # Optional — hide when these tools exist
|
||||
config: # Optional — config.yaml settings the skill needs
|
||||
- key: my.setting
|
||||
description: "What this setting controls"
|
||||
default: "sensible-default"
|
||||
prompt: "Display prompt for setup"
|
||||
required_environment_variables: # Optional — env vars the skill needs
|
||||
- name: MY_API_KEY
|
||||
prompt: "Enter your API key"
|
||||
help: "Get one at https://example.com"
|
||||
required_for: "API access"
|
||||
---
|
||||
|
||||
# Skill Title
|
||||
|
||||
Brief intro.
|
||||
|
||||
## When to Use
|
||||
Trigger conditions — when should the agent load this skill?
|
||||
|
||||
## Quick Reference
|
||||
Table of common commands or API calls.
|
||||
|
||||
## Procedure
|
||||
Step-by-step instructions the agent follows.
|
||||
|
||||
## Pitfalls
|
||||
Known failure modes and how to handle them.
|
||||
|
||||
## Verification
|
||||
How the agent confirms it worked.
|
||||
```
|
||||
|
||||
### 平台专属 Skill
|
||||
|
||||
Skill 可通过 `platforms` 字段将自身限制在特定操作系统上:
|
||||
|
||||
```yaml
|
||||
platforms: [macos] # 仅 macOS(例如 iMessage、Apple Reminders)
|
||||
platforms: [macos, linux] # macOS 和 Linux
|
||||
platforms: [windows] # 仅 Windows
|
||||
```
|
||||
|
||||
设置后,该 skill 会在不兼容的平台上自动从系统 prompt(提示词)、`skills_list()` 和斜杠命令中隐藏。若省略或留空,则在所有平台上加载(向后兼容)。
|
||||
|
||||
### 条件式 Skill 激活
|
||||
|
||||
Skill 可声明对特定 tool 或 toolset 的依赖,以控制该 skill 是否出现在当前会话的系统 prompt 中。
|
||||
|
||||
```yaml
|
||||
metadata:
|
||||
hermes:
|
||||
requires_toolsets: [web] # 若 web toolset 未激活则隐藏
|
||||
requires_tools: [web_search] # 若 web_search tool 不可用则隐藏
|
||||
fallback_for_toolsets: [browser] # 若 browser toolset 已激活则隐藏
|
||||
fallback_for_tools: [browser_navigate] # 若 browser_navigate 可用则隐藏
|
||||
```
|
||||
|
||||
| 字段 | 行为 |
|
||||
|-------|----------|
|
||||
| `requires_toolsets` | 当列出的**任意** toolset **不**可用时,skill **隐藏** |
|
||||
| `requires_tools` | 当列出的**任意** tool **不**可用时,skill **隐藏** |
|
||||
| `fallback_for_toolsets` | 当列出的**任意** toolset **已**可用时,skill **隐藏** |
|
||||
| `fallback_for_tools` | 当列出的**任意** tool **已**可用时,skill **隐藏** |
|
||||
|
||||
**`fallback_for_*` 使用场景:** 创建一个在主要 tool 不可用时作为替代方案的 skill。例如,带有 `fallback_for_tools: [web_search]` 的 `duckduckgo-search` skill 仅在未配置需要 API key 的 web search tool 时显示。
|
||||
|
||||
**`requires_*` 使用场景:** 创建仅在特定 tool 存在时才有意义的 skill。例如,带有 `requires_toolsets: [web]` 的网页抓取工作流 skill 在 web tool 被禁用时不会出现在 prompt 中。
|
||||
|
||||
### 环境变量要求
|
||||
|
||||
Skill 可声明所需的环境变量。当通过 `skill_view` 加载 skill 时,其所需变量会自动注册,以便透传(passthrough)到沙箱执行环境(terminal、execute_code)中。
|
||||
|
||||
```yaml
|
||||
required_environment_variables:
|
||||
- name: TENOR_API_KEY
|
||||
prompt: "Tenor API key" # 提示用户时显示
|
||||
help: "Get your key at https://tenor.com" # 帮助文本或 URL
|
||||
required_for: "GIF search functionality" # 哪个功能需要此变量
|
||||
```
|
||||
|
||||
每个条目支持:
|
||||
- `name`(必需)——环境变量名称
|
||||
- `prompt`(可选)——向用户询问值时的提示文本
|
||||
- `help`(可选)——获取该值的帮助文本或 URL
|
||||
- `required_for`(可选)——描述哪个功能需要此变量
|
||||
|
||||
用户也可在 `config.yaml` 中手动配置透传变量:
|
||||
|
||||
```yaml
|
||||
terminal:
|
||||
env_passthrough:
|
||||
- MY_CUSTOM_VAR
|
||||
- ANOTHER_VAR
|
||||
```
|
||||
|
||||
macOS 专属 skill 示例请参见 `skills/apple/`。
|
||||
|
||||
## 加载时的安全配置
|
||||
|
||||
当 skill 需要 API key 或 token 时,使用 `required_environment_variables`。缺少值**不会**将 skill 从发现列表中隐藏。Hermes 会在本地 CLI 加载 skill 时安全地提示用户输入。
|
||||
|
||||
```yaml
|
||||
required_environment_variables:
|
||||
- name: TENOR_API_KEY
|
||||
prompt: Tenor API key
|
||||
help: Get a key from https://developers.google.com/tenor
|
||||
required_for: full functionality
|
||||
```
|
||||
|
||||
用户可以跳过配置并继续加载 skill。Hermes 不会将原始密钥值暴露给模型。Gateway 和消息会话会显示本地配置指引,而不是在带内收集密钥。
|
||||
|
||||
:::tip 沙箱透传
|
||||
加载 skill 时,已设置的 `required_environment_variables` 会**自动透传**到 `execute_code` 和 `terminal` 沙箱——包括 Docker 和 Modal 等远程后端。Skill 的脚本无需用户额外配置即可访问 `$TENOR_API_KEY`(或 Python 中的 `os.environ["TENOR_API_KEY"]`)。详见 [环境变量透传](/user-guide/security#environment-variable-passthrough)。
|
||||
:::
|
||||
|
||||
旧版 `prerequisites.env_vars` 作为向后兼容的别名仍受支持。
|
||||
|
||||
### Config 配置项(config.yaml)
|
||||
|
||||
Skill 可声明非密钥配置项,这些配置项存储在 `config.yaml` 的 `skills.config` 命名空间下。与环境变量(存储密钥)不同,config 配置项用于路径、偏好设置及其他非敏感值。
|
||||
|
||||
```yaml
|
||||
metadata:
|
||||
hermes:
|
||||
config:
|
||||
- key: myplugin.path
|
||||
description: Path to the plugin data directory
|
||||
default: "~/myplugin-data"
|
||||
prompt: Plugin data directory path
|
||||
- key: myplugin.domain
|
||||
description: Domain the plugin operates on
|
||||
default: ""
|
||||
prompt: Plugin domain (e.g., AI/ML research)
|
||||
```
|
||||
|
||||
每个条目支持:
|
||||
- `key`(必需)——配置项的点路径(例如 `myplugin.path`)
|
||||
- `description`(必需)——说明该配置项的作用
|
||||
- `default`(可选)——用户未配置时的默认值
|
||||
- `prompt`(可选)——`hermes config migrate` 时显示的提示文本;若未设置则回退到 `description`
|
||||
|
||||
**工作原理:**
|
||||
|
||||
1. **存储:** 值写入 `config.yaml` 的 `skills.config.<key>` 下:
|
||||
```yaml
|
||||
skills:
|
||||
config:
|
||||
myplugin:
|
||||
path: ~/my-data
|
||||
```
|
||||
|
||||
2. **发现:** `hermes config migrate` 扫描所有已启用的 skill,找出未配置的项并提示用户。配置项也会在 `hermes config show` 的"Skill Settings"部分显示。
|
||||
|
||||
3. **运行时注入:** Skill 加载时,其 config 值会被解析并追加到 skill 消息中:
|
||||
```
|
||||
[Skill config (from ~/.hermes/config.yaml):
|
||||
myplugin.path = /home/user/my-data
|
||||
]
|
||||
```
|
||||
Agent 无需自行读取 `config.yaml` 即可看到已配置的值。
|
||||
|
||||
4. **手动配置:** 用户也可直接设置值:
|
||||
```bash
|
||||
hermes config set skills.config.myplugin.path ~/my-data
|
||||
```
|
||||
|
||||
:::tip 如何选择
|
||||
对 API key、token 及其他**密钥**使用 `required_environment_variables`(存储在 `~/.hermes/.env`,不向模型展示)。对**路径、偏好设置及非敏感配置**使用 `config`(存储在 `config.yaml`,在 config show 中可见)。
|
||||
:::
|
||||
|
||||
### 凭证文件要求(OAuth token 等)
|
||||
|
||||
使用 OAuth 或基于文件的凭证的 skill 可声明需要挂载到远程沙箱的文件。这适用于以**文件**形式存储的凭证(而非环境变量)——通常是由配置脚本生成的 OAuth token 文件。
|
||||
|
||||
```yaml
|
||||
required_credential_files:
|
||||
- path: google_token.json
|
||||
description: Google OAuth2 token (created by setup script)
|
||||
- path: google_client_secret.json
|
||||
description: Google OAuth2 client credentials
|
||||
```
|
||||
|
||||
每个条目支持:
|
||||
- `path`(必需)——相对于 `~/.hermes/` 的文件路径
|
||||
- `description`(可选)——说明该文件的用途及创建方式
|
||||
|
||||
加载时,Hermes 会检查这些文件是否存在。缺少文件会触发 `setup_needed`。已存在的文件会自动:
|
||||
- **挂载到 Docker** 容器中作为只读绑定挂载
|
||||
- **同步到 Modal** 沙箱(在创建时及每次命令前同步,因此会话中途的 OAuth 也能正常工作)
|
||||
- 在**本地**后端无需任何特殊处理即可使用
|
||||
|
||||
:::tip 如何选择
|
||||
对简单的 API key 和 token(存储在 `~/.hermes/.env` 中的字符串)使用 `required_environment_variables`。对 OAuth token 文件、客户端密钥、服务账号 JSON、证书或任何以磁盘文件形式存在的凭证使用 `required_credential_files`。
|
||||
:::
|
||||
|
||||
完整示例请参见 `skills/productivity/google-workspace/SKILL.md`,其中同时使用了两者。
|
||||
|
||||
## Skill 规范
|
||||
|
||||
### 无外部依赖
|
||||
|
||||
优先使用标准库 Python、curl 以及现有 Hermes tool(`web_extract`、`terminal`、`read_file`)。若确实需要依赖项,请在 skill 中记录安装步骤。
|
||||
|
||||
### 渐进式披露
|
||||
|
||||
将最常见的工作流放在最前面。边缘情况和高级用法放在底部。这样可以降低常见任务的 token 消耗。
|
||||
|
||||
### 包含辅助脚本
|
||||
|
||||
对于 XML/JSON 解析或复杂逻辑,请在 `scripts/` 中包含辅助脚本——不要每次都期望 LLM 内联编写解析器。
|
||||
|
||||
### 以文档形式传递媒体(`[[as_document]]`)
|
||||
|
||||
如果 skill 生成高分辨率截图、图表或任何有损预览压缩会造成损失的图片,请在响应中某处(通常是最后一行)输出字面指令 `[[as_document]]`。Gateway 会去除该指令,并将该响应中所有提取的媒体路径以可下载文件附件的形式传递,而非内联图片气泡。完整语义请参见 [Skill 输出与媒体传递](../user-guide/features/skills.md#skill-output-and-media-delivery)。
|
||||
|
||||
#### 在 SKILL.md 中引用内置脚本
|
||||
|
||||
Skill 加载时,激活消息会将 skill 目录的绝对路径以 `[Skill directory: /abs/path]` 的形式暴露,同时在 SKILL.md 正文中替换两个模板 token:
|
||||
|
||||
| Token | 替换为 |
|
||||
|---|---|
|
||||
| `${HERMES_SKILL_DIR}` | skill 目录的绝对路径 |
|
||||
| `${HERMES_SESSION_ID}` | 当前会话 ID(若无会话则保留原样) |
|
||||
|
||||
因此,SKILL.md 可以直接告知 agent 运行内置脚本:
|
||||
|
||||
```markdown
|
||||
To analyse the input, run:
|
||||
|
||||
node ${HERMES_SKILL_DIR}/scripts/analyse.js <input>
|
||||
```
|
||||
|
||||
Agent 看到替换后的绝对路径,并使用 `terminal` tool 执行已就绪的命令——无需路径计算,无需额外的 `skill_view` 往返。可在 `config.yaml` 中设置 `skills.template_vars: false` 全局禁用替换。
|
||||
|
||||
#### 内联 shell 片段(需手动开启)
|
||||
|
||||
Skill 也可在 SKILL.md 正文中嵌入以 `` !`cmd` `` 形式编写的内联 shell 片段。启用后,每个片段的 stdout 会在 agent 读取前内联到消息中,从而让 skill 注入动态上下文:
|
||||
|
||||
```markdown
|
||||
Current date: !`date -u +%Y-%m-%d`
|
||||
Git branch: !`git -C ${HERMES_SKILL_DIR} rev-parse --abbrev-ref HEAD`
|
||||
```
|
||||
|
||||
此功能**默认关闭**——SKILL.md 中的任何片段都会在未经审批的情况下在宿主机上运行,因此仅对你信任的 skill 来源启用:
|
||||
|
||||
```yaml
|
||||
# config.yaml
|
||||
skills:
|
||||
inline_shell: true
|
||||
inline_shell_timeout: 10 # 每个片段的超时秒数
|
||||
```
|
||||
|
||||
片段以 skill 目录为工作目录运行,输出上限为 4000 个字符。失败(超时、非零退出)会显示为简短的 `[inline-shell error: ...]` 标记,而不会导致整个 skill 中断。
|
||||
|
||||
### 测试
|
||||
|
||||
运行 skill 并验证 agent 是否正确遵循指令:
|
||||
|
||||
```bash
|
||||
hermes chat --toolsets skills -q "Use the X skill to do Y"
|
||||
```
|
||||
|
||||
## Skill 应放在哪里?
|
||||
|
||||
内置 skill(位于 `skills/`)随每次 Hermes 安装一起发布,应对**大多数用户广泛有用**:
|
||||
|
||||
- 文档处理、网页研究、常见开发工作流、系统管理
|
||||
- 被广泛人群定期使用
|
||||
|
||||
如果你的 skill 是官方的且有用,但并非所有人都需要(例如付费服务集成、重量级依赖),请放入 **`optional-skills/`**——它随仓库一起发布,可通过 `hermes skills browse` 发现(标记为"official"),并以内置信任级别安装。
|
||||
|
||||
如果你的 skill 是专业化的、社区贡献的或小众的,更适合放在 **Skills Hub**——将其上传到注册表并通过 `hermes skills install` 分享。
|
||||
|
||||
## 发布 Skill
|
||||
|
||||
### 发布到 Skills Hub
|
||||
|
||||
```bash
|
||||
hermes skills publish skills/my-skill --to github --repo owner/repo
|
||||
```
|
||||
|
||||
### 发布到自定义仓库
|
||||
|
||||
将你的仓库添加为 tap:
|
||||
|
||||
```bash
|
||||
hermes skills tap add owner/repo
|
||||
```
|
||||
|
||||
用户随后可从你的仓库搜索并安装。
|
||||
|
||||
## 安全扫描
|
||||
|
||||
所有从 hub 安装的 skill 都会经过安全扫描器检查:
|
||||
|
||||
- 数据泄露模式
|
||||
- Prompt 注入尝试
|
||||
- 破坏性命令
|
||||
- Shell 注入
|
||||
|
||||
信任级别:
|
||||
- `builtin`——随 Hermes 一起发布(始终受信任)
|
||||
- `official`——来自仓库中的 `optional-skills/`(内置信任,无第三方警告)
|
||||
- `trusted`——来自 openai/skills、anthropics/skills、huggingface/skills
|
||||
- `community`——非危险发现可通过 `--force` 覆盖;`dangerous` 判定仍会被阻止
|
||||
|
||||
Hermes 现在可以通过多种外部发现模型使用第三方 skill:
|
||||
- 直接 GitHub 标识符(例如 `openai/skills/k8s`)
|
||||
- `skills.sh` 标识符(例如 `skills-sh/vercel-labs/json-render/json-render-react`)
|
||||
- 从 `/.well-known/skills/index.json` 提供的知名端点
|
||||
|
||||
如果你希望 skill 无需 GitHub 专属安装器即可被发现,除了在仓库或市场中发布外,还可以考虑通过知名端点提供服务。
|
||||
@ -0,0 +1,228 @@
|
||||
---
|
||||
sidebar_position: 11
|
||||
title: "Cron 内部机制"
|
||||
description: "Hermes 如何存储、调度、编辑、暂停、加载技能以及投递 cron 任务"
|
||||
---
|
||||
|
||||
# Cron 内部机制
|
||||
|
||||
cron 子系统提供定时任务执行能力——从简单的单次延迟到带技能注入和跨平台投递的周期性 cron 表达式任务。
|
||||
|
||||
## 关键文件
|
||||
|
||||
| 文件 | 用途 |
|
||||
|------|---------|
|
||||
| `cron/jobs.py` | 任务模型、存储、对 `jobs.json` 的原子读写 |
|
||||
| `cron/scheduler.py` | 调度器循环——到期任务检测、执行、重复计数跟踪 |
|
||||
| `tools/cronjob_tools.py` | 面向模型的 `cronjob` 工具注册与处理器 |
|
||||
| `gateway/run.py` | Gateway 集成——在长运行循环中触发 cron tick |
|
||||
| `hermes_cli/cron.py` | CLI `hermes cron` 子命令 |
|
||||
|
||||
## 调度模型
|
||||
|
||||
支持四种调度格式:
|
||||
|
||||
| 格式 | 示例 | 行为 |
|
||||
|--------|---------|----------|
|
||||
| **相对延迟** | `30m`、`2h`、`1d` | 单次触发,在指定时长后执行 |
|
||||
| **间隔** | `every 2h`、`every 30m` | 周期触发,按固定间隔执行 |
|
||||
| **Cron 表达式** | `0 9 * * *` | 标准 5 字段 cron 语法(分钟、小时、日、月、星期) |
|
||||
| **ISO 时间戳** | `2025-01-15T09:00:00` | 单次触发,在精确时间点执行 |
|
||||
|
||||
面向模型的接口是单个 `cronjob` 工具,支持以下操作:`create`、`list`、`update`、`pause`、`resume`、`run`、`remove`。
|
||||
|
||||
## 任务存储
|
||||
|
||||
任务存储在 `~/.hermes/cron/jobs.json` 中,采用原子写入语义(先写入临时文件,再重命名)。每条任务记录包含:
|
||||
|
||||
```json
|
||||
{
|
||||
"id": "a1b2c3d4e5f6",
|
||||
"name": "Daily briefing",
|
||||
"prompt": "Summarize today's AI news and funding rounds",
|
||||
"schedule": {
|
||||
"kind": "cron",
|
||||
"expr": "0 9 * * *",
|
||||
"display": "0 9 * * *"
|
||||
},
|
||||
"skills": ["ai-funding-daily-report"],
|
||||
"deliver": "telegram:-1001234567890",
|
||||
"repeat": {
|
||||
"times": null,
|
||||
"completed": 42
|
||||
},
|
||||
"state": "scheduled",
|
||||
"enabled": true,
|
||||
"next_run_at": "2025-01-16T09:00:00Z",
|
||||
"last_run_at": "2025-01-15T09:00:00Z",
|
||||
"last_status": "ok",
|
||||
"created_at": "2025-01-01T00:00:00Z",
|
||||
"model": null,
|
||||
"provider": null,
|
||||
"script": null
|
||||
}
|
||||
```
|
||||
|
||||
### 任务生命周期状态
|
||||
|
||||
| 状态 | 含义 |
|
||||
|-------|---------|
|
||||
| `scheduled` | 活跃,将在下次计划时间触发 |
|
||||
| `paused` | 已暂停——恢复前不会触发 |
|
||||
| `completed` | 重复次数已耗尽,或单次任务已执行 |
|
||||
| `running` | 正在执行(瞬态状态) |
|
||||
|
||||
### 向后兼容性
|
||||
|
||||
旧版任务可能使用单个 `skill` 字段而非 `skills` 数组。调度器在加载时会对此进行规范化——单个 `skill` 会被提升为 `skills: [skill]`。
|
||||
|
||||
## 调度器运行时
|
||||
|
||||
### Tick 周期
|
||||
|
||||
调度器按周期性 tick 运行(默认:每 60 秒):
|
||||
|
||||
```text
|
||||
tick()
|
||||
1. 获取调度器锁(防止 tick 重叠)
|
||||
2. 从 jobs.json 加载所有任务
|
||||
3. 筛选到期任务(next_run <= now 且 state == "scheduled")
|
||||
4. 对每个到期任务:
|
||||
a. 将状态设为 "running"
|
||||
b. 创建全新的 AIAgent 会话(无对话历史)
|
||||
c. 按顺序加载附加技能(以用户消息形式注入)
|
||||
d. 通过 agent 执行任务 prompt(提示词)
|
||||
e. 将响应投递到配置的目标
|
||||
f. 更新 run_count,计算下次运行时间
|
||||
g. 若重复次数耗尽 → state = "completed"
|
||||
h. 否则 → state = "scheduled"
|
||||
5. 将更新后的任务写回 jobs.json
|
||||
6. 释放调度器锁
|
||||
```
|
||||
|
||||
### Gateway 集成
|
||||
|
||||
在 gateway 模式下,调度器运行在专用后台线程中(`gateway/run.py` 中的 `_start_cron_ticker`),每 60 秒调用一次 `scheduler.tick()`,与消息处理并行运行。
|
||||
|
||||
在 CLI 模式下,cron 任务仅在运行 `hermes cron` 命令或活跃 CLI 会话期间触发。
|
||||
|
||||
### 全新会话隔离
|
||||
|
||||
每个 cron 任务在完全全新的 agent 会话中运行:
|
||||
|
||||
- 无前次运行的对话历史
|
||||
- 无前次 cron 执行的记忆(除非已持久化到内存/文件)
|
||||
- prompt 必须自包含——cron 任务无法提出澄清性问题
|
||||
- `cronjob` 工具集已禁用(递归防护)
|
||||
|
||||
## 技能支持的任务
|
||||
|
||||
cron 任务可通过 `skills` 字段附加一个或多个技能。执行时:
|
||||
|
||||
1. 按指定顺序加载技能
|
||||
2. 每个技能的 SKILL.md 内容作为上下文注入
|
||||
3. 任务的 prompt 作为任务指令追加
|
||||
4. Agent 处理技能上下文与 prompt 的组合内容
|
||||
|
||||
这使得可复用、经过测试的工作流无需将完整指令粘贴到 cron prompt 中。例如:
|
||||
|
||||
```
|
||||
创建每日融资报告 → 附加 "ai-funding-daily-report" 技能
|
||||
```
|
||||
|
||||
### 脚本支持的任务
|
||||
|
||||
任务还可通过 `script` 字段附加 Python 脚本。该脚本在每次 agent 轮次*之前*运行,其 stdout 作为上下文注入到 prompt 中。这支持数据采集和变更检测模式:
|
||||
|
||||
```python
|
||||
# ~/.hermes/scripts/check_competitors.py
|
||||
import requests, json
|
||||
# 获取竞争对手发布说明,与上次运行结果进行差异比对
|
||||
# 将摘要打印到 stdout——agent 进行分析并报告
|
||||
```
|
||||
|
||||
脚本超时默认为 120 秒。`_get_script_timeout()` 通过三层链路解析限制:
|
||||
|
||||
1. **模块级覆盖** — `_SCRIPT_TIMEOUT`(用于测试/monkeypatching)。仅在与默认值不同时使用。
|
||||
2. **环境变量** — `HERMES_CRON_SCRIPT_TIMEOUT`
|
||||
3. **配置** — `config.yaml` 中的 `cron.script_timeout_seconds`(通过 `load_config()` 读取)
|
||||
4. **默认值** — 120 秒
|
||||
|
||||
### Provider 恢复
|
||||
|
||||
`run_job()` 将用户配置的备用 provider 和凭证池传入 `AIAgent` 实例:
|
||||
|
||||
- **备用 provider** — 从 `config.yaml` 读取 `fallback_providers`(列表)或 `fallback_model`(旧版字典),与 gateway 的 `_load_fallback_model()` 模式一致。以 `fallback_model=` 形式传入 `AIAgent.__init__`,后者将两种格式规范化为备用链。
|
||||
- **凭证池** — 通过 `agent.credential_pool` 中的 `load_pool(provider)` 使用解析后的运行时 provider 名称加载。仅在池中有凭证时传入(`pool.has_credentials()`)。在遭遇 429/限速错误时启用同 provider 的密钥轮换。
|
||||
|
||||
这与 gateway 的行为保持一致——否则 cron agent 在遭遇限速时将直接失败而不尝试恢复。
|
||||
|
||||
## 投递模型
|
||||
|
||||
Cron 任务结果可投递到任何受支持的平台:
|
||||
|
||||
| 目标 | 语法 | 示例 |
|
||||
|--------|--------|---------|
|
||||
| 来源聊天 | `origin` | 投递到创建该任务的聊天 |
|
||||
| 本地文件 | `local` | 保存到 `~/.hermes/cron/output/` |
|
||||
| Telegram | `telegram` 或 `telegram:<chat_id>` | `telegram:-1001234567890` |
|
||||
| Discord | `discord` 或 `discord:#channel` | `discord:#engineering` |
|
||||
| Slack | `slack` | 投递到 Slack 主频道 |
|
||||
| WhatsApp | `whatsapp` | 投递到 WhatsApp 主会话 |
|
||||
| Signal | `signal` | 投递到 Signal |
|
||||
| Matrix | `matrix` | 投递到 Matrix 主房间 |
|
||||
| Mattermost | `mattermost` | 投递到 Mattermost 主频道 |
|
||||
| Email | `email` | 通过邮件投递 |
|
||||
| SMS | `sms` | 通过短信投递 |
|
||||
| Home Assistant | `homeassistant` | 投递到 HA 对话 |
|
||||
| DingTalk | `dingtalk` | 投递到钉钉 |
|
||||
| Feishu | `feishu` | 投递到飞书 |
|
||||
| WeCom | `wecom` | 投递到企业微信 |
|
||||
| Weixin | `weixin` | 投递到微信(WeChat) |
|
||||
| BlueBubbles | `bluebubbles` | 通过 BlueBubbles 投递到 iMessage |
|
||||
| QQ Bot | `qqbot` | 通过官方 API v2 投递到 QQ(腾讯) |
|
||||
|
||||
对于 Telegram 话题,使用格式 `telegram:<chat_id>:<thread_id>`(例如 `telegram:-1001234567890:17585`)。
|
||||
|
||||
### 响应包装
|
||||
|
||||
默认情况下(`cron.wrap_response: true`),cron 投递内容会被包装:
|
||||
- 头部标识 cron 任务名称和任务内容
|
||||
- 尾部说明 agent 无法在对话中看到已投递的消息
|
||||
|
||||
cron 响应中的 `[SILENT]` 前缀会完全抑制投递——适用于只需写入文件或执行副作用的任务。
|
||||
|
||||
### 会话隔离
|
||||
|
||||
Cron 投递**不会**镜像到 gateway 会话的对话历史中。它们仅存在于 cron 任务自身的会话中。这可防止目标聊天对话中出现消息交替违规。
|
||||
|
||||
## 递归防护
|
||||
|
||||
Cron 运行的会话已禁用 `cronjob` 工具集。这可防止:
|
||||
- 定时任务创建新的 cron 任务
|
||||
- 可能导致 token 用量爆炸的递归调度
|
||||
- 在任务内部意外修改任务调度
|
||||
|
||||
## 锁机制
|
||||
|
||||
调度器使用跨进程文件锁(Unix 上的 `fcntl.flock`,Windows 上的 `msvcrt.locking`)防止重叠的 tick 对同一批到期任务执行两次——即使在 gateway 的进程内 ticker 与独立的 `hermes cron` / 手动 `tick()` 调用之间也如此。若无法获取锁,`tick()` 立即返回 0。
|
||||
|
||||
## CLI 接口
|
||||
|
||||
`hermes cron` CLI 提供直接的任务管理功能:
|
||||
|
||||
```bash
|
||||
hermes cron list # 显示所有任务
|
||||
hermes cron create # 交互式创建任务(别名:add)
|
||||
hermes cron edit <job_id> # 编辑任务配置
|
||||
hermes cron pause <job_id> # 暂停运行中的任务
|
||||
hermes cron resume <job_id> # 恢复已暂停的任务
|
||||
hermes cron run <job_id> # 触发立即执行
|
||||
hermes cron remove <job_id> # 删除任务
|
||||
```
|
||||
|
||||
## 相关文档
|
||||
|
||||
- [Cron 功能指南](/user-guide/features/cron)
|
||||
- [Gateway 内部机制](./gateway-internals.md)
|
||||
- [Agent 循环内部机制](./agent-loop.md)
|
||||
@ -0,0 +1,192 @@
|
||||
---
|
||||
sidebar_position: 8
|
||||
title: "扩展 CLI"
|
||||
description: "构建包装 CLI,通过自定义 widget、快捷键和布局变更来扩展 Hermes TUI"
|
||||
---
|
||||
|
||||
# 扩展 CLI
|
||||
|
||||
Hermes 在 `HermesCLI` 上暴露了受保护的扩展 hook(钩子),使包装 CLI 可以添加 widget、快捷键和布局自定义,而无需覆盖超过 1000 行的 `run()` 方法。这样可以让你的扩展与内部变更解耦。
|
||||
|
||||
## 扩展点
|
||||
|
||||
共有五个扩展接缝可用:
|
||||
|
||||
| Hook | 用途 | 何时覆盖 |
|
||||
|------|---------|------------------|
|
||||
| `_get_extra_tui_widgets()` | 向布局注入 widget | 需要持久 UI 元素(面板、状态栏、迷你播放器)时 |
|
||||
| `_register_extra_tui_keybindings(kb, *, input_area)` | 添加键盘快捷键 | 需要热键(切换面板、传输控制、模态快捷键)时 |
|
||||
| `_build_tui_layout_children(**widgets)` | 完全控制 widget 排序 | 需要重新排序或包装现有 widget 时(少见) |
|
||||
| `process_command()` | 添加自定义斜杠命令 | 需要处理 `/mycommand` 时(已有 hook) |
|
||||
| `_build_tui_style_dict()` | 自定义 prompt_toolkit 样式 | 需要自定义颜色或样式时(已有 hook) |
|
||||
|
||||
前三个是新增的受保护 hook,后两个已存在。
|
||||
|
||||
## 快速开始:包装 CLI
|
||||
|
||||
```python
|
||||
#!/usr/bin/env python3
|
||||
"""my_cli.py — Example wrapper CLI that extends Hermes."""
|
||||
|
||||
from cli import HermesCLI
|
||||
from prompt_toolkit.layout import FormattedTextControl, Window
|
||||
from prompt_toolkit.filters import Condition
|
||||
|
||||
|
||||
class MyCLI(HermesCLI):
|
||||
|
||||
def __init__(self, **kwargs):
|
||||
super().__init__(**kwargs)
|
||||
self._panel_visible = False
|
||||
|
||||
def _get_extra_tui_widgets(self):
|
||||
"""Add a toggleable info panel above the status bar."""
|
||||
cli_ref = self
|
||||
return [
|
||||
Window(
|
||||
FormattedTextControl(lambda: "📊 My custom panel content"),
|
||||
height=1,
|
||||
filter=Condition(lambda: cli_ref._panel_visible),
|
||||
),
|
||||
]
|
||||
|
||||
def _register_extra_tui_keybindings(self, kb, *, input_area):
|
||||
"""F2 toggles the custom panel."""
|
||||
cli_ref = self
|
||||
|
||||
@kb.add("f2")
|
||||
def _toggle_panel(event):
|
||||
cli_ref._panel_visible = not cli_ref._panel_visible
|
||||
|
||||
def process_command(self, cmd: str) -> bool:
|
||||
"""Add a /panel slash command."""
|
||||
if cmd.strip().lower() == "/panel":
|
||||
self._panel_visible = not self._panel_visible
|
||||
state = "visible" if self._panel_visible else "hidden"
|
||||
print(f"Panel is now {state}")
|
||||
return True
|
||||
return super().process_command(cmd)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
cli = MyCLI()
|
||||
cli.run()
|
||||
```
|
||||
|
||||
运行:
|
||||
|
||||
```bash
|
||||
cd ~/.hermes/hermes-agent
|
||||
source .venv/bin/activate
|
||||
python my_cli.py
|
||||
```
|
||||
|
||||
## Hook 参考
|
||||
|
||||
### `_get_extra_tui_widgets()`
|
||||
|
||||
返回要插入 TUI 布局的 prompt_toolkit widget 列表。Widget 出现在**间隔区与状态栏之间**——位于输入区上方、主输出区下方。
|
||||
|
||||
```python
|
||||
def _get_extra_tui_widgets(self) -> list:
|
||||
return [] # default: no extra widgets
|
||||
```
|
||||
|
||||
每个 widget 应为 prompt_toolkit 容器(如 `Window`、`ConditionalContainer`、`HSplit`)。使用 `ConditionalContainer` 或 `filter=Condition(...)` 可使 widget 支持切换显示。
|
||||
|
||||
```python
|
||||
from prompt_toolkit.layout import ConditionalContainer, Window, FormattedTextControl
|
||||
from prompt_toolkit.filters import Condition
|
||||
|
||||
def _get_extra_tui_widgets(self):
|
||||
return [
|
||||
ConditionalContainer(
|
||||
Window(FormattedTextControl("Status: connected"), height=1),
|
||||
filter=Condition(lambda: self._show_status),
|
||||
),
|
||||
]
|
||||
```
|
||||
|
||||
### `_register_extra_tui_keybindings(kb, *, input_area)`
|
||||
|
||||
在 Hermes 注册自身快捷键之后、布局构建之前调用。将你的快捷键添加到 `kb`。
|
||||
|
||||
```python
|
||||
def _register_extra_tui_keybindings(self, kb, *, input_area):
|
||||
pass # default: no extra keybindings
|
||||
```
|
||||
|
||||
参数:
|
||||
- **`kb`** — prompt_toolkit 应用的 `KeyBindings` 实例
|
||||
- **`input_area`** — 主 `TextArea` widget,用于读取或操作用户输入
|
||||
|
||||
```python
|
||||
def _register_extra_tui_keybindings(self, kb, *, input_area):
|
||||
cli_ref = self
|
||||
|
||||
@kb.add("f3")
|
||||
def _clear_input(event):
|
||||
input_area.text = ""
|
||||
|
||||
@kb.add("f4")
|
||||
def _insert_template(event):
|
||||
input_area.text = "/search "
|
||||
```
|
||||
|
||||
**避免与内置快捷键冲突**:`Enter`(提交)、`Escape Enter`(换行)、`Ctrl-C`(中断)、`Ctrl-D`(退出)、`Tab`(接受自动建议)。F2 及以上的功能键和 Ctrl 组合键通常是安全的。
|
||||
|
||||
### `_build_tui_layout_children(**widgets)`
|
||||
|
||||
仅在需要完全控制 widget 排序时才覆盖此方法。大多数扩展应使用 `_get_extra_tui_widgets()` 代替。
|
||||
|
||||
```python
|
||||
def _build_tui_layout_children(self, *, sudo_widget, secret_widget,
|
||||
approval_widget, clarify_widget, model_picker_widget=None,
|
||||
spinner_widget=None, spacer, status_bar, input_rule_top,
|
||||
image_bar, input_area, input_rule_bot, voice_status_bar,
|
||||
completions_menu) -> list:
|
||||
```
|
||||
|
||||
默认实现返回(值为 `None` 的 widget 会被过滤掉):
|
||||
|
||||
```python
|
||||
[
|
||||
Window(height=0), # anchor
|
||||
sudo_widget, # sudo password prompt (conditional)
|
||||
secret_widget, # secret input prompt (conditional)
|
||||
approval_widget, # dangerous command approval (conditional)
|
||||
clarify_widget, # clarify question UI (conditional)
|
||||
model_picker_widget, # model picker overlay (conditional)
|
||||
spinner_widget, # thinking spinner (conditional)
|
||||
spacer, # fills remaining vertical space
|
||||
*self._get_extra_tui_widgets(), # YOUR WIDGETS GO HERE
|
||||
status_bar, # model/token/context status line
|
||||
input_rule_top, # ─── border above input
|
||||
image_bar, # attached images indicator
|
||||
input_area, # user text input
|
||||
input_rule_bot, # ─── border below input
|
||||
voice_status_bar, # voice mode status (conditional)
|
||||
completions_menu, # autocomplete dropdown
|
||||
]
|
||||
```
|
||||
|
||||
## 布局示意图
|
||||
|
||||
默认布局从上到下:
|
||||
|
||||
1. **输出区** — 滚动的对话历史
|
||||
2. **间隔区**
|
||||
3. **额外 widget** — 来自 `_get_extra_tui_widgets()`
|
||||
4. **状态栏** — 模型、上下文占比、已用时间
|
||||
5. **图片栏** — 已附加图片数量
|
||||
6. **输入区** — 用户 prompt(提示词)
|
||||
7. **语音状态** — 录音指示器
|
||||
8. **补全菜单** — 自动补全建议
|
||||
|
||||
## 使用技巧
|
||||
|
||||
- **状态变更后刷新显示**:调用 `self._invalidate()` 触发 prompt_toolkit 重绘。
|
||||
- **访问 agent 状态**:`self.agent`、`self.model`、`self.conversation_history` 均可直接使用。
|
||||
- **自定义样式**:覆盖 `_build_tui_style_dict()` 并为自定义样式类添加条目。
|
||||
- **斜杠命令**:覆盖 `process_command()`,处理自己的命令,其余一律调用 `super().process_command(cmd)`。
|
||||
- **不要覆盖 `run()`**,除非绝对必要——扩展 hook 的存在正是为了避免这种耦合。
|
||||
@ -0,0 +1,262 @@
|
||||
---
|
||||
sidebar_position: 7
|
||||
title: "Gateway 内部机制"
|
||||
description: "消息 gateway 如何启动、授权用户、路由会话以及投递消息"
|
||||
---
|
||||
|
||||
# Gateway 内部机制
|
||||
|
||||
消息 gateway 是一个长期运行的进程,通过统一架构将 Hermes 连接到 20 余个外部消息平台。
|
||||
|
||||
## 关键文件
|
||||
|
||||
| 文件 | 用途 |
|
||||
|------|---------|
|
||||
| `gateway/run.py` | `GatewayRunner` — 主循环、斜杠命令、消息分发(大文件;请查看 git 获取当前行数) |
|
||||
| `gateway/session.py` | `SessionStore` — 会话持久化与会话键构造 |
|
||||
| `gateway/delivery.py` | 向目标平台/频道投递出站消息 |
|
||||
| `gateway/pairing.py` | 用于用户授权的 DM 配对流程 |
|
||||
| `gateway/channel_directory.py` | 将聊天 ID 映射为可读名称,用于 cron 投递 |
|
||||
| `gateway/hooks.py` | Hook(钩子)发现、加载与生命周期事件分发 |
|
||||
| `gateway/mirror.py` | 为 `send_message` 提供跨会话消息镜像 |
|
||||
| `gateway/status.py` | 面向 profile 范围的 gateway 实例的 token 锁管理 |
|
||||
| `gateway/builtin_hooks/` | 始终注册的 hook 扩展点(当前未内置任何 hook) |
|
||||
| `gateway/platforms/` | 平台适配器(每个消息平台一个) |
|
||||
|
||||
## 架构概览
|
||||
|
||||
```text
|
||||
┌─────────────────────────────────────────────────┐
|
||||
│ GatewayRunner │
|
||||
│ │
|
||||
│ ┌──────────┐ ┌──────────┐ ┌──────────┐ │
|
||||
│ │ Telegram │ │ Discord │ │ Slack │ │
|
||||
│ │ Adapter │ │ Adapter │ │ Adapter │ │
|
||||
│ └────┬─────┘ └────┬─────┘ └────┬─────┘ │
|
||||
│ │ │ │ │
|
||||
│ └─────────────┼─────────────┘ │
|
||||
│ ▼ │
|
||||
│ _handle_message() │
|
||||
│ │ │
|
||||
│ ┌───────────┼───────────┐ │
|
||||
│ ▼ ▼ ▼ │
|
||||
│ Slash command AIAgent Queue/BG │
|
||||
│ dispatch creation sessions │
|
||||
│ │ │
|
||||
│ ▼ │
|
||||
│ SessionStore │
|
||||
│ (SQLite persistence) │
|
||||
└───────┴─────────────┴─────────────┴─────────────┘
|
||||
```
|
||||
|
||||
## 消息流程
|
||||
|
||||
当消息从任意平台到达时:
|
||||
|
||||
1. **平台适配器**接收原始事件,将其规范化为 `MessageEvent`
|
||||
2. **基础适配器**检查活跃会话守卫:
|
||||
- 若该会话的 agent 正在运行 → 将消息加入队列,设置中断事件
|
||||
- 若为 `/approve`、`/deny`、`/stop` → 绕过守卫(内联分发)
|
||||
3. **GatewayRunner._handle_message()** 接收事件:
|
||||
- 通过 `_session_key_for_source()` 解析会话键(格式:`agent:main:{platform}:{chat_type}:{chat_id}`)
|
||||
- 检查授权(见下方授权章节)
|
||||
- 检查是否为斜杠命令 → 分发至命令处理器
|
||||
- 检查 agent 是否已在运行 → 拦截 `/stop`、`/status` 等命令
|
||||
- 否则 → 创建 `AIAgent` 实例并运行对话
|
||||
4. **响应**通过平台适配器回传
|
||||
|
||||
### 会话键格式
|
||||
|
||||
会话键编码了完整的路由上下文:
|
||||
|
||||
```
|
||||
agent:main:{platform}:{chat_type}:{chat_id}
|
||||
```
|
||||
|
||||
示例:`agent:main:telegram:private:123456789`
|
||||
|
||||
支持线程的平台(Telegram 论坛话题、Discord 线程、Slack 线程)可能在 chat_id 部分包含线程 ID。**切勿手动构造会话键** — 请始终使用 `gateway/session.py` 中的 `build_session_key()`。
|
||||
|
||||
### 两级消息守卫
|
||||
|
||||
当 agent 正在运行时,传入消息会依次经过两级守卫:
|
||||
|
||||
1. **第一级 — 基础适配器**(`gateway/platforms/base.py`):检查 `_active_sessions`。若会话处于活跃状态,将消息加入 `_pending_messages` 队列并设置中断事件。此级在消息到达 gateway runner *之前*进行拦截。
|
||||
|
||||
2. **第二级 — Gateway runner**(`gateway/run.py`):检查 `_running_agents`。拦截特定命令(`/stop`、`/new`、`/queue`、`/status`、`/approve`、`/deny`)并进行相应路由。其余所有消息触发 `running_agent.interrupt()`。
|
||||
|
||||
必须在 agent 被阻塞时到达 runner 的命令(如 `/approve`)通过 `await self._message_handler(event)` **内联**分发 — 绕过后台任务系统以避免竞态条件。
|
||||
|
||||
## 授权
|
||||
|
||||
Gateway 使用多层授权检查,按顺序评估:
|
||||
|
||||
1. **平台级全量放行标志**(如 `TELEGRAM_ALLOW_ALL_USERS`)— 若设置,该平台所有用户均被授权
|
||||
2. **平台白名单**(如 `TELEGRAM_ALLOWED_USERS`)— 逗号分隔的用户 ID
|
||||
3. **DM 配对** — 已认证用户可通过配对码为新用户授权
|
||||
4. **全局放行标志**(`GATEWAY_ALLOW_ALL_USERS`)— 若设置,所有平台的所有用户均被授权
|
||||
5. **默认:拒绝** — 未授权用户被拒绝
|
||||
|
||||
### DM 配对流程
|
||||
|
||||
```text
|
||||
Admin: /pair
|
||||
Gateway: "Pairing code: ABC123. Share with the user."
|
||||
New user: ABC123
|
||||
Gateway: "Paired! You're now authorized."
|
||||
```
|
||||
|
||||
配对状态持久化于 `gateway/pairing.py`,重启后仍然有效。
|
||||
|
||||
## 斜杠命令分发
|
||||
|
||||
Gateway 中所有斜杠命令均经过相同的解析流程:
|
||||
|
||||
1. `hermes_cli/commands.py` 中的 `resolve_command()` 将输入映射为规范名称(处理别名、前缀匹配)
|
||||
2. 规范名称与 `GATEWAY_KNOWN_COMMANDS` 进行比对
|
||||
3. `_handle_message()` 中的处理器根据规范名称进行分发
|
||||
4. 部分命令受配置门控(`CommandDef` 上的 `gateway_config_gate`)
|
||||
|
||||
### 运行中 Agent 守卫
|
||||
|
||||
在 agent 处理消息期间不得执行的命令会被提前拒绝:
|
||||
|
||||
```python
|
||||
if _quick_key in self._running_agents:
|
||||
if canonical == "model":
|
||||
return "⏳ Agent is running — wait for it to finish or /stop first."
|
||||
```
|
||||
|
||||
绕过命令(`/stop`、`/new`、`/approve`、`/deny`、`/queue`、`/status`)具有特殊处理逻辑。
|
||||
|
||||
## 配置来源
|
||||
|
||||
Gateway 从多个来源读取配置:
|
||||
|
||||
| 来源 | 提供内容 |
|
||||
|--------|-----------------|
|
||||
| `~/.hermes/.env` | API 密钥、bot token、平台凭据 |
|
||||
| `~/.hermes/config.yaml` | 模型设置、工具配置、显示选项 |
|
||||
| 环境变量 | 覆盖上述任意配置 |
|
||||
|
||||
与 CLI(使用带硬编码默认值的 `load_cli_config()`)不同,gateway 通过 YAML 加载器直接读取 `config.yaml`。这意味着存在于 CLI 默认值字典但不在用户配置文件中的配置键,在 CLI 和 gateway 之间可能表现不同。
|
||||
|
||||
## 平台适配器
|
||||
|
||||
每个消息平台在 `gateway/platforms/` 下均有对应适配器:
|
||||
|
||||
```text
|
||||
gateway/platforms/
|
||||
├── base.py # BaseAdapter — 所有平台的共享逻辑
|
||||
├── telegram.py # Telegram Bot API(长轮询或 webhook)
|
||||
├── discord.py # Discord bot(通过 discord.py)
|
||||
├── slack.py # Slack Socket Mode
|
||||
├── whatsapp.py # WhatsApp Business Cloud API
|
||||
├── signal.py # Signal(通过 signal-cli REST API)
|
||||
├── matrix.py # Matrix(通过 mautrix,可选 E2EE)
|
||||
├── mattermost.py # Mattermost WebSocket API
|
||||
├── email.py # 电子邮件(通过 IMAP/SMTP)
|
||||
├── sms.py # 短信(通过 Twilio)
|
||||
├── dingtalk.py # 钉钉 WebSocket
|
||||
├── feishu.py # 飞书/Lark WebSocket 或 webhook
|
||||
├── wecom.py # 企业微信(WeCom)回调
|
||||
├── weixin.py # 微信(个人版,通过 iLink Bot API)
|
||||
├── bluebubbles.py # Apple iMessage(通过 BlueBubbles macOS 服务端)
|
||||
├── qqbot/ # QQ Bot(腾讯 QQ,通过官方 API v2,子包:adapter.py、crypto.py、keyboards.py 等)
|
||||
├── yuanbao.py # 元宝(腾讯)私信/群组适配器
|
||||
├── feishu_comment.py # 飞书文档/云盘评论回复处理器
|
||||
├── msgraph_webhook.py # Microsoft Graph 变更通知 webhook(Teams、Outlook 等)
|
||||
├── webhook.py # 入站/出站 webhook 适配器
|
||||
├── api_server.py # REST API 服务器适配器
|
||||
└── homeassistant.py # Home Assistant 对话集成
|
||||
```
|
||||
|
||||
适配器实现统一接口:
|
||||
- `connect()` / `disconnect()` — 生命周期管理
|
||||
- `send_message()` — 出站消息投递
|
||||
- `on_message()` — 入站消息规范化 → `MessageEvent`
|
||||
|
||||
### Token 锁
|
||||
|
||||
使用唯一凭据连接的适配器在 `connect()` 中调用 `acquire_scoped_lock()`,在 `disconnect()` 中调用 `release_scoped_lock()`。这可防止两个 profile 同时使用同一 bot token。
|
||||
|
||||
## 投递路径
|
||||
|
||||
出站投递(`gateway/delivery.py`)处理以下场景:
|
||||
|
||||
- **直接回复** — 将响应发回原始聊天
|
||||
- **主频道投递** — 将 cron 任务输出和后台结果路由至已配置的主频道
|
||||
- **显式目标投递** — `send_message` 工具指定 `telegram:-1001234567890`,或通过 [`hermes send` CLI](/guides/pipe-script-output) 封装同一工具供 shell 脚本使用
|
||||
- **跨平台投递** — 投递至与原始消息不同的平台
|
||||
|
||||
Cron 任务投递**不会**镜像到 gateway 会话历史中 — 它们仅存在于各自的 cron 会话中。这是有意为之的设计选择,以避免消息交替违规。
|
||||
|
||||
## Hooks
|
||||
|
||||
Gateway hook 是响应生命周期事件的 Python 模块。
|
||||
|
||||
### Gateway Hook 事件
|
||||
|
||||
| 事件 | 触发时机 |
|
||||
|-------|-----------|
|
||||
| `gateway:startup` | Gateway 进程启动时 |
|
||||
| `session:start` | 新对话会话开始时 |
|
||||
| `session:end` | 会话完成或超时时 |
|
||||
| `session:reset` | 用户通过 `/new` 重置会话时 |
|
||||
| `agent:start` | Agent 开始处理消息时 |
|
||||
| `agent:step` | Agent 完成一次工具调用迭代时 |
|
||||
| `agent:end` | Agent 完成并返回响应时 |
|
||||
| `command:*` | 任意斜杠命令被执行时 |
|
||||
|
||||
Hook 从 `gateway/builtin_hooks/`(扩展点 — 当前发行版中为空;`_register_builtin_hooks()` 是一个空操作存根)和 `~/.hermes/hooks/`(用户安装)中发现。每个 hook 是一个包含 `HOOK.yaml` 清单和 `handler.py` 的目录。
|
||||
|
||||
## 内存提供者集成
|
||||
|
||||
当内存提供者插件(如 Honcho)启用时:
|
||||
|
||||
1. Gateway 为每条消息创建一个带会话 ID 的 `AIAgent`
|
||||
2. `MemoryManager` 使用会话上下文初始化提供者
|
||||
3. 提供者工具(如 `honcho_profile`、`viking_search`)通过以下路径路由:
|
||||
|
||||
```text
|
||||
AIAgent._invoke_tool()
|
||||
→ self._memory_manager.handle_tool_call(name, args)
|
||||
→ provider.handle_tool_call(name, args)
|
||||
```
|
||||
|
||||
4. 会话结束/重置时,`on_session_end()` 触发以进行清理和最终数据刷写
|
||||
|
||||
### 内存刷写生命周期
|
||||
|
||||
当会话被重置、恢复或过期时:
|
||||
1. 内置内存刷写至磁盘
|
||||
2. 内存提供者的 `on_session_end()` hook 触发
|
||||
3. 临时 `AIAgent` 运行仅含内存的对话轮次
|
||||
4. 上下文随后被丢弃或归档
|
||||
|
||||
## 后台维护
|
||||
|
||||
Gateway 在处理消息的同时运行周期性维护任务:
|
||||
|
||||
- **Cron 计时** — 检查任务计划并触发到期任务
|
||||
- **会话过期** — 超时后清理废弃会话
|
||||
- **内存刷写** — 在会话过期前主动刷写内存
|
||||
- **缓存刷新** — 刷新模型列表和提供者状态
|
||||
|
||||
## 进程管理
|
||||
|
||||
Gateway 作为长期运行进程运行,管理方式如下:
|
||||
|
||||
- `hermes gateway start` / `hermes gateway stop` — 手动控制
|
||||
- `systemctl`(Linux)或 `launchctl`(macOS)— 服务管理
|
||||
- PID 文件位于 `~/.hermes/gateway.pid` — 面向 profile 的进程追踪
|
||||
|
||||
**Profile 范围 vs 全局**:`start_gateway()` 使用 profile 范围的 PID 文件。`hermes gateway stop` 仅停止当前 profile 的 gateway。`hermes gateway stop --all` 使用全局 `ps aux` 扫描来终止所有 gateway 进程(用于更新时)。
|
||||
|
||||
## 相关文档
|
||||
|
||||
- [会话存储](./session-storage.md)
|
||||
- [Cron 内部机制](./cron-internals.md)
|
||||
- [ACP 内部机制](./acp-internals.md)
|
||||
- [Agent 循环内部机制](./agent-loop.md)
|
||||
- [消息 Gateway(用户指南)](/user-guide/messaging)
|
||||
@ -0,0 +1,288 @@
|
||||
---
|
||||
sidebar_position: 11
|
||||
title: "图像生成 Provider 插件"
|
||||
description: "如何为 Hermes Agent 构建图像生成后端插件"
|
||||
---
|
||||
|
||||
# 构建图像生成 Provider 插件
|
||||
|
||||
图像生成 provider 插件注册一个后端,用于处理所有 `image_generate` 工具调用——DALL·E、gpt-image、Grok、Flux、Imagen、Stable Diffusion、fal、Replicate、本地 ComfyUI 装置,任何后端均可。内置 provider(OpenAI、OpenAI-Codex、xAI)均以插件形式提供。你可以通过在 `plugins/image_gen/<name>/` 目录下放置一个目录来添加新的 provider,或覆盖内置 provider。
|
||||
|
||||
:::tip
|
||||
图像生成是 Hermes 支持的多种**后端插件**之一。其他插件(各有更专用的 ABC)包括:[Memory Provider 插件](/developer-guide/memory-provider-plugin)、[Context Engine 插件](/developer-guide/context-engine-plugin) 和 [Model Provider 插件](/developer-guide/model-provider-plugin)。通用工具/hook/CLI 插件请参阅 [构建 Hermes 插件](/guides/build-a-hermes-plugin)。
|
||||
:::
|
||||
|
||||
## 发现机制
|
||||
|
||||
Hermes 在三个位置扫描图像生成后端:
|
||||
|
||||
1. **内置** — `<repo>/plugins/image_gen/<name>/`(以 `kind: backend` 自动加载,始终可用)
|
||||
2. **用户** — `~/.hermes/plugins/image_gen/<name>/`(通过 `plugins.enabled` 选择启用)
|
||||
3. **Pip** — 声明了 `hermes_agent.plugins` 入口点的包
|
||||
|
||||
每个插件的 `register(ctx)` 函数调用 `ctx.register_image_gen_provider(...)` — 将其注册到 `agent/image_gen_registry.py` 中的注册表。活跃 provider 由 `config.yaml` 中的 `image_gen.provider` 指定;`hermes tools` 会引导用户完成选择。
|
||||
|
||||
`image_generate` 工具包装器向注册表请求活跃 provider 并分发调用。若未注册任何 provider,工具会显示一条有用的错误信息,指引用户使用 `hermes tools`。
|
||||
|
||||
## 目录结构
|
||||
|
||||
```
|
||||
plugins/image_gen/my-backend/
|
||||
├── __init__.py # ImageGenProvider 子类 + register()
|
||||
└── plugin.yaml # 包含 kind: backend 的清单文件
|
||||
```
|
||||
|
||||
内置插件到此即完整。位于 `~/.hermes/plugins/image_gen/<name>/` 的用户插件需要在 `config.yaml` 的 `plugins.enabled` 中添加(或运行 `hermes plugins enable <name>`)。
|
||||
|
||||
## ImageGenProvider ABC
|
||||
|
||||
继承 `agent.image_gen_provider.ImageGenProvider`。唯一必须实现的成员是 `name` 属性和 `generate()` 方法——其他所有成员均有合理的默认值:
|
||||
|
||||
```python
|
||||
# plugins/image_gen/my-backend/__init__.py
|
||||
from typing import Any, Dict, List, Optional
|
||||
import os
|
||||
|
||||
from agent.image_gen_provider import (
|
||||
DEFAULT_ASPECT_RATIO,
|
||||
ImageGenProvider,
|
||||
error_response,
|
||||
resolve_aspect_ratio,
|
||||
save_b64_image,
|
||||
success_response,
|
||||
)
|
||||
|
||||
|
||||
class MyBackendImageGenProvider(ImageGenProvider):
|
||||
@property
|
||||
def name(self) -> str:
|
||||
# Stable id used in image_gen.provider config. Lowercase, no spaces.
|
||||
return "my-backend"
|
||||
|
||||
@property
|
||||
def display_name(self) -> str:
|
||||
# Human label shown in `hermes tools`. Defaults to name.title() if omitted.
|
||||
return "My Backend"
|
||||
|
||||
def is_available(self) -> bool:
|
||||
# Return False if credentials or deps are missing.
|
||||
# The tool's availability gate calls this before dispatch.
|
||||
if not os.environ.get("MY_BACKEND_API_KEY"):
|
||||
return False
|
||||
try:
|
||||
import my_backend_sdk # noqa: F401
|
||||
except ImportError:
|
||||
return False
|
||||
return True
|
||||
|
||||
def list_models(self) -> List[Dict[str, Any]]:
|
||||
# Catalog shown in `hermes tools` model picker.
|
||||
return [
|
||||
{
|
||||
"id": "my-model-fast",
|
||||
"display": "My Model (Fast)",
|
||||
"speed": "~5s",
|
||||
"strengths": "Quick iteration",
|
||||
"price": "$0.01/image",
|
||||
},
|
||||
{
|
||||
"id": "my-model-hq",
|
||||
"display": "My Model (HQ)",
|
||||
"speed": "~30s",
|
||||
"strengths": "Highest fidelity",
|
||||
"price": "$0.04/image",
|
||||
},
|
||||
]
|
||||
|
||||
def default_model(self) -> Optional[str]:
|
||||
return "my-model-fast"
|
||||
|
||||
def get_setup_schema(self) -> Dict[str, Any]:
|
||||
# Metadata for the `hermes tools` picker — keys to prompt for at setup.
|
||||
return {
|
||||
"name": "My Backend",
|
||||
"badge": "paid", # optional; shown as a short tag in the picker
|
||||
"tag": "One-line description shown under the name",
|
||||
"env_vars": [
|
||||
{
|
||||
"key": "MY_BACKEND_API_KEY",
|
||||
"prompt": "My Backend API key",
|
||||
"url": "https://my-backend.example.com/api-keys",
|
||||
},
|
||||
],
|
||||
}
|
||||
|
||||
def generate(
|
||||
self,
|
||||
prompt: str,
|
||||
aspect_ratio: str = DEFAULT_ASPECT_RATIO,
|
||||
**kwargs: Any,
|
||||
) -> Dict[str, Any]:
|
||||
prompt = (prompt or "").strip()
|
||||
aspect_ratio = resolve_aspect_ratio(aspect_ratio)
|
||||
|
||||
if not prompt:
|
||||
return error_response(
|
||||
error="Prompt is required",
|
||||
error_type="invalid_input",
|
||||
provider=self.name,
|
||||
prompt="",
|
||||
aspect_ratio=aspect_ratio,
|
||||
)
|
||||
|
||||
# Model selection precedence: env var → config → default. The helper
|
||||
# _resolve_model() in the built-in openai plugin is a good reference.
|
||||
model_id = kwargs.get("model") or self.default_model() or "my-model-fast"
|
||||
|
||||
try:
|
||||
import my_backend_sdk
|
||||
client = my_backend_sdk.Client(api_key=os.environ["MY_BACKEND_API_KEY"])
|
||||
result = client.generate(
|
||||
prompt=prompt,
|
||||
model=model_id,
|
||||
aspect_ratio=aspect_ratio,
|
||||
)
|
||||
|
||||
# Two shapes supported:
|
||||
# - URL string: return it as `image`
|
||||
# - base64 data: save under $HERMES_HOME/cache/images/ via save_b64_image()
|
||||
if result.get("image_b64"):
|
||||
path = save_b64_image(
|
||||
result["image_b64"],
|
||||
prefix=self.name,
|
||||
extension="png",
|
||||
)
|
||||
image = str(path)
|
||||
else:
|
||||
image = result["image_url"]
|
||||
|
||||
return success_response(
|
||||
image=image,
|
||||
model=model_id,
|
||||
prompt=prompt,
|
||||
aspect_ratio=aspect_ratio,
|
||||
provider=self.name,
|
||||
)
|
||||
except Exception as exc:
|
||||
return error_response(
|
||||
error=str(exc),
|
||||
error_type=type(exc).__name__,
|
||||
provider=self.name,
|
||||
model=model_id,
|
||||
prompt=prompt,
|
||||
aspect_ratio=aspect_ratio,
|
||||
)
|
||||
|
||||
|
||||
def register(ctx) -> None:
|
||||
"""Plugin entry point — called once at load time."""
|
||||
ctx.register_image_gen_provider(MyBackendImageGenProvider())
|
||||
```
|
||||
|
||||
## plugin.yaml
|
||||
|
||||
```yaml
|
||||
name: my-backend
|
||||
version: 1.0.0
|
||||
description: My image backend — text-to-image via My Backend SDK
|
||||
author: Your Name
|
||||
kind: backend
|
||||
requires_env:
|
||||
- MY_BACKEND_API_KEY
|
||||
```
|
||||
|
||||
`kind: backend` 决定插件被路由到图像生成注册路径。`requires_env` 在 `hermes plugins install` 期间会提示用户输入。
|
||||
|
||||
## ABC 参考
|
||||
|
||||
完整契约位于 `agent/image_gen_provider.py`。通常需要覆盖的方法:
|
||||
|
||||
| 成员 | 必须 | 默认值 | 用途 |
|
||||
|---|---|---|---|
|
||||
| `name` | ✅ | — | 在 `image_gen.provider` 配置中使用的稳定 id |
|
||||
| `display_name` | — | `name.title()` | 在 `hermes tools` 中显示的标签 |
|
||||
| `is_available()` | — | `True` | 缺少凭据/依赖时的拦截门控 |
|
||||
| `list_models()` | — | `[]` | `hermes tools` 模型选择器的目录 |
|
||||
| `default_model()` | — | `list_models()` 的第一项 | 未配置模型时的回退 |
|
||||
| `get_setup_schema()` | — | 最小值 | 选择器元数据 + 环境变量提示 |
|
||||
| `generate(prompt, aspect_ratio, **kwargs)` | ✅ | — | 实际调用 |
|
||||
|
||||
## 响应格式
|
||||
|
||||
`generate()` 必须返回通过 `success_response()` 或 `error_response()` 构建的字典。两者均位于 `agent/image_gen_provider.py`。
|
||||
|
||||
**成功:**
|
||||
```python
|
||||
success_response(
|
||||
image=<url-or-absolute-path>,
|
||||
model=<model-id>,
|
||||
prompt=<echoed-prompt>,
|
||||
aspect_ratio="landscape" | "square" | "portrait",
|
||||
provider=<your-provider-name>,
|
||||
extra={...}, # optional backend-specific fields
|
||||
)
|
||||
```
|
||||
|
||||
**错误:**
|
||||
```python
|
||||
error_response(
|
||||
error="human-readable message",
|
||||
error_type="provider_error" | "invalid_input" | "<exception class name>",
|
||||
provider=<your-provider-name>,
|
||||
model=<model-id>,
|
||||
prompt=<prompt>,
|
||||
aspect_ratio=<resolved aspect>,
|
||||
)
|
||||
```
|
||||
|
||||
工具包装器将字典 JSON 序列化后传给 LLM。错误以工具结果的形式呈现;LLM 决定如何向用户解释。
|
||||
|
||||
## 处理 base64 与 URL 输出
|
||||
|
||||
部分后端返回图像 URL(fal、Replicate);其他后端返回 base64 载荷(OpenAI gpt-image-2)。对于 base64 情况,使用 `save_b64_image()` — 它将文件写入 `$HERMES_HOME/cache/images/<prefix>_<timestamp>_<uuid>.<ext>` 并返回绝对 `Path`。将该路径(转为 `str`)作为 `image=` 传入 `success_response()`。Gateway 投递(Telegram 图片气泡、Discord 附件)同时识别 URL 和绝对路径。
|
||||
|
||||
## 用户覆盖
|
||||
|
||||
在 `~/.hermes/plugins/image_gen/<name>/` 放置一个用户插件,使其 `name` 属性与某个内置插件相同,并通过 `hermes plugins enable <name>` 启用——注册表采用后写入优先策略,你的版本将替换内置版本。适用于将 `openai` 插件指向私有代理,或替换自定义模型目录等场景。
|
||||
|
||||
## 测试
|
||||
|
||||
```bash
|
||||
export HERMES_HOME=/tmp/hermes-imggen-test
|
||||
mkdir -p $HERMES_HOME/plugins/image_gen/my-backend
|
||||
# …copy __init__.py + plugin.yaml into that dir…
|
||||
|
||||
export MY_BACKEND_API_KEY=your-test-key
|
||||
hermes plugins enable my-backend
|
||||
|
||||
# Pick it as the active provider
|
||||
echo "image_gen:" >> $HERMES_HOME/config.yaml
|
||||
echo " provider: my-backend" >> $HERMES_HOME/config.yaml
|
||||
|
||||
# Exercise it
|
||||
hermes -z "Generate an image of a corgi in a spacesuit"
|
||||
```
|
||||
|
||||
或交互式操作:`hermes tools` → "Image Generation" → 选择 `my-backend` → 根据提示输入 API key。
|
||||
|
||||
## 参考实现
|
||||
|
||||
- **`plugins/image_gen/openai/__init__.py`** — gpt-image-2 以低/中/高三个档位作为三个虚拟模型 ID,共享同一 API 模型并使用不同的 `quality` 参数。适合参考单一后端下的分层模型设计 + config.yaml 优先级链。
|
||||
- **`plugins/image_gen/xai/__init__.py`** — 通过 xAI 的 Grok Imagine。不同的响应结构(URL 输出,目录更简单)。
|
||||
- **`plugins/image_gen/openai-codex/__init__.py`** — Codex 风格的 Responses API 变体,复用 OpenAI SDK 并使用不同的路由基础 URL。
|
||||
|
||||
## 通过 pip 分发
|
||||
|
||||
```toml
|
||||
# pyproject.toml
|
||||
[project.entry-points."hermes_agent.plugins"]
|
||||
my-backend-imggen = "my_backend_imggen_package"
|
||||
```
|
||||
|
||||
`my_backend_imggen_package` 必须暴露一个顶层 `register` 函数。完整配置请参阅通用插件指南中的 [通过 pip 分发](/guides/build-a-hermes-plugin#distribute-via-pip)。
|
||||
|
||||
## 相关页面
|
||||
|
||||
- [图像生成](/user-guide/features/image-generation) — 面向用户的功能文档
|
||||
- [插件概览](/user-guide/features/plugins) — 所有插件类型一览
|
||||
- [构建 Hermes 插件](/guides/build-a-hermes-plugin) — 通用工具/hook/斜杠命令指南
|
||||
@ -0,0 +1,258 @@
|
||||
---
|
||||
sidebar_position: 8
|
||||
title: "Memory Provider 插件"
|
||||
description: "如何为 Hermes Agent 构建 memory provider 插件"
|
||||
---
|
||||
|
||||
# 构建 Memory Provider 插件
|
||||
|
||||
Memory provider 插件为 Hermes Agent 提供跨会话的持久化知识,超越内置的 MEMORY.md 和 USER.md。本指南介绍如何构建一个 memory provider 插件。
|
||||
|
||||
:::tip
|
||||
Memory provider 是两种 **provider 插件**类型之一。另一种是 [Context Engine 插件](/developer-guide/context-engine-plugin),用于替换内置的上下文压缩器。两者遵循相同的模式:单选、配置驱动、通过 `hermes plugins` 管理。
|
||||
:::
|
||||
|
||||
## 目录结构
|
||||
|
||||
每个 memory provider 位于 `plugins/memory/<name>/`:
|
||||
|
||||
```
|
||||
plugins/memory/my-provider/
|
||||
├── __init__.py # MemoryProvider 实现 + register() 入口点
|
||||
├── plugin.yaml # 元数据(name、description、hooks)
|
||||
└── README.md # 配置说明、配置参考、工具
|
||||
```
|
||||
|
||||
## MemoryProvider 抽象基类
|
||||
|
||||
你的插件需要实现 `agent/memory_provider.py` 中的 `MemoryProvider` 抽象基类(ABC):
|
||||
|
||||
```python
|
||||
from agent.memory_provider import MemoryProvider
|
||||
|
||||
class MyMemoryProvider(MemoryProvider):
|
||||
@property
|
||||
def name(self) -> str:
|
||||
return "my-provider"
|
||||
|
||||
def is_available(self) -> bool:
|
||||
"""检查此 provider 是否可以激活。禁止发起网络请求。"""
|
||||
return bool(os.environ.get("MY_API_KEY"))
|
||||
|
||||
def initialize(self, session_id: str, **kwargs) -> None:
|
||||
"""在 agent 启动时调用一次。
|
||||
|
||||
kwargs 始终包含:
|
||||
hermes_home (str): 当前活跃的 HERMES_HOME 路径。用于存储数据。
|
||||
"""
|
||||
self._api_key = os.environ.get("MY_API_KEY", "")
|
||||
self._session_id = session_id
|
||||
|
||||
# ... 实现其余方法
|
||||
```
|
||||
|
||||
## 必须实现的方法
|
||||
|
||||
### 核心生命周期
|
||||
|
||||
| 方法 | 调用时机 | 是否必须实现? |
|
||||
|--------|-----------|-----------------|
|
||||
| `name`(property) | 始终 | **是** |
|
||||
| `is_available()` | agent 初始化,激活前 | **是** — 禁止网络请求 |
|
||||
| `initialize(session_id, **kwargs)` | agent 启动 | **是** |
|
||||
| `get_tool_schemas()` | 初始化后,用于注入工具 | **是** |
|
||||
| `handle_tool_call(name, args)` | agent 调用你的工具时 | **是**(如果有工具) |
|
||||
|
||||
### 配置
|
||||
|
||||
| 方法 | 用途 | 是否必须实现? |
|
||||
|--------|---------|-----------------|
|
||||
| `get_config_schema()` | 为 `hermes memory setup` 声明配置字段 | **是** |
|
||||
| `save_config(values, hermes_home)` | 将非敏感配置写入原生位置 | **是**(除非仅使用环境变量) |
|
||||
|
||||
### 可选 Hook
|
||||
|
||||
| 方法 | 调用时机 | 使用场景 |
|
||||
|--------|-----------|----------|
|
||||
| `system_prompt_block()` | 系统 prompt 组装时 | 静态 provider 信息 |
|
||||
| `prefetch(query)` | 每次 API 调用前 | 返回召回的上下文 |
|
||||
| `queue_prefetch(query)` | 每轮对话结束后 | 为下一轮预热 |
|
||||
| `sync_turn(user, assistant)` | 每轮对话完成后 | 持久化对话内容 |
|
||||
| `on_session_end(messages)` | 对话结束时 | 最终提取/刷新 |
|
||||
| `on_pre_compress(messages)` | 上下文压缩前 | 在丢弃前保存关键信息 |
|
||||
| `on_memory_write(action, target, content)` | 内置 memory 写入时 | 同步到你的后端 |
|
||||
| `shutdown()` | 进程退出时 | 清理连接 |
|
||||
|
||||
## 配置 Schema
|
||||
|
||||
`get_config_schema()` 返回一个字段描述符列表,供 `hermes memory setup` 使用:
|
||||
|
||||
```python
|
||||
def get_config_schema(self):
|
||||
return [
|
||||
{
|
||||
"key": "api_key",
|
||||
"description": "My Provider API key",
|
||||
"secret": True, # → 写入 .env
|
||||
"required": True,
|
||||
"env_var": "MY_API_KEY", # 显式指定环境变量名
|
||||
"url": "https://my-provider.com/keys", # 获取密钥的地址
|
||||
},
|
||||
{
|
||||
"key": "region",
|
||||
"description": "Server region",
|
||||
"default": "us-east",
|
||||
"choices": ["us-east", "eu-west", "ap-south"],
|
||||
},
|
||||
{
|
||||
"key": "project",
|
||||
"description": "Project identifier",
|
||||
"default": "hermes",
|
||||
},
|
||||
]
|
||||
```
|
||||
|
||||
`secret: True` 且带有 `env_var` 的字段写入 `.env`。非敏感字段传递给 `save_config()`。
|
||||
|
||||
:::tip 最简 Schema 与完整 Schema
|
||||
`get_config_schema()` 中的每个字段都会在 `hermes memory setup` 期间提示用户输入。选项较多的 provider 应保持 schema 精简——只包含用户**必须**配置的字段(API key、必要凭证)。可选配置请在配置文件参考文档中说明(例如 `$HERMES_HOME/myprovider.json`),而不是在 setup 向导中逐一提示。这样既能保持 setup 流程简洁,又支持高级配置。可参考 Supermemory provider 的实现——它只提示输入 API key,其余选项均位于 `supermemory.json` 中。
|
||||
:::
|
||||
|
||||
## 保存配置
|
||||
|
||||
```python
|
||||
def save_config(self, values: dict, hermes_home: str) -> None:
|
||||
"""将非敏感配置写入原生位置。"""
|
||||
import json
|
||||
from pathlib import Path
|
||||
config_path = Path(hermes_home) / "my-provider.json"
|
||||
config_path.write_text(json.dumps(values, indent=2))
|
||||
```
|
||||
|
||||
对于仅使用环境变量的 provider,保留默认的空实现即可。
|
||||
|
||||
## 插件入口点
|
||||
|
||||
```python
|
||||
def register(ctx) -> None:
|
||||
"""由 memory 插件发现系统调用。"""
|
||||
ctx.register_memory_provider(MyMemoryProvider())
|
||||
```
|
||||
|
||||
## plugin.yaml
|
||||
|
||||
```yaml
|
||||
name: my-provider
|
||||
version: 1.0.0
|
||||
description: "此 provider 功能的简短描述。"
|
||||
hooks:
|
||||
- on_session_end # 列出你实现的 hook
|
||||
```
|
||||
|
||||
## 线程约定
|
||||
|
||||
**`sync_turn()` 必须是非阻塞的。** 如果你的后端存在延迟(API 调用、LLM 处理),请在守护线程中执行:
|
||||
|
||||
```python
|
||||
def sync_turn(self, user_content, assistant_content):
|
||||
def _sync():
|
||||
try:
|
||||
self._api.ingest(user_content, assistant_content)
|
||||
except Exception as e:
|
||||
logger.warning("Sync failed: %s", e)
|
||||
|
||||
if self._sync_thread and self._sync_thread.is_alive():
|
||||
self._sync_thread.join(timeout=5.0)
|
||||
self._sync_thread = threading.Thread(target=_sync, daemon=True)
|
||||
self._sync_thread.start()
|
||||
```
|
||||
|
||||
## Profile 隔离
|
||||
|
||||
所有存储路径**必须**使用 `initialize()` 中的 `hermes_home` kwarg,而不是硬编码的 `~/.hermes`:
|
||||
|
||||
```python
|
||||
# 正确 — 按 profile 隔离
|
||||
from hermes_constants import get_hermes_home
|
||||
data_dir = get_hermes_home() / "my-provider"
|
||||
|
||||
# 错误 — 所有 profile 共享
|
||||
data_dir = Path("~/.hermes/my-provider").expanduser()
|
||||
```
|
||||
|
||||
## 测试
|
||||
|
||||
完整的端到端测试模式(使用真实 SQLite provider)请参见 `tests/agent/test_memory_plugin_e2e.py`。
|
||||
|
||||
```python
|
||||
from agent.memory_manager import MemoryManager
|
||||
|
||||
mgr = MemoryManager()
|
||||
mgr.add_provider(my_provider)
|
||||
mgr.initialize_all(session_id="test-1", platform="cli")
|
||||
|
||||
# 测试工具路由
|
||||
result = mgr.handle_tool_call("my_tool", {"action": "add", "content": "test"})
|
||||
|
||||
# 测试生命周期
|
||||
mgr.sync_all("user msg", "assistant msg")
|
||||
mgr.on_session_end([])
|
||||
mgr.shutdown_all()
|
||||
```
|
||||
|
||||
## 添加 CLI 命令
|
||||
|
||||
Memory provider 插件可以注册自己的 CLI 子命令树(例如 `hermes my-provider status`、`hermes my-provider config`)。这套系统基于约定发现,无需修改核心文件。
|
||||
|
||||
### 工作原理
|
||||
|
||||
1. 在插件目录中添加 `cli.py` 文件
|
||||
2. 定义 `register_cli(subparser)` 函数来构建 argparse 树
|
||||
3. memory 插件系统在启动时通过 `discover_plugin_cli_commands()` 自动发现
|
||||
4. 你的命令以 `hermes <provider-name> <subcommand>` 的形式出现
|
||||
|
||||
**仅对活跃 provider 开放:** 你的 CLI 命令只在你的 provider 是配置中活跃的 `memory.provider` 时才会出现。如果用户尚未配置你的 provider,你的命令不会显示在 `hermes --help` 中。
|
||||
|
||||
### 示例
|
||||
|
||||
```python
|
||||
# plugins/memory/my-provider/cli.py
|
||||
|
||||
def my_command(args):
|
||||
"""由 argparse 分发的处理函数。"""
|
||||
sub = getattr(args, "my_command", None)
|
||||
if sub == "status":
|
||||
print("Provider is active and connected.")
|
||||
elif sub == "config":
|
||||
print("Showing config...")
|
||||
else:
|
||||
print("Usage: hermes my-provider <status|config>")
|
||||
|
||||
def register_cli(subparser) -> None:
|
||||
"""构建 hermes my-provider 的 argparse 树。
|
||||
|
||||
在 argparse 初始化时由 discover_plugin_cli_commands() 调用。
|
||||
"""
|
||||
subs = subparser.add_subparsers(dest="my_command")
|
||||
subs.add_parser("status", help="Show provider status")
|
||||
subs.add_parser("config", help="Show provider config")
|
||||
subparser.set_defaults(func=my_command)
|
||||
```
|
||||
|
||||
### 参考实现
|
||||
|
||||
完整示例请参见 `plugins/memory/honcho/cli.py`,包含 13 个子命令、跨 profile 管理(`--target-profile`)以及配置读写。
|
||||
|
||||
### 含 CLI 的目录结构
|
||||
|
||||
```
|
||||
plugins/memory/my-provider/
|
||||
├── __init__.py # MemoryProvider 实现 + register()
|
||||
├── plugin.yaml # 元数据
|
||||
├── cli.py # register_cli(subparser) — CLI 命令
|
||||
└── README.md # 配置说明
|
||||
```
|
||||
|
||||
## 单 Provider 规则
|
||||
|
||||
同一时间只能有**一个**外部 memory provider 处于活跃状态。如果用户尝试注册第二个,MemoryManager 会拒绝并发出警告。这可以防止工具 schema 膨胀和后端冲突。
|
||||
@ -0,0 +1,267 @@
|
||||
---
|
||||
sidebar_position: 10
|
||||
title: "模型提供商插件"
|
||||
description: "如何为 Hermes Agent 构建模型提供商(推理后端)插件"
|
||||
---
|
||||
|
||||
# 构建模型提供商插件
|
||||
|
||||
模型提供商插件声明一个推理后端——兼容 OpenAI 的端点、Anthropic Messages 服务器、Codex 风格的 Responses API,或 Bedrock 原生接口——Hermes 可通过这些后端路由 `AIAgent` 调用。每个内置提供商(OpenRouter、Anthropic、GMI、DeepSeek、Nvidia……)都以此类插件形式提供。第三方可通过在 `$HERMES_HOME/plugins/model-providers/` 下放置一个目录来添加自己的提供商,无需对仓库做任何修改。
|
||||
|
||||
:::tip
|
||||
模型提供商插件是**提供商插件**的第三种类型。其他两种分别是 [Memory Provider 插件](/developer-guide/memory-provider-plugin)(跨会话知识)和 [Context Engine 插件](/developer-guide/context-engine-plugin)(上下文压缩策略)。三者均遵循相同的"放入目录、声明 profile、无需编辑仓库"模式。
|
||||
:::
|
||||
|
||||
## 发现机制
|
||||
|
||||
`providers/__init__.py._discover_providers()` 在任何代码首次调用 `get_provider_profile()` 或 `list_providers()` 时懒加载执行。发现顺序:
|
||||
|
||||
1. **内置插件** — `<repo>/plugins/model-providers/<name>/` — 随 Hermes 一同发布
|
||||
2. **用户插件** — `$HERMES_HOME/plugins/model-providers/<name>/` — 放入任意目录;后续会话无需重启即可生效
|
||||
3. **旧版单文件** — `<repo>/providers/<name>.py` — 为树外可编辑安装提供向后兼容
|
||||
|
||||
**同名用户插件会覆盖内置插件**,因为 `register_provider()` 采用后写者优先策略。放入 `$HERMES_HOME/plugins/model-providers/gmi/` 目录即可替换内置 GMI profile,无需修改仓库。
|
||||
|
||||
## 目录结构
|
||||
|
||||
```
|
||||
plugins/model-providers/my-provider/
|
||||
├── __init__.py # 在模块级别调用 register_provider(profile)
|
||||
├── plugin.yaml # kind: model-provider + 元数据(可选但推荐)
|
||||
└── README.md # 安装说明(可选)
|
||||
```
|
||||
|
||||
唯一必需的文件是 `__init__.py`。`plugin.yaml` 供 `hermes plugins` 用于自省,以及供通用 PluginManager 将插件路由到正确的加载器;若缺少该文件,通用加载器会回退到源码文本启发式检测。
|
||||
|
||||
## 最简示例——一个简单的 API key 提供商
|
||||
|
||||
```python
|
||||
# plugins/model-providers/acme-inference/__init__.py
|
||||
from providers import register_provider
|
||||
from providers.base import ProviderProfile
|
||||
|
||||
acme = ProviderProfile(
|
||||
name="acme-inference",
|
||||
aliases=("acme",),
|
||||
display_name="Acme Inference",
|
||||
description="Acme — OpenAI-compatible direct API",
|
||||
signup_url="https://acme.example.com/keys",
|
||||
env_vars=("ACME_API_KEY", "ACME_BASE_URL"),
|
||||
base_url="https://api.acme.example.com/v1",
|
||||
auth_type="api_key",
|
||||
default_aux_model="acme-small-fast",
|
||||
fallback_models=(
|
||||
"acme-large-v3",
|
||||
"acme-medium-v3",
|
||||
"acme-small-fast",
|
||||
),
|
||||
)
|
||||
|
||||
register_provider(acme)
|
||||
```
|
||||
|
||||
```yaml
|
||||
# plugins/model-providers/acme-inference/plugin.yaml
|
||||
name: acme-inference
|
||||
kind: model-provider
|
||||
version: 1.0.0
|
||||
description: Acme Inference — OpenAI-compatible direct API
|
||||
author: Your Name
|
||||
```
|
||||
|
||||
就这些。放入这两个文件后,以下集成**自动生效**,无需其他任何修改:
|
||||
|
||||
| 集成点 | 位置 | 获得的能力 |
|
||||
|---|---|---|
|
||||
| 凭据解析 | `hermes_cli/auth.py` | `PROVIDER_REGISTRY["acme-inference"]` 从 profile 填充 |
|
||||
| `--provider` CLI 标志 | `hermes_cli/main.py` | 接受 `acme-inference` |
|
||||
| `hermes model` 选择器 | `hermes_cli/models.py` | 出现在 `CANONICAL_PROVIDERS` 中,从 `{base_url}/models` 获取模型列表 |
|
||||
| `hermes doctor` | `hermes_cli/doctor.py` | 对 `ACME_API_KEY` 及 `{base_url}/models` 进行健康检查 |
|
||||
| `hermes setup` | `hermes_cli/config.py` | `ACME_API_KEY` 出现在 `OPTIONAL_ENV_VARS` 和设置向导中 |
|
||||
| URL 反向映射 | `agent/model_metadata.py` | 主机名 → 提供商名称,用于自动检测 |
|
||||
| 辅助模型 | `agent/auxiliary_client.py` | 使用 `default_aux_model` 进行压缩/摘要 |
|
||||
| 运行时解析 | `hermes_cli/runtime_provider.py` | 返回正确的 `base_url`、`api_key`、`api_mode` |
|
||||
| 传输层 | `agent/transports/chat_completions.py` | Profile 路径通过 `prepare_messages` / `build_extra_body` / `build_api_kwargs_extras` 生成 kwargs |
|
||||
|
||||
## ProviderProfile 字段
|
||||
|
||||
完整定义见 `providers/base.py`。最常用的字段:
|
||||
|
||||
| 字段 | 类型 | 用途 |
|
||||
|---|---|---|
|
||||
| `name` | str | 规范 ID——与 `config.yaml` 中的 `model.provider` 及 `--provider` 标志匹配 |
|
||||
| `aliases` | `tuple[str, ...]` | 由 `get_provider_profile()` 解析的别名(如 `grok` → `xai`) |
|
||||
| `api_mode` | str | `chat_completions` \| `codex_responses` \| `anthropic_messages` \| `bedrock_converse` |
|
||||
| `display_name` | str | 在 `hermes model` 选择器中显示的人类可读标签 |
|
||||
| `description` | str | 选择器副标题 |
|
||||
| `signup_url` | str | 首次运行设置时显示("在此获取 API key") |
|
||||
| `env_vars` | `tuple[str, ...]` | 按优先级排列的 API key 环境变量;最后一个 `*_BASE_URL` 条目用作用户 base URL 覆盖 |
|
||||
| `base_url` | str | 默认推理端点 |
|
||||
| `models_url` | str | 显式目录 URL(回退到 `{base_url}/models`) |
|
||||
| `auth_type` | str | `api_key` \| `oauth_device_code` \| `oauth_external` \| `copilot` \| `aws_sdk` \| `external_process` |
|
||||
| `fallback_models` | `tuple[str, ...]` | 实时目录获取失败时显示的精选列表 |
|
||||
| `default_headers` | `dict[str, str]` | 随每个请求发送(如 Copilot 的 `Editor-Version`) |
|
||||
| `fixed_temperature` | Any | `None` = 使用调用方的值;`OMIT_TEMPERATURE` 哨兵值 = 完全不发送 temperature(Kimi) |
|
||||
| `default_max_tokens` | `int \| None` | 提供商级别的 max_tokens 上限(Nvidia:16384) |
|
||||
| `default_aux_model` | str | 用于辅助任务(压缩、视觉、摘要)的廉价模型 |
|
||||
|
||||
## 可覆盖的 hook
|
||||
|
||||
对于非常规的特殊需求,可子类化 `ProviderProfile`:
|
||||
|
||||
```python
|
||||
from typing import Any
|
||||
from providers.base import ProviderProfile
|
||||
|
||||
class AcmeProfile(ProviderProfile):
|
||||
def prepare_messages(self, messages: list[dict[str, Any]]) -> list[dict[str, Any]]:
|
||||
"""提供商特定的消息预处理。在 codex 清理之后、developer-role 替换之前运行。
|
||||
默认:直接透传。"""
|
||||
# 示例:Qwen 将纯文本内容规范化为 list-of-parts 数组并注入 cache_control;
|
||||
# Kimi 重写 tool-call JSON
|
||||
return messages
|
||||
|
||||
def build_extra_body(self, *, session_id=None, **context) -> dict:
|
||||
"""提供商特定的 extra_body 字段,合并到 API 调用中。
|
||||
context 包含:session_id、provider_preferences、model、base_url、
|
||||
reasoning_config。默认:空 dict。"""
|
||||
# 示例:OpenRouter 的 provider-preferences 块,
|
||||
# Gemini 的 thinking_config 转换。
|
||||
return {}
|
||||
|
||||
def build_api_kwargs_extras(self, *, reasoning_config=None, **context):
|
||||
"""返回 (extra_body_additions, top_level_kwargs)。当某些字段需要放在顶层
|
||||
(Kimi 的 reasoning_effort)而另一些放在 extra_body(OpenRouter 的 reasoning dict)
|
||||
时需要此方法。默认:({}, {})。"""
|
||||
return {}, {}
|
||||
|
||||
def fetch_models(self, *, api_key=None, timeout=8.0) -> list[str] | None:
|
||||
"""实时目录获取。默认使用 Bearer 认证访问 {models_url or base_url}/models。
|
||||
以下情况需覆盖:自定义认证(Anthropic)、无 REST 端点(Bedrock → None),
|
||||
或公开/无认证目录(OpenRouter)。"""
|
||||
return super().fetch_models(api_key=api_key, timeout=timeout)
|
||||
```
|
||||
|
||||
## Hook 参考示例
|
||||
|
||||
参考以下内置插件了解常用写法:
|
||||
|
||||
| 插件 | 参考原因 |
|
||||
|---|---|
|
||||
| `plugins/model-providers/openrouter/` | 带 provider preferences 的聚合器,公开模型目录 |
|
||||
| `plugins/model-providers/gemini/` | `thinking_config` 转换(原生 + OpenAI 兼容嵌套形式) |
|
||||
| `plugins/model-providers/kimi-coding/` | `OMIT_TEMPERATURE`、`extra_body.thinking`、顶层 `reasoning_effort` |
|
||||
| `plugins/model-providers/qwen-oauth/` | 消息规范化、`cache_control` 注入、VL 高分辨率 |
|
||||
| `plugins/model-providers/nous/` | 归因标签、"禁用时省略 reasoning" |
|
||||
| `plugins/model-providers/custom/` | Ollama 的 `num_ctx` + `think: false` 特殊处理 |
|
||||
| `plugins/model-providers/bedrock/` | `api_mode="bedrock_converse"`,`fetch_models` 返回 None(无 REST 端点) |
|
||||
|
||||
## 用户覆盖——不修改仓库替换内置提供商
|
||||
|
||||
假设你想将 `gmi` 指向私有测试端点进行测试。创建 `~/.hermes/plugins/model-providers/gmi/__init__.py`:
|
||||
|
||||
```python
|
||||
from providers import register_provider
|
||||
from providers.base import ProviderProfile
|
||||
|
||||
register_provider(ProviderProfile(
|
||||
name="gmi",
|
||||
aliases=("gmi-cloud", "gmicloud"),
|
||||
env_vars=("GMI_API_KEY",),
|
||||
base_url="https://gmi-staging.internal.example.com/v1",
|
||||
auth_type="api_key",
|
||||
default_aux_model="google/gemini-3.1-flash-lite-preview",
|
||||
))
|
||||
```
|
||||
|
||||
下次会话时,`get_provider_profile("gmi").base_url` 将返回测试 URL。无需打补丁,无需重新构建。由于用户插件在内置插件之后被发现,用户的 `register_provider()` 调用会胜出。
|
||||
|
||||
## api_mode 选择
|
||||
|
||||
系统识别四个值。Hermes 的选择依据:
|
||||
|
||||
1. 用户显式覆盖(`config.yaml` 中设置了 `model.api_mode`)
|
||||
2. OpenCode 的按模型分发(Zen 和 Go 的 `opencode_model_api_mode`)
|
||||
3. URL 自动检测——`/anthropic` 后缀 → `anthropic_messages`,`api.openai.com` → `codex_responses`,`api.x.ai` → `codex_responses`,Kimi 域名上的 `/coding` → `chat_completions`
|
||||
4. **Profile 的 `api_mode`** 作为 URL 检测无结果时的回退
|
||||
5. 默认 `chat_completions`
|
||||
|
||||
将 `profile.api_mode` 设置为你的提供商默认使用的值——它作为提示使用。用户 URL 覆盖仍然优先。
|
||||
|
||||
## 认证类型
|
||||
|
||||
| `auth_type` | 含义 | 使用者 |
|
||||
|---|---|---|
|
||||
| `api_key` | 单个环境变量携带静态 API key | 大多数提供商 |
|
||||
| `oauth_device_code` | 设备码 OAuth 流程 | — |
|
||||
| `oauth_external` | 用户在其他地方登录,token 存入 `auth.json` | Anthropic OAuth、MiniMax OAuth、Gemini Cloud Code、Qwen Portal、Nous Portal |
|
||||
| `copilot` | GitHub Copilot token 刷新周期 | 仅 `copilot` 插件 |
|
||||
| `aws_sdk` | AWS SDK 凭据链(IAM role、profile、env) | 仅 `bedrock` 插件 |
|
||||
| `external_process` | 认证由 agent 启动的子进程处理 | 仅 `copilot-acp` 插件 |
|
||||
|
||||
`auth_type` 控制哪些代码路径将你的提供商视为"简单 api-key 提供商"——若不是 `api_key`,PluginManager 仍会记录 manifest,但 Hermes CLI 层面的自动化(doctor 检查、`--provider` 标志、设置向导委托)可能会跳过它。
|
||||
|
||||
## 发现时机
|
||||
|
||||
提供商发现是**懒加载**的——由进程中首次调用 `get_provider_profile()` 或 `list_providers()` 触发。实际上这在启动早期就会发生(`auth.py` 模块加载时会主动扩展 `PROVIDER_REGISTRY`)。若需验证插件是否已加载,运行:
|
||||
|
||||
```bash
|
||||
hermes doctor
|
||||
```
|
||||
|
||||
——成功的 `auth_type="api_key"` profile 会出现在 Provider Connectivity 部分,并附带 `/models` 探测结果。
|
||||
|
||||
编程方式检查:
|
||||
|
||||
```python
|
||||
from providers import list_providers
|
||||
for p in list_providers():
|
||||
print(p.name, p.base_url, p.api_mode)
|
||||
```
|
||||
|
||||
## 测试你的插件
|
||||
|
||||
将 `HERMES_HOME` 指向临时目录,避免污染真实配置:
|
||||
|
||||
```bash
|
||||
export HERMES_HOME=/tmp/hermes-plugin-test
|
||||
mkdir -p $HERMES_HOME/plugins/model-providers/my-provider
|
||||
cat > $HERMES_HOME/plugins/model-providers/my-provider/__init__.py <<'EOF'
|
||||
from providers import register_provider
|
||||
from providers.base import ProviderProfile
|
||||
register_provider(ProviderProfile(
|
||||
name="my-provider",
|
||||
env_vars=("MY_API_KEY",),
|
||||
base_url="https://api.my-provider.example.com/v1",
|
||||
auth_type="api_key",
|
||||
))
|
||||
EOF
|
||||
|
||||
export MY_API_KEY=your-test-key
|
||||
hermes -z "hello" --provider my-provider -m some-model
|
||||
```
|
||||
|
||||
## 通用 PluginManager 集成
|
||||
|
||||
通用 `PluginManager`(即 `hermes plugins` 操作的对象)**能看到**模型提供商插件,但不会导入它们——`providers/__init__.py` 负责管理其生命周期。Manager 记录 manifest 用于自省,并按 `kind: model-provider` 分类。当你将一个未标记的用户插件放入 `$HERMES_HOME/plugins/`,而该插件恰好调用了带 `ProviderProfile` 的 `register_provider`,Manager 会通过源码文本启发式检测自动将其归类为 `kind: model-provider`——因此即使没有 `plugin.yaml`,插件仍能正确路由。
|
||||
|
||||
## 通过 pip 分发
|
||||
|
||||
与所有 Hermes 插件一样,模型提供商可以作为 pip 包发布。在你的 `pyproject.toml` 中添加入口点:
|
||||
|
||||
```toml
|
||||
[project.entry-points."hermes.plugins"]
|
||||
acme-inference = "acme_hermes_plugin:register"
|
||||
```
|
||||
|
||||
……其中 `acme_hermes_plugin:register` 是一个调用 `register_provider(profile)` 的函数。通用 PluginManager 在 `discover_and_load()` 期间会拾取入口点插件。对于 `kind: model-provider` 的 pip 插件,你仍需在 manifest 中声明 kind(或依赖源码文本启发式检测)。
|
||||
|
||||
完整的入口点设置请参阅 [构建 Hermes 插件](/guides/build-a-hermes-plugin#distribute-via-pip)。
|
||||
|
||||
## 相关页面
|
||||
|
||||
- [Provider Runtime](/developer-guide/provider-runtime) — 解析优先级及各层读取 profile 的位置
|
||||
- [添加提供商](/developer-guide/adding-providers) — 新推理后端的端到端检查清单(涵盖快速插件路径和完整 CLI/auth 集成)
|
||||
- [Memory Provider 插件](/developer-guide/memory-provider-plugin)
|
||||
- [Context Engine 插件](/developer-guide/context-engine-plugin)
|
||||
- [构建 Hermes 插件](/guides/build-a-hermes-plugin) — 通用插件编写指南
|
||||
@ -0,0 +1,371 @@
|
||||
---
|
||||
sidebar_position: 11
|
||||
title: "Plugin LLM 访问"
|
||||
description: "通过 ctx.llm 在 plugin 内部运行任意 LLM 调用——支持对话或结构化输出、同步或异步。宿主持有认证凭据,失败关闭信任门控,可选 JSON Schema 验证。"
|
||||
---
|
||||
|
||||
# Plugin LLM 访问
|
||||
|
||||
`ctx.llm` 是 plugin 发起 LLM 调用的官方方式。
|
||||
对话补全、结构化提取、同步、异步、带或不带图像——
|
||||
同一接口,同一信任门控,同一宿主持有的凭据。
|
||||
|
||||
Plugin 在需要涉及模型但又不属于 agent 对话的场景时使用它。
|
||||
例如:将工具报错改写成非工程师也能理解的语言的 hook;
|
||||
在消息入队前进行翻译的 gateway 适配器;
|
||||
对长段粘贴内容进行摘要的斜杠命令;
|
||||
对前一天活动评分并向状态看板写一行记录的定时任务;
|
||||
以及决定某条消息是否值得唤醒 agent 的预过滤器。
|
||||
|
||||
这些任务不应让 agent 介入。它们只需要一次 LLM 调用、一个有类型的答案,然后结束。
|
||||
|
||||
## 最简调用
|
||||
|
||||
```python
|
||||
result = ctx.llm.complete(messages=[{"role": "user", "content": "ping"}])
|
||||
return result.text
|
||||
```
|
||||
|
||||
这就是整个 API 的一行示例。无需密钥、无需 provider 配置、无需 SDK 初始化。Plugin 运行在用户当前使用的任意 provider 和模型上——用户切换 provider 时,plugin 自动跟随。
|
||||
|
||||
## 更完整的对话示例
|
||||
|
||||
```python
|
||||
result = ctx.llm.complete(
|
||||
messages=[
|
||||
{"role": "system", "content": "Rewrite errors as one short sentence a non-engineer can act on."},
|
||||
{"role": "user", "content": traceback_text},
|
||||
],
|
||||
max_tokens=64,
|
||||
purpose="hooks.error-rewrite",
|
||||
)
|
||||
return result.text
|
||||
```
|
||||
|
||||
`purpose` 是一个自由格式的审计字符串——它会出现在 `agent.log` 和 `result.audit` 中,方便运营人员查看哪个 plugin 发起了哪次调用。可选,但对于频繁触发的场景建议填写。
|
||||
|
||||
## 结构化输出
|
||||
|
||||
当 plugin 需要有类型的答案时,切换到结构化模式:
|
||||
|
||||
```python
|
||||
result = ctx.llm.complete_structured(
|
||||
instructions="Score this support reply for urgency (0–1) and pick a category.",
|
||||
input=[{"type": "text", "text": message_body}],
|
||||
json_schema=TRIAGE_SCHEMA,
|
||||
purpose="support.triage",
|
||||
temperature=0.0,
|
||||
max_tokens=128,
|
||||
)
|
||||
|
||||
if result.parsed["urgency"] > 0.8:
|
||||
await dispatch_to_oncall(result.parsed["category"], message_body)
|
||||
```
|
||||
|
||||
宿主向 provider 请求 JSON 输出,在本地作为兜底进行解析,若安装了 `jsonschema` 则对你的 schema 进行验证,最终在 `result.parsed` 上返回一个 Python 对象。如果模型无法生成有效 JSON,`result.parsed` 为 `None`,`result.text` 携带原始响应。
|
||||
|
||||
## 此模式的优势
|
||||
|
||||
* **一次调用,四种形态。** `complete()` 用于对话,`complete_structured()` 用于有类型的 JSON,`acomplete()` 和 `acomplete_structured()` 用于 asyncio。参数相同,结果对象相同。
|
||||
* **宿主持有凭据。** OAuth token、刷新流程、凭据池、每任务辅助覆盖——Hermes 已有的所有凭据概念均适用。Plugin 永远看不到 token;宿主通过 `result.audit` 将调用归因回溯。
|
||||
* **有界。** 单次同步或异步调用。无流式输出,无工具循环,无需管理对话状态。给定输入,获取结果,返回。
|
||||
* **失败关闭信任。** 从未配置过的 plugin 无法自行选择 provider、模型、agent 或存储的凭据。默认行为是"使用用户正在使用的"。运营人员在 `config.yaml` 中按 plugin 逐一选择开启特定覆盖。
|
||||
|
||||
## 快速开始
|
||||
|
||||
以下是两个完整的 plugin 示例——一个对话,一个结构化。两者均在单个 `register(ctx)` 函数中实现,无需任何外部配置即可针对用户当前激活的模型运行。
|
||||
|
||||
### 对话补全——`/tldr`
|
||||
|
||||
```python
|
||||
def register(ctx):
|
||||
ctx.register_command(
|
||||
name="tldr",
|
||||
handler=lambda raw: _tldr(ctx, raw),
|
||||
description="Summarise the supplied text in one paragraph.",
|
||||
args_hint="<text>",
|
||||
)
|
||||
|
||||
|
||||
def _tldr(ctx, raw_args: str) -> str:
|
||||
text = raw_args.strip()
|
||||
if not text:
|
||||
return "Usage: /tldr <text to summarise>"
|
||||
result = ctx.llm.complete(
|
||||
messages=[
|
||||
{"role": "system",
|
||||
"content": "Summarise the user's text in one tight paragraph. No preamble."},
|
||||
{"role": "user", "content": text},
|
||||
],
|
||||
max_tokens=256,
|
||||
temperature=0.3,
|
||||
purpose="tldr",
|
||||
)
|
||||
return result.text
|
||||
```
|
||||
|
||||
`result.text` 是模型的响应;`result.usage` 携带 token 计数;`result.provider` 和 `result.model` 携带归因信息。
|
||||
|
||||
### 结构化提取——`/paste-to-tasks`
|
||||
|
||||
```python
|
||||
def register(ctx):
|
||||
ctx.register_command(
|
||||
name="paste-to-tasks",
|
||||
handler=lambda raw: _paste_to_tasks(ctx, raw),
|
||||
description="Turn freeform meeting notes into structured tasks.",
|
||||
args_hint="<text>",
|
||||
)
|
||||
|
||||
|
||||
_TASKS_SCHEMA = {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"tasks": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"owner": {"type": "string"},
|
||||
"action": {"type": "string"},
|
||||
"due": {"type": "string", "description": "ISO date or empty"},
|
||||
},
|
||||
"required": ["action"],
|
||||
},
|
||||
},
|
||||
},
|
||||
"required": ["tasks"],
|
||||
}
|
||||
|
||||
|
||||
def _paste_to_tasks(ctx, raw_args: str) -> str:
|
||||
if not raw_args.strip():
|
||||
return "Usage: /paste-to-tasks <meeting notes>"
|
||||
result = ctx.llm.complete_structured(
|
||||
instructions=(
|
||||
"Extract concrete action items from these meeting notes. "
|
||||
"One task per actionable line. If no owner is named, leave 'owner' blank."
|
||||
),
|
||||
input=[{"type": "text", "text": raw_args}],
|
||||
json_schema=_TASKS_SCHEMA,
|
||||
schema_name="meeting.tasks",
|
||||
purpose="paste-to-tasks",
|
||||
temperature=0.0,
|
||||
max_tokens=512,
|
||||
)
|
||||
if result.parsed is None:
|
||||
return f"Couldn't parse a response. Raw output:\n{result.text}"
|
||||
lines = [f"- [{t.get('owner') or '?'}] {t['action']}" for t in result.parsed["tasks"]]
|
||||
return "\n".join(lines) or "(no tasks found)"
|
||||
```
|
||||
|
||||
第三个完整示例(包含图像输入)位于
|
||||
[`hermes-example-plugins`](https://github.com/NousResearch/hermes-example-plugins/tree/main/plugin-llm-example)
|
||||
仓库(参考 plugin 的配套仓库——不随 hermes-agent 本体打包)。关于异步接口(`acomplete()` / `acomplete_structured()` 与 `asyncio.gather()` 配合使用),请参见同一仓库中的
|
||||
[`plugin-llm-async-example`](https://github.com/NousResearch/hermes-example-plugins/tree/main/plugin-llm-async-example)。
|
||||
|
||||
## 何时使用哪种方式
|
||||
|
||||
| 你需要…… | 使用 |
|
||||
|---|---|
|
||||
| 自由格式文本响应(翻译、摘要、改写、生成) | `complete()` |
|
||||
| 多轮 prompt(system + few-shot 示例 + user) | `complete()` |
|
||||
| 经 schema 验证的有类型 dict | `complete_structured()` |
|
||||
| 图像或文本输入并返回有类型 dict | `complete_structured()` |
|
||||
| 在异步代码中发起相同调用(gateway 适配器、异步 hook) | `acomplete()` / `acomplete_structured()` |
|
||||
|
||||
其他所有内容——provider 选择、模型解析、认证、回退、超时、视觉路由——在四种形态中完全一致。
|
||||
|
||||
## API 接口
|
||||
|
||||
`ctx.llm` 是 `agent.plugin_llm.PluginLlm` 的实例。
|
||||
|
||||
### `complete()`
|
||||
|
||||
```python
|
||||
result = ctx.llm.complete(
|
||||
messages=[{"role": "user", "content": "Hi"}],
|
||||
provider=None, # 可选,受门控——Hermes provider id(如 "openrouter")
|
||||
model=None, # 可选,受门控——该 provider 期望的任意字符串
|
||||
temperature=None,
|
||||
max_tokens=None,
|
||||
timeout=None, # 秒
|
||||
agent_id=None, # 可选,受门控
|
||||
profile=None, # 可选,受门控——显式指定认证 profile 名称
|
||||
purpose="optional-audit-string",
|
||||
)
|
||||
# → PluginLlmCompleteResult(text, provider, model, agent_id, usage, audit)
|
||||
```
|
||||
|
||||
普通对话补全。`messages` 采用标准 OpenAI 格式——`{"role": "...", "content": "..."}` 字典列表。多轮 prompt(system + few-shot user/assistant 对 + 最终 user)的用法与 OpenAI SDK 完全一致。
|
||||
|
||||
`provider=` 和 `model=` 相互独立,格式与宿主主配置(`model.provider` + `model.model`)相同。仅设置 `model=` 可在用户当前激活的 provider 上使用不同模型。同时设置两者则完全切换 provider。任一参数在未获运营人员授权时均会抛出 `PluginLlmTrustError`。
|
||||
|
||||
### `complete_structured()`
|
||||
|
||||
```python
|
||||
result = ctx.llm.complete_structured(
|
||||
instructions="What you want extracted.",
|
||||
input=[
|
||||
{"type": "text", "text": "..."},
|
||||
{"type": "image", "data": b"...", "mime_type": "image/png"},
|
||||
{"type": "image", "url": "https://..."},
|
||||
],
|
||||
json_schema={...}, # 可选——触发解析结果及验证
|
||||
json_mode=False, # 设为 True 可在不提供 schema 的情况下请求 JSON
|
||||
schema_name=None, # 可选的人类可读 schema 名称
|
||||
system_prompt=None,
|
||||
provider=None, # 可选,受门控
|
||||
model=None, # 可选,受门控
|
||||
temperature=None,
|
||||
max_tokens=None,
|
||||
timeout=None,
|
||||
agent_id=None,
|
||||
profile=None,
|
||||
purpose=None,
|
||||
)
|
||||
# → PluginLlmStructuredResult(text, provider, model, agent_id,
|
||||
# usage, parsed, content_type, audit)
|
||||
```
|
||||
|
||||
输入为有类型的文本或图像块(原始字节会自动 base64 编码为 `data:` URL)。当提供 `json_schema` 或设置 `json_mode=True` 时,宿主通过 `response_format` 向 provider 请求 JSON 输出,在本地作为兜底进行解析,若安装了 `jsonschema` 则对你的 schema 进行验证。
|
||||
|
||||
* `result.content_type == "json"` — `result.parsed` 是符合你 schema 的 Python 对象。
|
||||
* `result.content_type == "text"` — 解析或验证失败;检查 `result.text` 获取原始模型响应。
|
||||
|
||||
### 异步
|
||||
|
||||
```python
|
||||
result = await ctx.llm.acomplete(messages=...)
|
||||
result = await ctx.llm.acomplete_structured(instructions=..., input=...)
|
||||
```
|
||||
|
||||
参数和结果类型与对应的同步版本相同。在 gateway 适配器、异步 hook 或任何已运行在 asyncio 事件循环上的 plugin 代码中使用。
|
||||
|
||||
### 结果属性
|
||||
|
||||
```python
|
||||
@dataclass
|
||||
class PluginLlmCompleteResult:
|
||||
text: str # 助手的响应
|
||||
provider: str # 如 "openrouter"、"anthropic"
|
||||
model: str # provider 为本次调用返回的模型标识
|
||||
agent_id: str # 使用了哪个 agent 的模型/认证
|
||||
usage: PluginLlmUsage # token 数 + 缓存 + 费用估算
|
||||
audit: Dict[str, Any] # plugin_id、purpose、profile
|
||||
|
||||
@dataclass
|
||||
class PluginLlmStructuredResult(PluginLlmCompleteResult):
|
||||
parsed: Optional[Any] # content_type == "json" 时的 JSON 对象
|
||||
content_type: str # "json" 或 "text"
|
||||
# 提供 schema_name 时 audit 中也会携带该字段
|
||||
```
|
||||
|
||||
当 provider 返回相应字段时,`usage` 携带 `input_tokens`、`output_tokens`、`total_tokens`、`cache_read_tokens`、`cache_write_tokens` 和 `cost_usd`。
|
||||
|
||||
## 信任门控
|
||||
|
||||
默认行为是失败关闭。在没有 `plugins.entries` 配置块的情况下,plugin 可以:
|
||||
|
||||
* 针对用户当前激活的 provider 和模型运行四种方法中的任意一种,
|
||||
* 设置请求塑形参数(`temperature`、`max_tokens`、`timeout`、`system_prompt`、`purpose`、`messages`、`instructions`、`input`、`json_schema`),
|
||||
|
||||
……仅此而已。`provider=`、`model=`、`agent_id=` 和 `profile=` 参数在运营人员授权前均会抛出 `PluginLlmTrustError`。
|
||||
|
||||
**大多数 plugin 永远不需要此部分。** 仅调用 `ctx.llm.complete(messages=...)` 且不带任何覆盖的 plugin,会针对用户当前激活的内容运行,零配置即可工作。以下配置块仅在 plugin 明确需要固定到与用户不同的模型或 provider 时才有意义。
|
||||
|
||||
```yaml
|
||||
plugins:
|
||||
entries:
|
||||
my-plugin:
|
||||
llm:
|
||||
# 允许此 plugin 选择不同的 Hermes provider
|
||||
# (必须是 Hermes 已知的 provider——与
|
||||
# `hermes model` 和 config.yaml model.provider 中的名称相同)
|
||||
allow_provider_override: true
|
||||
|
||||
# 可选:限制允许的 provider。使用 ["*"] 表示任意。
|
||||
allowed_providers:
|
||||
- openrouter
|
||||
- anthropic
|
||||
|
||||
# 允许此 plugin 请求特定模型。
|
||||
allow_model_override: true
|
||||
|
||||
# 可选:限制允许的模型。使用 ["*"] 表示任意。
|
||||
# 模型与 plugin 发送的字符串进行字面匹配——
|
||||
# Hermes 不做任何查找。
|
||||
allowed_models:
|
||||
- openai/gpt-4o-mini
|
||||
- anthropic/claude-3-5-haiku
|
||||
|
||||
# 允许跨 agent 调用(罕见)。
|
||||
allow_agent_id_override: false
|
||||
|
||||
# 允许 plugin 请求特定的存储认证 profile
|
||||
# (如同一 provider 上的不同 OAuth 账户)。
|
||||
allow_profile_override: false
|
||||
```
|
||||
|
||||
Plugin id 对于扁平 plugin 是 manifest 中的 `name:` 字段,对于嵌套 plugin 是路径派生的键(`image_gen/openai`、`memory/honcho` 等)。
|
||||
|
||||
### 门控执行内容
|
||||
|
||||
| 覆盖项 | 默认 | 配置键 |
|
||||
| --------------- | ----- | -------------------------------- |
|
||||
| `provider=` | 拒绝 | `allow_provider_override: true` |
|
||||
| ↳ 允许列表 | — | `allowed_providers: [...]` |
|
||||
| `model=` | 拒绝 | `allow_model_override: true` |
|
||||
| ↳ 允许列表 | — | `allowed_models: [...]` |
|
||||
| `agent_id=` | 拒绝 | `allow_agent_id_override: true` |
|
||||
| `profile=` | 拒绝 | `allow_profile_override: true` |
|
||||
|
||||
每项覆盖独立门控。授予 `allow_model_override` **不会**同时授予 `allow_provider_override`——被信任可选择模型的 plugin,在未获得 provider 门控授权前仍固定在用户当前激活的 provider 上。
|
||||
|
||||
### 门控无需执行的内容
|
||||
|
||||
* 请求塑形参数——`temperature`、`max_tokens`、`timeout`、`system_prompt`、`purpose`、`messages`、`instructions`、`input`、`json_schema`、`schema_name`、`json_mode`——始终允许;它们不涉及凭据或路由选择。
|
||||
* 默认拒绝策略意味着未配置的 plugin 仍可完成有用的工作——只是针对当前激活的 provider 和模型运行。运营人员只需在 plugin 明确需要更精细路由时才考虑 `plugins.entries`。
|
||||
|
||||
## 宿主负责的内容
|
||||
|
||||
以下是 `ctx.llm` 为 plugin 代劳的完整列表,你无需自行处理:
|
||||
|
||||
* **Provider 解析。** 从用户配置中读取 `model.provider` + `model.model`(或在受信任时读取显式覆盖值)。
|
||||
* **认证。** 从 `~/.hermes/auth.json` / 环境变量中提取 API 密钥、OAuth token 或刷新 token,包括配置了凭据池时的处理。Plugin 永远看不到这些内容。
|
||||
* **视觉路由。** 当提供图像输入而用户当前激活的文本模型仅支持文本时,宿主自动回退到已配置的视觉模型。
|
||||
* **回退链。** 若用户主 provider 返回 5xx 或 429,请求在向 plugin 返回错误前会经过 Hermes 常规的聚合器感知回退流程。
|
||||
* **超时。** 遵循你的 `timeout=` 参数,回退到 `auxiliary.<task>.timeout` 配置或全局辅助默认值。
|
||||
* **JSON 塑形。** 在你请求 JSON 时向 provider 发送 `response_format`,若 provider 返回了代码围栏格式的响应则在本地重新解析。
|
||||
* **Schema 验证。** 安装了 `jsonschema` 时对你的 `json_schema` 进行验证;否则记录一行 debug 日志并跳过严格验证。
|
||||
* **审计日志。** 每次调用向 `agent.log` 写入一条 INFO 日志,包含 plugin id、provider/模型、purpose 和 token 总量。
|
||||
|
||||
## Plugin 负责的内容
|
||||
|
||||
* **请求结构。** 对话用 `messages`,结构化用 `instructions` + `input`。Plugin 构建 prompt(提示词);宿主执行它。
|
||||
* **Schema。** 你期望返回的任意结构。宿主不会为你推断。
|
||||
* **错误处理。** `complete_structured()` 在输入为空或 schema 验证失败时抛出 `ValueError`。信任门控拒绝覆盖时抛出 `PluginLlmTrustError`。其他情况(provider 5xx、未配置凭据、超时)抛出 `auxiliary_client.call_llm()` 本身抛出的异常。
|
||||
* **费用。** 每次调用都针对用户的付费 provider 运行。不要在不考虑 token 消耗的情况下对每条 gateway 消息循环调用 `complete()`。
|
||||
|
||||
## 在 plugin 接口中的定位
|
||||
|
||||
现有 `ctx.*` 方法各自扩展一个已有的 Hermes 子系统:
|
||||
|
||||
| `ctx.register_tool` | 添加 agent 可调用的工具 |
|
||||
| `ctx.register_platform` | 接入新的 gateway 适配器 |
|
||||
| `ctx.register_image_gen_provider` | 替换图像生成后端 |
|
||||
| `ctx.register_memory_provider` | 替换记忆后端 |
|
||||
| `ctx.register_context_engine` | 替换上下文压缩器 |
|
||||
| `ctx.register_hook` | 监听生命周期事件 |
|
||||
|
||||
`ctx.llm` 是第一个允许 plugin 在*带外*运行用户正在对话的同一模型的接口,无需上述任何注册。这是它唯一的职责。如果你的 plugin 需要注册一个由 agent 调用的工具,使用 `register_tool`。如果需要响应生命周期事件,使用 `register_hook`。如果需要发起自己的模型调用——无论出于何种原因,结构化与否——使用 `ctx.llm`。
|
||||
|
||||
## 参考资料
|
||||
|
||||
* 实现:[`agent/plugin_llm.py`](https://github.com/NousResearch/hermes-agent/blob/main/agent/plugin_llm.py)
|
||||
* 测试:[`tests/agent/test_plugin_llm.py`](https://github.com/NousResearch/hermes-agent/blob/main/tests/agent/test_plugin_llm.py)
|
||||
* 参考 plugin(配套仓库):
|
||||
* [`plugin-llm-example`](https://github.com/NousResearch/hermes-example-plugins/tree/main/plugin-llm-example) — 带图像输入的同步结构化提取
|
||||
* [`plugin-llm-async-example`](https://github.com/NousResearch/hermes-example-plugins/tree/main/plugin-llm-async-example) — 使用 `asyncio.gather()` 的异步示例
|
||||
* 辅助客户端(底层引擎):参见
|
||||
[Provider 运行时](/developer-guide/provider-runtime)。
|
||||
@ -0,0 +1,126 @@
|
||||
---
|
||||
sidebar_position: 8
|
||||
title: "程序化集成"
|
||||
description: "从外部程序驱动 hermes-agent 的三种协议:ACP、TUI gateway JSON-RPC 以及兼容 OpenAI 的 HTTP API"
|
||||
---
|
||||
|
||||
# 程序化集成
|
||||
|
||||
Hermes 提供三种协议,供外部程序驱动 agent——IDE 插件、自定义 UI、CI 流水线、嵌入式子 agent。根据你的传输方式和消费端选择合适的协议。
|
||||
|
||||
| 协议 | 传输方式 | 适用场景 | 定义位置 |
|
||||
|----------|-----------|----------|------------|
|
||||
| **ACP** | JSON-RPC over stdio | 已支持 [Agent Client Protocol](https://github.com/zed-industries/agent-client-protocol) 的 IDE 客户端(VS Code、Zed、JetBrains) | `acp_adapter/` |
|
||||
| **TUI gateway** | JSON-RPC over stdio(或 WebSocket) | 需要精细控制会话、slash 命令、审批及流式事件的自定义宿主 | `tui_gateway/server.py` |
|
||||
| **API server** | HTTP + Server-Sent Events | 兼容 OpenAI 的前端(Open WebUI、LobeChat、LibreChat……)及语言无关的 Web 客户端 | `gateway/platforms/api_server.py` |
|
||||
|
||||
三种协议均驱动同一个 `AIAgent` 核心,区别仅在于线路格式和所暴露的功能集。
|
||||
|
||||
---
|
||||
|
||||
## ACP(Agent Client Protocol)
|
||||
|
||||
`hermes acp` 启动一个基于 stdio 的 JSON-RPC 服务器,使用 ACP 协议。已在 VS Code(Zed Industries 的 ACP 扩展)、Zed 以及所有安装了 ACP 插件的 JetBrains IDE 中投入生产使用。
|
||||
|
||||
暴露的能力:会话创建、prompt(提示词)提交、流式 agent 消息块、工具调用事件、权限请求、会话 fork、取消及身份验证。工具输出会被渲染为 IDE 可理解的 ACP `Diff`/`ToolCall` 内容块。
|
||||
|
||||
完整生命周期、事件桥接及审批流程:[ACP 内部机制](./acp-internals)。
|
||||
|
||||
```bash
|
||||
hermes acp # 在 stdio 上提供 ACP 服务
|
||||
hermes acp --bootstrap # 打印适用于支持 ACP 的 IDE 的安装代码片段
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## TUI Gateway JSON-RPC
|
||||
|
||||
`tui_gateway/server.py` 是 Ink TUI(`hermes --tui`)和嵌入式仪表板 PTY 桥接所使用的协议。任何外部宿主均可通过 stdio(或经由 `tui_gateway/ws.py` 的 WebSocket)使用相同协议。
|
||||
|
||||
### 方法目录(精选)
|
||||
|
||||
```
|
||||
prompt.submit prompt.background session.steer
|
||||
session.create session.list session.interrupt
|
||||
session.history session.compress session.branch
|
||||
session.title session.usage session.status
|
||||
clarify.respond sudo.respond secret.respond
|
||||
approval.respond config.set / config.get commands.catalog
|
||||
command.resolve command.dispatch cli.exec
|
||||
reload.mcp reload.env process.stop
|
||||
delegation.status subagent.interrupt spawn_tree.save / list / load
|
||||
terminal.resize clipboard.paste image.attach
|
||||
```
|
||||
|
||||
### 流式返回的事件
|
||||
|
||||
`message.delta`、`message.complete`、`tool.start`、`tool.progress`、`tool.complete`、`approval.request`、`clarify.request`、`sudo.request`、`secret.request`、`gateway.ready`,以及会话生命周期和错误事件。
|
||||
|
||||
### Pi 风格 RPC 映射
|
||||
|
||||
Pi-mono RPC 规范([issue #360](https://github.com/NousResearch/hermes-agent/issues/360))中的每条命令均有对应的 TUI gateway 等价项:
|
||||
|
||||
| Pi 命令 | Hermes 等价项 |
|
||||
|------------|-------------------|
|
||||
| `prompt` | `prompt.submit`(或 ACP `session/prompt`) |
|
||||
| `steer` | `session.steer` |
|
||||
| `follow_up` | 在当前轮次结束后排队的 `prompt.submit` |
|
||||
| `abort` | `session.interrupt` |
|
||||
| `set_model` | 通过 `command.dispatch` 执行 `/model <provider:model>`(会话中途生效,持久化) |
|
||||
| `compact` | `session.compress` |
|
||||
| `get_state` | `session.status` |
|
||||
| `get_messages` | `session.history` |
|
||||
| `switch_session` | `session.resume` |
|
||||
| `fork` | `session.branch` |
|
||||
| `ui_request` / `ui_response` | `clarify.respond` / `sudo.respond` / `secret.respond` / `approval.respond` |
|
||||
|
||||
---
|
||||
|
||||
## 兼容 OpenAI 的 API Server
|
||||
|
||||
`gateway/platforms/api_server.py` 通过 HTTP 暴露 Hermes,供任何已支持 OpenAI 格式的客户端使用。适用于需要 Web 前端、curl 驱动的 CI 运行器或非 Python 消费端的场景。
|
||||
|
||||
端点:
|
||||
|
||||
```
|
||||
POST /v1/chat/completions OpenAI Chat Completions(通过 SSE 流式传输)
|
||||
POST /v1/responses OpenAI Responses API(有状态)
|
||||
POST /v1/runs 启动一次运行,返回 run_id(202)
|
||||
GET /v1/runs/{id} 运行状态
|
||||
GET /v1/runs/{id}/events 生命周期事件的 SSE 流
|
||||
POST /v1/runs/{id}/approval 解决待处理的审批
|
||||
POST /v1/runs/{id}/stop 中断运行
|
||||
GET /v1/capabilities 机器可读的功能标志
|
||||
GET /v1/models 列出 hermes-agent
|
||||
GET /health, /health/detailed
|
||||
```
|
||||
|
||||
配置、请求头(`X-Hermes-Session-Id`、`X-Hermes-Session-Key`)及前端接入:[API Server](../user-guide/features/api-server)。
|
||||
|
||||
---
|
||||
|
||||
## 该选哪个?
|
||||
|
||||
- **正在编写 IDE 插件,且 IDE 已支持 ACP** → 选 ACP。IDE 侧无需任何协议工作。
|
||||
- **正在编写自定义桌面 / Web / TUI 宿主,且需要 Hermes 的全部功能**(slash 命令、审批、clarify、多 agent、会话分支)→ 选 TUI gateway JSON-RPC。
|
||||
- **需要任意兼容 OpenAI 的前端、语言无关的 HTTP 客户端或 curl 驱动的自动化** → 选 API server。
|
||||
- **需要在 Python 进程内嵌入,不想启动子进程** → 直接导入 `run_agent.AIAgent`。参见 [Agent Loop](./agent-loop)。
|
||||
|
||||
---
|
||||
|
||||
## 模型热切换
|
||||
|
||||
会话中途切换模型在所有接入方式上均可用——底层均为 `/model` slash 命令。
|
||||
|
||||
- **CLI / TUI:** `/model claude-sonnet-4` 或 `/model openrouter:anthropic/claude-sonnet-4.6`
|
||||
- **TUI gateway RPC:** 使用 `{"command": "/model claude-sonnet-4"}` 调用 `command.dispatch`
|
||||
- **ACP:** IDE 将 slash 命令作为 prompt 发送,agent 负责分发
|
||||
- **API server:** 在请求体中包含 `model` 字段,或设置 `X-Hermes-Model`
|
||||
|
||||
内置 provider 感知解析(相同的模型名称会根据当前 provider 自动选择正确格式)。参见 `hermes_cli/model_switch.py`。
|
||||
|
||||
---
|
||||
|
||||
## 关于 `--mode rpc` 的说明
|
||||
|
||||
Hermes 没有 `--mode rpc` 标志。上述三种协议已覆盖所有使用场景——ACP 用于 IDE 协议客户端,TUI gateway 用于 stdio JSON-RPC 宿主,API server 用于 HTTP。如果你发现上述协议均无法满足的真实需求,请提交 issue 并说明你正在构建的具体消费端。
|
||||
@ -0,0 +1,270 @@
|
||||
---
|
||||
sidebar_position: 5
|
||||
title: "Prompt 组装"
|
||||
description: "Hermes 如何构建系统 prompt、保持缓存稳定性并注入临时层"
|
||||
---
|
||||
|
||||
# Prompt 组装
|
||||
|
||||
Hermes 刻意将以下内容分离:
|
||||
|
||||
- **已缓存的系统 prompt 状态**
|
||||
- **API 调用时临时添加的内容**
|
||||
|
||||
这是项目中最重要的设计决策之一,因为它影响:
|
||||
|
||||
- token 用量
|
||||
- prompt 缓存效果
|
||||
- 会话连续性
|
||||
- 记忆正确性
|
||||
|
||||
主要文件:
|
||||
|
||||
- `run_agent.py`
|
||||
- `agent/prompt_builder.py`
|
||||
- `tools/memory_tool.py`
|
||||
|
||||
## 已缓存的系统 prompt 层
|
||||
|
||||
已缓存的系统 prompt 大致按以下顺序组装:
|
||||
|
||||
1. agent 身份 — 优先使用 `HERMES_HOME` 中的 `SOUL.md`,否则回退到 `prompt_builder.py` 中的 `DEFAULT_AGENT_IDENTITY`
|
||||
2. 工具感知行为指导
|
||||
3. Honcho 静态块(激活时)
|
||||
4. 可选系统消息
|
||||
5. 冻结的 MEMORY 快照
|
||||
6. 冻结的 USER 配置文件快照
|
||||
7. skills 索引
|
||||
8. 上下文文件(`AGENTS.md`、`.cursorrules`、`.cursor/rules/*.mdc`)— 若 SOUL.md 已在第 1 步作为身份加载,则此处**不**再包含它
|
||||
9. 时间戳 / 可选会话 ID
|
||||
10. 平台提示
|
||||
|
||||
当设置了 `skip_context_files`(例如子 agent 委托)时,不会加载 SOUL.md,而是使用硬编码的 `DEFAULT_AGENT_IDENTITY`。
|
||||
|
||||
### 具体示例:组装后的系统 prompt
|
||||
|
||||
以下是所有层都存在时最终系统 prompt 的简化视图(注释说明每个部分的来源):
|
||||
|
||||
```
|
||||
# Layer 1: Agent Identity (from ~/.hermes/SOUL.md)
|
||||
You are Hermes, an AI assistant created by Nous Research.
|
||||
You are an expert software engineer and researcher.
|
||||
You value correctness, clarity, and efficiency.
|
||||
...
|
||||
|
||||
# Layer 2: Tool-aware behavior guidance
|
||||
You have persistent memory across sessions. Save durable facts using
|
||||
the memory tool: user preferences, environment details, tool quirks,
|
||||
and stable conventions. Memory is injected into every turn, so keep
|
||||
it compact and focused on facts that will still matter later.
|
||||
...
|
||||
When the user references something from a past conversation or you
|
||||
suspect relevant cross-session context exists, use session_search
|
||||
to recall it before asking them to repeat themselves.
|
||||
|
||||
# Tool-use enforcement (for GPT/Codex models only)
|
||||
You MUST use your tools to take action — do not describe what you
|
||||
would do or plan to do without actually doing it.
|
||||
...
|
||||
|
||||
# Layer 3: Honcho static block (when active)
|
||||
[Honcho personality/context data]
|
||||
|
||||
# Layer 4: Optional system message (from config or API)
|
||||
[User-configured system message override]
|
||||
|
||||
# Layer 5: Frozen MEMORY snapshot
|
||||
## Persistent Memory
|
||||
- User prefers Python 3.12, uses pyproject.toml
|
||||
- Default editor is nvim
|
||||
- Working on project "atlas" in ~/code/atlas
|
||||
- Timezone: US/Pacific
|
||||
|
||||
# Layer 6: Frozen USER profile snapshot
|
||||
## User Profile
|
||||
- Name: Alice
|
||||
- GitHub: alice-dev
|
||||
|
||||
# Layer 7: Skills index
|
||||
## Skills (mandatory)
|
||||
Before replying, scan the skills below. If one clearly matches
|
||||
your task, load it with skill_view(name) and follow its instructions.
|
||||
...
|
||||
<available_skills>
|
||||
software-development:
|
||||
- code-review: Structured code review workflow
|
||||
- test-driven-development: TDD methodology
|
||||
research:
|
||||
- arxiv: Search and summarize arXiv papers
|
||||
</available_skills>
|
||||
|
||||
# Layer 8: Context files (from project directory)
|
||||
# Project Context
|
||||
The following project context files have been loaded and should be followed:
|
||||
|
||||
## AGENTS.md
|
||||
This is the atlas project. Use pytest for testing. The main
|
||||
entry point is src/atlas/main.py. Always run `make lint` before
|
||||
committing.
|
||||
|
||||
# Layer 9: Timestamp + session
|
||||
Current time: 2026-03-30T14:30:00-07:00
|
||||
Session: abc123
|
||||
|
||||
# Layer 10: Platform hint
|
||||
You are a CLI AI Agent. Try not to use markdown but simple text
|
||||
renderable inside a terminal.
|
||||
```
|
||||
|
||||
## SOUL.md 在 prompt 中的位置
|
||||
|
||||
`SOUL.md` 位于 `~/.hermes/SOUL.md`,作为 agent 的身份标识——系统 prompt 的第一个部分。`prompt_builder.py` 中的加载逻辑如下:
|
||||
|
||||
```python
|
||||
# From agent/prompt_builder.py (simplified)
|
||||
def load_soul_md() -> Optional[str]:
|
||||
soul_path = get_hermes_home() / "SOUL.md"
|
||||
if not soul_path.exists():
|
||||
return None
|
||||
content = soul_path.read_text(encoding="utf-8").strip()
|
||||
content = _scan_context_content(content, "SOUL.md") # Security scan
|
||||
content = _truncate_content(content, "SOUL.md") # Cap at 20k chars
|
||||
return content
|
||||
```
|
||||
|
||||
当 `load_soul_md()` 返回内容时,它会替换硬编码的 `DEFAULT_AGENT_IDENTITY`。随后调用 `build_context_files_prompt()` 时传入 `skip_soul=True`,以防止 SOUL.md 出现两次(一次作为身份,一次作为上下文文件)。
|
||||
|
||||
若 `SOUL.md` 不存在,系统将回退到:
|
||||
|
||||
```
|
||||
You are Hermes Agent, an intelligent AI assistant created by Nous Research.
|
||||
You are helpful, knowledgeable, and direct. You assist users with a wide
|
||||
range of tasks including answering questions, writing and editing code,
|
||||
analyzing information, creative work, and executing actions via your tools.
|
||||
You communicate clearly, admit uncertainty when appropriate, and prioritize
|
||||
being genuinely useful over being verbose unless otherwise directed below.
|
||||
Be targeted and efficient in your exploration and investigations.
|
||||
```
|
||||
|
||||
## 上下文文件的注入方式
|
||||
|
||||
`build_context_files_prompt()` 使用**优先级系统**——只加载一种项目上下文类型(先匹配先赢):
|
||||
|
||||
```python
|
||||
# From agent/prompt_builder.py (simplified)
|
||||
def build_context_files_prompt(cwd=None, skip_soul=False):
|
||||
cwd_path = Path(cwd).resolve()
|
||||
|
||||
# Priority: first match wins — only ONE project context loaded
|
||||
project_context = (
|
||||
_load_hermes_md(cwd_path) # 1. .hermes.md / HERMES.md (walks to git root)
|
||||
or _load_agents_md(cwd_path) # 2. AGENTS.md (cwd only)
|
||||
or _load_claude_md(cwd_path) # 3. CLAUDE.md (cwd only)
|
||||
or _load_cursorrules(cwd_path) # 4. .cursorrules / .cursor/rules/*.mdc
|
||||
)
|
||||
|
||||
sections = []
|
||||
if project_context:
|
||||
sections.append(project_context)
|
||||
|
||||
# SOUL.md from HERMES_HOME (independent of project context)
|
||||
if not skip_soul:
|
||||
soul_content = load_soul_md()
|
||||
if soul_content:
|
||||
sections.append(soul_content)
|
||||
|
||||
if not sections:
|
||||
return ""
|
||||
|
||||
return (
|
||||
"# Project Context\n\n"
|
||||
"The following project context files have been loaded "
|
||||
"and should be followed:\n\n"
|
||||
+ "\n".join(sections)
|
||||
)
|
||||
```
|
||||
|
||||
### 上下文文件发现详情
|
||||
|
||||
| 优先级 | 文件 | 搜索范围 | 说明 |
|
||||
|--------|------|----------|------|
|
||||
| 1 | `.hermes.md`、`HERMES.md` | 从 CWD 向上至 git 根目录 | Hermes 原生项目配置 |
|
||||
| 2 | `AGENTS.md` | 仅 CWD | 常见 agent 指令文件 |
|
||||
| 3 | `CLAUDE.md` | 仅 CWD | Claude Code 兼容性 |
|
||||
| 4 | `.cursorrules`、`.cursor/rules/*.mdc` | 仅 CWD | Cursor 兼容性 |
|
||||
|
||||
所有上下文文件均会:
|
||||
- **安全扫描** — 检查 prompt 注入模式(不可见 unicode、"ignore previous instructions"、凭据窃取尝试)
|
||||
- **截断处理** — 使用 70/20 头尾比例上限为 20,000 字符,并附截断标记
|
||||
- **剥离 YAML frontmatter** — `.hermes.md` 的 frontmatter 会被移除(保留供未来配置覆盖使用)
|
||||
|
||||
## 仅在 API 调用时生效的层
|
||||
|
||||
以下内容刻意*不*作为已缓存系统 prompt 的一部分持久化:
|
||||
|
||||
- `ephemeral_system_prompt`
|
||||
- prefill 消息
|
||||
- gateway 派生的会话上下文覆盖层
|
||||
- 注入当前轮次用户消息的后续轮次 Honcho 召回内容
|
||||
|
||||
这种分离使稳定前缀保持稳定,从而有效缓存。
|
||||
|
||||
## 记忆快照
|
||||
|
||||
本地记忆和用户配置文件数据在会话开始时作为冻结快照注入。会话中途的写入操作会更新磁盘状态,但不会修改已构建的系统 prompt,直到新会话开始或强制重建时才生效。
|
||||
|
||||
## 上下文文件
|
||||
|
||||
`agent/prompt_builder.py` 使用**优先级系统**扫描并清理项目上下文文件——只加载一种类型(先匹配先赢):
|
||||
|
||||
1. `.hermes.md` / `HERMES.md`(向上遍历至 git 根目录)
|
||||
2. `AGENTS.md`(启动时的 CWD;子目录在会话期间通过 `agent/subdirectory_hints.py` 逐步发现)
|
||||
3. `CLAUDE.md`(仅 CWD)
|
||||
4. `.cursorrules` / `.cursor/rules/*.mdc`(仅 CWD)
|
||||
|
||||
`SOUL.md` 通过 `load_soul_md()` 单独加载用于身份槽位。加载成功后,`build_context_files_prompt(skip_soul=True)` 会防止其出现两次。
|
||||
|
||||
长文件在注入前会被截断。
|
||||
|
||||
## Skills 索引
|
||||
|
||||
当 skills 工具可用时,skills 系统会向 prompt 贡献一个紧凑的 skills 索引。
|
||||
|
||||
## 支持的 prompt 自定义入口
|
||||
|
||||
大多数用户应将 `agent/prompt_builder.py` 视为实现代码,而非配置入口。推荐的自定义路径是修改 Hermes 已加载的 prompt 输入,而非直接编辑 Python 模板。
|
||||
|
||||
### 优先使用这些入口
|
||||
|
||||
- `~/.hermes/SOUL.md` — 用自定义 agent 角色和固定行为替换内置默认身份块。
|
||||
- `~/.hermes/MEMORY.md` 和 `~/.hermes/USER.md` — 提供应在新会话中快照的持久跨会话事实和用户配置文件数据。
|
||||
- 项目上下文文件,如 `.hermes.md`、`HERMES.md`、`AGENTS.md`、`CLAUDE.md` 或 `.cursorrules` — 注入仓库特定的工作规则。
|
||||
- Skills — 打包可复用的工作流和参考资料,无需编辑核心 prompt 代码。
|
||||
- 可选系统 prompt 配置 / API 覆盖 — 添加部署特定的指令文本,无需 fork Hermes。
|
||||
- 临时覆盖层,如 `HERMES_EPHEMERAL_SYSTEM_PROMPT` 或 prefill 消息 — 添加不应成为已缓存 prompt 前缀一部分的轮次级指导。
|
||||
|
||||
### 何时应编辑代码
|
||||
|
||||
仅当你刻意维护一个 fork 或向上游贡献行为变更时,才编辑 `agent/prompt_builder.py`。该文件为每个会话组装 prompt 管道、缓存边界和注入顺序。直接编辑该文件是全局产品变更,而非针对单个用户的 prompt 自定义。
|
||||
|
||||
换言之:
|
||||
|
||||
- 若想要不同的助手身份,编辑 `SOUL.md`
|
||||
- 若想要不同的仓库规则,编辑项目上下文文件
|
||||
- 若想要可复用的操作流程,添加或修改 skills
|
||||
- 若想改变 Hermes 为所有人组装 prompt 的方式,修改 Python 代码并将其视为代码贡献
|
||||
|
||||
## Prompt 组装为何如此拆分
|
||||
|
||||
该架构刻意优化以:
|
||||
|
||||
- 保留提供商侧的 prompt 缓存
|
||||
- 避免不必要地修改历史记录
|
||||
- 保持记忆语义清晰可理解
|
||||
- 允许 gateway/ACP/CLI 添加上下文而不污染持久 prompt 状态
|
||||
|
||||
## 相关文档
|
||||
|
||||
- [上下文压缩与 Prompt 缓存](./context-compression-and-caching.md)
|
||||
- [会话存储](./session-storage.md)
|
||||
- [Gateway 内部机制](./gateway-internals.md)
|
||||
@ -0,0 +1,208 @@
|
||||
---
|
||||
sidebar_position: 4
|
||||
title: "Provider 运行时解析"
|
||||
description: "Hermes 如何在运行时解析 provider、凭据、API 模式及辅助模型"
|
||||
---
|
||||
|
||||
# Provider 运行时解析
|
||||
|
||||
Hermes 拥有一个共享的 provider 运行时解析器,用于以下场景:
|
||||
|
||||
- CLI
|
||||
- gateway
|
||||
- cron 任务
|
||||
- ACP
|
||||
- 辅助模型调用
|
||||
|
||||
主要实现:
|
||||
|
||||
- `hermes_cli/runtime_provider.py` — 凭据解析,`_resolve_custom_runtime()`
|
||||
- `hermes_cli/auth.py` — provider 注册表,`resolve_provider()`
|
||||
- `hermes_cli/model_switch.py` — 共享 `/model` 切换流水线(CLI + gateway)
|
||||
- `agent/auxiliary_client.py` — 辅助模型路由
|
||||
- `providers/` — ABC + 注册表入口点(`ProviderProfile`、`register_provider`、`get_provider_profile`、`list_providers`)
|
||||
- `plugins/model-providers/<name>/` — 每个 provider 的插件(内置),声明 `api_mode`、`base_url`、`env_vars`、`fallback_models` 并在首次访问时将自身注册到注册表。用户插件位于 `$HERMES_HOME/plugins/model-providers/<name>/`,会覆盖同名的内置插件。
|
||||
|
||||
`providers/` 中的 `get_provider_profile()` 为给定 provider id 返回一个 `ProviderProfile`。`runtime_provider.py` 在解析时调用它,以获取规范的 `base_url`、`env_vars` 优先级列表、`api_mode` 和 `fallback_models`,无需在多个文件中重复这些数据。在 `plugins/model-providers/<your-provider>/`(或 `$HERMES_HOME/plugins/model-providers/<your-provider>/`)下添加一个调用 `register_provider()` 的新插件,即可让 `runtime_provider.py` 自动识别它——无需在解析器本身中添加分支。
|
||||
|
||||
如果你想添加一个新的一等推理 provider,请结合本页阅读 [添加 Provider](./adding-providers.md) 和 [Model Provider 插件指南](./model-provider-plugin.md)。
|
||||
|
||||
## 解析优先级
|
||||
|
||||
从高层来看,provider 解析使用以下顺序:
|
||||
|
||||
1. 显式 CLI/运行时请求
|
||||
2. `config.yaml` 中的模型/provider 配置
|
||||
3. 环境变量
|
||||
4. provider 特定的默认值或自动解析
|
||||
|
||||
该顺序很重要,因为 Hermes 将已保存的模型/provider 选择视为正常运行的真实来源。这可以防止过时的 shell 导出变量悄悄覆盖用户在 `hermes model` 中最后选择的端点。
|
||||
|
||||
## Provider
|
||||
|
||||
当前 provider 系列包括(完整内置集合见 `plugins/model-providers/`):
|
||||
|
||||
- AI Gateway(Vercel)
|
||||
- OpenRouter
|
||||
- Nous Portal
|
||||
- OpenAI Codex
|
||||
- Copilot / Copilot ACP
|
||||
- Anthropic(原生)
|
||||
- Google / Gemini(`gemini`、`google-gemini-cli`)
|
||||
- Alibaba / DashScope(`alibaba`、`alibaba-coding-plan`)
|
||||
- DeepSeek
|
||||
- Z.AI
|
||||
- Kimi / Moonshot(`kimi-coding`、`kimi-coding-cn`)
|
||||
- MiniMax(`minimax`、`minimax-cn`、`minimax-oauth`)
|
||||
- Kilo Code
|
||||
- Hugging Face
|
||||
- OpenCode Zen / OpenCode Go
|
||||
- AWS Bedrock
|
||||
- Azure Foundry
|
||||
- NVIDIA NIM
|
||||
- xAI(Grok)
|
||||
- Arcee
|
||||
- GMI Cloud
|
||||
- StepFun
|
||||
- Qwen OAuth
|
||||
- Xiaomi
|
||||
- Ollama Cloud
|
||||
- LM Studio
|
||||
- Tencent TokenHub
|
||||
- Custom(`provider: custom`)— 适用于任何 OpenAI 兼容端点的一等 provider
|
||||
- 命名自定义 provider(`config.yaml` 中的 `custom_providers` 列表)
|
||||
|
||||
## 运行时解析的输出
|
||||
|
||||
运行时解析器返回的数据包括:
|
||||
|
||||
- `provider`
|
||||
- `api_mode`
|
||||
- `base_url`
|
||||
- `api_key`
|
||||
- `source`
|
||||
- provider 特定的元数据,如过期/刷新信息
|
||||
|
||||
## 为什么这很重要
|
||||
|
||||
该解析器是 Hermes 能够在以下场景之间共享认证/运行时逻辑的主要原因:
|
||||
|
||||
- `hermes chat`
|
||||
- gateway 消息处理
|
||||
- 在全新会话中运行的 cron 任务
|
||||
- ACP 编辑器会话
|
||||
- 辅助模型任务
|
||||
|
||||
## AI Gateway
|
||||
|
||||
在 `~/.hermes/.env` 中设置 `AI_GATEWAY_API_KEY`,并使用 `--provider ai-gateway` 运行。Hermes 从 gateway 的 `/models` 端点获取可用模型,筛选出支持工具调用的语言模型。
|
||||
|
||||
## OpenRouter、AI Gateway 与自定义 OpenAI 兼容 base URL
|
||||
|
||||
Hermes 包含相关逻辑,以避免在存在多个 provider 密钥时(例如同时存在 `OPENROUTER_API_KEY`、`AI_GATEWAY_API_KEY` 和 `OPENAI_API_KEY`)将错误的 API key 泄露给自定义端点。
|
||||
|
||||
每个 provider 的 API key 仅作用于其自身的 base URL:
|
||||
|
||||
- `OPENROUTER_API_KEY` 仅发送至 `openrouter.ai` 端点
|
||||
- `AI_GATEWAY_API_KEY` 仅发送至 `ai-gateway.vercel.sh` 端点
|
||||
- `OPENAI_API_KEY` 用于自定义端点及作为回退
|
||||
|
||||
Hermes 还区分以下两种情况:
|
||||
|
||||
- 用户主动选择的真实自定义端点
|
||||
- 未配置自定义端点时使用的 OpenRouter 回退路径
|
||||
|
||||
这种区分对以下场景尤为重要:
|
||||
|
||||
- 本地模型服务器
|
||||
- 非 OpenRouter/非 AI Gateway 的 OpenAI 兼容 API
|
||||
- 无需重新运行 setup 即可切换 provider
|
||||
- 通过 config 保存的自定义端点,即使当前 shell 中未导出 `OPENAI_BASE_URL` 也应正常工作
|
||||
|
||||
## 原生 Anthropic 路径
|
||||
|
||||
Anthropic 不再仅限于"通过 OpenRouter"访问。
|
||||
|
||||
当 provider 解析选择 `anthropic` 时,Hermes 使用:
|
||||
|
||||
- `api_mode = anthropic_messages`
|
||||
- 原生 Anthropic Messages API
|
||||
- `agent/anthropic_adapter.py` 进行转换
|
||||
|
||||
原生 Anthropic 的凭据解析现在在两者同时存在时,优先使用可刷新的 Claude Code 凭据,而非复制的环境变量 token。实际效果为:
|
||||
|
||||
- 包含可刷新认证的 Claude Code 凭据文件被视为首选来源
|
||||
- 手动设置的 `ANTHROPIC_TOKEN` / `CLAUDE_CODE_OAUTH_TOKEN` 值仍可作为显式覆盖
|
||||
- Hermes 在调用原生 Messages API 前会预检 Anthropic 凭据刷新
|
||||
- Hermes 在重建 Anthropic 客户端后,仍会在收到 401 时重试一次,作为回退路径
|
||||
|
||||
## OpenAI Codex 路径
|
||||
|
||||
Codex 使用独立的 Responses API 路径:
|
||||
|
||||
- `api_mode = codex_responses`
|
||||
- 专用的凭据解析和认证存储支持
|
||||
|
||||
## 辅助模型路由
|
||||
|
||||
辅助任务包括:
|
||||
|
||||
- 视觉
|
||||
- 网页提取摘要
|
||||
- 上下文压缩摘要
|
||||
- skills hub 操作
|
||||
- MCP 辅助操作
|
||||
- 记忆刷新
|
||||
|
||||
这些任务可以使用各自独立的 provider/模型路由,而非主对话模型。
|
||||
|
||||
当辅助任务配置的 provider 为 `main` 时,Hermes 通过与普通对话相同的共享运行时路径进行解析。实际效果为:
|
||||
|
||||
- 环境变量驱动的自定义端点仍然有效
|
||||
- 通过 `hermes model` / `config.yaml` 保存的自定义端点同样有效
|
||||
- 辅助路由能够区分真实保存的自定义端点与 OpenRouter 回退
|
||||
|
||||
## 回退模型
|
||||
|
||||
Hermes 支持配置回退 provider 链——一个按顺序尝试的 `(provider, model)` 条目列表,当主模型遇到错误时依次尝试。旧版单对 `fallback_model` 字典仍被接受以保持向后兼容(并在首次写入时迁移)。
|
||||
|
||||
### 内部工作原理
|
||||
|
||||
1. **存储**:`AIAgent.__init__` 存储 `fallback_model` 字典并将 `_fallback_activated` 设为 `False`。
|
||||
|
||||
2. **触发点**:`_try_activate_fallback()` 在 `run_agent.py` 主重试循环的三处被调用:
|
||||
- 在无效 API 响应(None choices、缺少 content)达到最大重试次数后
|
||||
- 在不可重试的客户端错误(HTTP 401、403、404)时
|
||||
- 在瞬时错误(HTTP 429、500、502、503)达到最大重试次数后
|
||||
|
||||
3. **激活流程**(`_try_activate_fallback`):
|
||||
- 若已激活或未配置,立即返回 `False`
|
||||
- 调用 `auxiliary_client.py` 中的 `resolve_provider_client()` 构建带有正确认证的新客户端
|
||||
- 确定 `api_mode`:openai-codex 使用 `codex_responses`,anthropic 使用 `anthropic_messages`,其余使用 `chat_completions`
|
||||
- 原地替换:`self.model`、`self.provider`、`self.base_url`、`self.api_mode`、`self.client`、`self._client_kwargs`
|
||||
- 对于 anthropic 回退:构建原生 Anthropic 客户端而非 OpenAI 兼容客户端
|
||||
- 重新评估 prompt 缓存(对 OpenRouter 上的 Claude 模型启用)
|
||||
- 将 `_fallback_activated` 设为 `True`——防止再次触发
|
||||
- 将重试计数重置为 0 并继续循环
|
||||
|
||||
4. **配置流程**:
|
||||
- CLI:`cli.py` 读取 `CLI_CONFIG["fallback_model"]` → 传递给 `AIAgent(fallback_model=...)`
|
||||
- Gateway:`gateway/run.py._load_fallback_model()` 读取 `config.yaml` → 传递给 `AIAgent`
|
||||
- 验证:`provider` 和 `model` 键均须非空,否则回退被禁用
|
||||
|
||||
### 不支持回退的场景
|
||||
|
||||
- **子代理委托**(`tools/delegate_tool.py`):子代理继承父代理的 provider,但不继承回退配置
|
||||
- **辅助任务**:使用各自独立的 provider 自动检测链(见上方辅助模型路由)
|
||||
|
||||
Cron 任务**支持**回退:`run_job()` 从 `config.yaml` 读取 `fallback_providers`(或旧版 `fallback_model`)并传递给 `AIAgent(fallback_model=...)`,与 gateway 的 `_load_fallback_model()` 模式一致。参见 [Cron 内部机制](./cron-internals.md)。
|
||||
|
||||
### 测试覆盖
|
||||
|
||||
参见 `tests/test_fallback_model.py`,其中包含覆盖所有支持 provider、单次触发语义及边界情况的完整测试。
|
||||
|
||||
## 相关文档
|
||||
|
||||
- [Agent 循环内部机制](./agent-loop.md)
|
||||
- [ACP 内部机制](./acp-internals.md)
|
||||
- [上下文压缩与 Prompt 缓存](./context-compression-and-caching.md)
|
||||
@ -0,0 +1,386 @@
|
||||
# 会话存储
|
||||
|
||||
Hermes Agent 使用 SQLite 数据库(`~/.hermes/state.db`)跨 CLI 和 gateway 会话持久化会话元数据、完整消息历史及模型配置。这替代了早期的逐会话 JSONL 文件方案。
|
||||
|
||||
源文件:`hermes_state.py`
|
||||
|
||||
|
||||
## 架构概览
|
||||
|
||||
```
|
||||
~/.hermes/state.db (SQLite, WAL mode)
|
||||
├── sessions — 会话元数据、token 计数、计费信息
|
||||
├── messages — 每个会话的完整消息历史
|
||||
├── messages_fts — FTS5 虚拟表(content + tool_name + tool_calls)
|
||||
├── messages_fts_trigram — 使用 trigram tokenizer 的 FTS5 虚拟表(CJK / 子串搜索)
|
||||
├── state_meta — 键值元数据表
|
||||
└── schema_version — 单行表,跟踪迁移状态
|
||||
```
|
||||
|
||||
关键设计决策:
|
||||
- **WAL 模式**:支持并发读取 + 单写入(gateway 多平台)
|
||||
- **FTS5 虚拟表**:跨所有会话消息的快速全文搜索
|
||||
- **会话血缘**:通过 `parent_session_id` 链实现(压缩触发的会话分割)
|
||||
- **来源标记**(`cli`、`telegram`、`discord` 等):用于平台过滤
|
||||
- 批量运行器和 RL 轨迹不存储于此(独立系统)
|
||||
|
||||
|
||||
## SQLite Schema
|
||||
|
||||
### Sessions 表
|
||||
|
||||
```sql
|
||||
CREATE TABLE IF NOT EXISTS sessions (
|
||||
id TEXT PRIMARY KEY,
|
||||
source TEXT NOT NULL,
|
||||
user_id TEXT,
|
||||
model TEXT,
|
||||
model_config TEXT,
|
||||
system_prompt TEXT,
|
||||
parent_session_id TEXT,
|
||||
started_at REAL NOT NULL,
|
||||
ended_at REAL,
|
||||
end_reason TEXT,
|
||||
message_count INTEGER DEFAULT 0,
|
||||
tool_call_count INTEGER DEFAULT 0,
|
||||
input_tokens INTEGER DEFAULT 0,
|
||||
output_tokens INTEGER DEFAULT 0,
|
||||
cache_read_tokens INTEGER DEFAULT 0,
|
||||
cache_write_tokens INTEGER DEFAULT 0,
|
||||
reasoning_tokens INTEGER DEFAULT 0,
|
||||
billing_provider TEXT,
|
||||
billing_base_url TEXT,
|
||||
billing_mode TEXT,
|
||||
estimated_cost_usd REAL,
|
||||
actual_cost_usd REAL,
|
||||
cost_status TEXT,
|
||||
cost_source TEXT,
|
||||
pricing_version TEXT,
|
||||
title TEXT,
|
||||
api_call_count INTEGER DEFAULT 0,
|
||||
FOREIGN KEY (parent_session_id) REFERENCES sessions(id)
|
||||
);
|
||||
|
||||
CREATE INDEX IF NOT EXISTS idx_sessions_source ON sessions(source);
|
||||
CREATE INDEX IF NOT EXISTS idx_sessions_parent ON sessions(parent_session_id);
|
||||
CREATE INDEX IF NOT EXISTS idx_sessions_started ON sessions(started_at DESC);
|
||||
CREATE UNIQUE INDEX IF NOT EXISTS idx_sessions_title_unique
|
||||
ON sessions(title) WHERE title IS NOT NULL;
|
||||
```
|
||||
|
||||
### Messages 表
|
||||
|
||||
```sql
|
||||
CREATE TABLE IF NOT EXISTS messages (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
session_id TEXT NOT NULL REFERENCES sessions(id),
|
||||
role TEXT NOT NULL,
|
||||
content TEXT,
|
||||
tool_call_id TEXT,
|
||||
tool_calls TEXT,
|
||||
tool_name TEXT,
|
||||
timestamp REAL NOT NULL,
|
||||
token_count INTEGER,
|
||||
finish_reason TEXT,
|
||||
reasoning TEXT,
|
||||
reasoning_content TEXT,
|
||||
reasoning_details TEXT,
|
||||
codex_reasoning_items TEXT,
|
||||
codex_message_items TEXT
|
||||
);
|
||||
|
||||
CREATE INDEX IF NOT EXISTS idx_messages_session ON messages(session_id, timestamp);
|
||||
```
|
||||
|
||||
说明:
|
||||
- `tool_calls` 以 JSON 字符串存储(序列化的 tool call 对象列表)
|
||||
- `reasoning_details`、`codex_reasoning_items` 和 `codex_message_items` 以 JSON 字符串存储
|
||||
- `reasoning` 存储提供商暴露的原始推理文本
|
||||
- 时间戳为 Unix epoch 浮点数(`time.time()`)
|
||||
|
||||
### FTS5 全文搜索
|
||||
|
||||
```sql
|
||||
CREATE VIRTUAL TABLE IF NOT EXISTS messages_fts USING fts5(
|
||||
content,
|
||||
content=messages,
|
||||
content_rowid=id
|
||||
);
|
||||
```
|
||||
|
||||
FTS5 表通过三个触发器与 `messages` 表保持同步,分别在 INSERT、UPDATE 和 DELETE 时触发:
|
||||
|
||||
```sql
|
||||
CREATE TRIGGER IF NOT EXISTS messages_fts_insert AFTER INSERT ON messages BEGIN
|
||||
INSERT INTO messages_fts(rowid, content) VALUES (new.id, new.content);
|
||||
END;
|
||||
|
||||
CREATE TRIGGER IF NOT EXISTS messages_fts_delete AFTER DELETE ON messages BEGIN
|
||||
INSERT INTO messages_fts(messages_fts, rowid, content)
|
||||
VALUES('delete', old.id, old.content);
|
||||
END;
|
||||
|
||||
CREATE TRIGGER IF NOT EXISTS messages_fts_update AFTER UPDATE ON messages BEGIN
|
||||
INSERT INTO messages_fts(messages_fts, rowid, content)
|
||||
VALUES('delete', old.id, old.content);
|
||||
INSERT INTO messages_fts(rowid, content) VALUES (new.id, new.content);
|
||||
END;
|
||||
```
|
||||
|
||||
|
||||
## Schema 版本与迁移
|
||||
|
||||
当前 schema 版本:**11**
|
||||
|
||||
`schema_version` 表存储单个整数。简单的列添加由 `_reconcile_columns()` 声明式处理(对比实时列与 `SCHEMA_SQL` 并 ADD 缺失列)。版本门控链保留用于无法声明式表达的数据迁移及索引/FTS 变更:
|
||||
|
||||
| 版本 | 变更 |
|
||||
|------|------|
|
||||
| 1 | 初始 schema(sessions、messages、FTS5) |
|
||||
| 2 | 向 messages 添加 `finish_reason` 列 |
|
||||
| 3 | 向 sessions 添加 `title` 列 |
|
||||
| 4 | 在 `title` 上添加唯一索引(允许 NULL,非 NULL 必须唯一) |
|
||||
| 5 | 添加计费列:`cache_read_tokens`、`cache_write_tokens`、`reasoning_tokens`、`billing_provider`、`billing_base_url`、`billing_mode`、`estimated_cost_usd`、`actual_cost_usd`、`cost_status`、`cost_source`、`pricing_version` |
|
||||
| 6 | 向 messages 添加推理列:`reasoning`、`reasoning_details`、`codex_reasoning_items` |
|
||||
| 7 | 向 messages 添加 `reasoning_content` 列 |
|
||||
| 8 | 向 sessions 添加 `api_call_count` 列 |
|
||||
| 9 | 向 messages 添加 `codex_message_items` 列,用于 Codex Responses 消息 id/phase 重放 |
|
||||
| 10 | 添加 `messages_fts_trigram` 虚拟表(trigram tokenizer,用于 CJK / 子串搜索)并回填现有行 |
|
||||
| 11 | 重新索引 `messages_fts` 和 `messages_fts_trigram` 以覆盖 `tool_name` + `tool_calls`,从外部内容模式切换为内联模式;删除旧触发器并回填所有消息行 |
|
||||
|
||||
声明式列添加使用 `ALTER TABLE ADD COLUMN`,包裹在 try/except 中以处理列已存在的情况(幂等)。每个成功的迁移块完成后版本号递增。
|
||||
|
||||
|
||||
## 写入竞争处理
|
||||
|
||||
多个 hermes 进程(gateway + CLI 会话 + worktree agent)共享同一个 `state.db`。`SessionDB` 类通过以下方式处理写入竞争:
|
||||
|
||||
- **短 SQLite 超时**(1 秒),而非默认的 30 秒
|
||||
- **应用层重试**,带随机抖动(20–150ms,最多 15 次重试)
|
||||
- **BEGIN IMMEDIATE** 事务,在事务开始时暴露锁竞争
|
||||
- **定期 WAL checkpoint**,每 50 次成功写入执行一次(PASSIVE 模式)
|
||||
|
||||
这避免了"护卫效应"——SQLite 确定性内部退避会导致所有竞争写入者在相同间隔重试。
|
||||
|
||||
```
|
||||
_WRITE_MAX_RETRIES = 15
|
||||
_WRITE_RETRY_MIN_S = 0.020 # 20ms
|
||||
_WRITE_RETRY_MAX_S = 0.150 # 150ms
|
||||
_CHECKPOINT_EVERY_N_WRITES = 50
|
||||
```
|
||||
|
||||
|
||||
## 常用操作
|
||||
|
||||
### 初始化
|
||||
|
||||
```python
|
||||
from hermes_state import SessionDB
|
||||
|
||||
db = SessionDB() # 默认:~/.hermes/state.db
|
||||
db = SessionDB(db_path=Path("/tmp/test.db")) # 自定义路径
|
||||
```
|
||||
|
||||
### 创建和管理会话
|
||||
|
||||
```python
|
||||
# 创建新会话
|
||||
db.create_session(
|
||||
session_id="sess_abc123",
|
||||
source="cli",
|
||||
model="anthropic/claude-sonnet-4.6",
|
||||
user_id="user_1",
|
||||
parent_session_id=None, # 或用于血缘追踪的上一个会话 ID
|
||||
)
|
||||
|
||||
# 结束会话
|
||||
db.end_session("sess_abc123", end_reason="user_exit")
|
||||
|
||||
# 重新打开会话(清除 ended_at/end_reason)
|
||||
db.reopen_session("sess_abc123")
|
||||
```
|
||||
|
||||
### 存储消息
|
||||
|
||||
```python
|
||||
msg_id = db.append_message(
|
||||
session_id="sess_abc123",
|
||||
role="assistant",
|
||||
content="Here's the answer...",
|
||||
tool_calls=[{"id": "call_1", "function": {"name": "terminal", "arguments": "{}"}}],
|
||||
token_count=150,
|
||||
finish_reason="stop",
|
||||
reasoning="Let me think about this...",
|
||||
)
|
||||
```
|
||||
|
||||
### 检索消息
|
||||
|
||||
```python
|
||||
# 包含所有元数据的原始消息
|
||||
messages = db.get_messages("sess_abc123")
|
||||
|
||||
# OpenAI 对话格式(用于 API 重放)
|
||||
conversation = db.get_messages_as_conversation("sess_abc123")
|
||||
# 返回:[{"role": "user", "content": "..."}, {"role": "assistant", ...}]
|
||||
```
|
||||
|
||||
### 会话标题
|
||||
|
||||
```python
|
||||
# 设置标题(非 NULL 标题中必须唯一)
|
||||
db.set_session_title("sess_abc123", "Fix Docker Build")
|
||||
|
||||
# 按标题解析(返回血缘中最新的)
|
||||
session_id = db.resolve_session_by_title("Fix Docker Build")
|
||||
|
||||
# 自动生成血缘中的下一个标题
|
||||
next_title = db.get_next_title_in_lineage("Fix Docker Build")
|
||||
# 返回:"Fix Docker Build #2"
|
||||
```
|
||||
|
||||
|
||||
## 全文搜索
|
||||
|
||||
`search_messages()` 方法支持 FTS5 查询语法,并自动对用户输入进行清理。
|
||||
|
||||
### 基本搜索
|
||||
|
||||
```python
|
||||
results = db.search_messages("docker deployment")
|
||||
```
|
||||
|
||||
### FTS5 查询语法
|
||||
|
||||
| 语法 | 示例 | 含义 |
|
||||
|------|------|------|
|
||||
| 关键词 | `docker deployment` | 两个词均包含(隐式 AND) |
|
||||
| 引号短语 | `"exact phrase"` | 精确短语匹配 |
|
||||
| 布尔 OR | `docker OR kubernetes` | 任一词 |
|
||||
| 布尔 NOT | `python NOT java` | 排除词 |
|
||||
| 前缀 | `deploy*` | 前缀匹配 |
|
||||
|
||||
### 过滤搜索
|
||||
|
||||
```python
|
||||
# 仅搜索 CLI 会话
|
||||
results = db.search_messages("error", source_filter=["cli"])
|
||||
|
||||
# 排除 gateway 会话
|
||||
results = db.search_messages("bug", exclude_sources=["telegram", "discord"])
|
||||
|
||||
# 仅搜索用户消息
|
||||
results = db.search_messages("help", role_filter=["user"])
|
||||
```
|
||||
|
||||
### 搜索结果格式
|
||||
|
||||
每条结果包含:
|
||||
- `id`、`session_id`、`role`、`timestamp`
|
||||
- `snippet` — FTS5 生成的片段,带 `>>>match<<<` 标记
|
||||
- `context` — 匹配前后各 1 条消息(内容截断至 200 字符)
|
||||
- `source`、`model`、`session_started` — 来自父会话
|
||||
|
||||
`_sanitize_fts5_query()` 方法处理边缘情况:
|
||||
- 去除不匹配的引号和特殊字符
|
||||
- 将含连字符的词包裹在引号中(`chat-send` → `"chat-send"`)
|
||||
- 移除悬空的布尔运算符(`hello AND` → `hello`)
|
||||
|
||||
|
||||
## 会话血缘
|
||||
|
||||
会话可通过 `parent_session_id` 形成链。这发生在 gateway 中上下文压缩触发会话分割时。
|
||||
|
||||
### 查询:查找会话血缘
|
||||
|
||||
```sql
|
||||
-- 查找会话的所有祖先
|
||||
WITH RECURSIVE lineage AS (
|
||||
SELECT * FROM sessions WHERE id = ?
|
||||
UNION ALL
|
||||
SELECT s.* FROM sessions s
|
||||
JOIN lineage l ON s.id = l.parent_session_id
|
||||
)
|
||||
SELECT id, title, started_at, parent_session_id FROM lineage;
|
||||
|
||||
-- 查找会话的所有后代
|
||||
WITH RECURSIVE descendants AS (
|
||||
SELECT * FROM sessions WHERE id = ?
|
||||
UNION ALL
|
||||
SELECT s.* FROM sessions s
|
||||
JOIN descendants d ON s.parent_session_id = d.id
|
||||
)
|
||||
SELECT id, title, started_at FROM descendants;
|
||||
```
|
||||
|
||||
### 查询:带预览的最近会话
|
||||
|
||||
```sql
|
||||
SELECT s.*,
|
||||
COALESCE(
|
||||
(SELECT SUBSTR(m.content, 1, 63)
|
||||
FROM messages m
|
||||
WHERE m.session_id = s.id AND m.role = 'user' AND m.content IS NOT NULL
|
||||
ORDER BY m.timestamp, m.id LIMIT 1),
|
||||
''
|
||||
) AS preview,
|
||||
COALESCE(
|
||||
(SELECT MAX(m2.timestamp) FROM messages m2 WHERE m2.session_id = s.id),
|
||||
s.started_at
|
||||
) AS last_active
|
||||
FROM sessions s
|
||||
ORDER BY s.started_at DESC
|
||||
LIMIT 20;
|
||||
```
|
||||
|
||||
### 查询:Token 使用统计
|
||||
|
||||
```sql
|
||||
-- 按模型统计总 token 数
|
||||
SELECT model,
|
||||
COUNT(*) as session_count,
|
||||
SUM(input_tokens) as total_input,
|
||||
SUM(output_tokens) as total_output,
|
||||
SUM(estimated_cost_usd) as total_cost
|
||||
FROM sessions
|
||||
WHERE model IS NOT NULL
|
||||
GROUP BY model
|
||||
ORDER BY total_cost DESC;
|
||||
|
||||
-- token 使用量最高的会话
|
||||
SELECT id, title, model, input_tokens + output_tokens AS total_tokens,
|
||||
estimated_cost_usd
|
||||
FROM sessions
|
||||
ORDER BY total_tokens DESC
|
||||
LIMIT 10;
|
||||
```
|
||||
|
||||
|
||||
## 导出与清理
|
||||
|
||||
```python
|
||||
# 导出单个会话及其消息
|
||||
data = db.export_session("sess_abc123")
|
||||
|
||||
# 导出所有会话(含消息)为字典列表
|
||||
all_data = db.export_all(source="cli")
|
||||
|
||||
# 删除旧会话(仅删除已结束的会话)
|
||||
deleted_count = db.prune_sessions(older_than_days=90)
|
||||
deleted_count = db.prune_sessions(older_than_days=30, source="telegram")
|
||||
|
||||
# 清除消息但保留会话记录
|
||||
db.clear_messages("sess_abc123")
|
||||
|
||||
# 删除会话及所有消息
|
||||
db.delete_session("sess_abc123")
|
||||
```
|
||||
|
||||
|
||||
## 数据库位置
|
||||
|
||||
默认路径:`~/.hermes/state.db`
|
||||
|
||||
该路径由 `hermes_constants.get_hermes_home()` 推导,默认解析为 `~/.hermes/`,或 `HERMES_HOME` 环境变量的值。
|
||||
|
||||
数据库文件、WAL 文件(`state.db-wal`)和共享内存文件(`state.db-shm`)均创建于同一目录。
|
||||
@ -0,0 +1,234 @@
|
||||
---
|
||||
sidebar_position: 9
|
||||
title: "工具运行时"
|
||||
description: "工具注册表、toolset、调度及终端环境的运行时行为"
|
||||
---
|
||||
|
||||
# 工具运行时
|
||||
|
||||
Hermes 工具是自注册函数,按 toolset(工具集)分组,并通过中央注册表/调度系统执行。
|
||||
|
||||
主要文件:
|
||||
|
||||
- `tools/registry.py`
|
||||
- `model_tools.py`
|
||||
- `toolsets.py`
|
||||
- `tools/terminal_tool.py`
|
||||
- `tools/environments/*`
|
||||
|
||||
## 工具注册模型
|
||||
|
||||
每个工具模块在导入时调用 `registry.register(...)`。
|
||||
|
||||
`model_tools.py` 负责导入/发现工具模块,并构建供模型使用的 schema 列表。
|
||||
|
||||
### `registry.register()` 的工作原理
|
||||
|
||||
`tools/` 中的每个工具文件在模块级别调用 `registry.register()` 来声明自身。函数签名如下:
|
||||
|
||||
```python
|
||||
registry.register(
|
||||
name="terminal", # 唯一工具名称(用于 API schema)
|
||||
toolset="terminal", # 该工具所属的 toolset
|
||||
schema={...}, # OpenAI function-calling schema(描述、参数)
|
||||
handler=handle_terminal, # 工具被调用时执行的函数
|
||||
check_fn=check_terminal, # 可选:返回 True/False 表示是否可用
|
||||
requires_env=["SOME_VAR"], # 可选:所需的环境变量(用于 UI 显示)
|
||||
is_async=False, # handler 是否为异步协程
|
||||
description="Run commands", # 人类可读的描述
|
||||
emoji="💻", # 用于 spinner/进度显示的 emoji
|
||||
)
|
||||
```
|
||||
|
||||
每次调用都会创建一个 `ToolEntry`,以工具名称为键存储在单例 `ToolRegistry._tools` 字典中。若不同 toolset 之间出现名称冲突,会记录警告,后注册的条目覆盖前者。
|
||||
|
||||
### 发现机制:`discover_builtin_tools()`
|
||||
|
||||
当 `model_tools.py` 被导入时,会调用 `tools/registry.py` 中的 `discover_builtin_tools()`。该函数使用 AST 解析扫描所有 `tools/*.py` 文件,找出包含顶层 `registry.register()` 调用的模块,然后导入它们:
|
||||
|
||||
```python
|
||||
# tools/registry.py(简化版)
|
||||
def discover_builtin_tools(tools_dir=None):
|
||||
tools_path = Path(tools_dir) if tools_dir else Path(__file__).parent
|
||||
for path in sorted(tools_path.glob("*.py")):
|
||||
if path.name in {"__init__.py", "registry.py", "mcp_tool.py"}:
|
||||
continue
|
||||
if _module_registers_tools(path): # AST 检查顶层 registry.register()
|
||||
importlib.import_module(f"tools.{path.stem}")
|
||||
```
|
||||
|
||||
这种自动发现机制意味着新工具文件会被自动识别——无需手动维护列表。AST 检查只匹配顶层的 `registry.register()` 调用(不匹配函数内部的调用),因此 `tools/` 中的辅助模块不会被导入。
|
||||
|
||||
每次导入都会触发模块的 `registry.register()` 调用。可选工具中的错误(例如图像生成工具缺少 `fal_client`)会被捕获并记录——不会阻止其他工具加载。
|
||||
|
||||
核心工具发现完成后,还会发现 MCP 工具和插件工具:
|
||||
|
||||
1. **MCP 工具** — `tools.mcp_tool.discover_mcp_tools()` 读取 MCP 服务器配置,并注册来自外部服务器的工具。
|
||||
2. **插件工具** — `hermes_cli.plugins.discover_plugins()` 加载用户/项目/pip 插件,这些插件可能注册额外的工具。
|
||||
|
||||
## 工具可用性检查(`check_fn`)
|
||||
|
||||
每个工具可以选择性地提供一个 `check_fn`——一个可调用对象,在工具可用时返回 `True`,否则返回 `False`。典型的检查包括:
|
||||
|
||||
- **API 密钥是否存在** — 例如,`lambda: bool(os.environ.get("SERP_API_KEY"))` 用于网络搜索
|
||||
- **服务是否运行** — 例如,检查 Honcho 服务器是否已配置
|
||||
- **二进制文件是否已安装** — 例如,验证浏览器工具的 `playwright` 是否可用
|
||||
|
||||
当 `registry.get_definitions()` 为模型构建 schema 列表时,会运行每个工具的 `check_fn()`:
|
||||
|
||||
```python
|
||||
# 简化自 registry.py
|
||||
if entry.check_fn:
|
||||
try:
|
||||
available = bool(entry.check_fn())
|
||||
except Exception:
|
||||
available = False # 异常 = 不可用
|
||||
if not available:
|
||||
continue # 完全跳过该工具
|
||||
```
|
||||
|
||||
关键行为:
|
||||
- 检查结果**按调用缓存**——若多个工具共享同一个 `check_fn`,只运行一次。
|
||||
- `check_fn()` 中的异常被视为"不可用"(故障安全)。
|
||||
- `is_toolset_available()` 方法检查某个 toolset 的 `check_fn` 是否通过,用于 UI 显示和 toolset 解析。
|
||||
|
||||
## Toolset 解析
|
||||
|
||||
Toolset 是工具的命名集合。Hermes 通过以下方式解析它们:
|
||||
|
||||
- 显式启用/禁用的 toolset 列表
|
||||
- 平台预设(`hermes-cli`、`hermes-telegram` 等)
|
||||
- 动态 MCP toolset
|
||||
- 精选的特殊用途集合,如 `hermes-acp`
|
||||
|
||||
### `get_tool_definitions()` 如何过滤工具
|
||||
|
||||
主入口点为 `model_tools.get_tool_definitions(enabled_toolsets, disabled_toolsets, quiet_mode)`:
|
||||
|
||||
1. **若提供了 `enabled_toolsets`** — 仅包含这些 toolset 中的工具。每个 toolset 名称通过 `resolve_toolset()` 解析,将复合 toolset 展开为单个工具名称。
|
||||
|
||||
2. **若提供了 `disabled_toolsets`** — 从所有 toolset 开始,减去已禁用的。
|
||||
|
||||
3. **若两者均未提供** — 包含所有已知 toolset。
|
||||
|
||||
4. **注册表过滤** — 解析后的工具名称集合传递给 `registry.get_definitions()`,后者应用 `check_fn` 过滤并返回 OpenAI 格式的 schema。
|
||||
|
||||
5. **动态 schema 修补** — 过滤后,`execute_code` 和 `browser_navigate` 的 schema 会被动态调整,仅引用实际通过过滤的工具(防止模型幻觉出不可用的工具)。
|
||||
|
||||
### 旧版 toolset 名称
|
||||
|
||||
带有 `_tools` 后缀的旧版 toolset 名称(例如 `web_tools`、`terminal_tools`)通过 `_LEGACY_TOOLSET_MAP` 映射到其现代工具名称,以保持向后兼容性。
|
||||
|
||||
## 调度
|
||||
|
||||
运行时,工具通过中央注册表调度,但部分 agent 级别的工具(如 memory/todo/session-search 处理)由 agent 循环直接处理。
|
||||
|
||||
### 调度流程:模型 tool_call → handler 执行
|
||||
|
||||
当模型返回 `tool_call` 时,流程如下:
|
||||
|
||||
```
|
||||
模型响应包含 tool_call
|
||||
↓
|
||||
run_agent.py agent 循环
|
||||
↓
|
||||
model_tools.handle_function_call(name, args, task_id, user_task)
|
||||
↓
|
||||
[Agent 循环工具?] → 由 agent 循环直接处理(todo、memory、session_search、delegate_task)
|
||||
↓
|
||||
[插件 pre-hook] → invoke_hook("pre_tool_call", ...)
|
||||
↓
|
||||
registry.dispatch(name, args, **kwargs)
|
||||
↓
|
||||
按名称查找 ToolEntry
|
||||
↓
|
||||
[异步 handler?] → 通过 _run_async() 桥接
|
||||
[同步 handler?] → 直接调用
|
||||
↓
|
||||
返回结果字符串(或 JSON 错误)
|
||||
↓
|
||||
[插件 post-hook] → invoke_hook("post_tool_call", ...)
|
||||
```
|
||||
|
||||
### 错误包装
|
||||
|
||||
所有工具执行在两个层级进行错误处理:
|
||||
|
||||
1. **`registry.dispatch()`** — 捕获 handler 抛出的任何异常,并以 JSON 形式返回 `{"error": "Tool execution failed: ExceptionType: message"}`。
|
||||
|
||||
2. **`handle_function_call()`** — 将整个调度包裹在次级 try/except 中,返回 `{"error": "Error executing tool_name: message"}`。
|
||||
|
||||
这确保模型始终收到格式正确的 JSON 字符串,而不会遇到未处理的异常。
|
||||
|
||||
### Agent 循环工具
|
||||
|
||||
以下四个工具在注册表调度之前被拦截,因为它们需要 agent 级别的状态(TodoStore、MemoryStore 等):
|
||||
|
||||
- `todo` — 规划/任务跟踪
|
||||
- `memory` — 持久化 memory 写入
|
||||
- `session_search` — 跨会话召回
|
||||
- `delegate_task` — 生成子 agent 会话
|
||||
|
||||
这些工具的 schema 仍在注册表中注册(供 `get_tool_definitions` 使用),但若调度以某种方式直接到达它们,其 handler 会返回一个存根错误。
|
||||
|
||||
### 异步桥接
|
||||
|
||||
当工具 handler 为异步时,`_run_async()` 将其桥接到同步调度路径:
|
||||
|
||||
- **CLI 路径(无运行中的事件循环)** — 使用持久化事件循环以保持缓存的异步客户端存活
|
||||
- **Gateway 路径(有运行中的事件循环)** — 使用 `asyncio.run()` 启动一个一次性线程
|
||||
- **工作线程(并行工具)** — 使用存储在线程本地存储中的每线程持久化循环
|
||||
|
||||
## DANGEROUS_PATTERNS 审批流程
|
||||
|
||||
终端工具集成了定义在 `tools/approval.py` 中的危险命令审批系统:
|
||||
|
||||
1. **模式检测** — `DANGEROUS_PATTERNS` 是一个 `(regex, description)` 元组列表,涵盖破坏性操作:
|
||||
- 递归删除(`rm -rf`)
|
||||
- 文件系统格式化(`mkfs`、`dd`)
|
||||
- SQL 破坏性操作(`DROP TABLE`、不带 `WHERE` 的 `DELETE FROM`)
|
||||
- 系统配置覆写(`> /etc/`)
|
||||
- 服务操控(`systemctl stop`)
|
||||
- 远程代码执行(`curl | sh`)
|
||||
- Fork bomb、进程终止等
|
||||
|
||||
2. **检测** — 在执行任何终端命令之前,`detect_dangerous_command(command)` 会对所有模式进行检查。
|
||||
|
||||
3. **审批提示** — 若发现匹配:
|
||||
- **CLI 模式** — 交互式提示要求用户批准、拒绝或永久允许
|
||||
- **Gateway 模式** — 异步审批回调将请求发送至消息平台
|
||||
- **智能审批** — 可选地,辅助 LLM 可自动批准匹配模式但风险较低的命令(例如,`rm -rf node_modules/` 是安全的,但匹配"递归删除"模式)
|
||||
|
||||
4. **会话状态** — 审批按会话跟踪。一旦在某个会话中批准了"递归删除",后续的 `rm -rf` 命令不会再次提示。
|
||||
|
||||
5. **永久允许列表** — "永久允许"选项会将该模式写入 `config.yaml` 的 `command_allowlist`,跨会话持久化。
|
||||
|
||||
## 终端/运行时环境
|
||||
|
||||
终端系统支持多种后端:
|
||||
|
||||
- local
|
||||
- docker
|
||||
- ssh
|
||||
- singularity
|
||||
- modal
|
||||
- daytona
|
||||
- vercel_sandbox
|
||||
|
||||
还支持:
|
||||
|
||||
- 按任务的 cwd 覆盖
|
||||
- 后台进程管理
|
||||
- PTY 模式
|
||||
- 危险命令的审批回调
|
||||
|
||||
## 并发
|
||||
|
||||
工具调用可以顺序执行,也可以并发执行,具体取决于工具组合和交互需求。
|
||||
|
||||
## 相关文档
|
||||
|
||||
- [Toolsets 参考](../reference/toolsets-reference.md)
|
||||
- [内置工具参考](../reference/tools-reference.md)
|
||||
- [Agent 循环内部机制](./agent-loop.md)
|
||||
- [ACP 内部机制](./acp-internals.md)
|
||||
@ -0,0 +1,222 @@
|
||||
# 轨迹格式
|
||||
|
||||
Hermes Agent 以 ShareGPT 兼容的 JSONL 格式保存对话轨迹,用于训练数据、调试产物和强化学习数据集。
|
||||
|
||||
源文件:`agent/trajectory.py`、`run_agent.py`(搜索 `_save_trajectory`)、`batch_runner.py`
|
||||
|
||||
|
||||
## 文件命名规范
|
||||
|
||||
轨迹写入当前工作目录下的文件:
|
||||
|
||||
| 文件 | 时机 |
|
||||
|------|------|
|
||||
| `trajectory_samples.jsonl` | 成功完成的对话(`completed=True`) |
|
||||
| `failed_trajectories.jsonl` | 失败或被中断的对话(`completed=False`) |
|
||||
|
||||
批量运行器(`batch_runner.py`)按批次写入自定义输出文件
|
||||
(例如 `batch_001_output.jsonl`),并附带额外的元数据字段。
|
||||
|
||||
可通过 `save_trajectory()` 的 `filename` 参数覆盖文件名。
|
||||
|
||||
|
||||
## JSONL 条目格式
|
||||
|
||||
文件中每一行是一个独立的 JSON 对象。共有两种变体:
|
||||
|
||||
### CLI/交互式格式(来自 `_save_trajectory`)
|
||||
|
||||
```json
|
||||
{
|
||||
"conversations": [ ... ],
|
||||
"timestamp": "2026-03-30T14:22:31.456789",
|
||||
"model": "anthropic/claude-sonnet-4.6",
|
||||
"completed": true
|
||||
}
|
||||
```
|
||||
|
||||
### 批量运行器格式(来自 `batch_runner.py`)
|
||||
|
||||
```json
|
||||
{
|
||||
"prompt_index": 42,
|
||||
"conversations": [ ... ],
|
||||
"metadata": { "prompt_source": "gsm8k", "difficulty": "hard" },
|
||||
"completed": true,
|
||||
"partial": false,
|
||||
"api_calls": 7,
|
||||
"toolsets_used": ["code_tools", "file_tools"],
|
||||
"tool_stats": {
|
||||
"terminal": {"count": 3, "success": 3, "failure": 0},
|
||||
"read_file": {"count": 2, "success": 2, "failure": 0},
|
||||
"write_file": {"count": 0, "success": 0, "failure": 0}
|
||||
},
|
||||
"tool_error_counts": {
|
||||
"terminal": 0,
|
||||
"read_file": 0,
|
||||
"write_file": 0
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
`tool_stats` 和 `tool_error_counts` 字典已规范化,包含所有可能的工具
|
||||
(来自 `model_tools.TOOL_TO_TOOLSET_MAP`),缺省值为零,
|
||||
确保各条目的 schema 一致,便于 HuggingFace 数据集加载。
|
||||
|
||||
|
||||
## conversations 数组(ShareGPT 格式)
|
||||
|
||||
`conversations` 数组使用 ShareGPT 角色约定:
|
||||
|
||||
| API 角色 | ShareGPT `from` |
|
||||
|----------|-----------------|
|
||||
| system | `"system"` |
|
||||
| user | `"human"` |
|
||||
| assistant | `"gpt"` |
|
||||
| tool | `"tool"` |
|
||||
|
||||
### 完整示例
|
||||
|
||||
```json
|
||||
{
|
||||
"conversations": [
|
||||
{
|
||||
"from": "system",
|
||||
"value": "You are a function calling AI model. You are provided with function signatures within <tools> </tools> XML tags. You may call one or more functions to assist with the user query. If available tools are not relevant in assisting with user query, just respond in natural conversational language. Don't make assumptions about what values to plug into functions. After calling & executing the functions, you will be provided with function results within <tool_response> </tool_response> XML tags. Here are the available tools:\n<tools>\n[{\"name\": \"terminal\", \"description\": \"Execute shell commands\", \"parameters\": {\"type\": \"object\", \"properties\": {\"command\": {\"type\": \"string\"}}}, \"required\": null}]\n</tools>\nFor each function call return a JSON object, with the following pydantic model json schema for each:\n{'title': 'FunctionCall', 'type': 'object', 'properties': {'name': {'title': 'Name', 'type': 'string'}, 'arguments': {'title': 'Arguments', 'type': 'object'}}, 'required': ['name', 'arguments']}\nEach function call should be enclosed within <tool_call> </tool_call> XML tags.\nExample:\n<tool_call>\n{'name': <function-name>,'arguments': <args-dict>}\n</tool_call>"
|
||||
},
|
||||
{
|
||||
"from": "human",
|
||||
"value": "What Python version is installed?"
|
||||
},
|
||||
{
|
||||
"from": "gpt",
|
||||
"value": "<think>\nThe user wants to know the Python version. I should run python3 --version.\n</think>\n<tool_call>\n{\"name\": \"terminal\", \"arguments\": {\"command\": \"python3 --version\"}}\n</tool_call>"
|
||||
},
|
||||
{
|
||||
"from": "tool",
|
||||
"value": "<tool_response>\n{\"tool_call_id\": \"call_abc123\", \"name\": \"terminal\", \"content\": \"Python 3.11.6\"}\n</tool_response>"
|
||||
},
|
||||
{
|
||||
"from": "gpt",
|
||||
"value": "<think>\nGot the version. I can now answer the user.\n</think>\nPython 3.11.6 is installed on this system."
|
||||
}
|
||||
],
|
||||
"timestamp": "2026-03-30T14:22:31.456789",
|
||||
"model": "anthropic/claude-sonnet-4.6",
|
||||
"completed": true
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
## 规范化规则
|
||||
|
||||
### 推理内容标记
|
||||
|
||||
轨迹转换器将所有推理内容统一规范化为 `<think>` 标签,无论模型最初以何种方式生成:
|
||||
|
||||
1. **原生思考 token**(来自 Anthropic、OpenAI o 系列等提供商的 `msg["reasoning"]` 字段):
|
||||
包装为 `<think>\n{reasoning}\n</think>\n` 并置于内容之前。
|
||||
|
||||
2. **REASONING_SCRATCHPAD XML**(禁用原生思考时,模型通过系统提示指令的 XML 进行推理):
|
||||
`<REASONING_SCRATCHPAD>` 标签通过 `convert_scratchpad_to_think()` 转换为 `<think>`。
|
||||
|
||||
3. **空 think 块**:每个 `gpt` 轮次都保证包含一个 `<think>` 块。若未产生任何推理内容,
|
||||
则插入空块:`<think>\n</think>\n`——确保训练数据格式一致。
|
||||
|
||||
### 工具调用规范化
|
||||
|
||||
API 格式的工具调用(含 `tool_call_id`、函数名、JSON 字符串形式的参数)
|
||||
转换为 XML 包裹的 JSON:
|
||||
|
||||
```
|
||||
<tool_call>
|
||||
{"name": "terminal", "arguments": {"command": "ls -la"}}
|
||||
</tool_call>
|
||||
```
|
||||
|
||||
- 参数从 JSON 字符串解析回对象(不进行二次编码)
|
||||
- 若 JSON 解析失败(正常情况下不应发生——对话期间已验证),
|
||||
则使用空 `{}` 并记录警告日志
|
||||
- 一个助手轮次中的多个工具调用,在单条 `gpt` 消息中生成多个 `<tool_call>` 块
|
||||
|
||||
### 工具响应规范化
|
||||
|
||||
跟随助手消息的所有工具结果,合并为单条 `tool` 轮次,以 XML 包裹的 JSON 响应呈现:
|
||||
|
||||
```
|
||||
<tool_response>
|
||||
{"tool_call_id": "call_abc123", "name": "terminal", "content": "output here"}
|
||||
</tool_response>
|
||||
```
|
||||
|
||||
- 若工具内容看起来像 JSON(以 `{` 或 `[` 开头),则解析后 content 字段包含 JSON 对象/数组,而非字符串
|
||||
- 多个工具结果以换行符连接,合并为一条消息
|
||||
- 工具名称按位置与父助手消息的 `tool_calls` 数组匹配
|
||||
|
||||
### 系统消息
|
||||
|
||||
系统消息在保存时生成(不取自对话内容),遵循 Hermes 函数调用 prompt 模板,包含:
|
||||
|
||||
- 说明函数调用协议的前言
|
||||
- 包含 JSON 工具定义的 `<tools>` XML 块
|
||||
- `FunctionCall` 对象的 schema 参考
|
||||
- `<tool_call>` 示例
|
||||
|
||||
工具定义包含 `name`、`description`、`parameters` 和 `required`
|
||||
(设为 `null` 以匹配规范格式)。
|
||||
|
||||
|
||||
## 加载轨迹
|
||||
|
||||
轨迹为标准 JSONL 格式——可用任意 JSON lines 读取器加载:
|
||||
|
||||
```python
|
||||
import json
|
||||
|
||||
def load_trajectories(path: str):
|
||||
"""Load trajectory entries from a JSONL file."""
|
||||
entries = []
|
||||
with open(path, "r", encoding="utf-8") as f:
|
||||
for line in f:
|
||||
line = line.strip()
|
||||
if line:
|
||||
entries.append(json.loads(line))
|
||||
return entries
|
||||
|
||||
# Filter to successful completions only
|
||||
successful = [e for e in load_trajectories("trajectory_samples.jsonl")
|
||||
if e.get("completed")]
|
||||
|
||||
# Extract just the conversations for training
|
||||
training_data = [e["conversations"] for e in successful]
|
||||
```
|
||||
|
||||
### 加载至 HuggingFace Datasets
|
||||
|
||||
```python
|
||||
from datasets import load_dataset
|
||||
|
||||
ds = load_dataset("json", data_files="trajectory_samples.jsonl")
|
||||
```
|
||||
|
||||
规范化的 `tool_stats` schema 确保所有条目具有相同的列,
|
||||
防止数据集加载时出现 Arrow schema 不匹配错误。
|
||||
|
||||
|
||||
## 控制轨迹保存
|
||||
|
||||
在 CLI 中,轨迹保存通过以下方式控制:
|
||||
|
||||
```yaml
|
||||
# config.yaml
|
||||
agent:
|
||||
save_trajectories: true # default: false
|
||||
```
|
||||
|
||||
或通过 `--save-trajectories` 标志。当 agent 以 `save_trajectories=True` 初始化时,
|
||||
`_save_trajectory()` 方法在每次对话轮次结束时调用。
|
||||
|
||||
批量运行器始终保存轨迹(这是其主要用途)。
|
||||
|
||||
所有轮次中推理内容为零的样本,将被批量运行器自动丢弃,
|
||||
以避免非推理示例污染训练数据。
|
||||
@ -0,0 +1,231 @@
|
||||
---
|
||||
sidebar_position: 12
|
||||
title: "视频生成 Provider 插件"
|
||||
description: "如何为 Hermes Agent 构建视频生成后端插件"
|
||||
---
|
||||
|
||||
# 构建视频生成 Provider 插件
|
||||
|
||||
视频生成 provider 插件注册一个后端,用于处理所有 `video_generate` 工具调用。内置 provider(xAI、FAL)以插件形式提供。将目录放入 `plugins/video_gen/<name>/` 即可添加新 provider 或覆盖内置 provider。
|
||||
|
||||
:::tip
|
||||
视频生成与[图像生成 Provider 插件](/developer-guide/image-gen-provider-plugin)几乎一一对应——如果你已构建过图像生成后端,对其结构应已了然于胸。主要区别在于:`capabilities()` 方法用于声明模态(modality)/宽高比/时长,以及路由约定(传入 `image_url` 则使用图生视频,省略则使用文生视频——provider 在内部选择正确的端点)。
|
||||
:::
|
||||
|
||||
## 统一接口(一个工具,两种模态)
|
||||
|
||||
`video_generate` 工具通过一个参数暴露两种模态:
|
||||
|
||||
- **文生视频(Text-to-video)** — 仅传入 `prompt`。Provider 路由至其文生视频端点。
|
||||
- **图生视频(Image-to-video)** — 同时传入 `prompt` 和 `image_url`。Provider 路由至其图生视频端点。
|
||||
|
||||
编辑和扩展功能有意不在支持范围内。大多数后端不支持这些功能,且不一致性会迫使 agent 的工具描述中出现针对各后端的说明文字。
|
||||
|
||||
## 发现机制
|
||||
|
||||
Hermes 在三个位置扫描视频生成后端:
|
||||
|
||||
1. **内置** — `<repo>/plugins/video_gen/<name>/`(通过 `kind: backend` 自动加载)
|
||||
2. **用户** — `~/.hermes/plugins/video_gen/<name>/`(通过 `plugins.enabled` 选择启用)
|
||||
3. **Pip** — 声明了 `hermes_agent.plugins` 入口点的包
|
||||
|
||||
每个插件的 `register(ctx)` 函数调用 `ctx.register_video_gen_provider(...)`。活跃 provider 由 `config.yaml` 中的 `video_gen.provider` 指定;`hermes tools` → Video Generation 引导用户完成选择。与 `image_generate` 不同,此处没有内置的遗留后端——每个 provider 都是插件。
|
||||
|
||||
## 目录结构
|
||||
|
||||
```
|
||||
plugins/video_gen/my-backend/
|
||||
├── __init__.py # VideoGenProvider 子类 + register()
|
||||
└── plugin.yaml # 包含 kind: backend 的清单文件
|
||||
```
|
||||
|
||||
## VideoGenProvider ABC
|
||||
|
||||
继承 `agent.video_gen_provider.VideoGenProvider`。必须实现:`name` 属性和 `generate()` 方法。
|
||||
|
||||
```python
|
||||
# plugins/video_gen/my-backend/__init__.py
|
||||
from typing import Any, Dict, List, Optional
|
||||
import os
|
||||
|
||||
from agent.video_gen_provider import (
|
||||
VideoGenProvider,
|
||||
error_response,
|
||||
success_response,
|
||||
)
|
||||
|
||||
|
||||
class MyVideoGenProvider(VideoGenProvider):
|
||||
@property
|
||||
def name(self) -> str:
|
||||
return "my-backend"
|
||||
|
||||
@property
|
||||
def display_name(self) -> str:
|
||||
return "My Backend"
|
||||
|
||||
def is_available(self) -> bool:
|
||||
return bool(os.environ.get("MY_API_KEY"))
|
||||
|
||||
def list_models(self) -> List[Dict[str, Any]]:
|
||||
# Each entry is a model FAMILY — a name the user picks once.
|
||||
# Your provider's generate() routes within the family based on
|
||||
# whether image_url was passed.
|
||||
return [
|
||||
{
|
||||
"id": "fast",
|
||||
"display": "Fast",
|
||||
"speed": "~30s",
|
||||
"strengths": "Cheapest tier",
|
||||
"price": "$0.05/s",
|
||||
"modalities": ["text", "image"], # advisory
|
||||
},
|
||||
]
|
||||
|
||||
def default_model(self) -> Optional[str]:
|
||||
return "fast"
|
||||
|
||||
def capabilities(self) -> Dict[str, Any]:
|
||||
return {
|
||||
"modalities": ["text", "image"],
|
||||
"aspect_ratios": ["16:9", "9:16"],
|
||||
"resolutions": ["720p", "1080p"],
|
||||
"min_duration": 1,
|
||||
"max_duration": 10,
|
||||
"supports_audio": False,
|
||||
"supports_negative_prompt": True,
|
||||
"max_reference_images": 0,
|
||||
}
|
||||
|
||||
def get_setup_schema(self) -> Dict[str, Any]:
|
||||
return {
|
||||
"name": "My Backend",
|
||||
"badge": "paid",
|
||||
"tag": "Short description shown in `hermes tools`",
|
||||
"env_vars": [
|
||||
{
|
||||
"key": "MY_API_KEY",
|
||||
"prompt": "My Backend API key",
|
||||
"url": "https://mybackend.example.com/keys",
|
||||
},
|
||||
],
|
||||
}
|
||||
|
||||
def generate(
|
||||
self,
|
||||
prompt: str,
|
||||
*,
|
||||
model: Optional[str] = None,
|
||||
image_url: Optional[str] = None,
|
||||
reference_image_urls: Optional[List[str]] = None,
|
||||
duration: Optional[int] = None,
|
||||
aspect_ratio: str = "16:9",
|
||||
resolution: str = "720p",
|
||||
negative_prompt: Optional[str] = None,
|
||||
audio: Optional[bool] = None,
|
||||
seed: Optional[int] = None,
|
||||
**kwargs: Any, # always ignore unknown kwargs for forward-compat
|
||||
) -> Dict[str, Any]:
|
||||
# ROUTE: image_url presence picks the endpoint.
|
||||
if image_url:
|
||||
endpoint = "my-backend/image-to-video"
|
||||
modality_used = "image"
|
||||
else:
|
||||
endpoint = "my-backend/text-to-video"
|
||||
modality_used = "text"
|
||||
|
||||
# ... call your API ...
|
||||
|
||||
return success_response(
|
||||
video="https://your-cdn/output.mp4",
|
||||
model=model or "fast",
|
||||
prompt=prompt,
|
||||
modality=modality_used,
|
||||
aspect_ratio=aspect_ratio,
|
||||
duration=duration or 5,
|
||||
provider=self.name,
|
||||
)
|
||||
|
||||
|
||||
def register(ctx) -> None:
|
||||
ctx.register_video_gen_provider(MyVideoGenProvider())
|
||||
```
|
||||
|
||||
## 插件清单
|
||||
|
||||
```yaml
|
||||
# plugins/video_gen/my-backend/plugin.yaml
|
||||
name: my-backend
|
||||
version: 1.0.0
|
||||
description: "My video generation backend"
|
||||
author: Your Name
|
||||
kind: backend
|
||||
requires_env:
|
||||
- MY_API_KEY
|
||||
```
|
||||
|
||||
## `video_generate` 参数模式
|
||||
|
||||
该工具在所有后端中使用统一的参数模式。Provider 忽略其不支持的参数。
|
||||
|
||||
| 参数 | 说明 |
|
||||
|---|---|
|
||||
| `prompt` | 文本指令(必填) |
|
||||
| `image_url` | 设置时 → 图生视频;省略时 → 文生视频 |
|
||||
| `reference_image_urls` | 风格/角色参考图(取决于 provider) |
|
||||
| `duration` | 秒数——provider 会进行截断 |
|
||||
| `aspect_ratio` | `"16:9"`、`"9:16"`、`"1:1"` 等——provider 会进行截断 |
|
||||
| `resolution` | `"480p"` / `"540p"` / `"720p"` / `"1080p"`——provider 会进行截断 |
|
||||
| `negative_prompt` | 需要避免的内容(仅 Pixverse/Kling 支持) |
|
||||
| `audio` | 原生音频(Veo3 / Pixverse 定价层级) |
|
||||
| `seed` | 可复现性 |
|
||||
| `model` | 覆盖当前活跃的模型/系列 |
|
||||
|
||||
Provider 的 `capabilities()` 声明上述哪些参数会被实际处理。Agent 在工具描述中看到的是当前活跃后端的能力信息,当用户通过 `hermes tools` 切换后端时会动态重建。
|
||||
|
||||
## 模型系列与端点路由(FAL 模式)
|
||||
|
||||
当你的后端每个"模型"对应多个端点时——例如 FAL,其中每个系列(Veo 3.1、Pixverse v6、Kling O3)都有 `/text-to-video` 和 `/image-to-video` 两个 URL——将每个**系列**表示为一个目录条目。你的 `generate()` 根据是否传入 `image_url` 来选择正确的端点:
|
||||
|
||||
```python
|
||||
FAMILIES = {
|
||||
"veo3.1": {
|
||||
"text_endpoint": "fal-ai/veo3.1",
|
||||
"image_endpoint": "fal-ai/veo3.1/image-to-video",
|
||||
# ... family-specific capability flags ...
|
||||
},
|
||||
}
|
||||
|
||||
def generate(self, prompt, *, image_url=None, model=None, **kwargs):
|
||||
family_id, family = _resolve_family(model)
|
||||
endpoint = family["image_endpoint"] if image_url else family["text_endpoint"]
|
||||
# ... build payload from family's declared capability flags, call endpoint ...
|
||||
```
|
||||
|
||||
用户在 `hermes tools` 中只需选择一次 `veo3.1`。Agent 无需关心端点——它只负责传入(或不传入)`image_url`。
|
||||
|
||||
## 选择优先级
|
||||
|
||||
针对每个实例的模型配置(参见 `plugins/video_gen/fal/__init__.py`):
|
||||
|
||||
1. 工具调用中的 `model=` 关键字参数
|
||||
2. `<PROVIDER>_VIDEO_MODEL` 环境变量
|
||||
3. `config.yaml` 中的 `video_gen.<provider>.model`
|
||||
4. `config.yaml` 中的 `video_gen.model`(当其值为你的某个 ID 时)
|
||||
5. Provider 的 `default_model()`
|
||||
|
||||
## 响应结构
|
||||
|
||||
`success_response()` 和 `error_response()` 生成每个后端返回的标准 dict 结构。请使用它们——不要手动构造 dict。
|
||||
|
||||
成功响应的键:`success`、`video`(URL 或绝对路径)、`model`、`prompt`、`modality`(`"text"` 或 `"image"`)、`aspect_ratio`、`duration`、`provider`,以及 `extra`。
|
||||
|
||||
错误响应的键:`success`、`video`(None)、`error`、`error_type`、`model`、`prompt`、`aspect_ratio`、`provider`。
|
||||
|
||||
## 产物保存位置
|
||||
|
||||
如果你的后端返回 base64 数据,使用 `save_b64_video()` 将其写入 `$HERMES_HOME/cache/videos/`。对于通过后续 HTTP 请求获取的原始字节,使用 `save_bytes_video()`。否则直接返回上游 URL——gateway 在交付时会解析远程 URL。
|
||||
|
||||
## 测试
|
||||
|
||||
在 `tests/plugins/video_gen/test_<name>_plugin.py` 下添加冒烟测试。xAI 和 FAL 的测试展示了标准模式——注册、验证目录、分别在传入和不传入 `image_url` 的情况下测试路由,并断言在缺少认证时返回干净的错误响应。
|
||||
@ -0,0 +1,265 @@
|
||||
---
|
||||
sidebar_position: 12
|
||||
title: "网页搜索提供商插件"
|
||||
description: "如何为 Hermes Agent 构建网页搜索/提取/爬取后端插件"
|
||||
---
|
||||
|
||||
# 构建网页搜索提供商插件
|
||||
|
||||
网页搜索提供商插件注册一个后端,用于处理 `web_search`、`web_extract` 以及(可选的)深度爬取工具调用。内置提供商——Firecrawl、SearXNG、Tavily、Exa、Parallel、Brave Search(免费层)和 DDGS——均以插件形式存放于 `plugins/web/<name>/` 目录下。你可以在该目录旁新建一个目录来添加新提供商,或覆盖已有的内置提供商。
|
||||
|
||||
:::tip
|
||||
网页搜索是 Hermes 支持的多种**后端插件**之一。其他插件(各有其 ABC)包括:[图像生成提供商插件](/developer-guide/image-gen-provider-plugin)、[视频生成提供商插件](/developer-guide/video-gen-provider-plugin)、[记忆提供商插件](/developer-guide/memory-provider-plugin)、[上下文引擎插件](/developer-guide/context-engine-plugin)和[模型提供商插件](/developer-guide/model-provider-plugin)。通用工具/hook/CLI 插件请参阅[构建 Hermes 插件](/guides/build-a-hermes-plugin)。
|
||||
:::
|
||||
|
||||
## 发现机制
|
||||
|
||||
Hermes 在三个位置扫描网页搜索后端:
|
||||
|
||||
1. **内置** — `<repo>/plugins/web/<name>/`(以 `kind: backend` 自动加载,始终可用)
|
||||
2. **用户** — `~/.hermes/plugins/web/<name>/`(通过 `plugins.enabled` 或 `hermes plugins enable <name>` 按需启用)
|
||||
3. **Pip** — 声明了 `hermes_agent.plugins` 入口点的包
|
||||
|
||||
每个插件的 `register(ctx)` 函数调用 `ctx.register_web_search_provider(...)` ——将实例注册到 `agent/web_search_registry.py` 中的注册表。各能力的活跃提供商由配置决定:
|
||||
|
||||
| 能力 | 配置键 | 回退至 |
|
||||
|---|---|---|
|
||||
| `web_search` | `web.search_backend` | `web.backend` |
|
||||
| `web_extract` | `web.extract_backend` | `web.backend` |
|
||||
| `web_extract` 内的深度爬取模式 | `web.extract_backend` | `web.backend` |
|
||||
|
||||
若两个键均未设置,Hermes 将根据环境中存在的 API key/URL 自动检测后端。`hermes tools` 会引导用户完成选择。
|
||||
|
||||
## 目录结构
|
||||
|
||||
```
|
||||
plugins/web/my-backend/
|
||||
├── __init__.py # register() 入口点
|
||||
├── provider.py # WebSearchProvider 子类
|
||||
└── plugin.yaml # 包含 kind: backend 和 provides_web_providers 的清单文件
|
||||
```
|
||||
|
||||
`brave_free/` 和 `ddgs/` 是代码库中最小的参考实现——`brave_free` 是需要 API key 的纯搜索提供商,`ddgs` 是无需 key 且懒加载 SDK 的提供商。
|
||||
|
||||
## WebSearchProvider ABC
|
||||
|
||||
继承 `agent.web_search_provider.WebSearchProvider`。唯一必须实现的成员是 `name`、`is_available()`,以及你所实现的 `search()` / `extract()` / `crawl()` 中的相应方法。
|
||||
|
||||
```python
|
||||
# plugins/web/my-backend/provider.py
|
||||
from __future__ import annotations
|
||||
|
||||
import os
|
||||
from typing import Any, Dict, List
|
||||
|
||||
from agent.web_search_provider import WebSearchProvider
|
||||
|
||||
|
||||
class MyBackendWebSearchProvider(WebSearchProvider):
|
||||
"""Minimal search-only provider against the My Backend HTTP API."""
|
||||
|
||||
@property
|
||||
def name(self) -> str:
|
||||
# Stable id used in web.search_backend / web.extract_backend / web.backend
|
||||
# config keys. Lowercase, no spaces; hyphens permitted.
|
||||
return "my-backend"
|
||||
|
||||
@property
|
||||
def display_name(self) -> str:
|
||||
# Human label shown in `hermes tools`. Defaults to `name`.
|
||||
return "My Backend"
|
||||
|
||||
def is_available(self) -> bool:
|
||||
# Cheap check — env var present, optional dep importable, etc.
|
||||
# MUST NOT make network calls (runs on every `hermes tools` paint).
|
||||
return bool(os.getenv("MY_BACKEND_API_KEY", "").strip())
|
||||
|
||||
def supports_search(self) -> bool:
|
||||
return True
|
||||
|
||||
def supports_extract(self) -> bool:
|
||||
return False
|
||||
|
||||
def supports_crawl(self) -> bool:
|
||||
return False
|
||||
|
||||
def search(self, query: str, limit: int = 5) -> Dict[str, Any]:
|
||||
import httpx
|
||||
|
||||
api_key = os.environ["MY_BACKEND_API_KEY"]
|
||||
try:
|
||||
resp = httpx.get(
|
||||
"https://api.example.com/search",
|
||||
params={"q": query, "count": max(1, min(int(limit), 20))},
|
||||
headers={"Authorization": f"Bearer {api_key}"},
|
||||
timeout=15,
|
||||
)
|
||||
resp.raise_for_status()
|
||||
data = resp.json()
|
||||
except httpx.HTTPError as exc:
|
||||
return {"success": False, "error": str(exc)}
|
||||
|
||||
# Response shape is fixed — see "Response shape" below.
|
||||
return {
|
||||
"success": True,
|
||||
"data": {
|
||||
"web": [
|
||||
{
|
||||
"title": item.get("title", ""),
|
||||
"url": item.get("url", ""),
|
||||
"description": item.get("snippet", ""),
|
||||
"position": idx + 1,
|
||||
}
|
||||
for idx, item in enumerate(data.get("results", []))
|
||||
],
|
||||
},
|
||||
}
|
||||
```
|
||||
|
||||
```python
|
||||
# plugins/web/my-backend/__init__.py
|
||||
from plugins.web.my_backend.provider import MyBackendWebSearchProvider
|
||||
|
||||
|
||||
def register(ctx) -> None:
|
||||
"""Plugin entry point — called once at load time."""
|
||||
ctx.register_web_search_provider(MyBackendWebSearchProvider())
|
||||
```
|
||||
|
||||
## plugin.yaml
|
||||
|
||||
```yaml
|
||||
name: web-my-backend
|
||||
version: 1.0.0
|
||||
description: "My Backend web search — Bearer-auth REST API"
|
||||
author: Your Name
|
||||
kind: backend
|
||||
provides_web_providers:
|
||||
- my-backend
|
||||
requires_env:
|
||||
- MY_BACKEND_API_KEY
|
||||
```
|
||||
|
||||
| 键 | 用途 |
|
||||
|---|---|
|
||||
| `kind: backend` | 将插件路由至后端加载路径 |
|
||||
| `provides_web_providers` | 该插件注册的提供商 `name` 列表——在 `register()` 运行之前,加载器即可通过此字段在 `hermes tools` 中公示插件 |
|
||||
| `requires_env` | 在 `hermes plugins install` 期间进行交互式凭据提示(富格式说明参见[构建 Hermes 插件](/guides/build-a-hermes-plugin#gate-on-environment-variables)) |
|
||||
|
||||
## ABC 参考
|
||||
|
||||
完整契约位于 `agent/web_search_provider.py`。可覆盖的方法如下:
|
||||
|
||||
| 成员 | 必须 | 默认值 | 用途 |
|
||||
|---|---|---|---|
|
||||
| `name` | ✅ | — | 在 `web.*_backend` 配置中使用的稳定 id |
|
||||
| `display_name` | — | `name` | 在 `hermes tools` 中显示的标签 |
|
||||
| `is_available()` | ✅ | — | 轻量可用性检查——环境变量、可选依赖等 |
|
||||
| `supports_search()` | — | `True` | `web_search` 路由的能力标志 |
|
||||
| `supports_extract()` | — | `False` | `web_extract` 路由的能力标志 |
|
||||
| `supports_crawl()` | — | `False` | 深度爬取模式的能力标志 |
|
||||
| `search(query, limit)` | 条件必须 | 抛出异常 | 当 `supports_search()` 返回 `True` 时必须实现 |
|
||||
| `extract(urls, **kwargs)` | 条件必须 | 抛出异常 | 当 `supports_extract()` 返回 `True` 时必须实现 |
|
||||
| `crawl(url, **kwargs)` | 条件必须 | 抛出异常 | 当 `supports_crawl()` 返回 `True` 时必须实现 |
|
||||
|
||||
提供商可以在单个类中声明多种能力——Firecrawl、Tavily、Exa 和 Parallel 均实现了搜索/提取/爬取三种能力。Brave Search 和 DDGS 仅支持搜索;SearXNG 也仅支持搜索,并有文档说明的"与提取提供商配对使用"工作流。
|
||||
|
||||
## 响应格式
|
||||
|
||||
工具包装器期望固定的响应信封(envelope),以避免在不同后端之间进行转换。
|
||||
|
||||
**搜索成功:**
|
||||
|
||||
```python
|
||||
{
|
||||
"success": True,
|
||||
"data": {
|
||||
"web": [
|
||||
{"title": str, "url": str, "description": str, "position": int},
|
||||
...
|
||||
],
|
||||
},
|
||||
}
|
||||
```
|
||||
|
||||
**提取成功:**
|
||||
|
||||
```python
|
||||
{
|
||||
"success": True,
|
||||
"data": [
|
||||
{
|
||||
"url": str,
|
||||
"title": str,
|
||||
"content": str,
|
||||
"raw_content": str,
|
||||
"metadata": dict, # optional
|
||||
"error": str, # optional, only on per-URL failure
|
||||
},
|
||||
...
|
||||
],
|
||||
}
|
||||
```
|
||||
|
||||
**任意能力,失败时:**
|
||||
|
||||
```python
|
||||
{"success": False, "error": "human-readable message"}
|
||||
```
|
||||
|
||||
`search()` 和 `extract()` 均可定义为 `async def`——调度器通过 `inspect.iscoroutinefunction` 检测协程函数并相应地进行 await。对于小型后端,执行阻塞 I/O(HTTP、SDK 调用)的同步实现也完全可行;调度器会处理线程调度。
|
||||
|
||||
## 能力标志
|
||||
|
||||
Hermes 根据 `supports_*` 标志将调用路由至正确的提供商。一种常见的多提供商配置:
|
||||
|
||||
```yaml
|
||||
# ~/.hermes/config.yaml
|
||||
web:
|
||||
search_backend: "brave-free" # 纯搜索,速度快,每月免费 2k 次
|
||||
extract_backend: "firecrawl" # 提取 + 爬取,付费配额
|
||||
```
|
||||
|
||||
当 `web.search_backend` 或 `web.extract_backend` 未设置时,均回退至 `web.backend`。若该项也未设置,Hermes 将根据环境变量的存在情况,选取第一个支持所请求能力的可用提供商。
|
||||
|
||||
如果你的提供商只支持一种能力,将其他标志保持默认值(`False`)即可,注册表会在对应工具调用时跳过它——当用户仅将 X 用于搜索而要求 agent 进行提取时,不会看到误导性的"提供商 X 失败"错误。
|
||||
|
||||
## Hermes 如何将其接入工具
|
||||
|
||||
`web_search` 和 `web_extract` 工具位于 `tools/web_tools.py`。调用时执行以下步骤:
|
||||
|
||||
1. 读取相关配置键(`web_search` 对应 `web.search_backend`,`web_extract` 对应 `web.extract_backend`)
|
||||
2. 向注册表查询具有该 `name` 的提供商
|
||||
3. 检查 `is_available()` 及对应的 `supports_*()` 标志
|
||||
4. 调度至 `search()` / `extract()` / `crawl()`,若方法为协程则进行 await
|
||||
5. 将响应信封 JSON 序列化后返回给 LLM
|
||||
|
||||
错误以工具结果的形式呈现;LLM 决定如何解释。若没有提供商被注册(或所有可用提供商均未通过能力检查),工具将返回一条指向 `hermes tools` 的友好错误信息。
|
||||
|
||||
## 懒加载可选依赖
|
||||
|
||||
如果你的提供商封装了第三方 SDK(如 DDGS 封装了 `ddgs` 包),请勿在模块顶层 `import`。在 `is_available()` 或 `search()` 内部使用 `tools.lazy_deps.ensure(...)` ——Hermes 将在首次使用时安装该包,并受 `security.allow_lazy_installs` 控制。安全模型详见[构建 Hermes 插件 → 懒加载](/guides/build-a-hermes-plugin#lazy-install-optional-python-dependencies)。
|
||||
|
||||
## 参考实现
|
||||
|
||||
- **`plugins/web/brave_free/`** — 小型、需要 API key 的纯搜索 HTTP 提供商。适合作为起始模板。
|
||||
- **`plugins/web/ddgs/`** — 无需 key、懒加载 SDK 的提供商。适用于封装 Python 包的后端。
|
||||
- **`plugins/web/firecrawl/`** — 完整的多能力提供商(搜索 + 提取 + 爬取),支持多种格式模式。
|
||||
- **`plugins/web/searxng/`** — 自托管、通过 URL 配置、无需认证的后端。
|
||||
- **`plugins/web/xai/`** — 通过 Grok 服务端 `web_search` 工具实现的 LLM 驱动搜索。展示了如何复用现有的 OAuth/环境变量凭据(`tools/xai_http.py`)而无需新增环境变量,以及如何编写遵守无网络调用约定的轻量 `is_available()`。
|
||||
|
||||
## 通过 pip 分发
|
||||
|
||||
```toml
|
||||
# pyproject.toml
|
||||
[project.entry-points."hermes_agent.plugins"]
|
||||
my-backend-web = "my_backend_web_package"
|
||||
```
|
||||
|
||||
`my_backend_web_package` 必须暴露顶层 `register` 函数。完整配置说明参见通用插件指南中的[通过 pip 分发](/guides/build-a-hermes-plugin#distribute-via-pip)。
|
||||
|
||||
## 相关页面
|
||||
|
||||
- [网页搜索](/user-guide/features/web-search) — 面向用户的功能文档及各后端配置说明
|
||||
- [插件概览](/user-guide/features/plugins) — 所有插件类型一览
|
||||
- [构建 Hermes 插件](/guides/build-a-hermes-plugin) — 通用工具/hook/斜杠命令指南
|
||||
@ -0,0 +1,201 @@
|
||||
---
|
||||
sidebar_position: 2
|
||||
title: "安装"
|
||||
description: "在 Linux、macOS、WSL2、原生 Windows(早期 Beta)或通过 Termux 在 Android 上安装 Hermes Agent"
|
||||
---
|
||||
|
||||
# 安装
|
||||
|
||||
使用一行安装命令,两分钟内即可启动并运行 Hermes Agent。
|
||||
|
||||
## 快速安装
|
||||
|
||||
### 一行安装命令(Linux / macOS / WSL2)
|
||||
|
||||
基于 git 的安装方式,跟踪 `main` 分支,可立即获取最新变更:
|
||||
|
||||
```bash
|
||||
curl -fsSL https://raw.githubusercontent.com/NousResearch/hermes-agent/main/scripts/install.sh | bash
|
||||
```
|
||||
|
||||
### Windows(原生,PowerShell)— 早期 Beta
|
||||
|
||||
:::warning 早期 BETA
|
||||
原生 Windows 支持处于**早期 beta** 阶段。常见路径下可正常安装和运行,但尚未像我们的 POSIX 安装程序那样经过广泛测试。遇到问题请[提交 issue](https://github.com/NousResearch/hermes-agent/issues)。目前在 Windows 上最稳定的方案是在 **WSL2** 内使用上方的 Linux/macOS 一行命令。
|
||||
:::
|
||||
|
||||
打开 PowerShell 并运行:
|
||||
|
||||
```powershell
|
||||
iex (irm https://raw.githubusercontent.com/NousResearch/hermes-agent/main/scripts/install.ps1)
|
||||
```
|
||||
|
||||
安装程序处理**一切**:`uv`、Python 3.11、Node.js 22、`ripgrep`、`ffmpeg`,**以及一个便携式 Git Bash**(PortableGit——一个自包含的 Git-for-Windows 发行版,附带 `bash.exe` 和 Hermes 用于 shell 命令的完整 POSIX 工具链;在 32 位 Windows 上安装程序会回退到 MinGit,后者缺少 bash,终端工具和 agent 浏览器功能将被禁用)。它将仓库克隆到 `%LOCALAPPDATA%\hermes\hermes-agent`,创建虚拟环境,并将 `hermes` 添加到**用户 PATH**。安装完成后请重启终端(或打开新的 PowerShell 窗口)以使 PATH 生效。
|
||||
|
||||
**Git 的处理方式:**
|
||||
1. 如果 `git` 已在你的 PATH 中,安装程序将使用现有安装。
|
||||
2. 否则,它会下载便携式 **PortableGit**(约 50MB,来自官方 `git-for-windows` GitHub 发布页)并解压到 `%LOCALAPPDATA%\hermes\git`。无需管理员权限,完全隔离——不会干扰任何系统 Git 安装,无论其状态如何。(在 32 位 Windows 上会回退到 MinGit,因为 PortableGit 仅提供 64 位和 ARM64 资产;依赖 bash 的 Hermes 功能在 32 位主机上无法使用。)
|
||||
|
||||
**为什么不使用 winget?** 早期设计通过 `winget install Git.Git` 自动安装 Git,但当系统 Git 安装处于部分损坏状态时,winget 会严重失败(而这恰恰是用户最需要安装程序正常工作的时候)。便携式 Git 方案绕过了 winget、Windows 安装程序注册表以及任何现有系统 Git。如果 Hermes 的 Git 安装本身出现问题,执行 `Remove-Item %LOCALAPPDATA%\hermes\git` 并重新运行安装程序即可——对系统无影响,无需卸载操作。
|
||||
|
||||
安装程序还会将 `HERMES_GIT_BASH_PATH` 设置为找到的 `bash.exe` 路径,以便 Hermes 在新 shell 中确定性地解析它。
|
||||
|
||||
如果你偏好 WSL2,上方的 Linux 安装程序可在其中运行;原生安装和 WSL 安装可以共存而不冲突(原生数据位于 `%LOCALAPPDATA%\hermes`,WSL 数据位于 `~/.hermes`)。
|
||||
|
||||
**桌面安装程序(替代方案):** 也提供一个轻量 GUI 安装程序——下载 Hermes Desktop,运行 `.exe`,首次启动时它会在后台调用 `install.ps1` 来配置 Python(通过 `uv`)、Node、PortableGit 及其余依赖。桌面应用和 PowerShell 安装的 CLI 共享相同的安装目录和数据目录,可以单独或同时使用。详见 [Windows(原生)指南](../user-guide/windows-native#desktop-installer-alternative)。
|
||||
|
||||
### Android / Termux
|
||||
|
||||
Hermes 现在也提供 Termux 感知的安装路径:
|
||||
|
||||
```bash
|
||||
curl -fsSL https://raw.githubusercontent.com/NousResearch/hermes-agent/main/scripts/install.sh | bash
|
||||
```
|
||||
|
||||
安装程序会自动检测 Termux 并切换到经过测试的 Android 流程:
|
||||
- 使用 Termux `pkg` 安装系统依赖(`git`、`python`、`nodejs`、`ripgrep`、`ffmpeg`、构建工具)
|
||||
- 使用 `python -m venv` 创建虚拟环境
|
||||
- 自动导出 `ANDROID_API_LEVEL` 以用于 Android wheel 构建
|
||||
- 优先使用较宽泛的 `.[termux-all]` extra,若首次编译失败则回退到较小的 `.[termux]` extra(最终回退到基础安装)
|
||||
- 默认跳过未经测试的浏览器 / WhatsApp 引导
|
||||
|
||||
如需完整的显式步骤,请参阅专门的 [Termux 指南](./termux.md)。
|
||||
|
||||
:::note Windows 功能对等性(早期 Beta)
|
||||
|
||||
原生 Windows 处于**早期 beta** 阶段。除基于浏览器的 dashboard 聊天终端外,其余功能均可在 Windows 上原生运行:
|
||||
- **CLI(`hermes chat`、`hermes setup`、`hermes gateway` 等)** — 原生,使用默认终端
|
||||
- **Gateway(Telegram、Discord、Slack 等)** — 原生,作为后台 PowerShell 进程运行
|
||||
- **Cron 调度器** — 原生
|
||||
- **浏览器工具** — 原生(通过 Node.js 使用 Chromium)
|
||||
- **MCP 服务器** — 原生(stdio 和 HTTP 传输均支持)
|
||||
- **Dashboard `/chat` 终端面板** — **仅限 WSL2**(使用 POSIX PTY(伪终端),原生 Windows 无等效实现)。Dashboard 的其余部分(会话、任务、指标)可原生运行——仅嵌入式 PTY 终端标签页受限。
|
||||
|
||||
如果遇到编码相关的 bug 并希望回退到旧版 cp1252 stdio 路径(用于问题定位),请在环境中设置 `HERMES_DISABLE_WINDOWS_UTF8=1`。
|
||||
:::
|
||||
|
||||
### 安装程序做了什么
|
||||
|
||||
安装程序自动处理一切——所有依赖(Python、Node.js、ripgrep、ffmpeg)、仓库克隆、虚拟环境、全局 `hermes` 命令配置以及 LLM 提供商配置。完成后即可开始聊天。
|
||||
|
||||
#### 安装目录结构
|
||||
|
||||
安装程序的存放位置取决于你是以普通用户还是 root 身份安装:
|
||||
|
||||
| 安装方式 | 代码位置 | `hermes` 二进制 | 数据目录 |
|
||||
|---|---|---|---|
|
||||
| pip install | Python site-packages | `~/.local/bin/hermes`(console_scripts) | `~/.hermes/` |
|
||||
| 用户级(git 安装程序) | `~/.hermes/hermes-agent/` | `~/.local/bin/hermes`(符号链接) | `~/.hermes/` |
|
||||
| Root 模式(`sudo curl … \| sudo bash`) | `/usr/local/lib/hermes-agent/` | `/usr/local/bin/hermes` | `/root/.hermes/`(或 `$HERMES_HOME`) |
|
||||
|
||||
Root 模式的 **FHS 布局**(`/usr/local/lib/…`、`/usr/local/bin/hermes`)与其他系统级开发工具在 Linux 上的安装位置一致。适用于共享机器部署场景,一次系统安装可服务所有用户。每个用户的个人配置(认证、技能、会话)仍位于各自的 `~/.hermes/` 或显式指定的 `HERMES_HOME` 下。
|
||||
|
||||
### 安装后
|
||||
|
||||
重新加载 shell 并开始聊天:
|
||||
|
||||
```bash
|
||||
source ~/.bashrc # 或:source ~/.zshrc
|
||||
hermes # 开始聊天!
|
||||
```
|
||||
|
||||
如需稍后重新配置单项设置,使用以下专用命令:
|
||||
|
||||
```bash
|
||||
hermes model # 选择 LLM 提供商和模型
|
||||
hermes tools # 配置启用的工具
|
||||
hermes gateway setup # 配置消息平台
|
||||
hermes config set # 设置单个配置项
|
||||
hermes setup # 或运行完整的设置向导一次性配置所有内容
|
||||
```
|
||||
|
||||
:::tip 最快路径:Nous Portal
|
||||
一个订阅涵盖 300+ 个模型以及 [Tool Gateway](/user-guide/features/tool-gateway)(网络搜索、图像生成、TTS、云端浏览器)。无需逐一管理各工具的密钥:
|
||||
|
||||
```bash
|
||||
hermes setup --portal
|
||||
```
|
||||
|
||||
该命令一次性完成登录、设置 Nous 为提供商并开启 Tool Gateway。
|
||||
:::
|
||||
|
||||
---
|
||||
|
||||
## 前置条件
|
||||
|
||||
**pip install:** 除 Python 3.11+ 外无其他前置条件,其余均自动处理。
|
||||
|
||||
**Git 安装程序:** 唯一的前置条件是 **Git**。安装程序自动处理其余一切:
|
||||
|
||||
- **uv**(快速 Python 包管理器)
|
||||
- **Python 3.11**(通过 uv,无需 sudo)
|
||||
- **Node.js v22**(用于浏览器自动化和 WhatsApp 桥接)
|
||||
- **ripgrep**(快速文件搜索)
|
||||
- **ffmpeg**(TTS 的音频格式转换)
|
||||
|
||||
:::info
|
||||
你**无需**手动安装 Python、Node.js、ripgrep 或 ffmpeg。安装程序会检测缺失的依赖并自动安装。只需确保 `git` 可用(`git --version`)。
|
||||
:::
|
||||
|
||||
:::tip Nix 用户
|
||||
如果你使用 Nix(在 NixOS、macOS 或 Linux 上),有专门的配置路径,包含 Nix flake、声明式 NixOS 模块和可选容器模式。请参阅 **[Nix & NixOS 配置](./nix-setup.md)** 指南。
|
||||
:::
|
||||
|
||||
---
|
||||
|
||||
## 手动 / 开发者安装
|
||||
|
||||
如果你想克隆仓库并从源码安装——用于贡献代码、从特定分支运行或完全控制虚拟环境——请参阅贡献指南中的[开发环境配置](../developer-guide/contributing.md#development-setup)章节。
|
||||
|
||||
---
|
||||
|
||||
## 非 Sudo / 系统服务用户安装
|
||||
|
||||
支持以专用非特权用户身份运行 Hermes(例如 `hermes` systemd 服务账户,或任何没有 `sudo` 权限的用户)。安装路径中真正需要 root 权限的只有 Playwright 的 `--with-deps` 步骤,该步骤通过 `apt` 安装 Chromium 所需的共享库(`libnss3`、`libxkbcommon` 等)。安装程序会检测 sudo 是否可用,并在不可用时优雅降级——它会将 Chromium 二进制安装到服务用户自己的 Playwright 缓存中,并打印管理员需要单独运行的确切命令。
|
||||
|
||||
**推荐的分步方式(Debian/Ubuntu):**
|
||||
|
||||
1. **一次性操作,以具有 sudo 权限的管理员用户身份**,安装 Chromium 所需的系统库:
|
||||
```bash
|
||||
sudo npx playwright install-deps chromium
|
||||
```
|
||||
(可在任意位置运行——`npx` 会自动获取 Playwright。)
|
||||
|
||||
2. **以非特权服务用户身份**,运行常规安装程序。它会检测到缺少 sudo,跳过 `--with-deps`,并将 Chromium 安装到用户本地的 Playwright 缓存中:
|
||||
```bash
|
||||
curl -fsSL https://raw.githubusercontent.com/NousResearch/hermes-agent/main/scripts/install.sh | bash
|
||||
```
|
||||
|
||||
如果想完全跳过 Playwright 步骤——例如在无头环境中运行且不需要浏览器自动化——传入 `--skip-browser`:
|
||||
```bash
|
||||
curl -fsSL https://raw.githubusercontent.com/NousResearch/hermes-agent/main/scripts/install.sh | bash -s -- --skip-browser
|
||||
```
|
||||
|
||||
3. **使 `hermes` 对服务用户的 shell 可用。** 安装程序将启动器写入 `~/.local/bin/hermes`。系统服务账户通常具有不包含 `~/.local/bin` 的最小 PATH。可以将其添加到用户环境,或将启动器符号链接到系统位置:
|
||||
```bash
|
||||
# 方案 A — 添加到服务用户的 profile
|
||||
echo 'export PATH="$HOME/.local/bin:$PATH"' >> ~/.bashrc
|
||||
|
||||
# 方案 B — 系统级符号链接(以管理员身份运行)
|
||||
sudo ln -s /home/hermes/.hermes/hermes-agent/venv/bin/hermes /usr/local/bin/hermes
|
||||
```
|
||||
|
||||
4. **验证:** `hermes doctor` 现在应能正常运行。如果出现 `ModuleNotFoundError: No module named 'dotenv'`,说明你在用系统 Python 调用仓库源码中的 `hermes` 文件(`~/.hermes/hermes-agent/hermes`),而非 venv 启动器(`~/.hermes/hermes-agent/venv/bin/hermes`)——请修正步骤 3。
|
||||
|
||||
同样的方式适用于 Arch(安装程序使用 pacman,具有相同的 sudo 检测逻辑)、Fedora/RHEL 和 openSUSE——这些发行版完全不支持 `--with-deps`,因此管理员始终需要单独安装系统库。安装程序会打印相应的 `dnf`/`zypper` 命令。
|
||||
|
||||
---
|
||||
|
||||
## 故障排查
|
||||
|
||||
| 问题 | 解决方案 |
|
||||
|---------|----------|
|
||||
| `hermes: command not found` | 重新加载 shell(`source ~/.bashrc`)或检查 PATH |
|
||||
| `API key not set` | 运行 `hermes model` 配置提供商,或 `hermes config set OPENROUTER_API_KEY your_key` |
|
||||
| 更新后配置丢失 | 运行 `hermes config check`,然后运行 `hermes config migrate` |
|
||||
|
||||
如需更多诊断信息,运行 `hermes doctor`——它会告诉你确切缺少什么以及如何修复。
|
||||
|
||||
## 安装方式自动检测
|
||||
|
||||
Hermes 会自动检测安装方式(`pip`、git 安装程序、Homebrew 或 NixOS),`hermes update` 会打印对应路径的更新命令。无需设置任何环境变量——检测基于安装目录结构(Python site-packages、`~/.hermes/hermes-agent/`、Homebrew 前缀或 Nix store 路径)。`hermes doctor` 也会在其环境摘要中显示检测到的安装方式。
|
||||
@ -0,0 +1,154 @@
|
||||
---
|
||||
sidebar_position: 3
|
||||
title: '学习路径'
|
||||
description: '根据您的经验水平和目标,选择适合您的 Hermes Agent 文档学习路径。'
|
||||
---
|
||||
|
||||
# 学习路径
|
||||
|
||||
Hermes Agent 功能丰富——CLI 助手、Telegram/Discord 机器人、任务自动化、强化学习训练等。本页帮助您根据自身经验水平和目标,确定从哪里开始、阅读哪些内容。
|
||||
|
||||
:::tip 从这里开始
|
||||
如果您尚未安装 Hermes Agent,请先阅读[安装指南](/getting-started/installation),然后完成[快速入门](/getting-started/quickstart)。以下内容均假设您已完成安装。
|
||||
:::
|
||||
|
||||
## 如何使用本页
|
||||
|
||||
- **已知自己的水平?** 跳转至[按经验水平](#by-experience-level)表格,按照对应层级的阅读顺序进行。
|
||||
- **有明确目标?** 跳至[按使用场景](#by-use-case),找到匹配的场景。
|
||||
- **随便浏览?** 查看[主要功能](#key-features-at-a-glance)表格,快速了解 Hermes Agent 的全部能力。
|
||||
|
||||
## 按经验水平
|
||||
|
||||
| 水平 | 目标 | 推荐阅读 | 预计时间 |
|
||||
|---|---|---|---|
|
||||
| **初级** | 快速上手,进行基本对话,使用内置工具 | [安装](/getting-started/installation) → [快速入门](/getting-started/quickstart) → [CLI 用法](/user-guide/cli) → [配置](/user-guide/configuration) | 约 1 小时 |
|
||||
| **中级** | 搭建消息机器人,使用记忆、cron 任务、技能等高级功能 | [会话](/user-guide/sessions) → [消息](/user-guide/messaging) → [工具](/user-guide/features/tools) → [技能](/user-guide/features/skills) → [记忆](/user-guide/features/memory) → [Cron](/user-guide/features/cron) | 约 2–3 小时 |
|
||||
| **高级** | 构建自定义工具、创建技能、使用强化学习训练模型、参与项目贡献 | [架构](/developer-guide/architecture) → [添加工具](/developer-guide/adding-tools) → [创建技能](/developer-guide/creating-skills) → [强化学习训练](/user-guide/features/rl-training) → [贡献指南](/developer-guide/contributing) | 约 4–6 小时 |
|
||||
|
||||
## 按使用场景
|
||||
|
||||
选择与您目标匹配的场景,每个场景均按推荐顺序链接到相关文档。
|
||||
|
||||
### "我想要一个 CLI 编程助手"
|
||||
|
||||
将 Hermes Agent 用作交互式终端助手,用于编写、审查和运行代码。
|
||||
|
||||
1. [安装](/getting-started/installation)
|
||||
2. [快速入门](/getting-started/quickstart)
|
||||
3. [CLI 用法](/user-guide/cli)
|
||||
4. [代码执行](/user-guide/features/code-execution)
|
||||
5. [上下文文件](/user-guide/features/context-files)
|
||||
6. [技巧与窍门](/guides/tips)
|
||||
|
||||
:::tip
|
||||
通过上下文文件将文件直接传入对话。Hermes Agent 可以读取、编辑并运行您项目中的代码。
|
||||
:::
|
||||
|
||||
### "我想要一个 Telegram/Discord 机器人"
|
||||
|
||||
将 Hermes Agent 部署为您常用消息平台上的机器人。
|
||||
|
||||
1. [安装](/getting-started/installation)
|
||||
2. [配置](/user-guide/configuration)
|
||||
3. [消息概览](/user-guide/messaging)
|
||||
4. [Telegram 配置](/user-guide/messaging/telegram)
|
||||
5. [Discord 配置](/user-guide/messaging/discord)
|
||||
6. [语音模式](/user-guide/features/voice-mode)
|
||||
7. [在 Hermes 中使用语音模式](/guides/use-voice-mode-with-hermes)
|
||||
8. [安全](/user-guide/security)
|
||||
|
||||
完整项目示例请参阅:
|
||||
- [每日简报机器人](/guides/daily-briefing-bot)
|
||||
- [团队 Telegram 助手](/guides/team-telegram-assistant)
|
||||
|
||||
### "我想自动化任务"
|
||||
|
||||
调度周期性任务、运行批处理作业,或将多个 agent 动作串联起来。
|
||||
|
||||
1. [快速入门](/getting-started/quickstart)
|
||||
2. [Cron 调度](/user-guide/features/cron)
|
||||
3. [批处理](/user-guide/features/batch-processing)
|
||||
4. [委派](/user-guide/features/delegation)
|
||||
5. [Hooks](/user-guide/features/hooks)
|
||||
|
||||
:::tip
|
||||
Cron 任务让 Hermes Agent 按计划执行任务——每日摘要、定期检查、自动报告——无需您在场。
|
||||
:::
|
||||
|
||||
### "我想构建自定义工具/技能"
|
||||
|
||||
通过自定义工具和可复用技能包扩展 Hermes Agent。
|
||||
|
||||
1. [插件](/user-guide/features/plugins)
|
||||
2. [构建 Hermes 插件](/guides/build-a-hermes-plugin)
|
||||
3. [工具概览](/user-guide/features/tools)
|
||||
4. [技能概览](/user-guide/features/skills)
|
||||
5. [MCP(模型上下文协议)](/user-guide/features/mcp)
|
||||
6. [架构](/developer-guide/architecture)
|
||||
7. [添加工具](/developer-guide/adding-tools)
|
||||
8. [创建技能](/developer-guide/creating-skills)
|
||||
|
||||
:::tip
|
||||
对于大多数自定义工具的创建,建议从插件开始。[添加工具](/developer-guide/adding-tools)页面面向 Hermes 核心内置开发,而非常规用户/自定义工具路径。
|
||||
:::
|
||||
|
||||
### "我想训练模型"
|
||||
|
||||
使用强化学习(RL)通过 Hermes Agent 内置的 RL 训练流水线对模型行为进行微调。
|
||||
|
||||
1. [快速入门](/getting-started/quickstart)
|
||||
2. [配置](/user-guide/configuration)
|
||||
3. [强化学习训练](/user-guide/features/rl-training)
|
||||
4. [Provider 路由](/user-guide/features/provider-routing)
|
||||
5. [架构](/developer-guide/architecture)
|
||||
|
||||
:::tip
|
||||
强化学习训练在您已了解 Hermes Agent 如何处理对话和工具调用的基础上效果最佳。如果您是新手,请先完成初级路径。
|
||||
:::
|
||||
|
||||
### "我想将其作为 Python 库使用"
|
||||
|
||||
以编程方式将 Hermes Agent 集成到您自己的 Python 应用中。
|
||||
|
||||
1. [安装](/getting-started/installation)
|
||||
2. [快速入门](/getting-started/quickstart)
|
||||
3. [Python 库指南](/guides/python-library)
|
||||
4. [架构](/developer-guide/architecture)
|
||||
5. [工具](/user-guide/features/tools)
|
||||
6. [会话](/user-guide/sessions)
|
||||
|
||||
## 主要功能一览
|
||||
|
||||
不确定有哪些功能?以下是主要功能的快速目录:
|
||||
|
||||
| 功能 | 说明 | 链接 |
|
||||
|---|---|---|
|
||||
| **工具** | Agent 可调用的内置工具(文件 I/O、搜索、Shell 等) | [工具](/user-guide/features/tools) |
|
||||
| **技能** | 可安装的插件包,用于添加新能力 | [技能](/user-guide/features/skills) |
|
||||
| **记忆** | 跨会话的持久化记忆 | [记忆](/user-guide/features/memory) |
|
||||
| **上下文文件** | 将文件和目录传入对话 | [上下文文件](/user-guide/features/context-files) |
|
||||
| **MCP** | 通过模型上下文协议连接外部工具服务器 | [MCP](/user-guide/features/mcp) |
|
||||
| **Cron** | 调度周期性 agent 任务 | [Cron](/user-guide/features/cron) |
|
||||
| **委派** | 生成子 agent 以并行处理工作 | [委派](/user-guide/features/delegation) |
|
||||
| **代码执行** | 运行以编程方式调用 Hermes 工具的 Python 脚本 | [代码执行](/user-guide/features/code-execution) |
|
||||
| **浏览器** | 网页浏览与抓取 | [浏览器](/user-guide/features/browser) |
|
||||
| **Hooks** | 事件驱动的回调与中间件 | [Hooks](/user-guide/features/hooks) |
|
||||
| **批处理** | 批量处理多个输入 | [批处理](/user-guide/features/batch-processing) |
|
||||
| **强化学习训练** | 使用强化学习微调模型 | [强化学习训练](/user-guide/features/rl-training) |
|
||||
| **Provider 路由** | 在多个 LLM provider 之间路由请求 | [Provider 路由](/user-guide/features/provider-routing) |
|
||||
|
||||
## 下一步阅读
|
||||
|
||||
根据您当前所处阶段:
|
||||
|
||||
- **刚完成安装?** → 前往[快速入门](/getting-started/quickstart),运行您的第一次对话。
|
||||
- **完成了快速入门?** → 阅读 [CLI 用法](/user-guide/cli)和[配置](/user-guide/configuration),自定义您的设置。
|
||||
- **已熟悉基础?** → 探索[工具](/user-guide/features/tools)、[技能](/user-guide/features/skills)和[记忆](/user-guide/features/memory),释放 agent 的全部能力。
|
||||
- **为团队部署?** → 阅读[安全](/user-guide/security)和[会话](/user-guide/sessions),了解访问控制与对话管理。
|
||||
- **准备好开发了?** → 进入[开发者指南](/developer-guide/architecture),了解内部机制并开始贡献。
|
||||
- **想要实际示例?** → 查看[指南](/guides/tips)部分,获取真实项目案例和技巧。
|
||||
|
||||
:::tip
|
||||
您无需阅读所有内容。选择与您目标匹配的路径,按顺序跟随链接,即可快速上手。随时可以回到本页寻找下一步。
|
||||
:::
|
||||
@ -0,0 +1,975 @@
|
||||
---
|
||||
sidebar_position: 3
|
||||
title: "Nix & NixOS 安装配置"
|
||||
description: "使用 Nix 安装和部署 Hermes Agent——从快速 `nix run` 到完全声明式的 NixOS 模块(含容器模式)"
|
||||
---
|
||||
|
||||
# Nix & NixOS 安装配置
|
||||
|
||||
Hermes Agent 提供了一个 Nix flake,支持三个层级的集成:
|
||||
|
||||
| 层级 | 适用对象 | 提供内容 |
|
||||
|-------|-------------|--------------|
|
||||
| **`nix run` / `nix profile install`** | 任意 Nix 用户(macOS、Linux) | 包含所有依赖的预构建二进制文件——然后使用标准 CLI 工作流 |
|
||||
| **NixOS 模块(原生)** | NixOS 服务器部署 | 声明式配置、加固的 systemd 服务、托管密钥 |
|
||||
| **NixOS 模块(容器)** | 需要自我修改能力的 Agent | 以上所有功能,加上一个持久化 Ubuntu 容器,Agent 可在其中执行 `apt`/`pip`/`npm install` |
|
||||
|
||||
:::info 与标准安装的区别
|
||||
`curl | bash` 安装程序自行管理 Python、Node 及依赖项。Nix flake 替代了所有这些——每个 Python 依赖都是由 [uv2nix](https://github.com/pyproject-nix/uv2nix) 构建的 Nix derivation,运行时工具(Node.js、git、ripgrep、ffmpeg)已封装进二进制文件的 PATH 中。不需要运行时 pip,不需要激活 venv,不需要 `npm install`。
|
||||
|
||||
**对于非 NixOS 用户**,这只影响安装步骤。之后的操作(`hermes setup`、`hermes gateway install`、编辑配置)与标准安装完全相同。
|
||||
|
||||
**对于 NixOS 模块用户**,整个生命周期有所不同:配置存放在 `configuration.nix` 中,密钥通过 sops-nix/agenix 管理,服务是一个 systemd 单元,CLI 配置命令被屏蔽。管理 hermes 的方式与管理其他 NixOS 服务相同。
|
||||
:::
|
||||
|
||||
## 前提条件
|
||||
|
||||
- **已启用 flakes 的 Nix** — 推荐使用 [Determinate Nix](https://install.determinate.systems)(默认启用 flakes)
|
||||
- **API 密钥**,用于你想使用的服务(至少需要一个 OpenRouter 或 Anthropic 密钥)
|
||||
|
||||
---
|
||||
|
||||
## 快速开始(任意 Nix 用户)
|
||||
|
||||
无需克隆仓库。Nix 会自动获取、构建并运行所有内容:
|
||||
|
||||
```bash
|
||||
# 直接运行(首次使用时构建,之后使用缓存)
|
||||
nix run github:NousResearch/hermes-agent -- setup
|
||||
nix run github:NousResearch/hermes-agent -- chat
|
||||
|
||||
# 或持久化安装
|
||||
nix profile install github:NousResearch/hermes-agent
|
||||
hermes setup
|
||||
hermes chat
|
||||
```
|
||||
|
||||
执行 `nix profile install` 后,`hermes`、`hermes-agent` 和 `hermes-acp` 将出现在你的 PATH 中。之后的工作流与[标准安装](./installation.md)完全相同——`hermes setup` 引导你完成提供商选择,`hermes gateway install` 设置 launchd(macOS)或 systemd 用户服务,配置存放在 `~/.hermes/`。
|
||||
|
||||
<details>
|
||||
<summary><strong>从本地克隆构建</strong></summary>
|
||||
|
||||
```bash
|
||||
git clone https://github.com/NousResearch/hermes-agent.git
|
||||
cd hermes-agent
|
||||
nix build
|
||||
./result/bin/hermes setup
|
||||
```
|
||||
|
||||
</details>
|
||||
|
||||
---
|
||||
|
||||
## NixOS 模块
|
||||
|
||||
该 flake 导出 `nixosModules.default`——一个完整的 NixOS 服务模块,以声明式方式管理用户创建、目录、配置生成、密钥、文档和服务生命周期。
|
||||
|
||||
:::note
|
||||
此模块需要 NixOS。对于非 NixOS 系统(macOS、其他 Linux 发行版),请使用 `nix profile install` 和上述标准 CLI 工作流。
|
||||
:::
|
||||
|
||||
### 添加 Flake 输入
|
||||
|
||||
```nix
|
||||
# /etc/nixos/flake.nix(或你的系统 flake)
|
||||
{
|
||||
inputs = {
|
||||
nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable";
|
||||
hermes-agent.url = "github:NousResearch/hermes-agent";
|
||||
};
|
||||
|
||||
outputs = { nixpkgs, hermes-agent, ... }: {
|
||||
nixosConfigurations.your-host = nixpkgs.lib.nixosSystem {
|
||||
system = "x86_64-linux";
|
||||
modules = [
|
||||
hermes-agent.nixosModules.default
|
||||
./configuration.nix
|
||||
];
|
||||
};
|
||||
};
|
||||
}
|
||||
```
|
||||
|
||||
### 最小化配置
|
||||
|
||||
```nix
|
||||
# configuration.nix
|
||||
{ config, ... }: {
|
||||
services.hermes-agent = {
|
||||
enable = true;
|
||||
settings.model.default = "anthropic/claude-sonnet-4";
|
||||
environmentFiles = [ config.sops.secrets."hermes-env".path ];
|
||||
addToSystemPackages = true;
|
||||
};
|
||||
}
|
||||
```
|
||||
|
||||
就这些。`nixos-rebuild switch` 会创建 `hermes` 用户、生成 `config.yaml`、连接密钥并启动 gateway——这是一个长期运行的服务,将 Agent 连接到消息平台(Telegram、Discord 等)并监听传入消息。
|
||||
|
||||
:::warning 密钥是必需的
|
||||
上面的 `environmentFiles` 行假设你已配置 [sops-nix](https://github.com/Mic92/sops-nix) 或 [agenix](https://github.com/ryantm/agenix)。该文件至少应包含一个 LLM 提供商密钥(例如 `OPENROUTER_API_KEY=sk-or-...`)。完整设置请参阅[密钥管理](#secrets-management)。如果你还没有密钥管理器,可以先使用普通文件——只需确保它不是全局可读的:
|
||||
|
||||
```bash
|
||||
echo "OPENROUTER_API_KEY=sk-or-your-key" | sudo install -m 0600 -o hermes /dev/stdin /var/lib/hermes/env
|
||||
```
|
||||
|
||||
```nix
|
||||
services.hermes-agent.environmentFiles = [ "/var/lib/hermes/env" ];
|
||||
```
|
||||
:::
|
||||
|
||||
:::tip addToSystemPackages
|
||||
设置 `addToSystemPackages = true` 有两个作用:将 `hermes` CLI 添加到系统 PATH,**并**在系统范围内设置 `HERMES_HOME`,使交互式 CLI 与 gateway 服务共享状态(会话、技能、cron)。不设置此项时,在 shell 中运行 `hermes` 会创建独立的 `~/.hermes/` 目录。
|
||||
:::
|
||||
|
||||
### 容器感知 CLI
|
||||
|
||||
:::info
|
||||
当 `container.enable = true` 且 `addToSystemPackages = true` 时,主机上的**所有** `hermes` 命令都会自动路由到托管容器中执行。这意味着你的交互式 CLI 会话在与 gateway 服务相同的环境中运行——可以访问所有容器内安装的包和工具。
|
||||
|
||||
- 路由是透明的:`hermes chat`、`hermes sessions list`、`hermes version` 等命令都会在底层 exec 进容器
|
||||
- 所有 CLI 参数原样转发
|
||||
- 如果容器未运行,CLI 会短暂重试(交互式使用时显示 5 秒 spinner,脚本中静默等待 10 秒),然后以明确的错误退出——不会静默回退
|
||||
- 对于在 hermes 代码库上工作的开发者,设置 `HERMES_DEV=1` 可绕过容器路由,直接运行本地检出版本
|
||||
|
||||
设置 `container.hostUsers` 可创建 `~/.hermes` 到服务状态目录的符号链接,使主机 CLI 和容器共享会话、配置和记忆:
|
||||
|
||||
```nix
|
||||
services.hermes-agent = {
|
||||
container.enable = true;
|
||||
container.hostUsers = [ "your-username" ];
|
||||
addToSystemPackages = true;
|
||||
};
|
||||
```
|
||||
|
||||
`hostUsers` 中列出的用户会自动加入 `hermes` 组以获得文件权限访问。
|
||||
|
||||
**Podman 用户:** NixOS 服务以 root 身份运行容器。Docker 用户通过 `docker` 组 socket 获得访问权限,但 Podman 的 rootful 容器需要 sudo。为你的容器运行时授予免密 sudo:
|
||||
|
||||
```nix
|
||||
security.sudo.extraRules = [{
|
||||
users = [ "your-username" ];
|
||||
commands = [{
|
||||
command = "/run/current-system/sw/bin/podman";
|
||||
options = [ "NOPASSWD" ];
|
||||
}];
|
||||
}];
|
||||
```
|
||||
|
||||
CLI 会自动检测何时需要 sudo 并透明地使用它。没有此配置,你需要手动运行 `sudo hermes chat`。
|
||||
:::
|
||||
|
||||
### 验证运行状态
|
||||
|
||||
执行 `nixos-rebuild switch` 后,检查服务是否正在运行:
|
||||
|
||||
```bash
|
||||
# 检查服务状态
|
||||
systemctl status hermes-agent
|
||||
|
||||
# 查看日志(Ctrl+C 停止)
|
||||
journalctl -u hermes-agent -f
|
||||
|
||||
# 如果 addToSystemPackages 为 true,测试 CLI
|
||||
hermes version
|
||||
hermes config # 显示生成的配置
|
||||
```
|
||||
|
||||
### 选择部署模式
|
||||
|
||||
模块支持两种模式,由 `container.enable` 控制:
|
||||
|
||||
| | **原生**(默认) | **容器** |
|
||||
|---|---|---|
|
||||
| 运行方式 | 主机上加固的 systemd 服务 | 持久化 Ubuntu 容器,`/nix/store` 以只读方式绑定挂载 |
|
||||
| 安全性 | `NoNewPrivileges`、`ProtectSystem=strict`、`PrivateTmp` | 容器隔离,内部以非特权用户运行 |
|
||||
| Agent 可自行安装包 | 否——仅限 Nix 提供的 PATH 上的工具 | 是——`apt`、`pip`、`npm` 安装的包在重启后持久保留 |
|
||||
| 配置界面 | 相同 | 相同 |
|
||||
| 适用场景 | 标准部署、最高安全性、可重现性 | Agent 需要运行时安装包、可变环境、实验性工具 |
|
||||
|
||||
启用容器模式只需添加一行:
|
||||
|
||||
```nix
|
||||
{
|
||||
services.hermes-agent = {
|
||||
enable = true;
|
||||
container.enable = true;
|
||||
# ... 其余配置相同
|
||||
};
|
||||
}
|
||||
```
|
||||
|
||||
:::info
|
||||
容器模式通过 `mkDefault` 自动启用 `virtualisation.docker.enable`。如果你使用 Podman,请设置 `container.backend = "podman"` 并将 `virtualisation.docker.enable` 设为 `false`。
|
||||
:::
|
||||
|
||||
---
|
||||
|
||||
## 配置
|
||||
|
||||
### 声明式设置
|
||||
|
||||
`settings` 选项接受任意 attrset,并将其渲染为 `config.yaml`。它支持跨多个模块定义的深度合并(通过 `lib.recursiveUpdate`),因此你可以将配置拆分到多个文件中:
|
||||
|
||||
```nix
|
||||
# base.nix
|
||||
services.hermes-agent.settings = {
|
||||
model.default = "anthropic/claude-sonnet-4";
|
||||
toolsets = [ "all" ];
|
||||
terminal = { backend = "local"; timeout = 180; };
|
||||
};
|
||||
|
||||
# personality.nix
|
||||
services.hermes-agent.settings = {
|
||||
display = { compact = false; personality = "kawaii"; };
|
||||
memory = { memory_enabled = true; user_profile_enabled = true; };
|
||||
};
|
||||
```
|
||||
|
||||
两者在求值时深度合并。Nix 声明的键始终优先于磁盘上现有 `config.yaml` 中的键,但 **Nix 未涉及的用户添加键会被保留**。这意味着如果 Agent 或手动编辑添加了 `skills.disabled` 或 `streaming.enabled` 等键,它们在 `nixos-rebuild switch` 后仍会保留。
|
||||
|
||||
:::note 模型命名
|
||||
`settings.model.default` 使用你的提供商所期望的模型标识符。使用 [OpenRouter](https://openrouter.ai)(默认)时,格式如 `"anthropic/claude-sonnet-4"` 或 `"google/gemini-3-flash"`。如果直接使用提供商(Anthropic、OpenAI),请将 `settings.model.base_url` 指向其 API,并使用其原生模型 ID(例如 `"claude-sonnet-4-20250514"`)。未设置 `base_url` 时,Hermes 默认使用 OpenRouter。
|
||||
:::
|
||||
|
||||
:::tip 查找可用配置键
|
||||
运行 `nix build .#configKeys && cat result` 可查看从 Python `DEFAULT_CONFIG` 中提取的所有叶配置键。你可以将现有的 `config.yaml` 粘贴到 `settings` attrset 中——结构是 1:1 对应的。
|
||||
:::
|
||||
|
||||
<details>
|
||||
<summary><strong>完整示例:所有常用自定义设置</strong></summary>
|
||||
|
||||
```nix
|
||||
{ config, ... }: {
|
||||
services.hermes-agent = {
|
||||
enable = true;
|
||||
container.enable = true;
|
||||
|
||||
# ── 模型 ──────────────────────────────────────────────────────────
|
||||
settings = {
|
||||
model = {
|
||||
base_url = "https://openrouter.ai/api/v1";
|
||||
default = "anthropic/claude-opus-4.6";
|
||||
};
|
||||
toolsets = [ "all" ];
|
||||
max_turns = 100;
|
||||
terminal = { backend = "local"; cwd = "."; timeout = 180; };
|
||||
compression = {
|
||||
enabled = true;
|
||||
threshold = 0.85;
|
||||
summary_model = "google/gemini-3-flash-preview";
|
||||
};
|
||||
memory = { memory_enabled = true; user_profile_enabled = true; };
|
||||
display = { compact = false; personality = "kawaii"; };
|
||||
agent = { max_turns = 60; verbose = false; };
|
||||
};
|
||||
|
||||
# ── 密钥 ────────────────────────────────────────────────────────
|
||||
environmentFiles = [ config.sops.secrets."hermes-env".path ];
|
||||
|
||||
# ── 文档 ──────────────────────────────────────────────────────────
|
||||
documents = {
|
||||
"USER.md" = ./documents/USER.md;
|
||||
};
|
||||
|
||||
# ── MCP 服务器 ────────────────────────────────────────────────────
|
||||
mcpServers.filesystem = {
|
||||
command = "npx";
|
||||
args = [ "-y" "@modelcontextprotocol/server-filesystem" "/data/workspace" ];
|
||||
};
|
||||
|
||||
# ── 容器选项 ──────────────────────────────────────────────────────
|
||||
container = {
|
||||
image = "ubuntu:24.04";
|
||||
backend = "docker";
|
||||
hostUsers = [ "your-username" ];
|
||||
extraVolumes = [ "/home/user/projects:/projects:rw" ];
|
||||
extraOptions = [ "--gpus" "all" ];
|
||||
};
|
||||
|
||||
# ── 服务调优 ─────────────────────────────────────────────────────
|
||||
addToSystemPackages = true;
|
||||
extraArgs = [ "--verbose" ];
|
||||
restart = "always";
|
||||
restartSec = 5;
|
||||
};
|
||||
}
|
||||
```
|
||||
|
||||
</details>
|
||||
|
||||
### 逃生舱:自带配置文件
|
||||
|
||||
如果你希望完全在 Nix 之外管理 `config.yaml`,请使用 `configFile`:
|
||||
|
||||
```nix
|
||||
services.hermes-agent.configFile = /etc/hermes/config.yaml;
|
||||
```
|
||||
|
||||
这会完全绕过 `settings`——不合并,不生成。每次激活时,该文件会原样复制到 `$HERMES_HOME/config.yaml`。
|
||||
|
||||
### 自定义速查表
|
||||
|
||||
Nix 用户最常见自定义需求的快速参考:
|
||||
|
||||
| 我想要... | 选项 | 示例 |
|
||||
|---|---|---|
|
||||
| 更改 LLM 模型 | `settings.model.default` | `"anthropic/claude-sonnet-4"` |
|
||||
| 使用不同的提供商端点 | `settings.model.base_url` | `"https://openrouter.ai/api/v1"` |
|
||||
| 添加 API 密钥 | `environmentFiles` | `[ config.sops.secrets."hermes-env".path ]` |
|
||||
| 给 Agent 设置个性 | `${services.hermes-agent.stateDir}/.hermes/SOUL.md` | 直接管理该文件 |
|
||||
| 添加 MCP 工具服务器 | `mcpServers.<name>` | 参见 [MCP 服务器](#mcp-servers) |
|
||||
| 将主机目录挂载到容器 | `container.extraVolumes` | `[ "/data:/data:rw" ]` |
|
||||
| 为容器传入 GPU 访问 | `container.extraOptions` | `[ "--gpus" "all" ]` |
|
||||
| 使用 Podman 替代 Docker | `container.backend` | `"podman"` |
|
||||
| 在主机 CLI 和容器间共享状态 | `container.hostUsers` | `[ "sidbin" ]` |
|
||||
| 为 Agent 提供额外工具 | `extraPackages` | `[ pkgs.pandoc pkgs.imagemagick ]` |
|
||||
| 使用自定义基础镜像 | `container.image` | `"ubuntu:24.04"` |
|
||||
| 覆盖 hermes 包 | `package` | `inputs.hermes-agent.packages.${system}.default.override { ... }` |
|
||||
| 更改状态目录 | `stateDir` | `"/opt/hermes"` |
|
||||
| 设置 Agent 的工作目录 | `workingDirectory` | `"/home/user/projects"` |
|
||||
|
||||
---
|
||||
|
||||
## 密钥管理
|
||||
|
||||
:::danger 切勿将 API 密钥放入 `settings` 或 `environment`
|
||||
Nix 表达式中的值会进入 `/nix/store`,该目录是全局可读的。请始终使用带有密钥管理器的 `environmentFiles`。
|
||||
:::
|
||||
|
||||
`environment`(非密钥变量)和 `environmentFiles`(密钥文件)在激活时(`nixos-rebuild switch`)都会合并到 `$HERMES_HOME/.env` 中。Hermes 在每次启动时读取此文件,因此更改在 `systemctl restart hermes-agent` 后生效——无需重建容器。
|
||||
|
||||
### sops-nix
|
||||
|
||||
```nix
|
||||
{
|
||||
sops = {
|
||||
defaultSopsFile = ./secrets/hermes.yaml;
|
||||
age.keyFile = "/home/user/.config/sops/age/keys.txt";
|
||||
secrets."hermes-env" = { format = "yaml"; };
|
||||
};
|
||||
|
||||
services.hermes-agent.environmentFiles = [
|
||||
config.sops.secrets."hermes-env".path
|
||||
];
|
||||
}
|
||||
```
|
||||
|
||||
密钥文件包含键值对:
|
||||
|
||||
```yaml
|
||||
# secrets/hermes.yaml(使用 sops 加密)
|
||||
hermes-env: |
|
||||
OPENROUTER_API_KEY=sk-or-...
|
||||
TELEGRAM_BOT_TOKEN=123456:ABC...
|
||||
ANTHROPIC_API_KEY=sk-ant-...
|
||||
```
|
||||
|
||||
### agenix
|
||||
|
||||
```nix
|
||||
{
|
||||
age.secrets.hermes-env.file = ./secrets/hermes-env.age;
|
||||
|
||||
services.hermes-agent.environmentFiles = [
|
||||
config.age.secrets.hermes-env.path
|
||||
];
|
||||
}
|
||||
```
|
||||
|
||||
### OAuth / 认证预置
|
||||
|
||||
对于需要 OAuth 的平台(例如 Discord),使用 `authFile` 在首次部署时预置凭据:
|
||||
|
||||
```nix
|
||||
{
|
||||
services.hermes-agent = {
|
||||
authFile = config.sops.secrets."hermes/auth.json".path;
|
||||
# authFileForceOverwrite = true; # 每次激活时强制覆盖
|
||||
};
|
||||
}
|
||||
```
|
||||
|
||||
仅当 `auth.json` 不存在时才复制该文件(除非 `authFileForceOverwrite = true`)。运行时 OAuth token 刷新会写入状态目录,并在重建后保留。
|
||||
|
||||
---
|
||||
|
||||
## 文档
|
||||
|
||||
`documents` 选项将文件安装到 Agent 的工作目录(即 `workingDirectory`,Agent 将其作为工作区读取)。Hermes 按约定查找特定文件名:
|
||||
|
||||
- **`USER.md`** — 关于 Agent 正在交互的用户的上下文信息。
|
||||
- 你放置在此处的任何其他文件对 Agent 都可见,作为工作区文件。
|
||||
|
||||
Agent 身份文件是独立的:Hermes 从 `$HERMES_HOME/SOUL.md` 加载其主要 `SOUL.md`,在 NixOS 模块中对应 `${services.hermes-agent.stateDir}/.hermes/SOUL.md`。将 `SOUL.md` 放入 `documents` 只会创建一个工作区文件,不会替换主角色文件。
|
||||
|
||||
```nix
|
||||
{
|
||||
services.hermes-agent.documents = {
|
||||
"USER.md" = ./documents/USER.md; # 路径引用,从 Nix store 复制
|
||||
};
|
||||
}
|
||||
```
|
||||
|
||||
值可以是内联字符串或路径引用。文件在每次 `nixos-rebuild switch` 时安装。
|
||||
|
||||
---
|
||||
|
||||
## MCP 服务器
|
||||
|
||||
`mcpServers` 选项以声明式方式配置 [MCP(Model Context Protocol,模型上下文协议)](https://modelcontextprotocol.io)服务器。每个服务器使用 **stdio**(本地命令)或 **HTTP**(远程 URL)传输方式。
|
||||
|
||||
### stdio 传输(本地服务器)
|
||||
|
||||
```nix
|
||||
{
|
||||
services.hermes-agent.mcpServers = {
|
||||
filesystem = {
|
||||
command = "npx";
|
||||
args = [ "-y" "@modelcontextprotocol/server-filesystem" "/data/workspace" ];
|
||||
};
|
||||
github = {
|
||||
command = "npx";
|
||||
args = [ "-y" "@modelcontextprotocol/server-github" ];
|
||||
env.GITHUB_PERSONAL_ACCESS_TOKEN = "\${GITHUB_TOKEN}"; # 从 .env 解析
|
||||
};
|
||||
};
|
||||
}
|
||||
```
|
||||
|
||||
:::tip
|
||||
`env` 值中的环境变量在运行时从 `$HERMES_HOME/.env` 解析。使用 `environmentFiles` 注入密钥——切勿将 token 直接放入 Nix 配置。
|
||||
:::
|
||||
|
||||
### HTTP 传输(远程服务器)
|
||||
|
||||
```nix
|
||||
{
|
||||
services.hermes-agent.mcpServers.remote-api = {
|
||||
url = "https://mcp.example.com/v1/mcp";
|
||||
headers.Authorization = "Bearer \${MCP_REMOTE_API_KEY}";
|
||||
timeout = 180;
|
||||
};
|
||||
}
|
||||
```
|
||||
|
||||
### 带 OAuth 的 HTTP 传输
|
||||
|
||||
对于使用 OAuth 2.1 的服务器,设置 `auth = "oauth"`。Hermes 实现了完整的 PKCE 流程——元数据发现、动态客户端注册、token 交换和自动刷新。
|
||||
|
||||
```nix
|
||||
{
|
||||
services.hermes-agent.mcpServers.my-oauth-server = {
|
||||
url = "https://mcp.example.com/mcp";
|
||||
auth = "oauth";
|
||||
};
|
||||
}
|
||||
```
|
||||
|
||||
Token 存储在 `$HERMES_HOME/mcp-tokens/<server-name>.json` 中,在重启和重建后持久保留。
|
||||
|
||||
<details>
|
||||
<summary><strong>无头服务器上的初始 OAuth 授权</strong></summary>
|
||||
|
||||
首次 OAuth 授权需要基于浏览器的同意流程。在无头部署中,Hermes 将授权 URL 打印到 stdout/日志,而不是打开浏览器。
|
||||
|
||||
**方案 A:交互式引导** — 通过 `docker exec`(容器)或 `sudo -u hermes`(原生)运行一次流程:
|
||||
|
||||
```bash
|
||||
# 容器模式
|
||||
docker exec -it hermes-agent \
|
||||
hermes mcp add my-oauth-server --url https://mcp.example.com/mcp --auth oauth
|
||||
|
||||
# 原生模式
|
||||
sudo -u hermes HERMES_HOME=/var/lib/hermes/.hermes \
|
||||
hermes mcp add my-oauth-server --url https://mcp.example.com/mcp --auth oauth
|
||||
```
|
||||
|
||||
容器使用 `--network=host`,因此 `127.0.0.1` 上的 OAuth 回调监听器可从主机浏览器访问。
|
||||
|
||||
**方案 B:预置 token** — 在工作站上完成流程,然后复制 token:
|
||||
|
||||
```bash
|
||||
hermes mcp add my-oauth-server --url https://mcp.example.com/mcp --auth oauth
|
||||
scp ~/.hermes/mcp-tokens/my-oauth-server{,.client}.json \
|
||||
server:/var/lib/hermes/.hermes/mcp-tokens/
|
||||
# 确保:chown hermes:hermes,chmod 0600
|
||||
```
|
||||
|
||||
</details>
|
||||
|
||||
### Sampling(服务器发起的 LLM 请求)
|
||||
|
||||
部分 MCP 服务器可以向 Agent 请求 LLM 补全:
|
||||
|
||||
```nix
|
||||
{
|
||||
services.hermes-agent.mcpServers.analysis = {
|
||||
command = "npx";
|
||||
args = [ "-y" "analysis-server" ];
|
||||
sampling = {
|
||||
enabled = true;
|
||||
model = "google/gemini-3-flash";
|
||||
max_tokens_cap = 4096;
|
||||
timeout = 30;
|
||||
max_rpm = 10;
|
||||
};
|
||||
};
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 托管模式
|
||||
|
||||
当 hermes 通过 NixOS 模块运行时,以下 CLI 命令会被**屏蔽**,并显示指向 `configuration.nix` 的描述性错误:
|
||||
|
||||
| 被屏蔽的命令 | 原因 |
|
||||
|---|---|
|
||||
| `hermes setup` | 配置是声明式的——请在 Nix 配置中编辑 `settings` |
|
||||
| `hermes config edit` | 配置由 `settings` 生成 |
|
||||
| `hermes config set <key> <value>` | 配置由 `settings` 生成 |
|
||||
| `hermes gateway install` | systemd 服务由 NixOS 管理 |
|
||||
| `hermes gateway uninstall` | systemd 服务由 NixOS 管理 |
|
||||
|
||||
这可以防止 Nix 声明的内容与磁盘上实际内容之间产生漂移。检测使用两个信号:
|
||||
|
||||
1. **`HERMES_MANAGED=true`** 环境变量——由 systemd 服务设置,对 gateway 进程可见
|
||||
2. **`.managed` 标记文件**,位于 `HERMES_HOME` 中——由激活脚本设置,对交互式 shell 可见(例如 `docker exec -it hermes-agent hermes config set ...` 也会被屏蔽)
|
||||
|
||||
要更改配置,请编辑你的 Nix 配置并运行 `sudo nixos-rebuild switch`。
|
||||
|
||||
---
|
||||
|
||||
## 容器架构
|
||||
|
||||
:::info
|
||||
本节仅在使用 `container.enable = true` 时相关。原生模式部署可跳过。
|
||||
:::
|
||||
|
||||
启用容器模式后,hermes 在持久化 Ubuntu 容器内运行,Nix 构建的二进制文件以只读方式从主机绑定挂载:
|
||||
|
||||
```
|
||||
主机 容器
|
||||
──── ─────────
|
||||
/nix/store/...-hermes-agent-0.1.0 ──► /nix/store/... (ro)
|
||||
~/.hermes -> /var/lib/hermes/.hermes (符号链接桥接,按 hostUsers)
|
||||
/var/lib/hermes/ ──► /data/ (rw)
|
||||
├── current-package -> /nix/store/... (符号链接,每次重建更新)
|
||||
├── .gc-root -> /nix/store/... (防止 nix-collect-garbage)
|
||||
├── .container-identity (sha256 哈希,触发重建)
|
||||
├── .hermes/ (HERMES_HOME)
|
||||
│ ├── .env (从 environment + environmentFiles 合并)
|
||||
│ ├── config.yaml (Nix 生成,激活时深度合并)
|
||||
│ ├── .managed (标记文件)
|
||||
│ ├── .container-mode (路由元数据:backend、exec_user 等)
|
||||
│ ├── state.db, sessions/, memories/ (运行时状态)
|
||||
│ └── mcp-tokens/ (MCP 服务器的 OAuth token)
|
||||
├── home/ ──► /home/hermes (rw)
|
||||
└── workspace/ (MESSAGING_CWD)
|
||||
├── SOUL.md (来自 documents 选项)
|
||||
└── (Agent 创建的文件)
|
||||
|
||||
容器可写层(apt/pip/npm): /usr, /usr/local, /tmp
|
||||
```
|
||||
|
||||
Nix 构建的二进制文件能在 Ubuntu 容器内运行,是因为 `/nix/store` 被绑定挂载——它携带自己的解释器和所有依赖,不依赖容器的系统库。容器入口点通过 `current-package` 符号链接解析:`/data/current-package/bin/hermes gateway run --replace`。执行 `nixos-rebuild switch` 时,只更新符号链接——容器继续运行。
|
||||
|
||||
### 各事件的持久性
|
||||
|
||||
| 事件 | 容器重建? | `/data`(状态) | `/home/hermes` | 可写层(`apt`/`pip`/`npm`) |
|
||||
|---|---|---|---|---|
|
||||
| `systemctl restart hermes-agent` | 否 | 保留 | 保留 | 保留 |
|
||||
| `nixos-rebuild switch`(代码变更) | 否(更新符号链接) | 保留 | 保留 | 保留 |
|
||||
| 主机重启 | 否 | 保留 | 保留 | 保留 |
|
||||
| `nix-collect-garbage` | 否(GC root) | 保留 | 保留 | 保留 |
|
||||
| 镜像变更(`container.image`) | **是** | 保留 | 保留 | **丢失** |
|
||||
| 卷/选项变更 | **是** | 保留 | 保留 | **丢失** |
|
||||
| `environment`/`environmentFiles` 变更 | 否 | 保留 | 保留 | 保留 |
|
||||
|
||||
仅当容器的**身份哈希**发生变化时才会重建容器。哈希涵盖:schema 版本、镜像、`extraVolumes`、`extraOptions` 和入口点脚本。环境变量、settings、文档或 hermes 包本身的变更**不会**触发重建。
|
||||
|
||||
:::warning 可写层丢失
|
||||
当身份哈希发生变化(镜像升级、新卷、新容器选项)时,容器会被销毁并从 `container.image` 的全新拉取重建。可写层中通过 `apt install`、`pip install` 或 `npm install` 安装的包将丢失。`/data` 和 `/home/hermes` 中的状态会保留(这些是绑定挂载)。
|
||||
|
||||
如果 Agent 依赖特定包,考虑将其烘焙到自定义镜像中(`container.image = "my-registry/hermes-base:latest"`),或在 Agent 的 SOUL.md 中编写安装脚本。
|
||||
:::
|
||||
|
||||
### GC Root 保护
|
||||
|
||||
`preStart` 脚本在 `${stateDir}/.gc-root` 创建一个指向当前 hermes 包的 GC root。这可以防止 `nix-collect-garbage` 删除正在运行的二进制文件。如果 GC root 损坏,重启服务会重新创建它。
|
||||
|
||||
---
|
||||
|
||||
## 插件
|
||||
|
||||
NixOS 模块支持声明式插件安装——无需命令式的 `hermes plugins install`。
|
||||
|
||||
### 目录插件(`extraPlugins`)
|
||||
|
||||
对于只包含 `plugin.yaml` + `__init__.py` 的源码树插件(例如 [hermes-lcm](https://github.com/stephenschoettler/hermes-lcm)):
|
||||
|
||||
```nix
|
||||
services.hermes-agent.extraPlugins = [
|
||||
(pkgs.fetchFromGitHub {
|
||||
owner = "stephenschoettler";
|
||||
repo = "hermes-lcm";
|
||||
rev = "v0.7.0";
|
||||
hash = "sha256-...";
|
||||
})
|
||||
];
|
||||
```
|
||||
|
||||
插件在激活时以符号链接方式安装到 `$HERMES_HOME/plugins/`。Hermes 通过其正常的目录扫描发现它们。从列表中移除插件并运行 `nixos-rebuild switch` 会删除符号链接。
|
||||
|
||||
### 入口点插件(`extraPythonPackages`)
|
||||
|
||||
对于通过 `[project.entry-points."hermes_agent.plugins"]` 注册的 pip 打包插件(例如 [rtk-hermes](https://github.com/ogallotti/rtk-hermes)):
|
||||
|
||||
```nix
|
||||
services.hermes-agent.extraPythonPackages = [
|
||||
(pkgs.python312Packages.buildPythonPackage {
|
||||
pname = "rtk-hermes";
|
||||
version = "1.0.0";
|
||||
src = pkgs.fetchFromGitHub {
|
||||
owner = "ogallotti";
|
||||
repo = "rtk-hermes";
|
||||
rev = "v1.0.0";
|
||||
hash = "sha256-...";
|
||||
};
|
||||
format = "pyproject";
|
||||
build-system = [ pkgs.python312Packages.setuptools ];
|
||||
})
|
||||
];
|
||||
```
|
||||
|
||||
该包的 `site-packages` 会添加到 hermes wrapper 的 PYTHONPATH 中。`importlib.metadata` 在会话启动时发现入口点。
|
||||
|
||||
### 可选依赖组(`extraDependencyGroups`)
|
||||
|
||||
对于已在 hermes-agent 的 `pyproject.toml` 中声明的可选 extras(例如 `hindsight` 或 `honcho` 等记忆提供商),使用 `extraDependencyGroups` 在构建时将其包含到封闭的 venv 中:
|
||||
|
||||
```nix
|
||||
services.hermes-agent = {
|
||||
extraDependencyGroups = [ "hindsight" ];
|
||||
settings.memory.provider = "hindsight";
|
||||
};
|
||||
```
|
||||
|
||||
这由 uv 与核心依赖在单次解析中完成——不需要 PYTHONPATH 补丁,没有冲突风险。可用的组与 `pyproject.toml` 中 `[project.optional-dependencies]` 的键对应(例如 `"hindsight"`、`"honcho"`、`"voice"`、`"matrix"`、`"mistral"`、`"bedrock"`)。
|
||||
|
||||
**何时使用哪个:**
|
||||
|
||||
| 需求 | 选项 |
|
||||
|------|--------|
|
||||
| 启用 pyproject.toml 可选 extra | `extraDependencyGroups` |
|
||||
| 添加不在 pyproject.toml 中的外部 Python 插件 | `extraPythonPackages` |
|
||||
| 添加系统二进制文件(pandoc、jq 等) | `extraPackages` |
|
||||
| 添加基于目录的插件源码树 | `extraPlugins` |
|
||||
|
||||
### 组合使用
|
||||
|
||||
带有第三方 Python 依赖的目录插件需要同时使用两个选项:
|
||||
|
||||
```nix
|
||||
services.hermes-agent = {
|
||||
extraPlugins = [ my-plugin-src ]; # 插件源码
|
||||
extraPythonPackages = [ pkgs.python312Packages.redis ]; # 其 Python 依赖
|
||||
extraPackages = [ pkgs.redis ]; # 其需要的系统二进制文件
|
||||
};
|
||||
```
|
||||
|
||||
### 使用 Overlay
|
||||
|
||||
外部 flake 可以直接覆盖包:
|
||||
|
||||
```nix
|
||||
{
|
||||
inputs.hermes-agent.url = "github:NousResearch/hermes-agent";
|
||||
outputs = { hermes-agent, nixpkgs, ... }: {
|
||||
nixpkgs.overlays = [ hermes-agent.overlays.default ];
|
||||
# 然后:
|
||||
# pkgs.hermes-agent.override { extraPythonPackages = [...]; }
|
||||
# pkgs.hermes-agent.override { extraDependencyGroups = [ "hindsight" ]; }
|
||||
};
|
||||
}
|
||||
```
|
||||
|
||||
### 插件配置
|
||||
|
||||
插件仍需在 `config.yaml` 中启用。通过声明式 settings 添加:
|
||||
|
||||
```nix
|
||||
services.hermes-agent.settings.plugins.enabled = [
|
||||
"hermes-lcm"
|
||||
"rtk-rewrite"
|
||||
];
|
||||
```
|
||||
|
||||
:::note
|
||||
构建时冲突检查可防止插件包覆盖核心 hermes 依赖。如果插件提供了封闭 venv 中已有的包,`nixos-rebuild` 会以明确的错误失败。
|
||||
:::
|
||||
|
||||
---
|
||||
|
||||
## 开发
|
||||
|
||||
### 开发 Shell
|
||||
|
||||
该 flake 提供了一个包含 Python 3.12、uv、Node.js 和所有运行时工具的开发 shell:
|
||||
|
||||
```bash
|
||||
cd hermes-agent
|
||||
nix develop
|
||||
|
||||
# Shell 提供:
|
||||
# - Python 3.12 + uv(首次进入时将依赖安装到 .venv)
|
||||
# - Node.js 22、ripgrep、git、openssh、ffmpeg 在 PATH 上
|
||||
# - 戳记文件优化:依赖未变更时重新进入几乎即时
|
||||
|
||||
hermes setup
|
||||
hermes chat
|
||||
```
|
||||
|
||||
### direnv(推荐)
|
||||
|
||||
包含的 `.envrc` 会自动激活开发 shell:
|
||||
|
||||
```bash
|
||||
cd hermes-agent
|
||||
direnv allow # 仅需一次
|
||||
# 后续进入几乎即时(戳记文件跳过依赖安装)
|
||||
```
|
||||
|
||||
### Flake 检查
|
||||
|
||||
该 flake 包含在 CI 和本地运行的构建时验证:
|
||||
|
||||
```bash
|
||||
# 运行所有检查
|
||||
nix flake check
|
||||
|
||||
# 单独检查
|
||||
nix build .#checks.x86_64-linux.package-contents # 二进制文件存在 + 版本
|
||||
nix build .#checks.x86_64-linux.entry-points-sync # pyproject.toml ↔ Nix 包同步
|
||||
nix build .#checks.x86_64-linux.cli-commands # gateway/config 子命令
|
||||
nix build .#checks.x86_64-linux.managed-guard # HERMES_MANAGED 屏蔽变更操作
|
||||
nix build .#checks.x86_64-linux.bundled-skills # 包中存在 skills
|
||||
nix build .#checks.x86_64-linux.config-roundtrip # 合并脚本保留用户键
|
||||
```
|
||||
|
||||
<details>
|
||||
<summary><strong>每项检查的验证内容</strong></summary>
|
||||
|
||||
| 检查 | 测试内容 |
|
||||
|---|---|
|
||||
| `package-contents` | `hermes` 和 `hermes-agent` 二进制文件存在且 `hermes version` 可运行 |
|
||||
| `entry-points-sync` | `pyproject.toml` 中 `[project.scripts]` 的每个条目在 Nix 包中都有对应的封装二进制文件 |
|
||||
| `cli-commands` | `hermes --help` 暴露 `gateway` 和 `config` 子命令 |
|
||||
| `managed-guard` | `HERMES_MANAGED=true hermes config set ...` 打印 NixOS 错误 |
|
||||
| `bundled-skills` | skills 目录存在,包含 SKILL.md 文件,wrapper 中设置了 `HERMES_BUNDLED_SKILLS` |
|
||||
| `config-roundtrip` | 7 种合并场景:全新安装、Nix 覆盖、用户键保留、混合合并、MCP 累加合并、嵌套深度合并、幂等性 |
|
||||
|
||||
</details>
|
||||
|
||||
---
|
||||
|
||||
## 选项参考
|
||||
|
||||
### 核心
|
||||
|
||||
| 选项 | 类型 | 默认值 | 描述 |
|
||||
|---|---|---|---|
|
||||
| `enable` | `bool` | `false` | 启用 hermes-agent 服务 |
|
||||
| `package` | `package` | `hermes-agent` | 使用的 hermes-agent 包 |
|
||||
| `user` | `str` | `"hermes"` | 系统用户 |
|
||||
| `group` | `str` | `"hermes"` | 系统组 |
|
||||
| `createUser` | `bool` | `true` | 自动创建用户/组 |
|
||||
| `stateDir` | `str` | `"/var/lib/hermes"` | 状态目录(`HERMES_HOME` 的父目录) |
|
||||
| `workingDirectory` | `str` | `"${stateDir}/workspace"` | Agent 工作目录(`MESSAGING_CWD`) |
|
||||
| `addToSystemPackages` | `bool` | `false` | 将 `hermes` CLI 添加到系统 PATH 并在系统范围内设置 `HERMES_HOME` |
|
||||
|
||||
### 配置
|
||||
|
||||
| 选项 | 类型 | 默认值 | 描述 |
|
||||
|---|---|---|---|
|
||||
| `settings` | `attrs`(深度合并) | `{}` | 声明式配置,渲染为 `config.yaml`。支持任意嵌套;多个定义通过 `lib.recursiveUpdate` 合并 |
|
||||
| `configFile` | `null` 或 `path` | `null` | 现有 `config.yaml` 的路径。设置后完全覆盖 `settings` |
|
||||
|
||||
### 密钥与环境
|
||||
|
||||
| 选项 | 类型 | 默认值 | 描述 |
|
||||
|---|---|---|---|
|
||||
| `environmentFiles` | `listOf str` | `[]` | 包含密钥的 env 文件路径。激活时合并到 `$HERMES_HOME/.env` |
|
||||
| `environment` | `attrsOf str` | `{}` | 非密钥环境变量。**在 Nix store 中可见**——请勿在此放置密钥 |
|
||||
| `authFile` | `null` 或 `path` | `null` | OAuth 凭据预置文件。仅在首次部署时复制 |
|
||||
| `authFileForceOverwrite` | `bool` | `false` | 每次激活时始终从 `authFile` 覆盖 `auth.json` |
|
||||
|
||||
### 文档
|
||||
|
||||
| 选项 | 类型 | 默认值 | 描述 |
|
||||
|---|---|---|---|
|
||||
| `documents` | `attrsOf (either str path)` | `{}` | 工作区文件。键为文件名,值为内联字符串或路径。激活时安装到 `workingDirectory` |
|
||||
|
||||
### MCP 服务器
|
||||
|
||||
| 选项 | 类型 | 默认值 | 描述 |
|
||||
|---|---|---|---|
|
||||
| `mcpServers` | `attrsOf submodule` | `{}` | MCP 服务器定义,合并到 `settings.mcp_servers` |
|
||||
| `mcpServers.<name>.command` | `null` 或 `str` | `null` | 服务器命令(stdio 传输) |
|
||||
| `mcpServers.<name>.args` | `listOf str` | `[]` | 命令参数 |
|
||||
| `mcpServers.<name>.env` | `attrsOf str` | `{}` | 服务器进程的环境变量 |
|
||||
| `mcpServers.<name>.url` | `null` 或 `str` | `null` | 服务器端点 URL(HTTP/StreamableHTTP 传输) |
|
||||
| `mcpServers.<name>.headers` | `attrsOf str` | `{}` | HTTP 头,例如 `Authorization` |
|
||||
| `mcpServers.<name>.auth` | `null` 或 `"oauth"` | `null` | 认证方式。`"oauth"` 启用 OAuth 2.1 PKCE |
|
||||
| `mcpServers.<name>.enabled` | `bool` | `true` | 启用或禁用此服务器 |
|
||||
| `mcpServers.<name>.timeout` | `null` 或 `int` | `null` | 工具调用超时(秒,默认:120) |
|
||||
| `mcpServers.<name>.connect_timeout` | `null` 或 `int` | `null` | 连接超时(秒,默认:60) |
|
||||
| `mcpServers.<name>.tools` | `null` 或 `submodule` | `null` | 工具过滤(`include`/`exclude` 列表) |
|
||||
| `mcpServers.<name>.sampling` | `null` 或 `submodule` | `null` | 服务器发起 LLM 请求的 sampling 配置 |
|
||||
|
||||
### 服务行为
|
||||
|
||||
| 选项 | 类型 | 默认值 | 描述 |
|
||||
|---|---|---|---|
|
||||
| `extraArgs` | `listOf str` | `[]` | `hermes gateway` 的额外参数 |
|
||||
| `extraPackages` | `listOf package` | `[]` | Agent 可用的额外包。添加到 hermes 用户的每用户 profile,终端命令、skills 和 cron 任务均可见 |
|
||||
| `extraPlugins` | `listOf package` | `[]` | 以符号链接方式安装到 `$HERMES_HOME/plugins/` 的目录插件包。每个包必须包含 `plugin.yaml` |
|
||||
| `extraPythonPackages` | `listOf package` | `[]` | 添加到 PYTHONPATH 用于入口点插件发现的 Python 包。使用 `python312Packages` 构建 |
|
||||
| `extraDependencyGroups` | `listOf str` | `[]` | 包含到封闭 venv 中的 pyproject.toml 可选 extras(例如 `["hindsight"]`)。由 uv 解析——无冲突 |
|
||||
| `restart` | `str` | `"always"` | systemd `Restart=` 策略 |
|
||||
| `restartSec` | `int` | `5` | systemd `RestartSec=` 值 |
|
||||
|
||||
### 容器
|
||||
|
||||
| 选项 | 类型 | 默认值 | 描述 |
|
||||
|---|---|---|---|
|
||||
| `container.enable` | `bool` | `false` | 启用 OCI 容器模式 |
|
||||
| `container.backend` | `enum ["docker" "podman"]` | `"docker"` | 容器运行时 |
|
||||
| `container.image` | `str` | `"ubuntu:24.04"` | 基础镜像(运行时拉取) |
|
||||
| `container.extraVolumes` | `listOf str` | `[]` | 额外卷挂载(`host:container:mode`) |
|
||||
| `container.extraOptions` | `listOf str` | `[]` | 传递给 `docker create` 的额外参数 |
|
||||
| `container.hostUsers` | `listOf str` | `[]` | 获得 `~/.hermes` 符号链接(指向服务 stateDir)的交互式用户,自动加入 `hermes` 组 |
|
||||
|
||||
---
|
||||
|
||||
## 目录结构
|
||||
|
||||
### 原生模式
|
||||
|
||||
```
|
||||
/var/lib/hermes/ # stateDir(归 hermes:hermes 所有,权限 0750)
|
||||
├── .hermes/ # HERMES_HOME
|
||||
│ ├── config.yaml # Nix 生成(每次重建深度合并)
|
||||
│ ├── .managed # 标记:CLI 配置变更被屏蔽
|
||||
│ ├── .env # 从 environment + environmentFiles 合并
|
||||
│ ├── auth.json # OAuth 凭据(预置后自我管理)
|
||||
│ ├── gateway.pid
|
||||
│ ├── state.db
|
||||
│ ├── mcp-tokens/ # MCP 服务器的 OAuth token
|
||||
│ ├── sessions/
|
||||
│ ├── memories/
|
||||
│ ├── skills/
|
||||
│ ├── cron/
|
||||
│ └── logs/
|
||||
├── home/ # Agent HOME
|
||||
└── workspace/ # MESSAGING_CWD
|
||||
├── SOUL.md # 来自 documents 选项
|
||||
└── (Agent 创建的文件)
|
||||
```
|
||||
|
||||
### 容器模式
|
||||
|
||||
相同的布局,挂载到容器中:
|
||||
|
||||
| 容器路径 | 主机路径 | 模式 | 说明 |
|
||||
|---|---|---|---|
|
||||
| `/nix/store` | `/nix/store` | `ro` | Hermes 二进制文件 + 所有 Nix 依赖 |
|
||||
| `/data` | `/var/lib/hermes` | `rw` | 所有状态、配置、工作区 |
|
||||
| `/home/hermes` | `${stateDir}/home` | `rw` | 持久化 Agent home——`pip install --user`、工具缓存 |
|
||||
| `/usr`、`/usr/local`、`/tmp` | (可写层) | `rw` | `apt`/`pip`/`npm` 安装——重启后持久,重建后丢失 |
|
||||
|
||||
---
|
||||
|
||||
## 更新
|
||||
|
||||
```bash
|
||||
# 更新 flake 输入(在包含 flake.nix 的目录中运行)
|
||||
cd /etc/nixos && nix flake update hermes-agent
|
||||
|
||||
# 重建
|
||||
sudo nixos-rebuild switch
|
||||
```
|
||||
|
||||
在容器模式下,`current-package` 符号链接会更新,Agent 在重启时获取新的二进制文件。不会重建容器,不会丢失已安装的包。
|
||||
|
||||
---
|
||||
|
||||
## 故障排查
|
||||
|
||||
:::tip Podman 用户
|
||||
以下所有 `docker` 命令在 `podman` 中同样适用。如果你设置了 `container.backend = "podman"`,请相应替换。
|
||||
:::
|
||||
|
||||
### 服务日志
|
||||
|
||||
```bash
|
||||
# 两种模式使用相同的 systemd 单元
|
||||
journalctl -u hermes-agent -f
|
||||
|
||||
# 容器模式:也可直接查看
|
||||
docker logs -f hermes-agent
|
||||
```
|
||||
|
||||
### 容器检查
|
||||
|
||||
```bash
|
||||
systemctl status hermes-agent
|
||||
docker ps -a --filter name=hermes-agent
|
||||
docker inspect hermes-agent --format='{{.State.Status}}'
|
||||
docker exec -it hermes-agent bash
|
||||
docker exec hermes-agent readlink /data/current-package
|
||||
docker exec hermes-agent cat /data/.container-identity
|
||||
```
|
||||
|
||||
### 强制重建容器
|
||||
|
||||
如果需要重置可写层(全新 Ubuntu):
|
||||
|
||||
```bash
|
||||
sudo systemctl stop hermes-agent
|
||||
docker rm -f hermes-agent
|
||||
sudo rm /var/lib/hermes/.container-identity
|
||||
sudo systemctl start hermes-agent
|
||||
```
|
||||
|
||||
### 验证密钥已加载
|
||||
|
||||
如果 Agent 启动但无法向 LLM 提供商认证,检查 `.env` 文件是否正确合并:
|
||||
|
||||
```bash
|
||||
# 原生模式
|
||||
sudo -u hermes cat /var/lib/hermes/.hermes/.env
|
||||
|
||||
# 容器模式
|
||||
docker exec hermes-agent cat /data/.hermes/.env
|
||||
```
|
||||
|
||||
### GC Root 验证
|
||||
|
||||
```bash
|
||||
nix-store --query --roots $(docker exec hermes-agent readlink /data/current-package)
|
||||
```
|
||||
|
||||
### 常见问题
|
||||
|
||||
| 现象 | 原因 | 解决方法 |
|
||||
|---|---|---|
|
||||
| `Cannot save configuration: managed by NixOS` | CLI 守卫已激活 | 编辑 `configuration.nix` 并执行 `nixos-rebuild switch` |
|
||||
| 容器意外重建 | `extraVolumes`、`extraOptions` 或 `image` 发生变更 | 预期行为——可写层重置。重新安装包或使用自定义镜像 |
|
||||
| `hermes version` 显示旧版本 | 容器未重启 | `systemctl restart hermes-agent` |
|
||||
| `/var/lib/hermes` 权限拒绝 | 状态目录为 `0750 hermes:hermes` | 使用 `docker exec` 或 `sudo -u hermes` |
|
||||
| `nix-collect-garbage` 删除了 hermes | GC root 缺失 | 重启服务(preStart 会重新创建 GC root) |
|
||||
| `no container with name or ID "hermes-agent"`(Podman) | Podman rootful 容器对普通用户不可见 | 为 podman 添加免密 sudo(参见[容器模式](#container-mode)章节) |
|
||||
| `unable to find user hermes` | 容器仍在启动中(入口点尚未创建用户) | 等待几秒后重试——CLI 会自动重试 |
|
||||
| 通过 `extraPackages` 添加的工具在终端中找不到 | 需要 `nixos-rebuild switch` 更新每用户 profile | 重建并重启:`nixos-rebuild switch && systemctl restart hermes-agent` |
|
||||
@ -0,0 +1,353 @@
|
||||
---
|
||||
sidebar_position: 1
|
||||
title: "快速入门"
|
||||
description: "与 Hermes Agent 的第一次对话——从安装到开始聊天,5 分钟内完成"
|
||||
---
|
||||
|
||||
# 快速入门
|
||||
|
||||
本指南带你从零开始搭建一个能够应对实际使用的 Hermes 环境。完成安装、选择 provider(服务提供商)、验证对话正常运行,并了解出现问题时的处理方法。
|
||||
|
||||
## 更喜欢看视频?
|
||||
|
||||
**Onchain AI Garage** 制作了一套涵盖安装、配置和基本命令的 Masterclass 演示视频——如果你更习惯跟着视频操作,这是本页的绝佳补充。更多内容请查看完整的 [Hermes Agent 教程与使用案例](https://www.youtube.com/channel/UCqB1bhMwGsW-yefBxYwFCCg) 播放列表。
|
||||
|
||||
<div style={{position: 'relative', paddingBottom: '56.25%', height: 0, overflow: 'hidden', maxWidth: '100%', marginBottom: '1.5rem'}}>
|
||||
<iframe
|
||||
style={{position: 'absolute', top: 0, left: 0, width: '100%', height: '100%'}}
|
||||
src="https://www.youtube-nocookie.com/embed/R3YOGfTBcQg"
|
||||
title="Hermes Agent Masterclass: Installation, Setup, Basic Commands"
|
||||
frameBorder="0"
|
||||
allow="accelerometer; clipboard-write; encrypted-media; gyroscope; picture-in-picture"
|
||||
allowFullScreen
|
||||
></iframe>
|
||||
</div>
|
||||
|
||||
## 适用人群
|
||||
|
||||
- 全新用户,想以最短路径完成可用配置
|
||||
- 正在切换 provider,不想因配置错误浪费时间
|
||||
- 为团队、机器人或长期运行的工作流配置 Hermes
|
||||
- 厌倦了"安装成功但什么都做不了"的情况
|
||||
|
||||
## 最快路径
|
||||
|
||||
根据你的目标选择对应行:
|
||||
|
||||
| 目标 | 先做这步 | 再做这步 |
|
||||
|---|---|---|
|
||||
| 只想让 Hermes 在本机跑起来 | `hermes setup` | 运行一次真实对话并验证有响应 |
|
||||
| 已知道要用哪个 provider | `hermes model` | 保存配置,然后开始聊天 |
|
||||
| 想搭建机器人或长期运行的服务 | CLI 正常后运行 `hermes gateway setup` | 接入 Telegram、Discord、Slack 或其他平台 |
|
||||
| 想使用本地或自托管模型 | `hermes model` → 自定义 endpoint | 验证 endpoint、模型名称和上下文长度 |
|
||||
| 想要多 provider 故障转移 | 先运行 `hermes model` | 基础对话正常后再添加路由和故障转移 |
|
||||
|
||||
**经验法则:** 如果 Hermes 无法完成一次正常对话,暂时不要添加更多功能。先让一次完整对话跑通,再逐步叠加 gateway、cron、skills、语音或路由。
|
||||
|
||||
---
|
||||
|
||||
## 1. 安装 Hermes Agent
|
||||
|
||||
**方式 A — pip(最简单):**
|
||||
|
||||
```bash
|
||||
pip install hermes-agent
|
||||
hermes postinstall # 可选:安装 Node.js、浏览器、ripgrep、ffmpeg 并运行 setup
|
||||
```
|
||||
|
||||
PyPI 发布版本跟踪带标签的版本(主/次版本发布),而非 `main` 分支上的每次提交。如需最新代码,请使用方式 B。
|
||||
|
||||
**方式 B — git 安装器(跟踪 main 分支):**
|
||||
|
||||
```bash
|
||||
# Linux / macOS / WSL2 / Android (Termux)
|
||||
curl -fsSL https://raw.githubusercontent.com/NousResearch/hermes-agent/main/scripts/install.sh | bash
|
||||
```
|
||||
|
||||
:::tip Android / Termux
|
||||
如果你在手机上安装,请参阅专门的 [Termux 指南](./termux.md),其中包含经过测试的手动安装步骤、支持的扩展功能以及当前 Android 特有的限制。
|
||||
:::
|
||||
|
||||
:::tip Windows 用户
|
||||
请先安装 [WSL2](https://learn.microsoft.com/en-us/windows/wsl/install),然后在 WSL2 终端中运行上述命令。
|
||||
:::
|
||||
|
||||
安装完成后,重新加载 shell:
|
||||
|
||||
```bash
|
||||
source ~/.bashrc # 或 source ~/.zshrc
|
||||
```
|
||||
|
||||
详细的安装选项、前置条件和故障排查,请参阅 [安装指南](./installation.md)。
|
||||
|
||||
## 2. 选择 Provider
|
||||
|
||||
这是最重要的配置步骤。使用 `hermes model` 以交互方式完成选择:
|
||||
|
||||
```bash
|
||||
hermes model
|
||||
```
|
||||
|
||||
:::tip 最简路径:Nous Portal
|
||||
一个订阅涵盖 300+ 个模型,以及 [Tool Gateway](../user-guide/features/tool-gateway.md)(网页搜索、图像生成、TTS、云端浏览器)。全新安装时:
|
||||
|
||||
```bash
|
||||
hermes setup --portal
|
||||
```
|
||||
|
||||
该命令一次性完成登录、设置 Nous 为 provider 并开启 Tool Gateway。
|
||||
:::
|
||||
|
||||
推荐默认选项:
|
||||
|
||||
| Provider | 说明 | 配置方式 |
|
||||
|----------|-----------|---------------|
|
||||
| **Nous Portal** | 订阅制,零配置 | 通过 `hermes model` 进行 OAuth 登录 |
|
||||
| **OpenAI Codex** | ChatGPT OAuth,使用 Codex 模型 | 通过 `hermes model` 进行设备码认证 |
|
||||
| **Anthropic** | 直接使用 Claude 模型——Max 计划 + 额外用量积分(OAuth),或按 token 付费的 API key | `hermes model` → OAuth 登录(需要 Max + 额外积分),或 Anthropic API key |
|
||||
| **OpenRouter** | 跨多个 provider 的多模型路由 | 输入 API key |
|
||||
| **Z.AI** | GLM / Zhipu 托管模型 | 设置 `GLM_API_KEY` / `ZAI_API_KEY` |
|
||||
| **Kimi / Moonshot** | Moonshot 托管的编程和对话模型 | 设置 `KIMI_API_KEY`(或 Kimi-Coding 专用的 `KIMI_CODING_API_KEY`) |
|
||||
| **Kimi / Moonshot China** | 中国区 Moonshot endpoint | 设置 `KIMI_CN_API_KEY` |
|
||||
| **Arcee AI** | Trinity 模型 | 设置 `ARCEEAI_API_KEY` |
|
||||
| **GMI Cloud** | 多模型直连 API | 设置 `GMI_API_KEY` |
|
||||
| **MiniMax (OAuth)** | 通过浏览器 OAuth 使用 MiniMax-M2.7,无需 API key | `hermes model` → MiniMax (OAuth) |
|
||||
| **MiniMax** | 国际版 MiniMax endpoint | 设置 `MINIMAX_API_KEY` |
|
||||
| **MiniMax China** | 中国区 MiniMax endpoint | 设置 `MINIMAX_CN_API_KEY` |
|
||||
| **Alibaba Cloud** | 通过 DashScope 使用 Qwen 模型 | 设置 `DASHSCOPE_API_KEY` |
|
||||
| **Hugging Face** | 通过统一路由器使用 20+ 开源模型(Qwen、DeepSeek、Kimi 等) | 设置 `HF_TOKEN` |
|
||||
| **AWS Bedrock** | 通过原生 Converse API 使用 Claude、Nova、Llama、DeepSeek | IAM 角色或 `aws configure`([指南](../guides/aws-bedrock.md)) |
|
||||
| **Kilo Code** | KiloCode 托管模型 | 设置 `KILOCODE_API_KEY` |
|
||||
| **OpenCode Zen** | 按需付费访问精选模型 | 设置 `OPENCODE_ZEN_API_KEY` |
|
||||
| **OpenCode Go** | $10/月订阅,访问开源模型 | 设置 `OPENCODE_GO_API_KEY` |
|
||||
| **DeepSeek** | 直接访问 DeepSeek API | 设置 `DEEPSEEK_API_KEY` |
|
||||
| **NVIDIA NIM** | 通过 build.nvidia.com 或本地 NIM 使用 Nemotron 模型 | 设置 `NVIDIA_API_KEY`(可选:`NVIDIA_BASE_URL`) |
|
||||
| **GitHub Copilot** | GitHub Copilot 订阅(GPT-5.x、Claude、Gemini 等) | 通过 `hermes model` 进行 OAuth,或设置 `COPILOT_GITHUB_TOKEN` / `GH_TOKEN` |
|
||||
| **GitHub Copilot ACP** | Copilot ACP agent 后端(在本地启动 `copilot` CLI) | `hermes model`(需要 `copilot` CLI + `copilot login`) |
|
||||
| **Vercel AI Gateway** | Vercel AI Gateway 路由 | 设置 `AI_GATEWAY_API_KEY` |
|
||||
| **Custom Endpoint** | VLLM、SGLang、Ollama 或任何兼容 OpenAI 的 API | 设置 base URL + API key |
|
||||
|
||||
对于大多数初次使用的用户:选择一个 provider,接受默认值(除非你明确知道为何要修改)。完整的 provider 目录及环境变量和配置步骤请参阅 [Providers](../integrations/providers.md) 页面。
|
||||
|
||||
:::caution 最低上下文要求:64K token
|
||||
Hermes Agent 要求模型至少具备 **64,000 个 token** 的上下文窗口。上下文窗口较小的模型无法为多步骤工具调用工作流维持足够的工作内存,启动时将被拒绝。大多数托管模型(Claude、GPT、Gemini、Qwen、DeepSeek)均轻松满足此要求。如果你运行本地模型,请将其上下文大小设置为至少 64K(例如 llama.cpp 使用 `--ctx-size 65536`,Ollama 使用 `-c 65536`)。
|
||||
:::
|
||||
|
||||
:::tip
|
||||
你可以随时通过 `hermes model` 切换 provider——没有锁定。所有支持的 provider 完整列表及配置详情,请参阅 [AI Providers](../integrations/providers.md)。
|
||||
:::
|
||||
|
||||
### 配置的存储方式
|
||||
|
||||
Hermes 将密钥与普通配置分开存储:
|
||||
|
||||
- **密钥和 token** → `~/.hermes/.env`
|
||||
- **非密钥配置** → `~/.hermes/config.yaml`
|
||||
|
||||
通过 CLI 设置值是最简便的方式,系统会自动将值写入正确的文件:
|
||||
|
||||
```bash
|
||||
hermes config set model anthropic/claude-opus-4.6
|
||||
hermes config set terminal.backend docker
|
||||
hermes config set OPENROUTER_API_KEY sk-or-...
|
||||
```
|
||||
|
||||
## 3. 运行第一次对话
|
||||
|
||||
```bash
|
||||
hermes # 经典 CLI
|
||||
hermes --tui # 现代 TUI(推荐)
|
||||
```
|
||||
|
||||
你会看到一个欢迎横幅,显示你的模型、可用工具和 skills。使用一个具体且易于验证的 prompt(提示词):
|
||||
|
||||
:::tip 选择你的界面
|
||||
Hermes 提供两种终端界面:经典的 `prompt_toolkit` CLI,以及更新的 [TUI](../user-guide/tui.md)(支持模态覆盖层、鼠标选择和非阻塞输入)。两者共享相同的会话、斜杠命令和配置——分别用 `hermes` 和 `hermes --tui` 试试看。
|
||||
:::
|
||||
|
||||
```
|
||||
Summarize this repo in 5 bullets and tell me what the main entrypoint is.
|
||||
```
|
||||
|
||||
```
|
||||
Check my current directory and tell me what looks like the main project file.
|
||||
```
|
||||
|
||||
```
|
||||
Help me set up a clean GitHub PR workflow for this codebase.
|
||||
```
|
||||
|
||||
**成功的标志:**
|
||||
|
||||
- 横幅显示你选择的模型/provider
|
||||
- Hermes 无错误地回复
|
||||
- 需要时能够使用工具(终端、文件读取、网页搜索)
|
||||
- 对话可以正常进行超过一轮
|
||||
|
||||
如果以上都正常,你已经过了最难的部分。
|
||||
|
||||
## 4. 验证会话功能
|
||||
|
||||
继续之前,确认恢复功能正常:
|
||||
|
||||
```bash
|
||||
hermes --continue # 恢复最近的会话
|
||||
hermes -c # 简写形式
|
||||
```
|
||||
|
||||
这应该会带你回到刚才的会话。如果不行,检查你是否在同一个 profile 下,以及会话是否实际已保存。当你同时管理多个配置或多台机器时,这一点很重要。
|
||||
|
||||
## 5. 尝试核心功能
|
||||
|
||||
### 使用终端
|
||||
|
||||
```
|
||||
❯ What's my disk usage? Show the top 5 largest directories.
|
||||
```
|
||||
|
||||
Agent 会代你执行终端命令并显示结果。
|
||||
|
||||
### 斜杠命令
|
||||
|
||||
输入 `/` 查看所有命令的自动补全下拉列表:
|
||||
|
||||
| 命令 | 功能 |
|
||||
|---------|-------------|
|
||||
| `/help` | 显示所有可用命令 |
|
||||
| `/tools` | 列出可用工具 |
|
||||
| `/model` | 交互式切换模型 |
|
||||
| `/personality pirate` | 尝试一个有趣的人格 |
|
||||
| `/save` | 保存对话 |
|
||||
|
||||
### 多行输入
|
||||
|
||||
按 `Alt+Enter`、`Ctrl+J` 或 `Shift+Enter` 换行。`Shift+Enter` 需要终端能将其作为独立序列发送(Kitty / foot / WezTerm / Ghostty 默认支持;iTerm2 / Alacritty / VS Code 终端需启用 Kitty 键盘协议)。`Alt+Enter` 和 `Ctrl+J` 在所有终端中均可使用。
|
||||
|
||||
### 中断 Agent
|
||||
|
||||
如果 agent 响应时间过长,输入新消息并按 Enter——这会中断当前任务并切换到你的新指令。`Ctrl+C` 同样有效。
|
||||
|
||||
## 6. 添加下一层功能
|
||||
|
||||
仅在基础对话正常后进行。按需选择:
|
||||
|
||||
### 机器人或共享助手
|
||||
|
||||
```bash
|
||||
hermes gateway setup # 交互式平台配置
|
||||
```
|
||||
|
||||
接入 [Telegram](/user-guide/messaging/telegram)、[Discord](/user-guide/messaging/discord)、[Slack](/user-guide/messaging/slack)、[WhatsApp](/user-guide/messaging/whatsapp)、[Signal](/user-guide/messaging/signal)、[Email](/user-guide/messaging/email)、[Home Assistant](/user-guide/messaging/homeassistant) 或 [Microsoft Teams](/user-guide/messaging/teams)。
|
||||
|
||||
### 自动化与工具
|
||||
|
||||
- `hermes tools` — 按平台调整工具访问权限
|
||||
- `hermes skills` — 浏览并安装可复用的工作流
|
||||
- Cron — 仅在机器人或 CLI 配置稳定后使用
|
||||
|
||||
### 沙箱终端
|
||||
|
||||
为了安全起见,在 Docker 容器或远程服务器中运行 agent:
|
||||
|
||||
```bash
|
||||
hermes config set terminal.backend docker # Docker 隔离
|
||||
hermes config set terminal.backend ssh # 远程服务器
|
||||
```
|
||||
|
||||
### 语音模式
|
||||
|
||||
```bash
|
||||
# 在 Hermes 安装目录下运行(curl 安装器在 Linux/macOS 上将其放置于
|
||||
# ~/.hermes/hermes-agent,在 Windows 上为 %LOCALAPPDATA%\hermes\hermes-agent):
|
||||
cd ~/.hermes/hermes-agent
|
||||
uv pip install -e ".[voice]"
|
||||
# 包含 faster-whisper,用于免费的本地语音转文字
|
||||
```
|
||||
|
||||
然后在 CLI 中输入:`/voice on`。按 `Ctrl+B` 开始录音。参阅 [语音模式](../user-guide/features/voice-mode.md)。
|
||||
|
||||
### Skills
|
||||
|
||||
```bash
|
||||
hermes skills search kubernetes
|
||||
hermes skills install openai/skills/k8s
|
||||
```
|
||||
|
||||
或在聊天会话中使用 `/skills`。
|
||||
|
||||
### MCP 服务器
|
||||
|
||||
```yaml
|
||||
# 添加到 ~/.hermes/config.yaml
|
||||
mcp_servers:
|
||||
github:
|
||||
command: npx
|
||||
args: ["-y", "@modelcontextprotocol/server-github"]
|
||||
env:
|
||||
GITHUB_PERSONAL_ACCESS_TOKEN: "ghp_xxx"
|
||||
```
|
||||
|
||||
### 编辑器集成(ACP)
|
||||
|
||||
ACP 支持已包含在标准 `[all]` 扩展中,因此 curl 安装器已默认包含。直接运行:
|
||||
|
||||
```bash
|
||||
hermes acp
|
||||
```
|
||||
|
||||
(如果安装时未包含 `[all]`,请先运行 `cd ~/.hermes/hermes-agent && uv pip install -e ".[acp]"`。)
|
||||
|
||||
参阅 [ACP 编辑器集成](../user-guide/features/acp.md)。
|
||||
|
||||
---
|
||||
|
||||
## 常见故障模式
|
||||
|
||||
以下是最容易浪费时间的问题:
|
||||
|
||||
| 现象 | 可能原因 | 解决方法 |
|
||||
|---|---|---|
|
||||
| Hermes 启动但回复为空或异常 | Provider 认证或模型选择有误 | 重新运行 `hermes model`,确认 provider、模型和认证信息 |
|
||||
| 自定义 endpoint "可用"但返回乱码 | base URL、模型名称有误,或实际上不兼容 OpenAI | 先用独立客户端验证该 endpoint |
|
||||
| Gateway 启动但无法收到消息 | Bot token、白名单或平台配置不完整 | 重新运行 `hermes gateway setup` 并检查 `hermes gateway status` |
|
||||
| `hermes --continue` 找不到旧会话 | 切换了 profile 或会话从未保存 | 检查 `hermes sessions list`,确认你在正确的 profile 下 |
|
||||
| 模型不可用或出现异常的故障转移行为 | Provider 路由或故障转移设置过于激进 | 在基础 provider 稳定之前关闭路由 |
|
||||
| `hermes doctor` 标记配置问题 | 配置值缺失或已过期 | 修复配置,在添加功能前重新测试普通对话 |
|
||||
|
||||
## 恢复工具包
|
||||
|
||||
当感觉有问题时,按以下顺序操作:
|
||||
|
||||
1. `hermes doctor`
|
||||
2. `hermes model`
|
||||
3. `hermes setup`
|
||||
4. `hermes sessions list`
|
||||
5. `hermes --continue`
|
||||
6. `hermes gateway status`
|
||||
|
||||
这个顺序能让你快速从"感觉哪里不对"回到已知的正常状态。
|
||||
|
||||
---
|
||||
|
||||
## 快速参考
|
||||
|
||||
| 命令 | 说明 |
|
||||
|---------|-------------|
|
||||
| `hermes` | 开始聊天 |
|
||||
| `hermes model` | 选择 LLM provider 和模型 |
|
||||
| `hermes tools` | 配置每个平台启用的工具 |
|
||||
| `hermes setup` | 完整配置向导(一次性配置所有内容) |
|
||||
| `hermes doctor` | 诊断问题 |
|
||||
| `hermes update` | 更新到最新版本 |
|
||||
| `hermes gateway` | 启动消息 gateway |
|
||||
| `hermes --continue` | 恢复上次会话 |
|
||||
|
||||
## 下一步
|
||||
|
||||
- **[CLI 指南](../user-guide/cli.md)** — 掌握终端界面
|
||||
- **[配置](../user-guide/configuration.md)** — 自定义你的配置
|
||||
- **[消息 Gateway](../user-guide/messaging/index.md)** — 接入 Telegram、Discord、Slack、WhatsApp、Signal、Email、Home Assistant、Teams 等
|
||||
- **[工具与工具集](../user-guide/features/tools.md)** — 探索可用功能
|
||||
- **[AI Providers](../integrations/providers.md)** — 完整 provider 列表及配置详情
|
||||
- **[Skills 系统](../user-guide/features/skills.md)** — 可复用的工作流与知识
|
||||
- **[技巧与最佳实践](../guides/tips.md)** — 高级用户技巧
|
||||
@ -0,0 +1,242 @@
|
||||
---
|
||||
sidebar_position: 3
|
||||
title: "Android / Termux"
|
||||
description: "通过 Termux 在 Android 手机上直接运行 Hermes Agent"
|
||||
---
|
||||
|
||||
# 在 Android 上通过 Termux 运行 Hermes
|
||||
|
||||
这是在 Android 手机上通过 [Termux](https://termux.dev/) 直接运行 Hermes Agent 的已验证路径。
|
||||
|
||||
它为你提供手机上可用的本地 CLI,以及目前已知可在 Android 上干净安装的核心扩展功能。
|
||||
|
||||
## 已验证路径支持哪些功能?
|
||||
|
||||
已验证的 Termux 安装包含:
|
||||
- Hermes CLI
|
||||
- cron 支持
|
||||
- PTY(伪终端)/后台终端支持
|
||||
- Telegram gateway 支持(手动 / 尽力而为的后台运行)
|
||||
- MCP 支持
|
||||
- Honcho 记忆支持
|
||||
- ACP 支持
|
||||
|
||||
具体对应以下命令:
|
||||
|
||||
```bash
|
||||
python -m pip install -e '.[termux]' -c constraints-termux.txt
|
||||
```
|
||||
|
||||
## 哪些功能尚未纳入已验证路径?
|
||||
|
||||
部分功能仍依赖桌面/服务器风格的依赖项,这些依赖项尚未为 Android 发布,或尚未在手机上验证:
|
||||
|
||||
- `.[all]` 目前不支持 Android
|
||||
- `voice` 扩展被 `faster-whisper -> ctranslate2` 阻塞,`ctranslate2` 未发布 Android wheel 包
|
||||
- 自动浏览器 / Playwright 引导在 Termux 安装程序中被跳过
|
||||
- 基于 Docker 的终端隔离在 Termux 内不可用
|
||||
- Android 可能仍会挂起 Termux 后台任务,因此 gateway 持久化是尽力而为,而非正常的托管服务
|
||||
|
||||
这并不妨碍 Hermes 作为手机原生 CLI agent 正常工作——只是意味着推荐的移动端安装有意比桌面/服务器安装更精简。
|
||||
|
||||
---
|
||||
|
||||
## 方式一:一行安装命令
|
||||
|
||||
Hermes 现已内置 Termux 感知的安装路径:
|
||||
|
||||
```bash
|
||||
curl -fsSL https://raw.githubusercontent.com/NousResearch/hermes-agent/main/scripts/install.sh | bash
|
||||
```
|
||||
|
||||
在 Termux 上,安装程序会自动:
|
||||
- 使用 `pkg` 安装系统包
|
||||
- 使用 `python -m venv` 创建虚拟环境
|
||||
- 优先尝试较大的 `.[termux-all]` 扩展,失败后回退到较小的 `.[termux]` 扩展(再次失败则进行基础安装)——curl 安装程序自动按此顺序执行
|
||||
- 将 `hermes` 链接到 `$PREFIX/bin`,使其保留在 Termux PATH 中
|
||||
- 跳过未经验证的浏览器 / WhatsApp 引导
|
||||
|
||||
如果你需要显式命令或需要调试失败的安装,请使用下方的手动安装路径。
|
||||
|
||||
---
|
||||
|
||||
## 方式二:手动安装(完全显式)
|
||||
|
||||
### 1. 更新 Termux 并安装系统包
|
||||
|
||||
```bash
|
||||
pkg update
|
||||
pkg install -y git python clang rust make pkg-config libffi openssl nodejs ripgrep ffmpeg
|
||||
```
|
||||
|
||||
各包用途说明:
|
||||
- `python` — 运行时 + 虚拟环境支持
|
||||
- `git` — 克隆/更新仓库
|
||||
- `clang`、`rust`、`make`、`pkg-config`、`libffi`、`openssl` — 在 Android 上构建部分 Python 依赖所需
|
||||
- `nodejs` — 可选的 Node 运行时,用于已验证核心路径之外的实验
|
||||
- `ripgrep` — 快速文件搜索
|
||||
- `ffmpeg` — 媒体 / TTS 转换
|
||||
|
||||
### 2. 克隆 Hermes
|
||||
|
||||
```bash
|
||||
git clone --recurse-submodules https://github.com/NousResearch/hermes-agent.git
|
||||
cd hermes-agent
|
||||
```
|
||||
|
||||
如果你已经克隆但未包含子模块:
|
||||
|
||||
```bash
|
||||
git submodule update --init --recursive
|
||||
```
|
||||
|
||||
### 3. 创建虚拟环境
|
||||
|
||||
```bash
|
||||
python -m venv venv
|
||||
source venv/bin/activate
|
||||
export ANDROID_API_LEVEL="$(getprop ro.build.version.sdk)"
|
||||
python -m pip install --upgrade pip setuptools wheel
|
||||
```
|
||||
|
||||
`ANDROID_API_LEVEL` 对于基于 Rust / maturin 的包(如 `jiter`)非常重要。
|
||||
|
||||
### 4. 安装已验证的 Termux 包
|
||||
|
||||
```bash
|
||||
python -m pip install -e '.[termux]' -c constraints-termux.txt
|
||||
```
|
||||
|
||||
如果你只需要最小化的核心 agent,以下命令同样有效:
|
||||
|
||||
```bash
|
||||
python -m pip install -e '.' -c constraints-termux.txt
|
||||
```
|
||||
|
||||
### 5. 将 `hermes` 添加到 Termux PATH
|
||||
|
||||
```bash
|
||||
ln -sf "$PWD/venv/bin/hermes" "$PREFIX/bin/hermes"
|
||||
```
|
||||
|
||||
`$PREFIX/bin` 在 Termux 中已默认在 PATH 中,因此这样做可以让 `hermes` 命令在新 shell 中持续可用,无需每次重新激活虚拟环境。
|
||||
|
||||
### 6. 验证安装
|
||||
|
||||
```bash
|
||||
hermes version
|
||||
hermes doctor
|
||||
```
|
||||
|
||||
### 7. 启动 Hermes
|
||||
|
||||
```bash
|
||||
hermes
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 推荐的后续配置
|
||||
|
||||
### 配置模型
|
||||
|
||||
```bash
|
||||
hermes model
|
||||
```
|
||||
|
||||
或直接在 `~/.hermes/.env` 中设置密钥。
|
||||
|
||||
### 稍后重新运行完整的交互式设置向导
|
||||
|
||||
```bash
|
||||
hermes setup
|
||||
```
|
||||
|
||||
### 手动安装可选的 Node 依赖
|
||||
|
||||
已验证的 Termux 路径有意跳过 Node/浏览器引导。如果你之后想尝试浏览器工具:
|
||||
|
||||
```bash
|
||||
pkg install nodejs-lts
|
||||
npm install
|
||||
```
|
||||
|
||||
浏览器工具会自动将 Termux 目录(`/data/data/com.termux/files/usr/bin`)纳入 PATH 搜索,因此无需额外配置 PATH 即可发现 `agent-browser` 和 `npx`。
|
||||
|
||||
在另有文档说明之前,请将 Android 上的浏览器 / WhatsApp 工具视为实验性功能。
|
||||
|
||||
---
|
||||
|
||||
## 故障排查
|
||||
|
||||
### 安装 `.[all]` 时出现 `No solution found`
|
||||
|
||||
改用已验证的 Termux 包:
|
||||
|
||||
```bash
|
||||
python -m pip install -e '.[termux]' -c constraints-termux.txt
|
||||
```
|
||||
|
||||
当前阻塞原因是 `voice` 扩展:
|
||||
- `voice` 依赖 `faster-whisper`
|
||||
- `faster-whisper` 依赖 `ctranslate2`
|
||||
- `ctranslate2` 未发布 Android wheel 包
|
||||
|
||||
### `uv pip install` 在 Android 上失败
|
||||
|
||||
改用标准库 venv + `pip` 的 Termux 路径:
|
||||
|
||||
```bash
|
||||
python -m venv venv
|
||||
source venv/bin/activate
|
||||
export ANDROID_API_LEVEL="$(getprop ro.build.version.sdk)"
|
||||
python -m pip install --upgrade pip setuptools wheel
|
||||
python -m pip install -e '.[termux]' -c constraints-termux.txt
|
||||
```
|
||||
|
||||
### `jiter` / `maturin` 报错提示缺少 `ANDROID_API_LEVEL`
|
||||
|
||||
在安装前显式设置 API 级别:
|
||||
|
||||
```bash
|
||||
export ANDROID_API_LEVEL="$(getprop ro.build.version.sdk)"
|
||||
python -m pip install -e '.[termux]' -c constraints-termux.txt
|
||||
```
|
||||
|
||||
### `hermes doctor` 提示缺少 ripgrep 或 Node
|
||||
|
||||
使用 Termux 包安装:
|
||||
|
||||
```bash
|
||||
pkg install ripgrep nodejs
|
||||
```
|
||||
|
||||
### 安装 Python 包时构建失败
|
||||
|
||||
确保已安装构建工具链:
|
||||
|
||||
```bash
|
||||
pkg install clang rust make pkg-config libffi openssl
|
||||
```
|
||||
|
||||
然后重试:
|
||||
|
||||
```bash
|
||||
python -m pip install -e '.[termux]' -c constraints-termux.txt
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 手机上的已知限制
|
||||
|
||||
- Docker 后端不可用
|
||||
- 通过 `faster-whisper` 进行的本地语音转录在已验证路径中不可用
|
||||
- 安装程序有意跳过浏览器自动化配置
|
||||
- 部分可选扩展可能可用,但目前仅 `.[termux]` 和 `.[termux-all]` 被记录为已验证的 Android 安装包
|
||||
|
||||
如果你遇到新的 Android 特定问题,请在 GitHub 上提交 issue,并附上:
|
||||
- 你的 Android 版本
|
||||
- `termux-info`
|
||||
- `python --version`
|
||||
- `hermes doctor`
|
||||
- 确切的安装命令及完整错误输出
|
||||
@ -0,0 +1,259 @@
|
||||
---
|
||||
sidebar_position: 3
|
||||
title: "更新与卸载"
|
||||
description: "如何将 Hermes Agent 更新至最新版本或将其卸载"
|
||||
---
|
||||
|
||||
# 更新与卸载
|
||||
|
||||
## 更新
|
||||
|
||||
### Git 安装方式
|
||||
|
||||
使用单条命令更新至最新版本:
|
||||
|
||||
```bash
|
||||
hermes update
|
||||
```
|
||||
|
||||
此命令会从 `main` 拉取最新代码、更新依赖项,并提示你配置自上次更新以来新增的选项。
|
||||
|
||||
### pip 安装方式
|
||||
|
||||
PyPI 发布版本跟踪**带标签的版本**(主版本和次版本发布),而非 `main` 上的每次提交。检查更新并升级:
|
||||
|
||||
```bash
|
||||
hermes update --check # 查看 PyPI 上是否有更新的版本
|
||||
hermes update # 执行 pip install --upgrade hermes-agent
|
||||
```
|
||||
|
||||
或手动执行:
|
||||
|
||||
```bash
|
||||
pip install --upgrade hermes-agent # 或:uv pip install --upgrade hermes-agent
|
||||
```
|
||||
|
||||
:::tip
|
||||
`hermes update` 会自动检测新的配置选项并提示你添加。如果跳过了该提示,可手动运行 `hermes config check` 查看缺失的选项,再运行 `hermes config migrate` 以交互方式添加。
|
||||
:::
|
||||
|
||||
### 更新过程(Git 安装方式)
|
||||
|
||||
运行 `hermes update` 时,将依次执行以下步骤:
|
||||
|
||||
1. **配对数据快照** — 保存一份轻量级的更新前状态快照(涵盖 `~/.hermes/pairing/`、飞书评论规则及其他运行时修改的状态文件)。可通过 [快照与回滚](../user-guide/checkpoints-and-rollback.md) 中描述的快照恢复流程进行恢复,或从 Hermes 写入 `~/.hermes/` 目录旁的最新快速快照 zip 文件中提取。
|
||||
2. **Git pull** — 从 `main` 分支拉取最新代码并更新子模块
|
||||
3. **依赖安装** — 运行 `uv pip install -e ".[all]"` 以获取新增或变更的依赖项
|
||||
4. **配置迁移** — 检测自当前版本以来新增的配置选项并提示设置
|
||||
5. **Gateway 自动重启** — 更新完成后刷新正在运行的 gateway,使新代码立即生效。由服务管理的 gateway(Linux 上的 systemd、macOS 上的 launchd)通过服务管理器重启;手动启动的 gateway 在 Hermes 能将运行中的 PID 映射回某个 profile 时会自动重新启动。
|
||||
|
||||
### 仅预览:`hermes update --check`
|
||||
|
||||
想在拉取前确认是否有更新?运行 `hermes update --check` — 对于 Git 安装方式,它会获取并与 `origin/main` 比较提交;对于 pip 安装方式,它会查询 PyPI 上的最新版本。不修改任何文件,不重启 gateway。适合在以"是否有更新"为条件的脚本和 cron 任务中使用。
|
||||
|
||||
### 完整更新前备份:`--backup`
|
||||
|
||||
对于高价值 profile(生产环境 gateway、团队共享安装),可选择在拉取前对 `HERMES_HOME`(配置、认证、会话、技能、配对数据)进行完整备份:
|
||||
|
||||
```bash
|
||||
hermes update --backup
|
||||
```
|
||||
|
||||
或将其设为每次运行的默认行为:
|
||||
|
||||
```yaml
|
||||
# ~/.hermes/config.yaml
|
||||
updates:
|
||||
pre_update_backup: true
|
||||
```
|
||||
|
||||
`--backup` 在早期版本中是始终开启的行为,但在大型 home 目录上会给每次更新增加数分钟时间,因此现已改为按需启用。上述轻量级配对数据快照仍会无条件执行。
|
||||
|
||||
### Windows:另一个 `hermes.exe` 正在运行
|
||||
|
||||
在 Windows 上,如果 `hermes update` 检测到另一个 `hermes.exe` 进程持有 venv 入口点可执行文件的句柄,它将拒绝运行 — 最常见的情况是 Hermes Desktop 应用启动的后端进程、另一个终端中打开的 `hermes` REPL,或正在运行的 gateway:
|
||||
|
||||
```
|
||||
$ hermes update
|
||||
✗ Another hermes.exe is running:
|
||||
PID 12345 hermes.exe
|
||||
|
||||
Updating now would fail to overwrite ...\venv\Scripts\hermes.exe because
|
||||
Windows blocks REPLACE on a running executable.
|
||||
|
||||
Close Hermes Desktop, exit any open `hermes` REPLs, and
|
||||
stop the gateway (`hermes gateway stop`) before retrying.
|
||||
Override with `hermes update --force` if you've already
|
||||
confirmed those processes will not write to the venv.
|
||||
```
|
||||
|
||||
关闭列出的进程后重试。如果你确定并发进程不会造成干扰(极少见 — 通常仅在杀毒软件 shim 被误判时有用),可传入 `--force` 跳过检查。此时更新程序仍会以指数退避方式重试 `.exe` 重命名操作,对于顽固的文件锁,会通过 `MoveFileEx(MOVEFILE_DELAY_UNTIL_REBOOT)` 将替换操作安排在下次重启时执行,以确保更新能够完成。
|
||||
|
||||
预期输出如下:
|
||||
|
||||
```
|
||||
$ hermes update
|
||||
Updating Hermes Agent...
|
||||
📥 Pulling latest code...
|
||||
Already up to date. (or: Updating abc1234..def5678)
|
||||
📦 Updating dependencies...
|
||||
✅ Dependencies updated
|
||||
🔍 Checking for new config options...
|
||||
✅ Config is up to date (or: Found 2 new options — running migration...)
|
||||
🔄 Restarting gateways...
|
||||
✅ Gateway restarted
|
||||
✅ Hermes Agent updated successfully!
|
||||
```
|
||||
|
||||
### 更新后建议的验证步骤
|
||||
|
||||
`hermes update` 处理主要的更新流程,但快速验证可确认一切正常落地:
|
||||
|
||||
1. `git status --short` — 若工作树出现意外的脏状态,请在继续前检查
|
||||
2. `hermes doctor` — 检查配置、依赖项和服务健康状态
|
||||
3. `hermes --version` — 确认版本已按预期更新
|
||||
4. 如果使用 gateway:`hermes gateway status`
|
||||
5. 如果 `doctor` 报告 npm audit 问题:在标记的目录中运行 `npm audit fix`
|
||||
|
||||
:::warning 更新后工作树出现脏状态
|
||||
如果 `hermes update` 后 `git status --short` 显示意外变更,请在继续前停下来检查。这通常意味着本地修改被重新应用到了更新后的代码之上,或依赖步骤刷新了锁文件。
|
||||
:::
|
||||
|
||||
### 终端在更新中途断开连接
|
||||
|
||||
`hermes update` 针对意外终端断开进行了保护:
|
||||
|
||||
- 更新会忽略 `SIGHUP`,因此关闭 SSH 会话或终端窗口不再会在安装中途终止它。`pip` 和 `git` 子进程继承此保护,因此 Python 环境不会因连接断开而处于半安装状态。
|
||||
- 更新运行期间,所有输出会同步镜像到 `~/.hermes/logs/update.log`。如果终端消失,重新连接后检查日志,确认更新是否完成以及 gateway 重启是否成功:
|
||||
|
||||
```bash
|
||||
tail -f ~/.hermes/logs/update.log
|
||||
```
|
||||
|
||||
- `Ctrl-C`(SIGINT)和系统关机(SIGTERM)仍会被响应 — 这些是主动取消操作,而非意外中断。
|
||||
|
||||
你不再需要将 `hermes update` 包裹在 `screen` 或 `tmux` 中来应对终端断开。
|
||||
|
||||
### 查看当前版本
|
||||
|
||||
```bash
|
||||
hermes version
|
||||
```
|
||||
|
||||
与 [GitHub releases 页面](https://github.com/NousResearch/hermes-agent/releases) 上的最新版本进行比较。
|
||||
|
||||
### 从消息平台更新
|
||||
|
||||
你也可以直接从 Telegram、Discord、Slack、WhatsApp 或 Teams 发送以下命令进行更新:
|
||||
|
||||
```
|
||||
/update
|
||||
```
|
||||
|
||||
此命令会拉取最新代码、更新依赖项并重启正在运行的 gateway。Bot 在重启期间会短暂下线(通常为 5–15 秒),之后恢复服务。
|
||||
|
||||
### 手动更新
|
||||
|
||||
如果你是手动安装的(未使用快速安装脚本):
|
||||
|
||||
```bash
|
||||
cd /path/to/hermes-agent
|
||||
export VIRTUAL_ENV="$(pwd)/venv"
|
||||
|
||||
# Pull latest code
|
||||
git pull origin main
|
||||
|
||||
# Reinstall (picks up new dependencies)
|
||||
uv pip install -e ".[all]"
|
||||
|
||||
# Check for new config options
|
||||
hermes config check
|
||||
hermes config migrate # Interactively add any missing options
|
||||
```
|
||||
|
||||
### 回滚说明
|
||||
|
||||
如果更新引入了问题,可以回滚到之前的版本:
|
||||
|
||||
```bash
|
||||
cd /path/to/hermes-agent
|
||||
|
||||
# List recent versions
|
||||
git log --oneline -10
|
||||
|
||||
# Roll back to a specific commit
|
||||
git checkout <commit-hash>
|
||||
git submodule update --init --recursive
|
||||
uv pip install -e ".[all]"
|
||||
|
||||
# Restart the gateway if running
|
||||
hermes gateway restart
|
||||
```
|
||||
|
||||
回滚到特定发布标签:
|
||||
|
||||
```bash
|
||||
git checkout v0.6.0
|
||||
git submodule update --init --recursive
|
||||
uv pip install -e ".[all]"
|
||||
```
|
||||
|
||||
:::warning
|
||||
如果新增了配置选项,回滚可能导致配置不兼容。回滚后运行 `hermes config check`,如果遇到错误,请从 `config.yaml` 中删除无法识别的选项。
|
||||
:::
|
||||
|
||||
### Nix 用户注意事项
|
||||
|
||||
如果你通过 Nix flake 安装,更新由 Nix 包管理器负责:
|
||||
|
||||
```bash
|
||||
# Update the flake input
|
||||
nix flake update hermes-agent
|
||||
|
||||
# Or rebuild with the latest
|
||||
nix profile upgrade hermes-agent
|
||||
```
|
||||
|
||||
Nix 安装是不可变的 — 回滚由 Nix 的 generation 系统处理:
|
||||
|
||||
```bash
|
||||
nix profile rollback
|
||||
```
|
||||
|
||||
详情参见 [Nix 安装](./nix-setup.md)。
|
||||
|
||||
---
|
||||
|
||||
## 卸载
|
||||
|
||||
### Git 安装方式
|
||||
|
||||
```bash
|
||||
hermes uninstall
|
||||
```
|
||||
|
||||
卸载程序会提供选项,让你保留配置文件(`~/.hermes/`)以便将来重新安装。
|
||||
|
||||
### pip 安装方式
|
||||
|
||||
```bash
|
||||
pip uninstall hermes-agent
|
||||
rm -rf ~/.hermes # 可选 — 如计划重新安装则保留
|
||||
```
|
||||
|
||||
### 手动卸载
|
||||
|
||||
```bash
|
||||
rm -f ~/.local/bin/hermes
|
||||
rm -rf /path/to/hermes-agent
|
||||
rm -rf ~/.hermes # 可选 — 如计划重新安装则保留
|
||||
```
|
||||
|
||||
:::info
|
||||
如果你将 gateway 安装为系统服务,请先停止并禁用它:
|
||||
```bash
|
||||
hermes gateway stop
|
||||
# Linux: systemctl --user disable hermes-gateway
|
||||
# macOS: launchctl remove ai.hermes.gateway
|
||||
```
|
||||
:::
|
||||
@ -0,0 +1,266 @@
|
||||
---
|
||||
sidebar_position: 11
|
||||
title: "用 Cron 自动化一切"
|
||||
description: "使用 Hermes cron 的真实自动化模式——监控、报告、数据管道与多技能工作流"
|
||||
---
|
||||
|
||||
# 用 Cron 自动化一切
|
||||
|
||||
[每日简报机器人教程](/guides/daily-briefing-bot)涵盖了基础内容。本指南更进一步——五种真实的自动化模式,可直接改造用于你自己的工作流。
|
||||
|
||||
完整功能参考请见 [定时任务(Cron)](/user-guide/features/cron)。
|
||||
|
||||
:::info 核心概念
|
||||
Cron 任务在全新的 agent 会话中运行,不保留当前对话的任何记忆。Prompt(提示词)必须**完全自包含**——把 agent 需要知道的一切都写进去。
|
||||
:::
|
||||
|
||||
:::tip 不需要 LLM?你有两种零 token 方案。
|
||||
- **循环看门狗**:脚本本身已能生成精确消息(内存告警、磁盘告警、心跳)时,使用 [纯脚本 cron 任务](/guides/cron-script-only)。相同的调度器,无需 LLM。你可以在对话中让 Hermes 帮你设置——`cronjob` 工具知道何时选择 `no_agent=True` 并为你编写脚本。
|
||||
- **已在运行的脚本发起的一次性通知**(CI 步骤、post-commit hook、部署脚本、外部调度的监控):使用 [`hermes send`](/guides/pipe-script-output) 将 stdout 或文件直接推送到 Telegram / Discord / Slack 等,无需设置 cron 条目。
|
||||
:::
|
||||
|
||||
---
|
||||
|
||||
## 模式一:网站变更监控
|
||||
|
||||
监视某个 URL 的变化,仅在内容发生变化时发送通知。
|
||||
|
||||
`script` 参数是这里的秘密武器。每次执行前会先运行一个 Python 脚本,其 stdout 作为上下文传给 agent。脚本负责机械性工作(抓取、对比差异);agent 负责推理(这个变化是否值得关注?)。
|
||||
|
||||
创建监控脚本:
|
||||
|
||||
```bash
|
||||
mkdir -p ~/.hermes/scripts
|
||||
```
|
||||
|
||||
```python title="~/.hermes/scripts/watch-site.py"
|
||||
import hashlib, json, os, urllib.request
|
||||
|
||||
URL = "https://example.com/pricing"
|
||||
STATE_FILE = os.path.expanduser("~/.hermes/scripts/.watch-site-state.json")
|
||||
|
||||
# Fetch current content
|
||||
req = urllib.request.Request(URL, headers={"User-Agent": "Hermes-Monitor/1.0"})
|
||||
content = urllib.request.urlopen(req, timeout=30).read().decode()
|
||||
current_hash = hashlib.sha256(content.encode()).hexdigest()
|
||||
|
||||
# Load previous state
|
||||
prev_hash = None
|
||||
if os.path.exists(STATE_FILE):
|
||||
with open(STATE_FILE) as f:
|
||||
prev_hash = json.load(f).get("hash")
|
||||
|
||||
# Save current state
|
||||
with open(STATE_FILE, "w") as f:
|
||||
json.dump({"hash": current_hash, "url": URL}, f)
|
||||
|
||||
# Output for the agent
|
||||
if prev_hash and prev_hash != current_hash:
|
||||
print(f"CHANGE DETECTED on {URL}")
|
||||
print(f"Previous hash: {prev_hash}")
|
||||
print(f"Current hash: {current_hash}")
|
||||
print(f"\nCurrent content (first 2000 chars):\n{content[:2000]}")
|
||||
else:
|
||||
print("NO_CHANGE")
|
||||
```
|
||||
|
||||
设置 cron 任务:
|
||||
|
||||
```bash
|
||||
/cron add "every 1h" "If the script output says CHANGE DETECTED, summarize what changed on the page and why it might matter. If it says NO_CHANGE, respond with just [SILENT]." --script ~/.hermes/scripts/watch-site.py --name "Pricing monitor" --deliver telegram
|
||||
```
|
||||
|
||||
:::tip `[SILENT]` 技巧
|
||||
当 agent 的最终响应包含 `[SILENT]` 时,投递会被抑制。这意味着只有在真正发生变化时你才会收到通知——安静时段不会产生垃圾消息。
|
||||
:::
|
||||
|
||||
---
|
||||
|
||||
## 模式二:每周报告
|
||||
|
||||
从多个来源汇总信息,生成格式化摘要。每周运行一次,投递到你的主频道。
|
||||
|
||||
```bash
|
||||
/cron add "0 9 * * 1" "Generate a weekly report covering:
|
||||
|
||||
1. Search the web for the top 5 AI news stories from the past week
|
||||
2. Search GitHub for trending repositories in the 'machine-learning' topic
|
||||
3. Check Hacker News for the most discussed AI/ML posts
|
||||
|
||||
Format as a clean summary with sections for each source. Include links.
|
||||
Keep it under 500 words — highlight only what matters." --name "Weekly AI digest" --deliver telegram
|
||||
```
|
||||
|
||||
通过 CLI:
|
||||
|
||||
```bash
|
||||
hermes cron create "0 9 * * 1" \
|
||||
"Generate a weekly report covering the top AI news, trending ML GitHub repos, and most-discussed HN posts. Format with sections, include links, keep under 500 words." \
|
||||
--name "Weekly AI digest" \
|
||||
--deliver telegram
|
||||
```
|
||||
|
||||
`0 9 * * 1` 是标准 cron 表达式:每周一上午 9:00。
|
||||
|
||||
---
|
||||
|
||||
## 模式三:GitHub 仓库监控
|
||||
|
||||
监控某个仓库的新 issue、PR 或 release。
|
||||
|
||||
```bash
|
||||
/cron add "every 6h" "Check the GitHub repository NousResearch/hermes-agent for:
|
||||
- New issues opened in the last 6 hours
|
||||
- New PRs opened or merged in the last 6 hours
|
||||
- Any new releases
|
||||
|
||||
Use the terminal to run gh commands:
|
||||
gh issue list --repo NousResearch/hermes-agent --state open --json number,title,author,createdAt --limit 10
|
||||
gh pr list --repo NousResearch/hermes-agent --state all --json number,title,author,createdAt,mergedAt --limit 10
|
||||
|
||||
Filter to only items from the last 6 hours. If nothing new, respond with [SILENT].
|
||||
Otherwise, provide a concise summary of the activity." --name "Repo watcher" --deliver discord
|
||||
```
|
||||
|
||||
:::warning 自包含的 Prompt
|
||||
注意 prompt 中包含了精确的 `gh` 命令。cron agent 不记得之前的运行记录或你的偏好——把所有内容都明确写出来。
|
||||
:::
|
||||
|
||||
---
|
||||
|
||||
## 模式四:数据采集管道
|
||||
|
||||
定期抓取数据、保存到文件,并随时间检测趋势。此模式将脚本(用于采集)与 agent(用于分析)结合使用。
|
||||
|
||||
```python title="~/.hermes/scripts/collect-prices.py"
|
||||
import json, os, urllib.request
|
||||
from datetime import datetime
|
||||
|
||||
DATA_DIR = os.path.expanduser("~/.hermes/data/prices")
|
||||
os.makedirs(DATA_DIR, exist_ok=True)
|
||||
|
||||
# Fetch current data (example: crypto prices)
|
||||
url = "https://api.coingecko.com/api/v3/simple/price?ids=bitcoin,ethereum&vs_currencies=usd"
|
||||
data = json.loads(urllib.request.urlopen(url, timeout=30).read())
|
||||
|
||||
# Append to history file
|
||||
entry = {"timestamp": datetime.now().isoformat(), "prices": data}
|
||||
history_file = os.path.join(DATA_DIR, "history.jsonl")
|
||||
with open(history_file, "a") as f:
|
||||
f.write(json.dumps(entry) + "\n")
|
||||
|
||||
# Load recent history for analysis
|
||||
lines = open(history_file).readlines()
|
||||
recent = [json.loads(l) for l in lines[-24:]] # Last 24 data points
|
||||
|
||||
# Output for the agent
|
||||
print(f"Current: BTC=${data['bitcoin']['usd']}, ETH=${data['ethereum']['usd']}")
|
||||
print(f"Data points collected: {len(lines)} total, showing last {len(recent)}")
|
||||
print(f"\nRecent history:")
|
||||
for r in recent[-6:]:
|
||||
print(f" {r['timestamp']}: BTC=${r['prices']['bitcoin']['usd']}, ETH=${r['prices']['ethereum']['usd']}")
|
||||
```
|
||||
|
||||
```bash
|
||||
/cron add "every 1h" "Analyze the price data from the script output. Report:
|
||||
1. Current prices
|
||||
2. Trend direction over the last 6 data points (up/down/flat)
|
||||
3. Any notable movements (>5% change)
|
||||
|
||||
If prices are flat and nothing notable, respond with [SILENT].
|
||||
If there's a significant move, explain what happened." \
|
||||
--script ~/.hermes/scripts/collect-prices.py \
|
||||
--name "Price tracker" \
|
||||
--deliver telegram
|
||||
```
|
||||
|
||||
脚本负责机械性的数据采集;agent 在此之上添加推理层。
|
||||
|
||||
---
|
||||
|
||||
## 模式五:多技能工作流
|
||||
|
||||
将多个 skill(技能)串联起来,完成复杂的定时任务。Skill 按顺序加载,然后执行 prompt。
|
||||
|
||||
```bash
|
||||
# 使用 arxiv skill 查找论文,再用 obsidian skill 保存笔记
|
||||
/cron add "0 8 * * *" "Search arXiv for the 3 most interesting papers on 'language model reasoning' from the past day. For each paper, create an Obsidian note with the title, authors, abstract summary, and key contribution." \
|
||||
--skill arxiv \
|
||||
--skill obsidian \
|
||||
--name "Paper digest"
|
||||
```
|
||||
|
||||
直接通过工具调用:
|
||||
|
||||
```python
|
||||
cronjob(
|
||||
action="create",
|
||||
skills=["arxiv", "obsidian"],
|
||||
prompt="Search arXiv for papers on 'language model reasoning' from the past day. Save the top 3 as Obsidian notes.",
|
||||
schedule="0 8 * * *",
|
||||
name="Paper digest",
|
||||
deliver="local"
|
||||
)
|
||||
```
|
||||
|
||||
Skill 按顺序加载——先加载 `arxiv`(教 agent 如何搜索论文),再加载 `obsidian`(教 agent 如何写笔记)。Prompt 将二者串联起来。
|
||||
|
||||
---
|
||||
|
||||
## 管理你的任务
|
||||
|
||||
```bash
|
||||
# 列出所有活跃任务
|
||||
/cron list
|
||||
|
||||
# 立即触发某个任务(用于测试)
|
||||
/cron run <job_id>
|
||||
|
||||
# 暂停任务而不删除
|
||||
/cron pause <job_id>
|
||||
|
||||
# 编辑运行中任务的调度或 prompt
|
||||
/cron edit <job_id> --schedule "every 4h"
|
||||
/cron edit <job_id> --prompt "Updated task description"
|
||||
|
||||
# 为现有任务添加或移除 skill
|
||||
/cron edit <job_id> --skill arxiv --skill obsidian
|
||||
/cron edit <job_id> --clear-skills
|
||||
|
||||
# 永久删除任务
|
||||
/cron remove <job_id>
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 投递目标
|
||||
|
||||
`--deliver` 标志控制结果发送到哪里:
|
||||
|
||||
| 目标 | 示例 | 使用场景 |
|
||||
|--------|---------|----------|
|
||||
| `origin` | `--deliver origin` | 创建该任务的对话(默认) |
|
||||
| `local` | `--deliver local` | 仅保存到本地文件 |
|
||||
| `telegram` | `--deliver telegram` | 你的 Telegram 主频道 |
|
||||
| `discord` | `--deliver discord` | 你的 Discord 主频道 |
|
||||
| `slack` | `--deliver slack` | 你的 Slack 主频道 |
|
||||
| 指定对话 | `--deliver telegram:-1001234567890` | 特定 Telegram 群组 |
|
||||
| 线程投递 | `--deliver telegram:-1001234567890:17585` | 特定 Telegram 话题线程 |
|
||||
|
||||
---
|
||||
|
||||
## 使用技巧
|
||||
|
||||
**让 prompt 完全自包含。** Cron 任务中的 agent 不记得你的任何对话。把 URL、仓库名、格式偏好和投递说明直接写进 prompt。
|
||||
|
||||
**大量使用 `[SILENT]`。** 对于监控类任务,始终加上类似"如果没有变化,回复 `[SILENT]`"的指令,防止通知噪音。
|
||||
|
||||
**用脚本做数据采集。** `script` 参数让 Python 脚本处理枯燥的部分(HTTP 请求、文件 I/O、状态追踪)。Agent 只看到脚本的 stdout,并对其进行推理。这比让 agent 自己抓取更省钱、更可靠。
|
||||
|
||||
**用 `/cron run` 测试。** 不要等调度触发,使用 `/cron run <job_id>` 立即执行,验证输出是否符合预期。
|
||||
|
||||
**调度表达式。** 支持的格式:相对延迟(`30m`)、间隔(`every 2h`)、标准 cron 表达式(`0 9 * * *`)、ISO 时间戳(`2025-06-15T09:00:00`)。不支持自然语言如 `daily at 9am`——请改用 `0 9 * * *`。
|
||||
|
||||
---
|
||||
|
||||
*完整的 cron 参考——所有参数、边界情况和内部机制——请见 [定时任务(Cron)](/user-guide/features/cron)。*
|
||||
@ -0,0 +1,593 @@
|
||||
---
|
||||
sidebar_position: 15
|
||||
title: "自动化模板"
|
||||
description: "开箱即用的自动化配方——定时任务、GitHub 事件触发、API webhook 及多技能工作流"
|
||||
---
|
||||
|
||||
# 自动化模板
|
||||
|
||||
常见自动化模式的复制粘贴配方。每个模板使用 Hermes 内置的 [cron 调度器](/user-guide/features/cron) 实现基于时间的触发,使用 [webhook 平台](/user-guide/messaging/webhooks) 实现事件驱动触发。
|
||||
|
||||
所有模板适用于**任意模型**——不绑定单一提供商。
|
||||
|
||||
:::tip 三种触发类型
|
||||
| 触发方式 | 方式 | 工具 |
|
||||
|---------|-----|------|
|
||||
| **定时** | 按周期运行(每小时、每晚、每周) | `cronjob` 工具或 `/cron` 斜杠命令 |
|
||||
| **GitHub 事件** | PR 开启、推送、issue、CI 结果时触发 | Webhook 平台(`hermes webhook subscribe`) |
|
||||
| **API 调用** | 外部服务向你的端点 POST JSON | Webhook 平台(config.yaml 路由或 `hermes webhook subscribe`) |
|
||||
|
||||
三种方式均支持投递到 Telegram、Discord、Slack、SMS、邮件、GitHub 评论或本地文件。
|
||||
:::
|
||||
|
||||
---
|
||||
|
||||
## 开发工作流
|
||||
|
||||
### 每晚待办事项分类
|
||||
|
||||
每晚自动对新 issue 进行标签分类、优先级排序和摘要汇总,并将摘要投递到团队频道。
|
||||
|
||||
**触发方式:** 定时(每晚)
|
||||
|
||||
```bash
|
||||
hermes cron create "0 2 * * *" \
|
||||
"You are a project manager triaging the NousResearch/hermes-agent GitHub repo.
|
||||
|
||||
1. Run: gh issue list --repo NousResearch/hermes-agent --state open --json number,title,labels,author,createdAt --limit 30
|
||||
2. Identify issues opened in the last 24 hours
|
||||
3. For each new issue:
|
||||
- Suggest a priority label (P0-critical, P1-high, P2-medium, P3-low)
|
||||
- Suggest a category label (bug, feature, docs, security)
|
||||
- Write a one-line triage note
|
||||
4. Summarize: total open issues, new today, breakdown by priority
|
||||
|
||||
Format as a clean digest. If no new issues, respond with [SILENT]." \
|
||||
--name "Nightly backlog triage" \
|
||||
--deliver telegram
|
||||
```
|
||||
|
||||
### 自动 PR 代码审查
|
||||
|
||||
PR 开启时自动进行审查,并直接在 PR 上发布审查评论。
|
||||
|
||||
**触发方式:** GitHub webhook
|
||||
|
||||
**方式 A——动态订阅(CLI):**
|
||||
|
||||
```bash
|
||||
hermes webhook subscribe github-pr-review \
|
||||
--events "pull_request" \
|
||||
--prompt "Review this pull request:
|
||||
Repository: {repository.full_name}
|
||||
PR #{pull_request.number}: {pull_request.title}
|
||||
Author: {pull_request.user.login}
|
||||
Action: {action}
|
||||
Diff URL: {pull_request.diff_url}
|
||||
|
||||
Fetch the diff with: curl -sL {pull_request.diff_url}
|
||||
|
||||
Review for:
|
||||
- Security issues (injection, auth bypass, secrets in code)
|
||||
- Performance concerns (N+1 queries, unbounded loops, memory leaks)
|
||||
- Code quality (naming, duplication, error handling)
|
||||
- Missing tests for new behavior
|
||||
|
||||
Post a concise review. If the PR is a trivial docs/typo change, say so briefly." \
|
||||
--skill github-code-review \
|
||||
--deliver github_comment
|
||||
```
|
||||
|
||||
**方式 B——静态路由(config.yaml):**
|
||||
|
||||
```yaml
|
||||
platforms:
|
||||
webhook:
|
||||
enabled: true
|
||||
extra:
|
||||
port: 8644
|
||||
secret: "your-global-secret"
|
||||
routes:
|
||||
github-pr-review:
|
||||
events: ["pull_request"]
|
||||
secret: "github-webhook-secret"
|
||||
prompt: |
|
||||
Review PR #{pull_request.number}: {pull_request.title}
|
||||
Repository: {repository.full_name}
|
||||
Author: {pull_request.user.login}
|
||||
Diff URL: {pull_request.diff_url}
|
||||
Review for security, performance, and code quality.
|
||||
skills: ["github-code-review"]
|
||||
deliver: "github_comment"
|
||||
deliver_extra:
|
||||
repo: "{repository.full_name}"
|
||||
pr_number: "{pull_request.number}"
|
||||
```
|
||||
|
||||
然后在 GitHub 中:**Settings → Webhooks → Add webhook** → Payload URL:`http://your-server:8644/webhooks/github-pr-review`,Content type:`application/json`,Secret:`github-webhook-secret`,Events:**Pull requests**。
|
||||
|
||||
### 文档偏差检测
|
||||
|
||||
每周扫描已合并的 PR,找出需要更新文档的 API 变更。
|
||||
|
||||
**触发方式:** 定时(每周)
|
||||
|
||||
```bash
|
||||
hermes cron create "0 9 * * 1" \
|
||||
"Scan the NousResearch/hermes-agent repo for documentation drift.
|
||||
|
||||
1. Run: gh pr list --repo NousResearch/hermes-agent --state merged --json number,title,files,mergedAt --limit 30
|
||||
2. Filter to PRs merged in the last 7 days
|
||||
3. For each merged PR, check if it modified:
|
||||
- Tool schemas (tools/*.py) — may need docs/reference/tools-reference.md update
|
||||
- CLI commands (hermes_cli/commands.py, hermes_cli/main.py) — may need docs/reference/cli-commands.md update
|
||||
- Config options (hermes_cli/config.py) — may need docs/user-guide/configuration.md update
|
||||
- Environment variables — may need docs/reference/environment-variables.md update
|
||||
4. Cross-reference: for each code change, check if the corresponding docs page was also updated in the same PR
|
||||
|
||||
Report any gaps where code changed but docs didn't. If everything is in sync, respond with [SILENT]." \
|
||||
--name "Docs drift detection" \
|
||||
--deliver telegram
|
||||
```
|
||||
|
||||
### 依赖安全审计
|
||||
|
||||
每日扫描项目依赖中的已知漏洞。
|
||||
|
||||
**触发方式:** 定时(每日)
|
||||
|
||||
```bash
|
||||
hermes cron create "0 6 * * *" \
|
||||
"Run a dependency security audit on the hermes-agent project.
|
||||
|
||||
1. cd ~/.hermes/hermes-agent && source .venv/bin/activate
|
||||
2. Run: pip audit --format json 2>/dev/null || pip audit 2>&1
|
||||
3. Run: npm audit --json 2>/dev/null (in website/ directory if it exists)
|
||||
4. Check for any CVEs with CVSS score >= 7.0
|
||||
|
||||
If vulnerabilities found:
|
||||
- List each one with package name, version, CVE ID, severity
|
||||
- Check if an upgrade is available
|
||||
- Note if it's a direct dependency or transitive
|
||||
|
||||
If no vulnerabilities, respond with [SILENT]." \
|
||||
--name "Dependency audit" \
|
||||
--deliver telegram
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## DevOps 与监控
|
||||
|
||||
### 部署验证
|
||||
|
||||
每次部署后触发冒烟测试。CI/CD 流水线在部署完成时向 webhook POST 请求。
|
||||
|
||||
**触发方式:** API 调用(webhook)
|
||||
|
||||
```bash
|
||||
hermes webhook subscribe deploy-verify \
|
||||
--events "deployment" \
|
||||
--prompt "A deployment just completed:
|
||||
Service: {service}
|
||||
Environment: {environment}
|
||||
Version: {version}
|
||||
Deployed by: {deployer}
|
||||
|
||||
Run these verification steps:
|
||||
1. Check if the service is responding: curl -s -o /dev/null -w '%{http_code}' {health_url}
|
||||
2. Search recent logs for errors: check the deployment payload for any error indicators
|
||||
3. Verify the version matches: curl -s {health_url}/version
|
||||
|
||||
Report: deployment status (healthy/degraded/failed), response time, any errors found.
|
||||
If healthy, keep it brief. If degraded or failed, provide detailed diagnostics." \
|
||||
--deliver telegram
|
||||
```
|
||||
|
||||
你的 CI/CD 流水线触发方式:
|
||||
|
||||
```bash
|
||||
curl -X POST http://your-server:8644/webhooks/deploy-verify \
|
||||
-H "Content-Type: application/json" \
|
||||
-H "X-Hub-Signature-256: sha256=$(echo -n '{"service":"api","environment":"prod","version":"2.1.0","deployer":"ci","health_url":"https://api.example.com/health"}' | openssl dgst -sha256 -hmac 'your-secret' | cut -d' ' -f2)" \
|
||||
-d '{"service":"api","environment":"prod","version":"2.1.0","deployer":"ci","health_url":"https://api.example.com/health"}'
|
||||
```
|
||||
|
||||
### 告警分类
|
||||
|
||||
将监控告警与近期变更关联,起草响应方案。适用于 Datadog、PagerDuty、Grafana 或任何能 POST JSON 的告警系统。
|
||||
|
||||
**触发方式:** API 调用(webhook)
|
||||
|
||||
```bash
|
||||
hermes webhook subscribe alert-triage \
|
||||
--prompt "Monitoring alert received:
|
||||
Alert: {alert.name}
|
||||
Severity: {alert.severity}
|
||||
Service: {alert.service}
|
||||
Message: {alert.message}
|
||||
Timestamp: {alert.timestamp}
|
||||
|
||||
Investigate:
|
||||
1. Search the web for known issues with this error pattern
|
||||
2. Check if this correlates with any recent deployments or config changes
|
||||
3. Draft a triage summary with:
|
||||
- Likely root cause
|
||||
- Suggested first response steps
|
||||
- Escalation recommendation (P1-P4)
|
||||
|
||||
Be concise. This goes to the on-call channel." \
|
||||
--deliver slack
|
||||
```
|
||||
|
||||
### 可用性监控
|
||||
|
||||
每 30 分钟检查一次端点,仅在服务宕机时发送通知。
|
||||
|
||||
**触发方式:** 定时(每 30 分钟)
|
||||
|
||||
```python title="~/.hermes/scripts/check-uptime.py"
|
||||
import urllib.request, json, time
|
||||
|
||||
ENDPOINTS = [
|
||||
{"name": "API", "url": "https://api.example.com/health"},
|
||||
{"name": "Web", "url": "https://www.example.com"},
|
||||
{"name": "Docs", "url": "https://docs.example.com"},
|
||||
]
|
||||
|
||||
results = []
|
||||
for ep in ENDPOINTS:
|
||||
try:
|
||||
start = time.time()
|
||||
req = urllib.request.Request(ep["url"], headers={"User-Agent": "Hermes-Monitor/1.0"})
|
||||
resp = urllib.request.urlopen(req, timeout=10)
|
||||
elapsed = round((time.time() - start) * 1000)
|
||||
results.append({"name": ep["name"], "status": resp.getcode(), "ms": elapsed})
|
||||
except Exception as e:
|
||||
results.append({"name": ep["name"], "status": "DOWN", "error": str(e)})
|
||||
|
||||
down = [r for r in results if r.get("status") == "DOWN" or (isinstance(r.get("status"), int) and r["status"] >= 500)]
|
||||
if down:
|
||||
print("OUTAGE DETECTED")
|
||||
for r in down:
|
||||
print(f" {r['name']}: {r.get('error', f'HTTP {r[\"status\"]}')} ")
|
||||
print(f"\nAll results: {json.dumps(results, indent=2)}")
|
||||
else:
|
||||
print("NO_ISSUES")
|
||||
```
|
||||
|
||||
```bash
|
||||
hermes cron create "every 30m" \
|
||||
"If the script reports OUTAGE DETECTED, summarize which services are down and suggest likely causes. If NO_ISSUES, respond with [SILENT]." \
|
||||
--script ~/.hermes/scripts/check-uptime.py \
|
||||
--name "Uptime monitor" \
|
||||
--deliver telegram
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 研究与情报
|
||||
|
||||
### 竞品仓库侦察
|
||||
|
||||
监控竞品仓库中有价值的 PR、功能和架构决策。
|
||||
|
||||
**触发方式:** 定时(每日)
|
||||
|
||||
```bash
|
||||
hermes cron create "0 8 * * *" \
|
||||
"Scout these AI agent repositories for notable activity in the last 24 hours:
|
||||
|
||||
Repos to check:
|
||||
- anthropics/claude-code
|
||||
- openai/codex
|
||||
- All-Hands-AI/OpenHands
|
||||
- Aider-AI/aider
|
||||
|
||||
For each repo:
|
||||
1. gh pr list --repo <repo> --state all --json number,title,author,createdAt,mergedAt --limit 15
|
||||
2. gh issue list --repo <repo> --state open --json number,title,labels,createdAt --limit 10
|
||||
|
||||
Focus on:
|
||||
- New features being developed
|
||||
- Architectural changes
|
||||
- Integration patterns we could learn from
|
||||
- Security fixes that might affect us too
|
||||
|
||||
Skip routine dependency bumps and CI fixes. If nothing notable, respond with [SILENT].
|
||||
If there are findings, organize by repo with brief analysis of each item." \
|
||||
--skill competitive-pr-scout \
|
||||
--name "Competitor scout" \
|
||||
--deliver telegram
|
||||
```
|
||||
|
||||
### AI 新闻摘要
|
||||
|
||||
每周汇总 AI/ML 领域动态。
|
||||
|
||||
**触发方式:** 定时(每周)
|
||||
|
||||
```bash
|
||||
hermes cron create "0 9 * * 1" \
|
||||
"Generate a weekly AI news digest covering the past 7 days:
|
||||
|
||||
1. Search the web for major AI announcements, model releases, and research breakthroughs
|
||||
2. Search for trending ML repositories on GitHub
|
||||
3. Check arXiv for highly-cited papers on language models and agents
|
||||
|
||||
Structure:
|
||||
## Headlines (3-5 major stories)
|
||||
## Notable Papers (2-3 papers with one-sentence summaries)
|
||||
## Open Source (interesting new repos or major releases)
|
||||
## Industry Moves (funding, acquisitions, launches)
|
||||
|
||||
Keep each item to 1-2 sentences. Include links. Total under 600 words." \
|
||||
--name "Weekly AI digest" \
|
||||
--deliver telegram
|
||||
```
|
||||
|
||||
### 论文摘要与笔记
|
||||
|
||||
每日扫描 arXiv 并将摘要保存到笔记系统。
|
||||
|
||||
**触发方式:** 定时(每日)
|
||||
|
||||
```bash
|
||||
hermes cron create "0 8 * * *" \
|
||||
"Search arXiv for the 3 most interesting papers on 'language model reasoning' OR 'tool-use agents' from the past day. For each paper, create an Obsidian note with the title, authors, abstract summary, key contribution, and potential relevance to Hermes Agent development." \
|
||||
--skill arxiv --skill obsidian \
|
||||
--name "Paper digest" \
|
||||
--deliver local
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## GitHub 事件自动化
|
||||
|
||||
### Issue 自动打标签
|
||||
|
||||
自动对新 issue 打标签并回复。
|
||||
|
||||
**触发方式:** GitHub webhook
|
||||
|
||||
```bash
|
||||
hermes webhook subscribe github-issues \
|
||||
--events "issues" \
|
||||
--prompt "New GitHub issue received:
|
||||
Repository: {repository.full_name}
|
||||
Issue #{issue.number}: {issue.title}
|
||||
Author: {issue.user.login}
|
||||
Action: {action}
|
||||
Body: {issue.body}
|
||||
Labels: {issue.labels}
|
||||
|
||||
If this is a new issue (action=opened):
|
||||
1. Read the issue title and body carefully
|
||||
2. Suggest appropriate labels (bug, feature, docs, security, question)
|
||||
3. If it's a bug report, check if you can identify the affected component from the description
|
||||
4. Post a helpful initial response acknowledging the issue
|
||||
|
||||
If this is a label or assignment change, respond with [SILENT]." \
|
||||
--deliver github_comment
|
||||
```
|
||||
|
||||
### CI 失败分析
|
||||
|
||||
分析 CI 失败原因并在 PR 上发布诊断信息。
|
||||
|
||||
**触发方式:** GitHub webhook
|
||||
|
||||
```yaml
|
||||
# config.yaml route
|
||||
platforms:
|
||||
webhook:
|
||||
enabled: true
|
||||
extra:
|
||||
routes:
|
||||
ci-failure:
|
||||
events: ["check_run"]
|
||||
secret: "ci-secret"
|
||||
prompt: |
|
||||
CI check failed:
|
||||
Repository: {repository.full_name}
|
||||
Check: {check_run.name}
|
||||
Status: {check_run.conclusion}
|
||||
PR: #{check_run.pull_requests.0.number}
|
||||
Details URL: {check_run.details_url}
|
||||
|
||||
If conclusion is "failure":
|
||||
1. Fetch the log from the details URL if accessible
|
||||
2. Identify the likely cause of failure
|
||||
3. Suggest a fix
|
||||
If conclusion is "success", respond with [SILENT].
|
||||
deliver: "github_comment"
|
||||
deliver_extra:
|
||||
repo: "{repository.full_name}"
|
||||
pr_number: "{check_run.pull_requests.0.number}"
|
||||
```
|
||||
|
||||
### 跨仓库自动移植变更
|
||||
|
||||
某仓库 PR 合并后,自动将等效变更移植到另一个仓库。
|
||||
|
||||
**触发方式:** GitHub webhook
|
||||
|
||||
```bash
|
||||
hermes webhook subscribe auto-port \
|
||||
--events "pull_request" \
|
||||
--prompt "PR merged in the source repository:
|
||||
Repository: {repository.full_name}
|
||||
PR #{pull_request.number}: {pull_request.title}
|
||||
Author: {pull_request.user.login}
|
||||
Action: {action}
|
||||
Merge commit: {pull_request.merge_commit_sha}
|
||||
|
||||
If action is 'closed' and pull_request.merged is true:
|
||||
1. Fetch the diff: curl -sL {pull_request.diff_url}
|
||||
2. Analyze what changed
|
||||
3. Determine if this change needs to be ported to the Go SDK equivalent
|
||||
4. If yes, create a branch, apply the equivalent changes, and open a PR on the target repo
|
||||
5. Reference the original PR in the new PR description
|
||||
|
||||
If action is not 'closed' or not merged, respond with [SILENT]." \
|
||||
--skill github-pr-workflow \
|
||||
--deliver log
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 业务运营
|
||||
|
||||
### Stripe 支付监控
|
||||
|
||||
跟踪支付事件并汇总失败情况。
|
||||
|
||||
**触发方式:** API 调用(webhook)
|
||||
|
||||
```bash
|
||||
hermes webhook subscribe stripe-payments \
|
||||
--events "payment_intent.succeeded,payment_intent.payment_failed,charge.dispute.created" \
|
||||
--prompt "Stripe event received:
|
||||
Event type: {type}
|
||||
Amount: {data.object.amount} cents ({data.object.currency})
|
||||
Customer: {data.object.customer}
|
||||
Status: {data.object.status}
|
||||
|
||||
For payment_intent.payment_failed:
|
||||
- Identify the failure reason from {data.object.last_payment_error}
|
||||
- Suggest whether this is a transient issue (retry) or permanent (contact customer)
|
||||
|
||||
For charge.dispute.created:
|
||||
- Flag as urgent
|
||||
- Summarize the dispute details
|
||||
|
||||
For payment_intent.succeeded:
|
||||
- Brief confirmation only
|
||||
|
||||
Keep responses concise for the ops channel." \
|
||||
--deliver slack
|
||||
```
|
||||
|
||||
### 每日营收摘要
|
||||
|
||||
每天早晨汇总关键业务指标。
|
||||
|
||||
**触发方式:** 定时(每日)
|
||||
|
||||
```bash
|
||||
hermes cron create "0 8 * * *" \
|
||||
"Generate a morning business metrics summary.
|
||||
|
||||
Search the web for:
|
||||
1. Current Bitcoin and Ethereum prices
|
||||
2. S&P 500 status (pre-market or previous close)
|
||||
3. Any major tech/AI industry news from the last 12 hours
|
||||
|
||||
Format as a brief morning briefing, 3-4 bullet points max.
|
||||
Deliver as a clean, scannable message." \
|
||||
--name "Morning briefing" \
|
||||
--deliver telegram
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 多技能工作流
|
||||
|
||||
### 安全审计流水线
|
||||
|
||||
组合多个技能,每周进行全面安全审查。
|
||||
|
||||
**触发方式:** 定时(每周)
|
||||
|
||||
```bash
|
||||
hermes cron create "0 3 * * 0" \
|
||||
"Run a comprehensive security audit of the hermes-agent codebase.
|
||||
|
||||
1. Check for dependency vulnerabilities (pip audit, npm audit)
|
||||
2. Search the codebase for common security anti-patterns:
|
||||
- Hardcoded secrets or API keys
|
||||
- SQL injection vectors (string formatting in queries)
|
||||
- Path traversal risks (user input in file paths without validation)
|
||||
- Unsafe deserialization (pickle.loads, yaml.load without SafeLoader)
|
||||
3. Review recent commits (last 7 days) for security-relevant changes
|
||||
4. Check if any new environment variables were added without being documented
|
||||
|
||||
Write a security report with findings categorized by severity (Critical, High, Medium, Low).
|
||||
If nothing found, report a clean bill of health." \
|
||||
--skill codebase-security-audit \
|
||||
--name "Weekly security audit" \
|
||||
--deliver telegram
|
||||
```
|
||||
|
||||
### 内容流水线
|
||||
|
||||
按计划研究、起草并准备内容。
|
||||
|
||||
**触发方式:** 定时(每周)
|
||||
|
||||
```bash
|
||||
hermes cron create "0 10 * * 3" \
|
||||
"Research and draft a technical blog post outline about a trending topic in AI agents.
|
||||
|
||||
1. Search the web for the most discussed AI agent topics this week
|
||||
2. Pick the most interesting one that's relevant to open-source AI agents
|
||||
3. Create an outline with:
|
||||
- Hook/intro angle
|
||||
- 3-4 key sections
|
||||
- Technical depth appropriate for developers
|
||||
- Conclusion with actionable takeaway
|
||||
4. Save the outline to ~/drafts/blog-$(date +%Y%m%d).md
|
||||
|
||||
Keep the outline to ~300 words. This is a starting point, not a finished post." \
|
||||
--name "Blog outline" \
|
||||
--deliver local
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 快速参考
|
||||
|
||||
### Cron 调度语法
|
||||
|
||||
| 表达式 | 含义 |
|
||||
|-----------|---------|
|
||||
| `every 30m` | 每 30 分钟 |
|
||||
| `every 2h` | 每 2 小时 |
|
||||
| `0 2 * * *` | 每天凌晨 2:00 |
|
||||
| `0 9 * * 1` | 每周一上午 9:00 |
|
||||
| `0 9 * * 1-5` | 工作日上午 9:00 |
|
||||
| `0 3 * * 0` | 每周日凌晨 3:00 |
|
||||
| `0 */6 * * *` | 每 6 小时 |
|
||||
|
||||
### 投递目标
|
||||
|
||||
| 目标 | 参数 | 说明 |
|
||||
|--------|------|-------|
|
||||
| 当前会话 | `--deliver origin` | 默认——投递到任务创建所在的位置 |
|
||||
| 本地文件 | `--deliver local` | 保存输出,不发送通知 |
|
||||
| Telegram | `--deliver telegram` | 主频道,或用 `telegram:CHAT_ID` 指定特定会话 |
|
||||
| Discord | `--deliver discord` | 主频道,或用 `discord:CHANNEL_ID` 指定 |
|
||||
| Slack | `--deliver slack` | 主频道 |
|
||||
| SMS | `--deliver sms:+15551234567` | 直接发送到手机号 |
|
||||
| 指定话题 | `--deliver telegram:-100123:456` | Telegram 论坛话题 |
|
||||
|
||||
### Webhook 模板变量
|
||||
|
||||
| 变量 | 说明 |
|
||||
|----------|-------------|
|
||||
| `{pull_request.title}` | PR 标题 |
|
||||
| `{issue.number}` | Issue 编号 |
|
||||
| `{repository.full_name}` | `owner/repo` |
|
||||
| `{action}` | 事件动作(opened、closed 等) |
|
||||
| `{__raw__}` | 完整 JSON payload(截断至 4000 字符) |
|
||||
| `{sender.login}` | 触发事件的 GitHub 用户 |
|
||||
|
||||
### [SILENT] 模式
|
||||
|
||||
当 cron 任务的响应包含 `[SILENT]` 时,投递将被抑制。使用此模式可避免在无事发生时产生通知噪音:
|
||||
|
||||
```
|
||||
If nothing noteworthy happened, respond with [SILENT].
|
||||
```
|
||||
|
||||
这样只有当 Agent 有内容需要汇报时,你才会收到通知。
|
||||
@ -0,0 +1,170 @@
|
||||
---
|
||||
sidebar_position: 14
|
||||
title: "AWS Bedrock"
|
||||
description: "将 Hermes Agent 与 Amazon Bedrock 配合使用——原生 Converse API、IAM 身份验证、Guardrails 及跨区域推理"
|
||||
---
|
||||
|
||||
# AWS Bedrock
|
||||
|
||||
Hermes Agent 通过 **Converse API** 原生支持 Amazon Bedrock——而非 OpenAI 兼容端点。这让你可以完整访问 Bedrock 生态系统:IAM 身份验证、Guardrails、跨区域推理配置文件以及所有基础模型。
|
||||
|
||||
## 前提条件
|
||||
|
||||
- **AWS 凭证** — [boto3 凭证链](https://boto3.amazonaws.com/v1/documentation/api/latest/guide/credentials.html)支持的任意来源:
|
||||
- IAM 实例角色(EC2、ECS、Lambda — 零配置)
|
||||
- `AWS_ACCESS_KEY_ID` + `AWS_SECRET_ACCESS_KEY` 环境变量
|
||||
- `AWS_PROFILE`(用于 SSO 或命名配置文件)
|
||||
- `aws configure`(用于本地开发)
|
||||
- **boto3** — 通过 `pip install hermes-agent[bedrock]` 安装
|
||||
- **IAM 权限** — 至少需要:
|
||||
- `bedrock:InvokeModel` 和 `bedrock:InvokeModelWithResponseStream`(用于推理)
|
||||
- `bedrock:ListFoundationModels` 和 `bedrock:ListInferenceProfiles`(用于模型发现)
|
||||
|
||||
:::tip EC2 / ECS / Lambda
|
||||
在 AWS 计算环境中,为实例附加带有 `AmazonBedrockFullAccess` 的 IAM 角色即可。无需 API 密钥,无需 `.env` 配置——Hermes 会自动检测实例角色。
|
||||
:::
|
||||
|
||||
## 快速开始
|
||||
|
||||
```bash
|
||||
# 安装并启用 Bedrock 支持
|
||||
pip install hermes-agent[bedrock]
|
||||
|
||||
# 选择 Bedrock 作为提供商
|
||||
hermes model
|
||||
# → 选择 "More providers..." → "AWS Bedrock"
|
||||
# → 选择你的区域和模型
|
||||
|
||||
# 开始对话
|
||||
hermes chat
|
||||
```
|
||||
|
||||
## 配置
|
||||
|
||||
运行 `hermes model` 后,你的 `~/.hermes/config.yaml` 将包含以下内容:
|
||||
|
||||
```yaml
|
||||
model:
|
||||
default: us.anthropic.claude-sonnet-4-6
|
||||
provider: bedrock
|
||||
base_url: https://bedrock-runtime.us-east-2.amazonaws.com
|
||||
|
||||
bedrock:
|
||||
region: us-east-2
|
||||
```
|
||||
|
||||
### 区域
|
||||
|
||||
通过以下任意方式设置 AWS 区域(优先级从高到低):
|
||||
|
||||
1. `config.yaml` 中的 `bedrock.region`
|
||||
2. `AWS_REGION` 环境变量
|
||||
3. `AWS_DEFAULT_REGION` 环境变量
|
||||
4. 默认值:`us-east-1`
|
||||
|
||||
### Guardrails
|
||||
|
||||
要对所有模型调用应用 [Amazon Bedrock Guardrails](https://docs.aws.amazon.com/bedrock/latest/userguide/guardrails.html):
|
||||
|
||||
```yaml
|
||||
bedrock:
|
||||
region: us-east-2
|
||||
guardrail:
|
||||
guardrail_identifier: "abc123def456" # 来自 Bedrock 控制台
|
||||
guardrail_version: "1" # 版本号或 "DRAFT"
|
||||
stream_processing_mode: "async" # "sync" 或 "async"
|
||||
trace: "disabled" # "enabled"、"disabled" 或 "enabled_full"
|
||||
```
|
||||
|
||||
### 模型发现
|
||||
|
||||
Hermes 通过 Bedrock 控制平面自动发现可用模型。你可以自定义发现行为:
|
||||
|
||||
```yaml
|
||||
bedrock:
|
||||
discovery:
|
||||
enabled: true
|
||||
provider_filter: ["anthropic", "amazon"] # 仅显示这些提供商
|
||||
refresh_interval: 3600 # 缓存 1 小时
|
||||
```
|
||||
|
||||
## 可用模型
|
||||
|
||||
Bedrock 模型使用**推理配置文件 ID** 进行按需调用。`hermes model` 选择器会自动显示这些 ID,并将推荐模型置于顶部:
|
||||
|
||||
| 模型 | ID | 备注 |
|
||||
|-------|-----|-------|
|
||||
| Claude Sonnet 4.6 | `us.anthropic.claude-sonnet-4-6` | 推荐——速度与能力的最佳平衡 |
|
||||
| Claude Opus 4.6 | `us.anthropic.claude-opus-4-6-v1` | 能力最强 |
|
||||
| Claude Haiku 4.5 | `us.anthropic.claude-haiku-4-5-20251001-v1:0` | 最快的 Claude |
|
||||
| Amazon Nova Pro | `us.amazon.nova-pro-v1:0` | Amazon 旗舰模型 |
|
||||
| Amazon Nova Micro | `us.amazon.nova-micro-v1:0` | 最快、最经济 |
|
||||
| DeepSeek V3.2 | `deepseek.v3.2` | 强大的开源模型 |
|
||||
| Llama 4 Scout 17B | `us.meta.llama4-scout-17b-instruct-v1:0` | Meta 最新模型 |
|
||||
|
||||
:::info 跨区域推理
|
||||
以 `us.` 为前缀的模型使用跨区域推理配置文件,可在多个 AWS 区域间提供更好的容量保障和自动故障转移。以 `global.` 为前缀的模型则在全球所有可用区域间路由。
|
||||
:::
|
||||
|
||||
## 会话中途切换模型
|
||||
|
||||
在对话过程中使用 `/model` 命令:
|
||||
|
||||
```
|
||||
/model us.amazon.nova-pro-v1:0
|
||||
/model deepseek.v3.2
|
||||
/model us.anthropic.claude-opus-4-6-v1
|
||||
```
|
||||
|
||||
## 诊断
|
||||
|
||||
```bash
|
||||
hermes doctor
|
||||
```
|
||||
|
||||
诊断工具会检查:
|
||||
- AWS 凭证是否可用(环境变量、IAM 角色、SSO)
|
||||
- `boto3` 是否已安装
|
||||
- Bedrock API 是否可达(ListFoundationModels)
|
||||
- 你所在区域的可用模型数量
|
||||
|
||||
## Gateway(消息平台)
|
||||
|
||||
Bedrock 可与所有 Hermes gateway 平台配合使用(Telegram、Discord、Slack、飞书等)。将 Bedrock 配置为提供商后,正常启动 gateway 即可:
|
||||
|
||||
```bash
|
||||
hermes gateway setup
|
||||
hermes gateway start
|
||||
```
|
||||
|
||||
Gateway 读取 `config.yaml` 并使用相同的 Bedrock 提供商配置。
|
||||
|
||||
## 故障排查
|
||||
|
||||
### "No API key found" / "No AWS credentials"
|
||||
|
||||
Hermes 按以下顺序检查凭证:
|
||||
1. `AWS_BEARER_TOKEN_BEDROCK`
|
||||
2. `AWS_ACCESS_KEY_ID` + `AWS_SECRET_ACCESS_KEY`
|
||||
3. `AWS_PROFILE`
|
||||
4. EC2 实例元数据(IMDS)
|
||||
5. ECS 容器凭证
|
||||
6. Lambda 执行角色
|
||||
|
||||
若均未找到,请运行 `aws configure` 或为你的计算实例附加 IAM 角色。
|
||||
|
||||
### "Invocation of model ID ... with on-demand throughput isn't supported"
|
||||
|
||||
请使用**推理配置文件 ID**(以 `us.` 或 `global.` 为前缀),而非裸基础模型 ID。例如:
|
||||
- ❌ `anthropic.claude-sonnet-4-6`
|
||||
- ✅ `us.anthropic.claude-sonnet-4-6`
|
||||
|
||||
### "ThrottlingException"
|
||||
|
||||
你已触及 Bedrock 单模型速率限制。Hermes 会自动进行退避重试。如需提高限额,请在 [AWS Service Quotas 控制台](https://console.aws.amazon.com/servicequotas/)申请配额提升。
|
||||
|
||||
## 一键 AWS 部署
|
||||
|
||||
如需在 EC2 上通过 CloudFormation 进行全自动部署:
|
||||
|
||||
**[sample-hermes-agent-on-aws-with-bedrock](https://github.com/JiaDe-Wu/sample-hermes-agent-on-aws-with-bedrock)** — 自动创建 VPC、IAM 角色、EC2 实例并配置 Bedrock。一键即可在任意区域完成部署。
|
||||
@ -0,0 +1,334 @@
|
||||
---
|
||||
sidebar_position: 15
|
||||
title: "Microsoft Foundry"
|
||||
description: "将 Hermes Agent 与 Microsoft Foundry 配合使用——OpenAI 风格与 Anthropic 风格端点、传输协议与已部署模型的自动检测"
|
||||
---
|
||||
|
||||
# Microsoft Foundry
|
||||
|
||||
Hermes Agent 的 `azure-foundry` provider 支持 Microsoft Foundry(原 Azure AI Foundry)和 Azure OpenAI。单个 Foundry 资源可以托管两种不同传输格式的模型:
|
||||
|
||||
- **OpenAI 风格** — 在 `https://<resource>.openai.azure.com/openai/v1` 等端点上执行 `POST /v1/chat/completions`。用于 GPT-4.x、GPT-5.x、Llama、Mistral 及大多数开放权重模型。
|
||||
- **Anthropic 风格** — 在 `https://<resource>.services.ai.azure.com/anthropic` 等端点上执行 `POST /v1/messages`。当 Microsoft Foundry 通过 Anthropic Messages API 格式提供 Claude 模型时使用。
|
||||
|
||||
设置向导会探测你的端点并自动检测所使用的传输协议、可用的部署以及每个模型的上下文长度。
|
||||
|
||||
## 前提条件
|
||||
|
||||
- 一个至少包含一个部署的 Microsoft Foundry 或 Azure OpenAI 资源
|
||||
- 该部署的端点 URL
|
||||
- **以下之一**:API 密钥(从 Azure Portal 的"Keys and Endpoint"获取),**或者**在 Foundry 资源上拥有 **Azure AI User** RBAC 角色(如果你计划使用 Microsoft Entra ID——即 Microsoft 推荐的无密钥方式)。某些租户在 Microsoft 重命名推出期间可能将该角色显示为 **Foundry User**。
|
||||
|
||||
## 快速开始
|
||||
|
||||
```bash
|
||||
hermes model
|
||||
# → 选择 "Azure Foundry"
|
||||
# → 输入你的端点 URL
|
||||
# → 选择认证方式:
|
||||
# 1. API key
|
||||
# 2. Microsoft Entra ID(托管标识 / 工作负载标识 / az login)
|
||||
# → (Entra)Hermes 探测 DefaultAzureCredential;成功后不再询问密钥
|
||||
# → (API key)输入你的 API 密钥
|
||||
# Hermes 探测端点并自动检测传输协议 + 模型
|
||||
# → 从列表中选择模型(或手动输入部署名称)
|
||||
```
|
||||
|
||||
向导将执行以下操作:
|
||||
|
||||
1. **嗅探 URL 路径** — 以 `/anthropic` 结尾的 URL 被识别为 Microsoft Foundry Claude 路由。
|
||||
2. **探测 `GET <base>/models`** — 如果端点返回 OpenAI 格式的模型列表,Hermes 切换到 `chat_completions` 并用返回的部署 ID 预填选择器。
|
||||
3. **探测 Anthropic Messages 格式** — 针对不暴露 `/models` 但接受 Anthropic Messages 格式的端点的回退方案。
|
||||
4. **回退到手动输入** — 拒绝所有探测的私有/受限端点仍然可用;你手动选择 API 模式并输入部署名称。
|
||||
|
||||
所选模型的上下文长度通过 Hermes 的标准元数据链(`models.dev`、provider 元数据及硬编码的系列回退)解析,并存储在 `config.yaml` 中,以便模型正确确定自身的上下文窗口大小。
|
||||
|
||||
## Microsoft Entra ID(无密钥,RBAC)——推荐
|
||||
|
||||
Microsoft 推荐在生产 Foundry 工作负载中使用 [Microsoft Entra ID 无密钥认证](https://learn.microsoft.com/azure/ai-foundry/foundry-models/how-to/configure-entra-id)。Hermes 对**两种** API 接口均支持 Entra ID:
|
||||
|
||||
- **OpenAI 风格**(`api_mode: chat_completions` / `codex_responses`)— GPT-4/5、Llama、Mistral、DeepSeek 等。
|
||||
- **Anthropic 风格**(`api_mode: anthropic_messages`)— Microsoft Foundry 上的 Claude 模型。
|
||||
|
||||
Foundry 的 RBAC 是按资源级别的(`Azure AI User` 授予两种接口的访问权限;某些租户可能显示为 `Foundry User`),Microsoft 文档对两者使用相同的推理 scope(`https://ai.azure.com/.default`)。底层实现:
|
||||
|
||||
- OpenAI 风格使用 OpenAI Python SDK 原生的可调用 `api_key=` 契约——SDK 每次请求自动生成新的 JWT。
|
||||
- Anthropic 风格使用带有请求事件 hook 的 `httpx.Client`,该 hook 由 `agent.azure_identity_adapter.build_bearer_http_client` 安装,因为 Anthropic SDK 原生不接受可调用的 `auth_token`。该 hook 在每次出站请求时重写 `Authorization: Bearer <fresh-jwt>`。RBAC 和 Foundry scope 相同——唯一的区别在于 SDK 契约。
|
||||
|
||||
### 为什么使用 Entra ID?
|
||||
|
||||
- 无需轮换或吊销长期有效的 API 密钥。
|
||||
- RBAC 驱动的访问控制——在 Foundry 资源上授予或移除 `Azure AI User`,无需重写配置。
|
||||
- 访问和审计日志按被分配者分段,而非所有调用者共享一个静态密钥。
|
||||
- 通过托管标识,为 Azure VM、AKS Pod、App Service、Functions、Container Apps 和 Foundry Agent Service 提供统一的认证接口。
|
||||
- 支持 CI/CD 流水线的工作负载标识和服务主体流程。
|
||||
|
||||
### 一次性设置(Azure 侧)
|
||||
|
||||
1. 在 Azure Portal 中,打开你的 Foundry 资源 → **访问控制 (IAM)** → **添加 → 添加角色分配**。
|
||||
2. 选择 **Azure AI User** 角色(如果你的租户已重命名,则选择 **Foundry User**)。
|
||||
3. 将其分配给:
|
||||
- **你的用户账户**,用于通过 `az login` 进行本地开发。
|
||||
- **托管标识或工作负载标识**,用于 Azure 托管计算(生产环境推荐)。
|
||||
- **Foundry Agent Service 托管 Agent 的 Agent 标识**,当 Hermes 在托管 Agent 内运行时。
|
||||
- **服务主体**,用于工作负载标识不可用时的 CI/CD 流水线。
|
||||
4. 等待约 5 分钟以使角色生效。
|
||||
|
||||
Azure CLI 等效命令:
|
||||
|
||||
```bash
|
||||
az role assignment create \
|
||||
--assignee <principal-or-agent-identity-client-id> \
|
||||
--role "Azure AI User" \
|
||||
--scope <foundry-resource-id>
|
||||
```
|
||||
|
||||
### 一次性设置(Hermes 侧)
|
||||
|
||||
```bash
|
||||
hermes model
|
||||
# → 选择 "Azure Foundry"
|
||||
# → 输入你的端点 URL
|
||||
# → 认证方式:2(Microsoft Entra ID)
|
||||
# → (可选)用户分配的托管标识客户端 ID
|
||||
# → (可选)Azure 租户 ID
|
||||
# → Hermes 探测 DefaultAzureCredential() 并报告哪个内部凭据成功
|
||||
# (例如 AzureCliCredential、ManagedIdentityCredential)
|
||||
```
|
||||
|
||||
向导运行一个有时间限制的预检探测(10 秒超时)。失败时提供"仍然保存,稍后验证"选项——适用于在当前机器上尚无凭据但运行时会有凭据的场景(例如为托管标识部署准备配置)。
|
||||
|
||||
`azure-identity` 在首次使用时通过 Hermes 的懒加载安装路径自动安装。如需预先安装:
|
||||
|
||||
```bash
|
||||
pip install azure-identity
|
||||
```
|
||||
|
||||
### 写入 `config.yaml` 的配置
|
||||
|
||||
```yaml
|
||||
model:
|
||||
provider: azure-foundry
|
||||
base_url: https://my-resource.openai.azure.com/openai/v1
|
||||
api_mode: chat_completions
|
||||
auth_mode: entra_id
|
||||
default: gpt-4o
|
||||
context_length: 128000
|
||||
entra:
|
||||
scope: https://ai.azure.com/.default # 仅在覆盖默认值时使用
|
||||
```
|
||||
|
||||
Hermes 在 `config.yaml` 中只管理一个 Entra 专属配置项:
|
||||
|
||||
- **`scope`** — OAuth 资源 scope。默认为 Microsoft 文档中的推理 scope(`https://ai.azure.com/.default`)。仅在你的资源针对非标准 audience 进行了预配时才需要覆盖。
|
||||
|
||||
其他所有内容(租户、服务主体密钥、联合令牌文件、主权云 authority、broker 偏好)均由 `azure-identity` 直接从标准 `AZURE_*` 环境变量读取——参见下方的[凭据解析顺序](#credential-resolution-order)。在 `~/.hermes/.env` 或你的部署环境中设置这些变量,与 Microsoft SDK 参考文档的描述完全一致。
|
||||
|
||||
Entra 模式下不会将任何密钥写入 `~/.hermes/.env`——`azure-identity` 在进程内缓存令牌(在可用时也会使用操作系统密钥链 / `~/.IdentityService`)。
|
||||
|
||||
### 凭据解析顺序
|
||||
|
||||
`azure-identity` 的 `DefaultAzureCredential` 在每次令牌请求时按以下链路逐一尝试,在第一个返回令牌的凭据处停止:
|
||||
|
||||
1. **环境凭据** — `AZURE_TENANT_ID` + `AZURE_CLIENT_ID` + `AZURE_CLIENT_SECRET`(或 `AZURE_CLIENT_CERTIFICATE_PATH` / `AZURE_FEDERATED_TOKEN_FILE`)。
|
||||
2. **工作负载标识** — `AZURE_FEDERATED_TOKEN_FILE`(AKS 联合令牌 / OIDC)。
|
||||
3. **托管标识** — 虚拟机使用 IMDS 端点(`169.254.169.254`);App Service / Functions / Container Apps 使用 `IDENTITY_ENDPOINT`。Foundry Agent Service 托管 Agent 使用托管 Agent 的 Agent 标识。
|
||||
4. **Visual Studio Code** — Azure 账户扩展。
|
||||
5. **Azure CLI** — `az login` 会话。
|
||||
6. **Azure Developer CLI** — `azd auth login`。
|
||||
7. **Azure PowerShell** — `Connect-AzAccount`。
|
||||
8. **Broker**(仅限 Windows / WSL)— Web Account Manager。
|
||||
|
||||
交互式浏览器凭据在无人值守的 Hermes 运行中默认被排除;请改用 Azure CLI、Azure Developer CLI、托管标识、工作负载标识或服务主体凭据。
|
||||
|
||||
### 部署模式
|
||||
|
||||
**本地开发:**
|
||||
```bash
|
||||
az login
|
||||
hermes model # 选择 Azure Foundry → Entra ID
|
||||
hermes # 使用你的 az login 令牌
|
||||
```
|
||||
|
||||
**Azure VM / Functions / App Service / Container Apps(系统分配的托管标识):**
|
||||
1. 在计算资源上启用系统分配的标识。
|
||||
2. 在 Foundry 资源上为该标识授予 `Azure AI User`(或 `Foundry User`)角色。
|
||||
3. 在 config.yaml 中设置 `model.auth_mode: entra_id`——无需环境变量。
|
||||
|
||||
**Azure VM / Functions / App Service / Container Apps(用户分配的托管标识):**
|
||||
- 将 `AZURE_CLIENT_ID` 设置为用户分配标识的客户端 ID,以便 `DefaultAzureCredential` 选择正确的标识。
|
||||
|
||||
**Foundry Agent Service 托管 Agent:**
|
||||
- 创建托管 Agent 并在 Foundry 资源上为该 Agent 的标识授予 `Azure AI User`(或 `Foundry User`)角色。Hermes 在托管 Agent 内部使用 `ManagedIdentityCredential`;角色分配应针对 Agent 标识,而非仅针对父项目或你的用户。
|
||||
|
||||
**AKS 工作负载标识(替代 AAD Pod Identity):**
|
||||
- 使用工作负载标识客户端 ID 注解 Pod 的服务账户。
|
||||
- Pod 的联合令牌文件通过 `AZURE_FEDERATED_TOKEN_FILE` 自动检测。
|
||||
- `model.auth_mode: entra_id` 无需进一步修改配置即可使用。
|
||||
|
||||
**CI 中的服务主体:**
|
||||
- 在 runner 环境中设置 `AZURE_TENANT_ID`、`AZURE_CLIENT_ID`、`AZURE_CLIENT_SECRET`。
|
||||
|
||||
#### 主权云(政府云、中国云)
|
||||
|
||||
导出 `AZURE_AUTHORITY_HOST`(例如 Azure Government 使用 `https://login.microsoftonline.us`,Azure China 使用 `https://login.partner.microsoftonline.cn`)。`azure-identity` 会直接读取该变量。
|
||||
|
||||
### 健康检查
|
||||
|
||||
当 `model.auth_mode: entra_id` 时,`hermes doctor` 会对 `DefaultAzureCredential` 运行 10 秒探测,报告哪个内部凭据成功(环境变量是否存在、托管标识端点是否可达等)。
|
||||
|
||||
`hermes auth` 显示结构化状态块:
|
||||
|
||||
```
|
||||
azure-foundry (Microsoft Entra ID):
|
||||
Endpoint: https://my-resource.openai.azure.com/openai/v1
|
||||
Scope: https://ai.azure.com/.default
|
||||
Status: configured; live token probe is skipped here
|
||||
```
|
||||
|
||||
### 限制
|
||||
|
||||
- **Anthropic 风格端点使用 httpx 事件 hook。** Anthropic Python SDK(≤ 0.86.0)原生不接受可调用的 `auth_token`。Hermes 在自定义 `httpx.Client` 上安装请求事件 hook,每次出站请求时生成新的 JWT 并重写 `Authorization: Bearer <jwt>`。这在功能上等同于 OpenAI SDK 原生的 `Callable[[], str]` 契约,但多了一层间接调用。如果 Anthropic SDK 在未来版本中添加对可调用认证的原生支持,Hermes 将透明地切换到该方式。
|
||||
- **批处理任务与 `multiprocessing.Pool`。** Entra 令牌 provider 是一个闭包,无法跨进程边界序列化。`batch_runner.py` 会自动从 worker 配置中移除该可调用对象,让每个 worker 进程从 `config.yaml` 重建自己的 provider——无需用户操作,但每个 worker 在启动时需要执行一次凭据链遍历。
|
||||
- **不在 `auth.json` 中持久化 Bearer JWT。** Hermes 不复制 `azure-identity` 的内部令牌缓存;冷启动时会在首次推理时遍历凭据链。
|
||||
|
||||
## 配置(写入 `config.yaml`)
|
||||
|
||||
运行向导后,你将看到类似如下的内容:
|
||||
|
||||
```yaml
|
||||
model:
|
||||
provider: azure-foundry
|
||||
base_url: https://my-resource.openai.azure.com/openai/v1
|
||||
api_mode: chat_completions # 或 "anthropic_messages"
|
||||
default: gpt-5.4-mini # 你的部署 / 模型名称
|
||||
context_length: 400000 # 自动检测
|
||||
```
|
||||
|
||||
以及在 `~/.hermes/.env` 中:
|
||||
|
||||
```
|
||||
AZURE_FOUNDRY_API_KEY=<your-azure-key>
|
||||
```
|
||||
|
||||
## OpenAI 风格端点(GPT、Llama 等)
|
||||
|
||||
Azure OpenAI 的 v1 GA 端点接受标准 `openai` Python 客户端,改动极少:
|
||||
|
||||
```yaml
|
||||
model:
|
||||
provider: azure-foundry
|
||||
base_url: https://my-resource.openai.azure.com/openai/v1
|
||||
api_mode: chat_completions
|
||||
default: gpt-5.4
|
||||
```
|
||||
|
||||
重要行为:
|
||||
|
||||
- **GPT-5.x、codex 和 o 系列自动路由到 Responses API。** Microsoft Foundry 将 GPT-5 / codex / o1 / o3 / o4 模型部署为仅支持 Responses API——对其调用 `/chat/completions` 会返回 `400 "The requested operation is unsupported."`。Hermes 通过名称检测这些模型系列,并透明地将 `api_mode` 升级为 `codex_responses`,即使 `config.yaml` 中仍写着 `api_mode: chat_completions`。GPT-4、GPT-4o、Llama、Mistral 及其他部署保持使用 `/chat/completions`。
|
||||
- **自动使用 `max_completion_tokens`。** Azure OpenAI(与直接使用 OpenAI 一样)对 gpt-4o、o 系列和 gpt-5.x 模型要求使用 `max_completion_tokens`。Hermes 根据端点发送正确的参数。
|
||||
- **需要 `api-version` 的旧版端点。** 如果你有类似 `https://<resource>.openai.azure.com/openai?api-version=2025-04-01-preview` 的旧版 base URL,Hermes 会提取查询字符串并通过每次请求的 `default_query` 转发(否则 OpenAI SDK 在拼接路径时会丢弃它)。
|
||||
|
||||
## Anthropic 风格端点(通过 Microsoft Foundry 使用 Claude)
|
||||
|
||||
对于 Claude 部署,使用 Anthropic 风格路由:
|
||||
|
||||
```yaml
|
||||
model:
|
||||
provider: azure-foundry
|
||||
base_url: https://my-resource.services.ai.azure.com/anthropic
|
||||
api_mode: anthropic_messages
|
||||
default: claude-sonnet-4-6
|
||||
```
|
||||
|
||||
重要行为:
|
||||
|
||||
- **从 base URL 中去除 `/v1`。** Anthropic SDK 在每次请求 URL 后追加 `/v1/messages`——Hermes 在将 URL 传递给 SDK 之前移除末尾的 `/v1`,以避免出现双重 `/v1` 路径。
|
||||
- **`api-version` 通过 `default_query` 传递,而非追加到 URL。** Azure Anthropic 要求 `api-version` 查询字符串。将其嵌入 base URL 会产生类似 `/anthropic?api-version=.../v1/messages` 的畸形路径并返回 404。Hermes 通过 Anthropic SDK 的 `default_query` 传递 `api-version=2025-04-15`。
|
||||
- **使用 Bearer 认证而非 `x-api-key`。** Azure 的 Anthropic 兼容路由要求 `Authorization: Bearer <key>`,而非 Anthropic 原生的 `x-api-key` 头。Hermes 检测到 base URL 中包含 `azure.com` 时,通过 SDK 的 `auth_token` 字段路由 API 密钥,确保正确的头部到达上游。
|
||||
- **保留 1M 上下文窗口 beta 头。** Azure 仍通过 `anthropic-beta: context-1m-2025-08-07` 头控制 1M token Claude 上下文(Opus 4.6/4.7、Sonnet 4.6)的访问。Hermes 在 Azure 路径上保留该 beta 头(在原生 Anthropic OAuth 请求中会被去除,因为某些订阅会拒绝它,但 Azure 要求它)。
|
||||
- **禁用 OAuth 令牌刷新。** Azure 部署使用静态 API 密钥。适用于 Anthropic Console 的 `~/.claude/.credentials.json` OAuth 令牌刷新循环对 Azure 端点明确跳过,以防止 Claude Code OAuth 令牌在会话中途覆盖你的 Azure 密钥。
|
||||
|
||||
## 替代方案:`provider: anthropic` + Azure base URL
|
||||
|
||||
如果你已配置 `provider: anthropic` 并只想将其指向 Microsoft Foundry 以使用 Claude,可以完全跳过 `azure-foundry` provider:
|
||||
|
||||
```yaml
|
||||
model:
|
||||
provider: anthropic
|
||||
base_url: https://my-resource.services.ai.azure.com/anthropic
|
||||
key_env: AZURE_ANTHROPIC_KEY
|
||||
default: claude-sonnet-4-6
|
||||
```
|
||||
|
||||
在 `~/.hermes/.env` 中设置 `AZURE_ANTHROPIC_KEY`。Hermes 检测到 base URL 中包含 `azure.com` 时,会绕过 Claude Code OAuth 令牌链,直接使用 Azure 密钥进行 `x-api-key` 认证。
|
||||
|
||||
`key_env` 是规范的 snake_case 字段名;`api_key_env`(以及驼峰式 `keyEnv` / `apiKeyEnv`)作为别名被接受。如果同时设置了 `key_env` 和 `AZURE_ANTHROPIC_KEY`/`ANTHROPIC_API_KEY`,`key_env` 指定的环境变量优先。
|
||||
|
||||
## 模型发现
|
||||
|
||||
Azure **不**暴露纯 API 密钥端点来列出你的*已部署*模型部署。部署枚举需要 Azure Resource Manager 认证(`az cognitiveservices account deployment list`)和 Azure AD 主体,而非推理 API 密钥。
|
||||
|
||||
Hermes 能做的:
|
||||
|
||||
- Azure OpenAI v1 端点(`<resource>.openai.azure.com/openai/v1`)通过 `GET /models` 暴露资源的**可用**模型目录。Hermes 使用此列表预填模型选择器。
|
||||
- Microsoft Foundry `/anthropic` 路由:通过 URL 路径检测,模型名称手动输入。
|
||||
- 私有 / 防火墙后的端点:手动输入,并显示友好的"无法探测"提示。
|
||||
|
||||
你始终可以直接输入部署名称——Hermes 不会对返回的列表进行验证。
|
||||
|
||||
## 环境变量
|
||||
|
||||
| 变量 | 用途 |
|
||||
|----------|---------|
|
||||
| `AZURE_FOUNDRY_API_KEY` | Microsoft Foundry / Azure OpenAI 的主 API 密钥(api_key 模式) |
|
||||
| `AZURE_FOUNDRY_BASE_URL` | 端点 URL(通过 `hermes model` 设置;环境变量作为回退) |
|
||||
| `AZURE_ANTHROPIC_KEY` | 由 `provider: anthropic` + Azure base URL 使用(`ANTHROPIC_API_KEY` 的替代) |
|
||||
| `AZURE_TENANT_ID` | 服务主体流程的 Entra ID 租户 |
|
||||
| `AZURE_CLIENT_ID` | Entra ID 客户端 ID(服务主体、工作负载标识或用户分配的托管标识) |
|
||||
| `AZURE_CLIENT_SECRET` | 服务主体密钥 |
|
||||
| `AZURE_CLIENT_CERTIFICATE_PATH` | 服务主体证书(密钥的替代方案) |
|
||||
| `AZURE_FEDERATED_TOKEN_FILE` | 工作负载标识联合令牌路径(AKS) |
|
||||
| `AZURE_AUTHORITY_HOST` | 主权云 authority 主机覆盖 |
|
||||
| `IDENTITY_ENDPOINT` / `MSI_ENDPOINT` | App Service、Functions 和 Container Apps 的托管标识端点;VM 通常改用 IMDS |
|
||||
|
||||
Azure SDK 直接读取 `AZURE_*` 环境变量。Hermes 除在 `hermes doctor` 输出中报告哪些来源存在外,不会检查这些变量。
|
||||
|
||||
## 故障排查
|
||||
|
||||
**gpt-5.x 部署返回 401 Unauthorized。**
|
||||
Azure 在 `/chat/completions` 上提供 gpt-5.x,而非 `/responses`。当 URL 包含 `openai.azure.com` 时,Hermes 会自动处理此问题,但如果你看到带有 `Invalid API key` 正文的 401,请检查 `config.yaml` 中的 `api_mode` 是否为 `chat_completions`。
|
||||
|
||||
**`/v1/messages?api-version=.../v1/messages` 返回 404。**
|
||||
这是修复前 Azure Anthropic 设置中的畸形 URL 问题。升级 Hermes——`api-version` 参数现在通过 `default_query` 传递,而非嵌入 base URL,因此 SDK 在 URL 拼接时不会破坏它。
|
||||
|
||||
**向导提示"自动检测不完整"。**
|
||||
端点拒绝了 `/models` 探测和 Anthropic Messages 探测。这对于防火墙后或设有 IP 白名单的私有端点是正常现象。回退到手动选择 API 模式并输入部署名称——一切仍然正常工作,Hermes 只是无法预填选择器。
|
||||
|
||||
**选择了错误的传输协议。**
|
||||
再次运行 `hermes model`,向导将重新探测。如果探测仍然选择了错误的模式,可以直接编辑 `config.yaml`:
|
||||
|
||||
```yaml
|
||||
model:
|
||||
provider: azure-foundry
|
||||
api_mode: anthropic_messages # 或 chat_completions
|
||||
```
|
||||
|
||||
**Entra ID:"credential chain exhausted" 或切换到 `auth_mode: entra_id` 后返回 401 Unauthorized。**
|
||||
- 运行 `az login` 刷新你的开发者会话(缓存的令牌可能已过期)。
|
||||
- 验证 `Azure AI User`(或 `Foundry User`)角色分配是否已生效:`az role assignment list --assignee <user-or-identity-id>` 应在你的 Foundry 资源上列出该角色。角色传播最多需要 5 分钟。
|
||||
- 对于用户分配的托管标识,请仔细检查 `AZURE_CLIENT_ID` 是否与附加到计算资源的标识匹配。
|
||||
- 运行 `hermes doctor`——Azure Entra 探测会报告令牌获取是否成功,并提供修复提示。
|
||||
|
||||
**Entra ID:向导预检挂起或超时。**
|
||||
10 秒预检是软性检查。选择"仍然保存,稍后验证",部署到目标环境后运行 `hermes doctor`。常见原因包括令牌服务不可达或本地登录状态过期——在 CI 中优先使用工作负载标识,使用服务主体时设置 `AZURE_TENANT_ID`+`AZURE_CLIENT_ID`+`AZURE_CLIENT_SECRET`,或在本地开发时运行 `az login`。
|
||||
|
||||
**Anthropic 风格端点使用 Entra ID 时返回 401。**
|
||||
验证同一 `Azure AI User`(或 `Foundry User`)角色是否已在 Foundry 资源上分配(它同时覆盖 `/openai/v1` 和 `/anthropic` 路径)。如果向导期间 OpenAI 风格探测成功,但运行时 `claude-*` 请求失败,最常见的原因是早期向导运行遗留的过时 `model.entra.scope`——从 `config.yaml` 中删除 `entra.scope` 行,使运行时回退到默认的 `https://ai.azure.com/.default` scope。
|
||||
|
||||
## 相关链接
|
||||
|
||||
- [环境变量](/reference/environment-variables)
|
||||
- [配置](/user-guide/configuration)
|
||||
- [AWS Bedrock](/guides/aws-bedrock) — 另一个主要的云 provider 集成
|
||||
- [Microsoft:为 Foundry 配置 Entra ID](https://learn.microsoft.com/azure/ai-foundry/foundry-models/how-to/configure-entra-id) — 无密钥路径的上游文档
|
||||
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,247 @@
|
||||
---
|
||||
sidebar_position: 13
|
||||
title: "纯脚本 Cron 任务(无 LLM)"
|
||||
description: "完全跳过 LLM 的经典看门狗 cron 任务——脚本按计划运行,其 stdout 输出直接投递到你的消息平台。内存告警、磁盘告警、CI 通知、定期健康检查。"
|
||||
---
|
||||
|
||||
# 纯脚本 Cron 任务
|
||||
|
||||
有时你已经清楚地知道要发送什么消息。你不需要 agent 来推理——你只需要一个脚本按计时器运行,并将其输出(如有)发送到 Telegram / Discord / Slack / Signal。
|
||||
|
||||
Hermes 将此称为**无 agent 模式**。这是去掉 LLM 的 cron 系统。
|
||||
|
||||
<!-- ascii-guard-ignore -->
|
||||
```
|
||||
┌──────────────────┐ ┌──────────────────┐
|
||||
│ scheduler tick │ every │ run script │
|
||||
│ (every N minutes)│ ──────▶ │ (bash or python) │
|
||||
└──────────────────┘ └──────────────────┘
|
||||
│
|
||||
│ stdout
|
||||
▼
|
||||
┌──────────────────┐
|
||||
│ delivery router │
|
||||
│ (telegram/disc…) │
|
||||
└──────────────────┘
|
||||
```
|
||||
<!-- ascii-guard-ignore-end -->
|
||||
|
||||
- **无 LLM 调用。** 零 token,零 agent 循环,零模型费用。
|
||||
- **脚本即任务。** 由脚本决定是否告警。有输出 → 发送消息;无输出 → 静默执行。
|
||||
- **Bash 或 Python。** `.sh` / `.bash` 文件在 `/bin/bash` 下运行;其他扩展名在当前 Python 解释器下运行。`~/.hermes/scripts/` 中的任何文件均可接受。
|
||||
- **同一调度器。** 与 LLM 任务共存于 `cronjob` 中——暂停、恢复、列出、日志和投递目标的操作方式完全相同。
|
||||
|
||||
## 适用场景
|
||||
|
||||
以下情况使用无 agent 模式:
|
||||
|
||||
- **内存 / 磁盘 / GPU 看门狗。** 每 5 分钟运行一次,仅在超过阈值时告警。
|
||||
- **CI hook(钩子)。** 部署完成 → 发送 commit SHA;构建失败 → 发送最后 100 行日志。
|
||||
- **定期指标。** "每天上午 9 点的 Stripe 收入"——一次简单的 API 调用加格式化输出。
|
||||
- **外部事件轮询。** 检查 API,在状态变化时告警。
|
||||
- **心跳。** 每 N 分钟 ping 一次仪表板,证明主机存活。
|
||||
|
||||
当你需要 agent **决定**说什么时——总结长文档、从 feed 中挑选有趣条目、起草友好提醒——请使用普通的(LLM 驱动的)cron 任务。无 agent 路径适用于脚本的 stdout 本身就是消息内容的场景。
|
||||
|
||||
## 通过聊天创建
|
||||
|
||||
无 agent 模式的真正优势在于:agent 本身可以为你设置看门狗——无需编辑器、无需 shell、无需记忆 CLI 参数。你描述需求,Hermes 编写脚本、安排计划,并告知你何时触发。
|
||||
|
||||
### 示例对话
|
||||
|
||||
> **你:** 每 5 分钟检查一次,如果内存超过 85% 就在 telegram 通知我
|
||||
>
|
||||
> **Hermes:** *(写入 `~/.hermes/scripts/memory-watchdog.sh`,然后以 `no_agent=true` 调用 `cronjob(...)`)*
|
||||
>
|
||||
> 已设置。每 5 分钟运行一次,仅在内存超过 85% 时告警 Telegram。脚本:`memory-watchdog.sh`。任务 ID:`abc123`。
|
||||
|
||||
在底层,agent 进行了两次工具调用:
|
||||
|
||||
```python
|
||||
# 1. 写入检查脚本
|
||||
write_file(
|
||||
path="~/.hermes/scripts/memory-watchdog.sh",
|
||||
content='''#!/usr/bin/env bash
|
||||
ram_pct=$(free | awk '/^Mem:/ {printf "%d", $3 * 100 / $2}')
|
||||
if [ "$ram_pct" -ge 85 ]; then
|
||||
echo "RAM ${ram_pct}% on $(hostname)"
|
||||
fi
|
||||
# Empty stdout = silent tick; no message sent.
|
||||
''',
|
||||
)
|
||||
|
||||
# 2. 调度任务 — no_agent=True 在每次触发时跳过 LLM
|
||||
cronjob(
|
||||
action="create",
|
||||
schedule="every 5m",
|
||||
script="memory-watchdog.sh",
|
||||
no_agent=True,
|
||||
deliver="telegram",
|
||||
name="memory-watchdog",
|
||||
)
|
||||
```
|
||||
|
||||
此后每次触发都是免费的:调度器运行脚本,若 stdout 非空则将其发送到 Telegram,且从不调用模型。
|
||||
|
||||
### agent 为你决定的内容
|
||||
|
||||
当你的请求类似"当 X 时告警我"或"每 N 分钟检查 Y,如果 Z 则通知我",Hermes 的 `cronjob` 工具描述会指示它在消息内容完全由脚本决定时使用 `no_agent=True`。当请求需要推理时(*"总结新 issue"*、*"挑选最有趣的头条"*、*"起草一条友好提醒"*),则回退到普通的 LLM 驱动路径。
|
||||
|
||||
你不必自己指定 `--no-agent`。只需描述你想要的行为即可。
|
||||
|
||||
### 通过聊天管理看门狗
|
||||
|
||||
agent 可以用与创建任务相同的方式暂停、恢复、编辑和删除任务:
|
||||
|
||||
> **你:** 今晚先停掉内存看门狗
|
||||
>
|
||||
> **Hermes:** *(调用 `cronjob(action='pause', job_id='abc123')`)*
|
||||
>
|
||||
> 已暂停。说"重新开启"或执行 `hermes cron resume abc123` 可恢复。
|
||||
|
||||
> **你:** 改成每 15 分钟一次
|
||||
>
|
||||
> **Hermes:** *(调用 `cronjob(action='update', job_id='abc123', schedule='every 15m')`)*
|
||||
|
||||
完整生命周期(创建 / 列出 / 更新 / 暂停 / 恢复 / 立即运行 / 删除)均可由 agent 操作,无需你学习任何 CLI 命令。
|
||||
|
||||
## 通过 CLI 创建
|
||||
|
||||
偏好 shell?CLI 路径用三条命令即可达到相同效果:
|
||||
|
||||
```bash
|
||||
# 1. 编写脚本
|
||||
cat > ~/.hermes/scripts/memory-watchdog.sh <<'EOF'
|
||||
#!/usr/bin/env bash
|
||||
# Alert when RAM usage is over 85%. Silent otherwise.
|
||||
RAM_PCT=$(free | awk '/^Mem:/ {printf "%d", $3 * 100 / $2}')
|
||||
if [ "$RAM_PCT" -ge 85 ]; then
|
||||
echo "⚠ RAM ${RAM_PCT}% on $(hostname)"
|
||||
fi
|
||||
# Empty stdout = silent run; no message sent.
|
||||
EOF
|
||||
chmod +x ~/.hermes/scripts/memory-watchdog.sh
|
||||
|
||||
# 2. 调度任务
|
||||
hermes cron create "every 5m" \
|
||||
--no-agent \
|
||||
--script memory-watchdog.sh \
|
||||
--deliver telegram \
|
||||
--name "memory-watchdog"
|
||||
|
||||
# 3. 验证
|
||||
hermes cron list
|
||||
hermes cron run <job_id> # 触发一次以测试
|
||||
```
|
||||
|
||||
就这些。无 prompt(提示词),无技能,无模型。
|
||||
|
||||
|
||||
## 脚本输出与投递的映射关系
|
||||
|
||||
| 脚本行为 | 结果 |
|
||||
|-----------------|--------|
|
||||
| 退出码 0,stdout 非空 | stdout 原样投递 |
|
||||
| 退出码 0,stdout 为空 | 静默执行——不投递 |
|
||||
| 退出码 0,stdout 最后一行包含 `{"wakeAgent": false}` | 静默执行(与 LLM 任务共用的门控) |
|
||||
| 非零退出码 | 投递错误告警(确保损坏的看门狗不会静默失败) |
|
||||
| 脚本超时 | 投递错误告警 |
|
||||
|
||||
"空则静默"的行为是经典看门狗模式的关键:脚本可以每分钟运行一次,但只有在真正需要关注时,频道才会收到消息。
|
||||
|
||||
## 脚本规则
|
||||
|
||||
脚本必须位于 `~/.hermes/scripts/`。这在任务创建时和运行时均会强制检查——绝对路径、`~/` 展开以及路径穿越模式(`../`)均会被拒绝。该目录与 LLM 任务使用的预检脚本门控共享。
|
||||
|
||||
解释器由文件扩展名决定:
|
||||
|
||||
| 扩展名 | 解释器 |
|
||||
|-----------|-------------|
|
||||
| `.sh`、`.bash` | `/bin/bash` |
|
||||
| 其他任意扩展名 | `sys.executable`(当前 Python) |
|
||||
|
||||
我们有意**不**遵循 `#!/...` shebang——保持解释器集合明确且精简,可减少调度器信任的攻击面。
|
||||
|
||||
## 计划语法
|
||||
|
||||
与所有其他 cron 任务相同:
|
||||
|
||||
```bash
|
||||
hermes cron create "every 5m" # 间隔
|
||||
hermes cron create "every 2h"
|
||||
hermes cron create "0 9 * * *" # 标准 cron:每天上午 9 点
|
||||
hermes cron create "30m" # 单次:30 分钟后运行一次
|
||||
```
|
||||
|
||||
完整语法请参阅 [cron 功能参考](/user-guide/features/cron)。
|
||||
|
||||
## 投递目标
|
||||
|
||||
`--deliver` 接受 gateway 已知的所有目标。常见形式:
|
||||
|
||||
```bash
|
||||
--deliver telegram # 平台默认频道
|
||||
--deliver telegram:-1001234567890 # 指定聊天
|
||||
--deliver telegram:-1001234567890:17585 # 指定 Telegram 论坛话题
|
||||
--deliver discord:#ops
|
||||
--deliver slack:#engineering
|
||||
--deliver signal:+15551234567
|
||||
--deliver local # 仅保存到 ~/.hermes/cron/output/
|
||||
```
|
||||
|
||||
对于使用 bot token 的平台(Telegram、Discord、Slack、Signal、SMS、WhatsApp),脚本运行时无需运行中的 gateway——工具直接使用 `~/.hermes/.env` / `~/.hermes/config.yaml` 中已有的凭据调用各平台的 REST 端点。
|
||||
|
||||
## 编辑与生命周期
|
||||
|
||||
```bash
|
||||
hermes cron list # 查看所有任务
|
||||
hermes cron pause <job_id> # 停止触发,保留定义
|
||||
hermes cron resume <job_id>
|
||||
hermes cron edit <job_id> --schedule "every 10m" # 调整频率
|
||||
hermes cron edit <job_id> --agent # 切换为 LLM 模式
|
||||
hermes cron edit <job_id> --no-agent --script … # 切换回无 agent 模式
|
||||
hermes cron remove <job_id> # 删除任务
|
||||
```
|
||||
|
||||
所有适用于 LLM 任务的操作(暂停、恢复、手动触发、投递目标变更)同样适用于无 agent 任务。
|
||||
|
||||
## 实战示例:磁盘空间告警
|
||||
|
||||
```bash
|
||||
cat > ~/.hermes/scripts/disk-alert.sh <<'EOF'
|
||||
#!/usr/bin/env bash
|
||||
# Alert when / or /home is over 90% full.
|
||||
THRESHOLD=90
|
||||
df -h / /home 2>/dev/null | awk -v t="$THRESHOLD" '
|
||||
NR > 1 && $5+0 >= t {
|
||||
printf "⚠ Disk %s full on %s\n", $5, $6
|
||||
}
|
||||
'
|
||||
EOF
|
||||
chmod +x ~/.hermes/scripts/disk-alert.sh
|
||||
|
||||
hermes cron create "*/15 * * * *" \
|
||||
--no-agent \
|
||||
--script disk-alert.sh \
|
||||
--deliver telegram \
|
||||
--name "disk-alert"
|
||||
```
|
||||
|
||||
当两个文件系统均低于 90% 时静默;当某个文件系统超出阈值时,每个超限文件系统触发一行告警。
|
||||
|
||||
## 与其他模式的对比
|
||||
|
||||
| 方式 | 运行内容 | 适用场景 |
|
||||
|----------|-----------|-------------|
|
||||
| `cronjob --no-agent`(本页) | 你的脚本,由 Hermes 调度 | 不需要推理的周期性看门狗 / 告警 / 指标 |
|
||||
| `cronjob`(默认,LLM) | 带可选预检脚本的 agent | 消息内容需要对数据进行推理时 |
|
||||
| OS cron + `curl` 到 [webhook 订阅](/user-guide/messaging/webhooks) | 你的脚本,由 OS 调度 | 当 Hermes 本身可能不健康时(即被监控对象) |
|
||||
|
||||
对于必须在 **gateway 宕机时也能触发**的关键系统健康看门狗,请使用 OS 级 cron 配合 `curl` 调用 Hermes webhook 订阅(或任何外部告警端点)——这些作为独立 OS 进程运行,不依赖 Hermes 是否在线。当被监控对象是外部系统时,in-gateway 调度器才是正确选择。
|
||||
|
||||
## 相关文档
|
||||
|
||||
- [用 Cron 自动化一切](/guides/automate-with-cron) — LLM 驱动的 cron 模式。
|
||||
- [定时任务(Cron)参考](/user-guide/features/cron) — 完整计划语法、生命周期、投递路由。
|
||||
- [Webhook 订阅](/user-guide/messaging/webhooks) — 供外部调度器使用的即发即忘 HTTP 入口。
|
||||
- [Gateway 内部机制](/developer-guide/gateway-internals) — 投递路由器内部实现。
|
||||
@ -0,0 +1,225 @@
|
||||
---
|
||||
sidebar_position: 12
|
||||
title: "Cron 故障排查"
|
||||
description: "诊断并修复常见的 Hermes cron 问题——任务未触发、投递失败、skill 加载错误及性能问题"
|
||||
---
|
||||
|
||||
# Cron 故障排查
|
||||
|
||||
当 cron 任务行为异常时,请按顺序逐项检查。大多数问题属于以下四类之一:时序、投递、权限或 skill 加载。
|
||||
|
||||
---
|
||||
|
||||
## 任务未触发
|
||||
|
||||
### 检查 1:确认任务存在且处于活跃状态
|
||||
|
||||
```bash
|
||||
hermes cron list
|
||||
```
|
||||
|
||||
找到该任务并确认其状态为 `[active]`(而非 `[paused]` 或 `[completed]`)。若显示 `[completed]`,可能是重复次数已耗尽——编辑该任务以重置。
|
||||
|
||||
### 检查 2:确认调度表达式正确
|
||||
|
||||
格式错误的调度表达式会静默降级为单次执行,或被直接拒绝。测试你的表达式:
|
||||
|
||||
| 你的表达式 | 应解析为 |
|
||||
|----------------|-------------------|
|
||||
| `0 9 * * *` | 每天上午 9:00 |
|
||||
| `0 9 * * 1` | 每周一上午 9:00 |
|
||||
| `every 2h` | 从现在起每 2 小时 |
|
||||
| `30m` | 从现在起 30 分钟后 |
|
||||
| `2025-06-01T09:00:00` | 2025 年 6 月 1 日 09:00 UTC |
|
||||
|
||||
若任务触发一次后从列表中消失,说明这是单次调度(`30m`、`1d` 或 ISO 时间戳)——属于预期行为。
|
||||
|
||||
### 检查 3:gateway 是否正在运行?
|
||||
|
||||
Cron 任务由 gateway 的后台 ticker 线程触发,该线程每 60 秒 tick 一次。普通的 CLI 聊天会话**不会**自动触发 cron 任务。
|
||||
|
||||
如果你期望任务自动触发,需要运行一个 gateway(前台运行用 `hermes gateway`,安装为服务用 `hermes gateway start`)。如需单次调试,可手动触发一次 tick:`hermes cron tick`。
|
||||
|
||||
### 检查 4:检查系统时钟和时区
|
||||
|
||||
任务使用本地时区。若机器时钟有误或时区与预期不符,任务将在错误的时间触发。验证方法:
|
||||
|
||||
```bash
|
||||
date
|
||||
hermes cron list # 将 next_run 时间与本地时间对比
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 投递失败
|
||||
|
||||
### 检查 1:确认投递目标正确
|
||||
|
||||
投递目标区分大小写,且要求对应平台已正确配置。目标配置错误会静默丢弃响应。
|
||||
|
||||
| 目标 | 所需配置 |
|
||||
|--------|----------|
|
||||
| `telegram` | `~/.hermes/.env` 中的 `TELEGRAM_BOT_TOKEN` |
|
||||
| `discord` | `~/.hermes/.env` 中的 `DISCORD_BOT_TOKEN` |
|
||||
| `slack` | `~/.hermes/.env` 中的 `SLACK_BOT_TOKEN` |
|
||||
| `whatsapp` | 已配置 WhatsApp gateway |
|
||||
| `signal` | 已配置 Signal gateway |
|
||||
| `matrix` | 已配置 Matrix homeserver |
|
||||
| `email` | `config.yaml` 中已配置 SMTP |
|
||||
| `sms` | 已配置 SMS 提供商 |
|
||||
| `local` | 对 `~/.hermes/cron/output/` 有写权限 |
|
||||
| `origin` | 投递到创建该任务的聊天会话 |
|
||||
|
||||
其他支持的平台包括 `mattermost`、`homeassistant`、`dingtalk`、`feishu`、`wecom`、`weixin`、`bluebubbles`、`qqbot` 和 `webhook`。你也可以使用 `platform:chat_id` 语法指定特定聊天(例如 `telegram:-1001234567890`)。
|
||||
|
||||
若投递失败,任务仍会执行——只是不会发送到任何地方。检查 `hermes cron list` 中的 `last_error` 字段(如有)。
|
||||
|
||||
### 检查 2:检查 `[SILENT]` 的使用
|
||||
|
||||
若你的 cron 任务没有输出,或 agent 响应为 `[SILENT]`,投递会被抑制。这对监控类任务是预期行为——但请确认你的 prompt(提示词)没有意外地抑制所有输出。
|
||||
|
||||
若 prompt 中写有"如果没有变化则回复 [SILENT]",非空响应也可能被静默吞掉。请检查你的条件逻辑。
|
||||
|
||||
### 检查 3:平台 token 权限
|
||||
|
||||
每个消息平台的 bot 需要特定权限才能发送消息。若投递静默失败:
|
||||
|
||||
- **Telegram**:Bot 必须是目标群组/频道的管理员
|
||||
- **Discord**:Bot 必须有目标频道的发送权限
|
||||
- **Slack**:Bot 必须已加入工作区并拥有 `chat:write` scope
|
||||
|
||||
### 检查 4:响应包装
|
||||
|
||||
默认情况下,cron 响应会添加页眉和页脚(`config.yaml` 中的 `cron.wrap_response: true`)。某些平台或集成可能无法正常处理。如需禁用:
|
||||
|
||||
```yaml
|
||||
cron:
|
||||
wrap_response: false
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Skill 加载失败
|
||||
|
||||
### 检查 1:确认 skill 已安装
|
||||
|
||||
```bash
|
||||
hermes skills list
|
||||
```
|
||||
|
||||
Skill 必须先安装才能附加到 cron 任务。若 skill 缺失,先用 `hermes skills install <skill-name>` 安装,或在 CLI 中通过 `/skills` 安装。
|
||||
|
||||
### 检查 2:检查 skill 名称与 skill 文件夹名称
|
||||
|
||||
Skill 名称区分大小写,必须与已安装 skill 的文件夹名称完全匹配。若任务指定的是 `ai-funding-daily-report`,但 skill 文件夹也是 `ai-funding-daily-report`,请从 `hermes skills list` 确认确切名称。
|
||||
|
||||
### 检查 3:依赖交互式工具的 skill
|
||||
|
||||
Cron 任务运行时,`cronjob`、`messaging` 和 `clarify` 工具集均被禁用。这可防止递归创建 cron、直接发送消息(投递由调度器处理)以及交互式提示。若某 skill 依赖这些工具集,它将无法在 cron 上下文中运行。
|
||||
|
||||
请查阅该 skill 的文档,确认其支持非交互式(headless)模式。
|
||||
|
||||
### 检查 4:多 skill 加载顺序
|
||||
|
||||
使用多个 skill 时,它们按顺序加载。若 Skill A 依赖 Skill B 的上下文,请确保 B 先加载:
|
||||
|
||||
```bash
|
||||
/cron add "0 9 * * *" "..." --skill context-skill --skill target-skill
|
||||
```
|
||||
|
||||
在此示例中,`context-skill` 先于 `target-skill` 加载。
|
||||
|
||||
---
|
||||
|
||||
## 任务错误与失败
|
||||
|
||||
### 检查 1:查看近期任务输出
|
||||
|
||||
若任务运行后失败,可在以下位置查看错误上下文:
|
||||
|
||||
1. 任务投递的聊天会话(若投递成功)
|
||||
2. `~/.hermes/logs/agent.log`(调度器消息)或 `errors.log`(警告信息)
|
||||
3. 通过 `hermes cron list` 查看任务的 `last_run` 元数据
|
||||
|
||||
### 检查 2:常见错误模式
|
||||
|
||||
**脚本报 "No such file or directory"**
|
||||
`script` 路径必须为绝对路径(或相对于 Hermes 配置目录的路径)。验证:
|
||||
```bash
|
||||
ls ~/.hermes/scripts/your-script.py # 必须存在
|
||||
hermes cron edit <job_id> --script ~/.hermes/scripts/your-script.py
|
||||
```
|
||||
|
||||
**任务执行时报 "Skill not found"**
|
||||
Skill 必须安装在运行调度器的机器上。若你在不同机器间切换,skill 不会自动同步——请用 `hermes skills install <skill-name>` 重新安装。
|
||||
|
||||
**任务运行但没有投递任何内容**
|
||||
可能是投递目标问题(见上方"投递失败"部分)或响应被静默抑制(`[SILENT]`)。
|
||||
|
||||
**任务挂起或超时**
|
||||
调度器使用基于不活跃时间的超时机制(默认 600 秒,可通过 `HERMES_CRON_TIMEOUT` 环境变量配置,`0` 表示无限制)。只要 agent 持续调用工具,就可以一直运行——计时器仅在持续不活跃后触发。长时间运行的任务应使用脚本处理数据采集,仅将结果投递出去。
|
||||
|
||||
### 检查 3:锁竞争
|
||||
|
||||
调度器使用基于文件的锁来防止 tick 重叠。若同时运行了两个 gateway 实例(或 CLI 会话与 gateway 冲突),任务可能被延迟或跳过。
|
||||
|
||||
终止重复的 gateway 进程:
|
||||
```bash
|
||||
ps aux | grep hermes
|
||||
# 终止重复进程,只保留一个
|
||||
```
|
||||
|
||||
### 检查 4:jobs.json 的权限
|
||||
|
||||
任务存储在 `~/.hermes/cron/jobs.json`。若该文件对当前用户不可读写,调度器将静默失败:
|
||||
|
||||
```bash
|
||||
ls -la ~/.hermes/cron/jobs.json
|
||||
chmod 600 ~/.hermes/cron/jobs.json # 应由你的用户拥有
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 性能问题
|
||||
|
||||
### 任务启动缓慢
|
||||
|
||||
每个 cron 任务都会创建一个全新的 AIAgent 会话,可能涉及提供商认证和模型加载。对于时间敏感的调度,请预留缓冲时间(例如用 `0 8 * * *` 代替 `0 9 * * *`)。
|
||||
|
||||
### 过多任务重叠
|
||||
|
||||
调度器在每次 tick 内顺序执行任务。若多个任务同时到期,它们将依次运行。考虑错开调度时间(例如用 `0 9 * * *` 和 `5 9 * * *` 代替两者都设为 `0 9 * * *`)以避免延迟。
|
||||
|
||||
### 脚本输出过大
|
||||
|
||||
输出数兆字节数据的脚本会拖慢 agent,并可能触及 token 限制。请在脚本层面进行过滤/摘要——只输出 agent 需要推理的内容。
|
||||
|
||||
---
|
||||
|
||||
## 诊断命令
|
||||
|
||||
```bash
|
||||
hermes cron list # 显示所有任务、状态、next_run 时间
|
||||
hermes cron run <job_id> # 安排在下次 tick 执行(用于测试)
|
||||
hermes cron edit <job_id> # 修复配置问题
|
||||
hermes logs # 查看近期 Hermes 日志
|
||||
hermes skills list # 确认已安装的 skill
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 获取更多帮助
|
||||
|
||||
若你已按本指南逐项排查,问题仍未解决:
|
||||
|
||||
1. 使用 `hermes cron run <job_id>` 运行任务(在下次 gateway tick 时触发),观察聊天输出中的错误
|
||||
2. 查看 `~/.hermes/logs/agent.log` 中的调度器消息和 `~/.hermes/logs/errors.log` 中的警告
|
||||
3. 在 [github.com/NousResearch/hermes-agent](https://github.com/NousResearch/hermes-agent) 提交 issue,并附上:
|
||||
- 任务 ID 和调度表达式
|
||||
- 投递目标
|
||||
- 预期行为与实际行为
|
||||
- 日志中的相关错误信息
|
||||
|
||||
---
|
||||
|
||||
*完整的 cron 参考文档,请参阅 [用 Cron 自动化一切](/guides/automate-with-cron) 和 [定时任务(Cron)](/user-guide/features/cron)。*
|
||||
@ -0,0 +1,268 @@
|
||||
---
|
||||
sidebar_position: 3
|
||||
title: "教程:每日简报机器人"
|
||||
description: "构建一个自动化每日简报机器人,研究主题、汇总发现,并每天早晨推送至 Telegram 或 Discord"
|
||||
---
|
||||
|
||||
# 教程:构建每日简报机器人
|
||||
|
||||
在本教程中,你将构建一个个人简报机器人,它每天早晨自动启动,研究你关心的主题,汇总发现,并将简洁的简报直接推送到你的 Telegram 或 Discord。
|
||||
|
||||
完成后,你将拥有一个完全自动化的工作流,结合了 **网页搜索**、**cron 调度**、**委托(delegation)** 和 **消息推送** — 无需编写代码。
|
||||
|
||||
## 我们要构建什么
|
||||
|
||||
流程如下:
|
||||
|
||||
1. **上午 8:00** — cron 调度器触发任务
|
||||
2. **Hermes 启动**一个全新的 agent 会话,使用你的 prompt(提示词)
|
||||
3. **网页搜索**拉取你关注主题的最新新闻
|
||||
4. **汇总**将内容提炼为简洁的简报格式
|
||||
5. **推送**将简报发送到你的 Telegram 或 Discord
|
||||
|
||||
整个流程无需人工干预。你只需在早晨喝咖啡时阅读简报即可。
|
||||
|
||||
## 前提条件
|
||||
|
||||
开始之前,请确保:
|
||||
|
||||
- **已安装 Hermes Agent** — 参见[安装指南](/getting-started/installation)
|
||||
- **Gateway 正在运行** — gateway 守护进程负责处理 cron 执行:
|
||||
```bash
|
||||
hermes gateway install # Install as a user service
|
||||
sudo hermes gateway install --system # Linux servers: boot-time system service
|
||||
# or
|
||||
hermes gateway # Run in foreground
|
||||
```
|
||||
- **Firecrawl API 密钥** — 在环境变量中设置 `FIRECRAWL_API_KEY` 以启用网页搜索
|
||||
- **已配置消息推送**(可选但推荐)— 已设置 [Telegram](/user-guide/messaging/telegram) 或 Discord 并配置了 home channel
|
||||
|
||||
:::tip 没有消息推送?没关系
|
||||
你仍然可以使用 `deliver: "local"` 跟随本教程。简报将保存至 `~/.hermes/cron/output/`,你可以随时查阅。
|
||||
:::
|
||||
|
||||
## 第一步:手动测试工作流
|
||||
|
||||
在自动化之前,先确认简报功能正常。启动聊天会话:
|
||||
|
||||
```bash
|
||||
hermes
|
||||
```
|
||||
|
||||
然后输入以下 prompt:
|
||||
|
||||
```
|
||||
Search for the latest news about AI agents and open source LLMs.
|
||||
Summarize the top 3 stories in a concise briefing format with links.
|
||||
```
|
||||
|
||||
Hermes 将搜索网页、阅读结果,并生成类似以下内容:
|
||||
|
||||
```
|
||||
☀️ Your AI Briefing — March 8, 2026
|
||||
|
||||
1. Qwen 3 Released with 235B Parameters
|
||||
Alibaba's latest open-weight model matches GPT-4.5 on several
|
||||
benchmarks while remaining fully open source.
|
||||
→ https://qwenlm.github.io/blog/qwen3/
|
||||
|
||||
2. LangChain Launches Agent Protocol Standard
|
||||
A new open standard for agent-to-agent communication gains
|
||||
adoption from 15 major frameworks in its first week.
|
||||
→ https://blog.langchain.dev/agent-protocol/
|
||||
|
||||
3. EU AI Act Enforcement Begins for General-Purpose Models
|
||||
The first compliance deadlines hit, with open source models
|
||||
receiving exemptions under the 10M parameter threshold.
|
||||
→ https://artificialintelligenceact.eu/updates/
|
||||
|
||||
---
|
||||
3 stories • Sources searched: 8 • Generated by Hermes Agent
|
||||
```
|
||||
|
||||
如果运行正常,你就可以开始自动化了。
|
||||
|
||||
:::tip 反复调整格式
|
||||
尝试不同的 prompt,直到得到你满意的输出。可以添加诸如"使用 emoji 标题"或"每条摘要不超过 2 句话"之类的指令。最终确定的内容将写入 cron 任务。
|
||||
:::
|
||||
|
||||
## 第二步:创建 Cron 任务
|
||||
|
||||
现在让我们将其设置为每天早晨自动运行。有两种方式可以实现。
|
||||
|
||||
在创建 cron 任务之前,请确保 Hermes 已全局配置了默认模型和 provider。如果你希望某个任务使用不同的值,可在创建时设置该任务专属的 model/provider 覆盖项。
|
||||
|
||||
### 方式 A:自然语言(在聊天中)
|
||||
|
||||
直接告诉 Hermes 你想要什么:
|
||||
|
||||
```
|
||||
Every morning at 8am, search the web for the latest news about AI agents
|
||||
and open source LLMs. Summarize the top 3 stories in a concise briefing
|
||||
with links. Use a friendly, professional tone. Deliver to telegram.
|
||||
```
|
||||
|
||||
Hermes 将使用统一的 `cronjob` 工具为你创建 cron 任务。
|
||||
|
||||
### 方式 B:CLI 斜杠命令
|
||||
|
||||
使用 `/cron` 命令进行更精细的控制:
|
||||
|
||||
```
|
||||
/cron add "0 8 * * *" "Search the web for the latest news about AI agents and open source LLMs. Find at least 5 recent articles from the past 24 hours. Summarize the top 3 most important stories in a concise daily briefing format. For each story include: a clear headline, a 2-sentence summary, and the source URL. Use a friendly, professional tone. Format with emoji bullet points and end with a total story count."
|
||||
```
|
||||
|
||||
### 黄金法则:自包含的 Prompt
|
||||
|
||||
:::warning 关键概念
|
||||
Cron 任务在**全新会话**中运行 — 不保留之前对话的任何记忆,也不了解你"之前设置"的任何内容。你的 prompt 必须包含 agent 完成任务所需的**一切信息**。
|
||||
:::
|
||||
|
||||
**糟糕的 prompt:**
|
||||
```
|
||||
Do my usual morning briefing.
|
||||
```
|
||||
|
||||
**好的 prompt:**
|
||||
```
|
||||
Search the web for the latest news about AI agents and open source LLMs.
|
||||
Find at least 5 recent articles from the past 24 hours. Summarize the
|
||||
top 3 most important stories in a concise daily briefing format. For each
|
||||
story include: a clear headline, a 2-sentence summary, and the source URL.
|
||||
Use a friendly, professional tone. Format with emoji bullet points.
|
||||
```
|
||||
|
||||
好的 prompt 明确说明了**搜索什么**、**多少篇文章**、**什么格式**以及**什么语气**。它在一次输入中包含了 agent 所需的全部信息。
|
||||
|
||||
## 第三步:自定义简报
|
||||
|
||||
基础简报运行正常后,你可以进一步发挥创意。
|
||||
|
||||
### 多主题简报
|
||||
|
||||
在一份简报中涵盖多个领域:
|
||||
|
||||
```
|
||||
/cron add "0 8 * * *" "Create a morning briefing covering three topics. For each topic, search the web for recent news from the past 24 hours and summarize the top 2 stories with links.
|
||||
|
||||
Topics:
|
||||
1. AI and machine learning — focus on open source models and agent frameworks
|
||||
2. Cryptocurrency — focus on Bitcoin, Ethereum, and regulatory news
|
||||
3. Space exploration — focus on SpaceX, NASA, and commercial space
|
||||
|
||||
Format as a clean briefing with section headers and emoji. End with today's date and a motivational quote."
|
||||
```
|
||||
|
||||
### 使用委托进行并行研究
|
||||
|
||||
若要加快简报生成速度,可以告诉 Hermes 将每个主题委托给子 agent:
|
||||
|
||||
```
|
||||
/cron add "0 8 * * *" "Create a morning briefing by delegating research to sub-agents. Delegate three parallel tasks:
|
||||
|
||||
1. Delegate: Search for the top 2 AI/ML news stories from the past 24 hours with links
|
||||
2. Delegate: Search for the top 2 cryptocurrency news stories from the past 24 hours with links
|
||||
3. Delegate: Search for the top 2 space exploration news stories from the past 24 hours with links
|
||||
|
||||
Collect all results and combine them into a single clean briefing with section headers, emoji formatting, and source links. Add today's date as a header."
|
||||
```
|
||||
|
||||
每个子 agent 独立并行搜索,然后主 agent 将所有内容合并为一份精美的简报。详见[委托文档](/user-guide/features/delegation)了解其工作原理。
|
||||
|
||||
### 仅工作日调度
|
||||
|
||||
不需要周末简报?使用针对周一至周五的 cron 表达式:
|
||||
|
||||
```
|
||||
/cron add "0 8 * * 1-5" "Search for the latest AI and tech news..."
|
||||
```
|
||||
|
||||
### 每日两次简报
|
||||
|
||||
获取早晨概览和傍晚回顾:
|
||||
|
||||
```
|
||||
/cron add "0 8 * * *" "Morning briefing: search for AI news from the past 12 hours..."
|
||||
/cron add "0 18 * * *" "Evening recap: search for AI news from the past 12 hours..."
|
||||
```
|
||||
|
||||
### 通过 Memory 添加个人上下文
|
||||
|
||||
如果你启用了 [memory(记忆)](/user-guide/features/memory),可以存储跨会话持久保留的偏好设置。但请记住 — cron 任务在全新会话中运行,不保留对话记忆。若要添加个人上下文,请直接将其写入 prompt:
|
||||
|
||||
```
|
||||
/cron add "0 8 * * *" "You are creating a briefing for a senior ML engineer who cares about: PyTorch ecosystem, transformer architectures, open-weight models, and AI regulation in the EU. Skip stories about product launches or funding rounds unless they involve open source.
|
||||
|
||||
Search for the latest news on these topics. Summarize the top 3 stories with links. Be concise and technical — this reader doesn't need basic explanations."
|
||||
```
|
||||
|
||||
:::tip 定制受众角色
|
||||
在 prompt 中加入简报受众的详细信息,能显著提升内容相关性。告诉 agent 你的角色、兴趣以及需要跳过的内容。
|
||||
:::
|
||||
|
||||
## 第四步:管理你的任务
|
||||
|
||||
### 列出所有已调度任务
|
||||
|
||||
在聊天中:
|
||||
```
|
||||
/cron list
|
||||
```
|
||||
|
||||
或在终端中:
|
||||
```bash
|
||||
hermes cron list
|
||||
```
|
||||
|
||||
你将看到类似以下的输出:
|
||||
|
||||
```
|
||||
ID | Name | Schedule | Next Run | Deliver
|
||||
------------|-------------------|-------------|--------------------|--------
|
||||
a1b2c3d4 | Morning Briefing | 0 8 * * * | 2026-03-09 08:00 | telegram
|
||||
e5f6g7h8 | Evening Recap | 0 18 * * * | 2026-03-08 18:00 | telegram
|
||||
```
|
||||
|
||||
### 删除任务
|
||||
|
||||
在聊天中:
|
||||
```
|
||||
/cron remove a1b2c3d4
|
||||
```
|
||||
|
||||
或通过对话方式:
|
||||
```
|
||||
Remove my morning briefing cron job.
|
||||
```
|
||||
|
||||
Hermes 将使用 `cronjob(action="list")` 查找任务,并使用 `cronjob(action="remove")` 将其删除。
|
||||
|
||||
### 检查 Gateway 状态
|
||||
|
||||
确认调度器正在运行:
|
||||
|
||||
```bash
|
||||
hermes cron status
|
||||
```
|
||||
|
||||
如果 gateway 未运行,你的任务将不会执行。将其安装为后台服务以确保可靠性:
|
||||
|
||||
```bash
|
||||
hermes gateway install
|
||||
# or on Linux servers
|
||||
sudo hermes gateway install --system
|
||||
```
|
||||
|
||||
## 进一步探索
|
||||
|
||||
你已经构建了一个可运行的每日简报机器人。以下是一些可以继续探索的方向:
|
||||
|
||||
- **[定时任务(Cron)](/user-guide/features/cron)** — 调度格式、重复限制和推送选项的完整参考
|
||||
- **[委托](/user-guide/features/delegation)** — 深入了解并行子 agent 工作流
|
||||
- **[消息推送平台](/user-guide/messaging)** — 设置 Telegram、Discord 或其他推送目标
|
||||
- **[Memory](/user-guide/features/memory)** — 跨会话的持久上下文
|
||||
- **[技巧与最佳实践](/guides/tips)** — 更多 prompt 工程建议
|
||||
|
||||
:::tip 还能调度什么?
|
||||
简报机器人的模式适用于任何场景:竞争对手监控、GitHub 仓库摘要、天气预报、投资组合追踪、服务器健康检查,甚至每日笑话。只要你能用 prompt 描述它,就能调度它。
|
||||
:::
|
||||
@ -0,0 +1,256 @@
|
||||
---
|
||||
sidebar_position: 13
|
||||
title: "委托与并行工作"
|
||||
description: "何时以及如何使用子代理委托——并行研究、代码审查和多文件工作的模式"
|
||||
---
|
||||
|
||||
# 委托与并行工作
|
||||
|
||||
Hermes 可以生成隔离的子代理来并行处理任务。每个子代理拥有独立的对话、终端会话和工具集。只有最终摘要会返回——中间工具调用不会进入你的上下文窗口。
|
||||
|
||||
完整功能参考,请参阅[子代理委托](/user-guide/features/delegation)。
|
||||
|
||||
---
|
||||
|
||||
## 何时委托
|
||||
|
||||
**适合委托的场景:**
|
||||
- 推理密集型子任务(调试、代码审查、研究综合)
|
||||
- 会用中间数据淹没上下文的任务
|
||||
- 并行独立工作流(同时进行研究 A 和研究 B)
|
||||
- 需要代理以无偏见方式处理的全新上下文任务
|
||||
|
||||
**使用其他方式的场景:**
|
||||
- 单次工具调用 → 直接使用工具
|
||||
- 步骤间有逻辑的机械性多步骤工作 → `execute_code`
|
||||
- 需要用户交互的任务 → 子代理无法使用 `clarify`
|
||||
- 快速文件编辑 → 直接操作
|
||||
- 必须在当前轮次结束后继续运行的持久性长任务 → `cronjob` 或 `terminal(background=True, notify_on_complete=True)`。`delegate_task` 是**同步**的:若父轮次被中断,活跃的子代理将被取消,其工作将被丢弃。
|
||||
|
||||
---
|
||||
|
||||
## 模式:并行研究
|
||||
|
||||
同时研究三个主题并获取结构化摘要:
|
||||
|
||||
```
|
||||
并行研究以下三个主题:
|
||||
1. WebAssembly 在浏览器之外的现状
|
||||
2. 2025 年 RISC-V 服务器芯片的采用情况
|
||||
3. 量子计算的实际应用
|
||||
|
||||
重点关注近期进展和关键参与者。
|
||||
```
|
||||
|
||||
在后台,Hermes 使用:
|
||||
|
||||
```python
|
||||
delegate_task(tasks=[
|
||||
{
|
||||
"goal": "Research WebAssembly outside the browser in 2025",
|
||||
"context": "Focus on: runtimes (Wasmtime, Wasmer), cloud/edge use cases, WASI progress",
|
||||
"toolsets": ["web"]
|
||||
},
|
||||
{
|
||||
"goal": "Research RISC-V server chip adoption",
|
||||
"context": "Focus on: server chips shipping, cloud providers adopting, software ecosystem",
|
||||
"toolsets": ["web"]
|
||||
},
|
||||
{
|
||||
"goal": "Research practical quantum computing applications",
|
||||
"context": "Focus on: error correction breakthroughs, real-world use cases, key companies",
|
||||
"toolsets": ["web"]
|
||||
}
|
||||
])
|
||||
```
|
||||
|
||||
三个任务并发运行。每个子代理独立搜索网络并返回摘要。父代理随后将它们综合成一份连贯的简报。
|
||||
|
||||
---
|
||||
|
||||
## 模式:代码审查
|
||||
|
||||
将安全审查委托给一个全新上下文的子代理,让它以无先入之见的方式审查代码:
|
||||
|
||||
```
|
||||
审查 src/auth/ 中的认证模块,检查安全问题。
|
||||
检查 SQL 注入、JWT 验证问题、密码处理
|
||||
和会话管理。修复发现的问题并运行测试。
|
||||
```
|
||||
|
||||
关键在于 `context` 字段——它必须包含子代理所需的一切信息:
|
||||
|
||||
```python
|
||||
delegate_task(
|
||||
goal="Review src/auth/ for security issues and fix any found",
|
||||
context="""Project at /home/user/webapp. Python 3.11, Flask, PyJWT, bcrypt.
|
||||
Auth files: src/auth/login.py, src/auth/jwt.py, src/auth/middleware.py
|
||||
Test command: pytest tests/auth/ -v
|
||||
Focus on: SQL injection, JWT validation, password hashing, session management.
|
||||
Fix issues found and verify tests pass.""",
|
||||
toolsets=["terminal", "file"]
|
||||
)
|
||||
```
|
||||
|
||||
:::warning 上下文问题
|
||||
子代理对你的对话**一无所知**。它们从完全空白的状态开始。如果你委托"修复我们讨论的那个 bug",子代理根本不知道你指的是哪个 bug。务必明确传递文件路径、错误信息、项目结构和约束条件。
|
||||
:::
|
||||
|
||||
---
|
||||
|
||||
## 模式:比较备选方案
|
||||
|
||||
并行评估同一问题的多种解决方案,然后选出最佳方案:
|
||||
|
||||
```
|
||||
我需要为 Django 应用添加全文搜索。并行评估三种方案:
|
||||
1. PostgreSQL tsvector(内置)
|
||||
2. 通过 django-elasticsearch-dsl 使用 Elasticsearch
|
||||
3. 通过 meilisearch-python 使用 Meilisearch
|
||||
|
||||
对每种方案评估:配置复杂度、查询能力、资源需求
|
||||
和维护开销。比较后推荐一种。
|
||||
```
|
||||
|
||||
每个子代理独立研究一个选项。由于它们相互隔离,不存在交叉干扰——每项评估都基于自身的优缺点。父代理获取全部三份摘要后进行比较。
|
||||
|
||||
---
|
||||
|
||||
## 模式:多文件重构
|
||||
|
||||
将大型重构任务拆分给并行子代理,每个子代理负责代码库的不同部分:
|
||||
|
||||
```python
|
||||
delegate_task(tasks=[
|
||||
{
|
||||
"goal": "Refactor all API endpoint handlers to use the new response format",
|
||||
"context": """Project at /home/user/api-server.
|
||||
Files: src/handlers/users.py, src/handlers/auth.py, src/handlers/billing.py
|
||||
Old format: return {"data": result, "status": "ok"}
|
||||
New format: return APIResponse(data=result, status=200).to_dict()
|
||||
Import: from src.responses import APIResponse
|
||||
Run tests after: pytest tests/handlers/ -v""",
|
||||
"toolsets": ["terminal", "file"]
|
||||
},
|
||||
{
|
||||
"goal": "Update all client SDK methods to handle the new response format",
|
||||
"context": """Project at /home/user/api-server.
|
||||
Files: sdk/python/client.py, sdk/python/models.py
|
||||
Old parsing: result = response.json()["data"]
|
||||
New parsing: result = response.json()["data"] (same key, but add status code checking)
|
||||
Also update sdk/python/tests/test_client.py""",
|
||||
"toolsets": ["terminal", "file"]
|
||||
},
|
||||
{
|
||||
"goal": "Update API documentation to reflect the new response format",
|
||||
"context": """Project at /home/user/api-server.
|
||||
Docs at: docs/api/. Format: Markdown with code examples.
|
||||
Update all response examples from old format to new format.
|
||||
Add a 'Response Format' section to docs/api/overview.md explaining the schema.""",
|
||||
"toolsets": ["terminal", "file"]
|
||||
}
|
||||
])
|
||||
```
|
||||
|
||||
:::tip
|
||||
每个子代理拥有独立的终端会话。只要它们编辑不同的文件,就可以在同一项目目录中工作而互不干扰。如果两个子代理可能修改同一文件,请在并行工作完成后自行处理该文件。
|
||||
:::
|
||||
|
||||
---
|
||||
|
||||
## 模式:先收集后分析
|
||||
|
||||
使用 `execute_code` 进行机械性数据收集,然后委托推理密集型分析:
|
||||
|
||||
```python
|
||||
# 第一步:机械性收集(此处 execute_code 更合适——无需推理)
|
||||
execute_code("""
|
||||
from hermes_tools import web_search, web_extract
|
||||
|
||||
results = []
|
||||
for query in ["AI funding Q1 2026", "AI startup acquisitions 2026", "AI IPOs 2026"]:
|
||||
r = web_search(query, limit=5)
|
||||
for item in r["data"]["web"]:
|
||||
results.append({"title": item["title"], "url": item["url"], "desc": item["description"]})
|
||||
|
||||
# Extract full content from top 5 most relevant
|
||||
urls = [r["url"] for r in results[:5]]
|
||||
content = web_extract(urls)
|
||||
|
||||
# Save for the analysis step
|
||||
import json
|
||||
with open("/tmp/ai-funding-data.json", "w") as f:
|
||||
json.dump({"search_results": results, "extracted": content["results"]}, f)
|
||||
print(f"Collected {len(results)} results, extracted {len(content['results'])} pages")
|
||||
""")
|
||||
|
||||
# 第二步:推理密集型分析(此处委托更合适)
|
||||
delegate_task(
|
||||
goal="Analyze AI funding data and write a market report",
|
||||
context="""Raw data at /tmp/ai-funding-data.json contains search results and
|
||||
extracted web pages about AI funding, acquisitions, and IPOs in Q1 2026.
|
||||
Write a structured market report: key deals, trends, notable players,
|
||||
and outlook. Focus on deals over $100M.""",
|
||||
toolsets=["terminal", "file"]
|
||||
)
|
||||
```
|
||||
|
||||
这通常是最高效的模式:`execute_code` 以低成本处理 10 余次顺序工具调用,然后子代理在干净的上下文中完成单次高成本推理任务。
|
||||
|
||||
---
|
||||
|
||||
## 工具集选择
|
||||
|
||||
根据子代理的需求选择工具集:
|
||||
|
||||
| 任务类型 | 工具集 | 原因 |
|
||||
|-----------|----------|-----|
|
||||
| 网络研究 | `["web"]` | 仅 web_search + web_extract |
|
||||
| 代码工作 | `["terminal", "file"]` | Shell 访问 + 文件操作 |
|
||||
| 全栈 | `["terminal", "file", "web"]` | 除消息功能外的全部工具 |
|
||||
| 只读分析 | `["file"]` | 只能读取文件,无 Shell |
|
||||
|
||||
限制工具集可使子代理保持专注,并防止意外副作用(例如研究子代理执行 Shell 命令)。
|
||||
|
||||
---
|
||||
|
||||
## 约束条件
|
||||
|
||||
- **默认 3 个并行任务**:批次默认并发 3 个子代理(可通过 config.yaml 中的 `delegation.max_concurrent_children` 配置,无硬性上限,最低为 1)
|
||||
- **嵌套委托需显式启用**:叶子子代理(默认)无法调用 `delegate_task`、`clarify`、`memory`、`send_message` 或 `execute_code`。编排器子代理(`role="orchestrator"`)保留 `delegate_task` 以支持进一步委托,但仅在 `delegation.max_spawn_depth` 高于默认值 1 时生效(支持 1-3);其余四项仍被禁用。可通过 `delegation.orchestrator_enabled: false` 全局禁用。
|
||||
|
||||
### 调整并发数与深度
|
||||
|
||||
| 配置项 | 默认值 | 范围 | 效果 |
|
||||
|--------|---------|-------|--------|
|
||||
| `max_concurrent_children` | 3 | >=1 | 每次 `delegate_task` 调用的并行批次大小 |
|
||||
| `max_spawn_depth` | 1 | 1-3 | 可进一步生成子代理的委托层级数 |
|
||||
|
||||
示例:运行 30 个并行 worker 并启用嵌套子代理:
|
||||
|
||||
```yaml
|
||||
delegation:
|
||||
max_concurrent_children: 30
|
||||
max_spawn_depth: 2
|
||||
```
|
||||
|
||||
- **独立终端** — 每个子代理拥有独立的终端会话,具有独立的工作目录和状态
|
||||
- **无对话历史** — 子代理只能看到父代理调用 `delegate_task` 时传入的 `goal` 和 `context`
|
||||
- **默认 50 次迭代** — 对简单任务设置较低的 `max_iterations` 以节省成本
|
||||
- **非持久性** — `delegate_task` 是同步的,在父轮次内运行。若父轮次被中断(新用户消息、`/stop`、`/new`),所有活跃子代理将被取消(`status="interrupted"`),其工作将被丢弃。对于必须在当前轮次结束后继续运行的工作,请使用 `cronjob` 或 `terminal(background=True, notify_on_complete=True)`。
|
||||
|
||||
---
|
||||
|
||||
## 技巧
|
||||
|
||||
**目标要具体。** "修复 bug"过于模糊。"修复 api/handlers.py 第 47 行的 TypeError,该错误由 parse_body() 向 process_request() 返回 None 引起"才能给子代理足够的信息。
|
||||
|
||||
**包含文件路径。** 子代理不了解你的项目结构。务必提供相关文件的绝对路径、项目根目录和测试命令。
|
||||
|
||||
**利用委托实现上下文隔离。** 有时你需要全新的视角。委托迫使你清晰地阐述问题,而子代理会在没有对话中积累的假设前提下处理它。
|
||||
|
||||
**核验结果。** 子代理的摘要只是摘要。如果子代理说"修复了 bug 且测试通过",请自行运行测试或查看 diff 来验证。
|
||||
|
||||
---
|
||||
|
||||
*完整的委托参考——所有参数、ACP 集成和高级配置——请参阅[子代理委托](/user-guide/features/delegation)。*
|
||||
@ -0,0 +1,303 @@
|
||||
---
|
||||
sidebar_position: 10
|
||||
title: "教程:GitHub PR 审查 Agent"
|
||||
description: "构建一个自动化 AI 代码审查器,监控你的仓库、审查 Pull Request 并自动发送反馈——全程无需人工干预"
|
||||
---
|
||||
|
||||
# 教程:构建 GitHub PR 审查 Agent
|
||||
|
||||
**问题所在:** 团队提交 PR 的速度比你审查的速度还快。PR 等待数天无人问津。初级开发者因为没人检查而合并了有 bug 的代码。你每天早上都在追赶 diff,而不是在写新功能。
|
||||
|
||||
**解决方案:** 一个全天候监控你的仓库的 AI agent,对每个新 PR 进行 bug、安全问题和代码质量审查,并向你发送摘要——这样你只需把时间花在真正需要人工判断的 PR 上。
|
||||
|
||||
**你将构建的内容:**
|
||||
|
||||
```
|
||||
┌───────────────────────────────────────────────────────────────────┐
|
||||
│ │
|
||||
│ Cron Timer ──▶ Hermes Agent ──▶ GitHub API ──▶ Review │
|
||||
│ (every 2h) + gh CLI (PR diffs) delivery │
|
||||
│ + skill (Telegram, │
|
||||
│ + memory Discord, │
|
||||
│ local) │
|
||||
│ │
|
||||
└───────────────────────────────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
本指南使用 **cron 任务**按计划轮询 PR——无需服务器或公开端点,在 NAT 和防火墙后面同样可用。
|
||||
|
||||
:::tip 想要实时审查?
|
||||
如果你有可用的公开端点,请查看[使用 Webhook 自动化 GitHub PR 评论](./webhook-github-pr-review.md)——GitHub 会在 PR 被打开或更新时立即向 Hermes 推送事件。
|
||||
:::
|
||||
|
||||
---
|
||||
|
||||
## 前提条件
|
||||
|
||||
- **已安装 Hermes Agent** — 参见[安装指南](/getting-started/installation)
|
||||
- **Gateway 已运行**(用于 cron 任务):
|
||||
```bash
|
||||
hermes gateway install # Install as a service
|
||||
# or
|
||||
hermes gateway # Run in foreground
|
||||
```
|
||||
- **已安装并认证 GitHub CLI(`gh`)**:
|
||||
```bash
|
||||
# Install
|
||||
brew install gh # macOS
|
||||
sudo apt install gh # Ubuntu/Debian
|
||||
|
||||
# Authenticate
|
||||
gh auth login
|
||||
```
|
||||
- **已配置消息通知**(可选)— [Telegram](/user-guide/messaging/telegram) 或 [Discord](/user-guide/messaging/discord)
|
||||
|
||||
:::tip 没有消息通知?没关系
|
||||
使用 `deliver: "local"` 将审查结果保存到 `~/.hermes/cron/output/`。在接入通知之前用于测试非常方便。
|
||||
:::
|
||||
|
||||
---
|
||||
|
||||
## 第一步:验证配置
|
||||
|
||||
确保 Hermes 可以访问 GitHub。启动对话:
|
||||
|
||||
```bash
|
||||
hermes
|
||||
```
|
||||
|
||||
用一个简单命令测试:
|
||||
|
||||
```
|
||||
Run: gh pr list --repo NousResearch/hermes-agent --state open --limit 3
|
||||
```
|
||||
|
||||
你应该能看到一个开放 PR 的列表。如果成功,就可以继续了。
|
||||
|
||||
---
|
||||
|
||||
## 第二步:手动试审一个 PR
|
||||
|
||||
仍在对话中,让 Hermes 审查一个真实的 PR:
|
||||
|
||||
```
|
||||
Review this pull request. Read the diff, check for bugs, security issues,
|
||||
and code quality. Be specific about line numbers and quote problematic code.
|
||||
|
||||
Run: gh pr diff 3888 --repo NousResearch/hermes-agent
|
||||
```
|
||||
|
||||
Hermes 将会:
|
||||
1. 执行 `gh pr diff` 获取代码变更
|
||||
2. 通读整个 diff
|
||||
3. 生成包含具体发现的结构化审查报告
|
||||
|
||||
如果你对审查质量满意,就可以开始自动化了。
|
||||
|
||||
---
|
||||
|
||||
## 第三步:创建审查 Skill
|
||||
|
||||
Skill 为 Hermes 提供一致的审查准则,在会话和 cron 运行之间持久保存。没有 skill,审查质量会参差不齐。
|
||||
|
||||
```bash
|
||||
mkdir -p ~/.hermes/skills/code-review
|
||||
```
|
||||
|
||||
创建 `~/.hermes/skills/code-review/SKILL.md`:
|
||||
|
||||
```markdown
|
||||
---
|
||||
name: code-review
|
||||
description: Review pull requests for bugs, security issues, and code quality
|
||||
---
|
||||
|
||||
# Code Review Guidelines
|
||||
|
||||
When reviewing a pull request:
|
||||
|
||||
## What to Check
|
||||
1. **Bugs** — Logic errors, off-by-one, null/undefined handling
|
||||
2. **Security** — Injection, auth bypass, secrets in code, SSRF
|
||||
3. **Performance** — N+1 queries, unbounded loops, memory leaks
|
||||
4. **Style** — Naming conventions, dead code, missing error handling
|
||||
5. **Tests** — Are changes tested? Do tests cover edge cases?
|
||||
|
||||
## Output Format
|
||||
For each finding:
|
||||
- **File:Line** — exact location
|
||||
- **Severity** — Critical / Warning / Suggestion
|
||||
- **What's wrong** — one sentence
|
||||
- **Fix** — how to fix it
|
||||
|
||||
## Rules
|
||||
- Be specific. Quote the problematic code.
|
||||
- Don't flag style nitpicks unless they affect readability.
|
||||
- If the PR looks good, say so. Don't invent problems.
|
||||
- End with: APPROVE / REQUEST_CHANGES / COMMENT
|
||||
```
|
||||
|
||||
验证是否已加载——启动 `hermes`,你应该能在启动时的 skill 列表中看到 `code-review`。
|
||||
|
||||
---
|
||||
|
||||
## 第四步:教会它你的团队规范
|
||||
|
||||
这才是让审查器真正有用的关键。启动一个会话,向 Hermes 传授你的团队标准:
|
||||
|
||||
```
|
||||
Remember: In our backend repo, we use Python with FastAPI.
|
||||
All endpoints must have type annotations and Pydantic models.
|
||||
We don't allow raw SQL — only SQLAlchemy ORM.
|
||||
Test files go in tests/ and must use pytest fixtures.
|
||||
```
|
||||
|
||||
```
|
||||
Remember: In our frontend repo, we use TypeScript with React.
|
||||
No `any` types allowed. All components must have props interfaces.
|
||||
We use React Query for data fetching, never useEffect for API calls.
|
||||
```
|
||||
|
||||
这些记忆会永久保存——审查器无需每次提醒就会自动执行你的规范。
|
||||
|
||||
---
|
||||
|
||||
## 第五步:创建自动化 Cron 任务
|
||||
|
||||
现在把所有内容串联起来。创建一个每 2 小时运行一次的 cron 任务:
|
||||
|
||||
```bash
|
||||
hermes cron create "0 */2 * * *" \
|
||||
"Check for new open PRs and review them.
|
||||
|
||||
Repos to monitor:
|
||||
- myorg/backend-api
|
||||
- myorg/frontend-app
|
||||
|
||||
Steps:
|
||||
1. Run: gh pr list --repo REPO --state open --limit 5 --json number,title,author,createdAt
|
||||
2. For each PR created or updated in the last 4 hours:
|
||||
- Run: gh pr diff NUMBER --repo REPO
|
||||
- Review the diff using the code-review guidelines
|
||||
3. Format output as:
|
||||
|
||||
## PR Reviews — today
|
||||
|
||||
### [repo] #[number]: [title]
|
||||
**Author:** [name] | **Verdict:** APPROVE/REQUEST_CHANGES/COMMENT
|
||||
[findings]
|
||||
|
||||
If no new PRs found, say: No new PRs to review." \
|
||||
--name "pr-review" \
|
||||
--deliver telegram \
|
||||
--skill code-review
|
||||
```
|
||||
|
||||
验证任务已调度:
|
||||
|
||||
```bash
|
||||
hermes cron list
|
||||
```
|
||||
|
||||
### 其他常用调度计划
|
||||
|
||||
| 计划 | 触发时机 |
|
||||
|------|----------|
|
||||
| `0 */2 * * *` | 每 2 小时 |
|
||||
| `0 9,13,17 * * 1-5` | 工作日每天三次 |
|
||||
| `0 9 * * 1` | 每周一早上汇总 |
|
||||
| `30m` | 每 30 分钟(高流量仓库) |
|
||||
|
||||
---
|
||||
|
||||
## 第六步:按需手动触发
|
||||
|
||||
不想等待调度?手动触发:
|
||||
|
||||
```bash
|
||||
hermes cron run pr-review
|
||||
```
|
||||
|
||||
或在对话会话中:
|
||||
|
||||
```
|
||||
/cron run pr-review
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 进阶用法
|
||||
|
||||
### 直接在 GitHub 上发布审查评论
|
||||
|
||||
不将结果发送到 Telegram,而是让 agent 直接在 PR 上评论:
|
||||
|
||||
在你的 cron prompt(提示词)中添加:
|
||||
|
||||
```
|
||||
After reviewing, post your review:
|
||||
- For issues: gh pr review NUMBER --repo REPO --comment --body "YOUR_REVIEW"
|
||||
- For critical issues: gh pr review NUMBER --repo REPO --request-changes --body "YOUR_REVIEW"
|
||||
- For clean PRs: gh pr review NUMBER --repo REPO --approve --body "Looks good"
|
||||
```
|
||||
|
||||
:::caution
|
||||
确保 `gh` 使用的 token 具有 `repo` 权限范围。审查评论将以 `gh` 当前认证的用户身份发布。
|
||||
:::
|
||||
|
||||
### 每周 PR 看板
|
||||
|
||||
创建一个每周一早上的仓库概览:
|
||||
|
||||
```bash
|
||||
hermes cron create "0 9 * * 1" \
|
||||
"Generate a weekly PR dashboard:
|
||||
- myorg/backend-api
|
||||
- myorg/frontend-app
|
||||
- myorg/infra
|
||||
|
||||
For each repo show:
|
||||
1. Open PR count and oldest PR age
|
||||
2. PRs merged this week
|
||||
3. Stale PRs (older than 5 days)
|
||||
4. PRs with no reviewer assigned
|
||||
|
||||
Format as a clean summary." \
|
||||
--name "weekly-dashboard" \
|
||||
--deliver telegram
|
||||
```
|
||||
|
||||
### 多仓库监控
|
||||
|
||||
在 prompt 中添加更多仓库即可扩展规模。Agent 会按顺序处理它们——无需额外配置。
|
||||
|
||||
---
|
||||
|
||||
## 故障排查
|
||||
|
||||
### "gh: command not found"
|
||||
Gateway 在精简环境中运行。请确保 `gh` 在系统 PATH 中,然后重启 gateway。
|
||||
|
||||
### 审查结果过于泛泛
|
||||
1. 添加 `code-review` skill(第三步)
|
||||
2. 通过 memory(记忆)向 Hermes 传授你的团队规范(第四步)
|
||||
3. 它对你的技术栈了解越多,审查质量越好
|
||||
|
||||
### Cron 任务未运行
|
||||
```bash
|
||||
hermes gateway status # Is the gateway running?
|
||||
hermes cron list # Is the job enabled?
|
||||
```
|
||||
|
||||
### 速率限制
|
||||
GitHub 对已认证用户每小时允许 5,000 次 API 请求。每次 PR 审查约消耗 3-5 次请求(列表 + diff + 可选评论)。即使每天审查 100 个 PR,也远低于限制。
|
||||
|
||||
---
|
||||
|
||||
## 下一步
|
||||
|
||||
- **[基于 Webhook 的 PR 审查](./webhook-github-pr-review.md)** — 在 PR 被打开时立即获得审查(需要公开端点)
|
||||
- **[每日简报 Bot](/guides/daily-briefing-bot)** — 将 PR 审查与你的晨间资讯摘要结合
|
||||
- **[构建 Plugin](/guides/build-a-hermes-plugin)** — 将审查逻辑封装为可共享的 plugin
|
||||
- **[Profiles](/user-guide/profiles)** — 运行一个专属审查器 profile,拥有独立的 memory 和配置
|
||||
- **[Fallback Providers](/user-guide/features/fallback-providers)** — 确保在某个 provider 不可用时审查任务仍能正常运行
|
||||
@ -0,0 +1,280 @@
|
||||
---
|
||||
sidebar_position: 16
|
||||
title: "Google Gemini"
|
||||
description: "将 Hermes Agent 与 Google Gemini 配合使用——原生 AI Studio API、API 密钥配置、OAuth 选项、工具调用、流式传输及配额说明"
|
||||
---
|
||||
|
||||
# Google Gemini
|
||||
|
||||
Hermes Agent 通过 **Google AI Studio / Gemini API** 原生支持 Google Gemini——而非 OpenAI 兼容端点。这使 Hermes 能够将其内部 OpenAI 格式的消息和工具循环转换为 Gemini 原生的 `generateContent` API,同时保留工具调用、流式传输、多模态输入以及 Gemini 特有的响应元数据。
|
||||
|
||||
Hermes 还支持独立的 **Google Gemini(OAuth)** provider,使用与 Google Gemini CLI 相同的 Cloud Code Assist 后端。如需最低风险的官方 API 路径,请使用 API 密钥 provider(`gemini`)。
|
||||
|
||||
## 前提条件
|
||||
|
||||
- **Google AI Studio API 密钥** — 在 [aistudio.google.com/apikey](https://aistudio.google.com/apikey) 创建
|
||||
- **已启用计费的 Google Cloud 项目** — 推荐用于 Agent 场景。Gemini 免费层级对长时间运行的 Agent 会话而言配额过小,因为 Hermes 每次用户交互可能发起多次模型调用。
|
||||
- **已安装 Hermes** — 原生 Gemini provider 无需额外安装 Python 包。
|
||||
|
||||
:::tip API 密钥路径
|
||||
设置 `GOOGLE_API_KEY` 或 `GEMINI_API_KEY`。Hermes 对 `gemini` provider 会同时检查这两个名称。
|
||||
:::
|
||||
|
||||
## 快速开始
|
||||
|
||||
```bash
|
||||
# 添加 Gemini API 密钥
|
||||
echo "GOOGLE_API_KEY=..." >> ~/.hermes/.env
|
||||
|
||||
# 选择 Gemini 作为 provider
|
||||
hermes model
|
||||
# → 选择 "More providers..." → "Google AI Studio"
|
||||
# → Hermes 检查密钥层级并显示 Gemini 模型列表
|
||||
# → 选择一个模型
|
||||
|
||||
# 开始对话
|
||||
hermes chat
|
||||
```
|
||||
|
||||
如果你偏好直接编辑配置文件,请使用原生 Gemini API 基础 URL:
|
||||
|
||||
```yaml
|
||||
model:
|
||||
default: gemini-3-flash-preview
|
||||
provider: gemini
|
||||
base_url: https://generativelanguage.googleapis.com/v1beta
|
||||
```
|
||||
|
||||
## 配置
|
||||
|
||||
运行 `hermes model` 后,`~/.hermes/config.yaml` 将包含:
|
||||
|
||||
```yaml
|
||||
model:
|
||||
default: gemini-3-flash-preview
|
||||
provider: gemini
|
||||
base_url: https://generativelanguage.googleapis.com/v1beta
|
||||
```
|
||||
|
||||
`~/.hermes/.env` 中:
|
||||
|
||||
```bash
|
||||
GOOGLE_API_KEY=...
|
||||
```
|
||||
|
||||
### 原生 Gemini API
|
||||
|
||||
推荐使用的端点为:
|
||||
|
||||
```text
|
||||
https://generativelanguage.googleapis.com/v1beta
|
||||
```
|
||||
|
||||
Hermes 检测到该端点后会创建原生 Gemini 适配器。在内部,Hermes 仍以 OpenAI 格式维护 Agent 循环,然后将每个请求转换为 Gemini 原生 schema:
|
||||
|
||||
- `messages[]` → Gemini `contents[]`
|
||||
- 系统提示(system prompt)→ Gemini `systemInstruction`
|
||||
- 工具 schema → Gemini `functionDeclarations`
|
||||
- 工具结果 → Gemini `functionResponse` 部分
|
||||
- 流式响应 → 供 Hermes 循环使用的 OpenAI 格式流式数据块
|
||||
|
||||
:::note Gemini 3 思维签名
|
||||
对于 Gemini 3 的工具调用,Hermes 会保留附加在函数调用部分的 `thoughtSignature` 值,并在下一个工具轮次中重放。这覆盖了多步骤 Agent 工作流中验证关键路径的需求。
|
||||
|
||||
Gemini 3 也可能在其他响应部分附加思维签名。Hermes 的原生适配器目前针对 Agent 工具循环进行了优化,尚未以完整的部分级保真度重放所有非工具调用签名。
|
||||
:::
|
||||
|
||||
### 优先使用原生端点
|
||||
|
||||
Google 还提供了 OpenAI 兼容端点:
|
||||
|
||||
```text
|
||||
https://generativelanguage.googleapis.com/v1beta/openai/
|
||||
```
|
||||
|
||||
对于 Hermes Agent 会话,请优先使用上述原生 Gemini 端点。Hermes 内置原生 Gemini 适配器,可将多轮工具调用、工具调用结果、流式传输、多模态输入以及 Gemini 响应元数据直接映射到 Gemini 的 `generateContent` API。OpenAI 兼容端点在你明确需要 OpenAI API 兼容性时仍然有用。
|
||||
|
||||
如果你之前将 `GEMINI_BASE_URL` 设置为 `/openai` URL,请将其删除或修改:
|
||||
|
||||
```bash
|
||||
GEMINI_BASE_URL=https://generativelanguage.googleapis.com/v1beta
|
||||
```
|
||||
|
||||
### OAuth Provider
|
||||
|
||||
Hermes 还提供 `google-gemini-cli` provider:
|
||||
|
||||
```bash
|
||||
hermes model
|
||||
# → 选择 "Google Gemini (OAuth)"
|
||||
```
|
||||
|
||||
该方式使用浏览器 PKCE 登录和 Cloud Code Assist 后端。对于希望使用 Gemini CLI 风格 OAuth 的用户可能有用,但 Hermes 会显示明确警告,因为 Google 可能将第三方软件使用 Gemini CLI OAuth 客户端的行为视为违反政策。对于生产环境或最低风险使用场景,请优先使用上述 API 密钥 provider。
|
||||
|
||||
## 可用模型
|
||||
|
||||
`hermes model` 选择器显示 Hermes provider 注册表中维护的 Gemini 模型。常见选项包括:
|
||||
|
||||
| 模型 | ID | 说明 |
|
||||
|------|----|------|
|
||||
| Gemini 3.1 Pro Preview | `gemini-3.1-pro-preview` | 可用时最强大的预览模型 |
|
||||
| Gemini 3 Pro Preview | `gemini-3-pro-preview` | 强大的推理和编码模型 |
|
||||
| Gemini 3 Flash Preview | `gemini-3-flash-preview` | 推荐的默认选项,速度与能力均衡 |
|
||||
| Gemini 3.1 Flash Lite Preview | `gemini-3.1-flash-lite-preview` | 可用时速度最快、成本最低的选项 |
|
||||
|
||||
模型可用性会随时间变化。如果某个模型消失或未对你的密钥启用,请重新运行 `hermes model` 并从当前列表中选择。
|
||||
|
||||
:::info 模型 ID
|
||||
当 `provider: gemini` 时,请使用 Gemini 原生模型 ID,如 `gemini-3-flash-preview`,而非 OpenRouter 风格的 ID(如 `google/gemini-3-flash-preview`)。
|
||||
:::
|
||||
|
||||
### 最新别名
|
||||
|
||||
Google 为 Pro 和 Flash Gemini 系列发布了滚动别名。当你希望 Google 自动升级模型而无需修改 Hermes 配置时,`gemini-pro-latest` 和 `gemini-flash-latest` 非常实用。
|
||||
|
||||
| 别名 | 当前指向 | 说明 |
|
||||
|------|----------|------|
|
||||
| `gemini-pro-latest` | 最新 Gemini Pro 模型 | 需要 Google 当前 Pro 默认值时的最佳选择 |
|
||||
| `gemini-flash-latest` | 最新 Gemini Flash 模型 | 需要 Google 当前 Flash 默认值时的最佳选择 |
|
||||
|
||||
```yaml
|
||||
model:
|
||||
default: gemini-pro-latest
|
||||
provider: gemini
|
||||
base_url: https://generativelanguage.googleapis.com/v1beta
|
||||
```
|
||||
|
||||
如果需要严格的可复现性,请优先使用明确的模型 ID,如 `gemini-3.1-pro-preview` 或 `gemini-3-flash-preview`。
|
||||
|
||||
### 通过 Gemini API 使用 Gemma
|
||||
|
||||
Google 也通过 Gemini API 提供 Gemma 模型。Hermes 将这些模型识别为 Google 模型,但会在默认模型选择器中隐藏吞吐量极低的 Gemma 条目,以防新用户在长时间运行的 Agent 会话中意外选择评估层级的模型。
|
||||
|
||||
常用评估 ID 包括:
|
||||
|
||||
| 模型 | ID | 说明 |
|
||||
|------|----|------|
|
||||
| Gemma 4 31B IT | `gemma-4-31b-it` | 较大的 Gemma 模型;适用于兼容性和质量评估 |
|
||||
| Gemma 4 26B A4B IT | `gemma-4-26b-a4b-it` | 可用时的较小活跃参数变体 |
|
||||
|
||||
这些模型最适合作为 Gemini API 密钥的评估选项。Google 的 Gemma API 定价仅限免费层级,与生产级 Gemini 模型相比使用上限较低,因此持续的 Hermes Agent 使用通常应切换到付费 Gemini 模型、自托管部署或具有适当配额的其他 provider。
|
||||
|
||||
如需使用选择器中隐藏的 Gemma 模型,请直接在配置中指定:
|
||||
|
||||
```yaml
|
||||
model:
|
||||
default: gemma-4-31b-it
|
||||
provider: gemini
|
||||
base_url: https://generativelanguage.googleapis.com/v1beta
|
||||
```
|
||||
|
||||
## 会话中途切换模型
|
||||
|
||||
在对话中使用 `/model` 命令:
|
||||
|
||||
```text
|
||||
/model gemini-3-flash-preview
|
||||
/model gemini-flash-latest
|
||||
/model gemini-3-pro-preview
|
||||
/model gemini-pro-latest
|
||||
/model gemma-4-31b-it
|
||||
/model gemini-3.1-flash-lite-preview
|
||||
```
|
||||
|
||||
如果尚未配置 Gemini,请退出会话并先运行 `hermes model`。`/model` 用于在已配置的 provider 和模型之间切换,不会收集新的 API 密钥。
|
||||
|
||||
## 诊断
|
||||
|
||||
```bash
|
||||
hermes doctor
|
||||
```
|
||||
|
||||
doctor 命令检查:
|
||||
|
||||
- `GOOGLE_API_KEY` 或 `GEMINI_API_KEY` 是否可用
|
||||
- `google-gemini-cli` 的 Gemini OAuth 凭据是否存在
|
||||
- 已配置的 provider 凭据是否可以解析
|
||||
|
||||
如需查看 OAuth 配额使用情况,请在 Hermes 会话中运行:
|
||||
|
||||
```text
|
||||
/gquota
|
||||
```
|
||||
|
||||
`/gquota` 适用于 `google-gemini-cli` OAuth provider,不适用于 AI Studio API 密钥 provider。
|
||||
|
||||
## Gateway(消息平台)
|
||||
|
||||
Gemini 可与所有 Hermes gateway 平台配合使用(Telegram、Discord、Slack、WhatsApp、LINE、飞书等)。将 Gemini 配置为你的 provider,然后正常启动 gateway:
|
||||
|
||||
```bash
|
||||
hermes gateway setup
|
||||
hermes gateway start
|
||||
```
|
||||
|
||||
gateway 读取 `config.yaml` 并使用相同的 Gemini provider 配置。
|
||||
|
||||
## 故障排查
|
||||
|
||||
### "Gemini native client requires an API key"
|
||||
|
||||
Hermes 找不到可用的 API 密钥。请将以下任一项添加到 `~/.hermes/.env`:
|
||||
|
||||
```bash
|
||||
GOOGLE_API_KEY=...
|
||||
# 或
|
||||
GEMINI_API_KEY=...
|
||||
```
|
||||
|
||||
然后重新运行 `hermes model`。
|
||||
|
||||
### "This Google API key is on the free tier"
|
||||
|
||||
Hermes 在设置期间会探测 Gemini API 密钥。由于工具调用、重试、压缩和辅助任务可能需要多次模型调用,免费层级配额在少数几轮 Agent 交互后即可耗尽。
|
||||
|
||||
请为与密钥关联的 Google Cloud 项目启用计费,必要时重新生成密钥,然后运行:
|
||||
|
||||
```bash
|
||||
hermes model
|
||||
```
|
||||
|
||||
### "404 model not found"
|
||||
|
||||
所选模型对你的账号、地区或密钥不可用。重新运行 `hermes model` 并从当前列表中选择其他 Gemini 模型。
|
||||
|
||||
### Gemma 模型未显示在 `hermes model` 中
|
||||
|
||||
Hermes 默认可能会在选择器中隐藏低吞吐量的 Gemma 模型。如果你有意评估某个模型,请直接在 `~/.hermes/config.yaml` 中设置模型 ID。
|
||||
|
||||
### Gemma 出现 "429 quota exceeded"
|
||||
|
||||
通过 Gemini API 提供的 Gemma 模型适合评估使用,但其 Gemini API 免费层级上限较低。请将其用于兼容性测试,然后切换到付费 Gemini 模型或其他 provider 以进行持续的 Agent 会话。
|
||||
|
||||
### 已配置 OpenAI 兼容端点
|
||||
|
||||
检查 `~/.hermes/.env` 中是否存在:
|
||||
|
||||
```bash
|
||||
GEMINI_BASE_URL=https://generativelanguage.googleapis.com/v1beta/openai/
|
||||
```
|
||||
|
||||
将其修改为原生端点或删除该覆盖项:
|
||||
|
||||
```bash
|
||||
GEMINI_BASE_URL=https://generativelanguage.googleapis.com/v1beta
|
||||
```
|
||||
|
||||
### OAuth 登录警告
|
||||
|
||||
`google-gemini-cli` provider 使用 Gemini CLI / Cloud Code Assist OAuth 流程。Hermes 在启动前会发出警告,因为这与官方 AI Studio API 密钥路径不同。如需官方 API 密钥集成,请使用 `provider: gemini` 配合 `GOOGLE_API_KEY`。
|
||||
|
||||
### 工具调用因 schema 错误而失败
|
||||
|
||||
升级 Hermes 并重新运行 `hermes model`。原生 Gemini 适配器会针对 Gemini 更严格的函数声明格式对工具 schema 进行清理;旧版本或自定义端点可能不支持此功能。
|
||||
|
||||
## 相关链接
|
||||
|
||||
- [AI Providers](/integrations/providers)
|
||||
- [Configuration](/user-guide/configuration)
|
||||
- [Fallback Providers](/user-guide/features/fallback-providers)
|
||||
- [AWS Bedrock](/guides/aws-bedrock) — 使用 AWS 凭据的原生云 provider 集成
|
||||
@ -0,0 +1,240 @@
|
||||
---
|
||||
sidebar_position: 2
|
||||
title: "在 Mac 上运行本地 LLM"
|
||||
description: "使用 llama.cpp 或 MLX 在 macOS 上搭建兼容 OpenAI 的本地 LLM 服务器,涵盖模型选择、内存优化以及 Apple Silicon 上的实测基准数据"
|
||||
---
|
||||
|
||||
# 在 Mac 上运行本地 LLM
|
||||
|
||||
本指南介绍如何在 macOS 上运行一个兼容 OpenAI API 的本地 LLM 服务器。你将获得完整的隐私保护、零 API 费用,以及 Apple Silicon 上出乎意料的出色性能。
|
||||
|
||||
我们涵盖两个后端:
|
||||
|
||||
| 后端 | 安装方式 | 优势 | 格式 |
|
||||
|---------|---------|---------|--------|
|
||||
| **llama.cpp** | `brew install llama.cpp` | 首 token 延迟最低,量化 KV 缓存节省内存 | GGUF |
|
||||
| **omlx** | [omlx.ai](https://omlx.ai) | token 生成速度最快,原生 Metal 优化 | MLX (safetensors) |
|
||||
|
||||
两者均暴露兼容 OpenAI 的 `/v1/chat/completions` 端点。Hermes 支持任意一个——只需将其指向 `http://localhost:8080` 或 `http://localhost:8000`。
|
||||
|
||||
:::info 仅限 Apple Silicon
|
||||
本指南面向搭载 Apple Silicon(M1 及更新)的 Mac。Intel Mac 可使用 llama.cpp,但无 GPU 加速——性能会明显更慢。
|
||||
:::
|
||||
|
||||
---
|
||||
|
||||
## 选择模型
|
||||
|
||||
入门推荐 **Qwen3.5-9B**——这是一个强推理模型,量化后可在 8GB+ 统一内存上轻松运行。
|
||||
|
||||
| 变体 | 磁盘占用 | 所需内存(128K 上下文) | 后端 |
|
||||
|---------|-------------|---------------------------|---------|
|
||||
| Qwen3.5-9B-Q4_K_M (GGUF) | 5.3 GB | ~10 GB(含量化 KV 缓存) | llama.cpp |
|
||||
| Qwen3.5-9B-mlx-lm-mxfp4 (MLX) | ~5 GB | ~12 GB | omlx |
|
||||
|
||||
**内存估算规则:** 模型大小 + KV 缓存。9B Q4 模型约 5 GB。128K 上下文下 Q4 量化的 KV 缓存额外占用约 4–5 GB。若使用默认(f16)KV 缓存,则会膨胀至约 16 GB。llama.cpp 中的量化 KV 缓存参数是内存受限系统的关键技巧。
|
||||
|
||||
对于更大的模型(27B、35B),你需要 32 GB+ 的统一内存。9B 是 8–16 GB 机器的最佳选择。
|
||||
|
||||
---
|
||||
|
||||
## 方案 A:llama.cpp
|
||||
|
||||
llama.cpp 是移植性最强的本地 LLM 运行时。在 macOS 上,它开箱即用地通过 Metal 进行 GPU 加速。
|
||||
|
||||
### 安装
|
||||
|
||||
```bash
|
||||
brew install llama.cpp
|
||||
```
|
||||
|
||||
安装后即可全局使用 `llama-server` 命令。
|
||||
|
||||
### 下载模型
|
||||
|
||||
你需要 GGUF 格式的模型。最简便的来源是通过 `huggingface-cli` 从 Hugging Face 下载:
|
||||
|
||||
```bash
|
||||
brew install huggingface-cli
|
||||
```
|
||||
|
||||
然后下载:
|
||||
|
||||
```bash
|
||||
huggingface-cli download unsloth/Qwen3.5-9B-GGUF Qwen3.5-9B-Q4_K_M.gguf --local-dir ~/models
|
||||
```
|
||||
|
||||
:::tip 受限模型
|
||||
Hugging Face 上的部分模型需要身份验证。如果遇到 401 或 404 错误,请先运行 `huggingface-cli login`。
|
||||
:::
|
||||
|
||||
### 启动服务器
|
||||
|
||||
```bash
|
||||
llama-server -m ~/models/Qwen3.5-9B-Q4_K_M.gguf \
|
||||
-ngl 99 \
|
||||
-c 131072 \
|
||||
-np 1 \
|
||||
-fa on \
|
||||
--cache-type-k q4_0 \
|
||||
--cache-type-v q4_0 \
|
||||
--host 0.0.0.0
|
||||
```
|
||||
|
||||
各参数说明:
|
||||
|
||||
| 参数 | 用途 |
|
||||
|------|---------|
|
||||
| `-ngl 99` | 将所有层卸载到 GPU(Metal)。设置较大的数值以确保没有层留在 CPU 上。 |
|
||||
| `-c 131072` | 上下文窗口大小(128K token)。内存不足时可减小此值。 |
|
||||
| `-np 1` | 并行槽数量。单用户使用时保持为 1——更多槽会分摊内存预算。 |
|
||||
| `-fa on` | Flash attention。减少内存占用并加速长上下文推理。 |
|
||||
| `--cache-type-k q4_0` | 将 key 缓存量化为 4-bit。**这是最大的内存节省手段。** |
|
||||
| `--cache-type-v q4_0` | 将 value 缓存量化为 4-bit。与上一项合用,相比 f16 可将 KV 缓存内存减少约 75%。 |
|
||||
| `--host 0.0.0.0` | 监听所有网络接口。若不需要网络访问,可改为 `127.0.0.1`。 |
|
||||
|
||||
当你看到以下输出时,服务器已就绪:
|
||||
|
||||
```
|
||||
main: server is listening on http://0.0.0.0:8080
|
||||
srv update_slots: all slots are idle
|
||||
```
|
||||
|
||||
### 内存受限系统的优化
|
||||
|
||||
`--cache-type-k q4_0 --cache-type-v q4_0` 参数是内存有限系统最重要的优化手段。以下是 128K 上下文下的影响对比:
|
||||
|
||||
| KV 缓存类型 | KV 缓存内存(128K 上下文,9B 模型) |
|
||||
|---------------|--------------------------------------|
|
||||
| f16(默认) | ~16 GB |
|
||||
| q8_0 | ~8 GB |
|
||||
| **q4_0** | **~4 GB** |
|
||||
|
||||
在 8 GB Mac 上,使用 `q4_0` KV 缓存并将上下文缩减为 `-c 32768`(32K)。在 16 GB 上,可以轻松使用 128K 上下文。在 32 GB+ 上,可以运行更大的模型或多个并行槽。
|
||||
|
||||
如果仍然内存不足,优先减小上下文大小(`-c`),然后尝试更小的量化级别(Q3_K_M 代替 Q4_K_M)。
|
||||
|
||||
### 测试
|
||||
|
||||
```bash
|
||||
curl -s http://localhost:8080/v1/chat/completions \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{
|
||||
"model": "Qwen3.5-9B-Q4_K_M.gguf",
|
||||
"messages": [{"role": "user", "content": "Hello!"}],
|
||||
"max_tokens": 50
|
||||
}' | jq .choices[0].message.content
|
||||
```
|
||||
|
||||
### 获取模型名称
|
||||
|
||||
如果忘记了模型名称,可查询 models 端点:
|
||||
|
||||
```bash
|
||||
curl -s http://localhost:8080/v1/models | jq '.data[].id'
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 方案 B:通过 omlx 使用 MLX
|
||||
|
||||
[omlx](https://omlx.ai) 是一款 macOS 原生应用,用于管理和提供 MLX 模型服务。MLX 是 Apple 自研的机器学习框架,专为 Apple Silicon 统一内存架构优化。
|
||||
|
||||
### 安装
|
||||
|
||||
从 [omlx.ai](https://omlx.ai) 下载并安装。它提供图形界面用于模型管理,并内置服务器。
|
||||
|
||||
### 下载模型
|
||||
|
||||
使用 omlx 应用浏览并下载模型。搜索 `Qwen3.5-9B-mlx-lm-mxfp4` 并下载。模型存储在本地(通常位于 `~/.omlx/models/`)。
|
||||
|
||||
### 启动服务器
|
||||
|
||||
omlx 默认在 `http://127.0.0.1:8000` 上提供服务。通过应用 UI 启动服务,或在可用时使用 CLI。
|
||||
|
||||
### 测试
|
||||
|
||||
```bash
|
||||
curl -s http://127.0.0.1:8000/v1/chat/completions \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{
|
||||
"model": "Qwen3.5-9B-mlx-lm-mxfp4",
|
||||
"messages": [{"role": "user", "content": "Hello!"}],
|
||||
"max_tokens": 50
|
||||
}' | jq .choices[0].message.content
|
||||
```
|
||||
|
||||
### 列出可用模型
|
||||
|
||||
omlx 可同时提供多个模型的服务:
|
||||
|
||||
```bash
|
||||
curl -s http://127.0.0.1:8000/v1/models | jq '.data[].id'
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 基准测试:llama.cpp vs MLX
|
||||
|
||||
两个后端在同一台机器(Apple M5 Max,128 GB 统一内存)上测试,使用相同模型(Qwen3.5-9B),量化级别相当(GGUF 使用 Q4_K_M,MLX 使用 mxfp4)。五个不同 prompt,每个运行三次,后端顺序测试以避免资源竞争。
|
||||
|
||||
### 结果
|
||||
|
||||
| 指标 | llama.cpp (Q4_K_M) | MLX (mxfp4) | 胜者 |
|
||||
|--------|-------------------|-------------|--------|
|
||||
| **TTFT(首 token 延迟,均值)** | **67 ms** | 289 ms | llama.cpp(快 4.3 倍) |
|
||||
| **TTFT(p50)** | **66 ms** | 286 ms | llama.cpp(快 4.3 倍) |
|
||||
| **生成速度(均值)** | 70 tok/s | **96 tok/s** | MLX(快 37%) |
|
||||
| **生成速度(p50)** | 70 tok/s | **96 tok/s** | MLX(快 37%) |
|
||||
| **总耗时(512 token)** | 7.3s | **5.5s** | MLX(快 25%) |
|
||||
|
||||
### 含义解读
|
||||
|
||||
- **llama.cpp** 在 prompt 处理上表现突出——其 flash attention + 量化 KV 缓存流水线可在约 66ms 内返回第一个 token。如果你在构建对响应速度敏感的交互式应用(聊天机器人、自动补全),这是显著优势。
|
||||
|
||||
- **MLX** 一旦开始生成,token 速度快约 37%。对于批量任务、长文本生成,或任何更关注总完成时间而非初始延迟的场景,MLX 完成得更快。
|
||||
|
||||
- 两个后端都**极为稳定**——多次运行间的方差可忽略不计。这些数据可作为可靠参考。
|
||||
|
||||
### 如何选择?
|
||||
|
||||
| 使用场景 | 推荐 |
|
||||
|----------|---------------|
|
||||
| 交互式聊天、低延迟工具 | llama.cpp |
|
||||
| 长文本生成、批量处理 | MLX (omlx) |
|
||||
| 内存受限(8–16 GB) | llama.cpp(量化 KV 缓存无可匹敌) |
|
||||
| 同时提供多个模型服务 | omlx(内置多模型支持) |
|
||||
| 最大兼容性(含 Linux) | llama.cpp |
|
||||
|
||||
---
|
||||
|
||||
## 连接 Hermes
|
||||
|
||||
本地服务器启动后:
|
||||
|
||||
```bash
|
||||
hermes model
|
||||
```
|
||||
|
||||
选择 **Custom endpoint**,按提示操作。系统会询问 base URL 和模型名称——使用你所配置的后端对应的值即可。
|
||||
|
||||
---
|
||||
|
||||
## 超时设置
|
||||
|
||||
Hermes 会自动检测本地端点(localhost、局域网 IP)并放宽其流式传输超时限制。大多数情况下无需额外配置。
|
||||
|
||||
如果仍然遇到超时错误(例如在慢速硬件上使用超大上下文),可以覆盖流式读取超时:
|
||||
|
||||
```bash
|
||||
# 在 .env 中——将默认的 120s 提高到 30 分钟
|
||||
HERMES_STREAM_READ_TIMEOUT=1800
|
||||
```
|
||||
|
||||
| 超时类型 | 默认值 | 本地自动调整 | 环境变量覆盖 |
|
||||
|---------|---------|----------------------|------------------|
|
||||
| 流式读取(socket 级别) | 120s | 提升至 1800s | `HERMES_STREAM_READ_TIMEOUT` |
|
||||
| 停滞流检测 | 180s | 完全禁用 | `HERMES_STREAM_STALE_TIMEOUT` |
|
||||
| API 调用(非流式) | 1800s | 无需调整 | `HERMES_API_TIMEOUT` |
|
||||
|
||||
流式读取超时最容易引发问题——它是接收下一个数据块的 socket 级别截止时间。在大上下文的预填充(prefill)阶段,本地模型可能在处理 prompt 时数分钟内没有任何输出。自动检测机制会透明地处理这一情况。
|
||||
@ -0,0 +1,317 @@
|
||||
---
|
||||
sidebar_position: 9
|
||||
title: "使用 Ollama 在本地运行 Hermes — 零 API 费用"
|
||||
description: "使用 Ollama 和 Gemma 4 等开放权重模型在本机完整运行 Hermes Agent 的分步指南,无需云端 API 密钥或付费订阅"
|
||||
---
|
||||
|
||||
# 使用 Ollama 在本地运行 Hermes — 零 API 费用
|
||||
|
||||
## 问题所在
|
||||
|
||||
云端 LLM API 按 token(令牌)计费。一次高强度的编程会话可能花费 5–20 美元。对于个人项目、学习或隐私敏感的工作,费用会不断累积——而且你的每一段对话都会发送给第三方。
|
||||
|
||||
## 本指南解决什么
|
||||
|
||||
你将在自己的硬件上完整运行 Hermes Agent,使用 [Ollama](https://ollama.com) 作为模型后端。无需 API 密钥,无需订阅,数据不会离开你的机器。配置完成后,Hermes 的使用体验与 OpenRouter 或 Anthropic 完全一致——终端命令、文件编辑、网页浏览、任务委派——只是模型在本地运行。
|
||||
|
||||
完成后,你将拥有:
|
||||
|
||||
- Ollama 提供一个或多个开放权重模型的服务
|
||||
- Hermes 通过自定义端点连接到 Ollama
|
||||
- 一个可以编辑文件、执行命令、浏览网页的本地 agent
|
||||
- 可选:由你自己的硬件驱动的 Telegram/Discord 机器人
|
||||
|
||||
## 所需条件
|
||||
|
||||
| 组件 | 最低配置 | 推荐配置 |
|
||||
|-----------|---------|-------------|
|
||||
| **内存** | 8 GB(适用于 3B 模型) | 32+ GB(适用于 27B+ 模型) |
|
||||
| **存储** | 5 GB 可用空间 | 30+ GB(适用于多个模型) |
|
||||
| **CPU** | 4 核 | 8+ 核(AMD EPYC、Ryzen、Intel Xeon) |
|
||||
| **GPU** | 非必需 | 配备 8+ GB 显存的 NVIDIA GPU 可显著提速 |
|
||||
|
||||
:::tip 仅 CPU 可用,但响应速度较慢
|
||||
Ollama 可在纯 CPU 服务器上运行。现代 8 核 CPU 运行 9B 模型约可达 ~10 tokens/sec。31B 模型在 CPU 上更慢(~2–5 tokens/sec)——每次响应需要 30–120 秒,但可以正常工作。GPU 能大幅改善这一情况。对于纯 CPU 环境,通过环境变量(而非 `config.yaml` 键)放宽 API 超时时间:
|
||||
|
||||
```bash
|
||||
# ~/.hermes/.env
|
||||
HERMES_API_TIMEOUT=1800 # 30 分钟 — 为慢速本地模型留出充裕时间
|
||||
```
|
||||
:::
|
||||
|
||||
## 第一步:安装 Ollama
|
||||
|
||||
```bash
|
||||
curl -fsSL https://ollama.com/install.sh | sh
|
||||
```
|
||||
|
||||
验证是否正在运行:
|
||||
|
||||
```bash
|
||||
ollama --version
|
||||
curl http://localhost:11434/api/tags # 应返回 {"models":[]}
|
||||
```
|
||||
|
||||
## 第二步:拉取模型
|
||||
|
||||
根据你的硬件选择:
|
||||
|
||||
| 模型 | 磁盘占用 | 所需内存 | 工具调用 | 适用场景 |
|
||||
|-------|-------------|------------|:------------:|----------|
|
||||
| `gemma4:31b` | ~20 GB | 24+ GB | 支持 | 最佳质量——工具使用和推理能力强 |
|
||||
| `gemma2:27b` | ~16 GB | 20+ GB | 不支持 | 对话任务,不支持工具使用 |
|
||||
| `gemma2:9b` | ~5 GB | 8+ GB | 不支持 | 快速问答——无法调用工具 |
|
||||
| `llama3.2:3b` | ~2 GB | 4+ GB | 不支持 | 仅适合轻量级快速回答 |
|
||||
|
||||
:::warning 工具调用至关重要
|
||||
Hermes 是一个**agentic(智能体)**助手——它通过工具调用来编辑文件、执行命令和浏览网页。不支持工具调用的模型只能进行对话,无法执行操作。要体验完整的 Hermes 功能,请使用支持工具的模型(如 `gemma4:31b`)。
|
||||
:::
|
||||
|
||||
拉取你选择的模型:
|
||||
|
||||
```bash
|
||||
ollama pull gemma4:31b
|
||||
```
|
||||
|
||||
:::info 多个模型
|
||||
你可以拉取多个模型,并在 Hermes 中使用 `/model` 切换。Ollama 按需将活跃模型加载到内存,并自动卸载空闲模型。
|
||||
:::
|
||||
|
||||
验证模型是否正常工作:
|
||||
|
||||
```bash
|
||||
curl http://localhost:11434/v1/chat/completions \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{
|
||||
"model": "gemma4:31b",
|
||||
"messages": [{"role": "user", "content": "Say hello"}],
|
||||
"max_tokens": 50
|
||||
}'
|
||||
```
|
||||
|
||||
你应该看到包含模型回复的 JSON 响应。
|
||||
|
||||
## 第三步:配置 Hermes
|
||||
|
||||
运行 Hermes 设置向导:
|
||||
|
||||
```bash
|
||||
hermes setup
|
||||
```
|
||||
|
||||
当提示选择提供商时,选择 **Custom Endpoint**,并输入:
|
||||
|
||||
- **Base URL:** `http://localhost:11434/v1`
|
||||
- **API Key:** 留空或输入 `no-key`(Ollama 不需要密钥)
|
||||
- **Model:** `gemma4:31b`(或你拉取的模型)
|
||||
|
||||
也可以直接编辑 `~/.hermes/config.yaml`:
|
||||
|
||||
```yaml
|
||||
model:
|
||||
default: "gemma4:31b"
|
||||
provider: "custom"
|
||||
base_url: "http://localhost:11434/v1"
|
||||
```
|
||||
|
||||
## 第四步:开始使用 Hermes
|
||||
|
||||
```bash
|
||||
hermes
|
||||
```
|
||||
|
||||
就这样。你现在运行的是一个完全本地化的 agent。试试看:
|
||||
|
||||
```
|
||||
You: List all Python files in this directory and count the lines of code in each
|
||||
|
||||
You: Read the README.md and summarize what this project does
|
||||
|
||||
You: Create a Python script that fetches the weather for Ho Chi Minh City
|
||||
```
|
||||
|
||||
Hermes 将使用终端工具、文件操作和你的本地模型——无需任何云端调用。
|
||||
|
||||
## 第五步:为任务选择合适的模型
|
||||
|
||||
并非每个任务都需要最大的模型。以下是实用指南:
|
||||
|
||||
| 任务 | 推荐模型 | 原因 |
|
||||
|------|-------------------|-----|
|
||||
| 文件编辑、代码、终端命令 | `gemma4:31b` | 唯一具备可靠工具调用能力的模型 |
|
||||
| 快速问答(无需工具调用) | `gemma2:9b` | 对话任务响应速度快 |
|
||||
| 轻量级聊天 | `llama3.2:3b` | 最快,但能力非常有限 |
|
||||
|
||||
:::note
|
||||
对于完整的 agentic 工作(编辑文件、执行命令、浏览网页),`gemma4:31b` 目前是支持工具调用的最佳本地选项。请关注 [Ollama 的模型库](https://ollama.com/library) 以获取更新模型——工具调用支持正在快速扩展。
|
||||
:::
|
||||
|
||||
在会话中即时切换模型:
|
||||
|
||||
```
|
||||
/model gemma2:9b
|
||||
```
|
||||
|
||||
## 第六步:优化速度
|
||||
|
||||
### 增大 Ollama 的上下文窗口
|
||||
|
||||
默认情况下,Ollama 使用 2048 token 的上下文。对于 agentic 工作(工具调用、长对话),需要更大的上下文:
|
||||
|
||||
```bash
|
||||
# 创建一个扩展上下文的 Modelfile
|
||||
cat > /tmp/Modelfile << 'EOF'
|
||||
FROM gemma4:31b
|
||||
PARAMETER num_ctx 16384
|
||||
EOF
|
||||
|
||||
ollama create gemma4-16k -f /tmp/Modelfile
|
||||
```
|
||||
|
||||
然后将 Hermes 配置中的模型名称更新为 `gemma4-16k`。
|
||||
|
||||
### 保持模型常驻内存
|
||||
|
||||
默认情况下,Ollama 在模型空闲 5 分钟后将其卸载。对于持久化的 gateway 机器人,保持模型常驻:
|
||||
|
||||
```bash
|
||||
# 将 keep-alive 设置为 24 小时
|
||||
curl http://localhost:11434/api/generate \
|
||||
-d '{"model": "gemma4:31b", "keep_alive": "24h"}'
|
||||
```
|
||||
|
||||
或在 Ollama 的环境变量中全局设置:
|
||||
|
||||
```bash
|
||||
# /etc/systemd/system/ollama.service.d/override.conf
|
||||
[Service]
|
||||
Environment="OLLAMA_KEEP_ALIVE=24h"
|
||||
```
|
||||
|
||||
### 使用 GPU 卸载(如有)
|
||||
|
||||
如果你有 NVIDIA GPU,Ollama 会自动将层卸载到 GPU。通过以下命令检查:
|
||||
|
||||
```bash
|
||||
ollama ps # 显示已加载的模型及 GPU 层数
|
||||
```
|
||||
|
||||
对于 12 GB 显存 GPU 上的 31B 模型,你将获得部分卸载(约 40 层在 GPU 上,其余在 CPU 上),仍能带来显著的速度提升。
|
||||
|
||||
## 第七步:作为 Gateway 机器人运行(可选)
|
||||
|
||||
一旦 Hermes 在 CLI 中本地运行正常,你可以将其作为 Telegram 或 Discord 机器人对外提供服务——仍完全运行在你的硬件上。
|
||||
|
||||
### Telegram
|
||||
|
||||
1. 通过 [@BotFather](https://t.me/BotFather) 创建机器人并获取 token
|
||||
2. 添加到 `~/.hermes/config.yaml`:
|
||||
|
||||
```yaml
|
||||
model:
|
||||
default: "gemma4:31b"
|
||||
provider: "custom"
|
||||
base_url: "http://localhost:11434/v1"
|
||||
|
||||
platforms:
|
||||
telegram:
|
||||
enabled: true
|
||||
token: "YOUR_TELEGRAM_BOT_TOKEN"
|
||||
```
|
||||
|
||||
3. 启动 gateway:
|
||||
|
||||
```bash
|
||||
hermes gateway
|
||||
```
|
||||
|
||||
现在在 Telegram 上给你的机器人发消息——它将使用你的本地模型进行响应。
|
||||
|
||||
### Discord
|
||||
|
||||
1. 在 [discord.com/developers](https://discord.com/developers/applications) 创建 Discord 应用
|
||||
2. 添加到配置:
|
||||
|
||||
```yaml
|
||||
platforms:
|
||||
discord:
|
||||
enabled: true
|
||||
token: "YOUR_DISCORD_BOT_TOKEN"
|
||||
```
|
||||
|
||||
3. 启动:`hermes gateway`
|
||||
|
||||
## 第八步:设置回退方案(可选)
|
||||
|
||||
本地模型在处理复杂任务时可能力不从心。设置一个仅在本地模型失败时激活的云端回退:
|
||||
|
||||
```yaml
|
||||
model:
|
||||
default: "gemma4:31b"
|
||||
provider: "custom"
|
||||
base_url: "http://localhost:11434/v1"
|
||||
|
||||
fallback_providers:
|
||||
- provider: openrouter
|
||||
model: anthropic/claude-sonnet-4
|
||||
```
|
||||
|
||||
这样,90% 的使用是免费的(本地),只有困难任务才会调用付费 API。
|
||||
|
||||
## 故障排查
|
||||
|
||||
### 启动时出现"Connection refused"
|
||||
|
||||
Ollama 未在运行。启动它:
|
||||
|
||||
```bash
|
||||
sudo systemctl start ollama
|
||||
# 或
|
||||
ollama serve
|
||||
```
|
||||
|
||||
### 响应缓慢
|
||||
|
||||
- **检查模型大小与内存:** 如果模型所需内存超过可用内存,会发生磁盘交换。请使用更小的模型或增加内存。
|
||||
- **检查 `ollama ps`:** 如果没有 GPU 层被卸载,响应受 CPU 限制。这对于纯 CPU 服务器是正常现象。
|
||||
- **减少上下文:** 长对话会降低推理速度。定期使用 `/compress`,或在配置中设置更低的压缩阈值。
|
||||
|
||||
### 模型不遵循工具调用
|
||||
|
||||
较小的模型(3B、7B)有时会忽略工具调用指令,输出纯文本而非结构化的函数调用。解决方案:
|
||||
|
||||
- **使用更大的模型** —— `gemma4:31b` 或 `gemma2:27b` 处理工具调用的能力远优于 3B/7B 模型。
|
||||
- **Hermes 具备自动修复功能** —— 它能检测格式错误的工具调用并自动尝试修复。
|
||||
- **设置回退方案** —— 如果本地模型连续失败 3 次,Hermes 将回退到云端提供商。
|
||||
|
||||
### 上下文窗口错误
|
||||
|
||||
Ollama 默认上下文(2048 token)对于 agentic 工作来说太小。请参阅[第六步](#step-6-optimize-for-speed)了解如何增大上下文。
|
||||
|
||||
## 费用对比
|
||||
|
||||
以下是与云端 API 相比,本地运行的节省情况,基于典型编程会话(约 10 万 token 输入,约 2 万 token 输出):
|
||||
|
||||
| 提供商 | 每次会话费用 | 每月费用(每日使用) |
|
||||
|----------|-----------------|---------------------|
|
||||
| Anthropic Claude Sonnet | ~$0.80 | ~$24 |
|
||||
| OpenRouter(GPT-4o) | ~$0.60 | ~$18 |
|
||||
| **Ollama(本地)** | **$0.00** | **$0.00** |
|
||||
|
||||
你唯一的成本是电费——根据硬件不同,每次会话约 $0.01–0.05。
|
||||
|
||||
## 本地运行效果好的场景
|
||||
|
||||
- **文件编辑和代码生成** —— 9B+ 模型处理效果良好
|
||||
- **终端命令** —— Hermes 封装命令、执行并读取输出,与模型无关
|
||||
- **网页浏览** —— 浏览器工具负责抓取内容,模型只需解读结果
|
||||
- **定时任务(Cron job)和计划任务** —— 与云端设置完全一致
|
||||
- **多平台 gateway** —— Telegram、Discord、Slack 均可与本地模型配合使用
|
||||
|
||||
## 云端模型更具优势的场景
|
||||
|
||||
- **非常复杂的多步推理** —— 70B+ 或 Claude Opus 等云端模型明显更强
|
||||
- **长上下文窗口** —— 云端模型提供 10 万–100 万 token;本地模型通常为 8K–32K
|
||||
- **大篇幅响应的速度** —— 对于长文本生成,云端推理比纯 CPU 本地运行更快
|
||||
|
||||
最佳策略:日常任务使用本地模型,困难任务设置云端回退。
|
||||
@ -0,0 +1,180 @@
|
||||
---
|
||||
title: "注册 Microsoft Graph 应用程序"
|
||||
description: "Azure 门户操作指南:创建为 Teams 会议流水线提供支持的应用注册"
|
||||
---
|
||||
|
||||
# 注册 Microsoft Graph 应用程序
|
||||
|
||||
Teams 会议流水线使用**仅限应用**(daemon)身份验证从 Microsoft Graph 读取会议转录、录制及相关产物——无需用户登录,无需每次会议单独交互式授权。这需要一个经过管理员同意、具备应用程序权限的 Azure AD 应用注册。
|
||||
|
||||
本指南涵盖以下步骤:
|
||||
|
||||
1. 创建应用注册
|
||||
2. 创建客户端密钥
|
||||
3. 授予流水线所需的 Graph API 权限
|
||||
4. 管理员同意这些权限
|
||||
5. (可选)通过应用程序访问策略将应用限定到特定用户
|
||||
|
||||
完成本指南需要**租户管理员权限**(或由管理员代为授予同意)。请记录收集到的值——最终需要填入 `~/.hermes/.env`。
|
||||
|
||||
## 前提条件
|
||||
|
||||
- 一个具备 Teams Premium 或 Teams 许可证(可生成会议转录和录制)的 Microsoft 365 租户
|
||||
- 可访问 Azure 门户 [entra.microsoft.com](https://entra.microsoft.com) 的管理员权限
|
||||
- 一个可公开访问的 HTTPS 端点,用于接收 Graph 变更通知(在后续 webhook 监听器步骤中配置)
|
||||
|
||||
## 步骤 1:创建应用注册
|
||||
|
||||
1. 以租户管理员身份登录 [entra.microsoft.com](https://entra.microsoft.com)。
|
||||
2. 导航至 **Identity → Applications → App registrations**。
|
||||
3. 点击 **New registration**。
|
||||
4. 填写以下内容:
|
||||
- **Name:**`Hermes Teams Meeting Pipeline`(或任何你能识别的名称)。
|
||||
- **Supported account types:***Accounts in this organizational directory only (Single tenant)*。
|
||||
- **Redirect URI:**留空——仅限应用的身份验证不需要此项。
|
||||
5. 点击 **Register**。
|
||||
|
||||
页面将跳转至应用概览页。复制以下两个值:
|
||||
|
||||
- **Application (client) ID** → `MSGRAPH_CLIENT_ID`
|
||||
- **Directory (tenant) ID** → `MSGRAPH_TENANT_ID`
|
||||
|
||||
## 步骤 2:创建客户端密钥
|
||||
|
||||
1. 在左侧导航栏中,打开 **Certificates & secrets**。
|
||||
2. 点击 **New client secret**。
|
||||
3. **Description:**`hermes-graph-secret`。**Expires:**根据你的轮换策略选择合适的值(通常为 6-24 个月)。
|
||||
4. 点击 **Add**。
|
||||
5. 立即复制 **Value** 列的值——该值仅显示一次。此值即为 `MSGRAPH_CLIENT_SECRET`。
|
||||
|
||||
> **Secret ID** 列不是密钥本身。你需要的是 **Value** 列。
|
||||
|
||||
## 步骤 3:授予 Graph API 权限
|
||||
|
||||
流水线使用最小化的应用程序权限集。仅添加所需权限;每项权限都会扩大应用在租户范围内的读取能力。
|
||||
|
||||
1. 在左侧导航栏中,打开 **API permissions**。
|
||||
2. 点击 **Add a permission** → **Microsoft Graph** → **Application permissions**。
|
||||
3. 根据下表添加流水线所需的权限。
|
||||
4. 添加完成后,点击 **Grant admin consent for `<your tenant>`**。每项权限的 Status 列应变为绿色对勾。
|
||||
|
||||
### 转录优先摘要所需权限
|
||||
|
||||
| 权限 | 允许应用执行的操作 |
|
||||
|------------|--------------------------|
|
||||
| `OnlineMeetings.Read.All` | 读取 Teams 在线会议元数据(主题、参与者、加入 URL)。 |
|
||||
| `OnlineMeetingTranscript.Read.All` | 读取 Teams 生成的会议转录。 |
|
||||
|
||||
### 录制回退所需权限(当转录不可用时)
|
||||
|
||||
| 权限 | 允许应用执行的操作 |
|
||||
|------------|--------------------------|
|
||||
| `OnlineMeetingRecording.Read.All` | 下载 Teams 会议录制以进行离线语音转文字处理。 |
|
||||
| `CallRecords.Read.All` | 仅知道加入 URL 时,通过通话记录解析会议信息。 |
|
||||
|
||||
### 出站摘要投递所需权限(仅限 Graph 模式)
|
||||
|
||||
若 `platforms.teams.extra.delivery_mode` 设置为 `graph`,流水线将通过 Graph API 将摘要发布到 Teams 频道或聊天。如果使用 `incoming_webhook` 投递模式,可跳过这些权限。
|
||||
|
||||
| 权限 | 允许应用执行的操作 |
|
||||
|------------|--------------------------|
|
||||
| `ChannelMessage.Send` | 以应用身份向 Teams 频道发布消息。 |
|
||||
| `Chat.ReadWrite.All` | 向一对一及群组聊天发布消息(仅在将 `chat_id` 设为投递目标时需要)。 |
|
||||
|
||||
### 不推荐的权限
|
||||
|
||||
- `OnlineMeetings.ReadWrite.All` / `Chat.ReadWrite`(不带 `.All`)——权限范围超出流水线所需。
|
||||
- 委托权限——流水线使用仅限应用(客户端凭据)流程;委托权限在没有用户登录的情况下无法生效。
|
||||
|
||||
## 步骤 4:(推荐)通过应用程序访问策略限定应用范围
|
||||
|
||||
默认情况下,`OnlineMeetings.Read.All` 等应用程序权限会授予应用访问租户中**所有**会议的权限。对于合作伙伴演示和开发租户而言这没有问题;但在生产环境中,你几乎肯定需要限制应用可读取哪些用户的会议。
|
||||
|
||||
Microsoft 专门为 Teams 提供了**应用程序访问策略**(Application Access Policies)。该策略仅支持 PowerShell 操作,没有门户 UI。
|
||||
|
||||
在已安装并连接 MicrosoftTeams 模块的管理员 PowerShell 中(`Connect-MicrosoftTeams`)执行:
|
||||
|
||||
```powershell
|
||||
# Create a policy scoped to the Hermes app
|
||||
New-CsApplicationAccessPolicy `
|
||||
-Identity "Hermes-Meeting-Pipeline-Policy" `
|
||||
-AppIds "<MSGRAPH_CLIENT_ID>" `
|
||||
-Description "Restrict Hermes meeting pipeline to allow-listed users"
|
||||
|
||||
# Grant the policy to specific users whose meetings the pipeline may read
|
||||
Grant-CsApplicationAccessPolicy `
|
||||
-PolicyName "Hermes-Meeting-Pipeline-Policy" `
|
||||
-Identity "alice@example.com"
|
||||
|
||||
Grant-CsApplicationAccessPolicy `
|
||||
-PolicyName "Hermes-Meeting-Pipeline-Policy" `
|
||||
-Identity "bob@example.com"
|
||||
```
|
||||
|
||||
授权后策略生效最长需要 30 分钟。使用以下命令验证:
|
||||
|
||||
```powershell
|
||||
Test-CsApplicationAccessPolicy -Identity "alice@example.com" -AppId "<MSGRAPH_CLIENT_ID>"
|
||||
```
|
||||
|
||||
若不配置此策略,**任何**用户的会议均可被读取——这正是该权限在技术层面所授予的范围。生产租户请勿跳过此步骤。
|
||||
|
||||
## 步骤 5:将凭据写入环境文件
|
||||
|
||||
将收集到的三个值填入 `~/.hermes/.env`:
|
||||
|
||||
```bash
|
||||
MSGRAPH_TENANT_ID=<directory-tenant-id>
|
||||
MSGRAPH_CLIENT_ID=<application-client-id>
|
||||
MSGRAPH_CLIENT_SECRET=<client-secret-value>
|
||||
```
|
||||
|
||||
设置文件权限,确保只有你能读取密钥:
|
||||
|
||||
```bash
|
||||
chmod 600 ~/.hermes/.env
|
||||
```
|
||||
|
||||
## 步骤 6:验证令牌流程
|
||||
|
||||
Hermes 内置了 Graph 身份验证冒烟测试。在 Hermes 安装目录下执行:
|
||||
|
||||
```python
|
||||
python -c "
|
||||
import asyncio
|
||||
from tools.microsoft_graph_auth import MicrosoftGraphTokenProvider
|
||||
provider = MicrosoftGraphTokenProvider.from_env()
|
||||
token = asyncio.run(provider.get_access_token())
|
||||
print('Token acquired, length:', len(token))
|
||||
print(provider.inspect_token_health())
|
||||
"
|
||||
```
|
||||
|
||||
成功执行后将打印一个较长的 token(令牌)字符串,以及一个健康状态字典,其中 `cached: True`,`expires_in_seconds` 值接近 3600。失败时将抛出 `MicrosoftGraphTokenError`,并附带 Azure 错误码——最常见的错误如下:
|
||||
|
||||
| Azure 错误码 | 含义 | 修复方法 |
|
||||
|-------------|---------|-----|
|
||||
| `AADSTS7000215: Invalid client secret` | 密钥值不匹配或已过期。 | 在步骤 2 中生成新密钥,并更新 `.env`。 |
|
||||
| `AADSTS700016: Application not found` | `MSGRAPH_CLIENT_ID` 错误或租户不匹配。 | 确认步骤 1 中的值来自同一应用。 |
|
||||
| `AADSTS90002: Tenant not found` | `MSGRAPH_TENANT_ID` 存在拼写错误。 | 重新从应用概览页复制 Directory (tenant) ID。 |
|
||||
| `insufficient_claims`(调用时报错,非获取令牌时) | 令牌获取成功,但 Graph 返回 401/403。 | 跳过了步骤 3 的管理员同意,或添加权限后未重新同意。重新进入 API permissions 并点击 **Grant admin consent**。 |
|
||||
|
||||
## 轮换客户端密钥
|
||||
|
||||
Azure 客户端密钥有固定的过期时间。在密钥过期前:
|
||||
|
||||
1. 在步骤 2 中创建第二个客户端密钥,不要删除第一个。
|
||||
2. 用新值更新 `~/.hermes/.env` 中的 `MSGRAPH_CLIENT_SECRET`。
|
||||
3. 重启 gateway 以使新密钥生效:`hermes gateway restart`。
|
||||
4. 使用上述冒烟测试进行验证。
|
||||
5. 在 Azure 门户中删除旧密钥。
|
||||
|
||||
## 后续步骤
|
||||
|
||||
凭据验证通过后,继续完成以下配置:
|
||||
|
||||
- **Webhook 监听器配置**——部署接收 Graph 变更通知的 `msgraph_webhook` gateway 平台。
|
||||
- **流水线配置**——配置 Teams 会议流水线运行时及操作员 CLI。
|
||||
- **出站投递**——将摘要回传至 Teams 频道或聊天。
|
||||
|
||||
上述页面将随添加对应运行时的 PR 一并发布。本凭据配置是独立的前提步骤,可提前完成。
|
||||
@ -0,0 +1,250 @@
|
||||
---
|
||||
sidebar_position: 10
|
||||
title: "从 OpenClaw 迁移"
|
||||
description: "将 OpenClaw / Clawdbot 配置迁移到 Hermes Agent 的完整指南——包括迁移内容、配置键映射及迁移后的检查事项。"
|
||||
---
|
||||
|
||||
# 从 OpenClaw 迁移
|
||||
|
||||
`hermes claw migrate` 将你的 OpenClaw(或旧版 Clawdbot/Moldbot)配置导入 Hermes。本指南详细说明迁移内容、配置键映射以及迁移后的验证步骤。
|
||||
|
||||
## 快速开始
|
||||
|
||||
```bash
|
||||
# 预览后迁移(始终先显示预览,再要求确认)
|
||||
hermes claw migrate
|
||||
|
||||
# 仅预览,不做任何更改
|
||||
hermes claw migrate --dry-run
|
||||
|
||||
# 完整迁移,包含 API 密钥,跳过确认
|
||||
hermes claw migrate --preset full --migrate-secrets --yes
|
||||
```
|
||||
|
||||
迁移操作在执行任何更改前,始终会显示完整的导入预览。请检查列表后确认继续。
|
||||
|
||||
默认从 `~/.openclaw/` 读取。旧版 `~/.clawdbot/` 或 `~/.moltbot/` 目录会被自动检测,旧版配置文件名(`clawdbot.json`、`moltbot.json`)同理。
|
||||
|
||||
## 选项
|
||||
|
||||
| 选项 | 说明 |
|
||||
|--------|-------------|
|
||||
| `--dry-run` | 仅预览——显示将迁移的内容后停止。 |
|
||||
| `--preset <name>` | `full`(所有兼容设置)或 `user-data`(排除基础设施配置)。两种预设默认均不导入密钥——需显式传入 `--migrate-secrets`。 |
|
||||
| `--overwrite` | 冲突时覆盖已有 Hermes 文件(默认:计划存在冲突时拒绝执行)。 |
|
||||
| `--migrate-secrets` | 包含 API 密钥。即使使用 `--preset full` 也需要显式指定——没有任何预设会静默导入密钥。 |
|
||||
| `--no-backup` | 跳过迁移前对 `~/.hermes/` 的 zip 快照备份(默认在执行前写入单个还原点归档,位于 `~/.hermes/backups/pre-migration-*.zip`;可通过 `hermes import` 还原)。 |
|
||||
| `--source <path>` | 自定义 OpenClaw 目录。 |
|
||||
| `--workspace-target <path>` | `AGENTS.md` 的放置位置。 |
|
||||
| `--skill-conflict <mode>` | `skip`(默认)、`overwrite` 或 `rename`。 |
|
||||
| `--yes` | 跳过预览后的确认提示。 |
|
||||
|
||||
## 迁移内容
|
||||
|
||||
### Persona(角色设定)、记忆与指令
|
||||
|
||||
| 内容 | OpenClaw 来源 | Hermes 目标 | 备注 |
|
||||
|------|----------------|-------------------|-------|
|
||||
| Persona | `workspace/SOUL.md` | `~/.hermes/SOUL.md` | 直接复制 |
|
||||
| 工作区指令 | `workspace/AGENTS.md` | `--workspace-target` 中的 `AGENTS.md` | 需要 `--workspace-target` 标志 |
|
||||
| 长期记忆 | `workspace/MEMORY.md` | `~/.hermes/memories/MEMORY.md` | 解析为条目,与现有内容合并并去重,使用 `§` 分隔符 |
|
||||
| 用户档案 | `workspace/USER.md` | `~/.hermes/memories/USER.md` | 与记忆相同的条目合并逻辑 |
|
||||
| 每日记忆文件 | `workspace/memory/*.md` | `~/.hermes/memories/MEMORY.md` | 所有每日文件合并至主记忆 |
|
||||
|
||||
工作区文件还会在 `workspace.default/` 和 `workspace-main/` 作为备用路径进行检测(OpenClaw 在近期版本中将 `workspace/` 重命名为 `workspace-main/`,多 Agent 配置下使用 `workspace-{agentId}`)。
|
||||
|
||||
### Skills(技能,4 个来源)
|
||||
|
||||
| 来源 | OpenClaw 位置 | Hermes 目标 |
|
||||
|--------|------------------|-------------------|
|
||||
| 工作区 skills | `workspace/skills/` | `~/.hermes/skills/openclaw-imports/` |
|
||||
| 托管/共享 skills | `~/.openclaw/skills/` | `~/.hermes/skills/openclaw-imports/` |
|
||||
| 个人跨项目 skills | `~/.agents/skills/` | `~/.hermes/skills/openclaw-imports/` |
|
||||
| 项目级共享 skills | `workspace/.agents/skills/` | `~/.hermes/skills/openclaw-imports/` |
|
||||
|
||||
Skill 冲突由 `--skill-conflict` 处理:`skip` 保留现有 Hermes skill,`overwrite` 替换,`rename` 创建带 `-imported` 后缀的副本。
|
||||
|
||||
### 模型与 Provider 配置
|
||||
|
||||
| 内容 | OpenClaw 配置路径 | Hermes 目标 | 备注 |
|
||||
|------|---------------------|-------------------|-------|
|
||||
| 默认模型 | `agents.defaults.model` | `config.yaml` → `model` | 可为字符串或 `{primary, fallbacks}` 对象 |
|
||||
| 自定义 providers | `models.providers.*` | `config.yaml` → `custom_providers` | 映射 `baseUrl`、`apiType`/`api`——同时处理短格式("openai"、"anthropic")和带连字符格式("openai-completions"、"anthropic-messages"、"google-generative-ai") |
|
||||
| Provider API 密钥 | `models.providers.*.apiKey` | `~/.hermes/.env` | 需要 `--migrate-secrets`。参见下方 [API 密钥解析](#api-key-resolution) |
|
||||
|
||||
### Agent 行为
|
||||
|
||||
| 内容 | OpenClaw 配置路径 | Hermes 配置路径 | 映射规则 |
|
||||
|------|---------------------|-------------------|---------|
|
||||
| 最大轮次 | `agents.defaults.timeoutSeconds` | `agent.max_turns` | `timeoutSeconds / 10`,上限 200 |
|
||||
| 详细模式 | `agents.defaults.verboseDefault` | `agent.verbose` | "off" / "on" / "full" |
|
||||
| 推理强度 | `agents.defaults.thinkingDefault` | `agent.reasoning_effort` | "always"/"high"/"xhigh" → "high","auto"/"medium"/"adaptive" → "medium","off"/"low"/"none"/"minimal" → "low" |
|
||||
| 压缩 | `agents.defaults.compaction.mode` | `compression.enabled` | "off" → false,其他 → true |
|
||||
| 压缩模型 | `agents.defaults.compaction.model` | `compression.summary_model` | 直接字符串复制 |
|
||||
| 人工延迟 | `agents.defaults.humanDelay.mode` | `human_delay.mode` | "natural" / "custom" / "off" |
|
||||
| 人工延迟时间 | `agents.defaults.humanDelay.minMs` / `.maxMs` | `human_delay.min_ms` / `.max_ms` | 直接复制 |
|
||||
| 时区 | `agents.defaults.userTimezone` | `timezone` | 直接字符串复制 |
|
||||
| 执行超时 | `tools.exec.timeoutSec` | `terminal.timeout` | 直接复制(字段名为 `timeoutSec`,非 `timeout`) |
|
||||
| Docker 沙箱 | `agents.defaults.sandbox.backend` | `terminal.backend` | "docker" → "docker" |
|
||||
| Docker 镜像 | `agents.defaults.sandbox.docker.image` | `terminal.docker_image` | 直接复制 |
|
||||
|
||||
### 会话重置策略
|
||||
|
||||
| OpenClaw 配置路径 | Hermes 配置路径 | 备注 |
|
||||
|---------------------|-------------------|-------|
|
||||
| `session.reset.mode` | `session_reset.mode` | "daily"、"idle" 或两者 |
|
||||
| `session.reset.atHour` | `session_reset.at_hour` | 每日重置的小时(0–23) |
|
||||
| `session.reset.idleMinutes` | `session_reset.idle_minutes` | 不活跃分钟数 |
|
||||
|
||||
注意:OpenClaw 还有 `session.resetTriggers`(简单字符串数组,如 `["daily", "idle"]`)。若结构化的 `session.reset` 不存在,迁移将回退到从 `resetTriggers` 推断。
|
||||
|
||||
### MCP 服务器
|
||||
|
||||
| OpenClaw 字段 | Hermes 字段 | 备注 |
|
||||
|----------------|-------------|-------|
|
||||
| `mcp.servers.*.command` | `mcp_servers.*.command` | stdio 传输 |
|
||||
| `mcp.servers.*.args` | `mcp_servers.*.args` | |
|
||||
| `mcp.servers.*.env` | `mcp_servers.*.env` | |
|
||||
| `mcp.servers.*.cwd` | `mcp_servers.*.cwd` | |
|
||||
| `mcp.servers.*.url` | `mcp_servers.*.url` | HTTP/SSE 传输 |
|
||||
| `mcp.servers.*.tools.include` | `mcp_servers.*.tools.include` | 工具过滤 |
|
||||
| `mcp.servers.*.tools.exclude` | `mcp_servers.*.tools.exclude` | |
|
||||
|
||||
### TTS(文字转语音)
|
||||
|
||||
TTS 设置从 OpenClaw 配置的**两个**位置读取,优先级如下:
|
||||
|
||||
1. `messages.tts.providers.{provider}.*`(规范位置)
|
||||
2. 顶层 `talk.providers.{provider}.*`(备用)
|
||||
3. 旧版扁平键 `messages.tts.{provider}.*`(最旧格式)
|
||||
|
||||
| 内容 | Hermes 目标 |
|
||||
|------|-------------------|
|
||||
| Provider 名称 | `config.yaml` → `tts.provider` |
|
||||
| ElevenLabs voice ID | `config.yaml` → `tts.elevenlabs.voice_id` |
|
||||
| ElevenLabs model ID | `config.yaml` → `tts.elevenlabs.model_id` |
|
||||
| OpenAI 模型 | `config.yaml` → `tts.openai.model` |
|
||||
| OpenAI 语音 | `config.yaml` → `tts.openai.voice` |
|
||||
| Edge TTS 语音 | `config.yaml` → `tts.edge.voice`(OpenClaw 将 "edge" 重命名为 "microsoft"——两者均可识别) |
|
||||
| TTS 资源文件 | `~/.hermes/tts/`(文件复制) |
|
||||
|
||||
### 消息平台
|
||||
|
||||
| 平台 | OpenClaw 配置路径 | Hermes `.env` 变量 | 备注 |
|
||||
|----------|---------------------|----------------------|-------|
|
||||
| Telegram | `channels.telegram.botToken` 或 `.accounts.default.botToken` | `TELEGRAM_BOT_TOKEN` | Token 可为字符串或 [SecretRef](#secretref-handling),支持扁平和 accounts 两种布局 |
|
||||
| Telegram | `credentials/telegram-default-allowFrom.json` | `TELEGRAM_ALLOWED_USERS` | 从 `allowFrom[]` 数组逗号拼接 |
|
||||
| Discord | `channels.discord.token` 或 `.accounts.default.token` | `DISCORD_BOT_TOKEN` | |
|
||||
| Discord | `channels.discord.allowFrom` 或 `.accounts.default.allowFrom` | `DISCORD_ALLOWED_USERS` | |
|
||||
| Slack | `channels.slack.botToken` 或 `.accounts.default.botToken` | `SLACK_BOT_TOKEN` | |
|
||||
| Slack | `channels.slack.appToken` 或 `.accounts.default.appToken` | `SLACK_APP_TOKEN` | |
|
||||
| Slack | `channels.slack.allowFrom` 或 `.accounts.default.allowFrom` | `SLACK_ALLOWED_USERS` | |
|
||||
| WhatsApp | `channels.whatsapp.allowFrom` 或 `.accounts.default.allowFrom` | `WHATSAPP_ALLOWED_USERS` | 通过 Baileys 二维码配对认证——迁移后需重新配对 |
|
||||
| Signal | `channels.signal.account` 或 `.accounts.default.account` | `SIGNAL_ACCOUNT` | |
|
||||
| Signal | `channels.signal.httpUrl` 或 `.accounts.default.httpUrl` | `SIGNAL_HTTP_URL` | |
|
||||
| Signal | `channels.signal.allowFrom` 或 `.accounts.default.allowFrom` | `SIGNAL_ALLOWED_USERS` | |
|
||||
| Matrix | `channels.matrix.accessToken` 或 `.accounts.default.accessToken` | `MATRIX_ACCESS_TOKEN` | 使用 `accessToken`(非 `botToken`) |
|
||||
| Mattermost | `channels.mattermost.botToken` 或 `.accounts.default.botToken` | `MATTERMOST_BOT_TOKEN` | |
|
||||
|
||||
### 其他配置
|
||||
|
||||
| 内容 | OpenClaw 路径 | Hermes 路径 | 备注 |
|
||||
|------|-------------|-------------|-------|
|
||||
| 审批模式 | `approvals.exec.mode` | `config.yaml` → `approvals.mode` | "auto"→"off","always"→"manual","smart"→"smart" |
|
||||
| 命令白名单 | `exec-approvals.json` | `config.yaml` → `command_allowlist` | 模式合并并去重 |
|
||||
| 浏览器 CDP URL | `browser.cdpUrl` | `config.yaml` → `browser.cdp_url` | |
|
||||
| 浏览器无头模式 | `browser.headless` | `config.yaml` → `browser.headless` | |
|
||||
| Brave 搜索密钥 | `tools.web.search.brave.apiKey` | `.env` → `BRAVE_API_KEY` | 需要 `--migrate-secrets` |
|
||||
| Gateway 认证 token | `gateway.auth.token` | `.env` → `HERMES_GATEWAY_TOKEN` | 需要 `--migrate-secrets` |
|
||||
| 工作目录 | `agents.defaults.workspace` | `.env` → `MESSAGING_CWD` | |
|
||||
|
||||
### 已归档(无对应 Hermes 等效项)
|
||||
|
||||
以下内容保存至 `~/.hermes/migration/openclaw/<timestamp>/archive/` 供人工审查:
|
||||
|
||||
| 内容 | 归档文件 | 在 Hermes 中的重建方式 |
|
||||
|------|-------------|--------------------------|
|
||||
| `IDENTITY.md` | `archive/workspace/IDENTITY.md` | 合并至 `SOUL.md` |
|
||||
| `TOOLS.md` | `archive/workspace/TOOLS.md` | Hermes 内置工具说明 |
|
||||
| `HEARTBEAT.md` | `archive/workspace/HEARTBEAT.md` | 使用 cron 作业执行周期性任务 |
|
||||
| `BOOTSTRAP.md` | `archive/workspace/BOOTSTRAP.md` | 使用上下文文件或 skills |
|
||||
| Cron 作业 | `archive/cron-config.json` | 通过 `hermes cron create` 重建 |
|
||||
| 插件 | `archive/plugins-config.json` | 参见 [插件指南](/user-guide/features/hooks) |
|
||||
| Hooks/webhooks | `archive/hooks-config.json` | 使用 `hermes webhook` 或 gateway hooks |
|
||||
| 记忆后端 | `archive/memory-backend-config.json` | 通过 `hermes honcho` 配置 |
|
||||
| Skills 注册表 | `archive/skills-registry-config.json` | 使用 `hermes skills config` |
|
||||
| UI/身份 | `archive/ui-identity-config.json` | 使用 `/skin` 命令 |
|
||||
| 日志 | `archive/logging-diagnostics-config.json` | 在 `config.yaml` 日志部分设置 |
|
||||
| 多 Agent 列表 | `archive/agents-list.json` | 使用 Hermes profiles |
|
||||
| 频道绑定 | `archive/bindings.json` | 按平台手动配置 |
|
||||
| 复杂频道配置 | `archive/channels-deep-config.json` | 手动配置各平台 |
|
||||
|
||||
## API 密钥解析
|
||||
|
||||
启用 `--migrate-secrets` 时,API 密钥按以下优先级从**四个来源**收集:
|
||||
|
||||
1. **配置值** — `openclaw.json` 中的 `models.providers.*.apiKey` 及 TTS provider 密钥
|
||||
2. **环境文件** — `~/.openclaw/.env`(如 `OPENROUTER_API_KEY`、`ANTHROPIC_API_KEY` 等)
|
||||
3. **配置 env 子对象** — `openclaw.json` → `"env"` 或 `"env"."vars"`(部分配置将密钥存于此处而非单独的 `.env` 文件)
|
||||
4. **认证档案** — `~/.openclaw/agents/main/agent/auth-profiles.json`(每个 Agent 的凭据)
|
||||
|
||||
配置值优先级最高,后续来源依次填补剩余空缺。
|
||||
|
||||
### 支持的密钥目标
|
||||
|
||||
`OPENROUTER_API_KEY`、`OPENAI_API_KEY`、`ANTHROPIC_API_KEY`、`DEEPSEEK_API_KEY`、`GEMINI_API_KEY`、`ZAI_API_KEY`、`MINIMAX_API_KEY`、`ELEVENLABS_API_KEY`、`TELEGRAM_BOT_TOKEN`、`VOICE_TOOLS_OPENAI_KEY`
|
||||
|
||||
不在此白名单中的密钥一律不会被复制。
|
||||
|
||||
## SecretRef 处理
|
||||
|
||||
OpenClaw 配置中 token 和 API 密钥的值支持三种格式:
|
||||
|
||||
```json
|
||||
// 纯字符串
|
||||
"channels": { "telegram": { "botToken": "123456:ABC-DEF..." } }
|
||||
|
||||
// 环境变量模板
|
||||
"channels": { "telegram": { "botToken": "${TELEGRAM_BOT_TOKEN}" } }
|
||||
|
||||
// SecretRef 对象
|
||||
"channels": { "telegram": { "botToken": { "source": "env", "id": "TELEGRAM_BOT_TOKEN" } } }
|
||||
```
|
||||
|
||||
迁移会解析所有三种格式。对于环境变量模板和 `source: "env"` 的 SecretRef 对象,会从 `~/.openclaw/.env` 和 `openclaw.json` 的 env 子对象中查找值。`source: "file"` 或 `source: "exec"` 的 SecretRef 对象无法自动解析——迁移会对此发出警告,相关值需通过 `hermes config set` 手动添加至 Hermes。
|
||||
|
||||
## 迁移后
|
||||
|
||||
1. **检查迁移报告** — 完成后打印,包含已迁移、已跳过和冲突项的计数。
|
||||
|
||||
2. **审查归档文件** — `~/.hermes/migration/openclaw/<timestamp>/archive/` 中的所有内容需要人工处理。
|
||||
|
||||
3. **开启新会话** — 导入的 skills 和记忆条目在新会话中生效,当前会话不受影响。
|
||||
|
||||
4. **验证 API 密钥** — 运行 `hermes status` 检查 provider 认证状态。
|
||||
|
||||
5. **测试消息平台** — 若迁移了平台 token,重启 gateway:`systemctl --user restart hermes-gateway`
|
||||
|
||||
6. **检查会话策略** — 验证 `hermes config get session_reset` 是否符合预期。
|
||||
|
||||
7. **重新配对 WhatsApp** — WhatsApp 使用二维码配对(Baileys),不支持 token 迁移。运行 `hermes whatsapp` 进行配对。
|
||||
|
||||
8. **清理归档** — 确认一切正常后,运行 `hermes claw cleanup` 将残留的 OpenClaw 目录重命名为 `.pre-migration/`(防止状态混淆)。
|
||||
|
||||
## 故障排查
|
||||
|
||||
### "OpenClaw directory not found"
|
||||
|
||||
迁移依次检查 `~/.openclaw/`、`~/.clawdbot/`、`~/.moltbot/`。若你的安装路径不同,请使用 `--source /path/to/your/openclaw`。
|
||||
|
||||
### "No provider API keys found"
|
||||
|
||||
根据 OpenClaw 版本不同,密钥可能存储在多个位置:`openclaw.json` 中 `models.providers.*.apiKey` 内联、`~/.openclaw/.env`、`openclaw.json` 的 `"env"` 子对象,或 `agents/main/agent/auth-profiles.json`。迁移会检查所有四个位置。若密钥使用 `source: "file"` 或 `source: "exec"` 的 SecretRef,则无法自动解析——请通过 `hermes config set` 手动添加。
|
||||
|
||||
### 迁移后 skills 未出现
|
||||
|
||||
导入的 skills 位于 `~/.hermes/skills/openclaw-imports/`。开启新会话后生效,或运行 `/skills` 验证是否已加载。
|
||||
|
||||
### TTS 语音未迁移
|
||||
|
||||
OpenClaw 在两处存储 TTS 设置:`messages.tts.providers.*` 和顶层 `talk` 配置。迁移会检查两处。若你的 voice ID 是通过 OpenClaw UI 设置的(存储路径不同),可能需要手动设置:`hermes config set tts.elevenlabs.voice_id YOUR_VOICE_ID`。
|
||||
@ -0,0 +1,228 @@
|
||||
---
|
||||
sidebar_position: 15
|
||||
title: "MiniMax OAuth"
|
||||
description: "通过浏览器 OAuth 登录 MiniMax,在 Hermes Agent 中使用 MiniMax-M2.7 模型——无需 API 密钥"
|
||||
---
|
||||
|
||||
# MiniMax OAuth
|
||||
|
||||
Hermes Agent 通过基于浏览器的 OAuth 登录流程支持 **MiniMax**,使用与 [MiniMax 门户](https://www.minimax.io) 相同的凭据。无需 API 密钥或信用卡——登录一次,Hermes 即可自动刷新您的会话。
|
||||
|
||||
该传输层复用了 `anthropic_messages` 适配器(MiniMax 在 `/anthropic` 路径暴露了一个兼容 Anthropic Messages 的端点),因此所有现有的工具调用、流式传输和上下文功能无需任何适配器改动即可正常使用。
|
||||
|
||||
## 概览
|
||||
|
||||
| 项目 | 值 |
|
||||
|------|-------|
|
||||
| Provider ID | `minimax-oauth` |
|
||||
| 显示名称 | MiniMax (OAuth) |
|
||||
| 认证类型 | 浏览器 OAuth(PKCE 设备码流程) |
|
||||
| 传输层 | 兼容 Anthropic Messages(`anthropic_messages`) |
|
||||
| 模型 | `MiniMax-M2.7`、`MiniMax-M2.7-highspeed` |
|
||||
| 全球端点 | `https://api.minimax.io/anthropic` |
|
||||
| 中国端点 | `https://api.minimaxi.com/anthropic` |
|
||||
| 需要环境变量 | 否(`MINIMAX_API_KEY` **不**用于此 provider) |
|
||||
|
||||
## 前提条件
|
||||
|
||||
- Python 3.9+
|
||||
- 已安装 Hermes Agent
|
||||
- 在 [minimax.io](https://www.minimax.io)(全球)或 [minimaxi.com](https://www.minimaxi.com)(中国)注册的 MiniMax 账户
|
||||
- 本地机器上可用的浏览器(远程会话请使用 `--no-browser`)
|
||||
|
||||
## 快速开始
|
||||
|
||||
```bash
|
||||
# 启动 provider 和模型选择器
|
||||
hermes model
|
||||
# → 从 provider 列表中选择 "MiniMax (OAuth)"
|
||||
# → Hermes 在浏览器中打开 MiniMax 授权页面
|
||||
# → 在浏览器中批准访问
|
||||
# → 选择模型(MiniMax-M2.7 或 MiniMax-M2.7-highspeed)
|
||||
# → 开始对话
|
||||
|
||||
hermes
|
||||
```
|
||||
|
||||
首次登录后,凭据将存储在 `~/.hermes/auth.json` 下,并在每次会话前自动刷新。
|
||||
|
||||
## 手动登录
|
||||
|
||||
您可以在不经过模型选择器的情况下触发登录:
|
||||
|
||||
```bash
|
||||
hermes auth add minimax-oauth
|
||||
```
|
||||
|
||||
### 中国区域
|
||||
|
||||
如果您的账户在中国平台(`minimaxi.com`),请改用中国区域 OAuth provider id `minimax-cn`,或跳过 OAuth 直接配置 `MINIMAX_CN_API_KEY` / `MINIMAX_CN_BASE_URL`。旧版文档中描述的 `--region cn` 标志**未**接入 CLI 的参数解析器;请改用 `minimax-cn` provider:
|
||||
|
||||
```bash
|
||||
hermes auth add minimax-cn --type oauth # 如果您的中国账户支持 OAuth
|
||||
# 或更简单的方式:
|
||||
echo 'MINIMAX_CN_API_KEY=your-key' >> ~/.hermes/.env
|
||||
```
|
||||
|
||||
### 远程/无头会话
|
||||
|
||||
在没有浏览器的服务器或容器上:
|
||||
|
||||
```bash
|
||||
hermes auth add minimax-oauth --no-browser
|
||||
```
|
||||
|
||||
Hermes 将打印验证 URL 和用户码——在任意设备上打开该 URL,并在提示时输入用户码。
|
||||
|
||||
## OAuth 流程
|
||||
|
||||
Hermes 针对 MiniMax OAuth 端点实现了 PKCE 设备码流程:
|
||||
|
||||
1. Hermes 生成 PKCE verifier/challenge 对和一个随机 state 值。
|
||||
2. 携带 challenge 向 `{base_url}/oauth/code` 发送 POST 请求,获取 `user_code` 和 `verification_uri`。
|
||||
3. 浏览器打开 `verification_uri`。如有提示,输入 `user_code`。
|
||||
4. Hermes 轮询 `{base_url}/oauth/token`,直到令牌到达(或超过截止时间)。
|
||||
5. 令牌(`access_token`、`refresh_token`、过期时间)以 `minimax-oauth` 为键保存到 `~/.hermes/auth.json`。
|
||||
|
||||
令牌刷新(标准 OAuth `refresh_token` 授权)在每次会话启动时自动执行,当 access token 距过期不足 60 秒时触发。
|
||||
|
||||
## 检查登录状态
|
||||
|
||||
```bash
|
||||
hermes doctor
|
||||
```
|
||||
|
||||
`◆ Auth Providers` 部分将显示:
|
||||
|
||||
```
|
||||
✓ MiniMax OAuth (logged in, region=global)
|
||||
```
|
||||
|
||||
或者,如果未登录:
|
||||
|
||||
```
|
||||
⚠ MiniMax OAuth (not logged in)
|
||||
```
|
||||
|
||||
## 切换模型
|
||||
|
||||
```bash
|
||||
hermes model
|
||||
# → 选择 "MiniMax (OAuth)"
|
||||
# → 从模型列表中选择
|
||||
```
|
||||
|
||||
或直接设置模型:
|
||||
|
||||
```bash
|
||||
hermes config set model MiniMax-M2.7
|
||||
hermes config set provider minimax-oauth
|
||||
```
|
||||
|
||||
## 配置参考
|
||||
|
||||
登录后,`~/.hermes/config.yaml` 将包含类似如下的条目:
|
||||
|
||||
```yaml
|
||||
model:
|
||||
default: MiniMax-M2.7
|
||||
provider: minimax-oauth
|
||||
base_url: https://api.minimax.io/anthropic
|
||||
```
|
||||
|
||||
### 区域端点
|
||||
|
||||
| Provider id | 门户 | 推理端点 |
|
||||
|-------------|--------|-------------------|
|
||||
| `minimax-oauth`(全球) | `https://api.minimax.io` | `https://api.minimax.io/anthropic` |
|
||||
| `minimax-cn`(中国) | `https://api.minimaxi.com` | `https://api.minimaxi.com/anthropic` |
|
||||
|
||||
### Provider 别名
|
||||
|
||||
以下所有别名均解析为 `minimax-oauth`:
|
||||
|
||||
```bash
|
||||
hermes --provider minimax-oauth # 规范名称
|
||||
hermes --provider minimax-portal # 别名
|
||||
hermes --provider minimax-global # 别名
|
||||
hermes --provider minimax_oauth # 别名(下划线形式)
|
||||
```
|
||||
|
||||
## 环境变量
|
||||
|
||||
`minimax-oauth` provider **不**使用 `MINIMAX_API_KEY` 或 `MINIMAX_BASE_URL`。这些变量仅用于基于 API 密钥的 `minimax` 和 `minimax-cn` provider。
|
||||
|
||||
| 变量 | 作用 |
|
||||
|----------|--------|
|
||||
| `MINIMAX_API_KEY` | 仅用于 `minimax` provider——对 `minimax-oauth` 无效 |
|
||||
| `MINIMAX_CN_API_KEY` | 仅用于 `minimax-cn` provider——对 `minimax-oauth` 无效 |
|
||||
|
||||
要将 `minimax-oauth` 设为活跃 provider,请在 `config.yaml` 中设置 `model.provider: minimax-oauth`(使用 `hermes setup` 进行引导式配置),或在单次调用时传入 `--provider minimax-oauth`:
|
||||
|
||||
```bash
|
||||
hermes --provider minimax-oauth
|
||||
```
|
||||
|
||||
## 模型
|
||||
|
||||
| 模型 | 最适合 |
|
||||
|-------|----------|
|
||||
| `MiniMax-M2.7` | 长上下文推理、复杂工具调用 |
|
||||
| `MiniMax-M2.7-highspeed` | 低延迟、轻量任务、辅助调用 |
|
||||
|
||||
两个模型均支持最多 200,000 个 token 的上下文。
|
||||
|
||||
当 `minimax-oauth` 为主 provider 时,`MiniMax-M2.7-highspeed` 也会自动用作视觉和委托任务的辅助模型。
|
||||
|
||||
## 故障排查
|
||||
|
||||
### 令牌已过期——未自动重新登录
|
||||
|
||||
Hermes 在每次会话启动时,若 access token 距过期不足 60 秒则刷新令牌。如果 access token 已经过期(例如长时间离线后),刷新将在下一次请求时自动触发。如果刷新失败并返回 `refresh_token_reused` 或 `invalid_grant`,Hermes 会将会话标记为需要重新登录。
|
||||
|
||||
当刷新失败为终态(HTTP 4xx、`invalid_grant`、授权已撤销等)时,Hermes 将 refresh token 标记为失效并在本地隔离,避免持续重放注定失败的交换。Agent 会显示一条"需要重新认证"的消息,并在您再次登录之前保持等待。
|
||||
|
||||
**解决方法:** 再次运行 `hermes auth add minimax-oauth` 以开始全新登录。下一次成功交换后隔离状态将自动清除。
|
||||
|
||||
### 授权超时
|
||||
|
||||
设备码流程有有限的过期窗口。如果您未在规定时间内批准登录,Hermes 将抛出超时错误。
|
||||
|
||||
**解决方法:** 重新运行 `hermes auth add minimax-oauth`(或 `hermes model`)。流程将重新开始。
|
||||
|
||||
### State 不匹配(可能的 CSRF)
|
||||
|
||||
Hermes 检测到授权服务器返回的 `state` 值与其发送的值不匹配。
|
||||
|
||||
**解决方法:** 重新运行登录。如果问题持续,请检查是否有代理或重定向正在修改 OAuth 响应。
|
||||
|
||||
### 从远程服务器登录
|
||||
|
||||
如果 `hermes` 无法打开浏览器窗口,请使用 `--no-browser`:
|
||||
|
||||
```bash
|
||||
hermes auth add minimax-oauth --no-browser
|
||||
```
|
||||
|
||||
Hermes 将打印 URL 和用户码。在任意设备上打开该 URL 并在那里完成流程。
|
||||
|
||||
### 运行时出现"未登录 MiniMax OAuth"错误
|
||||
|
||||
auth 存储中没有 `minimax-oauth` 的凭据。您尚未登录,或凭据文件已被删除。
|
||||
|
||||
**解决方法:** 运行 `hermes model` 并选择 MiniMax (OAuth),或运行 `hermes auth add minimax-oauth`。
|
||||
|
||||
## 退出登录
|
||||
|
||||
要移除已存储的 MiniMax OAuth 凭据:
|
||||
|
||||
```bash
|
||||
hermes auth remove minimax-oauth
|
||||
```
|
||||
|
||||
## 另请参阅
|
||||
|
||||
- [AI Providers 参考](../integrations/providers.md)
|
||||
- [环境变量](../reference/environment-variables.md)
|
||||
- [配置](../user-guide/configuration.md)
|
||||
- [hermes doctor](../reference/cli-commands.md)
|
||||
@ -0,0 +1,154 @@
|
||||
---
|
||||
sidebar_position: 17
|
||||
title: "SSH / 远程主机上的 OAuth"
|
||||
description: "当 Hermes 运行在远程机器、容器或跳板机后面时,如何完成基于浏览器的 OAuth(xAI、Spotify)"
|
||||
---
|
||||
|
||||
# SSH / 远程主机上的 OAuth
|
||||
|
||||
部分 Hermes 提供商——目前是 **xAI Grok OAuth** 和 **Spotify**——使用*回环重定向(loopback redirect)* OAuth 流程。认证服务器(xAI、Spotify)将浏览器重定向到 `http://127.0.0.1:<port>/callback`,由 `hermes auth ...` 命令启动的一个小型 HTTP 监听器来获取授权码。
|
||||
|
||||
当 Hermes 和浏览器在同一台机器上时,这一切运行正常。一旦两者不在同一台机器上就会出问题:你笔记本上的浏览器试图访问**你笔记本**上的 `127.0.0.1`,但监听器绑定的是**远程服务器**上的 `127.0.0.1`。
|
||||
|
||||
解决方法是一行 SSH 本地端口转发——**或者**,当你没有真正的 SSH 客户端时(GCP Cloud Shell、GitHub Codespaces、EC2 Instance Connect、Gitpod、基于浏览器的 Web IDE),使用 [#26923](https://github.com/NousResearch/hermes-agent/issues/26923) 中引入的新 `--manual-paste` 标志。
|
||||
|
||||
## 快速概览
|
||||
|
||||
```bash
|
||||
# 在你的本地机器(笔记本)上,另开一个终端:
|
||||
ssh -N -L 56121:127.0.0.1:56121 user@remote-host
|
||||
|
||||
# 在远程机器的现有 SSH 会话中:
|
||||
hermes auth add xai-oauth --no-browser
|
||||
# → Hermes 打印一个授权 URL,在笔记本的浏览器中打开它。
|
||||
# → 浏览器重定向到 127.0.0.1:56121/callback,隧道将请求转发
|
||||
# 到远程监听器,登录完成。
|
||||
```
|
||||
|
||||
`56121` 是 xAI OAuth 使用的端口。Spotify 请将其替换为 `43827`。Hermes 会在 `Waiting for callback on ...` 这一行打印它实际绑定的端口——从那里复制。
|
||||
|
||||
## 仅限浏览器的远程环境(Cloud Shell / Codespaces / EC2 Instance Connect)
|
||||
|
||||
如果你没有常规的 SSH 客户端——例如你在 GCP Cloud Shell、GitHub Codespaces、AWS EC2 Instance Connect、Gitpod 或其他基于浏览器的控制台中运行 Hermes——上述 SSH 隧道不可用。请改用 `--manual-paste`:
|
||||
|
||||
```bash
|
||||
hermes auth add xai-oauth --manual-paste
|
||||
# → Hermes 打印一个授权 URL,在笔记本的浏览器中打开它。
|
||||
# → 在浏览器中批准。重定向到 127.0.0.1:56121/callback 会加载失败
|
||||
# ——这是预期行为。
|
||||
# → 从失败页面的地址栏复制完整 URL。
|
||||
# → 在终端的 "Callback URL:" 提示处粘贴。
|
||||
```
|
||||
|
||||
同样的标志也适用于集成模型选择器的 `hermes model --manual-paste`。如果不想粘贴完整 URL,也可以只接受裸的 `?code=...&state=...` 查询片段。
|
||||
|
||||
Hermes 对两种路径使用**相同的 PKCE verifier、state 和 nonce**,因此上游 OAuth 流程在字节层面完全一致——`--manual-paste` 纯粹是回调跳转的传输方式变更,不会降低安全性。
|
||||
|
||||
## 哪些提供商需要此操作
|
||||
|
||||
| 提供商 | 回环端口 | 需要隧道? |
|
||||
|----------|---------------|----------------|
|
||||
| `xai-oauth`(Grok SuperGrok) | `56121` | 是,当 Hermes 在远程时 |
|
||||
| Spotify | `43827` | 是,当 Hermes 在远程时 |
|
||||
| `anthropic`(Claude Pro/Max) | 不适用 | 否——粘贴代码流程 |
|
||||
| `openai-codex`(ChatGPT Plus/Pro) | 不适用 | 否——设备码流程 |
|
||||
| `minimax`、`nous-portal` | 不适用 | 否——设备码流程 |
|
||||
|
||||
如果你的提供商不在表中,则不需要隧道。
|
||||
|
||||
## 为什么监听器不能直接绑定 0.0.0.0
|
||||
|
||||
xAI 和 Spotify 都会根据白名单验证 `redirect_uri` 参数。两者都要求回环形式(`http://127.0.0.1:<exact-port>/callback`)。将监听器绑定到 `0.0.0.0` 或不同端口会导致认证服务器以 redirect_uri 不匹配为由拒绝请求。SSH 隧道可以端到端保持回环 URI 不变。
|
||||
|
||||
## 分步说明:单跳 SSH
|
||||
|
||||
### 1. 从本地机器启动隧道
|
||||
|
||||
```bash
|
||||
# xAI Grok OAuth(端口 56121)
|
||||
ssh -N -L 56121:127.0.0.1:56121 user@remote-host
|
||||
|
||||
# 或 Spotify(端口 43827)
|
||||
ssh -N -L 43827:127.0.0.1:43827 user@remote-host
|
||||
```
|
||||
|
||||
`-N` 表示"不打开远程 shell,只保持隧道开启"。在登录期间保持此终端运行。
|
||||
|
||||
### 2. 在另一个 SSH 会话中运行认证命令
|
||||
|
||||
```bash
|
||||
ssh user@remote-host
|
||||
hermes auth add xai-oauth --no-browser
|
||||
# 或 Spotify:
|
||||
# hermes auth add spotify --no-browser
|
||||
```
|
||||
|
||||
Hermes 检测到 SSH 会话后,跳过自动打开浏览器,打印授权 URL 以及 `Waiting for callback on http://127.0.0.1:<port>/callback` 这一行。
|
||||
|
||||
### 3. 在本地浏览器中打开 URL
|
||||
|
||||
从远程终端复制授权 URL,粘贴到笔记本的浏览器中。批准同意页面。认证服务器重定向到 `http://127.0.0.1:<port>/callback`。浏览器访问隧道,请求被转发到远程监听器,Hermes 打印 `Login successful!`。
|
||||
|
||||
看到成功提示后,可以关闭隧道(在第一个终端按 Ctrl+C)。
|
||||
|
||||
## 分步说明:通过跳板机
|
||||
|
||||
如果你通过堡垒机 / 跳板机访问 Hermes,使用 SSH 内置的 `-J`(ProxyJump):
|
||||
|
||||
```bash
|
||||
ssh -N -L 56121:127.0.0.1:56121 -J jump-user@jump-host user@final-host
|
||||
```
|
||||
|
||||
这会通过跳板机链式建立 SSH 连接,而不会将回环端口暴露在跳板机上。你笔记本上的本地 `127.0.0.1:56121` 直接隧道到最终远程主机上的 `127.0.0.1:56121`。
|
||||
|
||||
对于不支持 `-J` 的旧版 OpenSSH,完整写法为:
|
||||
|
||||
```bash
|
||||
ssh -N \
|
||||
-o "ProxyCommand=ssh -W %h:%p jump-user@jump-host" \
|
||||
-L 56121:127.0.0.1:56121 \
|
||||
user@final-host
|
||||
```
|
||||
|
||||
## Mosh、tmux、ssh ControlMaster
|
||||
|
||||
隧道是底层 SSH 连接的属性。如果你在 mosh 会话中的 `tmux` 里运行 Hermes,mosh 的漫游不会携带 `-L` 转发。**单独**开一个普通 SSH 会话**仅用于** `-L` 隧道——这个连接必须在整个认证流程期间保持存活。你的交互式 mosh/tmux 会话可以继续正常运行 Hermes。
|
||||
|
||||
如果你使用 `ssh -o ControlMaster=auto`,多路复用连接上的端口转发共享主连接的生命周期。如果隧道未能建立,重启主连接:
|
||||
|
||||
```bash
|
||||
ssh -O exit user@remote-host
|
||||
ssh -N -L 56121:127.0.0.1:56121 user@remote-host
|
||||
```
|
||||
|
||||
## 故障排查
|
||||
|
||||
### `bind [127.0.0.1]:56121: Address already in use`
|
||||
|
||||
你笔记本上已有某个程序占用了该端口。可能是上一个隧道没有正常关闭,或者本地也有一个 Hermes 在监听。找到并终止占用进程:
|
||||
|
||||
```bash
|
||||
# macOS / Linux
|
||||
lsof -iTCP:56121 -sTCP:LISTEN
|
||||
kill <PID>
|
||||
```
|
||||
|
||||
然后重试 `ssh -L` 命令。
|
||||
|
||||
### "Could not establish connection. We couldn't reach your app."(xAI)
|
||||
|
||||
当 xAI 重定向到 `127.0.0.1:<port>/callback` 未能到达监听器时,xAI 的授权页面会显示此错误。可能是隧道未运行、端口错误,或者你使用的是 Hermes 上一次运行时打印的端口(如果首选端口被占用,端口可能会自动递增——始终以最新的 `Waiting for callback on ...` 行为准)。
|
||||
|
||||
### `xAI authorization timed out waiting for the local callback`
|
||||
|
||||
与上述原因相同——重定向从未返回。检查隧道是否仍然存活(`ssh -N` 不显示输出,查看启动它的终端),必要时重启,然后重新运行 `hermes auth add xai-oauth --no-browser`。
|
||||
|
||||
### Token 写入了错误的 `~/.hermes`
|
||||
|
||||
Token 写入运行 `hermes auth add ...` 的 Linux 用户目录下。如果你的网关 / systemd 服务以不同用户(如 `root` 或专用的 `hermes` 用户)运行,请以**该**用户身份进行认证,使 token 写入其 `~/.hermes/auth.json`。使用 `sudo -u hermes -i` 或等效命令。
|
||||
|
||||
## 另请参阅
|
||||
|
||||
- [xAI Grok OAuth](./xai-grok-oauth.md)
|
||||
- [Spotify(`通过 SSH 运行`)](../user-guide/features/spotify.md#running-over-ssh--in-a-headless-environment)
|
||||
- [SSH `-J` / ProxyJump(man 手册)](https://man.openbsd.org/ssh#J)
|
||||
@ -0,0 +1,288 @@
|
||||
---
|
||||
title: "操作 Teams 会议流水线"
|
||||
description: "Microsoft Teams 会议流水线的运行手册、上线检查清单及操作员工作表"
|
||||
---
|
||||
|
||||
# 操作 Teams 会议流水线
|
||||
|
||||
本指南适用于已通过 [Teams Meetings](/user-guide/messaging/teams-meetings) 启用该功能之后的操作阶段。
|
||||
|
||||
本页内容:
|
||||
- 操作员 CLI 流程
|
||||
- 日常订阅维护
|
||||
- 故障排查
|
||||
- 上线检查
|
||||
- 上线工作表
|
||||
|
||||
## 核心操作员命令
|
||||
|
||||
### 验证配置快照
|
||||
|
||||
```bash
|
||||
hermes teams-pipeline validate
|
||||
```
|
||||
|
||||
每次配置变更后首先执行此命令。
|
||||
|
||||
### 检查 token 健康状态
|
||||
|
||||
```bash
|
||||
hermes teams-pipeline token-health
|
||||
hermes teams-pipeline token-health --force-refresh
|
||||
```
|
||||
|
||||
当怀疑 auth(认证)状态过期时,使用 `--force-refresh`。
|
||||
|
||||
### 检查订阅
|
||||
|
||||
```bash
|
||||
hermes teams-pipeline subscriptions
|
||||
```
|
||||
|
||||
### 续期即将到期的订阅
|
||||
|
||||
```bash
|
||||
hermes teams-pipeline maintain-subscriptions
|
||||
hermes teams-pipeline maintain-subscriptions --dry-run
|
||||
```
|
||||
|
||||
### 自动化订阅续期(生产环境必须配置)
|
||||
|
||||
**Microsoft Graph 订阅最多 72 小时后过期。** 若无任何续期操作,会议通知将在 3 天后静默停止,流水线看起来像是"故障"。这是所有基于 Graph 的集成中最常见的运维故障模式。
|
||||
|
||||
你**必须**按计划运行 `maintain-subscriptions`。从以下三种方式中选择一种:
|
||||
|
||||
#### 方式一:Hermes cron(若已运行 Hermes gateway,推荐此方式)
|
||||
|
||||
Hermes 内置 cron 调度器。`--no-agent` 模式以脚本作为任务执行(而非使用 LLM),`--script` 必须指向 `~/.hermes/scripts/` 下的文件。首先创建脚本:
|
||||
|
||||
```bash
|
||||
mkdir -p ~/.hermes/scripts
|
||||
cat > ~/.hermes/scripts/maintain-teams-subscriptions.sh <<'EOF'
|
||||
#!/usr/bin/env bash
|
||||
exec hermes teams-pipeline maintain-subscriptions
|
||||
EOF
|
||||
chmod +x ~/.hermes/scripts/maintain-teams-subscriptions.sh
|
||||
```
|
||||
|
||||
然后注册一个每 12 小时运行一次的纯脚本 cron 任务(相对于 72 小时过期窗口有 6 倍余量):
|
||||
|
||||
```bash
|
||||
hermes cron create "0 */12 * * *" \
|
||||
--name "teams-pipeline-maintain-subscriptions" \
|
||||
--no-agent \
|
||||
--script maintain-teams-subscriptions.sh \
|
||||
--deliver local
|
||||
```
|
||||
|
||||
验证注册情况并查看下次运行时间:
|
||||
|
||||
```bash
|
||||
hermes cron list
|
||||
hermes cron status # 调度器状态
|
||||
```
|
||||
|
||||
#### 方式二:systemd timer(推荐用于 Linux 生产部署)
|
||||
|
||||
创建 `/etc/systemd/system/hermes-teams-pipeline-maintain.service`:
|
||||
|
||||
```ini
|
||||
[Unit]
|
||||
Description=Hermes Teams pipeline subscription maintenance
|
||||
After=network-online.target
|
||||
|
||||
[Service]
|
||||
Type=oneshot
|
||||
User=hermes
|
||||
EnvironmentFile=/etc/hermes/env
|
||||
ExecStart=/usr/local/bin/hermes teams-pipeline maintain-subscriptions
|
||||
```
|
||||
|
||||
以及 `/etc/systemd/system/hermes-teams-pipeline-maintain.timer`:
|
||||
|
||||
```ini
|
||||
[Unit]
|
||||
Description=Run Hermes Teams pipeline subscription maintenance every 12 hours
|
||||
|
||||
[Timer]
|
||||
OnBootSec=5min
|
||||
OnUnitActiveSec=12h
|
||||
Persistent=true
|
||||
|
||||
[Install]
|
||||
WantedBy=timers.target
|
||||
```
|
||||
|
||||
启用:
|
||||
|
||||
```bash
|
||||
sudo systemctl daemon-reload
|
||||
sudo systemctl enable --now hermes-teams-pipeline-maintain.timer
|
||||
systemctl list-timers hermes-teams-pipeline-maintain.timer
|
||||
```
|
||||
|
||||
#### 方式三:普通 crontab
|
||||
|
||||
```cron
|
||||
0 */12 * * * /usr/local/bin/hermes teams-pipeline maintain-subscriptions >> /var/log/hermes/teams-pipeline-maintain.log 2>&1
|
||||
```
|
||||
|
||||
确保 cron 环境中包含 `MSGRAPH_*` 凭据。最简单的方法:在 crontab 调用的包装脚本顶部 source `~/.hermes/.env`。
|
||||
|
||||
#### 验证续期是否正常工作
|
||||
|
||||
设置好计划任务后,在首次计划运行后检查续期活动:
|
||||
|
||||
```bash
|
||||
hermes teams-pipeline subscriptions # 应显示 expirationDateTime 已推进
|
||||
hermes teams-pipeline maintain-subscriptions --dry-run # 大多数时候应显示"0 expiring soon"
|
||||
```
|
||||
|
||||
如果你发现 Graph webhook 在恰好约 72 小时后神秘地"停止工作",这是首先要检查的地方:续期任务是否实际运行了?
|
||||
|
||||
### 查看最近的任务
|
||||
|
||||
```bash
|
||||
hermes teams-pipeline list
|
||||
hermes teams-pipeline list --status failed
|
||||
hermes teams-pipeline show <job-id>
|
||||
```
|
||||
|
||||
### 重放已存储的任务
|
||||
|
||||
```bash
|
||||
hermes teams-pipeline run <job-id>
|
||||
```
|
||||
|
||||
### 干运行会议产物拉取
|
||||
|
||||
```bash
|
||||
hermes teams-pipeline fetch --meeting-id <meeting-id>
|
||||
hermes teams-pipeline fetch --join-web-url "<join-url>"
|
||||
```
|
||||
|
||||
## 日常运行手册
|
||||
|
||||
### 首次设置后
|
||||
|
||||
按顺序执行:
|
||||
|
||||
```bash
|
||||
hermes teams-pipeline validate
|
||||
hermes teams-pipeline token-health --force-refresh
|
||||
hermes teams-pipeline subscriptions
|
||||
```
|
||||
|
||||
然后触发或等待一个真实的会议事件,并确认:
|
||||
|
||||
```bash
|
||||
hermes teams-pipeline list
|
||||
hermes teams-pipeline show <job-id>
|
||||
```
|
||||
|
||||
### 每日或定期检查
|
||||
|
||||
- 运行 `hermes teams-pipeline maintain-subscriptions --dry-run`
|
||||
- 检查 `hermes teams-pipeline list --status failed`
|
||||
- 确认 Teams 投递目标仍为正确的聊天或频道
|
||||
|
||||
### 变更 webhook URL 或投递目标前
|
||||
|
||||
- 更新公共通知 URL 或 Teams 目标配置
|
||||
- 运行 `hermes teams-pipeline validate`
|
||||
- 续期或重新创建受影响的订阅
|
||||
- 确认新事件落入预期的接收端
|
||||
|
||||
## 故障排查
|
||||
|
||||
### 未创建任何任务
|
||||
|
||||
检查:
|
||||
- `msgraph_webhook` 是否已启用
|
||||
- 公共通知 URL 是否指向 `/msgraph/webhook`
|
||||
- 订阅中的 client state 是否与 `MSGRAPH_WEBHOOK_CLIENT_STATE` 匹配
|
||||
- 订阅是否在远端仍然存在且未过期
|
||||
|
||||
### 任务停留在重试状态或在摘要生成前失败
|
||||
|
||||
检查:
|
||||
- 转录权限及可用性
|
||||
- 录制权限及产物可用性
|
||||
- 若启用了录制回退,检查 `ffmpeg` 是否可用
|
||||
- Graph token 健康状态
|
||||
|
||||
### 摘要已生成但未投递到 Teams
|
||||
|
||||
检查:
|
||||
- `platforms.teams.enabled: true`
|
||||
- `delivery_mode`
|
||||
- webhook 模式下的 `incoming_webhook_url`
|
||||
- Graph 模式下的 `chat_id` 或 `team_id` 加 `channel_id`
|
||||
- 若使用 Graph 发帖,检查 Teams auth 配置
|
||||
|
||||
### 重复或意外的重放
|
||||
|
||||
检查:
|
||||
- 是否手动通过 `hermes teams-pipeline run` 重放了任务
|
||||
- 该会议的 sink 记录是否已存在
|
||||
- 是否在本地配置中有意启用了重发路径
|
||||
|
||||
## 上线检查清单
|
||||
|
||||
- [ ] Graph 凭据已存在且正确
|
||||
- [ ] `msgraph_webhook` 已启用且可从公网访问
|
||||
- [ ] `MSGRAPH_WEBHOOK_CLIENT_STATE` 已设置且与订阅匹配
|
||||
- [ ] 转录订阅已创建
|
||||
- [ ] 若需要 STT 回退,录制订阅已创建
|
||||
- [ ] 若启用录制回退,`ffmpeg` 已安装
|
||||
- [ ] Teams 出站投递目标已配置并验证
|
||||
- [ ] Notion 和 Linear 接收端仅在实际需要时配置
|
||||
- [ ] `hermes teams-pipeline validate` 返回 OK 快照
|
||||
- [ ] `hermes teams-pipeline token-health --force-refresh` 执行成功
|
||||
- [ ] **`maintain-subscriptions` 已配置计划任务**(Hermes cron、systemd timer 或 crontab——参见[自动化订阅续期](#automating-subscription-renewal-required-for-production))。若未配置,Graph 订阅将在 72 小时内静默过期。
|
||||
- [ ] 一个真实的端到端会议事件已生成存储任务
|
||||
- [ ] 至少一条摘要已到达预期的投递接收端
|
||||
|
||||
## 投递模式决策指南
|
||||
|
||||
| 模式 | 适用场景 | 权衡 |
|
||||
|------|----------|----------|
|
||||
| `incoming_webhook` | 仅需简单地向 Teams 发帖 | 配置最简单,控制较少 |
|
||||
| `graph` | 需要通过 Graph 向频道或聊天发帖 | 控制更多,auth 和目标配置更复杂 |
|
||||
|
||||
## 操作员工作表
|
||||
|
||||
上线前填写:
|
||||
|
||||
| 项目 | 值 |
|
||||
|------|-------|
|
||||
| 公共通知 URL | |
|
||||
| Graph 租户 ID | |
|
||||
| Graph 客户端 ID | |
|
||||
| Webhook client state | |
|
||||
| 转录资源订阅 | |
|
||||
| 录制资源订阅 | |
|
||||
| Teams 投递模式 | |
|
||||
| Teams 聊天 ID 或团队/频道 | |
|
||||
| Notion 数据库 ID | |
|
||||
| Linear 团队 ID | |
|
||||
| Store 路径覆盖(如有) | |
|
||||
| 每日检查负责人 | |
|
||||
|
||||
## 变更审查工作表
|
||||
|
||||
变更部署前使用:
|
||||
|
||||
| 问题 | 答案 |
|
||||
|----------|--------|
|
||||
| 是否正在变更公共 webhook URL? | |
|
||||
| 是否正在轮换 Graph 凭据? | |
|
||||
| 是否正在变更 Teams 投递模式? | |
|
||||
| 是否正在迁移到新的 Teams 聊天或频道? | |
|
||||
| 订阅是否需要重新创建或续期? | |
|
||||
| 是否需要重新进行端到端验证? | |
|
||||
|
||||
## 相关文档
|
||||
|
||||
- [Teams Meetings 设置](/user-guide/messaging/teams-meetings)
|
||||
- [Microsoft Teams bot 设置](/user-guide/messaging/teams)
|
||||
@ -0,0 +1,217 @@
|
||||
---
|
||||
sidebar_position: 12
|
||||
title: "将脚本输出推送到消息平台"
|
||||
description: "使用 `hermes send` 将任意 shell 脚本、cron 任务、CI hook 或监控守护进程的文本发送到 Telegram、Discord、Slack、Signal 等平台。"
|
||||
---
|
||||
|
||||
# 将脚本输出推送到消息平台
|
||||
|
||||
`hermes send` 是一个轻量、可脚本化的 CLI,能将消息推送到 Hermes 已配置的任意消息平台。可以把它理解为跨平台的通知专用 `curl`——无需运行中的 gateway,无需 LLM,也无需在每个脚本里重复粘贴 bot token。
|
||||
|
||||
适用场景:
|
||||
|
||||
- 系统监控(内存、磁盘、GPU 温度、长时任务完成通知)
|
||||
- CI/CD 通知(部署完成、测试失败)
|
||||
- 需要将结果推送给你的 cron 脚本
|
||||
- 从终端发送一次性消息
|
||||
- 将任意工具的输出管道到任意平台(`make | hermes send --to slack:#builds`)
|
||||
|
||||
该命令复用 `hermes gateway` 已有的凭据和平台适配器,无需维护第二套配置。
|
||||
|
||||
---
|
||||
|
||||
## 快速开始
|
||||
|
||||
```bash
|
||||
# 向某平台的默认频道发送纯文本
|
||||
hermes send --to telegram "deploy finished"
|
||||
|
||||
# 将任意命令的 stdout 通过管道传入
|
||||
echo "RAM 92%" | hermes send --to telegram:-1001234567890
|
||||
|
||||
# 发送文件
|
||||
hermes send --to discord:#ops --file /tmp/report.md
|
||||
|
||||
# 附加主题/标题行
|
||||
hermes send --to slack:#eng --subject "[CI] build.log" --file build.log
|
||||
|
||||
# 指定线程目标(Telegram 话题、Discord 线程)
|
||||
hermes send --to telegram:-1001234567890:17585 "threaded reply"
|
||||
|
||||
# 列出所有已配置的目标
|
||||
hermes send --list
|
||||
|
||||
# 按平台过滤
|
||||
hermes send --list telegram
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 参数参考
|
||||
|
||||
| 标志 | 说明 |
|
||||
|------|-------------|
|
||||
| `-t, --to TARGET` | 目标地址。参见[目标格式](#target-formats)。 |
|
||||
| `message`(位置参数) | 消息文本。省略时从 `--file` 或 stdin 读取。 |
|
||||
| `-f, --file PATH` | 从文件读取消息体。`--file -` 强制从 stdin 读取。 |
|
||||
| `-s, --subject LINE` | 在消息体前添加标题/主题行。 |
|
||||
| `-l, --list` | 列出可用目标。可选位置参数用于按平台过滤。 |
|
||||
| `-q, --quiet` | 成功时不输出到 stdout(仅返回退出码——适合脚本使用)。 |
|
||||
| `--json` | 输出发送结果的原始 JSON。 |
|
||||
| `-h, --help` | 显示内置帮助文本。 |
|
||||
|
||||
### 目标格式 {#target-formats}
|
||||
|
||||
| 格式 | 示例 | 含义 |
|
||||
|--------|---------|---------|
|
||||
| `platform` | `telegram` | 发送到该平台配置的默认频道 |
|
||||
| `platform:chat_id` | `telegram:-1001234567890` | 指定数字 chat / 群组 / 用户 |
|
||||
| `platform:chat_id:thread_id` | `telegram:-1001234567890:17585` | 指定线程或 Telegram 论坛话题 |
|
||||
| `platform:#channel` | `discord:#ops` | 易读的频道名称(通过频道目录解析) |
|
||||
| `platform:+E164` | `signal:+15551234567` | 以电话号码寻址的平台:Signal、SMS、WhatsApp |
|
||||
|
||||
Hermes 附带适配器的所有平台均可作为目标:
|
||||
`telegram`、`discord`、`slack`、`signal`、`sms`、`whatsapp`、`matrix`、
|
||||
`mattermost`、`feishu`、`dingtalk`、`wecom`、`weixin`、`email` 等。
|
||||
|
||||
### 退出码
|
||||
|
||||
| 码 | 含义 |
|
||||
|------|---------|
|
||||
| `0` | 发送(或列出)成功 |
|
||||
| `1` | 平台层面投递失败(认证、权限、网络) |
|
||||
| `2` | 用法 / 参数 / 配置错误 |
|
||||
|
||||
退出码遵循标准 Unix 惯例,脚本可以像处理 `curl` 或 `grep` 一样对其进行分支判断。
|
||||
|
||||
---
|
||||
|
||||
## 消息体解析顺序
|
||||
|
||||
`hermes send` 按以下顺序解析消息体:
|
||||
|
||||
1. **位置参数** — `hermes send --to telegram "hi"`
|
||||
2. **`--file PATH`** — `hermes send --to telegram --file msg.txt`
|
||||
3. **管道 stdin** — `echo hi | hermes send --to telegram`
|
||||
|
||||
当 stdin 是 TTY(无管道)时,Hermes **不会**等待输入——你会收到明确的用法错误提示。这可以防止脚本在意外省略消息体时挂起。
|
||||
|
||||
---
|
||||
|
||||
## 实际使用示例
|
||||
|
||||
### 监控:内存 / 磁盘告警
|
||||
|
||||
用一行简洁的代码替换 watchdog 脚本中的 `curl https://api.telegram.org/...` 调用:
|
||||
|
||||
```bash
|
||||
#!/usr/bin/env bash
|
||||
ram_pct=$(free | awk '/^Mem:/ {printf "%d", $3 * 100 / $2}')
|
||||
if [ "$ram_pct" -ge 85 ]; then
|
||||
hermes send --to telegram --subject "⚠ MEMORY WARNING" \
|
||||
"RAM ${ram_pct}% on $(hostname)"
|
||||
fi
|
||||
```
|
||||
|
||||
由于 `hermes send` 复用你的 Hermes 配置,同一脚本可在任何安装了 Hermes 的主机上运行——无需手动将 bot token 导出到每台机器的环境变量中。
|
||||
|
||||
:::tip 不要用 gateway 监控自身
|
||||
对于可能在 gateway 本身出现问题时触发的 watchdog(OOM 告警、磁盘满告警),请继续使用最简单的 `curl` 调用,而非 `hermes send`。如果 Python 解释器因机器抖动无法加载,你仍然希望告警能发出去。
|
||||
:::
|
||||
|
||||
### CI / CD:构建与测试结果
|
||||
|
||||
```bash
|
||||
# 在 .github/workflows/deploy.yml 或任意 CI 脚本中
|
||||
if ./scripts/deploy.sh; then
|
||||
hermes send --to slack:#deploys "✅ ${CI_COMMIT_SHA:0:7} deployed"
|
||||
else
|
||||
tail -n 100 deploy.log | hermes send \
|
||||
--to slack:#deploys --subject "❌ deploy failed"
|
||||
exit 1
|
||||
fi
|
||||
```
|
||||
|
||||
### Cron:每日报告
|
||||
|
||||
```bash
|
||||
# Crontab 条目
|
||||
0 9 * * * /usr/local/bin/generate-metrics.sh \
|
||||
| /home/me/.hermes/bin/hermes send \
|
||||
--to telegram --subject "Daily metrics $(date +%Y-%m-%d)"
|
||||
```
|
||||
|
||||
### 长时任务:完成后推送通知
|
||||
|
||||
```bash
|
||||
./train.py --epochs 200 && \
|
||||
hermes send --to telegram "training done" || \
|
||||
hermes send --to telegram "training failed (exit $?)"
|
||||
```
|
||||
|
||||
### 脚本中使用 `--json` 与 `--quiet`
|
||||
|
||||
```bash
|
||||
# 投递失败时让脚本硬失败;成功时不污染日志
|
||||
hermes send --to telegram --quiet "keepalive" || {
|
||||
echo "Telegram delivery failed" >&2
|
||||
exit 1
|
||||
}
|
||||
|
||||
# 捕获消息 ID 以便后续编辑 / 回复线程
|
||||
msg_id=$(hermes send --to discord:#ops --json "build started" \
|
||||
| jq -r .message_id)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## `hermes send` 需要 gateway 运行吗?
|
||||
|
||||
**通常不需要。** 对于所有基于 bot token 的平台——Telegram、Discord、Slack、Signal、SMS、WhatsApp Cloud API 等——`hermes send` 直接使用 `~/.hermes/.env` 和 `~/.hermes/config.yaml` 中的凭据调用平台的 REST 接口。它是一个独立的子进程,消息投递完成后即退出。
|
||||
|
||||
只有依赖持久适配器连接的**插件平台**才需要运行中的 gateway(例如,某个保持长连接 WebSocket 的自定义插件)。此时你会收到明确的错误提示,指引你启动 gateway;执行 `hermes gateway start` 后重试即可。
|
||||
|
||||
---
|
||||
|
||||
## 列出与发现目标
|
||||
|
||||
在向特定频道发送消息之前,可以查看可用目标:
|
||||
|
||||
```bash
|
||||
# 列出所有已配置平台的所有目标
|
||||
hermes send --list
|
||||
|
||||
# 仅列出 Telegram 目标
|
||||
hermes send --list telegram
|
||||
|
||||
# 机器可读格式
|
||||
hermes send --list --json
|
||||
```
|
||||
|
||||
列表数据来源于 `~/.hermes/channel_directory.json`,gateway 运行期间每隔几分钟刷新一次。如果看到"尚未发现频道",请先启动一次 gateway(`hermes gateway start`)以填充缓存。
|
||||
|
||||
易读名称(`discord:#ops`、`slack:#engineering`)在发送时通过该缓存解析,无需记忆数字 ID。
|
||||
|
||||
---
|
||||
|
||||
## 与其他方案的对比
|
||||
|
||||
| 方案 | 多平台 | 复用 Hermes 凭据 | 需要 gateway | 最适合 |
|
||||
|----------|----------------|---------------------|---------------|----------|
|
||||
| `hermes send` | ✅ | ✅ | 否(bot token) | 以下所有场景 |
|
||||
| 对各平台直接 `curl` | 各自单独编写 | 手动管理 | 否 | 关键 watchdog |
|
||||
| 带 `--deliver` 的 `cron` 任务 | ✅ | ✅ | 否 | 定时 agent 任务 |
|
||||
| `send_message` agent 工具 | ✅ | ✅ | 否 | agent 循环内部 |
|
||||
|
||||
`hermes send` 有意保持最简接口。如果需要 agent 决定说什么,请在对话或 cron 任务中使用 `send_message` 工具。如果需要定时运行并生成 LLM 内容,请使用带 `deliver='telegram:...'` 的 `cronjob(action='create', prompt=...)`。如果只需要管道传输原始字符串,直接用 `hermes send`。
|
||||
|
||||
---
|
||||
|
||||
## 相关文档
|
||||
|
||||
- [用 Cron 自动化一切](/guides/automate-with-cron) —
|
||||
输出自动投递到任意平台的定时任务。
|
||||
- [Gateway 内部机制](/developer-guide/gateway-internals) —
|
||||
`hermes send` 与 cron 投递共享的投递路由器。
|
||||
- [消息平台配置](/user-guide/messaging/) —
|
||||
各平台的一次性配置说明。
|
||||
@ -0,0 +1,341 @@
|
||||
---
|
||||
sidebar_position: 5
|
||||
title: "将 Hermes 作为 Python 库使用"
|
||||
description: "将 AIAgent 嵌入你自己的 Python 脚本、Web 应用或自动化流水线——无需 CLI"
|
||||
---
|
||||
|
||||
# 将 Hermes 作为 Python 库使用
|
||||
|
||||
Hermes 不仅仅是一个 CLI 工具。你可以直接导入 `AIAgent`,在自己的 Python 脚本、Web 应用或自动化流水线中以编程方式使用它。本指南将介绍具体方法。
|
||||
|
||||
---
|
||||
|
||||
## 安装
|
||||
|
||||
直接从仓库安装 Hermes:
|
||||
|
||||
```bash
|
||||
pip install git+https://github.com/NousResearch/hermes-agent.git
|
||||
```
|
||||
|
||||
或使用 [uv](https://docs.astral.sh/uv/):
|
||||
|
||||
```bash
|
||||
uv pip install git+https://github.com/NousResearch/hermes-agent.git
|
||||
```
|
||||
|
||||
也可以在 `requirements.txt` 中固定版本:
|
||||
|
||||
```text
|
||||
hermes-agent @ git+https://github.com/NousResearch/hermes-agent.git
|
||||
```
|
||||
|
||||
:::tip
|
||||
将 Hermes 作为库使用时,CLI 所需的环境变量同样必须设置。至少需要设置 `OPENROUTER_API_KEY`(若直接访问提供商,则设置 `OPENAI_API_KEY` 或 `ANTHROPIC_API_KEY`)。
|
||||
:::
|
||||
|
||||
---
|
||||
|
||||
## 基本用法
|
||||
|
||||
使用 Hermes 最简单的方式是 `chat()` 方法——传入一条消息,返回一个字符串:
|
||||
|
||||
```python
|
||||
from run_agent import AIAgent
|
||||
|
||||
agent = AIAgent(
|
||||
model="anthropic/claude-sonnet-4",
|
||||
quiet_mode=True,
|
||||
)
|
||||
response = agent.chat("What is the capital of France?")
|
||||
print(response)
|
||||
```
|
||||
|
||||
`chat()` 在内部处理完整的对话循环——工具调用、重试等一切事务——并仅返回最终的文本响应。
|
||||
|
||||
:::warning
|
||||
将 Hermes 嵌入自己的代码时,务必设置 `quiet_mode=True`。否则,agent 会打印 CLI 的加载动画、进度指示器及其他终端输出,从而干扰你的应用输出。
|
||||
:::
|
||||
|
||||
---
|
||||
|
||||
## 完整对话控制
|
||||
|
||||
如需对对话进行更精细的控制,可直接使用 `run_conversation()`。它返回一个包含完整响应、消息历史和元数据的字典:
|
||||
|
||||
```python
|
||||
agent = AIAgent(
|
||||
model="anthropic/claude-sonnet-4",
|
||||
quiet_mode=True,
|
||||
)
|
||||
|
||||
result = agent.run_conversation(
|
||||
user_message="Search for recent Python 3.13 features",
|
||||
task_id="my-task-1",
|
||||
)
|
||||
|
||||
print(result["final_response"])
|
||||
print(f"Messages exchanged: {len(result['messages'])}")
|
||||
```
|
||||
|
||||
返回的字典包含:
|
||||
- **`final_response`** — agent 的最终文本回复
|
||||
- **`messages`** — 完整的消息历史(系统消息、用户消息、助手消息、工具调用)
|
||||
|
||||
(传入的 `task_id` 存储在 agent 实例上用于 VM 隔离,不会在返回字典中回显。)
|
||||
|
||||
你也可以传入自定义系统消息,覆盖该次调用的临时系统 prompt(提示词):
|
||||
|
||||
```python
|
||||
result = agent.run_conversation(
|
||||
user_message="Explain quicksort",
|
||||
system_message="You are a computer science tutor. Use simple analogies.",
|
||||
)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 配置工具集
|
||||
|
||||
使用 `enabled_toolsets` 或 `disabled_toolsets` 控制 agent 可访问的工具集:
|
||||
|
||||
```python
|
||||
# 仅启用 Web 工具(浏览、搜索)
|
||||
agent = AIAgent(
|
||||
model="anthropic/claude-sonnet-4",
|
||||
enabled_toolsets=["web"],
|
||||
quiet_mode=True,
|
||||
)
|
||||
|
||||
# 启用除终端访问外的所有功能
|
||||
agent = AIAgent(
|
||||
model="anthropic/claude-sonnet-4",
|
||||
disabled_toolsets=["terminal"],
|
||||
quiet_mode=True,
|
||||
)
|
||||
```
|
||||
|
||||
:::tip
|
||||
当你需要一个功能最小化、受限的 agent 时(例如,仅用于研究机器人的 Web 搜索),使用 `enabled_toolsets`。当你需要大部分功能但需限制特定能力时(例如,在共享环境中禁用终端访问),使用 `disabled_toolsets`。
|
||||
:::
|
||||
|
||||
---
|
||||
|
||||
## 多轮对话
|
||||
|
||||
通过将消息历史传回来维护多轮对话的状态:
|
||||
|
||||
```python
|
||||
agent = AIAgent(
|
||||
model="anthropic/claude-sonnet-4",
|
||||
quiet_mode=True,
|
||||
)
|
||||
|
||||
# 第一轮
|
||||
result1 = agent.run_conversation("My name is Alice")
|
||||
history = result1["messages"]
|
||||
|
||||
# 第二轮——agent 记住了上下文
|
||||
result2 = agent.run_conversation(
|
||||
"What's my name?",
|
||||
conversation_history=history,
|
||||
)
|
||||
print(result2["final_response"]) # "Your name is Alice."
|
||||
```
|
||||
|
||||
`conversation_history` 参数接受上一次结果的 `messages` 列表。agent 会在内部复制该列表,因此你的原始列表不会被修改。
|
||||
|
||||
---
|
||||
|
||||
## 保存轨迹数据
|
||||
|
||||
启用轨迹保存,以 ShareGPT 格式捕获对话——适用于生成训练数据或调试:
|
||||
|
||||
```python
|
||||
agent = AIAgent(
|
||||
model="anthropic/claude-sonnet-4",
|
||||
save_trajectories=True,
|
||||
quiet_mode=True,
|
||||
)
|
||||
|
||||
agent.chat("Write a Python function to sort a list")
|
||||
# 以 ShareGPT 格式保存到 trajectory_samples.jsonl
|
||||
```
|
||||
|
||||
每次对话以单行 JSONL 的形式追加写入,便于从自动化运行中收集数据集。
|
||||
|
||||
---
|
||||
|
||||
## 自定义系统 Prompt
|
||||
|
||||
使用 `ephemeral_system_prompt` 设置自定义系统 prompt,用于引导 agent 的行为,但**不会**保存到轨迹文件中(保持训练数据的整洁):
|
||||
|
||||
```python
|
||||
agent = AIAgent(
|
||||
model="anthropic/claude-sonnet-4",
|
||||
ephemeral_system_prompt="You are a SQL expert. Only answer database questions.",
|
||||
quiet_mode=True,
|
||||
)
|
||||
|
||||
response = agent.chat("How do I write a JOIN query?")
|
||||
print(response)
|
||||
```
|
||||
|
||||
这非常适合构建专用 agent——代码审查员、文档撰写员、SQL 助手——全部使用相同的底层工具。
|
||||
|
||||
---
|
||||
|
||||
## 批量处理
|
||||
|
||||
如需并行运行大量 prompt,Hermes 提供了 `batch_runner.py`,它可管理并发的 `AIAgent` 实例并进行适当的资源隔离:
|
||||
|
||||
```bash
|
||||
python batch_runner.py --input prompts.jsonl --output results.jsonl
|
||||
```
|
||||
|
||||
每个 prompt 都有自己的 `task_id` 和隔离环境。如果需要自定义批处理逻辑,可以直接使用 `AIAgent` 构建:
|
||||
|
||||
```python
|
||||
import concurrent.futures
|
||||
from run_agent import AIAgent
|
||||
|
||||
prompts = [
|
||||
"Explain recursion",
|
||||
"What is a hash table?",
|
||||
"How does garbage collection work?",
|
||||
]
|
||||
|
||||
def process_prompt(prompt):
|
||||
# 每个任务创建一个新的 agent 实例以保证线程安全
|
||||
agent = AIAgent(
|
||||
model="anthropic/claude-sonnet-4",
|
||||
quiet_mode=True,
|
||||
skip_memory=True,
|
||||
)
|
||||
return agent.chat(prompt)
|
||||
|
||||
with concurrent.futures.ThreadPoolExecutor(max_workers=3) as executor:
|
||||
results = list(executor.map(process_prompt, prompts))
|
||||
|
||||
for prompt, result in zip(prompts, results):
|
||||
print(f"Q: {prompt}\nA: {result}\n")
|
||||
```
|
||||
|
||||
:::warning
|
||||
务必为**每个线程或任务创建一个新的 `AIAgent` 实例**。agent 维护着内部状态(对话历史、工具会话、迭代计数器),这些状态不是线程安全的,不能共享。
|
||||
:::
|
||||
|
||||
---
|
||||
|
||||
## 集成示例
|
||||
|
||||
### FastAPI 端点
|
||||
|
||||
```python
|
||||
from fastapi import FastAPI
|
||||
from pydantic import BaseModel
|
||||
from run_agent import AIAgent
|
||||
|
||||
app = FastAPI()
|
||||
|
||||
class ChatRequest(BaseModel):
|
||||
message: str
|
||||
model: str = "anthropic/claude-sonnet-4"
|
||||
|
||||
@app.post("/chat")
|
||||
async def chat(request: ChatRequest):
|
||||
agent = AIAgent(
|
||||
model=request.model,
|
||||
quiet_mode=True,
|
||||
skip_context_files=True,
|
||||
skip_memory=True,
|
||||
)
|
||||
response = agent.chat(request.message)
|
||||
return {"response": response}
|
||||
```
|
||||
|
||||
### Discord 机器人
|
||||
|
||||
```python
|
||||
import discord
|
||||
from run_agent import AIAgent
|
||||
|
||||
client = discord.Client(intents=discord.Intents.default())
|
||||
|
||||
@client.event
|
||||
async def on_message(message):
|
||||
if message.author == client.user:
|
||||
return
|
||||
if message.content.startswith("!hermes "):
|
||||
query = message.content[8:]
|
||||
agent = AIAgent(
|
||||
model="anthropic/claude-sonnet-4",
|
||||
quiet_mode=True,
|
||||
skip_context_files=True,
|
||||
skip_memory=True,
|
||||
platform="discord",
|
||||
)
|
||||
response = agent.chat(query)
|
||||
await message.channel.send(response[:2000])
|
||||
|
||||
client.run("YOUR_DISCORD_TOKEN")
|
||||
```
|
||||
|
||||
### CI/CD 流水线步骤
|
||||
|
||||
```python
|
||||
#!/usr/bin/env python3
|
||||
"""CI step: auto-review a PR diff."""
|
||||
import subprocess
|
||||
from run_agent import AIAgent
|
||||
|
||||
diff = subprocess.check_output(["git", "diff", "main...HEAD"]).decode()
|
||||
|
||||
agent = AIAgent(
|
||||
model="anthropic/claude-sonnet-4",
|
||||
quiet_mode=True,
|
||||
skip_context_files=True,
|
||||
skip_memory=True,
|
||||
disabled_toolsets=["terminal", "browser"],
|
||||
)
|
||||
|
||||
review = agent.chat(
|
||||
f"Review this PR diff for bugs, security issues, and style problems:\n\n{diff}"
|
||||
)
|
||||
print(review)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 关键构造函数参数
|
||||
|
||||
| 参数 | 类型 | 默认值 | 描述 |
|
||||
|-----------|------|---------|-------------|
|
||||
| `model` | `str` | `"anthropic/claude-opus-4.6"` | OpenRouter 格式的模型名称 |
|
||||
| `quiet_mode` | `bool` | `False` | 抑制 CLI 输出 |
|
||||
| `enabled_toolsets` | `List[str]` | `None` | 白名单指定工具集 |
|
||||
| `disabled_toolsets` | `List[str]` | `None` | 黑名单指定工具集 |
|
||||
| `save_trajectories` | `bool` | `False` | 将对话保存为 JSONL |
|
||||
| `ephemeral_system_prompt` | `str` | `None` | 自定义系统 prompt(不保存到轨迹文件) |
|
||||
| `max_iterations` | `int` | `90` | 每次对话的最大工具调用迭代次数 |
|
||||
| `skip_context_files` | `bool` | `False` | 跳过加载 AGENTS.md 文件 |
|
||||
| `skip_memory` | `bool` | `False` | 禁用持久化内存的读写 |
|
||||
| `api_key` | `str` | `None` | API 密钥(回退到环境变量) |
|
||||
| `base_url` | `str` | `None` | 自定义 API 端点 URL |
|
||||
| `platform` | `str` | `None` | 平台提示(`"discord"`、`"telegram"` 等) |
|
||||
|
||||
---
|
||||
|
||||
## 重要说明
|
||||
|
||||
:::tip
|
||||
- 如果不希望将工作目录中的 `AGENTS.md` 文件加载到系统 prompt 中,请设置 **`skip_context_files=True`**。
|
||||
- 设置 **`skip_memory=True`** 可阻止 agent 读写持久化内存——推荐用于无状态 API 端点。
|
||||
- `platform` 参数(如 `"discord"`、`"telegram"`)会注入平台特定的格式化提示,使 agent 适配其输出风格。
|
||||
:::
|
||||
|
||||
:::warning
|
||||
- **线程安全**:每个线程或任务创建一个 `AIAgent` 实例。切勿在并发调用中共享同一实例。
|
||||
- **资源清理**:agent 在对话结束时会自动清理资源(终端会话、浏览器实例)。若在长期运行的进程中使用,请确保每次对话正常结束。
|
||||
- **迭代限制**:默认的 `max_iterations=90` 较为宽松。对于简单的问答场景,建议适当降低该值(如 `max_iterations=10`),以防止工具调用循环失控并控制成本。
|
||||
:::
|
||||
@ -0,0 +1,273 @@
|
||||
---
|
||||
sidebar_position: 1
|
||||
title: "通过 Nous Portal 运行 Hermes Agent"
|
||||
description: "完整操作指南:订阅、配置、切换模型、启用 gateway 工具并验证路由"
|
||||
---
|
||||
|
||||
# 通过 Nous Portal 运行 Hermes Agent
|
||||
|
||||
本指南带你从头到尾完成在 [Nous Portal](https://portal.nousresearch.com) 订阅下运行 Hermes Agent 的全过程——从注册账号到验证每个工具的路由是否正确。如果你只想了解 Portal 的概述及订阅内容,请参阅 [Nous Portal 集成页面](/integrations/nous-portal)。本页是操作步骤脚本。
|
||||
|
||||
## 前提条件
|
||||
|
||||
- 已安装 Hermes Agent([快速入门](/getting-started/quickstart))
|
||||
- 在你正在配置的机器上有可用的浏览器(或 SSH 端口转发——参见 [OAuth over SSH](/guides/oauth-over-ssh))
|
||||
- 约 5 分钟时间
|
||||
|
||||
你**不需要**:OpenAI 密钥、Anthropic 密钥、Firecrawl 账号、FAL 账号、Browser Use 账号,或任何其他按供应商分配的凭证。这正是 Portal 的意义所在。
|
||||
|
||||
## 1. 获取订阅
|
||||
|
||||
打开 [portal.nousresearch.com/manage-subscription](https://portal.nousresearch.com/manage-subscription),注册并选择一个套餐。
|
||||
|
||||
已订阅?跳至第 2 步。
|
||||
|
||||
## 2. 运行一键配置
|
||||
|
||||
```bash
|
||||
hermes setup --portal
|
||||
```
|
||||
|
||||
这条命令会完成五件事:
|
||||
|
||||
1. 打开浏览器跳转至 portal.nousresearch.com 进行 OAuth 登录
|
||||
2. 将 refresh token 存储至 `~/.hermes/auth.json`
|
||||
3. 在 `~/.hermes/config.yaml` 中设置 `model.provider: nous`
|
||||
4. 选择一个默认的 agentic 模型(`anthropic/claude-sonnet-4.6` 或类似模型)
|
||||
5. 为网页搜索、图像生成、TTS 和浏览器自动化开启 Tool Gateway
|
||||
|
||||
命令执行完毕后,你将回到终端,可以直接开始对话。
|
||||
|
||||
### 如果我通过 SSH 连接到服务器怎么办?
|
||||
|
||||
OAuth 需要浏览器,但 loopback 回调运行在 Hermes 所在的机器上。有两种方案:
|
||||
|
||||
```bash
|
||||
# 方案 A:SSH 端口转发(推荐)
|
||||
ssh -N -L 8642:127.0.0.1:8642 user@remote-host # 在本地终端执行
|
||||
hermes setup --portal # 在远程机器上执行,在本地浏览器中打开打印出的 URL
|
||||
|
||||
# 方案 B:手动粘贴(适用于 Cloud Shell、Codespaces、EC2 Instance Connect)
|
||||
hermes auth add nous --type oauth --manual-paste
|
||||
# 然后重新运行 `hermes setup --portal` 以连接 provider + gateway
|
||||
```
|
||||
|
||||
完整操作说明(包括 ProxyJump 链、mosh/tmux 和 ControlMaster 注意事项)请参阅 [OAuth over SSH / 远程主机](/guides/oauth-over-ssh)。
|
||||
|
||||
## 3. 验证配置是否成功
|
||||
|
||||
```bash
|
||||
hermes portal status
|
||||
```
|
||||
|
||||
你应该看到:
|
||||
|
||||
```
|
||||
Nous Portal
|
||||
───────────
|
||||
Auth: ✓ logged in
|
||||
Portal: https://portal.nousresearch.com
|
||||
Model: ✓ using Nous as inference provider
|
||||
|
||||
Tool Gateway
|
||||
────────────
|
||||
Web search & extract via Nous Portal
|
||||
Image generation via Nous Portal
|
||||
Text-to-speech via Nous Portal
|
||||
Browser automation via Nous Portal
|
||||
```
|
||||
|
||||
如果任何一行显示的不是"via Nous Portal",或者 auth 行显示"not logged in",请跳至下方的[故障排查](#troubleshooting)。
|
||||
|
||||
## 4. 运行第一次对话
|
||||
|
||||
```bash
|
||||
hermes chat
|
||||
```
|
||||
|
||||
尝试一个同时调用模型和 Tool Gateway 的请求:
|
||||
|
||||
```
|
||||
Hey, search the web for "Hermes Agent release notes" and summarize the top 3 hits.
|
||||
```
|
||||
|
||||
你应该看到 Hermes 调用 `web_search`(通过 gateway 由 Firecrawl 提供支持)并返回摘要。如果搜索正常执行且响应内容合理,说明配置完成——Portal 已端到端连通。
|
||||
|
||||
## 5. 选择你实际需要的模型
|
||||
|
||||
`hermes setup --portal` 后的默认模型是一个合理的通用模型,但订阅的意义在于可以访问完整的模型目录。在会话中使用 `/model` 切换:
|
||||
|
||||
```bash
|
||||
/model anthropic/claude-sonnet-4.6 # 最佳通用 agentic 模型
|
||||
/model openai/gpt-5.4 # 强推理 + 工具调用
|
||||
/model google/gemini-2.5-pro # 超大上下文窗口
|
||||
/model deepseek/deepseek-v3.2 # 高性价比编程模型
|
||||
/model anthropic/claude-opus-4.6 # 处理复杂问题的重量级模型
|
||||
```
|
||||
|
||||
或者打开选择器浏览:
|
||||
|
||||
```bash
|
||||
/model
|
||||
```
|
||||
|
||||
永久设置不同的默认模型:
|
||||
|
||||
```bash
|
||||
# 在终端中,在任何会话之外执行
|
||||
hermes config set model.default anthropic/claude-sonnet-4.6
|
||||
```
|
||||
|
||||
### 不要在 agent 任务中使用 Hermes-4
|
||||
|
||||
Hermes-4-70B 和 Hermes-4-405B 在 Portal 上以大幅折扣提供,但它们是**对话/推理模型**,并非针对工具调用优化的模型。它们在多步骤 agent 循环中表现不佳。请通过 [Nous Chat](https://chat.nousresearch.com) 将它们用于对话/研究工作,或通过[订阅代理](/user-guide/features/subscription-proxy)从非 agent 工具中使用。对于 Hermes Agent 本身,请坚持使用上述前沿 agentic 模型。
|
||||
|
||||
Portal 的[信息页面](https://portal.nousresearch.com/info)也有此说明——这是 Nous 官方指导,并非仅代表 Hermes 一方的意见。
|
||||
|
||||
## 6. (可选)自定义 Tool Gateway 路由
|
||||
|
||||
gateway 是按工具选择启用的,而非全部开启或全部关闭。如果你已有 Browserbase 账号并希望继续使用,同时将网页搜索和图像生成路由至 Nous,这是支持的:
|
||||
|
||||
```bash
|
||||
hermes tools
|
||||
# → Web search → "Nous Subscription" (推荐)
|
||||
# → Image generation → "Nous Subscription" (推荐)
|
||||
# → Browser → "Browserbase" (你自己的密钥)
|
||||
# → TTS → "Nous Subscription" (推荐)
|
||||
```
|
||||
|
||||
使用以下命令验证你的混合配置:
|
||||
|
||||
```bash
|
||||
hermes portal tools
|
||||
```
|
||||
|
||||
你将看到每个工具的路由情况——通过订阅路由的工具显示 `via Nous Portal`,使用你自己密钥的工具显示合作方名称(`browserbase`、`firecrawl` 等)。
|
||||
|
||||
## 7. (可选)启用语音模式
|
||||
|
||||
由于 Tool Gateway 包含 OpenAI TTS,无需单独的 OpenAI 密钥即可使用[语音模式](/user-guide/features/voice-mode):
|
||||
|
||||
```bash
|
||||
hermes setup voice
|
||||
# → 为 TTS 选择 "Nous Subscription"
|
||||
# → 选择语音转文字后端(本地 faster-whisper 免费,无需配置)
|
||||
```
|
||||
|
||||
之后在任何消息平台会话中(Telegram、Discord、Signal 等),发送语音消息,Hermes 将转录内容、生成回复并以合成语音回复——全部通过你的 Portal 订阅完成。
|
||||
|
||||
## 8. (可选)Cron 定时任务与常驻工作流
|
||||
|
||||
Portal 订阅对 [cron 定时任务](/user-guide/features/cron)和[批处理](/user-guide/features/batch-processing)的支持方式与交互式对话相同——OAuth refresh token 会自动复用。无需额外配置,直接安排 cron 任务,费用将计入你的订阅。
|
||||
|
||||
```bash
|
||||
hermes cron add "Daily AI news summary" "every day at 9am" \
|
||||
"Search the web for top AI news and summarize the 5 most important stories"
|
||||
```
|
||||
|
||||
该 cron 任务无人值守运行,调用模型、网页搜索和摘要生成,全部通过你的 Portal 订阅完成。
|
||||
|
||||
## Profiles 与多用户配置
|
||||
|
||||
如果你使用 [Hermes profiles](/user-guide/profiles)(例如每个项目单独一套配置),Portal refresh token 会通过共享 token 存储自动在所有 profiles 之间共享。在任意 profile 上登录一次,其余 profiles 会自动获取。
|
||||
|
||||
对于多人共用一台机器的团队场景,每个人有自己的 Portal 账号 → 每个 home 目录保存各自的 `~/.hermes/auth.json` → 用户之间不共享 token。这是正确的边界划分。
|
||||
|
||||
## 故障排查
|
||||
|
||||
### 运行 `hermes setup --portal` 后,`hermes portal status` 显示"not logged in"
|
||||
|
||||
OAuth 流程未完成。重新运行:
|
||||
|
||||
```bash
|
||||
hermes auth add nous --type oauth
|
||||
```
|
||||
|
||||
如果浏览器未打开或回调失败,你可能在远程/无头主机上——参见 [OAuth over SSH](/guides/oauth-over-ssh) 了解端口转发和手动粘贴的解决方案。
|
||||
|
||||
### "Model: currently openrouter"(或其他 provider)而非"using Nous as inference provider"
|
||||
|
||||
本地配置发生了偏移。OAuth 成功,但 `model.provider` 仍指向其他 provider。修复方法:
|
||||
|
||||
```bash
|
||||
hermes config set model.provider nous
|
||||
```
|
||||
|
||||
或以交互方式:
|
||||
|
||||
```bash
|
||||
hermes model
|
||||
# 选择 Nous Portal
|
||||
```
|
||||
|
||||
使用 `hermes portal status` 重新验证。
|
||||
|
||||
### Tool Gateway 工具显示合作方名称而非"via Nous Portal"
|
||||
|
||||
按工具的配置覆盖了 gateway 设置。运行:
|
||||
|
||||
```bash
|
||||
hermes tools
|
||||
# 对需要通过 gateway 路由的工具选择 "Nous Subscription"
|
||||
```
|
||||
|
||||
部分用户会有意混合使用——例如网页搜索通过 Nous 路由,但浏览器使用自己的 Browserbase 密钥。如果这是有意为之,保持不变即可。如果不是,此命令可修复。
|
||||
|
||||
### 会话中途出现"Re-authentication required"
|
||||
|
||||
你的 Portal refresh token 已失效(密码更改、手动撤销、会话过期)。该 token 现已在本地被隔离,以防 Hermes 无限重试。重新登录即可:
|
||||
|
||||
```bash
|
||||
hermes auth add nous
|
||||
```
|
||||
|
||||
成功重新登录后,隔离状态会自动解除。
|
||||
|
||||
### 我想要的模型不在 `/model` 选择器中
|
||||
|
||||
Portal 目录镜像了 OpenRouter 的模型列表(300+ 个)。如果某个模型缺失,尝试直接输入 OpenRouter 风格的 slug:
|
||||
|
||||
```bash
|
||||
/model anthropic/claude-opus-4.6
|
||||
/model openai/o1-2025-12-17
|
||||
```
|
||||
|
||||
如果某个模型确实不可用,请[提交 issue](https://github.com/NousResearch/hermes-agent/issues)——大多数缺失是我们可以更新的路由配置问题。
|
||||
|
||||
### 账单未出现在我的 Portal 账号中
|
||||
|
||||
`hermes portal status` 会告诉你是否真的在通过 Portal 路由,还是使用了其他 provider。常见原因:
|
||||
|
||||
- `model.provider` 设置为 `openrouter`/`anthropic`/等,而非 `nous`
|
||||
- OAuth refresh 失败后回退到了其他已配置的 provider
|
||||
- 存在多个 Hermes profiles,你使用的是错误的那个(检查 `hermes profile current`)
|
||||
|
||||
### 想要撤销并重新开始
|
||||
|
||||
```bash
|
||||
hermes auth remove nous # 清除本地 refresh token
|
||||
# 然后重新运行 setup,或在 Portal 网页界面取消订阅
|
||||
```
|
||||
|
||||
## 用具体数字说明 Portal 的价值
|
||||
|
||||
| 不使用 Portal | 使用 Portal |
|
||||
|----------------|-------------|
|
||||
| 1 个 OpenRouter / Anthropic / OpenAI 密钥写入 `.env` | 1 个 OAuth refresh token,无需 `.env` 密钥 |
|
||||
| 1 个 Firecrawl 密钥用于网页搜索 | 网页搜索通过 gateway 路由 |
|
||||
| 1 个 FAL 密钥用于图像生成 | 图像生成通过 gateway 路由 |
|
||||
| 1 个 Browser Use / Browserbase 密钥用于浏览器 | 浏览器通过 gateway 路由 |
|
||||
| 1 个 OpenAI 密钥用于 TTS / 语音模式 | TTS 通过 gateway 路由 |
|
||||
| 5 个独立的控制台、充值、发票 | 1 个订阅,1 张发票 |
|
||||
| 跨机器:复制全部 5 个密钥 | 跨机器:重新 OAuth 一次 |
|
||||
|
||||
这就是 Portal 的价值。如果你本来就在使用其中两个以上的后端,订阅费用自然就回来了。
|
||||
|
||||
## 另请参阅
|
||||
|
||||
- **[Nous Portal 集成页面](/integrations/nous-portal)** — 订阅内容概述
|
||||
- **[Tool Gateway](/user-guide/features/tool-gateway)** — 每个 gateway 路由工具的完整说明
|
||||
- **[订阅代理](/user-guide/features/subscription-proxy)** — 在非 Hermes 工具中使用你的 Portal 订阅
|
||||
- **[语音模式](/user-guide/features/voice-mode)** — 在 Portal 订阅上配置语音对话
|
||||
- **[OAuth over SSH](/guides/oauth-over-ssh)** — 远程/无头主机登录方案
|
||||
- **[Profiles](/user-guide/profiles)** — 在多个 Hermes 配置之间共享一个 Portal 登录
|
||||
@ -0,0 +1,441 @@
|
||||
---
|
||||
sidebar_position: 4
|
||||
title: "教程:团队 Telegram 助手"
|
||||
description: "逐步指南:为整个团队搭建一个 Telegram 机器人,用于代码帮助、研究、系统管理等"
|
||||
---
|
||||
|
||||
# 搭建团队 Telegram 助手
|
||||
|
||||
本教程将引导你搭建一个由 Hermes Agent 驱动的 Telegram 机器人,供多名团队成员使用。完成后,你的团队将拥有一个共享 AI 助手,可以向它发消息寻求代码、研究、系统管理等方面的帮助——并通过按用户授权保障安全。
|
||||
|
||||
## 我们要构建什么
|
||||
|
||||
一个 Telegram 机器人,具备以下能力:
|
||||
|
||||
- **任何已授权的团队成员**都可以私信寻求帮助——代码审查、研究、Shell 命令、调试
|
||||
- **运行在你的服务器上**,拥有完整工具访问权限——终端、文件编辑、网络搜索、代码执行
|
||||
- **按用户会话隔离**——每个人拥有独立的对话上下文
|
||||
- **默认安全**——只有经过审批的用户才能交互,支持两种授权方式
|
||||
- **定时任务**——每日站会、健康检查和提醒推送到团队频道
|
||||
|
||||
---
|
||||
|
||||
## 前提条件
|
||||
|
||||
开始前,请确保你已具备:
|
||||
|
||||
- **已在服务器或 VPS 上安装 Hermes Agent**(不是你的笔记本——机器人需要持续运行)。如尚未安装,请参阅[安装指南](/getting-started/installation)。
|
||||
- **一个 Telegram 账号**(机器人所有者)
|
||||
- **已配置 LLM 提供商**——至少在 `~/.hermes/.env` 中配置了 OpenAI、Anthropic 或其他受支持提供商的 API 密钥
|
||||
|
||||
:::tip
|
||||
一台 $5/月的 VPS 足以运行 gateway(网关)。Hermes 本身很轻量——花钱的是 LLM API 调用,而那些调用发生在远端。
|
||||
:::
|
||||
|
||||
---
|
||||
|
||||
## 第一步:创建 Telegram 机器人
|
||||
|
||||
每个 Telegram 机器人都从 **@BotFather** 开始——这是 Telegram 官方用于创建机器人的机器人。
|
||||
|
||||
1. **打开 Telegram**,搜索 `@BotFather`,或访问 [t.me/BotFather](https://t.me/BotFather)
|
||||
|
||||
2. **发送 `/newbot`**——BotFather 会询问两件事:
|
||||
- **显示名称**——用户看到的名字(例如 `Team Hermes Assistant`)
|
||||
- **用户名**——必须以 `bot` 结尾(例如 `myteam_hermes_bot`)
|
||||
|
||||
3. **复制机器人 token**——BotFather 会回复类似内容:
|
||||
```
|
||||
Use this token to access the HTTP API:
|
||||
7123456789:AAH1bGciOiJSUzI1NiIsInR5cCI6Ikp...
|
||||
```
|
||||
保存此 token——下一步会用到。
|
||||
|
||||
4. **设置描述**(可选,但推荐):
|
||||
```
|
||||
/setdescription
|
||||
```
|
||||
选择你的机器人,然后输入类似内容:
|
||||
```
|
||||
Team AI assistant powered by Hermes Agent. DM me for help with code, research, debugging, and more.
|
||||
```
|
||||
|
||||
5. **设置机器人命令**(可选——为用户提供命令菜单):
|
||||
```
|
||||
/setcommands
|
||||
```
|
||||
选择你的机器人,然后粘贴:
|
||||
```
|
||||
new - Start a fresh conversation
|
||||
model - Show or change the AI model
|
||||
status - Show session info
|
||||
help - Show available commands
|
||||
stop - Stop the current task
|
||||
```
|
||||
|
||||
:::warning
|
||||
请妥善保管你的机器人 token。任何持有该 token 的人都可以控制机器人。如果泄露,请在 BotFather 中使用 `/revoke` 生成新 token。
|
||||
:::
|
||||
|
||||
---
|
||||
|
||||
## 第二步:配置 Gateway
|
||||
|
||||
你有两种选择:交互式设置向导(推荐)或手动配置。
|
||||
|
||||
### 方式 A:交互式设置(推荐)
|
||||
|
||||
```bash
|
||||
hermes gateway setup
|
||||
```
|
||||
|
||||
通过方向键选择完成所有配置。选择 **Telegram**,粘贴你的机器人 token,并在提示时输入你的用户 ID。
|
||||
|
||||
### 方式 B:手动配置
|
||||
|
||||
在 `~/.hermes/.env` 中添加以下内容:
|
||||
|
||||
```bash
|
||||
# Telegram bot token from BotFather
|
||||
TELEGRAM_BOT_TOKEN=7123456789:AAH1bGciOiJSUzI1NiIsInR5cCI6Ikp...
|
||||
|
||||
# Your Telegram user ID (numeric)
|
||||
TELEGRAM_ALLOWED_USERS=123456789
|
||||
```
|
||||
|
||||
### 查找你的用户 ID
|
||||
|
||||
你的 Telegram 用户 ID 是一个数字值(不是你的用户名)。查找方式:
|
||||
|
||||
1. 在 Telegram 上给 [@userinfobot](https://t.me/userinfobot) 发消息
|
||||
2. 它会立即回复你的数字用户 ID
|
||||
3. 将该数字填入 `TELEGRAM_ALLOWED_USERS`
|
||||
|
||||
:::info
|
||||
Telegram 用户 ID 是永久性数字,例如 `123456789`。它与可以更改的 `@username` 不同。白名单中请始终使用数字 ID。
|
||||
:::
|
||||
|
||||
---
|
||||
|
||||
## 第三步:启动 Gateway
|
||||
|
||||
### 快速测试
|
||||
|
||||
先在前台运行 gateway,确认一切正常:
|
||||
|
||||
```bash
|
||||
hermes gateway
|
||||
```
|
||||
|
||||
你应该看到类似输出:
|
||||
|
||||
```
|
||||
[Gateway] Starting Hermes Gateway...
|
||||
[Gateway] Telegram adapter connected
|
||||
[Gateway] Cron scheduler started (tick every 60s)
|
||||
```
|
||||
|
||||
打开 Telegram,找到你的机器人,发送一条消息。如果它回复了,说明一切正常。按 `Ctrl+C` 停止。
|
||||
|
||||
### 生产环境:安装为服务
|
||||
|
||||
若要持久部署并在重启后自动恢复:
|
||||
|
||||
```bash
|
||||
hermes gateway install
|
||||
sudo hermes gateway install --system # 仅 Linux:开机启动的系统服务
|
||||
```
|
||||
|
||||
这会创建一个后台服务:Linux 上默认为用户级 **systemd** 服务,macOS 上为 **launchd** 服务,传入 `--system` 则创建开机启动的 Linux 系统服务。
|
||||
|
||||
```bash
|
||||
# Linux——管理默认用户服务
|
||||
hermes gateway start
|
||||
hermes gateway stop
|
||||
hermes gateway status
|
||||
|
||||
# 查看实时日志
|
||||
journalctl --user -u hermes-gateway -f
|
||||
|
||||
# SSH 退出后保持运行
|
||||
sudo loginctl enable-linger $USER
|
||||
|
||||
# Linux 服务器——显式系统服务命令
|
||||
sudo hermes gateway start --system
|
||||
sudo hermes gateway status --system
|
||||
journalctl -u hermes-gateway -f
|
||||
```
|
||||
|
||||
```bash
|
||||
# macOS——管理服务
|
||||
hermes gateway start
|
||||
hermes gateway stop
|
||||
tail -f ~/.hermes/logs/gateway.log
|
||||
```
|
||||
|
||||
:::tip macOS PATH
|
||||
launchd plist 在安装时捕获你的 Shell PATH,以便 gateway 子进程能找到 Node.js 和 ffmpeg 等工具。如果之后安装了新工具,请重新运行 `hermes gateway install` 以更新 plist。
|
||||
:::
|
||||
|
||||
### 验证运行状态
|
||||
|
||||
```bash
|
||||
hermes gateway status
|
||||
```
|
||||
|
||||
然后在 Telegram 上向你的机器人发送测试消息。几秒内应收到回复。
|
||||
|
||||
---
|
||||
|
||||
## 第四步:设置团队访问权限
|
||||
|
||||
现在让你的队友获得访问权限。有两种方式。
|
||||
|
||||
### 方式 A:静态白名单
|
||||
|
||||
收集每位团队成员的 Telegram 用户 ID(让他们给 [@userinfobot](https://t.me/userinfobot) 发消息),然后以逗号分隔的列表形式添加:
|
||||
|
||||
```bash
|
||||
# 在 ~/.hermes/.env 中
|
||||
TELEGRAM_ALLOWED_USERS=123456789,987654321,555555555
|
||||
```
|
||||
|
||||
修改后重启 gateway:
|
||||
|
||||
```bash
|
||||
hermes gateway stop && hermes gateway start
|
||||
```
|
||||
|
||||
### 方式 B:私信配对(推荐用于团队)
|
||||
|
||||
私信配对更灵活——无需提前收集用户 ID。工作流程如下:
|
||||
|
||||
1. **队友私信机器人**——由于不在白名单中,机器人会回复一次性配对码:
|
||||
```
|
||||
🔐 Pairing code: XKGH5N7P
|
||||
Send this code to the bot owner for approval.
|
||||
```
|
||||
|
||||
2. **队友将配对码发给你**(通过任何渠道——Slack、邮件或当面)
|
||||
|
||||
3. **你在服务器上审批**:
|
||||
```bash
|
||||
hermes pairing approve telegram XKGH5N7P
|
||||
```
|
||||
|
||||
4. **他们即可使用**——机器人立即开始响应他们的消息
|
||||
|
||||
**管理已配对用户:**
|
||||
|
||||
```bash
|
||||
# 查看所有待审批和已审批用户
|
||||
hermes pairing list
|
||||
|
||||
# 撤销某人的访问权限
|
||||
hermes pairing revoke telegram 987654321
|
||||
|
||||
# 清除已过期的待审批码
|
||||
hermes pairing clear-pending
|
||||
```
|
||||
|
||||
:::tip
|
||||
私信配对非常适合团队使用,因为添加新用户时无需重启 gateway。审批立即生效。
|
||||
:::
|
||||
|
||||
### 安全注意事项
|
||||
|
||||
- **切勿在拥有终端访问权限的机器人上设置 `GATEWAY_ALLOW_ALL_USERS=true`**——任何找到你机器人的人都可能在你的服务器上执行命令
|
||||
- 配对码在 **1 小时**后过期,并使用密码学随机数生成
|
||||
- 速率限制防止暴力破解:每用户每 10 分钟 1 次请求,每平台最多 3 个待审批码
|
||||
- 5 次审批失败后,该平台进入 1 小时锁定状态
|
||||
- 所有配对数据以 `chmod 0600` 权限存储
|
||||
|
||||
---
|
||||
|
||||
## 第五步:配置机器人
|
||||
|
||||
### 设置主频道
|
||||
|
||||
**主频道**是机器人投递 cron 任务结果和主动消息的地方。没有主频道,定时任务将无处发送输出。
|
||||
|
||||
**方式 1:** 在机器人所在的任意 Telegram 群组或聊天中使用 `/sethome` 命令。
|
||||
|
||||
**方式 2:** 在 `~/.hermes/.env` 中手动设置:
|
||||
|
||||
```bash
|
||||
TELEGRAM_HOME_CHANNEL=-1001234567890
|
||||
TELEGRAM_HOME_CHANNEL_NAME="Team Updates"
|
||||
```
|
||||
|
||||
要查找频道 ID,可将 [@userinfobot](https://t.me/userinfobot) 添加到群组——它会报告该群组的聊天 ID。
|
||||
|
||||
### 配置工具进度显示
|
||||
|
||||
控制机器人在使用工具时显示的详细程度。在 `~/.hermes/config.yaml` 中:
|
||||
|
||||
```yaml
|
||||
display:
|
||||
tool_progress: new # off | new | all | verbose
|
||||
```
|
||||
|
||||
| 模式 | 显示内容 |
|
||||
|------|-------------|
|
||||
| `off` | 仅显示干净的回复——无工具活动 |
|
||||
| `new` | 每次新工具调用的简短状态(推荐用于消息场景) |
|
||||
| `all` | 每次工具调用及其详情 |
|
||||
| `verbose` | 完整工具输出,包括命令结果 |
|
||||
|
||||
用户也可以在聊天中使用 `/verbose` 命令按会话更改此设置。
|
||||
|
||||
### 使用 SOUL.md 设置个性
|
||||
|
||||
通过编辑 `~/.hermes/SOUL.md` 自定义机器人的沟通方式:
|
||||
|
||||
完整指南请参阅[在 Hermes 中使用 SOUL.md](/guides/use-soul-with-hermes)。
|
||||
|
||||
```markdown
|
||||
# Soul
|
||||
You are a helpful team assistant. Be concise and technical.
|
||||
Use code blocks for any code. Skip pleasantries — the team
|
||||
values directness. When debugging, always ask for error logs
|
||||
before guessing at solutions.
|
||||
```
|
||||
|
||||
### 添加项目上下文
|
||||
|
||||
如果你的团队在特定项目上工作,可以创建上下文文件,让机器人了解你们的技术栈:
|
||||
|
||||
```markdown
|
||||
<!-- ~/.hermes/AGENTS.md -->
|
||||
# Team Context
|
||||
- We use Python 3.12 with FastAPI and SQLAlchemy
|
||||
- Frontend is React with TypeScript
|
||||
- CI/CD runs on GitHub Actions
|
||||
- Production deploys to AWS ECS
|
||||
- Always suggest writing tests for new code
|
||||
```
|
||||
|
||||
:::info
|
||||
上下文文件会注入到每个会话的系统 prompt(提示词)中。请保持简洁——每个字符都会占用你的 token 预算。
|
||||
:::
|
||||
|
||||
---
|
||||
|
||||
## 第六步:设置定时任务
|
||||
|
||||
gateway 运行后,你可以安排定期任务,将结果投递到团队频道。
|
||||
|
||||
### 每日站会摘要
|
||||
|
||||
在 Telegram 上给机器人发消息:
|
||||
|
||||
```
|
||||
Every weekday at 9am, check the GitHub repository at
|
||||
github.com/myorg/myproject for:
|
||||
1. Pull requests opened/merged in the last 24 hours
|
||||
2. Issues created or closed
|
||||
3. Any CI/CD failures on the main branch
|
||||
Format as a brief standup-style summary.
|
||||
```
|
||||
|
||||
Agent 会自动创建一个 cron 任务,并将结果投递到你提问的聊天(或主频道)。
|
||||
|
||||
### 服务器健康检查
|
||||
|
||||
```
|
||||
Every 6 hours, check disk usage with 'df -h', memory with 'free -h',
|
||||
and Docker container status with 'docker ps'. Report anything unusual —
|
||||
partitions above 80%, containers that have restarted, or high memory usage.
|
||||
```
|
||||
|
||||
### 管理定时任务
|
||||
|
||||
```bash
|
||||
# 通过 CLI
|
||||
hermes cron list # 查看所有定时任务
|
||||
hermes cron status # 检查调度器是否运行
|
||||
|
||||
# 通过 Telegram 聊天
|
||||
/cron list # 查看任务
|
||||
/cron remove <job_id> # 删除任务
|
||||
```
|
||||
|
||||
:::warning
|
||||
Cron 任务的 prompt 在完全全新的会话中运行,不保留任何先前对话的记忆。请确保每个 prompt 包含 agent 所需的**全部**上下文——文件路径、URL、服务器地址以及清晰的指令。
|
||||
:::
|
||||
|
||||
---
|
||||
|
||||
## 生产环境建议
|
||||
|
||||
### 使用 Docker 保障安全
|
||||
|
||||
在共享团队机器人上,使用 Docker 作为终端后端,让 agent 命令在容器中运行,而非直接在宿主机上运行:
|
||||
|
||||
```bash
|
||||
# 在 ~/.hermes/.env 中
|
||||
TERMINAL_BACKEND=docker
|
||||
TERMINAL_DOCKER_IMAGE=nikolaik/python-nodejs:python3.11-nodejs20
|
||||
```
|
||||
|
||||
或在 `~/.hermes/config.yaml` 中:
|
||||
|
||||
```yaml
|
||||
terminal:
|
||||
backend: docker
|
||||
container_cpu: 1
|
||||
container_memory: 5120
|
||||
container_persistent: true
|
||||
```
|
||||
|
||||
这样即使有人要求机器人执行破坏性操作,你的宿主系统也受到保护。
|
||||
|
||||
### 监控 Gateway
|
||||
|
||||
```bash
|
||||
# 检查 gateway 是否运行
|
||||
hermes gateway status
|
||||
|
||||
# 查看实时日志(Linux)
|
||||
journalctl --user -u hermes-gateway -f
|
||||
|
||||
# 查看实时日志(macOS)
|
||||
tail -f ~/.hermes/logs/gateway.log
|
||||
```
|
||||
|
||||
### 保持 Hermes 更新
|
||||
|
||||
在 Telegram 中向机器人发送 `/update`——它会拉取最新版本并重启。或在服务器上执行:
|
||||
|
||||
```bash
|
||||
hermes update
|
||||
hermes gateway stop && hermes gateway start
|
||||
```
|
||||
|
||||
### 日志位置
|
||||
|
||||
| 内容 | 位置 |
|
||||
|------|----------|
|
||||
| Gateway 日志 | `journalctl --user -u hermes-gateway`(Linux)或 `~/.hermes/logs/gateway.log`(macOS) |
|
||||
| Cron 任务输出 | `~/.hermes/cron/output/{job_id}/{timestamp}.md` |
|
||||
| Cron 任务定义 | `~/.hermes/cron/jobs.json` |
|
||||
| 配对数据 | `~/.hermes/pairing/` |
|
||||
| 会话历史 | `~/.hermes/sessions/` |
|
||||
|
||||
---
|
||||
|
||||
## 进一步探索
|
||||
|
||||
你已经拥有一个可用的团队 Telegram 助手。以下是一些后续步骤:
|
||||
|
||||
- **[安全指南](/user-guide/security)**——深入了解授权、容器隔离和命令审批
|
||||
- **[消息 Gateway](/user-guide/messaging)**——gateway 架构、会话管理和聊天命令的完整参考
|
||||
- **[Telegram 设置](/user-guide/messaging/telegram)**——平台专属详情,包括语音消息和 TTS
|
||||
- **[定时任务](/user-guide/features/cron)**——高级 cron 调度,含投递选项和 cron 表达式
|
||||
- **[上下文文件](/user-guide/features/context-files)**——用于项目知识的 AGENTS.md、SOUL.md 和 .cursorrules
|
||||
- **[个性设置](/user-guide/features/personality)**——内置个性预设和自定义角色定义
|
||||
- **添加更多平台**——同一 gateway 可同时运行 [Discord](/user-guide/messaging/discord)、[Slack](/user-guide/messaging/slack) 和 [WhatsApp](/user-guide/messaging/whatsapp)
|
||||
|
||||
---
|
||||
|
||||
*有问题或遇到问题?请在 GitHub 上提 issue——欢迎贡献。*
|
||||
@ -0,0 +1,234 @@
|
||||
---
|
||||
sidebar_position: 1
|
||||
title: "技巧与最佳实践"
|
||||
description: "充分发挥 Hermes Agent 潜力的实用建议——prompt 技巧、CLI 快捷键、上下文文件、记忆、成本优化与安全"
|
||||
---
|
||||
|
||||
# 技巧与最佳实践
|
||||
|
||||
一份实用技巧速查集,帮助你立即提升使用 Hermes Agent 的效率。每个章节针对不同方面——扫描标题,直接跳到相关内容。
|
||||
|
||||
---
|
||||
|
||||
## 获得最佳结果
|
||||
|
||||
### 明确说明你的需求
|
||||
|
||||
模糊的 prompt(提示词)只会产生模糊的结果。不要说"修复代码",而要说"修复 `api/handlers.py` 第 47 行的 TypeError——`process_request()` 函数从 `parse_body()` 收到了 `None`。"给出的上下文越多,所需的迭代次数就越少。
|
||||
|
||||
### 预先提供上下文
|
||||
|
||||
在请求开头就给出相关细节:文件路径、错误信息、预期行为。一条精心构造的消息胜过三轮来回确认。直接粘贴错误堆栈——agent 能够解析它们。
|
||||
|
||||
### 使用上下文文件处理重复指令
|
||||
|
||||
如果你发现自己在反复输入相同的指令("用 tab 而非空格"、"我们用 pytest"、"API 地址是 `/api/v2`"),把它们放进 `AGENTS.md` 文件。agent 每次会话都会自动读取它——设置一次,永久生效。
|
||||
|
||||
### 让 Agent 使用它的工具
|
||||
|
||||
不要试图手把手指导每一步。说"找到并修复失败的测试",而不是"打开 `tests/test_foo.py`,看第 42 行,然后……"。agent 拥有文件搜索、终端访问和代码执行能力——让它自行探索和迭代。
|
||||
|
||||
### 对复杂工作流使用 Skill
|
||||
|
||||
在写一大段 prompt 解释如何做某件事之前,先检查是否已有对应的 skill。输入 `/skills` 浏览可用的 skill,或直接调用,例如 `/axolotl` 或 `/github-pr-workflow`。
|
||||
|
||||
## CLI 高级用户技巧
|
||||
|
||||
### 多行输入
|
||||
|
||||
按 **Alt+Enter**、**Ctrl+J** 或 **Shift+Enter** 可插入换行而不发送消息。`Shift+Enter` 仅在终端将其作为独立按键发送时有效(Kitty / foot / WezTerm / Ghostty 默认支持;iTerm2 / Alacritty / VS Code 终端需启用 Kitty 键盘协议)。另外两种方式在所有终端中均可使用。
|
||||
|
||||
### 粘贴检测
|
||||
|
||||
CLI 会自动检测多行粘贴。直接粘贴代码块或错误堆栈——不会将每行作为单独消息发送。粘贴内容会被缓冲后作为一条消息发送。
|
||||
|
||||
### 中断与重定向
|
||||
|
||||
按一次 **Ctrl+C** 可中断 agent 的响应过程,然后输入新消息重新引导它。在 2 秒内双击 Ctrl+C 可强制退出。当 agent 开始走错方向时,这个功能非常有用。
|
||||
|
||||
### 使用 `-c` 恢复会话
|
||||
|
||||
上次会话有遗漏?运行 `hermes -c` 可精确恢复到上次离开的位置,完整对话历史全部还原。也可以按标题恢复:`hermes -r "my research project"`。
|
||||
|
||||
### 剪贴板图片粘贴
|
||||
|
||||
按 **Ctrl+V** 可将剪贴板中的图片直接粘贴到对话中。agent 会使用视觉能力分析截图、图表、错误弹窗或 UI 原型——无需先保存为文件。
|
||||
|
||||
### Slash 命令自动补全
|
||||
|
||||
输入 `/` 后按 **Tab** 可查看所有可用命令,包括内置命令(`/compress`、`/model`、`/title`)和所有已安装的 skill。无需记忆任何内容——Tab 补全全部搞定。
|
||||
|
||||
:::tip
|
||||
使用 `/verbose` 循环切换工具输出显示模式:**off → new → all → verbose**。"all" 模式非常适合观察 agent 的操作过程;"off" 模式在简单问答时最为简洁。
|
||||
:::
|
||||
|
||||
## 上下文文件
|
||||
|
||||
### AGENTS.md:你的项目大脑
|
||||
|
||||
在项目根目录创建 `AGENTS.md`,写入架构决策、编码规范和项目专属指令。该文件会自动注入每次会话,让 agent 始终了解你的项目规则。
|
||||
|
||||
```markdown
|
||||
# Project Context
|
||||
- This is a FastAPI backend with SQLAlchemy ORM
|
||||
- Always use async/await for database operations
|
||||
- Tests go in tests/ and use pytest-asyncio
|
||||
- Never commit .env files
|
||||
```
|
||||
|
||||
### SOUL.md:自定义个性
|
||||
|
||||
想让 Hermes 拥有稳定的默认风格?编辑 `~/.hermes/SOUL.md`(如果使用自定义 Hermes home,则为 `$HERMES_HOME/SOUL.md`)。Hermes 现在会自动生成一个初始 SOUL 文件,并将该全局文件作为实例级个性来源。
|
||||
|
||||
完整说明请参阅 [在 Hermes 中使用 SOUL.md](/guides/use-soul-with-hermes)。
|
||||
|
||||
```markdown
|
||||
# Soul
|
||||
You are a senior backend engineer. Be terse and direct.
|
||||
Skip explanations unless asked. Prefer one-liners over verbose solutions.
|
||||
Always consider error handling and edge cases.
|
||||
```
|
||||
|
||||
使用 `SOUL.md` 设置持久个性,使用 `AGENTS.md` 设置项目专属指令。
|
||||
|
||||
### .cursorrules 兼容性
|
||||
|
||||
已有 `.cursorrules` 或 `.cursor/rules/*.mdc` 文件?Hermes 同样会读取它们。无需重复编写编码规范——这些文件会从工作目录自动加载。
|
||||
|
||||
### 发现机制
|
||||
|
||||
Hermes 在会话启动时从当前工作目录加载顶层 `AGENTS.md`。子目录中的 `AGENTS.md` 文件在工具调用期间通过 `subdirectory_hints.py` 延迟发现,并注入工具结果——不会在启动时预先加载到系统 prompt 中。
|
||||
|
||||
:::tip
|
||||
保持上下文文件简洁聚焦。每个字符都会消耗 token 配额,因为它们会注入到每一条消息中。
|
||||
:::
|
||||
|
||||
## 记忆与 Skill
|
||||
|
||||
### 记忆 vs. Skill:各司其职
|
||||
|
||||
**记忆(Memory)** 用于存储事实:你的环境、偏好、项目位置,以及 agent 了解到的关于你的信息。**Skill** 用于存储流程:多步骤工作流、特定工具的操作指南和可复用的操作方案。记忆存"是什么",skill 存"怎么做"。
|
||||
|
||||
### 何时创建 Skill
|
||||
|
||||
如果某个任务需要 5 步以上且你会重复执行,就让 agent 为它创建一个 skill。说"把你刚才做的保存为名为 `deploy-staging` 的 skill"。下次只需输入 `/deploy-staging`,agent 就会加载完整流程。
|
||||
|
||||
### 管理记忆容量
|
||||
|
||||
记忆容量是有意限制的(`MEMORY.md` 约 2,200 字符,`USER.md` 约 1,375 字符)。当记忆填满时,agent 会自动整合条目。你也可以主动说"清理你的记忆"或"替换旧的 Python 3.9 备注——我们现在用 3.12 了"。
|
||||
|
||||
### 让 Agent 记住内容
|
||||
|
||||
在一次高效的会话结束后,说"记住这些以备下次使用",agent 会保存关键要点。也可以具体指定:"保存到记忆中,我们的 CI 使用 GitHub Actions 的 `deploy.yml` 工作流。"
|
||||
|
||||
:::warning
|
||||
记忆是一个冻结的快照——会话期间的修改不会出现在系统 prompt 中,直到下一次会话开始。agent 会立即写入磁盘,但 prompt 缓存在会话中途不会失效。
|
||||
:::
|
||||
|
||||
## 性能与成本
|
||||
|
||||
### 不要破坏 Prompt 缓存
|
||||
|
||||
大多数 LLM 提供商会缓存系统 prompt 前缀。如果你保持系统 prompt 稳定(相同的上下文文件、相同的记忆),同一会话中的后续消息会命中**缓存**,成本显著降低。避免在会话中途切换模型或修改系统 prompt。
|
||||
|
||||
### 在达到限制前使用 /compress
|
||||
|
||||
长会话会积累大量 token。当你发现响应变慢或被截断时,运行 `/compress`。这会对对话历史进行摘要,在大幅减少 token 数量的同时保留关键上下文。使用 `/usage` 查看当前用量。
|
||||
|
||||
### 使用委托实现并行工作
|
||||
|
||||
需要同时研究三个主题?让 agent 使用 `delegate_task` 并行分配子任务。每个子 agent 独立运行,拥有各自的上下文,最终只有摘要结果返回——大幅减少主对话的 token 消耗。
|
||||
|
||||
### 使用 execute_code 进行批量操作
|
||||
|
||||
不要逐条运行终端命令,而是让 agent 编写一个脚本一次性完成所有操作。"写一个 Python 脚本把所有 `.jpeg` 文件重命名为 `.jpg` 并运行它"比逐个重命名文件更省钱、更快速。
|
||||
|
||||
### 选择合适的模型
|
||||
|
||||
使用 `/model` 在会话中途切换模型。对于复杂推理和架构决策,使用前沿模型(Claude Sonnet/Opus、GPT-4o);对于格式化、重命名或样板代码生成等简单任务,切换到更快的模型。
|
||||
|
||||
:::tip
|
||||
定期运行 `/usage` 查看 token 消耗情况。运行 `/insights` 可查看过去 30 天的用量模式概览。
|
||||
:::
|
||||
|
||||
## 消息技巧
|
||||
|
||||
### 设置主频道
|
||||
|
||||
在你偏好的 Telegram 或 Discord 聊天中使用 `/sethome`,将其指定为主频道。定时任务结果和计划任务输出会发送到这里。没有主频道,agent 就没有地方发送主动消息。
|
||||
|
||||
### 使用 /title 整理会话
|
||||
|
||||
用 `/title auth-refactor` 或 `/title research-llm-quantization` 为会话命名。命名后的会话可通过 `hermes sessions list` 轻松找到,并用 `hermes -r "auth-refactor"` 恢复。未命名的会话会堆积起来,难以区分。
|
||||
|
||||
### DM 配对实现团队访问
|
||||
|
||||
不要手动收集用户 ID 来维护白名单,而是启用 DM 配对。当团队成员向 bot 发送私信时,他们会收到一次性配对码。你用 `hermes pairing approve telegram XKGH5N7P` 批准即可——简单且安全。
|
||||
|
||||
### 工具进度显示模式
|
||||
|
||||
使用 `/verbose` 控制工具活动的显示详细程度。在消息平台上,通常越简洁越好——保持"new"模式只查看新的工具调用。在 CLI 中,"all" 模式可以实时查看 agent 的所有操作。
|
||||
|
||||
:::tip
|
||||
在消息平台上,会话会在空闲一段时间后自动重置(默认 24 小时),或每天凌晨 4 点重置。如需更长的会话时间,可在 `~/.hermes/config.yaml` 中按平台调整。
|
||||
:::
|
||||
|
||||
## 安全
|
||||
|
||||
### 对不可信代码使用 Docker
|
||||
|
||||
在处理不可信仓库或运行陌生代码时,使用 Docker 或 Daytona 作为终端后端。在 `.env` 中设置 `TERMINAL_BACKEND=docker`。容器内的破坏性命令不会影响宿主系统。
|
||||
|
||||
```bash
|
||||
# In your .env:
|
||||
TERMINAL_BACKEND=docker
|
||||
TERMINAL_DOCKER_IMAGE=hermes-sandbox:latest
|
||||
```
|
||||
|
||||
### 避免 Windows 编码陷阱
|
||||
|
||||
在 Windows 上,某些默认编码(如 `cp125x`)无法表示所有 Unicode 字符,在测试或脚本中写入文件时可能导致 `UnicodeEncodeError`。
|
||||
|
||||
- 建议在打开文件时显式指定 UTF-8 编码:
|
||||
|
||||
```python
|
||||
with open("results.txt", "w", encoding="utf-8") as f:
|
||||
f.write("✓ All good\n")
|
||||
```
|
||||
|
||||
- 在 PowerShell 中,也可以将当前会话的控制台和原生命令输出切换为 UTF-8:
|
||||
|
||||
```powershell
|
||||
$OutputEncoding = [Console]::OutputEncoding = [Text.UTF8Encoding]::new($false)
|
||||
```
|
||||
|
||||
这样可以让 PowerShell 和子进程统一使用 UTF-8,避免仅在 Windows 上出现的失败。
|
||||
|
||||
### 谨慎选择"始终允许"
|
||||
|
||||
当 agent 触发危险命令审批(`rm -rf`、`DROP TABLE` 等)时,你有四个选项:**once(仅此一次)**、**session(本次会话)**、**always(始终允许)**、**deny(拒绝)**。选择"always"前请仔细考虑——它会永久将该模式加入白名单。在熟悉之前,先用"session"。
|
||||
|
||||
### 命令审批是你的安全防线
|
||||
|
||||
Hermes 在执行每条命令前都会与一份精心维护的危险模式列表进行比对,包括递归删除、SQL DROP、curl 管道到 shell 等。不要在生产环境中禁用此功能——它的存在有充分的理由。
|
||||
|
||||
:::warning
|
||||
在容器后端(Docker、Singularity、Modal、Daytona)中运行时,危险命令检查会被**跳过**,因为容器本身就是安全边界。请确保你的容器镜像已妥善加固。
|
||||
:::
|
||||
|
||||
### 为消息 Bot 使用白名单
|
||||
|
||||
永远不要在拥有终端访问权限的 bot 上设置 `GATEWAY_ALLOW_ALL_USERS=true`。始终使用平台专属白名单(`TELEGRAM_ALLOWED_USERS`、`DISCORD_ALLOWED_USERS`)或 DM 配对来控制谁可以与你的 agent 交互。
|
||||
|
||||
```bash
|
||||
# Recommended: explicit allowlists per platform
|
||||
TELEGRAM_ALLOWED_USERS=123456789,987654321
|
||||
DISCORD_ALLOWED_USERS=123456789012345678
|
||||
|
||||
# Or use cross-platform allowlist
|
||||
GATEWAY_ALLOWED_USERS=123456789,987654321
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
*有值得收录的技巧?欢迎提交 issue 或 PR——社区贡献随时欢迎。*
|
||||
@ -0,0 +1,490 @@
|
||||
---
|
||||
sidebar_position: 6
|
||||
title: "在 Hermes 中使用 MCP"
|
||||
description: "将 MCP 服务器连接到 Hermes Agent、过滤其工具并在实际工作流中安全使用的实践指南"
|
||||
---
|
||||
|
||||
# 在 Hermes 中使用 MCP
|
||||
|
||||
本指南介绍如何在日常工作流中实际使用 Hermes Agent 的 MCP 功能。
|
||||
|
||||
如果功能页面解释的是 MCP 是什么,本指南则关注如何快速、安全地从中获取价值。
|
||||
|
||||
## 何时应该使用 MCP?
|
||||
|
||||
在以下情况下使用 MCP:
|
||||
- 工具已以 MCP 形式存在,且你不想构建原生 Hermes 工具
|
||||
- 你希望 Hermes 通过干净的 RPC 层操作本地或远程系统
|
||||
- 你需要细粒度的按服务器暴露控制
|
||||
- 你希望将 Hermes 连接到内部 API、数据库或公司系统,而无需修改 Hermes 核心
|
||||
|
||||
在以下情况下不要使用 MCP:
|
||||
- 内置 Hermes 工具已能很好地完成该工作
|
||||
- 服务器暴露了大量危险工具,而你没有准备好对其进行过滤
|
||||
- 你只需要一个非常窄的集成,原生工具会更简单、更安全
|
||||
|
||||
## 心智模型
|
||||
|
||||
将 MCP 视为一个适配器层:
|
||||
|
||||
- Hermes 仍然是 agent
|
||||
- MCP 服务器提供工具
|
||||
- Hermes 在启动或重新加载时发现这些工具
|
||||
- 模型可以像使用普通工具一样使用它们
|
||||
- 你控制每个服务器有多少内容可见
|
||||
|
||||
最后一点很重要。良好的 MCP 使用不是"连接一切",而是"以最小的有效范围连接正确的东西"。
|
||||
|
||||
## 第一步:安装 MCP 支持
|
||||
|
||||
如果你使用标准安装脚本安装了 Hermes,MCP 支持已包含在内(安装程序会运行 `uv pip install -e ".[all]"`)。
|
||||
|
||||
如果你在没有附加组件的情况下安装,需要单独添加 MCP:
|
||||
|
||||
```bash
|
||||
cd ~/.hermes/hermes-agent
|
||||
uv pip install -e ".[mcp]"
|
||||
```
|
||||
|
||||
对于基于 npm 的服务器,请确保 Node.js 和 `npx` 可用。
|
||||
|
||||
对于许多 Python MCP 服务器,`uvx` 是一个不错的默认选择。
|
||||
|
||||
## 第二步:先添加一个服务器
|
||||
|
||||
从单个、安全的服务器开始。
|
||||
|
||||
示例:仅访问一个项目目录的文件系统。
|
||||
|
||||
```yaml
|
||||
mcp_servers:
|
||||
project_fs:
|
||||
command: "npx"
|
||||
args: ["-y", "@modelcontextprotocol/server-filesystem", "/home/user/my-project"]
|
||||
```
|
||||
|
||||
然后启动 Hermes:
|
||||
|
||||
```bash
|
||||
hermes chat
|
||||
```
|
||||
|
||||
现在提出一个具体问题:
|
||||
|
||||
```text
|
||||
Inspect this project and summarize the repo layout.
|
||||
```
|
||||
|
||||
## 第三步:验证 MCP 已加载
|
||||
|
||||
你可以通过以下几种方式验证 MCP:
|
||||
|
||||
- 配置后 Hermes 横幅/状态应显示 MCP 集成
|
||||
- 询问 Hermes 当前有哪些可用工具
|
||||
- 配置更改后使用 `/reload-mcp`
|
||||
- 如果服务器连接失败,检查日志
|
||||
|
||||
一个实用的测试 prompt(提示词):
|
||||
|
||||
```text
|
||||
Tell me which MCP-backed tools are available right now.
|
||||
```
|
||||
|
||||
## 第四步:立即开始过滤
|
||||
|
||||
如果服务器暴露了大量工具,不要等到以后再过滤。
|
||||
|
||||
### 示例:仅白名单你需要的内容
|
||||
|
||||
```yaml
|
||||
mcp_servers:
|
||||
github:
|
||||
command: "npx"
|
||||
args: ["-y", "@modelcontextprotocol/server-github"]
|
||||
env:
|
||||
GITHUB_PERSONAL_ACCESS_TOKEN: "***"
|
||||
tools:
|
||||
include: [list_issues, create_issue, search_code]
|
||||
```
|
||||
|
||||
对于敏感系统,这通常是最佳默认设置。
|
||||
|
||||
## WSL2:将 WSL 中的 Hermes 桥接到 Windows Chrome
|
||||
|
||||
以下是适用场景的实际配置:
|
||||
|
||||
- Hermes 在 WSL2 内运行
|
||||
- 你想控制的浏览器是 Windows 上已登录的普通 Chrome
|
||||
- 从 WSL 使用 `/browser connect` 不稳定或不可靠
|
||||
|
||||
在此配置中,Hermes **不**直接连接到 Chrome,而是:
|
||||
|
||||
- Hermes 在 WSL 中运行
|
||||
- Hermes 启动一个本地 stdio MCP 服务器
|
||||
- 该 MCP 服务器通过 Windows 互操作(`cmd.exe` 或 `powershell.exe`)启动
|
||||
- MCP 服务器附加到你的实时 Windows Chrome 会话
|
||||
|
||||
心智模型:
|
||||
|
||||
```text
|
||||
Hermes (WSL) -> MCP stdio bridge -> Windows Chrome
|
||||
```
|
||||
|
||||
### 为什么此模式有用
|
||||
|
||||
- 你保留真实的 Windows 浏览器配置文件、Cookie 和登录状态
|
||||
- Hermes 保持在其支持的 Unix 环境(WSL2)中
|
||||
- 浏览器控制以 MCP 工具的形式暴露,而不依赖 Hermes 核心浏览器传输
|
||||
|
||||
### 推荐服务器
|
||||
|
||||
使用 `chrome-devtools-mcp`。
|
||||
|
||||
如果你的 Windows Chrome 已通过 `chrome://inspect/#remote-debugging` 启用了实时远程调试,在 WSL 中按如下方式添加:
|
||||
|
||||
```bash
|
||||
hermes mcp add chrome-devtools-win --command cmd.exe --args /c npx -y chrome-devtools-mcp@latest --autoConnect --no-usage-statistics
|
||||
```
|
||||
|
||||
保存服务器后:
|
||||
|
||||
```bash
|
||||
hermes mcp test chrome-devtools-win
|
||||
```
|
||||
|
||||
然后启动一个新的 Hermes 会话或运行:
|
||||
|
||||
```text
|
||||
/reload-mcp
|
||||
```
|
||||
|
||||
### 典型 prompt
|
||||
|
||||
加载后,Hermes 可以直接使用带 MCP 前缀的浏览器工具。例如:
|
||||
|
||||
```text
|
||||
调用 MCP 工具 mcp_chrome_devtools_win_list_pages,列出当前浏览器标签页。
|
||||
```
|
||||
|
||||
### 何时 `/browser connect` 不适用
|
||||
|
||||
如果 Hermes 在 WSL 中运行而 Chrome 在 Windows 上运行,即使 Chrome 已打开且可调试,`/browser connect` 也可能失败。
|
||||
|
||||
常见原因:
|
||||
|
||||
- WSL 无法访问 Chrome 向 Windows 工具暴露的同一主机本地端点
|
||||
- 较新的 Chrome 实时调试流程与经典的 `ws://localhost:9222` 不同
|
||||
- 从 Windows 端辅助工具(如 `chrome-devtools-mcp`)附加浏览器更容易
|
||||
|
||||
在这些情况下,将 `/browser connect` 用于同环境配置,使用 MCP 进行 WSL 到 Windows 的浏览器桥接。
|
||||
|
||||
### 已知问题
|
||||
|
||||
- 通过 MCP 使用 Windows stdio 可执行文件时,从 `/mnt/c/Users/<you>` 或 `/mnt/c/workspace/...` 等 Windows 挂载路径启动 Hermes。
|
||||
- 如果从 `/root` 或 `/home/...` 启动 Hermes,Windows 可能在 MCP 服务器启动前发出 `UNC` 当前目录警告。
|
||||
- 如果 `chrome-devtools-mcp --autoConnect` 在枚举页面时超时,请减少 Chrome 中的后台/冻结标签页并重试。
|
||||
|
||||
### 示例:黑名单危险操作
|
||||
|
||||
```yaml
|
||||
mcp_servers:
|
||||
stripe:
|
||||
url: "https://mcp.stripe.com"
|
||||
headers:
|
||||
Authorization: "Bearer ***"
|
||||
tools:
|
||||
exclude: [delete_customer, refund_payment]
|
||||
```
|
||||
|
||||
### 示例:同时禁用实用工具包装器
|
||||
|
||||
```yaml
|
||||
mcp_servers:
|
||||
docs:
|
||||
url: "https://mcp.docs.example.com"
|
||||
tools:
|
||||
prompts: false
|
||||
resources: false
|
||||
```
|
||||
|
||||
## 过滤实际影响什么?
|
||||
|
||||
Hermes 中 MCP 暴露的功能分为两类:
|
||||
|
||||
1. 服务器原生 MCP 工具
|
||||
- 通过以下方式过滤:
|
||||
- `tools.include`
|
||||
- `tools.exclude`
|
||||
|
||||
2. Hermes 添加的实用工具包装器
|
||||
- 通过以下方式过滤:
|
||||
- `tools.resources`
|
||||
- `tools.prompts`
|
||||
|
||||
### 你可能看到的实用工具包装器
|
||||
|
||||
Resources(资源):
|
||||
- `list_resources`
|
||||
- `read_resource`
|
||||
|
||||
Prompts(提示词):
|
||||
- `list_prompts`
|
||||
- `get_prompt`
|
||||
|
||||
这些包装器仅在以下情况下出现:
|
||||
- 你的配置允许它们,且
|
||||
- MCP 服务器会话实际支持这些能力
|
||||
|
||||
因此,如果服务器不支持 resources/prompts,Hermes 不会假装它支持。
|
||||
|
||||
## 常见模式
|
||||
|
||||
### 模式 1:本地项目助手
|
||||
|
||||
当你希望 Hermes 在有界工作区内推理时,使用 MCP 连接仓库本地的文件系统或 git 服务器。
|
||||
|
||||
```yaml
|
||||
mcp_servers:
|
||||
fs:
|
||||
command: "npx"
|
||||
args: ["-y", "@modelcontextprotocol/server-filesystem", "/home/user/project"]
|
||||
|
||||
git:
|
||||
command: "uvx"
|
||||
args: ["mcp-server-git", "--repository", "/home/user/project"]
|
||||
```
|
||||
|
||||
好的 prompt:
|
||||
|
||||
```text
|
||||
Review the project structure and identify where configuration lives.
|
||||
```
|
||||
|
||||
```text
|
||||
Check the local git state and summarize what changed recently.
|
||||
```
|
||||
|
||||
### 模式 2:GitHub 分类助手
|
||||
|
||||
```yaml
|
||||
mcp_servers:
|
||||
github:
|
||||
command: "npx"
|
||||
args: ["-y", "@modelcontextprotocol/server-github"]
|
||||
env:
|
||||
GITHUB_PERSONAL_ACCESS_TOKEN: "***"
|
||||
tools:
|
||||
include: [list_issues, create_issue, update_issue, search_code]
|
||||
prompts: false
|
||||
resources: false
|
||||
```
|
||||
|
||||
好的 prompt:
|
||||
|
||||
```text
|
||||
List open issues about MCP, cluster them by theme, and draft a high-quality issue for the most common bug.
|
||||
```
|
||||
|
||||
```text
|
||||
Search the repo for uses of _discover_and_register_server and explain how MCP tools are registered.
|
||||
```
|
||||
|
||||
### 模式 3:内部 API 助手
|
||||
|
||||
```yaml
|
||||
mcp_servers:
|
||||
internal_api:
|
||||
url: "https://mcp.internal.example.com"
|
||||
headers:
|
||||
Authorization: "Bearer ***"
|
||||
tools:
|
||||
include: [list_customers, get_customer, list_invoices]
|
||||
resources: false
|
||||
prompts: false
|
||||
```
|
||||
|
||||
好的 prompt:
|
||||
|
||||
```text
|
||||
Look up customer ACME Corp and summarize recent invoice activity.
|
||||
```
|
||||
|
||||
在这类场景中,严格的白名单远优于排除列表。
|
||||
|
||||
### 模式 4:文档/知识服务器
|
||||
|
||||
某些 MCP 服务器暴露的 prompts 或 resources 更像是共享知识资产,而非直接操作。
|
||||
|
||||
```yaml
|
||||
mcp_servers:
|
||||
docs:
|
||||
url: "https://mcp.docs.example.com"
|
||||
tools:
|
||||
prompts: true
|
||||
resources: true
|
||||
```
|
||||
|
||||
好的 prompt:
|
||||
|
||||
```text
|
||||
List available MCP resources from the docs server, then read the onboarding guide and summarize it.
|
||||
```
|
||||
|
||||
```text
|
||||
List prompts exposed by the docs server and tell me which ones would help with incident response.
|
||||
```
|
||||
|
||||
## 教程:带过滤的端到端配置
|
||||
|
||||
以下是一个实际的渐进式流程。
|
||||
|
||||
### 阶段 1:使用严格白名单添加 GitHub MCP
|
||||
|
||||
```yaml
|
||||
mcp_servers:
|
||||
github:
|
||||
command: "npx"
|
||||
args: ["-y", "@modelcontextprotocol/server-github"]
|
||||
env:
|
||||
GITHUB_PERSONAL_ACCESS_TOKEN: "***"
|
||||
tools:
|
||||
include: [list_issues, create_issue, search_code]
|
||||
prompts: false
|
||||
resources: false
|
||||
```
|
||||
|
||||
启动 Hermes 并询问:
|
||||
|
||||
```text
|
||||
Search the codebase for references to MCP and summarize the main integration points.
|
||||
```
|
||||
|
||||
### 阶段 2:仅在需要时扩展
|
||||
|
||||
如果之后还需要更新 issue:
|
||||
|
||||
```yaml
|
||||
tools:
|
||||
include: [list_issues, create_issue, update_issue, search_code]
|
||||
```
|
||||
|
||||
然后重新加载:
|
||||
|
||||
```text
|
||||
/reload-mcp
|
||||
```
|
||||
|
||||
### 阶段 3:添加具有不同策略的第二个服务器
|
||||
|
||||
```yaml
|
||||
mcp_servers:
|
||||
github:
|
||||
command: "npx"
|
||||
args: ["-y", "@modelcontextprotocol/server-github"]
|
||||
env:
|
||||
GITHUB_PERSONAL_ACCESS_TOKEN: "***"
|
||||
tools:
|
||||
include: [list_issues, create_issue, update_issue, search_code]
|
||||
prompts: false
|
||||
resources: false
|
||||
|
||||
filesystem:
|
||||
command: "npx"
|
||||
args: ["-y", "@modelcontextprotocol/server-filesystem", "/home/user/project"]
|
||||
```
|
||||
|
||||
现在 Hermes 可以组合使用它们:
|
||||
|
||||
```text
|
||||
Inspect the local project files, then create a GitHub issue summarizing the bug you find.
|
||||
```
|
||||
|
||||
这就是 MCP 的强大之处:无需修改 Hermes 核心即可实现多系统工作流。
|
||||
|
||||
## 安全使用建议
|
||||
|
||||
### 对危险系统优先使用白名单
|
||||
|
||||
对于任何涉及财务、面向客户或具有破坏性的系统:
|
||||
- 使用 `tools.include`
|
||||
- 从尽可能小的集合开始
|
||||
|
||||
### 禁用未使用的实用工具
|
||||
|
||||
如果你不希望模型浏览服务器提供的 resources/prompts,请将其关闭:
|
||||
|
||||
```yaml
|
||||
tools:
|
||||
resources: false
|
||||
prompts: false
|
||||
```
|
||||
|
||||
### 保持服务器范围狭窄
|
||||
|
||||
示例:
|
||||
- 文件系统服务器根目录指向一个项目目录,而非整个主目录
|
||||
- git 服务器指向一个仓库
|
||||
- 内部 API 服务器默认以读取为主的工具暴露
|
||||
|
||||
### 配置更改后重新加载
|
||||
|
||||
```text
|
||||
/reload-mcp
|
||||
```
|
||||
|
||||
在更改以下内容后执行此操作:
|
||||
- include/exclude 列表
|
||||
- enabled 标志
|
||||
- resources/prompts 开关
|
||||
- 认证 header / env
|
||||
|
||||
## 按症状排查问题
|
||||
|
||||
### "服务器已连接,但我期望的工具不见了"
|
||||
|
||||
可能原因:
|
||||
- 被 `tools.include` 过滤
|
||||
- 被 `tools.exclude` 排除
|
||||
- 实用工具包装器通过 `resources: false` 或 `prompts: false` 禁用
|
||||
- 服务器实际上不支持 resources/prompts
|
||||
|
||||
### "服务器已配置,但什么都没加载"
|
||||
|
||||
检查:
|
||||
- 配置中是否遗留了 `enabled: false`
|
||||
- 命令/运行时是否存在(`npx`、`uvx` 等)
|
||||
- HTTP 端点是否可达
|
||||
- 认证 env 或 header 是否正确
|
||||
|
||||
### "为什么我看到的工具比 MCP 服务器公告的少?"
|
||||
|
||||
因为 Hermes 现在遵守你的按服务器策略和能力感知注册。这是预期行为,通常也是期望的结果。
|
||||
|
||||
### "如何在不删除配置的情况下移除 MCP 服务器?"
|
||||
|
||||
使用:
|
||||
|
||||
```yaml
|
||||
enabled: false
|
||||
```
|
||||
|
||||
这会保留配置,但阻止连接和注册。
|
||||
|
||||
## 推荐的首批 MCP 配置
|
||||
|
||||
适合大多数用户的首选服务器:
|
||||
- filesystem
|
||||
- git
|
||||
- GitHub
|
||||
- fetch / 文档 MCP 服务器
|
||||
- 一个范围窄的内部 API
|
||||
|
||||
不适合作为首选的服务器:
|
||||
- 具有大量破坏性操作且未经过滤的大型业务系统
|
||||
- 任何你不够了解、无法加以约束的系统
|
||||
|
||||
## 相关文档
|
||||
|
||||
- [MCP(模型上下文协议)](/user-guide/features/mcp)
|
||||
- [FAQ](/reference/faq)
|
||||
- [斜杠命令](/reference/slash-commands)
|
||||
@ -0,0 +1,264 @@
|
||||
---
|
||||
sidebar_position: 7
|
||||
title: "在 Hermes 中使用 SOUL.md"
|
||||
description: "如何使用 SOUL.md 塑造 Hermes Agent 的默认风格,哪些内容应放在其中,以及它与 AGENTS.md 和 /personality 的区别"
|
||||
---
|
||||
|
||||
# 在 Hermes 中使用 SOUL.md
|
||||
|
||||
`SOUL.md` 是你的 Hermes 实例的**主要身份标识**。它是系统提示词(system prompt)中的第一项内容——定义了 Agent 是谁、如何表达,以及应避免什么。
|
||||
|
||||
如果你希望每次与 Hermes 交谈时都感受到一致的助手风格,或者想用自己的角色完全替换 Hermes 的默认人设,这就是你需要编辑的文件。
|
||||
|
||||
## SOUL.md 的用途
|
||||
|
||||
`SOUL.md` 适用于:
|
||||
- 语气
|
||||
- 个性
|
||||
- 沟通风格
|
||||
- Hermes 应有多直接或多温和
|
||||
- Hermes 在风格上应避免什么
|
||||
- Hermes 如何应对不确定性、分歧和模糊情况
|
||||
|
||||
简而言之:
|
||||
- `SOUL.md` 关注的是 Hermes 是谁,以及 Hermes 如何表达
|
||||
|
||||
## SOUL.md 不适用的内容
|
||||
|
||||
不要在其中放置:
|
||||
- 特定代码仓库的编码规范
|
||||
- 文件路径
|
||||
- 命令
|
||||
- 服务端口
|
||||
- 架构说明
|
||||
- 项目工作流指令
|
||||
|
||||
这些内容属于 `AGENTS.md`。
|
||||
|
||||
一个简单的判断原则:
|
||||
- 如果某项内容应在所有地方生效,放入 `SOUL.md`
|
||||
- 如果某项内容只属于某个项目,放入 `AGENTS.md`
|
||||
|
||||
## 文件位置
|
||||
|
||||
Hermes 目前仅使用当前实例的全局 SOUL 文件:
|
||||
|
||||
```text
|
||||
~/.hermes/SOUL.md
|
||||
```
|
||||
|
||||
如果你使用自定义主目录运行 Hermes,路径变为:
|
||||
|
||||
```text
|
||||
$HERMES_HOME/SOUL.md
|
||||
```
|
||||
|
||||
## 首次运行行为
|
||||
|
||||
如果 `SOUL.md` 尚不存在,Hermes 会自动为你生成一个初始文件。
|
||||
|
||||
这意味着大多数用户一开始就有一个可以立即阅读和编辑的真实文件。
|
||||
|
||||
注意:
|
||||
- 如果你已有 `SOUL.md`,Hermes 不会覆盖它
|
||||
- 如果文件存在但为空,Hermes 不会从中向提示词添加任何内容
|
||||
|
||||
## Hermes 如何使用它
|
||||
|
||||
Hermes 启动会话时,会从 `HERMES_HOME` 读取 `SOUL.md`,扫描其中的提示词注入(prompt-injection)模式,必要时进行截断,并将其作为 **Agent 身份标识**——系统提示词中的第 1 个槽位。这意味着 `SOUL.md` 会完全替换内置的默认身份文本。
|
||||
|
||||
如果 `SOUL.md` 缺失、为空或无法加载,Hermes 将回退到内置的默认身份。
|
||||
|
||||
文件内容不会被任何包装语言包裹。内容本身才是关键——按照你希望 Agent 思考和表达的方式来写。
|
||||
|
||||
## 第一次编辑建议
|
||||
|
||||
如果你只做一件事,打开文件并修改几行,让它感觉像你自己的风格。
|
||||
|
||||
例如:
|
||||
|
||||
```markdown
|
||||
You are direct, calm, and technically precise.
|
||||
Prefer substance over politeness theater.
|
||||
Push back clearly when an idea is weak.
|
||||
Keep answers compact unless deeper detail is useful.
|
||||
```
|
||||
|
||||
仅此一项就能明显改变 Hermes 的感觉。
|
||||
|
||||
## 示例风格
|
||||
|
||||
### 1. 务实工程师
|
||||
|
||||
```markdown
|
||||
You are a pragmatic senior engineer.
|
||||
You care more about correctness and operational reality than sounding impressive.
|
||||
|
||||
## Style
|
||||
- Be direct
|
||||
- Be concise unless complexity requires depth
|
||||
- Say when something is a bad idea
|
||||
- Prefer practical tradeoffs over idealized abstractions
|
||||
|
||||
## Avoid
|
||||
- Sycophancy
|
||||
- Hype language
|
||||
- Overexplaining obvious things
|
||||
```
|
||||
|
||||
### 2. 研究伙伴
|
||||
|
||||
```markdown
|
||||
You are a thoughtful research collaborator.
|
||||
You are curious, honest about uncertainty, and excited by unusual ideas.
|
||||
|
||||
## Style
|
||||
- Explore possibilities without pretending certainty
|
||||
- Distinguish speculation from evidence
|
||||
- Ask clarifying questions when the idea space is underspecified
|
||||
- Prefer conceptual depth over shallow completeness
|
||||
```
|
||||
|
||||
### 3. 教师/讲解者
|
||||
|
||||
```markdown
|
||||
You are a patient technical teacher.
|
||||
You care about understanding, not performance.
|
||||
|
||||
## Style
|
||||
- Explain clearly
|
||||
- Use examples when they help
|
||||
- Do not assume prior knowledge unless the user signals it
|
||||
- Build from intuition to details
|
||||
```
|
||||
|
||||
### 4. 严格审阅者
|
||||
|
||||
```markdown
|
||||
You are a rigorous reviewer.
|
||||
You are fair, but you do not soften important criticism.
|
||||
|
||||
## Style
|
||||
- Point out weak assumptions directly
|
||||
- Prioritize correctness over harmony
|
||||
- Be explicit about risks and tradeoffs
|
||||
- Prefer blunt clarity to vague diplomacy
|
||||
```
|
||||
|
||||
## 什么是优质的 SOUL.md?
|
||||
|
||||
优质的 `SOUL.md` 具备以下特点:
|
||||
- 稳定
|
||||
- 广泛适用
|
||||
- 风格具体
|
||||
- 不堆砌临时指令
|
||||
|
||||
劣质的 `SOUL.md` 则是:
|
||||
- 充斥项目细节
|
||||
- 自相矛盾
|
||||
- 试图微观管理每一个回复的形式
|
||||
- 大量泛泛之词,如"要有帮助"和"要清晰"
|
||||
|
||||
Hermes 本身已经尽力做到有帮助且清晰。`SOUL.md` 应当赋予真实的个性和风格,而不是重申显而易见的默认行为。
|
||||
|
||||
## 建议结构
|
||||
|
||||
不需要标题,但标题有助于组织内容。
|
||||
|
||||
一个实用的简单结构:
|
||||
|
||||
```markdown
|
||||
# Identity
|
||||
Who Hermes is.
|
||||
|
||||
# Style
|
||||
How Hermes should sound.
|
||||
|
||||
# Avoid
|
||||
What Hermes should not do.
|
||||
|
||||
# Defaults
|
||||
How Hermes should behave when ambiguity appears.
|
||||
```
|
||||
|
||||
## SOUL.md 与 /personality 的区别
|
||||
|
||||
两者互为补充。
|
||||
|
||||
使用 `SOUL.md` 作为持久的基础设定。
|
||||
使用 `/personality` 进行临时的模式切换。
|
||||
|
||||
示例:
|
||||
- 你的默认 SOUL 是务实且直接的
|
||||
- 某次会话中你使用 `/personality teacher`
|
||||
- 之后切换回来,无需修改基础风格文件
|
||||
|
||||
## SOUL.md 与 AGENTS.md 的区别
|
||||
|
||||
这是最常见的误用。
|
||||
|
||||
### 放入 SOUL.md 的内容
|
||||
- "Be direct."
|
||||
- "Avoid hype language."
|
||||
- "Prefer short answers unless depth helps."
|
||||
- "Push back when the user is wrong."
|
||||
|
||||
### 放入 AGENTS.md 的内容
|
||||
- "Use pytest, not unittest."
|
||||
- "Frontend lives in `frontend/`."
|
||||
- "Never edit migrations directly."
|
||||
- "The API runs on port 8000."
|
||||
|
||||
## 如何编辑
|
||||
|
||||
```bash
|
||||
nano ~/.hermes/SOUL.md
|
||||
```
|
||||
|
||||
或
|
||||
|
||||
```bash
|
||||
vim ~/.hermes/SOUL.md
|
||||
```
|
||||
|
||||
然后重启 Hermes 或开启新会话。
|
||||
|
||||
## 实用工作流
|
||||
|
||||
1. 从自动生成的默认文件开始
|
||||
2. 删除不符合你期望风格的内容
|
||||
3. 添加 4–8 行清晰定义语气和默认行为的文字
|
||||
4. 与 Hermes 交谈一段时间
|
||||
5. 根据仍感觉不对的地方进行调整
|
||||
|
||||
这种迭代方式比一次性设计完美人设更有效。
|
||||
|
||||
## 故障排查
|
||||
|
||||
### 我编辑了 SOUL.md,但 Hermes 听起来还是一样
|
||||
|
||||
检查:
|
||||
- 你编辑的是 `~/.hermes/SOUL.md` 或 `$HERMES_HOME/SOUL.md`
|
||||
- 而不是某个仓库本地的 `SOUL.md`
|
||||
- 文件不为空
|
||||
- 编辑后已重启会话
|
||||
- 没有 `/personality` 覆盖层主导了结果
|
||||
|
||||
### Hermes 忽略了我 SOUL.md 中的部分内容
|
||||
|
||||
可能原因:
|
||||
- 更高优先级的指令覆盖了它
|
||||
- 文件中包含相互冲突的指导内容
|
||||
- 文件过长被截断
|
||||
- 部分文本类似提示词注入内容,可能被扫描器拦截或修改
|
||||
|
||||
### 我的 SOUL.md 变得过于项目化
|
||||
|
||||
将项目指令移入 `AGENTS.md`,保持 `SOUL.md` 专注于身份标识和风格。
|
||||
|
||||
## 相关文档
|
||||
|
||||
- [个性与 SOUL.md](/user-guide/features/personality)
|
||||
- [上下文文件](/user-guide/features/context-files)
|
||||
- [配置](/user-guide/configuration)
|
||||
- [技巧与最佳实践](/guides/tips)
|
||||
@ -0,0 +1,456 @@
|
||||
---
|
||||
sidebar_position: 8
|
||||
title: "在 Hermes 中使用语音模式"
|
||||
description: "在 CLI、Telegram、Discord 及 Discord 语音频道中设置和使用 Hermes 语音模式的实用指南"
|
||||
---
|
||||
|
||||
# 在 Hermes 中使用语音模式
|
||||
|
||||
本指南是[语音模式功能参考](/user-guide/features/voice-mode)的实用配套文档。
|
||||
|
||||
功能页面介绍语音模式能做什么,本指南则说明如何真正用好它。
|
||||
|
||||
## 语音模式适合哪些场景
|
||||
|
||||
语音模式在以下情况特别有用:
|
||||
- 需要免手持的 CLI 工作流
|
||||
- 希望在 Telegram 或 Discord 中获得语音回复
|
||||
- 希望 Hermes 加入 Discord 语音频道进行实时对话
|
||||
- 边走动边快速记录想法、调试问题或来回交流,而不是打字
|
||||
|
||||
## 选择你的语音模式方案
|
||||
|
||||
Hermes 中实际上有三种不同的语音体验。
|
||||
|
||||
| 模式 | 最适合 | 平台 |
|
||||
|---|---|---|
|
||||
| 交互式麦克风循环 | 编码或研究时的个人免手持使用 | CLI |
|
||||
| 聊天中的语音回复 | 在正常消息旁附带语音回复 | Telegram、Discord |
|
||||
| 实时语音频道机器人 | 在语音频道中进行群组或个人实时对话 | Discord 语音频道 |
|
||||
|
||||
推荐路径:
|
||||
1. 先让文本模式正常工作
|
||||
2. 再启用语音回复
|
||||
3. 最后如需完整体验,再切换到 Discord 语音频道
|
||||
|
||||
## 第一步:确保普通 Hermes 先正常运行
|
||||
|
||||
在接触语音模式之前,请确认:
|
||||
- Hermes 能正常启动
|
||||
- 已配置好 provider(提供商)
|
||||
- Agent 能正常回答文本 prompt(提示词)
|
||||
|
||||
```bash
|
||||
hermes
|
||||
```
|
||||
|
||||
问一个简单的问题:
|
||||
|
||||
```text
|
||||
What tools do you have available?
|
||||
```
|
||||
|
||||
如果文本模式还不稳定,请先修复它。
|
||||
|
||||
## 第二步:安装所需的额外依赖
|
||||
|
||||
### CLI 麦克风 + 播放
|
||||
|
||||
```bash
|
||||
pip install "hermes-agent[voice]"
|
||||
```
|
||||
|
||||
### 消息平台
|
||||
|
||||
```bash
|
||||
pip install "hermes-agent[messaging]"
|
||||
```
|
||||
|
||||
### 高级 ElevenLabs TTS
|
||||
|
||||
```bash
|
||||
pip install "hermes-agent[tts-premium]"
|
||||
```
|
||||
|
||||
### 本地 NeuTTS(可选)
|
||||
|
||||
```bash
|
||||
python -m pip install -U neutts[all]
|
||||
```
|
||||
|
||||
### 全部安装
|
||||
|
||||
```bash
|
||||
pip install "hermes-agent[all]"
|
||||
```
|
||||
|
||||
## 第三步:安装系统依赖
|
||||
|
||||
### macOS
|
||||
|
||||
```bash
|
||||
brew install portaudio ffmpeg opus
|
||||
brew install espeak-ng
|
||||
```
|
||||
|
||||
### Ubuntu / Debian
|
||||
|
||||
```bash
|
||||
sudo apt install portaudio19-dev ffmpeg libopus0
|
||||
sudo apt install espeak-ng
|
||||
```
|
||||
|
||||
各依赖的作用:
|
||||
- `portaudio` → CLI 语音模式的麦克风输入与播放
|
||||
- `ffmpeg` → TTS 和消息传递的音频转换
|
||||
- `opus` → Discord 语音编解码器支持
|
||||
- `espeak-ng` → NeuTTS 的 phonemizer 后端
|
||||
|
||||
## 第四步:选择 STT 和 TTS 提供商
|
||||
|
||||
Hermes 同时支持本地和云端语音处理方案。
|
||||
|
||||
### 最简单 / 最低成本的方案
|
||||
|
||||
使用本地 STT 和免费的 Edge TTS:
|
||||
- STT provider:`local`
|
||||
- TTS provider:`edge`
|
||||
|
||||
这通常是最好的起点。
|
||||
|
||||
### 环境变量文件示例
|
||||
|
||||
添加到 `~/.hermes/.env`:
|
||||
|
||||
```bash
|
||||
# 云端 STT 选项(本地无需密钥)
|
||||
GROQ_API_KEY=***
|
||||
VOICE_TOOLS_OPENAI_KEY=***
|
||||
|
||||
# 高级 TTS(可选)
|
||||
ELEVENLABS_API_KEY=***
|
||||
```
|
||||
|
||||
### Provider 推荐
|
||||
|
||||
#### 语音转文字(STT)
|
||||
|
||||
- `local` → 隐私保护和零成本使用的最佳默认选项
|
||||
- `groq` → 极快的云端转录
|
||||
- `openai` → 良好的付费备选
|
||||
|
||||
#### 文字转语音(TTS)
|
||||
|
||||
- `edge` → 免费,对大多数用户已足够
|
||||
- `neutts` → 免费的本地/设备端 TTS
|
||||
- `elevenlabs` → 最佳质量
|
||||
- `openai` → 良好的中间选项
|
||||
- `mistral` → 多语言,原生 Opus
|
||||
|
||||
### 如果使用 `hermes setup`
|
||||
|
||||
如果你在设置向导中选择了 NeuTTS,Hermes 会检查 `neutts` 是否已安装。如果缺失,向导会告知你 NeuTTS 需要 Python 包 `neutts` 和系统包 `espeak-ng`,并提供自动安装,使用平台包管理器安装 `espeak-ng`,然后运行:
|
||||
|
||||
```bash
|
||||
python -m pip install -U neutts[all]
|
||||
```
|
||||
|
||||
如果跳过安装或安装失败,向导会回退到 Edge TTS。
|
||||
|
||||
## 第五步:推荐配置
|
||||
|
||||
```yaml
|
||||
voice:
|
||||
record_key: "ctrl+b"
|
||||
max_recording_seconds: 120
|
||||
auto_tts: false
|
||||
beep_enabled: true
|
||||
silence_threshold: 200
|
||||
silence_duration: 3.0
|
||||
|
||||
stt:
|
||||
provider: "local"
|
||||
local:
|
||||
model: "base"
|
||||
|
||||
tts:
|
||||
provider: "edge"
|
||||
edge:
|
||||
voice: "en-US-AriaNeural"
|
||||
```
|
||||
|
||||
这是适合大多数人的保守默认配置。
|
||||
|
||||
如果想改用本地 TTS,将 `tts` 块替换为:
|
||||
|
||||
```yaml
|
||||
tts:
|
||||
provider: "neutts"
|
||||
neutts:
|
||||
ref_audio: ''
|
||||
ref_text: ''
|
||||
model: neuphonic/neutts-air-q4-gguf
|
||||
device: cpu
|
||||
```
|
||||
|
||||
## 使用场景一:CLI 语音模式
|
||||
|
||||
## 开启方式
|
||||
|
||||
启动 Hermes:
|
||||
|
||||
```bash
|
||||
hermes
|
||||
```
|
||||
|
||||
在 CLI 内执行:
|
||||
|
||||
```text
|
||||
/voice on
|
||||
```
|
||||
|
||||
### 录音流程
|
||||
|
||||
默认按键:
|
||||
- `Ctrl+B`
|
||||
|
||||
工作流程:
|
||||
1. 按下 `Ctrl+B`
|
||||
2. 说话
|
||||
3. 等待静音检测自动停止录音
|
||||
4. Hermes 转录并回复
|
||||
5. 如果开启了 TTS,它会朗读答案
|
||||
6. 循环可自动重启以持续使用
|
||||
|
||||
### 常用命令
|
||||
|
||||
```text
|
||||
/voice
|
||||
/voice on
|
||||
/voice off
|
||||
/voice tts
|
||||
/voice status
|
||||
```
|
||||
|
||||
### 推荐的 CLI 工作流
|
||||
|
||||
#### 随走随调试
|
||||
|
||||
说:
|
||||
|
||||
```text
|
||||
I keep getting a docker permission error. Help me debug it.
|
||||
```
|
||||
|
||||
然后继续免手持操作:
|
||||
- "再读一遍最后的错误"
|
||||
- "用更简单的语言解释根本原因"
|
||||
- "现在给我精确的修复方案"
|
||||
|
||||
#### 研究 / 头脑风暴
|
||||
|
||||
非常适合:
|
||||
- 边走动边思考
|
||||
- 口述半成形的想法
|
||||
- 让 Hermes 实时整理你的思路
|
||||
|
||||
#### 无障碍 / 少打字场景
|
||||
|
||||
如果打字不方便,语音模式是保持完整 Hermes 工作流的最快方式之一。
|
||||
|
||||
## 调整 CLI 行为
|
||||
|
||||
### 静音阈值
|
||||
|
||||
如果 Hermes 开始/停止过于激进,调整:
|
||||
|
||||
```yaml
|
||||
voice:
|
||||
silence_threshold: 250
|
||||
```
|
||||
|
||||
阈值越高 = 灵敏度越低。
|
||||
|
||||
### 静音时长
|
||||
|
||||
如果你在句子之间经常停顿,增大该值:
|
||||
|
||||
```yaml
|
||||
voice:
|
||||
silence_duration: 4.0
|
||||
```
|
||||
|
||||
### 录音按键
|
||||
|
||||
如果 `Ctrl+B` 与你的终端或 tmux 习惯冲突:
|
||||
|
||||
```yaml
|
||||
voice:
|
||||
record_key: "ctrl+space"
|
||||
```
|
||||
|
||||
## 使用场景二:Telegram 或 Discord 中的语音回复
|
||||
|
||||
此模式比完整语音频道更简单。
|
||||
|
||||
Hermes 仍作为普通聊天机器人运行,但可以朗读回复。
|
||||
|
||||
### 启动 gateway
|
||||
|
||||
```bash
|
||||
hermes gateway
|
||||
```
|
||||
|
||||
### 开启语音回复
|
||||
|
||||
在 Telegram 或 Discord 中:
|
||||
|
||||
```text
|
||||
/voice on
|
||||
```
|
||||
|
||||
或
|
||||
|
||||
```text
|
||||
/voice tts
|
||||
```
|
||||
|
||||
### 模式说明
|
||||
|
||||
| 模式 | 含义 |
|
||||
|---|---|
|
||||
| `off` | 仅文本 |
|
||||
| `voice_only` | 仅当用户发送语音时才朗读 |
|
||||
| `all` | 朗读每条回复 |
|
||||
|
||||
### 何时使用哪种模式
|
||||
|
||||
- `/voice on`:仅对语音来源的消息给出语音回复
|
||||
- `/voice tts`:始终作为完整语音助手运行
|
||||
|
||||
### 推荐的消息平台工作流
|
||||
|
||||
#### 手机上的 Telegram 助手
|
||||
|
||||
适用于:
|
||||
- 离开电脑时
|
||||
- 发送语音备忘并获取快速语音回复
|
||||
- 希望 Hermes 充当便携式研究或运维助手
|
||||
|
||||
#### Discord 私信中的语音输出
|
||||
|
||||
适用于希望私密交互、避免服务器频道 @mention 行为的场景。
|
||||
|
||||
## 使用场景三:Discord 语音频道
|
||||
|
||||
这是最高级的模式。
|
||||
|
||||
Hermes 加入 Discord 语音频道(VC),监听用户语音,转录后运行正常的 agent 流水线,并将回复朗读回频道。
|
||||
|
||||
## 所需的 Discord 权限
|
||||
|
||||
除了普通文本机器人设置外,请确保机器人拥有:
|
||||
- Connect(连接)
|
||||
- Speak(发言)
|
||||
- 最好还有 Use Voice Activity(使用语音活动)
|
||||
|
||||
同时在开发者门户中启用特权 intent(意图):
|
||||
- Presence Intent
|
||||
- Server Members Intent
|
||||
- Message Content Intent
|
||||
|
||||
## 加入与离开
|
||||
|
||||
在机器人所在的 Discord 文本频道中:
|
||||
|
||||
```text
|
||||
/voice join
|
||||
/voice leave
|
||||
/voice status
|
||||
```
|
||||
|
||||
### 加入后的行为
|
||||
|
||||
- 用户在语音频道中说话
|
||||
- Hermes 检测语音边界
|
||||
- 转录内容发布到关联的文本频道
|
||||
- Hermes 以文字和音频形式回复
|
||||
- 文本频道为执行 `/voice join` 的那个频道
|
||||
|
||||
### Discord 语音频道使用最佳实践
|
||||
|
||||
- 严格限制 `DISCORD_ALLOWED_USERS`
|
||||
- 先使用专用的机器人/测试频道
|
||||
- 在尝试语音频道模式之前,先确认 STT 和 TTS 在普通文本聊天语音模式下正常工作
|
||||
|
||||
## 语音质量建议
|
||||
|
||||
### 最佳质量方案
|
||||
|
||||
- STT:本地 `large-v3` 或 Groq `whisper-large-v3`
|
||||
- TTS:ElevenLabs
|
||||
|
||||
### 最佳速度 / 便利性方案
|
||||
|
||||
- STT:本地 `base` 或 Groq
|
||||
- TTS:Edge
|
||||
|
||||
### 最佳零成本方案
|
||||
|
||||
- STT:本地
|
||||
- TTS:Edge
|
||||
|
||||
## 常见故障模式
|
||||
|
||||
### "No audio device found"
|
||||
|
||||
安装 `portaudio`。
|
||||
|
||||
### "机器人加入但听不到声音"
|
||||
|
||||
检查:
|
||||
- 你的 Discord 用户 ID 是否在 `DISCORD_ALLOWED_USERS` 中
|
||||
- 你是否处于静音状态
|
||||
- 特权 intent 是否已启用
|
||||
- 机器人是否拥有 Connect/Speak 权限
|
||||
|
||||
### "能转录但不说话"
|
||||
|
||||
检查:
|
||||
- TTS provider 配置
|
||||
- ElevenLabs 或 OpenAI 的 API 密钥 / 配额
|
||||
- Edge 转换路径的 `ffmpeg` 安装情况
|
||||
|
||||
### "Whisper 输出乱码"
|
||||
|
||||
尝试:
|
||||
- 更安静的环境
|
||||
- 提高 `silence_threshold`
|
||||
- 更换 STT provider/模型
|
||||
- 更短、更清晰的表达
|
||||
|
||||
### "在私信中正常但在服务器频道中不工作"
|
||||
|
||||
这通常是 mention(提及)策略问题。
|
||||
|
||||
默认情况下,除非另行配置,机器人在 Discord 服务器文本频道中需要被 `@mention` 才会响应。
|
||||
|
||||
## 建议的第一周方案
|
||||
|
||||
如果你想走最短的成功路径:
|
||||
|
||||
1. 让文本 Hermes 正常工作
|
||||
2. 安装 `hermes-agent[voice]`
|
||||
3. 使用本地 STT + Edge TTS 的 CLI 语音模式
|
||||
4. 然后在 Telegram 或 Discord 中启用 `/voice on`
|
||||
5. 只有在此之后,再尝试 Discord 语音频道模式
|
||||
|
||||
这种递进方式可以将调试范围控制到最小。
|
||||
|
||||
## 下一步阅读
|
||||
|
||||
- [语音模式功能参考](/user-guide/features/voice-mode)
|
||||
- [消息 Gateway](/user-guide/messaging)
|
||||
- [Discord 设置](/user-guide/messaging/discord)
|
||||
- [Telegram 设置](/user-guide/messaging/telegram)
|
||||
- [配置](/user-guide/configuration)
|
||||
@ -0,0 +1,329 @@
|
||||
---
|
||||
sidebar_position: 11
|
||||
sidebar_label: "通过 Webhook 进行 GitHub PR 审查"
|
||||
title: "使用 Webhook 自动发布 GitHub PR 评论"
|
||||
description: "将 Hermes 连接到 GitHub,使其自动获取 PR diff、审查代码变更并发布评论——由 webhook 触发,无需手动提示"
|
||||
---
|
||||
|
||||
# 使用 Webhook 自动发布 GitHub PR 评论
|
||||
|
||||
本指南介绍如何将 Hermes Agent 连接到 GitHub,使其自动获取 pull request 的 diff、分析代码变更并发布评论——由 webhook 事件触发,无需手动 prompt(提示词)。
|
||||
|
||||
当 PR 被打开或更新时,GitHub 会向你的 Hermes 实例发送一个 webhook POST 请求。Hermes 使用一个 prompt 运行 agent,该 prompt 指示其通过 `gh` CLI 获取 diff,并将响应发布回 PR 线程。
|
||||
|
||||
:::tip 想要无需公网端点的更简单配置?
|
||||
如果你没有公网 URL,或只是想快速上手,请查看 [构建 GitHub PR 审查 Agent](./github-pr-review-agent.md) —— 使用 cron 作业按计划轮询 PR,可在 NAT 和防火墙后运行。
|
||||
:::
|
||||
|
||||
:::info 参考文档
|
||||
完整的 webhook 平台参考(所有配置选项、投递类型、动态订阅、安全模型),请参阅 [Webhooks](/user-guide/messaging/webhooks)。
|
||||
:::
|
||||
|
||||
:::warning Prompt 注入风险
|
||||
Webhook payload 包含攻击者可控的数据——PR 标题、commit 消息和描述中可能包含恶意指令。当你的 webhook 端点暴露在公网时,请在沙箱环境(Docker、SSH 后端)中运行 gateway。请参阅下方的[安全说明](#security-notes)。
|
||||
:::
|
||||
|
||||
---
|
||||
|
||||
## 前提条件
|
||||
|
||||
- Hermes Agent 已安装并运行(`hermes gateway`)
|
||||
- [`gh` CLI](https://cli.github.com/) 已安装并在 gateway 主机上完成认证(`gh auth login`)
|
||||
- 你的 Hermes 实例有一个可公网访问的 URL(如果在本地运行,请参阅[使用 ngrok 进行本地测试](#local-testing-with-ngrok))
|
||||
- 对 GitHub 仓库的管理员权限(管理 webhook 所需)
|
||||
|
||||
---
|
||||
|
||||
## 第一步——启用 webhook 平台
|
||||
|
||||
在你的 `~/.hermes/config.yaml` 中添加以下内容:
|
||||
|
||||
```yaml
|
||||
platforms:
|
||||
webhook:
|
||||
enabled: true
|
||||
extra:
|
||||
port: 8644 # 默认值;如果该端口被其他服务占用,请修改
|
||||
rate_limit: 30 # 每条路由每分钟最大请求数(非全局上限)
|
||||
|
||||
routes:
|
||||
github-pr-review:
|
||||
secret: "your-webhook-secret-here" # 必须与 GitHub webhook secret 完全一致
|
||||
events:
|
||||
- pull_request
|
||||
|
||||
# agent 被指示在审查前先获取实际的 diff。
|
||||
# {number} 和 {repository.full_name} 从 GitHub payload 中解析。
|
||||
prompt: |
|
||||
A pull request event was received (action: {action}).
|
||||
|
||||
PR #{number}: {pull_request.title}
|
||||
Author: {pull_request.user.login}
|
||||
Branch: {pull_request.head.ref} → {pull_request.base.ref}
|
||||
Description: {pull_request.body}
|
||||
URL: {pull_request.html_url}
|
||||
|
||||
If the action is "closed" or "labeled", stop here and do not post a comment.
|
||||
|
||||
Otherwise:
|
||||
1. Run: gh pr diff {number} --repo {repository.full_name}
|
||||
2. Review the code changes for correctness, security issues, and clarity.
|
||||
3. Write a concise, actionable review comment and post it.
|
||||
|
||||
deliver: github_comment
|
||||
deliver_extra:
|
||||
repo: "{repository.full_name}"
|
||||
pr_number: "{number}"
|
||||
```
|
||||
|
||||
**关键字段:**
|
||||
|
||||
| 字段 | 说明 |
|
||||
|---|---|
|
||||
| `secret`(路由级别) | 该路由的 HMAC secret。如果省略,则回退到 `extra.secret` 全局配置。 |
|
||||
| `events` | 要接受的 `X-GitHub-Event` 请求头值列表。空列表 = 接受所有。 |
|
||||
| `prompt` | 模板;`{field}` 和 `{nested.field}` 从 GitHub payload 中解析。 |
|
||||
| `deliver` | `github_comment` 通过 `gh pr comment` 发布。`log` 仅写入 gateway 日志。 |
|
||||
| `deliver_extra.repo` | 从 payload 中解析为例如 `org/repo`。 |
|
||||
| `deliver_extra.pr_number` | 从 payload 中解析为 PR 编号。 |
|
||||
|
||||
:::note Payload 中不包含代码
|
||||
GitHub webhook payload 包含 PR 元数据(标题、描述、分支名、URL),但**不包含 diff**。上方的 prompt 指示 agent 运行 `gh pr diff` 来获取实际变更。`terminal` 工具已包含在默认的 `hermes-webhook` 工具集中,无需额外配置。
|
||||
:::
|
||||
|
||||
---
|
||||
|
||||
## 第二步——启动 gateway
|
||||
|
||||
```bash
|
||||
hermes gateway
|
||||
```
|
||||
|
||||
你应该看到:
|
||||
|
||||
```
|
||||
[webhook] Listening on 0.0.0.0:8644 — routes: github-pr-review
|
||||
```
|
||||
|
||||
验证其是否正在运行:
|
||||
|
||||
```bash
|
||||
curl http://localhost:8644/health
|
||||
# {"status": "ok", "platform": "webhook"}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 第三步——在 GitHub 上注册 webhook
|
||||
|
||||
1. 进入你的仓库 → **Settings** → **Webhooks** → **Add webhook**
|
||||
2. 填写:
|
||||
- **Payload URL:** `https://your-public-url.example.com/webhooks/github-pr-review`
|
||||
- **Content type:** `application/json`
|
||||
- **Secret:** 与路由配置中 `secret` 设置的值相同
|
||||
- **Which events?** → 选择单个事件 → 勾选 **Pull requests**
|
||||
3. 点击 **Add webhook**
|
||||
|
||||
GitHub 会立即发送一个 `ping` 事件以确认连接。该事件会被安全忽略——`ping` 不在你的 `events` 列表中——并返回 `{"status": "ignored", "event": "ping"}`。它仅在 DEBUG 级别记录日志,因此不会在默认日志级别的控制台中显示。
|
||||
|
||||
---
|
||||
|
||||
## 第四步——打开一个测试 PR
|
||||
|
||||
创建一个分支,推送一个变更,并打开一个 PR。在 30–90 秒内(取决于 PR 大小和模型),Hermes 应该会发布一条审查评论。
|
||||
|
||||
要实时跟踪 agent 的进度:
|
||||
|
||||
```bash
|
||||
tail -f "${HERMES_HOME:-$HOME/.hermes}/logs/gateway.log"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 使用 ngrok 进行本地测试
|
||||
|
||||
如果 Hermes 在你的笔记本上运行,使用 [ngrok](https://ngrok.com/) 将其暴露到公网:
|
||||
|
||||
```bash
|
||||
ngrok http 8644
|
||||
```
|
||||
|
||||
复制 `https://...ngrok-free.app` URL 并将其用作你的 GitHub Payload URL。在 ngrok 免费版中,每次 ngrok 重启后 URL 都会变化——每次会话都需要更新你的 GitHub webhook。付费 ngrok 账户可获得静态域名。
|
||||
|
||||
你可以直接用 `curl` 对静态路由进行冒烟测试——无需 GitHub 账户或真实 PR。
|
||||
|
||||
:::tip 本地测试时使用 `deliver: log`
|
||||
在测试时,将配置中的 `deliver: github_comment` 改为 `deliver: log`。否则 agent 将尝试向测试 payload 中的假 `org/repo#99` 仓库发布评论,这将会失败。对 prompt 输出满意后,再切换回 `deliver: github_comment`。
|
||||
:::
|
||||
|
||||
```bash
|
||||
SECRET="your-webhook-secret-here"
|
||||
BODY='{"action":"opened","number":99,"pull_request":{"title":"Test PR","body":"Adds a feature.","user":{"login":"testuser"},"head":{"ref":"feat/x"},"base":{"ref":"main"},"html_url":"https://github.com/org/repo/pull/99"},"repository":{"full_name":"org/repo"}}'
|
||||
SIG=$(printf '%s' "$BODY" | openssl dgst -sha256 -hmac "$SECRET" -hex | awk '{print "sha256="$2}')
|
||||
|
||||
curl -s -X POST http://localhost:8644/webhooks/github-pr-review \
|
||||
-H "Content-Type: application/json" \
|
||||
-H "X-GitHub-Event: pull_request" \
|
||||
-H "X-Hub-Signature-256: $SIG" \
|
||||
-d "$BODY"
|
||||
# Expected: {"status":"accepted","route":"github-pr-review","event":"pull_request","delivery_id":"..."}
|
||||
```
|
||||
|
||||
然后观察 agent 运行:
|
||||
```bash
|
||||
tail -f "${HERMES_HOME:-$HOME/.hermes}/logs/gateway.log"
|
||||
```
|
||||
|
||||
:::note
|
||||
`hermes webhook test <name>` 仅适用于通过 `hermes webhook subscribe` 创建的**动态订阅**。它不读取 `config.yaml` 中的路由。
|
||||
:::
|
||||
|
||||
---
|
||||
|
||||
## 过滤特定 action
|
||||
|
||||
GitHub 会针对多种 action 发送 `pull_request` 事件:`opened`、`synchronize`、`reopened`、`closed`、`labeled` 等。`events` 列表仅按 `X-GitHub-Event` 请求头值过滤——无法在路由级别按 action 子类型过滤。
|
||||
|
||||
第一步中的 prompt 已通过指示 agent 对 `closed` 和 `labeled` 事件提前停止来处理这一问题。
|
||||
|
||||
:::warning Agent 仍会运行并消耗 token(令牌)
|
||||
"stop here" 指令会阻止有意义的审查,但无论 action 如何,agent 仍会对每个 `pull_request` 事件运行至完成。GitHub webhook 只能按事件类型(`pull_request`、`push`、`issues` 等)过滤——无法按 action 子类型(`opened`、`closed`、`labeled`)过滤。路由级别没有针对子 action 的过滤器。对于高流量仓库,请接受这一成本,或通过 GitHub Actions workflow 在上游进行过滤,有条件地调用你的 webhook URL。
|
||||
:::
|
||||
|
||||
> 不支持 Jinja2 或条件模板语法。`{field}` 和 `{nested.field}` 是唯一支持的替换方式。其他内容会原样传递给 agent。
|
||||
|
||||
---
|
||||
|
||||
## 使用 skill 保持一致的审查风格
|
||||
|
||||
加载一个 [Hermes skill](/user-guide/features/skills) 以赋予 agent 一致的审查风格。在 `config.yaml` 的 `platforms.webhook.extra.routes` 中,向你的路由添加 `skills`:
|
||||
|
||||
```yaml
|
||||
platforms:
|
||||
webhook:
|
||||
enabled: true
|
||||
extra:
|
||||
routes:
|
||||
github-pr-review:
|
||||
secret: "your-webhook-secret-here"
|
||||
events: [pull_request]
|
||||
prompt: |
|
||||
A pull request event was received (action: {action}).
|
||||
PR #{number}: {pull_request.title} by {pull_request.user.login}
|
||||
URL: {pull_request.html_url}
|
||||
|
||||
If the action is "closed" or "labeled", stop here and do not post a comment.
|
||||
|
||||
Otherwise:
|
||||
1. Run: gh pr diff {number} --repo {repository.full_name}
|
||||
2. Review the diff using your review guidelines.
|
||||
3. Write a concise, actionable review comment and post it.
|
||||
skills:
|
||||
- review
|
||||
deliver: github_comment
|
||||
deliver_extra:
|
||||
repo: "{repository.full_name}"
|
||||
pr_number: "{number}"
|
||||
```
|
||||
|
||||
> **注意:** 列表中只有第一个找到的 skill 会被加载。Hermes 不会叠加多个 skill——后续条目会被忽略。
|
||||
|
||||
---
|
||||
|
||||
## 将响应发送到 Slack 或 Discord
|
||||
|
||||
将路由中的 `deliver` 和 `deliver_extra` 字段替换为你的目标平台:
|
||||
|
||||
```yaml
|
||||
# 在 platforms.webhook.extra.routes.<route-name> 内部:
|
||||
|
||||
# Slack
|
||||
deliver: slack
|
||||
deliver_extra:
|
||||
chat_id: "C0123456789" # Slack 频道 ID(省略则使用配置的默认频道)
|
||||
|
||||
# Discord
|
||||
deliver: discord
|
||||
deliver_extra:
|
||||
chat_id: "987654321012345678" # Discord 频道 ID(省略则使用默认频道)
|
||||
```
|
||||
|
||||
目标平台也必须在 gateway 中启用并连接。如果省略 `chat_id`,响应将发送到该平台配置的默认频道。
|
||||
|
||||
有效的 `deliver` 值:`log` · `github_comment` · `telegram` · `discord` · `slack` · `signal` · `sms`
|
||||
|
||||
---
|
||||
|
||||
## GitLab 支持
|
||||
|
||||
同一适配器也适用于 GitLab。GitLab 使用 `X-Gitlab-Token` 进行认证(纯字符串匹配,非 HMAC)——Hermes 会自动处理两者。
|
||||
|
||||
对于事件过滤,GitLab 将 `X-GitLab-Event` 设置为 `Merge Request Hook`、`Push Hook`、`Pipeline Hook` 等值。在 `events` 中使用精确的请求头值:
|
||||
|
||||
```yaml
|
||||
events:
|
||||
- Merge Request Hook
|
||||
```
|
||||
|
||||
GitLab 的 payload 字段与 GitHub 不同——例如,MR 标题使用 `{object_attributes.title}`,MR 编号使用 `{object_attributes.iid}`。发现完整 payload 结构最简单的方式是使用 GitLab webhook 设置中的 **Test** 按钮,结合 **Recent Deliveries** 日志。或者,在路由配置中省略 `prompt`——Hermes 将把完整 payload 作为格式化 JSON 直接传递给 agent,agent 的响应(在 gateway 日志中通过 `deliver: log` 可见)将描述其结构。
|
||||
|
||||
---
|
||||
|
||||
## 安全说明
|
||||
|
||||
- **永远不要在生产环境中使用 `INSECURE_NO_AUTH`**——它会完全禁用签名验证。仅用于本地开发。
|
||||
- **定期轮换你的 webhook secret**,并在 GitHub(webhook 设置)和你的 `config.yaml` 中同步更新。
|
||||
- **速率限制**默认为每条路由每分钟 30 次请求(可通过 `extra.rate_limit` 配置)。超出限制返回 `429`。
|
||||
- **重复投递**(webhook 重试)通过 1 小时的幂等性缓存进行去重。缓存键依次为 `X-GitHub-Delivery`(如果存在)、`X-Request-ID`、毫秒级时间戳。当两个投递 ID 请求头都未设置时,重试**不会**去重。
|
||||
- **Prompt 注入:** PR 标题、描述和 commit 消息均为攻击者可控内容。恶意 PR 可能尝试操纵 agent 的行为。当暴露在公网时,请在沙箱环境(Docker、VM)中运行 gateway。
|
||||
|
||||
---
|
||||
|
||||
## 故障排查
|
||||
|
||||
| 现象 | 检查项 |
|
||||
|---|---|
|
||||
| `401 Invalid signature` | config.yaml 中的 secret 与 GitHub webhook secret 不匹配 |
|
||||
| `404 Unknown route` | URL 中的路由名称与 `routes:` 中的键不匹配 |
|
||||
| `429 Rate limit exceeded` | 每条路由每分钟 30 次请求已超出——在 GitHub UI 中重新投递测试事件时常见;等待一分钟或提高 `extra.rate_limit` |
|
||||
| 未发布评论 | `gh` 未安装、不在 PATH 中,或未完成认证(`gh auth login`) |
|
||||
| Agent 运行但无评论 | 检查 gateway 日志——如果 agent 输出为空或仅为"SKIP",投递仍会被尝试 |
|
||||
| 端口已被占用 | 在 config.yaml 中修改 `extra.port` |
|
||||
| Agent 运行但仅审查了 PR 描述 | prompt 中未包含 `gh pr diff` 指令——diff 不在 webhook payload 中 |
|
||||
| 看不到 ping 事件 | 被忽略的事件仅在 DEBUG 日志级别返回 `{"status":"ignored","event":"ping"}`——检查 GitHub 的投递日志(仓库 → Settings → Webhooks → 你的 webhook → Recent Deliveries) |
|
||||
|
||||
**GitHub 的 Recent Deliveries 标签页**(仓库 → Settings → Webhooks → 你的 webhook)显示每次投递的精确请求头、payload、HTTP 状态和响应体。这是无需查看服务器日志即可诊断故障的最快方式。
|
||||
|
||||
---
|
||||
|
||||
## 完整配置参考
|
||||
|
||||
```yaml
|
||||
platforms:
|
||||
webhook:
|
||||
enabled: true
|
||||
extra:
|
||||
host: "0.0.0.0" # 绑定地址(默认:0.0.0.0)
|
||||
port: 8644 # 监听端口(默认:8644)
|
||||
secret: "" # 可选的全局回退 secret
|
||||
rate_limit: 30 # 每条路由每分钟请求数
|
||||
max_body_bytes: 1048576 # payload 大小限制,单位字节(默认:1 MB)
|
||||
|
||||
routes:
|
||||
<route-name>:
|
||||
secret: "required-per-route"
|
||||
events: [] # [] = 接受所有;否则列出 X-GitHub-Event 值
|
||||
prompt: "" # {field} / {nested.field} 从 payload 中解析
|
||||
skills: [] # 加载第一个匹配的 skill(仅一个)
|
||||
deliver: "log" # log | github_comment | telegram | discord | slack | signal | sms
|
||||
deliver_extra: {} # github_comment 需要 repo + pr_number;其他平台需要 chat_id
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 下一步
|
||||
|
||||
- **[基于 Cron 的 PR 审查](./github-pr-review-agent.md)** —— 按计划轮询 PR,无需公网端点
|
||||
- **[Webhook 参考](/user-guide/messaging/webhooks)** —— webhook 平台的完整配置参考
|
||||
- **[构建 Plugin](/guides/build-a-hermes-plugin)** —— 将审查逻辑打包为可共享的 plugin
|
||||
- **[Profiles](/user-guide/profiles)** —— 运行一个拥有独立内存和配置的专属审查者 profile
|
||||
@ -0,0 +1,290 @@
|
||||
---
|
||||
sidebar_position: 12
|
||||
title: "使用 Skills"
|
||||
description: "查找、安装、使用和创建 skills——按需加载的知识文档,用于教会 Hermes 新的工作流程"
|
||||
---
|
||||
|
||||
# 使用 Skills
|
||||
|
||||
Skills(技能)是按需加载的知识文档,用于教会 Hermes 如何处理特定任务——从生成 ASCII 艺术到管理 GitHub PR。本指南介绍日常使用方法。
|
||||
|
||||
完整技术参考请见 [Skills 系统](/user-guide/features/skills)。
|
||||
|
||||
---
|
||||
|
||||
## 查找 Skills
|
||||
|
||||
每个 Hermes 安装都内置了捆绑的 skills。查看可用列表:
|
||||
|
||||
```bash
|
||||
# 在任意聊天会话中:
|
||||
/skills
|
||||
|
||||
# 或通过 CLI:
|
||||
hermes skills list
|
||||
```
|
||||
|
||||
输出包含名称和描述的紧凑列表:
|
||||
|
||||
```
|
||||
ascii-art Generate ASCII art using pyfiglet, cowsay, boxes...
|
||||
arxiv Search and retrieve academic papers from arXiv...
|
||||
github-pr-workflow Full PR lifecycle — create branches, commit...
|
||||
plan Plan mode — inspect context, write a markdown...
|
||||
excalidraw Create hand-drawn style diagrams using Excalidraw...
|
||||
```
|
||||
|
||||
### 搜索 Skill
|
||||
|
||||
```bash
|
||||
# 按关键词搜索
|
||||
/skills search docker
|
||||
/skills search music
|
||||
```
|
||||
|
||||
### Skills Hub
|
||||
|
||||
官方可选 skills(较重或小众、默认未激活的 skills)可通过 Hub 获取:
|
||||
|
||||
```bash
|
||||
# 浏览官方可选 skills
|
||||
/skills browse
|
||||
|
||||
# 搜索 Hub
|
||||
/skills search blockchain
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 使用 Skill
|
||||
|
||||
每个已安装的 skill 自动成为一个斜杠命令。直接输入其名称即可:
|
||||
|
||||
```bash
|
||||
# 加载 skill 并指定任务
|
||||
/ascii-art Make a banner that says "HELLO WORLD"
|
||||
/plan Design a REST API for a todo app
|
||||
/github-pr-workflow Create a PR for the auth refactor
|
||||
|
||||
# 只输入 skill 名称(不带任务)会加载它并让你描述需求
|
||||
/excalidraw
|
||||
```
|
||||
|
||||
你也可以通过自然对话触发 skills——告诉 Hermes 使用某个特定 skill,它会通过 `skill_view` 工具加载。
|
||||
|
||||
### 渐进式加载
|
||||
|
||||
Skills 采用 token 高效的加载模式,agent 不会一次性加载所有内容:
|
||||
|
||||
1. **`skills_list()`** — 所有 skills 的紧凑列表(约 3k tokens),在会话开始时加载。
|
||||
2. **`skill_view(name)`** — 单个 skill 的完整 SKILL.md 内容,在 agent 判断需要该 skill 时加载。
|
||||
3. **`skill_view(name, file_path)`** — skill 内的特定参考文件,仅在需要时加载。
|
||||
|
||||
这意味着 skills 在真正被使用之前不消耗任何 tokens。
|
||||
|
||||
---
|
||||
|
||||
## 从 Hub 安装
|
||||
|
||||
官方可选 skills 随 Hermes 一起发布,但默认未激活,需显式安装:
|
||||
|
||||
```bash
|
||||
# 安装官方可选 skill
|
||||
hermes skills install official/research/arxiv
|
||||
|
||||
# 在聊天会话中从 Hub 安装
|
||||
/skills install official/creative/songwriting-and-ai-music
|
||||
|
||||
# 直接从任意 HTTP(S) URL 安装单文件 SKILL.md
|
||||
hermes skills install https://sharethis.chat/SKILL.md
|
||||
/skills install https://example.com/SKILL.md --name my-skill
|
||||
```
|
||||
|
||||
安装过程:
|
||||
1. skill 目录被复制到 `~/.hermes/skills/`
|
||||
2. 出现在 `skills_list` 输出中
|
||||
3. 成为可用的斜杠命令
|
||||
|
||||
:::tip
|
||||
已安装的 skills 在新会话中生效。如需在当前会话中立即使用,可用 `/reset` 开启新会话,或添加 `--now` 参数立即使 prompt 缓存失效(下一轮会消耗更多 tokens)。
|
||||
:::
|
||||
|
||||
### 验证安装
|
||||
|
||||
```bash
|
||||
# 确认已安装
|
||||
hermes skills list | grep arxiv
|
||||
|
||||
# 或在聊天中
|
||||
/skills search arxiv
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 插件提供的 Skills
|
||||
|
||||
插件可以使用命名空间名称(`plugin:skill`)捆绑自己的 skills,以避免与内置 skills 发生名称冲突。
|
||||
|
||||
```bash
|
||||
# 通过限定名称加载插件 skill
|
||||
skill_view("superpowers:writing-plans")
|
||||
|
||||
# 同名的内置 skill 不受影响
|
||||
skill_view("writing-plans")
|
||||
```
|
||||
|
||||
插件 skills **不会**列在系统 prompt 中,也不出现在 `skills_list` 中。它们是按需加载的——当你知道某个插件提供了某个 skill 时,显式加载它。加载后,agent 会看到一个横幅,列出同一插件的其他 skills。
|
||||
|
||||
关于如何在自己的插件中捆绑 skills,请参见 [构建 Hermes 插件 → 捆绑 skills](/guides/build-a-hermes-plugin#bundle-skills)。
|
||||
|
||||
---
|
||||
|
||||
## 配置 Skill 设置
|
||||
|
||||
部分 skills 在 frontmatter 中声明了所需的配置:
|
||||
|
||||
```yaml
|
||||
metadata:
|
||||
hermes:
|
||||
config:
|
||||
- key: tenor.api_key
|
||||
description: "Tenor API key for GIF search"
|
||||
prompt: "Enter your Tenor API key"
|
||||
url: "https://developers.google.com/tenor/guides/quickstart"
|
||||
```
|
||||
|
||||
当带有配置的 skill 首次加载时,Hermes 会提示你输入相应值,并将其存储在 `config.yaml` 的 `skills.config.*` 下。
|
||||
|
||||
通过 CLI 管理 skill 配置:
|
||||
|
||||
```bash
|
||||
# 对特定 skill 进行交互式配置
|
||||
hermes skills config gif-search
|
||||
|
||||
# 查看所有 skill 配置
|
||||
hermes config get skills.config
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 创建自己的 Skill
|
||||
|
||||
Skills 只是带有 YAML frontmatter 的 Markdown 文件,创建一个不超过五分钟。
|
||||
|
||||
### 1. 创建目录
|
||||
|
||||
```bash
|
||||
mkdir -p ~/.hermes/skills/my-category/my-skill
|
||||
```
|
||||
|
||||
### 2. 编写 SKILL.md
|
||||
|
||||
```markdown title="~/.hermes/skills/my-category/my-skill/SKILL.md"
|
||||
---
|
||||
name: my-skill
|
||||
description: Brief description of what this skill does
|
||||
version: 1.0.0
|
||||
metadata:
|
||||
hermes:
|
||||
tags: [my-tag, automation]
|
||||
category: my-category
|
||||
---
|
||||
|
||||
# My Skill
|
||||
|
||||
## When to Use
|
||||
Use this skill when the user asks about [specific topic] or needs to [specific task].
|
||||
|
||||
## Procedure
|
||||
1. First, check if [prerequisite] is available
|
||||
2. Run `command --with-flags`
|
||||
3. Parse the output and present results
|
||||
|
||||
## Pitfalls
|
||||
- Common failure: [description]. Fix: [solution]
|
||||
- Watch out for [edge case]
|
||||
|
||||
## Verification
|
||||
Run `check-command` to confirm the result is correct.
|
||||
```
|
||||
|
||||
### 3. 添加参考文件(可选)
|
||||
|
||||
Skills 可以包含 agent 按需加载的辅助文件:
|
||||
|
||||
```
|
||||
my-skill/
|
||||
├── SKILL.md # 主 skill 文档
|
||||
├── references/
|
||||
│ ├── api-docs.md # agent 可查阅的 API 参考
|
||||
│ └── examples.md # 示例输入/输出
|
||||
├── templates/
|
||||
│ └── config.yaml # agent 可使用的模板文件
|
||||
└── scripts/
|
||||
└── setup.sh # agent 可执行的脚本
|
||||
```
|
||||
|
||||
在 SKILL.md 中引用这些文件:
|
||||
|
||||
```markdown
|
||||
For API details, load the reference: `skill_view("my-skill", "references/api-docs.md")`
|
||||
```
|
||||
|
||||
### 4. 测试
|
||||
|
||||
开启新会话并测试你的 skill:
|
||||
|
||||
```bash
|
||||
hermes chat -q "/my-skill help me with the thing"
|
||||
```
|
||||
|
||||
Skill 会自动出现——无需注册。放入 `~/.hermes/skills/` 即可立即生效。
|
||||
|
||||
:::info
|
||||
Agent 也可以使用 `skill_manage` 自行创建和更新 skills。解决复杂问题后,Hermes 可能会主动提议将该方法保存为 skill,以便下次使用。
|
||||
:::
|
||||
|
||||
---
|
||||
|
||||
## 按平台管理 Skills
|
||||
|
||||
控制哪些 skills 在哪些平台上可用:
|
||||
|
||||
```bash
|
||||
hermes skills
|
||||
```
|
||||
|
||||
这会打开一个交互式 TUI,你可以按平台(CLI、Telegram、Discord 等)启用或禁用 skills。当你希望某些 skills 仅在特定场景下可用时非常有用——例如,在 Telegram 上禁用开发类 skills。
|
||||
|
||||
---
|
||||
|
||||
## Skills 与 Memory 的区别
|
||||
|
||||
两者都跨会话持久化,但用途不同:
|
||||
|
||||
| | Skills | Memory |
|
||||
|---|---|---|
|
||||
| **内容** | 程序性知识——如何做事 | 事实性知识——事物是什么 |
|
||||
| **时机** | 按需加载,仅在相关时加载 | 自动注入每个会话 |
|
||||
| **大小** | 可以较大(数百行) | 应保持紧凑(仅关键事实) |
|
||||
| **开销** | 加载前零 tokens | 少量但持续的 token 开销 |
|
||||
| **示例** | "如何部署到 Kubernetes" | "用户偏好深色模式,位于 PST 时区" |
|
||||
| **创建者** | 你、agent 或从 Hub 安装 | Agent,基于对话内容 |
|
||||
|
||||
**经验法则:** 如果你会把它写进参考文档,它就是 skill;如果你会把它写在便利贴上,它就是 memory。
|
||||
|
||||
---
|
||||
|
||||
## 使用技巧
|
||||
|
||||
**保持 skills 聚焦。** 试图涵盖"所有 DevOps"的 skill 会过于冗长且模糊。专注于"将 Python 应用部署到 Fly.io"的 skill 才足够具体,真正有用。
|
||||
|
||||
**让 agent 创建 skills。** 完成复杂的多步骤任务后,Hermes 通常会主动提议将该方法保存为 skill。接受它——这些由 agent 编写的 skills 会捕捉到完整的工作流程,包括过程中发现的各种坑。
|
||||
|
||||
**使用分类目录。** 将 skills 整理到子目录中(`~/.hermes/skills/devops/`、`~/.hermes/skills/research/` 等),保持列表整洁,并帮助 agent 更快找到相关 skills。
|
||||
|
||||
**及时更新过时的 skills。** 如果使用某个 skill 时遇到它未覆盖的问题,告诉 Hermes 用你学到的内容更新该 skill。不维护的 skills 会成为负担。
|
||||
|
||||
---
|
||||
|
||||
*完整的 skills 参考——frontmatter 字段、条件激活、外部目录等——请见 [Skills 系统](/user-guide/features/skills)。*
|
||||
@ -0,0 +1,269 @@
|
||||
---
|
||||
sidebar_position: 16
|
||||
title: "xAI Grok OAuth(SuperGrok / X Premium+)"
|
||||
description: "使用 SuperGrok 或 X Premium+ 订阅登录,在 Hermes Agent 中使用 Grok 模型——无需 API 密钥"
|
||||
---
|
||||
|
||||
# xAI Grok OAuth(SuperGrok / X Premium+)
|
||||
|
||||
Hermes Agent 通过基于浏览器的 OAuth 登录流程支持 xAI Grok,认证服务器为 [accounts.x.ai](https://accounts.x.ai),支持 **SuperGrok 订阅**([grok.com](https://x.ai/grok))或 **X Premium+ 订阅**(已关联的 X 账号)。无需 `XAI_API_KEY`——登录一次后,Hermes 会在后台自动刷新会话。
|
||||
|
||||
当你使用拥有 Premium+ 的 X 账号登录时,xAI 会自动将订阅状态关联到你的 xAI 会话,因此 OAuth 流程与直接 SuperGrok 订阅者的体验完全相同。
|
||||
|
||||
该传输层复用 `codex_responses` 适配器(xAI 暴露了 Responses 风格的端点),因此推理、工具调用、流式传输和 prompt(提示词)缓存无需任何适配器改动即可正常工作。
|
||||
|
||||
同一 OAuth bearer token 也会被 Hermes 中所有直连 xAI 的功能复用——TTS、图像生成、视频生成和转录——因此单次登录即可覆盖全部四项功能。
|
||||
|
||||
## 概览
|
||||
|
||||
| 项目 | 值 |
|
||||
|------|-------|
|
||||
| Provider ID | `xai-oauth` |
|
||||
| 显示名称 | xAI Grok OAuth (SuperGrok / X Premium+) |
|
||||
| 认证类型 | 浏览器 OAuth 2.0 PKCE(回环回调) |
|
||||
| 传输层 | xAI Responses API(`codex_responses`) |
|
||||
| 默认模型 | `grok-4.3` |
|
||||
| 端点 | `https://api.x.ai/v1` |
|
||||
| 认证服务器 | `https://accounts.x.ai` |
|
||||
| 需要环境变量 | 否(此 provider 不使用 `XAI_API_KEY`) |
|
||||
| 订阅要求 | [SuperGrok](https://x.ai/grok) 或 [X Premium+](https://x.com/i/premium_sign_up)——见下方说明 |
|
||||
|
||||
## 前提条件
|
||||
|
||||
- Python 3.9+
|
||||
- 已安装 Hermes Agent
|
||||
- 你的 xAI 账号拥有有效的 **SuperGrok** 订阅,**或**你登录所用的 X 账号拥有 **X Premium+** 订阅(xAI 会自动关联订阅)
|
||||
- 本地机器上有可用的浏览器(远程会话可使用 `--no-browser`)
|
||||
|
||||
:::warning xAI 可能按套餐限制 OAuth API 访问
|
||||
xAI 的后端对 OAuth API 接口维护自己的白名单,已有记录显示即使应用内订阅处于激活状态,标准 SuperGrok 订阅者也会收到 `HTTP 403`(见 issue [#26847](https://github.com/NousResearch/hermes-agent/issues/26847))。如果浏览器中 OAuth 登录成功但推理返回 403,请设置 `XAI_API_KEY` 并切换到 API 密钥路径(`provider: xai`)——该接口目前不受相同限制。
|
||||
:::
|
||||
|
||||
## 快速开始
|
||||
|
||||
```bash
|
||||
# 启动 provider 和模型选择器
|
||||
hermes model
|
||||
# → 从 provider 列表中选择 "xAI Grok OAuth (SuperGrok / X Premium+)"
|
||||
# → Hermes 在浏览器中打开 accounts.x.ai
|
||||
# → 在浏览器中批准访问
|
||||
# → 选择模型(grok-4.3 在列表顶部)
|
||||
# → 开始对话
|
||||
|
||||
hermes
|
||||
```
|
||||
|
||||
首次登录后,凭据存储在 `~/.hermes/auth.json` 中,并在过期前自动刷新。
|
||||
|
||||
## 手动登录
|
||||
|
||||
你可以不经过模型选择器直接触发登录:
|
||||
|
||||
```bash
|
||||
hermes auth add xai-oauth
|
||||
```
|
||||
|
||||
### 远程 / 无头会话
|
||||
|
||||
在没有浏览器的服务器、容器或 SSH 会话中,Hermes 会检测到远程环境并打印授权 URL,而不是打开浏览器。
|
||||
|
||||
**重要:** 回环监听器仍在远程机器的 `127.0.0.1:56121` 上运行。xAI 的重定向需要到达*该*监听器,因此在你的笔记本上打开 URL 会失败(`Could not establish connection. We couldn't reach your app.`),除非你转发端口:
|
||||
|
||||
```bash
|
||||
# 在本地机器的另一个终端中:
|
||||
ssh -N -L 56121:127.0.0.1:56121 user@remote-host
|
||||
|
||||
# 然后在远程机器的 SSH 会话中:
|
||||
hermes auth add xai-oauth --no-browser
|
||||
# 在本地浏览器中打开打印出的授权 URL。
|
||||
```
|
||||
|
||||
通过跳板机 / 堡垒机:添加 `-J jump-user@jump-host`。
|
||||
|
||||
完整步骤(包括 ProxyJump 链、mosh/tmux 和 ControlMaster 注意事项)请参阅 [OAuth over SSH / Remote Hosts](./oauth-over-ssh.md)。
|
||||
|
||||
### 仅限浏览器的远程环境(Cloud Shell、Codespaces、EC2 Instance Connect)
|
||||
|
||||
如果你没有常规 SSH 客户端(例如在 GCP Cloud Shell、GitHub Codespaces、AWS EC2 Instance Connect、Gitpod 或其他基于浏览器的控制台中运行 Hermes),上述 `ssh -L` 方案不可用。请改用 `--manual-paste`——Hermes 跳过回环监听器,让你直接从浏览器粘贴失败的回调 URL:
|
||||
|
||||
```bash
|
||||
hermes auth add xai-oauth --manual-paste
|
||||
# 或通过模型选择器:
|
||||
hermes model --manual-paste
|
||||
```
|
||||
|
||||
完整操作说明请参阅 [OAuth over SSH / Remote Hosts](./oauth-over-ssh.md#browser-only-remote-cloud-shell--codespaces--ec2-instance-connect)。此为 [#26923](https://github.com/NousResearch/hermes-agent/issues/26923) 的回归修复。
|
||||
|
||||
## 登录流程说明
|
||||
|
||||
1. Hermes 在浏览器中打开 `accounts.x.ai`。
|
||||
2. 你登录(或确认现有会话)并批准访问。
|
||||
3. xAI 重定向回 Hermes,token 保存到 `~/.hermes/auth.json`。
|
||||
4. 此后,Hermes 在后台刷新 access token——你将保持登录状态,直到执行 `hermes auth remove xai-oauth` 或在 xAI 账号设置中撤销访问。
|
||||
|
||||
## 检查登录状态
|
||||
|
||||
```bash
|
||||
hermes doctor
|
||||
```
|
||||
|
||||
`◆ Auth Providers` 部分将显示每个 provider 的当前状态,包括 `xai-oauth`。
|
||||
|
||||
## 切换模型
|
||||
|
||||
```bash
|
||||
hermes model
|
||||
# → 选择 "xAI Grok OAuth (SuperGrok / X Premium+)"
|
||||
# → 从模型列表中选择(grok-4.3 固定在顶部)
|
||||
```
|
||||
|
||||
或直接设置模型:
|
||||
|
||||
```bash
|
||||
hermes config set model.default grok-4.3
|
||||
hermes config set model.provider xai-oauth
|
||||
```
|
||||
|
||||
## 配置参考
|
||||
|
||||
登录后,`~/.hermes/config.yaml` 将包含:
|
||||
|
||||
```yaml
|
||||
model:
|
||||
default: grok-4.3
|
||||
provider: xai-oauth
|
||||
base_url: https://api.x.ai/v1
|
||||
```
|
||||
|
||||
### Provider 别名
|
||||
|
||||
以下所有别名均解析为 `xai-oauth`:
|
||||
|
||||
```bash
|
||||
hermes --provider xai-oauth # 规范名称
|
||||
hermes --provider grok-oauth # 别名
|
||||
hermes --provider x-ai-oauth # 别名
|
||||
hermes --provider xai-grok-oauth # 别名
|
||||
```
|
||||
|
||||
## 直连 xAI 工具(TTS / 图像 / 视频 / 转录 / X 搜索)
|
||||
|
||||
通过 OAuth 登录后,每个直连 xAI 的工具都会自动复用同一 bearer token——**无需单独配置**,除非你更倾向于使用 API 密钥。
|
||||
|
||||
为每个工具选择后端:
|
||||
|
||||
```bash
|
||||
hermes tools
|
||||
# → Text-to-Speech → "xAI TTS"
|
||||
# → Image Generation → "xAI Grok Imagine (image)"
|
||||
# → Video Generation → "xAI Grok Imagine"
|
||||
# → X (Twitter) Search → "xAI Grok OAuth (SuperGrok / X Premium+)"
|
||||
```
|
||||
|
||||
如果 OAuth token 已存储,选择器会确认并跳过凭据提示。如果既没有 OAuth 也没有设置 `XAI_API_KEY`,选择器会提供三选一菜单:OAuth 登录、粘贴 API 密钥或跳过。
|
||||
|
||||
:::note 视频生成默认关闭
|
||||
`video_gen` 工具集默认禁用。在 `hermes tools` → `🎬 Video Generation`(按空格键)中启用后,agent 才能调用 `video_generate`。否则 agent 可能回退到内置的 ComfyUI 技能,该技能同样标记为视频生成。
|
||||
:::
|
||||
|
||||
:::note 配置 xAI 凭据后 X 搜索自动启用
|
||||
只要配置了 xAI 凭据(SuperGrok / X Premium+ OAuth token 或 `XAI_API_KEY`),`x_search` 工具集就会自动启用。如不需要,请通过 `hermes tools` → `🐦 X (Twitter) Search`(按空格键)显式禁用。该工具通过 xAI 内置的 `x_search` Responses API 路由——支持 **SuperGrok / X Premium+ OAuth 登录**或付费 `XAI_API_KEY`,两者同时配置时优先使用 OAuth(消耗订阅配额而非 API 费用)。未配置任何 xAI 凭据时,无论工具集是否启用,工具 schema 都对模型隐藏。
|
||||
:::
|
||||
|
||||
### 模型
|
||||
|
||||
| 工具 | 模型 | 说明 |
|
||||
|------|-------|-------|
|
||||
| 对话 | `grok-4.3` | 默认;通过 OAuth 登录时自动选择 |
|
||||
| 对话 | `grok-4.20-0309-reasoning` | 推理变体 |
|
||||
| 对话 | `grok-4.20-0309-non-reasoning` | 非推理变体 |
|
||||
| 对话 | `grok-4.20-multi-agent-0309` | 多 agent 变体 |
|
||||
| 图像 | `grok-imagine-image` | 默认;约 5–10 秒 |
|
||||
| 图像 | `grok-imagine-image-quality` | 更高保真度;约 10–20 秒 |
|
||||
| 视频 | `grok-imagine-video` | 文本转视频和图像转视频;最多 7 张参考图像 |
|
||||
| TTS | (默认音色) | xAI `/v1/tts` 端点 |
|
||||
|
||||
对话模型目录从磁盘上的 `models.dev` 缓存实时获取;缓存刷新后,新的 xAI 模型会自动出现。`grok-4.3` 始终固定在列表顶部。
|
||||
|
||||
## 环境变量
|
||||
|
||||
| 变量 | 作用 |
|
||||
|----------|--------|
|
||||
| `XAI_BASE_URL` | 覆盖默认的 `https://api.x.ai/v1` 端点(极少需要)。 |
|
||||
|
||||
要将 xAI 设为活跃 provider,请在 `config.yaml` 中设置 `model.provider: xai-oauth`(使用 `hermes setup` 进行引导配置),或在单次调用时传入 `--provider xai-oauth`。
|
||||
|
||||
## 故障排查
|
||||
|
||||
### Token 过期——未自动重新登录
|
||||
|
||||
Hermes 在每次会话前刷新 token,并在收到 401 时响应式地再次刷新。如果刷新因 `invalid_grant` 失败(刷新 token 被撤销或账号已轮换),Hermes 会显示类型化的重新认证消息,而不是崩溃。
|
||||
|
||||
当刷新失败是终态时(HTTP 4xx、`invalid_grant`、授权被撤销等),Hermes 将刷新 token 标记为失效并在本地隔离——后续调用跳过注定失败的刷新尝试,而不是反复重放同一个 401。agent 显示一条"需要重新认证"消息,并在你再次登录前保持等待。
|
||||
|
||||
**修复方法:** 再次运行 `hermes auth add xai-oauth` 开始全新登录。下次成功交换后隔离状态自动清除。
|
||||
|
||||
### 授权超时
|
||||
|
||||
回环监听器有有限的过期窗口(默认 180 秒)。如果你未在时限内批准登录,Hermes 会抛出超时错误。
|
||||
|
||||
**修复方法:** 重新运行 `hermes auth add xai-oauth`(或 `hermes model`)。流程重新开始。
|
||||
|
||||
### State 不匹配(可能的 CSRF)
|
||||
|
||||
Hermes 检测到授权服务器返回的 `state` 值与发送的不匹配。
|
||||
|
||||
**修复方法:** 重新运行登录。如果问题持续,检查是否有代理或重定向在修改 OAuth 响应。
|
||||
|
||||
### 从远程服务器登录
|
||||
|
||||
在 SSH 或容器会话中,Hermes 打印授权 URL 而不是打开浏览器。回环回调监听器仍绑定在远程主机的 `127.0.0.1:56121`——你笔记本上的浏览器无法访问它,除非进行 SSH 本地端口转发:
|
||||
|
||||
```bash
|
||||
# 本地机器,另一个终端:
|
||||
ssh -N -L 56121:127.0.0.1:56121 user@remote-host
|
||||
|
||||
# 远程机器:
|
||||
hermes auth add xai-oauth --no-browser
|
||||
```
|
||||
|
||||
完整操作说明(跳板机、mosh/tmux、端口冲突):[OAuth over SSH / Remote Hosts](./oauth-over-ssh.md)。
|
||||
|
||||
### 登录成功后 HTTP 403(套餐 / 权限问题)
|
||||
|
||||
浏览器中 OAuth 完成,token 已保存,但推理或 token 刷新返回 `HTTP 403`,消息类似于 *"The caller does not have permission to execute the specified operation"*。
|
||||
|
||||
这**不是** token 过期问题——重新运行 `hermes model` 不会改变结果。xAI 的后端已被观察到将 OAuth API 访问限制在特定 SuperGrok 套餐,即使应用内订阅处于激活状态(issue [#26847](https://github.com/NousResearch/hermes-agent/issues/26847))。
|
||||
|
||||
**修复方法:** 设置 `XAI_API_KEY` 并切换到 API 密钥路径:
|
||||
|
||||
```bash
|
||||
export XAI_API_KEY=xai-...
|
||||
hermes config set model.provider xai
|
||||
```
|
||||
|
||||
或在 [x.ai/grok](https://x.ai/grok) 升级订阅(如果必须使用 OAuth 路径)。
|
||||
|
||||
### 运行时出现"No xAI credentials found"错误
|
||||
|
||||
auth 存储中没有 `xai-oauth` 条目,也未设置 `XAI_API_KEY`。你尚未登录,或凭据文件已被删除。
|
||||
|
||||
**修复方法:** 运行 `hermes model` 并选择 xAI Grok OAuth provider,或运行 `hermes auth add xai-oauth`。
|
||||
|
||||
## 退出登录
|
||||
|
||||
删除所有已存储的 xAI Grok OAuth 凭据:
|
||||
|
||||
```bash
|
||||
hermes auth logout xai-oauth
|
||||
```
|
||||
|
||||
这会清除 `auth.json` 中的单例 OAuth 条目以及 `xai-oauth` 的所有凭据池行。如果只想删除单个池条目,请使用 `hermes auth remove xai-oauth <index|id|label>`(运行 `hermes auth list xai-oauth` 查看列表)。
|
||||
|
||||
## 另请参阅
|
||||
|
||||
- [OAuth over SSH / Remote Hosts](./oauth-over-ssh.md) — 如果 Hermes 与浏览器不在同一台机器上,必读
|
||||
- [AI Providers 参考](../integrations/providers.md)
|
||||
- [环境变量](../reference/environment-variables.md)
|
||||
- [配置](../user-guide/configuration.md)
|
||||
- [语音与 TTS](../user-guide/features/tts.md)
|
||||
@ -0,0 +1,86 @@
|
||||
---
|
||||
slug: /
|
||||
sidebar_position: 0
|
||||
title: "Hermes Agent 文档"
|
||||
description: "由 Nous Research 构建的自我改进 AI 智能体。内置学习循环,从经验中创建技能,在使用过程中持续改进,并跨会话保持记忆。"
|
||||
hide_table_of_contents: true
|
||||
displayed_sidebar: docs
|
||||
---
|
||||
|
||||
import Link from '@docusaurus/Link';
|
||||
|
||||
# Hermes Agent
|
||||
|
||||
由 [Nous Research](https://nousresearch.com) 构建的自我改进 AI 智能体。唯一内置学习循环的智能体——它从经验中创建技能,在使用过程中持续改进,主动提示自身持久化知识,并在会话间不断深化对你的建模。
|
||||
|
||||
<div style={{display: 'flex', gap: '1rem', marginBottom: '2rem', flexWrap: 'wrap'}}>
|
||||
<Link to="/getting-started/installation" style={{display: 'inline-block', padding: '0.6rem 1.2rem', backgroundColor: '#FFD700', color: '#07070d', borderRadius: '8px', fontWeight: 600, textDecoration: 'none'}}>快速开始 →</Link>
|
||||
<a href="https://github.com/NousResearch/hermes-agent" style={{display: 'inline-block', padding: '0.6rem 1.2rem', border: '1px solid rgba(255,215,0,0.2)', borderRadius: '8px', textDecoration: 'none'}}>在 GitHub 上查看</a>
|
||||
</div>
|
||||
|
||||
## 安装
|
||||
|
||||
**Linux / macOS / WSL2**
|
||||
|
||||
```bash
|
||||
curl -fsSL https://raw.githubusercontent.com/NousResearch/hermes-agent/main/scripts/install.sh | bash
|
||||
```
|
||||
|
||||
**Windows(原生,PowerShell)** — *早期测试版,[详情 →](/user-guide/windows-native)*
|
||||
|
||||
```powershell
|
||||
iex (irm https://raw.githubusercontent.com/NousResearch/hermes-agent/main/scripts/install.ps1)
|
||||
```
|
||||
|
||||
**Android(Termux)** — 与 Linux 相同的 curl 一行命令;安装程序会自动检测 Termux。
|
||||
|
||||
请参阅完整的 **[安装指南](/getting-started/installation)**,了解安装程序的具体操作、按用户与 root 的目录布局以及 Windows 相关说明。
|
||||
|
||||
## Hermes Agent 是什么?
|
||||
|
||||
它不是绑定在 IDE 上的编程副驾驶,也不是对单一 API 的聊天机器人封装。它是一个**自主智能体**,运行时间越长,能力越强。它可以部署在任何地方——5 美元的 VPS、GPU 集群,或者闲置时几乎零成本的 serverless 基础设施(Daytona、Modal)。在 Telegram 上与它对话,同时让它在你从未亲自 SSH 登录的云端虚拟机上工作。它不依赖你的本地电脑。
|
||||
|
||||
## 快速链接
|
||||
|
||||
| | |
|
||||
|---|---|
|
||||
| 🚀 **[安装](/getting-started/installation)** | 在 Linux、macOS、WSL2 或原生 Windows(早期测试版)上 60 秒完成安装 |
|
||||
| 📖 **[快速入门教程](/getting-started/quickstart)** | 第一次对话及值得尝试的核心功能 |
|
||||
| 🗺️ **[学习路径](/getting-started/learning-path)** | 根据你的经验水平找到合适的文档 |
|
||||
| ⚙️ **[配置](/user-guide/configuration)** | 配置文件、提供商、模型及选项 |
|
||||
| 💬 **[消息网关](/user-guide/messaging)** | 配置 Telegram、Discord、Slack、WhatsApp、Teams 等平台 |
|
||||
| 🔧 **[工具与工具集](/user-guide/features/tools)** | 70+ 内置工具及其配置方式 |
|
||||
| 🧠 **[记忆系统](/user-guide/features/memory)** | 跨会话持续增长的持久记忆 |
|
||||
| 📚 **[技能系统](/user-guide/features/skills)** | 智能体创建并复用的程序性记忆 |
|
||||
| 🔌 **[MCP 集成](/user-guide/features/mcp)** | 连接 MCP 服务器、过滤其工具,并安全扩展 Hermes |
|
||||
| 🧭 **[在 Hermes 中使用 MCP](/guides/use-mcp-with-hermes)** | 实用的 MCP 配置模式、示例与教程 |
|
||||
| 🎙️ **[语音模式](/user-guide/features/voice-mode)** | 在 CLI、Telegram、Discord 及 Discord 语音频道中进行实时语音交互 |
|
||||
| 🗣️ **[在 Hermes 中使用语音模式](/guides/use-voice-mode-with-hermes)** | Hermes 语音工作流的实操配置与使用模式 |
|
||||
| 🎭 **[个性与 SOUL.md](/user-guide/features/personality)** | 通过全局 SOUL.md 定义 Hermes 的默认风格 |
|
||||
| 📄 **[上下文文件](/user-guide/features/context-files)** | 影响每次对话的项目上下文文件 |
|
||||
| 🔒 **[安全](/user-guide/security)** | 命令审批、授权与容器隔离 |
|
||||
| 💡 **[技巧与最佳实践](/guides/tips)** | 快速上手,充分发挥 Hermes 的潜力 |
|
||||
| 🏗️ **[架构](/developer-guide/architecture)** | 底层工作原理 |
|
||||
| ❓ **[常见问题与故障排查](/reference/faq)** | 常见问题及解决方案 |
|
||||
|
||||
## 核心功能
|
||||
|
||||
- **闭环学习循环** — 智能体管理的记忆,配合定期提示、自主技能创建、使用中的技能自我改进、基于 FTS5 的跨会话召回与 LLM 摘要,以及 [Honcho](https://github.com/plastic-labs/honcho) 辩证式用户建模
|
||||
- **随处运行,不限于本地** — 6 种终端后端:本地、Docker、SSH、Daytona、Singularity、Modal。Daytona 和 Modal 提供 serverless 持久化——环境闲置时休眠,几乎零成本
|
||||
- **在你所在的地方** — CLI、Telegram、Discord、Slack、WhatsApp、Signal、Matrix、Mattermost、Email、SMS、DingTalk、Feishu、WeCom、Weixin、QQ Bot、Yuanbao、BlueBubbles、Home Assistant、Microsoft Teams、Google Chat 等——通过一个网关支持 20+ 平台
|
||||
- **由模型训练者构建** — 由 [Nous Research](https://nousresearch.com) 创建,该实验室是 Hermes、Nomos 和 Psyche 背后的团队。支持 [Nous Portal](https://portal.nousresearch.com)、[OpenRouter](https://openrouter.ai)、OpenAI 或任意端点
|
||||
- **定时自动化** — 内置 cron,可向任意平台投递
|
||||
- **委托与并行** — 派生隔离的子智能体以并行处理多个工作流。通过 `execute_code` 实现程序化工具调用,将多步骤流水线压缩为单次推理调用
|
||||
- **开放标准技能** — 兼容 [agentskills.io](https://agentskills.io)。技能可移植、可共享,并通过 Skills Hub 接受社区贡献
|
||||
- **完整的 Web 控制** — 搜索、提取、浏览、视觉、图像生成、TTS
|
||||
- **MCP 支持** — 连接任意 MCP 服务器以扩展工具能力
|
||||
- **研究就绪** — 批处理、轨迹导出、基于 Atropos 的 RL 训练。由 [Nous Research](https://nousresearch.com) 构建——该实验室是 Hermes、Nomos 和 Psyche 模型背后的团队
|
||||
|
||||
## 面向 LLM 和编程智能体
|
||||
|
||||
本文档的机器可读入口:
|
||||
|
||||
- **[`/llms.txt`](/llms.txt)** — 每个文档页面的精选索引,附简短描述。约 17 KB,可安全加载到 LLM 上下文中。
|
||||
- **[`/llms-full.txt`](/llms-full.txt)** — 所有文档页面拼接为单一 markdown 文件,支持一次性摄取。约 1.8 MB。
|
||||
|
||||
两个文件同样可通过 `/docs/llms.txt` 和 `/docs/llms-full.txt` 访问。每次部署时全新生成。
|
||||
@ -0,0 +1,100 @@
|
||||
---
|
||||
title: "集成"
|
||||
sidebar_label: "概览"
|
||||
sidebar_position: 0
|
||||
---
|
||||
|
||||
# 集成
|
||||
|
||||
Hermes Agent 可连接外部系统,用于 AI 推理、工具服务器、IDE 工作流、程序化访问等。这些集成扩展了 Hermes 的能力边界与运行环境。
|
||||
|
||||
## AI 提供商与路由
|
||||
|
||||
Hermes 开箱即支持多个 AI 推理提供商。使用 `hermes model` 进行交互式配置,或在 `config.yaml` 中直接设置。
|
||||
|
||||
- **[AI 提供商](/user-guide/features/provider-routing)** — OpenRouter、Anthropic、OpenAI、Google 以及任何兼容 OpenAI 的端点。Hermes 会自动检测每个提供商的能力,包括视觉、流式传输和工具调用。
|
||||
- **[提供商路由](/user-guide/features/provider-routing)** — 精细控制哪些底层提供商处理你的 OpenRouter 请求。通过排序、白名单、黑名单和显式优先级排序,在成本、速度或质量之间优化。
|
||||
- **[备用提供商](/user-guide/features/fallback-providers)** — 当主模型遇到错误时,自动故障转移到备用 LLM 提供商。包括主模型回退,以及用于视觉、压缩和网页提取的独立辅助任务回退。
|
||||
|
||||
## 工具服务器(MCP)
|
||||
|
||||
- **[MCP 服务器](/user-guide/features/mcp)** — 通过 Model Context Protocol 将 Hermes 连接到外部工具服务器。无需编写原生 Hermes 工具,即可访问来自 GitHub、数据库、文件系统、浏览器栈、内部 API 等的工具。支持 stdio 和 SSE 两种传输方式、按服务器过滤工具,以及具备能力感知的资源/prompt 注册。
|
||||
|
||||
## 网页搜索后端
|
||||
|
||||
`web_search` 和 `web_extract` 工具支持四个后端提供商,通过 `config.yaml` 或 `hermes tools` 配置:
|
||||
|
||||
| 后端 | 环境变量 | 搜索 | 提取 | 爬取 |
|
||||
|---------|---------|--------|---------|-------|
|
||||
| **Firecrawl**(默认) | `FIRECRAWL_API_KEY` | ✔ | ✔ | ✔ |
|
||||
| **Parallel** | `PARALLEL_API_KEY` | ✔ | ✔ | — |
|
||||
| **Tavily** | `TAVILY_API_KEY` | ✔ | ✔ | ✔ |
|
||||
| **Exa** | `EXA_API_KEY` | ✔ | ✔ | — |
|
||||
|
||||
快速配置示例:
|
||||
|
||||
```yaml
|
||||
web:
|
||||
backend: firecrawl # firecrawl | parallel | tavily | exa
|
||||
```
|
||||
|
||||
若未设置 `web.backend`,后端将根据可用的 API key 自动检测。也支持通过 `FIRECRAWL_API_URL` 使用自托管的 Firecrawl。
|
||||
|
||||
## 浏览器自动化
|
||||
|
||||
Hermes 内置完整的浏览器自动化功能,提供多种后端选项,用于网站导航、表单填写和信息提取:
|
||||
|
||||
- **Browserbase** — 托管云端浏览器,具备反机器人工具、CAPTCHA 解决和住宅代理
|
||||
- **Browser Use** — 备选云端浏览器提供商
|
||||
- **本地 Chromium 系 CDP** — 使用 `/browser connect` 连接正在运行的 Chrome、Brave、Chromium 或 Edge 浏览器
|
||||
- **本地 Chromium** — 通过 `agent-browser` CLI 使用无头本地浏览器
|
||||
|
||||
详见[浏览器自动化](/user-guide/features/browser)的配置与使用说明。
|
||||
|
||||
## 语音与 TTS 提供商
|
||||
|
||||
跨所有消息平台的文字转语音与语音转文字:
|
||||
|
||||
| 提供商 | 质量 | 费用 | API Key |
|
||||
|----------|---------|------|---------|
|
||||
| **Edge TTS**(默认) | 良好 | 免费 | 无需 |
|
||||
| **ElevenLabs** | 优秀 | 付费 | `ELEVENLABS_API_KEY` |
|
||||
| **OpenAI TTS** | 良好 | 付费 | `VOICE_TOOLS_OPENAI_KEY` |
|
||||
| **MiniMax** | 良好 | 付费 | `MINIMAX_API_KEY` |
|
||||
| **NeuTTS** | 良好 | 免费 | 无需 |
|
||||
|
||||
语音转文字支持六个提供商:本地 faster-whisper(免费,设备端运行)、本地命令封装器、Groq、OpenAI Whisper API、Mistral 和 xAI。语音消息转录支持 Telegram、Discord、WhatsApp 及其他消息平台。详见[语音与 TTS](/user-guide/features/tts) 和[语音模式](/user-guide/features/voice-mode)。
|
||||
|
||||
## IDE 与编辑器集成
|
||||
|
||||
- **[IDE 集成(ACP)](/user-guide/features/acp)** — 在兼容 ACP 的编辑器(如 VS Code、Zed 和 JetBrains)中使用 Hermes Agent。Hermes 作为 ACP 服务器运行,在编辑器内渲染聊天消息、工具活动、文件差异和终端命令。
|
||||
|
||||
## 程序化访问
|
||||
|
||||
- **[API 服务器](/user-guide/features/api-server)** — 将 Hermes 暴露为兼容 OpenAI 的 HTTP 端点。任何支持 OpenAI 格式的前端——Open WebUI、LobeChat、LibreChat、NextChat、ChatBox——均可连接并将 Hermes 作为后端使用,享有其完整工具集。
|
||||
|
||||
## 记忆与个性化
|
||||
|
||||
- **[内置记忆](/user-guide/features/memory)** — 通过 `MEMORY.md` 和 `USER.md` 文件实现持久化、精选记忆。Agent 维护有界的个人笔记和用户画像数据存储,跨会话保留。
|
||||
- **[记忆提供商](/user-guide/features/memory-providers)** — 接入外部记忆后端以实现更深度的个性化。支持八个提供商:Honcho(辩证推理)、OpenViking(分层检索)、Mem0(云端提取)、Hindsight(知识图谱)、Holographic(本地 SQLite)、RetainDB(混合搜索)、ByteRover(基于 CLI)和 Supermemory。
|
||||
|
||||
## 消息平台
|
||||
|
||||
Hermes 可作为 gateway(网关)机器人运行于 19+ 个消息平台,均通过同一 `gateway` 子系统配置:
|
||||
|
||||
- **[Telegram](/user-guide/messaging/telegram)**、**[Discord](/user-guide/messaging/discord)**、**[Slack](/user-guide/messaging/slack)**、**[WhatsApp](/user-guide/messaging/whatsapp)**、**[Signal](/user-guide/messaging/signal)**、**[Matrix](/user-guide/messaging/matrix)**、**[Mattermost](/user-guide/messaging/mattermost)**、**[Email](/user-guide/messaging/email)**、**[SMS](/user-guide/messaging/sms)**、**[DingTalk](/user-guide/messaging/dingtalk)**、**[Feishu/Lark](/user-guide/messaging/feishu)**、**[WeCom](/user-guide/messaging/wecom)**、**[WeCom Callback](/user-guide/messaging/wecom-callback)**、**[Weixin](/user-guide/messaging/weixin)**、**[BlueBubbles](/user-guide/messaging/bluebubbles)**、**[QQ Bot](/user-guide/messaging/qqbot)**、**[Yuanbao](/user-guide/messaging/yuanbao)**、**[Home Assistant](/user-guide/messaging/homeassistant)**、**[Microsoft Teams](/user-guide/messaging/teams)**、**[Webhooks](/user-guide/messaging/webhooks)**
|
||||
|
||||
平台对比表和配置指南详见[消息 Gateway 概览](/user-guide/messaging)。
|
||||
|
||||
## 家庭自动化
|
||||
|
||||
- **[Home Assistant](/user-guide/messaging/homeassistant)** — 通过四个专用工具(`ha_list_entities`、`ha_get_state`、`ha_list_services`、`ha_call_service`)控制智能家居设备。配置 `HASS_TOKEN` 后,Home Assistant 工具集将自动激活。
|
||||
|
||||
## 插件
|
||||
|
||||
- **[插件系统](/user-guide/features/plugins)** — 无需修改核心代码,通过自定义工具、生命周期 hook(钩子)和 CLI 命令扩展 Hermes。插件从 `~/.hermes/plugins/`、项目本地 `.hermes/plugins/` 以及通过 pip 安装的入口点自动发现。
|
||||
- **[构建插件](/guides/build-a-hermes-plugin)** — 创建包含工具、hook 和 CLI 命令的 Hermes 插件的分步指南。
|
||||
|
||||
## 训练与评估
|
||||
|
||||
- **[批处理](/user-guide/features/batch-processing)** — 并行跨数百个 prompt(提示词)运行 Agent,生成结构化的 ShareGPT 格式轨迹数据,用于训练数据生成或评估。
|
||||
@ -0,0 +1,268 @@
|
||||
---
|
||||
sidebar_position: 1
|
||||
title: "Nous Portal"
|
||||
description: "一个订阅,300+ 前沿模型,Tool Gateway,以及 Nous Chat —— 运行 Hermes Agent 的推荐方式"
|
||||
---
|
||||
|
||||
# Nous Portal
|
||||
|
||||
[Nous Portal](https://portal.nousresearch.com) 是 Nous Research 的统一订阅网关,也是**运行 Hermes Agent 的推荐方式**。一次 OAuth 登录,即可替代原本需要手动配置的各模型厂商独立账号、API 密钥和计费关系。
|
||||
|
||||
如果你只有时间配置一件事,就配置这个。最快路径:
|
||||
|
||||
```bash
|
||||
hermes setup --portal
|
||||
```
|
||||
|
||||
这条命令会完成 Portal OAuth 认证,在 `config.yaml` 中将 Nous 设为推理提供商,并开启 Tool Gateway。完成后即可立即运行 `hermes chat`。
|
||||
|
||||
还没有订阅?前往 [portal.nousresearch.com/manage-subscription](https://portal.nousresearch.com/manage-subscription) 注册,然后回来运行上面的命令。
|
||||
|
||||
## 订阅包含的内容
|
||||
|
||||
### 300+ 前沿模型,统一账单
|
||||
|
||||
Portal 代理了来自整个生态系统的精选 agentic 模型目录——统一计入你的 Nous 订阅,而非每个厂商单独充值。
|
||||
|
||||
| 系列 | 模型 |
|
||||
|--------|--------|
|
||||
| **Anthropic Claude** | Opus、Sonnet、Haiku(4.x 系列) |
|
||||
| **OpenAI** | GPT-5.4、o 系列推理模型 |
|
||||
| **Google Gemini** | 2.5 Pro、2.5 Flash |
|
||||
| **DeepSeek** | DeepSeek V3.2、DeepSeek-R1 |
|
||||
| **Qwen** | Qwen3 系列、Qwen Coder |
|
||||
| **Kimi / Moonshot** | Kimi-K2、Kimi-Latest |
|
||||
| **GLM / Zhipu** | GLM-4.6、GLM-4-Plus |
|
||||
| **MiniMax** | M2.7、M1 |
|
||||
| **xAI** | Grok-4、Grok-3 |
|
||||
| **Hermes** | Hermes-4-70B、Hermes-4-405B(对话,见[下方说明](#a-note-on-hermes-4)) |
|
||||
| **+ 其他所有模型** | 240+ 额外模型——完整的 agentic 前沿生态 |
|
||||
|
||||
底层路由通过 OpenRouter 实现,因此模型可用性和故障转移行为与使用 OpenRouter 密钥一致——只是计费走你的 Nous 订阅。在会话中途用 `/model` 即可在 Claude Sonnet 4.6(适合代码)和 Gemini 2.5 Pro(适合长上下文)之间切换——无需新凭证,无需充值,不会遇到余额为零的意外报错。
|
||||
|
||||
### Nous Tool Gateway
|
||||
|
||||
同一订阅还解锁了 [Tool Gateway](/user-guide/features/tool-gateway),将 Hermes Agent 的工具调用路由至 Nous 托管的基础设施。五个后端,一次登录:
|
||||
|
||||
| 工具 | 合作方 | 功能说明 |
|
||||
|------|---------|--------------|
|
||||
| **网页搜索与抓取** | Firecrawl | Agent 级搜索与整页内容提取。无需 Firecrawl API 密钥,无需管理速率限制。 |
|
||||
| **图像生成** | FAL | 单一端点下的九个模型:FLUX 2 Klein 9B、FLUX 2 Pro、Z-Image Turbo、Nano Banana Pro(Gemini 3 Pro Image)、GPT Image 1.5、GPT Image 2、Ideogram V3、Recraft V4 Pro、Qwen Image。 |
|
||||
| **文字转语音** | OpenAI TTS | 无需独立 OpenAI 密钥的高质量 TTS。在各消息平台上启用[语音模式](/user-guide/features/voice-mode)。 |
|
||||
| **云端浏览器自动化** | Browser Use | 用于 `browser_navigate`、`browser_click`、`browser_type`、`browser_vision` 的无头 Chromium 会话。无需 Browserbase 账号。 |
|
||||
| **云端终端沙箱** | Modal | 用于代码执行的无服务器终端沙箱(可选附加项)。 |
|
||||
|
||||
不使用 gateway 的话,接入上述每项服务意味着:一个 Firecrawl 账号、一个 FAL 账号、一个 Browser Use 账号、一个 OpenAI 密钥、一个 Modal 账号——五次独立注册、五个独立控制台、五套独立充值流程。使用 gateway 后,所有内容通过一个订阅统一路由。
|
||||
|
||||
你也可以只启用特定的 gateway 工具(例如只开启网页搜索,不开启图像生成)——详见下方[将 gateway 与自有后端混用](#mixing-the-gateway-with-your-own-backends)。
|
||||
|
||||
### Nous Chat
|
||||
|
||||
你的 Portal 账号同样覆盖 [chat.nousresearch.com](https://chat.nousresearch.com)——Nous Research 的网页对话界面,使用相同的模型目录。适合离开终端时使用,或用于非 agent 的普通对话场景。
|
||||
|
||||
### 凭证不落入 dotfiles
|
||||
|
||||
由于所有请求都通过一个经 OAuth 认证的 Portal 会话路由,你不会积累一个包含十几个长期 API 密钥的 `.env` 文件。磁盘上唯一的凭证是 `~/.hermes/auth.json` 中的 refresh token(刷新令牌),Hermes 会在每次请求时从中生成短期 JWT——详见下方[令牌处理](#token-handling)。
|
||||
|
||||
### 跨平台一致性
|
||||
|
||||
[原生 Windows](/user-guide/windows-native) 仍处于早期 beta 阶段,逐个配置 API 密钥是其最大痛点——在 Windows 上分别安装 Firecrawl 账号、FAL 账号、Browser Use 账号、OpenAI 密钥,是整个 agent 配置过程中摩擦最高的部分。Portal 订阅消除了这一问题:一次 OAuth 覆盖模型和所有 gateway 工具,Windows 用户无需手动配置四个后端,即可获得与 macOS/Linux 相同的体验。
|
||||
|
||||
## 关于 Hermes 4 的说明
|
||||
|
||||
Nous Research 自家的 **Hermes 4** 系列(Hermes-4-70B、Hermes-4-405B)通过 Portal 提供,享有大幅折扣。这些是**前沿混合推理对话模型**——在数学、科学、指令遵循、schema 遵从、角色扮演和长文写作方面表现出色。
|
||||
|
||||
但**不建议在 Hermes Agent 内部使用它们**。Hermes 4 针对对话和推理进行了调优,而非 agent 所依赖的高频工具调用循环。请将它们用于 [Nous Chat](https://chat.nousresearch.com)、研究工作流,或通过[订阅代理](/user-guide/features/subscription-proxy)从其他工具调用——但在 agent 场景下,请从目录中选择前沿 agentic 模型:
|
||||
|
||||
```bash
|
||||
/model anthropic/claude-sonnet-4.6 # 最佳通用 agentic 模型
|
||||
/model openai/gpt-5.4 # 强推理 + 工具调用
|
||||
/model google/gemini-2.5-pro # 超大上下文窗口
|
||||
/model deepseek/deepseek-v3.2 # 高性价比代码模型
|
||||
```
|
||||
|
||||
Portal 自身的[模型信息页](https://portal.nousresearch.com/info)也有相同警告,因此这不是 Hermes 侧的主观意见——这是 Nous Research 的官方指导。
|
||||
|
||||
## 配置
|
||||
|
||||
### 全新安装——一条命令
|
||||
|
||||
```bash
|
||||
hermes setup --portal
|
||||
```
|
||||
|
||||
一次性完成全部配置:
|
||||
|
||||
1. 打开浏览器跳转至 portal.nousresearch.com 进行 OAuth 登录
|
||||
2. 将 refresh token 存储至 `~/.hermes/auth.json`
|
||||
3. 在 `~/.hermes/config.yaml` 中将 Nous 设为推理提供商
|
||||
4. 开启 Tool Gateway(网页、图像、TTS、浏览器路由)
|
||||
5. 返回终端,即可运行 `hermes chat`
|
||||
|
||||
如果还没有订阅,请先在 [portal.nousresearch.com/manage-subscription](https://portal.nousresearch.com/manage-subscription) 注册。
|
||||
|
||||
### 已有安装——在现有提供商旁添加 Portal
|
||||
|
||||
如果你已经配置了 OpenRouter、Anthropic 或其他提供商,想在此基础上添加 Portal:
|
||||
|
||||
```bash
|
||||
hermes model
|
||||
# 从提供商列表中选择 "Nous Portal"
|
||||
# 浏览器打开,登录,完成
|
||||
```
|
||||
|
||||
你现有的提供商配置保持不变。可以在会话中途用 `/model` 切换,或在会话间用 `hermes model` 切换——Portal 成为你的可用提供商之一,而非唯一选项。
|
||||
|
||||
### 无头环境 / SSH / 远程配置
|
||||
|
||||
OAuth 需要浏览器,但回调的 loopback 运行在 Hermes 所在的机器上。对于远程主机,请参阅 [OAuth over SSH / 远程主机](/guides/oauth-over-ssh)——与其他基于 OAuth 的提供商相同的方式同样适用于 Portal(`ssh -L` 端口转发,或在 Cloud Shell / Codespaces 等纯浏览器环境中使用 `--manual-paste`)。
|
||||
|
||||
### Profile 配置
|
||||
|
||||
如果你使用 [Hermes profiles(配置文件)](/user-guide/profiles),Portal 的 refresh token 会通过共享令牌存储自动在所有 profile 间共享。在任意 profile 上登录一次,其余 profile 自动获取——无需为每个 profile 重复 OAuth 流程。
|
||||
|
||||
## 日常使用 Portal
|
||||
|
||||
### 查看当前配置状态
|
||||
|
||||
```bash
|
||||
hermes portal status # 登录状态、订阅信息、模型与 gateway 路由
|
||||
hermes portal tools # 详细的 Tool Gateway 目录及每个工具的路由信息
|
||||
hermes portal open # 在浏览器中打开订阅管理页面
|
||||
```
|
||||
|
||||
`hermes portal status`(或直接 `hermes portal`)给出高层概览:
|
||||
|
||||
```
|
||||
Nous Portal
|
||||
───────────
|
||||
Auth: ✓ logged in
|
||||
Portal: https://portal.nousresearch.com
|
||||
Model: ✓ using Nous as inference provider
|
||||
|
||||
Tool Gateway
|
||||
────────────
|
||||
Web search & extract via Nous Portal
|
||||
Image generation via Nous Portal
|
||||
Text-to-speech via Nous Portal
|
||||
Browser automation via Nous Portal
|
||||
Cloud terminal not configured
|
||||
```
|
||||
|
||||
### 切换模型
|
||||
|
||||
在会话中:
|
||||
|
||||
```bash
|
||||
/model anthropic/claude-sonnet-4.6
|
||||
/model openai/gpt-5.4
|
||||
/model google/gemini-2.5-pro
|
||||
```
|
||||
|
||||
或打开选择器:
|
||||
|
||||
```bash
|
||||
/model
|
||||
# 方向键选择,回车确认
|
||||
```
|
||||
|
||||
在会话外(完整配置向导,适合添加新提供商时使用):
|
||||
|
||||
```bash
|
||||
hermes model
|
||||
```
|
||||
|
||||
### 将 gateway 与自有后端混用
|
||||
|
||||
如果你已有 Browserbase 账号并希望继续使用,同时通过 Nous 路由网页搜索和图像生成,这是支持的。使用 `hermes tools` 为每个工具单独选择后端:
|
||||
|
||||
```bash
|
||||
hermes tools
|
||||
# → 网页搜索 → "Nous Subscription"
|
||||
# → 图像生成 → "Nous Subscription"
|
||||
# → 浏览器 → "Browserbase"(你的现有密钥)
|
||||
# → TTS → "Nous Subscription"
|
||||
```
|
||||
|
||||
Tool Gateway 是按工具单独选择启用的,而非全部或全不。完整的每工具配置矩阵请参阅 [Tool Gateway 文档](/user-guide/features/tool-gateway)。
|
||||
|
||||
### 订阅管理
|
||||
|
||||
随时管理套餐、查看用量或升级/取消:
|
||||
|
||||
- **网页端:** [portal.nousresearch.com/manage-subscription](https://portal.nousresearch.com/manage-subscription)
|
||||
- **CLI 快捷方式:** `hermes portal open`(在默认浏览器中打开同一页面)
|
||||
|
||||
## 配置参考
|
||||
|
||||
运行 `hermes setup --portal` 后,`~/.hermes/config.yaml` 将如下所示:
|
||||
|
||||
```yaml
|
||||
model:
|
||||
provider: nous
|
||||
default: anthropic/claude-sonnet-4.6 # 或你选择的其他模型
|
||||
base_url: https://inference.nousresearch.com/v1
|
||||
```
|
||||
|
||||
Tool Gateway 设置位于各自工具的配置节下:
|
||||
|
||||
```yaml
|
||||
web:
|
||||
backend: nous # 网页搜索/抓取通过 Tool Gateway 路由
|
||||
|
||||
image_gen:
|
||||
provider: nous
|
||||
|
||||
tts:
|
||||
provider: nous
|
||||
|
||||
browser:
|
||||
backend: nous
|
||||
```
|
||||
|
||||
OAuth refresh token 单独存储在 `~/.hermes/auth.json`(不在 `config.yaml` 中——凭证与配置有意分开存放)。
|
||||
|
||||
## 令牌处理
|
||||
|
||||
Hermes 在每次推理调用时从存储的 Portal refresh token 生成短期 JWT,而非复用长期 API 密钥。令牌生命周期完全自动管理——刷新、生成、在瞬时 401 时重试——你无需关心这些细节。
|
||||
|
||||
如果 Portal 使 refresh token 失效(修改密码、手动撤销、会话过期),失效的 refresh token 会被**本地隔离**,Hermes 停止重放该令牌,你不会看到一连串相同的 401 错误。下一次调用会显示清晰的"需要重新认证"提示。运行 `hermes auth add nous` 重新登录;隔离状态在下次成功登录时自动清除。
|
||||
|
||||
## 故障排查
|
||||
|
||||
### `hermes portal status` 显示"not logged in"
|
||||
|
||||
你尚未完成 OAuth 流程,或 refresh token 已被清除。运行:
|
||||
|
||||
```bash
|
||||
hermes auth add nous --type oauth
|
||||
```
|
||||
|
||||
或使用 `hermes model` 重新选择 Nous Portal。
|
||||
|
||||
### 会话中途收到"需要重新认证"提示
|
||||
|
||||
你的 Portal refresh token 已失效(修改密码、手动撤销或会话过期)。运行 `hermes auth add nous`,下一次请求将使用新凭证。旧令牌的隔离状态在成功重新登录后自动清除。
|
||||
|
||||
### 想使用 Portal 未暴露的特定提供商模型
|
||||
|
||||
Portal 通过 OpenRouter 代理,因此 OpenRouter 支持的所有模型通常都可用。如果某个模型未出现在 `/model` 中,可直接尝试 OpenRouter 风格的 slug:
|
||||
|
||||
```bash
|
||||
/model anthropic/claude-opus-4.6
|
||||
```
|
||||
|
||||
如果某个模型确实缺失,请[提交 issue](https://github.com/NousResearch/hermes-agent/issues)——我们将 Portal 目录同步至 Hermes,缺口通常意味着可以更新的路由配置。
|
||||
|
||||
### 账单未出现在我的 Portal 账号中
|
||||
|
||||
先检查 `hermes portal status`——如果显示你正在使用其他提供商(`Model: currently openrouter` 而非 `using Nous as inference provider`),说明本地配置已偏离。运行 `hermes model`,选择 Nous Portal,下一次请求将通过你的订阅路由。
|
||||
|
||||
## 另请参阅
|
||||
|
||||
- **[Tool Gateway](/user-guide/features/tool-gateway)** —— 每个 gateway 工具的完整详情、每工具配置及定价
|
||||
- **[订阅代理](/user-guide/features/subscription-proxy)** —— 在非 Hermes 工具(其他 agent、脚本、第三方客户端)中使用你的 Portal 订阅
|
||||
- **[语音模式](/user-guide/features/voice-mode)** —— 使用 Portal 的 OpenAI TTS 进行语音对话
|
||||
- **[AI 提供商](/integrations/providers)** —— 完整提供商目录,供对比参考
|
||||
- **[OAuth over SSH](/guides/oauth-over-ssh)** —— 从远程主机或纯浏览器环境登录
|
||||
- **[Profiles](/user-guide/profiles)** —— 多个 Hermes 配置共享一个 Portal 登录
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,661 @@
|
||||
---
|
||||
sidebar_position: 2
|
||||
title: "环境变量"
|
||||
description: "Hermes Agent 使用的所有环境变量完整参考"
|
||||
---
|
||||
|
||||
# 环境变量参考
|
||||
|
||||
所有变量均写入 `~/.hermes/.env`。也可以使用 `hermes config set VAR value` 进行设置。
|
||||
|
||||
## LLM 提供商
|
||||
|
||||
| 变量 | 描述 |
|
||||
|----------|-------------|
|
||||
| `OPENROUTER_API_KEY` | OpenRouter API 密钥(推荐,灵活性强) |
|
||||
| `OPENROUTER_BASE_URL` | 覆盖 OpenRouter 兼容的 base URL |
|
||||
| `HERMES_OPENROUTER_CACHE` | 启用 OpenRouter 响应缓存(`1`/`true`/`yes`/`on`)。覆盖 config.yaml 中的 `openrouter.response_cache`。参见 [Response Caching](https://openrouter.ai/docs/guides/features/response-caching)。 |
|
||||
| `HERMES_OPENROUTER_CACHE_TTL` | 缓存 TTL(秒,1-86400)。覆盖 config.yaml 中的 `openrouter.response_cache_ttl`。 |
|
||||
| `NOUS_BASE_URL` | 覆盖 Nous Portal base URL(极少使用;仅用于开发/测试) |
|
||||
| `NOUS_INFERENCE_BASE_URL` | 直接覆盖 Nous 推理端点 |
|
||||
| `AI_GATEWAY_API_KEY` | Vercel AI Gateway API 密钥([ai-gateway.vercel.sh](https://ai-gateway.vercel.sh)) |
|
||||
| `AI_GATEWAY_BASE_URL` | 覆盖 AI Gateway base URL(默认:`https://ai-gateway.vercel.sh/v1`) |
|
||||
| `OPENAI_API_KEY` | 自定义 OpenAI 兼容端点的 API 密钥(与 `OPENAI_BASE_URL` 配合使用) |
|
||||
| `OPENAI_BASE_URL` | 自定义端点的 base URL(VLLM、SGLang 等) |
|
||||
| `COPILOT_GITHUB_TOKEN` | 用于 Copilot API 的 GitHub token——最高优先级(OAuth `gho_*` 或细粒度 PAT `github_pat_*`;经典 PAT `ghp_*` **不支持**) |
|
||||
| `GH_TOKEN` | GitHub token——Copilot 第二优先级(也供 `gh` CLI 使用) |
|
||||
| `GITHUB_TOKEN` | GitHub token——Copilot 第三优先级 |
|
||||
| `HERMES_COPILOT_ACP_COMMAND` | 覆盖 Copilot ACP CLI 二进制路径(默认:`copilot`) |
|
||||
| `COPILOT_CLI_PATH` | `HERMES_COPILOT_ACP_COMMAND` 的别名 |
|
||||
| `HERMES_COPILOT_ACP_ARGS` | 覆盖 Copilot ACP 参数(默认:`--acp --stdio`) |
|
||||
| `COPILOT_ACP_BASE_URL` | 覆盖 Copilot ACP base URL |
|
||||
| `GLM_API_KEY` | z.ai / ZhipuAI GLM API 密钥([z.ai](https://z.ai)) |
|
||||
| `ZAI_API_KEY` | `GLM_API_KEY` 的别名 |
|
||||
| `Z_AI_API_KEY` | `GLM_API_KEY` 的别名 |
|
||||
| `GLM_BASE_URL` | 覆盖 z.ai base URL(默认:`https://api.z.ai/api/paas/v4`) |
|
||||
| `KIMI_API_KEY` | Kimi / Moonshot AI API 密钥([moonshot.ai](https://platform.moonshot.ai)) |
|
||||
| `KIMI_BASE_URL` | 覆盖 Kimi base URL(默认:`https://api.moonshot.ai/v1`) |
|
||||
| `KIMI_CN_API_KEY` | Kimi / Moonshot 中国区 API 密钥([moonshot.cn](https://platform.moonshot.cn)) |
|
||||
| `ARCEEAI_API_KEY` | Arcee AI API 密钥([chat.arcee.ai](https://chat.arcee.ai/)) |
|
||||
| `ARCEE_BASE_URL` | 覆盖 Arcee base URL(默认:`https://api.arcee.ai/api/v1`) |
|
||||
| `GMI_API_KEY` | GMI Cloud API 密钥([gmicloud.ai](https://www.gmicloud.ai/)) |
|
||||
| `GMI_BASE_URL` | 覆盖 GMI Cloud base URL(默认:`https://api.gmi-serving.com/v1`) |
|
||||
| `MINIMAX_API_KEY` | MiniMax API 密钥——全球端点([minimax.io](https://www.minimax.io))。**`minimax-oauth` 不使用此变量**(OAuth 路径通过浏览器登录)。 |
|
||||
| `MINIMAX_BASE_URL` | 覆盖 MiniMax base URL(默认:`https://api.minimax.io/anthropic`——Hermes 使用 MiniMax 的 Anthropic Messages 兼容端点)。**`minimax-oauth` 不使用此变量**。 |
|
||||
| `MINIMAX_CN_API_KEY` | MiniMax API 密钥——中国区端点([minimaxi.com](https://www.minimaxi.com))。**`minimax-oauth` 不使用此变量**(OAuth 路径通过浏览器登录)。 |
|
||||
| `MINIMAX_CN_BASE_URL` | 覆盖 MiniMax 中国区 base URL(默认:`https://api.minimaxi.com/anthropic`)。**`minimax-oauth` 不使用此变量**。 |
|
||||
| `KILOCODE_API_KEY` | Kilo Code API 密钥([kilo.ai](https://kilo.ai)) |
|
||||
| `KILOCODE_BASE_URL` | 覆盖 Kilo Code base URL(默认:`https://api.kilo.ai/api/gateway`) |
|
||||
| `XIAOMI_API_KEY` | 小米 MiMo API 密钥([platform.xiaomimimo.com](https://platform.xiaomimimo.com)) |
|
||||
| `XIAOMI_BASE_URL` | 覆盖小米 MiMo base URL(默认:`https://api.xiaomimimo.com/v1`) |
|
||||
| `TOKENHUB_API_KEY` | 腾讯 TokenHub API 密钥([tokenhub.tencentmaas.com](https://tokenhub.tencentmaas.com)) |
|
||||
| `TOKENHUB_BASE_URL` | 覆盖腾讯 TokenHub base URL(默认:`https://tokenhub.tencentmaas.com/v1`) |
|
||||
| `AZURE_FOUNDRY_API_KEY` | Microsoft Foundry / Azure OpenAI API 密钥([ai.azure.com](https://ai.azure.com/))。当 `model.auth_mode: entra_id` 时不需要 |
|
||||
| `AZURE_FOUNDRY_BASE_URL` | Microsoft Foundry 端点 URL(例如 OpenAI 风格:`https://<resource>.openai.azure.com/openai/v1`,Anthropic 风格:`https://<resource>.services.ai.azure.com/anthropic`) |
|
||||
| `AZURE_ANTHROPIC_KEY` | 用于 `provider: anthropic` + `base_url` 指向 Microsoft Foundry Claude 部署的 Azure Anthropic API 密钥(当同时配置了 Anthropic 和 Azure Anthropic 时,作为 `ANTHROPIC_API_KEY` 的替代) |
|
||||
| `AZURE_TENANT_ID` | Entra ID 租户 ID(服务主体流程;当 `model.auth_mode: entra_id` 时由 `azure-identity` 读取) |
|
||||
| `AZURE_CLIENT_ID` | Entra ID 客户端 ID(服务主体、工作负载标识或用户分配的托管标识) |
|
||||
| `AZURE_CLIENT_SECRET` | `EnvironmentCredential` 使用的服务主体密钥 |
|
||||
| `AZURE_CLIENT_CERTIFICATE_PATH` | 服务主体证书(`AZURE_CLIENT_SECRET` 的替代方案) |
|
||||
| `AZURE_FEDERATED_TOKEN_FILE` | AKS Workload Identity / OIDC 流程的联合 token 文件路径 |
|
||||
| `AZURE_AUTHORITY_HOST` | 主权云 authority 覆盖(例如 Azure Government 使用 `https://login.microsoftonline.us`)。参见 [Azure Foundry 指南](/guides/azure-foundry#sovereign-clouds-government-china) |
|
||||
| `IDENTITY_ENDPOINT` / `MSI_ENDPOINT` | App Service、Functions 和 Container Apps 的托管标识端点;VM 通常使用 IMDS 而不设置这些变量 |
|
||||
| `HF_TOKEN` | Hugging Face Inference Providers token([huggingface.co/settings/tokens](https://huggingface.co/settings/tokens)) |
|
||||
| `HF_BASE_URL` | 覆盖 Hugging Face base URL(默认:`https://router.huggingface.co/v1`) |
|
||||
| `GOOGLE_API_KEY` | Google AI Studio API 密钥([aistudio.google.com/app/apikey](https://aistudio.google.com/app/apikey)) |
|
||||
| `GEMINI_API_KEY` | `GOOGLE_API_KEY` 的别名 |
|
||||
| `GEMINI_BASE_URL` | 覆盖 Google AI Studio base URL |
|
||||
| `HERMES_GEMINI_CLIENT_ID` | `google-gemini-cli` PKCE 登录的 OAuth 客户端 ID(可选;默认使用 Google 公共 gemini-cli 客户端) |
|
||||
| `HERMES_GEMINI_CLIENT_SECRET` | `google-gemini-cli` 的 OAuth 客户端密钥(可选) |
|
||||
| `HERMES_GEMINI_PROJECT_ID` | 付费 Gemini 层级的 GCP 项目 ID(免费层级自动配置) |
|
||||
| `ANTHROPIC_API_KEY` | Anthropic Console API 密钥([console.anthropic.com](https://console.anthropic.com/)) |
|
||||
| `ANTHROPIC_TOKEN` | 手动或旧版 Anthropic OAuth/setup-token 覆盖 |
|
||||
| `DASHSCOPE_API_KEY` | Qwen Cloud(阿里巴巴 DashScope)Qwen 模型 API 密钥([modelstudio.console.alibabacloud.com](https://modelstudio.console.alibabacloud.com/)) |
|
||||
| `DASHSCOPE_BASE_URL` | 自定义 DashScope base URL(默认:`https://dashscope-intl.aliyuncs.com/compatible-mode/v1`;中国大陆区域使用 `https://dashscope.aliyuncs.com/compatible-mode/v1`) |
|
||||
| `DEEPSEEK_API_KEY` | 直接访问 DeepSeek 的 API 密钥([platform.deepseek.com](https://platform.deepseek.com/api_keys)) |
|
||||
| `DEEPSEEK_BASE_URL` | 自定义 DeepSeek API base URL |
|
||||
| `NOVITA_API_KEY` | NovitaAI API 密钥——面向 Model API、Agent Sandbox 和 GPU Cloud 的 AI 原生云([novita.ai/settings/key-management](https://novita.ai/settings/key-management)) |
|
||||
| `NOVITA_BASE_URL` | 覆盖 NovitaAI base URL(默认:`https://api.novita.ai/openai/v1`) |
|
||||
| `NVIDIA_API_KEY` | NVIDIA NIM API 密钥——Nemotron 及开源模型([build.nvidia.com](https://build.nvidia.com)) |
|
||||
| `NVIDIA_BASE_URL` | 覆盖 NVIDIA base URL(默认:`https://integrate.api.nvidia.com/v1`;本地 NIM 端点设为 `http://localhost:8000/v1`) |
|
||||
| `STEPFUN_API_KEY` | StepFun API 密钥——Step 系列模型([platform.stepfun.com](https://platform.stepfun.com)) |
|
||||
| `STEPFUN_BASE_URL` | 覆盖 StepFun base URL(默认:`https://api.stepfun.com/v1`) |
|
||||
| `OLLAMA_API_KEY` | Ollama Cloud API 密钥——无需本地 GPU 的托管 Ollama 目录([ollama.com/settings/keys](https://ollama.com/settings/keys)) |
|
||||
| `OLLAMA_BASE_URL` | 覆盖 Ollama Cloud base URL(默认:`https://ollama.com/v1`) |
|
||||
| `XAI_API_KEY` | xAI(Grok)API 密钥,支持聊天、TTS 和网络搜索([console.x.ai](https://console.x.ai/)) |
|
||||
| `XAI_BASE_URL` | 覆盖 xAI base URL(默认:`https://api.x.ai/v1`) |
|
||||
| `MISTRAL_API_KEY` | Mistral API 密钥,用于 Voxtral TTS 和 Voxtral STT([console.mistral.ai](https://console.mistral.ai)) |
|
||||
| `AWS_REGION` | Bedrock 推理的 AWS 区域(例如 `us-east-1`、`eu-central-1`)。由 boto3 读取。 |
|
||||
| `AWS_PROFILE` | Bedrock 认证的 AWS 命名配置文件(读取 `~/.aws/credentials`)。不设置则使用默认 boto3 凭证链。 |
|
||||
| `BEDROCK_BASE_URL` | 覆盖 Bedrock runtime base URL(默认:`https://bedrock-runtime.us-east-1.amazonaws.com`;通常不设置,改用 `AWS_REGION`) |
|
||||
| `HERMES_QWEN_BASE_URL` | Qwen Portal base URL 覆盖(默认:`https://portal.qwen.ai/v1`) |
|
||||
| `OPENCODE_ZEN_API_KEY` | OpenCode Zen API 密钥——按需付费访问精选模型([opencode.ai](https://opencode.ai/auth)) |
|
||||
| `OPENCODE_ZEN_BASE_URL` | 覆盖 OpenCode Zen base URL |
|
||||
| `OPENCODE_GO_API_KEY` | OpenCode Go API 密钥——$10/月订阅开源模型([opencode.ai](https://opencode.ai/auth)) |
|
||||
| `OPENCODE_GO_BASE_URL` | 覆盖 OpenCode Go base URL |
|
||||
| `CLAUDE_CODE_OAUTH_TOKEN` | 手动导出时的显式 Claude Code token 覆盖 |
|
||||
| `HERMES_MODEL` | 在进程级别覆盖模型名称(供 cron 调度器使用;正常使用请优先在 `config.yaml` 中配置) |
|
||||
| `VOICE_TOOLS_OPENAI_KEY` | OpenAI 语音转文字和文字转语音提供商的首选 OpenAI 密钥 |
|
||||
| `HERMES_LOCAL_STT_COMMAND` | 可选的本地语音转文字命令模板。支持 `{input_path}`、`{output_dir}`、`{language}` 和 `{model}` 占位符 |
|
||||
| `HERMES_LOCAL_STT_LANGUAGE` | 传递给 `HERMES_LOCAL_STT_COMMAND` 或自动检测的本地 `whisper` CLI 回退的默认语言(默认:`en`) |
|
||||
| `HERMES_HOME` | 覆盖 Hermes 配置目录(默认:`~/.hermes`)。同时限定 gateway PID 文件和 systemd 服务名称,允许多个安装并发运行 |
|
||||
| `HERMES_GIT_BASH_PATH` | **仅 Windows。** 覆盖终端工具的 `bash.exe` 发现路径。可指向任意 bash——完整 Git-for-Windows 安装、通过符号链接的 WSL bash、MSYS2、Cygwin。安装程序会自动将其设置为所配置的 PortableGit。参见 [Windows(原生)指南](../user-guide/windows-native.md#how-hermes-runs-shell-commands-on-windows) |
|
||||
| `HERMES_DISABLE_WINDOWS_UTF8` | **仅 Windows。** 设为 `1` 可禁用 UTF-8 stdio shim(`configure_windows_stdio()`),回退到控制台的本地代码页。用于排查编码问题;正常操作中极少需要 |
|
||||
| `HERMES_KANBAN_HOME` | 覆盖锚定 kanban 看板(数据库 + 工作区 + 工作日志)的共享 Hermes 根目录。回退到 `get_default_hermes_root()`(任意活动 profile 的父目录)。适用于测试和非常规部署 |
|
||||
| `HERMES_KANBAN_BOARD` | 为当前进程固定活动 kanban 看板。优先于 `~/.hermes/kanban/current`;调度器将其注入工作进程子进程环境,使工作进程无法看到其他看板上的任务。默认为 `default`。slug 验证:小写字母数字 + 连字符 + 下划线,1-64 字符 |
|
||||
| `HERMES_KANBAN_DB` | 直接固定 kanban 数据库文件路径(最高优先级;优先于 `HERMES_KANBAN_BOARD` 和 `HERMES_KANBAN_HOME`)。调度器将其注入工作进程子进程环境,使 profile 工作进程收敛到调度器的看板 |
|
||||
| `HERMES_KANBAN_WORKSPACES_ROOT` | 直接固定 kanban 工作区根目录(工作区最高优先级;优先于 `HERMES_KANBAN_HOME`)。调度器将其注入工作进程子进程环境 |
|
||||
| `HERMES_KANBAN_DISPATCH_IN_GATEWAY` | `kanban.dispatch_in_gateway` 的运行时覆盖。设为 `0`、`false`、`no` 或 `off` 可阻止 gateway 启动内嵌 Kanban 调度器;任何其他非空值则启用。适用于独立调度器进程拥有看板的场景。 |
|
||||
|
||||
## 提供商认证(OAuth)
|
||||
|
||||
对于原生 Anthropic 认证,Hermes 在 Claude Code 自身凭证文件存在时优先使用,因为这些凭证可以自动刷新。**针对 Anthropic 的 OAuth 需要购买了额外使用额度的 Claude Max 计划**——Hermes 以 Claude Code 身份路由,仅消耗 Max 计划的额外/超额额度,不消耗基础 Max 配额,且不适用于 Claude Pro。没有 Max + 额外额度时,请改用 API 密钥。`ANTHROPIC_TOKEN` 等环境变量作为手动覆盖仍然有用,但不再是 Claude Max 登录的首选路径。
|
||||
|
||||
| 变量 | 描述 |
|
||||
|----------|-------------|
|
||||
| `HERMES_PORTAL_BASE_URL` | 覆盖 Nous Portal URL(用于开发/测试) |
|
||||
| `NOUS_INFERENCE_BASE_URL` | 覆盖 Nous 推理 API URL |
|
||||
| `HERMES_NOUS_MIN_KEY_TTL_SECONDS` | 重新铸造前的最小 agent 密钥 TTL(默认:1800 = 30 分钟) |
|
||||
| `HERMES_NOUS_TIMEOUT_SECONDS` | Nous 凭证/token 流程的 HTTP 超时 |
|
||||
| `HERMES_DUMP_REQUESTS` | 将 API 请求载荷转储到日志文件(`true`/`false`) |
|
||||
| `HERMES_PREFILL_MESSAGES_FILE` | 包含在 API 调用时注入的临时预填消息的 JSON 文件路径 |
|
||||
| `HERMES_TIMEZONE` | IANA 时区覆盖(例如 `America/New_York`) |
|
||||
|
||||
## 工具 API
|
||||
|
||||
| 变量 | 描述 |
|
||||
|----------|-------------|
|
||||
| `PARALLEL_API_KEY` | AI 原生网络搜索([parallel.ai](https://parallel.ai/)) |
|
||||
| `FIRECRAWL_API_KEY` | 网页抓取和云浏览器([firecrawl.dev](https://firecrawl.dev/)) |
|
||||
| `FIRECRAWL_API_URL` | 自托管实例的自定义 Firecrawl API 端点(可选) |
|
||||
| `TAVILY_API_KEY` | Tavily API 密钥,用于 AI 原生网络搜索、提取和爬取([app.tavily.com](https://app.tavily.com/home)) |
|
||||
| `SEARXNG_URL` | 免费自托管网络搜索的 SearXNG 实例 URL——无需 API 密钥([searxng.github.io](https://searxng.github.io/searxng/)) |
|
||||
| `TAVILY_BASE_URL` | 覆盖 Tavily API 端点。适用于企业代理和自托管 Tavily 兼容搜索后端。与 `GROQ_BASE_URL` 模式相同。 |
|
||||
| `EXA_API_KEY` | Exa API 密钥,用于 AI 原生网络搜索和内容获取([exa.ai](https://exa.ai/)) |
|
||||
| `BROWSERBASE_API_KEY` | 浏览器自动化([browserbase.com](https://browserbase.com/)) |
|
||||
| `BROWSERBASE_PROJECT_ID` | Browserbase 项目 ID |
|
||||
| `BROWSER_USE_API_KEY` | Browser Use 云浏览器 API 密钥([browser-use.com](https://browser-use.com/)) |
|
||||
| `FIRECRAWL_BROWSER_TTL` | Firecrawl 浏览器会话 TTL(秒,默认:300) |
|
||||
| `BROWSER_CDP_URL` | 本地浏览器的 Chrome DevTools Protocol(CDP)URL(通过 `/browser connect` 设置,例如 `ws://localhost:9222`) |
|
||||
| `CAMOFOX_URL` | Camofox 本地反检测浏览器 URL(默认:`http://localhost:9377`) |
|
||||
| `CAMOFOX_USER_ID` | 可选的外部管理 Camofox 用户 ID,用于共享可见会话 |
|
||||
| `CAMOFOX_SESSION_KEY` | 为 `CAMOFOX_USER_ID` 创建标签页时使用的可选 Camofox 会话密钥 |
|
||||
| `CAMOFOX_ADOPT_EXISTING_TAB` | 设为 `true` 可在创建新标签页前复用现有 Camofox 标签页 |
|
||||
| `BROWSER_INACTIVITY_TIMEOUT` | 浏览器会话不活动超时(秒) |
|
||||
| `AGENT_BROWSER_ARGS` | 额外的 Chromium 启动标志(逗号或换行分隔)。以 root 身份运行或在 AppArmor 限制的非特权用户命名空间(Ubuntu 23.10+、DGX Spark、许多容器镜像)中运行时,Hermes 自动注入 `--no-sandbox,--disable-dev-shm-usage`;仅在需要覆盖或添加其他标志时手动设置。 |
|
||||
| `FAL_KEY` | 图像生成([fal.ai](https://fal.ai/)) |
|
||||
| `GROQ_API_KEY` | Groq Whisper STT API 密钥([groq.com](https://groq.com/)) |
|
||||
| `ELEVENLABS_API_KEY` | ElevenLabs 高级 TTS 语音([elevenlabs.io](https://elevenlabs.io/)) |
|
||||
| `STT_GROQ_MODEL` | 覆盖 Groq STT 模型(默认:`whisper-large-v3-turbo`) |
|
||||
| `GROQ_BASE_URL` | 覆盖 Groq OpenAI 兼容 STT 端点 |
|
||||
| `STT_OPENAI_MODEL` | 覆盖 OpenAI STT 模型(默认:`whisper-1`) |
|
||||
| `STT_OPENAI_BASE_URL` | 覆盖 OpenAI 兼容 STT 端点 |
|
||||
| `GITHUB_TOKEN` | Skills Hub 的 GitHub token(更高 API 速率限制,技能发布) |
|
||||
| `HONCHO_API_KEY` | 跨会话用户建模([honcho.dev](https://honcho.dev/)) |
|
||||
| `HONCHO_BASE_URL` | 自托管 Honcho 实例的 base URL(默认:Honcho 云)。本地实例无需 API 密钥 |
|
||||
| `HINDSIGHT_TIMEOUT` | Hindsight 内存提供商 API 调用超时(秒,默认:`60`)。如果 Hindsight 实例在 `/sync` 或 `on_session_switch` 期间响应缓慢并出现超时,请增大此值,并检查 `errors.log`。 |
|
||||
| `SUPERMEMORY_API_KEY` | 支持 profile 召回和会话摄取的语义长期记忆([supermemory.ai](https://supermemory.ai)) |
|
||||
| `DAYTONA_API_KEY` | Daytona 云沙箱([daytona.io](https://daytona.io/)) |
|
||||
| `VERCEL_TOKEN` | Vercel Sandbox 访问 token([vercel.com](https://vercel.com/)) |
|
||||
| `VERCEL_PROJECT_ID` | Vercel 项目 ID(与 `VERCEL_TOKEN` 配合使用) |
|
||||
| `VERCEL_TEAM_ID` | Vercel 团队 ID(与 `VERCEL_TOKEN` 配合使用) |
|
||||
| `VERCEL_OIDC_TOKEN` | Vercel 短期 OIDC token(仅用于开发的替代方案) |
|
||||
|
||||
### Langfuse 可观测性
|
||||
|
||||
内置 [`observability/langfuse`](/user-guide/features/built-in-plugins#observabilitylangfuse) 插件的环境变量。在 `~/.hermes/.env` 中设置。在这些变量生效之前,还必须启用该插件(`hermes plugins enable observability/langfuse`,或在 `hermes plugins` 中勾选)。
|
||||
|
||||
| 变量 | 描述 |
|
||||
|----------|-------------|
|
||||
| `HERMES_LANGFUSE_PUBLIC_KEY` | Langfuse 项目公钥(`pk-lf-...`)。必填。 |
|
||||
| `HERMES_LANGFUSE_SECRET_KEY` | Langfuse 项目密钥(`sk-lf-...`)。必填。 |
|
||||
| `HERMES_LANGFUSE_BASE_URL` | Langfuse 服务器 URL(默认:`https://cloud.langfuse.com`)。自托管时设置。 |
|
||||
| `HERMES_LANGFUSE_ENV` | trace 上的环境标签(`production`、`staging` 等) |
|
||||
| `HERMES_LANGFUSE_RELEASE` | trace 上的发布/版本标签 |
|
||||
| `HERMES_LANGFUSE_SAMPLE_RATE` | SDK 采样率 0.0–1.0(默认:`1.0`) |
|
||||
| `HERMES_LANGFUSE_MAX_CHARS` | 序列化载荷的每字段截断长度(默认:`12000`) |
|
||||
| `HERMES_LANGFUSE_DEBUG` | `true` 可将详细插件日志输出到 `agent.log` |
|
||||
| `LANGFUSE_PUBLIC_KEY` / `LANGFUSE_SECRET_KEY` / `LANGFUSE_BASE_URL` | 标准 Langfuse SDK 变量名。当对应的 `HERMES_LANGFUSE_*` 未设置时作为回退。 |
|
||||
|
||||
### Nous Tool Gateway
|
||||
|
||||
这些变量为付费 Nous 订阅者或自托管 gateway 部署配置 [Tool Gateway](/user-guide/features/tool-gateway)。大多数用户无需设置——gateway 通过 `hermes model` 或 `hermes tools` 自动配置。
|
||||
|
||||
| 变量 | 描述 |
|
||||
|----------|-------------|
|
||||
| `TOOL_GATEWAY_DOMAIN` | Tool Gateway 路由的基础域名(默认:`nousresearch.com`) |
|
||||
| `TOOL_GATEWAY_SCHEME` | gateway URL 的 HTTP 或 HTTPS 协议(默认:`https`) |
|
||||
| `TOOL_GATEWAY_USER_TOKEN` | Tool Gateway 的认证 token(通常由 Nous 认证自动填充) |
|
||||
| `FIRECRAWL_GATEWAY_URL` | 专门覆盖 Firecrawl gateway 端点的 URL |
|
||||
|
||||
## 终端后端
|
||||
|
||||
| 变量 | 描述 |
|
||||
|----------|-------------|
|
||||
| `TERMINAL_ENV` | 后端:`local`、`docker`、`ssh`、`singularity`、`modal`、`daytona`、`vercel_sandbox` |
|
||||
| `HERMES_DOCKER_BINARY` | 覆盖 Hermes 调用的容器二进制(例如 `podman`、`/usr/local/bin/docker`)。未设置时,Hermes 自动在 `PATH` 上发现 `docker` 或 `podman`。当两者都已安装且需要非默认选项,或二进制不在 `PATH` 中时使用。 |
|
||||
| `TERMINAL_DOCKER_IMAGE` | Docker 镜像(默认:`nikolaik/python-nodejs:python3.11-nodejs20`) |
|
||||
| `TERMINAL_DOCKER_FORWARD_ENV` | 显式转发到 Docker 终端会话的环境变量名 JSON 数组。注意:技能声明的 `required_environment_variables` 会自动转发——仅对未被任何技能声明的变量使用此项。 |
|
||||
| `TERMINAL_DOCKER_VOLUMES` | 额外的 Docker 卷挂载(逗号分隔的 `host:container` 对) |
|
||||
| `TERMINAL_DOCKER_MOUNT_CWD_TO_WORKSPACE` | 高级选项:将启动时的 cwd 挂载到 Docker `/workspace`(`true`/`false`,默认:`false`) |
|
||||
| `TERMINAL_SINGULARITY_IMAGE` | Singularity 镜像或 `.sif` 路径 |
|
||||
| `TERMINAL_MODAL_IMAGE` | Modal 容器镜像 |
|
||||
| `TERMINAL_DAYTONA_IMAGE` | Daytona 沙箱镜像 |
|
||||
| `TERMINAL_VERCEL_RUNTIME` | Vercel Sandbox 运行时(`node24`、`node22`、`python3.13`) |
|
||||
| `TERMINAL_TIMEOUT` | 命令超时(秒) |
|
||||
| `TERMINAL_LIFETIME_SECONDS` | 终端会话最大生命周期(秒) |
|
||||
| `TERMINAL_CWD` | 终端会话的工作目录(仅 gateway/cron;CLI 使用启动目录) |
|
||||
| `SUDO_PASSWORD` | 无需交互提示即可使用 sudo |
|
||||
|
||||
对于云沙箱后端,持久化以文件系统为导向。`TERMINAL_LIFETIME_SECONDS` 控制 Hermes 何时清理空闲终端会话,后续恢复可能会重新创建沙箱而非保持相同的活跃进程。
|
||||
|
||||
## SSH 后端
|
||||
|
||||
| 变量 | 描述 |
|
||||
|----------|-------------|
|
||||
| `TERMINAL_SSH_HOST` | 远程服务器主机名 |
|
||||
| `TERMINAL_SSH_USER` | SSH 用户名 |
|
||||
| `TERMINAL_SSH_PORT` | SSH 端口(默认:22) |
|
||||
| `TERMINAL_SSH_KEY` | 私钥路径 |
|
||||
| `TERMINAL_SSH_PERSISTENT` | 覆盖 SSH 的持久 shell(默认:跟随 `TERMINAL_PERSISTENT_SHELL`) |
|
||||
|
||||
## 容器资源(Docker、Singularity、Modal、Daytona)
|
||||
|
||||
| 变量 | 描述 |
|
||||
|----------|-------------|
|
||||
| `TERMINAL_CONTAINER_CPU` | CPU 核心数(默认:1) |
|
||||
| `TERMINAL_CONTAINER_MEMORY` | 内存(MB,默认:5120) |
|
||||
| `TERMINAL_CONTAINER_DISK` | 磁盘(MB,默认:51200) |
|
||||
| `TERMINAL_CONTAINER_PERSISTENT` | 跨会话持久化容器文件系统(默认:`true`) |
|
||||
| `TERMINAL_SANDBOX_DIR` | 工作区和 overlay 的宿主机目录(默认:`~/.hermes/sandboxes/`) |
|
||||
|
||||
## 持久 Shell
|
||||
|
||||
| 变量 | 描述 |
|
||||
|----------|-------------|
|
||||
| `TERMINAL_PERSISTENT_SHELL` | 为非本地后端启用持久 shell(默认:`true`)。也可通过 config.yaml 中的 `terminal.persistent_shell` 设置 |
|
||||
| `TERMINAL_LOCAL_PERSISTENT` | 为本地后端启用持久 shell(默认:`false`) |
|
||||
| `TERMINAL_SSH_PERSISTENT` | 覆盖 SSH 后端的持久 shell(默认:跟随 `TERMINAL_PERSISTENT_SHELL`) |
|
||||
|
||||
## 消息平台
|
||||
|
||||
| 变量 | 描述 |
|
||||
|----------|-------------|
|
||||
| `TELEGRAM_BOT_TOKEN` | Telegram bot token(来自 @BotFather) |
|
||||
| `TELEGRAM_ALLOWED_USERS` | 允许使用 bot 的逗号分隔用户 ID(适用于私聊、群组和论坛) |
|
||||
| `TELEGRAM_GROUP_ALLOWED_USERS` | 仅在群组/论坛中授权的逗号分隔发送者用户 ID(**不**授予私聊权限)。以 `-` 开头的聊天 ID 形式值仍作为聊天 ID 处理,以向后兼容 #17686 之前的配置,并显示弃用警告。 |
|
||||
| `TELEGRAM_GROUP_ALLOWED_CHATS` | 逗号分隔的群组/论坛聊天 ID;任意成员均可授权 |
|
||||
| `TELEGRAM_HOME_CHANNEL` | cron 投递的默认 Telegram 聊天/频道 |
|
||||
| `TELEGRAM_HOME_CHANNEL_NAME` | Telegram 主频道的显示名称 |
|
||||
| `TELEGRAM_CRON_THREAD_ID` | 接收 cron 投递的论坛话题 ID;仅对 cron 覆盖 `TELEGRAM_HOME_CHANNEL_THREAD_ID`。在话题模式下使用,使 cron 消息的回复开启新会话而非进入系统大厅(#24409)。 |
|
||||
| `TELEGRAM_WEBHOOK_URL` | webhook 模式的公共 HTTPS URL(启用 webhook 而非轮询) |
|
||||
| `TELEGRAM_WEBHOOK_PORT` | webhook 服务器本地监听端口(默认:`8443`) |
|
||||
| `TELEGRAM_WEBHOOK_SECRET` | Telegram 在每次更新中回传的密钥 token,用于验证。**设置 `TELEGRAM_WEBHOOK_URL` 时必填**——未设置时 gateway 拒绝启动(GHSA-3vpc-7q5r-276h)。使用 `openssl rand -hex 32` 生成。 |
|
||||
| `TELEGRAM_REACTIONS` | 处理期间在消息上启用 emoji 反应(默认:`false`) |
|
||||
| `TELEGRAM_REQUIRE_MENTION` | 在 Telegram 群组中响应前要求显式触发。等同于 `config.yaml` 中的 `telegram.require_mention`。 |
|
||||
| `TELEGRAM_MENTION_PATTERNS` | 启用 Telegram 群组 mention 门控时接受的正则唤醒词模式,JSON 数组、换行分隔列表或逗号分隔列表。等同于 `telegram.mention_patterns`。 |
|
||||
| `TELEGRAM_EXCLUSIVE_BOT_MENTIONS` | 启用后,Telegram 群组中的显式 `@...bot` mention 仅路由到被 mention 的 bot 用户名,然后再执行回复或唤醒词回退。默认:`true`。等同于 `telegram.exclusive_bot_mentions`。 |
|
||||
| `TELEGRAM_REPLY_TO_MODE` | 回复引用行为:`off`、`first`(默认)或 `all`。与 Discord 模式一致。 |
|
||||
| `TELEGRAM_IGNORED_THREADS` | bot 永不响应的逗号分隔 Telegram 论坛话题/线程 ID |
|
||||
| `TELEGRAM_PROXY` | Telegram 连接的代理 URL——覆盖 `HTTPS_PROXY`。支持 `http://`、`https://`、`socks5://` |
|
||||
| `DISCORD_BOT_TOKEN` | Discord bot token |
|
||||
| `DISCORD_ALLOWED_USERS` | 允许使用 bot 的逗号分隔 Discord 用户 ID |
|
||||
| `DISCORD_ALLOWED_ROLES` | 允许使用 bot 的逗号分隔 Discord 角色 ID(与 `DISCORD_ALLOWED_USERS` 取 OR)。自动启用 Members intent。适用于管理团队频繁变动的场景——角色授权自动传播。 |
|
||||
| `DISCORD_ALLOWED_CHANNELS` | 逗号分隔的 Discord 频道 ID。设置后,bot 仅在这些频道(以及允许的私聊)中响应。覆盖 `config.yaml` 中的 `discord.allowed_channels`。 |
|
||||
| `DISCORD_PROXY` | Discord 连接的代理 URL——覆盖 `HTTPS_PROXY`。支持 `http://`、`https://`、`socks5://` |
|
||||
| `DISCORD_HOME_CHANNEL` | cron 投递的默认 Discord 频道 |
|
||||
| `DISCORD_HOME_CHANNEL_NAME` | Discord 主频道的显示名称 |
|
||||
| `DISCORD_COMMAND_SYNC_POLICY` | Discord 斜杠命令启动同步策略:`safe`(差异对比并协调)、`bulk`(旧版 `tree.sync()`)或 `off` |
|
||||
| `DISCORD_REQUIRE_MENTION` | 在服务器频道中响应前要求 @mention |
|
||||
| `DISCORD_FREE_RESPONSE_CHANNELS` | 不需要 mention 的逗号分隔频道 ID |
|
||||
| `DISCORD_AUTO_THREAD` | 支持时自动将长回复转为线程 |
|
||||
| `DISCORD_ALLOW_ANY_ATTACHMENT` | 设为 `true` 时接受任意文件类型的附件(不仅限于内置的 PDF/文本/zip/office 白名单)。未知类型被缓存并以本地路径形式提供给 agent,供其通过 `terminal`/`read_file`/`ffprobe` 检查。默认 `false`。 |
|
||||
| `DISCORD_MAX_ATTACHMENT_BYTES` | gateway 缓存的每个附件最大字节数。默认 `33554432`(32 MiB)。设为 `0` 表示无上限(附件在写入时保存在内存中)。 |
|
||||
| `DISCORD_REACTIONS` | 处理期间在消息上启用 emoji 反应(默认:`true`) |
|
||||
| `DISCORD_IGNORED_CHANNELS` | bot 永不响应的逗号分隔频道 ID |
|
||||
| `DISCORD_NO_THREAD_CHANNELS` | bot 不自动创建线程的逗号分隔频道 ID |
|
||||
| `DISCORD_REPLY_TO_MODE` | 回复引用行为:`off`、`first`(默认)或 `all` |
|
||||
| `DISCORD_ALLOW_MENTION_EVERYONE` | 允许 bot ping `@everyone`/`@here`(默认:`false`)。参见 [Mention 控制](../user-guide/messaging/discord.md#mention-control)。 |
|
||||
| `DISCORD_ALLOW_MENTION_ROLES` | 允许 bot ping `@role` mention(默认:`false`)。 |
|
||||
| `DISCORD_ALLOW_MENTION_USERS` | 允许 bot ping 单个 `@user` mention(默认:`true`)。 |
|
||||
| `DISCORD_ALLOW_MENTION_REPLIED_USER` | 回复消息时 ping 原作者(默认:`true`)。 |
|
||||
| `SLACK_BOT_TOKEN` | Slack bot token(`xoxb-...`) |
|
||||
| `SLACK_APP_TOKEN` | Slack 应用级 token(`xapp-...`,Socket Mode 必需) |
|
||||
| `SLACK_ALLOWED_USERS` | 逗号分隔的 Slack 用户 ID |
|
||||
| `SLACK_HOME_CHANNEL` | cron 投递的默认 Slack 频道 |
|
||||
| `SLACK_HOME_CHANNEL_NAME` | Slack 主频道的显示名称 |
|
||||
| `GOOGLE_CHAT_PROJECT_ID` | 托管 Pub/Sub 话题的 GCP 项目(回退到 `GOOGLE_CLOUD_PROJECT`) |
|
||||
| `GOOGLE_CHAT_SUBSCRIPTION_NAME` | 完整 Pub/Sub 订阅路径,`projects/{proj}/subscriptions/{sub}`(旧版别名:`GOOGLE_CHAT_SUBSCRIPTION`) |
|
||||
| `GOOGLE_CHAT_SERVICE_ACCOUNT_JSON` | Service Account JSON 文件路径,或内联 JSON(回退到 `GOOGLE_APPLICATION_CREDENTIALS`) |
|
||||
| `GOOGLE_CHAT_ALLOWED_USERS` | 允许与 bot 聊天的逗号分隔用户邮箱 |
|
||||
| `GOOGLE_CHAT_ALLOW_ALL_USERS` | 允许任意 Google Chat 用户触发 bot(仅用于开发) |
|
||||
| `GOOGLE_CHAT_HOME_CHANNEL` | cron 投递的默认空间(例如 `spaces/AAAA...`) |
|
||||
| `GOOGLE_CHAT_HOME_CHANNEL_NAME` | Google Chat 主空间的显示名称 |
|
||||
| `GOOGLE_CHAT_MAX_MESSAGES` | Pub/Sub FlowControl 最大在途消息数(默认:`1`) |
|
||||
| `GOOGLE_CHAT_MAX_BYTES` | Pub/Sub FlowControl 最大在途字节数(默认:`16777216`,16 MiB) |
|
||||
| `GOOGLE_CHAT_BOOTSTRAP_SPACES` | 启动时探测以解析 bot 自身 `users/{id}` 的逗号分隔额外空间 ID |
|
||||
| `GOOGLE_CHAT_DEBUG_RAW` | 设置任意值可在 DEBUG 级别记录脱敏的 Pub/Sub 信封(仅用于调试) |
|
||||
| `WHATSAPP_ENABLED` | 启用 WhatsApp 桥接(`true`/`false`) |
|
||||
| `WHATSAPP_MODE` | `bot`(独立号码)或 `self-chat`(给自己发消息) |
|
||||
| `WHATSAPP_ALLOWED_USERS` | 逗号分隔的手机号码(含国家代码,不含 `+`),或 `*` 允许所有发送者 |
|
||||
| `WHATSAPP_ALLOW_ALL_USERS` | 无需白名单允许所有 WhatsApp 发送者(`true`/`false`) |
|
||||
| `WHATSAPP_DEBUG` | 在桥接中记录原始消息事件以供排查(`true`/`false`) |
|
||||
| `SIGNAL_HTTP_URL` | signal-cli 守护进程 HTTP 端点(例如 `http://127.0.0.1:8080`) |
|
||||
| `SIGNAL_ACCOUNT` | E.164 格式的 bot 手机号码 |
|
||||
| `SIGNAL_ALLOWED_USERS` | 逗号分隔的 E.164 手机号码或 UUID |
|
||||
| `SIGNAL_GROUP_ALLOWED_USERS` | 逗号分隔的群组 ID,或 `*` 表示所有群组 |
|
||||
| `SIGNAL_HOME_CHANNEL_NAME` | Signal 主频道的显示名称 |
|
||||
| `SIGNAL_IGNORE_STORIES` | 忽略 Signal 故事/状态更新 |
|
||||
| `SIGNAL_ALLOW_ALL_USERS` | 无需白名单允许所有 Signal 用户 |
|
||||
| `TWILIO_ACCOUNT_SID` | Twilio Account SID(与电话技能共享) |
|
||||
| `TWILIO_AUTH_TOKEN` | Twilio Auth Token(与电话技能共享;也用于 webhook 签名验证) |
|
||||
| `TWILIO_PHONE_NUMBER` | E.164 格式的 Twilio 手机号码(与电话技能共享) |
|
||||
| `SMS_WEBHOOK_URL` | Twilio 签名验证的公共 URL——必须与 Twilio Console 中的 webhook URL 一致(必填) |
|
||||
| `SMS_WEBHOOK_PORT` | 入站 SMS 的 webhook 监听端口(默认:`8080`) |
|
||||
| `SMS_WEBHOOK_HOST` | webhook 绑定地址(默认:`0.0.0.0`) |
|
||||
| `SMS_INSECURE_NO_SIGNATURE` | 设为 `true` 可禁用 Twilio 签名验证(仅用于本地开发——不适用于生产环境) |
|
||||
| `SMS_ALLOWED_USERS` | 允许聊天的逗号分隔 E.164 手机号码 |
|
||||
| `SMS_ALLOW_ALL_USERS` | 无需白名单允许所有 SMS 发送者 |
|
||||
| `SMS_HOME_CHANNEL` | cron 任务/通知投递的手机号码 |
|
||||
| `SMS_HOME_CHANNEL_NAME` | SMS 主频道的显示名称 |
|
||||
| `EMAIL_ADDRESS` | Email gateway 适配器的邮箱地址 |
|
||||
| `EMAIL_PASSWORD` | 邮箱账户的密码或应用密码 |
|
||||
| `EMAIL_IMAP_HOST` | 邮件适配器的 IMAP 主机名 |
|
||||
| `EMAIL_IMAP_PORT` | IMAP 端口 |
|
||||
| `EMAIL_SMTP_HOST` | 邮件适配器的 SMTP 主机名 |
|
||||
| `EMAIL_SMTP_PORT` | SMTP 端口 |
|
||||
| `EMAIL_ALLOWED_USERS` | 允许向 bot 发送消息的逗号分隔邮箱地址 |
|
||||
| `EMAIL_HOME_ADDRESS` | 主动邮件投递的默认收件人 |
|
||||
| `EMAIL_HOME_ADDRESS_NAME` | 邮件主目标的显示名称 |
|
||||
| `EMAIL_POLL_INTERVAL` | 邮件轮询间隔(秒) |
|
||||
| `EMAIL_ALLOW_ALL_USERS` | 允许所有入站邮件发送者 |
|
||||
| `DINGTALK_CLIENT_ID` | 来自开发者门户的钉钉 bot AppKey([open.dingtalk.com](https://open.dingtalk.com)) |
|
||||
| `DINGTALK_CLIENT_SECRET` | 来自开发者门户的钉钉 bot AppSecret |
|
||||
| `DINGTALK_ALLOWED_USERS` | 允许向 bot 发送消息的逗号分隔钉钉用户 ID |
|
||||
| `FEISHU_APP_ID` | 来自 [open.feishu.cn](https://open.feishu.cn/) 的飞书/Lark bot App ID |
|
||||
| `FEISHU_APP_SECRET` | 飞书/Lark bot App Secret |
|
||||
| `FEISHU_DOMAIN` | `feishu`(中国)或 `lark`(国际)。默认:`feishu` |
|
||||
| `FEISHU_CONNECTION_MODE` | `websocket`(推荐)或 `webhook`。默认:`websocket` |
|
||||
| `FEISHU_ENCRYPT_KEY` | webhook 模式的可选加密密钥 |
|
||||
| `FEISHU_VERIFICATION_TOKEN` | webhook 模式的可选验证 token |
|
||||
| `FEISHU_ALLOWED_USERS` | 允许向 bot 发送消息的逗号分隔飞书用户 ID |
|
||||
| `FEISHU_ALLOW_BOTS` | `none`(默认)/`mentions`/`all`——接受来自其他 bot 的入站消息。参见 [bot 间消息传递](../user-guide/messaging/feishu.md#bot-to-bot-messaging) |
|
||||
| `FEISHU_REQUIRE_MENTION` | `true`(默认)/`false`——群组消息是否必须 @mention bot。可通过 `group_rules.<chat_id>.require_mention` 按聊天覆盖。 |
|
||||
| `FEISHU_HOME_CHANNEL` | cron 投递和通知的飞书聊天 ID |
|
||||
| `WECOM_BOT_ID` | 来自管理控制台的企业微信 AI Bot ID |
|
||||
| `WECOM_SECRET` | 企业微信 AI Bot 密钥 |
|
||||
| `WECOM_WEBSOCKET_URL` | 自定义 WebSocket URL(默认:`wss://openws.work.weixin.qq.com`) |
|
||||
| `WECOM_ALLOWED_USERS` | 允许向 bot 发送消息的逗号分隔企业微信用户 ID |
|
||||
| `WECOM_HOME_CHANNEL` | cron 投递和通知的企业微信聊天 ID |
|
||||
| `WECOM_CALLBACK_CORP_ID` | 企业微信回调自建应用的企业 Corp ID |
|
||||
| `WECOM_CALLBACK_CORP_SECRET` | 自建应用的企业密钥 |
|
||||
| `WECOM_CALLBACK_AGENT_ID` | 自建应用的 Agent ID |
|
||||
| `WECOM_CALLBACK_TOKEN` | 回调验证 token |
|
||||
| `WECOM_CALLBACK_ENCODING_AES_KEY` | 回调加密的 AES 密钥 |
|
||||
| `WECOM_CALLBACK_HOST` | 回调服务器绑定地址(默认:`0.0.0.0`) |
|
||||
| `WECOM_CALLBACK_PORT` | 回调服务器端口(默认:`8645`) |
|
||||
| `WECOM_CALLBACK_ALLOWED_USERS` | 白名单的逗号分隔用户 ID |
|
||||
| `WECOM_CALLBACK_ALLOW_ALL_USERS` | 设为 `true` 可无需白名单允许所有用户 |
|
||||
| `WEIXIN_ACCOUNT_ID` | 通过 iLink Bot API 扫码登录获取的微信账号 ID |
|
||||
| `WEIXIN_TOKEN` | 通过 iLink Bot API 扫码登录获取的微信认证 token |
|
||||
| `WEIXIN_BASE_URL` | 覆盖微信 iLink Bot API base URL(默认:`https://ilinkai.weixin.qq.com`) |
|
||||
| `WEIXIN_CDN_BASE_URL` | 覆盖媒体的微信 CDN base URL(默认:`https://novac2c.cdn.weixin.qq.com/c2c`) |
|
||||
| `WEIXIN_DM_POLICY` | 私信策略:`open`、`allowlist`、`pairing`、`disabled`(默认:`open`) |
|
||||
| `WEIXIN_GROUP_POLICY` | 群消息策略:`open`、`allowlist`、`disabled`(默认:`disabled`) |
|
||||
| `WEIXIN_ALLOWED_USERS` | 允许私信 bot 的逗号分隔微信用户 ID |
|
||||
| `WEIXIN_GROUP_ALLOWED_USERS` | 允许与 bot 互动的逗号分隔微信**群聊 ID**(非成员用户 ID)。变量名为历史遗留——期望传入群 ID。仅当 iLink 实际投递群事件时生效;扫码登录的 iLink bot 身份(`...@im.bot`)通常不接收普通微信群消息。 |
|
||||
| `WEIXIN_HOME_CHANNEL` | cron 投递和通知的微信聊天 ID |
|
||||
| `WEIXIN_HOME_CHANNEL_NAME` | 微信主频道的显示名称 |
|
||||
| `WEIXIN_ALLOW_ALL_USERS` | 无需白名单允许所有微信用户(`true`/`false`) |
|
||||
| `BLUEBUBBLES_SERVER_URL` | BlueBubbles 服务器 URL(例如 `http://192.168.1.10:1234`) |
|
||||
| `BLUEBUBBLES_PASSWORD` | BlueBubbles 服务器密码 |
|
||||
| `BLUEBUBBLES_WEBHOOK_HOST` | webhook 监听绑定地址(默认:`127.0.0.1`) |
|
||||
| `BLUEBUBBLES_WEBHOOK_PORT` | webhook 监听端口(默认:`8645`) |
|
||||
| `BLUEBUBBLES_HOME_CHANNEL` | cron/通知投递的手机/邮箱 |
|
||||
| `BLUEBUBBLES_ALLOWED_USERS` | 逗号分隔的授权用户 |
|
||||
| `BLUEBUBBLES_ALLOW_ALL_USERS` | 允许所有用户(`true`/`false`) |
|
||||
| `QQ_APP_ID` | 来自 [q.qq.com](https://q.qq.com) 的 QQ Bot App ID |
|
||||
| `QQ_CLIENT_SECRET` | 来自 [q.qq.com](https://q.qq.com) 的 QQ Bot App Secret |
|
||||
| `QQ_STT_API_KEY` | 外部 STT 回退提供商的 API 密钥(可选,当 QQ 内置 ASR 未返回文本时使用) |
|
||||
| `QQ_STT_BASE_URL` | 外部 STT 提供商的 base URL(可选) |
|
||||
| `QQ_STT_MODEL` | 外部 STT 提供商的模型名称(可选) |
|
||||
| `QQ_ALLOWED_USERS` | 允许向 bot 发送消息的逗号分隔 QQ 用户 openID |
|
||||
| `QQ_GROUP_ALLOWED_USERS` | 群 @消息访问的逗号分隔 QQ 群 ID |
|
||||
| `QQ_ALLOW_ALL_USERS` | 允许所有用户(`true`/`false`,覆盖 `QQ_ALLOWED_USERS`) |
|
||||
| `QQBOT_HOME_CHANNEL` | cron 投递和通知的 QQ 用户/群 openID |
|
||||
| `QQBOT_HOME_CHANNEL_NAME` | QQ 主频道的显示名称 |
|
||||
| `QQ_PORTAL_HOST` | 覆盖 QQ portal 主机(设为 `sandbox.q.qq.com` 可通过沙箱 gateway 路由;默认:`q.qq.com`)。 |
|
||||
| `MATTERMOST_URL` | Mattermost 服务器 URL(例如 `https://mm.example.com`) |
|
||||
| `MATTERMOST_TOKEN` | Mattermost 的 bot token 或个人访问 token |
|
||||
| `MATTERMOST_ALLOWED_USERS` | 允许向 bot 发送消息的逗号分隔 Mattermost 用户 ID |
|
||||
| `MATTERMOST_HOME_CHANNEL` | 主动消息投递(cron、通知)的频道 ID |
|
||||
| `MATTERMOST_REQUIRE_MENTION` | 在频道中要求 `@mention`(默认:`true`)。设为 `false` 可响应所有消息。 |
|
||||
| `MATTERMOST_FREE_RESPONSE_CHANNELS` | bot 无需 `@mention` 即可响应的逗号分隔频道 ID |
|
||||
| `MATTERMOST_REPLY_MODE` | 回复风格:`thread`(线程回复)或 `off`(平铺消息,默认) |
|
||||
| `MATRIX_HOMESERVER` | Matrix homeserver URL(例如 `https://matrix.org`) |
|
||||
| `MATRIX_ACCESS_TOKEN` | bot 认证的 Matrix 访问 token |
|
||||
| `MATRIX_USER_ID` | Matrix 用户 ID(例如 `@hermes:matrix.org`)——密码登录时必填,使用访问 token 时可选 |
|
||||
| `MATRIX_PASSWORD` | Matrix 密码(访问 token 的替代方案) |
|
||||
| `MATRIX_ALLOWED_USERS` | 允许向 bot 发送消息的逗号分隔 Matrix 用户 ID(例如 `@alice:matrix.org`) |
|
||||
| `MATRIX_HOME_ROOM` | 主动消息投递的房间 ID(例如 `!abc123:matrix.org`) |
|
||||
| `MATRIX_ENCRYPTION` | 启用端到端加密(`true`/`false`,默认:`false`) |
|
||||
| `MATRIX_DEVICE_ID` | 用于 E2EE 跨重启持久化的稳定 Matrix 设备 ID(例如 `HERMES_BOT`)。不设置时,E2EE 密钥每次启动都会轮换,历史房间解密将失败。 |
|
||||
| `MATRIX_REACTIONS` | 对入站消息启用处理生命周期 emoji 反应(默认:`true`)。设为 `false` 可禁用。 |
|
||||
| `MATRIX_REQUIRE_MENTION` | 在房间中要求 `@mention`(默认:`true`)。设为 `false` 可响应所有消息。 |
|
||||
| `MATRIX_FREE_RESPONSE_ROOMS` | bot 无需 `@mention` 即可响应的逗号分隔房间 ID |
|
||||
| `MATRIX_AUTO_THREAD` | 为房间消息自动创建线程(默认:`true`) |
|
||||
| `MATRIX_DM_MENTION_THREADS` | 在私聊中被 `@mention` 时创建线程(默认:`false`) |
|
||||
| `MATRIX_RECOVERY_KEY` | 设备密钥轮换后交叉签名验证的恢复密钥。推荐用于启用了交叉签名的 E2EE 设置。 |
|
||||
| `HASS_TOKEN` | Home Assistant 长期访问 token(启用 HA 平台 + 工具) |
|
||||
| `HASS_URL` | Home Assistant URL(默认:`http://homeassistant.local:8123`) |
|
||||
| `WEBHOOK_ENABLED` | 启用 webhook 平台适配器(`true`/`false`) |
|
||||
| `WEBHOOK_PORT` | 接收 webhook 的 HTTP 服务器端口(默认:`8644`) |
|
||||
| `WEBHOOK_SECRET` | webhook 签名验证的全局 HMAC 密钥(当路由未指定自己的密钥时作为回退) |
|
||||
| `API_SERVER_ENABLED` | 启用 OpenAI 兼容 API 服务器(`true`/`false`)。与其他平台并行运行。 |
|
||||
| `API_SERVER_KEY` | API 服务器认证的 Bearer token。非回环绑定时强制执行。 |
|
||||
| `API_SERVER_CORS_ORIGINS` | 允许直接调用 API 服务器的逗号分隔浏览器来源(例如 `http://localhost:3000,http://127.0.0.1:3000`)。默认:禁用。 |
|
||||
| `API_SERVER_PORT` | API 服务器端口(默认:`8642`) |
|
||||
| `API_SERVER_HOST` | API 服务器主机/绑定地址(默认:`127.0.0.1`)。使用 `0.0.0.0` 开放网络访问——需要 `API_SERVER_KEY` 和严格的 `API_SERVER_CORS_ORIGINS` 白名单。 |
|
||||
| `API_SERVER_MODEL_NAME` | `/v1/models` 上公告的模型名称。默认为 profile 名称(默认 profile 为 `hermes-agent`)。适用于 Open WebUI 等前端需要每个连接使用不同模型名称的多用户场景。 |
|
||||
| `GATEWAY_PROXY_URL` | 将消息转发到的远程 Hermes API 服务器 URL([代理模式](/user-guide/messaging/matrix#proxy-mode-e2ee-on-macos))。设置后,gateway 仅处理平台 I/O——所有 agent 工作委托给远程服务器。也可通过 `config.yaml` 中的 `gateway.proxy_url` 配置。 |
|
||||
| `GATEWAY_PROXY_KEY` | 代理模式下与远程 API 服务器认证的 Bearer token。必须与远程主机上的 `API_SERVER_KEY` 一致。 |
|
||||
| `MESSAGING_CWD` | 消息模式下终端命令的工作目录(默认:`~`) |
|
||||
| `GATEWAY_ALLOWED_USERS` | 跨所有平台允许的逗号分隔用户 ID |
|
||||
| `GATEWAY_ALLOW_ALL_USERS` | 无需白名单允许所有用户(`true`/`false`,默认:`false`) |
|
||||
|
||||
### Microsoft Graph(Teams 会议)
|
||||
|
||||
用于即将推出的 Teams 会议摘要流水线的 Microsoft Graph REST 客户端的仅应用凭证。Azure 门户操作步骤和所需 API 权限详见[注册 Microsoft Graph 应用程序](/guides/microsoft-graph-app-registration)。
|
||||
|
||||
| 变量 | 描述 |
|
||||
|----------|-------------|
|
||||
| `MSGRAPH_TENANT_ID` | Graph 应用注册的 Azure AD 租户 ID(目录 GUID)。 |
|
||||
| `MSGRAPH_CLIENT_ID` | Azure 应用注册的应用程序(客户端)ID。 |
|
||||
| `MSGRAPH_CLIENT_SECRET` | 应用注册的客户端密钥值。存储在 `~/.hermes/.env` 中并设置 `chmod 600`;定期通过 Azure 门户轮换。 |
|
||||
| `MSGRAPH_SCOPE` | 客户端凭证 token 请求的 OAuth2 范围(默认:`https://graph.microsoft.com/.default`)。 |
|
||||
| `MSGRAPH_AUTHORITY_URL` | Microsoft 身份平台 authority(默认:`https://login.microsoftonline.com`)。仅对国家/主权云覆盖(例如 GCC High 使用 `https://login.microsoftonline.us`)。 |
|
||||
|
||||
### Microsoft Graph Webhook 监听器
|
||||
|
||||
Graph 事件(Teams 会议、日历、聊天等)的入站变更通知监听器。设置和安全加固详见 [Microsoft Graph Webhook 监听器](/user-guide/messaging/msgraph-webhook)。
|
||||
|
||||
| 变量 | 描述 |
|
||||
|----------|-------------|
|
||||
| `MSGRAPH_WEBHOOK_ENABLED` | 启用 `msgraph_webhook` gateway 平台(`true`/`1`/`yes`)。 |
|
||||
| `MSGRAPH_WEBHOOK_PORT` | 监听器绑定端口(默认:`8646`)。 |
|
||||
| `MSGRAPH_WEBHOOK_CLIENT_STATE` | Graph 在每次通知中回传的共享密钥;与 `hmac.compare_digest` 比较。使用 `openssl rand -hex 32` 生成。 |
|
||||
| `MSGRAPH_WEBHOOK_ACCEPTED_RESOURCES` | 逗号分隔的 Graph 资源路径/模式白名单(例如 `communications/onlineMeetings,chats/*/messages`)。末尾 `*` 为前缀匹配。为空则接受所有。 |
|
||||
| `MSGRAPH_WEBHOOK_ALLOWED_SOURCE_CIDRS` | 允许 POST 到监听器的逗号分隔 CIDR 范围(例如 `52.96.0.0/14,52.104.0.0/14`)。为空则允许所有(默认)。生产环境中应限制为 Microsoft Graph 公布的出口范围。 |
|
||||
|
||||
### Teams 会议摘要投递
|
||||
|
||||
仅在启用 [`teams_pipeline` 插件](/user-guide/messaging/msgraph-webhook)时使用。设置也可在 `config.yaml` 的 `platforms.teams.extra` 下配置——两者都设置时环境变量优先。参见 [Microsoft Teams → 会议摘要投递](/user-guide/messaging/teams#meeting-summary-delivery-teams-meeting-pipeline)。
|
||||
|
||||
| 变量 | 描述 |
|
||||
|----------|-------------|
|
||||
| `TEAMS_DELIVERY_MODE` | `graph` 或 `incoming_webhook`。 |
|
||||
| `TEAMS_INCOMING_WEBHOOK_URL` | Teams 生成的 webhook URL;`TEAMS_DELIVERY_MODE=incoming_webhook` 时必填。 |
|
||||
| `TEAMS_GRAPH_ACCESS_TOKEN` | Graph 投递的预获取委托访问 token。极少需要——未设置时 writer 回退到 `MSGRAPH_*` 应用凭证。 |
|
||||
| `TEAMS_TEAM_ID` | 频道投递的目标 Team ID(`graph` 模式)。 |
|
||||
| `TEAMS_CHANNEL_ID` | 目标频道 ID(与 `TEAMS_TEAM_ID` 配对)。 |
|
||||
| `TEAMS_CHAT_ID` | 目标 1:1 或群聊 ID(`graph` 模式下 team+channel 的替代方案)。 |
|
||||
|
||||
### LINE Messaging API
|
||||
|
||||
由内置 LINE 平台插件(`plugins/platforms/line/`)使用。完整设置详见 [消息 Gateway → LINE](/user-guide/messaging/line)。
|
||||
|
||||
| 变量 | 描述 |
|
||||
|----------|-------------|
|
||||
| `LINE_CHANNEL_ACCESS_TOKEN` | 来自 LINE Developers Console(Messaging API 标签)的长期频道访问 token。必填。 |
|
||||
| `LINE_CHANNEL_SECRET` | 频道密钥(Basic settings 标签);用于 HMAC-SHA256 webhook 签名验证。必填。 |
|
||||
| `LINE_HOST` | webhook 绑定主机(默认:`0.0.0.0`)。 |
|
||||
| `LINE_PORT` | webhook 绑定端口(默认:`8646`)。 |
|
||||
| `LINE_PUBLIC_URL` | 公共 HTTPS base URL(例如 `https://my-tunnel.example.com`)。发送图片/音频/视频时必填——LINE 仅接受 HTTPS 可访问的 URL。 |
|
||||
| `LINE_ALLOWED_USERS` | 允许私信 bot 的逗号分隔用户 ID(`U` 前缀)。 |
|
||||
| `LINE_ALLOWED_GROUPS` | bot 将在其中响应的逗号分隔群组 ID(`C` 前缀)。 |
|
||||
| `LINE_ALLOWED_ROOMS` | bot 将在其中响应的逗号分隔房间 ID(`R` 前缀)。 |
|
||||
| `LINE_ALLOW_ALL_USERS` | 仅用于开发的逃生舱——接受任意来源。默认:`false`。 |
|
||||
| `LINE_HOME_CHANNEL` | `deliver: line` 的 cron 任务的默认投递目标。 |
|
||||
| `LINE_SLOW_RESPONSE_THRESHOLD` | 慢速 LLM Template Buttons postback 触发前的等待秒数(默认:`45`)。设为 `0` 可禁用并始终使用 Push 回退。 |
|
||||
| `LINE_PENDING_TEXT` | 与 postback 按钮一起显示的气泡文本。 |
|
||||
| `LINE_BUTTON_LABEL` | Postback 按钮标签(默认:`Get answer`)。 |
|
||||
| `LINE_DELIVERED_TEXT` | 再次点击已投递 postback 时的回复(默认:`Already replied ✅`)。 |
|
||||
| `LINE_INTERRUPTED_TEXT` | 点击 `/stop` 孤立 postback 按钮时的回复(默认:`Run was interrupted before completion.`)。 |
|
||||
|
||||
### ntfy(推送通知)
|
||||
|
||||
[ntfy](https://ntfy.sh/) 是一个轻量级基于 HTTP 的推送通知服务。通过 [ntfy 移动应用](https://ntfy.sh/docs/subscribe/phone/)订阅话题,向该话题发布消息即可与 agent 交互。
|
||||
|
||||
| 变量 | 描述 |
|
||||
|----------|-------------|
|
||||
| `NTFY_TOPIC` | 订阅的话题(入站消息)。必填。 |
|
||||
| `NTFY_SERVER_URL` | 服务器 URL(默认:`https://ntfy.sh`)。指向自托管 ntfy 以保护隐私。 |
|
||||
| `NTFY_TOKEN` | 可选认证 token。Bearer token(例如 `tk_xyz`)或 `user:pass` 用于 Basic 认证。 |
|
||||
| `NTFY_PUBLISH_TOPIC` | 出站回复的话题(默认为 `NTFY_TOPIC`)。 |
|
||||
| `NTFY_MARKDOWN` | 设为 `true` 可使用 `X-Markdown: true` 头发送回复。默认:`false`。 |
|
||||
| `NTFY_ALLOWED_USERS` | 白名单(视为用户 ID;在 ntfy 中即话题名称)。通常设为与 `NTFY_TOPIC` 相同的值。 |
|
||||
| `NTFY_ALLOW_ALL_USERS` | 仅用于开发的逃生舱——仅在访问控制的私有话题上安全。默认:`false`。 |
|
||||
| `NTFY_HOME_CHANNEL` | `deliver: ntfy` 的 cron 任务的默认投递目标。 |
|
||||
| `NTFY_HOME_CHANNEL_NAME` | 主频道的人类可读标签(默认为话题名称)。 |
|
||||
|
||||
在使用不受信任的话题部署前,请参阅 [ntfy 消息指南](/user-guide/messaging/ntfy)——特别是**身份模型**部分。
|
||||
|
||||
### 高级消息调优
|
||||
|
||||
用于限制出站消息批处理器的高级每平台旋钮。大多数用户无需调整;默认值已设置为在遵守各平台速率限制的同时不显得迟缓。
|
||||
|
||||
| 变量 | 描述 |
|
||||
|----------|-------------|
|
||||
| `HERMES_TELEGRAM_TEXT_BATCH_DELAY_SECONDS` | 刷新排队 Telegram 文本块前的宽限窗口(默认:`0.6`)。 |
|
||||
| `HERMES_TELEGRAM_TEXT_BATCH_SPLIT_DELAY_SECONDS` | 单条 Telegram 消息超过长度限制时分块之间的延迟(默认:`2.0`)。 |
|
||||
| `HERMES_TELEGRAM_MEDIA_BATCH_DELAY_SECONDS` | 刷新排队 Telegram 媒体前的宽限窗口(默认:`0.6`)。 |
|
||||
| `HERMES_TELEGRAM_FOLLOWUP_GRACE_SECONDS` | agent 完成后发送后续消息前的延迟,以避免与最后一个流块竞争。 |
|
||||
| `HERMES_TELEGRAM_HTTP_CONNECT_TIMEOUT` / `_READ_TIMEOUT` / `_WRITE_TIMEOUT` / `_POOL_TIMEOUT` | 覆盖底层 `python-telegram-bot` HTTP 超时(秒)。 |
|
||||
| `HERMES_TELEGRAM_HTTP_POOL_SIZE` | 到 Telegram API 的最大并发 HTTP 连接数。 |
|
||||
| `HERMES_TELEGRAM_DISABLE_FALLBACK_IPS` | 禁用 DNS 失败时使用的硬编码 Cloudflare 回退 IP(`true`/`false`)。 |
|
||||
| `HERMES_DISCORD_TEXT_BATCH_DELAY_SECONDS` | 刷新排队 Discord 文本块前的宽限窗口(默认:`0.6`)。 |
|
||||
| `HERMES_DISCORD_TEXT_BATCH_SPLIT_DELAY_SECONDS` | Discord 消息超过长度限制时分块之间的延迟(默认:`2.0`)。 |
|
||||
| `HERMES_MATRIX_TEXT_BATCH_DELAY_SECONDS` / `_SPLIT_DELAY_SECONDS` | Matrix 等同于 Telegram 批处理旋钮。 |
|
||||
| `HERMES_FEISHU_TEXT_BATCH_DELAY_SECONDS` / `_SPLIT_DELAY_SECONDS` / `_MAX_CHARS` / `_MAX_MESSAGES` | 飞书批处理器调优——延迟、分块延迟、每条消息最大字符数、每批最大消息数。 |
|
||||
| `HERMES_FEISHU_MEDIA_BATCH_DELAY_SECONDS` | 飞书媒体刷新延迟。 |
|
||||
| `HERMES_FEISHU_DEDUP_CACHE_SIZE` | 飞书 webhook 去重缓存大小(默认:`1024`)。 |
|
||||
| `HERMES_WECOM_TEXT_BATCH_DELAY_SECONDS` / `_SPLIT_DELAY_SECONDS` | 企业微信批处理器调优。 |
|
||||
| `HERMES_VISION_DOWNLOAD_TIMEOUT` | 将图片交给视觉模型前下载的超时(秒,默认:`30`)。 |
|
||||
| `HERMES_RESTART_DRAIN_TIMEOUT` | Gateway:`/restart` 时等待活跃运行排空的秒数,超时后强制重启(默认:`900`)。 |
|
||||
| `HERMES_GATEWAY_PLATFORM_CONNECT_TIMEOUT` | gateway 启动期间每个平台的连接超时(秒)。 |
|
||||
| `HERMES_GATEWAY_BUSY_INPUT_MODE` | 默认 gateway 繁忙输入行为:`queue`、`steer` 或 `interrupt`。可通过 `/busy` 按聊天覆盖。 |
|
||||
| `HERMES_GATEWAY_BUSY_ACK_ENABLED` | gateway 是否在用户 agent 繁忙时发送确认消息(⚡/⏳/⏩)(默认:`true`)。设为 `false` 可完全抑制这些消息——输入仍会正常排队/引导/中断,只是聊天回复被静默。从 `config.yaml` 中的 `display.busy_ack_enabled` 桥接。 |
|
||||
| `HERMES_FILE_MUTATION_VERIFIER` | 启用每轮文件变更验证器页脚(默认:`true`)。启用后,Hermes 附加一个建议列表,列出本轮中失败且未被成功写入覆盖的 `write_file`/`patch` 调用。设为 `0`、`false`、`no` 或 `off` 可抑制。镜像 `config.yaml` 中的 `display.file_mutation_verifier`;设置时环境变量优先。 |
|
||||
| `HERMES_CRON_TIMEOUT` | cron 任务 agent 运行的不活动超时(秒,默认:`600`)。agent 在主动调用工具或接收流 token 时可无限运行——仅在空闲时触发。设为 `0` 表示无限制。 |
|
||||
| `HERMES_CRON_SCRIPT_TIMEOUT` | cron 任务附加的预运行脚本超时(秒,默认:`120`)。对需要更长执行时间的脚本(例如随机延迟的反机器人计时)可增大此值。也可通过 `config.yaml` 中的 `cron.script_timeout_seconds` 配置。 |
|
||||
| `HERMES_CRON_MAX_PARALLEL` | 每次 tick 并行运行的最大 cron 任务数(默认:`4`)。 |
|
||||
|
||||
## Agent 行为
|
||||
|
||||
| 变量 | 描述 |
|
||||
|----------|-------------|
|
||||
| `HERMES_MAX_ITERATIONS` | 每次对话的最大工具调用迭代次数(默认:90) |
|
||||
| `HERMES_INFERENCE_MODEL` | 在进程级别覆盖模型名称(优先于本次会话的 `config.yaml`)。也可通过 `-m`/`--model` 标志设置。 |
|
||||
| `HERMES_YOLO_MODE` | 设为 `1` 可绕过危险命令审批提示。等同于 `--yolo`。 |
|
||||
| `HERMES_ACCEPT_HOOKS` | 无需 TTY 提示自动批准 `config.yaml` 中声明的任何未见过的 shell hook。等同于 `--accept-hooks` 或 `hooks_auto_accept: true`。 |
|
||||
| `HERMES_IGNORE_USER_CONFIG` | 跳过 `~/.hermes/config.yaml` 并使用内置默认值(`.env` 中的凭证仍会加载)。等同于 `--ignore-user-config`。 |
|
||||
| `HERMES_IGNORE_RULES` | 跳过 `AGENTS.md`、`SOUL.md`、`.cursorrules`、记忆和预加载技能的自动注入。等同于 `--ignore-rules`。 |
|
||||
| `HERMES_MD_NAMES` | 自动注入的规则文件名逗号分隔列表(默认:`AGENTS.md,CLAUDE.md,.cursorrules,SOUL.md`)。 |
|
||||
| `HERMES_TOOL_PROGRESS` | 工具进度显示的已弃用兼容变量。优先使用 `config.yaml` 中的 `display.tool_progress`。 |
|
||||
| `HERMES_TOOL_PROGRESS_MODE` | 工具进度模式的已弃用兼容变量。优先使用 `config.yaml` 中的 `display.tool_progress`。 |
|
||||
| `HERMES_HUMAN_DELAY_MODE` | 响应节奏:`off`/`natural`/`custom` |
|
||||
| `HERMES_HUMAN_DELAY_MIN_MS` | 自定义延迟范围最小值(毫秒) |
|
||||
| `HERMES_HUMAN_DELAY_MAX_MS` | 自定义延迟范围最大值(毫秒) |
|
||||
| `HERMES_QUIET` | 抑制非必要输出(`true`/`false`) |
|
||||
| `CODEX_HOME` | 启用 [Codex 应用服务器运行时](../user-guide/features/codex-app-server-runtime)时,覆盖 Codex CLI 读取其配置 + 认证的目录(默认:`~/.codex`)。Hermes 的迁移将托管块写入 `<CODEX_HOME>/config.toml`。 |
|
||||
| `HERMES_KANBAN_TASK` | kanban 调度器生成工作进程时设置(任务 UUID)。工作进程和生成的 `hermes-tools` MCP 子进程继承它,以便 kanban 工具正确门控。请勿手动设置。 |
|
||||
| `HERMES_API_TIMEOUT` | LLM API 调用超时(秒,默认:`1800`) |
|
||||
| `HERMES_API_CALL_STALE_TIMEOUT` | 非流式过期调用超时(秒,默认:`300`)。未设置时对本地提供商自动禁用。也可通过 `config.yaml` 中的 `providers.<id>.stale_timeout_seconds` 或 `providers.<id>.models.<model>.stale_timeout_seconds` 配置。 |
|
||||
| `HERMES_STREAM_READ_TIMEOUT` | 流式 socket 读取超时(秒,默认:`120`)。对本地提供商自动增大到 `HERMES_API_TIMEOUT`。如果本地 LLM 在长代码生成期间超时,请增大此值。 |
|
||||
| `HERMES_STREAM_STALE_TIMEOUT` | 过期流检测超时(秒,默认:`180`)。对本地提供商自动禁用。在此窗口内无块到达时触发连接终止。 |
|
||||
| `HERMES_STREAM_RETRIES` | 瞬时网络错误时的流中重连尝试次数(默认:`3`)。 |
|
||||
| `HERMES_AGENT_TIMEOUT` | gateway 中运行 agent 的不活动超时(秒,默认:`900`)。每次工具调用和流 token 时重置。设为 `0` 可禁用。 |
|
||||
| `HERMES_AGENT_TIMEOUT_WARNING` | Gateway:不活动超过此秒数后发送警告消息(默认:`HERMES_AGENT_TIMEOUT` 的 75%)。 |
|
||||
| `HERMES_AGENT_NOTIFY_INTERVAL` | Gateway:长时间运行的 agent 轮次中进度通知的间隔(秒)。 |
|
||||
| `HERMES_CHECKPOINT_TIMEOUT` | 文件系统检查点创建超时(秒,默认:`30`)。 |
|
||||
| `HERMES_EXEC_ASK` | 在 gateway 模式下启用执行审批提示(`true`/`false`) |
|
||||
| `HERMES_ENABLE_PROJECT_PLUGINS` | 为 agent 加载器和仪表板 Web 服务器启用从 `./.hermes/plugins/` 自动发现仓库本地插件。接受标准真值集:`1`/`true`/`yes`/`on`(不区分大小写)。其他所有值——包括 `0`、`false`、`no`、`off` 和空字符串——均视为**禁用**(默认)。注意:自 GHSA-5qr3-c538-wm9j(#29156)起,即使启用此变量,仪表板 Web 服务器也拒绝自动导入项目插件的 Python `api` 文件——项目插件可通过静态 JS/CSS 扩展 UI,但其后端路由仅在移至 `~/.hermes/plugins/` 后才会加载。 |
|
||||
| `HERMES_PLUGINS_DEBUG` | `1`/`true` 可在 stderr 上输出详细的插件发现日志——扫描的目录、解析的 manifest、跳过原因以及解析或 `register()` 失败时的完整回溯。面向插件作者。 |
|
||||
| `HERMES_BACKGROUND_NOTIFICATIONS` | gateway 中后台进程通知模式:`all`(默认)、`result`、`error`、`off` |
|
||||
| `HERMES_EPHEMERAL_SYSTEM_PROMPT` | 在 API 调用时注入的临时系统 prompt(永不持久化到会话) |
|
||||
| `HERMES_PREFILL_MESSAGES_FILE` | 包含在 API 调用时注入的临时预填消息的 JSON 文件路径。 |
|
||||
| `HERMES_ALLOW_PRIVATE_URLS` | `true`/`false`——允许工具获取 localhost/私有网络 URL。gateway 模式下默认关闭。 |
|
||||
| `HERMES_REDACT_SECRETS` | `true`/`false`——控制工具输出、日志和聊天响应中的密钥脱敏(默认:`true`)。 |
|
||||
| `HERMES_WRITE_SAFE_ROOT` | 可选目录前缀,限制 `write_file`/`patch` 写入;超出范围的路径需要审批。 |
|
||||
| `HERMES_DISABLE_FILE_STATE_GUARD` | 设为 `1` 可关闭 `patch`/`write_file` 上的"文件自上次读取后已更改"保护。 |
|
||||
| `HERMES_CORE_TOOLS` | 规范核心工具列表的逗号分隔覆盖(高级;极少需要)。 |
|
||||
| `HERMES_BUNDLED_SKILLS` | 启动时加载的内置技能列表的逗号分隔覆盖。 |
|
||||
| `HERMES_OPTIONAL_SKILLS` | 首次运行时自动安装的可选技能名称逗号分隔列表。 |
|
||||
| `HERMES_DEBUG_INTERRUPT` | 设为 `1` 可将详细的中断/取消追踪记录到 `agent.log`。 |
|
||||
| `HERMES_DUMP_REQUESTS` | 将 API 请求载荷转储到日志文件(`true`/`false`) |
|
||||
| `HERMES_DUMP_REQUEST_STDOUT` | 将 API 请求载荷转储到 stdout 而非日志文件。 |
|
||||
| `HERMES_OAUTH_TRACE` | 设为 `1` 可记录 OAuth token 交换和刷新尝试。包含脱敏的时序信息。 |
|
||||
| `HERMES_OAUTH_FILE` | 覆盖 OAuth 凭证存储路径(默认:`~/.hermes/auth.json`)。 |
|
||||
| `HERMES_AGENT_HELP_GUIDANCE` | 为自定义部署在系统 prompt 中追加额外指导文本。 |
|
||||
| `HERMES_AGENT_LOGO` | 覆盖 CLI 启动时的 ASCII 横幅 logo。 |
|
||||
| `DELEGATION_MAX_CONCURRENT_CHILDREN` | 每个 `delegate_task` 批次的最大并行子 agent 数(默认:`3`,下限为 1,无上限)。也可通过 `config.yaml` 中的 `delegation.max_concurrent_children` 配置——config 值优先。 |
|
||||
|
||||
## 界面
|
||||
|
||||
| 变量 | 描述 |
|
||||
|----------|-------------|
|
||||
| `HERMES_TUI` | 设为 `1` 时启动 [TUI](../user-guide/tui.md) 而非经典 CLI。等同于传入 `--tui`。 |
|
||||
| `HERMES_TUI_DIR` | 预构建 `ui-tui/` 目录的路径(必须包含 `dist/entry.js` 和已填充的 `node_modules`)。供发行版和 Nix 使用以跳过首次启动时的 `npm install`。 |
|
||||
| `HERMES_TUI_RESUME` | 启动时按 ID 恢复特定 TUI 会话。设置后,`hermes --tui` 跳过创建新会话并接续指定会话——适用于断开连接或终端崩溃后重新连接。 |
|
||||
| `HERMES_TUI_THEME` | 强制 TUI 颜色主题:`light`、`dark` 或原始 6 字符背景十六进制(例如 `ffffff` 或 `1a1a2e`)。未设置时,Hermes 使用 `COLORFGBG` 和终端背景查询自动检测;此变量覆盖不设置 `COLORFGBG` 的终端(Ghostty、Warp、iTerm2 等)上的检测。 |
|
||||
| `HERMES_INFERENCE_MODEL` | 为 `hermes -z`/`hermes chat` 强制指定模型而不修改 `config.yaml`。与 `--provider` 标志配合使用。适用于需要每次运行覆盖默认模型的脚本调用者(sweeper、CI、批量运行器)。 |
|
||||
|
||||
## 会话设置
|
||||
|
||||
| 变量 | 描述 |
|
||||
|----------|-------------|
|
||||
| `SESSION_IDLE_MINUTES` | 不活动 N 分钟后重置会话(默认:1440) |
|
||||
| `SESSION_RESET_HOUR` | 24 小时制每日重置时间(默认:4 = 凌晨 4 点) |
|
||||
| `HERMES_SESSION_ID` | **自动导出到 Hermes 生成的每个工具子进程**(`terminal`、`execute_code`、持久 shell、Docker/Singularity 后端、委托子 agent 运行)。由 agent 设置为当前会话 ID;从工具调用的用户脚本可读取它,以将其输出、遥测或副作用与原始 Hermes 会话关联。**不应手动设置**——从父 shell 覆盖仅在 agent 运行外生效,且 agent 启动会话时会被覆盖。 |
|
||||
|
||||
## 上下文压缩(仅 config.yaml)
|
||||
|
||||
上下文压缩完全通过 `config.yaml` 配置——没有对应的环境变量。阈值设置位于 `compression:` 块,摘要模型/提供商位于 `auxiliary.compression:` 下。
|
||||
|
||||
```yaml
|
||||
compression:
|
||||
enabled: true
|
||||
threshold: 0.50
|
||||
target_ratio: 0.20 # fraction of threshold to preserve as recent tail
|
||||
protect_last_n: 20 # minimum recent messages to keep uncompressed
|
||||
```
|
||||
|
||||
:::info 旧版迁移
|
||||
包含 `compression.summary_model`、`compression.summary_provider` 和 `compression.summary_base_url` 的旧版配置在首次加载时自动迁移到 `auxiliary.compression.*`。
|
||||
:::
|
||||
|
||||
## 辅助任务覆盖
|
||||
|
||||
| 变量 | 描述 |
|
||||
|----------|-------------|
|
||||
| `AUXILIARY_VISION_PROVIDER` | 覆盖视觉任务的提供商 |
|
||||
| `AUXILIARY_VISION_MODEL` | 覆盖视觉任务的模型 |
|
||||
| `AUXILIARY_VISION_BASE_URL` | 视觉任务的直接 OpenAI 兼容端点 |
|
||||
| `AUXILIARY_VISION_API_KEY` | 与 `AUXILIARY_VISION_BASE_URL` 配对的 API 密钥 |
|
||||
| `AUXILIARY_WEB_EXTRACT_PROVIDER` | 覆盖网页提取/摘要的提供商 |
|
||||
| `AUXILIARY_WEB_EXTRACT_MODEL` | 覆盖网页提取/摘要的模型 |
|
||||
| `AUXILIARY_WEB_EXTRACT_BASE_URL` | 网页提取/摘要的直接 OpenAI 兼容端点 |
|
||||
| `AUXILIARY_WEB_EXTRACT_API_KEY` | 与 `AUXILIARY_WEB_EXTRACT_BASE_URL` 配对的 API 密钥 |
|
||||
|
||||
对于特定任务的直接端点,Hermes 使用该任务配置的 API 密钥或 `OPENAI_API_KEY`。不会为这些自定义端点复用 `OPENROUTER_API_KEY`。
|
||||
|
||||
## 回退提供商(仅 config.yaml)
|
||||
|
||||
主模型回退链完全通过 `config.yaml` 配置——没有对应的环境变量。在顶层添加包含 `provider` 和 `model` 键的 `fallback_providers` 列表,以在主模型遇到错误时启用自动故障转移。
|
||||
|
||||
```yaml
|
||||
fallback_providers:
|
||||
- provider: openrouter
|
||||
model: anthropic/claude-sonnet-4
|
||||
```
|
||||
|
||||
旧版顶层 `fallback_model` 单提供商格式仍可向后兼容读取,但新配置应使用 `fallback_providers`。
|
||||
|
||||
详见 [回退提供商](/user-guide/features/fallback-providers)。
|
||||
|
||||
## 提供商路由(仅 config.yaml)
|
||||
|
||||
这些配置写入 `~/.hermes/config.yaml` 的 `provider_routing` 部分:
|
||||
|
||||
| 键 | 描述 |
|
||||
|-----|-------------|
|
||||
| `sort` | 排序提供商:`"price"`(默认)、`"throughput"` 或 `"latency"` |
|
||||
| `only` | 允许的提供商 slug 列表(例如 `["anthropic", "google"]`) |
|
||||
| `ignore` | 跳过的提供商 slug 列表 |
|
||||
| `order` | 按顺序尝试的提供商 slug 列表 |
|
||||
| `require_parameters` | 仅使用支持所有请求参数的提供商(`true`/`false`) |
|
||||
| `data_collection` | `"allow"`(默认)或 `"deny"` 以排除存储数据的提供商 |
|
||||
|
||||
:::tip
|
||||
使用 `hermes config set` 设置环境变量——它会自动将其保存到正确的文件(密钥保存到 `.env`,其他所有内容保存到 `config.yaml`)。
|
||||
:::
|
||||
@ -0,0 +1,859 @@
|
||||
---
|
||||
sidebar_position: 3
|
||||
title: "常见问题与故障排查"
|
||||
description: "Hermes Agent 常见问题解答及常见问题解决方案"
|
||||
---
|
||||
|
||||
# 常见问题与故障排查
|
||||
|
||||
针对最常见问题的快速解答与修复方法。
|
||||
|
||||
---
|
||||
|
||||
## 常见问题
|
||||
|
||||
### Hermes 支持哪些 LLM 提供商?
|
||||
|
||||
Hermes Agent 可与任何兼容 OpenAI 的 API 配合使用。支持的提供商包括:
|
||||
|
||||
- **[OpenRouter](https://openrouter.ai/)** — 通过一个 API key 访问数百个模型(推荐,灵活性强)
|
||||
- **Nous Portal** — Nous Research 自有推理端点
|
||||
- **OpenAI** — GPT-5.4、GPT-5-codex、GPT-4.1、GPT-4o 等
|
||||
- **Anthropic** — Claude 模型(直接 API、通过 `hermes login anthropic` 进行 OAuth、OpenRouter 或任何兼容代理)
|
||||
- **Google** — Gemini 模型(通过 `gemini` 提供商直接调用 API、`google-gemini-cli` OAuth 提供商、OpenRouter 或兼容代理)
|
||||
- **z.ai / ZhipuAI** — GLM 模型
|
||||
- **Kimi / Moonshot AI** — Kimi 模型
|
||||
- **MiniMax** — 全球及中国区端点
|
||||
- **本地模型** — 通过 [Ollama](https://ollama.com/)、[vLLM](https://docs.vllm.ai/)、[llama.cpp](https://github.com/ggerganov/llama.cpp)、[SGLang](https://github.com/sgl-project/sglang) 或任何兼容 OpenAI 的服务器
|
||||
|
||||
使用 `hermes model` 设置提供商,或直接编辑 `~/.hermes/.env`。所有提供商 key 请参阅[环境变量](./environment-variables.md)参考文档。
|
||||
|
||||
### 支持 Windows 吗?
|
||||
|
||||
**原生不支持。** Hermes Agent 需要类 Unix 环境。在 Windows 上,请安装 [WSL2](https://learn.microsoft.com/en-us/windows/wsl/install) 并在其中运行 Hermes。标准安装命令在 WSL2 中可完美运行:
|
||||
|
||||
```bash
|
||||
curl -fsSL https://raw.githubusercontent.com/NousResearch/hermes-agent/main/scripts/install.sh | bash
|
||||
```
|
||||
|
||||
### 我在 WSL2 中运行 Hermes,如何控制 Windows 上的普通 Chrome?
|
||||
|
||||
推荐使用 MCP bridge(桥接),而非 `/browser connect`。
|
||||
|
||||
推荐方案:
|
||||
|
||||
- 在 WSL2 内运行 Hermes
|
||||
- 继续使用 Windows 上已登录的普通 Chrome
|
||||
- 通过 `cmd.exe` 或 `powershell.exe` 将 `chrome-devtools-mcp` 添加为 MCP 服务器
|
||||
- 让 Hermes 使用生成的 MCP 浏览器工具
|
||||
|
||||
这比强制 Hermes 核心浏览器传输直接跨越 WSL2/Windows 边界进行附加更为可靠。
|
||||
|
||||
参见:
|
||||
|
||||
- [在 Hermes 中使用 MCP](../guides/use-mcp-with-hermes.md#wsl2-bridge-hermes-in-wsl-to-windows-chrome)
|
||||
- [浏览器自动化](../user-guide/features/browser.md#wsl2--windows-chrome-prefer-mcp-over-browser-connect)
|
||||
|
||||
### 支持 Android / Termux 吗?
|
||||
|
||||
支持 — Hermes 现已为 Android 手机提供经过测试的 Termux 安装路径。
|
||||
|
||||
快速安装:
|
||||
|
||||
```bash
|
||||
curl -fsSL https://raw.githubusercontent.com/NousResearch/hermes-agent/main/scripts/install.sh | bash
|
||||
```
|
||||
|
||||
完整的手动步骤、支持的扩展及当前限制,请参阅 [Termux 指南](../getting-started/termux.md)。
|
||||
|
||||
重要说明:完整的 `.[all]` 扩展目前在 Android 上不可用,因为 `voice` 扩展依赖 `faster-whisper` → `ctranslate2`,而 `ctranslate2` 未发布 Android wheel 包。请改用经过测试的 `.[termux]` 扩展。
|
||||
|
||||
### 我的数据会被发送到哪里?
|
||||
|
||||
API 调用**仅发送至您配置的 LLM 提供商**(例如 OpenRouter、您本地的 Ollama 实例)。Hermes Agent 不收集遥测数据、使用数据或分析数据。您的对话、记忆和技能均存储在本地 `~/.hermes/` 目录中。
|
||||
|
||||
### 可以离线使用 / 使用本地模型吗?
|
||||
|
||||
可以。运行 `hermes model`,选择**自定义端点**,然后输入您服务器的 URL:
|
||||
|
||||
```bash
|
||||
hermes model
|
||||
# 选择:Custom endpoint(手动输入 URL)
|
||||
# API base URL: http://localhost:11434/v1
|
||||
# API key: ollama
|
||||
# Model name: qwen3.5:27b
|
||||
# Context length: 32768 ← 设置为与您服务器实际上下文窗口匹配的值
|
||||
```
|
||||
|
||||
或直接在 `config.yaml` 中配置:
|
||||
|
||||
```yaml
|
||||
model:
|
||||
default: qwen3.5:27b
|
||||
provider: custom
|
||||
base_url: http://localhost:11434/v1
|
||||
```
|
||||
|
||||
Hermes 会将端点、提供商和 base URL 持久化到 `config.yaml`,重启后仍然有效。如果您的本地服务器只加载了一个模型,`/model custom` 会自动检测到它。您也可以在 config.yaml 中设置 `provider: custom` — 这是一个一等提供商,不是其他任何东西的别名。
|
||||
|
||||
此方式适用于 Ollama、vLLM、llama.cpp server、SGLang、LocalAI 等。详情请参阅[配置指南](../user-guide/configuration.md)。
|
||||
|
||||
:::tip Ollama 用户
|
||||
如果您在 Ollama 中设置了自定义 `num_ctx`(例如 `ollama run --num_ctx 16384`),请确保在 Hermes 中设置匹配的上下文长度 — Ollama 的 `/api/show` 报告的是模型的*最大*上下文,而非您配置的实际 `num_ctx`。
|
||||
:::
|
||||
|
||||
:::tip 本地模型超时问题
|
||||
Hermes 会自动检测本地端点并放宽流式传输超时(读取超时从 120s 提升至 1800s,禁用停滞流检测)。如果在非常大的上下文下仍然超时,请在 `.env` 中设置 `HERMES_STREAM_READ_TIMEOUT=1800`。详情请参阅[本地 LLM 指南](../guides/local-llm-on-mac.md#timeouts)。
|
||||
:::
|
||||
|
||||
### 费用是多少?
|
||||
|
||||
Hermes Agent 本身**免费且开源**(MIT 许可证)。您只需为所选提供商的 LLM API 用量付费。本地模型完全免费运行。
|
||||
|
||||
### 多人可以使用同一个实例吗?
|
||||
|
||||
可以。[消息网关](../user-guide/messaging/index.md)允许多个用户通过 Telegram、Discord、Slack、WhatsApp 或 Home Assistant 与同一个 Hermes Agent 实例交互。访问权限通过白名单(特定用户 ID)和私信配对(第一个发消息的用户获得访问权)来控制。
|
||||
|
||||
### 记忆(memory)和技能(skills)有什么区别?
|
||||
|
||||
- **记忆**存储**事实** — 智能体了解的关于您、您的项目和偏好的信息。记忆根据相关性自动检索。
|
||||
- **技能**存储**流程** — 如何完成某件事的分步说明。当智能体遇到类似任务时会调用技能。
|
||||
|
||||
两者均跨会话持久化。详情请参阅[记忆](../user-guide/features/memory.md)和[技能](../user-guide/features/skills.md)。
|
||||
|
||||
### 可以在我自己的 Python 项目中使用吗?
|
||||
|
||||
可以。导入 `AIAgent` 类,以编程方式使用 Hermes:
|
||||
|
||||
```python
|
||||
from run_agent import AIAgent
|
||||
|
||||
agent = AIAgent(model="anthropic/claude-opus-4.7")
|
||||
response = agent.chat("Explain quantum computing briefly")
|
||||
```
|
||||
|
||||
完整 API 用法请参阅 [Python 库指南](../user-guide/features/code-execution.md)。
|
||||
|
||||
---
|
||||
|
||||
## 故障排查
|
||||
|
||||
### 安装问题
|
||||
|
||||
#### 安装后出现 `hermes: command not found`
|
||||
|
||||
**原因:** Shell 未重新加载更新后的 PATH。
|
||||
|
||||
**解决方案:**
|
||||
```bash
|
||||
# 重新加载 shell 配置文件
|
||||
source ~/.bashrc # bash
|
||||
source ~/.zshrc # zsh
|
||||
|
||||
# 或开启一个新的终端会话
|
||||
```
|
||||
|
||||
如果仍然无效,请验证安装位置:
|
||||
```bash
|
||||
which hermes
|
||||
ls ~/.local/bin/hermes
|
||||
```
|
||||
|
||||
:::tip
|
||||
安装程序会将 `~/.local/bin` 添加到您的 PATH。如果您使用非标准 shell 配置,请手动添加 `export PATH="$HOME/.local/bin:$PATH"`。
|
||||
:::
|
||||
|
||||
#### Python 版本过旧
|
||||
|
||||
**原因:** Hermes 需要 Python 3.11 或更新版本。
|
||||
|
||||
**解决方案:**
|
||||
```bash
|
||||
python3 --version # 检查当前版本
|
||||
|
||||
# 安装更新的 Python
|
||||
sudo apt install python3.12 # Ubuntu/Debian
|
||||
brew install python@3.12 # macOS
|
||||
```
|
||||
|
||||
安装程序会自动处理此问题 — 如果在手动安装时看到此错误,请先升级 Python。
|
||||
|
||||
#### 终端命令提示 `node: command not found`(或 `nvm`、`pyenv`、`asdf` 等)
|
||||
|
||||
**原因:** Hermes 在启动时通过运行一次 `bash -l` 构建每个会话的环境快照。bash 登录 shell 会读取 `/etc/profile`、`~/.bash_profile` 和 `~/.profile`,但**不会 source `~/.bashrc`** — 因此在 `~/.bashrc` 中安装自身的工具(`nvm`、`asdf`、`pyenv`、`cargo`、自定义 `PATH` 导出)对快照不可见。当 Hermes 在 systemd 下运行或在未预加载交互式 shell 配置的最小 shell 中运行时,此问题最为常见。
|
||||
|
||||
**解决方案:** Hermes 默认自动 source `~/.bashrc`。如果这还不够 — 例如您是 zsh 用户,PATH 在 `~/.zshrc` 中,或者您从独立文件初始化 `nvm` — 请在 `~/.hermes/config.yaml` 中列出需要额外 source 的文件:
|
||||
|
||||
```yaml
|
||||
terminal:
|
||||
shell_init_files:
|
||||
- ~/.zshrc # zsh 用户:将 zsh 管理的 PATH 引入 bash 快照
|
||||
- ~/.nvm/nvm.sh # 直接初始化 nvm(不依赖 shell 类型)
|
||||
- /etc/profile.d/cargo.sh # 系统级 rc 文件
|
||||
# 设置此列表后,默认的 ~/.bashrc 自动 source 不会被添加 —
|
||||
# 如需同时保留,请显式包含:
|
||||
# - ~/.bashrc
|
||||
# - ~/.zshrc
|
||||
```
|
||||
|
||||
缺失的文件会被静默跳过。source 在 bash 中执行,因此依赖 zsh 专有语法的文件可能报错 — 如有顾虑,建议只 source PATH 设置部分(例如直接 source nvm 的 `nvm.sh`),而非整个 rc 文件。
|
||||
|
||||
如需禁用自动 source 行为(仅使用严格的登录 shell 语义):
|
||||
|
||||
```yaml
|
||||
terminal:
|
||||
auto_source_bashrc: false
|
||||
```
|
||||
|
||||
#### `uv: command not found`
|
||||
|
||||
**原因:** `uv` 包管理器未安装或不在 PATH 中。
|
||||
|
||||
**解决方案:**
|
||||
```bash
|
||||
curl -LsSf https://astral.sh/uv/install.sh | sh
|
||||
source ~/.bashrc
|
||||
```
|
||||
|
||||
#### 安装时出现权限拒绝错误
|
||||
|
||||
**原因:** 对安装目录的写入权限不足。
|
||||
|
||||
**解决方案:**
|
||||
```bash
|
||||
# 不要对安装程序使用 sudo — 它安装到 ~/.local/bin
|
||||
# 如果之前使用 sudo 安装,请先清理:
|
||||
sudo rm /usr/local/bin/hermes
|
||||
# 然后重新运行标准安装程序
|
||||
curl -fsSL https://raw.githubusercontent.com/NousResearch/hermes-agent/main/scripts/install.sh | bash
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### 提供商与模型问题
|
||||
|
||||
#### `/model` 只显示一个提供商 / 无法切换提供商
|
||||
|
||||
**原因:** 会话内的 `/model` 只能在您**已配置**的提供商之间切换。如果您只设置了 OpenRouter,`/model` 就只会显示 OpenRouter。
|
||||
|
||||
**解决方案:** 退出当前会话,在终端中使用 `hermes model` 添加新提供商:
|
||||
|
||||
```bash
|
||||
# 先退出 Hermes 聊天会话(Ctrl+C 或 /quit)
|
||||
|
||||
# 运行完整的提供商设置向导
|
||||
hermes model
|
||||
|
||||
# 此命令可以:添加提供商、运行 OAuth、输入 API key、配置端点
|
||||
```
|
||||
|
||||
通过 `hermes model` 添加新提供商后,启动新的聊天会话 — `/model` 将显示所有已配置的提供商。
|
||||
|
||||
:::tip 快速参考
|
||||
| 目标 | 使用方式 |
|
||||
|-----------|-----|
|
||||
| 添加新提供商 | `hermes model`(从终端) |
|
||||
| 输入/更改 API key | `hermes model`(从终端) |
|
||||
| 会话中途切换模型 | `/model <name>`(会话内) |
|
||||
| 切换到其他已配置的提供商 | `/model provider:model`(会话内) |
|
||||
:::
|
||||
|
||||
#### API key 不起作用
|
||||
|
||||
**原因:** key 缺失、已过期、设置错误或属于错误的提供商。
|
||||
|
||||
**解决方案:**
|
||||
```bash
|
||||
# 检查您的配置
|
||||
hermes config show
|
||||
|
||||
# 重新配置您的提供商
|
||||
hermes model
|
||||
|
||||
# 或直接设置
|
||||
hermes config set OPENROUTER_API_KEY sk-or-v1-xxxxxxxxxxxx
|
||||
```
|
||||
|
||||
:::warning
|
||||
请确保 key 与提供商匹配。OpenAI 的 key 无法用于 OpenRouter,反之亦然。检查 `~/.hermes/.env` 中是否有冲突条目。
|
||||
:::
|
||||
|
||||
#### 模型不可用 / 找不到模型
|
||||
|
||||
**原因:** 模型标识符不正确,或该模型在您的提供商上不可用。
|
||||
|
||||
**解决方案:**
|
||||
```bash
|
||||
# 列出您的提供商可用的模型
|
||||
hermes model
|
||||
|
||||
# 设置有效的模型
|
||||
hermes config set HERMES_MODEL anthropic/claude-opus-4.7
|
||||
|
||||
# 或按会话指定
|
||||
hermes chat --model openrouter/meta-llama/llama-3.1-70b-instruct
|
||||
```
|
||||
|
||||
#### 速率限制(429 错误)
|
||||
|
||||
**原因:** 您已超出提供商的速率限制。
|
||||
|
||||
**解决方案:** 稍等片刻后重试。对于持续使用,请考虑:
|
||||
- 升级您的提供商套餐
|
||||
- 切换到其他模型或提供商
|
||||
- 使用 `hermes chat --provider <alternative>` 路由到其他后端
|
||||
|
||||
#### 上下文长度超限
|
||||
|
||||
**原因:** 对话内容超出模型的上下文窗口,或 Hermes 检测到的模型上下文长度有误。
|
||||
|
||||
**解决方案:**
|
||||
```bash
|
||||
# 压缩当前会话
|
||||
/compress
|
||||
|
||||
# 或开始新会话
|
||||
hermes chat
|
||||
|
||||
# 使用上下文窗口更大的模型
|
||||
hermes chat --model openrouter/google/gemini-3-flash-preview
|
||||
```
|
||||
|
||||
如果在第一次长对话时就出现此问题,Hermes 可能检测到了错误的模型上下文长度。检查检测结果:
|
||||
|
||||
查看 CLI 启动行 — 它会显示检测到的上下文长度(例如 `📊 Context limit: 128000 tokens`)。您也可以在会话中使用 `/usage` 查看。
|
||||
|
||||
如需修正上下文检测,请显式设置:
|
||||
|
||||
```yaml
|
||||
# 在 ~/.hermes/config.yaml 中
|
||||
model:
|
||||
default: your-model-name
|
||||
context_length: 131072 # 您模型的实际上下文窗口
|
||||
```
|
||||
|
||||
或对于自定义端点,按模型添加:
|
||||
|
||||
```yaml
|
||||
custom_providers:
|
||||
- name: "My Server"
|
||||
base_url: "http://localhost:11434/v1"
|
||||
models:
|
||||
qwen3.5:27b:
|
||||
context_length: 32768
|
||||
```
|
||||
|
||||
有关自动检测的工作原理及所有覆盖选项,请参阅[上下文长度检测](../integrations/providers.md#context-length-detection)。
|
||||
|
||||
---
|
||||
|
||||
### 终端问题
|
||||
|
||||
#### 命令被标记为危险而阻止
|
||||
|
||||
**原因:** Hermes 检测到潜在的破坏性命令(例如 `rm -rf`、`DROP TABLE`)。这是一项安全功能。
|
||||
|
||||
**解决方案:** 出现提示时,检查命令并输入 `y` 批准执行。您也可以:
|
||||
- 要求智能体使用更安全的替代方案
|
||||
- 在[安全文档](../user-guide/security.md)中查看完整的危险模式列表
|
||||
|
||||
:::tip
|
||||
这是预期行为 — Hermes 绝不会静默执行破坏性命令。审批提示会向您显示将要执行的确切内容。
|
||||
:::
|
||||
|
||||
#### 通过消息网关时 `sudo` 不起作用
|
||||
|
||||
**原因:** 消息网关在没有交互式终端的情况下运行,因此 `sudo` 无法提示输入密码。
|
||||
|
||||
**解决方案:**
|
||||
- 在消息中避免使用 `sudo` — 请智能体寻找替代方案
|
||||
- 如果必须使用 `sudo`,在 `/etc/sudoers` 中为特定命令配置免密 sudo
|
||||
- 或切换到终端界面执行管理任务:`hermes chat`
|
||||
|
||||
#### Docker 后端无法连接
|
||||
|
||||
**原因:** Docker 守护进程未运行,或用户缺少相应权限。
|
||||
|
||||
**解决方案:**
|
||||
```bash
|
||||
# 检查 Docker 是否在运行
|
||||
docker info
|
||||
|
||||
# 将您的用户添加到 docker 组
|
||||
sudo usermod -aG docker $USER
|
||||
newgrp docker
|
||||
|
||||
# 验证
|
||||
docker run hello-world
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### 消息问题
|
||||
|
||||
#### Bot 不响应消息
|
||||
|
||||
**原因:** Bot 未运行、未授权,或您的用户不在白名单中。
|
||||
|
||||
**解决方案:**
|
||||
```bash
|
||||
# 检查网关是否在运行
|
||||
hermes gateway status
|
||||
|
||||
# 启动网关
|
||||
hermes gateway start
|
||||
|
||||
# 查看错误日志
|
||||
cat ~/.hermes/logs/gateway.log | tail -50
|
||||
```
|
||||
|
||||
#### 消息未送达
|
||||
|
||||
**原因:** 网络问题、bot token 已过期,或平台 webhook 配置错误。
|
||||
|
||||
**解决方案:**
|
||||
- 使用 `hermes gateway setup` 验证您的 bot token 是否有效
|
||||
- 检查网关日志:`cat ~/.hermes/logs/gateway.log | tail -50`
|
||||
- 对于基于 webhook 的平台(Slack、WhatsApp),确保您的服务器可公开访问
|
||||
|
||||
#### 白名单混淆 — 谁可以与 bot 交互?
|
||||
|
||||
**原因:** 授权模式决定谁可以获得访问权限。
|
||||
|
||||
**解决方案:**
|
||||
|
||||
| 模式 | 工作方式 |
|
||||
|------|-------------|
|
||||
| **白名单** | 只有配置中列出的用户 ID 可以交互 |
|
||||
| **私信配对** | 第一个在私信中发消息的用户获得独占访问权 |
|
||||
| **开放** | 任何人都可以交互(不建议用于生产环境) |
|
||||
|
||||
在 `~/.hermes/config.yaml` 中您的网关设置下进行配置。请参阅[消息文档](../user-guide/messaging/index.md)。
|
||||
|
||||
#### 网关无法启动
|
||||
|
||||
**原因:** 缺少依赖项、端口冲突或 token 配置错误。
|
||||
|
||||
**解决方案:**
|
||||
```bash
|
||||
# 安装核心消息网关依赖项
|
||||
pip install "hermes-agent[messaging]" # Telegram、Discord、Slack 及共享网关依赖
|
||||
|
||||
# 检查端口冲突
|
||||
lsof -i :8080
|
||||
|
||||
# 验证配置
|
||||
hermes config show
|
||||
```
|
||||
|
||||
#### WSL:网关持续断开连接或 `hermes gateway start` 失败
|
||||
|
||||
**原因:** WSL 的 systemd 支持不稳定。许多 WSL2 安装未启用 systemd,即使启用,服务也可能在 WSL 重启或 Windows 空闲关机后无法存活。
|
||||
|
||||
**解决方案:** 使用前台模式代替 systemd 服务:
|
||||
|
||||
```bash
|
||||
# 方案一:直接前台运行(最简单)
|
||||
hermes gateway run
|
||||
|
||||
# 方案二:通过 tmux 持久运行(关闭终端后仍存活)
|
||||
tmux new -s hermes 'hermes gateway run'
|
||||
# 稍后重新连接:tmux attach -t hermes
|
||||
|
||||
# 方案三:通过 nohup 后台运行
|
||||
nohup hermes gateway run > ~/.hermes/logs/gateway.log 2>&1 &
|
||||
```
|
||||
|
||||
如果仍想尝试 systemd,请确保已启用:
|
||||
|
||||
1. 打开 `/etc/wsl.conf`(不存在则创建)
|
||||
2. 添加:
|
||||
```ini
|
||||
[boot]
|
||||
systemd=true
|
||||
```
|
||||
3. 在 PowerShell 中执行:`wsl --shutdown`
|
||||
4. 重新打开 WSL 终端
|
||||
5. 验证:`systemctl is-system-running` 应显示 "running" 或 "degraded"
|
||||
|
||||
:::tip Windows 开机自启
|
||||
如需可靠的自启动,使用 Windows 任务计划程序在登录时启动 WSL + 网关:
|
||||
1. 创建一个任务,运行 `wsl -d Ubuntu -- bash -lc 'hermes gateway run'`
|
||||
2. 设置在用户登录时触发
|
||||
:::
|
||||
|
||||
#### macOS:网关找不到 Node.js / ffmpeg / 其他工具
|
||||
|
||||
**原因:** launchd 服务继承的是最小 PATH(`/usr/bin:/bin:/usr/sbin:/sbin`),不包含 Homebrew、nvm、cargo 或其他用户安装的工具目录。这通常会导致 WhatsApp bridge(`node not found`)或语音转录(`ffmpeg not found`)失败。
|
||||
|
||||
**解决方案:** 网关在您运行 `hermes gateway install` 时会捕获您的 shell PATH。如果您在设置网关后安装了新工具,请重新运行 install 以捕获更新后的 PATH:
|
||||
|
||||
```bash
|
||||
hermes gateway install # 重新快照当前 PATH
|
||||
hermes gateway start # 检测到更新的 plist 并重新加载
|
||||
```
|
||||
|
||||
您可以验证 plist 中的 PATH 是否正确:
|
||||
```bash
|
||||
/usr/libexec/PlistBuddy -c "Print :EnvironmentVariables:PATH" \
|
||||
~/Library/LaunchAgents/ai.hermes.gateway.plist
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### 性能问题
|
||||
|
||||
#### 响应缓慢
|
||||
|
||||
**原因:** 模型较大、API 服务器距离较远,或系统 prompt(提示词)包含过多工具。
|
||||
|
||||
**解决方案:**
|
||||
- 尝试更快/更小的模型:`hermes chat --model openrouter/meta-llama/llama-3.1-8b-instruct`
|
||||
- 减少激活的工具集:`hermes chat -t "terminal"`
|
||||
- 检查到提供商的网络延迟
|
||||
- 对于本地模型,确保有足够的 GPU VRAM
|
||||
|
||||
#### token 用量过高
|
||||
|
||||
**原因:** 对话过长、系统 prompt 冗长,或大量工具调用积累了上下文。
|
||||
|
||||
**解决方案:**
|
||||
```bash
|
||||
# 压缩对话以减少 token
|
||||
/compress
|
||||
|
||||
# 查看会话 token 用量
|
||||
/usage
|
||||
```
|
||||
|
||||
:::tip
|
||||
在长会话中定期使用 `/compress`。它会对对话历史进行摘要,在保留上下文的同时显著减少 token 用量。
|
||||
:::
|
||||
|
||||
#### 会话过长
|
||||
|
||||
**原因:** 长时间对话积累了大量消息和工具输出,接近上下文限制。
|
||||
|
||||
**解决方案:**
|
||||
```bash
|
||||
# 压缩当前会话(保留关键上下文)
|
||||
/compress
|
||||
|
||||
# 开始新会话并引用旧会话
|
||||
hermes chat
|
||||
|
||||
# 如需稍后继续特定会话
|
||||
hermes chat --continue
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### MCP 问题
|
||||
|
||||
#### MCP 服务器无法连接
|
||||
|
||||
**原因:** 找不到服务器二进制文件、命令路径错误或缺少运行时。
|
||||
|
||||
**解决方案:**
|
||||
```bash
|
||||
# 确保 MCP 依赖项已安装(标准安装中已包含)
|
||||
cd ~/.hermes/hermes-agent && uv pip install -e ".[mcp]"
|
||||
|
||||
# 对于基于 npm 的服务器,确保 Node.js 可用
|
||||
node --version
|
||||
npx --version
|
||||
|
||||
# 手动测试服务器
|
||||
npx -y @modelcontextprotocol/server-filesystem /tmp
|
||||
```
|
||||
|
||||
验证您的 `~/.hermes/config.yaml` 中的 MCP 配置:
|
||||
```yaml
|
||||
mcp_servers:
|
||||
filesystem:
|
||||
command: "npx"
|
||||
args: ["-y", "@modelcontextprotocol/server-filesystem", "/home/user/docs"]
|
||||
```
|
||||
|
||||
#### MCP 服务器的工具未显示
|
||||
|
||||
**原因:** 服务器已启动但工具发现失败、工具被配置过滤掉,或服务器不支持您期望的 MCP 能力。
|
||||
|
||||
**解决方案:**
|
||||
- 检查网关/智能体日志中的 MCP 连接错误
|
||||
- 确保服务器响应 `tools/list` RPC 方法
|
||||
- 检查该服务器下的 `tools.include`、`tools.exclude`、`tools.resources`、`tools.prompts` 或 `enabled` 设置
|
||||
- 请注意,资源/prompt 工具仅在会话实际支持相应能力时才会注册
|
||||
- 更改配置后使用 `/reload-mcp`
|
||||
|
||||
```bash
|
||||
# 验证 MCP 服务器已配置
|
||||
hermes config show | grep -A 12 mcp_servers
|
||||
|
||||
# 更改配置后重启 Hermes 或重新加载 MCP
|
||||
hermes chat
|
||||
```
|
||||
|
||||
另请参阅:
|
||||
- [MCP(模型上下文协议)](/user-guide/features/mcp)
|
||||
- [在 Hermes 中使用 MCP](/guides/use-mcp-with-hermes)
|
||||
- [MCP 配置参考](/reference/mcp-config-reference)
|
||||
|
||||
#### MCP 超时错误
|
||||
|
||||
**原因:** MCP 服务器响应时间过长,或在执行过程中崩溃。
|
||||
|
||||
**解决方案:**
|
||||
- 如果 MCP 服务器配置支持,增加超时时间
|
||||
- 检查 MCP 服务器进程是否仍在运行
|
||||
- 对于远程 HTTP MCP 服务器,检查网络连接
|
||||
|
||||
:::warning
|
||||
如果 MCP 服务器在请求中途崩溃,Hermes 会报告超时。请检查服务器自身的日志(而非仅 Hermes 日志)以诊断根本原因。
|
||||
:::
|
||||
|
||||
---
|
||||
|
||||
## Profiles(配置文件)
|
||||
|
||||
### Profiles 与直接设置 HERMES_HOME 有何不同?
|
||||
|
||||
Profiles 是构建在 `HERMES_HOME` 之上的托管层。您*可以*在每次命令前手动设置 `HERMES_HOME=/some/path`,但 profiles 会为您处理所有底层工作:创建目录结构、生成 shell 别名(`hermes-work`)、在 `~/.hermes/active_profile` 中跟踪活动 profile,以及自动跨所有 profiles 同步技能更新。它们还与 tab 补全集成,让您无需记忆路径。
|
||||
|
||||
### 两个 profiles 可以共享同一个 bot token 吗?
|
||||
|
||||
不可以。每个消息平台(Telegram、Discord 等)都需要对 bot token 的独占访问权。如果两个 profiles 同时尝试使用同一个 token,第二个网关将无法连接。请为每个 profile 创建单独的 bot — 对于 Telegram,请与 [@BotFather](https://t.me/BotFather) 对话以创建额外的 bot。
|
||||
|
||||
### Profiles 共享记忆或会话吗?
|
||||
|
||||
不共享。每个 profile 都有自己独立的记忆存储、会话数据库和技能目录,完全隔离。如果您想用现有的记忆和会话创建新 profile,请使用 `hermes profile create newname --clone-all` 从当前 profile 复制所有内容。
|
||||
|
||||
### 运行 `hermes update` 时会发生什么?
|
||||
|
||||
`hermes update` 拉取最新代码并重新安装依赖项**一次**(不是每个 profile 各一次)。然后自动将更新的技能同步到所有 profiles。您只需运行一次 `hermes update` — 它覆盖机器上的每个 profile。
|
||||
|
||||
### 可以运行多少个 profiles?
|
||||
|
||||
没有硬性限制。每个 profile 只是 `~/.hermes/profiles/` 下的一个目录。实际限制取决于您的磁盘空间以及系统能处理多少个并发网关(每个网关是一个轻量级 Python 进程)。运行数十个 profiles 完全没问题;每个空闲的 profile 不占用任何资源。
|
||||
|
||||
---
|
||||
|
||||
## 工作流与模式
|
||||
|
||||
### 针对不同任务使用不同模型(多模型工作流)
|
||||
|
||||
**场景:** 您日常使用 GPT-5.4,但 Gemini 或 Grok 写社交媒体内容更好。每次手动切换模型很繁琐。
|
||||
|
||||
**解决方案:委托配置。** Hermes 可以自动将子智能体路由到不同的模型。在 `~/.hermes/config.yaml` 中设置:
|
||||
|
||||
```yaml
|
||||
delegation:
|
||||
model: "google/gemini-3-flash-preview" # 子智能体使用此模型
|
||||
provider: "openrouter" # 子智能体的提供商
|
||||
```
|
||||
|
||||
现在当您告诉 Hermes "帮我写一个关于 X 的 Twitter 帖子"并生成 `delegate_task` 子智能体时,该子智能体将在 Gemini 上运行,而非您的主模型。您的主对话仍在 GPT-5.4 上进行。
|
||||
|
||||
您也可以在 prompt 中明确指定:*"委托一个任务来撰写关于我们产品发布的社交媒体帖子。让你的子智能体负责实际写作。"* 智能体将使用 `delegate_task`,它会自动读取委托配置。
|
||||
|
||||
如需一次性切换模型而不使用委托,请在 CLI 中使用 `/model`:
|
||||
|
||||
```bash
|
||||
/model google/gemini-3-flash-preview # 在本次会话中切换
|
||||
# ... 撰写内容 ...
|
||||
/model openai/gpt-5.4 # 切换回来
|
||||
```
|
||||
|
||||
有关委托工作原理的更多信息,请参阅[子智能体委托](../user-guide/features/delegation.md)。
|
||||
|
||||
### 在一个 WhatsApp 号码上运行多个智能体(按聊天绑定)
|
||||
|
||||
**场景:** 在 OpenClaw 中,您可以将多个独立智能体绑定到特定的 WhatsApp 聊天 — 一个用于家庭购物清单群组,另一个用于您的私聊。Hermes 能做到吗?
|
||||
|
||||
**当前限制:** Hermes 的每个 profile 都需要自己的 WhatsApp 号码/会话。您无法将多个 profiles 绑定到同一个 WhatsApp 号码上的不同聊天 — WhatsApp bridge(Baileys)每个号码使用一个已认证的会话。
|
||||
|
||||
**变通方案:**
|
||||
|
||||
1. **使用单个 profile 配合人格切换。** 创建不同的 `AGENTS.md` 上下文文件或使用 `/personality` 命令按聊天更改行为。智能体能感知当前所在的聊天并进行适应。
|
||||
|
||||
2. **使用 cron 作业处理专项任务。** 对于购物清单跟踪器,设置一个监控特定聊天并管理清单的 cron 作业 — 无需单独的智能体。
|
||||
|
||||
3. **使用独立号码。** 如果您需要真正独立的智能体,将每个 profile 与其自己的 WhatsApp 号码配对。Google Voice 等服务提供的虚拟号码可用于此目的。
|
||||
|
||||
4. **改用 Telegram 或 Discord。** 这些平台更自然地支持按聊天绑定 — 每个 Telegram 群组或 Discord 频道获得自己的会话,您可以在同一账户上运行多个 bot token(每个 profile 一个)。
|
||||
|
||||
详情请参阅 [Profiles](../user-guide/profiles.md) 和 [WhatsApp 设置](../user-guide/messaging/whatsapp.md)。
|
||||
|
||||
### 控制 Telegram 中显示的内容(隐藏日志和推理过程)
|
||||
|
||||
**场景:** 您在 Telegram 中看到了网关执行日志、Hermes 推理过程和工具调用详情,而不是最终输出。
|
||||
|
||||
**解决方案:** `config.yaml` 中的 `display.tool_progress` 设置控制显示多少工具活动:
|
||||
|
||||
```yaml
|
||||
display:
|
||||
tool_progress: "off" # 选项:off、new、all、verbose
|
||||
```
|
||||
|
||||
- **`off`** — 仅显示最终响应。无工具调用、无推理过程、无日志。
|
||||
- **`new`** — 实时显示新的工具调用(简短单行)。
|
||||
- **`all`** — 显示所有工具活动,包括结果。
|
||||
- **`verbose`** — 完整详情,包括工具参数和输出。
|
||||
|
||||
对于消息平台,通常选择 `off` 或 `new`。编辑 `config.yaml` 后,重启网关使更改生效。
|
||||
|
||||
您也可以通过 `/verbose` 命令按会话切换(如果已启用):
|
||||
|
||||
```yaml
|
||||
display:
|
||||
tool_progress_command: true # 在网关中启用 /verbose
|
||||
```
|
||||
|
||||
### 在 Telegram 上管理技能(slash 命令限制)
|
||||
|
||||
**场景:** Telegram 有 100 个 slash 命令的限制,您的技能数量已超过此限制。您想禁用 Telegram 上不需要的技能,但 `hermes skills config` 设置似乎没有生效。
|
||||
|
||||
**解决方案:** 使用 `hermes skills config` 按平台禁用技能。这会写入 `config.yaml`:
|
||||
|
||||
```yaml
|
||||
skills:
|
||||
disabled: [] # 全局禁用的技能
|
||||
platform_disabled:
|
||||
telegram: [skill-a, skill-b] # 仅在 telegram 上禁用
|
||||
```
|
||||
|
||||
更改后,**重启网关**(`hermes gateway restart` 或终止并重新启动)。Telegram bot 命令菜单在启动时重建。
|
||||
|
||||
:::tip
|
||||
描述过长的技能在 Telegram 菜单中会被截断为 40 个字符,以符合 payload 大小限制。如果技能未出现,可能是总 payload 大小问题而非 100 个命令数量限制 — 禁用未使用的技能对两者都有帮助。
|
||||
:::
|
||||
|
||||
### 共享线程会话(多用户,一个对话)
|
||||
|
||||
**场景:** 您有一个 Telegram 或 Discord 线程,多人在其中 @ bot。您希望该线程中的所有 @ 都属于一个共享对话,而非每个用户各自独立的会话。
|
||||
|
||||
**当前行为:** Hermes 在大多数平台上按用户 ID 创建会话,因此每个人都有自己的对话上下文。这是出于隐私和上下文隔离的设计考量。
|
||||
|
||||
**变通方案:**
|
||||
|
||||
1. **使用 Slack。** Slack 会话按线程而非用户进行键控。同一线程中的多个用户共享一个对话 — 正是您描述的行为。这是最自然的选择。
|
||||
|
||||
2. **使用单用户的群聊。** 如果由一个人作为指定"操作员"转达问题,会话保持统一。其他人可以旁观。
|
||||
|
||||
3. **使用 Discord 频道。** Discord 会话按频道键控,因此同一频道中的所有用户共享上下文。为共享对话使用专用频道。
|
||||
|
||||
### 将 Hermes 迁移到另一台机器
|
||||
|
||||
**场景:** 您在一台机器上积累了技能、cron 作业和记忆,想将所有内容迁移到新的专用 Linux 机器。
|
||||
|
||||
**解决方案:**
|
||||
|
||||
1. 在新机器上安装 Hermes Agent:
|
||||
```bash
|
||||
curl -fsSL https://raw.githubusercontent.com/NousResearch/hermes-agent/main/scripts/install.sh | bash
|
||||
```
|
||||
|
||||
2. 在**源机器**上创建完整备份:
|
||||
```bash
|
||||
hermes backup
|
||||
```
|
||||
这会将您整个 `~/.hermes/` 目录(配置、API key、记忆、技能、会话和 profiles)打包为 zip 文件,保存到主目录 `~/hermes-backup-<timestamp>.zip`。
|
||||
|
||||
3. 将 zip 文件复制到新机器并导入:
|
||||
```bash
|
||||
# 在源机器上
|
||||
scp ~/hermes-backup-<timestamp>.zip newmachine:~/
|
||||
|
||||
# 在新机器上
|
||||
hermes import ~/hermes-backup-<timestamp>.zip
|
||||
```
|
||||
|
||||
4. 在新机器上运行 `hermes setup` 以验证 API key 和提供商配置是否正常工作。
|
||||
|
||||
### 将单个 profile 迁移到另一台机器
|
||||
|
||||
**场景:** 您想迁移或共享某个特定 profile,而非整个安装。
|
||||
|
||||
```bash
|
||||
# 在源机器上
|
||||
hermes profile export work ./work-backup.tar.gz
|
||||
|
||||
# 将文件复制到目标机器,然后:
|
||||
hermes profile import ./work-backup.tar.gz work
|
||||
```
|
||||
|
||||
导入的 profile 将包含导出时的所有配置、记忆、会话和技能。如果新机器的设置不同,您可能需要更新路径或重新向提供商进行身份验证。
|
||||
|
||||
### `hermes backup` 与 `hermes profile export` 的对比
|
||||
|
||||
| 功能 | `hermes backup` | `hermes profile export` |
|
||||
| :--- | :--- | :--- |
|
||||
| **使用场景** | **整机迁移** | **移植/共享特定 profile** |
|
||||
| **范围** | 全局(整个 `~/.hermes` 目录) | 局部(单个 profile 目录) |
|
||||
| **包含内容** | 所有 profiles、全局配置、API key、会话 | 单个 profile:SOUL.md、记忆、会话、技能 |
|
||||
| **凭据** | **包含**(`.env` 和 `auth.json`) | **排除**(为安全共享而剥离) |
|
||||
| **格式** | `.zip` | `.tar.gz` |
|
||||
|
||||
**手动备选方案(rsync):** 如果您倾向于直接复制文件,请排除代码仓库:
|
||||
```bash
|
||||
rsync -av --exclude='hermes-agent' ~/.hermes/ newmachine:~/.hermes/
|
||||
```
|
||||
|
||||
:::tip
|
||||
`hermes backup` 即使在 Hermes 正在运行时也能生成一致的快照。还原的归档文件不包含机器本地的运行时文件,如 `gateway.pid` 和 `cron.pid`。
|
||||
:::
|
||||
|
||||
### 安装后重新加载 shell 时出现权限拒绝
|
||||
|
||||
**场景:** 运行 Hermes 安装程序后,`source ~/.zshrc` 提示权限拒绝错误。
|
||||
|
||||
**原因:** 这通常发生在 `~/.zshrc`(或 `~/.bashrc`)文件权限不正确,或安装程序无法干净写入时。这不是 Hermes 特有的问题 — 而是 shell 配置权限问题。
|
||||
|
||||
**解决方案:**
|
||||
```bash
|
||||
# 检查权限
|
||||
ls -la ~/.zshrc
|
||||
|
||||
# 如需修复(应为 -rw-r--r-- 或 644)
|
||||
chmod 644 ~/.zshrc
|
||||
|
||||
# 然后重新加载
|
||||
source ~/.zshrc
|
||||
|
||||
# 或直接打开新终端窗口 — 它会自动读取 PATH 更改
|
||||
```
|
||||
|
||||
如果安装程序已添加 PATH 行但权限有误,您可以手动添加:
|
||||
```bash
|
||||
echo 'export PATH="$HOME/.local/bin:$PATH"' >> ~/.zshrc
|
||||
```
|
||||
|
||||
### 首次运行智能体时出现 400 错误
|
||||
|
||||
**场景:** 设置顺利完成,但第一次聊天尝试失败,提示 HTTP 400。
|
||||
|
||||
**原因:** 通常是模型名称不匹配 — 配置的模型在您的提供商上不存在,或 API key 没有访问该模型的权限。
|
||||
|
||||
**解决方案:**
|
||||
```bash
|
||||
# 检查已配置的模型和提供商
|
||||
hermes config show | head -20
|
||||
|
||||
# 重新运行模型选择
|
||||
hermes model
|
||||
|
||||
# 或使用已知可用的模型测试
|
||||
hermes chat -q "hello" --model anthropic/claude-opus-4.7
|
||||
```
|
||||
|
||||
如果使用 OpenRouter,请确保您的 API key 有余额。OpenRouter 返回 400 通常意味着该模型需要付费套餐,或模型 ID 有拼写错误。
|
||||
|
||||
---
|
||||
|
||||
## 仍然遇到问题?
|
||||
|
||||
如果您的问题未在此处涵盖:
|
||||
|
||||
1. **搜索现有 issue:** [GitHub Issues](https://github.com/NousResearch/hermes-agent/issues)
|
||||
2. **向社区提问:** [Nous Research Discord](https://discord.gg/nousresearch)
|
||||
3. **提交 bug 报告:** 请包含您的操作系统、Python 版本(`python3 --version`)、Hermes 版本(`hermes --version`)以及完整的错误信息
|
||||
@ -0,0 +1,249 @@
|
||||
---
|
||||
sidebar_position: 8
|
||||
title: "MCP 配置参考"
|
||||
description: "Hermes Agent MCP 配置键、过滤语义及工具策略参考"
|
||||
---
|
||||
|
||||
# MCP 配置参考
|
||||
|
||||
本页是主 MCP 文档的简明参考手册。
|
||||
|
||||
概念说明请参阅:
|
||||
- [MCP(Model Context Protocol)](/user-guide/features/mcp)
|
||||
- [在 Hermes 中使用 MCP](/guides/use-mcp-with-hermes)
|
||||
|
||||
## 根配置结构
|
||||
|
||||
```yaml
|
||||
mcp_servers:
|
||||
<server_name>:
|
||||
command: "..." # stdio servers
|
||||
args: []
|
||||
env: {}
|
||||
|
||||
# OR
|
||||
url: "..." # HTTP servers
|
||||
headers: {}
|
||||
|
||||
enabled: true
|
||||
timeout: 120
|
||||
connect_timeout: 60
|
||||
supports_parallel_tool_calls: false
|
||||
tools:
|
||||
include: []
|
||||
exclude: []
|
||||
resources: true
|
||||
prompts: true
|
||||
```
|
||||
|
||||
## 服务器键
|
||||
|
||||
| 键 | 类型 | 适用范围 | 含义 |
|
||||
|---|---|---|---|
|
||||
| `command` | string | stdio | 要启动的可执行文件 |
|
||||
| `args` | list | stdio | 子进程的参数 |
|
||||
| `env` | mapping | stdio | 传递给子进程的环境变量 |
|
||||
| `url` | string | HTTP | 远程 MCP 端点 |
|
||||
| `headers` | mapping | HTTP | 远程服务器请求的请求头 |
|
||||
| `enabled` | bool | 两者 | 为 false 时完全跳过该服务器 |
|
||||
| `timeout` | number | 两者 | 工具调用超时时间 |
|
||||
| `connect_timeout` | number | 两者 | 初始连接超时时间 |
|
||||
| `supports_parallel_tool_calls` | bool | 两者 | 允许该服务器的工具并发执行 |
|
||||
| `tools` | mapping | 两者 | 过滤及工具策略 |
|
||||
| `auth` | string | HTTP | 认证方式。设为 `oauth` 可启用带 PKCE 的 OAuth 2.1 |
|
||||
| `sampling` | mapping | 两者 | 服务器发起的 LLM 请求策略(参见 MCP 指南) |
|
||||
|
||||
## `tools` 策略键
|
||||
|
||||
| 键 | 类型 | 含义 |
|
||||
|---|---|---|
|
||||
| `include` | string 或 list | 白名单:指定允许注册的服务器原生 MCP 工具 |
|
||||
| `exclude` | string 或 list | 黑名单:指定禁止注册的服务器原生 MCP 工具 |
|
||||
| `resources` | bool-like | 启用/禁用 `list_resources` + `read_resource` |
|
||||
| `prompts` | bool-like | 启用/禁用 `list_prompts` + `get_prompt` |
|
||||
|
||||
## 过滤语义
|
||||
|
||||
### `include`
|
||||
|
||||
若设置了 `include`,则只注册其中列出的服务器原生 MCP 工具。
|
||||
|
||||
```yaml
|
||||
tools:
|
||||
include: [create_issue, list_issues]
|
||||
```
|
||||
|
||||
### `exclude`
|
||||
|
||||
若设置了 `exclude` 且未设置 `include`,则注册除列出名称之外的所有服务器原生 MCP 工具。
|
||||
|
||||
```yaml
|
||||
tools:
|
||||
exclude: [delete_customer]
|
||||
```
|
||||
|
||||
### 优先级
|
||||
|
||||
若两者同时设置,`include` 优先。
|
||||
|
||||
```yaml
|
||||
tools:
|
||||
include: [create_issue]
|
||||
exclude: [create_issue, delete_issue]
|
||||
```
|
||||
|
||||
结果:
|
||||
- `create_issue` 仍被允许
|
||||
- `delete_issue` 被忽略,因为 `include` 优先级更高
|
||||
|
||||
## 工具策略
|
||||
|
||||
Hermes 可为每个 MCP 服务器注册以下工具包装器:
|
||||
|
||||
Resources(资源):
|
||||
- `list_resources`
|
||||
- `read_resource`
|
||||
|
||||
Prompts(提示词):
|
||||
- `list_prompts`
|
||||
- `get_prompt`
|
||||
|
||||
### 禁用 resources
|
||||
|
||||
```yaml
|
||||
tools:
|
||||
resources: false
|
||||
```
|
||||
|
||||
### 禁用 prompts
|
||||
|
||||
```yaml
|
||||
tools:
|
||||
prompts: false
|
||||
```
|
||||
|
||||
### 能力感知注册
|
||||
|
||||
即使设置了 `resources: true` 或 `prompts: true`,Hermes 也只在 MCP 会话实际暴露对应能力时才注册相应工具。
|
||||
|
||||
因此以下情况属于正常现象:
|
||||
- 你启用了 prompts
|
||||
- 但没有出现任何 prompt 工具
|
||||
- 原因是该服务器不支持 prompts
|
||||
|
||||
## `enabled: false`
|
||||
|
||||
```yaml
|
||||
mcp_servers:
|
||||
legacy:
|
||||
url: "https://mcp.legacy.internal"
|
||||
enabled: false
|
||||
```
|
||||
|
||||
行为:
|
||||
- 不发起连接
|
||||
- 不进行服务发现
|
||||
- 不注册工具
|
||||
- 配置保留,供后续复用
|
||||
|
||||
## 空结果行为
|
||||
|
||||
若过滤后服务器原生工具全部被移除,且没有工具被注册,Hermes 不会为该服务器创建空的 MCP 运行时工具集。
|
||||
|
||||
## 配置示例
|
||||
|
||||
### GitHub 安全白名单
|
||||
|
||||
```yaml
|
||||
mcp_servers:
|
||||
github:
|
||||
command: "npx"
|
||||
args: ["-y", "@modelcontextprotocol/server-github"]
|
||||
env:
|
||||
GITHUB_PERSONAL_ACCESS_TOKEN: "***"
|
||||
tools:
|
||||
include: [list_issues, create_issue, update_issue, search_code]
|
||||
resources: false
|
||||
prompts: false
|
||||
```
|
||||
|
||||
### Stripe 黑名单
|
||||
|
||||
```yaml
|
||||
mcp_servers:
|
||||
stripe:
|
||||
url: "https://mcp.stripe.com"
|
||||
headers:
|
||||
Authorization: "Bearer ***"
|
||||
tools:
|
||||
exclude: [delete_customer, refund_payment]
|
||||
```
|
||||
|
||||
### 仅资源的文档服务器
|
||||
|
||||
```yaml
|
||||
mcp_servers:
|
||||
docs:
|
||||
url: "https://mcp.docs.example.com"
|
||||
tools:
|
||||
include: []
|
||||
resources: true
|
||||
prompts: false
|
||||
```
|
||||
|
||||
## 重新加载配置
|
||||
|
||||
修改 MCP 配置后,使用以下命令重新加载服务器:
|
||||
|
||||
```text
|
||||
/reload-mcp
|
||||
```
|
||||
|
||||
## 工具命名
|
||||
|
||||
服务器原生 MCP 工具的命名格式为:
|
||||
|
||||
```text
|
||||
mcp_<server>_<tool>
|
||||
```
|
||||
|
||||
示例:
|
||||
- `mcp_github_create_issue`
|
||||
- `mcp_filesystem_read_file`
|
||||
- `mcp_my_api_query_data`
|
||||
|
||||
工具包装器遵循相同的前缀规则:
|
||||
- `mcp_<server>_list_resources`
|
||||
- `mcp_<server>_read_resource`
|
||||
- `mcp_<server>_list_prompts`
|
||||
- `mcp_<server>_get_prompt`
|
||||
|
||||
### 名称规范化
|
||||
|
||||
服务器名称和工具名称中的连字符(`-`)和点号(`.`)在注册前均会替换为下划线。这确保工具名称是 LLM function-calling API 的合法标识符。
|
||||
|
||||
例如,名为 `my-api` 的服务器暴露了名为 `list-items.v2` 的工具,注册后变为:
|
||||
|
||||
```text
|
||||
mcp_my_api_list_items_v2
|
||||
```
|
||||
|
||||
编写 `include` / `exclude` 过滤器时请注意——使用**原始** MCP 工具名称(含连字符/点号),而非规范化后的名称。
|
||||
|
||||
## OAuth 2.1 认证
|
||||
|
||||
对于需要 OAuth 的 HTTP 服务器,在服务器条目中设置 `auth: oauth`:
|
||||
|
||||
```yaml
|
||||
mcp_servers:
|
||||
protected_api:
|
||||
url: "https://mcp.example.com/mcp"
|
||||
auth: oauth
|
||||
```
|
||||
|
||||
行为:
|
||||
- Hermes 使用 MCP SDK 的 OAuth 2.1 PKCE 流程(元数据发现、动态客户端注册、token 交换及刷新)
|
||||
- 首次连接时,浏览器窗口将打开以完成授权
|
||||
- Token 持久化至 `~/.hermes/mcp-tokens/<server>.json`,跨会话复用
|
||||
- Token 刷新自动进行;仅在刷新失败时才需重新授权
|
||||
- 仅适用于 HTTP/StreamableHTTP 传输(基于 `url` 的服务器)
|
||||
@ -0,0 +1,103 @@
|
||||
---
|
||||
sidebar_position: 11
|
||||
title: 模型目录
|
||||
description: 远程托管的清单文件,驱动 OpenRouter 和 Nous Portal 的精选模型选择器列表。
|
||||
---
|
||||
|
||||
# 模型目录
|
||||
|
||||
Hermes 从托管于文档站点旁的 JSON 清单中获取 **OpenRouter** 和 **Nous Portal** 的精选模型列表。这样维护者无需发布新的 `hermes-agent` 版本即可更新选择器列表。
|
||||
|
||||
当清单不可达时(离线、网络受阻、托管故障),Hermes 会静默回退到随 CLI 一同发布的仓库内置快照。清单永远不会导致选择器崩溃——最坏情况下,你看到的是与已安装版本捆绑的列表。
|
||||
|
||||
## 线上清单 URL
|
||||
|
||||
```
|
||||
https://hermes-agent.nousresearch.com/docs/api/model-catalog.json
|
||||
```
|
||||
|
||||
每次合并到 `main` 时,通过现有的 `deploy-site.yml` GitHub Pages 流水线发布。真实来源位于仓库的 `website/static/api/model-catalog.json`。
|
||||
|
||||
## Schema(模式)
|
||||
|
||||
```json
|
||||
{
|
||||
"version": 1,
|
||||
"updated_at": "2026-04-25T22:00:00Z",
|
||||
"metadata": {},
|
||||
"providers": {
|
||||
"openrouter": {
|
||||
"metadata": {},
|
||||
"models": [
|
||||
{"id": "moonshotai/kimi-k2.6", "description": "recommended", "metadata": {}},
|
||||
{"id": "openai/gpt-5.4", "description": ""}
|
||||
]
|
||||
},
|
||||
"nous": {
|
||||
"metadata": {},
|
||||
"models": [
|
||||
{"id": "anthropic/claude-opus-4.7"},
|
||||
{"id": "moonshotai/kimi-k2.6"}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
字段说明:
|
||||
|
||||
- **`version`** — 整数类型的 schema 版本号。未来的 schema 会递增此值;Hermes 拒绝处理版本号未知的清单,并回退到硬编码快照。
|
||||
- **`metadata`** — 清单、provider 及模型级别的自由格式字典,支持任意键。Hermes 会忽略未知字段,因此你可以为条目添加注解(如 `"tier": "paid"`、`"tags": [...]` 等),无需协调 schema 变更。
|
||||
- **`description`** — 仅限 OpenRouter。驱动选择器徽章文本(`"recommended"`、`"free"` 或空字符串)。Nous Portal 不使用此字段——免费层级的限制由 Portal 的定价端点实时决定。
|
||||
- **定价和上下文长度**不在清单中。这些数据在获取时来自各 provider 的实时 API(`/v1/models` 端点、models.dev)。
|
||||
|
||||
## 获取行为
|
||||
|
||||
| 时机 | 行为 |
|
||||
|---|---|
|
||||
| `/model` 或 `hermes model` | 若磁盘缓存已过期则重新获取,否则使用缓存 |
|
||||
| 磁盘缓存新鲜(< TTL) | 不发起网络请求 |
|
||||
| 网络故障且有缓存 | 静默回退到缓存,输出一行日志 |
|
||||
| 网络故障且无缓存 | 静默回退到仓库内置快照 |
|
||||
| 清单未通过 schema 校验 | 视为不可达 |
|
||||
|
||||
缓存位置:`~/.hermes/cache/model_catalog.json`。
|
||||
|
||||
## 配置
|
||||
|
||||
```yaml
|
||||
model_catalog:
|
||||
enabled: true
|
||||
url: https://hermes-agent.nousresearch.com/docs/api/model-catalog.json
|
||||
ttl_hours: 24
|
||||
providers: {}
|
||||
```
|
||||
|
||||
将 `enabled` 设为 `false` 可完全禁用远程获取,始终使用仓库内置快照。
|
||||
|
||||
### 按 provider 覆盖 URL
|
||||
|
||||
第三方可使用相同 schema 自托管自己的精选列表。将某个 provider 指向自定义 URL:
|
||||
|
||||
```yaml
|
||||
model_catalog:
|
||||
providers:
|
||||
openrouter:
|
||||
url: https://example.com/my-openrouter-curation.json
|
||||
```
|
||||
|
||||
覆盖清单只需填充其关心的 provider 块,其他 provider 继续从主 URL 解析。
|
||||
|
||||
## 更新清单
|
||||
|
||||
维护者操作:
|
||||
|
||||
```bash
|
||||
# 从仓库内硬编码列表重新生成(在编辑 hermes_cli/models.py 中的
|
||||
# OPENROUTER_MODELS 或 _PROVIDER_MODELS["nous"] 后保持清单同步)。
|
||||
python scripts/build_model_catalog.py
|
||||
```
|
||||
|
||||
然后将 `website/static/api/model-catalog.json` 的变更提交 PR 到 `main`。文档站点在合并后自动部署,新清单将在几分钟内生效。
|
||||
|
||||
你也可以直接手动编辑 JSON,用于不适合放入仓库内置快照的细粒度元数据变更——生成脚本只是便捷工具,并非唯一的真实来源。
|
||||
@ -0,0 +1,205 @@
|
||||
---
|
||||
sidebar_position: 9
|
||||
title: "可选技能目录"
|
||||
description: "hermes-agent 附带的官方可选技能 — 通过 hermes skills install official/<category>/<skill> 安装"
|
||||
---
|
||||
|
||||
# 可选技能目录
|
||||
|
||||
可选技能随 hermes-agent 一起发布,位于 `optional-skills/` 目录下,但**默认未激活**。请显式安装:
|
||||
|
||||
```bash
|
||||
hermes skills install official/<category>/<skill>
|
||||
```
|
||||
|
||||
示例:
|
||||
|
||||
```bash
|
||||
hermes skills install official/blockchain/solana
|
||||
hermes skills install official/mlops/flash-attention
|
||||
```
|
||||
|
||||
下方每个技能均链接至专属页面,包含完整定义、配置和使用说明。
|
||||
|
||||
卸载方式:
|
||||
|
||||
```bash
|
||||
hermes skills uninstall <skill-name>
|
||||
```
|
||||
|
||||
## autonomous-ai-agents
|
||||
|
||||
| 技能 | 描述 |
|
||||
|-------|-------------|
|
||||
| [**blackbox**](/user-guide/skills/optional/autonomous-ai-agents/autonomous-ai-agents-blackbox) | 将编码任务委托给 Blackbox AI CLI agent。内置评判机制的多模型 agent,通过多个 LLM 运行任务并选出最佳结果。需要 blackbox CLI 和 Blackbox AI API 密钥。 |
|
||||
| [**honcho**](/user-guide/skills/optional/autonomous-ai-agents/autonomous-ai-agents-honcho) | 配置并使用 Honcho 记忆与 Hermes — 跨会话用户建模、多配置文件对等隔离、观测配置、辩证推理、会话摘要及上下文预算执行。适用于配置 Honcho、故障排查等场景。 |
|
||||
|
||||
## blockchain
|
||||
|
||||
| 技能 | 描述 |
|
||||
|-------|-------------|
|
||||
| [**evm**](/user-guide/skills/optional/blockchain/blockchain-evm) | 只读 EVM 客户端:支持 8 条链的钱包、代币、Gas 查询。 |
|
||||
| [**hyperliquid**](/user-guide/skills/optional/blockchain/blockchain-hyperliquid) | Hyperliquid 市场数据、账户历史、交易回顾。 |
|
||||
| [**solana**](/user-guide/skills/optional/blockchain/blockchain-solana) | 查询 Solana 链上数据并附带 USD 定价 — 钱包余额、带估值的代币组合、交易详情、NFT、巨鲸检测及实时网络统计。使用 Solana RPC + CoinGecko,无需 API 密钥。 |
|
||||
|
||||
## communication
|
||||
|
||||
| 技能 | 描述 |
|
||||
|-------|-------------|
|
||||
| [**one-three-one-rule**](/user-guide/skills/optional/communication/communication-one-three-one-rule) | 用于技术提案和权衡分析的结构化决策框架。当用户面临多种方案选择(架构决策、工具选型、重构策略、迁移路径)时,本技能提供系统化的分析流程。 |
|
||||
|
||||
## creative
|
||||
|
||||
| 技能 | 描述 |
|
||||
|-------|-------------|
|
||||
| [**blender-mcp**](/user-guide/skills/optional/creative/creative-blender-mcp) | 通过 socket 连接 blender-mcp 插件,直接从 Hermes 控制 Blender。创建 3D 对象、材质、动画,并运行任意 Blender Python(bpy)代码。适用于用户希望在 Blender 中创建或修改任何内容的场景。 |
|
||||
| [**concept-diagrams**](/user-guide/skills/optional/creative/creative-concept-diagrams) | 生成扁平、极简、支持亮色/暗色模式的 SVG 图表,输出为独立 HTML 文件,采用统一的教育视觉语言,包含 9 种语义色阶、句首大写排版及自动暗色模式。最适合教育和说明类内容。 |
|
||||
| [**hyperframes**](/user-guide/skills/optional/creative/creative-hyperframes) | 使用 HyperFrames 创建基于 HTML 的视频合成、动态标题卡、社交叠层、字幕访谈视频、音频响应视觉效果及着色器转场。HTML 是视频的唯一来源。适用于用户希望制作任何视频内容的场景。 |
|
||||
| [**kanban-video-orchestrator**](/user-guide/skills/optional/creative/creative-kanban-video-orchestrator) | 规划、搭建并监控由 Hermes Kanban 支撑的多 agent 视频制作流水线。适用于用户希望制作任何类型视频的场景 — 叙事影片、产品/营销视频、MV、解说视频、ASCII/终端艺术、抽象/生成式循环等。 |
|
||||
| [**meme-generation**](/user-guide/skills/optional/creative/creative-meme-generation) | 通过选取模板并使用 Pillow 叠加文字来生成真实的 meme 图片,输出实际的 .png 文件。 |
|
||||
|
||||
## devops
|
||||
|
||||
| 技能 | 描述 |
|
||||
|-------|-------------|
|
||||
| [**inference-sh-cli**](/user-guide/skills/optional/devops/devops-cli) | 通过 inference.sh CLI(infsh)运行 150+ AI 应用 — 图像生成、视频创作、LLM、搜索、3D、社交自动化。使用终端工具。触发词:inference.sh、infsh、ai apps、flux、veo、图像生成、视频生成、seedrea 等。 |
|
||||
| [**docker-management**](/user-guide/skills/optional/devops/devops-docker-management) | 管理 Docker 容器、镜像、卷、网络及 Compose 栈 — 生命周期操作、调试、清理及 Dockerfile 优化。 |
|
||||
| [**pinggy-tunnel**](/user-guide/skills/optional/devops/devops-pinggy-tunnel) | 通过 Pinggy 经 SSH 实现零安装本地隧道。 |
|
||||
| [**watchers**](/user-guide/skills/optional/devops/devops-watchers) | 轮询 RSS、JSON API 和 GitHub,并使用水印去重。 |
|
||||
|
||||
## dogfood
|
||||
|
||||
| 技能 | 描述 |
|
||||
|-------|-------------|
|
||||
| [**adversarial-ux-test**](/user-guide/skills/optional/dogfood/dogfood-adversarial-ux-test) | 扮演产品中最难应对的技术抵触型用户。以该角色浏览应用,找出所有 UX 痛点,再通过实用主义过滤层区分真实问题与噪音,生成可执行的工单。 |
|
||||
|
||||
## email
|
||||
|
||||
| 技能 | 描述 |
|
||||
|-------|-------------|
|
||||
| [**agentmail**](/user-guide/skills/optional/email/email-agentmail) | 通过 AgentMail 为 agent 提供专属邮箱。使用 agent 专属邮件地址(如 hermes-agent@agentmail.to)自主发送、接收和管理邮件。 |
|
||||
|
||||
## finance
|
||||
|
||||
| 技能 | 描述 |
|
||||
|-------|-------------|
|
||||
| [**3-statement-model**](/user-guide/skills/optional/finance/finance-3-statement-model) | 在 Excel 中构建完整集成的三表模型(利润表、资产负债表、现金流量表),包含营运资本计划、折旧摊销滚动、债务计划及使现金与留存收益平衡的勾稽项。与 excel-author 配合使用。 |
|
||||
| [**comps-analysis**](/user-guide/skills/optional/finance/finance-comps-analysis) | 在 Excel 中构建可比公司分析 — 运营指标、估值倍数、与同行集合的统计基准对比。与 excel-author 配合使用。适用于上市公司估值、IPO 定价、行业基准或异常值检测。 |
|
||||
| [**dcf-model**](/user-guide/skills/optional/finance/finance-dcf-model) | 在 Excel 中构建机构级 DCF 估值模型 — 收入预测、自由现金流构建、WACC、终值、悲观/基准/乐观情景及 5×5 敏感性分析表。与 excel-author 配合使用。适用于内在价值股权分析。 |
|
||||
| [**excel-author**](/user-guide/skills/optional/finance/finance-excel-author) | 使用 openpyxl 无头构建可审计的 Excel 工作簿 — 蓝/黑/绿单元格规范、公式优先于硬编码、命名区域、余额校验、敏感性分析表。适用于财务模型、审计输出、对账。 |
|
||||
| [**lbo-model**](/user-guide/skills/optional/finance/finance-lbo-model) | 在 Excel 中构建杠杆收购模型 — 资金来源与用途、债务计划、现金清偿、退出倍数、IRR/MOIC 敏感性分析。与 excel-author 配合使用。适用于 PE 筛选、主导方案估值或 pitch 中的示意性 LBO。 |
|
||||
| [**merger-model**](/user-guide/skills/optional/finance/finance-merger-model) | 在 Excel 中构建增厚/摊薄(并购)模型 — 合并后利润表、协同效应、融资结构、每股收益影响。与 excel-author 配合使用。适用于并购 pitch、董事会材料或交易评估。 |
|
||||
| [**pptx-author**](/user-guide/skills/optional/finance/finance-pptx-author) | 使用 python-pptx 无头构建 PowerPoint 演示文稿。与 excel-author 配合,制作每个数字均可追溯至工作簿单元格的模型支撑型幻灯片。适用于 pitch deck、投委会备忘录、盈利说明。 |
|
||||
| [**stocks**](/user-guide/skills/optional/finance/finance-stocks) | 通过 Yahoo 获取股票报价、历史数据、搜索、对比及加密货币行情。 |
|
||||
|
||||
## health
|
||||
|
||||
| 技能 | 描述 |
|
||||
|-------|-------------|
|
||||
| [**fitness-nutrition**](/user-guide/skills/optional/health/health-fitness-nutrition) | 健身训练计划与营养追踪。通过 wger 按肌肉群、器械或类别搜索 690+ 种训练动作。通过 USDA FoodData Central 查询 380,000+ 种食物的宏量营养素和热量。计算 BMI、TDEE、单次最大重量、宏量营养素分配及体成分。 |
|
||||
| [**neuroskill-bci**](/user-guide/skills/optional/health/health-neuroskill-bci) | 连接运行中的 NeuroSkill 实例,将用户的实时认知和情绪状态(专注度、放松度、情绪、认知负荷、困倦度、心率、HRV、睡眠分期及 40+ 项衍生 EXG 评分)融入响应中。 |
|
||||
|
||||
## mcp
|
||||
|
||||
| 技能 | 描述 |
|
||||
|-------|-------------|
|
||||
| [**fastmcp**](/user-guide/skills/optional/mcp/mcp-fastmcp) | 使用 Python 中的 FastMCP 构建、测试、检查、安装和部署 MCP 服务器。适用于创建新 MCP 服务器、将 API 或数据库封装为 MCP 工具、暴露资源或 prompt(提示词),或为 Claude Code、Cursor 等准备 FastMCP 服务器的场景。 |
|
||||
| [**mcporter**](/user-guide/skills/optional/mcp/mcp-mcporter) | 使用 mcporter CLI 列出、配置、鉴权并直接调用 MCP 服务器/工具(HTTP 或 stdio),包括临时服务器、配置编辑及 CLI/类型生成。 |
|
||||
|
||||
## migration
|
||||
|
||||
| 技能 | 描述 |
|
||||
|-------|-------------|
|
||||
| [**openclaw-migration**](/user-guide/skills/optional/migration/migration-openclaw-migration) | 将用户的 OpenClaw 自定义配置迁移至 Hermes Agent。从 ~/.openclaw 导入兼容 Hermes 的记忆、SOUL.md、命令白名单、用户技能及选定的工作区资产,并报告无法迁移的内容。 |
|
||||
|
||||
## mlops
|
||||
|
||||
| 技能 | 描述 |
|
||||
|-------|-------------|
|
||||
| [**huggingface-accelerate**](/user-guide/skills/optional/mlops/mlops-accelerate) | 最简单的分布式训练 API。仅需 4 行代码即可为任意 PyTorch 脚本添加分布式支持。统一支持 DeepSpeed/FSDP/Megatron/DDP 的 API。自动设备放置,混合精度(FP16/BF16/FP8)。交互式配置,单一启动命令。 |
|
||||
| [**axolotl**](/user-guide/skills/optional/mlops/mlops-training-axolotl) | Axolotl:基于 YAML 配置的 LLM 微调(LoRA、DPO、GRPO)。 |
|
||||
| [**chroma**](/user-guide/skills/optional/mlops/mlops-chroma) | 面向 AI 应用的开源 embedding(向量嵌入)数据库。存储 embedding 和元数据,执行向量及全文搜索,按元数据过滤。简洁的 4 函数 API,从 notebook 扩展至生产集群。适用于语义搜索、RAG 等场景。 |
|
||||
| [**clip**](/user-guide/skills/optional/mlops/mlops-clip) | OpenAI 连接视觉与语言的模型。支持零样本图像分类、图文匹配及跨模态检索。在 4 亿图文对上训练。适用于图像搜索、内容审核或视觉语言任务。 |
|
||||
| [**faiss**](/user-guide/skills/optional/mlops/mlops-faiss) | Facebook 用于高效相似性搜索和稠密向量聚类的库。支持数十亿向量、GPU 加速及多种索引类型(Flat、IVF、HNSW)。适用于快速 k-NN 搜索、大规模向量检索等场景。 |
|
||||
| [**optimizing-attention-flash**](/user-guide/skills/optional/mlops/mlops-flash-attention) | 使用 Flash Attention 优化 transformer 注意力机制,实现 2-4 倍加速和 10-20 倍显存降低。适用于训练/运行长序列(>512 token)transformer、遇到注意力 GPU 显存问题或需要更快推理的场景。 |
|
||||
| [**guidance**](/user-guide/skills/optional/mlops/mlops-guidance) | 使用 Guidance(微软研究院的约束生成框架)通过正则表达式和语法控制 LLM 输出,保证生成有效的 JSON/XML/代码,强制结构化格式,并构建多步骤工作流。 |
|
||||
| [**huggingface-tokenizers**](/user-guide/skills/optional/mlops/mlops-huggingface-tokenizers) | 为研究和生产优化的快速 tokenizer(分词器)。基于 Rust 实现,可在 20 秒内对 1GB 文本完成分词。支持 BPE、WordPiece 和 Unigram 算法。训练自定义词表、追踪对齐、处理填充/截断,与 HuggingFace 生态集成。 |
|
||||
| [**instructor**](/user-guide/skills/optional/mlops/mlops-instructor) | 使用 Instructor(久经考验的结构化输出库)从 LLM 响应中提取带 Pydantic 验证的结构化数据,自动重试失败的提取,以类型安全方式解析复杂 JSON,并流式传输部分结果。 |
|
||||
| [**lambda-labs-gpu-cloud**](/user-guide/skills/optional/mlops/mlops-lambda-labs) | 用于 ML 训练和推理的按需及预留 GPU 云实例。适用于需要通过简单 SSH 访问专用 GPU 实例、持久化文件系统或用于大规模训练的高性能多节点集群的场景。 |
|
||||
| [**llava**](/user-guide/skills/optional/mlops/mlops-llava) | 大型语言与视觉助手。支持视觉指令微调和基于图像的对话。结合 CLIP 视觉编码器与 Vicuna/LLaMA 语言模型。支持多轮图像对话、视觉问答及指令跟随。 |
|
||||
| [**modal-serverless-gpu**](/user-guide/skills/optional/mlops/mlops-modal) | 用于运行 ML 工作负载的 serverless GPU 云平台。适用于无需基础设施管理的按需 GPU 访问、将 ML 模型部署为 API 或运行自动扩缩容批处理任务的场景。 |
|
||||
| [**nemo-curator**](/user-guide/skills/optional/mlops/mlops-nemo-curator) | 面向 LLM 训练的 GPU 加速数据整理工具。支持文本/图像/视频/音频。具备模糊去重(快 16 倍)、质量过滤(30+ 启发式规则)、语义去重、PII 脱敏、NSFW 检测等功能,可跨 GPU 扩展。 |
|
||||
| [**outlines**](/user-guide/skills/optional/mlops/mlops-inference-outlines) | Outlines:结构化 JSON/正则表达式/Pydantic LLM 生成。 |
|
||||
| [**peft-fine-tuning**](/user-guide/skills/optional/mlops/mlops-peft) | 使用 LoRA、QLoRA 及 25+ 种方法对 LLM 进行参数高效微调(PEFT)。适用于在有限 GPU 显存下微调大型模型(7B-70B)、仅训练不到 1% 参数且精度损失极小,或进行多适配器服务的场景。 |
|
||||
| [**pinecone**](/user-guide/skills/optional/mlops/mlops-pinecone) | 面向生产 AI 应用的托管向量数据库。全托管、自动扩缩容,支持混合搜索(稠密+稀疏)、元数据过滤和命名空间。低延迟(p95 <100ms)。适用于生产 RAG、推荐系统等场景。 |
|
||||
| [**pytorch-fsdp**](/user-guide/skills/optional/mlops/mlops-pytorch-fsdp) | PyTorch FSDP 全分片数据并行训练专家指导 — 参数分片、混合精度、CPU 卸载、FSDP2。 |
|
||||
| [**pytorch-lightning**](/user-guide/skills/optional/mlops/mlops-pytorch-lightning) | 高层 PyTorch 框架,提供 Trainer 类、自动分布式训练(DDP/FSDP/DeepSpeed)、回调系统及极少样板代码。同一套代码可从笔记本扩展至超算。适用于希望训练循环简洁、同时保留完整 PyTorch 灵活性的场景。 |
|
||||
| [**qdrant-vector-search**](/user-guide/skills/optional/mlops/mlops-qdrant) | 高性能向量相似性搜索引擎,适用于 RAG 和语义搜索。适用于构建需要快速近邻搜索、带过滤的混合搜索或基于 Rust 高性能的可扩展向量存储的生产 RAG 系统。 |
|
||||
| [**sparse-autoencoder-training**](/user-guide/skills/optional/mlops/mlops-saelens) | 提供使用 SAELens 训练和分析稀疏自编码器(SAE)的指导,将神经网络激活分解为可解释特征。适用于发现可解释特征、分析叠加现象或研究神经网络内部结构的场景。 |
|
||||
| [**simpo-training**](/user-guide/skills/optional/mlops/mlops-simpo) | 用于 LLM 对齐的简单偏好优化(SimPO)。无需参考模型的 DPO 替代方案,性能更优(在 AlpacaEval 2.0 上提升 +6.4 分)。比 DPO 更高效。适用于希望简化偏好对齐流程的场景。 |
|
||||
| [**slime-rl-training**](/user-guide/skills/optional/mlops/mlops-slime) | 提供使用 slime(Megatron+SGLang 框架)进行 LLM RL 后训练的指导。适用于训练 GLM 模型、实现自定义数据生成工作流或需要紧密 Megatron-LM 集成以进行 RL 扩展的场景。 |
|
||||
| [**stable-diffusion-image-generation**](/user-guide/skills/optional/mlops/mlops-stable-diffusion) | 通过 HuggingFace Diffusers 使用 Stable Diffusion 模型进行最先进的文本到图像生成。适用于从文本 prompt 生成图像、图像到图像转换、图像修复或构建自定义扩散流水线的场景。 |
|
||||
| [**tensorrt-llm**](/user-guide/skills/optional/mlops/mlops-tensorrt-llm) | 使用 NVIDIA TensorRT 优化 LLM 推理,实现最大吞吐量和最低延迟。适用于在 NVIDIA GPU(A100/H100)上进行生产部署、需要比 PyTorch 快 10-100 倍的推理,或使用量化服务模型的场景。 |
|
||||
| [**distributed-llm-pretraining-torchtitan**](/user-guide/skills/optional/mlops/mlops-torchtitan) | 使用 torchtitan 进行 PyTorch 原生分布式 LLM 预训练,支持 4D 并行(FSDP2、TP、PP、CP)。适用于在 8 到 512+ GPU 上预训练 Llama 3.1、DeepSeek V3 或自定义模型,并使用 Float8、torch.compile 及分布式检查点的场景。 |
|
||||
| [**fine-tuning-with-trl**](/user-guide/skills/optional/mlops/mlops-training-trl-fine-tuning) | TRL:用于 LLM RLHF 的 SFT、DPO、PPO、GRPO 及奖励建模。 |
|
||||
| [**unsloth**](/user-guide/skills/optional/mlops/mlops-training-unsloth) | Unsloth:2-5 倍更快的 LoRA/QLoRA 微调,更低 VRAM 占用。 |
|
||||
| [**whisper**](/user-guide/skills/optional/mlops/mlops-whisper) | OpenAI 的通用语音识别模型。支持 99 种语言、转录、翻译为英语及语言识别。六种模型规格,从 tiny(39M 参数)到 large(1550M 参数)。适用于语音转文字、播客转录等场景。 |
|
||||
|
||||
## productivity
|
||||
|
||||
| 技能 | 描述 |
|
||||
|-------|-------------|
|
||||
| [**canvas**](/user-guide/skills/optional/productivity/productivity-canvas) | Canvas LMS 集成 — 使用 API token 认证获取已注册课程和作业。 |
|
||||
| [**here.now**](/user-guide/skills/optional/productivity/productivity-here-now) | 将静态站点发布至 {slug}.here.now,并将私有文件存储在云端 Drive 中以供 agent 间交接。 |
|
||||
| [**memento-flashcards**](/user-guide/skills/optional/productivity/productivity-memento-flashcards) | 间隔重复闪卡系统。从事实或文本创建卡片,通过 agent 评分的自由文本回答与闪卡对话,从 YouTube 字幕生成测验,使用自适应调度复习到期卡片,并支持导出/导入。 |
|
||||
| [**shop-app**](/user-guide/skills/optional/productivity/productivity-shop-app) | Shop.app:商品搜索、订单追踪、退货、重新下单。 |
|
||||
| [**shopify**](/user-guide/skills/optional/productivity/productivity-shopify) | 通过 curl 使用 Shopify Admin 和 Storefront GraphQL API。支持商品、订单、客户、库存、元字段。 |
|
||||
| [**siyuan**](/user-guide/skills/optional/productivity/productivity-siyuan) | 通过 curl 使用 SiYuan Note API,在自托管知识库中搜索、读取、创建和管理块与文档。 |
|
||||
| [**telephony**](/user-guide/skills/optional/productivity/productivity-telephony) | 为 Hermes 添加电话能力,无需修改核心工具。配置并持久化 Twilio 号码,发送和接收 SMS/MMS,拨打直接通话,并通过 Bland.ai 或 Vapi 发起 AI 驱动的外呼。 |
|
||||
|
||||
## research
|
||||
|
||||
| 技能 | 描述 |
|
||||
|-------|-------------|
|
||||
| [**bioinformatics**](/user-guide/skills/optional/research/research-bioinformatics) | 通往 bioSkills 和 ClawBio 400+ 生物信息学技能的入口。涵盖基因组学、转录组学、单细胞、变异检测、药物基因组学、宏基因组学、结构生物学等领域,按需获取特定领域参考资料。 |
|
||||
| [**darwinian-evolver**](/user-guide/skills/optional/research/research-darwinian-evolver) | 使用 Imbue 的进化循环演化 prompt/正则表达式/SQL/代码。 |
|
||||
| [**domain-intel**](/user-guide/skills/optional/research/research-domain-intel) | 使用 Python 标准库进行被动域名侦察。子域名发现、SSL 证书检查、WHOIS 查询、DNS 记录、域名可用性检测及批量多域名分析。无需 API 密钥。 |
|
||||
| [**drug-discovery**](/user-guide/skills/optional/research/research-drug-discovery) | 药物发现工作流的制药研究助手。在 ChEMBL 上搜索生物活性化合物,计算类药性(Lipinski Ro5、QED、TPSA、合成可及性),通过 OpenFDA 查询药物相互作用,解读 ADMET 属性。 |
|
||||
| [**duckduckgo-search**](/user-guide/skills/optional/research/research-duckduckgo-search) | 通过 DuckDuckGo 免费网络搜索 — 文本、新闻、图片、视频。无需 API 密钥。优先使用已安装的 `ddgs` CLI;仅在确认当前运行时中 `ddgs` 可用后才使用 Python DDGS 库。 |
|
||||
| [**gitnexus-explorer**](/user-guide/skills/optional/research/research-gitnexus-explorer) | 使用 GitNexus 为代码库建立索引,并通过 Web UI + Cloudflare 隧道提供交互式知识图谱。 |
|
||||
| [**osint-investigation**](/user-guide/skills/optional/research/research-osint-investigation) | 公开记录 OSINT 调查框架 — SEC EDGAR 文件、USAspending 合同、参议院游说记录、OFAC 制裁、ICIJ 离岸泄露、纽约市房产记录(ACRIS)、OpenCorporates 注册信息、CourtListener 法院记录、Wayback Machine 等。 |
|
||||
| [**parallel-cli**](/user-guide/skills/optional/research/research-parallel-cli) | Parallel CLI 的可选厂商技能 — agent 原生网络搜索、提取、深度研究、数据增强、FindAll 及监控。优先使用 JSON 输出和非交互式流程。 |
|
||||
| [**qmd**](/user-guide/skills/optional/research/research-qmd) | 使用 qmd(一款结合 BM25、向量搜索和 LLM 重排序的混合检索引擎)在本地搜索个人知识库、笔记、文档和会议记录。支持 CLI 和 MCP 集成。 |
|
||||
| [**scrapling**](/user-guide/skills/optional/research/research-scrapling) | 使用 Scrapling 进行网页抓取 — 通过 CLI 和 Python 实现 HTTP 获取、隐身浏览器自动化、Cloudflare 绕过及爬虫抓取。 |
|
||||
| [**searxng-search**](/user-guide/skills/optional/research/research-searxng-search) | 通过 SearXNG 免费元搜索 — 聚合 70+ 搜索引擎的结果。可自托管或使用公共实例。无需 API 密钥。当网络搜索工具集不可用时自动回退。 |
|
||||
|
||||
## security
|
||||
|
||||
| 技能 | 描述 |
|
||||
|-------|-------------|
|
||||
| [**1password**](/user-guide/skills/optional/security/security-1password) | 配置并使用 1Password CLI(op)。适用于安装 CLI、启用桌面应用集成、登录及为命令读取/注入密钥的场景。 |
|
||||
| [**oss-forensics**](/user-guide/skills/optional/security/security-oss-forensics) | 针对 GitHub 仓库的供应链调查、证据恢复和取证分析。涵盖已删除提交恢复、强制推送检测、IOC 提取、多源证据收集、假设形成/验证等。 |
|
||||
| [**sherlock**](/user-guide/skills/optional/security/security-sherlock) | 跨 400+ 社交网络的 OSINT 用户名搜索。通过用户名追踪社交媒体账号。 |
|
||||
|
||||
## software-development
|
||||
|
||||
| 技能 | 描述 |
|
||||
|-------|-------------|
|
||||
| [**rest-graphql-debug**](/user-guide/skills/optional/software-development/software-development-rest-graphql-debug) | 调试 REST/GraphQL API:状态码、认证、schema、问题复现。 |
|
||||
|
||||
## web-development
|
||||
|
||||
| 技能 | 描述 |
|
||||
|-------|-------------|
|
||||
| [**page-agent**](/user-guide/skills/optional/web-development/web-development-page-agent) | 将 alibaba/page-agent 嵌入您自己的 Web 应用 — 一个纯 JavaScript 页内 GUI agent,以单个 `<script>` 标签或 npm 包形式提供,让您网站的终端用户可以用自然语言驱动 UI(如"点击登录,填写用户名...")。 |
|
||||
|
||||
---
|
||||
|
||||
## 贡献可选技能
|
||||
|
||||
向仓库添加新的可选技能:
|
||||
|
||||
1. 在 `optional-skills/<category>/<skill-name>/` 下创建目录
|
||||
2. 添加包含标准 frontmatter 的 `SKILL.md`(name、description、version、author)
|
||||
3. 在 `references/`、`templates/` 或 `scripts/` 子目录中包含所有支撑文件
|
||||
4. 提交 pull request — 合并后该技能将出现在本目录并获得专属文档页面
|
||||
@ -0,0 +1,464 @@
|
||||
---
|
||||
sidebar_position: 7
|
||||
---
|
||||
|
||||
# Profile 命令参考
|
||||
|
||||
本页涵盖所有与 [Hermes profiles](../user-guide/profiles.md) 相关的命令。通用 CLI 命令请参阅 [CLI 命令参考](./cli-commands.md)。
|
||||
|
||||
## `hermes profile`
|
||||
|
||||
```bash
|
||||
hermes profile <subcommand>
|
||||
```
|
||||
|
||||
管理 profile 的顶级命令。不带子命令运行 `hermes profile` 将显示帮助信息。
|
||||
|
||||
| 子命令 | 描述 |
|
||||
|------------|-------------|
|
||||
| `list` | 列出所有 profile。 |
|
||||
| `use` | 设置当前活跃(默认)profile。 |
|
||||
| `create` | 创建新 profile。 |
|
||||
| `delete` | 删除 profile。 |
|
||||
| `show` | 显示 profile 详情。 |
|
||||
| `alias` | 重新生成 profile 的 shell alias。 |
|
||||
| `rename` | 重命名 profile。 |
|
||||
| `export` | 将 profile 导出为 tar.gz 归档文件。 |
|
||||
| `import` | 从 tar.gz 归档文件导入 profile。 |
|
||||
| `install` | 从 git URL 或本地目录安装 profile 发行版。参见 [Profile 发行版](../user-guide/profile-distributions.md)。 |
|
||||
| `update` | 重新拉取发行版管理的 profile 并重新应用其 bundle。 |
|
||||
| `info` | 显示 profile 的发行版元数据(来源 URL、commit、最后更新时间)。 |
|
||||
|
||||
## `hermes profile list`
|
||||
|
||||
```bash
|
||||
hermes profile list
|
||||
```
|
||||
|
||||
列出所有 profile。当前活跃的 profile 以 `*` 标记。
|
||||
|
||||
**示例:**
|
||||
|
||||
```bash
|
||||
$ hermes profile list
|
||||
default
|
||||
* work
|
||||
dev
|
||||
personal
|
||||
```
|
||||
|
||||
无选项。
|
||||
|
||||
## `hermes profile use`
|
||||
|
||||
```bash
|
||||
hermes profile use <name>
|
||||
```
|
||||
|
||||
将 `<name>` 设为活跃 profile。此后所有 `hermes` 命令(不带 `-p`)都将使用该 profile。
|
||||
|
||||
| 参数 | 描述 |
|
||||
|----------|-------------|
|
||||
| `<name>` | 要激活的 profile 名称。使用 `default` 可返回基础 profile。 |
|
||||
|
||||
**示例:**
|
||||
|
||||
```bash
|
||||
hermes profile use work
|
||||
hermes profile use default
|
||||
```
|
||||
|
||||
## `hermes profile create`
|
||||
|
||||
```bash
|
||||
hermes profile create <name> [options]
|
||||
```
|
||||
|
||||
创建新 profile。
|
||||
|
||||
| 参数 / 选项 | 描述 |
|
||||
|-------------------|-------------|
|
||||
| `<name>` | 新 profile 的名称。必须是合法的目录名(字母数字、连字符、下划线)。 |
|
||||
| `--clone` | 从当前 profile 复制 `config.yaml`、`.env` 和 `SOUL.md`。 |
|
||||
| `--clone-all` | 从当前 profile 复制所有内容(config、memories、skills、sessions、state)。 |
|
||||
| `--clone-from <profile>` | 从指定 profile 克隆,而非当前 profile。与 `--clone` 或 `--clone-all` 配合使用。 |
|
||||
| `--no-alias` | 跳过 wrapper 脚本创建。 |
|
||||
| `--description "<text>"` | 一到两句话描述该 profile 的用途。供 kanban 编排器根据角色而非仅凭 profile 名称来路由任务。可跳过,稍后通过 `hermes profile describe` 添加。持久化保存在 `<profile_dir>/profile.yaml` 中。 |
|
||||
| `--no-skills` | 创建一个**空** profile,不启用任何内置 skill。会在 profile 目录中写入 `.no-skills` 标记,使后续 `hermes update` 不再重新植入内置 skill 集,且拒绝与 `--clone` / `--clone-all` 组合使用(因为后者会复制 skill)。适用于不应继承完整 skill 目录的窄化编排器 profile 或沙箱 profile。 |
|
||||
|
||||
创建 profile **不会**将该 profile 目录设为终端命令的默认项目/工作目录。如需让某个 profile 从特定项目目录启动,请在该 profile 的 `config.yaml` 中设置 `terminal.cwd`。
|
||||
|
||||
**示例:**
|
||||
|
||||
```bash
|
||||
# 空白 profile — 需要完整配置
|
||||
hermes profile create mybot
|
||||
|
||||
# 仅从当前 profile 克隆 config
|
||||
hermes profile create work --clone
|
||||
|
||||
# 从当前 profile 克隆所有内容
|
||||
hermes profile create backup --clone-all
|
||||
|
||||
# 从指定 profile 克隆 config
|
||||
hermes profile create work2 --clone --clone-from work
|
||||
```
|
||||
|
||||
## `hermes profile describe`
|
||||
|
||||
```bash
|
||||
hermes profile describe [<name>] [options]
|
||||
```
|
||||
|
||||
读取或设置 profile 的描述。描述由 kanban 编排器使用,用于根据每个 profile 的能力路由任务,而非仅凭 profile 名称猜测。持久化保存在 `<profile_dir>/profile.yaml` 中,重启后仍有效,并与 gateway 共享。
|
||||
|
||||
不带任何标志时,打印当前描述(若为空则显示 `(no description set for '<name>')`)。
|
||||
|
||||
| 参数 / 选项 | 描述 |
|
||||
|-------------------|-------------|
|
||||
| `<name>` | 要描述的 profile。除非使用 `--all --auto`,否则必填。 |
|
||||
| `--text "<text>"` | 将描述设置为此精确文本(用户编写)。覆盖已有描述。 |
|
||||
| `--auto` | 通过辅助 LLM 自动生成 1-2 句描述,依据为该 profile 已安装的 skill、配置的模型和名称。在 `config.yaml` 的 `auxiliary.profile_describer` 下配置模型。自动生成的描述会标记 `description_auto: true`,以便 dashboard 标记供审查。 |
|
||||
| `--overwrite` | 与 `--auto` 配合使用时,也替换用户编写的描述(默认:跳过已明确设置描述的 profile)。 |
|
||||
| `--all` | 与 `--auto` 配合使用时,扫描所有缺少描述的 profile。 |
|
||||
|
||||
**示例:**
|
||||
|
||||
```bash
|
||||
# 读取当前描述
|
||||
hermes profile describe researcher
|
||||
|
||||
# 显式设置描述
|
||||
hermes profile describe researcher --text "Reads source code and writes findings."
|
||||
|
||||
# 让 LLM 生成描述
|
||||
hermes profile describe researcher --auto
|
||||
|
||||
# 为所有没有描述的 profile 填充描述
|
||||
hermes profile describe --all --auto
|
||||
```
|
||||
|
||||
## `hermes profile delete`
|
||||
|
||||
```bash
|
||||
hermes profile delete <name> [options]
|
||||
```
|
||||
|
||||
删除 profile 并移除其 shell alias。
|
||||
|
||||
| 参数 / 选项 | 描述 |
|
||||
|-------------------|-------------|
|
||||
| `<name>` | 要删除的 profile。 |
|
||||
| `--yes`, `-y` | 跳过确认提示。 |
|
||||
|
||||
**示例:**
|
||||
|
||||
```bash
|
||||
hermes profile delete mybot
|
||||
hermes profile delete mybot --yes
|
||||
```
|
||||
|
||||
:::warning
|
||||
此操作将永久删除 profile 的整个目录,包括所有 config、memories、sessions 和 skills。无法删除当前活跃的 profile。
|
||||
:::
|
||||
|
||||
## `hermes profile show`
|
||||
|
||||
```bash
|
||||
hermes profile show <name>
|
||||
```
|
||||
|
||||
显示 profile 的详细信息,包括其主目录、配置的模型、gateway 状态、skill 数量和配置文件状态。
|
||||
|
||||
此处显示的是 profile 的 Hermes 主目录,而非终端工作目录。终端命令从 `terminal.cwd` 启动(或在本地后端 `cwd: "."` 时从启动目录启动)。
|
||||
|
||||
| 参数 | 描述 |
|
||||
|----------|-------------|
|
||||
| `<name>` | 要查看的 profile。 |
|
||||
|
||||
**示例:**
|
||||
|
||||
```bash
|
||||
$ hermes profile show work
|
||||
Profile: work
|
||||
Path: ~/.hermes/profiles/work
|
||||
Model: anthropic/claude-sonnet-4 (anthropic)
|
||||
Gateway: stopped
|
||||
Skills: 12
|
||||
.env: exists
|
||||
SOUL.md: exists
|
||||
Alias: ~/.local/bin/work
|
||||
```
|
||||
|
||||
## `hermes profile alias`
|
||||
|
||||
```bash
|
||||
hermes profile alias <name> [options]
|
||||
```
|
||||
|
||||
重新生成位于 `~/.local/bin/<name>` 的 shell alias 脚本。适用于 alias 被意外删除,或移动 Hermes 安装目录后需要更新的情况。
|
||||
|
||||
| 参数 / 选项 | 描述 |
|
||||
|-------------------|-------------|
|
||||
| `<name>` | 要创建/更新 alias 的 profile。 |
|
||||
| `--remove` | 移除 wrapper 脚本而非创建。 |
|
||||
| `--name <alias>` | 自定义 alias 名称(默认:profile 名称)。 |
|
||||
|
||||
**示例:**
|
||||
|
||||
```bash
|
||||
hermes profile alias work
|
||||
# 创建/更新 ~/.local/bin/work
|
||||
|
||||
hermes profile alias work --name mywork
|
||||
# 创建 ~/.local/bin/mywork
|
||||
|
||||
hermes profile alias work --remove
|
||||
# 移除 wrapper 脚本
|
||||
```
|
||||
|
||||
## `hermes profile rename`
|
||||
|
||||
```bash
|
||||
hermes profile rename <old-name> <new-name>
|
||||
```
|
||||
|
||||
重命名 profile,同时更新目录和 shell alias。
|
||||
|
||||
| 参数 | 描述 |
|
||||
|----------|-------------|
|
||||
| `<old-name>` | 当前 profile 名称。 |
|
||||
| `<new-name>` | 新 profile 名称。 |
|
||||
|
||||
**示例:**
|
||||
|
||||
```bash
|
||||
hermes profile rename mybot assistant
|
||||
# ~/.hermes/profiles/mybot → ~/.hermes/profiles/assistant
|
||||
# ~/.local/bin/mybot → ~/.local/bin/assistant
|
||||
```
|
||||
|
||||
## `hermes profile export`
|
||||
|
||||
```bash
|
||||
hermes profile export <name> [options]
|
||||
```
|
||||
|
||||
将 profile 导出为压缩的 tar.gz 归档文件。
|
||||
|
||||
| 参数 / 选项 | 描述 |
|
||||
|-------------------|-------------|
|
||||
| `<name>` | 要导出的 profile。 |
|
||||
| `-o`, `--output <path>` | 输出文件路径(默认:`<name>.tar.gz`)。 |
|
||||
|
||||
**示例:**
|
||||
|
||||
```bash
|
||||
hermes profile export work
|
||||
# 在当前目录创建 work.tar.gz
|
||||
|
||||
hermes profile export work -o ./work-2026-03-29.tar.gz
|
||||
```
|
||||
|
||||
## `hermes profile import`
|
||||
|
||||
```bash
|
||||
hermes profile import <archive> [options]
|
||||
```
|
||||
|
||||
从 tar.gz 归档文件导入 profile。
|
||||
|
||||
| 参数 / 选项 | 描述 |
|
||||
|-------------------|-------------|
|
||||
| `<archive>` | 要导入的 tar.gz 归档文件路径。 |
|
||||
| `--name <name>` | 导入后的 profile 名称(默认:从归档文件推断)。 |
|
||||
|
||||
**示例:**
|
||||
|
||||
```bash
|
||||
hermes profile import ./work-2026-03-29.tar.gz
|
||||
# 从归档文件推断 profile 名称
|
||||
|
||||
hermes profile import ./work-2026-03-29.tar.gz --name work-restored
|
||||
```
|
||||
|
||||
## 发行版命令
|
||||
|
||||
:::tip
|
||||
**初次接触发行版?** 请先阅读 [Profile 发行版用户指南](../user-guide/profile-distributions.md) — 其中通过完整示例介绍了原因、时机和方法。以下章节是在你已知需求时使用的简明 CLI 参考。
|
||||
:::
|
||||
|
||||
发行版将 profile 转变为可共享、有版本的制品,以 **git 仓库**形式发布。接收方只需一条命令即可安装发行版,并可在不影响本地 memories、sessions 或凭据的情况下就地更新。
|
||||
|
||||
`auth.json` 和 `.env` 永远不属于发行版的一部分 — 它们保留在安装用户的机器上。
|
||||
|
||||
接收方的用户数据(memories、sessions、auth、对 `.env` 的自有编辑)在初次安装和后续更新中始终得到保留。
|
||||
|
||||
:::info
|
||||
`hermes profile export` / `import` 仍是在**本机进行 profile 本地备份和恢复**的正确命令。发行版(`install` / `update` / `info`)是独立概念:通过 git 分发 profile,供他人安装。
|
||||
:::
|
||||
|
||||
### `hermes profile install`
|
||||
|
||||
```bash
|
||||
hermes profile install <source> [--name <name>] [--alias] [--force] [--yes]
|
||||
```
|
||||
|
||||
从 git URL 或本地目录安装 profile 发行版。
|
||||
|
||||
| 选项 | 描述 |
|
||||
|--------|-------------|
|
||||
| `<source>` | Git URL(`github.com/user/repo`、`https://...`、`git@...`、`ssh://`、`git://`)或包含 `distribution.yaml` 的本地目录根路径。 |
|
||||
| `--name NAME` | 覆盖 manifest 中的 profile 名称。 |
|
||||
| `--alias` | 同时创建 shell wrapper(例如 `telemetry` → `hermes -p telemetry`)。 |
|
||||
| `--force` | 覆盖同名的已有 profile。用户数据仍会保留。 |
|
||||
| `-y`, `--yes` | 跳过 manifest 预览确认提示。 |
|
||||
|
||||
安装程序会显示 manifest、列出所需的环境变量,并在询问确认前提示 cron 任务信息。所需环境变量会写入 `.env.EXAMPLE` 文件,复制为 `.env` 后填写即可。
|
||||
|
||||
**示例:**
|
||||
|
||||
```bash
|
||||
# 从 GitHub 仓库安装(简写)
|
||||
hermes profile install github.com/kyle/telemetry-distribution --alias
|
||||
|
||||
# 从完整 HTTPS git URL 安装
|
||||
hermes profile install https://github.com/kyle/telemetry-distribution.git
|
||||
|
||||
# 从 SSH 安装
|
||||
hermes profile install git@github.com:kyle/telemetry-distribution.git
|
||||
|
||||
# 开发时从本地目录安装
|
||||
hermes profile install ./telemetry/
|
||||
```
|
||||
|
||||
### `hermes profile update`
|
||||
|
||||
```bash
|
||||
hermes profile update <name> [--force-config] [--yes]
|
||||
```
|
||||
|
||||
从记录的来源重新克隆发行版并应用更新。发行版所有的文件(SOUL.md、skills/、cron/、mcp.json)会被覆盖;用户数据(memories、sessions、auth、.env)不会被修改。
|
||||
|
||||
默认保留 `config.yaml` 以保持本地覆盖设置。传入 `--force-config` 可将其重置为发行版附带的 config。
|
||||
|
||||
### `hermes profile info`
|
||||
|
||||
```bash
|
||||
hermes profile info <name>
|
||||
```
|
||||
|
||||
打印 profile 的发行版 manifest — 名称、版本、所需 Hermes 版本、作者、环境变量要求、来源 URL/路径,以及发行版最后一次 `install` 或 `update` 时记录的 `Installed:` 时间戳。适用于安装前检查共享 profile 的需求,以及发现"该 profile 已安装 6 个月未更新"等情况。
|
||||
|
||||
`hermes profile list` 也会在 `Distribution` 列中显示发行版名称和版本,`hermes profile show <name>` / `delete <name>` 会显示来源 URL,让你一眼看出哪些 profile 来自 git 仓库,哪些是本地创建的。
|
||||
|
||||
### 私有发行版
|
||||
|
||||
私有 git 仓库无需额外配置即可作为发行版来源 — 安装时会调用系统的 `git` 二进制文件,因此 shell 已配置的任何认证方式(SSH 密钥、`git credential` helper、GitHub CLI 存储的 HTTPS 凭据)均可透明生效。
|
||||
|
||||
```bash
|
||||
# 使用 SSH 密钥,与普通 `git clone` 相同
|
||||
hermes profile install git@github.com:your-org/internal-assistant.git
|
||||
|
||||
# 使用 git credential helper
|
||||
hermes profile install https://github.com/your-org/internal-assistant.git
|
||||
```
|
||||
|
||||
如果克隆时在终端交互式提示输入凭据,该提示会正常显示。请先按照对同一仓库执行 `git clone` 的方式配置好认证,再执行安装。
|
||||
|
||||
### 发行版 manifest(`distribution.yaml`)
|
||||
|
||||
每个发行版在其仓库根目录都有一个 `distribution.yaml`:
|
||||
|
||||
```yaml
|
||||
name: telemetry
|
||||
version: 0.1.0
|
||||
description: "Compliance monitoring harness"
|
||||
hermes_requires: ">=0.12.0"
|
||||
author: "Your Name"
|
||||
license: "MIT"
|
||||
env_requires:
|
||||
- name: OPENAI_API_KEY
|
||||
description: "OpenAI API key"
|
||||
required: true
|
||||
- name: GRAPHITI_MCP_URL
|
||||
description: "Memory graph URL"
|
||||
required: false
|
||||
default: "http://127.0.0.1:8000/sse"
|
||||
distribution_owned: # optional; defaults to SOUL.md, config.yaml,
|
||||
# mcp.json, skills/, cron/, distribution.yaml
|
||||
- SOUL.md
|
||||
- skills/compliance/
|
||||
- cron/
|
||||
```
|
||||
|
||||
`hermes_requires` 支持 `>=`、`<=`、`==`、`!=`、`>`、`<`,或裸版本号(视为 `>=`)。若当前 Hermes 版本不满足规格,安装将失败并给出明确错误。
|
||||
|
||||
`distribution_owned` 为可选项。若设置,更新时仅替换这些路径;profile 中的其他内容保持用户所有。若省略,则应用上述默认值。
|
||||
|
||||
### 发布发行版
|
||||
|
||||
编写发行版就是一次 git push:
|
||||
|
||||
1. 在你的 profile 目录中创建 `distribution.yaml`,至少包含 `name` 和 `version`。
|
||||
2. 初始化 git 仓库(或使用已有仓库),推送到 GitHub / GitLab / 任何 Hermes 可克隆的托管平台。
|
||||
3. 告知接收方运行 `hermes profile install <your-repo-url>`。
|
||||
|
||||
使用 git tag 进行版本化发布 — 克隆 `HEAD` 的接收方将获得最新状态,你也可以随时在 manifest 中更新 `version:`。
|
||||
|
||||
## `hermes -p` / `hermes --profile`
|
||||
|
||||
```bash
|
||||
hermes -p <name> <command> [options]
|
||||
hermes --profile <name> <command> [options]
|
||||
```
|
||||
|
||||
全局标志,用于在不更改默认 profile 的情况下,在指定 profile 下运行任意 Hermes 命令。仅在该命令执行期间覆盖活跃 profile。
|
||||
|
||||
| 选项 | 描述 |
|
||||
|--------|-------------|
|
||||
| `-p <name>`, `--profile <name>` | 本次命令使用的 profile。 |
|
||||
|
||||
**示例:**
|
||||
|
||||
```bash
|
||||
hermes -p work chat -q "Check the server status"
|
||||
hermes --profile dev gateway start
|
||||
hermes -p personal skills list
|
||||
hermes -p work config edit
|
||||
```
|
||||
|
||||
## `hermes completion`
|
||||
|
||||
```bash
|
||||
hermes completion <shell>
|
||||
```
|
||||
|
||||
生成 shell 补全脚本。包含对 profile 名称和 profile 子命令的补全。
|
||||
|
||||
| 参数 | 描述 |
|
||||
|----------|-------------|
|
||||
| `<shell>` | 要生成补全脚本的 shell:`bash`、`zsh` 或 `fish`。 |
|
||||
|
||||
**示例:**
|
||||
|
||||
```bash
|
||||
# 安装补全脚本
|
||||
hermes completion bash >> ~/.bashrc
|
||||
hermes completion zsh >> ~/.zshrc
|
||||
hermes completion fish > ~/.config/fish/completions/hermes.fish
|
||||
|
||||
# 重新加载 shell
|
||||
source ~/.bashrc
|
||||
```
|
||||
|
||||
安装后,Tab 补全适用于:
|
||||
- `hermes profile <TAB>` — 子命令(list、use、create 等)
|
||||
- `hermes profile use <TAB>` — profile 名称
|
||||
- `hermes -p <TAB>` — profile 名称
|
||||
|
||||
## 另请参阅
|
||||
|
||||
- [Profiles 用户指南](../user-guide/profiles.md)
|
||||
- [CLI 命令参考](./cli-commands.md)
|
||||
- [FAQ — Profiles 章节](./faq.md#profiles)
|
||||
@ -0,0 +1,201 @@
|
||||
---
|
||||
sidebar_position: 5
|
||||
title: "内置技能目录"
|
||||
description: "随 Hermes Agent 附带的内置技能目录"
|
||||
---
|
||||
|
||||
# 内置技能目录
|
||||
|
||||
Hermes 附带一个大型内置技能库,安装时会复制到 `~/.hermes/skills/`。下方每个技能均链接至专属页面,包含完整定义、配置和用法说明。
|
||||
|
||||
Hermes 在执行 `hermes update` 时也会同步内置技能,但同步清单会尊重本地删除和用户编辑。如果此处列出的某个技能在你的 `~/.hermes/skills/` 目录树中缺失,它仍随 Hermes 一同发布;可通过 `hermes skills reset <name> --restore` 恢复。
|
||||
|
||||
如果某个技能未出现在此列表中但存在于仓库中,目录由 `website/scripts/generate-skill-docs.py` 重新生成。
|
||||
|
||||
## apple
|
||||
|
||||
| 技能 | 描述 | 路径 |
|
||||
|-------|-------------|------|
|
||||
| [`apple-notes`](/user-guide/skills/bundled/apple/apple-apple-notes) | 通过 memo CLI 管理 Apple Notes:创建、搜索、编辑。 | `apple/apple-notes` |
|
||||
| [`apple-reminders`](/user-guide/skills/bundled/apple/apple-apple-reminders) | 通过 remindctl 操作 Apple Reminders:添加、列出、完成。 | `apple/apple-reminders` |
|
||||
| [`findmy`](/user-guide/skills/bundled/apple/apple-findmy) | 在 macOS 上通过 FindMy.app 追踪 Apple 设备/AirTag。 | `apple/findmy` |
|
||||
| [`imessage`](/user-guide/skills/bundled/apple/apple-imessage) | 在 macOS 上通过 imsg CLI 发送和接收 iMessage/SMS。 | `apple/imessage` |
|
||||
| [`macos-computer-use`](/user-guide/skills/bundled/apple/apple-macos-computer-use) | 在后台驱动 macOS 桌面——截图、鼠标、键盘、滚动、拖拽——不抢占用户的光标、键盘焦点或 Space。适用于任何支持工具调用的模型。每当需要 `computer_use` 工具时加载此技能。 | `apple/macos-computer-use` |
|
||||
|
||||
## autonomous-ai-agents
|
||||
|
||||
| 技能 | 描述 | 路径 |
|
||||
|-------|-------------|------|
|
||||
| [`claude-code`](/user-guide/skills/bundled/autonomous-ai-agents/autonomous-ai-agents-claude-code) | 将编码任务委托给 Claude Code CLI(功能开发、PR)。 | `autonomous-ai-agents/claude-code` |
|
||||
| [`codex`](/user-guide/skills/bundled/autonomous-ai-agents/autonomous-ai-agents-codex) | 将编码任务委托给 OpenAI Codex CLI(功能开发、PR)。 | `autonomous-ai-agents/codex` |
|
||||
| [`hermes-agent`](/user-guide/skills/bundled/autonomous-ai-agents/autonomous-ai-agents-hermes-agent) | 配置、扩展或贡献 Hermes Agent。 | `autonomous-ai-agents/hermes-agent` |
|
||||
| [`opencode`](/user-guide/skills/bundled/autonomous-ai-agents/autonomous-ai-agents-opencode) | 将编码任务委托给 OpenCode CLI(功能开发、PR 审查)。 | `autonomous-ai-agents/opencode` |
|
||||
|
||||
## creative
|
||||
|
||||
| 技能 | 描述 | 路径 |
|
||||
|-------|-------------|------|
|
||||
| [`architecture-diagram`](/user-guide/skills/bundled/creative/creative-architecture-diagram) | 以 HTML 形式生成深色主题的 SVG 架构/云/基础设施图。 | `creative/architecture-diagram` |
|
||||
| [`ascii-art`](/user-guide/skills/bundled/creative/creative-ascii-art) | ASCII 艺术:pyfiglet、cowsay、boxes、图像转 ASCII。 | `creative/ascii-art` |
|
||||
| [`ascii-video`](/user-guide/skills/bundled/creative/creative-ascii-video) | ASCII 视频:将视频/音频转换为彩色 ASCII MP4/GIF。 | `creative/ascii-video` |
|
||||
| [`baoyu-article-illustrator`](/user-guide/skills/bundled/creative/creative-baoyu-article-illustrator) | 文章插图:类型 × 风格 × 调色板一致性。 | `creative/baoyu-article-illustrator` |
|
||||
| [`baoyu-comic`](/user-guide/skills/bundled/creative/creative-baoyu-comic) | 知识漫画:教育、传记、教程。 | `creative/baoyu-comic` |
|
||||
| [`baoyu-infographic`](/user-guide/skills/bundled/creative/creative-baoyu-infographic) | 信息图(可视化):21 种布局 × 21 种风格。 | `creative/baoyu-infographic` |
|
||||
| [`claude-design`](/user-guide/skills/bundled/creative/creative-claude-design) | 设计一次性 HTML 制品(落地页、幻灯片、原型)。 | `creative/claude-design` |
|
||||
| [`comfyui`](/user-guide/skills/bundled/creative/creative-comfyui) | 使用 ComfyUI 生成图像、视频和音频——安装、启动、管理节点/模型、运行带参数注入的工作流。使用官方 comfy-cli 管理生命周期,通过 REST/WebSocket API 直接执行。 | `creative/comfyui` |
|
||||
| [`ideation`](/user-guide/skills/bundled/creative/creative-creative-ideation) | 通过创意约束生成项目创意。 | `creative/creative-ideation` |
|
||||
| [`design-md`](/user-guide/skills/bundled/creative/creative-design-md) | 编写/验证/导出 Google 的 DESIGN.md token 规范文件。 | `creative/design-md` |
|
||||
| [`excalidraw`](/user-guide/skills/bundled/creative/creative-excalidraw) | 手绘风格的 Excalidraw JSON 图表(架构、流程、时序)。 | `creative/excalidraw` |
|
||||
| [`humanizer`](/user-guide/skills/bundled/creative/creative-humanizer) | 人性化文本:去除 AI 腔,加入真实语气。 | `creative/humanizer` |
|
||||
| [`manim-video`](/user-guide/skills/bundled/creative/creative-manim-video) | Manim CE 动画:3Blue1Brown 风格数学/算法视频。 | `creative/manim-video` |
|
||||
| [`p5js`](/user-guide/skills/bundled/creative/creative-p5js) | p5.js 草图:生成艺术、着色器、交互、3D。 | `creative/p5js` |
|
||||
| [`pixel-art`](/user-guide/skills/bundled/creative/creative-pixel-art) | 像素艺术,支持复古调色板(NES、Game Boy、PICO-8)。 | `creative/pixel-art` |
|
||||
| [`popular-web-designs`](/user-guide/skills/bundled/creative/creative-popular-web-designs) | 54 种真实设计系统(Stripe、Linear、Vercel)的 HTML/CSS 实现。 | `creative/popular-web-designs` |
|
||||
| [`pretext`](/user-guide/skills/bundled/creative/creative-pretext) | 使用 @chenglou/pretext 构建创意浏览器 demo——无 DOM 的文本布局,支持 ASCII 艺术、绕障碍物的排版流、文字即几何游戏、动态排版和文字驱动的生成艺术。生成单文件 HTML。 | `creative/pretext` |
|
||||
| [`sketch`](/user-guide/skills/bundled/creative/creative-sketch) | 一次性 HTML 原型:生成 2-3 个设计变体供对比。 | `creative/sketch` |
|
||||
| [`songwriting-and-ai-music`](/user-guide/skills/bundled/creative/creative-songwriting-and-ai-music) | 歌曲创作技巧与 Suno AI 音乐 prompt(提示词)。 | `creative/songwriting-and-ai-music` |
|
||||
| [`touchdesigner-mcp`](/user-guide/skills/bundled/creative/creative-touchdesigner-mcp) | 通过 twozero MCP 控制运行中的 TouchDesigner 实例——创建算子、设置参数、连接节点、执行 Python、构建实时视觉效果。36 个原生工具。 | `creative/touchdesigner-mcp` |
|
||||
|
||||
## data-science
|
||||
|
||||
| 技能 | 描述 | 路径 |
|
||||
|-------|-------------|------|
|
||||
| [`jupyter-live-kernel`](/user-guide/skills/bundled/data-science/data-science-jupyter-live-kernel) | 通过实时 Jupyter kernel(hamelnb)进行迭代式 Python 开发。 | `data-science/jupyter-live-kernel` |
|
||||
|
||||
## devops
|
||||
|
||||
| 技能 | 描述 | 路径 |
|
||||
|-------|-------------|------|
|
||||
| [`kanban-orchestrator`](/user-guide/skills/bundled/devops/devops-kanban-orchestrator) | 面向编排器(orchestrator)配置文件的分解策略与反诱惑规则,用于通过 Kanban 路由工作。"不要自己做工作"规则和基本生命周期会自动注入每个 Kanban worker 的系统 prompt;如需更深入的细节,请加载此技能。 | `devops/kanban-orchestrator` |
|
||||
| [`kanban-worker`](/user-guide/skills/bundled/devops/devops-kanban-worker) | Hermes Kanban worker 的陷阱、示例和边界情况。生命周期本身会作为 `KANBAN_GUIDANCE` 自动注入每个 worker 的系统 prompt(来自 `agent/prompt_builder.py`);当需要更深入细节时加载此技能。 | `devops/kanban-worker` |
|
||||
| [`webhook-subscriptions`](/user-guide/skills/bundled/devops/devops-webhook-subscriptions) | Webhook 订阅:事件驱动的 agent 运行。 | `devops/webhook-subscriptions` |
|
||||
|
||||
## dogfood
|
||||
|
||||
| 技能 | 描述 | 路径 |
|
||||
|-------|-------------|------|
|
||||
| [`dogfood`](/user-guide/skills/bundled/dogfood/dogfood-dogfood) | Web 应用探索性 QA:发现 bug、收集证据、生成报告。 | `dogfood` |
|
||||
|
||||
## email
|
||||
|
||||
| 技能 | 描述 | 路径 |
|
||||
|-------|-------------|------|
|
||||
| [`himalaya`](/user-guide/skills/bundled/email/email-himalaya) | Himalaya CLI:在终端中收发 IMAP/SMTP 邮件。 | `email/himalaya` |
|
||||
|
||||
## gaming
|
||||
|
||||
| 技能 | 描述 | 路径 |
|
||||
|-------|-------------|------|
|
||||
| [`minecraft-modpack-server`](/user-guide/skills/bundled/gaming/gaming-minecraft-modpack-server) | 托管模组版 Minecraft 服务器(CurseForge、Modrinth)。 | `gaming/minecraft-modpack-server` |
|
||||
| [`pokemon-player`](/user-guide/skills/bundled/gaming/gaming-pokemon-player) | 通过无头模拟器 + RAM 读取来游玩 Pokemon。 | `gaming/pokemon-player` |
|
||||
|
||||
## github
|
||||
|
||||
| 技能 | 描述 | 路径 |
|
||||
|-------|-------------|------|
|
||||
| [`codebase-inspection`](/user-guide/skills/bundled/github/github-codebase-inspection) | 使用 pygount 检查代码库:代码行数、语言、占比。 | `github/codebase-inspection` |
|
||||
| [`github-auth`](/user-guide/skills/bundled/github/github-github-auth) | GitHub 认证配置:HTTPS token、SSH 密钥、gh CLI 登录。 | `github/github-auth` |
|
||||
| [`github-code-review`](/user-guide/skills/bundled/github/github-github-code-review) | 审查 PR:通过 gh 或 REST API 查看 diff、添加行内评论。 | `github/github-code-review` |
|
||||
| [`github-issues`](/user-guide/skills/bundled/github/github-github-issues) | 通过 gh 或 REST API 创建、分类、标记、分配 GitHub issue。 | `github/github-issues` |
|
||||
| [`github-pr-workflow`](/user-guide/skills/bundled/github/github-github-pr-workflow) | GitHub PR 生命周期:分支、提交、开启、CI、合并。 | `github/github-pr-workflow` |
|
||||
| [`github-repo-management`](/user-guide/skills/bundled/github/github-github-repo-management) | 克隆/创建/fork 仓库;管理远程、发布版本。 | `github/github-repo-management` |
|
||||
|
||||
## mcp
|
||||
|
||||
| 技能 | 描述 | 路径 |
|
||||
|-------|-------------|------|
|
||||
| [`native-mcp`](/user-guide/skills/bundled/mcp/mcp-native-mcp) | MCP 客户端:连接服务器、注册工具(stdio/HTTP)。 | `mcp/native-mcp` |
|
||||
|
||||
## media
|
||||
|
||||
| 技能 | 描述 | 路径 |
|
||||
|-------|-------------|------|
|
||||
| [`gif-search`](/user-guide/skills/bundled/media/media-gif-search) | 通过 curl + jq 从 Tenor 搜索/下载 GIF。 | `media/gif-search` |
|
||||
| [`heartmula`](/user-guide/skills/bundled/media/media-heartmula) | HeartMuLa:根据歌词 + 标签生成类 Suno 风格的歌曲。 | `media/heartmula` |
|
||||
| [`songsee`](/user-guide/skills/bundled/media/media-songsee) | 通过 CLI 生成音频频谱图/特征(mel、chroma、MFCC)。 | `media/songsee` |
|
||||
| [`spotify`](/user-guide/skills/bundled/media/media-spotify) | Spotify:播放、搜索、排队、管理播放列表和设备。 | `media/spotify` |
|
||||
| [`youtube-content`](/user-guide/skills/bundled/media/media-youtube-content) | 将 YouTube 字幕转换为摘要、推文串、博客文章。 | `media/youtube-content` |
|
||||
|
||||
## mlops
|
||||
|
||||
| 技能 | 描述 | 路径 |
|
||||
|-------|-------------|------|
|
||||
| [`audiocraft-audio-generation`](/user-guide/skills/bundled/mlops/mlops-models-audiocraft) | AudioCraft:MusicGen 文本转音乐、AudioGen 文本转音效。 | `mlops/models/audiocraft` |
|
||||
| [`dspy`](/user-guide/skills/bundled/mlops/mlops-research-dspy) | DSPy:声明式 LM 程序,自动优化 prompt,支持 RAG。 | `mlops/research/dspy` |
|
||||
| [`huggingface-hub`](/user-guide/skills/bundled/mlops/mlops-huggingface-hub) | HuggingFace hf CLI:搜索/下载/上传模型、数据集。 | `mlops/huggingface-hub` |
|
||||
| [`llama-cpp`](/user-guide/skills/bundled/mlops/mlops-inference-llama-cpp) | llama.cpp 本地 GGUF 推理 + HF Hub 模型发现。 | `mlops/inference/llama-cpp` |
|
||||
| [`evaluating-llms-harness`](/user-guide/skills/bundled/mlops/mlops-evaluation-lm-evaluation-harness) | lm-eval-harness:对 LLM 进行基准测试(MMLU、GSM8K 等)。 | `mlops/evaluation/lm-evaluation-harness` |
|
||||
| [`obliteratus`](/user-guide/skills/bundled/mlops/mlops-inference-obliteratus) | OBLITERATUS:消除 LLM 拒绝行为(均值差分法)。 | `mlops/inference/obliteratus` |
|
||||
| [`segment-anything-model`](/user-guide/skills/bundled/mlops/mlops-models-segment-anything) | SAM:通过点、框、掩码进行零样本图像分割。 | `mlops/models/segment-anything` |
|
||||
| [`serving-llms-vllm`](/user-guide/skills/bundled/mlops/mlops-inference-vllm) | vLLM:高吞吐量 LLM 服务、OpenAI API 兼容、量化支持。 | `mlops/inference/vllm` |
|
||||
| [`weights-and-biases`](/user-guide/skills/bundled/mlops/mlops-evaluation-weights-and-biases) | W&B:记录 ML 实验、超参数搜索、模型注册表、仪表盘。 | `mlops/evaluation/weights-and-biases` |
|
||||
|
||||
## note-taking
|
||||
|
||||
| 技能 | 描述 | 路径 |
|
||||
|-------|-------------|------|
|
||||
| [`obsidian`](/user-guide/skills/bundled/note-taking/note-taking-obsidian) | 在 Obsidian 知识库中读取、搜索、创建和编辑笔记。 | `note-taking/obsidian` |
|
||||
|
||||
## productivity
|
||||
|
||||
| 技能 | 描述 | 路径 |
|
||||
|-------|-------------|------|
|
||||
| [`airtable`](/user-guide/skills/bundled/productivity/productivity-airtable) | 通过 curl 调用 Airtable REST API:记录增删改查、过滤、upsert。 | `productivity/airtable` |
|
||||
| [`google-workspace`](/user-guide/skills/bundled/productivity/productivity-google-workspace) | 通过 gws CLI 或 Python 操作 Gmail、Calendar、Drive、Docs、Sheets。 | `productivity/google-workspace` |
|
||||
| [`linear`](/user-guide/skills/bundled/productivity/productivity-linear) | Linear:通过 GraphQL + curl 管理 issue、项目、团队。 | `productivity/linear` |
|
||||
| [`maps`](/user-guide/skills/bundled/productivity/productivity-maps) | 通过 OpenStreetMap/OSRM 进行地理编码、POI 查询、路线规划、时区查询。 | `productivity/maps` |
|
||||
| [`nano-pdf`](/user-guide/skills/bundled/productivity/productivity-nano-pdf) | 通过 nano-pdf CLI 编辑 PDF 文本/错别字/标题(自然语言 prompt)。 | `productivity/nano-pdf` |
|
||||
| [`notion`](/user-guide/skills/bundled/productivity/productivity-notion) | Notion API + ntn CLI:页面、数据库、Markdown、Workers。 | `productivity/notion` |
|
||||
| [`ocr-and-documents`](/user-guide/skills/bundled/productivity/productivity-ocr-and-documents) | 从 PDF/扫描件中提取文本(pymupdf、marker-pdf)。 | `productivity/ocr-and-documents` |
|
||||
| [`powerpoint`](/user-guide/skills/bundled/productivity/productivity-powerpoint) | 创建、读取、编辑 .pptx 演示文稿、幻灯片、备注、模板。 | `productivity/powerpoint` |
|
||||
| [`teams-meeting-pipeline`](/user-guide/skills/bundled/productivity/productivity-teams-meeting-pipeline) | 通过 Hermes CLI 操作 Teams 会议摘要流水线——汇总会议、检查流水线状态、重放任务、管理 Microsoft Graph 订阅。 | `productivity/teams-meeting-pipeline` |
|
||||
|
||||
## red-teaming
|
||||
|
||||
| 技能 | 描述 | 路径 |
|
||||
|-------|-------------|------|
|
||||
| [`godmode`](/user-guide/skills/bundled/red-teaming/red-teaming-godmode) | 越狱 LLM:Parseltongue、GODMODE、ULTRAPLINIAN。 | `red-teaming/godmode` |
|
||||
|
||||
## research
|
||||
|
||||
| 技能 | 描述 | 路径 |
|
||||
|-------|-------------|------|
|
||||
| [`arxiv`](/user-guide/skills/bundled/research/research-arxiv) | 按关键词、作者、分类或 ID 搜索 arXiv 论文。 | `research/arxiv` |
|
||||
| [`blogwatcher`](/user-guide/skills/bundled/research/research-blogwatcher) | 通过 blogwatcher-cli 工具监控博客和 RSS/Atom 订阅源。 | `research/blogwatcher` |
|
||||
| [`llm-wiki`](/user-guide/skills/bundled/research/research-llm-wiki) | Karpathy 的 LLM Wiki:构建/查询互联 Markdown 知识库。 | `research/llm-wiki` |
|
||||
| [`polymarket`](/user-guide/skills/bundled/research/research-polymarket) | 查询 Polymarket:市场、价格、订单簿、历史数据。 | `research/polymarket` |
|
||||
| [`research-paper-writing`](/user-guide/skills/bundled/research/research-research-paper-writing) | 为 NeurIPS/ICML/ICLR 撰写 ML 论文:从设计到投稿。 | `research/research-paper-writing` |
|
||||
|
||||
## smart-home
|
||||
|
||||
| 技能 | 描述 | 路径 |
|
||||
|-------|-------------|------|
|
||||
| [`openhue`](/user-guide/skills/bundled/smart-home/smart-home-openhue) | 通过 OpenHue CLI 控制 Philips Hue 灯光、场景、房间。 | `smart-home/openhue` |
|
||||
|
||||
## social-media
|
||||
|
||||
| 技能 | 描述 | 路径 |
|
||||
|-------|-------------|------|
|
||||
| [`xurl`](/user-guide/skills/bundled/social-media/social-media-xurl) | 通过 xurl CLI 操作 X/Twitter:发帖、搜索、私信、媒体、v2 API。 | `social-media/xurl` |
|
||||
|
||||
## software-development
|
||||
|
||||
| 技能 | 描述 | 路径 |
|
||||
|-------|-------------|------|
|
||||
| [`debugging-hermes-tui-commands`](/user-guide/skills/bundled/software-development/software-development-debugging-hermes-tui-commands) | 调试 Hermes TUI 斜杠命令:Python、gateway、Ink UI。 | `software-development/debugging-hermes-tui-commands` |
|
||||
| [`hermes-agent-skill-authoring`](/user-guide/skills/bundled/software-development/software-development-hermes-agent-skill-authoring) | 编写仓库内 SKILL.md:frontmatter、验证器、结构规范。 | `software-development/hermes-agent-skill-authoring` |
|
||||
| [`node-inspect-debugger`](/user-guide/skills/bundled/software-development/software-development-node-inspect-debugger) | 通过 --inspect + Chrome DevTools Protocol CLI 调试 Node.js。 | `software-development/node-inspect-debugger` |
|
||||
| [`plan`](/user-guide/skills/bundled/software-development/software-development-plan) | 计划模式:将 Markdown 计划写入 `.hermes/plans/`,不执行。 | `software-development/plan` |
|
||||
| [`python-debugpy`](/user-guide/skills/bundled/software-development/software-development-python-debugpy) | 调试 Python:pdb REPL + debugpy 远程调试(DAP)。 | `software-development/python-debugpy` |
|
||||
| [`requesting-code-review`](/user-guide/skills/bundled/software-development/software-development-requesting-code-review) | 提交前审查:安全扫描、质量门控、自动修复。 | `software-development/requesting-code-review` |
|
||||
| [`spike`](/user-guide/skills/bundled/software-development/software-development-spike) | 一次性实验,在正式构建前验证想法。 | `software-development/spike` |
|
||||
| [`subagent-driven-development`](/user-guide/skills/bundled/software-development/software-development-subagent-driven-development) | 通过 `delegate_task` 子 agent 执行计划(两阶段审查)。 | `software-development/subagent-driven-development` |
|
||||
| [`systematic-debugging`](/user-guide/skills/bundled/software-development/software-development-systematic-debugging) | 四阶段根因调试:先理解 bug,再修复。 | `software-development/systematic-debugging` |
|
||||
| [`test-driven-development`](/user-guide/skills/bundled/software-development/software-development-test-driven-development) | TDD:强制执行红-绿-重构流程,先写测试再写代码。 | `software-development/test-driven-development` |
|
||||
| [`writing-plans`](/user-guide/skills/bundled/software-development/software-development-writing-plans) | 编写实现计划:细粒度任务、路径、代码。 | `software-development/writing-plans` |
|
||||
|
||||
## yuanbao
|
||||
|
||||
| 技能 | 描述 | 路径 |
|
||||
|-------|-------------|------|
|
||||
| [`yuanbao`](/user-guide/skills/bundled/yuanbao/yuanbao-yuanbao) | 元宝(Yuanbao)群组:@提及用户、查询信息/成员。 | `yuanbao` |
|
||||
@ -0,0 +1,257 @@
|
||||
---
|
||||
sidebar_position: 2
|
||||
title: "斜杠命令参考"
|
||||
description: "交互式 CLI 和消息平台斜杠命令完整参考"
|
||||
---
|
||||
|
||||
# 斜杠命令参考
|
||||
|
||||
Hermes 有两个斜杠命令入口,均由 `hermes_cli/commands.py` 中的中央 `COMMAND_REGISTRY` 驱动:
|
||||
|
||||
- **交互式 CLI 斜杠命令** — 由 `cli.py` 分发,支持从注册表自动补全
|
||||
- **消息平台斜杠命令** — 由 `gateway/run.py` 分发,帮助文本和平台菜单均从注册表生成
|
||||
|
||||
已安装的 skill(技能)也会在两个入口以动态斜杠命令的形式暴露。这包括内置 skill,如 `/plan`,它会打开计划模式并将 markdown 计划保存在活动工作区/后端工作目录下的 `.hermes/plans/` 中。
|
||||
|
||||
## 权限与管理员/用户分级
|
||||
|
||||
每个支持按用户白名单的消息平台(Telegram、Discord、Slack、Matrix、Mattermost、Signal 等)都支持两级斜杠命令分级:**管理员**可使用所有已注册命令,**普通用户**只能使用你在 `user_allowed_commands` 中列出的命令(以及始终允许的 `/help` 和 `/whoami`)。在 `~/.hermes/gateway-config.yaml` 中对应平台的 `extra:` 块内配置 `allow_admin_from` 和 `user_allowed_commands`(以及群组等效项 `group_allow_admin_from` / `group_user_allowed_commands`)。
|
||||
|
||||
各平台文档中有示例——结构在各平台间完全一致:
|
||||
|
||||
- [Telegram](../user-guide/messaging/telegram.md#slash-command-access-control)
|
||||
- [Discord](../user-guide/messaging/discord.md)
|
||||
- [Slack](../user-guide/messaging/slack.md)
|
||||
- [Matrix](../user-guide/messaging/matrix.md)
|
||||
- [Mattermost](../user-guide/messaging/mattermost.md)
|
||||
- [Signal](../user-guide/messaging/signal.md)
|
||||
|
||||
如果某个作用域未设置 `allow_admin_from`,该作用域将保持不受限的向后兼容模式——所有允许的用户均可运行所有命令。
|
||||
|
||||
## 交互式 CLI 斜杠命令
|
||||
|
||||
在 CLI 中输入 `/` 可打开自动补全菜单。内置命令不区分大小写。
|
||||
|
||||
### 会话
|
||||
|
||||
| 命令 | 描述 |
|
||||
|---------|-------------|
|
||||
| `/new [name]`(别名:`/reset`) | 开始新会话(全新会话 ID + 历史记录)。可选的 `[name]` 设置初始会话标题——例如 `/new my-experiment` 打开一个已命名为 `my-experiment` 的新会话,便于之后用 `/resume` 或 `/sessions` 查找。追加 `now`、`--yes` 或 `-y` 可跳过确认弹窗——例如 `/reset now`、`/new --yes my-experiment`。 |
|
||||
| `/clear` | 清屏并开始新会话 |
|
||||
| `/history` | 显示对话历史 |
|
||||
| `/save` | 保存当前对话 |
|
||||
| `/retry` | 重试最后一条消息(重新发送给 agent) |
|
||||
| `/undo` | 移除最后一轮用户/助手对话 |
|
||||
| `/title` | 为当前会话设置标题(用法:/title My Session Name) |
|
||||
| `/compress [focus topic]` | 手动压缩对话上下文(刷新记忆 + 摘要)。可选的焦点主题可缩小摘要保留的范围。 |
|
||||
| `/rollback` | 列出或恢复文件系统检查点(用法:/rollback [number]) |
|
||||
| `/snapshot [create\|restore <id>\|prune]`(别名:`/snap`) | 创建或恢复 Hermes 配置/状态的快照。`create [label]` 保存快照,`restore <id>` 回滚到该快照,`prune [N]` 删除旧快照,不带参数则列出所有快照。 |
|
||||
| `/stop` | 终止所有正在运行的后台进程 |
|
||||
| `/queue <prompt>`(别名:`/q`) | 将 prompt(提示词)加入队列等待下一轮处理(不会中断当前 agent 响应)。 |
|
||||
| `/steer <prompt>` | 在**下一次工具调用之后**向 agent 注入一条中途说明——不中断、不产生新的用户轮次。当前工具完成后,该文本会追加到最后一条工具结果的内容中,在不打断当前工具调用循环的情况下为 agent 提供新上下文。可用于在任务进行中调整方向(例如在 agent 运行测试时说"专注于 auth 模块")。 |
|
||||
| `/goal <text>` | 设置一个持续目标,Hermes 将跨轮次持续推进——这是我们对 Ralph loop 的实现。每轮结束后,辅助裁判模型会判断目标是否完成;若未完成,Hermes 自动继续。子命令:`/goal status`、`/goal pause`、`/goal resume`、`/goal clear`。预算默认为 20 轮(`goals.max_turns`);任何真实用户消息都会抢占继续循环,状态在 `/resume` 后保留。完整说明见 [持续目标](/user-guide/features/goals)。 |
|
||||
| `/subgoal <text>` | 在循环进行中向活动目标追加一个用户自定义条件。继续 prompt 会将所有子目标原文呈现给 agent,裁判也会将其纳入 DONE/CONTINUE 判断——因此只有原始目标**和**所有子目标都满足时,目标才会被标记为完成。子命令:`/subgoal`(列出)、`/subgoal remove <N>`、`/subgoal clear`。需要有活动的 `/goal`。 |
|
||||
| `/resume [name]` | 恢复之前命名的会话 |
|
||||
| `/sessions` | 在交互式选择器中浏览并恢复历史会话 |
|
||||
| `/redraw` | 强制完整重绘 UI(在 tmux 调整大小、鼠标选择产生残影等导致终端错位后恢复)。 |
|
||||
| `/status` | 显示会话信息——模型、提供商、profile、会话 ID、工作目录、标题、创建/更新时间戳、token 总量、agent 运行状态——随后显示本地**会话摘要**块(近期用户/助手轮次数、工具结果数、最常用工具、最近访问的文件、最新用户 prompt 和最新助手回复)。摘要从内存中的对话本地计算,不调用 LLM,不影响 prompt 缓存。 |
|
||||
| `/agents`(别名:`/tasks`) | 显示当前会话中的活动 agent 和运行中的任务。 |
|
||||
| `/background <prompt>`(别名:`/bg`、`/btw`) | 在独立的后台会话中运行 prompt。agent 独立处理你的 prompt——当前会话保持空闲可继续其他工作。任务完成后结果以面板形式显示。见 [CLI 后台会话](/user-guide/cli#background-sessions)。 |
|
||||
| `/branch [name]`(别名:`/fork`) | 分支当前会话(探索不同路径) |
|
||||
| `/handoff <platform>` | **仅限 CLI。** 将当前会话移交给消息平台(Telegram、Discord、Slack、WhatsApp、Signal、Matrix)。gateway 立即接管,在支持线程的平台上创建新线程(Telegram 话题、Discord 文字频道线程、Slack 消息锚定线程),将目标重新绑定到你的 CLI session_id 以重放完整的角色感知转录,并伪造一条合成用户轮次让 agent 确认已在新位置工作。成功后 CLI 干净退出并提示 `/resume`;随时可用 `/resume <title>` 在本地恢复。轮次进行中拒绝执行。需要 gateway 正在运行且目标平台已配置 home 频道(从目标聊天中执行 `/sethome`)。见 [跨平台移交](/user-guide/sessions#cross-platform-handoff)。 |
|
||||
|
||||
### 配置
|
||||
|
||||
| 命令 | 描述 |
|
||||
|---------|-------------|
|
||||
| `/config` | 显示当前配置 |
|
||||
| `/model [model-name]` | 显示或更改当前模型。支持:`/model claude-sonnet-4`、`/model provider:model`(切换提供商)、`/model custom:model`(自定义端点)、`/model custom:name:model`(命名自定义提供商)、`/model custom`(从端点自动检测),以及用户自定义别名(`/model fav`、`/model grok`——见[自定义模型别名](#custom-model-aliases))。使用 `--global` 将更改持久化到 config.yaml。**注意:** `/model` 只能在已配置的提供商之间切换。如需添加新提供商,请退出会话后在终端运行 `hermes model`。 |
|
||||
| `/codex-runtime [auto\|codex_app_server\|on\|off]` | 切换 OpenAI/Codex 模型的可选 [Codex app-server runtime](../user-guide/features/codex-app-server-runtime)。`auto`(默认)使用 Hermes 标准 chat completions;`codex_app_server` 将轮次交给 `codex app-server` 子进程,支持原生 shell、apply_patch、ChatGPT 订阅认证和迁移的 Codex 插件。下次会话生效。 |
|
||||
| `/personality` | 设置预定义的 personality(人格) |
|
||||
| `/verbose` | 循环切换工具进度显示:off → new → all → verbose。可通过配置[为消息平台启用](#notes)。 |
|
||||
| `/fast [normal\|fast\|status]` | 切换快速模式——OpenAI Priority Processing / Anthropic Fast Mode。选项:`normal`、`fast`、`status`。 |
|
||||
| `/reasoning` | 管理推理力度和显示(用法:/reasoning [level\|show\|hide]) |
|
||||
| `/skin` | 显示或更改显示皮肤/主题 |
|
||||
| `/statusbar`(别名:`/sb`) | 切换上下文/模型状态栏的显示与隐藏 |
|
||||
| `/voice [on\|off\|tts\|status]` | 切换 CLI 语音模式和语音播放。录音使用 `voice.record_key`(默认:`Ctrl+B`)。 |
|
||||
| `/yolo` | 切换 YOLO 模式——跳过所有危险命令审批提示。 |
|
||||
| `/footer [on\|off\|status]` | 切换最终回复中的 gateway 运行时元数据页脚(显示模型、工具调用次数、耗时)。 |
|
||||
| `/busy [queue\|steer\|interrupt\|status]` | 仅限 CLI:控制 Hermes 工作时按下 Enter 的行为——将新消息加入队列、中途引导,或立即中断。 |
|
||||
| `/indicator [kaomoji\|emoji\|unicode\|ascii]` | 仅限 CLI:选择 TUI 忙碌指示器样式。 |
|
||||
|
||||
### 工具与 Skill
|
||||
|
||||
| 命令 | 描述 |
|
||||
|---------|-------------|
|
||||
| `/tools [list\|disable\|enable] [name...]` | 管理工具:列出可用工具,或为当前会话禁用/启用特定工具。禁用工具会将其从 agent 工具集中移除并触发会话重置。 |
|
||||
| `/toolsets` | 列出可用工具集 |
|
||||
| `/browser [connect\|disconnect\|status]` | 管理本地 Chromium 系浏览器的 CDP 连接。`connect` 将浏览器工具附加到正在运行的 Chrome、Brave、Chromium 或 Edge 实例(默认:`http://127.0.0.1:9222`)。`disconnect` 断开连接。`status` 显示当前连接状态。若未检测到调试器,则自动启动支持的 Chromium 系浏览器。 |
|
||||
| `/skills` | 从在线注册表搜索、安装、检查或管理 skill |
|
||||
| `/cron` | 管理定时任务(列出、添加/创建、编辑、暂停、恢复、运行、删除) |
|
||||
| `/curator` | 后台 skill 维护——`status`、`run`、`pin`、`archive`。见 [Curator](/user-guide/features/curator)。 |
|
||||
| `/kanban <action>` | 无需离开聊天即可操作多 profile、多项目协作看板。完整的 `hermes kanban` 命令面均可用:`/kanban list`、`/kanban show t_abc`、`/kanban create "title" --assignee X`、`/kanban comment t_abc "text"`、`/kanban unblock t_abc`、`/kanban dispatch` 等。支持多看板:`/kanban boards list`、`/kanban boards create <slug>`、`/kanban boards switch <slug>`、`/kanban --board <slug> <action>`。见 [Kanban 斜杠命令](/user-guide/features/kanban#kanban-slash-command)。 |
|
||||
| `/reload-mcp`(别名:`/reload_mcp`) | 从 config.yaml 重新加载 MCP 服务器 |
|
||||
| `/reload-skills`(别名:`/reload_skills`) | 重新扫描 `~/.hermes/skills/` 以发现新安装或已删除的 skill |
|
||||
| `/reload` | 将 `.env` 变量重新加载到运行中的会话(无需重启即可获取新 API 密钥) |
|
||||
| `/plugins` | 列出已安装的插件及其状态 |
|
||||
|
||||
### 信息
|
||||
|
||||
| 命令 | 描述 |
|
||||
|---------|-------------|
|
||||
| `/help` | 显示帮助信息 |
|
||||
| `/usage` | 显示 token 用量、费用明细、会话时长,以及——当活动提供商支持时——从提供商 API 实时拉取的**账户限额**部分,包含剩余配额/积分/套餐用量。 |
|
||||
| `/insights` | 显示用量洞察和分析(最近 30 天) |
|
||||
| `/platforms`(别名:`/gateway`) | 显示 gateway/消息平台状态(仅限 CLI 摘要视图)。 |
|
||||
| `/platform <list\|pause\|resume> [name]` | 操作正在运行的 gateway 平台。`/platform list` 列出所有适配器及其状态(运行中、熔断器暂停、手动暂停);`/platform pause <name>` 停止向该适配器分发新消息但不卸载它;`/platform resume <name>` 重新启用它。当适配器的熔断器因反复可重试失败(网络/限流/5xx)触发时,gateway 也会自动暂停该适配器——上游恢复健康后使用 `/platform resume <name>` 清除熔断器。在 gateway 可达的任何地方均可使用(CLI 会话、Telegram、Discord 等)。 |
|
||||
| `/paste` | 附加剪贴板图片 |
|
||||
| `/copy [number]` | 将最后一条助手回复复制到剪贴板(或用数字指定倒数第 N 条)。仅限 CLI。 |
|
||||
| `/image <path>` | 为下一条 prompt 附加本地图片文件。 |
|
||||
| `/debug` | 上传调试报告(系统信息 + 日志)并获取可分享链接。消息平台中也可用。 |
|
||||
| `/profile` | 显示活动 profile 名称和主目录 |
|
||||
| `/gquota` | 以进度条形式显示 Google Gemini Code Assist 配额用量(仅在 `google-gemini-cli` 提供商激活时可用)。 |
|
||||
|
||||
### 退出
|
||||
|
||||
| 命令 | 描述 |
|
||||
|---------|-------------|
|
||||
| `/quit` | 退出 CLI(也可用:`/exit`)。关于 `/q` 请参见上方 `/queue` 的说明。传入 `--delete`(或 `-d`)——例如 `/exit --delete`——可在退出前永久删除当前会话的 SQLite 历史记录和磁盘上的转录文件。适用于隐私敏感或一次性任务。 |
|
||||
|
||||
### 动态 CLI 斜杠命令
|
||||
|
||||
| 命令 | 描述 |
|
||||
|---------|-------------|
|
||||
| `/<skill-name>` | 将任意已安装的 skill 作为按需命令加载。示例:`/gif-search`、`/github-pr-workflow`、`/excalidraw`。 |
|
||||
| `/skills ...` | 从注册表和官方可选 skill 目录搜索、浏览、检查、安装、审计、发布和配置 skill。 |
|
||||
|
||||
### 快捷命令
|
||||
|
||||
用户自定义快捷命令将一个短斜杠命令映射到 shell 命令或另一个斜杠命令。在 `~/.hermes/config.yaml` 中配置:
|
||||
|
||||
```yaml
|
||||
quick_commands:
|
||||
status:
|
||||
type: exec
|
||||
command: systemctl status hermes-agent
|
||||
deploy:
|
||||
type: exec
|
||||
command: scripts/deploy.sh
|
||||
inbox:
|
||||
type: alias
|
||||
target: /gmail unread
|
||||
```
|
||||
|
||||
然后在 CLI 或消息平台中输入 `/status`、`/deploy` 或 `/inbox`。快捷命令在分发时解析,可能不会出现在所有内置自动补全/帮助表中。
|
||||
|
||||
不支持将纯字符串 prompt 快捷方式作为快捷命令。较长的可复用 prompt 请放入 skill,或使用 `type: alias` 指向现有斜杠命令。
|
||||
|
||||
### 自定义模型别名
|
||||
|
||||
为常用模型定义自己的短名称,然后在 CLI 或任意消息平台中通过 `/model <alias>` 调用。别名在两者中的行为完全一致,支持仅会话(默认)和 `--global` 切换。
|
||||
|
||||
支持两种配置格式:
|
||||
|
||||
**完整格式** — 固定精确的模型、提供商,以及可选的 base URL。写入 `~/.hermes/config.yaml`:
|
||||
|
||||
```yaml
|
||||
model_aliases:
|
||||
fav:
|
||||
model: claude-sonnet-4.6
|
||||
provider: anthropic
|
||||
grok:
|
||||
model: grok-4
|
||||
provider: x-ai
|
||||
ollama-qwen:
|
||||
model: qwen3-coder:30b
|
||||
provider: custom
|
||||
base_url: http://localhost:11434/v1
|
||||
```
|
||||
|
||||
**简短格式** — 用一个字符串表示 `provider/model`。无需编辑 YAML,直接从 shell 设置:
|
||||
|
||||
```bash
|
||||
hermes config set model.aliases.fav anthropic/claude-opus-4.6
|
||||
hermes config set model.aliases.grok x-ai/grok-4
|
||||
```
|
||||
|
||||
然后在聊天中:
|
||||
|
||||
```
|
||||
/model fav # 仅当前会话
|
||||
/model grok --global # 同时将当前模型更改持久化到 config.yaml
|
||||
```
|
||||
|
||||
用户别名优先于内置短名称,因此将别名命名为 `sonnet`、`kimi`、`opus` 等会覆盖内置名称。别名名称不区分大小写。
|
||||
|
||||
### 别名解析
|
||||
|
||||
命令支持前缀匹配:输入 `/h` 解析为 `/help`,`/mod` 解析为 `/model`。当前缀有歧义(匹配多个命令)时,注册表顺序中的第一个匹配项优先。完整命令名和已注册别名始终优先于前缀匹配。
|
||||
|
||||
## 消息平台斜杠命令
|
||||
|
||||
消息 gateway 在 Telegram、Discord、Slack、WhatsApp、Signal、Email、Home Assistant 和 Teams 聊天中支持以下内置命令:
|
||||
|
||||
| 命令 | 描述 |
|
||||
|---------|-------------|
|
||||
| `/new` | 开始新对话。 |
|
||||
| `/reset` | 重置对话历史。 |
|
||||
| `/status` | 显示会话信息,随后显示本地**会话摘要**块(近期轮次数、最常用工具、访问的文件、最新 prompt + 回复)。 |
|
||||
| `/stop` | 终止所有正在运行的后台进程并中断运行中的 agent。 |
|
||||
| `/model [provider:model]` | 显示或更改模型。支持提供商切换(`/model zai:glm-5`)、自定义端点(`/model custom:model`)、命名自定义提供商(`/model custom:local:qwen`)、自动检测(`/model custom`),以及用户自定义别名(`/model fav`、`/model grok`——见[自定义模型别名](#custom-model-aliases))。使用 `--global` 将更改持久化到 config.yaml。**注意:** `/model` 只能在已配置的提供商之间切换。如需添加新提供商或设置 API 密钥,请在终端(聊天会话外)运行 `hermes model`。 |
|
||||
| `/codex-runtime [auto\|codex_app_server\|on\|off]` | 切换可选的 [Codex app-server runtime](../user-guide/features/codex-app-server-runtime)。持久化到 config.yaml 中的 `model.openai_runtime` 并驱逐缓存的 agent,使下一条消息使用新 runtime。下次会话生效。 |
|
||||
| `/personality [name]` | 为会话设置 personality 覆盖层。 |
|
||||
| `/fast [normal\|fast\|status]` | 切换快速模式——OpenAI Priority Processing / Anthropic Fast Mode。 |
|
||||
| `/retry` | 重试最后一条消息。 |
|
||||
| `/undo` | 移除最后一轮对话。 |
|
||||
| `/sethome`(别名:`/set-home`) | 将当前聊天标记为该平台的 home 频道,用于消息投递。 |
|
||||
| `/compress [focus topic]` | 手动压缩对话上下文。可选的焦点主题可缩小摘要保留的范围。 |
|
||||
| `/topic [off\|help\|session-id]` | **仅限 Telegram DM。** 管理用户自主的多会话话题模式。`/topic` 启用或显示状态;`/topic off` 禁用并清除绑定;`/topic help` 显示用法;在话题中执行 `/topic <session-id>` 可恢复之前的会话。见 [多会话 DM 模式](/user-guide/messaging/telegram#multi-session-dm-mode-topic)。 |
|
||||
| `/title [name]` | 设置或显示会话标题。 |
|
||||
| `/resume [name]` | 恢复之前命名的会话。 |
|
||||
| `/usage` | 显示 token 用量、估算费用明细(输入/输出)、上下文窗口状态、会话时长,以及——当活动提供商支持时——从提供商 API 实时拉取的**账户限额**部分,包含剩余配额/积分。 |
|
||||
| `/insights [days]` | 显示用量分析。 |
|
||||
| `/reasoning [level\|show\|hide]` | 更改推理力度或切换推理显示。 |
|
||||
| `/voice [on\|off\|tts\|join\|channel\|leave\|status]` | 控制聊天中的语音回复。`join`/`channel`/`leave` 管理 Discord 语音频道模式。 |
|
||||
| `/rollback [number]` | 列出或恢复文件系统检查点。 |
|
||||
| `/background <prompt>` | 在独立的后台会话中运行 prompt。任务完成后结果投递回同一聊天。见 [消息平台后台会话](/user-guide/messaging/#background-sessions)。 |
|
||||
| `/queue <prompt>`(别名:`/q`) | 将 prompt 加入队列等待下一轮处理,不中断当前轮次。 |
|
||||
| `/steer <prompt>` | 在下一次工具调用后注入一条消息,不中断——模型在下一次迭代时获取,而非作为新轮次。 |
|
||||
| `/goal <text>` | 设置一个持续目标,Hermes 将跨轮次持续推进——这是我们对 Ralph loop 的实现。裁判模型在每轮后检查;若未完成,Hermes 自动继续,直到完成、你暂停/清除,或达到轮次预算(默认 20)。子命令:`/goal status`、`/goal pause`、`/goal resume`、`/goal clear`。agent 运行中可安全执行 status/pause/clear;设置新目标需先执行 `/stop`。见 [持续目标](/user-guide/features/goals)。 |
|
||||
| `/footer [on\|off\|status]` | 切换最终回复中的运行时元数据页脚(显示模型、工具调用次数、耗时)。 |
|
||||
| `/curator [status\|run\|pin\|archive]` | 后台 skill 维护控制。 |
|
||||
| `/kanban <action>` | 从聊天中操作多 profile、多项目协作看板——参数与 CLI 完全一致。绕过运行中 agent 的保护,因此 `/kanban unblock t_abc`、`/kanban comment t_abc "…"`、`/kanban list --mine`、`/kanban boards switch <slug>` 等均可在轮次进行中使用。`/kanban create …` 会自动将发起聊天订阅到新任务的终态事件。见 [Kanban 斜杠命令](/user-guide/features/kanban#kanban-slash-command)。 |
|
||||
| `/reload-mcp`(别名:`/reload_mcp`) | 从配置重新加载 MCP 服务器。 |
|
||||
| `/yolo` | 切换 YOLO 模式——跳过所有危险命令审批提示。 |
|
||||
| `/commands [page]` | 浏览所有命令和 skill(分页)。 |
|
||||
| `/approve [session\|always]` | 审批并执行待处理的危险命令。`session` 仅为本次会话审批;`always` 添加到永久白名单。 |
|
||||
| `/deny` | 拒绝待处理的危险命令。 |
|
||||
| `/update` | 将 Hermes Agent 更新到最新版本。 |
|
||||
| `/restart` | 在排空活动运行后优雅重启 gateway。gateway 重新上线后,会向请求者的聊天/线程发送确认消息。 |
|
||||
| `/debug` | 上传调试报告(系统信息 + 日志)并获取可分享链接。 |
|
||||
| `/help` | 显示消息平台帮助。 |
|
||||
| `/<skill-name>` | 按名称调用任意已安装的 skill。 |
|
||||
|
||||
## 注意事项
|
||||
|
||||
- `/skin`、`/snapshot`、`/gquota`、`/reload`、`/tools`、`/toolsets`、`/browser`、`/config`、`/cron`、`/skills`、`/platforms`、`/paste`、`/image`、`/statusbar`、`/plugins`、`/busy`、`/indicator`、`/redraw`、`/clear`、`/history`、`/save`、`/copy`、`/handoff` 和 `/quit` 是**仅限 CLI** 的命令。
|
||||
- `/verbose` **默认仅限 CLI**,但可通过在 `config.yaml` 中设置 `display.tool_progress_command: true` 为消息平台启用。启用后,它会循环切换 `display.tool_progress` 模式并保存到配置。
|
||||
- `/sethome`、`/update`、`/restart`、`/approve`、`/deny`、`/topic` 和 `/commands` 是**仅限消息平台**的命令。
|
||||
- `/status`、`/background`、`/queue`、`/steer`、`/voice`、`/reload-mcp`、`/reload-skills`、`/rollback`、`/debug`、`/fast`、`/footer`、`/curator`、`/kanban`、`/sessions` 和 `/yolo` 在 **CLI 和消息 gateway 中均可使用**。
|
||||
- `/voice join`、`/voice channel` 和 `/voice leave` 仅在 Discord 上有意义。
|
||||
|
||||
## 破坏性命令的确认提示
|
||||
|
||||
CLI 在执行会丢弃未保存会话状态的斜杠命令前会提示确认。当前破坏性命令集为:
|
||||
|
||||
| 命令 | 销毁的内容 |
|
||||
|---------|------------------|
|
||||
| `/clear` | 清屏并开始新会话——当前会话 ID 和内存中的历史记录将丢失。 |
|
||||
| `/new` / `/reset` | 开始新会话(新会话 ID + 空历史记录)。 |
|
||||
| `/undo` | 从历史记录中移除最后一轮用户/助手对话。 |
|
||||
| `/exit --delete` / `/quit --delete` | 退出**并**永久删除当前会话的 SQLite 历史记录和磁盘上的转录文件。 |
|
||||
|
||||
对于上述每个命令,CLI 会打开一个三选项弹窗:**Approve Once**(本次执行)、**Always Approve**(执行并持久化 `approvals.destructive_slash_confirm: false`,使未来的破坏性命令无需提示直接运行),或 **Cancel**。
|
||||
|
||||
**内联跳过:** 追加 `now`、`--yes` 或 `-y` 可为单次调用绕过弹窗——例如 `/reset now`、`/new --yes my-session`、`/clear -y`、`/undo -y`。适用于弹窗在你的终端无法正常渲染的情况(见 [issue #30768](https://github.com/NousResearch/hermes-agent/issues/30768),原生 Windows PowerShell)或对 CLI 进行脚本化操作时。
|
||||
|
||||
在 `~/.hermes/config.yaml` 中设置 `approvals.destructive_slash_confirm: false` 可全局禁用提示;设置回 `true` 可重新启用。背景说明见 [安全——破坏性斜杠命令确认](../user-guide/security.md#dangerous-command-approval)。
|
||||
@ -0,0 +1,267 @@
|
||||
---
|
||||
sidebar_position: 3
|
||||
title: "内置工具参考"
|
||||
description: "Hermes 内置工具权威参考,按工具集分组"
|
||||
---
|
||||
|
||||
# 内置工具参考
|
||||
|
||||
本页记录 Hermes 的内置工具,按工具集分组。可用性因平台、凭据和已启用的工具集而异。
|
||||
|
||||
**当前注册表快速统计:** 约 70 个工具 —— 10 个浏览器工具(核心)+ 2 个 CDP 门控浏览器工具、4 个文件工具、10 个 RL 工具、4 个 Home Assistant 工具、2 个终端工具、2 个 Web 工具、5 个 Feishu 工具、7 个 Spotify 工具(由内置 `spotify` 插件注册)、5 个 Yuanbao 工具、7 个 kanban 工具(在 kanban 调度器生成 agent 时注册)、2 个 Discord 工具,以及若干独立工具(`memory`、`clarify`、`delegate_task`、`execute_code`、`cronjob`、`session_search`、`skill_view`/`skill_manage`/`skills_list`、`text_to_speech`、`image_generate`、`video_generate`、`vision_analyze`、`video_analyze`、`mixture_of_agents`、`send_message`、`todo`、`computer_use`、`process`)。
|
||||
|
||||
:::tip MCP 工具
|
||||
除内置工具外,Hermes 还可从 MCP 服务器动态加载工具。MCP 工具以 `mcp_<server>_` 为前缀(例如,`github` MCP 服务器的 `mcp_github_create_issue`)。配置方法见 [MCP 集成](/user-guide/features/mcp)。
|
||||
:::
|
||||
|
||||
## `browser` 工具集
|
||||
|
||||
| 工具 | 描述 | 所需环境 |
|
||||
|------|------|----------|
|
||||
| `browser_back` | 在浏览器历史记录中导航回上一页。需先调用 `browser_navigate`。 | — |
|
||||
| `browser_click` | 点击快照中由 ref ID 标识的元素(如 `@e5`)。ref ID 显示在快照输出的方括号中。需先调用 `browser_navigate` 和 `browser_snapshot`。 | — |
|
||||
| `browser_console` | 获取当前页面的浏览器控制台输出和 JavaScript 错误。返回 `console.log`/`warn`/`error`/`info` 消息及未捕获的 JS 异常。用于检测静默 JavaScript 错误、失败的 API 调用和应用警告。需先调用… | — |
|
||||
| `browser_get_images` | 获取当前页面所有图片的列表,包含 URL 和 alt 文本。可用于查找供 vision 工具分析的图片。需先调用 `browser_navigate`。 | — |
|
||||
| `browser_navigate` | 在浏览器中导航到某个 URL,初始化会话并加载页面。必须在其他浏览器工具之前调用。对于简单信息检索,优先使用 `web_search` 或 `web_extract`(更快、更省)。当需要… 时使用浏览器工具。 | — |
|
||||
| `browser_press` | 按下键盘按键。适用于提交表单(Enter)、导航(Tab)或键盘快捷键。需先调用 `browser_navigate`。 | — |
|
||||
| `browser_scroll` | 向某个方向滚动页面。用于显示当前视口上方或下方的更多内容。需先调用 `browser_navigate`。 | — |
|
||||
| `browser_snapshot` | 获取当前页面无障碍树的文本快照。返回带 ref ID(如 `@e1`、`@e2`)的交互元素,供 `browser_click` 和 `browser_type` 使用。`full=false`(默认):仅含交互元素的紧凑视图。`full=true`:完整… | — |
|
||||
| `browser_type` | 向由 ref ID 标识的输入框中输入文本。先清空字段,再输入新文本。需先调用 `browser_navigate` 和 `browser_snapshot`。 | — |
|
||||
| `browser_vision` | 对当前页面截图并用视觉 AI 分析。当需要直观理解页面内容时使用——尤其适用于 CAPTCHA、视觉验证挑战、复杂布局,或文本快照… 时。 | — |
|
||||
|
||||
## `browser` 工具集(CDP 门控工具)
|
||||
|
||||
这两个工具属于 `browser` 工具集,但仅在会话启动时可访问 Chrome DevTools Protocol(CDP)端点时才注册——通过 `/browser connect`、`browser.cdp_url` 配置、Browserbase 会话或 Camofox。
|
||||
|
||||
| 工具 | 描述 | 所需环境 |
|
||||
|------|------|----------|
|
||||
| `browser_cdp` | 发送原始 Chrome DevTools Protocol 命令。用于高层 `browser_*` 工具未覆盖的浏览器操作的逃生舱口。参见 https://chromedevtools.github.io/devtools-protocol/ | CDP 端点 |
|
||||
| `browser_dialog` | 响应原生 JavaScript 对话框(alert / confirm / prompt / beforeunload)。先调用 `browser_snapshot`——待处理的对话框会出现在其 `pending_dialogs` 字段中。然后调用 `browser_dialog(action='accept'\|'dismiss')`。 | CDP 端点 |
|
||||
|
||||
## `clarify` 工具集
|
||||
|
||||
| 工具 | 描述 | 所需环境 |
|
||||
|------|------|----------|
|
||||
| `clarify` | 在需要澄清、反馈或决策时向用户提问。支持两种模式:1. **多选** —— 提供最多 4 个选项,用户从中选择或通过第 5 个"其他"选项自行输入。2.… | — |
|
||||
|
||||
## `code_execution` 工具集
|
||||
|
||||
| 工具 | 描述 | 所需环境 |
|
||||
|------|------|----------|
|
||||
| `execute_code` | 运行可以编程方式调用 Hermes 工具的 Python 脚本。当需要 3 次以上工具调用且调用之间有处理逻辑、需要在大型工具输出进入上下文前过滤/压缩、需要条件分支(…)时使用。 | — |
|
||||
|
||||
## `cronjob` 工具集
|
||||
|
||||
| 工具 | 描述 | 所需环境 |
|
||||
|------|------|----------|
|
||||
| `cronjob` | 统一的定时任务管理器。使用 `action="create"`、`"list"`、`"update"`、`"pause"`、`"resume"`、`"run"` 或 `"remove"` 管理任务。支持带一个或多个附加 skill 的 skill 驱动任务,`update` 时 `skills=[]` 可清除已附加的 skill。Cron 任务在无当前聊天上下文的全新会话中运行。 | — |
|
||||
|
||||
## `delegation` 工具集
|
||||
|
||||
| 工具 | 描述 | 所需环境 |
|
||||
|------|------|----------|
|
||||
| `delegate_task` | 生成一个或多个子 agent,在隔离上下文中处理任务。每个子 agent 拥有独立的对话、终端会话和工具集。仅返回最终摘要——中间工具结果不会进入你的上下文窗口。两种… | — |
|
||||
|
||||
## `feishu_doc` 工具集
|
||||
|
||||
仅限飞书文档评论智能回复处理器(`gateway/platforms/feishu_comment.py`)使用。不在 `hermes-cli` 或常规飞书聊天适配器中暴露。
|
||||
|
||||
| 工具 | 描述 | 所需环境 |
|
||||
|------|------|----------|
|
||||
| `feishu_doc_read` | 根据 `file_type` 和 token 读取飞书/Lark 文档(Docx、Doc 或 Sheet)的完整文本内容。 | 飞书应用凭据 |
|
||||
|
||||
## `feishu_drive` 工具集
|
||||
|
||||
仅限飞书文档评论处理器使用。驱动云盘文件的评论读写操作。
|
||||
|
||||
| 工具 | 描述 | 所需环境 |
|
||||
|------|------|----------|
|
||||
| `feishu_drive_add_comment` | 在飞书/Lark 文档或文件上添加顶级评论。 | 飞书应用凭据 |
|
||||
| `feishu_drive_list_comments` | 列出飞书/Lark 文件的全文档评论,最新的排在最前。 | 飞书应用凭据 |
|
||||
| `feishu_drive_list_comment_replies` | 列出特定飞书评论线程(全文档或局部选区)的回复。 | 飞书应用凭据 |
|
||||
| `feishu_drive_reply_comment` | 在飞书评论线程上发布回复,支持可选的 `@` 提及。 | 飞书应用凭据 |
|
||||
|
||||
## `file` 工具集
|
||||
|
||||
| 工具 | 描述 | 所需环境 |
|
||||
|------|------|----------|
|
||||
| `patch` | 对文件进行精准的查找替换编辑。用于替代终端中的 `sed`/`awk`。使用模糊匹配(9 种策略),轻微的空白/缩进差异不会导致失败。返回统一差异格式。编辑后自动运行语法检查… | — |
|
||||
| `read_file` | 带行号和分页功能读取文本文件。用于替代终端中的 `cat`/`head`/`tail`。输出格式:`LINE_NUM\|CONTENT`。找不到文件时建议相似文件名。对大文件使用 `offset` 和 `limit`。注意:无法读取图片或… | — |
|
||||
| `search_files` | 搜索文件内容或按名称查找文件。用于替代终端中的 `grep`/`rg`/`find`/`ls`。基于 Ripgrep,比 shell 等效命令更快。内容搜索(`target='content'`):在文件内进行正则搜索。输出模式:带行号的完整匹配… | — |
|
||||
| `write_file` | 将内容写入文件,完全替换现有内容。用于替代终端中的 `echo`/`cat heredoc`。自动创建父目录。**覆盖整个文件** —— 精准编辑请使用 `patch`。 | — |
|
||||
|
||||
## `homeassistant` 工具集
|
||||
|
||||
| 工具 | 描述 | 所需环境 |
|
||||
|------|------|----------|
|
||||
| `ha_call_service` | 调用 Home Assistant 服务以控制设备。使用 `ha_list_services` 发现各域的可用服务及其参数。 | — |
|
||||
| `ha_get_state` | 获取单个 Home Assistant 实体的详细状态,包括所有属性(亮度、颜色、温度设定值、传感器读数等)。 | — |
|
||||
| `ha_list_entities` | 列出 Home Assistant 实体。可按域(light、switch、climate、sensor、binary_sensor、cover、fan 等)或区域名称(客厅、厨房、卧室等)过滤。 | — |
|
||||
| `ha_list_services` | 列出用于设备控制的可用 Home Assistant 服务(动作)。显示每种设备类型可执行的操作及其接受的参数。用于发现如何控制通过 `ha_list_entities` 找到的设备。 | — |
|
||||
|
||||
## `computer_use` 工具集
|
||||
|
||||
| 工具 | 描述 | 所需环境 |
|
||||
|------|------|----------|
|
||||
| `computer_use` | 通过 cua-driver 在后台控制 macOS 桌面——截图(SOM / vision / AX)、点击 / 拖拽 / 滚动 / 输入 / 按键 / 等待、`list_apps`、`focus_app`。**不会**抢占用户的光标或键盘焦点。适用于任何支持工具的模型。仅限 macOS。 | `cua-driver` 在 `$PATH` 中(通过 `hermes tools` 安装)。 |
|
||||
|
||||
:::note
|
||||
**Honcho 工具**(`honcho_profile`、`honcho_search`、`honcho_context`、`honcho_reasoning`、`honcho_conclude`)不再是内置工具。它们通过 `plugins/memory/honcho/` 的 Honcho 记忆提供者插件提供。安装和使用方法见 [Memory Providers](../user-guide/features/memory-providers.md)。
|
||||
:::
|
||||
|
||||
## `image_gen` 工具集
|
||||
|
||||
| 工具 | 描述 | 所需环境 |
|
||||
|------|------|----------|
|
||||
| `image_generate` | 使用 FAL.ai 从文本 prompt(提示词)生成高质量图片。底层模型由用户配置(默认:FLUX 2 Klein 9B,生成时间低于 1 秒),agent 不可选择。返回单个图片 URL。使用… 显示。 | FAL_KEY |
|
||||
|
||||
## `kanban` 工具集
|
||||
|
||||
在以下情况下注册:(a) agent 由 kanban 调度器生成(设置了 `HERMES_KANBAN_TASK` 环境变量),或 (b) 在显式启用 `kanban` 工具集的 profile 中运行。任务范围的 worker 使用生命周期工具处理其分配的任务;编排器 profile 还额外获得 `kanban_list` 和 `kanban_unblock` 等看板路由工具。完整工作流见 [Kanban 多 Agent](/user-guide/features/kanban)。
|
||||
|
||||
| 工具 | 描述 | 所需环境 |
|
||||
|------|------|----------|
|
||||
| `kanban_show` | 显示分配给当前 worker 的活跃 kanban 任务(标题、描述、评论、依赖项)。 | `HERMES_KANBAN_TASK` 或 `kanban` 工具集 |
|
||||
| `kanban_list` | 带过滤器列出看板任务。仅限编排器;对调度器生成的任务 worker 隐藏。 | 含 `kanban` 工具集的 profile |
|
||||
| `kanban_complete` | 用结构化交接载荷(结果、产物、后续事项)将当前任务标记为完成。 | `HERMES_KANBAN_TASK` 或 `kanban` 工具集 |
|
||||
| `kanban_block` | 因需向用户提问而阻塞当前任务——调度器暂停、呈现问题,并在人工回复后恢复。 | `HERMES_KANBAN_TASK` 或 `kanban` 工具集 |
|
||||
| `kanban_heartbeat` | 在长时间运行的操作期间发送进度心跳,让调度器知道 worker 仍在运行。 | `HERMES_KANBAN_TASK` 或 `kanban` 工具集 |
|
||||
| `kanban_comment` | 在不改变任务状态的情况下向任务线程添加评论——适用于呈现中间发现。 | `HERMES_KANBAN_TASK` 或 `kanban` 工具集 |
|
||||
| `kanban_create` | 从当前任务派生子任务。由编排器和生成后续任务的 worker 使用。 | `HERMES_KANBAN_TASK` 或 `kanban` 工具集 |
|
||||
| `kanban_link` | 用父 → 子依赖边链接任务。 | `HERMES_KANBAN_TASK` 或 `kanban` 工具集 |
|
||||
| `kanban_unblock` | 将被阻塞的任务恢复为 `ready` 状态。仅限编排器;对调度器生成的任务 worker 隐藏。 | 含 `kanban` 工具集的 profile |
|
||||
|
||||
## `memory` 工具集
|
||||
|
||||
| 工具 | 描述 | 所需环境 |
|
||||
|------|------|----------|
|
||||
| `memory` | 将重要信息保存到跨会话持久化的记忆中。你的记忆会在会话启动时出现在系统 prompt 中——这是你在对话之间记住用户信息和环境信息的方式。何时保存… | — |
|
||||
|
||||
## `messaging` 工具集
|
||||
|
||||
| 工具 | 描述 | 所需环境 |
|
||||
|------|------|----------|
|
||||
| `send_message` | 向已连接的消息平台发送消息,或列出可用目标。重要:当用户要求发送到特定频道或人员(而非仅平台名称)时,请先调用 `send_message(action='list')` 查看可用目标… | — |
|
||||
|
||||
## `moa` 工具集
|
||||
|
||||
| 工具 | 描述 | 所需环境 |
|
||||
|------|------|----------|
|
||||
| `mixture_of_agents` | 将难题路由给多个前沿 LLM 协作处理。进行 5 次 API 调用(4 个参考模型 + 1 个聚合器),以最大推理力度运行——请谨慎用于真正困难的问题。最适合:复杂数学、高级算法… | OPENROUTER_API_KEY |
|
||||
|
||||
## `session_search` 工具集
|
||||
|
||||
| 工具 | 描述 | 所需环境 |
|
||||
|------|------|----------|
|
||||
| `session_search` | 搜索存储在本地会话数据库中的历史会话,或在某个会话内滚动浏览。基于 FTS5 检索;返回数据库中的实际消息(无 LLM 调用)。三种形态:发现(传入 `query`)、滚动(传入 `session_id` + `around_message_id`)、浏览(无参数)。 | — |
|
||||
|
||||
## `skills` 工具集
|
||||
|
||||
| 工具 | 描述 | 所需环境 |
|
||||
|------|------|----------|
|
||||
| `skill_manage` | 管理 skill(创建、更新、删除)。Skill 是你的程序性记忆——针对重复任务类型的可复用方法。新 skill 保存到 `~/.hermes/skills/`;现有 skill 可在其所在位置修改。操作:create(完整 SKILL.m…) | — |
|
||||
| `skill_view` | Skill 允许加载特定任务和工作流的信息,以及脚本和模板。加载某个 skill 的完整内容或访问其链接文件(参考资料、模板、脚本)。首次调用返回 SKILL.md 内容及… | — |
|
||||
| `skills_list` | 列出可用 skill(名称 + 描述)。使用 `skill_view(name)` 加载完整内容。 | — |
|
||||
|
||||
## `terminal` 工具集
|
||||
|
||||
| 工具 | 描述 | 所需环境 |
|
||||
|------|------|----------|
|
||||
| `process` | 管理通过 `terminal(background=true)` 启动的后台进程。操作:`list`(显示所有)、`poll`(检查状态 + 新输出)、`log`(带分页的完整输出)、`wait`(阻塞直到完成或超时)、`kill`(终止)、`write`(发送…) | — |
|
||||
| `terminal` | 在 Linux 环境中执行 shell 命令。文件系统在调用之间持久化。对长时间运行的服务器设置 `background=true`。设置 `notify_on_complete=true`(配合 `background=true`)可在进程完成时自动收到通知——无需轮询。**不要**使用 `cat`/`head`/`tail`——使用 `read_file`。**不要**使用 `grep`/`rg`/`find`——使用 `search_files`。 | — |
|
||||
|
||||
## `todo` 工具集
|
||||
|
||||
| 工具 | 描述 | 所需环境 |
|
||||
|------|------|----------|
|
||||
| `todo` | 管理当前会话的任务列表。适用于包含 3 个以上步骤的复杂任务,或用户提供多个任务时。不带参数调用可读取当前列表。写入:- 提供 `todos` 数组以创建/更新条目 - `merge=`… | — |
|
||||
|
||||
## `vision` 工具集
|
||||
|
||||
| 工具 | 描述 | 所需环境 |
|
||||
|------|------|----------|
|
||||
| `vision_analyze` | 使用 AI 视觉分析图片。在支持视觉的主模型上,将原始图片像素作为多模态工具结果返回,使模型在下一轮能原生看到图片。在纯文本主模型上,回退到辅助视觉模型描述图片并以文本形式返回描述。两种情况下工具签名完全相同。 | — |
|
||||
|
||||
## `video` 工具集
|
||||
|
||||
可选工具集(默认 `hermes-cli` 集中不加载)。通过 `--toolsets video` 添加,或在 `toolsets:` 配置中包含 `video`。
|
||||
|
||||
| 工具 | 描述 | 所需环境 |
|
||||
|------|------|----------|
|
||||
| `video_analyze` | 分析来自 URL 或文件路径的视频内容——字幕、场景分解、关键时间戳和视觉描述。 | — |
|
||||
|
||||
## `video_gen` 工具集
|
||||
|
||||
可选工具集(默认 `hermes-cli` 集中不加载)。通过 `--toolsets video_gen` 添加,或在 `hermes tools` → Video Generation 中启用(同时引导你选择后端)。
|
||||
|
||||
后端以插件形式存放于 `plugins/video_gen/<name>/`:
|
||||
|
||||
- **xAI Grok-Imagine** —— 文本生成视频和图片生成视频(SuperGrok OAuth 或 `XAI_API_KEY`)。
|
||||
- **FAL.ai** —— Veo 3.1、Pixverse v6、Kling O3(需要 `FAL_KEY`)。
|
||||
|
||||
单个 `video_generate` 工具涵盖两种模态——传入 `image_url` 可为静态图片制作动画,省略则从文本生成。活跃后端自动路由到正确的端点。工具描述在会话启动时重建,以反映活跃后端的实际能力(模态、宽高比、分辨率、时长范围、最大参考图片数、音频支持)。后端开发见 [视频生成提供者插件](/developer-guide/video-gen-provider-plugin)。
|
||||
|
||||
| 工具 | 描述 | 所需环境 |
|
||||
|------|------|----------|
|
||||
| `video_generate` | 使用用户配置的视频生成后端,从文本 prompt 生成视频(文本生成视频)或为静态图片制作动画(图片生成视频)。传入 `image_url` 可为该图片制作动画;省略则从文本生成。后端自动路由到正确端点。在 `video` 字段中返回 HTTP URL 或绝对文件路径。 | 活跃的 `video_gen` 插件 + 其凭据(如 `XAI_API_KEY`、`FAL_KEY`) |
|
||||
|
||||
## `web` 工具集
|
||||
|
||||
| 工具 | 描述 | 所需环境 |
|
||||
|------|------|----------|
|
||||
| `web_search` | 在网络上搜索信息。默认返回最多 5 条结果,包含标题、URL 和描述。接受可选的 `limit`(1-100,默认 5)。查询直接传递给配置的后端,因此当后端支持时,`site:domain`、`filetype:pdf`、`intitle:word`、`-term`、`"exact phrase"` 等运算符可能有效。 | EXA_API_KEY 或 PARALLEL_API_KEY 或 FIRECRAWL_API_KEY 或 TAVILY_API_KEY |
|
||||
| `web_extract` | 从网页 URL 提取内容。以 Markdown 格式返回页面内容。也支持 PDF URL——直接传入 PDF 链接即可转换为 Markdown 文本。5000 字符以下的页面返回完整 Markdown;更大的页面由 LLM 摘要处理。 | EXA_API_KEY 或 PARALLEL_API_KEY 或 FIRECRAWL_API_KEY 或 TAVILY_API_KEY |
|
||||
|
||||
## `x_search` 工具集
|
||||
|
||||
| 工具 | 描述 | 所需环境 |
|
||||
|------|------|----------|
|
||||
| `x_search` | 使用 xAI 内置的 `x_search` Responses 工具搜索 X(Twitter)帖子、主页和话题串。用于获取 X 上的当前讨论、反应或观点,而非通用网页。默认关闭——通过 `hermes tools` → 🐦 X (Twitter) Search 选择启用。仅在配置了 xAI 凭据时注册 schema(check_fn 门控)。 | XAI_API_KEY **或** xAI Grok OAuth(SuperGrok / Premium+)登录 |
|
||||
|
||||
## `tts` 工具集
|
||||
|
||||
| 工具 | 描述 | 所需环境 |
|
||||
|------|------|----------|
|
||||
| `text_to_speech` | 将文本转换为语音音频。返回平台以语音消息形式传递的 `MEDIA:` 路径。在 Telegram 上以语音气泡播放,在 Discord/WhatsApp 上作为音频附件。在 CLI 模式下保存到 `~/voice-memos/`。语音和提供者… | — |
|
||||
|
||||
## `discord` 工具集
|
||||
|
||||
在 `hermes-discord` 平台工具集(仅 gateway)上注册。使用与消息适配器相同的 bot token。
|
||||
|
||||
| 工具 | 描述 | 所需环境 |
|
||||
|------|------|----------|
|
||||
| `discord` | 读取并参与 Discord 服务器。操作包括 `search_members`、`fetch_messages`、`send_message`、`react`、`fetch_channel`、`list_channels` 等。 | `DISCORD_BOT_TOKEN` |
|
||||
|
||||
## `discord_admin` 工具集
|
||||
|
||||
在 `hermes-discord` 平台工具集上注册。审核操作需要 bot 持有相应的 Discord 权限。
|
||||
|
||||
| 工具 | 描述 | 所需环境 |
|
||||
|------|------|----------|
|
||||
| `discord_admin` | 通过 REST API 管理 Discord 服务器:列出 guild/频道/角色,创建/编辑/删除频道,管理角色授予、禁言、踢出和封禁。 | `DISCORD_BOT_TOKEN` + bot 权限 |
|
||||
|
||||
## `spotify` 工具集
|
||||
|
||||
由内置 `spotify` 插件注册。需要 OAuth token——运行一次 `hermes spotify setup` 进行授权。
|
||||
|
||||
| 工具 | 描述 | 所需环境 |
|
||||
|------|------|----------|
|
||||
| `spotify_playback` | 控制 Spotify 播放、查看当前播放状态或获取最近播放的曲目。 | Spotify OAuth |
|
||||
| `spotify_devices` | 列出 Spotify Connect 设备或将播放转移到其他设备。 | Spotify OAuth |
|
||||
| `spotify_queue` | 查看用户的 Spotify 队列或向其添加项目。 | Spotify OAuth |
|
||||
| `spotify_search` | 在 Spotify 目录中搜索曲目、专辑、艺术家、播放列表、节目或单集。 | Spotify OAuth |
|
||||
| `spotify_playlists` | 列出、查看、创建、更新和修改 Spotify 播放列表。 | Spotify OAuth |
|
||||
| `spotify_albums` | 获取 Spotify 专辑元数据或专辑曲目。 | Spotify OAuth |
|
||||
| `spotify_library` | 列出、保存或移除用户已保存的 Spotify 曲目或专辑。 | Spotify OAuth |
|
||||
|
||||
## `hermes-yuanbao` 工具集
|
||||
|
||||
仅在 `hermes-yuanbao` 平台工具集上注册。元宝是腾讯的聊天应用;这些工具驱动其私信/群组/表情包 API。
|
||||
|
||||
| 工具 | 描述 | 所需环境 |
|
||||
|------|------|----------|
|
||||
| `yb_query_group_info` | 查询群组(应用内称为"派/Pai")的基本信息:名称、群主、成员数。 | 元宝凭据 |
|
||||
| `yb_query_group_members` | 查询群组成员(用于 `@` 提及、按名称查找用户、列出机器人)。 | 元宝凭据 |
|
||||
| `yb_send_dm` | 向群组中的用户发送私信,支持可选的媒体文件。 | 元宝凭据 |
|
||||
| `yb_search_sticker` | 按关键词搜索元宝内置表情(TIM 表情)目录。 | 元宝凭据 |
|
||||
| `yb_send_sticker` | 向当前元宝聊天发送内置表情。 | 元宝凭据 |
|
||||
@ -0,0 +1,163 @@
|
||||
---
|
||||
sidebar_position: 4
|
||||
title: "工具集参考"
|
||||
description: "Hermes 核心、复合、平台及动态工具集参考"
|
||||
---
|
||||
|
||||
# 工具集参考
|
||||
|
||||
工具集(Toolset)是工具的命名集合,用于控制 agent 可以执行的操作。它是按平台、按会话或按任务配置工具可用性的主要机制。
|
||||
|
||||
## 工具集的工作原理
|
||||
|
||||
每个工具恰好属于一个工具集。启用某个工具集后,该集合中的所有工具都将对 agent 可用。工具集分为三种类型:
|
||||
|
||||
- **核心(Core)** — 一组相关工具的逻辑分组(例如,`file` 包含 `read_file`、`write_file`、`patch`、`search_files`)
|
||||
- **复合(Composite)** — 将多个核心工具集组合用于常见场景(例如,`debugging` 包含 file、terminal 和 web 工具)
|
||||
- **平台(Platform)** — 针对特定部署环境的完整工具配置(例如,`hermes-cli` 是交互式 CLI 会话的默认配置)
|
||||
|
||||
## 配置工具集
|
||||
|
||||
### 按会话(CLI)
|
||||
|
||||
```bash
|
||||
hermes chat --toolsets web,file,terminal
|
||||
hermes chat --toolsets debugging # composite — expands to file + terminal + web
|
||||
hermes chat --toolsets all # everything
|
||||
```
|
||||
|
||||
### 按平台(config.yaml)
|
||||
|
||||
```yaml
|
||||
toolsets:
|
||||
- hermes-cli # default for CLI
|
||||
# - hermes-telegram # override for Telegram gateway
|
||||
```
|
||||
|
||||
### 交互式管理
|
||||
|
||||
```bash
|
||||
hermes tools # curses UI to enable/disable per platform
|
||||
```
|
||||
|
||||
或在会话中:
|
||||
|
||||
```
|
||||
/tools list
|
||||
/tools disable browser
|
||||
/tools enable homeassistant
|
||||
```
|
||||
|
||||
## 核心工具集
|
||||
|
||||
| 工具集 | 工具 | 用途 |
|
||||
|--------|------|------|
|
||||
| `browser` | `browser_back`, `browser_cdp`, `browser_click`, `browser_console`, `browser_dialog`, `browser_get_images`, `browser_navigate`, `browser_press`, `browser_scroll`, `browser_snapshot`, `browser_type`, `browser_vision`, `web_search` | 核心浏览器自动化。包含 `web_search` 作为快速查询的备用方案。`browser_cdp` 和 `browser_dialog` 在运行时受限——仅在会话启动时 CDP 端点可达(通过 `/browser connect`、`browser.cdp_url` 配置、Browserbase 或 Camofox)时才注册。`browser_dialog` 与 `browser_snapshot` 在附加 CDP supervisor 时添加的 `pending_dialogs` 和 `frame_tree` 字段配合使用。 |
|
||||
| `clarify` | `clarify` | 当 agent 需要澄清时向用户提问。 |
|
||||
| `code_execution` | `execute_code` | 运行以编程方式调用 Hermes 工具的 Python 脚本。 |
|
||||
| `cronjob` | `cronjob` | 调度和管理周期性任务。 |
|
||||
| `debugging` | 复合(`file` + `terminal` + `web`) | 调试套件——文件、进程/终端、网页提取/搜索。 |
|
||||
| `delegation` | `delegate_task` | 生成隔离的子 agent 实例以并行执行工作。 |
|
||||
| `discord` | `discord` | 核心 Discord 文本/嵌入/私信操作(仅限 gateway)。在 `hermes-discord` 工具集上激活。 |
|
||||
| `discord_admin` | `discord_admin` | Discord 管理操作(封禁、角色变更、频道管理)。在 `hermes-discord` 工具集上激活;需要 bot 持有相关 Discord 权限。 |
|
||||
| `feishu_doc` | `feishu_doc_read` | 读取飞书/Lark 文档内容。由飞书文档评论智能回复处理器使用。 |
|
||||
| `feishu_drive` | `feishu_drive_add_comment`, `feishu_drive_list_comments`, `feishu_drive_list_comment_replies`, `feishu_drive_reply_comment` | 飞书/Lark 云盘评论操作。仅限评论 agent 使用;不在 `hermes-cli` 或其他消息工具集上暴露。 |
|
||||
| `file` | `patch`, `read_file`, `search_files`, `write_file` | 文件读取、写入、搜索和编辑。 |
|
||||
| `homeassistant` | `ha_call_service`, `ha_get_state`, `ha_list_entities`, `ha_list_services` | 通过 Home Assistant 进行智能家居控制。仅在设置 `HASS_TOKEN` 时可用。 |
|
||||
| `computer_use` | `computer_use` | 通过 cua-driver 进行后台 macOS 桌面控制——不抢占光标/焦点。适用于任何支持工具调用的模型。仅限 macOS;需要 `cua-driver` 在 `$PATH` 中。 |
|
||||
| `image_gen` | `image_generate` | 通过 FAL.ai 进行文本生成图像(支持可选的 OpenAI / xAI 后端)。 |
|
||||
| `video_gen` | `video_generate` | 通过插件注册的后端(xAI Grok-Imagine、FAL.ai Veo 3.1 / Pixverse v6 / Kling O3)进行文本生成视频和图像生成视频。传入 `image_url` 可对图像进行动画化;省略则为文本生成视频。 |
|
||||
| `kanban` | `kanban_block`, `kanban_comment`, `kanban_complete`, `kanban_create`, `kanban_heartbeat`, `kanban_link`, `kanban_list`, `kanban_show`, `kanban_unblock` | 多 agent 协调工具。为调度器生成的任务工作者(`HERMES_KANBAN_TASK`)以及显式启用 `kanban` 工具集的 profile 注册。工作者可标记任务完成、阻塞、心跳、评论以及创建/关联后续任务;编排器 profile 还额外获得看板路由工具,如 list/unblock。 |
|
||||
| `memory` | `memory` | 持久化跨会话记忆管理。 |
|
||||
| `messaging` | `send_message` | 在会话中向其他平台(Telegram、Discord 等)发送消息。 |
|
||||
| `moa` | `mixture_of_agents` | 通过 Mixture of Agents 实现多模型共识。 |
|
||||
| `safe` | `image_generate`, `vision_analyze`, `web_extract`, `web_search`(通过 `includes`) | 只读研究 + 媒体生成。无文件写入、无终端、无代码执行。 |
|
||||
| `search` | `web_search` | 仅网页搜索(不含提取)。 |
|
||||
| `session_search` | `session_search` | 搜索历史会话记录。 |
|
||||
| `skills` | `skill_manage`, `skill_view`, `skills_list` | 技能的增删改查与浏览。 |
|
||||
| `spotify` | `spotify_albums`, `spotify_devices`, `spotify_library`, `spotify_playback`, `spotify_playlists`, `spotify_queue`, `spotify_search` | 原生 Spotify 控制(播放、队列、搜索、播放列表、专辑、音乐库)。由内置 `spotify` 插件注册。 |
|
||||
| `terminal` | `process`, `terminal` | Shell 命令执行和后台进程管理。 |
|
||||
| `todo` | `todo` | 会话内任务列表管理。 |
|
||||
| `tts` | `text_to_speech` | 文本转语音音频生成。 |
|
||||
| `vision` | `vision_analyze` | 通过视觉能力模型进行图像分析。 |
|
||||
| `video` | `video_analyze` | 视频分析与理解工具(需手动启用,不在默认工具集中——通过 `--toolsets` 显式添加)。 |
|
||||
| `web` | `web_extract`, `web_search` | 网页搜索和页面内容提取。 |
|
||||
| `x_search` | `x_search` | 通过 xAI 内置的 `x_search` Responses 工具搜索 X(Twitter)帖子和话题。默认关闭;通过 `hermes tools` 启用。仅在配置了 xAI 凭据(SuperGrok OAuth 或 `XAI_API_KEY`)时注册 schema。 |
|
||||
| `yuanbao` | `yb_query_group_info`, `yb_query_group_members`, `yb_search_sticker`, `yb_send_dm`, `yb_send_sticker` | 元宝私信/群组操作和表情包搜索。仅在 `hermes-yuanbao` 上注册。 |
|
||||
|
||||
## 平台工具集
|
||||
|
||||
平台工具集定义了部署目标的完整工具配置。大多数消息平台使用与 `hermes-cli` 相同的配置:
|
||||
|
||||
| 工具集 | 与 `hermes-cli` 的差异 |
|
||||
|--------|------------------------|
|
||||
| `hermes-cli` | 完整工具集——交互式 CLI 会话的默认配置。包含 file、terminal、web、browser、memory、skills、vision、image_gen、todo、tts、delegation、code_execution、cronjob、session_search、clarify 和 `safe`(只读)套件,以及标准消息工具。 |
|
||||
| `hermes-acp` | 移除了 `clarify`、`cronjob`、`image_generate`、`send_message`、`text_to_speech` 以及全部四个 Home Assistant 工具。专注于 IDE 环境中的编码任务。 |
|
||||
| `hermes-api-server` | 移除了 `clarify`、`send_message` 和 `text_to_speech`。保留其他所有工具——适用于无法进行用户交互的程序化访问场景。 |
|
||||
| `hermes-cron` | 与 `hermes-cli` 相同。 |
|
||||
| `hermes-telegram` | 与 `hermes-cli` 相同。 |
|
||||
| `hermes-discord` | 在 `hermes-cli` 基础上添加了 `discord` 和 `discord_admin`。 |
|
||||
| `hermes-slack` | 与 `hermes-cli` 相同。 |
|
||||
| `hermes-whatsapp` | 与 `hermes-cli` 相同。 |
|
||||
| `hermes-signal` | 与 `hermes-cli` 相同。 |
|
||||
| `hermes-matrix` | 与 `hermes-cli` 相同。 |
|
||||
| `hermes-mattermost` | 与 `hermes-cli` 相同。 |
|
||||
| `hermes-email` | 与 `hermes-cli` 相同。 |
|
||||
| `hermes-sms` | 与 `hermes-cli` 相同。 |
|
||||
| `hermes-bluebubbles` | 与 `hermes-cli` 相同。 |
|
||||
| `hermes-dingtalk` | 与 `hermes-cli` 相同。 |
|
||||
| `hermes-feishu` | 添加了五个 `feishu_doc_*` / `feishu_drive_*` 工具(仅由文档评论处理器使用,不用于常规聊天适配器)。 |
|
||||
| `hermes-qqbot` | 与 `hermes-cli` 相同。 |
|
||||
| `hermes-wecom` | 与 `hermes-cli` 相同。 |
|
||||
| `hermes-wecom-callback` | 与 `hermes-cli` 相同。 |
|
||||
| `hermes-weixin` | 与 `hermes-cli` 相同。 |
|
||||
| `hermes-yuanbao` | 在 `hermes-cli` 基础上添加了五个 `yb_*` 工具(私信/群组/表情包)。 |
|
||||
| `hermes-homeassistant` | 与 `hermes-cli` 相同(Home Assistant 工具默认已存在,在设置 `HASS_TOKEN` 时激活)。 |
|
||||
| `hermes-webhook` | 与 `hermes-cli` 相同。 |
|
||||
| `hermes-gateway` | 内部 gateway 编排器工具集——所有 `hermes-<platform>` 工具集的并集;当 gateway 需要接受任意消息来源时使用。 |
|
||||
|
||||
## 动态工具集
|
||||
|
||||
### MCP server 工具集
|
||||
|
||||
每个已配置的 MCP server 在运行时会生成一个 `mcp-<server>` 工具集。例如,若配置了 `github` MCP server,则会创建包含该 server 所有暴露工具的 `mcp-github` 工具集。
|
||||
|
||||
```yaml
|
||||
# config.yaml
|
||||
mcp_servers:
|
||||
github:
|
||||
command: npx
|
||||
args: ["-y", "@modelcontextprotocol/server-github"]
|
||||
```
|
||||
|
||||
这将创建一个 `mcp-github` 工具集,可在 `--toolsets` 或平台配置中引用。
|
||||
|
||||
### 插件工具集
|
||||
|
||||
插件可在初始化期间通过 `ctx.register_tool()` 注册自己的工具集。这些工具集与内置工具集并列显示,可以用相同方式启用/禁用。
|
||||
|
||||
### 自定义工具集
|
||||
|
||||
在 `config.yaml` 中定义自定义工具集,以创建项目专属的工具集合:
|
||||
|
||||
```yaml
|
||||
toolsets:
|
||||
- hermes-cli
|
||||
custom_toolsets:
|
||||
data-science:
|
||||
- file
|
||||
- terminal
|
||||
- code_execution
|
||||
- web
|
||||
- vision
|
||||
```
|
||||
|
||||
### 通配符
|
||||
|
||||
- `all` 或 `*` — 展开为所有已注册的工具集(内置 + 动态 + 插件)
|
||||
|
||||
## 与 `hermes tools` 的关系
|
||||
|
||||
`hermes tools` 命令提供基于 curses 的 UI,用于按平台切换单个工具的启用/禁用状态。该操作在工具级别进行(比工具集更细粒度),并持久化到 `config.yaml`。即使工具集已启用,被禁用的工具也会被过滤掉。
|
||||
|
||||
另请参阅:[工具参考](./tools-reference.md),获取所有单个工具及其参数的完整列表。
|
||||
@ -0,0 +1,249 @@
|
||||
---
|
||||
sidebar_position: 8
|
||||
sidebar_label: "Checkpoints & Rollback"
|
||||
title: "检查点与 /rollback"
|
||||
description: "使用影子 git 仓库和自动快照为破坏性操作提供文件系统安全保障"
|
||||
---
|
||||
|
||||
# 检查点与 `/rollback`
|
||||
|
||||
Hermes Agent 可以在**破坏性操作**之前自动为你的项目创建快照,并通过单条命令恢复。检查点在 v2 中为**按需启用**——大多数用户从不使用 `/rollback`,且影子存储(shadow-store)随时间增长不可忽视,因此默认关闭。
|
||||
|
||||
在会话中通过 `--checkpoints` 启用检查点:
|
||||
|
||||
```bash
|
||||
hermes chat --checkpoints
|
||||
```
|
||||
|
||||
或在 `~/.hermes/config.yaml` 中全局启用:
|
||||
|
||||
```yaml
|
||||
checkpoints:
|
||||
enabled: true
|
||||
```
|
||||
|
||||
此安全机制由内部**检查点管理器(Checkpoint Manager)**驱动,它在 `~/.hermes/checkpoints/store/` 下维护一个共享的影子 git 仓库——你真实项目的 `.git` 永远不会被触碰。Agent 操作的所有项目共享同一个存储,因此 git 的内容寻址对象数据库可以跨项目、跨轮次去重。
|
||||
|
||||
## 触发检查点的条件
|
||||
|
||||
检查点在以下操作之前自动创建:
|
||||
|
||||
- **文件工具** — `write_file` 和 `patch`
|
||||
- **破坏性终端命令** — `rm`、`rmdir`、`cp`、`install`、`mv`、`sed -i`、`truncate`、`dd`、`shred`、输出重定向(`>`),以及 `git reset`/`clean`/`checkout`
|
||||
|
||||
Agent 每个目录每轮**最多创建一个检查点**,因此长时间运行的会话不会产生大量快照。
|
||||
|
||||
## 快速参考
|
||||
|
||||
会话内斜杠命令:
|
||||
|
||||
| 命令 | 说明 |
|
||||
|---------|-------------|
|
||||
| `/rollback` | 列出所有检查点及变更统计 |
|
||||
| `/rollback <N>` | 恢复到检查点 N(同时撤销最后一轮对话) |
|
||||
| `/rollback diff <N>` | 预览检查点 N 与当前状态的差异 |
|
||||
| `/rollback <N> <file>` | 从检查点 N 恢复单个文件 |
|
||||
|
||||
在会话外检查和管理存储的 CLI 命令:
|
||||
|
||||
| 命令 | 说明 |
|
||||
|---------|-------------|
|
||||
| `hermes checkpoints` | 显示总大小、项目数量及各项目明细 |
|
||||
| `hermes checkpoints status` | 与裸 `checkpoints` 相同 |
|
||||
| `hermes checkpoints list` | `status` 的别名 |
|
||||
| `hermes checkpoints prune` | 强制执行清理:删除孤立/过期条目、GC、强制大小上限 |
|
||||
| `hermes checkpoints clear` | 清除整个检查点库(会先询问确认) |
|
||||
| `hermes checkpoints clear-legacy` | 仅删除 v1 迁移留下的 `legacy-*` 归档 |
|
||||
|
||||
## 检查点的工作原理
|
||||
|
||||
概要流程:
|
||||
|
||||
- Hermes 检测到工具即将**修改**工作树中的文件。
|
||||
- 每轮对话(每个目录)执行一次:
|
||||
- 为该文件解析合理的项目根目录。
|
||||
- 初始化或复用位于 `~/.hermes/checkpoints/store/` 的**单一共享影子存储**。
|
||||
- 写入每个项目的索引,构建树对象,并提交到每个项目的引用(`refs/hermes/<project-hash>`)。
|
||||
- 这些每项目引用构成可通过 `/rollback` 检查和恢复的检查点历史。
|
||||
|
||||
```mermaid
|
||||
flowchart LR
|
||||
user["User command\n(hermes, gateway)"]
|
||||
agent["AIAgent\n(run_agent.py)"]
|
||||
tools["File & terminal tools"]
|
||||
cpMgr["CheckpointManager"]
|
||||
store["Shared shadow store\n~/.hermes/checkpoints/store/"]
|
||||
|
||||
user --> agent
|
||||
agent -->|"tool call"| tools
|
||||
tools -->|"before mutate\nensure_checkpoint()"| cpMgr
|
||||
cpMgr -->|"git add/commit-tree/update-ref"| store
|
||||
cpMgr -->|"OK / skipped"| tools
|
||||
tools -->|"apply changes"| agent
|
||||
```
|
||||
|
||||
## 配置
|
||||
|
||||
在 `~/.hermes/config.yaml` 中配置:
|
||||
|
||||
```yaml
|
||||
checkpoints:
|
||||
enabled: false # 主开关(默认:false — 按需启用)
|
||||
max_snapshots: 20 # 每个项目的最大检查点数(通过引用重写 + gc 强制执行)
|
||||
max_total_size_mb: 500 # 存储总大小硬上限;超出时丢弃最旧的提交
|
||||
max_file_size_mb: 10 # 跳过大于此值的单个文件
|
||||
|
||||
# 自动维护(默认开启):启动时扫描 ~/.hermes/checkpoints/,
|
||||
# 删除工作目录已不存在的项目条目(孤立项)或 last_touch 超过
|
||||
# retention_days 的条目。通过 .last_prune 标记控制,
|
||||
# 最多每 min_interval_hours 运行一次。
|
||||
auto_prune: true
|
||||
retention_days: 7
|
||||
delete_orphans: true
|
||||
min_interval_hours: 24
|
||||
```
|
||||
|
||||
完全禁用:
|
||||
|
||||
```yaml
|
||||
checkpoints:
|
||||
enabled: false
|
||||
auto_prune: false
|
||||
```
|
||||
|
||||
当 `enabled: false` 时,检查点管理器为空操作,不会尝试任何 git 操作。当 `auto_prune: false` 时,存储持续增长,直到你手动运行 `hermes checkpoints prune`。
|
||||
|
||||
## 列出检查点
|
||||
|
||||
在 CLI 会话中:
|
||||
|
||||
```
|
||||
/rollback
|
||||
```
|
||||
|
||||
Hermes 返回带有变更统计的格式化列表:
|
||||
|
||||
```text
|
||||
📸 Checkpoints for /path/to/project:
|
||||
|
||||
1. 4270a8c 2026-03-16 04:36 before patch (1 file, +1/-0)
|
||||
2. eaf4c1f 2026-03-16 04:35 before write_file
|
||||
3. b3f9d2e 2026-03-16 04:34 before terminal: sed -i s/old/new/ config.py (1 file, +1/-1)
|
||||
|
||||
/rollback <N> restore to checkpoint N
|
||||
/rollback diff <N> preview changes since checkpoint N
|
||||
/rollback <N> <file> restore a single file from checkpoint N
|
||||
```
|
||||
|
||||
## 从 Shell 检查存储
|
||||
|
||||
```bash
|
||||
hermes checkpoints
|
||||
```
|
||||
|
||||
示例输出:
|
||||
|
||||
```text
|
||||
Checkpoint base: /home/you/.hermes/checkpoints
|
||||
Total size: 142.3 MB
|
||||
store/ 138.1 MB
|
||||
legacy-* 4.2 MB
|
||||
Projects: 12
|
||||
|
||||
WORKDIR COMMITS LAST TOUCH STATE
|
||||
/home/you/code/hermes-agent 20 2h ago live
|
||||
/home/you/code/experiments/rl-runner 8 1d ago live
|
||||
/home/you/code/old-prototype 3 9d ago orphan
|
||||
...
|
||||
|
||||
Legacy archives (1):
|
||||
legacy-20260506-050616 4.2 MB
|
||||
|
||||
Clear with: hermes checkpoints clear-legacy
|
||||
```
|
||||
|
||||
强制执行完整清理(忽略 24h 幂等性标记):
|
||||
|
||||
```bash
|
||||
hermes checkpoints prune --retention-days 3 --max-size-mb 200
|
||||
```
|
||||
|
||||
## 使用 `/rollback diff` 预览变更
|
||||
|
||||
在执行恢复之前,预览自某个检查点以来的变更:
|
||||
|
||||
```
|
||||
/rollback diff 1
|
||||
```
|
||||
|
||||
此命令显示 git diff 统计摘要,随后是完整差异内容。
|
||||
|
||||
## 使用 `/rollback` 恢复
|
||||
|
||||
```
|
||||
/rollback 1
|
||||
```
|
||||
|
||||
Hermes 在后台执行:
|
||||
|
||||
1. 验证目标提交存在于影子存储中。
|
||||
2. 对当前状态创建**回滚前快照**,以便之后可以"撤销撤销"。
|
||||
3. 恢复工作目录中被跟踪的文件。
|
||||
4. **撤销最后一轮对话**,使 Agent 的上下文与恢复后的文件系统状态一致。
|
||||
|
||||
## 单文件恢复
|
||||
|
||||
从检查点恢复单个文件,不影响目录中的其他内容:
|
||||
|
||||
```
|
||||
/rollback 1 src/broken_file.py
|
||||
```
|
||||
|
||||
## 安全与性能保障
|
||||
|
||||
- **Git 可用性** — 若 `PATH` 中找不到 `git`,检查点功能将透明地禁用。
|
||||
- **目录范围** — Hermes 跳过过于宽泛的目录(根目录 `/`、家目录 `$HOME`)。
|
||||
- **仓库大小** — 超过 50,000 个文件的目录将被跳过。
|
||||
- **单文件大小上限** — 大于 `max_file_size_mb`(默认 10 MB)的文件不纳入快照,防止意外将数据集、模型权重或生成的媒体文件纳入存储。
|
||||
- **存储总大小上限** — 当存储超过 `max_total_size_mb`(默认 500 MB)时,按轮询方式丢弃每个项目最旧的提交,直到低于上限。
|
||||
- **真实剪枝** — `max_snapshots` 通过重写每项目引用并随后运行 `git gc --prune=now` 来强制执行,避免松散对象积累。
|
||||
- **无变更快照** — 若自上次快照以来没有变更,则跳过本次检查点。
|
||||
- **非致命错误** — 检查点管理器内部的所有错误均以 debug 级别记录;工具继续正常运行。
|
||||
|
||||
## 检查点的存储位置
|
||||
|
||||
```text
|
||||
~/.hermes/checkpoints/
|
||||
├── store/ # 单一共享裸 git 仓库
|
||||
│ ├── HEAD, objects/ # git 内部结构(跨项目共享)
|
||||
│ ├── refs/hermes/<hash> # 每项目分支尖端
|
||||
│ ├── indexes/<hash> # 每项目 git 索引
|
||||
│ ├── projects/<hash>.json # workdir + created_at + last_touch
|
||||
│ └── info/exclude
|
||||
├── .last_prune # 自动剪枝幂等性标记
|
||||
└── legacy-<ts>/ # 归档的 v2 之前每项目影子仓库
|
||||
```
|
||||
|
||||
每个 `<hash>` 由工作目录的绝对路径派生。通常无需手动操作这些文件——使用 `hermes checkpoints status` / `prune` / `clear` 即可。
|
||||
|
||||
### 从 v1 迁移
|
||||
|
||||
在 v2 重写之前,每个工作目录在 `~/.hermes/checkpoints/<hash>/` 下拥有独立的完整影子 git 仓库。该布局无法跨项目去重对象,且剪枝器有已知的空操作问题——存储会无限增长。
|
||||
|
||||
首次运行 v2 时,所有 v2 之前的影子仓库将被移入 `~/.hermes/checkpoints/legacy-<timestamp>/`,使新的单存储布局从干净状态开始。旧的 `/rollback` 历史仍可通过 `git` 手动检查 legacy 归档访问;确认不再需要后,运行:
|
||||
|
||||
```bash
|
||||
hermes checkpoints clear-legacy
|
||||
```
|
||||
|
||||
以回收空间。Legacy 归档也会在 `retention_days` 到期后由 `auto_prune` 清理。
|
||||
|
||||
## 最佳实践
|
||||
|
||||
- **仅在需要时启用检查点** — 使用 `hermes chat --checkpoints` 或在配置文件中设置 `enabled: true`。
|
||||
- **恢复前使用 `/rollback diff` 预览** — 查看将发生的变更,选择正确的检查点。
|
||||
- **使用 `/rollback` 而非 `git reset`** 来撤销 Agent 驱动的变更。
|
||||
- **定期检查 `hermes checkpoints status`**(如果你经常使用检查点)——显示哪些项目处于活跃状态以及存储占用情况。
|
||||
- **结合 Git worktree 使用以获得最高安全性** — 将每个 Hermes 会话保持在独立的 worktree/分支中,以检查点作为额外保障层。
|
||||
|
||||
关于在同一仓库中并行运行多个 Agent,请参阅 [Git worktrees](./git-worktrees.md) 指南。
|
||||
@ -0,0 +1,440 @@
|
||||
---
|
||||
sidebar_position: 1
|
||||
title: "CLI 界面"
|
||||
description: "掌握 Hermes Agent 终端界面——命令、快捷键、人格设定等"
|
||||
---
|
||||
|
||||
# CLI 界面
|
||||
|
||||
Hermes Agent 的 CLI 是一个完整的终端用户界面(TUI),而非 Web UI。它支持多行编辑、斜杠命令自动补全、对话历史、中断并重定向,以及流式工具输出。专为常驻终端的用户而生。
|
||||
|
||||
:::tip
|
||||
Hermes 还提供了一个现代 TUI,支持模态覆盖层、鼠标选择和非阻塞输入。使用 `hermes --tui` 启动——参见 [TUI](tui.md) 指南。
|
||||
:::
|
||||
|
||||
## 运行 CLI
|
||||
|
||||
```bash
|
||||
# 启动交互式会话(默认)
|
||||
hermes
|
||||
|
||||
# 单次查询模式(非交互式)
|
||||
hermes chat -q "Hello"
|
||||
|
||||
# 使用指定模型
|
||||
hermes chat --model "anthropic/claude-sonnet-4"
|
||||
|
||||
# 使用指定提供商
|
||||
hermes chat --provider nous # 使用 Nous Portal
|
||||
hermes chat --provider openrouter # 强制使用 OpenRouter
|
||||
|
||||
# 使用指定工具集
|
||||
hermes chat --toolsets "web,terminal,skills"
|
||||
|
||||
# 启动时预加载一个或多个 skill
|
||||
hermes -s hermes-agent-dev,github-auth
|
||||
hermes chat -s github-pr-workflow -q "open a draft PR"
|
||||
|
||||
# 恢复之前的会话
|
||||
hermes --continue # 恢复最近的 CLI 会话(-c)
|
||||
hermes --resume <session_id> # 通过 ID 恢复指定会话(-r)
|
||||
|
||||
# 详细模式(调试输出)
|
||||
hermes chat --verbose
|
||||
|
||||
# 隔离的 git worktree(用于并行运行多个 agent)
|
||||
hermes -w # 在 worktree 中以交互模式运行
|
||||
hermes -w -q "Fix issue #123" # 在 worktree 中以单次查询模式运行
|
||||
```
|
||||
|
||||
## 界面布局
|
||||
|
||||
<img className="docs-terminal-figure" src="/img/docs/cli-layout.svg" alt="Hermes CLI 布局的风格化预览,展示了横幅、对话区域和固定输入提示符。" />
|
||||
<p className="docs-figure-caption">Hermes CLI 横幅、对话流和固定输入提示符,以稳定的文档图示形式呈现,而非脆弱的文字艺术。</p>
|
||||
|
||||
欢迎横幅一目了然地显示当前模型、终端后端、工作目录、可用工具和已安装的 skill。
|
||||
|
||||
### 状态栏
|
||||
|
||||
一个持久状态栏位于输入区域上方,实时更新:
|
||||
|
||||
```
|
||||
⚕ claude-sonnet-4-20250514 │ 12.4K/200K │ [██████░░░░] 6% │ $0.06 │ 15m
|
||||
```
|
||||
|
||||
| 元素 | 描述 |
|
||||
|---------|-------------|
|
||||
| 模型名称 | 当前模型(超过 26 个字符时截断) |
|
||||
| Token 计数 | 已使用的上下文 token 数 / 最大上下文窗口 |
|
||||
| 上下文进度条 | 带颜色阈值编码的可视填充指示器 |
|
||||
| 费用 | 预估会话费用(未知或零价格模型显示 `n/a`) |
|
||||
| 🗜️ N | **上下文压缩次数**——当前运行会话被自动压缩的次数。首次压缩触发后显示。 |
|
||||
| ▶ N | **活跃后台任务数**——当前会话中仍在运行的 `/background` prompt(提示词)数量。至少有一个任务进行中时显示。 |
|
||||
| 时长 | 会话已用时间 |
|
||||
| ⚠ YOLO | **YOLO 模式警告**——当 `HERMES_YOLO_MODE` 开启时显示(通过启动时的 `hermes --yolo` 或会话中的 `/yolo` 切换)。与横幅行警告保持同步,确保你不会忘记自己处于自动批准模式。 |
|
||||
|
||||
状态栏会根据终端宽度自适应——≥ 76 列时显示完整布局,52–75 列时显示紧凑布局,低于 52 列时显示最简布局(模型 + 时长,以及 YOLO 徽章(如已激活))。
|
||||
|
||||
**上下文颜色编码:**
|
||||
|
||||
| 颜色 | 阈值 | 含义 |
|
||||
|-------|-----------|---------|
|
||||
| 绿色 | < 50% | 空间充足 |
|
||||
| 黄色 | 50–80% | 趋于饱满 |
|
||||
| 橙色 | 80–95% | 接近上限 |
|
||||
| 红色 | ≥ 95% | 即将溢出——考虑使用 `/compress` |
|
||||
|
||||
使用 `/usage` 查看详细分解,包括各类别费用(输入 vs 输出 token)。
|
||||
|
||||
### 会话恢复显示
|
||||
|
||||
恢复之前的会话时(`hermes -c` 或 `hermes --resume <id>`),横幅与输入提示符之间会出现一个"Previous Conversation"面板,显示对话历史的简洁摘要。详情及配置说明参见[会话——恢复时的对话摘要](sessions.md#conversation-recap-on-resume)。
|
||||
|
||||
## 快捷键
|
||||
|
||||
| 按键 | 操作 |
|
||||
|-----|--------|
|
||||
| `Enter` | 发送消息 |
|
||||
| `Alt+Enter`、`Ctrl+J` 或 `Shift+Enter` | 换行(多行输入)。`Shift+Enter` 需要终端能够将其与 `Enter` 区分——见下文。在 Windows Terminal 中,`Alt+Enter` 被终端捕获(切换全屏);请改用 `Ctrl+Enter` 或 `Ctrl+J`。 |
|
||||
| `Alt+V` | 在终端支持时从剪贴板粘贴图片 |
|
||||
| `Ctrl+V` | 粘贴文本,并尝试附加剪贴板中的图片 |
|
||||
| `Ctrl+B` | 语音模式启用时开始/停止录音(`voice.record_key`,默认:`ctrl+b`) |
|
||||
| `Ctrl+G` | 在 `$EDITOR`(vim/nvim/nano/VS Code 等)中打开当前输入缓冲区。保存并退出后,编辑后的文本将作为下一条 prompt 发送——适合编写长篇多段落 prompt。 |
|
||||
| `Ctrl+X Ctrl+E` | 外部编辑器的 Emacs 风格备用绑定(与 `Ctrl+G` 行为相同)。 |
|
||||
| `Ctrl+C` | 中断 agent(2 秒内双击强制退出) |
|
||||
| `Ctrl+D` | 退出 |
|
||||
| `Ctrl+Z` | 将 Hermes 挂起到后台(仅 Unix)。在 shell 中运行 `fg` 恢复。 |
|
||||
| `Tab` | 接受自动建议(ghost text)或自动补全斜杠命令 |
|
||||
|
||||
**多行粘贴预览。** 粘贴多行内容时,CLI 会显示一行简洁的单行预览(`[pasted: 47 lines, 1,842 chars — press Enter to send]`),而非将全部内容倾倒到滚动缓冲区。实际发送的仍是完整内容;这只是显示上的优化。
|
||||
|
||||
**最终响应中的 Markdown 剥离。** CLI 会从 agent 的*最终*回复中剥离最冗长的 Markdown 围栏以及 `**粗体**` / `*斜体*` 包装,使其在终端中呈现为可读的纯文本,而非原始源码。代码块和列表会被保留。这不影响 gateway 平台或工具结果——它们保留 Markdown 以供原生渲染。
|
||||
|
||||
## 斜杠命令
|
||||
|
||||
输入 `/` 查看自动补全下拉菜单。Hermes 支持大量 CLI 斜杠命令、动态 skill 命令和用户自定义快捷命令。
|
||||
|
||||
常用示例:
|
||||
|
||||
| 命令 | 描述 |
|
||||
|---------|-------------|
|
||||
| `/help` | 显示命令帮助 |
|
||||
| `/model` | 显示或更改当前模型 |
|
||||
| `/tools` | 列出当前可用工具 |
|
||||
| `/skills browse` | 浏览 skill 中心和官方可选 skill |
|
||||
| `/background <prompt>` | 在独立后台会话中运行一个 prompt |
|
||||
| `/skin` | 显示或切换当前 CLI 皮肤 |
|
||||
| `/voice on` | 启用 CLI 语音模式(按 `Ctrl+B` 录音) |
|
||||
| `/voice tts` | 切换 Hermes 回复的语音播放 |
|
||||
| `/reasoning high` | 提高推理强度 |
|
||||
| `/title My Session` | 为当前会话命名 |
|
||||
| `/status` | 显示会话信息——模型/配置/token/时长——以及本地**会话摘要**块(近期轮次数、常用工具、涉及文件、最新用户 prompt + 助手回复)。纯本地计算,不调用 LLM。 |
|
||||
| `/sessions` | 在经典 CLI 中直接打开交互式会话选择器(与 TUI 使用同一界面)。输入过滤,方向键导航,Enter 恢复。 |
|
||||
|
||||
完整的内置 CLI 和消息列表,参见[斜杠命令参考](../reference/slash-commands.md)。
|
||||
|
||||
语音模式的设置、提供商、静音调节以及消息/Discord 语音用法,参见[语音模式](features/voice-mode.md)。
|
||||
|
||||
:::tip
|
||||
命令不区分大小写——`/HELP` 与 `/help` 效果相同。已安装的 skill 也会自动成为斜杠命令。
|
||||
:::
|
||||
|
||||
## 快捷命令
|
||||
|
||||
你可以定义自定义命令,无需调用 LLM 即可立即执行 shell 命令。这些命令在 CLI 和消息平台(Telegram、Discord 等)中均可使用。
|
||||
|
||||
```yaml
|
||||
# ~/.hermes/config.yaml
|
||||
quick_commands:
|
||||
status:
|
||||
type: exec
|
||||
command: systemctl status hermes-agent
|
||||
gpu:
|
||||
type: exec
|
||||
command: nvidia-smi --query-gpu=utilization.gpu,memory.used --format=csv,noheader
|
||||
restart:
|
||||
type: alias
|
||||
target: /gateway restart
|
||||
```
|
||||
|
||||
然后在任意聊天中输入 `/status`、`/gpu` 或 `/restart`。更多示例参见[配置指南](/user-guide/configuration#quick-commands)。
|
||||
|
||||
## 启动时预加载 Skill
|
||||
|
||||
如果你已知道本次会话需要哪些 skill,可在启动时传入:
|
||||
|
||||
```bash
|
||||
hermes -s hermes-agent-dev,github-auth
|
||||
hermes chat -s github-pr-workflow -s github-auth
|
||||
```
|
||||
|
||||
Hermes 会在第一轮对话前将每个指定的 skill 加载到会话 prompt 中。该标志在交互模式和单次查询模式下均有效。
|
||||
|
||||
## Skill 斜杠命令
|
||||
|
||||
`~/.hermes/skills/` 中每个已安装的 skill 都会自动注册为斜杠命令。skill 名称即为命令名:
|
||||
|
||||
```
|
||||
/gif-search funny cats
|
||||
/axolotl help me fine-tune Llama 3 on my dataset
|
||||
/github-pr-workflow create a PR for the auth refactor
|
||||
|
||||
# 仅输入 skill 名称即可加载它,让 agent 询问你的需求:
|
||||
/excalidraw
|
||||
```
|
||||
|
||||
## 人格设定
|
||||
|
||||
设置预定义人格以改变 agent 的语气:
|
||||
|
||||
```
|
||||
/personality pirate
|
||||
/personality kawaii
|
||||
/personality concise
|
||||
```
|
||||
|
||||
内置人格包括:`helpful`、`concise`、`technical`、`creative`、`teacher`、`kawaii`、`catgirl`、`pirate`、`shakespeare`、`surfer`、`noir`、`uwu`、`philosopher`、`hype`。
|
||||
|
||||
你也可以在 `~/.hermes/config.yaml` 中定义自定义人格:
|
||||
|
||||
```yaml
|
||||
personalities:
|
||||
helpful: "You are a helpful, friendly AI assistant."
|
||||
kawaii: "You are a kawaii assistant! Use cute expressions..."
|
||||
pirate: "Arrr! Ye be talkin' to Captain Hermes..."
|
||||
# 添加你自己的!
|
||||
```
|
||||
|
||||
## 多行输入
|
||||
|
||||
有两种方式输入多行消息:
|
||||
|
||||
1. **`Alt+Enter`、`Ctrl+J` 或 `Shift+Enter`** — 插入新行
|
||||
2. **反斜杠续行** — 在行尾加 `\` 继续输入:
|
||||
|
||||
```
|
||||
❯ Write a function that:\
|
||||
1. Takes a list of numbers\
|
||||
2. Returns the sum
|
||||
```
|
||||
|
||||
:::info
|
||||
支持粘贴多行文本——使用上述任意换行键,或直接粘贴内容。
|
||||
:::
|
||||
|
||||
### Shift+Enter 兼容性
|
||||
|
||||
大多数终端默认对 `Enter` 和 `Shift+Enter` 发送相同的字节序列,因此应用程序无法区分它们。Hermes 仅在终端通过 [Kitty 键盘协议](https://sw.kovidgoyal.net/kitty/keyboard-protocol/)或 xterm 的 `modifyOtherKeys` 模式发送不同序列时才能识别 `Shift+Enter`。
|
||||
|
||||
| 终端 | 状态 |
|
||||
|---|---|
|
||||
| Kitty、foot、WezTerm、Ghostty | 默认启用独立的 `Shift+Enter` |
|
||||
| iTerm2(近期版本)、Alacritty、VS Code terminal、Warp | 在设置中启用 Kitty 协议后支持 |
|
||||
| Windows Terminal Preview 1.25+ | 在设置中启用 Kitty 协议后支持 |
|
||||
| macOS Terminal.app、Windows Terminal 稳定版 | 不支持——`Shift+Enter` 与 `Enter` 无法区分 |
|
||||
|
||||
当终端无法区分时,`Alt+Enter` 和 `Ctrl+J` 在所有终端中均可正常使用。**特别是在 Windows Terminal 中,`Alt+Enter` 被终端捕获(切换全屏),永远不会传递给 Hermes——请直接使用 `Ctrl+Enter`(传递为 `Ctrl+J`)或 `Ctrl+J` 来换行。**
|
||||
|
||||
## 中断 Agent
|
||||
|
||||
你可以在任意时刻中断 agent:
|
||||
|
||||
- **输入新消息 + Enter**,在 agent 工作时——中断并处理你的新指令
|
||||
- **`Ctrl+C`**——中断当前操作(2 秒内双击强制退出)
|
||||
- 正在进行的终端命令会立即被终止(SIGTERM,1 秒后 SIGKILL)
|
||||
- 中断期间输入的多条消息会合并为一条 prompt
|
||||
|
||||
### 繁忙输入模式
|
||||
|
||||
`display.busy_input_mode` 配置项控制在 agent 工作时按下 Enter 的行为:
|
||||
|
||||
| 模式 | 行为 |
|
||||
|------|----------|
|
||||
| `"interrupt"`(默认) | 你的消息中断当前操作并立即处理 |
|
||||
| `"queue"` | 你的消息被静默排队,在 agent 完成后作为下一轮发送 |
|
||||
| `"steer"` | 你的消息通过 `/steer` 注入当前运行,在下一次工具调用后到达 agent——不中断,不开启新轮次 |
|
||||
|
||||
```yaml
|
||||
# ~/.hermes/config.yaml
|
||||
display:
|
||||
busy_input_mode: "steer" # 或 "queue" 或 "interrupt"(默认)
|
||||
```
|
||||
|
||||
`"queue"` 模式适合在不意外取消进行中工作的情况下准备后续消息。`"steer"` 模式适合在不中断的情况下在任务执行中途重定向 agent——例如在它还在编辑代码时说"顺便也检查一下测试"。未知值会回退到 `"interrupt"`。
|
||||
|
||||
`"steer"` 有两个自动回退:如果 agent 尚未启动,或附有图片,消息会回退到 `"queue"` 行为,确保内容不丢失。
|
||||
|
||||
你也可以在 CLI 中动态更改:
|
||||
|
||||
```text
|
||||
/busy queue
|
||||
/busy steer
|
||||
/busy interrupt
|
||||
/busy status
|
||||
```
|
||||
|
||||
:::tip 首次提示
|
||||
第一次在 Hermes 工作时按下 Enter,Hermes 会打印一行提示,说明 `/busy` 选项(`"(tip) Your message interrupted the current run…"`)。每次安装只触发一次——`config.yaml` 中 `onboarding.seen.busy_input_prompt` 下的标志会锁定它。删除该键可再次看到提示。
|
||||
:::
|
||||
|
||||
### 挂起到后台
|
||||
|
||||
在 Unix 系统上,按 **`Ctrl+Z`** 将 Hermes 挂起到后台——与任何终端进程一样。shell 会打印确认信息:
|
||||
|
||||
```
|
||||
Hermes Agent has been suspended. Run `fg` to bring Hermes Agent back.
|
||||
```
|
||||
|
||||
在 shell 中输入 `fg` 即可从中断处恢复会话。Windows 不支持此功能。
|
||||
|
||||
## 工具进度显示
|
||||
|
||||
CLI 在 agent 工作时显示动态反馈:
|
||||
|
||||
**思考动画**(API 调用期间):
|
||||
```
|
||||
◜ (。•́︿•̀。) pondering... (1.2s)
|
||||
◠ (⊙_⊙) contemplating... (2.4s)
|
||||
✧٩(ˊᗜˋ*)و✧ got it! (3.1s)
|
||||
```
|
||||
|
||||
**工具执行信息流:**
|
||||
```
|
||||
┊ 💻 terminal `ls -la` (0.3s)
|
||||
┊ 🔍 web_search (1.2s)
|
||||
┊ 📄 web_extract (2.1s)
|
||||
```
|
||||
|
||||
使用 `/verbose` 循环切换显示模式:`off → new → all → verbose`。该命令也可为消息平台启用——参见[配置](/user-guide/configuration#display-settings)。
|
||||
|
||||
### 工具预览长度
|
||||
|
||||
`display.tool_preview_length` 配置项控制工具调用预览行(如文件路径、终端命令)中显示的最大字符数。默认值为 `0`,表示无限制——显示完整路径和命令。
|
||||
|
||||
```yaml
|
||||
# ~/.hermes/config.yaml
|
||||
display:
|
||||
tool_preview_length: 80 # 将工具预览截断为 80 个字符(0 = 无限制)
|
||||
```
|
||||
|
||||
这在终端较窄或工具参数包含很长文件路径时非常有用。
|
||||
|
||||
## 会话管理
|
||||
|
||||
### 恢复会话
|
||||
|
||||
退出 CLI 会话时,会打印恢复命令:
|
||||
|
||||
```
|
||||
Resume this session with:
|
||||
hermes --resume 20260225_143052_a1b2c3
|
||||
|
||||
Session: 20260225_143052_a1b2c3
|
||||
Duration: 12m 34s
|
||||
Messages: 28 (5 user, 18 tool calls)
|
||||
```
|
||||
|
||||
恢复选项:
|
||||
|
||||
```bash
|
||||
hermes --continue # 恢复最近的 CLI 会话
|
||||
hermes -c # 简写形式
|
||||
hermes -c "my project" # 恢复命名会话(谱系中最新的)
|
||||
hermes --resume 20260225_143052_a1b2c3 # 通过 ID 恢复指定会话
|
||||
hermes --resume "refactoring auth" # 通过标题恢复
|
||||
hermes -r 20260225_143052_a1b2c3 # 简写形式
|
||||
```
|
||||
|
||||
恢复会从 SQLite 中还原完整的对话历史。agent 能看到所有之前的消息、工具调用和响应——就像从未离开一样。
|
||||
|
||||
在聊天中使用 `/title My Session Name` 为当前会话命名,或从命令行使用 `hermes sessions rename <id> <title>`。使用 `hermes sessions list` 浏览历史会话。
|
||||
|
||||
### 会话存储
|
||||
|
||||
CLI 会话存储在 Hermes 的 SQLite 状态数据库 `~/.hermes/state.db` 中。数据库保存:
|
||||
|
||||
- 会话元数据(ID、标题、时间戳、token 计数器)
|
||||
- 消息历史
|
||||
- 跨压缩/恢复会话的谱系
|
||||
- `session_search` 使用的全文搜索索引
|
||||
|
||||
部分消息适配器还会在数据库旁保存各平台的转录文件,但 CLI 本身从 SQLite 会话存储中恢复。
|
||||
|
||||
### 上下文压缩
|
||||
|
||||
长对话在接近上下文限制时会自动摘要:
|
||||
|
||||
```yaml
|
||||
# 在 ~/.hermes/config.yaml 中
|
||||
compression:
|
||||
enabled: true
|
||||
threshold: 0.50 # 默认在上下文限制的 50% 时压缩
|
||||
|
||||
# 摘要模型在 auxiliary 下配置:
|
||||
auxiliary:
|
||||
compression:
|
||||
model: "" # 留空则使用主聊天模型(默认)。或指定一个廉价快速的模型,如 "google/gemini-3-flash-preview"。
|
||||
```
|
||||
|
||||
压缩触发时,中间轮次会被摘要,同时始终保留前 3 轮和后 20 轮。
|
||||
|
||||
## 后台会话
|
||||
|
||||
在独立的后台会话中运行 prompt,同时继续使用 CLI 进行其他工作:
|
||||
|
||||
```
|
||||
/background Analyze the logs in /var/log and summarize any errors from today
|
||||
```
|
||||
|
||||
Hermes 立即确认任务并将提示符还给你:
|
||||
|
||||
```
|
||||
🔄 Background task #1 started: "Analyze the logs in /var/log and summarize..."
|
||||
Task ID: bg_143022_a1b2c3
|
||||
```
|
||||
|
||||
### 工作原理
|
||||
|
||||
每个 `/background` prompt 会在守护线程中生成一个**完全独立的 agent 会话**:
|
||||
|
||||
- **隔离对话**——后台 agent 不了解当前会话的历史。它只接收你提供的 prompt。
|
||||
- **相同配置**——后台 agent 继承当前会话的模型、提供商、工具集、推理设置和回退模型。
|
||||
- **非阻塞**——前台会话保持完全交互。你可以聊天、运行命令,甚至启动更多后台任务。
|
||||
- **多任务**——你可以同时运行多个后台任务。每个任务都有编号 ID。
|
||||
|
||||
### 结果
|
||||
|
||||
后台任务完成时,结果会以面板形式出现在终端中:
|
||||
|
||||
```
|
||||
╭─ ⚕ Hermes (background #1) ──────────────────────────────────╮
|
||||
│ Found 3 errors in syslog from today: │
|
||||
│ 1. OOM killer invoked at 03:22 — killed process nginx │
|
||||
│ 2. Disk I/O error on /dev/sda1 at 07:15 │
|
||||
│ 3. Failed SSH login attempts from 192.168.1.50 at 14:30 │
|
||||
╰──────────────────────────────────────────────────────────────╯
|
||||
```
|
||||
|
||||
如果任务失败,你会看到错误通知。如果配置中启用了 `display.bell_on_complete`,任务完成时终端会响铃。
|
||||
|
||||
### 使用场景
|
||||
|
||||
- **长时间研究**——"/background research the latest developments in quantum error correction",同时继续编写代码
|
||||
- **文件处理**——"/background analyze all Python files in this repo and list any security issues",同时继续对话
|
||||
- **并行调查**——同时启动多个后台任务,从不同角度探索问题
|
||||
|
||||
:::info
|
||||
后台会话不会出现在主对话历史中。它们是独立会话,拥有各自的任务 ID(如 `bg_143022_a1b2c3`)。
|
||||
:::
|
||||
|
||||
## 静默模式
|
||||
|
||||
默认情况下,CLI 以静默模式运行,该模式会:
|
||||
- 抑制工具的详细日志
|
||||
- 启用 kawaii 风格的动态反馈
|
||||
- 保持输出简洁易读
|
||||
|
||||
如需调试输出:
|
||||
```bash
|
||||
hermes chat --verbose
|
||||
```
|
||||
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,237 @@
|
||||
---
|
||||
sidebar_position: 3
|
||||
---
|
||||
|
||||
# 配置模型
|
||||
|
||||
Hermes 使用两类模型槽位:
|
||||
|
||||
- **主模型** — agent 的思考核心。每条用户消息、每个工具调用循环、每次流式响应都经由该模型处理。
|
||||
- **辅助模型** — agent 卸载给较小模型的边缘任务。包括上下文压缩、视觉(图像分析)、网页摘要、审批评分、MCP 工具路由、会话标题生成和技能搜索。每项任务有独立槽位,可单独覆盖。
|
||||
|
||||
本页介绍如何通过仪表板配置上述两类模型。如需使用配置文件或 CLI,请跳至底部的[其他方法](#alternative-methods)。
|
||||
|
||||
:::tip 最快路径:Nous Portal
|
||||
[Nous Portal](/user-guide/features/tool-gateway) 在单一订阅下提供 300+ 个模型。全新安装后,运行 `hermes setup --portal` 即可登录并一键将 Nous 设为提供商。使用 `hermes portal status` 查看当前配置。
|
||||
:::
|
||||
|
||||
## Models 页面
|
||||
|
||||
打开仪表板,点击侧边栏中的 **Models**。页面分为两个区域:
|
||||
|
||||
1. **Model Settings** — 顶部面板,用于为各槽位分配模型。
|
||||
2. **使用分析** — 按排名显示所选时间段内运行过会话的所有模型,包含 token 数量、费用和能力标签。
|
||||
|
||||

|
||||
|
||||
顶部卡片为 **Model Settings** 面板。主行始终显示 agent 将为新会话启动的模型。点击 **Change** 打开选择器。
|
||||
|
||||
## 设置主模型
|
||||
|
||||
点击主模型行上的 **Change**:
|
||||
|
||||

|
||||
|
||||
选择器分为两列:
|
||||
|
||||
- **左列** — 已认证的提供商。仅显示已配置的提供商(已设置 API key、完成 OAuth 或定义了自定义端点)。若某提供商未出现,请前往 **Keys** 添加凭据。
|
||||
- **右列** — 所选提供商的精选模型列表。这些是 Hermes 针对该提供商推荐的 agentic 模型,而非原始的 `/models` 接口返回结果(OpenRouter 的原始列表包含 400+ 个模型,涵盖 TTS、图像生成器和重排序器)。
|
||||
|
||||
在过滤框中输入提供商名称、slug 或模型 ID 进行筛选。
|
||||
|
||||
选择模型后点击 **Switch**,Hermes 会将其写入 `~/.hermes/config.yaml` 的 `model` 部分。**此操作仅对新会话生效** — 已打开的聊天标签页将继续使用启动时的模型。如需在当前聊天中热切换,请在聊天内使用 `/model` 斜杠命令。
|
||||
|
||||
## 设置辅助模型
|
||||
|
||||
点击 **Show auxiliary** 展开八个任务槽位:
|
||||
|
||||

|
||||
|
||||
每个辅助任务默认为 `auto`,即 Hermes 对该任务也使用主模型。当某个边缘任务需要更便宜或更快的模型时,可单独覆盖该槽位。
|
||||
|
||||
### 常见覆盖模式
|
||||
|
||||
| 任务 | 何时覆盖 |
|
||||
|---|---|
|
||||
| **Title Gen(标题生成)** | 几乎总是。$0.10/M 的 flash 模型生成会话标题的效果与 Opus 相当。默认配置在 OpenRouter 上将此项设为 `google/gemini-3-flash-preview`。 |
|
||||
| **Vision(视觉)** | 当主模型是不支持视觉的编程模型时(如 Kimi、DeepSeek)。将其指向 `google/gemini-2.5-flash` 或 `gpt-4o-mini`。 |
|
||||
| **Compression(压缩)** | 当你在用 Opus/M2.7 的推理 token 来摘要上下文时。快速聊天模型以 1/50 的成本即可完成此工作。 |
|
||||
| **Approval(审批)** | 用于 `approval_mode: smart` — 由快速/廉价模型(haiku、flash、gpt-5-mini)决定是否自动批准低风险命令。此处使用昂贵模型是浪费。 |
|
||||
| **Web Extract(网页提取)** | 当你大量使用 `web_extract` 时。逻辑同压缩 — 摘要任务不需要推理能力。 |
|
||||
| **Skills Hub(技能中心)** | `hermes skills search` 使用此槽位。通常保持 `auto` 即可。 |
|
||||
| **MCP** | MCP 工具路由。通常保持 `auto` 即可。 |
|
||||
|
||||
### 单任务覆盖
|
||||
|
||||
点击任意辅助行上的 **Change**,打开相同的选择器,操作方式相同 — 选择提供商和模型,点击 Switch。该行将从 `auto (use main model)` 更新为 `provider · model`。
|
||||
|
||||
### 全部重置为 auto
|
||||
|
||||
如果调整过度想重新开始,点击辅助区域顶部的 **Reset all to auto**。所有槽位将恢复使用主模型。
|
||||
|
||||
## "Use as" 快捷方式
|
||||
|
||||
页面上每张模型卡片都有 **Use as** 下拉菜单。这是快捷路径 — 从分析数据中选择一个模型,点击 **Use as**,一键将其分配到主槽位或任意辅助任务:
|
||||
|
||||

|
||||
|
||||
下拉菜单包含:
|
||||
|
||||
- **Main model** — 与点击主行上的 Change 效果相同。
|
||||
- **All auxiliary tasks** — 将此模型分配给全部 8 个辅助槽位。适合将所有边缘任务统一切换到廉价 flash 模型的场景。
|
||||
- **单项任务选项** — Vision、Web Extract、Compression 等。每项任务当前分配的模型标记为 `current`。
|
||||
|
||||
当模型卡片当前已分配到某个槽位时,会显示 `main` 或 `aux · <task>` 标签,方便一眼看出历史模型的使用情况。
|
||||
|
||||
## 写入 `config.yaml` 的内容
|
||||
|
||||
通过仪表板保存时,Hermes 写入 `~/.hermes/config.yaml`:
|
||||
|
||||
**主模型:**
|
||||
```yaml
|
||||
model:
|
||||
provider: openrouter
|
||||
default: anthropic/claude-opus-4.7
|
||||
base_url: '' # cleared on provider switch
|
||||
api_mode: chat_completions
|
||||
```
|
||||
|
||||
**辅助覆盖示例(视觉任务使用 gemini-flash):**
|
||||
```yaml
|
||||
auxiliary:
|
||||
vision:
|
||||
provider: openrouter
|
||||
model: google/gemini-2.5-flash
|
||||
base_url: ''
|
||||
api_key: ''
|
||||
timeout: 120
|
||||
extra_body: {}
|
||||
download_timeout: 30
|
||||
```
|
||||
|
||||
**辅助任务处于 auto(默认):**
|
||||
```yaml
|
||||
auxiliary:
|
||||
compression:
|
||||
provider: auto
|
||||
model: ''
|
||||
base_url: ''
|
||||
# ... other fields unchanged
|
||||
```
|
||||
|
||||
`provider: auto` 加 `model: ''` 表示 Hermes 对该任务使用主模型。
|
||||
|
||||
## 何时生效?
|
||||
|
||||
- **CLI**(`hermes chat`):下次执行 `hermes chat` 时生效。
|
||||
- **Gateway**(Telegram、Discord、Slack 等):下一个*新*会话生效。现有会话保持原有模型。如需强制所有会话使用新配置,重启 gateway(`hermes gateway restart`)。
|
||||
- **仪表板聊天标签页**(`/chat`):下一个新 PTY 生效。当前打开的聊天保持原有模型 — 在聊天内使用 `/model` 进行热切换。
|
||||
|
||||
更改不会使运行中会话的 prompt 缓存失效。这是有意为之:在会话内切换主模型需要重置缓存(系统 prompt 包含模型特定内容),该操作保留给聊天内的显式 `/model` 斜杠命令。
|
||||
|
||||
## 故障排查
|
||||
|
||||
### 选择器中显示"No authenticated providers"
|
||||
|
||||
Hermes 仅列出具有有效凭据的提供商。检查侧边栏中的 **Keys** — 应存在以下之一:API key、成功的 OAuth 或自定义端点 URL。若所需提供商不在列表中,运行 `hermes setup` 进行配置,或前往 **Keys** 添加环境变量。
|
||||
|
||||
### 主模型在运行中的聊天里未发生变化
|
||||
|
||||
符合预期。仪表板写入 `config.yaml`,新会话读取该文件。当前打开的聊天是一个活跃的 agent 进程 — 它保持启动时的模型。在聊天内使用 `/model <name>` 对该会话进行热切换。
|
||||
|
||||
### 辅助覆盖"未生效"
|
||||
|
||||
检查以下三点:
|
||||
|
||||
1. **是否启动了新会话?** 现有聊天不会重新读取配置。
|
||||
2. **`provider` 是否设置为非 `auto` 的值?** 若字段显示 `auto`,该任务仍在使用主模型。点击 **Change** 选择实际的提供商。
|
||||
3. **提供商是否已认证?** 若将 `minimax` 分配给某任务但没有 MiniMax API key,该任务将回退到 openrouter 默认值,并在 `agent.log` 中记录警告。
|
||||
|
||||
### 我选择了模型,但 Hermes 切换了提供商
|
||||
|
||||
在 OpenRouter(或任何聚合器)上,裸模型名称会优先在聚合器内解析。因此 OpenRouter 上的 `claude-sonnet-4` 会解析为 `anthropic/claude-sonnet-4.6`,保持在你的 OpenRouter 认证下。但若在原生 Anthropic 认证下输入 `claude-sonnet-4`,则会保持为 `claude-sonnet-4-6`。若出现意外的提供商切换,请确认当前提供商是否符合预期 — 选择器始终在对话框顶部显示当前主模型。
|
||||
|
||||
## 其他方法 {#alternative-methods}
|
||||
|
||||
### CLI 斜杠命令
|
||||
|
||||
在任意 `hermes chat` 会话内:
|
||||
|
||||
```
|
||||
/model gpt-5.4 --provider openrouter # 仅当前会话
|
||||
/model gpt-5.4 --provider openrouter --global # 同时持久化到 config.yaml
|
||||
```
|
||||
|
||||
`--global` 与仪表板 **Change** 按钮效果相同,并额外在当前会话内原地切换模型。
|
||||
|
||||
### 自定义别名
|
||||
|
||||
为常用模型定义短名称,然后在 CLI 或任意消息平台中使用 `/model <alias>`:
|
||||
|
||||
```yaml
|
||||
# ~/.hermes/config.yaml
|
||||
model_aliases:
|
||||
fav:
|
||||
model: claude-sonnet-4.6
|
||||
provider: anthropic
|
||||
grok:
|
||||
model: grok-4
|
||||
provider: x-ai
|
||||
```
|
||||
|
||||
或通过 shell 命令(简写形式,`provider/model`):
|
||||
|
||||
```bash
|
||||
hermes config set model.aliases.fav anthropic/claude-opus-4.6
|
||||
hermes config set model.aliases.grok x-ai/grok-4
|
||||
```
|
||||
|
||||
然后在聊天中使用 `/model fav` 或 `/model grok`。用户别名会覆盖内置短名称(`sonnet`、`kimi`、`opus` 等)。完整参考请见[自定义模型别名](/reference/slash-commands#custom-model-aliases)。
|
||||
|
||||
### `hermes model` 子命令
|
||||
|
||||
```bash
|
||||
hermes model # 交互式提供商 + 模型选择器(切换默认值的标准方式)
|
||||
```
|
||||
|
||||
`hermes model` 引导你选择提供商、完成认证(OAuth 流程会打开浏览器;API key 提供商会提示输入密钥),然后从该提供商的精选目录中选择具体模型。选择结果写入 `~/.hermes/config.yaml` 的 `model.provider` 和 `model.model` 字段。
|
||||
|
||||
如需在不启动选择器的情况下列出提供商/模型,请使用仪表板或下方的 REST 端点。查看 CLI 当前实际使用的配置:`hermes config get model` 和 `hermes status`。
|
||||
|
||||
### 直接编辑配置文件
|
||||
|
||||
编辑 `~/.hermes/config.yaml` 后重启相关服务。完整 schema 请见[配置参考](./configuration.md)。
|
||||
|
||||
### REST API
|
||||
|
||||
仪表板使用以下三个端点,可用于脚本化操作:
|
||||
|
||||
```bash
|
||||
# 列出已认证的提供商及精选模型列表
|
||||
curl -H "X-Hermes-Session-Token: $TOKEN" http://localhost:PORT/api/model/options
|
||||
|
||||
# 读取当前主模型及辅助任务分配
|
||||
curl -H "X-Hermes-Session-Token: $TOKEN" http://localhost:PORT/api/model/auxiliary
|
||||
|
||||
# 设置主模型
|
||||
curl -X POST -H "Content-Type: application/json" -H "X-Hermes-Session-Token: $TOKEN" \
|
||||
-d '{"scope":"main","provider":"openrouter","model":"anthropic/claude-opus-4.7"}' \
|
||||
http://localhost:PORT/api/model/set
|
||||
|
||||
# 覆盖单个辅助任务
|
||||
curl -X POST -H "Content-Type: application/json" -H "X-Hermes-Session-Token: $TOKEN" \
|
||||
-d '{"scope":"auxiliary","task":"vision","provider":"openrouter","model":"google/gemini-2.5-flash"}' \
|
||||
http://localhost:PORT/api/model/set
|
||||
|
||||
# 将一个模型分配给所有辅助任务
|
||||
curl -X POST -H "Content-Type: application/json" -H "X-Hermes-Session-Token: $TOKEN" \
|
||||
-d '{"scope":"auxiliary","task":"","provider":"openrouter","model":"google/gemini-2.5-flash"}' \
|
||||
http://localhost:PORT/api/model/set
|
||||
|
||||
# 将所有辅助任务重置为 auto
|
||||
curl -X POST -H "Content-Type: application/json" -H "X-Hermes-Session-Token: $TOKEN" \
|
||||
-d '{"scope":"auxiliary","task":"__reset__","provider":"","model":""}' \
|
||||
http://localhost:PORT/api/model/set
|
||||
```
|
||||
|
||||
session token 在启动时注入仪表板 HTML,每次服务器重启后轮换。如需对运行中的仪表板编写脚本,可从浏览器开发者工具中获取(`window.__HERMES_SESSION_TOKEN__`)。
|
||||
@ -0,0 +1,596 @@
|
||||
---
|
||||
sidebar_position: 7
|
||||
title: "Docker"
|
||||
description: "在 Docker 中运行 Hermes Agent 以及将 Docker 用作终端后端"
|
||||
---
|
||||
|
||||
# Hermes Agent — Docker
|
||||
|
||||
Docker 与 Hermes Agent 的交集有两种截然不同的方式:
|
||||
|
||||
1. **在 Docker 中运行 Hermes** — agent 本身在容器内运行(本页的主要内容)
|
||||
2. **Docker 作为终端后端** — agent 在宿主机上运行,但将每条命令在单个持久化 Docker 沙箱容器中执行,该容器在工具调用、`/new` 和子 agent 之间保持存活,直至 Hermes 进程结束(参见 [配置 → Docker 后端](./configuration.md#docker-backend))
|
||||
|
||||
本页介绍选项 1。容器将所有用户数据(配置、API 密钥、会话、技能、记忆)存储在从宿主机挂载于 `/opt/data` 的单个目录中。镜像本身是无状态的,可通过拉取新版本进行升级而不会丢失任何配置。
|
||||
|
||||
## 快速开始
|
||||
|
||||
如果这是你第一次运行 Hermes Agent,请在宿主机上创建一个数据目录,并以交互方式启动容器以运行设置向导:
|
||||
|
||||
```sh
|
||||
mkdir -p ~/.hermes
|
||||
docker run -it --rm \
|
||||
-v ~/.hermes:/opt/data \
|
||||
nousresearch/hermes-agent setup
|
||||
```
|
||||
|
||||
这将进入设置向导,向导会提示你输入 API 密钥并将其写入 `~/.hermes/.env`。你只需执行一次。强烈建议此时为 gateway 配置一个聊天系统。
|
||||
|
||||
## 以 gateway 模式运行
|
||||
|
||||
配置完成后,将容器作为持久化 gateway(Telegram、Discord、Slack、WhatsApp 等)在后台运行:
|
||||
|
||||
```sh
|
||||
docker run -d \
|
||||
--name hermes \
|
||||
--restart unless-stopped \
|
||||
-v ~/.hermes:/opt/data \
|
||||
-p 8642:8642 \
|
||||
nousresearch/hermes-agent gateway run
|
||||
```
|
||||
|
||||
端口 8642 暴露 gateway 的 [OpenAI 兼容 API 服务器](./features/api-server.md)和健康检查端点。如果你只使用聊天平台(Telegram、Discord 等),该端口是可选的;但如果你希望 dashboard 或外部工具访问 gateway,则必须开放。
|
||||
|
||||
注意:API 服务器需设置 `API_SERVER_ENABLED=true` 才会启用。若要在容器内将其暴露至 `127.0.0.1` 以外,还需设置 `API_SERVER_HOST=0.0.0.0` 和 `API_SERVER_KEY`(最少 8 个字符——可用 `openssl rand -hex 32` 生成)。示例:
|
||||
|
||||
```sh
|
||||
docker run -d \
|
||||
--name hermes \
|
||||
--restart unless-stopped \
|
||||
-v ~/.hermes:/opt/data \
|
||||
-p 8642:8642 \
|
||||
-e API_SERVER_ENABLED=true \
|
||||
-e API_SERVER_HOST=0.0.0.0 \
|
||||
-e API_SERVER_KEY="$(openssl rand -hex 32)" \
|
||||
-e API_SERVER_CORS_ORIGINS='*' \
|
||||
nousresearch/hermes-agent gateway run
|
||||
```
|
||||
|
||||
在面向互联网的机器上开放任何端口都存在安全风险。除非你了解相关风险,否则不应这样做。
|
||||
|
||||
## 运行 dashboard
|
||||
|
||||
内置 Web dashboard 作为可选的子进程在与 gateway 相同的容器内运行。设置 `HERMES_DASHBOARD=1` 可在容器回环地址(`127.0.0.1`)上默认运行 dashboard:
|
||||
|
||||
```sh
|
||||
docker run -d \
|
||||
--name hermes \
|
||||
--restart unless-stopped \
|
||||
-v ~/.hermes:/opt/data \
|
||||
-p 8642:8642 \
|
||||
-e HERMES_DASHBOARD=1 \
|
||||
nousresearch/hermes-agent gateway run
|
||||
```
|
||||
|
||||
入口点在 `exec` 主命令之前,以非 root 用户 `hermes` 在后台启动 `hermes dashboard`。Dashboard 输出在 `docker logs` 中以 `[dashboard]` 为前缀,便于与 gateway 日志区分。
|
||||
|
||||
| 环境变量 | 描述 | 默认值 |
|
||||
|---------------------|-------------|---------|
|
||||
| `HERMES_DASHBOARD` | 设为 `1`(或 `true` / `yes`)以在主命令旁启动 dashboard | *(未设置——不启动 dashboard)* |
|
||||
| `HERMES_DASHBOARD_HOST` | dashboard HTTP 服务器的绑定地址 | `127.0.0.1` |
|
||||
| `HERMES_DASHBOARD_PORT` | dashboard HTTP 服务器的端口 | `9119` |
|
||||
| `HERMES_DASHBOARD_TUI` | 设为 `1` 以启用浏览器内 Chat 标签页(通过 PTY/WebSocket 嵌入 `hermes --tui`) | *(未设置)* |
|
||||
|
||||
默认情况下,dashboard 保持在回环地址,以避免将未经身份验证的 Web 界面暴露到网络。若要有意发布,请设置 `HERMES_DASHBOARD_HOST=0.0.0.0` 并配置你自己的可信网络边界/反向代理。在这种情况下,你必须通过命令路径中的 host/flags 显式添加 `--insecure` 行为(入口点不再自动启用不安全模式)。
|
||||
|
||||
:::note
|
||||
dashboard 在容器内作为受监管的 s6 服务运行。如果
|
||||
dashboard 进程崩溃,s6-overlay 会在短暂退避后自动
|
||||
重启它——你会看到新的 PID,无需重启容器。日志和崩溃输出可通过
|
||||
`docker logs <container>` 查看(s6 将服务的 stdout/stderr 转发至此)。
|
||||
|
||||
不支持将 dashboard 作为独立容器运行:其
|
||||
gateway 存活检测需要与 gateway 进程共享 PID 命名空间。
|
||||
:::
|
||||
|
||||
## 交互式运行(CLI 聊天)
|
||||
|
||||
对已有数据目录打开交互式聊天会话:
|
||||
|
||||
```sh
|
||||
docker run -it --rm \
|
||||
-v ~/.hermes:/opt/data \
|
||||
nousresearch/hermes-agent
|
||||
```
|
||||
|
||||
或者,如果你已通过 Docker Desktop 等方式在运行中的容器内打开了终端,直接运行:
|
||||
|
||||
```sh
|
||||
/opt/hermes/.venv/bin/hermes
|
||||
```
|
||||
|
||||
## 持久化卷
|
||||
|
||||
`/opt/data` 卷是所有 Hermes 状态的唯一数据来源。它映射到宿主机的 `~/.hermes/` 目录,包含:
|
||||
|
||||
| 路径 | 内容 |
|
||||
|------|----------|
|
||||
| `.env` | API 密钥和机密 |
|
||||
| `config.yaml` | 所有 Hermes 配置 |
|
||||
| `SOUL.md` | Agent 个性/身份 |
|
||||
| `sessions/` | 对话历史 |
|
||||
| `memories/` | 持久化记忆存储 |
|
||||
| `skills/` | 已安装的技能 |
|
||||
| `cron/` | 定时任务定义 |
|
||||
| `hooks/` | 事件 hook |
|
||||
| `logs/` | 运行时日志 |
|
||||
| `skins/` | 自定义 CLI 皮肤 |
|
||||
|
||||
:::warning
|
||||
切勿同时对同一数据目录运行两个 Hermes **gateway** 容器——会话文件和记忆存储不支持并发写入。
|
||||
:::
|
||||
|
||||
## 多 profile 支持
|
||||
|
||||
Hermes 支持[多个 profile](../reference/profile-commands.md)——独立的 `~/.hermes/` 目录,让你可以从单个安装运行独立的 agent(不同的 SOUL、技能、记忆、会话、凭据)。**在 Docker 下运行时,不建议使用 Hermes 内置的多 profile 功能。**
|
||||
|
||||
推荐的模式是**每个 profile 一个容器**,每个容器将各自的宿主机目录绑定挂载为 `/opt/data`:
|
||||
|
||||
```sh
|
||||
# 工作 profile
|
||||
docker run -d \
|
||||
--name hermes-work \
|
||||
--restart unless-stopped \
|
||||
-v ~/.hermes-work:/opt/data \
|
||||
-p 8642:8642 \
|
||||
nousresearch/hermes-agent gateway run
|
||||
|
||||
# 个人 profile
|
||||
docker run -d \
|
||||
--name hermes-personal \
|
||||
--restart unless-stopped \
|
||||
-v ~/.hermes-personal:/opt/data \
|
||||
-p 8643:8642 \
|
||||
nousresearch/hermes-agent gateway run
|
||||
```
|
||||
|
||||
在 Docker 中使用独立容器而非 profile 的原因:
|
||||
|
||||
- **隔离性** — 每个容器有独立的文件系统、进程表和资源限制。一个 profile 中的崩溃、依赖变更或失控会话不会影响另一个。
|
||||
- **独立生命周期** — 可独立升级、重启、暂停或回滚每个 agent(`docker restart hermes-work` 不会影响 `hermes-personal`)。
|
||||
- **清晰的端口和网络隔离** — 每个 gateway 绑定各自的宿主机端口;聊天平台或 API 服务器之间不存在串扰风险。
|
||||
- **更简单的心智模型** — 容器即 profile。备份、迁移和权限管理都跟随绑定挂载的目录,无需记住额外的 `--profile` 标志。
|
||||
- **避免并发写入风险** — 上述关于不得对同一数据目录运行两个 gateway 的警告同样适用于单个容器内的 profile。
|
||||
|
||||
在 Docker Compose 中,只需为每个 profile 声明一个服务,使用不同的 `container_name`、`volumes` 和 `ports`:
|
||||
|
||||
```yaml
|
||||
services:
|
||||
hermes-work:
|
||||
image: nousresearch/hermes-agent:latest
|
||||
container_name: hermes-work
|
||||
restart: unless-stopped
|
||||
command: gateway run
|
||||
ports:
|
||||
- "8642:8642"
|
||||
volumes:
|
||||
- ~/.hermes-work:/opt/data
|
||||
|
||||
hermes-personal:
|
||||
image: nousresearch/hermes-agent:latest
|
||||
container_name: hermes-personal
|
||||
restart: unless-stopped
|
||||
command: gateway run
|
||||
ports:
|
||||
- "8643:8642"
|
||||
volumes:
|
||||
- ~/.hermes-personal:/opt/data
|
||||
```
|
||||
|
||||
## 环境变量转发
|
||||
|
||||
API 密钥从容器内的 `/opt/data/.env` 读取。你也可以直接传递环境变量:
|
||||
|
||||
```sh
|
||||
docker run -it --rm \
|
||||
-v ~/.hermes:/opt/data \
|
||||
-e ANTHROPIC_API_KEY="sk-ant-..." \
|
||||
-e OPENAI_API_KEY="sk-..." \
|
||||
nousresearch/hermes-agent
|
||||
```
|
||||
|
||||
直接传入的 `-e` 标志会覆盖 `.env` 中的值。这对于不希望将密钥写入磁盘的 CI/CD 或密钥管理器集成非常有用。
|
||||
|
||||
:::note 寻找 Docker 作为**终端后端**的说明?
|
||||
本页介绍在 Docker 内运行 Hermes 本身。如果你希望 Hermes 在 Docker 沙箱容器内执行 agent 的 `terminal` / `execute_code` 调用(每个 Hermes 进程对应一个持久容器),那是另一个配置块——`terminal.backend: docker` 加上 `terminal.docker_image`、`terminal.docker_volumes`、`terminal.docker_forward_env`、`terminal.docker_run_as_host_user` 和 `terminal.docker_extra_args`。完整配置请参见 [配置 → Docker 后端](configuration.md#docker-backend)。
|
||||
:::
|
||||
|
||||
## Docker Compose 示例
|
||||
|
||||
对于同时运行 gateway 和 dashboard 的持久化部署,使用 `docker-compose.yaml` 更为方便:
|
||||
|
||||
```yaml
|
||||
services:
|
||||
hermes:
|
||||
image: nousresearch/hermes-agent:latest
|
||||
container_name: hermes
|
||||
restart: unless-stopped
|
||||
command: gateway run
|
||||
ports:
|
||||
- "8642:8642" # gateway API
|
||||
- "9119:9119" # dashboard(仅在 HERMES_DASHBOARD=1 时生效)
|
||||
volumes:
|
||||
- ~/.hermes:/opt/data
|
||||
environment:
|
||||
- HERMES_DASHBOARD=1
|
||||
# 取消注释以直接转发特定环境变量而非使用 .env 文件:
|
||||
# - ANTHROPIC_API_KEY=${ANTHROPIC_API_KEY}
|
||||
# - OPENAI_API_KEY=${OPENAI_API_KEY}
|
||||
# - TELEGRAM_BOT_TOKEN=${TELEGRAM_BOT_TOKEN}
|
||||
deploy:
|
||||
resources:
|
||||
limits:
|
||||
memory: 4G
|
||||
cpus: "2.0"
|
||||
```
|
||||
|
||||
使用 `docker compose up -d` 启动,使用 `docker compose logs -f` 查看日志。Dashboard 输出以 `[dashboard]` 为前缀,便于从 gateway 日志中过滤。
|
||||
|
||||
## 资源限制
|
||||
|
||||
Hermes 容器需要适量资源。推荐最低配置:
|
||||
|
||||
| 资源 | 最低 | 推荐 |
|
||||
|----------|---------|-------------|
|
||||
| 内存 | 1 GB | 2–4 GB |
|
||||
| CPU | 1 核 | 2 核 |
|
||||
| 磁盘(数据卷) | 500 MB | 2+ GB(随会话/技能增长) |
|
||||
|
||||
浏览器自动化(Playwright/Chromium)是最耗内存的功能。如果不需要浏览器工具,1 GB 即可。启用浏览器工具时,请至少分配 2 GB。
|
||||
|
||||
在 Docker 中设置限制:
|
||||
|
||||
```sh
|
||||
docker run -d \
|
||||
--name hermes \
|
||||
--restart unless-stopped \
|
||||
--memory=4g --cpus=2 \
|
||||
-v ~/.hermes:/opt/data \
|
||||
nousresearch/hermes-agent gateway run
|
||||
```
|
||||
|
||||
## Dockerfile 说明
|
||||
|
||||
官方镜像基于 `debian:13.4`,包含:
|
||||
|
||||
- Python 3 及所有 Hermes 依赖(`uv pip install -e ".[all]"`)
|
||||
- Node.js + npm(用于浏览器自动化和 WhatsApp 桥接)
|
||||
- Playwright 与 Chromium(`npx playwright install --with-deps chromium --only-shell`)
|
||||
- ripgrep、ffmpeg、git 和 `xz-utils` 作为系统工具
|
||||
- **`docker-cli`** — 使容器内运行的 agent 可以驱动宿主机的 Docker 守护进程(绑定挂载 `/var/run/docker.sock` 以启用),用于 `docker build`、`docker run`、容器检查等操作
|
||||
- **`openssh-client`** — 从容器内启用 [SSH 终端后端](/user-guide/configuration#ssh-backend)。SSH 后端调用系统 `ssh` 二进制文件;若缺少此组件,在容器化安装中会静默失败
|
||||
- WhatsApp 桥接(`scripts/whatsapp-bridge/`)
|
||||
- **[`s6-overlay`](https://github.com/just-containers/s6-overlay) v3** 作为 PID 1(替代旧版 `tini`)——监管 dashboard 和各 profile gateway,崩溃后自动重启,回收僵尸子进程,并转发信号
|
||||
|
||||
容器的 `ENTRYPOINT` 是 s6-overlay 的 `/init`。启动时:
|
||||
1. 以 root 身份运行 `/etc/cont-init.d/01-hermes-setup`(即 `docker/stage2-hook.sh`):可选的 UID/GID 重映射、修复卷所有权、首次启动时初始化 `.env` / `config.yaml` / `SOUL.md`、同步内置技能。
|
||||
2. 运行 `/etc/cont-init.d/02-reconcile-profiles`(即 `hermes_cli.container_boot`):遍历 `$HERMES_HOME/profiles/<name>/`,在 `/run/service/gateway-<profile>/` 下重建各 profile 的 gateway s6 服务槽,并仅自动启动上次记录状态为 `running` 的 profile(参见 [Per-profile gateway 监管](#per-profile-gateway-supervision))。
|
||||
3. 启动静态的 `main-hermes` 和 `dashboard` s6-rc 服务。
|
||||
4. 将容器的 CMD 作为主程序 exec(`/opt/hermes/docker/main-wrapper.sh`),根据用户传给 `docker run` 的参数进行路由:
|
||||
- 无参数 → `hermes`(默认)
|
||||
- 第一个参数是 PATH 上的可执行文件(如 `sleep`、`bash`)→ 直接 exec
|
||||
- 其他情况 → `hermes <args>`(子命令透传)
|
||||
主程序退出时容器退出,并使用其退出码。
|
||||
|
||||
:::warning 与 pre-s6 镜像的破坏性变更
|
||||
容器 ENTRYPOINT 现在是 `/init`(s6-overlay),而非 `/usr/bin/tini`。所有五种已记录的 `docker run` 调用模式(无参数、`chat -q "…"`、`sleep infinity`、`bash`、`--tui`)的行为与基于 tini 的镜像完全相同。如果你有依赖 tini 特定信号行为或硬编码 `/usr/bin/tini --` 调用的下游封装,请固定到之前的镜像标签。
|
||||
:::
|
||||
|
||||
:::warning 权限模型
|
||||
除非你在命令链中保留 `/init`(或等效的旧版 `docker/entrypoint.sh` shim,它会转发到 stage2 hook),否则不要覆盖镜像入口点。s6-overlay 的 `/init` 以 root 运行,以便在首次启动时对卷执行 chown,然后通过 `s6-setuidgid` 为每个受监管的服务**以及**主程序降权至 `hermes` 用户。在官方镜像内以 root 启动 `hermes gateway run` 默认会被拒绝,因为这可能在 `/opt/data` 中留下 root 所有的文件,导致后续 dashboard 或 gateway 启动失败。仅在你有意接受该风险时才设置 `HERMES_ALLOW_ROOT_GATEWAY=1`。
|
||||
:::
|
||||
|
||||
### Per-profile gateway 监管
|
||||
|
||||
在容器内,每个通过 `hermes profile create <name>` 创建的 profile 都会自动在 `/run/service/gateway-<name>/` 注册一个受 s6 监管的 gateway 服务。你在宿主机上运行的生命周期命令在此同样适用:
|
||||
|
||||
```sh
|
||||
hermes profile create coder # 注册 gateway-coder s6 槽
|
||||
hermes -p coder gateway start # s6-svc -u → 受监管的 gateway
|
||||
hermes -p coder gateway stop # s6-svc -d → 服务停止
|
||||
hermes -p coder gateway restart # s6-svc -t → 向 supervisor 发送 SIGTERM
|
||||
hermes profile delete coder # 拆除 s6 槽
|
||||
```
|
||||
|
||||
**相比 pre-s6 镜像的监管优势:**
|
||||
|
||||
- Gateway 崩溃后由 `s6-supervise` 在约 1 秒退避后自动重启。
|
||||
- Dashboard 崩溃后自动重启(设置 `HERMES_DASHBOARD=1` 以启动)。
|
||||
- `docker restart` 保留运行中的 gateway:cont-init 协调器读取 `$HERMES_HOME/profiles/<name>/gateway_state.json`,若上次记录状态为 `running` 则恢复该槽。已停止的 gateway 保持停止状态。
|
||||
- 各 profile 的 gateway 日志持久化于 `$HERMES_HOME/logs/gateways/<profile>/current`(由 `s6-log` 轮转),协调器的操作记录在每次启动时追加到 `$HERMES_HOME/logs/container-boot.log`。
|
||||
|
||||
在容器内执行 `hermes status` 会显示 `Manager: s6 (container supervisor)`。使用 `/command/s6-svstat /run/service/gateway-<name>` 查看原始 supervisor 状态(注意 `/command/` 仅在监管树进程的 PATH 中;从 `docker exec` 调用时请传入绝对路径)。
|
||||
|
||||
## 升级
|
||||
|
||||
拉取最新镜像并重建容器。你的数据目录不受影响。
|
||||
|
||||
```sh
|
||||
docker pull nousresearch/hermes-agent:latest
|
||||
docker rm -f hermes
|
||||
docker run -d \
|
||||
--name hermes \
|
||||
--restart unless-stopped \
|
||||
-v ~/.hermes:/opt/data \
|
||||
nousresearch/hermes-agent gateway run
|
||||
```
|
||||
|
||||
或使用 Docker Compose:
|
||||
|
||||
```sh
|
||||
docker compose pull
|
||||
docker compose up -d
|
||||
```
|
||||
|
||||
## 技能与凭据文件
|
||||
|
||||
当使用 Docker 作为执行环境时(不是上述方法,而是 agent 在 Docker 沙箱内运行命令——参见 [配置 → Docker 后端](./configuration.md#docker-backend)),Hermes 为所有工具调用复用单个长期运行的容器,并自动将技能目录(`~/.hermes/skills/`)和技能声明的所有凭据文件以只读卷的形式绑定挂载到该容器中。技能脚本、模板和引用在沙箱内无需手动配置即可使用,由于容器在 Hermes 进程的整个生命周期内持续存在,你安装的任何依赖或写入的文件都会在下次工具调用时保留。
|
||||
|
||||
SSH 和 Modal 后端也会进行相同的同步——技能和凭据文件在每次命令执行前通过 rsync 或 Modal mount API 上传。
|
||||
|
||||
## 在容器中安装更多工具
|
||||
|
||||
官方镜像预装了一套精选工具(参见 [Dockerfile 说明](#what-the-dockerfile-does)),但并非 agent 可能需要的每个工具都已预装。以下是五种推荐方式,按工作量和持久性递增排列。
|
||||
|
||||
### npm 或 Python 工具——使用 `npx` 或 `uvx`
|
||||
|
||||
对于发布到 npm 或 PyPI 的任何工具,指示 Hermes 通过 `npx`(npm)或 `uvx`(Python)运行,并将该命令记入其持久记忆。如果工具需要配置文件或凭据,指示其将这些文件放在 `/opt/data` 下(如 `/opt/data/<tool>/config.yaml`)。
|
||||
|
||||
依赖按需获取并在容器生命周期内缓存。写入 `/opt/data` 的配置在容器重启后仍然存在,因为它位于绑定挂载的宿主机目录上。包缓存本身在 `docker rm` 后会重建,但 `npx` 和 `uvx` 会在下次运行工具时透明地重新获取。
|
||||
|
||||
### 其他工具(apt 包、二进制文件)——安装并记住
|
||||
|
||||
对于 npm 或 PyPI 之外的工具——`apt` 包、预构建二进制文件、镜像中未包含的语言运行时——指示 Hermes 如何安装(如 `apt-get update && apt-get install -y <package>`),并告知它记住该安装命令。工具在容器剩余生命周期内持续可用,Hermes 在容器重启后下次需要该工具时会重新运行安装命令。
|
||||
|
||||
这种方式适合安装快速且偶尔使用的工具。对于频繁使用的工具,建议采用下一种方式。
|
||||
|
||||
### 持久安装——构建派生镜像
|
||||
|
||||
当工具必须在每次容器启动时立即可用且无需重新安装延迟时,构建一个继承自 `nousresearch/hermes-agent` 并在层中安装该工具的新镜像:
|
||||
|
||||
```dockerfile
|
||||
FROM nousresearch/hermes-agent:latest
|
||||
|
||||
USER root
|
||||
RUN apt-get update \
|
||||
&& apt-get install -y --no-install-recommends <your-package> \
|
||||
&& rm -rf /var/lib/apt/lists/*
|
||||
USER hermes
|
||||
```
|
||||
|
||||
构建并替换官方镜像使用:
|
||||
|
||||
```sh
|
||||
docker build -t my-hermes:latest .
|
||||
docker run -d \
|
||||
--name hermes \
|
||||
--restart unless-stopped \
|
||||
-v ~/.hermes:/opt/data \
|
||||
-p 8642:8642 \
|
||||
my-hermes:latest gateway run
|
||||
```
|
||||
|
||||
入口点脚本和 `/opt/data` 语义原样继承,本页其余内容仍然适用。拉取更新的上游 `nousresearch/hermes-agent` 时记得重新构建镜像。
|
||||
|
||||
### 复杂工具或多服务栈——运行 sidecar 容器
|
||||
|
||||
对于自带服务(数据库、Web 服务器、队列、无头浏览器集群)或过于庞大而不适合放在 Hermes 容器内的工具,将其作为独立容器运行在共享 Docker 网络上。Hermes 通过容器名称访问 sidecar,与访问本地推理服务器的方式相同(参见 [连接本地推理服务器](#connecting-to-local-inference-servers-vllm-ollama-etc))。
|
||||
|
||||
```yaml
|
||||
services:
|
||||
hermes:
|
||||
image: nousresearch/hermes-agent:latest
|
||||
container_name: hermes
|
||||
restart: unless-stopped
|
||||
command: gateway run
|
||||
ports:
|
||||
- "8642:8642"
|
||||
volumes:
|
||||
- ~/.hermes:/opt/data
|
||||
networks:
|
||||
- hermes-net
|
||||
|
||||
my-tool:
|
||||
image: example/my-tool:latest
|
||||
container_name: my-tool
|
||||
restart: unless-stopped
|
||||
networks:
|
||||
- hermes-net
|
||||
|
||||
networks:
|
||||
hermes-net:
|
||||
driver: bridge
|
||||
```
|
||||
|
||||
在 Hermes 容器内,sidecar 可通过 `http://my-tool:<port>` 访问(或其提供的任何协议)。这种模式使每个服务的生命周期、资源限制和升级节奏保持独立,避免因单个工具的依赖而使 Hermes 镜像臃肿。
|
||||
|
||||
### 广泛有用的工具——提交 issue 或 pull request
|
||||
|
||||
如果某个工具可能对大多数 Hermes Agent 用户有用,考虑将其贡献到上游,而不是在私有派生镜像中维护。在 [hermes-agent 仓库](https://github.com/NousResearch/hermes-agent)提交 issue 或 pull request,描述该工具及其使用场景。被纳入官方镜像的工具惠及所有用户,并避免了维护下游 fork 的开销。
|
||||
|
||||
## 连接本地推理服务器(vLLM、Ollama 等)
|
||||
|
||||
在 Docker 中运行 Hermes 且推理服务器(vLLM、Ollama、text-generation-inference 等)也在宿主机或另一个容器中运行时,网络配置需要额外注意。
|
||||
|
||||
### Docker Compose(推荐)
|
||||
|
||||
将两个服务放在同一 Docker 网络上。这是最可靠的方式:
|
||||
|
||||
```yaml
|
||||
services:
|
||||
vllm:
|
||||
image: vllm/vllm-openai:latest
|
||||
container_name: vllm
|
||||
command: >
|
||||
--model Qwen/Qwen2.5-7B-Instruct
|
||||
--served-model-name my-model
|
||||
--host 0.0.0.0
|
||||
--port 8000
|
||||
ports:
|
||||
- "8000:8000"
|
||||
networks:
|
||||
- hermes-net
|
||||
deploy:
|
||||
resources:
|
||||
reservations:
|
||||
devices:
|
||||
- capabilities: [gpu]
|
||||
|
||||
hermes:
|
||||
image: nousresearch/hermes-agent:latest
|
||||
container_name: hermes
|
||||
restart: unless-stopped
|
||||
command: gateway run
|
||||
ports:
|
||||
- "8642:8642"
|
||||
volumes:
|
||||
- ~/.hermes:/opt/data
|
||||
networks:
|
||||
- hermes-net
|
||||
|
||||
networks:
|
||||
hermes-net:
|
||||
driver: bridge
|
||||
```
|
||||
|
||||
然后在 `~/.hermes/config.yaml` 中,使用**容器名称**作为主机名:
|
||||
|
||||
```yaml
|
||||
model:
|
||||
provider: custom
|
||||
model: my-model
|
||||
base_url: http://vllm:8000/v1
|
||||
api_key: "none"
|
||||
```
|
||||
|
||||
:::tip 关键点
|
||||
- 使用**容器名称**(`vllm`)作为主机名——而非 `localhost` 或 `127.0.0.1`,它们指向 Hermes 容器本身。
|
||||
- `model` 值必须与传给 vLLM 的 `--served-model-name` 一致。
|
||||
- 将 `api_key` 设为任意非空字符串(vLLM 要求该请求头,但默认不验证其值)。
|
||||
- `base_url` 末尾**不要**加斜杠。
|
||||
:::
|
||||
|
||||
### 独立 Docker run(无 Compose)
|
||||
|
||||
如果推理服务器直接在宿主机上运行(不在 Docker 中),在 macOS/Windows 上使用 `host.docker.internal`,在 Linux 上使用 `--network host`:
|
||||
|
||||
**macOS / Windows:**
|
||||
|
||||
```sh
|
||||
docker run -d \
|
||||
--name hermes \
|
||||
-v ~/.hermes:/opt/data \
|
||||
-p 8642:8642 \
|
||||
nousresearch/hermes-agent gateway run
|
||||
```
|
||||
|
||||
```yaml
|
||||
# config.yaml
|
||||
model:
|
||||
provider: custom
|
||||
model: my-model
|
||||
base_url: http://host.docker.internal:8000/v1
|
||||
api_key: "none"
|
||||
```
|
||||
|
||||
**Linux(host 网络):**
|
||||
|
||||
```sh
|
||||
docker run -d \
|
||||
--name hermes \
|
||||
--network host \
|
||||
-v ~/.hermes:/opt/data \
|
||||
nousresearch/hermes-agent gateway run
|
||||
```
|
||||
|
||||
```yaml
|
||||
# config.yaml
|
||||
model:
|
||||
provider: custom
|
||||
model: my-model
|
||||
base_url: http://127.0.0.1:8000/v1
|
||||
api_key: "none"
|
||||
```
|
||||
|
||||
:::warning 使用 `--network host` 时,`-p` 标志会被忽略——所有容器端口直接暴露在宿主机上。
|
||||
:::
|
||||
|
||||
### 验证连通性
|
||||
|
||||
从 Hermes 容器内部确认推理服务器可达:
|
||||
|
||||
```sh
|
||||
docker exec hermes curl -s http://vllm:8000/v1/models
|
||||
```
|
||||
|
||||
你应该看到列出已服务模型的 JSON 响应。如果失败,请检查:
|
||||
|
||||
1. 两个容器是否在同一 Docker 网络上(`docker network inspect hermes-net`)
|
||||
2. 推理服务器是否监听 `0.0.0.0` 而非 `127.0.0.1`
|
||||
3. 端口号是否匹配
|
||||
|
||||
### Ollama
|
||||
|
||||
Ollama 的配置方式相同。如果 Ollama 在宿主机上运行,使用 `host.docker.internal:11434`(macOS/Windows)或 `127.0.0.1:11434`(Linux 使用 `--network host`)。如果 Ollama 在同一 Docker 网络的独立容器中运行:
|
||||
|
||||
```yaml
|
||||
model:
|
||||
provider: custom
|
||||
model: llama3
|
||||
base_url: http://ollama:11434/v1
|
||||
api_key: "none"
|
||||
```
|
||||
|
||||
## 故障排查
|
||||
|
||||
### 容器立即退出
|
||||
|
||||
检查日志:`docker logs hermes`。常见原因:
|
||||
- `.env` 文件缺失或无效——先以交互方式运行以完成设置
|
||||
- 开放端口时存在端口冲突
|
||||
|
||||
### "Permission denied" 错误
|
||||
|
||||
容器的 stage2 hook 通过 `s6-setuidgid` 在每个受监管的服务内将权限降至非 root 用户 `hermes`(UID 10000)。如果宿主机的 `~/.hermes/` 由不同 UID 拥有,请设置 `HERMES_UID`/`HERMES_GID` 以匹配宿主机用户,或确保数据目录可写:
|
||||
|
||||
```sh
|
||||
chmod -R 755 ~/.hermes
|
||||
```
|
||||
|
||||
### 浏览器工具无法使用
|
||||
|
||||
Playwright 需要共享内存。在 Docker run 命令中添加 `--shm-size=1g`:
|
||||
|
||||
```sh
|
||||
docker run -d \
|
||||
--name hermes \
|
||||
--shm-size=1g \
|
||||
-v ~/.hermes:/opt/data \
|
||||
nousresearch/hermes-agent gateway run
|
||||
```
|
||||
|
||||
### 网络问题后 gateway 无法重连
|
||||
|
||||
`--restart unless-stopped` 标志可处理大多数瞬时故障。如果 gateway 卡住,重启容器:
|
||||
|
||||
```sh
|
||||
docker restart hermes
|
||||
```
|
||||
|
||||
### 检查容器健康状态
|
||||
|
||||
```sh
|
||||
docker logs --tail 50 hermes # 最近日志
|
||||
docker run -it --rm nousresearch/hermes-agent:latest version # 验证版本
|
||||
docker stats hermes # 资源使用情况
|
||||
```
|
||||
@ -0,0 +1,275 @@
|
||||
---
|
||||
sidebar_position: 11
|
||||
title: "ACP 编辑器集成"
|
||||
description: "在 VS Code、Zed 和 JetBrains 等兼容 ACP 的编辑器中使用 Hermes Agent"
|
||||
---
|
||||
|
||||
# ACP 编辑器集成
|
||||
|
||||
Hermes Agent 可作为 ACP 服务器运行,让兼容 ACP 的编辑器通过 stdio 与 Hermes 通信并渲染:
|
||||
|
||||
- 聊天消息
|
||||
- 工具活动
|
||||
- 文件差异
|
||||
- 终端命令
|
||||
- 审批 prompt(提示词)
|
||||
- 流式思考 / 响应块
|
||||
|
||||
当你希望 Hermes 表现得像编辑器原生的编码 agent,而非独立 CLI 或消息机器人时,ACP 是合适的选择。
|
||||
|
||||
## Hermes 在 ACP 模式下暴露的内容
|
||||
|
||||
Hermes 使用专为编辑器工作流设计的精选 `hermes-acp` 工具集运行,包括:
|
||||
|
||||
- 文件工具:`read_file`、`write_file`、`patch`、`search_files`
|
||||
- 终端工具:`terminal`、`process`
|
||||
- 网页/浏览器工具
|
||||
- 记忆、待办事项、会话搜索
|
||||
- skills
|
||||
- `execute_code` 和 `delegate_task`
|
||||
- 视觉
|
||||
|
||||
它有意排除了不适合典型编辑器 UX 的功能,例如消息投递和 cronjob 管理。
|
||||
|
||||
## 安装
|
||||
|
||||
正常安装 Hermes 后,添加 ACP 扩展:
|
||||
|
||||
```bash
|
||||
pip install -e '.[acp]'
|
||||
```
|
||||
|
||||
这将安装 `agent-client-protocol` 依赖并启用:
|
||||
|
||||
- `hermes acp`
|
||||
- `hermes-acp`
|
||||
- `python -m acp_adapter`
|
||||
|
||||
对于 Zed registry 安装,Zed 通过官方 ACP Registry 条目启动 Hermes。该条目使用 `uvx` 发行版运行:
|
||||
|
||||
```bash
|
||||
uvx --from 'hermes-agent[acp]==<version>' hermes-acp
|
||||
```
|
||||
|
||||
使用 registry 安装路径前,请确保 `uv` 已在 `PATH` 中可用。
|
||||
|
||||
## 启动 ACP 服务器
|
||||
|
||||
以下任意命令均可以 ACP 模式启动 Hermes:
|
||||
|
||||
```bash
|
||||
hermes acp
|
||||
```
|
||||
|
||||
```bash
|
||||
hermes-acp
|
||||
```
|
||||
|
||||
```bash
|
||||
python -m acp_adapter
|
||||
```
|
||||
|
||||
Hermes 将日志输出到 stderr,以保留 stdout 用于 ACP JSON-RPC 流量。
|
||||
|
||||
非交互式检查:
|
||||
|
||||
```bash
|
||||
hermes acp --version
|
||||
hermes acp --check
|
||||
```
|
||||
|
||||
### 浏览器工具(可选)
|
||||
|
||||
浏览器工具(`browser_navigate`、`browser_click` 等)依赖 `agent-browser` npm 包和 Chromium,这些不包含在 Python wheel 中。通过以下命令安装:
|
||||
|
||||
```bash
|
||||
hermes acp --setup-browser # 交互式(下载约 400 MB 前会提示确认)
|
||||
hermes acp --setup-browser --yes # 非交互式接受下载
|
||||
```
|
||||
|
||||
这是独立命令。Zed registry 的终端认证流程(`hermes acp --setup`)在模型选择后也会将浏览器引导作为后续问题提供,因此大多数用户无需直接运行 `--setup-browser`。
|
||||
|
||||
具体操作:
|
||||
|
||||
- 若缺少 Node.js 22 LTS,将其安装到 `~/.hermes/node/`
|
||||
- 将 `npm install -g agent-browser @askjo/camofox-browser` 安装到该前缀(无需 sudo — `npm` 的 `--prefix` 指向用户可写的 Hermes 管理 Node)
|
||||
- 安装 Playwright Chromium,或在检测到系统 Chrome/Chromium 时使用已有版本
|
||||
|
||||
该引导过程是幂等的——重复运行速度很快,已完成的步骤会被跳过。
|
||||
|
||||
## 编辑器设置
|
||||
|
||||
### VS Code
|
||||
|
||||
安装 [ACP Client](https://marketplace.visualstudio.com/items?itemName=formulahendry.acp-client) 扩展。
|
||||
|
||||
连接步骤:
|
||||
|
||||
1. 从活动栏打开 ACP Client 面板。
|
||||
2. 从内置 agent 列表中选择 **Hermes Agent**。
|
||||
3. 连接并开始聊天。
|
||||
|
||||
如需手动定义 Hermes,通过 VS Code 设置在 `acp.agents` 下添加:
|
||||
|
||||
```json
|
||||
{
|
||||
"acp.agents": {
|
||||
"Hermes Agent": {
|
||||
"command": "hermes",
|
||||
"args": ["acp"]
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Zed
|
||||
|
||||
Zed v0.221.x 及更新版本通过官方 ACP Registry 安装外部 agent。
|
||||
|
||||
1. 打开 Agent 面板。
|
||||
2. 点击 **Add Agent**,或运行 `zed: acp registry` 命令。
|
||||
3. 搜索 **Hermes Agent**。
|
||||
4. 安装后启动新的 Hermes 外部 agent 线程。
|
||||
|
||||
前提条件:
|
||||
|
||||
- 先通过 `hermes model` 配置 Hermes provider 凭据,或在 `~/.hermes/.env` / `~/.hermes/config.yaml` 中设置。
|
||||
- 安装 `uv`,以便 registry 启动器可以运行 `uvx --from 'hermes-agent[acp]==<version>' hermes-acp`。
|
||||
|
||||
在 registry 条目可用之前进行本地开发时,在 Zed 设置中使用自定义 agent 服务器:
|
||||
|
||||
```json
|
||||
{
|
||||
"agent_servers": {
|
||||
"hermes-agent": {
|
||||
"type": "custom",
|
||||
"command": "hermes",
|
||||
"args": ["acp"]
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### JetBrains
|
||||
|
||||
使用兼容 ACP 的插件并将其指向:
|
||||
|
||||
```text
|
||||
/path/to/hermes-agent/acp_registry
|
||||
```
|
||||
|
||||
## Registry 清单
|
||||
|
||||
Hermes 官方 ACP Registry 元数据的源文件位于:
|
||||
|
||||
```text
|
||||
acp_registry/agent.json
|
||||
acp_registry/icon.svg
|
||||
```
|
||||
|
||||
上游 registry PR 将这些文件复制到 `agentclientprotocol/registry` 中的顶层 `hermes-agent/` 目录。
|
||||
|
||||
Registry 条目使用直接指向 `hermes-agent` PyPI 发行版的 `uvx` 发行版:
|
||||
|
||||
```text
|
||||
uvx --from 'hermes-agent[acp]==<version>' hermes-acp
|
||||
```
|
||||
|
||||
Registry CI 会验证固定版本是否存在于 PyPI,因此清单的 `version` 和 uvx `package` 固定版本必须始终与 `pyproject.toml` 匹配。`scripts/release.py` 会自动保持它们同步。
|
||||
|
||||
## 配置与凭据
|
||||
|
||||
ACP 模式使用与 CLI 相同的 Hermes 配置:
|
||||
|
||||
- `~/.hermes/.env`
|
||||
- `~/.hermes/config.yaml`
|
||||
- `~/.hermes/skills/`
|
||||
- `~/.hermes/state.db`
|
||||
|
||||
Provider 解析使用 Hermes 的正常运行时解析器,因此 ACP 继承当前配置的 provider 和凭据。Hermes 还为首次运行的 registry 客户端提供终端认证方法(`--setup`);这将打开 Hermes 的交互式模型/provider 设置。
|
||||
|
||||
## 会话行为
|
||||
|
||||
ACP 会话在服务器运行期间由 ACP 适配器的内存会话管理器跟踪。
|
||||
|
||||
每个会话存储:
|
||||
|
||||
- 会话 ID
|
||||
- 工作目录
|
||||
- 已选模型
|
||||
- 当前对话历史
|
||||
- 取消事件
|
||||
|
||||
底层 `AIAgent` 仍使用 Hermes 的正常持久化/日志路径,但 ACP 的 `list/load/resume/fork` 仅限于当前运行的 ACP 服务器进程。
|
||||
|
||||
## 工作目录行为
|
||||
|
||||
ACP 会话将编辑器的 cwd 绑定到 Hermes 任务 ID,使文件和终端工具相对于编辑器工作区运行,而非服务器进程的 cwd。
|
||||
|
||||
## 审批
|
||||
|
||||
危险的终端命令可作为审批 prompt 路由回编辑器。ACP 审批选项比 CLI 流程更简单:
|
||||
|
||||
- 允许一次
|
||||
- 始终允许
|
||||
- 拒绝
|
||||
|
||||
超时或出错时,审批桥接会拒绝请求。
|
||||
|
||||
### 会话范围的编辑自动审批
|
||||
|
||||
ACP 在*允许一次*和*始终允许*之间提供第三层:**允许本次会话**。在编辑器的权限提示中选择此选项,会将审批记录在当前 ACP 会话内——该会话中所有后续匹配命令无需提示即可通过,但新的 ACP 会话(或重启编辑器)会重置状态,并在第一次时重新提示。
|
||||
|
||||
| 选项 | 编辑器标签 | 范围 | 重启后是否持久化 |
|
||||
|---|---|---|---|
|
||||
| `allow_once` | 允许一次 | 本次工具调用 | 否 |
|
||||
| `allow_session` | 允许本次会话 | 本 ACP 会话中所有匹配调用 | 否——会话结束时清除 |
|
||||
| `allow_always` | 始终允许 | 所有未来会话 | 是(写入 Hermes 永久允许列表) |
|
||||
| `deny` | 拒绝 | 本次工具调用 | 否 |
|
||||
|
||||
`allow_session` 是编辑器工作流的正确默认选项——你在任务期间信任 agent,但不想授予长期允许列表条目。安全权衡很直接:范围越广,编辑器打断你的次数越少,行为异常的 agent(或 prompt 注入)在被发现前能造成的损害也越大。对不熟悉的命令从 `allow_once` 开始;在看到 agent 多次正确运行相同模式后升级为 `allow_session`;将 `allow_always` 保留给你永远信任的真正幂等命令(例如 `git status`)。
|
||||
|
||||
ACP 桥接将这些选项映射到 Hermes 的内部审批语义——`allow_always` 与 CLI 相同地写入永久允许列表条目,而 `allow_session` 仅影响当前 ACP 会话的进程内审批缓存。
|
||||
|
||||
## 故障排查
|
||||
|
||||
### ACP agent 未出现在编辑器中
|
||||
|
||||
检查:
|
||||
|
||||
- 在 Zed 中,使用 `zed: acp registry` 打开 ACP Registry 并搜索 **Hermes Agent**。
|
||||
- 对于手动/本地开发,验证自定义 `agent_servers` 命令是否指向 `hermes acp`。
|
||||
- Hermes 已安装且在 PATH 中。
|
||||
- ACP 扩展已安装(`pip install -e '.[acp]'`)。
|
||||
- 如果从官方 Zed registry 条目启动,`uv` 已安装。
|
||||
|
||||
### ACP 启动后立即报错
|
||||
|
||||
尝试以下检查:
|
||||
|
||||
```bash
|
||||
hermes acp --version
|
||||
hermes acp --check
|
||||
hermes doctor
|
||||
hermes status
|
||||
```
|
||||
|
||||
### 缺少凭据
|
||||
|
||||
ACP 模式使用 Hermes 现有的 provider 设置。通过以下方式配置凭据:
|
||||
|
||||
```bash
|
||||
hermes model
|
||||
```
|
||||
|
||||
或编辑 `~/.hermes/.env`。Registry 客户端也可以触发 Hermes 的终端认证流程,该流程运行相同的交互式 provider/模型设置。
|
||||
|
||||
### Zed registry 启动器找不到 uv
|
||||
|
||||
从官方 uv 安装文档安装 `uv`,然后从 Zed 重试 Hermes Agent 线程。
|
||||
|
||||
## 另请参阅
|
||||
|
||||
- [ACP 内部机制](../../developer-guide/acp-internals.md)
|
||||
- [Provider 运行时解析](../../developer-guide/provider-runtime.md)
|
||||
- [工具运行时](../../developer-guide/tools-runtime.md)
|
||||
@ -0,0 +1,441 @@
|
||||
---
|
||||
sidebar_position: 14
|
||||
title: "API 服务器"
|
||||
description: "将 hermes-agent 作为 OpenAI 兼容的 API 暴露给任意前端"
|
||||
---
|
||||
|
||||
# API 服务器
|
||||
|
||||
API 服务器将 hermes-agent 作为 OpenAI 兼容的 HTTP 端点暴露出来。任何支持 OpenAI 格式的前端——Open WebUI、LobeChat、LibreChat、NextChat、ChatBox 以及数百个其他工具——都可以连接到 hermes-agent 并将其用作后端。
|
||||
|
||||
你的 agent 使用完整工具集(终端、文件操作、网络搜索、记忆、技能)处理请求,并返回最终响应。在流式传输时,工具进度指示器会内联显示,让前端能够展示 agent 正在执行的操作。
|
||||
|
||||
:::tip 一个后端同时覆盖模型与工具
|
||||
Hermes 本身需要配置好 provider(提供商)和工具后端,API 服务器才能发挥作用。[Nous Portal](/user-guide/features/tool-gateway) 订阅同时处理两者——300+ 个模型,以及通过 Tool Gateway 提供的网络/图像/TTS/浏览器功能。在启动 API 服务器之前运行一次 `hermes setup --portal`,Open WebUI 或 LobeChat 等前端即可获得一个完整配备工具的后端。
|
||||
:::
|
||||
|
||||
## 快速开始
|
||||
|
||||
### 1. 启用 API 服务器
|
||||
|
||||
在 `~/.hermes/.env` 中添加:
|
||||
|
||||
```bash
|
||||
API_SERVER_ENABLED=true
|
||||
API_SERVER_KEY=change-me-local-dev
|
||||
# 可选:仅当浏览器需要直接调用 Hermes 时
|
||||
# API_SERVER_CORS_ORIGINS=http://localhost:3000
|
||||
```
|
||||
|
||||
### 2. 启动 gateway
|
||||
|
||||
```bash
|
||||
hermes gateway
|
||||
```
|
||||
|
||||
你将看到:
|
||||
|
||||
```
|
||||
[API Server] API server listening on http://127.0.0.1:8642
|
||||
```
|
||||
|
||||
### 3. 连接前端
|
||||
|
||||
将任何 OpenAI 兼容客户端指向 `http://localhost:8642/v1`:
|
||||
|
||||
```bash
|
||||
# 使用 curl 测试
|
||||
curl http://localhost:8642/v1/chat/completions \
|
||||
-H "Authorization: Bearer change-me-local-dev" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{"model": "hermes-agent", "messages": [{"role": "user", "content": "Hello!"}]}'
|
||||
```
|
||||
|
||||
或连接 Open WebUI、LobeChat 或其他任意前端——参见 [Open WebUI 集成指南](/user-guide/messaging/open-webui)获取分步说明。
|
||||
|
||||
## 端点
|
||||
|
||||
### POST /v1/chat/completions
|
||||
|
||||
标准 OpenAI Chat Completions 格式。无状态——完整对话通过每次请求的 `messages` 数组传入。
|
||||
|
||||
**请求:**
|
||||
```json
|
||||
{
|
||||
"model": "hermes-agent",
|
||||
"messages": [
|
||||
{"role": "system", "content": "You are a Python expert."},
|
||||
{"role": "user", "content": "Write a fibonacci function"}
|
||||
],
|
||||
"stream": false
|
||||
}
|
||||
```
|
||||
|
||||
**响应:**
|
||||
```json
|
||||
{
|
||||
"id": "chatcmpl-abc123",
|
||||
"object": "chat.completion",
|
||||
"created": 1710000000,
|
||||
"model": "hermes-agent",
|
||||
"choices": [{
|
||||
"index": 0,
|
||||
"message": {"role": "assistant", "content": "Here's a fibonacci function..."},
|
||||
"finish_reason": "stop"
|
||||
}],
|
||||
"usage": {"prompt_tokens": 50, "completion_tokens": 200, "total_tokens": 250}
|
||||
}
|
||||
```
|
||||
|
||||
**内联图像输入:** 用户消息可以将 `content` 作为 `text` 和 `image_url` 部分的数组发送。支持远程 `http(s)` URL 和 `data:image/...` URL:
|
||||
|
||||
```json
|
||||
{
|
||||
"model": "hermes-agent",
|
||||
"messages": [
|
||||
{
|
||||
"role": "user",
|
||||
"content": [
|
||||
{"type": "text", "text": "What is in this image?"},
|
||||
{"type": "image_url", "image_url": {"url": "https://example.com/cat.png", "detail": "high"}}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
上传的文件(`file` / `input_file` / `file_id`)和非图像 `data:` URL 将返回 `400 unsupported_content_type`。
|
||||
|
||||
**流式传输**(`"stream": true`):返回逐 token 响应块的 Server-Sent Events(SSE)。对于 **Chat Completions**,流使用标准 `chat.completion.chunk` 事件,以及 Hermes 自定义的 `hermes.tool.progress` 事件用于工具启动的 UX 展示。对于 **Responses**,流使用 OpenAI Responses 事件类型,如 `response.created`、`response.output_text.delta`、`response.output_item.added`、`response.output_item.done` 和 `response.completed`。
|
||||
|
||||
**流中的工具进度:**
|
||||
- **Chat Completions**:Hermes 发出 `event: hermes.tool.progress` 以提供工具启动可见性,同时不污染持久化的 assistant 文本。
|
||||
- **Responses**:Hermes 在 SSE 流期间发出符合规范的 `function_call` 和 `function_call_output` 输出项,让客户端能够实时渲染结构化工具 UI。
|
||||
|
||||
### POST /v1/responses
|
||||
|
||||
OpenAI Responses API 格式。通过 `previous_response_id` 支持服务端对话状态——服务器存储完整的对话历史(包括工具调用和结果),因此多轮上下文无需客户端自行管理。
|
||||
|
||||
**请求:**
|
||||
```json
|
||||
{
|
||||
"model": "hermes-agent",
|
||||
"input": "What files are in my project?",
|
||||
"instructions": "You are a helpful coding assistant.",
|
||||
"store": true
|
||||
}
|
||||
```
|
||||
|
||||
**响应:**
|
||||
```json
|
||||
{
|
||||
"id": "resp_abc123",
|
||||
"object": "response",
|
||||
"status": "completed",
|
||||
"model": "hermes-agent",
|
||||
"output": [
|
||||
{"type": "function_call", "name": "terminal", "arguments": "{\"command\": \"ls\"}", "call_id": "call_1"},
|
||||
{"type": "function_call_output", "call_id": "call_1", "output": "README.md src/ tests/"},
|
||||
{"type": "message", "role": "assistant", "content": [{"type": "output_text", "text": "Your project has..."}]}
|
||||
],
|
||||
"usage": {"input_tokens": 50, "output_tokens": 200, "total_tokens": 250}
|
||||
}
|
||||
```
|
||||
|
||||
**内联图像输入:** `input[].content` 可以包含 `input_text` 和 `input_image` 部分。支持远程 URL 和 `data:image/...` URL:
|
||||
|
||||
```json
|
||||
{
|
||||
"model": "hermes-agent",
|
||||
"input": [
|
||||
{
|
||||
"role": "user",
|
||||
"content": [
|
||||
{"type": "input_text", "text": "Describe this screenshot."},
|
||||
{"type": "input_image", "image_url": "data:image/png;base64,iVBORw0K..."}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
上传的文件(`input_file` / `file_id`)和非图像 `data:` URL 将返回 `400 unsupported_content_type`。
|
||||
|
||||
#### 使用 previous_response_id 进行多轮对话
|
||||
|
||||
链式响应以在多轮之间保持完整上下文(包括工具调用):
|
||||
|
||||
```json
|
||||
{
|
||||
"input": "Now show me the README",
|
||||
"previous_response_id": "resp_abc123"
|
||||
}
|
||||
```
|
||||
|
||||
服务器从存储的响应链重建完整对话——所有之前的工具调用和结果均被保留。链式请求还共享同一个 session,因此多轮对话在仪表板和 session 历史中显示为单个条目。
|
||||
|
||||
#### 命名对话
|
||||
|
||||
使用 `conversation` 参数代替追踪响应 ID:
|
||||
|
||||
```json
|
||||
{"input": "Hello", "conversation": "my-project"}
|
||||
{"input": "What's in src/?", "conversation": "my-project"}
|
||||
{"input": "Run the tests", "conversation": "my-project"}
|
||||
```
|
||||
|
||||
服务器自动链接到该对话中的最新响应。类似于 gateway session 的 `/title` 命令。
|
||||
|
||||
### GET /v1/responses/\{id\}
|
||||
|
||||
通过 ID 检索之前存储的响应。
|
||||
|
||||
### DELETE /v1/responses/\{id\}
|
||||
|
||||
删除存储的响应。
|
||||
|
||||
### GET /v1/models
|
||||
|
||||
将 agent 列为可用模型。广播的模型名称默认为 [profile](/user-guide/profiles) 名称(默认 profile 则为 `hermes-agent`)。大多数前端进行模型发现时需要此端点。
|
||||
|
||||
### GET /v1/capabilities
|
||||
|
||||
返回 API 服务器稳定接口的机器可读描述,供外部 UI、编排器和插件桥接使用。
|
||||
|
||||
```json
|
||||
{
|
||||
"object": "hermes.api_server.capabilities",
|
||||
"platform": "hermes-agent",
|
||||
"model": "hermes-agent",
|
||||
"auth": {"type": "bearer", "required": true},
|
||||
"features": {
|
||||
"chat_completions": true,
|
||||
"responses_api": true,
|
||||
"run_submission": true,
|
||||
"run_status": true,
|
||||
"run_events_sse": true,
|
||||
"run_stop": true
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
在集成仪表板、浏览器 UI 或控制平面时使用此端点,以便它们能够发现当前运行的 Hermes 版本是否支持 runs、流式传输、取消和 session 连续性,而无需依赖私有 Python 内部实现。
|
||||
|
||||
### GET /health
|
||||
|
||||
健康检查。返回 `{"status": "ok"}`。也可通过 **GET /v1/health** 访问,供期望 `/v1/` 前缀的 OpenAI 兼容客户端使用。
|
||||
|
||||
### GET /health/detailed
|
||||
|
||||
扩展健康检查,同时报告活跃 session、运行中的 agent 和资源使用情况。适用于监控/可观测性工具。
|
||||
|
||||
## Runs API(流式友好的替代方案)
|
||||
|
||||
除 `/v1/chat/completions` 和 `/v1/responses` 外,服务器还暴露了一个 **runs** API,适用于客户端希望订阅进度事件而非自行管理流式传输的长时 session。
|
||||
|
||||
### POST /v1/runs
|
||||
|
||||
创建新的 agent run。返回可用于订阅进度事件的 `run_id`。
|
||||
|
||||
```json
|
||||
{
|
||||
"run_id": "run_abc123",
|
||||
"status": "started"
|
||||
}
|
||||
```
|
||||
|
||||
Runs 接受简单的 `input` 字符串,以及可选的 `session_id`、`instructions`、`conversation_history` 或 `previous_response_id`。当提供 `session_id` 时,Hermes 会在 run 状态中暴露它,以便外部 UI 将 run 与自己的对话 ID 关联。
|
||||
|
||||
### GET /v1/runs/\{run_id\}
|
||||
|
||||
轮询当前 run 状态。适用于需要状态但不想保持 SSE 连接的仪表板,或在导航后重新连接的 UI。
|
||||
|
||||
```json
|
||||
{
|
||||
"object": "hermes.run",
|
||||
"run_id": "run_abc123",
|
||||
"status": "completed",
|
||||
"session_id": "space-session",
|
||||
"model": "hermes-agent",
|
||||
"output": "Done.",
|
||||
"usage": {"input_tokens": 50, "output_tokens": 200, "total_tokens": 250}
|
||||
}
|
||||
```
|
||||
|
||||
状态在终态(`completed`、`failed` 或 `cancelled`)之后会短暂保留,以供轮询和 UI 对账使用。
|
||||
|
||||
### GET /v1/runs/\{run_id\}/events
|
||||
|
||||
run 的工具调用进度、token 增量和生命周期事件的 Server-Sent Events 流。专为需要附加/分离而不丢失状态的仪表板和厚客户端设计。
|
||||
|
||||
### POST /v1/runs/\{run_id\}/stop
|
||||
|
||||
中断正在运行的 agent 轮次。端点立即返回 `{"status": "stopping"}`,同时 Hermes 要求活跃 agent 在下一个安全中断点停止。
|
||||
|
||||
## Jobs API(后台计划任务)
|
||||
|
||||
服务器暴露了一个轻量级 jobs CRUD 接口,用于从远程客户端管理计划/后台 agent run。所有端点均受同一 bearer 认证保护。
|
||||
|
||||
### GET /api/jobs
|
||||
|
||||
列出所有计划任务。
|
||||
|
||||
### POST /api/jobs
|
||||
|
||||
创建新的计划任务。请求体接受与 `hermes cron` 相同的结构——prompt(提示词)、schedule(计划)、skills(技能)、provider 覆盖、投递目标。
|
||||
|
||||
### GET /api/jobs/\{job_id\}
|
||||
|
||||
获取单个任务的定义和最后一次运行状态。
|
||||
|
||||
### PATCH /api/jobs/\{job_id\}
|
||||
|
||||
更新现有任务的字段(prompt、schedule 等)。部分更新会被合并。
|
||||
|
||||
### DELETE /api/jobs/\{job_id\}
|
||||
|
||||
删除任务。同时取消任何正在进行的 run。
|
||||
|
||||
### POST /api/jobs/\{job_id\}/pause
|
||||
|
||||
暂停任务而不删除它。下次计划运行的时间戳将被挂起,直到恢复。
|
||||
|
||||
### POST /api/jobs/\{job_id\}/resume
|
||||
|
||||
恢复之前暂停的任务。
|
||||
|
||||
### POST /api/jobs/\{job_id\}/run
|
||||
|
||||
立即触发任务运行,不受计划限制。
|
||||
|
||||
## 系统 Prompt 处理
|
||||
|
||||
当前端发送 `system` 消息(Chat Completions)或 `instructions` 字段(Responses API)时,hermes-agent 会将其**叠加在**核心系统 prompt 之上。你的 agent 保留所有工具、记忆和技能——前端的系统 prompt 只是添加额外指令。
|
||||
|
||||
这意味着你可以按前端自定义行为,而不会失去能力:
|
||||
- Open WebUI 系统 prompt:"You are a Python expert. Always include type hints."
|
||||
- agent 仍然拥有终端、文件工具、网络搜索、记忆等。
|
||||
|
||||
## 认证
|
||||
|
||||
通过 `Authorization` 请求头进行 Bearer token 认证:
|
||||
|
||||
```
|
||||
Authorization: Bearer ***
|
||||
```
|
||||
|
||||
通过 `API_SERVER_KEY` 环境变量配置密钥。如果需要浏览器直接调用 Hermes,还需将 `API_SERVER_CORS_ORIGINS` 设置为明确的允许列表。
|
||||
|
||||
:::warning 安全
|
||||
API 服务器提供对 hermes-agent 工具集的完整访问权限,**包括终端命令**。当绑定到非回环地址(如 `0.0.0.0`)时,**必须**设置 `API_SERVER_KEY`。同时保持 `API_SERVER_CORS_ORIGINS` 范围尽量小,以控制浏览器访问。
|
||||
|
||||
默认绑定地址(`127.0.0.1`)仅供本地使用。浏览器访问默认禁用;仅为明确的可信来源启用。
|
||||
:::
|
||||
|
||||
## 配置
|
||||
|
||||
### 环境变量
|
||||
|
||||
| 变量 | 默认值 | 描述 |
|
||||
|----------|---------|-------------|
|
||||
| `API_SERVER_ENABLED` | `false` | 启用 API 服务器 |
|
||||
| `API_SERVER_PORT` | `8642` | HTTP 服务器端口 |
|
||||
| `API_SERVER_HOST` | `127.0.0.1` | 绑定地址(默认仅限本地) |
|
||||
| `API_SERVER_KEY` | _(无)_ | 认证用 Bearer token |
|
||||
| `API_SERVER_CORS_ORIGINS` | _(无)_ | 逗号分隔的允许浏览器来源 |
|
||||
| `API_SERVER_MODEL_NAME` | _(profile 名称)_ | `/v1/models` 上的模型名称。默认为 profile 名称,默认 profile 则为 `hermes-agent`。 |
|
||||
|
||||
### config.yaml
|
||||
|
||||
```yaml
|
||||
# 暂不支持——请使用环境变量。
|
||||
# config.yaml 支持将在未来版本中推出。
|
||||
```
|
||||
|
||||
## 安全响应头
|
||||
|
||||
所有响应均包含安全响应头:
|
||||
- `X-Content-Type-Options: nosniff` — 防止 MIME 类型嗅探
|
||||
- `Referrer-Policy: no-referrer` — 防止 referrer 泄露
|
||||
|
||||
## CORS
|
||||
|
||||
API 服务器默认**不**启用浏览器 CORS。
|
||||
|
||||
如需直接浏览器访问,请设置明确的允许列表:
|
||||
|
||||
```bash
|
||||
API_SERVER_CORS_ORIGINS=http://localhost:3000,http://127.0.0.1:3000
|
||||
```
|
||||
|
||||
启用 CORS 后:
|
||||
- **预检响应**包含 `Access-Control-Max-Age: 600`(10 分钟缓存)
|
||||
- **SSE 流式响应**包含 CORS 头,使浏览器 EventSource 客户端能够正常工作
|
||||
- **`Idempotency-Key`** 是允许的请求头——客户端可发送它用于去重(响应按 key 缓存 5 分钟)
|
||||
|
||||
大多数已记录的前端(如 Open WebUI)采用服务器到服务器连接,完全不需要 CORS。
|
||||
|
||||
## 兼容前端
|
||||
|
||||
任何支持 OpenAI API 格式的前端均可使用。已测试/记录的集成:
|
||||
|
||||
| 前端 | Stars | 连接方式 |
|
||||
|----------|-------|------------|
|
||||
| [Open WebUI](/user-guide/messaging/open-webui) | 126k | 提供完整指南 |
|
||||
| LobeChat | 73k | 自定义 provider 端点 |
|
||||
| LibreChat | 34k | librechat.yaml 中的自定义端点 |
|
||||
| AnythingLLM | 56k | 通用 OpenAI provider |
|
||||
| NextChat | 87k | BASE_URL 环境变量 |
|
||||
| ChatBox | 39k | API Host 设置 |
|
||||
| Jan | 26k | 远程模型配置 |
|
||||
| HF Chat-UI | 8k | OPENAI_BASE_URL |
|
||||
| big-AGI | 7k | 自定义端点 |
|
||||
| OpenAI Python SDK | — | `OpenAI(base_url="http://localhost:8642/v1")` |
|
||||
| curl | — | 直接 HTTP 请求 |
|
||||
|
||||
## 使用 Profiles 的多用户设置
|
||||
|
||||
要为多个用户提供各自隔离的 Hermes 实例(独立的配置、记忆、技能),请使用 [profiles](/user-guide/profiles):
|
||||
|
||||
```bash
|
||||
# 为每个用户创建 profile
|
||||
hermes profile create alice
|
||||
hermes profile create bob
|
||||
|
||||
# 在不同端口上配置每个 profile 的 API 服务器。API_SERVER_* 是环境变量
|
||||
# (不是 config.yaml 键),因此将它们写入每个 profile 的 .env:
|
||||
cat >> ~/.hermes/profiles/alice/.env <<EOF
|
||||
API_SERVER_ENABLED=true
|
||||
API_SERVER_PORT=8643
|
||||
API_SERVER_KEY=alice-secret
|
||||
EOF
|
||||
|
||||
cat >> ~/.hermes/profiles/bob/.env <<EOF
|
||||
API_SERVER_ENABLED=true
|
||||
API_SERVER_PORT=8644
|
||||
API_SERVER_KEY=bob-secret
|
||||
EOF
|
||||
|
||||
# 启动每个 profile 的 gateway
|
||||
hermes -p alice gateway &
|
||||
hermes -p bob gateway &
|
||||
```
|
||||
|
||||
每个 profile 的 API 服务器自动将 profile 名称作为模型 ID 广播:
|
||||
|
||||
- `http://localhost:8643/v1/models` → 模型 `alice`
|
||||
- `http://localhost:8644/v1/models` → 模型 `bob`
|
||||
|
||||
在 Open WebUI 中,将每个添加为单独的连接。模型下拉列表显示 `alice` 和 `bob` 作为不同模型,每个均由完全隔离的 Hermes 实例支持。详见 [Open WebUI 指南](/user-guide/messaging/open-webui#multi-user-setup-with-profiles)。
|
||||
|
||||
## 限制
|
||||
|
||||
- **响应存储** — 存储的响应(用于 `previous_response_id`)持久化在 SQLite 中,gateway 重启后仍然存在。最多存储 100 个响应(LRU 淘汰)。
|
||||
- **不支持文件上传** — 两个端点(`/v1/chat/completions` 和 `/v1/responses`)均支持内联图像,但不支持通过 API 上传文件(`file`、`input_file`、`file_id`)和非图像文档输入。
|
||||
- **model 字段仅为展示用途** — 请求中的 `model` 字段会被接受,但实际使用的 LLM 模型在服务端的 config.yaml 中配置。
|
||||
|
||||
## 代理模式
|
||||
|
||||
API 服务器还作为 **gateway 代理模式**的后端。当另一个 Hermes gateway 实例配置了指向此 API 服务器的 `GATEWAY_PROXY_URL` 时,它会将所有消息转发到这里,而不是运行自己的 agent。这支持分离部署——例如,一个处理 Matrix E2EE 的 Docker 容器将请求中继到宿主机侧的 agent。
|
||||
|
||||
完整设置指南参见 [Matrix 代理模式](/user-guide/messaging/matrix#proxy-mode-e2ee-on-macos)。
|
||||
@ -0,0 +1,230 @@
|
||||
---
|
||||
sidebar_position: 12
|
||||
title: "批量处理"
|
||||
description: "大规模生成 agent 轨迹——并行处理、断点续跑与工具集分布"
|
||||
---
|
||||
|
||||
# 批量处理
|
||||
|
||||
批量处理让你能够并行地在数百乃至数千个 prompt(提示词)上运行 Hermes agent,生成结构化的轨迹数据。其主要用途是**训练数据生成**——产出包含工具使用统计信息的 ShareGPT 格式轨迹,可用于微调或评估。
|
||||
|
||||
## 概述
|
||||
|
||||
批量运行器(`batch_runner.py`)处理一个由 prompt 组成的 JSONL 数据集,将每条 prompt 通过完整的 agent 会话(含工具访问权限)运行一遍。每条 prompt 都拥有独立隔离的环境。输出为结构化轨迹数据,包含完整对话历史、工具调用统计信息以及推理覆盖率指标。
|
||||
|
||||
## 快速开始
|
||||
|
||||
```bash
|
||||
# 基本批量运行
|
||||
python batch_runner.py \
|
||||
--dataset_file=data/prompts.jsonl \
|
||||
--batch_size=10 \
|
||||
--run_name=my_first_run \
|
||||
--model=anthropic/claude-sonnet-4.6 \
|
||||
--num_workers=4
|
||||
|
||||
# 恢复中断的运行
|
||||
python batch_runner.py \
|
||||
--dataset_file=data/prompts.jsonl \
|
||||
--batch_size=10 \
|
||||
--run_name=my_first_run \
|
||||
--resume
|
||||
|
||||
# 列出可用的工具集分布
|
||||
python batch_runner.py --list_distributions
|
||||
```
|
||||
|
||||
:::tip 大规模运行下的可预测成本
|
||||
批量运行会启动大量并发 agent 会话,每个会话都会调用模型和工具。[Nous Portal](/user-guide/features/tool-gateway) 订阅将模型访问、网页搜索、图像生成、TTS 以及云端浏览器统一计费——当你希望在不同供应商账户间稳定控制每条轨迹成本、避免触碰速率限制时非常实用。使用 `hermes setup --portal` 完成配置,然后将 `--model` 指向 Nous 模型。
|
||||
:::
|
||||
|
||||
## 数据集格式
|
||||
|
||||
输入数据集为 JSONL 文件(每行一个 JSON 对象)。每条记录必须包含 `prompt` 字段:
|
||||
|
||||
```jsonl
|
||||
{"prompt": "Write a Python function that finds the longest palindromic substring"}
|
||||
{"prompt": "Create a REST API endpoint for user authentication using Flask"}
|
||||
{"prompt": "Debug this error: TypeError: cannot unpack non-iterable NoneType object"}
|
||||
```
|
||||
|
||||
记录还可以选填以下字段:
|
||||
- `image` 或 `docker_image`:用于该 prompt 沙箱的容器镜像(适用于 Docker、Modal 和 Singularity 后端)
|
||||
- `cwd`:任务终端会话的工作目录覆盖值
|
||||
|
||||
## 配置选项
|
||||
|
||||
| 参数 | 默认值 | 说明 |
|
||||
|-----------|---------|-------------|
|
||||
| `--dataset_file` | (必填) | JSONL 数据集路径 |
|
||||
| `--batch_size` | (必填) | 每批处理的 prompt 数量 |
|
||||
| `--run_name` | (必填) | 本次运行的名称(用于输出目录和断点续跑) |
|
||||
| `--distribution` | `"default"` | 采样所用的工具集分布 |
|
||||
| `--model` | `claude-sonnet-4.6` | 使用的模型 |
|
||||
| `--base_url` | `https://openrouter.ai/api/v1` | API 基础 URL |
|
||||
| `--api_key` | (环境变量) | 模型的 API 密钥 |
|
||||
| `--max_turns` | `10` | 每条 prompt 的最大工具调用轮次 |
|
||||
| `--num_workers` | `4` | 并行工作进程数 |
|
||||
| `--resume` | `false` | 从断点恢复 |
|
||||
| `--verbose` | `false` | 启用详细日志 |
|
||||
| `--max_samples` | 全部 | 仅处理数据集中前 N 条样本 |
|
||||
| `--max_tokens` | 模型默认值 | 每次模型响应的最大 token 数 |
|
||||
|
||||
### 供应商路由(OpenRouter)
|
||||
|
||||
| 参数 | 说明 |
|
||||
|-----------|-------------|
|
||||
| `--providers_allowed` | 允许的供应商,逗号分隔(例如 `"anthropic,openai"`) |
|
||||
| `--providers_ignored` | 忽略的供应商,逗号分隔(例如 `"together,deepinfra"`) |
|
||||
| `--providers_order` | 首选供应商顺序,逗号分隔 |
|
||||
| `--provider_sort` | 按 `"price"`、`"throughput"` 或 `"latency"` 排序 |
|
||||
|
||||
### 推理控制
|
||||
|
||||
| 参数 | 说明 |
|
||||
|-----------|-------------|
|
||||
| `--reasoning_effort` | 推理力度:`none`、`minimal`、`low`、`medium`、`high`、`xhigh` |
|
||||
| `--reasoning_disabled` | 完全禁用推理/思考 token |
|
||||
|
||||
### 高级选项
|
||||
|
||||
| 参数 | 说明 |
|
||||
|-----------|-------------|
|
||||
| `--ephemeral_system_prompt` | 执行时使用但**不**保存到轨迹中的系统 prompt |
|
||||
| `--log_prefix_chars` | 日志预览中显示的字符数(默认:100) |
|
||||
| `--prefill_messages_file` | 包含 few-shot 预填充消息的 JSON 文件路径 |
|
||||
|
||||
## 工具集分布
|
||||
|
||||
每条 prompt 会从一个**分布**中随机采样一组工具集。这确保训练数据覆盖多样化的工具组合。使用 `--list_distributions` 查看所有可用分布。
|
||||
|
||||
在当前实现中,分布为**每个独立工具集**分配一个概率。采样器对每个工具集独立进行伯努利抽样,并保证至少有一个工具集被启用。这与手工编写的预设组合表不同。
|
||||
|
||||
## 输出格式
|
||||
|
||||
所有输出写入 `data/<run_name>/`:
|
||||
|
||||
```text
|
||||
data/my_run/
|
||||
├── trajectories.jsonl # 合并后的最终输出(所有批次合并)
|
||||
├── batch_0.jsonl # 各批次结果
|
||||
├── batch_1.jsonl
|
||||
├── ...
|
||||
├── checkpoint.json # 断点续跑检查点
|
||||
└── statistics.json # 汇总工具使用统计
|
||||
```
|
||||
|
||||
### 轨迹格式
|
||||
|
||||
`trajectories.jsonl` 中每行是一个 JSON 对象:
|
||||
|
||||
```json
|
||||
{
|
||||
"prompt_index": 42,
|
||||
"conversations": [
|
||||
{"from": "human", "value": "Write a function..."},
|
||||
{"from": "gpt", "value": "I'll create that function...",
|
||||
"tool_calls": [...]},
|
||||
{"from": "tool", "value": "..."},
|
||||
{"from": "gpt", "value": "Here's the completed function..."}
|
||||
],
|
||||
"metadata": {
|
||||
"batch_num": 2,
|
||||
"timestamp": "2026-01-15T10:30:00",
|
||||
"model": "anthropic/claude-sonnet-4.6"
|
||||
},
|
||||
"completed": true,
|
||||
"partial": false,
|
||||
"api_calls": 3,
|
||||
"toolsets_used": ["terminal", "file"],
|
||||
"tool_stats": {
|
||||
"terminal": {"count": 2, "success": 2, "failure": 0},
|
||||
"read_file": {"count": 1, "success": 1, "failure": 0}
|
||||
},
|
||||
"tool_error_counts": {
|
||||
"terminal": 0,
|
||||
"read_file": 0
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
`conversations` 字段使用类 ShareGPT 格式,包含 `from` 和 `value` 字段。工具统计信息经过规范化处理,所有可能的工具均以零值默认填充,确保各条记录的 schema 一致,兼容 HuggingFace 数据集格式。
|
||||
|
||||
## 断点续跑
|
||||
|
||||
批量运行器具备健壮的断点续跑机制以应对故障:
|
||||
|
||||
- **检查点文件:** 每批完成后保存,记录已完成的 prompt 索引
|
||||
- **基于内容的恢复:** 使用 `--resume` 时,运行器扫描现有批次文件,通过实际文本内容(而非索引)匹配已完成的 prompt,即使数据集顺序发生变化也能正常恢复
|
||||
- **失败的 prompt:** 只有成功完成的 prompt 才会被标记为已完成——失败的 prompt 在恢复时会重新尝试
|
||||
- **批次合并:** 完成后,所有批次文件(包括之前运行的)会合并为单个 `trajectories.jsonl`
|
||||
|
||||
### 恢复流程
|
||||
|
||||
1. 扫描所有 `batch_*.jsonl` 文件,通过内容匹配找出已完成的 prompt
|
||||
2. 过滤数据集,排除已完成的 prompt
|
||||
3. 对剩余 prompt 重新分批
|
||||
4. 仅处理剩余 prompt
|
||||
5. 将所有批次文件(旧的 + 新的)合并为最终输出
|
||||
|
||||
## 质量过滤
|
||||
|
||||
批量运行器会自动进行质量过滤:
|
||||
|
||||
- **无推理过滤:** 所有 assistant 轮次均不包含推理内容(无 `<REASONING_SCRATCHPAD>` 或原生思考 token)的样本将被丢弃
|
||||
- **损坏条目过滤:** 包含幻觉工具名称(不在有效工具列表中)的条目在最终合并时会被过滤掉
|
||||
- **推理统计:** 跟踪整个运行过程中包含/不包含推理内容的轮次百分比
|
||||
|
||||
## 统计信息
|
||||
|
||||
完成后,运行器会打印全面的统计信息:
|
||||
|
||||
- **工具使用情况:** 每个工具的调用次数、成功/失败率
|
||||
- **推理覆盖率:** 包含推理内容的 assistant 轮次百分比
|
||||
- **丢弃样本数:** 因缺少推理内容而被过滤的样本数量
|
||||
- **耗时:** 总处理时间
|
||||
|
||||
统计信息同时保存至 `statistics.json`,便于程序化分析。
|
||||
|
||||
## 使用场景
|
||||
|
||||
### 训练数据生成
|
||||
|
||||
生成多样化的工具使用轨迹用于微调:
|
||||
|
||||
```bash
|
||||
python batch_runner.py \
|
||||
--dataset_file=data/coding_prompts.jsonl \
|
||||
--batch_size=20 \
|
||||
--run_name=coding_v1 \
|
||||
--model=anthropic/claude-sonnet-4.6 \
|
||||
--num_workers=8 \
|
||||
--distribution=default \
|
||||
--max_turns=15
|
||||
```
|
||||
|
||||
### 模型评估
|
||||
|
||||
在标准化 prompt 集上评估模型的工具使用能力:
|
||||
|
||||
```bash
|
||||
python batch_runner.py \
|
||||
--dataset_file=data/eval_suite.jsonl \
|
||||
--batch_size=10 \
|
||||
--run_name=eval_gpt4 \
|
||||
--model=openai/gpt-4o \
|
||||
--num_workers=4 \
|
||||
--max_turns=10
|
||||
```
|
||||
|
||||
### 按 Prompt 指定容器镜像
|
||||
|
||||
对于需要特定环境的基准测试,每条 prompt 可以指定自己的容器镜像:
|
||||
|
||||
```jsonl
|
||||
{"prompt": "Install numpy and compute eigenvalues of a 3x3 matrix", "image": "python:3.11-slim"}
|
||||
{"prompt": "Compile this Rust program and run it", "image": "rust:1.75"}
|
||||
{"prompt": "Set up a Node.js Express server", "image": "node:20-alpine", "cwd": "/app"}
|
||||
```
|
||||
|
||||
批量运行器会在运行每条 prompt 前验证 Docker 镜像是否可访问。
|
||||
@ -0,0 +1,627 @@
|
||||
---
|
||||
title: 浏览器自动化
|
||||
description: 通过多种提供商控制浏览器,支持通过 CDP 连接本地 Chromium 系浏览器或云端浏览器,用于网页交互、表单填写、数据抓取等场景。
|
||||
sidebar_label: Browser
|
||||
sidebar_position: 5
|
||||
---
|
||||
|
||||
# 浏览器自动化
|
||||
|
||||
Hermes Agent 内置完整的浏览器自动化工具集,支持多种后端选项:
|
||||
|
||||
- **Browserbase 云端模式** — 通过 [Browserbase](https://browserbase.com) 使用托管云端浏览器及反机器人工具
|
||||
- **Browser Use 云端模式** — 通过 [Browser Use](https://browser-use.com) 作为备选云端浏览器提供商
|
||||
- **Firecrawl 云端模式** — 通过 [Firecrawl](https://firecrawl.dev) 使用内置抓取功能的云端浏览器
|
||||
- **Camofox 本地模式** — 通过 [Camofox](https://github.com/jo-inc/camofox-browser) 实现本地反检测浏览(基于 Firefox 的指纹伪装)
|
||||
- **本地 Chromium 系 CDP** — 使用 `/browser connect` 将浏览器工具连接到本地运行的 Chrome、Brave、Chromium 或 Edge 实例
|
||||
- **本地浏览器模式** — 通过 `agent-browser` CLI 和本地 Chromium 安装运行
|
||||
|
||||
所有模式下,Agent 均可导航网站、与页面元素交互、填写表单并提取信息。
|
||||
|
||||
## 概述
|
||||
|
||||
页面以**无障碍树**(accessibility tree,基于文本的快照)表示,非常适合 LLM Agent 使用。交互元素会获得引用 ID(如 `@e1`、`@e2`),Agent 通过这些 ID 执行点击和输入操作。
|
||||
|
||||
核心能力:
|
||||
|
||||
- **多提供商云端执行** — Browserbase、Browser Use 或 Firecrawl — 无需本地浏览器
|
||||
- **本地 Chromium 系集成** — 通过 CDP 连接正在运行的 Chrome、Brave、Chromium 或 Edge 浏览器,实现实时操控
|
||||
- **内置隐身功能** — 随机指纹、CAPTCHA 解决、住宅代理(Browserbase)
|
||||
- **会话隔离** — 每个任务拥有独立的浏览器会话
|
||||
- **自动清理** — 非活跃会话在超时后自动关闭
|
||||
- **视觉分析** — 截图 + AI 分析,实现视觉理解
|
||||
|
||||
## 配置
|
||||
|
||||
:::tip Nous 订阅用户
|
||||
如果您拥有付费 [Nous Portal](https://portal.nousresearch.com) 订阅,可通过 **[Tool Gateway](tool-gateway.md)** 使用浏览器自动化功能,无需单独的 API 密钥。新安装可运行 `hermes setup --portal` 登录并一次性开启所有 gateway 工具;已有安装可通过 `hermes model` 或 `hermes tools` 选择 **Nous Subscription** 作为浏览器提供商。
|
||||
:::
|
||||
|
||||
### Browserbase 云端模式
|
||||
|
||||
要使用 Browserbase 托管的云端浏览器,请添加:
|
||||
|
||||
```bash
|
||||
# Add to ~/.hermes/.env
|
||||
BROWSERBASE_API_KEY=***
|
||||
BROWSERBASE_PROJECT_ID=your-project-id-here
|
||||
```
|
||||
|
||||
在 [browserbase.com](https://browserbase.com) 获取您的凭据。
|
||||
|
||||
### Browser Use 云端模式
|
||||
|
||||
要使用 Browser Use 作为云端浏览器提供商,请添加:
|
||||
|
||||
```bash
|
||||
# Add to ~/.hermes/.env
|
||||
BROWSER_USE_API_KEY=***
|
||||
```
|
||||
|
||||
在 [browser-use.com](https://browser-use.com) 获取 API 密钥。Browser Use 通过 REST API 提供云端浏览器。若同时设置了 Browserbase 和 Browser Use 凭据,Browserbase 优先。
|
||||
|
||||
### Firecrawl 云端模式
|
||||
|
||||
要使用 Firecrawl 作为云端浏览器提供商,请添加:
|
||||
|
||||
```bash
|
||||
# Add to ~/.hermes/.env
|
||||
FIRECRAWL_API_KEY=fc-***
|
||||
```
|
||||
|
||||
在 [firecrawl.dev](https://firecrawl.dev) 获取 API 密钥,然后选择 Firecrawl 作为浏览器提供商:
|
||||
|
||||
```bash
|
||||
hermes setup tools
|
||||
# → Browser Automation → Firecrawl
|
||||
```
|
||||
|
||||
可选配置:
|
||||
|
||||
```bash
|
||||
# Self-hosted Firecrawl instance (default: https://api.firecrawl.dev)
|
||||
FIRECRAWL_API_URL=http://localhost:3002
|
||||
|
||||
# Session TTL in seconds (default: 300)
|
||||
FIRECRAWL_BROWSER_TTL=600
|
||||
```
|
||||
|
||||
### 混合路由:公网 URL 使用云端,LAN/localhost 使用本地
|
||||
|
||||
配置云端提供商后,Hermes 会为解析到私有/回环/LAN 地址的 URL(`localhost`、`127.0.0.1`、`192.168.x.x`、`10.x.x.x`、`172.16-31.x.x`、`*.local`、`*.lan`、`*.internal`、IPv6 回环 `::1`、链路本地 `169.254.x.x`)自动启动一个**本地 Chromium 辅助进程**。公网 URL 在同一对话中继续使用云端提供商。
|
||||
|
||||
这解决了常见的"本地开发但使用 Browserbase"场景 — Agent 可以截取 `http://localhost:3000` 上的仪表盘,同时抓取 `https://github.com`,无需切换提供商或禁用 SSRF 防护。云端提供商永远不会看到私有 URL。
|
||||
|
||||
该功能**默认开启**。如需禁用(所有 URL 均走已配置的云端提供商,与之前行为一致):
|
||||
|
||||
```yaml
|
||||
# ~/.hermes/config.yaml
|
||||
browser:
|
||||
cloud_provider: browserbase
|
||||
auto_local_for_private_urls: false
|
||||
```
|
||||
|
||||
禁用自动路由后,私有 URL 将被拒绝并返回 `"Blocked: URL targets a private or internal address"`,除非同时设置 `browser.allow_private_urls: true`(允许云端提供商尝试访问,但通常无法成功,因为 Browserbase 等无法访问您的 LAN)。
|
||||
|
||||
要求:本地辅助进程使用与纯本地模式相同的 `agent-browser` CLI,因此需要先安装(`hermes setup tools → Browser Automation` 会自动安装)。从公网 URL 导航后重定向到私有地址的情况仍会被阻止(无法通过公网路径的重定向访问 LAN)。
|
||||
|
||||
### Camofox 本地模式
|
||||
|
||||
[Camofox](https://github.com/jo-inc/camofox-browser) 是一个自托管的 Node.js 服务器,封装了 Camoufox(一个带有 C++ 指纹伪装的 Firefox 分支)。它无需云端依赖即可提供本地反检测浏览。
|
||||
|
||||
```bash
|
||||
# Clone the Camofox browser server first
|
||||
git clone https://github.com/jo-inc/camofox-browser
|
||||
cd camofox-browser
|
||||
|
||||
# Build and start with Docker using the default container settings
|
||||
# (auto-detects arch: aarch64 on M1/M2, x86_64 on Intel)
|
||||
make up
|
||||
|
||||
# Stop and remove the default container
|
||||
make down
|
||||
|
||||
# Force a clean rebuild (for example, after upgrading VERSION/RELEASE)
|
||||
make reset
|
||||
|
||||
# Just download binaries without building
|
||||
make fetch
|
||||
|
||||
# Override arch or version explicitly
|
||||
make up ARCH=x86_64
|
||||
make up VERSION=135.0.1 RELEASE=beta.24
|
||||
```
|
||||
|
||||
`make up` 会立即启动默认容器。如需自定义运行时设置(如更大的 Node 堆内存、VNC 或持久化 profile 目录),请先构建镜像再手动运行:
|
||||
|
||||
```bash
|
||||
# Build the image without starting the default container
|
||||
make build
|
||||
|
||||
# Start with persistence, VNC live view, and a larger Node heap
|
||||
mkdir -p ~/.camofox-docker
|
||||
docker run -d \
|
||||
--name camofox-browser \
|
||||
--restart unless-stopped \
|
||||
-p 9377:9377 \
|
||||
-p 6080:6080 \
|
||||
-p 5901:5900 \
|
||||
-e CAMOFOX_PORT=9377 \
|
||||
-e ENABLE_VNC=1 \
|
||||
-e VNC_BIND=0.0.0.0 \
|
||||
-e VNC_RESOLUTION=1920x1080 \
|
||||
-e MAX_OLD_SPACE_SIZE=2048 \
|
||||
-v ~/.camofox-docker:/root/.camofox \
|
||||
camofox-browser:135.0.1-aarch64
|
||||
```
|
||||
|
||||
启用 VNC 后,浏览器以有头模式运行,可在浏览器中通过 `http://localhost:6080`(noVNC)实时查看。也可使用原生 VNC 客户端连接 `localhost:5901`。
|
||||
|
||||
如果已运行过 `make up`,请在启动自定义容器前先停止并删除默认容器:
|
||||
|
||||
```bash
|
||||
make down
|
||||
# then run the custom docker run command above
|
||||
```
|
||||
|
||||
然后在 `~/.hermes/.env` 中设置:
|
||||
|
||||
```bash
|
||||
CAMOFOX_URL=http://localhost:9377
|
||||
```
|
||||
|
||||
或通过 `hermes tools` → Browser Automation → Camofox 进行配置。
|
||||
|
||||
设置 `CAMOFOX_URL` 后,所有浏览器工具将自动通过 Camofox 路由,而非 Browserbase 或 agent-browser。
|
||||
|
||||
#### 持久化浏览器会话
|
||||
|
||||
默认情况下,每个 Camofox 会话使用随机身份 — Cookie 和登录状态不会在 Agent 重启后保留。要启用持久化浏览器会话,请在 `~/.hermes/config.yaml` 中添加:
|
||||
|
||||
```yaml
|
||||
browser:
|
||||
camofox:
|
||||
managed_persistence: true
|
||||
```
|
||||
|
||||
然后完全重启 Hermes 以使新配置生效。
|
||||
|
||||
:::warning 嵌套路径很重要
|
||||
Hermes 读取的是 `browser.camofox.managed_persistence`,**而非**顶层的 `managed_persistence`。常见错误写法:
|
||||
|
||||
```yaml
|
||||
# ❌ Wrong — Hermes ignores this
|
||||
managed_persistence: true
|
||||
```
|
||||
|
||||
如果该标志放在错误的路径下,Hermes 会静默回退到随机临时 `userId`,您的登录状态将在每次会话后丢失。
|
||||
:::
|
||||
|
||||
##### Hermes 的行为
|
||||
- 向 Camofox 发送确定性的 profile 范围 `userId`,使服务器能够跨会话复用同一 Firefox profile。
|
||||
- 在清理时跳过服务端 context 销毁,使 Cookie 和登录状态在 Agent 任务间保留。
|
||||
- 将 `userId` 限定在当前 Hermes profile 范围内,不同 Hermes profile 对应不同浏览器 profile(profile 隔离)。
|
||||
|
||||
##### Hermes 不做的事
|
||||
- 不会强制 Camofox 服务器持久化。Hermes 只发送稳定的 `userId`;服务器必须通过将该 `userId` 映射到持久化 Firefox profile 目录来支持它。
|
||||
- 如果您的 Camofox 服务器构建将每个请求视为临时的(例如始终调用 `browser.newContext()` 而不加载已存储的 profile),Hermes 无法使这些会话持久化。请确保运行的 Camofox 版本实现了基于 userId 的 profile 持久化。
|
||||
|
||||
##### 验证是否正常工作
|
||||
|
||||
1. 启动 Hermes 和 Camofox 服务器。
|
||||
2. 在浏览器任务中打开 Google(或任意登录网站)并手动登录。
|
||||
3. 正常结束浏览器任务。
|
||||
4. 开始新的浏览器任务。
|
||||
5. 再次打开同一网站 — 应仍处于登录状态。
|
||||
|
||||
如果第 5 步退出了登录,说明 Camofox 服务器未遵守稳定的 `userId`。请检查配置路径,确认编辑 `config.yaml` 后已完全重启 Hermes,并验证您的 Camofox 服务器版本是否支持基于用户的持久化 profile。
|
||||
|
||||
##### 状态存储位置
|
||||
|
||||
Hermes 从 profile 范围目录 `~/.hermes/browser_auth/camofox/`(非默认 profile 则在 `$HERMES_HOME` 下的对应位置)派生稳定的 `userId`。实际浏览器 profile 数据存储在 Camofox 服务器端,以该 `userId` 为键。要完全重置持久化 profile,请在 Camofox 服务器端清除对应数据,并删除相应 Hermes profile 的状态目录。
|
||||
|
||||
#### 外部管理的 Camofox 会话
|
||||
|
||||
当另一个应用驱动可见的 Camofox 浏览器(桌面助手、自定义集成、另一个 Agent)时,可配置 Hermes 在同一身份下运行,而非启动独立的隔离 profile。
|
||||
|
||||
三个参数控制行为:
|
||||
|
||||
| 设置 | 环境变量 | 效果 |
|
||||
|---------|---------|--------|
|
||||
| `browser.camofox.user_id` | `CAMOFOX_USER_ID` | Hermes 创建标签页时使用的 Camofox `userId`。设置此项即进入"外部管理"模式。 |
|
||||
| `browser.camofox.session_key` | `CAMOFOX_SESSION_KEY` | 创建标签页时发送的 `sessionKey`(即 `listItemId`)。用于接管时匹配已有标签页。未设置时默认为每任务值。 |
|
||||
| `browser.camofox.adopt_existing_tab` | `CAMOFOX_ADOPT_EXISTING_TAB` | 为 true 时,Hermes 在首次使用时调用 `GET /tabs?userId=<user_id>` 并优先复用已有标签页,而非新建。 |
|
||||
|
||||
环境变量优先于 `config.yaml`。两种形式均可:
|
||||
|
||||
```yaml
|
||||
browser:
|
||||
camofox:
|
||||
user_id: shared-camofox
|
||||
session_key: visible-tab
|
||||
adopt_existing_tab: true
|
||||
```
|
||||
|
||||
```bash
|
||||
CAMOFOX_USER_ID=shared-camofox
|
||||
CAMOFOX_SESSION_KEY=visible-tab
|
||||
CAMOFOX_ADOPT_EXISTING_TAB=true
|
||||
```
|
||||
|
||||
**设置 `user_id` 后的变化:**
|
||||
|
||||
- Hermes 在任务结束时跳过破坏性清理(与 `managed_persistence: true` 相同)。其他应用的标签页/Cookie/profile 得以保留。
|
||||
- Hermes **不会**调用 `DELETE /sessions/<user_id>` — 该端点会清除所有用户数据,若触发将销毁外部应用的会话。
|
||||
|
||||
**标签页接管的工作方式(当 `adopt_existing_tab: true` 时):**
|
||||
|
||||
1. 进程启动后首次调用浏览器工具时,Hermes 发出 `GET /tabs?userId=<user_id>`(5 秒超时)。
|
||||
2. 若响应中有标签页的 `listItemId == session_key`,Hermes 接管该组中最近创建的一个。
|
||||
3. 否则,Hermes 接管该用户最近创建的标签页(任意 `listItemId`)。
|
||||
4. 若无标签页或请求失败,Hermes 在下次操作时回退到新建标签页。
|
||||
|
||||
接管仅在会话的 `tab_id` 填充之前触发一次。若外部应用在运行中关闭了被接管的标签页,下次浏览器工具调用将返回 Camofox 错误 — Hermes 不会在每次调用时重新轮询新标签页。
|
||||
|
||||
**选择 `session_key`:** 若要 Hermes 可靠地附加到*特定*已有标签页,请将 `session_key` 设置为外部应用创建该标签页时使用的 `listItemId`。若只设置 `user_id` 而不设置 `session_key`,Hermes 会生成每任务的 `session_key`(`task_<id>`)— Hermes 将与外部应用共享 Cookie 和 profile,但会并排打开自己的标签页而非复用已有标签页。
|
||||
|
||||
**并发说明:** 外部应用和 Hermes 可同时驱动同一 Camofox `userId`,但 Camofox 不会在客户端之间协调每个标签页的焦点。请在应用层协调所有权(例如,Hermes 运行时外部应用暂停)。
|
||||
|
||||
#### VNC 实时查看
|
||||
|
||||
当 Camofox 以有头模式运行(带可见浏览器窗口)时,其健康检查响应中会暴露 VNC 端口。Hermes 自动发现此信息,并在导航响应中包含 VNC URL,Agent 可分享链接供您实时查看浏览器。
|
||||
|
||||
### 通过 CDP 连接本地 Chromium 系浏览器(`/browser connect`)
|
||||
|
||||
除云端提供商外,您还可以通过 Chrome DevTools Protocol(CDP)将 Hermes 浏览器工具连接到本地运行的 Chrome、Brave、Chromium 或 Edge 实例。当您希望实时查看 Agent 操作、与需要自身 Cookie/会话的页面交互,或避免云端浏览器费用时,此方式非常有用。
|
||||
|
||||
:::note
|
||||
`/browser connect` 是**交互式 CLI 斜杠命令** — 不由 gateway 分发。若在 WebUI、Telegram、Discord 或其他 gateway 聊天中尝试运行,消息将作为纯文本发送给 Agent,命令不会执行。请从终端启动 Hermes(`hermes` 或 `hermes chat`)并在那里执行 `/browser connect`。
|
||||
:::
|
||||
|
||||
在 CLI 中使用:
|
||||
|
||||
```
|
||||
/browser connect # Auto-launch/connect to a local Chromium-family browser at http://127.0.0.1:9222
|
||||
/browser connect ws://host:port # Connect to a specific CDP endpoint
|
||||
/browser status # Check current connection
|
||||
/browser disconnect # Detach and return to cloud/local mode
|
||||
```
|
||||
|
||||
若浏览器尚未以远程调试模式运行,Hermes 将尝试自动启动支持的 Chromium 系浏览器并使用 `--remote-debugging-port=9222`。检测范围包括 Brave、Google Chrome、Chromium 和 Microsoft Edge,以及常见 Linux 安装路径(如 `/opt/brave-bin/brave` 和 `/snap/bin/brave`)。
|
||||
|
||||
:::tip
|
||||
要手动启动带 CDP 的 Chromium 系浏览器,请使用专用的 user-data-dir,确保即使浏览器已以普通 profile 运行,调试端口也能正常开启:
|
||||
|
||||
```bash
|
||||
# Linux — Brave
|
||||
brave-browser \
|
||||
--remote-debugging-port=9222 \
|
||||
--user-data-dir=$HOME/.hermes/chrome-debug \
|
||||
--no-first-run \
|
||||
--no-default-browser-check &
|
||||
|
||||
# Linux — Google Chrome
|
||||
google-chrome \
|
||||
--remote-debugging-port=9222 \
|
||||
--user-data-dir=$HOME/.hermes/chrome-debug \
|
||||
--no-first-run \
|
||||
--no-default-browser-check &
|
||||
|
||||
# macOS — Brave
|
||||
"/Applications/Brave Browser.app/Contents/MacOS/Brave Browser" \
|
||||
--remote-debugging-port=9222 \
|
||||
--user-data-dir="$HOME/.hermes/chrome-debug" \
|
||||
--no-first-run \
|
||||
--no-default-browser-check &
|
||||
|
||||
# macOS — Google Chrome
|
||||
"/Applications/Google Chrome.app/Contents/MacOS/Google Chrome" \
|
||||
--remote-debugging-port=9222 \
|
||||
--user-data-dir="$HOME/.hermes/chrome-debug" \
|
||||
--no-first-run \
|
||||
--no-default-browser-check &
|
||||
```
|
||||
|
||||
然后启动 Hermes CLI 并运行 `/browser connect`。
|
||||
|
||||
**为什么需要 `--user-data-dir`?** 若不指定,在普通实例已运行时启动 Chromium 系浏览器通常只会在现有进程上打开新窗口 — 而该进程启动时未带 `--remote-debugging-port`,因此端口 9222 永远不会开启。专用的 user-data-dir 会强制启动新的浏览器进程,使调试端口正常监听。`--no-first-run --no-default-browser-check` 跳过新 profile 的首次启动向导。
|
||||
:::
|
||||
|
||||
通过 CDP 连接后,所有浏览器工具(`browser_navigate`、`browser_click` 等)将在您的实时浏览器实例上运行,而非启动云端会话。
|
||||
|
||||
### WSL2 + Windows Chrome:优先使用 MCP 而非 `/browser connect`
|
||||
|
||||
若 Hermes 在 WSL2 内运行,但您想控制的 Chrome 窗口在 Windows 宿主机上,`/browser connect` 通常不是最佳方案。
|
||||
|
||||
原因:
|
||||
|
||||
- `/browser connect` 要求 Hermes 本身能访问可用的 CDP 端点
|
||||
- 现代 Chrome 实时调试会话通常暴露仅宿主机本地可访问的端点,WSL 无法像访问经典 `9222` 端口那样直接访问
|
||||
- 即使 Windows Chrome 可调试,最简洁的集成方式通常是让 Windows 侧的浏览器 MCP 服务器连接 Chrome,再让 Hermes 与该 MCP 服务器通信
|
||||
|
||||
对于此场景,建议通过 Hermes MCP 支持使用 `chrome-devtools-mcp`。
|
||||
|
||||
具体配置请参阅 MCP 指南:
|
||||
|
||||
- [在 Hermes 中使用 MCP](../../guides/use-mcp-with-hermes.md#wsl2-bridge-hermes-in-wsl-to-windows-chrome)
|
||||
|
||||
### 本地浏览器模式
|
||||
|
||||
若**未**设置任何云端凭据且未使用 `/browser connect`,Hermes 仍可通过由 `agent-browser` 驱动的本地 Chromium 安装使用浏览器工具。
|
||||
|
||||
### 可选环境变量
|
||||
|
||||
```bash
|
||||
# Residential proxies for better CAPTCHA solving (default: "true")
|
||||
BROWSERBASE_PROXIES=true
|
||||
|
||||
# Advanced stealth with custom Chromium — requires Scale Plan (default: "false")
|
||||
BROWSERBASE_ADVANCED_STEALTH=false
|
||||
|
||||
# Session reconnection after disconnects — requires paid plan (default: "true")
|
||||
BROWSERBASE_KEEP_ALIVE=true
|
||||
|
||||
# Custom session timeout in milliseconds (default: project default)
|
||||
# Examples: 600000 (10min), 1800000 (30min)
|
||||
BROWSERBASE_SESSION_TIMEOUT=600000
|
||||
|
||||
# Inactivity timeout before auto-cleanup in seconds (default: 120)
|
||||
BROWSER_INACTIVITY_TIMEOUT=120
|
||||
|
||||
# Extra Chromium launch flags (comma- or newline-separated). Hermes auto-injects
|
||||
# `--no-sandbox,--disable-dev-shm-usage` when it detects root or AppArmor-restricted
|
||||
# unprivileged user namespaces (Ubuntu 23.10+, DGX Spark, many container images),
|
||||
# so most users don't need to set this. Set it manually only if you need a flag
|
||||
# Hermes doesn't add automatically; setting it disables the auto-injection.
|
||||
AGENT_BROWSER_ARGS=--no-sandbox
|
||||
```
|
||||
|
||||
### 安装 agent-browser CLI
|
||||
|
||||
```bash
|
||||
npm install -g agent-browser
|
||||
# Or install locally in the repo:
|
||||
npm install
|
||||
```
|
||||
|
||||
:::info
|
||||
`browser` 工具集必须包含在配置的 `toolsets` 列表中,或通过 `hermes config set toolsets '["hermes-cli", "browser"]'` 启用。
|
||||
:::
|
||||
|
||||
## 可用工具
|
||||
|
||||
### `browser_navigate`
|
||||
|
||||
导航到指定 URL。必须在其他任何浏览器工具之前调用。初始化 Browserbase 会话。
|
||||
|
||||
```
|
||||
Navigate to https://github.com/NousResearch
|
||||
```
|
||||
|
||||
:::tip
|
||||
对于简单的信息检索,优先使用 `web_search` 或 `web_extract` — 它们更快且成本更低。仅在需要**与页面交互**(点击按钮、填写表单、处理动态内容)时使用浏览器工具。
|
||||
:::
|
||||
|
||||
### `browser_snapshot`
|
||||
|
||||
获取当前页面无障碍树的文本快照。返回带有引用 ID(如 `@e1`、`@e2`)的交互元素,供 `browser_click` 和 `browser_type` 使用。
|
||||
|
||||
- **`full=false`**(默认):仅显示交互元素的紧凑视图
|
||||
- **`full=true`**:完整页面内容
|
||||
|
||||
超过 8000 字符的快照将由 LLM 自动摘要。
|
||||
|
||||
### `browser_click`
|
||||
|
||||
点击快照中由引用 ID 标识的元素。
|
||||
|
||||
```
|
||||
Click @e5 to press the "Sign In" button
|
||||
```
|
||||
|
||||
### `browser_type`
|
||||
|
||||
向输入框输入文本。先清空字段,再输入新文本。
|
||||
|
||||
```
|
||||
Type "hermes agent" into the search field @e3
|
||||
```
|
||||
|
||||
### `browser_scroll`
|
||||
|
||||
向上或向下滚动页面以显示更多内容。
|
||||
|
||||
```
|
||||
Scroll down to see more results
|
||||
```
|
||||
|
||||
### `browser_press`
|
||||
|
||||
按下键盘按键。适用于提交表单或导航。
|
||||
|
||||
```
|
||||
Press Enter to submit the form
|
||||
```
|
||||
|
||||
支持的按键:`Enter`、`Tab`、`Escape`、`ArrowDown`、`ArrowUp` 等。
|
||||
|
||||
### `browser_back`
|
||||
|
||||
在浏览器历史记录中返回上一页。
|
||||
|
||||
### `browser_get_images`
|
||||
|
||||
列出当前页面上所有图片及其 URL 和 alt 文本。适用于查找需要分析的图片。
|
||||
|
||||
### `browser_vision`
|
||||
|
||||
截图并使用视觉 AI 进行分析。当文本快照无法捕获重要视觉信息时使用 — 尤其适用于 CAPTCHA、复杂布局或视觉验证挑战。
|
||||
|
||||
截图会持久保存,文件路径与 AI 分析结果一并返回。在消息平台(Telegram、Discord、Slack、WhatsApp)上,您可以要求 Agent 分享截图 — 它将通过 `MEDIA:` 机制作为原生图片附件发送。
|
||||
|
||||
```
|
||||
What does the chart on this page show?
|
||||
```
|
||||
|
||||
截图存储在 `~/.hermes/cache/screenshots/`,24 小时后自动清理。
|
||||
|
||||
### `browser_console`
|
||||
|
||||
获取当前页面的浏览器控制台输出(log/warn/error 消息)及未捕获的 JavaScript 异常。对于检测无障碍树中不可见的静默 JS 错误至关重要。
|
||||
|
||||
```
|
||||
Check the browser console for any JavaScript errors
|
||||
```
|
||||
|
||||
使用 `clear=True` 可在读取后清空控制台,使后续调用只显示新消息。
|
||||
|
||||
`browser_console` 在带有 `expression` 参数调用时也可执行 JavaScript — 与 DevTools 控制台形式相同,结果以解析后的形式返回(JSON 序列化的对象变为 dict;原始值保持原始类型)。
|
||||
|
||||
```
|
||||
browser_console(expression="document.querySelector('h1').textContent")
|
||||
browser_console(expression="JSON.stringify(performance.timing)")
|
||||
```
|
||||
|
||||
当当前会话存在活跃的 CDP 监督器时(通常适用于任何对 CDP 兼容后端运行过 `browser_navigate` 的会话),执行通过监督器的持久 WebSocket 进行 — 无子进程启动开销。否则回退到标准 agent-browser CLI 路径。两种方式行为完全相同,仅延迟有差异。
|
||||
|
||||
### `browser_cdp`
|
||||
|
||||
原始 Chrome DevTools Protocol 直通 — 用于其他工具未覆盖的浏览器操作的逃生舱口。适用于原生对话框处理、iframe 范围内的执行、Cookie/网络控制,或 Agent 需要的任何 CDP 命令。
|
||||
|
||||
**仅在会话启动时 CDP 端点可访问的情况下可用** — 即 `/browser connect` 已连接到运行中的 Chrome、Brave、Chromium 或 Edge 浏览器,或 `config.yaml` 中设置了 `browser.cdp_url`。默认本地 agent-browser 模式、Camofox 和云端提供商(Browserbase、Browser Use、Firecrawl)目前不向此工具暴露 CDP — 云端提供商有每会话 CDP URL,但实时会话路由是后续功能。
|
||||
|
||||
**CDP 方法参考:** https://chromedevtools.github.io/devtools-protocol/ — Agent 可通过 `web_extract` 访问特定方法页面以查阅参数和返回结构。
|
||||
|
||||
常见用法:
|
||||
|
||||
```
|
||||
# List tabs (browser-level, no target_id)
|
||||
browser_cdp(method="Target.getTargets")
|
||||
|
||||
# Handle a native JS dialog on a tab
|
||||
browser_cdp(method="Page.handleJavaScriptDialog",
|
||||
params={"accept": true, "promptText": ""},
|
||||
target_id="<tabId>")
|
||||
|
||||
# Evaluate JS in a specific tab
|
||||
browser_cdp(method="Runtime.evaluate",
|
||||
params={"expression": "document.title", "returnByValue": true},
|
||||
target_id="<tabId>")
|
||||
|
||||
# Get all cookies
|
||||
browser_cdp(method="Network.getAllCookies")
|
||||
```
|
||||
|
||||
浏览器级方法(`Target.*`、`Browser.*`、`Storage.*`)省略 `target_id`。页面级方法(`Page.*`、`Runtime.*`、`DOM.*`、`Emulation.*`)需要来自 `Target.getTargets` 的 `target_id`。每次无状态调用相互独立 — 调用间不保留会话状态。
|
||||
|
||||
**跨域 iframe:** 传入 `frame_id`(来自 `browser_snapshot.frame_tree.children[]` 中 `is_oopif=true` 的条目)可通过监督器的实时会话路由该 iframe 的 CDP 调用。这是在 Browserbase 上对跨域 iframe 执行 `Runtime.evaluate` 的方式,避免无状态 CDP 连接遭遇签名 URL 过期问题。示例:
|
||||
|
||||
```
|
||||
browser_cdp(
|
||||
method="Runtime.evaluate",
|
||||
params={"expression": "document.title", "returnByValue": True},
|
||||
frame_id="<frame_id from browser_snapshot>",
|
||||
)
|
||||
```
|
||||
|
||||
同域 iframe 无需 `frame_id` — 在顶层 `Runtime.evaluate` 中使用 `document.querySelector('iframe').contentDocument` 即可。
|
||||
|
||||
### `browser_dialog`
|
||||
|
||||
响应原生 JS 对话框(`alert` / `confirm` / `prompt` / `beforeunload`)。在此工具出现之前,对话框会静默阻塞页面的 JavaScript 线程,后续 `browser_*` 调用会挂起或抛出异常;现在 Agent 可在 `browser_snapshot` 输出中看到待处理对话框并显式响应。
|
||||
|
||||
**工作流程:**
|
||||
1. 调用 `browser_snapshot`。若对话框正在阻塞页面,将显示为 `pending_dialogs: [{"id": "d-1", "type": "alert", "message": "..."}]`。
|
||||
2. 调用 `browser_dialog(action="accept")` 或 `browser_dialog(action="dismiss")`。对于 `prompt()` 对话框,传入 `prompt_text="..."` 提供响应内容。
|
||||
3. 重新快照 — `pending_dialogs` 为空;页面 JS 线程已恢复。
|
||||
|
||||
**检测通过持久 CDP 监督器自动进行** — 每个任务一个 WebSocket,订阅 Page/Runtime/Target 事件。监督器还会在快照中填充 `frame_tree` 字段,使 Agent 可查看当前页面的 iframe 结构,包括跨域(OOPIF)iframe。
|
||||
|
||||
**可用性矩阵:**
|
||||
|
||||
| 后端 | 通过 `pending_dialogs` 检测 | 响应(`browser_dialog` 工具) |
|
||||
|---|---|---|
|
||||
| 通过 `/browser connect` 或 `browser.cdp_url` 连接的本地 Chrome | ✓ | ✓ 完整工作流 |
|
||||
| Browserbase | ✓ | ✓ 完整工作流(通过注入的 XHR 桥接) |
|
||||
| Camofox / 默认本地 agent-browser | ✗ | ✗(无 CDP 端点) |
|
||||
|
||||
**在 Browserbase 上的工作原理。** Browserbase 的 CDP 代理会在约 10ms 内在服务端自动关闭真实的原生对话框,因此无法使用 `Page.handleJavaScriptDialog`。监督器通过 `Page.addScriptToEvaluateOnNewDocument` 注入一段小脚本,将 `window.alert`/`confirm`/`prompt` 替换为同步 XHR。我们通过 `Fetch.enable` 拦截这些 XHR — 页面 JS 线程在 XHR 上保持阻塞,直到我们用 Agent 的响应调用 `Fetch.fulfillRequest`。`prompt()` 的返回值原样传回页面 JS。
|
||||
|
||||
**对话框策略**在 `config.yaml` 的 `browser.dialog_policy` 下配置:
|
||||
|
||||
| 策略 | 行为 |
|
||||
|--------|----------|
|
||||
| `must_respond`(默认) | 捕获,在快照中显示,等待显式 `browser_dialog()` 调用。在 `browser.dialog_timeout_s`(默认 300 秒)后安全自动关闭,防止有问题的 Agent 永久阻塞。 |
|
||||
| `auto_dismiss` | 捕获,立即关闭。Agent 仍可在 `browser_state` 历史中看到对话框,但无需操作。 |
|
||||
| `auto_accept` | 捕获,立即接受。适用于导航带有频繁 `beforeunload` 提示的页面。 |
|
||||
|
||||
`browser_snapshot.frame_tree` 中的**帧树**上限为 30 帧、OOPIF 深度 2,以控制广告密集页面的负载大小。达到限制时会显示 `truncated: true` 标志;需要完整帧树的 Agent 可使用 `browser_cdp` 配合 `Page.getFrameTree`。
|
||||
|
||||
## 实际示例
|
||||
|
||||
### 填写网页表单
|
||||
|
||||
```
|
||||
User: Sign up for an account on example.com with my email john@example.com
|
||||
|
||||
Agent workflow:
|
||||
1. browser_navigate("https://example.com/signup")
|
||||
2. browser_snapshot() → sees form fields with refs
|
||||
3. browser_type(ref="@e3", text="john@example.com")
|
||||
4. browser_type(ref="@e5", text="SecurePass123")
|
||||
5. browser_click(ref="@e8") → clicks "Create Account"
|
||||
6. browser_snapshot() → confirms success
|
||||
```
|
||||
|
||||
### 研究动态内容
|
||||
|
||||
```
|
||||
User: What are the top trending repos on GitHub right now?
|
||||
|
||||
Agent workflow:
|
||||
1. browser_navigate("https://github.com/trending")
|
||||
2. browser_snapshot(full=true) → reads trending repo list
|
||||
3. Returns formatted results
|
||||
```
|
||||
|
||||
## 会话录制
|
||||
|
||||
自动将浏览器会话录制为 WebM 视频文件:
|
||||
|
||||
```yaml
|
||||
browser:
|
||||
record_sessions: true # default: false
|
||||
```
|
||||
|
||||
启用后,录制在首次 `browser_navigate` 时自动开始,会话关闭时保存到 `~/.hermes/browser_recordings/`。本地模式和云端模式(Browserbase)均支持。超过 72 小时的录制文件自动清理。
|
||||
|
||||
## 隐身功能
|
||||
|
||||
Browserbase 提供自动隐身能力:
|
||||
|
||||
| 功能 | 默认状态 | 说明 |
|
||||
|---------|---------|-------|
|
||||
| 基础隐身 | 始终开启 | 随机指纹、视口随机化、CAPTCHA 解决 |
|
||||
| 住宅代理 | 开启 | 通过住宅 IP 路由以提高访问成功率 |
|
||||
| 高级隐身 | 关闭 | 自定义 Chromium 构建,需要 Scale 计划 |
|
||||
| Keep Alive | 开启 | 网络中断后的会话重连 |
|
||||
|
||||
:::note
|
||||
若付费功能在您的计划中不可用,Hermes 会自动降级 — 先禁用 `keepAlive`,再禁用代理 — 确保免费计划也能正常浏览。
|
||||
:::
|
||||
|
||||
## 会话管理
|
||||
|
||||
- 每个任务通过 Browserbase 获得独立的浏览器会话
|
||||
- 非活跃会话在超时后自动清理(默认:2 分钟)
|
||||
- 后台线程每 30 秒检查一次过期会话
|
||||
- 进程退出时执行紧急清理,防止孤立会话
|
||||
- 通过 Browserbase API 释放会话(`REQUEST_RELEASE` 状态)
|
||||
|
||||
## 限制
|
||||
|
||||
- **基于文本的交互** — 依赖无障碍树,而非像素坐标
|
||||
- **快照大小** — 大型页面可能在 8000 字符处被截断或由 LLM 摘要
|
||||
- **会话超时** — 云端会话根据提供商计划设置过期
|
||||
- **费用** — 云端会话消耗提供商额度;对话结束或非活跃后会话自动清理。使用 `/browser connect` 可免费本地浏览。
|
||||
- **不支持文件下载** — 无法从浏览器下载文件
|
||||
@ -0,0 +1,269 @@
|
||||
---
|
||||
sidebar_position: 12
|
||||
sidebar_label: "内置插件"
|
||||
title: "内置插件"
|
||||
description: "随 Hermes Agent 附带并通过生命周期 hook 自动运行的插件——disk-cleanup 等"
|
||||
---
|
||||
|
||||
# 内置插件
|
||||
|
||||
Hermes 随仓库附带了一小组插件。它们位于 `<repo>/plugins/<name>/`,与用户安装在 `~/.hermes/plugins/` 中的插件一同自动加载。它们使用与第三方插件相同的插件接口——hook、工具、斜杠命令——只是在仓库内维护。
|
||||
|
||||
请参阅 [插件](/user-guide/features/plugins) 页面了解通用插件系统,以及 [构建 Hermes 插件](/guides/build-a-hermes-plugin) 了解如何编写自己的插件。
|
||||
|
||||
## 发现机制
|
||||
|
||||
`PluginManager` 按顺序扫描四个来源:
|
||||
|
||||
1. **内置(Bundled)** — `<repo>/plugins/<name>/`(本页所记录的内容)
|
||||
2. **用户(User)** — `~/.hermes/plugins/<name>/`
|
||||
3. **项目(Project)** — `./.hermes/plugins/<name>/`(需要 `HERMES_ENABLE_PROJECT_PLUGINS=1`)
|
||||
4. **Pip 入口点(Entry points)** — `hermes_agent.plugins`
|
||||
|
||||
名称冲突时,后面的来源优先——名为 `disk-cleanup` 的用户插件会替换内置版本。
|
||||
|
||||
`plugins/memory/` 和 `plugins/context_engine/` 被刻意排除在内置扫描之外。这两个目录使用各自的发现路径,因为内存提供者和上下文引擎是通过 `hermes memory setup` / 配置中的 `context.engine` 进行单选配置的提供者。
|
||||
|
||||
## 内置插件默认不启用
|
||||
|
||||
内置插件随附时处于禁用状态。发现机制会找到它们(它们会出现在 `hermes plugins list` 和交互式 `hermes plugins` UI 中),但在你明确启用之前不会加载:
|
||||
|
||||
```bash
|
||||
hermes plugins enable disk-cleanup
|
||||
```
|
||||
|
||||
或通过 `~/.hermes/config.yaml`:
|
||||
|
||||
```yaml
|
||||
plugins:
|
||||
enabled:
|
||||
- disk-cleanup
|
||||
```
|
||||
|
||||
这与用户安装的插件使用的机制相同。内置插件永远不会自动启用——无论是全新安装,还是现有用户升级到更新版本的 Hermes,都需要你明确选择启用。
|
||||
|
||||
要再次关闭内置插件:
|
||||
|
||||
```bash
|
||||
hermes plugins disable disk-cleanup
|
||||
# 或:从 config.yaml 的 plugins.enabled 中移除它
|
||||
```
|
||||
|
||||
## 当前附带的插件
|
||||
|
||||
仓库在 `plugins/` 下附带了以下内置插件。所有插件均需手动启用——通过 `hermes plugins enable <name>` 启用。
|
||||
|
||||
| 插件 | 类型 | 用途 |
|
||||
|---|---|---|
|
||||
| `disk-cleanup` | hook + 斜杠命令 | 自动追踪临时文件并在会话结束时清理 |
|
||||
| `observability/langfuse` | hook | 将轮次 / LLM 调用 / 工具追踪到 [Langfuse](https://langfuse.com) |
|
||||
| `spotify` | 后端(7 个工具) | 原生 Spotify 播放、队列、搜索、播放列表、专辑、曲库 |
|
||||
| `google_meet` | 独立插件 | 加入 Meet 通话、实时字幕转录、可选实时双工音频 |
|
||||
| `image_gen/openai` | 图像后端 | OpenAI `gpt-image-2` 图像生成后端(FAL 的替代方案) |
|
||||
| `image_gen/openai-codex` | 图像后端 | 通过 Codex OAuth 使用 OpenAI 图像生成 |
|
||||
| `image_gen/xai` | 图像后端 | xAI `grok-2-image` 后端 |
|
||||
| `hermes-achievements` | 仪表盘标签页 | Steam 风格的可收集徽章,根据你真实的 Hermes 会话历史生成 |
|
||||
| `kanban/dashboard` | 仪表盘标签页 | 多智能体调度器的看板(Kanban)UI——任务、评论、扇出、切换看板。参见 [Kanban 多智能体](./kanban.md)。 |
|
||||
|
||||
内存提供者(`plugins/memory/*`)和上下文引擎(`plugins/context_engine/*`)在 [内存提供者](./memory-providers.md) 中单独列出——它们分别通过 `hermes memory` 和 `hermes plugins` 管理。以下是两个长期运行的基于 hook 的插件的详细说明。
|
||||
|
||||
### disk-cleanup
|
||||
|
||||
自动追踪并删除会话期间创建的临时文件——测试脚本、临时输出、cron 日志、过期的 Chrome 配置文件——无需 agent 记住调用工具。
|
||||
|
||||
**工作原理:**
|
||||
|
||||
| Hook | 行为 |
|
||||
|---|---|
|
||||
| `post_tool_call` | 当 `write_file` / `terminal` / `patch` 在 `HERMES_HOME` 或 `/tmp/hermes-*` 内创建匹配 `test_*`、`tmp_*` 或 `*.test.*` 的文件时,静默追踪为 `test` / `temp` / `cron-output`。 |
|
||||
| `on_session_end` | 如果本轮中有任何测试文件被自动追踪,则执行安全的 `quick` 清理并记录一行摘要。否则保持静默。 |
|
||||
|
||||
**删除规则:**
|
||||
|
||||
| 类别 | 阈值 | 确认 |
|
||||
|---|---|---|
|
||||
| `test` | 每次会话结束 | 从不 |
|
||||
| `temp` | 追踪后超过 7 天 | 从不 |
|
||||
| `cron-output` | 追踪后超过 14 天 | 从不 |
|
||||
| HERMES_HOME 下的空目录 | 始终 | 从不 |
|
||||
| `research` | 超过 30 天,且超出最新 10 个 | 始终(仅 deep 模式) |
|
||||
| `chrome-profile` | 追踪后超过 14 天 | 始终(仅 deep 模式) |
|
||||
| 超过 500 MB 的文件 | 从不自动删除 | 始终(仅 deep 模式) |
|
||||
|
||||
**斜杠命令** — `/disk-cleanup` 在 CLI 和 gateway 会话中均可用:
|
||||
|
||||
```
|
||||
/disk-cleanup status # 分类明细 + 最大的 10 个文件
|
||||
/disk-cleanup dry-run # 预览,不实际删除
|
||||
/disk-cleanup quick # 立即执行安全清理
|
||||
/disk-cleanup deep # quick + 列出需要确认的项目
|
||||
/disk-cleanup track <path> <category> # 手动追踪
|
||||
/disk-cleanup forget <path> # 停止追踪(不删除)
|
||||
```
|
||||
|
||||
**状态** — 所有内容存储在 `$HERMES_HOME/disk-cleanup/`:
|
||||
|
||||
| 文件 | 内容 |
|
||||
|---|---|
|
||||
| `tracked.json` | 已追踪路径,包含类别、大小和时间戳 |
|
||||
| `tracked.json.bak` | 上述文件的原子写入备份 |
|
||||
| `cleanup.log` | 每次追踪 / 跳过 / 拒绝 / 删除操作的仅追加审计日志 |
|
||||
|
||||
**安全性** — 清理操作仅涉及 `HERMES_HOME` 或 `/tmp/hermes-*` 下的路径。Windows 挂载点(`/mnt/c/...`)会被拒绝。已知的顶级状态目录(`logs/`、`memories/`、`sessions/`、`cron/`、`cache/`、`skills/`、`plugins/`、`disk-cleanup/` 本身)即使为空也不会被删除——全新安装不会在第一次会话结束时被清空。
|
||||
|
||||
**启用:** `hermes plugins enable disk-cleanup`(或在 `hermes plugins` 中勾选复选框)。
|
||||
|
||||
**再次禁用:** `hermes plugins disable disk-cleanup`。
|
||||
|
||||
### observability/langfuse
|
||||
|
||||
将 Hermes 的轮次、LLM 调用和工具调用追踪到 [Langfuse](https://langfuse.com)——一个开源 LLM 可观测性平台。每轮一个 span,每次 API 调用一个 generation,每次工具调用一个 tool observation。用量总计、各类型 token 数量和成本估算来自 Hermes 的标准 `agent.usage_pricing` 数据,因此 Langfuse 仪表盘看到的分类(input / output / `cache_read_input_tokens` / `cache_creation_input_tokens` / `reasoning_tokens`)与 `hermes logs` 中显示的一致。
|
||||
|
||||
该插件采用失败开放(fail-open)策略:未安装 SDK、无凭据或 Langfuse 出现瞬时错误——所有情况都会在 hook 中静默处理为无操作。agent 循环不受任何影响。
|
||||
|
||||
**设置:**
|
||||
|
||||
```bash
|
||||
pip install langfuse
|
||||
hermes plugins enable observability/langfuse
|
||||
```
|
||||
|
||||
或在交互式 `hermes plugins` UI 中勾选复选框。然后将凭据写入 `~/.hermes/.env`:
|
||||
|
||||
```bash
|
||||
HERMES_LANGFUSE_PUBLIC_KEY=pk-lf-...
|
||||
HERMES_LANGFUSE_SECRET_KEY=sk-lf-...
|
||||
HERMES_LANGFUSE_BASE_URL=https://cloud.langfuse.com # 或你的自托管 URL
|
||||
```
|
||||
|
||||
**工作原理:**
|
||||
|
||||
| Hook | 行为 |
|
||||
|---|---|
|
||||
| `pre_api_request` / `pre_llm_call` | 打开(或复用)每轮的根 span "Hermes turn"。为本次 API 调用启动一个 `generation` 子 observation,将最近的消息序列化为输入。 |
|
||||
| `post_api_request` / `post_llm_call` | 关闭 generation,附加 `usage_details`、`cost_details`、`finish_reason`、助手输出和工具调用。如果没有工具调用且内容非空,则关闭本轮。 |
|
||||
| `pre_tool_call` | 启动一个带有经过清理的 `args` 的 `tool` 子 observation。 |
|
||||
| `post_tool_call` | 关闭 tool observation,附加经过清理的 `result`。`read_file` 的内容会被摘要化(头部 + 尾部 + 省略行数),以使大文件读取保持在 `HERMES_LANGFUSE_MAX_CHARS` 以内。 |
|
||||
|
||||
会话分组基于 Hermes 会话 ID(或子 agent 的任务 ID),通过 `langfuse.propagate_attributes` 实现,因此单次 `hermes chat` 会话中的所有内容都归属于同一个 Langfuse session。
|
||||
|
||||
**验证:**
|
||||
|
||||
```bash
|
||||
hermes plugins list # observability/langfuse 应显示 "enabled"
|
||||
hermes chat -q "hello" # 在 Langfuse UI 中检查是否有 "Hermes turn" trace
|
||||
```
|
||||
|
||||
**可选调优**(在 `.env` 中):
|
||||
|
||||
| 变量 | 默认值 | 用途 |
|
||||
|---|---|---|
|
||||
| `HERMES_LANGFUSE_ENV` | — | trace 上的环境标签(`production`、`staging` 等) |
|
||||
| `HERMES_LANGFUSE_RELEASE` | — | 发布/版本标签 |
|
||||
| `HERMES_LANGFUSE_SAMPLE_RATE` | `1.0` | 传递给 SDK 的采样率(0.0–1.0) |
|
||||
| `HERMES_LANGFUSE_MAX_CHARS` | `12000` | 消息内容 / 工具参数 / 工具结果的单字段截断长度 |
|
||||
| `HERMES_LANGFUSE_DEBUG` | `false` | 向 `agent.log` 输出详细插件日志 |
|
||||
|
||||
Hermes 前缀的环境变量和标准 SDK 环境变量(`LANGFUSE_PUBLIC_KEY`、`LANGFUSE_SECRET_KEY`、`LANGFUSE_BASE_URL`)均被接受——两者同时设置时,Hermes 前缀的优先。
|
||||
|
||||
**性能:** Langfuse 客户端在第一次 hook 调用后被缓存。如果凭据或 SDK 缺失,该决定也会被缓存——后续 hook 会快速返回,不再重新检查环境变量或重新加载配置。
|
||||
|
||||
**禁用:** `hermes plugins disable observability/langfuse`。插件模块仍会被发现,但在你重新启用之前不会运行任何模块代码。
|
||||
|
||||
### google_meet
|
||||
|
||||
让 agent **加入、转录并参与 Google Meet 通话**——记录会议笔记、事后总结对话内容、跟进特定要点,并可选择通过 TTS 将回复发回通话中。
|
||||
|
||||
**新增功能:**
|
||||
|
||||
- 使用浏览器自动化加入 Meet URL 的无头虚拟参与者
|
||||
- 通过配置的 STT 提供者对会议音频进行实时转录
|
||||
- agent 调用的 `meet_summarize` / `meet_speak` / `meet_followup` 工具集,用于对所听内容采取行动
|
||||
- 会后产物(转录、带发言人归属的笔记、行动项)保存在 `~/.hermes/cache/google_meet/<meeting_id>/`
|
||||
|
||||
**设置:**
|
||||
|
||||
```bash
|
||||
hermes plugins enable google_meet
|
||||
# 首次使用时会提示你通过插件的 OAuth 流程登录——
|
||||
# 需要有 Meet 访问权限的 Google 账号。如果会议强制要求
|
||||
# "仅受邀参与者可加入",可能需要主持人批准。
|
||||
```
|
||||
|
||||
在聊天中使用:
|
||||
|
||||
> "加入 meet.google.com/abc-defg-hij 并记录笔记。通话结束后,给我发一份包含行动项的摘要。"
|
||||
|
||||
agent 会启动会议加入流程,在通话进行时将转录内容流式传输到其上下文中,并在会议结束(或你告知停止)时生成结构化摘要。
|
||||
|
||||
**适用场景:** 需要机器人转录并为异步参与者总结的定期站会;需要结构化笔记的访谈式会议;任何原本需要 Fireflies / Otter / Grain 的场景。如果你不希望有 AI 在旁监听——请勿启用。
|
||||
|
||||
**禁用:** `hermes plugins disable google_meet`。已缓存的转录和录音保留在 `~/.hermes/cache/google_meet/`,直到你手动删除。
|
||||
|
||||
### hermes-achievements
|
||||
|
||||
在仪表盘中添加一个 **Steam 风格的成就标签页**——60 多个可收集的分级徽章,根据你真实的 Hermes 会话历史生成。工具链成就、调试模式、vibe-coding 连击、技能/内存使用、模型/提供者多样性、生活方式特征(周末和夜间会话)。最初由 [@PCinkusz](https://github.com/PCinkusz) 作为外部插件编写;已并入仓库,以便与 Hermes 功能变更保持同步。
|
||||
|
||||
**工作原理:**
|
||||
|
||||
- 在仪表盘后端扫描你的整个 `~/.hermes/state.db` 会话历史
|
||||
- 每个会话的统计数据按 `(started_at, last_active)` 指纹缓存,因此后续扫描只重新分析新增或变更的会话
|
||||
- 首次扫描在后台线程中运行——即使数据库有数千个会话,仪表盘也不会阻塞等待
|
||||
- 解锁状态持久化到 `$HERMES_HOME/plugins/hermes-achievements/state.json`
|
||||
|
||||
**等级进阶:** 铜 → 银 → 金 → 钻石 → 奥林匹斯。每张卡片都有"计算方式"部分,列出所追踪的确切指标。
|
||||
|
||||
**成就状态:**
|
||||
|
||||
| 状态 | 含义 |
|
||||
|---|---|
|
||||
| 已解锁 | 至少达到一个等级 |
|
||||
| 已发现 | 已知成就,进度可见,尚未获得 |
|
||||
| 隐藏 | 在 Hermes 检测到你历史中的第一个相关信号之前保持隐藏 |
|
||||
|
||||
**API** — 路由挂载在 `/api/plugins/hermes-achievements/` 下:
|
||||
|
||||
| 端点 | 用途 |
|
||||
|---|---|
|
||||
| `GET /achievements` | 完整目录,包含每个徽章的解锁状态(首次冷扫描运行期间返回待处理占位符) |
|
||||
| `GET /scan-status` | 后台扫描器状态:`idle` / `running` / `failed`,上次耗时,运行次数 |
|
||||
| `GET /recent-unlocks` | 最近解锁的 20 个徽章,最新的在前 |
|
||||
| `GET /sessions/{id}/badges` | 主要在某个特定会话中获得的徽章 |
|
||||
| `POST /rescan` | 手动同步重新扫描(阻塞;在用户点击重新扫描按钮时使用) |
|
||||
| `POST /reset-state` | 清除解锁历史和缓存快照 |
|
||||
|
||||
**状态文件** — 位于 `$HERMES_HOME/plugins/hermes-achievements/`:
|
||||
|
||||
| 文件 | 内容 |
|
||||
|---|---|
|
||||
| `state.json` | 解锁历史:你获得了哪些徽章以及获得时间。在 Hermes 更新间保持稳定。 |
|
||||
| `scan_snapshot.json` | 上次完成的扫描载荷(在仪表盘加载时立即提供) |
|
||||
| `scan_checkpoint.json` | 按指纹键控的每会话统计缓存(使热重扫描更快) |
|
||||
|
||||
**性能说明:**
|
||||
|
||||
- 约 8,000 个会话的冷扫描需要几分钟。它在首次仪表盘请求时在后台线程中运行;UI 显示待处理占位符并轮询 `/scan-status`。
|
||||
- **冷扫描期间的增量结果** — 扫描器每约 250 个会话发布一次部分快照,因此每次仪表盘刷新都会显示更多已解锁的徽章。不会出现盯着零数字等待一分钟的情况。
|
||||
- 热重扫描对每个 `started_at` + `last_active` 指纹与检查点匹配的会话复用每会话统计——即使在大型历史记录上也能在几秒内完成。
|
||||
- 内存快照 TTL 为 120 秒;过期请求立即提供旧快照并触发后台刷新。不会因为 TTL 过期就让你等待加载动画。
|
||||
|
||||
**启用:** 无需启用——`hermes-achievements` 是一个仅限仪表盘的插件(无生命周期 hook,无模型可见工具)。它在 `hermes dashboard` 首次启动时自动注册为标签页。`plugins.enabled` 配置仅控制生命周期/工具插件;仪表盘插件完全通过其 `dashboard/manifest.json` 发现。
|
||||
|
||||
**退出:** 删除或重命名 `plugins/hermes-achievements/dashboard/manifest.json`,或在 `~/.hermes/plugins/hermes-achievements/` 中用同名用户插件覆盖它(该插件不包含仪表盘)。`$HERMES_HOME/plugins/hermes-achievements/` 下的插件状态文件会保留——重新安装后你的解锁历史依然存在。
|
||||
|
||||
## 添加内置插件
|
||||
|
||||
内置插件的编写方式与其他 Hermes 插件完全相同——参见 [构建 Hermes 插件](/guides/build-a-hermes-plugin)。唯一的区别是:
|
||||
|
||||
- 目录位于 `<repo>/plugins/<name>/`,而非 `~/.hermes/plugins/<name>/`
|
||||
- 在 `hermes plugins list` 中,manifest 来源显示为 `bundled`
|
||||
- 同名用户插件会覆盖内置版本
|
||||
|
||||
以下情况适合将插件纳入内置:
|
||||
|
||||
- 没有可选依赖项(或它们已经是 `pip install .[all]` 的依赖)
|
||||
- 该行为对大多数用户有益,且是默认启用、需要主动关闭的
|
||||
- 逻辑与生命周期 hook 紧密结合,否则 agent 需要记住手动调用
|
||||
- 在不扩展模型可见工具接口的前提下补充核心能力
|
||||
|
||||
反例——应作为用户可安装插件而非内置插件的情况:需要 API 密钥的第三方集成、小众工作流、大型依赖树、任何会默认改变 agent 行为的内容。
|
||||
@ -0,0 +1,240 @@
|
||||
---
|
||||
sidebar_position: 8
|
||||
title: "代码执行"
|
||||
description: "通过 RPC 工具访问实现程序化 Python 执行——将多步骤工作流压缩至单次对话轮次"
|
||||
---
|
||||
|
||||
# 代码执行(程序化工具调用)
|
||||
|
||||
`execute_code` 工具允许 agent 编写调用 Hermes 工具的 Python 脚本,将多步骤工作流压缩至单次 LLM 对话轮次。脚本在 agent 宿主机的子进程中运行,通过 Unix 域套接字 RPC 与 Hermes 通信。
|
||||
|
||||
## 工作原理
|
||||
|
||||
1. Agent 编写使用 `from hermes_tools import ...` 的 Python 脚本
|
||||
2. Hermes 生成带有 RPC 函数的 `hermes_tools.py` 存根模块
|
||||
3. Hermes 打开 Unix 域套接字并启动 RPC 监听线程
|
||||
4. 脚本在子进程中运行——工具调用通过套接字传回 Hermes
|
||||
5. 只有脚本的 `print()` 输出会返回给 LLM;中间工具结果不会进入上下文窗口
|
||||
|
||||
```python
|
||||
# The agent can write scripts like:
|
||||
from hermes_tools import web_search, web_extract
|
||||
|
||||
results = web_search("Python 3.13 features", limit=5)
|
||||
for r in results["data"]["web"]:
|
||||
content = web_extract([r["url"]])
|
||||
# ... filter and process ...
|
||||
print(summary)
|
||||
```
|
||||
|
||||
**脚本内可用工具:** `web_search`、`web_extract`、`read_file`、`write_file`、`search_files`、`patch`、`terminal`(仅前台模式)。
|
||||
|
||||
## Agent 何时使用此功能
|
||||
|
||||
当存在以下情况时,agent 会使用 `execute_code`:
|
||||
|
||||
- **3 次及以上工具调用**,且调用之间包含处理逻辑
|
||||
- 批量数据过滤或条件分支
|
||||
- 对结果进行循环处理
|
||||
|
||||
核心优势:中间工具结果不会进入上下文窗口——只有最终的 `print()` 输出会返回,大幅降低 token 用量。
|
||||
|
||||
## 实际示例
|
||||
|
||||
### 数据处理流水线
|
||||
|
||||
```python
|
||||
from hermes_tools import search_files, read_file
|
||||
import json
|
||||
|
||||
# Find all config files and extract database settings
|
||||
matches = search_files("database", path=".", file_glob="*.yaml", limit=20)
|
||||
configs = []
|
||||
for match in matches.get("matches", []):
|
||||
content = read_file(match["path"])
|
||||
configs.append({"file": match["path"], "preview": content["content"][:200]})
|
||||
|
||||
print(json.dumps(configs, indent=2))
|
||||
```
|
||||
|
||||
### 多步骤网络调研
|
||||
|
||||
```python
|
||||
from hermes_tools import web_search, web_extract
|
||||
import json
|
||||
|
||||
# Search, extract, and summarize in one turn
|
||||
results = web_search("Rust async runtime comparison 2025", limit=5)
|
||||
summaries = []
|
||||
for r in results["data"]["web"]:
|
||||
page = web_extract([r["url"]])
|
||||
for p in page.get("results", []):
|
||||
if p.get("content"):
|
||||
summaries.append({
|
||||
"title": r["title"],
|
||||
"url": r["url"],
|
||||
"excerpt": p["content"][:500]
|
||||
})
|
||||
|
||||
print(json.dumps(summaries, indent=2))
|
||||
```
|
||||
|
||||
### 批量文件重构
|
||||
|
||||
```python
|
||||
from hermes_tools import search_files, read_file, patch
|
||||
|
||||
# Find all Python files using deprecated API and fix them
|
||||
matches = search_files("old_api_call", path="src/", file_glob="*.py")
|
||||
fixed = 0
|
||||
for match in matches.get("matches", []):
|
||||
result = patch(
|
||||
path=match["path"],
|
||||
old_string="old_api_call(",
|
||||
new_string="new_api_call(",
|
||||
replace_all=True
|
||||
)
|
||||
if "error" not in str(result):
|
||||
fixed += 1
|
||||
|
||||
print(f"Fixed {fixed} files out of {len(matches.get('matches', []))} matches")
|
||||
```
|
||||
|
||||
### 构建与测试流水线
|
||||
|
||||
```python
|
||||
from hermes_tools import terminal, read_file
|
||||
import json
|
||||
|
||||
# Run tests, parse results, and report
|
||||
result = terminal("cd /project && python -m pytest --tb=short -q 2>&1", timeout=120)
|
||||
output = result.get("output", "")
|
||||
|
||||
# Parse test output
|
||||
passed = output.count(" passed")
|
||||
failed = output.count(" failed")
|
||||
errors = output.count(" error")
|
||||
|
||||
report = {
|
||||
"passed": passed,
|
||||
"failed": failed,
|
||||
"errors": errors,
|
||||
"exit_code": result.get("exit_code", -1),
|
||||
"summary": output[-500:] if len(output) > 500 else output
|
||||
}
|
||||
|
||||
print(json.dumps(report, indent=2))
|
||||
```
|
||||
|
||||
## 执行模式
|
||||
|
||||
`execute_code` 有两种执行模式,通过 `~/.hermes/config.yaml` 中的 `code_execution.mode` 控制:
|
||||
|
||||
| 模式 | 工作目录 | Python 解释器 |
|
||||
|------|----------|---------------|
|
||||
| **`project`**(默认) | 会话的工作目录(与 `terminal()` 相同) | 活跃的 `VIRTUAL_ENV` / `CONDA_PREFIX` python,回退至 Hermes 自身的 python |
|
||||
| `strict` | 与用户项目隔离的临时暂存目录 | `sys.executable`(Hermes 自身的 python) |
|
||||
|
||||
**何时保持 `project` 模式:** 当你希望 `import pandas`、`from my_project import foo` 或 `open(".env")` 等相对路径与 `terminal()` 中的行为一致时。这几乎是你始终想要的模式。
|
||||
|
||||
**何时切换至 `strict` 模式:** 当你需要最大可复现性时——希望无论用户激活哪个 venv,每次会话都使用相同的解释器,并且希望脚本与项目目录隔离(避免通过相对路径意外读取项目文件)。
|
||||
|
||||
```yaml
|
||||
# ~/.hermes/config.yaml
|
||||
code_execution:
|
||||
mode: project # or "strict"
|
||||
```
|
||||
|
||||
`project` 模式的回退行为:若 `VIRTUAL_ENV` / `CONDA_PREFIX` 未设置、已损坏或指向低于 3.8 的 Python,解析器会干净地回退至 `sys.executable`——agent 始终有可用的解释器。
|
||||
|
||||
两种模式的安全关键不变量完全相同:
|
||||
|
||||
- 环境变量清理(API key、token、凭据默认被剥离)
|
||||
- 工具白名单(脚本不能递归调用 `execute_code`、`delegate_task` 或 MCP 工具)
|
||||
- 资源限制(超时、stdout 上限、工具调用上限)
|
||||
|
||||
切换模式只改变脚本的运行位置和使用的解释器,不改变脚本可见的凭据或可调用的工具。
|
||||
|
||||
## 资源限制
|
||||
|
||||
| 资源 | 限制 | 说明 |
|
||||
|------|------|------|
|
||||
| **超时** | 5 分钟(300 秒) | 脚本先收到 SIGTERM,5 秒宽限期后收到 SIGKILL |
|
||||
| **Stdout** | 50 KB | 输出截断并附加 `[output truncated at 50KB]` 提示 |
|
||||
| **Stderr** | 10 KB | 非零退出时包含在输出中,用于调试 |
|
||||
| **工具调用** | 每次执行 50 次 | 达到上限时返回错误 |
|
||||
|
||||
所有限制均可通过 `config.yaml` 配置:
|
||||
|
||||
```yaml
|
||||
# In ~/.hermes/config.yaml
|
||||
code_execution:
|
||||
mode: project # project (default) | strict
|
||||
timeout: 300 # Max seconds per script (default: 300)
|
||||
max_tool_calls: 50 # Max tool calls per execution (default: 50)
|
||||
```
|
||||
|
||||
## 脚本内工具调用的工作方式
|
||||
|
||||
当脚本调用 `web_search("query")` 等函数时:
|
||||
|
||||
1. 调用被序列化为 JSON,通过 Unix 域套接字发送至父进程
|
||||
2. 父进程通过标准 `handle_function_call` 处理器进行分发
|
||||
3. 结果通过套接字发回
|
||||
4. 函数返回解析后的结果
|
||||
|
||||
这意味着脚本内的工具调用与普通工具调用行为完全一致——相同的速率限制、相同的错误处理、相同的能力。唯一的限制是 `terminal()` 仅支持前台模式(不支持 `background` 或 `pty` 参数)。
|
||||
|
||||
## 错误处理
|
||||
|
||||
脚本失败时,agent 会收到结构化的错误信息:
|
||||
|
||||
- **非零退出码**:stderr 包含在输出中,agent 可看到完整的 traceback
|
||||
- **超时**:脚本被终止,agent 看到 `"Script timed out after 300s and was killed."`
|
||||
- **中断**:若用户在执行期间发送新消息,脚本被终止,agent 看到 `[execution interrupted — user sent a new message]`
|
||||
- **工具调用上限**:达到 50 次调用上限后,后续工具调用返回错误消息
|
||||
|
||||
响应始终包含 `status`(success/error/timeout/interrupted)、`output`、`tool_calls_made` 和 `duration_seconds`。
|
||||
|
||||
## 安全性
|
||||
|
||||
:::danger 安全模型
|
||||
子进程在**最小化环境**中运行。API key、token 和凭据默认被剥离。脚本只能通过 RPC 通道访问工具——除非显式允许,否则无法从环境变量中读取密钥。
|
||||
:::
|
||||
|
||||
名称中包含 `KEY`、`TOKEN`、`SECRET`、`PASSWORD`、`CREDENTIAL`、`PASSWD` 或 `AUTH` 的环境变量会被排除。只有安全的系统变量(`PATH`、`HOME`、`LANG`、`SHELL`、`PYTHONPATH`、`VIRTUAL_ENV` 等)会被传递。
|
||||
|
||||
### Skill 环境变量透传
|
||||
|
||||
当 skill 在其 frontmatter 中声明 `required_environment_variables` 时,这些变量会在 skill 加载后**自动透传**至 `execute_code` 和 `terminal` 子进程。这使 skill 可以使用其声明的 API key,而不会削弱任意代码的安全态势。
|
||||
|
||||
对于非 skill 场景,可在 `config.yaml` 中显式添加变量白名单:
|
||||
|
||||
```yaml
|
||||
terminal:
|
||||
env_passthrough:
|
||||
- MY_CUSTOM_KEY
|
||||
- ANOTHER_TOKEN
|
||||
```
|
||||
|
||||
详情参见[安全指南](/user-guide/security#environment-variable-passthrough)。
|
||||
|
||||
Hermes 始终将脚本和自动生成的 `hermes_tools.py` RPC 存根写入临时暂存目录,执行完成后清理。在 `strict` 模式下,脚本也在该目录中*运行*;在 `project` 模式下,脚本在会话的工作目录中运行(暂存目录保留在 `PYTHONPATH` 中以确保导入正常解析)。子进程在独立的进程组中运行,以便在超时或中断时干净地终止。
|
||||
|
||||
## execute_code 与 terminal 对比
|
||||
|
||||
| 使用场景 | execute_code | terminal |
|
||||
|----------|-------------|----------|
|
||||
| 调用之间含逻辑的多步骤工作流 | ✅ | ❌ |
|
||||
| 简单 shell 命令 | ❌ | ✅ |
|
||||
| 过滤/处理大量工具输出 | ✅ | ❌ |
|
||||
| 运行构建或测试套件 | ❌ | ✅ |
|
||||
| 对搜索结果进行循环处理 | ✅ | ❌ |
|
||||
| 交互式/后台进程 | ❌ | ✅ |
|
||||
| 需要环境变量中的 API key | ⚠️ 仅通过[透传](/user-guide/security#environment-variable-passthrough) | ✅(大多数可透传) |
|
||||
|
||||
**经验法则:** 需要在调用之间含逻辑地程序化调用 Hermes 工具时,使用 `execute_code`。运行 shell 命令、构建和进程时,使用 `terminal`。
|
||||
|
||||
## 平台支持
|
||||
|
||||
代码执行依赖 Unix 域套接字,仅在 **Linux 和 macOS** 上可用。在 Windows 上会自动禁用——agent 回退至常规的顺序工具调用。
|
||||
@ -0,0 +1,441 @@
|
||||
---
|
||||
title: Codex App-Server 运行时(可选)
|
||||
sidebar_label: Codex App-Server 运行时
|
||||
---
|
||||
|
||||
# Codex App-Server 运行时
|
||||
|
||||
Hermes 可以选择将 `openai/*` 和 `openai-codex/*` 的轮次交由 [Codex CLI app-server](https://github.com/openai/codex) 处理,而不是运行自己的工具循环。启用后,终端命令、文件编辑、沙箱隔离以及 MCP 工具调用均在 Codex 的运行时内执行——Hermes 成为其外层 shell(会话数据库、斜杠命令、gateway、记忆与技能审查)。
|
||||
|
||||
此功能**仅限手动启用**。除非你主动切换该标志,否则 Hermes 的默认行为不变。Hermes 不会自动将你路由到此运行时。
|
||||
|
||||
## 为什么使用
|
||||
|
||||
- 通过 Codex CLI 使用的相同认证流程,使用你的 **ChatGPT 订阅**运行 OpenAI agent 轮次(无需 API 密钥)。
|
||||
- 使用 **Codex 自带的工具集和沙箱**——`shell` 用于终端/读/写/搜索,`apply_patch` 用于结构化编辑,`update_plan` 用于规划,全部在 seatbelt/landlock 沙箱内运行。
|
||||
- **原生 Codex 插件**——Linear、GitHub、Gmail、Calendar、Canva 等——通过 `codex plugin` 安装后,会自动迁移并在你的 Hermes 会话中激活。
|
||||
- **Hermes 的丰富工具一并可用**——web_search、web_extract、浏览器自动化、视觉、图像生成、技能和 TTS 通过 MCP 回调提供。Codex 会回调 Hermes 获取其自身没有内置的工具。
|
||||
- **记忆与技能提示持续生效**——Codex 的事件被投影为 Hermes 的消息格式,使自我改进循环看到正常的对话记录。
|
||||
|
||||
## 模型实际拥有哪些工具
|
||||
|
||||
这是大多数用户最想提前了解的部分。当此运行时开启时,执行你的轮次的模型拥有三个独立的工具来源:
|
||||
|
||||
### 1. Codex 内置工具集(始终开启)
|
||||
|
||||
这些工具随 `codex app-server` 本身一起提供——无需 Hermes 介入,无需 MCP,无需插件。运行时启动后,以下五个工具立即可用:
|
||||
|
||||
- **`shell`** — 在沙箱内运行任意 shell 命令。模型通过此工具读取文件(`cat`、`head`、`tail`)、写入文件(`echo > foo`、heredoc)、搜索文件(`find`、`rg`、`grep`)、浏览目录(`ls`、`cd`)、运行构建、管理进程,以及其他任何你在 bash 中能做的事。
|
||||
- **`apply_patch`** — 以 Codex 的 patch 格式应用结构化的多文件差异。模型将此工具用于非简单的代码编辑(添加函数、跨文件重构);单次写入仍可使用 shell heredoc。
|
||||
- **`update_plan`** — Codex 的内部待办/计划跟踪器。等同于 Hermes 的 `todo` 工具,但完全在 Codex 运行时内部管理。
|
||||
- **`view_image`** — 将本地图像文件加载到对话中,使模型能够查看它。
|
||||
- **`web_search`** — 配置后 Codex 拥有自己的内置网络搜索。Hermes 也通过下方的回调暴露 `web_search`(基于 Firecrawl);模型会选择其偏好的那个。
|
||||
|
||||
因此,**任何你通过终端完成的操作——读/写/搜索/查找/运行——Codex 都能原生处理**。沙箱配置文件(启用运行时时默认为 `:workspace`)控制可写范围。
|
||||
|
||||
### 2. 原生 Codex 插件(从你的 `codex plugin` 安装中自动迁移)
|
||||
|
||||
启用运行时时,Hermes 会查询 Codex 的 `plugin/list` RPC,并为你已安装的每个插件写入一条 `[plugins."<name>@openai-curated"]` 配置项。插件本身由 Codex 管理,并通过 Codex 自己的 UI 完成一次性授权。
|
||||
|
||||
示例(OpenClaw 帖子中被称为"值得录制视频"的那些):
|
||||
|
||||
- **Linear** — 查找/更新 issue
|
||||
- **GitHub** — 搜索代码、查看 PR、评论
|
||||
- **Gmail** — 读取/发送邮件
|
||||
- **Google Calendar** — 创建/查找日程
|
||||
- **Outlook 日历/邮件** — 通过 Microsoft 连接器提供相同功能
|
||||
- **Canva** — 设计生成
|
||||
- ……以及其他你通过 `codex plugin marketplace add openai-curated` + `codex plugin install ...` 安装的插件
|
||||
|
||||
**未迁移的内容:**
|
||||
- 你尚未安装的插件——请先在 Codex 中安装。
|
||||
- ChatGPT 应用市场条目(`app/list`)——这些已通过你的账户认证在 Codex 内部启用。
|
||||
|
||||
### 3. Hermes 工具回调(MCP server,注册在 `~/.codex/config.toml` 中)
|
||||
|
||||
Hermes 将自身注册为 MCP server,以便 Codex 能够回调获取 Codex 自身未内置的工具。通过回调可用的工具:
|
||||
|
||||
- **`web_search`** / **`web_extract`** — 基于 Firecrawl;对于结构化内容,通常比直接抓取更干净。
|
||||
- **`browser_navigate` / `browser_click` / `browser_type` / `browser_press` / `browser_snapshot` / `browser_scroll` / `browser_back` / `browser_get_images` / `browser_console` / `browser_vision`** — 通过 Camofox 或 Browserbase 实现完整的浏览器自动化。
|
||||
- **`vision_analyze`** — 调用独立的视觉模型检查图像(与 Codex 的 `view_image` 不同,后者是将图像加载到对话中)。
|
||||
- **`image_generate`** — 通过 Hermes 的 image_gen 插件链生成图像。
|
||||
- **`skill_view` / `skills_list`** — 读取 Hermes 的技能库。
|
||||
- **`text_to_speech`** — 通过 Hermes 配置的提供商进行 TTS。
|
||||
|
||||
当模型需要其中某个工具时,Codex 通过 stdio MCP 生成 `hermes_tools_mcp_server` 子进程,调用通过 `model_tools.handle_function_call()` 分发(与 Hermes 默认运行时的代码路径相同),结果像其他 MCP 响应一样返回给 Codex。
|
||||
|
||||
### 此运行时上不可用的工具
|
||||
|
||||
以下四个 Hermes 工具需要运行中的 AIAgent 上下文(循环中间状态)才能分发,无状态的 MCP 回调无法驱动它们。需要这些工具时,请切换回默认运行时(`/codex-runtime auto`):
|
||||
|
||||
- **`delegate_task`** — 生成子 agent
|
||||
- **`memory`** — Hermes 的持久记忆存储
|
||||
- **`session_search`** — 跨会话搜索
|
||||
- **`todo`** — Hermes 的待办存储(Codex 的 `update_plan` 是运行时内的等效工具)
|
||||
|
||||
## 工作流功能(`/goal`、kanban、cron)
|
||||
|
||||
### `/goal`(Ralph 循环)
|
||||
|
||||
**在此运行时上可用。** 目标以会话 id 为键持久化在 `state_meta` 中,续接提示通过 `run_conversation()` 作为普通用户消息回传,Codex 原生执行下一轮次。目标判断器通过辅助客户端运行(在 config.yaml 中通过 `auxiliary.goal_judge` 配置),与当前活跃的运行时无关。判断器的"受阻,需要用户输入"裁决是 Codex 卡在审批时的干净退出路径。
|
||||
|
||||
**需要注意的一点:** 每个续接提示都是一次全新的 Codex 轮次,这意味着 Codex 会从头重新评估命令审批策略。如果你在执行包含大量写操作的长期目标,预期会看到比单次会话内任务更多的审批提示。设置 `default_permissions = ":workspace"`(启用运行时时 Hermes 会自动设置)可避免简单的工作区写操作触发提示。
|
||||
|
||||
### Kanban(多 agent 工作树分发)
|
||||
|
||||
**在此运行时上可用,但有一个细微依赖。** Kanban 分发器将每个 worker 生成为独立的 `hermes chat -q` 子进程,该子进程读取用户配置——这意味着如果全局设置了 `model.openai_runtime: codex_app_server`,worker 也会在 Codex 运行时上启动。
|
||||
|
||||
Codex 运行时 worker 内可用的功能:
|
||||
- Codex 完整工具集(shell、apply_patch、update_plan、view_image、web_search)——worker 原生完成实际任务
|
||||
- 已迁移的 Codex 插件——Linear、GitHub 等
|
||||
- 用于 browser_*、vision、image_gen、技能、TTS 的 Hermes 工具回调
|
||||
|
||||
通过 MCP 回调同样可用的功能:
|
||||
- **`kanban_complete` / `kanban_block` / `kanban_comment` / `kanban_heartbeat`** — worker 交接工具。这些工具从环境变量中读取 `HERMES_KANBAN_TASK`(由分发器设置),正确进行访问控制,并写入由 `HERMES_KANBAN_DB` 固定的每个看板 SQLite 数据库。若回调中没有这些工具,此运行时上的 worker 可以完成任务但无法汇报,会一直挂起直到分发器超时。
|
||||
- **`kanban_show` / `kanban_list`** — 只读看板查询,供 worker 检查自身上下文。
|
||||
- **`kanban_create` / `kanban_unblock` / `kanban_link`** — 仅限编排器的操作。供运行在 Codex 运行时上、需要分发新任务的编排器 agent 使用。
|
||||
|
||||
Kanban 工具通过分发器设置的 `HERMES_KANBAN_TASK` 环境变量进行访问控制——该变量会传播到 Codex 子进程(Codex 继承环境变量),再从那里传播到生成的 `hermes-tools` MCP server 子进程。因此工具能看到正确的任务 id 并正确进行访问控制。对于 Codex app-server worker,当 `HERMES_KANBAN_TASK` 存在时,Hermes 还会传入精细的 app-server 沙箱覆盖配置:保持 `workspace-write` 沙箱,将**看板数据库目录以及分发器固定的所有 Kanban 路径**作为额外可写根目录添加(`HERMES_KANBAN_WORKSPACES_ROOT`、`HERMES_KANBAN_WORKSPACE`、旧版 `HERMES_KANBAN_ROOT`——去重,数据库目录优先),并默认禁用网络。这避免了脆弱的 `:danger-no-sandbox` 变通方案,同时允许 `kanban_complete` / `kanban_block` 更新看板数据库,**并且**允许 worker 在数据库目录之外的工作区挂载点下写入报告/产物(例如独立驱动器上的 `/media/.../kanban-workspaces/...`——[issue #27941](https://github.com/NousResearch/hermes-agent/issues/27941))。
|
||||
|
||||
### Cron 任务
|
||||
|
||||
**尚未经过专项测试。** Cron 任务通过 `cronjob` → `AIAgent.run_conversation` 运行,与 CLI 的代码路径相同。如果 cron 任务的配置中有 `openai_runtime: codex_app_server`,它将在 Codex 上运行。相同的工具可用性规则适用——Codex 内置工具 + 插件 + MCP 回调可用,agent 循环工具(delegate_task、memory、session_search、todo)不可用。如果你的 cron 任务依赖这些工具,请将 cron 限定在使用默认运行时的配置文件中。
|
||||
|
||||
## 权衡对比
|
||||
|
||||
| | Hermes 默认运行时 | Codex app-server(可选启用) |
|
||||
|---|---|---|
|
||||
| `delegate_task` 子 agent | 是 | 不可用——需要 agent 循环上下文 |
|
||||
| `memory`、`session_search`、`todo` | 是 | 不可用——需要 agent 循环上下文 |
|
||||
| `web_search`、`web_extract` | 是 | 是(通过 MCP 回调) |
|
||||
| 浏览器自动化(Camofox/Browserbase) | 是 | 是(通过 MCP 回调) |
|
||||
| `vision_analyze`、`image_generate` | 是 | 是(通过 MCP 回调) |
|
||||
| `skill_view`、`skills_list` | 是 | 是(通过 MCP 回调) |
|
||||
| `text_to_speech` | 是 | 是(通过 MCP 回调) |
|
||||
| Codex `shell`(终端/读/写/搜索/查找/运行) | — | 是(Codex 内置) |
|
||||
| Codex `apply_patch`(结构化多文件编辑) | — | 是(Codex 内置) |
|
||||
| Codex `update_plan`(运行时内待办) | — | 是(Codex 内置) |
|
||||
| Codex `view_image`(将图像加载到对话) | — | 是(Codex 内置) |
|
||||
| Codex 沙箱(seatbelt/landlock,配置文件) | — | 是(Codex 内置) |
|
||||
| ChatGPT 订阅认证 | — | 是(通过 `openai-codex` 提供商) |
|
||||
| 原生 Codex 插件(Linear、GitHub 等) | — | 是(自动迁移) |
|
||||
| 用户 MCP server | 是 | 是(自动迁移到 Codex) |
|
||||
| 记忆 + 技能审查(后台) | 是 | 是(通过事件投影) |
|
||||
| 多轮对话 | 是 | 是 |
|
||||
| `/goal`(Ralph 循环) | 是 | 是 |
|
||||
| Kanban worker 分发 | 是 | 是(通过回调) |
|
||||
| Kanban 编排器工具 | 是 | 是(通过回调) |
|
||||
| 所有 gateway 平台 | 是 | 是 |
|
||||
| 非 OpenAI 提供商 | 是 | 不适用——仅限 OpenAI/Codex |
|
||||
|
||||
## 前提条件
|
||||
|
||||
1. **已安装 Codex CLI:**
|
||||
```bash
|
||||
npm i -g @openai/codex
|
||||
codex --version # 0.130.0 或更新版本
|
||||
```
|
||||
2. **Codex OAuth 登录。** Codex 子进程读取 `~/.codex/auth.json`。有两种方式填充它:
|
||||
```bash
|
||||
codex login # 将 token 写入 ~/.codex/auth.json
|
||||
```
|
||||
Hermes 自己的 `hermes auth login codex` 写入 `~/.hermes/auth.json`——那是独立的会话。**如果你还没有运行过 `codex login`,请单独运行它。**
|
||||
|
||||
3. **(可选)安装你想要的 Codex 插件。** 启用运行时时,Hermes 会自动迁移你已通过 Codex CLI 安装的所有精选插件:
|
||||
```bash
|
||||
codex plugin marketplace add openai-curated
|
||||
# 然后通过 Codex 的 TUI 安装 Linear / GitHub / Gmail 等
|
||||
```
|
||||
Hermes 会自动发现它们并将 `[plugins."<name>@openai-curated"]` 条目写入 `~/.codex/config.toml`。
|
||||
|
||||
## 启用
|
||||
|
||||
在 Hermes 会话中:
|
||||
|
||||
```
|
||||
/codex-runtime codex_app_server
|
||||
```
|
||||
|
||||
该命令会:
|
||||
- 验证 `codex` CLI 是否已安装(若未安装则阻止并提示安装方法)。
|
||||
- 将 `model.openai_runtime: codex_app_server` 持久化到你的 config.yaml。
|
||||
- 将用户 MCP server 从 `~/.hermes/config.yaml` 迁移到 `~/.codex/config.toml`。
|
||||
- **发现并迁移已安装的原生 Codex 插件**(Linear、GitHub、Gmail、Calendar、Canva 等),通过查询 Codex 的 `plugin/list` RPC 实现。
|
||||
- **将 Hermes 自身的工具注册为 MCP server**,以便 Codex 子进程能够回调获取 Codex 未内置的工具。
|
||||
- **写入 `default_permissions = ":workspace"`**,使沙箱允许在工作区内写入,无需对每次操作进行提示。
|
||||
- 告知你迁移了哪些内容。在**下一个**会话生效——当前缓存的 agent 保持之前的运行时,以保持 prompt 缓存有效。
|
||||
|
||||
同义命令:`/codex-runtime on`、`/codex-runtime off`、`/codex-runtime auto`。
|
||||
|
||||
查看当前状态而不做任何更改:
|
||||
```
|
||||
/codex-runtime
|
||||
```
|
||||
|
||||
你也可以在 `~/.hermes/config.yaml` 中手动设置:
|
||||
```yaml
|
||||
model:
|
||||
openai_runtime: codex_app_server # 默认值为 "auto"(= Hermes 运行时)
|
||||
```
|
||||
|
||||
## 自我改进循环(记忆 + 技能提示)
|
||||
|
||||
Hermes 的后台自我改进在计数器达到阈值时触发:
|
||||
|
||||
- 每 10 个用户 prompt(提示词)→ 一个分叉的审查 agent 查看对话,决定是否有内容应保存到记忆中。
|
||||
- 单次轮次内每 10 次工具迭代 → 同样的逻辑,但针对技能(`skill_manage` 写入)。
|
||||
|
||||
**两者在 Codex 运行时上均持续生效。** Codex 路径将每个已完成的 `commandExecution` / `fileChange` / `mcpToolCall` / `dynamicToolCall` 事件项投影为合成的 `assistant tool_call` + `tool` 结果消息,因此审查运行时看到的格式与在默认 Hermes 运行时上看到的相同。
|
||||
|
||||
连接方式保持等效:
|
||||
|
||||
| | 默认运行时 | Codex 运行时 |
|
||||
|---|---|---|
|
||||
| `_turns_since_memory` 递增 | 每个用户 prompt,在 run_conversation 预循环中 | 相同代码路径,在提前返回之前 |
|
||||
| `_iters_since_skill` 递增 | 在聊天补全循环的每次工具迭代中 | 通过 Codex 轮次返回后的 `turn.tool_iterations` |
|
||||
| 记忆触发(`_turns_since_memory >= _memory_nudge_interval`) | 在预循环中计算,响应后触发 | 在预循环中计算,传递给 Codex 辅助函数 |
|
||||
| 技能触发(`_iters_since_skill >= _skill_nudge_interval`) | 在循环结束后计算 | 在 Codex 轮次结束后计算 |
|
||||
| `_spawn_background_review(messages_snapshot=..., review_memory=..., review_skills=...)` | 任一触发器触发时调用 | 任一触发器触发时以相同方式调用 |
|
||||
|
||||
一个细节:审查分叉本身需要调用 Hermes 的 agent 循环工具(`memory`、`skill_manage`),这需要 Hermes 自身的分发。因此,当父 agent 处于 `codex_app_server` 时,审查分叉会**降级为 `codex_responses`**——相同的 OAuth 凭据,相同的 `openai-codex` 提供商,但直接与 OpenAI 的 Responses API 通信,使 Hermes 拥有循环控制权,agent 循环工具得以正常工作。这对用户不可见。
|
||||
|
||||
最终效果:启用 Codex 运行时后,你的记忆 + 技能提示计数器与之前完全一样持续触发。
|
||||
|
||||
## 审批流程
|
||||
|
||||
Codex 在执行命令或应用 patch 之前会请求审批。这些请求会被转换为 Hermes 标准的"危险命令"提示:
|
||||
|
||||
```
|
||||
╭───────────────────────────────────────╮
|
||||
│ Dangerous Command │
|
||||
│ │
|
||||
│ /bin/bash -lc 'echo hello > foo.txt' │
|
||||
│ │
|
||||
│ ❯ 1. Allow once │
|
||||
│ 2. Allow for this session │
|
||||
│ 3. Deny │
|
||||
│ │
|
||||
│ Codex requests exec in /your/cwd │
|
||||
╰───────────────────────────────────────╯
|
||||
```
|
||||
|
||||
- **Allow once** → 批准此单次命令。
|
||||
- **Allow for this session** → Codex 不会再对类似命令重复提示。
|
||||
- **Deny** → 命令被拒绝;Codex 以只读模式继续运行。
|
||||
|
||||
对于 `apply_patch`(文件编辑)审批,当 Codex 通过对应的 `fileChange` 事件项提供数据时,Hermes 会显示变更摘要(`1 add, 1 update: /tmp/new.py, /tmp/old.py`)。
|
||||
|
||||
## 权限配置文件
|
||||
|
||||
Codex 有三个内置权限配置文件:
|
||||
- `:read-only` — 禁止写入;每条 shell 命令都需要审批
|
||||
- `:workspace` — 允许在当前工作区内写入而无需提示(启用运行时时 Hermes 的默认值)
|
||||
- `:danger-no-sandbox` — 完全不使用沙箱(除非你清楚其含义,否则不要使用)
|
||||
|
||||
你可以在 Hermes 管理块之外的 `~/.codex/config.toml` 中覆盖默认值:
|
||||
|
||||
```toml
|
||||
default_permissions = ":read-only"
|
||||
```
|
||||
|
||||
(只要你的覆盖配置位于 `# managed by hermes-agent` 标记之外,Hermes 在重新迁移时会保留它。)
|
||||
|
||||
## 辅助任务与 ChatGPT 订阅 token 消耗
|
||||
|
||||
当此运行时与 `openai-codex` 提供商一起开启时,**辅助任务(标题生成、上下文压缩、视觉自动检测、后台自我改进审查分叉)默认也会通过你的 ChatGPT 订阅流转**,因为 Hermes 的辅助客户端在没有设置每任务覆盖时使用主提供商/模型。
|
||||
|
||||
这并非 `codex_app_server` 特有——现有的 `codex_responses` 路径也是如此——但在这里更为明显,因为你是在明确选择订阅计费。
|
||||
|
||||
要将特定辅助任务路由到更便宜/不同的模型,请在 `~/.hermes/config.yaml` 中设置显式覆盖:
|
||||
|
||||
```yaml
|
||||
auxiliary:
|
||||
title_generation:
|
||||
provider: openrouter
|
||||
model: google/gemini-3-flash-preview
|
||||
context_compression:
|
||||
provider: openrouter
|
||||
model: google/gemini-3-flash-preview
|
||||
vision_detect:
|
||||
provider: openrouter
|
||||
model: google/gemini-3-flash-preview
|
||||
goal_judge:
|
||||
provider: openrouter
|
||||
model: google/gemini-3-flash-preview
|
||||
```
|
||||
|
||||
自我改进审查分叉通过 `_current_main_runtime()` 继承主运行时,Hermes 会自动将其从 `codex_app_server` 降级为 `codex_responses`(以便分叉能够实际调用 `memory` 和 `skill_manage`——Hermes 自身的 agent 循环工具)。除非你已将辅助任务路由到其他地方,否则该分叉仍使用你的订阅认证。
|
||||
|
||||
## 安全编辑 `~/.codex/config.toml`
|
||||
|
||||
Hermes 将其管理的所有内容包裹在两个标记注释之间:
|
||||
|
||||
```toml
|
||||
# managed by hermes-agent — `hermes codex-runtime migrate` regenerates this section
|
||||
default_permissions = ":workspace"
|
||||
[mcp_servers.filesystem]
|
||||
...
|
||||
[plugins."github@openai-curated"]
|
||||
...
|
||||
# end hermes-agent managed section
|
||||
```
|
||||
|
||||
该块**之外**的内容归你所有。重新运行迁移(通过 `/codex-runtime codex_app_server` 或每次切换运行时时)会原地替换管理块,但完整保留其上下方的用户内容。这意味着你可以:
|
||||
|
||||
- 添加 Hermes 不知道的自定义 MCP server
|
||||
- 将 `default_permissions` 覆盖为 `:read-only`(如果你希望被提示)
|
||||
- 配置仅 Codex 使用的选项(model、providers、otel 等)
|
||||
- 在 `[permissions.<name>]` 表中添加用户自定义权限配置文件
|
||||
|
||||
你在管理块**内部**添加的任何内容都会在下次迁移时被覆盖。如果你需要修改管理块中的某项配置,请提交 issue,我们会添加相应的开关。
|
||||
|
||||
## 多配置文件 / 多租户设置
|
||||
|
||||
默认情况下,无论哪个 Hermes 配置文件处于活跃状态,Hermes 都将 Codex 子进程指向 `~/.codex/`。这意味着 `hermes -p work` 和 `hermes -p personal` 共享相同的 Codex 认证、插件和配置。对大多数用户来说这是正确的行为——与直接运行 `codex` CLI 的效果一致。
|
||||
|
||||
如果你需要按配置文件隔离 Codex(独立的认证、独立的已安装插件、独立的配置),请为每个配置文件显式设置 `CODEX_HOME`。最简洁的方式是指向你 `HERMES_HOME` 下的某个目录:
|
||||
|
||||
```bash
|
||||
# 在 work 配置文件中,你可以这样包装 hermes:
|
||||
CODEX_HOME=~/.hermes/profiles/work/codex hermes chat
|
||||
```
|
||||
|
||||
你需要在设置了该 `CODEX_HOME` 的情况下重新运行一次 `codex login`,以便 OAuth token 落入配置文件范围的位置。之后,`hermes -p work` 将在隔离的 Codex 状态下运行。
|
||||
|
||||
我们不自动限定此范围,因为移动现有用户的 `~/.codex/` 会静默地使其 Codex CLI 认证失效——任何已运行过 `codex login` 的用户都需要重新认证。选择加入比给用户带来意外更安全。
|
||||
|
||||
## HOME 环境变量透传
|
||||
|
||||
Hermes 在生成 Codex app-server 子进程时**不会**重写 `HOME`(我们使用 `os.environ.copy()`,仅覆盖 `CODEX_HOME` 和 `RUST_LOG`)。这意味着:
|
||||
|
||||
- Codex 通过其 `shell` 工具运行的命令能看到真实的用户 `HOME`,并能正确找到 `~/.gitconfig`、`~/.gh/`、`~/.aws/`、`~/.npmrc` 等。
|
||||
- Codex 的内部状态通过 `CODEX_HOME` 保持隔离(默认指向 `~/.codex/`)。
|
||||
|
||||
这与 OpenClaw 在早期实验后得出的边界一致:隔离 Codex 的状态,保持用户主目录不变。(参见 openclaw/openclaw#81562。)
|
||||
|
||||
## MCP server 迁移
|
||||
|
||||
Hermes 的 `mcp_servers` 配置会自动转换为 Codex 所需的 TOML 格式。迁移在每次启用运行时时运行,且是幂等的——重新运行会替换管理块,但保留用户编辑的 Codex 配置。
|
||||
|
||||
转换内容:
|
||||
|
||||
| Hermes(`config.yaml`) | Codex(`config.toml`) |
|
||||
|---|---|
|
||||
| `command` + `args` + `env` | stdio transport |
|
||||
| `url` + `headers` | streamable_http transport |
|
||||
| `timeout` | `tool_timeout_sec` |
|
||||
| `connect_timeout` | `startup_timeout_sec` |
|
||||
| `enabled: false` | `enabled = false` |
|
||||
|
||||
未迁移的内容:
|
||||
- Hermes 特有的键,如 `sampling`(Codex 的 MCP 客户端没有等效项——这些会被丢弃并附带每个 server 的警告)。
|
||||
|
||||
## 原生 Codex 插件迁移
|
||||
|
||||
通过 `codex plugin` 安装的插件(Linear、GitHub、Gmail、Calendar、Canva 等)通过 Codex 的 `plugin/list` RPC 被发现。对于每个 `installed: true` 的插件,Hermes 会写入一个 `[plugins."<name>@openai-curated"]` 块,在你的 Hermes 会话中启用它。
|
||||
|
||||
这意味着:当你的朋友说"我在 Codex CLI 中设置了 Calendar 和 GitHub",他们启用 Hermes 的 Codex 运行时后,Hermes 会自动激活这些插件。无需重新配置。
|
||||
|
||||
**未迁移的内容:**
|
||||
- 你尚未安装的插件——请先在 Codex 中安装。
|
||||
- Codex 报告 `availability != AVAILABLE` 的插件(安装损坏、OAuth 过期、已从市场下架等)。这些会被跳过,以避免写入激活时会失败的配置。
|
||||
- ChatGPT 应用市场条目(每账户的 `app/list` 结果——这些已通过你的账户认证在 Codex 内部启用)。
|
||||
- 插件 OAuth——你在 Codex 本身中对每个插件授权一次;Hermes 不接触凭据。
|
||||
|
||||
## Hermes 工具回调(新 MCP server)
|
||||
|
||||
Codex 的内置工具集涵盖 shell/文件操作/patch,但没有网络搜索、浏览器自动化、视觉、图像生成等功能。为了在 Codex 轮次中保持这些工具可用,Hermes 在 `~/.codex/config.toml` 中将自身注册为 MCP server:
|
||||
|
||||
```toml
|
||||
[mcp_servers.hermes-tools]
|
||||
command = "/path/to/python"
|
||||
args = ["-m", "agent.transports.hermes_tools_mcp_server"]
|
||||
env = { HERMES_HOME = "/your/.hermes", PYTHONPATH = "...", HERMES_QUIET = "1" }
|
||||
startup_timeout_sec = 30.0
|
||||
tool_timeout_sec = 600.0
|
||||
```
|
||||
|
||||
当模型调用 `web_search`(或其他暴露的 Hermes 工具)时,Codex 通过 stdio 生成 `hermes_tools_mcp_server` 子进程,请求通过 `model_tools.handle_function_call()` 分发,结果像其他 MCP 响应一样投影回 Codex。
|
||||
|
||||
**通过回调可用的工具:** `web_search`、`web_extract`、`browser_navigate`、`browser_click`、`browser_type`、`browser_press`、`browser_snapshot`、`browser_scroll`、`browser_back`、`browser_get_images`、`browser_console`、`browser_vision`、`vision_analyze`、`image_generate`、`skill_view`、`skills_list`、`text_to_speech`。
|
||||
|
||||
**不可用的工具:** `delegate_task`、`memory`、`session_search`、`todo`。这些工具需要运行中的 AIAgent 上下文(循环中间状态)才能分发,无状态的 MCP 回调无法驱动它们。需要这些工具时,请使用默认 Hermes 运行时(`/codex-runtime auto`)。
|
||||
|
||||
## 禁用
|
||||
|
||||
随时切换回来:
|
||||
|
||||
```
|
||||
/codex-runtime auto
|
||||
```
|
||||
|
||||
在下一个会话生效。Codex 管理块保留在 `~/.codex/config.toml` 中,以便你之后重新启用时不会丢失配置——如果你希望,也可以手动删除它。
|
||||
|
||||
## 限制
|
||||
|
||||
此运行时为**可选启用的 beta 功能**。以下功能在 Hermes Agent 2026.5 + Codex CLI 0.130.0 上已验证可用:
|
||||
|
||||
- 多轮对话
|
||||
- 通过 Hermes UI 进行 `commandExecution` 和 `fileChange`(apply_patch)审批
|
||||
- MCP 工具调用(已针对 `@modelcontextprotocol/server-filesystem` 和新的 `hermes-tools` 回调验证)
|
||||
- 原生 Codex 插件迁移(已针对 Linear / GitHub / Calendar 清单验证)
|
||||
- 拒绝/取消路径
|
||||
- 开关切换循环
|
||||
- 记忆和技能提示计数器(已通过集成测试实时验证)
|
||||
- 通过 Codex 使用 Hermes web_search(已实时验证:"OpenAI Codex CLI – Getting Started" 端到端返回结果)
|
||||
|
||||
已知限制:
|
||||
|
||||
- **Hermes 认证和 Codex 认证是独立的会话。** 为获得最佳体验,你需要同时运行 `codex login` 和 `hermes auth login codex`(运行时使用 Codex 的会话进行 LLM 调用)。这是 Hermes `_import_codex_cli_tokens` 中的有意设计——Hermes 不会与 Codex CLI 共享 OAuth 状态,以避免在 token 刷新时相互覆盖。
|
||||
- **`delegate_task`、`memory`、`session_search`、`todo` 在此运行时上不可用。** 它们需要运行中的 AIAgent 上下文,无状态的 MCP 回调无法提供。需要这些工具时,请使用 `/codex-runtime auto`。
|
||||
- **当 Codex 未跟踪变更集时,审批提示中没有内联 patch 预览。** Codex 的 `fileChange` 审批参数并不总是携带变更集。Hermes 会尽可能从对应的 `item/started` 通知中缓存数据,但如果审批在事件项流式传输完成之前到达,提示会回退到 Codex 提供的 `reason`。
|
||||
- **亚秒级取消无法保证。** 流式传输中途的中断(Codex 响应时按 Ctrl+C)通过 `turn/interrupt` 发送,但如果 Codex 已经刷新了最终消息,你仍会收到该响应。
|
||||
|
||||
如果你发现 bug,请[提交 issue](https://github.com/NousResearch/hermes-agent/issues),附上 `hermes logs --since 5m` 的输出。在标题中注明 `codex-runtime` 以便于分类处理。
|
||||
|
||||
## 架构
|
||||
|
||||
```
|
||||
┌─── Hermes shell (CLI / TUI / gateway) ───┐
|
||||
│ sessions DB · slash commands · memory │
|
||||
│ & skill review · cron · session pickers │
|
||||
└──┬──────────────────────────────────────┬┘
|
||||
│ user_message final │
|
||||
▼ text + │
|
||||
┌──────────────────────────────────┐ projected │
|
||||
│ AIAgent.run_conversation() │ messages │
|
||||
│ if api_mode == codex_app_server │ │
|
||||
│ → CodexAppServerSession │ │
|
||||
│ else: chat_completions / codex_responses (default)
|
||||
└────┬─────────────────────────────┘ │
|
||||
│ JSON-RPC over stdio │
|
||||
▼ │
|
||||
┌──────────────────────────────────┐ │
|
||||
│ codex app-server (subprocess) │──────────────┘
|
||||
│ thread/start, turn/start │
|
||||
│ item/* notifications │
|
||||
│ shell + apply_patch + update_plan│
|
||||
│ view_image + sandbox │
|
||||
│ ┌─────────────────────────┐ │
|
||||
│ │ MCP client │ │
|
||||
│ │ ├─ user MCP servers │ │
|
||||
│ │ ├─ native plugins │ │
|
||||
│ │ │ (linear, github, │ │
|
||||
│ │ │ gmail, calendar, │ │
|
||||
│ │ │ canva, ...) │ │
|
||||
│ │ └─ hermes-tools ───────┼─────────────────┐
|
||||
│ │ (callback to │ │ │
|
||||
│ │ Hermes' richer │ │ │
|
||||
│ │ tools) │ │ │
|
||||
│ └─────────────────────────┘ │ │
|
||||
└──────────────────────────────────┘ │
|
||||
│
|
||||
▼
|
||||
┌──────────────────────────────────────────────────────────┐
|
||||
│ hermes_tools_mcp_server.py (subprocess on demand) │
|
||||
│ web_search, web_extract, browser_*, vision_analyze, │
|
||||
│ image_generate, skill_view, skills_list, text_to_speech│
|
||||
└──────────────────────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
有关实现细节,请参阅 [PR #24182](https://github.com/NousResearch/hermes-agent/pull/24182) 和 [Codex app-server 协议 README](https://github.com/openai/codex/blob/main/codex-rs/app-server/README.md)。
|
||||
@ -0,0 +1,140 @@
|
||||
# 电脑操控(macOS)
|
||||
|
||||
Hermes Agent 可以在**后台**驱动你的 Mac 桌面——点击、输入、滚动、拖拽。你的光标不会移动,键盘焦点不会改变,macOS 也不会切换 Spaces。你和 Agent 可以在同一台机器上协同工作。
|
||||
|
||||
与大多数电脑操控集成不同,这适用于**任何支持工具调用的模型**——Claude、GPT、Gemini,或本地 vLLM 端点上的开源模型。无需关心 Anthropic 原生 schema。
|
||||
|
||||
## 工作原理
|
||||
|
||||
`computer_use` 工具集通过 stdio 以 MCP 协议与 [`cua-driver`](https://github.com/trycua/cua) 通信。`cua-driver` 是一个 macOS 驱动,使用 SkyLight 私有 SPI(`SLEventPostToPid`、`SLPSPostEventRecordTo`)以及 `_AXObserverAddNotificationAndCheckRemote` 无障碍 SPI,实现以下功能:
|
||||
|
||||
- 直接向目标进程投递合成事件——无需 HID 事件 tap,无需光标跳转。
|
||||
- 在不提升窗口的情况下切换 AppKit 激活状态——不触发 Space 切换。
|
||||
- 在窗口被遮挡时保持 Chromium/Electron 无障碍树存活。
|
||||
|
||||
这一组合正是 OpenAI Codex「后台电脑操控」所采用的方案。cua-driver 是其开源等价实现。
|
||||
|
||||
## 启用
|
||||
|
||||
选择最方便的方式——两种方式运行的是同一个上游安装程序:
|
||||
|
||||
**方式一:使用专用 CLI 命令(最直接)。**
|
||||
|
||||
```
|
||||
hermes computer-use install
|
||||
```
|
||||
|
||||
此命令会获取并运行上游 cua-driver 安装脚本:
|
||||
`curl -fsSL https://raw.githubusercontent.com/trycua/cua/main/libs/cua-driver/scripts/install.sh`。
|
||||
使用 `hermes computer-use status` 验证安装结果。
|
||||
|
||||
**方式二:通过交互式界面启用工具集。**
|
||||
|
||||
1. 运行 `hermes tools`,选择 `🖱️ Computer Use (macOS)` → `cua-driver (background)`。
|
||||
2. 安装程序将运行上游安装脚本(与方式一相同)。
|
||||
|
||||
安装完成后,无论采用哪种方式,继续执行以下步骤:
|
||||
|
||||
3. 在提示时授予 macOS 权限:
|
||||
- **系统设置 → 隐私与安全性 → 辅助功能** → 允许终端(或 Hermes 应用)。
|
||||
- **系统设置 → 隐私与安全性 → 屏幕录制** → 允许同一应用。
|
||||
4. 启动启用了该工具集的会话:
|
||||
```
|
||||
hermes -t computer_use chat
|
||||
```
|
||||
或在 `~/.hermes/config.yaml` 中将 `computer_use` 添加到已启用的工具集列表。
|
||||
|
||||
## 保持 cua-driver 最新
|
||||
|
||||
cua-driver 项目会定期发布修复(例如 v0.1.6 修复了 UTM 工作流中的 Safari 窗口焦点问题)。Hermes 在两处刷新二进制文件,避免你停留在过时版本:
|
||||
|
||||
- **`hermes update`** — 更新 Hermes 本身时,如果 `cua-driver` 在 PATH 中,更新结束时会重新运行上游安装程序。对非 macOS 用户及未安装 cua-driver 的用户无操作。
|
||||
- **`hermes computer-use install --upgrade`** — 手动强制刷新。无论 cua-driver 是否已安装,都会重新运行上游安装程序。在不等待下次 Agent 更新的情况下获取最新修复时使用此命令。
|
||||
|
||||
`hermes computer-use status` 会在二进制路径旁显示已安装的版本号。
|
||||
|
||||
## 快速示例
|
||||
|
||||
用户 prompt(提示词):*「找到我最近一封来自 Stripe 的邮件,总结他们希望我做什么。」*
|
||||
|
||||
Agent 的执行计划:
|
||||
|
||||
1. `computer_use(action="capture", mode="som", app="Mail")` — 获取 Mail 的截图,其中每个侧边栏项目、工具栏按钮和邮件行均已编号。
|
||||
2. `computer_use(action="click", element=14)` — 点击搜索框(来自截图的第 #14 号元素)。
|
||||
3. `computer_use(action="type", text="from:stripe")`
|
||||
4. `computer_use(action="key", keys="return", capture_after=True)` — 提交并获取新截图。
|
||||
5. 点击最顶部的结果,读取正文,进行总结。
|
||||
|
||||
整个过程中,你的光标保持原位,Mail 窗口始终不会切换到前台。
|
||||
|
||||
## 提供商兼容性
|
||||
|
||||
| 提供商 | 支持视觉? | 可用? | 备注 |
|
||||
|---|---|---|---|
|
||||
| Anthropic(Claude Sonnet/Opus 3+) | ✅ | ✅ | 综合表现最佳;支持 SOM 与原始坐标。 |
|
||||
| OpenRouter(任意视觉模型) | ✅ | ✅ | 支持多部分工具消息。 |
|
||||
| OpenAI(GPT-4+、GPT-5) | ✅ | ✅ | 同上。 |
|
||||
| 本地 vLLM / LM Studio(视觉模型) | ✅ | ✅ | 需模型支持多部分工具内容。 |
|
||||
| 纯文本模型 | ❌ | ✅(降级) | 使用 `mode="ax"` 仅通过无障碍树操作。 |
|
||||
|
||||
截图以 OpenAI 风格的 `image_url` 部分内联在工具结果中发送。对于 Anthropic,适配器会将其转换为原生 `tool_result` 图像块。
|
||||
|
||||
## 安全性
|
||||
|
||||
Hermes 应用多层防护机制:
|
||||
|
||||
- 破坏性操作(click、type、drag、scroll、key、focus_app)需要审批——通过 CLI 对话框交互确认,或通过消息平台审批按钮确认。
|
||||
- 工具层面硬性屏蔽的按键组合:清空废纸篓、强制删除、锁定屏幕、注销、强制注销。
|
||||
- 硬性屏蔽的输入模式:`curl | bash`、`sudo rm -rf /`、fork bomb 等。
|
||||
- Agent 的系统 prompt 明确规定:不得点击权限对话框,不得输入密码,不得执行截图中嵌入的指令。
|
||||
|
||||
如需对每个操作进行确认,可在 `~/.hermes/config.yaml` 中配置 `approvals.mode: manual`。
|
||||
|
||||
## Token 效率
|
||||
|
||||
截图开销较大。Hermes 应用四层优化措施:
|
||||
|
||||
- **截图淘汰** — Anthropic 适配器在上下文中仅保留最近 3 张截图;较旧的截图替换为 `[screenshot removed to save context]` 占位符。
|
||||
- **客户端压缩裁剪** — 上下文压缩器检测多模态工具结果,并从旧结果中剥离图像部分。
|
||||
- **图像感知 token 估算** — 每张图像计为约 1500 个 token(Anthropic 的固定费率),而非其 base64 字符长度。
|
||||
- **服务端上下文编辑(仅限 Anthropic)** — 激活后,适配器通过 `context_management` 启用 `clear_tool_uses_20250919`,由 Anthropic API 在服务端清除旧工具结果。
|
||||
|
||||
在 1568×900 分辨率下执行 20 个操作的会话,截图上下文通常消耗约 3 万个 token,而非约 60 万个。
|
||||
|
||||
## 限制
|
||||
|
||||
- **仅限 macOS。** cua-driver 使用的私有 Apple SPI 在 Linux 或 Windows 上不存在。跨平台 GUI 自动化请使用 `browser` 工具集。
|
||||
- **私有 SPI 风险。** Apple 可能在任何 OS 更新中更改 SkyLight 的符号接口。如需在 macOS 版本升级时保持可复现性,请通过 `HERMES_CUA_DRIVER_VERSION` 环境变量固定驱动版本。
|
||||
- **性能。** 后台模式比前台模式慢——SkyLight 路由事件耗时约 5–20ms,而直接 HID 投递更快。对于 Agent 速度的点击操作无明显影响;若尝试录制速通视频则会有感知。
|
||||
- **不支持键盘输入密码。** `type` 对命令行 payload 有硬性屏蔽模式;密码请使用系统自动填充功能。
|
||||
|
||||
## 配置
|
||||
|
||||
覆盖驱动二进制路径(用于测试 / CI):
|
||||
|
||||
```
|
||||
HERMES_CUA_DRIVER_CMD=/opt/homebrew/bin/cua-driver
|
||||
HERMES_CUA_DRIVER_VERSION=0.5.0 # optional pin
|
||||
```
|
||||
|
||||
完全替换后端(用于测试):
|
||||
|
||||
```
|
||||
HERMES_COMPUTER_USE_BACKEND=noop # records calls, no side effects
|
||||
```
|
||||
|
||||
## 故障排查
|
||||
|
||||
**`computer_use backend unavailable: cua-driver is not installed`** — 运行 `hermes computer-use install` 获取 cua-driver 二进制文件,或运行 `hermes tools` 并启用 Computer Use 工具集。
|
||||
|
||||
**点击似乎没有效果** — 截图并验证。可能有一个你未注意到的模态框正在阻止输入。使用 `escape` 或关闭按钮将其关闭。
|
||||
|
||||
**元素索引已过期** — SOM 索引仅在下次 `capture` 之前有效。任何改变状态的操作后请重新截图。
|
||||
|
||||
**「blocked pattern in type text」** — 你尝试 `type` 的文本匹配了危险 shell 模式列表。请拆分命令或重新考虑操作方式。
|
||||
|
||||
## 另请参阅
|
||||
|
||||
- [通用技能:`macos-computer-use`](https://github.com/NousResearch/hermes-agent/blob/main/skills/apple/macos-computer-use/SKILL.md)
|
||||
- [cua-driver 源码(trycua/cua)](https://github.com/trycua/cua)
|
||||
- 跨平台 Web 任务请参阅[浏览器自动化](./browser.md)。
|
||||
@ -0,0 +1,218 @@
|
||||
---
|
||||
sidebar_position: 8
|
||||
title: "上下文文件"
|
||||
description: "项目上下文文件 — .hermes.md、AGENTS.md、CLAUDE.md、全局 SOUL.md 以及 .cursorrules — 自动注入每次对话"
|
||||
---
|
||||
|
||||
# 上下文文件
|
||||
|
||||
Hermes Agent 会自动发现并加载上下文文件,以塑造其行为方式。部分文件属于项目本地文件,从工作目录中发现。`SOUL.md` 现在对整个 Hermes 实例全局生效,仅从 `HERMES_HOME` 加载。
|
||||
|
||||
## 支持的上下文文件
|
||||
|
||||
| 文件 | 用途 | 发现方式 |
|
||||
|------|---------|-----------|
|
||||
| **.hermes.md** / **HERMES.md** | 项目指令(最高优先级) | 向上遍历至 git 根目录 |
|
||||
| **AGENTS.md** | 项目指令、规范、架构说明 | 启动时的 CWD 及子目录(渐进式) |
|
||||
| **CLAUDE.md** | Claude Code 上下文文件(同样支持检测) | 启动时的 CWD 及子目录(渐进式) |
|
||||
| **SOUL.md** | 当前 Hermes 实例的全局个性与语气定制 | 仅 `HERMES_HOME/SOUL.md` |
|
||||
| **.cursorrules** | Cursor IDE 编码规范 | 仅 CWD |
|
||||
| **.cursor/rules/*.mdc** | Cursor IDE 规则模块 | 仅 CWD |
|
||||
|
||||
:::info 优先级系统
|
||||
每次会话仅加载**一种**项目上下文类型(先匹配先生效):`.hermes.md` → `AGENTS.md` → `CLAUDE.md` → `.cursorrules`。**SOUL.md** 始终作为 agent 身份独立加载(插槽 #1)。
|
||||
:::
|
||||
|
||||
## AGENTS.md
|
||||
|
||||
`AGENTS.md` 是主要的项目上下文文件。它告知 agent 项目的结构、需要遵循的规范以及任何特殊指令。
|
||||
|
||||
### 渐进式子目录发现
|
||||
|
||||
会话启动时,Hermes 将工作目录中的 `AGENTS.md` 加载到系统 prompt(提示词)中。在会话期间,当 agent 通过 `read_file`、`terminal`、`search_files` 等工具导航进入子目录时,它会**渐进式发现**这些目录中的上下文文件,并在其变得相关的时刻将其注入对话。
|
||||
|
||||
```
|
||||
my-project/
|
||||
├── AGENTS.md ← 启动时加载(系统 prompt)
|
||||
├── frontend/
|
||||
│ └── AGENTS.md ← agent 读取 frontend/ 文件时发现
|
||||
├── backend/
|
||||
│ └── AGENTS.md ← agent 读取 backend/ 文件时发现
|
||||
└── shared/
|
||||
└── AGENTS.md ← agent 读取 shared/ 文件时发现
|
||||
```
|
||||
|
||||
与启动时加载所有内容相比,此方式有两个优势:
|
||||
- **避免系统 prompt 膨胀** — 子目录提示仅在需要时出现
|
||||
- **保留 prompt 缓存** — 系统 prompt 在各轮次间保持稳定
|
||||
|
||||
每个子目录在每次会话中最多检查一次。发现机制同样会向上遍历父目录,因此读取 `backend/src/main.py` 时,即使 `backend/src/` 没有自己的上下文文件,也会发现 `backend/AGENTS.md`。
|
||||
|
||||
:::info
|
||||
子目录上下文文件与启动时的上下文文件经过相同的[安全扫描](#security-prompt-injection-protection)。恶意文件会被拦截。
|
||||
:::
|
||||
|
||||
### AGENTS.md 示例
|
||||
|
||||
```markdown
|
||||
# Project Context
|
||||
|
||||
This is a Next.js 14 web application with a Python FastAPI backend.
|
||||
|
||||
## Architecture
|
||||
- Frontend: Next.js 14 with App Router in `/frontend`
|
||||
- Backend: FastAPI in `/backend`, uses SQLAlchemy ORM
|
||||
- Database: PostgreSQL 16
|
||||
- Deployment: Docker Compose on a Hetzner VPS
|
||||
|
||||
## Conventions
|
||||
- Use TypeScript strict mode for all frontend code
|
||||
- Python code follows PEP 8, use type hints everywhere
|
||||
- All API endpoints return JSON with `{data, error, meta}` shape
|
||||
- Tests go in `__tests__/` directories (frontend) or `tests/` (backend)
|
||||
|
||||
## Important Notes
|
||||
- Never modify migration files directly — use Alembic commands
|
||||
- The `.env.local` file has real API keys, don't commit it
|
||||
- Frontend port is 3000, backend is 8000, DB is 5432
|
||||
```
|
||||
|
||||
## SOUL.md
|
||||
|
||||
`SOUL.md` 控制 agent 的个性、语气和沟通风格。完整详情请参阅[个性](/user-guide/features/personality)页面。
|
||||
|
||||
**位置:**
|
||||
|
||||
- `~/.hermes/SOUL.md`
|
||||
- 或 `$HERMES_HOME/SOUL.md`(若使用自定义主目录运行 Hermes)
|
||||
|
||||
重要说明:
|
||||
|
||||
- 若 `SOUL.md` 尚不存在,Hermes 会自动生成一个默认文件
|
||||
- Hermes 仅从 `HERMES_HOME` 加载 `SOUL.md`
|
||||
- Hermes 不会在工作目录中探测 `SOUL.md`
|
||||
- 若文件为空,`SOUL.md` 中的内容不会添加到 prompt
|
||||
- 若文件有内容,内容在扫描和截断后原样注入
|
||||
|
||||
## .cursorrules
|
||||
|
||||
Hermes 兼容 Cursor IDE 的 `.cursorrules` 文件和 `.cursor/rules/*.mdc` 规则模块。若这些文件存在于项目根目录,且未找到更高优先级的上下文文件(`.hermes.md`、`AGENTS.md` 或 `CLAUDE.md`),则将其作为项目上下文加载。
|
||||
|
||||
这意味着使用 Hermes 时,现有的 Cursor 规范会自动生效。
|
||||
|
||||
## 上下文文件的加载方式
|
||||
|
||||
### 启动时(系统 prompt)
|
||||
|
||||
上下文文件由 `agent/prompt_builder.py` 中的 `build_context_files_prompt()` 加载:
|
||||
|
||||
1. **扫描工作目录** — 依次检查 `.hermes.md` → `AGENTS.md` → `CLAUDE.md` → `.cursorrules`(先匹配先生效)
|
||||
2. **读取内容** — 以 UTF-8 文本读取每个文件
|
||||
3. **安全扫描** — 检查内容是否存在 prompt 注入模式
|
||||
4. **截断** — 超过 20,000 个字符的文件进行首尾截断(70% 头部,20% 尾部,中间插入标记)
|
||||
5. **组装** — 所有部分合并在 `# Project Context` 标题下
|
||||
6. **注入** — 组装后的内容添加到系统 prompt
|
||||
|
||||
### 会话期间(渐进式发现)
|
||||
|
||||
`agent/subdirectory_hints.py` 中的 `SubdirectoryHintTracker` 监视工具调用参数中的文件路径:
|
||||
|
||||
1. **路径提取** — 每次工具调用后,从参数(`path`、`workdir`、shell 命令)中提取文件路径
|
||||
2. **祖先目录遍历** — 检查该目录及最多 5 个父目录(跳过已访问的目录)
|
||||
3. **提示加载** — 若发现 `AGENTS.md`、`CLAUDE.md` 或 `.cursorrules`,则加载(每个目录先匹配先生效)
|
||||
4. **安全扫描** — 与启动文件相同的 prompt 注入扫描
|
||||
5. **截断** — 每个文件最多 8,000 个字符
|
||||
6. **注入** — 追加到工具结果中,使模型在上下文中自然看到
|
||||
|
||||
最终 prompt 部分大致如下:
|
||||
|
||||
```text
|
||||
# Project Context
|
||||
|
||||
The following project context files have been loaded and should be followed:
|
||||
|
||||
## AGENTS.md
|
||||
|
||||
[Your AGENTS.md content here]
|
||||
|
||||
## .cursorrules
|
||||
|
||||
[Your .cursorrules content here]
|
||||
|
||||
[Your SOUL.md content here]
|
||||
```
|
||||
|
||||
注意,SOUL 内容直接插入,不带额外的包装文本。
|
||||
|
||||
## 安全性:Prompt 注入防护
|
||||
|
||||
所有上下文文件在被纳入之前都会扫描潜在的 prompt 注入。扫描器检查以下内容:
|
||||
|
||||
- **指令覆盖尝试**:「ignore previous instructions」、「disregard your rules」
|
||||
- **欺骗模式**:「do not tell the user」
|
||||
- **系统 prompt 覆盖**:「system prompt override」
|
||||
- **隐藏 HTML 注释**:`<!-- ignore instructions -->`
|
||||
- **隐藏 div 元素**:`<div style="display:none">`
|
||||
- **凭据窃取**:`curl ... $API_KEY`
|
||||
- **密钥文件访问**:`cat .env`、`cat credentials`
|
||||
- **不可见字符**:零宽空格、双向覆盖字符、词连接符
|
||||
|
||||
若检测到任何威胁模式,该文件将被拦截:
|
||||
|
||||
```
|
||||
[BLOCKED: AGENTS.md contained potential prompt injection (prompt_injection). Content not loaded.]
|
||||
```
|
||||
|
||||
:::warning
|
||||
此扫描器可防范常见注入模式,但不能替代对上下文文件的人工审查。对于非本人编写的共享仓库,请务必验证 AGENTS.md 的内容。
|
||||
:::
|
||||
|
||||
## 大小限制
|
||||
|
||||
| 限制 | 值 |
|
||||
|-------|-------|
|
||||
| 每个文件最大字符数 | 20,000(约 7,000 个 token) |
|
||||
| 头部截断比例 | 70% |
|
||||
| 尾部截断比例 | 20% |
|
||||
| 截断标记 | 10%(显示字符数并建议使用文件工具) |
|
||||
|
||||
当文件超过 20,000 个字符时,截断提示如下:
|
||||
|
||||
```
|
||||
[...truncated AGENTS.md: kept 14000+4000 of 25000 chars. Use file tools to read the full file.]
|
||||
```
|
||||
|
||||
## 有效使用上下文文件的技巧
|
||||
|
||||
:::tip AGENTS.md 最佳实践
|
||||
1. **保持简洁** — 远低于 20K 字符;agent 每轮都会读取
|
||||
2. **使用标题结构** — 用 `##` 分节描述架构、规范、重要说明
|
||||
3. **包含具体示例** — 展示首选代码模式、API 结构、命名规范
|
||||
4. **说明禁止事项** — 例如「不得直接修改迁移文件」
|
||||
5. **列出关键路径和端口** — agent 在执行终端命令时会用到
|
||||
6. **随项目演进更新** — 过时的上下文比没有上下文更糟
|
||||
:::
|
||||
|
||||
### 子目录上下文
|
||||
|
||||
对于 monorepo,在嵌套的 AGENTS.md 文件中放置子目录专属指令:
|
||||
|
||||
```markdown
|
||||
<!-- frontend/AGENTS.md -->
|
||||
# Frontend Context
|
||||
|
||||
- Use `pnpm` not `npm` for package management
|
||||
- Components go in `src/components/`, pages in `src/app/`
|
||||
- Use Tailwind CSS, never inline styles
|
||||
- Run tests with `pnpm test`
|
||||
```
|
||||
|
||||
```markdown
|
||||
<!-- backend/AGENTS.md -->
|
||||
# Backend Context
|
||||
|
||||
- Use `poetry` for dependency management
|
||||
- Run the dev server with `poetry run uvicorn main:app --reload`
|
||||
- All endpoints need OpenAPI docstrings
|
||||
- Database models are in `models/`, schemas in `schemas/`
|
||||
```
|
||||
@ -0,0 +1,142 @@
|
||||
---
|
||||
sidebar_position: 9
|
||||
sidebar_label: "Context References"
|
||||
title: "Context References"
|
||||
description: "用于将文件、文件夹、git diff 及 URL 直接附加到消息中的内联 @-语法"
|
||||
---
|
||||
|
||||
# Context References
|
||||
|
||||
输入 `@` 后跟一个引用,即可将内容直接注入消息。Hermes 会将引用内联展开,并在 `--- Attached Context ---` 区块下追加相应内容。
|
||||
|
||||
## 支持的引用类型
|
||||
|
||||
| 语法 | 说明 |
|
||||
|--------|-------------|
|
||||
| `@file:path/to/file.py` | 注入文件内容 |
|
||||
| `@file:path/to/file.py:10-25` | 注入指定行范围(从 1 开始,含首尾) |
|
||||
| `@folder:path/to/dir` | 注入目录树列表及文件元数据 |
|
||||
| `@diff` | 注入 `git diff`(未暂存的工作区变更) |
|
||||
| `@staged` | 注入 `git diff --staged`(已暂存的变更) |
|
||||
| `@git:5` | 注入最近 N 次提交及补丁(最多 10 次) |
|
||||
| `@url:https://example.com` | 抓取并注入网页内容 |
|
||||
|
||||
## 使用示例
|
||||
|
||||
```text
|
||||
Review @file:src/main.py and suggest improvements
|
||||
|
||||
What changed? @diff
|
||||
|
||||
Compare @file:old_config.yaml and @file:new_config.yaml
|
||||
|
||||
What's in @folder:src/components?
|
||||
|
||||
Summarize this article @url:https://arxiv.org/abs/2301.00001
|
||||
```
|
||||
|
||||
单条消息中可使用多个引用:
|
||||
|
||||
```text
|
||||
Check @file:main.py, and also @file:test.py.
|
||||
```
|
||||
|
||||
引用值末尾的标点符号(`,`、`.`、`;`、`!`、`?`)会被自动去除。
|
||||
|
||||
## CLI Tab 补全
|
||||
|
||||
在交互式 CLI 中,输入 `@` 会触发自动补全:
|
||||
|
||||
- `@` 显示所有引用类型(`@diff`、`@staged`、`@file:`、`@folder:`、`@git:`、`@url:`)
|
||||
- `@file:` 和 `@folder:` 触发文件系统路径补全,并显示文件大小元数据
|
||||
- 裸 `@` 后跟部分文本时,显示当前目录中匹配的文件和文件夹
|
||||
|
||||
## 行范围
|
||||
|
||||
`@file:` 引用支持行范围,用于精确注入内容:
|
||||
|
||||
```text
|
||||
@file:src/main.py:42 # 单行第 42 行
|
||||
@file:src/main.py:10-25 # 第 10 至 25 行(含首尾)
|
||||
```
|
||||
|
||||
行号从 1 开始。无效范围会被静默忽略(返回完整文件)。
|
||||
|
||||
## 大小限制
|
||||
|
||||
Context references 受大小限制,以防止超出模型的 context window(上下文窗口):
|
||||
|
||||
| 阈值 | 值 | 行为 |
|
||||
|-----------|-------|----------|
|
||||
| 软限制 | 上下文长度的 25% | 追加警告,继续展开 |
|
||||
| 硬限制 | 上下文长度的 50% | 拒绝展开,返回原始消息不变 |
|
||||
| 文件夹条目 | 最多 200 个文件 | 超出部分替换为 `- ...` |
|
||||
| Git 提交数 | 最多 10 次 | `@git:N` 限制在 [1, 10] 范围内 |
|
||||
|
||||
## 安全性
|
||||
|
||||
### 敏感路径拦截
|
||||
|
||||
以下路径始终被 `@file:` 引用拦截,以防止凭据泄露:
|
||||
|
||||
- SSH 密钥及配置:`~/.ssh/id_rsa`、`~/.ssh/id_ed25519`、`~/.ssh/authorized_keys`、`~/.ssh/config`
|
||||
- Shell 配置文件:`~/.bashrc`、`~/.zshrc`、`~/.profile`、`~/.bash_profile`、`~/.zprofile`
|
||||
- 凭据文件:`~/.netrc`、`~/.pgpass`、`~/.npmrc`、`~/.pypirc`
|
||||
- Hermes 环境文件:`$HERMES_HOME/.env`
|
||||
|
||||
以下目录被完全拦截(目录内的任意文件均不可访问):
|
||||
- `~/.ssh/`、`~/.aws/`、`~/.gnupg/`、`~/.kube/`、`$HERMES_HOME/skills/.hub/`
|
||||
|
||||
### 路径遍历防护
|
||||
|
||||
所有路径均相对于工作目录解析。解析结果超出允许的工作区根目录的引用将被拒绝。
|
||||
|
||||
### 二进制文件检测
|
||||
|
||||
通过 MIME 类型和空字节扫描检测二进制文件。已知文本扩展名(`.py`、`.md`、`.json`、`.yaml`、`.toml`、`.js`、`.ts` 等)会跳过基于 MIME 的检测。二进制文件将被拒绝并附带警告。
|
||||
|
||||
## 平台可用性
|
||||
|
||||
Context references 主要是 **CLI 功能**。它们在交互式 CLI 中有效,`@` 触发 tab 补全,引用在消息发送给 agent 之前完成展开。
|
||||
|
||||
在**消息平台**(Telegram、Discord 等)中,`@` 语法不会被 gateway 展开——消息原样透传。agent 本身仍可通过 `read_file`、`search_files` 和 `web_extract` 工具引用文件。
|
||||
|
||||
## 与 Context 压缩的交互
|
||||
|
||||
当对话 context 被压缩时,展开后的引用内容会被纳入压缩摘要。这意味着:
|
||||
|
||||
- 通过 `@file:` 注入的大文件内容会占用 context 用量
|
||||
- 若对话后续被压缩,文件内容将被摘要处理(而非原文保留)
|
||||
- 对于非常大的文件,建议使用行范围(`@file:main.py:100-200`)仅注入相关片段
|
||||
|
||||
## 常用模式
|
||||
|
||||
```text
|
||||
# 代码审查工作流
|
||||
Review @diff and check for security issues
|
||||
|
||||
# 带上下文的调试
|
||||
This test is failing. Here's the test @file:tests/test_auth.py
|
||||
and the implementation @file:src/auth.py:50-80
|
||||
|
||||
# 项目探索
|
||||
What does this project do? @folder:src @file:README.md
|
||||
|
||||
# 研究
|
||||
Compare the approaches in @url:https://arxiv.org/abs/2301.00001
|
||||
and @url:https://arxiv.org/abs/2301.00002
|
||||
```
|
||||
|
||||
## 错误处理
|
||||
|
||||
无效引用会产生内联警告而非直接报错:
|
||||
|
||||
| 条件 | 行为 |
|
||||
|-----------|----------|
|
||||
| 文件未找到 | 警告:"file not found" |
|
||||
| 二进制文件 | 警告:"binary files are not supported" |
|
||||
| 文件夹未找到 | 警告:"folder not found" |
|
||||
| Git 命令失败 | 警告附带 git stderr 输出 |
|
||||
| URL 无内容返回 | 警告:"no content extracted" |
|
||||
| 敏感路径 | 警告:"path is a sensitive credential file" |
|
||||
| 路径超出工作区 | 警告:"path is outside the allowed workspace" |
|
||||
@ -0,0 +1,237 @@
|
||||
---
|
||||
title: 凭证池
|
||||
description: 为每个提供商池化多个 API 密钥或 OAuth 令牌,实现自动轮换和速率限制恢复。
|
||||
sidebar_label: 凭证池
|
||||
sidebar_position: 9
|
||||
---
|
||||
|
||||
# 凭证池
|
||||
|
||||
凭证池允许你为同一提供商注册多个 API 密钥或 OAuth 令牌。当某个密钥触达速率限制或计费配额时,Hermes 会自动轮换到下一个健康密钥——在不切换提供商的情况下保持会话持续运行。
|
||||
|
||||
这与[备用提供商](./fallback-providers.md)不同,后者会切换到*另一个*提供商。凭证池是同一提供商内的轮换;备用提供商是跨提供商的故障转移。池会优先尝试——如果池中所有密钥都耗尽,*才会*激活备用提供商。
|
||||
|
||||
## 工作原理
|
||||
|
||||
```
|
||||
Your request
|
||||
→ Pick key from pool (round_robin / least_used / fill_first / random)
|
||||
→ Send to provider
|
||||
→ 429 rate limit?
|
||||
→ Retry same key once (transient blip)
|
||||
→ Second 429 → rotate to next pool key
|
||||
→ All keys exhausted → fallback_model (different provider)
|
||||
→ 402 billing error?
|
||||
→ Immediately rotate to next pool key (24h cooldown)
|
||||
→ 401 auth expired?
|
||||
→ Try refreshing the token (OAuth)
|
||||
→ Refresh failed → rotate to next pool key
|
||||
→ Success → continue normally
|
||||
```
|
||||
|
||||
## 快速开始
|
||||
|
||||
如果你已在 `.env` 中设置了 API 密钥,Hermes 会自动将其识别为单密钥池。要充分利用池化功能,请添加更多密钥:
|
||||
|
||||
```bash
|
||||
# Add a second OpenRouter key
|
||||
hermes auth add openrouter --api-key sk-or-v1-your-second-key
|
||||
|
||||
# Add a second Anthropic key
|
||||
hermes auth add anthropic --type api-key --api-key sk-ant-api03-your-second-key
|
||||
|
||||
# Add an Anthropic OAuth credential (requires Claude Max plan + extra usage credits)
|
||||
hermes auth add anthropic --type oauth
|
||||
# Opens browser for OAuth login
|
||||
```
|
||||
|
||||
查看你的池:
|
||||
|
||||
```bash
|
||||
hermes auth list
|
||||
```
|
||||
|
||||
输出:
|
||||
```
|
||||
openrouter (2 credentials):
|
||||
#1 OPENROUTER_API_KEY api_key env:OPENROUTER_API_KEY ←
|
||||
#2 backup-key api_key manual
|
||||
|
||||
anthropic (3 credentials):
|
||||
#1 hermes_pkce oauth hermes_pkce ←
|
||||
#2 claude_code oauth claude_code
|
||||
#3 ANTHROPIC_API_KEY api_key env:ANTHROPIC_API_KEY
|
||||
```
|
||||
|
||||
`←` 标记当前选中的凭证。
|
||||
|
||||
## 交互式管理
|
||||
|
||||
不带子命令运行 `hermes auth` 以进入交互式向导:
|
||||
|
||||
```bash
|
||||
hermes auth
|
||||
```
|
||||
|
||||
这会显示完整的池状态并提供操作菜单:
|
||||
|
||||
```
|
||||
What would you like to do?
|
||||
1. Add a credential
|
||||
2. Remove a credential
|
||||
3. Reset cooldowns for a provider
|
||||
4. Set rotation strategy for a provider
|
||||
5. Exit
|
||||
```
|
||||
|
||||
对于同时支持 API 密钥和 OAuth 的提供商(Anthropic、Nous、Codex),添加流程会询问类型:
|
||||
|
||||
```
|
||||
anthropic supports both API keys and OAuth login.
|
||||
1. API key (paste a key from the provider dashboard)
|
||||
2. OAuth login (authenticate via browser)
|
||||
Type [1/2]:
|
||||
```
|
||||
|
||||
## CLI 命令
|
||||
|
||||
| 命令 | 说明 |
|
||||
|---------|-------------|
|
||||
| `hermes auth` | 交互式池管理向导 |
|
||||
| `hermes auth list` | 显示所有池和凭证 |
|
||||
| `hermes auth list <provider>` | 显示指定提供商的池 |
|
||||
| `hermes auth add <provider>` | 添加凭证(提示选择类型和密钥) |
|
||||
| `hermes auth add <provider> --type api-key --api-key <key>` | 非交互式添加 API 密钥 |
|
||||
| `hermes auth add <provider> --type oauth` | 通过浏览器登录添加 OAuth 凭证 |
|
||||
| `hermes auth remove <provider> <index>` | 按从 1 开始的索引删除凭证 |
|
||||
| `hermes auth reset <provider>` | 清除所有冷却时间/耗尽状态 |
|
||||
|
||||
## 轮换策略
|
||||
|
||||
通过 `hermes auth` → "Set rotation strategy" 配置,或在 `config.yaml` 中设置:
|
||||
|
||||
```yaml
|
||||
credential_pool_strategies:
|
||||
openrouter: round_robin
|
||||
anthropic: least_used
|
||||
```
|
||||
|
||||
| 策略 | 行为 |
|
||||
|----------|----------|
|
||||
| `fill_first`(默认) | 持续使用第一个健康密钥直至耗尽,然后切换到下一个 |
|
||||
| `round_robin` | 均匀循环遍历所有密钥,每次选择后轮换 |
|
||||
| `least_used` | 始终选择请求次数最少的密钥 |
|
||||
| `random` | 在健康密钥中随机选择 |
|
||||
|
||||
## 错误恢复
|
||||
|
||||
池对不同错误的处理方式不同:
|
||||
|
||||
| 错误 | 行为 | 冷却时间 |
|
||||
|-------|----------|----------|
|
||||
| **429 速率限制** | 对同一密钥重试一次(瞬时错误)。连续第二次 429 则轮换到下一个密钥 | 1 小时 |
|
||||
| **402 计费/配额** | 立即轮换到下一个密钥 | 24 小时 |
|
||||
| **401 认证过期** | 先尝试刷新 OAuth 令牌。仅在刷新失败时才轮换 | — |
|
||||
| **所有密钥耗尽** | 若已配置则转入 `fallback_model` | — |
|
||||
|
||||
`has_retried_429` 标志在每次成功的 API 调用后重置,因此单次瞬时 429 不会触发轮换。
|
||||
|
||||
## 自定义端点池
|
||||
|
||||
自定义 OpenAI 兼容端点(Together.ai、RunPod、本地服务器)拥有各自的池,以 `config.yaml` 中 `custom_providers` 的端点名称作为键。
|
||||
|
||||
通过 `hermes model` 设置自定义端点时,会自动生成类似 "Together.ai" 或 "Local (localhost:8080)" 的名称,该名称即成为池的键。
|
||||
|
||||
```bash
|
||||
# After setting up a custom endpoint via hermes model:
|
||||
hermes auth list
|
||||
# Shows:
|
||||
# Together.ai (1 credential):
|
||||
# #1 config key api_key config:Together.ai ←
|
||||
|
||||
# Add a second key for the same endpoint:
|
||||
hermes auth add Together.ai --api-key sk-together-second-key
|
||||
```
|
||||
|
||||
自定义端点池以 `custom:` 前缀存储在 `auth.json` 的 `credential_pool` 下:
|
||||
|
||||
```json
|
||||
{
|
||||
"credential_pool": {
|
||||
"openrouter": [...],
|
||||
"custom:together.ai": [...]
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## 自动发现
|
||||
|
||||
Hermes 在启动时自动从多个来源发现凭证并初始化池:
|
||||
|
||||
| 来源 | 示例 | 自动初始化? |
|
||||
|--------|---------|-------------|
|
||||
| 环境变量 | `OPENROUTER_API_KEY`、`ANTHROPIC_API_KEY` | 是 |
|
||||
| OAuth 令牌(auth.json) | Codex device code、Nous device code | 是 |
|
||||
| Claude Code 凭证 | `~/.claude/.credentials.json` | 是(Anthropic) |
|
||||
| Hermes PKCE OAuth | `~/.hermes/auth.json` | 是(Anthropic) |
|
||||
| 自定义端点配置 | `config.yaml` 中的 `model.api_key` | 是(自定义端点) |
|
||||
| 手动条目 | 通过 `hermes auth add` 添加 | 持久化至 auth.json |
|
||||
|
||||
自动初始化的条目在每次池加载时更新——如果你删除了某个环境变量,其池条目会自动清除。通过 `hermes auth add` 添加的手动条目永远不会被自动清除。
|
||||
|
||||
## 委托与子代理共享
|
||||
|
||||
当代理通过 `delegate_task` 派生子代理时,父代理的凭证池会自动共享给子代理:
|
||||
|
||||
- **相同提供商** — 子代理接收父代理的完整池,在触达速率限制时可进行密钥轮换
|
||||
- **不同提供商** — 子代理加载该提供商自己的池(如已配置)
|
||||
- **未配置池** — 子代理回退到继承的单个 API 密钥
|
||||
|
||||
这意味着子代理无需额外配置即可获得与父代理相同的速率限制弹性。按任务的凭证租用机制确保子代理在并发轮换密钥时不会相互冲突。
|
||||
|
||||
## 线程安全
|
||||
|
||||
凭证池对所有状态变更操作(`select()`、`mark_exhausted_and_rotate()`、`try_refresh_current()`、`mark_used()`)使用线程锁,确保 gateway(网关)同时处理多个聊天会话时的并发访问安全。
|
||||
|
||||
## 架构
|
||||
|
||||
完整的数据流图请参见仓库中的 [`docs/credential-pool-flow.excalidraw`](https://excalidraw.com/#json=2Ycqhqpi6f12E_3ITyiwh,c7u9jSt5BwrmiVzHGbm87g)。
|
||||
|
||||
凭证池集成于提供商解析层:
|
||||
|
||||
1. **`agent/credential_pool.py`** — 池管理器:存储、选择、轮换、冷却时间
|
||||
2. **`hermes_cli/auth_commands.py`** — CLI 命令和交互式向导
|
||||
3. **`hermes_cli/runtime_provider.py`** — 感知池的凭证解析
|
||||
4. **`run_agent.py`** — 错误恢复:429/402/401 → 池轮换 → 备用
|
||||
|
||||
## 存储
|
||||
|
||||
池状态存储在 `~/.hermes/auth.json` 的 `credential_pool` 键下:
|
||||
|
||||
```json
|
||||
{
|
||||
"version": 1,
|
||||
"credential_pool": {
|
||||
"openrouter": [
|
||||
{
|
||||
"id": "abc123",
|
||||
"label": "OPENROUTER_API_KEY",
|
||||
"auth_type": "api_key",
|
||||
"priority": 0,
|
||||
"source": "env:OPENROUTER_API_KEY",
|
||||
"access_token": "sk-or-v1-...",
|
||||
"last_status": "ok",
|
||||
"request_count": 142
|
||||
}
|
||||
]
|
||||
},
|
||||
}
|
||||
```
|
||||
|
||||
策略存储在 `config.yaml` 中(而非 `auth.json`):
|
||||
|
||||
```yaml
|
||||
credential_pool_strategies:
|
||||
openrouter: round_robin
|
||||
anthropic: least_used
|
||||
```
|
||||
@ -0,0 +1,682 @@
|
||||
---
|
||||
sidebar_position: 5
|
||||
title: "定时任务(Cron)"
|
||||
description: "用自然语言调度自动化任务,通过单一 cron 工具管理,并附加一个或多个 skill"
|
||||
---
|
||||
|
||||
# 定时任务(Cron)
|
||||
|
||||
使用自然语言或 cron 表达式调度自动运行的任务。Hermes 通过单一 `cronjob` 工具暴露 cron 管理能力,采用动作式操作,而非分散的 schedule/list/remove 工具。
|
||||
|
||||
## Cron 当前能做什么
|
||||
|
||||
Cron 任务可以:
|
||||
|
||||
- 调度一次性或周期性任务
|
||||
- 暂停、恢复、编辑、触发和删除任务
|
||||
- 为任务附加零个、一个或多个 skill
|
||||
- 将结果回传到来源会话、本地文件或已配置的平台目标
|
||||
- 在全新的 agent 会话中运行,使用正常的静态工具列表
|
||||
- 以**无 agent 模式**运行——按计划执行脚本,其 stdout 原样投递,零 LLM 参与(参见下方[无 agent 模式](#no-agent-mode-script-only-jobs)章节)
|
||||
|
||||
所有这些功能均可通过 `cronjob` 工具由 Hermes 自身使用,因此你可以用自然语言创建、暂停、编辑和删除任务——无需 CLI。
|
||||
|
||||
:::warning
|
||||
Cron 运行的会话不能递归创建更多 cron 任务。Hermes 在 cron 执行内部禁用了 cron 管理工具,以防止失控的调度循环。
|
||||
:::
|
||||
|
||||
## 创建定时任务
|
||||
|
||||
### 在聊天中使用 `/cron`
|
||||
|
||||
```bash
|
||||
/cron add 30m "Remind me to check the build"
|
||||
/cron add "every 2h" "Check server status"
|
||||
/cron add "every 1h" "Summarize new feed items" --skill blogwatcher
|
||||
/cron add "every 1h" "Use both skills and combine the result" --skill blogwatcher --skill maps
|
||||
```
|
||||
|
||||
### 从独立 CLI
|
||||
|
||||
```bash
|
||||
hermes cron create "every 2h" "Check server status"
|
||||
hermes cron create "every 1h" "Summarize new feed items" --skill blogwatcher
|
||||
hermes cron create "every 1h" "Use both skills and combine the result" \
|
||||
--skill blogwatcher \
|
||||
--skill maps \
|
||||
--name "Skill combo"
|
||||
```
|
||||
|
||||
### 通过自然对话
|
||||
|
||||
直接向 Hermes 描述:
|
||||
|
||||
```text
|
||||
Every morning at 9am, check Hacker News for AI news and send me a summary on Telegram.
|
||||
```
|
||||
|
||||
Hermes 会在内部使用统一的 `cronjob` 工具。
|
||||
|
||||
## 附带 skill 的 cron 任务
|
||||
|
||||
Cron 任务可以在运行 prompt(提示词)之前加载一个或多个 skill。
|
||||
|
||||
### 单个 skill
|
||||
|
||||
```python
|
||||
cronjob(
|
||||
action="create",
|
||||
skill="blogwatcher",
|
||||
prompt="Check the configured feeds and summarize anything new.",
|
||||
schedule="0 9 * * *",
|
||||
name="Morning feeds",
|
||||
)
|
||||
```
|
||||
|
||||
### 多个 skill
|
||||
|
||||
Skill 按顺序加载。Prompt 作为任务指令叠加在这些 skill 之上。
|
||||
|
||||
```python
|
||||
cronjob(
|
||||
action="create",
|
||||
skills=["blogwatcher", "maps"],
|
||||
prompt="Look for new local events and interesting nearby places, then combine them into one short brief.",
|
||||
schedule="every 6h",
|
||||
name="Local brief",
|
||||
)
|
||||
```
|
||||
|
||||
当你希望定时 agent 继承可复用的工作流,而不必将完整的 skill 文本塞入 cron prompt 本身时,这非常有用。
|
||||
|
||||
## 在指定项目目录中运行任务
|
||||
|
||||
Cron 任务默认与任何代码仓库脱离运行——不加载 `AGENTS.md`、`CLAUDE.md` 或 `.cursorrules`,终端/文件/代码执行工具从 gateway 启动时的工作目录运行。传入 `--workdir`(CLI)或 `workdir=`(工具调用)可更改此行为:
|
||||
|
||||
```bash
|
||||
# 独立 CLI(schedule 和 prompt 为位置参数)
|
||||
hermes cron create "every 1d at 09:00" \
|
||||
"Audit open PRs, summarize CI health, and post to #eng" \
|
||||
--workdir /home/me/projects/acme
|
||||
```
|
||||
|
||||
```python
|
||||
# 在聊天中,通过 cronjob 工具
|
||||
cronjob(
|
||||
action="create",
|
||||
schedule="every 1d at 09:00",
|
||||
workdir="/home/me/projects/acme",
|
||||
prompt="Audit open PRs, summarize CI health, and post to #eng",
|
||||
)
|
||||
```
|
||||
|
||||
设置 `workdir` 后:
|
||||
|
||||
- 该目录中的 `AGENTS.md`、`CLAUDE.md` 和 `.cursorrules` 会被注入系统 prompt(发现顺序与交互式 CLI 相同)
|
||||
- `terminal`、`read_file`、`write_file`、`patch`、`search_files` 和 `execute_code` 均以该目录为工作目录(通过 `TERMINAL_CWD`)
|
||||
- 路径必须是已存在的绝对目录——相对路径和不存在的目录在创建/更新时会被拒绝
|
||||
- 编辑时传入 `--workdir ""`(或工具中的 `workdir=""`)可清除该设置并恢复原有行为
|
||||
|
||||
:::note 串行化
|
||||
设置了 `workdir` 的任务在调度器 tick 时串行运行,而非在并行池中运行。这是有意为之——`TERMINAL_CWD` 是进程全局变量,两个 workdir 任务同时运行会互相破坏各自的 cwd。无 workdir 的任务仍像以前一样并行运行。
|
||||
:::
|
||||
|
||||
## 在指定 profile 中运行 cron 任务
|
||||
|
||||
默认情况下,cron 任务继承创建它的 gateway/CLI 所属的 Hermes profile。传入 `--profile <name>`(CLI)或 `profile=`(cronjob 工具)可将任务重定向到不同的 profile——调度器会解析该 profile 的 `HERMES_HOME`,在运行期间临时切换到该 profile,加载其 `.env` 和 `config.yaml`,并在其中执行任务:
|
||||
|
||||
```bash
|
||||
# 将任务固定到 `night-ops` profile,无论在哪里调度
|
||||
hermes cron create "every 1d at 03:00" \
|
||||
"Tail the security log and flag anomalies" \
|
||||
--profile night-ops
|
||||
```
|
||||
|
||||
```python
|
||||
# 在聊天中,通过 cronjob 工具
|
||||
cronjob(
|
||||
action="create",
|
||||
schedule="every 1d at 03:00",
|
||||
prompt="Tail the security log and flag anomalies",
|
||||
profile="night-ops",
|
||||
)
|
||||
```
|
||||
|
||||
使用 `--profile default` 可显式固定到根 Hermes profile。指定的 profile 必须已存在;调度器不会动态创建 profile。在 `cron edit` 时清除 profile 固定,传入空字符串(`--profile ""` 或 `profile=""`)——任务将恢复在调度器当前所在的 profile 中运行。
|
||||
|
||||
如果固定的 profile 后来被删除,调度器会记录警告并回退到在当前 profile 中运行该任务,而不是崩溃——因此过期的 `profile` 引用不会卡住任务。
|
||||
|
||||
:::note 串行化
|
||||
设置了 `profile` 的任务也串行运行,原因与 `workdir` 固定任务相同:切换 `HERMES_HOME` 是进程全局变更,两个 profile 固定任务并行运行会产生竞争。未固定的任务仍在正常并行池中运行。
|
||||
:::
|
||||
|
||||
## 编辑任务
|
||||
|
||||
无需删除并重建任务来修改它们。
|
||||
|
||||
:::tip 任务引用
|
||||
下方(以及[生命周期操作](#lifecycle-actions)中)的 `<job_id>` 占位符也接受任务名称(不区分大小写)——当你记得 `morning-digest` 但不记得十六进制 ID 时很方便。精确的任务 ID 优先于名称匹配;如果引用不是 ID 且名称匹配到多个任务,命令会拒绝执行并打印候选 ID 供你消歧义。
|
||||
:::
|
||||
|
||||
### 聊天
|
||||
|
||||
```bash
|
||||
/cron edit <job_id> --schedule "every 4h"
|
||||
/cron edit <job_id> --prompt "Use the revised task"
|
||||
/cron edit <job_id> --skill blogwatcher --skill maps
|
||||
/cron edit <job_id> --remove-skill blogwatcher
|
||||
/cron edit <job_id> --clear-skills
|
||||
```
|
||||
|
||||
### 独立 CLI
|
||||
|
||||
```bash
|
||||
hermes cron edit <job_id> --schedule "every 4h"
|
||||
hermes cron edit <job_id> --prompt "Use the revised task"
|
||||
hermes cron edit <job_id> --skill blogwatcher --skill maps
|
||||
hermes cron edit <job_id> --add-skill maps
|
||||
hermes cron edit <job_id> --remove-skill blogwatcher
|
||||
hermes cron edit <job_id> --clear-skills
|
||||
```
|
||||
|
||||
注意:
|
||||
|
||||
- 重复使用 `--skill` 会替换任务已附加的 skill 列表
|
||||
- `--add-skill` 追加到现有列表,不替换
|
||||
- `--remove-skill` 删除指定的已附加 skill
|
||||
- `--clear-skills` 删除所有已附加的 skill
|
||||
|
||||
## 生命周期操作
|
||||
|
||||
Cron 任务现在拥有比创建/删除更完整的生命周期。
|
||||
|
||||
### 聊天
|
||||
|
||||
```bash
|
||||
/cron list
|
||||
/cron pause <job_id>
|
||||
/cron resume <job_id>
|
||||
/cron run <job_id>
|
||||
/cron remove <job_id>
|
||||
```
|
||||
|
||||
### 独立 CLI
|
||||
|
||||
```bash
|
||||
hermes cron list
|
||||
hermes cron pause <job_id>
|
||||
hermes cron resume <job_id>
|
||||
hermes cron run <job_id>
|
||||
hermes cron remove <job_id>
|
||||
hermes cron status
|
||||
hermes cron tick
|
||||
```
|
||||
|
||||
各操作说明:
|
||||
|
||||
- `pause` — 保留任务但停止调度
|
||||
- `resume` — 重新启用任务并计算下次运行时间
|
||||
- `run` — 在下次调度器 tick 时触发任务
|
||||
- `remove` — 彻底删除任务
|
||||
|
||||
## 工作原理
|
||||
|
||||
**Cron 执行由 gateway 守护进程处理。** Gateway 每 60 秒 tick 一次调度器,在隔离的 agent 会话中运行到期的任务。
|
||||
|
||||
```bash
|
||||
hermes gateway install # 安装为用户服务
|
||||
sudo hermes gateway install --system # Linux:服务器开机启动的系统服务
|
||||
hermes gateway # 或在前台运行
|
||||
|
||||
hermes cron list
|
||||
hermes cron status
|
||||
```
|
||||
|
||||
### Gateway 调度器行为
|
||||
|
||||
每次 tick 时,Hermes:
|
||||
|
||||
1. 从 `~/.hermes/cron/jobs.json` 加载任务
|
||||
2. 对照当前时间检查 `next_run_at`
|
||||
3. 为每个到期任务启动全新的 `AIAgent` 会话
|
||||
4. 可选地将一个或多个已附加的 skill 注入该新会话
|
||||
5. 将 prompt 运行至完成
|
||||
6. 投递最终响应
|
||||
7. 更新运行元数据和下次调度时间
|
||||
|
||||
`~/.hermes/cron/.tick.lock` 处的文件锁防止重叠的调度器 tick 重复运行同一批任务。
|
||||
|
||||
## 投递选项
|
||||
|
||||
调度任务时,你可以指定输出的去向:
|
||||
|
||||
| 选项 | 说明 | 示例 |
|
||||
|--------|-------------|---------|
|
||||
| `"origin"` | 回传到任务创建的来源 | 消息平台上的默认值 |
|
||||
| `"local"` | 仅保存到本地文件(`~/.hermes/cron/output/`) | CLI 上的默认值 |
|
||||
| `"telegram"` | Telegram 主频道 | 使用 `TELEGRAM_HOME_CHANNEL` |
|
||||
| `"telegram:123456"` | 按 ID 指定的 Telegram 会话 | 直接投递 |
|
||||
| `"telegram:-100123:17585"` | 指定 Telegram 话题 | `chat_id:thread_id` 格式 |
|
||||
| `"discord"` | Discord 主频道 | 使用 `DISCORD_HOME_CHANNEL` |
|
||||
| `"discord:#engineering"` | 按频道名指定的 Discord 频道 | 按频道名 |
|
||||
| `"slack"` | Slack 主频道 | |
|
||||
| `"whatsapp"` | WhatsApp 主账号 | |
|
||||
| `"signal"` | Signal | |
|
||||
| `"matrix"` | Matrix 主房间 | |
|
||||
| `"mattermost"` | Mattermost 主频道 | |
|
||||
| `"email"` | 邮件 | |
|
||||
| `"sms"` | 通过 Twilio 发送 SMS | |
|
||||
| `"homeassistant"` | Home Assistant | |
|
||||
| `"dingtalk"` | 钉钉 | |
|
||||
| `"feishu"` | 飞书/Lark | |
|
||||
| `"wecom"` | 企业微信 | |
|
||||
| `"weixin"` | 微信(WeChat) | |
|
||||
| `"bluebubbles"` | BlueBubbles(iMessage) | |
|
||||
| `"qqbot"` | QQ Bot(腾讯 QQ) | |
|
||||
| `"all"` | 扇出到所有已连接的主频道 | 触发时解析 |
|
||||
| `"telegram,discord"` | 扇出到指定的一组频道 | 逗号分隔列表 |
|
||||
| `"origin,all"` | 投递到来源**加上**所有其他已连接频道 | 可组合任意 token |
|
||||
|
||||
Agent 的最终响应会自动投递,无需在 cron prompt 中调用 `send_message`。
|
||||
|
||||
### 路由意图(`all`)
|
||||
|
||||
`all` 让你将一个 cron 任务发送到所有已配置的消息频道,无需逐一列举名称。它在**触发时解析**,因此在你配置 `TELEGRAM_HOME_CHANNEL` 之前创建的任务,会在下次 tick 时自动纳入 Telegram。
|
||||
|
||||
语义:`all` 展开为所有已配置主频道的平台。零个也没问题;任务只是没有投递目标,并在上游记录为投递失败。
|
||||
|
||||
`all` 可与显式目标组合。`origin,all` 投递到来源会话**加上**所有其他已连接的主频道,按 `(platform, chat_id, thread_id)` 去重。
|
||||
|
||||
### Telegram cron 话题(`TELEGRAM_CRON_THREAD_ID`)
|
||||
|
||||
启用 Telegram 话题模式后,根 DM 被保留为系统大厅——发送到那里的回复会被拒绝并附带大厅提示,`reply_to_message_id` 会被丢弃,因此你无法回复落在主聊天中的 cron 消息。
|
||||
|
||||
将 cron 指向专用的论坛话题:
|
||||
|
||||
1. 在 Telegram 中打开机器人 DM,创建一个名为 `Cron` 的话题。长按话题标题 → **复制链接**;末尾的整数即为该话题的 `message_thread_id`。
|
||||
2. 在 `.env` 中设置 `TELEGRAM_CRON_THREAD_ID=<该 id>`。
|
||||
|
||||
这仅适用于 cron 投递。`TELEGRAM_HOME_CHANNEL_THREAD_ID`(用于其他地方,如重启通知)不受影响。显式的 `deliver="telegram:chat_id:thread_id"` 目标仍优先于环境变量。对 cron 消息的回复现在会进入已有的话题会话,你可以直接在其中操作。
|
||||
|
||||
### 响应包装
|
||||
|
||||
默认情况下,投递的 cron 输出会带有页眉和页脚,以便接收方知道这来自定时任务:
|
||||
|
||||
```
|
||||
Cronjob Response: Morning feeds
|
||||
-------------
|
||||
|
||||
<agent output here>
|
||||
|
||||
Note: The agent cannot see this message, and therefore cannot respond to it.
|
||||
```
|
||||
|
||||
若要投递不带包装的原始 agent 输出,将 `cron.wrap_response` 设为 `false`:
|
||||
|
||||
```yaml
|
||||
# ~/.hermes/config.yaml
|
||||
cron:
|
||||
wrap_response: false
|
||||
```
|
||||
|
||||
### 静默抑制
|
||||
|
||||
如果 agent 的最终响应以 `[SILENT]` 开头,投递将被完全抑制。输出仍会保存到本地以供审计(位于 `~/.hermes/cron/output/`),但不会向投递目标发送任何消息。
|
||||
|
||||
这对于只在出现问题时才需要上报的监控任务很有用:
|
||||
|
||||
```text
|
||||
Check if nginx is running. If everything is healthy, respond with only [SILENT].
|
||||
Otherwise, report the issue.
|
||||
```
|
||||
|
||||
失败的任务无论 `[SILENT]` 标记如何都会投递——只有成功的运行才能被静默。
|
||||
|
||||
## 脚本超时
|
||||
|
||||
预运行脚本(通过 `script` 参数附加)的默认超时为 120 秒。如果你的脚本需要更长时间——例如,包含随机延迟以避免类机器人的时序模式——可以增加此值:
|
||||
|
||||
```yaml
|
||||
# ~/.hermes/config.yaml
|
||||
cron:
|
||||
script_timeout_seconds: 300 # 5 分钟
|
||||
```
|
||||
|
||||
或设置 `HERMES_CRON_SCRIPT_TIMEOUT` 环境变量。解析顺序为:环境变量 → config.yaml → 默认 120 秒。
|
||||
|
||||
## 无 agent 模式(纯脚本任务)
|
||||
|
||||
对于不需要 LLM 推理的周期性任务——经典的看门狗、磁盘/内存告警、心跳、CI ping——在创建时传入 `no_agent=True`。调度器按计划运行你的脚本,并直接投递其 stdout,完全跳过 agent:
|
||||
|
||||
```bash
|
||||
hermes cron create "every 5m" \
|
||||
--no-agent \
|
||||
--script memory-watchdog.sh \
|
||||
--deliver telegram \
|
||||
--name "memory-watchdog"
|
||||
```
|
||||
|
||||
语义:
|
||||
|
||||
- 脚本 stdout(去除首尾空白)→ 原样作为消息投递。
|
||||
- **stdout 为空 → 静默 tick**,不投递。这是看门狗模式:"只在出现问题时才说话"。
|
||||
- 非零退出或超时 → 投递错误告警,确保损坏的看门狗不会静默失败。
|
||||
- 最后一行输出 `{"wakeAgent": false}` → 静默 tick(与 LLM 任务使用相同的门控)。
|
||||
- 无 token、无模型、无 provider 回退——任务永远不会触及推理层。
|
||||
|
||||
`.sh`/`.bash` 文件在 `/bin/bash` 下运行;其他文件在当前 Python 解释器(`sys.executable`)下运行。脚本必须位于 `~/.hermes/scripts/`(与预运行脚本门控相同的沙箱规则)。
|
||||
|
||||
### Agent 为你设置这些
|
||||
|
||||
`cronjob` 工具的 schema 直接向 Hermes 暴露了 `no_agent`,因此你可以在聊天中描述一个看门狗,让 agent 来配置它:
|
||||
|
||||
```text
|
||||
Ping me on Telegram if RAM is over 85%, every 5 minutes.
|
||||
```
|
||||
|
||||
Hermes 会通过 `write_file` 将检查脚本写入 `~/.hermes/scripts/`,然后调用:
|
||||
|
||||
```python
|
||||
cronjob(action="create", schedule="every 5m",
|
||||
script="memory-watchdog.sh", no_agent=True,
|
||||
deliver="telegram", name="memory-watchdog")
|
||||
```
|
||||
|
||||
当消息内容完全由脚本决定时(看门狗、阈值告警、心跳),它会自动选择 `no_agent=True`。同一工具也让 agent 可以暂停、恢复、编辑和删除任务——整个生命周期都通过聊天驱动,无需任何人接触 CLI。
|
||||
|
||||
参见[纯脚本 Cron 任务指南](/guides/cron-script-only)获取实际示例。
|
||||
|
||||
## 通过 `context_from` 串联任务
|
||||
|
||||
Cron 任务在隔离的会话中运行,不保留之前运行的记忆。但有时一个任务的输出恰好是下一个任务所需的输入。`context_from` 参数自动建立这种连接——任务 B 的 prompt 在运行时会将任务 A 的最新输出作为上下文前置。
|
||||
|
||||
```python
|
||||
# 任务 1:收集原始数据
|
||||
cronjob(
|
||||
action="create",
|
||||
prompt="Fetch the top 10 AI/ML stories from Hacker News. Save them to ~/.hermes/data/briefs/raw.md in markdown format with title, URL, and score.",
|
||||
schedule="0 7 * * *",
|
||||
name="AI News Collector",
|
||||
)
|
||||
|
||||
# 任务 2:分类——接收任务 1 的输出作为上下文
|
||||
# 从 cronjob(action="list") 获取任务 1 的 ID
|
||||
cronjob(
|
||||
action="create",
|
||||
prompt="Read ~/.hermes/data/briefs/raw.md. Score each story 1–10 for engagement potential and novelty. Output the top 5 to ~/.hermes/data/briefs/ranked.md.",
|
||||
schedule="30 7 * * *",
|
||||
context_from="<job1_id>",
|
||||
name="AI News Triage",
|
||||
)
|
||||
|
||||
# 任务 3:发布——接收任务 2 的输出作为上下文
|
||||
cronjob(
|
||||
action="create",
|
||||
prompt="Read ~/.hermes/data/briefs/ranked.md. Write 3 tweet drafts (hook + body + hashtags). Deliver to telegram:7976161601.",
|
||||
schedule="0 8 * * *",
|
||||
context_from="<job2_id>",
|
||||
name="AI News Brief",
|
||||
)
|
||||
```
|
||||
|
||||
**工作原理:**
|
||||
|
||||
- 任务 2 触发时,Hermes 从 `~/.hermes/cron/output/{job1_id}/*.md` 读取任务 1 的最新输出
|
||||
- 该输出自动前置到任务 2 的 prompt
|
||||
- 任务 2 无需硬编码"读取此文件"——它以上下文形式接收内容
|
||||
- 链可以是任意长度:任务 1 → 任务 2 → 任务 3 → …
|
||||
|
||||
**`context_from` 接受的格式:**
|
||||
|
||||
| 格式 | 示例 |
|
||||
|--------|---------|
|
||||
| 单个任务 ID(字符串) | `context_from="a1b2c3d4"` |
|
||||
| 多个任务 ID(列表) | `context_from=["job_a", "job_b"]` |
|
||||
|
||||
输出按列表顺序拼接。
|
||||
|
||||
**适用场景:**
|
||||
|
||||
- 多阶段流水线(收集 → 过滤 → 格式化 → 投递)
|
||||
- 步骤 N 依赖步骤 N−1 输出的依赖任务
|
||||
- 一个任务聚合多个其他任务结果的扇入模式
|
||||
|
||||
## Provider 恢复
|
||||
|
||||
Cron 任务继承你配置的回退 provider 和凭证池轮换。如果主 API key 被限速或 provider 返回错误,cron agent 可以:
|
||||
|
||||
- **回退到备用 provider**,前提是你在 `config.yaml` 中配置了 `fallback_providers`(或旧版 `fallback_model`)
|
||||
- **轮换到下一个凭证**,即同一 provider 的[凭证池](/user-guide/configuration#credential-pool-strategies)中的下一个
|
||||
|
||||
这意味着高频运行或在高峰时段运行的 cron 任务更具弹性——单个被限速的 key 不会导致整次运行失败。
|
||||
|
||||
## 调度格式
|
||||
|
||||
Agent 的最终响应会自动投递——你**无需**在 cron prompt 中为同一目标包含 `send_message`。如果 cron 运行调用了 `send_message` 且目标与调度器已投递的目标完全相同,Hermes 会跳过该重复发送,并告知模型将面向用户的内容放在最终响应中。仅对额外或不同的目标使用 `send_message`。
|
||||
|
||||
### 相对延迟(一次性)
|
||||
|
||||
```text
|
||||
30m → 30 分钟后运行一次
|
||||
2h → 2 小时后运行一次
|
||||
1d → 1 天后运行一次
|
||||
```
|
||||
|
||||
### 间隔(周期性)
|
||||
|
||||
```text
|
||||
every 30m → 每 30 分钟
|
||||
every 2h → 每 2 小时
|
||||
every 1d → 每天
|
||||
```
|
||||
|
||||
### Cron 表达式
|
||||
|
||||
```text
|
||||
0 9 * * * → 每天上午 9:00
|
||||
0 9 * * 1-5 → 工作日上午 9:00
|
||||
0 */6 * * * → 每 6 小时
|
||||
30 8 1 * * → 每月 1 日上午 8:30
|
||||
0 0 * * 0 → 每周日午夜
|
||||
```
|
||||
|
||||
### ISO 时间戳
|
||||
|
||||
```text
|
||||
2026-03-15T09:00:00 → 2026 年 3 月 15 日上午 9:00 一次性运行
|
||||
```
|
||||
|
||||
## 重复行为
|
||||
|
||||
| 调度类型 | 默认重复次数 | 行为 |
|
||||
|--------------|----------------|----------|
|
||||
| 一次性(`30m`、时间戳) | 1 | 运行一次 |
|
||||
| 间隔(`every 2h`) | 永久 | 运行直到删除 |
|
||||
| Cron 表达式 | 永久 | 运行直到删除 |
|
||||
|
||||
可以覆盖:
|
||||
|
||||
```python
|
||||
cronjob(
|
||||
action="create",
|
||||
prompt="...",
|
||||
schedule="every 2h",
|
||||
repeat=5,
|
||||
)
|
||||
```
|
||||
|
||||
## 以编程方式管理任务
|
||||
|
||||
面向 agent 的 API 是单一工具:
|
||||
|
||||
```python
|
||||
cronjob(action="create", ...)
|
||||
cronjob(action="list")
|
||||
cronjob(action="update", job_id="...")
|
||||
cronjob(action="pause", job_id="...")
|
||||
cronjob(action="resume", job_id="...")
|
||||
cronjob(action="run", job_id="...")
|
||||
cronjob(action="remove", job_id="...")
|
||||
```
|
||||
|
||||
对于 `update`,传入 `skills=[]` 可删除所有已附加的 skill。
|
||||
|
||||
## Cron 任务可用的工具集
|
||||
|
||||
Cron 在全新的 agent 会话中运行每个任务,不附加任何聊天平台。默认情况下,cron agent 获得**你在 `hermes tools` 中为 `cron` 平台配置的工具集**——不是 CLI 默认值,也不是所有工具。
|
||||
|
||||
```bash
|
||||
hermes tools
|
||||
# → 在 curses UI 中选择 "cron" 平台
|
||||
# → 像 Telegram/Discord 等平台一样切换工具集开关
|
||||
```
|
||||
|
||||
通过 `cronjob.create`(或通过 `cronjob.update` 对现有任务)上的 `enabled_toolsets` 字段可进行更精细的单任务控制:
|
||||
|
||||
```text
|
||||
cronjob(action="create", name="weekly-news-summary",
|
||||
schedule="every sunday 9am",
|
||||
enabled_toolsets=["web", "file"], # 仅 web + file,无 terminal/browser 等
|
||||
prompt="Summarize this week's AI news: ...")
|
||||
```
|
||||
|
||||
当任务上设置了 `enabled_toolsets` 时,它优先生效;否则 `hermes tools` 的 cron 平台配置生效;否则 Hermes 回退到内置默认值。这对成本控制很重要:在每个小型"获取新闻"任务中携带 `moa`、`browser`、`delegation` 会在每次 LLM 调用时膨胀工具 schema prompt。
|
||||
|
||||
### 完全跳过 agent:`wakeAgent`
|
||||
|
||||
如果你的 cron 任务附加了预检脚本(通过 `script=`),脚本可以在运行时决定 Hermes 是否应该调用 agent。在 stdout 最后一行输出如下格式:
|
||||
|
||||
```text
|
||||
{"wakeAgent": false}
|
||||
```
|
||||
|
||||
……cron 将完全跳过本次 tick 的 agent 运行。适用于高频轮询(每 1–5 分钟),只在状态实际发生变化时才需要唤醒 LLM——否则你会为一遍遍的零内容 agent 轮次付费。
|
||||
|
||||
```python
|
||||
# 预检脚本
|
||||
import json, sys
|
||||
latest = fetch_latest_issue_count()
|
||||
prev = read_state("issue_count")
|
||||
if latest == prev:
|
||||
print(json.dumps({"wakeAgent": False})) # 跳过本次 tick
|
||||
sys.exit(0)
|
||||
write_state("issue_count", latest)
|
||||
print(json.dumps({"wakeAgent": True, "context": {"new_issues": latest - prev}}))
|
||||
```
|
||||
|
||||
省略 `wakeAgent` 时,默认为 `true`(照常唤醒 agent)。
|
||||
|
||||
#### 实用方案:低成本预运行门控
|
||||
|
||||
`wakeAgent` 门控提供了一种零成本的方式,用于决定定时任务是否应该消耗任何 LLM token。三种模式覆盖了大多数使用场景。
|
||||
|
||||
**文件变更门控**——仅在被监视文件自上次成功 tick 以来有新内容时运行。调度器记录每个任务的 `last_run_at`;将其与文件的 mtime 比较。
|
||||
|
||||
```bash
|
||||
#!/bin/bash
|
||||
# ~/.hermes/scripts/feed-changed.sh
|
||||
FEED="$HOME/data/feed.json"
|
||||
STATE="$HOME/.hermes/scripts/.feed-changed.last"
|
||||
test -f "$FEED" || { echo '{"wakeAgent": false}'; exit 0; }
|
||||
mtime=$(stat -c %Y "$FEED")
|
||||
last=$(cat "$STATE" 2>/dev/null || echo 0)
|
||||
if [ "$mtime" -le "$last" ]; then
|
||||
echo '{"wakeAgent": false}'
|
||||
else
|
||||
echo "$mtime" > "$STATE"
|
||||
echo '{"wakeAgent": true}'
|
||||
fi
|
||||
```
|
||||
|
||||
```text
|
||||
cronjob(action="create", name="process-feed",
|
||||
schedule="every 30m",
|
||||
script="feed-changed.sh",
|
||||
prompt="A new ~/data/feed.json has landed. Summarize what changed.")
|
||||
```
|
||||
|
||||
**外部标志门控**——仅在其他进程发出就绪信号时运行(例如,部署 hook 落下一个文件,CI 任务在状态存储中设置一个值)。
|
||||
|
||||
```bash
|
||||
#!/bin/bash
|
||||
# ~/.hermes/scripts/flag-ready.sh
|
||||
if test -f /tmp/new-data-ready; then
|
||||
rm -f /tmp/new-data-ready
|
||||
echo '{"wakeAgent": true}'
|
||||
else
|
||||
echo '{"wakeAgent": false}'
|
||||
fi
|
||||
```
|
||||
|
||||
```text
|
||||
cronjob(action="create", name="nightly-analysis",
|
||||
schedule="0 9 * * *",
|
||||
script="flag-ready.sh",
|
||||
prompt="Run the nightly analysis over today's batch.")
|
||||
```
|
||||
|
||||
**SQL 计数门控**——仅在你自己的数据库中有新行需要处理时运行。脚本还可以通过 `context` 将计数传递给 agent,让 agent 无需重新查询就知道数据量。
|
||||
|
||||
```python
|
||||
#!/usr/bin/env python
|
||||
# ~/.hermes/scripts/new-rows.py
|
||||
import json, sqlite3
|
||||
conn = sqlite3.connect("/home/me/data/app.db")
|
||||
n = conn.execute(
|
||||
"SELECT COUNT(*) FROM messages WHERE ts > strftime('%s','now','-2 hours')"
|
||||
).fetchone()[0]
|
||||
if n < 1:
|
||||
print(json.dumps({"wakeAgent": False}))
|
||||
else:
|
||||
print(json.dumps({"wakeAgent": True, "context": {"new_rows": n}}))
|
||||
```
|
||||
|
||||
```text
|
||||
cronjob(action="create", name="summarize-new-msgs",
|
||||
schedule="every 2h",
|
||||
script="new-rows.py",
|
||||
prompt="Summarize the new messages from the last 2 hours.")
|
||||
```
|
||||
|
||||
同样的模式适用于任何可以从脚本查询的数据源——Postgres、HTTP API、你自己的状态存储——无需将 SQL 求值器内置到 cron 子系统中。
|
||||
|
||||
:::tip
|
||||
Hermes 自身的 `~/.hermes/state.db` 是内部 schema,会在版本间变更。不要从预运行门控中查询它——指向你自己的数据库或 feed。
|
||||
:::
|
||||
|
||||
致谢:此方案集由 @iankar8 在 [#2654](https://github.com/NousResearch/hermes-agent/pull/2654) 中的探索所启发,该 PR 提议将 sql/file/command 触发器作为并行机制添加。`script` + `wakeAgent` 门控已以零成本覆盖了所有三种情况,因此该工作以文档形式落地。
|
||||
|
||||
### 串联任务:`context_from`
|
||||
|
||||
Cron 任务可以通过在 `context_from` 中列出其他任务的名称(或 ID)来消费这些任务最近一次成功运行的输出:
|
||||
|
||||
```text
|
||||
cronjob(action="create", name="daily-digest",
|
||||
schedule="every day 7am",
|
||||
context_from=["ai-news-fetch", "github-prs-fetch"],
|
||||
prompt="Write the daily digest using the outputs above.")
|
||||
```
|
||||
|
||||
被引用任务最近一次完成的输出会作为上下文注入到本次运行的 prompt 之上。每个上游条目必须是有效的任务 ID 或名称(参见 `cronjob action="list"`)。注意:串联读取的是*最近一次完成*的输出——它不会等待同一 tick 中正在运行的上游任务。
|
||||
|
||||
## 任务存储
|
||||
|
||||
任务存储在 `~/.hermes/cron/jobs.json`。任务运行的输出保存到 `~/.hermes/cron/output/{job_id}/{timestamp}.md`。
|
||||
|
||||
任务可能将 `model` 和 `provider` 存储为 `null`。省略这些字段时,Hermes 在执行时从全局配置中解析它们。只有设置了单任务覆盖时,这些字段才会出现在任务记录中。
|
||||
|
||||
存储使用原子文件写入,因此中断的写入不会留下部分写入的任务文件。
|
||||
|
||||
## 自包含的 prompt 仍然重要
|
||||
|
||||
:::warning 重要
|
||||
Cron 任务在完全全新的 agent 会话中运行。Prompt 必须包含 agent 所需的一切,除非已由附加的 skill 提供。
|
||||
:::
|
||||
|
||||
**错误:** `"Check on that server issue"`
|
||||
|
||||
**正确:** `"SSH into server 192.168.1.100 as user 'deploy', check if nginx is running with 'systemctl status nginx', and verify https://example.com returns HTTP 200."`
|
||||
|
||||
## 安全性
|
||||
|
||||
定时任务的 prompt 在创建和更新时会扫描 prompt 注入和凭证外泄模式。包含不可见 Unicode 技巧、SSH 后门尝试或明显的密钥外泄载荷的 prompt 会被拦截。
|
||||
@ -0,0 +1,248 @@
|
||||
---
|
||||
sidebar_position: 3
|
||||
title: "Curator"
|
||||
description: "Agent 创建的技能的后台维护——使用跟踪、过期检测、归档及 LLM 驱动的审查"
|
||||
---
|
||||
|
||||
# Curator
|
||||
|
||||
Curator 是针对 **agent 创建的技能**的后台维护流程。它跟踪每个技能被查看、使用和修补的频率,将长期未使用的技能经历 `active → stale → archived` 状态流转,并定期启动一个短暂的辅助模型审查,提出合并或修补漂移的建议。
|
||||
|
||||
它的存在是为了防止通过[自我改进循环](/user-guide/features/skills#agent-managed-skills-skill_manage-tool)创建的技能无限堆积。每次 agent 解决新问题并保存技能时,该技能都会落入 `~/.hermes/skills/`。若没有维护,最终会出现数十个范围狭窄的近似重复项,污染技能目录并浪费 token(令牌)。
|
||||
|
||||
Curator **绝不触碰**随仓库附带的捆绑技能,也不触碰通过 [agentskills.io](https://agentskills.io) 安装的 hub 技能。它只审查 agent 自身创作的技能。它也**绝不自动删除**——最坏的结果是归档到 `~/.hermes/skills/.archive/`,这是可恢复的。
|
||||
|
||||
跟踪 [issue #7816](https://github.com/NousResearch/hermes-agent/issues/7816)。
|
||||
|
||||
## 运行方式
|
||||
|
||||
Curator 由空闲检查触发,而非 cron 守护进程。在 CLI 会话启动时,以及 gateway 的 cron-ticker 线程内的周期性 tick 中,Hermes 会检查以下条件是否同时满足:
|
||||
|
||||
1. 距上次 curator 运行已过去足够长的时间(`interval_hours`,默认 **7 天**),以及
|
||||
2. agent 已空闲足够长的时间(`min_idle_hours`,默认 **2 小时**)。
|
||||
|
||||
若两个条件均满足,则会派生一个 `AIAgent` 的后台 fork——与内存/技能自我改进 nudge 使用的模式相同。该 fork 在自己的 prompt(提示词)缓存中运行,绝不触碰当前活跃的对话。
|
||||
|
||||
:::info 首次运行行为
|
||||
在全新安装时(或 pre-curator 版本在 `hermes update` 后首次 tick 时),curator **不会立即运行**。首次观测会将 `last_run_at` 设为"当前时间",并将第一次真正的运行推迟整整一个 `interval_hours`。这给了你一个完整的间隔时间来审查技能库、固定重要内容,或在 curator 真正触碰它之前完全退出。
|
||||
|
||||
如果你想在 curator 真正运行之前查看它*会*做什么,请运行 `hermes curator run --dry-run`——它会生成相同的审查报告,但不会修改技能库。
|
||||
:::
|
||||
|
||||
一次运行分为两个阶段:
|
||||
|
||||
1. **自动状态转换**(确定性,无 LLM)。未使用时间超过 `stale_after_days`(30 天)的技能变为 `stale`;未使用时间超过 `archive_after_days`(90 天)的技能被移至 `~/.hermes/skills/.archive/`。
|
||||
2. **LLM 审查**(单次辅助模型 pass,`max_iterations=8`)。派生的 agent 审查 agent 创建的技能,可通过 `skill_view` 读取任意技能,并逐技能决定是保留、修补(通过 `skill_manage`)、合并重叠项,还是通过终端工具归档。
|
||||
|
||||
已固定(pinned)的技能对 curator 的自动状态转换和 agent 自身的 `skill_manage` 工具均不可操作。详见下方[固定技能](#pinning-a-skill)。
|
||||
|
||||
## 配置
|
||||
|
||||
所有设置位于 `config.yaml` 的 `curator:` 下(不在 `.env` 中——这不是密钥)。默认值:
|
||||
|
||||
```yaml
|
||||
curator:
|
||||
enabled: true
|
||||
interval_hours: 168 # 7 days
|
||||
min_idle_hours: 2
|
||||
stale_after_days: 30
|
||||
archive_after_days: 90
|
||||
```
|
||||
|
||||
若要完全禁用,设置 `curator.enabled: false`。
|
||||
|
||||
### 在更便宜的辅助模型上运行审查
|
||||
|
||||
Curator 的 LLM 审查 pass 是一个常规辅助任务槽——`auxiliary.curator`——与 Vision、Compression、Session Search 等并列。"Auto" 表示"使用我的主聊天模型";可覆盖该槽以为审查 pass 指定特定的 provider + model。
|
||||
|
||||
**最简单——`hermes model`:**
|
||||
|
||||
```bash
|
||||
hermes model # → "Auxiliary models — side-task routing"
|
||||
# → pick "Curator" → pick provider → pick model
|
||||
```
|
||||
|
||||
同样的选择器也可在 Web 控制台的 **Models** 标签页中使用。
|
||||
|
||||
**直接编辑 config.yaml(等效):**
|
||||
|
||||
```yaml
|
||||
auxiliary:
|
||||
curator:
|
||||
provider: openrouter
|
||||
model: google/gemini-3-flash-preview
|
||||
timeout: 600 # generous — reviews can take several minutes
|
||||
```
|
||||
|
||||
保持 `provider: auto`(默认值)会将审查 pass 路由到主聊天模型,与所有其他辅助任务的行为一致。
|
||||
|
||||
:::note 旧版配置
|
||||
早期版本使用独立的 `curator.auxiliary.{provider,model}` 块。该路径仍然有效,但会输出一条弃用日志——请迁移到上方的 `auxiliary.curator`,使 curator 与其他所有辅助任务共享相同的管道(`hermes model`、控制台 Models 标签页、`base_url`、`api_key`、`timeout`、`extra_body`)。
|
||||
:::
|
||||
|
||||
## CLI
|
||||
|
||||
```bash
|
||||
hermes curator status # last run, counts, pinned list, LRU top 5
|
||||
hermes curator run # trigger a review now (blocks until the LLM pass finishes)
|
||||
hermes curator run --background # fire-and-forget: start the LLM pass in a background thread
|
||||
hermes curator run --dry-run # preview only — report without any mutations
|
||||
hermes curator backup # take a manual snapshot of ~/.hermes/skills/
|
||||
hermes curator rollback # restore from the newest snapshot
|
||||
hermes curator rollback --list # list available snapshots
|
||||
hermes curator rollback --id <ts> # restore a specific snapshot
|
||||
hermes curator rollback -y # skip the confirmation prompt
|
||||
hermes curator pause # stop runs until resumed
|
||||
hermes curator resume
|
||||
hermes curator pin <skill> # never auto-transition this skill
|
||||
hermes curator unpin <skill>
|
||||
hermes curator restore <skill> # move an archived skill back to active
|
||||
```
|
||||
|
||||
## 备份与回滚
|
||||
|
||||
在每次真正的 curator pass 之前,Hermes 会在 `~/.hermes/skills/.curator_backups/<utc-iso>/skills.tar.gz` 处对 `~/.hermes/skills/` 进行 tar.gz 快照。如果某次 pass 归档或合并了你不希望被触碰的内容,可以用一条命令撤销整次运行:
|
||||
|
||||
```bash
|
||||
hermes curator rollback # restore newest snapshot (with confirmation)
|
||||
hermes curator rollback -y # skip the prompt
|
||||
hermes curator rollback --list # see all snapshots with reason + size
|
||||
```
|
||||
|
||||
回滚本身也是可逆的:在替换技能树之前,Hermes 会再次创建一个标记为 `pre-rollback to <target-id>` 的快照,因此误操作的回滚可以通过 `--id` 滚动到该快照来撤销。
|
||||
|
||||
你也可以随时通过 `hermes curator backup --reason "before-refactor"` 手动创建快照。`--reason` 字符串会写入快照的 `manifest.json`,并在 `--list` 中显示。
|
||||
|
||||
快照会被裁剪至 `curator.backup.keep`(默认 5 个)以控制磁盘占用:
|
||||
|
||||
```yaml
|
||||
curator:
|
||||
backup:
|
||||
enabled: true
|
||||
keep: 5
|
||||
```
|
||||
|
||||
设置 `curator.backup.enabled: false` 可禁用自动快照。手动 `hermes curator backup` 命令仅在 `enabled: true` 时才能工作——该标志对两条路径对称生效,因此不会在变更性运行中意外跳过 pre-run 快照。
|
||||
|
||||
`hermes curator status` 还会列出五个最近最少使用的技能——快速查看哪些技能可能即将变为 stale。
|
||||
|
||||
相同的子命令也可作为 `/curator` 斜杠命令在运行中的会话(CLI 或 gateway 平台)内使用。
|
||||
|
||||
## "agent 创建"的含义
|
||||
|
||||
若技能名称**不在**以下列表中,则视为 agent 创建:
|
||||
|
||||
- `~/.hermes/skills/.bundled_manifest`(安装时从仓库复制的技能),以及
|
||||
- `~/.hermes/skills/.hub/lock.json`(通过 `hermes skills install` 安装的技能)。
|
||||
|
||||
`~/.hermes/skills/` 中的其他所有内容均在 curator 的处理范围内,包括:
|
||||
|
||||
- agent 在对话中通过 `skill_manage(action="create")` 保存的技能。
|
||||
- 你手动编写 `SKILL.md` 创建的技能。
|
||||
- 通过你指向 Hermes 的外部技能目录添加的技能。
|
||||
|
||||
:::warning 你手写的技能与 agent 保存的技能看起来完全相同
|
||||
此处的来源判断是**二元的**(捆绑/hub 与其他所有内容)。Curator 无法区分你依赖于私有工作流的手写技能与自我改进循环在会话中途保存的技能。两者都落入"agent 创建"的桶中。
|
||||
|
||||
在第一次真正运行之前(默认为安装后 7 天),请花时间:
|
||||
|
||||
1. 运行 `hermes curator run --dry-run` 查看 curator 具体会提出什么建议。
|
||||
2. 使用 `hermes curator pin <name>` 保护任何你不希望被触碰的内容。
|
||||
3. 或者在 `config.yaml` 中设置 `curator.enabled: false`,如果你更愿意自己管理技能库。
|
||||
|
||||
归档始终可通过 `hermes curator restore <name>` 恢复,但事先 pin 比事后追查合并结果要容易得多。
|
||||
:::
|
||||
|
||||
如果你想保护某个特定技能不被触碰——例如你依赖的手写技能——请使用 `hermes curator pin <name>`。详见下一节。
|
||||
|
||||
## 固定技能 {#pinning-a-skill}
|
||||
|
||||
固定(pinning)可保护技能不被删除——包括 curator 的自动归档 pass 和 agent 的 `skill_manage(action="delete")` 工具调用。技能一旦被固定:
|
||||
|
||||
- **Curator** 在自动状态转换(`active → stale → archived`)时跳过它,其 LLM 审查 pass 也被指示不予处理。
|
||||
- **Agent 的 `skill_manage` 工具**拒绝对其执行 `delete`,并提示用户使用 `hermes curator unpin <name>`。修补和编辑仍然可以进行,因此 agent 可以在遇到问题时改进已固定技能的内容,无需反复 pin/unpin/re-pin。
|
||||
|
||||
使用以下命令固定和取消固定:
|
||||
|
||||
```bash
|
||||
hermes curator pin <skill>
|
||||
hermes curator unpin <skill>
|
||||
```
|
||||
|
||||
该标志以 `"pinned": true` 的形式存储在 `~/.hermes/skills/.usage.json` 中技能对应的条目上,因此跨会话持久有效。
|
||||
|
||||
只有 **agent 创建**的技能才能被固定——捆绑和 hub 安装的技能本就不受 curator 变更,若你尝试固定它们,`hermes curator pin` 会拒绝并给出说明。
|
||||
|
||||
如果你想要比"禁止删除"更强的保证——例如在 agent 仍可读取技能的同时完全冻结其内容——请直接用编辑器编辑 `~/.hermes/skills/<name>/SKILL.md`。pin 保护的是工具驱动的删除,而非你自己的文件系统访问。
|
||||
|
||||
## 使用遥测
|
||||
|
||||
Curator 在 `~/.hermes/skills/.usage.json` 维护一个附属文件,每个技能对应一条记录:
|
||||
|
||||
```json
|
||||
{
|
||||
"my-skill": {
|
||||
"use_count": 12,
|
||||
"view_count": 34,
|
||||
"last_used_at": "2026-04-24T18:12:03Z",
|
||||
"last_viewed_at": "2026-04-23T09:44:17Z",
|
||||
"patch_count": 3,
|
||||
"last_patched_at": "2026-04-20T22:01:55Z",
|
||||
"created_at": "2026-03-01T14:20:00Z",
|
||||
"state": "active",
|
||||
"pinned": false,
|
||||
"archived_at": null
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
计数器在以下情况递增:
|
||||
|
||||
- `view_count`:agent 对该技能调用 `skill_view`。
|
||||
- `use_count`:技能被加载到对话的 prompt 中。
|
||||
- `patch_count`:对该技能执行 `skill_manage patch/edit/write_file/remove_file`。
|
||||
|
||||
捆绑和 hub 安装的技能被明确排除在遥测写入之外。
|
||||
|
||||
## 每次运行的报告
|
||||
|
||||
每次 curator 运行都会在 `~/.hermes/logs/curator/` 下写入一个带时间戳的目录:
|
||||
|
||||
```
|
||||
~/.hermes/logs/curator/
|
||||
└── 20260429-111512/
|
||||
├── run.json # machine-readable: full fidelity, stats, LLM output
|
||||
└── REPORT.md # human-readable summary
|
||||
```
|
||||
|
||||
`REPORT.md` 是快速查看某次运行所做操作的方式——哪些技能发生了状态转换、LLM 审查者说了什么、修补了哪些技能。无需 grep `agent.log` 即可完成审计。
|
||||
|
||||
### 摘要中的重命名映射
|
||||
|
||||
如果某次运行将多个技能合并到一个总括技能下(或合并了近似重复项),运行结束时打印的用户可见摘要会包含一个明确的重命名映射,显示 curator 应用的每个 `旧名称 → 新名称` 对。这是对逐技能状态转换行的补充,因此当一批重命名落地时,你可以一眼发现,无需对比 JSON 报告。该提示也会在 `hermes curator pin` 下显示,以便你在需要时立即固定新标签。
|
||||
|
||||
## 恢复已归档的技能
|
||||
|
||||
如果 curator 归档了你仍需要的技能:
|
||||
|
||||
```bash
|
||||
hermes curator restore <skill-name>
|
||||
```
|
||||
|
||||
这会将技能从 `~/.hermes/skills/.archive/` 移回活跃树,并将其状态重置为 `active`。如果此后有同名的捆绑或 hub 安装技能(会遮蔽上游),则恢复操作会被拒绝。
|
||||
|
||||
## 按环境禁用
|
||||
|
||||
Curator 默认开启。若要关闭:
|
||||
|
||||
- **仅针对某个 profile:** 编辑 `~/.hermes/config.yaml`(或当前活跃 profile 的配置),设置 `curator.enabled: false`。
|
||||
- **仅针对单次运行:** `hermes curator pause`——暂停跨会话持久有效;使用 `resume` 重新启用。
|
||||
|
||||
Curator 在 `min_idle_hours` 未经过时也会拒绝运行,因此在活跃的开发机器上,它自然只会在安静时段运行。
|
||||
|
||||
## 另请参阅
|
||||
|
||||
- [技能系统](/user-guide/features/skills)——技能的总体工作原理及创建技能的自我改进循环
|
||||
- [内存](/user-guide/features/memory)——维护长期记忆的并行后台审查
|
||||
- [捆绑技能目录](/reference/skills-catalog)
|
||||
- [Issue #7816](https://github.com/NousResearch/hermes-agent/issues/7816)——原始提案与设计讨论
|
||||
@ -0,0 +1,285 @@
|
||||
---
|
||||
sidebar_position: 7
|
||||
title: "子智能体委派"
|
||||
description: "使用 delegate_task 为并行工作流生成隔离的子智能体"
|
||||
---
|
||||
|
||||
# 子智能体委派
|
||||
|
||||
`delegate_task` 工具会生成具有隔离上下文、受限工具集和独立终端会话的子 AIAgent 实例。每个子智能体获得全新的对话并独立运行——只有其最终摘要会进入父智能体的上下文。
|
||||
|
||||
## 单任务
|
||||
|
||||
```python
|
||||
delegate_task(
|
||||
goal="Debug why tests fail",
|
||||
context="Error: assertion in test_foo.py line 42",
|
||||
toolsets=["terminal", "file"]
|
||||
)
|
||||
```
|
||||
|
||||
## 并行批处理
|
||||
|
||||
默认最多 3 个并发子智能体(可配置,无硬性上限):
|
||||
|
||||
```python
|
||||
delegate_task(tasks=[
|
||||
{"goal": "Research topic A", "toolsets": ["web"]},
|
||||
{"goal": "Research topic B", "toolsets": ["web"]},
|
||||
{"goal": "Fix the build", "toolsets": ["terminal", "file"]}
|
||||
])
|
||||
```
|
||||
|
||||
## 子智能体上下文的工作方式
|
||||
|
||||
:::warning 关键:子智能体一无所知
|
||||
子智能体以**全新对话**启动。它们对父智能体的对话历史、之前的工具调用或委派前讨论的任何内容一无所知。子智能体的唯一上下文来自父智能体调用 `delegate_task` 时填写的 `goal` 和 `context` 字段。
|
||||
:::
|
||||
|
||||
这意味着父智能体必须在调用中传递子智能体所需的**一切**信息:
|
||||
|
||||
```python
|
||||
# BAD - subagent has no idea what "the error" is
|
||||
delegate_task(goal="Fix the error")
|
||||
|
||||
# GOOD - subagent has all context it needs
|
||||
delegate_task(
|
||||
goal="Fix the TypeError in api/handlers.py",
|
||||
context="""The file api/handlers.py has a TypeError on line 47:
|
||||
'NoneType' object has no attribute 'get'.
|
||||
The function process_request() receives a dict from parse_body(),
|
||||
but parse_body() returns None when Content-Type is missing.
|
||||
The project is at /home/user/myproject and uses Python 3.11."""
|
||||
)
|
||||
```
|
||||
|
||||
子智能体会收到一个基于你的 goal 和 context 构建的专注系统 prompt(提示词),指示其完成任务并提供结构化摘要,包括所做的事情、发现的内容、修改的文件以及遇到的问题。
|
||||
|
||||
## 实际示例
|
||||
|
||||
### 并行研究
|
||||
|
||||
同时研究多个主题并收集摘要:
|
||||
|
||||
```python
|
||||
delegate_task(tasks=[
|
||||
{
|
||||
"goal": "Research the current state of WebAssembly in 2025",
|
||||
"context": "Focus on: browser support, non-browser runtimes, language support",
|
||||
"toolsets": ["web"]
|
||||
},
|
||||
{
|
||||
"goal": "Research the current state of RISC-V adoption in 2025",
|
||||
"context": "Focus on: server chips, embedded systems, software ecosystem",
|
||||
"toolsets": ["web"]
|
||||
},
|
||||
{
|
||||
"goal": "Research quantum computing progress in 2025",
|
||||
"context": "Focus on: error correction breakthroughs, practical applications, key players",
|
||||
"toolsets": ["web"]
|
||||
}
|
||||
])
|
||||
```
|
||||
|
||||
### 代码审查 + 修复
|
||||
|
||||
将审查并修复的工作流委派给全新上下文:
|
||||
|
||||
```python
|
||||
delegate_task(
|
||||
goal="Review the authentication module for security issues and fix any found",
|
||||
context="""Project at /home/user/webapp.
|
||||
Auth module files: src/auth/login.py, src/auth/jwt.py, src/auth/middleware.py.
|
||||
The project uses Flask, PyJWT, and bcrypt.
|
||||
Focus on: SQL injection, JWT validation, password handling, session management.
|
||||
Fix any issues found and run the test suite (pytest tests/auth/).""",
|
||||
toolsets=["terminal", "file"]
|
||||
)
|
||||
```
|
||||
|
||||
### 多文件重构
|
||||
|
||||
将会大量占用父智能体上下文的大型重构任务委派出去:
|
||||
|
||||
```python
|
||||
delegate_task(
|
||||
goal="Refactor all Python files in src/ to replace print() with proper logging",
|
||||
context="""Project at /home/user/myproject.
|
||||
Use the 'logging' module with logger = logging.getLogger(__name__).
|
||||
Replace print() calls with appropriate log levels:
|
||||
- print(f"Error: ...") -> logger.error(...)
|
||||
- print(f"Warning: ...") -> logger.warning(...)
|
||||
- print(f"Debug: ...") -> logger.debug(...)
|
||||
- Other prints -> logger.info(...)
|
||||
Don't change print() in test files or CLI output.
|
||||
Run pytest after to verify nothing broke.""",
|
||||
toolsets=["terminal", "file"]
|
||||
)
|
||||
```
|
||||
|
||||
## 批处理模式详情
|
||||
|
||||
当你提供 `tasks` 数组时,子智能体会使用线程池**并行**运行:
|
||||
|
||||
- **最大并发数:** 默认 3 个任务(可通过 `delegation.max_concurrent_children` 或环境变量 `DELEGATION_MAX_CONCURRENT_CHILDREN` 配置;最低为 1,无硬性上限)。超出限制的批次会返回工具错误,而不是被静默截断。
|
||||
- **线程池:** 使用 `ThreadPoolExecutor`,以配置的并发限制作为最大工作线程数
|
||||
- **进度显示:** 在 CLI 模式下,树形视图会实时显示每个子智能体的工具调用,并附带每个任务的完成行。在 gateway 模式下,进度会被批量汇总并转发给父智能体的进度回调
|
||||
- **结果排序:** 结果按任务索引排序,与输入顺序一致,不受完成顺序影响
|
||||
- **中断传播:** 中断父智能体(例如发送新消息)会中断所有活跃的子智能体
|
||||
|
||||
单任务委派直接运行,无线程池开销。
|
||||
|
||||
## 模型覆盖
|
||||
|
||||
你可以通过 `config.yaml` 为子智能体配置不同的模型——适用于将简单任务委派给更便宜/更快的模型:
|
||||
|
||||
```yaml
|
||||
# In ~/.hermes/config.yaml
|
||||
delegation:
|
||||
model: "google/gemini-flash-2.0" # Cheaper model for subagents
|
||||
provider: "openrouter" # Optional: route subagents to a different provider
|
||||
```
|
||||
|
||||
如果省略,子智能体将使用与父智能体相同的模型。
|
||||
|
||||
## 工具集选择建议
|
||||
|
||||
`toolsets` 参数控制子智能体可以访问的工具。根据任务选择:
|
||||
|
||||
| 工具集模式 | 使用场景 |
|
||||
|----------------|----------|
|
||||
| `["terminal", "file"]` | 代码工作、调试、文件编辑、构建 |
|
||||
| `["web"]` | 研究、事实核查、文档查阅 |
|
||||
| `["terminal", "file", "web"]` | 全栈任务(默认) |
|
||||
| `["file"]` | 只读分析、无需执行的代码审查 |
|
||||
| `["terminal"]` | 系统管理、进程管理 |
|
||||
|
||||
无论你指定什么,某些工具集对子智能体始终被屏蔽:
|
||||
- `delegation` — 对叶子子智能体屏蔽(默认)。`role="orchestrator"` 的子智能体可保留,受 `max_spawn_depth` 约束——参见下方[深度限制与嵌套编排](#depth-limit-and-nested-orchestration)。
|
||||
- `clarify` — 子智能体无法与用户交互
|
||||
- `memory` — 不可写入共享持久内存
|
||||
- `code_execution` — 子智能体应逐步推理
|
||||
- `send_message` — 无跨平台副作用(例如发送 Telegram 消息)
|
||||
|
||||
## 最大迭代次数
|
||||
|
||||
每个子智能体都有迭代次数限制(默认:50),控制其可进行的工具调用轮次:
|
||||
|
||||
```python
|
||||
delegate_task(
|
||||
goal="Quick file check",
|
||||
context="Check if /etc/nginx/nginx.conf exists and print its first 10 lines",
|
||||
max_iterations=10 # Simple task, don't need many turns
|
||||
)
|
||||
```
|
||||
|
||||
## 子智能体超时
|
||||
|
||||
如果子智能体静默超过 `delegation.child_timeout_seconds` 秒(挂钟时间),则会被判定为卡死并终止。默认值为 **600**(10 分钟)——相比早期版本的 300 秒有所提升,因为高推理能力模型在处理非平凡研究任务时会在推理中途被终止。可按安装实例调整:
|
||||
|
||||
```yaml
|
||||
delegation:
|
||||
child_timeout_seconds: 600 # default
|
||||
```
|
||||
|
||||
对于快速本地模型可降低此值;对于处理难题的慢速推理模型可提高此值。计时器在子智能体每次发起 API 调用或工具调用时重置——只有真正空闲的工作线程才会触发终止。
|
||||
|
||||
:::tip 零调用超时时的诊断转储
|
||||
如果子智能体在**零次** API 调用的情况下超时(通常原因:provider 不可达、认证失败或工具 schema 被拒绝),`delegate_task` 会将结构化诊断信息写入 `~/.hermes/logs/subagent-timeout-<session>-<timestamp>.log`,其中包含子智能体的配置快照、凭据解析追踪以及早期错误消息。比之前的静默超时行为更易于定位根因。
|
||||
:::
|
||||
|
||||
## 监控运行中的子智能体(`/agents`)
|
||||
|
||||
TUI 提供 `/agents` 浮层(别名 `/tasks`),将递归 `delegate_task` 扇出转化为一级审计界面:
|
||||
|
||||
- 运行中和最近完成的子智能体的实时树形视图,按父智能体分组
|
||||
- 每个分支的费用、token 和已触及文件的汇总
|
||||
- 终止和暂停控制——可在不中断其兄弟智能体的情况下取消特定子智能体
|
||||
- 事后回顾:即使子智能体已返回父智能体,也可逐轮查看其历史记录
|
||||
|
||||
经典 CLI 仅将 `/agents` 打印为文本摘要;TUI 才是浮层真正发挥作用的地方。参见 [TUI — 斜杠命令](/user-guide/tui#slash-commands)。
|
||||
|
||||
## 深度限制与嵌套编排 {#depth-limit-and-nested-orchestration}
|
||||
|
||||
默认情况下,委派是**扁平的**:父智能体(深度 0)生成子智能体(深度 1),而这些子智能体无法进一步委派。这可防止失控的递归委派。
|
||||
|
||||
对于多阶段工作流(研究 → 综合,或对子问题进行并行编排),父智能体可以生成**编排者**子智能体,这些子智能体*可以*委派自己的工作线程:
|
||||
|
||||
```python
|
||||
delegate_task(
|
||||
goal="Survey three code review approaches and recommend one",
|
||||
role="orchestrator", # Allows this child to spawn its own workers
|
||||
context="...",
|
||||
)
|
||||
```
|
||||
|
||||
- `role="leaf"`(默认):子智能体无法进一步委派——与扁平委派行为相同。
|
||||
- `role="orchestrator"`:子智能体保留 `delegation` 工具集。受 `delegation.max_spawn_depth` 约束(默认 **1** = 扁平,因此在默认设置下 `role="orchestrator"` 无效)。将 `max_spawn_depth` 提高到 2 可允许编排者子智能体生成叶子孙智能体;设为 3 则允许三层(上限)。
|
||||
- `delegation.orchestrator_enabled: false`:全局开关,无论 `role` 参数如何,强制所有子智能体为 `leaf`。
|
||||
|
||||
**费用警告:** 在 `max_spawn_depth: 3` 和 `max_concurrent_children: 3` 的情况下,树可达到 3×3×3 = 27 个并发叶子智能体。每增加一层都会成倍增加开销——请谨慎提高 `max_spawn_depth`。
|
||||
|
||||
## 生命周期与持久性
|
||||
|
||||
:::warning delegate_task 是同步的——不具备持久性
|
||||
`delegate_task` 在**父智能体的当前轮次内**运行。它会阻塞父智能体,直到所有子智能体完成(或被取消)。它**不是**后台任务队列:
|
||||
|
||||
- 如果父智能体被中断(用户发送新消息、`/stop`、`/new`),所有活跃的子智能体都会被取消并返回 `status="interrupted"`。其进行中的工作将被丢弃。
|
||||
- 子智能体在父智能体轮次结束后**不会**继续运行。
|
||||
- 被取消的子智能体会返回结构化结果(`status="interrupted"`,`exit_reason="interrupted"`),但由于父智能体也被中断,该结果通常不会出现在用户可见的回复中。
|
||||
|
||||
对于必须在中断后存活或超出当前轮次的**持久长时间运行工作**,请使用:
|
||||
|
||||
- `cronjob`(action=`create`)——调度独立的智能体运行;不受父智能体轮次中断影响。
|
||||
- `terminal(background=True, notify_on_complete=True)`——长时间运行的 shell 命令,在智能体执行其他操作时持续运行。
|
||||
:::
|
||||
|
||||
## 关键特性
|
||||
|
||||
- 每个子智能体获得其**独立的终端会话**(与父智能体分离)
|
||||
- **嵌套委派为可选项**——只有 `role="orchestrator"` 的子智能体可以进一步委派,且仅在 `max_spawn_depth` 从默认值 1(扁平)提高后才生效。可通过 `orchestrator_enabled: false` 全局禁用。
|
||||
- 叶子子智能体**不能**调用:`delegate_task`、`clarify`、`memory`、`send_message`、`execute_code`。编排者子智能体保留 `delegate_task`,但仍不能使用其他四个。
|
||||
- **中断传播**——中断父智能体会中断所有活跃的子智能体(包括编排者下的孙智能体)
|
||||
- 只有最终摘要进入父智能体的上下文,保持 token 使用高效
|
||||
- 子智能体继承父智能体的 **API 密钥、provider 配置和凭据池**(支持在速率限制时轮换密钥)
|
||||
|
||||
## delegate_task 与 execute_code 对比
|
||||
|
||||
| 因素 | delegate_task | execute_code |
|
||||
|--------|--------------|-------------|
|
||||
| **推理** | 完整 LLM 推理循环 | 仅 Python 代码执行 |
|
||||
| **上下文** | 全新隔离对话 | 无对话,仅脚本 |
|
||||
| **工具访问** | 所有非屏蔽工具,具备推理能力 | 通过 RPC 访问 7 个工具,无推理 |
|
||||
| **并行性** | 默认 3 个并发子智能体(可配置) | 单脚本 |
|
||||
| **最适合** | 需要判断力的复杂任务 | 机械式多步骤流水线 |
|
||||
| **Token 费用** | 较高(完整 LLM 循环) | 较低(仅返回 stdout) |
|
||||
| **用户交互** | 无(子智能体无法澄清) | 无 |
|
||||
|
||||
**经验法则:** 当子任务需要推理、判断或多步骤问题解决时,使用 `delegate_task`。当需要机械式数据处理或脚本化工作流时,使用 `execute_code`。
|
||||
|
||||
## 配置
|
||||
|
||||
```yaml
|
||||
# In ~/.hermes/config.yaml
|
||||
delegation:
|
||||
max_iterations: 50 # Max turns per child (default: 50)
|
||||
# max_concurrent_children: 3 # Parallel children per batch (default: 3)
|
||||
# max_spawn_depth: 1 # Tree depth (1-3, default 1 = flat). Raise to 2 to allow orchestrator children to spawn leaves; 3 for three levels.
|
||||
# orchestrator_enabled: true # Disable to force all children to leaf role.
|
||||
model: "google/gemini-3-flash-preview" # Optional provider/model override
|
||||
provider: "openrouter" # Optional built-in provider
|
||||
api_mode: anthropic_messages # optional; auto-detected from base_url for anthropic_messages endpoints
|
||||
|
||||
# Or use a direct custom endpoint instead of provider:
|
||||
delegation:
|
||||
model: "qwen2.5-coder"
|
||||
base_url: "http://localhost:1234/v1"
|
||||
api_key: "local-key"
|
||||
# api_mode: "anthropic_messages" # Optional. Wire protocol override for base_url ("chat_completions", "codex_responses", or "anthropic_messages"). Empty = auto-detect from URL (e.g. /anthropic suffix). Set explicitly for endpoints the heuristic can't classify (Azure AI Foundry, MiniMax, Zhipu GLM, LiteLLM proxies, …).
|
||||
```
|
||||
|
||||
当 `base_url` 指向 Anthropic 兼容端点时——例如路径以 `/anthropic` 结尾、Azure Foundry Claude 路由或 MiniMax `/anthropic` 代理——`api_mode` 会被自动检测为 `anthropic_messages`,子智能体无需任何配置即可使用正确的传输格式。当自动检测结果有误时(罕见),请显式设置 `api_mode`。
|
||||
|
||||
:::tip
|
||||
智能体会根据任务复杂度自动处理委派。你无需明确要求它进行委派——它会在合适时自行决定。
|
||||
:::
|
||||
@ -0,0 +1,91 @@
|
||||
---
|
||||
title: 可交付成果模式(聊天中的 Artifacts)
|
||||
sidebar_label: 可交付成果模式
|
||||
description: Agent 如何将生成的图表、PDF、电子表格及其他文件作为原生附件发送到消息平台。
|
||||
---
|
||||
|
||||
# 可交付成果模式
|
||||
|
||||
当 Hermes Agent 在消息 gateway(Slack、Discord、Telegram、WhatsApp、Signal 等)中运行时,它可以将生成的文件直接发送到聊天中——不是让用户自行复制路径,而是作为原生附件。
|
||||
|
||||
图表以内联图片形式显示。PDF 报告以文件下载形式显示。电子表格以 `.xlsx` 格式上传。Agent 无需写入 `MEDIA:` 标签或进行任何特殊操作——只需生成文件并在回复中提及其绝对路径。Gateway 会从文本中提取路径,将其从可见消息中移除,并原生上传文件。
|
||||
|
||||
## 工作原理
|
||||
|
||||
三个部分协同配合:
|
||||
|
||||
1. **Agent 拥有可生成文件的工具。** `execute_code` 用于通过 matplotlib 生成图表,`latex-pdf-report` skill 用于生成 PDF,`powerpoint` skill 用于生成演示文稿,`image_generate` 用于生成图片,`text_to_speech` 用于生成音频,等等。
|
||||
|
||||
2. **Gateway 扫描 agent 回复中的文件路径。** 任何以支持扩展名结尾的绝对路径(`/tmp/...`)或相对主目录路径(`~/...`)都会被提取。代码块和内联代码中的路径会被忽略,以避免代码示例被破坏。
|
||||
|
||||
3. **Gateway 按文件类型分发。** 在平台支持的情况下,图片以内联方式嵌入;视频以内联方式嵌入;音频路由至语音/音频附件;其他所有内容作为文件附件上传。
|
||||
|
||||
## 支持的文件扩展名
|
||||
|
||||
| 类别 | 扩展名 | 发送方式 |
|
||||
|---|---|---|
|
||||
| 图片 | `.png .jpg .jpeg .gif .webp .bmp .tiff .svg` | 内联嵌入 |
|
||||
| 视频 | `.mp4 .mov .avi .mkv .webm` | 内联嵌入(平台支持时) |
|
||||
| 音频 | `.mp3 .wav .ogg .m4a .flac` | 语音/音频附件 |
|
||||
| 文档 | `.pdf .docx .doc .odt .rtf .txt .md` | 文件上传 |
|
||||
| 数据 | `.xlsx .xls .csv .tsv .json .xml .yaml .yml` | 文件上传 |
|
||||
| 演示文稿 | `.pptx .ppt .odp` | 文件上传 |
|
||||
| 压缩包 | `.zip .tar .gz .tgz .bz2 .7z` | 文件上传 |
|
||||
| Web | `.html .htm` | 文件上传 |
|
||||
|
||||
`.py`、`.log` 及其他源文件扩展名被有意排除,以防 agent 自动发送任意源文件;如需向用户发送代码,请使用代码块。
|
||||
|
||||
## 引导 Agent 生成 Artifacts
|
||||
|
||||
Agent 默认不会主动生成 artifacts——需要明确告知。有两种方式:
|
||||
|
||||
**单次会话:** 明确提出请求("以图表形式发给我对比结果"、"将数据以 CSV 格式返回"),或编写自定义指令/个性化条目,使其在消息平台上倾向于以 artifact 形式回复。
|
||||
|
||||
**项目级别:** 将偏好设置添加到项目中的 `AGENTS.md` / `CLAUDE.md` / `.cursorrules`(agent 从该项目工作),或添加到 `~/.hermes/config.yaml` 中 `agent.custom_instructions` 下的全局自定义指令。
|
||||
|
||||
Agent 需要使用的机制很简单:将文件渲染到绝对路径(例如 `/tmp/q3-revenue.png`),并在回复中以纯文本形式提及该路径。Gateway 负责其余工作。围栏代码块或反引号中的路径会被忽略,以避免代码示例被破坏。
|
||||
|
||||
## Kanban:Artifacts 随完成通知一并发送
|
||||
|
||||
如果使用 Hermes 的 kanban(看板)多 agent 工作流,worker 可以在调用 `kanban_complete` 时附加可交付文件:
|
||||
|
||||
```python
|
||||
kanban_complete(
|
||||
summary="rendered Q3 revenue chart and report",
|
||||
artifacts=[
|
||||
"/tmp/q3-revenue.png",
|
||||
"/tmp/q3-report.pdf",
|
||||
],
|
||||
)
|
||||
```
|
||||
|
||||
当 gateway 通知器将"任务完成"消息发送给在 Slack/Telegram 等平台订阅该任务的用户时,也会将每个 artifact 作为原生附件上传到对应聊天中。用户在同一位置获得可交付成果和摘要。
|
||||
|
||||
通知器运行时磁盘上不存在的文件会被静默跳过。
|
||||
|
||||
## 通过 MCP 连接更多服务
|
||||
|
||||
除 artifact 发送管道外,agent 还可以通过 MCP(Model Context Protocol,模型上下文协议)接入其他服务。MCP 生态系统为大多数主流工具提供了社区服务器——按需安装:
|
||||
|
||||
| 服务 | 解锁功能 |
|
||||
|---|---|
|
||||
| **Notion** | 读写 Notion 页面、数据库,查询工作区 |
|
||||
| **GitHub** | Issues、PR、评论、超出 gh CLI 范围的仓库搜索 |
|
||||
| **Linear** | 工单、项目、迭代周期 |
|
||||
| **Slack** | 工作区全局搜索、读取其他频道 |
|
||||
| **Gmail** | 收件箱整理、发送邮件、标签管理 |
|
||||
| **Salesforce** | 线索、商机、账户数据 |
|
||||
| **Snowflake / BigQuery** | 对数据仓库执行 SQL |
|
||||
| **Google Drive** | 文件搜索、内容读取、共享管理 |
|
||||
|
||||
通过 `~/.hermes/config.yaml` 中的 `mcp_servers` 部分安装 MCP 服务器。完整配置指南请参阅 [MCP 集成](./mcp.md)。
|
||||
|
||||
## 与 Perplexity Computer in Slack 的对比
|
||||
|
||||
Perplexity Computer 的 Slack 集成基于相同理念:agent 生成可交付成果(图表、PDF、幻灯片),并将其作为原生附件发回线程。Hermes Agent 的可交付成果模式在本地提供相同的用户体验:
|
||||
|
||||
- 生成在用户自己的 venv/沙箱中进行(无远程租户)。
|
||||
- 文件通过相同的 Slack `files.uploadV2` API 发送到聊天。
|
||||
- 连接器广度通过 MCP 实现,而非精心策划的 400 个托管集成目录——按需安装所需的即可。
|
||||
|
||||
OAuth token 保存在用户本机的 `auth.json` / `.env` 中。无托管 token 存储。无多租户 microVM。最终效果相同。
|
||||
@ -0,0 +1,907 @@
|
||||
---
|
||||
sidebar_position: 17
|
||||
title: "扩展 Dashboard"
|
||||
description: "为 Hermes Web Dashboard 构建主题和插件——调色板、字体排版、布局、自定义标签页、shell 插槽、页面级插槽以及后端 API 路由"
|
||||
---
|
||||
|
||||
# 扩展 Dashboard
|
||||
|
||||
Hermes Web Dashboard(`hermes dashboard`)在设计上支持换肤和扩展,无需 fork 代码库。对外暴露三个层次:
|
||||
|
||||
1. **主题(Themes)** — YAML 文件,用于重绘 dashboard 的调色板、字体排版、布局以及各组件的外观。将文件放入 `~/.hermes/dashboard-themes/`,即可在主题切换器中看到它。
|
||||
2. **UI 插件(UI plugins)** — 一个包含 `manifest.json` 和 JavaScript bundle 的目录,可注册标签页、替换内置页面、通过页面级插槽增强内置页面,或向命名 shell 插槽注入组件。
|
||||
3. **后端插件(Backend plugins)** — 插件目录内的 Python 文件,暴露一个 FastAPI `router`;路由挂载在 `/api/plugins/<name>/` 下,由插件的 UI 调用。
|
||||
|
||||
三者均为**运行时即插即用**:无需克隆仓库、无需 `npm run build`、无需修改 dashboard 源码。本页是三者的权威参考文档。
|
||||
|
||||
如果只是想使用 dashboard,请参阅 [Web Dashboard](./web-dashboard)。如果想为终端 CLI(而非 Web Dashboard)换肤,请参阅 [Skins & Themes](./skins) —— CLI 皮肤系统与 dashboard 主题无关。
|
||||
|
||||
:::note 各部分如何组合
|
||||
主题和插件相互独立,但可协同工作。主题可以单独使用(仅一个 YAML 文件)。插件也可以单独使用(仅一个标签页)。两者结合可构建带有自定义 HUD 的完整视觉换肤方案——内置的 `strike-freedom-cockpit` 演示正是如此。参见[主题 + 插件组合演示](#combined-theme--plugin-demo)。
|
||||
:::
|
||||
|
||||
---
|
||||
|
||||
## 目录
|
||||
|
||||
- [主题](#themes)
|
||||
- [快速上手——你的第一个主题](#quick-start--your-first-theme)
|
||||
- [调色板、字体排版、布局](#palette-typography-layout)
|
||||
- [布局变体](#layout-variants)
|
||||
- [主题资源(图片作为 CSS 变量)](#theme-assets-images-as-css-vars)
|
||||
- [组件外观覆盖](#component-chrome-overrides)
|
||||
- [颜色覆盖](#color-overrides)
|
||||
- [原始 `customCSS`](#raw-customcss)
|
||||
- [内置主题](#built-in-themes)
|
||||
- [完整主题 YAML 参考](#full-theme-yaml-reference)
|
||||
- [插件](#plugins)
|
||||
- [快速上手——你的第一个插件](#quick-start--your-first-plugin)
|
||||
- [目录结构](#directory-layout)
|
||||
- [Manifest 参考](#manifest-reference)
|
||||
- [Plugin SDK](#the-plugin-sdk)
|
||||
- [Shell 插槽](#shell-slots)
|
||||
- [替换内置页面(`tab.override`)](#replacing-built-in-pages-taboverride)
|
||||
- [增强内置页面(页面级插槽)](#augmenting-built-in-pages-page-scoped-slots)
|
||||
- [仅插槽插件(`tab.hidden`)](#slot-only-plugins-tabhidden)
|
||||
- [后端 API 路由](#backend-api-routes)
|
||||
- [插件自定义 CSS](#custom-css-per-plugin)
|
||||
- [插件发现与重载](#plugin-discovery--reload)
|
||||
- [主题 + 插件组合演示](#combined-theme--plugin-demo)
|
||||
- [API 参考](#api-reference)
|
||||
- [故障排查](#troubleshooting)
|
||||
|
||||
---
|
||||
|
||||
## 主题
|
||||
|
||||
主题是存储在 `~/.hermes/dashboard-themes/` 中的 YAML 文件。文件名无关紧要(系统使用主题的 `name:` 字段),但惯例是 `<name>.yaml`。所有字段均为可选——缺失的键会回退到内置的 `default` 主题,因此一个主题可以只包含一个颜色。
|
||||
|
||||
### 快速上手——你的第一个主题
|
||||
|
||||
```bash
|
||||
mkdir -p ~/.hermes/dashboard-themes
|
||||
```
|
||||
|
||||
```yaml
|
||||
# ~/.hermes/dashboard-themes/neon.yaml
|
||||
name: neon
|
||||
label: Neon
|
||||
description: Pure magenta on black
|
||||
|
||||
palette:
|
||||
background: "#000000"
|
||||
midground: "#ff00ff"
|
||||
```
|
||||
|
||||
刷新 dashboard。点击顶栏的调色板图标,选择 **Neon**。背景变为黑色,文字和强调色变为洋红色,所有派生颜色(card、border、muted、ring 等)均通过 CSS 的 `color-mix()` 从这两个颜色自动计算得出。
|
||||
|
||||
这就是全部入门流程:一个文件,两个颜色。以下内容均为可选的进阶配置。
|
||||
|
||||
### 调色板、字体排版、布局
|
||||
|
||||
这三个块是主题的核心。每个块相互独立——覆盖其中一个,其余保持不变。
|
||||
|
||||
#### 调色板(3 层)
|
||||
|
||||
调色板由三层颜色加一个暖光晕(warm-glow)颜色和一个噪点颗粒倍增器组成。Dashboard 的设计系统级联通过 CSS `color-mix()` 从这三层颜色派生出所有兼容 shadcn 的 token(card、popover、muted、border、primary、destructive、ring 等)。覆盖三个颜色即可级联影响整个 UI。
|
||||
|
||||
| 键 | 描述 |
|
||||
|-----|-------------|
|
||||
| `palette.background` | 最深的画布颜色——通常接近黑色。驱动页面背景和卡片填充。 |
|
||||
| `palette.midground` | 主要文字和强调色。大多数 UI 外观读取此值(前景文字、按钮轮廓、焦点环)。 |
|
||||
| `palette.foreground` | 顶层高亮色。默认主题将其设为 alpha 为 0 的白色(不可见);需要顶层亮色强调的主题可提高其 alpha 值。 |
|
||||
| `palette.warmGlow` | `rgba(...)` 字符串,用作 `<Backdrop />` 的晕光颜色。 |
|
||||
| `palette.noiseOpacity` | 0–1.2 的颗粒叠加层倍增器。越低越柔和,越高越粗粝。 |
|
||||
|
||||
每层接受 `{hex: "#RRGGBB", alpha: 0.0–1.0}` 或裸十六进制字符串(alpha 默认为 1.0)。
|
||||
|
||||
```yaml
|
||||
palette:
|
||||
background:
|
||||
hex: "#05091a"
|
||||
alpha: 1.0
|
||||
midground: "#d8f0ff" # bare hex, alpha = 1.0
|
||||
foreground:
|
||||
hex: "#ffffff"
|
||||
alpha: 0 # invisible top layer
|
||||
warmGlow: "rgba(255, 199, 55, 0.24)"
|
||||
noiseOpacity: 0.7
|
||||
```
|
||||
|
||||
#### 字体排版
|
||||
|
||||
| 键 | 类型 | 描述 |
|
||||
|-----|------|-------------|
|
||||
| `fontSans` | string | 正文的 CSS font-family 栈(应用于 `html`、`body`)。 |
|
||||
| `fontMono` | string | 代码块、`<code>`、`.font-mono` 工具类的 CSS font-family 栈。 |
|
||||
| `fontDisplay` | string | 可选的标题/展示字体栈。回退到 `fontSans`。 |
|
||||
| `fontUrl` | string | 可选的外部样式表 URL。在主题切换时以 `<link rel="stylesheet">` 注入 `<head>`。相同 URL 不会重复注入。支持 Google Fonts、Bunny Fonts、自托管 `@font-face` 样式表——任何可链接的资源均可。 |
|
||||
| `baseSize` | string | 根字体大小——控制 rem 比例。例如 `"14px"`、`"16px"`。 |
|
||||
| `lineHeight` | string | 默认行高。例如 `"1.5"`、`"1.65"`。 |
|
||||
| `letterSpacing` | string | 默认字间距。例如 `"0"`、`"0.01em"`、`"-0.01em"`。 |
|
||||
|
||||
```yaml
|
||||
typography:
|
||||
fontSans: '"Orbitron", "Eurostile", "Impact", sans-serif'
|
||||
fontMono: '"Share Tech Mono", ui-monospace, monospace'
|
||||
fontDisplay: '"Orbitron", "Eurostile", sans-serif'
|
||||
fontUrl: "https://fonts.googleapis.com/css2?family=Orbitron:wght@400;500;600;700&family=Share+Tech+Mono&display=swap"
|
||||
baseSize: "14px"
|
||||
lineHeight: "1.5"
|
||||
letterSpacing: "0.04em"
|
||||
```
|
||||
|
||||
#### 布局
|
||||
|
||||
| 键 | 值 | 描述 |
|
||||
|-----|--------|-------------|
|
||||
| `radius` | 任意 CSS 长度(`"0"`、`"0.25rem"`、`"0.5rem"`、`"1rem"` 等) | 圆角 token。映射到 `--radius` 并级联到 `--radius-sm/md/lg/xl`——所有圆角元素同步变化。 |
|
||||
| `density` | `compact` \| `comfortable` \| `spacious` | 间距倍增器,以 `--spacing-mul` CSS 变量形式应用。`compact = 0.85×`,`comfortable = 1.0×`(默认),`spacious = 1.2×`。缩放 Tailwind 的基础间距,因此 padding、gap 和 space-between 工具类均按比例调整。 |
|
||||
|
||||
```yaml
|
||||
layout:
|
||||
radius: "0"
|
||||
density: compact
|
||||
```
|
||||
|
||||
### 布局变体
|
||||
|
||||
`layoutVariant` 选择整体 shell 布局。缺省时默认为 `"standard"`。
|
||||
|
||||
| 变体 | 行为 |
|
||||
|---------|-----------|
|
||||
| `standard` | 单列,最大宽度 1600px(默认)。 |
|
||||
| `cockpit` | 左侧边栏轨道(260px)+ 主内容区。由插件通过 `sidebar` 插槽填充——参见 [Shell 插槽](#shell-slots)。没有插件时轨道显示占位符。 |
|
||||
| `tiled` | 取消最大宽度限制,页面可使用完整视口宽度。 |
|
||||
|
||||
```yaml
|
||||
layoutVariant: cockpit
|
||||
```
|
||||
|
||||
当前变体通过 `document.documentElement.dataset.layoutVariant` 暴露,因此 `customCSS` 中的原始 CSS 可通过 `:root[data-layout-variant="cockpit"] ...` 定向匹配。
|
||||
|
||||
### 主题资源(图片作为 CSS 变量)
|
||||
|
||||
随主题附带图片 URL。每个命名插槽会成为一个 CSS 变量(`--theme-asset-<name>`),内置 shell 和任何插件均可读取。`bg` 插槽自动接入 backdrop;其他插槽面向插件开放。
|
||||
|
||||
```yaml
|
||||
assets:
|
||||
bg: "https://example.com/hero-bg.jpg" # auto-wired into <Backdrop />
|
||||
hero: "/my-images/strike-freedom.png" # for plugin sidebars
|
||||
crest: "/my-images/crest.svg" # for header-left plugins
|
||||
logo: "/my-images/logo.png"
|
||||
sidebar: "/my-images/rail.png"
|
||||
header: "/my-images/header-art.png"
|
||||
custom:
|
||||
scanLines: "/my-images/scanlines.png" # → --theme-asset-custom-scanLines
|
||||
```
|
||||
|
||||
值接受:
|
||||
|
||||
- 裸 URL——自动包装为 `url(...)`。
|
||||
- 已包装的 `url(...)`、`linear-gradient(...)`、`radial-gradient(...)` 表达式——直接使用。
|
||||
- `"none"` ——明确禁用。
|
||||
|
||||
每个资源还会以 `--theme-asset-<name>-raw`(未包装的 URL)形式输出,以便插件需要将其传给 `<img src>` 而非 `background-image` 时使用。
|
||||
|
||||
插件通过普通 CSS 或 JS 读取这些变量:
|
||||
|
||||
```javascript
|
||||
// In a plugin slot
|
||||
const hero = getComputedStyle(document.documentElement)
|
||||
.getPropertyValue("--theme-asset-hero").trim();
|
||||
```
|
||||
|
||||
### 组件外观覆盖
|
||||
|
||||
`componentStyles` 可在不编写 CSS 选择器的情况下重新设置各 shell 组件的样式。每个桶(bucket)的条目会成为 CSS 变量(`--component-<bucket>-<kebab-property>`),shell 的共享组件会读取这些变量。因此 `card:` 的覆盖应用于所有 `<Card>`,`header:` 应用于应用栏,以此类推。
|
||||
|
||||
```yaml
|
||||
componentStyles:
|
||||
card:
|
||||
clipPath: "polygon(12px 0, 100% 0, 100% calc(100% - 12px), calc(100% - 12px) 100%, 0 100%, 0 12px)"
|
||||
background: "linear-gradient(180deg, rgba(10, 22, 52, 0.85), rgba(5, 9, 26, 0.92))"
|
||||
boxShadow: "inset 0 0 0 1px rgba(64, 200, 255, 0.28)"
|
||||
header:
|
||||
background: "linear-gradient(180deg, rgba(16, 32, 72, 0.95), rgba(5, 9, 26, 0.9))"
|
||||
tab:
|
||||
clipPath: "polygon(6px 0, 100% 0, calc(100% - 6px) 100%, 0 100%)"
|
||||
sidebar: {}
|
||||
backdrop: {}
|
||||
footer: {}
|
||||
progress: {}
|
||||
badge: {}
|
||||
page: {}
|
||||
```
|
||||
|
||||
支持的桶:`card`、`header`、`footer`、`sidebar`、`tab`、`progress`、`badge`、`backdrop`、`page`。
|
||||
|
||||
属性名使用 camelCase(`clipPath`),输出为 kebab-case(`clip-path`)。值为纯 CSS 字符串——CSS 接受的任何内容均可(`clip-path`、`border-image`、`background`、`box-shadow`、`animation` 等)。
|
||||
|
||||
### 颜色覆盖
|
||||
|
||||
大多数主题不需要此功能——3 层调色板已派生出所有 shadcn token。当你需要派生无法产生的特定强调色时(例如柔和主题的更柔和的破坏性红色,或品牌专属的成功绿色),才使用 `colorOverrides`。
|
||||
|
||||
```yaml
|
||||
colorOverrides:
|
||||
primary: "#ffce3a"
|
||||
primaryForeground: "#05091a"
|
||||
accent: "#3fd3ff"
|
||||
ring: "#3fd3ff"
|
||||
destructive: "#ff3a5e"
|
||||
border: "rgba(64, 200, 255, 0.28)"
|
||||
```
|
||||
|
||||
支持的键:`card`、`cardForeground`、`popover`、`popoverForeground`、`primary`、`primaryForeground`、`secondary`、`secondaryForeground`、`muted`、`mutedForeground`、`accent`、`accentForeground`、`destructive`、`destructiveForeground`、`success`、`warning`、`border`、`input`、`ring`。
|
||||
|
||||
每个键与 `--color-<kebab>` CSS 变量一一对应(例如 `primaryForeground` → `--color-primary-foreground`)。此处设置的任何键仅对当前激活主题生效,切换到其他主题时覆盖会被清除。
|
||||
|
||||
### 原始 `customCSS`
|
||||
|
||||
对于 `componentStyles` 无法表达的选择器级外观——伪元素、动画、媒体查询、主题范围内的覆盖——可将原始 CSS 写入 `customCSS`:
|
||||
|
||||
```yaml
|
||||
customCSS: |
|
||||
/* Scanline overlay — only visible when cockpit variant is active. */
|
||||
:root[data-layout-variant="cockpit"] body::before {
|
||||
content: "";
|
||||
position: fixed;
|
||||
inset: 0;
|
||||
pointer-events: none;
|
||||
z-index: 100;
|
||||
background: repeating-linear-gradient(to bottom,
|
||||
transparent 0px, transparent 2px,
|
||||
rgba(64, 200, 255, 0.035) 3px, rgba(64, 200, 255, 0.035) 4px);
|
||||
mix-blend-mode: screen;
|
||||
}
|
||||
```
|
||||
|
||||
CSS 在主题应用时以单个带作用域的 `<style data-hermes-theme-css>` 标签注入,主题切换时清除。**每个主题上限为 32 KiB。**
|
||||
|
||||
### 内置主题
|
||||
|
||||
每个内置主题都有自己的调色板、字体排版和布局——切换时产生的变化不仅限于颜色。
|
||||
|
||||
| 主题 | 调色板 | 字体排版 | 布局 |
|
||||
|-------|---------|------------|--------|
|
||||
| **Hermes Teal**(`default`) | 深青色 + 奶油色 | 系统字体栈,15px | 0.5rem 圆角,comfortable |
|
||||
| **Hermes Teal (Large)**(`default-large`) | 同 default | 系统字体栈,18px,行高 1.65 | 0.5rem 圆角,spacious |
|
||||
| **Midnight**(`midnight`) | 深蓝紫色 | Inter + JetBrains Mono,14px | 0.75rem 圆角,comfortable |
|
||||
| **Ember**(`ember`) | 暖深红 + 古铜色 | Spectral(衬线)+ IBM Plex Mono,15px | 0.25rem 圆角,comfortable |
|
||||
| **Mono**(`mono`) | 灰度 | IBM Plex Sans + IBM Plex Mono,13px | 0 圆角,compact |
|
||||
| **Cyberpunk**(`cyberpunk`) | 黑底霓虹绿 | Share Tech Mono 全局,14px | 0 圆角,compact |
|
||||
| **Rosé**(`rose`) | 粉色 + 象牙色 | Fraunces(衬线)+ DM Mono,16px | 1rem 圆角,spacious |
|
||||
|
||||
引用 Google Fonts 的主题(除 Hermes Teal 外均如此)会按需加载样式表——首次切换时会向 `<head>` 注入一个 `<link>` 标签。
|
||||
|
||||
### 完整主题 YAML 参考
|
||||
|
||||
所有配置项汇总在一个文件中——复制后删除不需要的部分:
|
||||
|
||||
```yaml
|
||||
# ~/.hermes/dashboard-themes/ocean.yaml
|
||||
name: ocean
|
||||
label: Ocean Deep
|
||||
description: Deep sea blues with coral accents
|
||||
|
||||
# 3-layer palette (accepts {hex, alpha} or bare hex)
|
||||
palette:
|
||||
background:
|
||||
hex: "#0a1628"
|
||||
alpha: 1.0
|
||||
midground:
|
||||
hex: "#a8d0ff"
|
||||
alpha: 1.0
|
||||
foreground:
|
||||
hex: "#ffffff"
|
||||
alpha: 0.0
|
||||
warmGlow: "rgba(255, 107, 107, 0.35)"
|
||||
noiseOpacity: 0.7
|
||||
|
||||
typography:
|
||||
fontSans: "Poppins, system-ui, sans-serif"
|
||||
fontMono: "Fira Code, ui-monospace, monospace"
|
||||
fontDisplay: "Poppins, system-ui, sans-serif" # optional
|
||||
fontUrl: "https://fonts.googleapis.com/css2?family=Poppins:wght@400;500;600&family=Fira+Code:wght@400;500&display=swap"
|
||||
baseSize: "15px"
|
||||
lineHeight: "1.6"
|
||||
letterSpacing: "-0.003em"
|
||||
|
||||
layout:
|
||||
radius: "0.75rem"
|
||||
density: comfortable
|
||||
|
||||
layoutVariant: standard # standard | cockpit | tiled
|
||||
|
||||
assets:
|
||||
bg: "https://example.com/ocean-bg.jpg"
|
||||
hero: "/my-images/kraken.png"
|
||||
crest: "/my-images/anchor.svg"
|
||||
logo: "/my-images/logo.png"
|
||||
custom:
|
||||
pattern: "/my-images/waves.svg"
|
||||
|
||||
componentStyles:
|
||||
card:
|
||||
boxShadow: "inset 0 0 0 1px rgba(168, 208, 255, 0.18)"
|
||||
header:
|
||||
background: "linear-gradient(180deg, rgba(10, 22, 40, 0.95), rgba(5, 9, 26, 0.9))"
|
||||
|
||||
colorOverrides:
|
||||
destructive: "#ff6b6b"
|
||||
ring: "#ff6b6b"
|
||||
|
||||
customCSS: |
|
||||
/* Any additional selector-level tweaks */
|
||||
```
|
||||
|
||||
创建文件后刷新 dashboard。通过顶栏的调色板图标实时切换主题。选择结果会持久化到 `config.yaml` 的 `dashboard.theme` 下,并在重载时恢复。
|
||||
|
||||
---
|
||||
|
||||
## 插件
|
||||
|
||||
Dashboard 插件是一个包含 `manifest.json`、预构建 JS bundle,以及可选的 CSS 文件和带 FastAPI 路由的 Python 文件的目录。插件与其他 Hermes 插件一起存放在 `~/.hermes/plugins/<name>/`——dashboard 扩展是该插件目录内的 `dashboard/` 子文件夹,因此一个插件可以从单次安装中同时扩展 CLI/gateway 和 dashboard。
|
||||
|
||||
插件不打包 React 或 UI 组件,而是使用暴露在 `window.__HERMES_PLUGIN_SDK__` 上的 **Plugin SDK**。这使插件 bundle 保持极小体积(通常只有几 KB),并避免版本冲突。
|
||||
|
||||
### 快速上手——你的第一个插件
|
||||
|
||||
创建目录结构:
|
||||
|
||||
```bash
|
||||
mkdir -p ~/.hermes/plugins/my-plugin/dashboard/dist
|
||||
```
|
||||
|
||||
编写 manifest:
|
||||
|
||||
```json
|
||||
// ~/.hermes/plugins/my-plugin/dashboard/manifest.json
|
||||
{
|
||||
"name": "my-plugin",
|
||||
"label": "My Plugin",
|
||||
"icon": "Sparkles",
|
||||
"version": "1.0.0",
|
||||
"tab": {
|
||||
"path": "/my-plugin",
|
||||
"position": "after:skills"
|
||||
},
|
||||
"entry": "dist/index.js"
|
||||
}
|
||||
```
|
||||
|
||||
编写 JS bundle(普通 IIFE——无需构建步骤):
|
||||
|
||||
```javascript
|
||||
// ~/.hermes/plugins/my-plugin/dashboard/dist/index.js
|
||||
(function () {
|
||||
"use strict";
|
||||
|
||||
const SDK = window.__HERMES_PLUGIN_SDK__;
|
||||
const { React } = SDK;
|
||||
const { Card, CardHeader, CardTitle, CardContent } = SDK.components;
|
||||
|
||||
function MyPage() {
|
||||
return React.createElement(Card, null,
|
||||
React.createElement(CardHeader, null,
|
||||
React.createElement(CardTitle, null, "My Plugin"),
|
||||
),
|
||||
React.createElement(CardContent, null,
|
||||
React.createElement("p", { className: "text-sm text-muted-foreground" },
|
||||
"Hello from my custom dashboard tab.",
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
window.__HERMES_PLUGINS__.register("my-plugin", MyPage);
|
||||
})();
|
||||
```
|
||||
|
||||
刷新 dashboard——你的标签页出现在导航栏中,位于 **Skills** 之后。
|
||||
|
||||
:::tip 跳过 React.createElement
|
||||
如果你偏好 JSX,可使用任意打包工具(esbuild、Vite、rollup),将 React 设为外部依赖并输出 IIFE 格式。唯一的硬性要求是最终文件是可通过 `<script>` 加载的单个 JS 文件。React 永远不会被打包进去;它来自 `SDK.React`。
|
||||
:::
|
||||
|
||||
### 目录结构
|
||||
|
||||
```
|
||||
~/.hermes/plugins/my-plugin/
|
||||
├── plugin.yaml # optional — existing CLI/gateway plugin manifest
|
||||
├── __init__.py # optional — existing CLI/gateway hooks
|
||||
└── dashboard/ # dashboard extension
|
||||
├── manifest.json # required — tab config, icon, entry point
|
||||
├── dist/
|
||||
│ ├── index.js # required — pre-built JS bundle (IIFE)
|
||||
│ └── style.css # optional — custom CSS
|
||||
└── plugin_api.py # optional — backend API routes (FastAPI)
|
||||
```
|
||||
|
||||
单个插件目录可承载三个正交扩展:
|
||||
|
||||
- `plugin.yaml` + `__init__.py` — CLI/gateway 插件([参见插件页面](./plugins))。
|
||||
- `dashboard/manifest.json` + `dashboard/dist/index.js` — dashboard UI 插件。
|
||||
- `dashboard/plugin_api.py` — dashboard 后端路由。
|
||||
|
||||
三者均非必须;按需包含所需层次即可。
|
||||
|
||||
### Manifest 参考
|
||||
|
||||
```json
|
||||
{
|
||||
"name": "my-plugin",
|
||||
"label": "My Plugin",
|
||||
"description": "What this plugin does",
|
||||
"icon": "Sparkles",
|
||||
"version": "1.0.0",
|
||||
"tab": {
|
||||
"path": "/my-plugin",
|
||||
"position": "after:skills",
|
||||
"override": "/",
|
||||
"hidden": false
|
||||
},
|
||||
"slots": ["sidebar", "header-left"],
|
||||
"entry": "dist/index.js",
|
||||
"css": "dist/style.css",
|
||||
"api": "plugin_api.py"
|
||||
}
|
||||
```
|
||||
|
||||
| 字段 | 必填 | 描述 |
|
||||
|-------|----------|-------------|
|
||||
| `name` | 是 | 唯一插件标识符。小写,可用连字符。用于 URL 和注册。 |
|
||||
| `label` | 是 | 导航标签页中显示的名称。 |
|
||||
| `description` | 否 | 简短描述(显示在 dashboard 管理界面)。 |
|
||||
| `icon` | 否 | Lucide 图标名称。默认为 `Puzzle`。未知名称回退到 `Puzzle`。 |
|
||||
| `version` | 否 | Semver 字符串。默认为 `0.0.0`。 |
|
||||
| `tab.path` | 是 | 标签页的 URL 路径(例如 `/my-plugin`)。 |
|
||||
| `tab.position` | 否 | 标签页插入位置。`"end"`(默认)、`"after:<path>"` 或 `"before:<path>"`——冒号后的值是目标标签页的**路径段**(无前导斜杠)。例如:`"after:skills"`、`"before:config"`。 |
|
||||
| `tab.override` | 否 | 设置为内置路由路径(`"/"`、`"/sessions"`、`"/config"` 等)以**替换**该页面,而非添加新标签页。参见[替换内置页面](#replacing-built-in-pages-taboverride)。 |
|
||||
| `tab.hidden` | 否 | 为 true 时,注册组件和所有插槽,但不向导航添加标签页。用于仅插槽插件。参见[仅插槽插件](#slot-only-plugins-tabhidden)。 |
|
||||
| `slots` | 否 | 此插件填充的命名 shell 插槽。**仅作文档说明**——实际注册通过 JS bundle 中的 `registerSlot()` 完成。在此列出插槽可使发现界面更具信息量。 |
|
||||
| `entry` | 是 | 相对于 `dashboard/` 的 JS bundle 路径。默认为 `dist/index.js`。 |
|
||||
| `css` | 否 | 以 `<link>` 标签注入的 CSS 文件路径。 |
|
||||
| `api` | 否 | 包含 FastAPI 路由的 Python 文件路径。挂载在 `/api/plugins/<name>/`。 |
|
||||
|
||||
#### 可用图标
|
||||
|
||||
插件使用 Lucide 图标名称。Dashboard 按名称映射——未知名称静默回退到 `Puzzle`。
|
||||
|
||||
当前已映射:`Activity`、`BarChart3`、`Clock`、`Code`、`Database`、`Eye`、`FileText`、`Globe`、`Heart`、`KeyRound`、`MessageSquare`、`Package`、`Puzzle`、`Settings`、`Shield`、`Sparkles`、`Star`、`Terminal`、`Wrench`、`Zap`。
|
||||
|
||||
需要其他图标?向 `web/src/App.tsx` 的 `ICON_MAP` 提交 PR——纯增量修改。
|
||||
|
||||
### Plugin SDK
|
||||
|
||||
插件所需的一切均在 `window.__HERMES_PLUGIN_SDK__` 上。插件不应直接导入 React。
|
||||
|
||||
```javascript
|
||||
const SDK = window.__HERMES_PLUGIN_SDK__;
|
||||
|
||||
// React + hooks
|
||||
SDK.React // the React instance
|
||||
SDK.hooks.useState
|
||||
SDK.hooks.useEffect
|
||||
SDK.hooks.useCallback
|
||||
SDK.hooks.useMemo
|
||||
SDK.hooks.useRef
|
||||
SDK.hooks.useContext
|
||||
SDK.hooks.createContext
|
||||
|
||||
// UI components (shadcn/ui primitives)
|
||||
SDK.components.Card
|
||||
SDK.components.CardHeader
|
||||
SDK.components.CardTitle
|
||||
SDK.components.CardContent
|
||||
SDK.components.Badge
|
||||
SDK.components.Button
|
||||
SDK.components.Input
|
||||
SDK.components.Label
|
||||
SDK.components.Select
|
||||
SDK.components.SelectOption
|
||||
SDK.components.Separator
|
||||
SDK.components.Tabs
|
||||
SDK.components.TabsList
|
||||
SDK.components.TabsTrigger
|
||||
SDK.components.PluginSlot // render a named slot (useful for nested plugin UIs)
|
||||
|
||||
// Hermes API client + raw fetcher
|
||||
SDK.api // typed client — getStatus, getSessions, getConfig, ...
|
||||
SDK.fetchJSON // raw fetch for custom endpoints (plugin-registered routes)
|
||||
|
||||
// Utilities
|
||||
SDK.utils.cn // Tailwind class merger (clsx + twMerge)
|
||||
SDK.utils.timeAgo // "5m ago" from unix timestamp
|
||||
SDK.utils.isoTimeAgo // "5m ago" from ISO string
|
||||
|
||||
// Hooks
|
||||
SDK.useI18n // i18n hook for multi-language plugins
|
||||
```
|
||||
|
||||
#### 调用插件的后端
|
||||
|
||||
```javascript
|
||||
SDK.fetchJSON("/api/plugins/my-plugin/data")
|
||||
.then((data) => console.log(data))
|
||||
.catch((err) => console.error("API call failed:", err));
|
||||
```
|
||||
|
||||
`fetchJSON` 会自动注入会话认证 token,将错误作为异常抛出,并自动解析 JSON。
|
||||
|
||||
#### 调用内置 Hermes 端点
|
||||
|
||||
```javascript
|
||||
// Agent status
|
||||
SDK.api.getStatus().then((s) => console.log("Version:", s.version));
|
||||
|
||||
// Recent sessions
|
||||
SDK.api.getSessions(10).then((resp) => console.log(resp.sessions.length));
|
||||
```
|
||||
|
||||
完整列表参见 [Web Dashboard → REST API](./web-dashboard#rest-api)。
|
||||
|
||||
### Shell 插槽
|
||||
|
||||
插槽(slot)允许插件向应用 shell 的命名位置注入组件——cockpit 侧边栏、顶栏、底栏、覆盖层——而无需占用整个标签页。多个插件可以填充同一个插槽;它们按注册顺序堆叠渲染。
|
||||
|
||||
在插件 bundle 内部注册:
|
||||
|
||||
```javascript
|
||||
window.__HERMES_PLUGINS__.registerSlot("my-plugin", "sidebar", MySidebar);
|
||||
window.__HERMES_PLUGINS__.registerSlot("my-plugin", "header-left", MyCrest);
|
||||
```
|
||||
|
||||
#### 插槽目录
|
||||
|
||||
**Shell 全局插槽**(在应用外壳的任意位置渲染):
|
||||
|
||||
| 插槽 | 位置 |
|
||||
|------|----------|
|
||||
| `backdrop` | `<Backdrop />` 层叠栈内,噪点层之上。 |
|
||||
| `header-left` | 顶栏 Hermes 品牌之前。 |
|
||||
| `header-right` | 顶栏主题/语言切换器之前。 |
|
||||
| `header-banner` | 导航栏下方的全宽条带。 |
|
||||
| `sidebar` | Cockpit 侧边栏轨道——**仅在 `layoutVariant === "cockpit"` 时渲染**。 |
|
||||
| `pre-main` | 路由出口之上(`<main>` 内部)。 |
|
||||
| `post-main` | 路由出口之下(`<main>` 内部)。 |
|
||||
| `footer-left` | 底栏单元格内容(替换默认内容)。 |
|
||||
| `footer-right` | 底栏单元格内容(替换默认内容)。 |
|
||||
| `overlay` | 位于所有内容之上的固定定位层。适用于 `customCSS` 无法单独实现的外观效果(扫描线、晕影等)。 |
|
||||
|
||||
**页面级插槽**(仅在指定内置页面上渲染——用于向现有页面注入小部件、卡片或工具栏,而无需覆盖整个路由):
|
||||
|
||||
| 插槽 | 渲染位置 |
|
||||
|------|------------------|
|
||||
| `sessions:top` / `sessions:bottom` | `/sessions` 页面顶部 / 底部。 |
|
||||
| `analytics:top` / `analytics:bottom` | `/analytics` 页面顶部 / 底部。 |
|
||||
| `logs:top` / `logs:bottom` | `/logs` 顶部(过滤工具栏之上)/ 底部(日志查看器之下)。 |
|
||||
| `cron:top` / `cron:bottom` | `/cron` 页面顶部 / 底部。 |
|
||||
| `skills:top` / `skills:bottom` | `/skills` 页面顶部 / 底部。 |
|
||||
| `config:top` / `config:bottom` | `/config` 页面顶部 / 底部。 |
|
||||
| `env:top` / `env:bottom` | `/env`(Keys)页面顶部 / 底部。 |
|
||||
| `docs:top` / `docs:bottom` | `/docs` 顶部(iframe 之上)/ 底部。 |
|
||||
| `chat:top` / `chat:bottom` | `/chat` 顶部 / 底部(仅在启用嵌入式聊天时有效)。 |
|
||||
|
||||
示例——向 Sessions 页面顶部添加横幅卡片:
|
||||
|
||||
```javascript
|
||||
function PinnedSessionsBanner() {
|
||||
return React.createElement(Card, null,
|
||||
React.createElement(CardContent, { className: "py-2 text-xs" },
|
||||
"Pinned note injected by my-plugin"),
|
||||
);
|
||||
}
|
||||
|
||||
window.__HERMES_PLUGINS__.registerSlot("my-plugin", "sessions:top", PinnedSessionsBanner);
|
||||
```
|
||||
|
||||
如果插件只增强现有页面而不需要独立的侧边栏标签页,可将页面级插槽与 `tab.hidden: true` 结合使用。
|
||||
|
||||
Shell 只为上述插槽渲染 `<PluginSlot name="..." />`。注册表接受额外的名称用于嵌套插件 UI——插件可通过 `SDK.components.PluginSlot` 暴露自己的插槽。
|
||||
|
||||
#### 重复注册与 HMR
|
||||
|
||||
如果同一个 `(plugin, slot)` 对被注册两次,后一次调用会替换前一次——这与 React HMR 期望插件重新挂载时的行为一致。
|
||||
|
||||
### 替换内置页面(`tab.override`)
|
||||
|
||||
将 `tab.override` 设置为内置路由路径,可使插件组件替换该页面,而非添加新标签页。适用于主题希望自定义首页(`/`)但保留 dashboard 其余部分的场景。
|
||||
|
||||
```json
|
||||
{
|
||||
"name": "my-home",
|
||||
"label": "Home",
|
||||
"tab": {
|
||||
"path": "/my-home",
|
||||
"override": "/",
|
||||
"position": "end"
|
||||
},
|
||||
"entry": "dist/index.js"
|
||||
}
|
||||
```
|
||||
|
||||
设置 `override` 后:
|
||||
|
||||
- 路由器中 `/` 处的原始页面组件被移除。
|
||||
- 你的插件改为在 `/` 处渲染。
|
||||
- 不会为 `tab.path` 添加导航标签页(覆盖本身才是目的)。
|
||||
|
||||
每个路径只能有一个插件进行覆盖。如果两个插件声明相同的覆盖路径,第一个生效,第二个被忽略并在开发模式下输出警告。
|
||||
|
||||
如果只需要向现有页面添加卡片或工具栏而不完全接管它,请改用[页面级插槽](#augmenting-built-in-pages-page-scoped-slots)。
|
||||
|
||||
### 增强内置页面(页面级插槽)
|
||||
|
||||
通过 `tab.override` 完全替换页面代价较重——你的插件现在拥有整个页面,包括我们未来对其的所有更新。大多数情况下,你只是想向现有页面添加横幅、卡片或工具栏。这正是**页面级插槽**的用途。
|
||||
|
||||
每个内置页面都在其内容区域的顶部和底部暴露 `<page>:top` 和 `<page>:bottom` 插槽。你的插件通过调用 `registerSlot()` 填充其中一个——内置页面正常工作,你的组件在其旁边渲染。
|
||||
|
||||
可用插槽:`sessions:*`、`analytics:*`、`logs:*`、`cron:*`、`skills:*`、`config:*`、`env:*`、`docs:*`、`chat:*`(每个均有 `:top` 和 `:bottom`)。完整目录参见 [Shell 插槽 → 插槽目录](#slot-catalogue)。
|
||||
|
||||
最简示例——在 Sessions 页面顶部固定一个横幅:
|
||||
|
||||
```json
|
||||
// ~/.hermes/plugins/session-notes/dashboard/manifest.json
|
||||
{
|
||||
"name": "session-notes",
|
||||
"label": "Session Notes",
|
||||
"tab": { "path": "/session-notes", "hidden": true },
|
||||
"slots": ["sessions:top"],
|
||||
"entry": "dist/index.js"
|
||||
}
|
||||
```
|
||||
|
||||
```javascript
|
||||
// ~/.hermes/plugins/session-notes/dashboard/dist/index.js
|
||||
(function () {
|
||||
const SDK = window.__HERMES_PLUGIN_SDK__;
|
||||
const { React } = SDK;
|
||||
const { Card, CardContent } = SDK.components;
|
||||
|
||||
function Banner() {
|
||||
return React.createElement(Card, null,
|
||||
React.createElement(CardContent, { className: "py-2 text-xs" },
|
||||
"Remember to label important sessions before archiving."),
|
||||
);
|
||||
}
|
||||
|
||||
// Placeholder for the hidden tab.
|
||||
window.__HERMES_PLUGINS__.register("session-notes", function () { return null; });
|
||||
|
||||
// The real work.
|
||||
window.__HERMES_PLUGINS__.registerSlot("session-notes", "sessions:top", Banner);
|
||||
})();
|
||||
```
|
||||
|
||||
要点:
|
||||
|
||||
- `tab.hidden: true` 使插件不出现在侧边栏——它没有独立页面。
|
||||
- manifest 中的 `slots` 字段仅作文档说明。实际绑定通过 JS bundle 中的 `registerSlot()` 完成。
|
||||
- 多个插件可以声明同一个页面级插槽。它们按注册顺序堆叠渲染。
|
||||
- 无插件注册时零开销:内置页面与之前完全相同地渲染。
|
||||
|
||||
参考插件([`hermes-example-plugins`](https://github.com/NousResearch/hermes-example-plugins/tree/main/example-dashboard) 中的 `example-dashboard`)提供了一个向 `sessions:top` 注入横幅的实时演示——安装它可端到端了解该模式。
|
||||
|
||||
### 仅插槽插件(`tab.hidden`)
|
||||
|
||||
当 `tab.hidden: true` 时,插件注册其组件(用于直接 URL 访问)和所有插槽,但不向导航添加标签页。适用于仅用于注入插槽的插件——顶栏徽标、侧边栏 HUD、覆盖层。
|
||||
|
||||
```json
|
||||
{
|
||||
"name": "header-crest",
|
||||
"label": "Header Crest",
|
||||
"tab": {
|
||||
"path": "/header-crest",
|
||||
"position": "end",
|
||||
"hidden": true
|
||||
},
|
||||
"slots": ["header-left"],
|
||||
"entry": "dist/index.js"
|
||||
}
|
||||
```
|
||||
|
||||
Bundle 仍需调用带占位符组件的 `register()`(以防有人直接访问该 URL),然后调用 `registerSlot()` 完成实际工作。
|
||||
|
||||
### 后端 API 路由
|
||||
|
||||
插件可通过在 manifest 中设置 `api` 来注册 FastAPI 路由。创建文件并导出 `router`:
|
||||
|
||||
```python
|
||||
# ~/.hermes/plugins/my-plugin/dashboard/plugin_api.py
|
||||
from fastapi import APIRouter
|
||||
|
||||
router = APIRouter()
|
||||
|
||||
@router.get("/data")
|
||||
async def get_data():
|
||||
return {"items": ["one", "two", "three"]}
|
||||
|
||||
@router.post("/action")
|
||||
async def do_action(body: dict):
|
||||
return {"ok": True, "received": body}
|
||||
```
|
||||
|
||||
路由挂载在 `/api/plugins/<name>/` 下,因此上述路由变为:
|
||||
|
||||
- `GET /api/plugins/my-plugin/data`
|
||||
- `POST /api/plugins/my-plugin/action`
|
||||
|
||||
插件 API 路由绕过会话 token 认证,因为 dashboard 服务器默认绑定到 localhost。**如果运行不受信任的插件,请勿使用 `--host 0.0.0.0` 将 dashboard 暴露在公共接口上**——其路由也会变得可访问。
|
||||
|
||||
#### 访问 Hermes 内部模块
|
||||
|
||||
后端路由在 dashboard 进程内运行,因此可以直接从 hermes-agent 代码库导入:
|
||||
|
||||
```python
|
||||
from fastapi import APIRouter
|
||||
from hermes_state import SessionDB
|
||||
from hermes_cli.config import load_config
|
||||
|
||||
router = APIRouter()
|
||||
|
||||
@router.get("/session-count")
|
||||
async def session_count():
|
||||
db = SessionDB()
|
||||
try:
|
||||
count = len(db.list_sessions(limit=9999))
|
||||
return {"count": count}
|
||||
finally:
|
||||
db.close()
|
||||
|
||||
@router.get("/config-snapshot")
|
||||
async def config_snapshot():
|
||||
cfg = load_config()
|
||||
return {"model": cfg.get("model", {})}
|
||||
```
|
||||
|
||||
### 插件自定义 CSS
|
||||
|
||||
如果插件需要超出 Tailwind 类和内联 `style=` 的样式,可添加 CSS 文件并在 manifest 中引用:
|
||||
|
||||
```json
|
||||
{
|
||||
"css": "dist/style.css"
|
||||
}
|
||||
```
|
||||
|
||||
文件在插件加载时以 `<link>` 标签注入。使用特定类名以避免与 dashboard 样式冲突,并引用 dashboard 的 CSS 变量以保持主题感知:
|
||||
|
||||
```css
|
||||
/* dist/style.css */
|
||||
.my-plugin-chart {
|
||||
border: 1px solid var(--color-border);
|
||||
background: var(--color-card);
|
||||
color: var(--color-card-foreground);
|
||||
padding: 1rem;
|
||||
}
|
||||
.my-plugin-chart:hover {
|
||||
border-color: var(--color-ring);
|
||||
}
|
||||
```
|
||||
|
||||
Dashboard 将每个 shadcn token 暴露为 `--color-*`,以及主题额外变量(`--theme-asset-*`、`--component-<bucket>-*`、`--radius`、`--spacing-mul`)。引用这些变量后,你的插件会随激活主题自动换肤。
|
||||
|
||||
### 插件发现与重载
|
||||
|
||||
Dashboard 扫描三个目录中的 `dashboard/manifest.json`:
|
||||
|
||||
| 优先级 | 目录 | 来源标签 |
|
||||
|----------|-----------|--------------|
|
||||
| 1(冲突时优先) | `~/.hermes/plugins/<name>/dashboard/` | `user` |
|
||||
| 2 | `<repo>/plugins/memory/<name>/dashboard/` | `bundled` |
|
||||
| 2 | `<repo>/plugins/<name>/dashboard/` | `bundled` |
|
||||
| 3 | `./.hermes/plugins/<name>/dashboard/` | `project`——仅在设置 `HERMES_ENABLE_PROJECT_PLUGINS` 时生效 |
|
||||
|
||||
发现结果在每个 dashboard 进程中缓存。添加新插件后,可以:
|
||||
|
||||
```bash
|
||||
# Force a rescan without restart
|
||||
curl http://127.0.0.1:9119/api/dashboard/plugins/rescan
|
||||
```
|
||||
|
||||
……或重启 `hermes dashboard`。
|
||||
|
||||
#### 插件加载生命周期
|
||||
|
||||
1. Dashboard 加载。`main.tsx` 在 `window.__HERMES_PLUGIN_SDK__` 上暴露 SDK,在 `window.__HERMES_PLUGINS__` 上暴露注册表。
|
||||
2. `App.tsx` 调用 `usePlugins()` → 获取 `GET /api/dashboard/plugins`。
|
||||
3. 对于每个 manifest:注入 CSS `<link>`(如已声明),然后通过 `<script>` 标签加载 JS bundle。
|
||||
4. 插件的 IIFE 运行并调用 `window.__HERMES_PLUGINS__.register(name, Component)`——以及可选的 `.registerSlot(name, slot, Component)` 用于每个插槽。
|
||||
5. Dashboard 将注册的组件与 manifest 对应,将标签页添加到导航(除非 `hidden`),并将组件挂载为路由。
|
||||
|
||||
插件在脚本加载后最多有 **2 秒**时间调用 `register()`。超时后 dashboard 停止等待并完成初始渲染。如果插件之后才注册,它仍会出现——导航是响应式的。
|
||||
|
||||
如果插件脚本加载失败(404、语法错误、IIFE 执行期间抛出异常),dashboard 会向浏览器控制台输出警告并继续运行。
|
||||
|
||||
---
|
||||
|
||||
## 主题 + 插件组合演示
|
||||
|
||||
[`strike-freedom-cockpit`](https://github.com/NousResearch/hermes-example-plugins/tree/main/strike-freedom-cockpit) 插件(伴随仓库 `hermes-example-plugins`)是一个完整的换肤演示。它将主题 YAML 与仅插槽插件配对,在不 fork dashboard 的情况下生成驾驶舱风格的 HUD。
|
||||
|
||||
**演示内容:**
|
||||
|
||||
- 完整主题,使用调色板、字体排版、`fontUrl`、`layoutVariant: cockpit`、`assets`、`componentStyles`(切角卡片、渐变背景)、`colorOverrides` 和 `customCSS`(扫描线叠加)。
|
||||
- 仅插槽插件(`tab.hidden: true`),注册到三个插槽:
|
||||
- `sidebar` — 带有由 `SDK.api.getStatus()` 驱动的实时遥测条的 MS-STATUS 面板。
|
||||
- `header-left` — 从激活主题读取 `--theme-asset-crest` 的派系徽标。
|
||||
- `footer-right` — 替换默认组织行的自定义标语。
|
||||
- 插件通过 CSS 变量读取主题提供的图片,因此切换主题可在不修改插件代码的情况下更换英雄图/徽标。
|
||||
|
||||
**安装:**
|
||||
|
||||
```bash
|
||||
git clone https://github.com/NousResearch/hermes-example-plugins.git
|
||||
|
||||
# Theme
|
||||
cp hermes-example-plugins/strike-freedom-cockpit/theme/strike-freedom.yaml \
|
||||
~/.hermes/dashboard-themes/
|
||||
|
||||
# Plugin
|
||||
cp -r hermes-example-plugins/strike-freedom-cockpit ~/.hermes/plugins/
|
||||
```
|
||||
|
||||
打开 dashboard,从主题切换器中选择 **Strike Freedom**。驾驶舱侧边栏出现,徽标显示在顶栏,标语替换底栏。切换回 **Hermes Teal**,插件仍然安装但不可见(`sidebar` 插槽仅在 `cockpit` 布局变体下渲染)。
|
||||
|
||||
阅读插件源码(伴随仓库中的 `strike-freedom-cockpit/dashboard/dist/index.js`),了解它如何读取 CSS 变量、防范不支持插槽的旧版 dashboard,以及如何从单个 bundle 注册三个插槽。
|
||||
|
||||
---
|
||||
|
||||
## API 参考
|
||||
|
||||
### 主题端点
|
||||
|
||||
| 端点 | 方法 | 描述 |
|
||||
|----------|--------|-------------|
|
||||
| `/api/dashboard/themes` | GET | 列出可用主题及当前激活名称。内置主题返回 `{name, label, description}`;用户主题还包含带有完整规范化主题对象的 `definition` 字段。 |
|
||||
| `/api/dashboard/theme` | PUT | 设置激活主题。请求体:`{"name": "midnight"}`。持久化到 `config.yaml` 的 `dashboard.theme` 下。 |
|
||||
|
||||
### 插件端点
|
||||
|
||||
| 端点 | 方法 | 描述 |
|
||||
|----------|--------|-------------|
|
||||
| `/api/dashboard/plugins` | GET | 列出已发现的插件(含 manifest,去除内部字段)。 |
|
||||
| `/api/dashboard/plugins/rescan` | GET | 强制重新扫描插件目录,无需重启。 |
|
||||
| `/dashboard-plugins/<name>/<path>` | GET | 从插件的 `dashboard/` 目录提供静态资源。路径遍历已被阻止。 |
|
||||
| `/api/plugins/<name>/*` | * | 插件注册的后端路由。 |
|
||||
|
||||
### `window` 上的 SDK
|
||||
|
||||
| 全局变量 | 类型 | 提供方 |
|
||||
|--------|------|----------|
|
||||
| `window.__HERMES_PLUGIN_SDK__` | object | `registry.ts` — React、hooks、UI 组件、API 客户端、工具函数。 |
|
||||
| `window.__HERMES_PLUGINS__.register(name, Component)` | function | 注册插件的主组件。 |
|
||||
| `window.__HERMES_PLUGINS__.registerSlot(name, slot, Component)` | function | 注册到命名 shell 插槽。 |
|
||||
|
||||
---
|
||||
|
||||
## 故障排查
|
||||
|
||||
**我的主题没有出现在选择器中。**
|
||||
检查文件是否在 `~/.hermes/dashboard-themes/` 中且以 `.yaml` 或 `.yml` 结尾。刷新页面。运行 `curl http://127.0.0.1:9119/api/dashboard/themes`——你的主题应出现在响应中。如果 YAML 有解析错误,dashboard 会记录到 `~/.hermes/logs/` 下的 `errors.log`。
|
||||
|
||||
**我的插件标签页没有显示。**
|
||||
1. 检查 manifest 是否在 `~/.hermes/plugins/<name>/dashboard/manifest.json`(注意 `dashboard/` 子目录)。
|
||||
2. 运行 `curl http://127.0.0.1:9119/api/dashboard/plugins/rescan` 强制重新发现。
|
||||
3. 打开浏览器开发工具 → Network——确认 `manifest.json`、`index.js` 和任何 CSS 均无 404 加载成功。
|
||||
4. 打开浏览器开发工具 → Console——查找 IIFE 执行期间的错误或 `window.__HERMES_PLUGINS__ is undefined`(表示 SDK 未初始化,通常是更早的 React 渲染崩溃导致)。
|
||||
5. 验证你的 bundle 以与 `manifest.json:name` **相同的名称**调用 `window.__HERMES_PLUGINS__.register(...)`。
|
||||
|
||||
**插槽注册的组件没有渲染。**
|
||||
`sidebar` 插槽仅在激活主题设置了 `layoutVariant: cockpit` 时渲染。其他插槽始终渲染。如果你注册到某个插槽但没有命中,在 `registerSlot` 内添加 `console.log` 以确认插件 bundle 是否已运行。
|
||||
|
||||
**插件后端路由返回 404。**
|
||||
1. 确认 manifest 中有 `"api": "plugin_api.py"` 且指向 `dashboard/` 内的现有文件。
|
||||
2. 重启 `hermes dashboard`——插件 API 路由在启动时挂载一次,**不会**在重新扫描时挂载。
|
||||
3. 检查 `plugin_api.py` 是否导出了模块级的 `router = APIRouter()`。其他导出名称不会被识别。
|
||||
4. 查看 `~/.hermes/logs/errors.log` 中的 `Failed to load plugin <name> API routes`——导入错误会记录在那里。
|
||||
|
||||
**切换主题后我的颜色覆盖丢失了。**
|
||||
`colorOverrides` 的作用域限于激活主题,切换主题时会被清除——这是设计行为。如果你希望覆盖持久化,请将其写入主题的 YAML,而非实时切换器。
|
||||
|
||||
**主题 customCSS 被截断了。**
|
||||
`customCSS` 块每个主题上限为 32 KiB。可将大型样式表拆分到多个主题中,或改用通过 `css` 字段注入完整样式表的插件(无大小限制)。
|
||||
|
||||
**我想在 PyPI 上发布插件。**
|
||||
Dashboard 插件通过目录结构安装,而非 pip 入口点。目前最简洁的分发方式是用户克隆到 `~/.hermes/plugins/` 的 git 仓库。基于 pip 的 dashboard 插件安装器目前尚未实现。
|
||||
@ -0,0 +1,414 @@
|
||||
---
|
||||
title: 备用提供商
|
||||
description: 配置自动故障转移,在主模型不可用时切换到备用 LLM 提供商。
|
||||
sidebar_label: 备用提供商
|
||||
sidebar_position: 8
|
||||
---
|
||||
|
||||
# 备用提供商
|
||||
|
||||
Hermes Agent 具备三层弹性机制,在提供商出现问题时保持会话正常运行:
|
||||
|
||||
1. **[凭据池](./credential-pools.md)** — 在*同一*提供商的多个 API 密钥之间轮换(优先尝试)
|
||||
2. **主模型备用** — 当主模型失败时,自动切换到*不同*的提供商:模型
|
||||
3. **辅助任务备用** — 针对视觉、压缩、网页提取等附属任务的独立提供商解析
|
||||
|
||||
凭据池处理同一提供商内的轮换(例如多个 OpenRouter 密钥)。本页介绍跨提供商的备用机制。两者均为可选,且相互独立。
|
||||
|
||||
## 主模型备用
|
||||
|
||||
当主 LLM 提供商遇到错误——速率限制、服务器过载、认证失败、连接中断——Hermes 可以在会话中途自动切换到备用提供商:模型对,且不会丢失对话内容。
|
||||
|
||||
### 配置
|
||||
|
||||
最简便的方式是使用交互式管理器:
|
||||
|
||||
```bash
|
||||
hermes fallback
|
||||
```
|
||||
|
||||
`hermes fallback` 复用 `hermes model` 的提供商选择器——相同的提供商列表、相同的凭据提示、相同的验证流程。使用子命令 `add`、`list`(别名 `ls`)、`remove`(别名 `rm`)和 `clear` 来管理备用链。更改会持久化到 `config.yaml` 顶层的 `fallback_providers:` 列表中。
|
||||
|
||||
如果你更倾向于直接编辑 YAML,可在 `~/.hermes/config.yaml` 中添加 `fallback_model` 部分:
|
||||
|
||||
```yaml
|
||||
fallback_model:
|
||||
provider: openrouter
|
||||
model: anthropic/claude-sonnet-4
|
||||
```
|
||||
|
||||
`provider` 和 `model` 均为**必填项**。若任一缺失,备用功能将被禁用。
|
||||
|
||||
:::note `fallback_model` 与 `fallback_providers`
|
||||
`fallback_model`(单数)是旧版单备用键——Hermes 仍支持以保持向后兼容。`fallback_providers`(复数,列表)支持按顺序尝试多个备用;`hermes fallback` 写入此键。当两者同时设置时,Hermes 会合并它们,`fallback_providers` 优先。
|
||||
:::
|
||||
|
||||
### 支持的提供商
|
||||
|
||||
| 提供商 | 值 | 要求 |
|
||||
|----------|-------|-------------|
|
||||
| AI Gateway | `ai-gateway` | `AI_GATEWAY_API_KEY` |
|
||||
| OpenRouter | `openrouter` | `OPENROUTER_API_KEY` |
|
||||
| Nous Portal | `nous` | `hermes setup --portal`(全新安装)或 `hermes auth add nous`(OAuth) |
|
||||
| OpenAI Codex | `openai-codex` | `hermes model`(ChatGPT OAuth) |
|
||||
| GitHub Copilot | `copilot` | `COPILOT_GITHUB_TOKEN`、`GH_TOKEN` 或 `GITHUB_TOKEN` |
|
||||
| GitHub Copilot ACP | `copilot-acp` | 外部进程(编辑器集成) |
|
||||
| Anthropic | `anthropic` | `ANTHROPIC_API_KEY` 或 Claude Code 凭据 |
|
||||
| z.ai / GLM | `zai` | `GLM_API_KEY` |
|
||||
| Kimi / Moonshot | `kimi-coding` | `KIMI_API_KEY` |
|
||||
| MiniMax | `minimax` | `MINIMAX_API_KEY` |
|
||||
| MiniMax(中国)| `minimax-cn` | `MINIMAX_CN_API_KEY` |
|
||||
| DeepSeek | `deepseek` | `DEEPSEEK_API_KEY` |
|
||||
| NVIDIA NIM | `nvidia` | `NVIDIA_API_KEY`(可选:`NVIDIA_BASE_URL`) |
|
||||
| GMI Cloud | `gmi` | `GMI_API_KEY`(可选:`GMI_BASE_URL`) |
|
||||
| StepFun | `stepfun` | `STEPFUN_API_KEY`(可选:`STEPFUN_BASE_URL`) |
|
||||
| Ollama Cloud | `ollama-cloud` | `OLLAMA_API_KEY` |
|
||||
| Google Gemini(OAuth) | `google-gemini-cli` | `hermes model`(Google OAuth;可选:`HERMES_GEMINI_PROJECT_ID`) |
|
||||
| Google AI Studio | `gemini` | `GOOGLE_API_KEY`(别名:`GEMINI_API_KEY`) |
|
||||
| xAI(Grok) | `xai`(别名 `grok`) | `XAI_API_KEY`(可选:`XAI_BASE_URL`) |
|
||||
| xAI Grok OAuth(SuperGrok) | `xai-oauth`(别名 `grok-oauth`) | `hermes model` → xAI Grok OAuth(浏览器登录;需 SuperGrok 订阅) |
|
||||
| AWS Bedrock | `bedrock` | 标准 boto3 认证(`AWS_REGION` + `AWS_PROFILE` 或 `AWS_ACCESS_KEY_ID`) |
|
||||
| Qwen Portal(OAuth) | `qwen-oauth` | `hermes model`(Qwen Portal OAuth;可选:`HERMES_QWEN_BASE_URL`) |
|
||||
| MiniMax(OAuth) | `minimax-oauth` | `hermes model`(MiniMax 门户 OAuth) |
|
||||
| OpenCode Zen | `opencode-zen` | `OPENCODE_ZEN_API_KEY` |
|
||||
| OpenCode Go | `opencode-go` | `OPENCODE_GO_API_KEY` |
|
||||
| Kilo Code | `kilocode` | `KILOCODE_API_KEY` |
|
||||
| Xiaomi MiMo | `xiaomi` | `XIAOMI_API_KEY` |
|
||||
| Arcee AI | `arcee` | `ARCEEAI_API_KEY` |
|
||||
| GMI Cloud | `gmi` | `GMI_API_KEY` |
|
||||
| Alibaba / DashScope | `alibaba` | `DASHSCOPE_API_KEY` |
|
||||
| Alibaba Coding Plan | `alibaba-coding-plan` | `ALIBABA_CODING_PLAN_API_KEY`(回退到 `DASHSCOPE_API_KEY`) |
|
||||
| Kimi / Moonshot(中国) | `kimi-coding-cn` | `KIMI_CN_API_KEY` |
|
||||
| StepFun | `stepfun` | `STEPFUN_API_KEY` |
|
||||
| Tencent TokenHub | `tencent-tokenhub` | `TOKENHUB_API_KEY` |
|
||||
| Microsoft Foundry | `azure-foundry` | `AZURE_FOUNDRY_API_KEY` + `AZURE_FOUNDRY_BASE_URL` |
|
||||
| LM Studio(本地) | `lmstudio` | `LM_API_KEY`(本地可不填)+ `LM_BASE_URL` |
|
||||
| Hugging Face | `huggingface` | `HF_TOKEN` |
|
||||
| 自定义端点 | `custom` | `base_url` + `key_env`(见下文) |
|
||||
|
||||
### 自定义端点备用
|
||||
|
||||
对于兼容 OpenAI 的自定义端点,添加 `base_url` 并可选填 `key_env`:
|
||||
|
||||
```yaml
|
||||
fallback_model:
|
||||
provider: custom
|
||||
model: my-local-model
|
||||
base_url: http://localhost:8000/v1
|
||||
key_env: MY_LOCAL_KEY # 包含 API 密钥的环境变量名
|
||||
```
|
||||
|
||||
### 备用触发条件
|
||||
|
||||
当主模型出现以下失败时,备用机制自动激活:
|
||||
|
||||
- **速率限制**(HTTP 429)——耗尽重试次数后
|
||||
- **服务器错误**(HTTP 500、502、503)——耗尽重试次数后
|
||||
- **认证失败**(HTTP 401、403)——立即触发(重试无意义)
|
||||
- **未找到**(HTTP 404)——立即触发
|
||||
- **无效响应**——API 多次返回格式错误或空响应时
|
||||
|
||||
触发后,Hermes 将:
|
||||
|
||||
1. 解析备用提供商的凭据
|
||||
2. 构建新的 API 客户端
|
||||
3. 就地替换模型、提供商和客户端
|
||||
4. 重置重试计数器并继续对话
|
||||
|
||||
切换是无感知的——对话历史、工具调用和上下文均被保留。Agent 从中断处继续,只是使用了不同的模型。
|
||||
|
||||
:::info 按轮次,而非按会话
|
||||
备用机制的**作用域为单次轮次**:每条新用户消息都从主模型重新开始。若主模型在某轮次中途失败,备用仅对该轮次生效。下一条消息时,Hermes 会再次尝试主模型。在单次轮次内,备用最多激活一次——若备用也失败,则进入常规错误处理流程(重试,然后返回错误消息)。这既防止了单轮次内的级联故障转移循环,又让主模型在每轮次都有重新尝试的机会。
|
||||
:::
|
||||
|
||||
### 示例
|
||||
|
||||
**以 OpenRouter 作为 Anthropic 原生的备用:**
|
||||
```yaml
|
||||
model:
|
||||
provider: anthropic
|
||||
default: claude-sonnet-4-6
|
||||
|
||||
fallback_model:
|
||||
provider: openrouter
|
||||
model: anthropic/claude-sonnet-4
|
||||
```
|
||||
|
||||
**以 Nous Portal 作为 OpenRouter 的备用:**
|
||||
```yaml
|
||||
model:
|
||||
provider: openrouter
|
||||
default: anthropic/claude-opus-4
|
||||
|
||||
fallback_model:
|
||||
provider: nous
|
||||
model: nous-hermes-3
|
||||
```
|
||||
|
||||
**以本地模型作为云端的备用:**
|
||||
```yaml
|
||||
fallback_model:
|
||||
provider: custom
|
||||
model: llama-3.1-70b
|
||||
base_url: http://localhost:8000/v1
|
||||
key_env: LOCAL_API_KEY
|
||||
```
|
||||
|
||||
**以 Codex OAuth 作为备用:**
|
||||
```yaml
|
||||
fallback_model:
|
||||
provider: openai-codex
|
||||
model: gpt-5.3-codex
|
||||
```
|
||||
|
||||
### 备用适用范围
|
||||
|
||||
| 场景 | 是否支持备用 |
|
||||
|---------|-------------------|
|
||||
| CLI 会话 | ✔ |
|
||||
| 消息网关(Telegram、Discord 等) | ✔ |
|
||||
| 子 Agent 委派 | ✘(子 Agent 不继承备用配置) |
|
||||
| Cron 任务 | ✘(使用固定提供商运行) |
|
||||
| 辅助任务(视觉、压缩等) | ✘(使用各自的提供商链——见下文) |
|
||||
|
||||
:::tip
|
||||
`fallback_model` 没有对应的环境变量——它只能通过 `config.yaml` 配置。这是有意为之:备用配置是一个经过深思熟虑的选择,不应被过期的 shell 导出变量覆盖。
|
||||
:::
|
||||
|
||||
---
|
||||
|
||||
## 辅助任务备用
|
||||
|
||||
Hermes 为附属任务使用独立的轻量级模型。每个任务都有自己的提供商解析链,充当内置的备用系统。
|
||||
|
||||
### 具有独立提供商解析的任务
|
||||
|
||||
| 任务 | 功能说明 | 配置键 |
|
||||
|------|-------------|-----------|
|
||||
| 视觉 | 图像分析、浏览器截图 | `auxiliary.vision` |
|
||||
| 网页提取 | 网页内容摘要 | `auxiliary.web_extract` |
|
||||
| 压缩 | 上下文压缩摘要 | `auxiliary.compression` |
|
||||
| Skills Hub | 技能搜索与发现 | `auxiliary.skills_hub` |
|
||||
| MCP | MCP 辅助操作 | `auxiliary.mcp` |
|
||||
| 审批 | 智能命令审批分类 | `auxiliary.approval` |
|
||||
| 标题生成 | 会话标题摘要 | `auxiliary.title_generation` |
|
||||
| Triage Specifier | `hermes kanban specify` / 看板(kanban)✨ 按钮——将单行 triage 任务扩展为完整规格 | `auxiliary.triage_specifier` |
|
||||
|
||||
### 自动检测链
|
||||
|
||||
当任务的提供商设置为 `"auto"`(默认值)时,Hermes 按顺序尝试各提供商,直到找到可用的:
|
||||
|
||||
**文本任务(压缩、网页提取等):**
|
||||
|
||||
```text
|
||||
OpenRouter → Nous Portal → 自定义端点 → Codex OAuth →
|
||||
API 密钥提供商(z.ai、Kimi、MiniMax、Xiaomi MiMo、Hugging Face、Anthropic)→ 放弃
|
||||
```
|
||||
|
||||
**视觉任务:**
|
||||
|
||||
```text
|
||||
主提供商(若支持视觉)→ OpenRouter → Nous Portal →
|
||||
Codex OAuth → Anthropic → 自定义端点 → 放弃
|
||||
```
|
||||
|
||||
若解析到的提供商在调用时失败,Hermes 还有内部重试机制:若该提供商不是 OpenRouter 且未设置显式 `base_url`,则尝试以 OpenRouter 作为最后备用。
|
||||
|
||||
### 配置辅助提供商
|
||||
|
||||
每个任务可在 `config.yaml` 中独立配置:
|
||||
|
||||
```yaml
|
||||
auxiliary:
|
||||
vision:
|
||||
provider: "auto" # auto | openrouter | nous | codex | main | anthropic
|
||||
model: "" # 例如 "openai/gpt-4o"
|
||||
base_url: "" # 直接端点(优先于 provider)
|
||||
api_key: "" # base_url 的 API 密钥
|
||||
|
||||
web_extract:
|
||||
provider: "auto"
|
||||
model: ""
|
||||
|
||||
compression:
|
||||
provider: "auto"
|
||||
model: ""
|
||||
|
||||
skills_hub:
|
||||
provider: "auto"
|
||||
model: ""
|
||||
|
||||
mcp:
|
||||
provider: "auto"
|
||||
model: ""
|
||||
```
|
||||
|
||||
以上每个任务均遵循相同的 **provider / model / base_url** 模式。上下文压缩在 `auxiliary.compression` 下配置:
|
||||
|
||||
```yaml
|
||||
auxiliary:
|
||||
compression:
|
||||
provider: main # 与其他辅助任务相同的提供商选项
|
||||
model: google/gemini-3-flash-preview
|
||||
base_url: null # 自定义 OpenAI 兼容端点
|
||||
```
|
||||
|
||||
备用模型使用:
|
||||
|
||||
```yaml
|
||||
fallback_model:
|
||||
provider: openrouter
|
||||
model: anthropic/claude-sonnet-4
|
||||
# base_url: http://localhost:8000/v1 # 可选自定义端点
|
||||
```
|
||||
|
||||
三者——辅助任务、压缩、备用——工作方式相同:设置 `provider` 指定处理请求的提供商,`model` 指定使用的模型,`base_url` 指向自定义端点(会覆盖 provider)。
|
||||
|
||||
### 辅助任务的提供商选项
|
||||
|
||||
以下选项仅适用于 `auxiliary:`、`compression:` 和 `fallback_model:` 配置——`"main"` **不是**顶层 `model.provider` 的有效值。对于自定义端点,请在 `model:` 部分使用 `provider: custom`(参见 [AI 提供商](/integrations/providers))。
|
||||
|
||||
| 提供商 | 说明 | 要求 |
|
||||
|----------|-------------|-------------|
|
||||
| `"auto"` | 按顺序尝试各提供商直到找到可用的(默认) | 至少配置一个提供商 |
|
||||
| `"openrouter"` | 强制使用 OpenRouter | `OPENROUTER_API_KEY` |
|
||||
| `"nous"` | 强制使用 Nous Portal | `hermes auth` |
|
||||
| `"codex"` | 强制使用 Codex OAuth | `hermes model` → Codex |
|
||||
| `"main"` | 使用主 Agent 当前的提供商(仅限辅助任务) | 已配置活跃的主提供商 |
|
||||
| `"anthropic"` | 强制使用 Anthropic 原生 | `ANTHROPIC_API_KEY` 或 Claude Code 凭据 |
|
||||
|
||||
### 直接端点覆盖
|
||||
|
||||
对于任意辅助任务,设置 `base_url` 将完全绕过提供商解析,直接向该端点发送请求:
|
||||
|
||||
```yaml
|
||||
auxiliary:
|
||||
vision:
|
||||
base_url: "http://localhost:1234/v1"
|
||||
api_key: "local-key"
|
||||
model: "qwen2.5-vl"
|
||||
```
|
||||
|
||||
`base_url` 优先于 `provider`。Hermes 使用配置的 `api_key` 进行认证,若未设置则回退到 `OPENAI_API_KEY`。对于自定义端点,**不会**复用 `OPENROUTER_API_KEY`。
|
||||
|
||||
---
|
||||
|
||||
## 辅助任务容量错误备用
|
||||
|
||||
当你设置了显式的辅助提供商(例如 `auxiliary.vision.provider: glm`)时,Hermes 将其视为首选——但若该提供商因**容量错误**(HTTP 402 付款要求、HTTP 429 每日配额耗尽、连接失败)而无法处理请求,Hermes 会通过分层链进行备用,而不是静默失败:
|
||||
|
||||
1. **主辅助提供商** — 你配置的那个(始终优先尝试)
|
||||
2. **`auxiliary.<task>.fallback_chain`** — 你的每任务覆盖列表(若已配置)
|
||||
3. **主 Agent 提供商 + 模型** — 最后的安全网(始终尝试,即使未配置链)
|
||||
4. **警告 + 重新抛出** — 若所有层均失败,Hermes 以 WARNING 级别记录 `Auxiliary <task>: ... all fallbacks exhausted` 并重新抛出原始错误
|
||||
|
||||
瞬时 HTTP 429 速率限制(`Retry-After: ...`)被视为请求约束,而非容量问题——它们遵守你的显式提供商选择,**不会**触发备用链。只有每日/每月配额耗尽、付款错误和连接失败才会绕过显式提供商限制。
|
||||
|
||||
对于使用 `provider: auto`(无显式辅助提供商)的用户,现有的自动检测链将替代步骤 2–3 运行。其第一步已经是主 Agent 模型,因此 `auto` 用户无需任何配置即可获得相同效果。
|
||||
|
||||
### 可选:每任务备用链
|
||||
|
||||
若你希望使用与"主 Agent 模型优先"不同的备用顺序,可显式配置 `fallback_chain`。每个条目至少需要 `provider`;`model`、`base_url` 和 `api_key` 为可选。
|
||||
|
||||
```yaml
|
||||
auxiliary:
|
||||
vision:
|
||||
provider: glm
|
||||
model: glm-4v-flash
|
||||
fallback_chain:
|
||||
- provider: openrouter
|
||||
model: google/gemini-3-flash-preview
|
||||
- provider: nous
|
||||
model: anthropic/claude-sonnet-4
|
||||
|
||||
compression:
|
||||
provider: openrouter
|
||||
fallback_chain:
|
||||
- provider: openai
|
||||
model: gpt-4o-mini
|
||||
```
|
||||
|
||||
你**不需要**配置 `fallback_chain` 才能获得备用功能——主 Agent 安全网无论如何都会运行。仅当你明确希望使用与默认不同的顺序时才需配置。
|
||||
|
||||
### 触发备用的提供商配额错误
|
||||
|
||||
Hermes 将以下情况识别为等同于 402 额度耗尽的容量错误(而非瞬时速率限制):
|
||||
|
||||
- Bedrock / LiteLLM:`Too many tokens per day`、`daily limit`、`tokens per day`
|
||||
- Vertex AI / GCP:`quota exceeded`、`resource exhausted`、`RESOURCE_EXHAUSTED`
|
||||
- 通用:`daily quota`、`quota_exceeded`
|
||||
|
||||
若你的提供商对每日配额耗尽返回不同的错误信息,而 Hermes 未触发备用,这是一个 bug——请附上确切的错误字符串提交 issue。
|
||||
|
||||
---
|
||||
|
||||
## 上下文压缩备用
|
||||
|
||||
上下文压缩使用 `auxiliary.compression` 配置块来控制处理摘要的模型和提供商:
|
||||
|
||||
```yaml
|
||||
auxiliary:
|
||||
compression:
|
||||
provider: "auto" # auto | openrouter | nous | main
|
||||
model: "google/gemini-3-flash-preview"
|
||||
```
|
||||
|
||||
:::info 旧版迁移
|
||||
旧版配置中的 `compression.summary_model` / `compression.summary_provider` / `compression.summary_base_url` 会在首次加载时自动迁移到 `auxiliary.compression.*`(配置版本 17)。
|
||||
:::
|
||||
|
||||
若压缩没有可用的提供商,Hermes 会直接丢弃中间对话轮次而不生成摘要,而不是让会话失败。
|
||||
|
||||
---
|
||||
|
||||
## 委派提供商覆盖
|
||||
|
||||
由 `delegate_task` 生成的子 Agent **不会**使用主备用模型。但可以将它们路由到不同的提供商:模型对以优化成本:
|
||||
|
||||
```yaml
|
||||
delegation:
|
||||
provider: "openrouter" # 覆盖所有子 Agent 的提供商
|
||||
model: "google/gemini-3-flash-preview" # 覆盖模型
|
||||
# base_url: "http://localhost:1234/v1" # 或使用直接端点
|
||||
# api_key: "local-key"
|
||||
```
|
||||
|
||||
完整配置详情参见[子 Agent 委派](/user-guide/features/delegation)。
|
||||
|
||||
---
|
||||
|
||||
## Cron 任务提供商
|
||||
|
||||
Cron 任务使用执行时配置的提供商运行,不支持备用模型。若要为 Cron 任务使用不同的提供商,请在 Cron 任务本身上配置 `provider` 和 `model` 覆盖:
|
||||
|
||||
```python
|
||||
cronjob(
|
||||
action="create",
|
||||
schedule="every 2h",
|
||||
prompt="Check server status",
|
||||
provider="openrouter",
|
||||
model="google/gemini-3-flash-preview"
|
||||
)
|
||||
```
|
||||
|
||||
完整配置详情参见[定时任务(Cron)](/user-guide/features/cron)。
|
||||
|
||||
---
|
||||
|
||||
## 总结
|
||||
|
||||
| 功能 | 备用机制 | 配置位置 |
|
||||
|---------|-------------------|----------------|
|
||||
| 主 Agent 模型 | `fallback_model`(config.yaml 中)——出错时按轮次故障转移(每轮次恢复主模型) | `fallback_model:`(顶层) |
|
||||
| 辅助任务(任意)— auto 用户 | 容量错误时完整自动检测链(主 Agent 模型优先,然后提供商链) | `auxiliary.<task>.provider: auto` |
|
||||
| 辅助任务(任意)— 显式提供商 | `fallback_chain`(若已设置)→ 主 Agent 模型 → 警告 + 抛出,仅在容量错误时触发 | `auxiliary.<task>.fallback_chain` |
|
||||
| 视觉 | 分层(见上文)+ 内部 OpenRouter 重试 | `auxiliary.vision` |
|
||||
| 网页提取 | 分层(见上文)+ 内部 OpenRouter 重试 | `auxiliary.web_extract` |
|
||||
| 上下文压缩 | 分层(见上文);所有层不可用时降级为无摘要 | `auxiliary.compression` |
|
||||
| Skills Hub | 分层(见上文) | `auxiliary.skills_hub` |
|
||||
| MCP 辅助 | 分层(见上文) | `auxiliary.mcp` |
|
||||
| 审批分类 | 分层(见上文) | `auxiliary.approval` |
|
||||
| 标题生成 | 分层(见上文) | `auxiliary.title_generation` |
|
||||
| Triage Specifier | 分层(见上文) | `auxiliary.triage_specifier` |
|
||||
| 委派 | 仅提供商覆盖(无自动备用) | `delegation.provider` / `delegation.model` |
|
||||
| Cron 任务 | 仅每任务提供商覆盖(无自动备用) | 每任务 `provider` / `model` |
|
||||
@ -0,0 +1,180 @@
|
||||
---
|
||||
sidebar_position: 16
|
||||
title: "持久目标"
|
||||
description: "设置一个持续目标,让 Hermes 跨轮次持续工作直到完成。我们对 Ralph loop 的实现。"
|
||||
---
|
||||
|
||||
# 持久目标(`/goal`)
|
||||
|
||||
`/goal` 为 Hermes 设置一个跨轮次持续存在的目标。每轮结束后,一个轻量级裁判模型会检查目标是否已被助手的最新回复满足。若未满足,Hermes 会自动将一条续行 prompt(提示词)注入同一会话并继续工作——直到目标达成、你暂停或清除目标,或者轮次预算耗尽为止。
|
||||
|
||||
这是我们对 **Ralph loop** 的实现,直接受 Eric Traut(OpenAI)在 [Codex CLI 0.128.0 的 `/goal`](https://github.com/openai/codex) 中的启发。核心思路——跨轮次保持目标存活、不达成不停止——源自他们。此处的实现是独立的,并已适配 Hermes 的架构。
|
||||
|
||||
## 适用场景
|
||||
|
||||
当你希望 Hermes 自主迭代、无需每轮重新提示时,使用 `/goal`:
|
||||
|
||||
- "修复 `src/` 中的所有 lint 错误,并验证 `ruff check` 通过"
|
||||
- "从仓库 Y 移植功能 X,包含测试,并让 CI 变绿"
|
||||
- "调查为何会话 ID 有时在中途压缩时发生漂移,并撰写报告"
|
||||
- "构建一个小型 CLI,按 EXIF 日期重命名文件,然后对 photos/ 文件夹进行测试"
|
||||
|
||||
只需一轮即可完成的任务不需要 `/goal`。*否则你需要说三次"继续"* 的任务,才是它的用武之地。
|
||||
|
||||
## 快速开始
|
||||
|
||||
```
|
||||
/goal Fix every failing test in tests/hermes_cli/ and make sure scripts/run_tests.sh passes for that directory
|
||||
```
|
||||
|
||||
你将看到:
|
||||
|
||||
1. **目标已接受** — `⊙ Goal set (20-turn budget): <your goal>`
|
||||
2. **第 1 轮运行** — Hermes 开始工作,就像你发送了一条普通消息一样。
|
||||
3. **裁判运行** — 轮次结束后,裁判模型判定 `done` 或 `continue`。
|
||||
4. **若需要则触发循环** — 若为 `continue`,你将看到 `↻ Continuing toward goal (1/20): <judge's reason>`,Hermes 自动执行下一步。
|
||||
5. **终止** — 最终你会看到 `✓ Goal achieved: <reason>` 或 `⏸ Goal paused — N/20 turns used`。
|
||||
|
||||
## 命令
|
||||
|
||||
| 命令 | 功能 |
|
||||
|---|---|
|
||||
| `/goal <text>` | 设置(或替换)持续目标。立即启动第一轮,无需再发送单独消息。 |
|
||||
| `/goal` 或 `/goal status` | 显示当前目标、状态及已用轮次。 |
|
||||
| `/goal pause` | 停止自动续行循环,但不清除目标。 |
|
||||
| `/goal resume` | 恢复循环(将轮次计数器重置为零)。 |
|
||||
| `/goal clear` | 完全删除目标。 |
|
||||
|
||||
在 CLI 及所有 gateway 平台(Telegram、Discord、Slack、Matrix、Signal、WhatsApp、SMS、iMessage、Webhook、API server 以及 Web 控制台)上行为完全一致。
|
||||
|
||||
## 目标进行中追加条件:`/subgoal`
|
||||
|
||||
目标激活期间,你可以使用 `/subgoal <text>` 追加额外的验收条件,而不会重置循环。每次调用会向目标的子目标列表添加一个编号条目;下一轮 agent 看到的**续行 prompt** 包含原始目标以及一个"用户在循环中途追加的额外条件"块,**裁判 prompt** 也会被重写,使裁判在判定时必须考虑所有子目标——只有原始目标**和**所有子目标均满足时,目标才会被标记为完成。
|
||||
|
||||
| 命令 | 功能 |
|
||||
|---|---|
|
||||
| `/subgoal <text>` | 向活跃目标追加一个新条件。需要有活跃的 `/goal`。 |
|
||||
| `/subgoal`(无参数) | 显示当前编号子目标列表。 |
|
||||
| `/subgoal remove <N>` | 删除第 N 个子目标(从 1 开始计数)。 |
|
||||
| `/subgoal clear` | 删除所有子目标,但保留原始目标。 |
|
||||
|
||||
子目标与目标一起持久化存储在 `SessionDB.state_meta` 中,因此在 `/resume` 后依然有效。设置新的 `/goal <text>` 会替换目标并清空子目标列表;`/goal clear` 同样如此。
|
||||
|
||||
当你启动一个循环("修复失败的测试")后,中途发现还需要"为刚修复的 bug 添加回归测试"时,使用此功能——`/subgoal add a regression test` 可在不中断运行循环的情况下收紧成功条件。
|
||||
|
||||
## 行为细节
|
||||
|
||||
### 裁判
|
||||
|
||||
每轮结束后,Hermes 会调用一个辅助模型,传入:
|
||||
|
||||
- 持续目标文本
|
||||
- agent 最新的最终回复(最后约 4 KB 文本)
|
||||
- 一个系统 prompt,要求裁判以严格 JSON 格式回复:`{"done": <bool>, "reason": "<one-sentence rationale>"}`
|
||||
|
||||
裁判刻意保守:只有当回复**明确**确认目标已完成、最终交付物已清晰产出,或目标不可达/被阻塞时(视为 DONE 并附带阻塞原因,以免在不可能的任务上消耗预算),才会将目标标记为 `done`。
|
||||
|
||||
### 失败开放语义
|
||||
|
||||
若裁判出错(网络抖动、响应格式错误、辅助客户端不可用),Hermes 将判定视为 `continue`——损坏的裁判不会阻塞进度。**轮次预算**才是真正的兜底机制。
|
||||
|
||||
### 轮次预算
|
||||
|
||||
默认为 20 个续行轮次(`config.yaml` 中的 `goals.max_turns`)。预算耗尽时,Hermes 自动暂停并告知你如何继续:
|
||||
|
||||
```
|
||||
⏸ Goal paused — 20/20 turns used. Use /goal resume to keep going, or /goal clear to stop.
|
||||
```
|
||||
|
||||
`/goal resume` 将计数器重置为零,你可以按可控的块继续推进。
|
||||
|
||||
### 用户消息始终优先
|
||||
|
||||
目标激活期间,你发送的任何真实消息都优先于续行循环。在 CLI 上,你的消息会在队列中的续行消息之前进入 `_pending_input`;在 gateway 上,它以同样的方式通过适配器 FIFO 传递。你的轮次结束后裁判会再次运行——因此如果你的消息恰好完成了目标,裁判会捕获到并停止循环。
|
||||
|
||||
### 运行中安全性(gateway)
|
||||
|
||||
agent 正在运行时,`/goal status`、`/goal pause` 和 `/goal clear` 可以安全执行——它们只操作控制面状态,不会中断当前轮次。在运行中设置**新**目标(`/goal <new text>`)会被拒绝,并提示你先执行 `/stop`,以防旧续行与新目标产生竞争。
|
||||
|
||||
### 持久化
|
||||
|
||||
目标状态存储在 `SessionDB.state_meta` 中,以 `goal:<session_id>` 为键。这意味着 `/resume` 可以从你离开的地方继续——设置目标、合上笔记本、明天回来、执行 `/resume`,目标依然完好如初(活跃、暂停或已完成)。
|
||||
|
||||
### Prompt 缓存
|
||||
|
||||
续行 prompt 是一条以用户角色追加到历史记录中的普通消息。它**不会**修改系统 prompt、切换工具集,也不会以任何使 Hermes prompt 缓存失效的方式改动对话。运行一个 20 轮目标,在缓存层面与 20 轮普通对话的开销相同。
|
||||
|
||||
## 配置
|
||||
|
||||
在 `~/.hermes/config.yaml` 中添加:
|
||||
|
||||
```yaml
|
||||
goals:
|
||||
# Hermes 自动暂停并要求你执行 /goal resume 之前的最大续行轮次。
|
||||
# 默认 20。若想要更紧凑的循环可降低此值;
|
||||
# 长时间重构可适当提高。
|
||||
max_turns: 20
|
||||
```
|
||||
|
||||
### 选择裁判模型
|
||||
|
||||
裁判使用 `goal_judge` 辅助任务。默认情况下,它解析为你的主模型(参见[辅助模型](/user-guide/configuration#auxiliary-models))。若想将裁判路由到廉价快速的模型以降低成本,可添加覆盖配置:
|
||||
|
||||
```yaml
|
||||
auxiliary:
|
||||
goal_judge:
|
||||
provider: openrouter
|
||||
model: google/gemini-3-flash-preview
|
||||
```
|
||||
|
||||
裁判调用量小(约 200 个输出 token),每轮运行一次,因此廉价快速的模型通常是正确选择。
|
||||
|
||||
## 示例演练
|
||||
|
||||
```
|
||||
You: /goal Create four files /tmp/note_{1..4}.txt, one per turn, each containing its number as text
|
||||
|
||||
⊙ Goal set (20-turn budget): Create four files /tmp/note_{1..4}.txt, one per turn, each containing its number as text
|
||||
|
||||
Hermes: Creating /tmp/note_1.txt now.
|
||||
💻 echo "1" > /tmp/note_1.txt (0.1s)
|
||||
I've created /tmp/note_1.txt with the content "1". I'll continue with the remaining files on the next turn as you specified.
|
||||
|
||||
↻ Continuing toward goal (1/20): Only 1 of 4 files has been created; 3 files remain.
|
||||
|
||||
Hermes: [Continuing toward your standing goal]
|
||||
💻 echo "2" > /tmp/note_2.txt (0.1s)
|
||||
Created /tmp/note_2.txt. Two more to go.
|
||||
|
||||
↻ Continuing toward goal (2/20): 2 of 4 files created; 2 remain.
|
||||
|
||||
Hermes: [Continuing toward your standing goal]
|
||||
💻 echo "3" > /tmp/note_3.txt (0.1s)
|
||||
Created /tmp/note_3.txt.
|
||||
|
||||
↻ Continuing toward goal (3/20): 3 of 4 files created; 1 remains.
|
||||
|
||||
Hermes: [Continuing toward your standing goal]
|
||||
💻 echo "4" > /tmp/note_4.txt (0.1s)
|
||||
All four files have been created: /tmp/note_1.txt through /tmp/note_4.txt, each containing its number.
|
||||
|
||||
✓ Goal achieved: All four files were created with the specified content, completing the goal.
|
||||
|
||||
You: _
|
||||
```
|
||||
|
||||
四轮,一次 `/goal` 调用,你零次"继续"提示。
|
||||
|
||||
## 裁判判断有误时
|
||||
|
||||
没有裁判是完美的。需注意两种失败模式:
|
||||
|
||||
**假阴性——目标实际已完成,裁判却说继续。** 轮次预算会兜底。你会看到 `⏸ Goal paused`,可以执行 `/goal clear` 或直接发送新消息。
|
||||
|
||||
**假阳性——工作尚未完成,裁判却说已完成。** 你会看到 `✓ Goal achieved`,但你知道实际情况并非如此。发送后续消息继续,或更精确地重新设置目标:`/goal <更具体的文本>`。裁判的系统 prompt 刻意保守,以使假阳性比假阴性更少出现。
|
||||
|
||||
如果你觉得某次裁判判定不可信,`↻ Continuing toward goal` 或 `✓ Goal achieved` 行中的原因文本会告诉你裁判看到了什么。这通常足以诊断出是目标文本存在歧义,还是模型的回复有问题。
|
||||
|
||||
## 致谢
|
||||
|
||||
`/goal` 是 Hermes 对 **Ralph loop** 模式的实现。面向用户的设计——跨轮次保持目标存活、不达成不停止,以及创建/暂停/恢复/清除控制——由 OpenAI Codex 团队的 Eric Traut 在 [Codex CLI 0.128.0](https://github.com/openai/codex) 中推广并落地。我们的实现是独立的(中央 `CommandDef` 注册表、`SessionDB.state_meta` 持久化、辅助客户端裁判、gateway 侧的适配器 FIFO 续行),但这个想法源自他们。功劳归于应得之人。
|
||||
@ -0,0 +1,233 @@
|
||||
---
|
||||
sidebar_position: 99
|
||||
title: "Honcho Memory"
|
||||
description: "通过 Honcho 实现 AI 原生持久记忆——辩证推理、多智能体用户建模与深度个性化"
|
||||
---
|
||||
|
||||
# Honcho Memory
|
||||
|
||||
[Honcho](https://github.com/plastic-labs/honcho) 是一个 AI 原生记忆后端,在 Hermes 内置记忆系统之上增加了辩证推理(dialectic reasoning)和深度用户建模能力。它不是简单的键值存储,而是通过对对话事后推理,持续维护一个关于用户的动态模型——涵盖其偏好、沟通风格、目标与行为模式。
|
||||
|
||||
:::info Honcho 是一个 Memory Provider 插件
|
||||
Honcho 已集成到 [Memory Providers](./memory-providers.md) 系统中。以下所有功能均可通过统一的 memory provider 接口使用。
|
||||
:::
|
||||
|
||||
## Honcho 新增了什么
|
||||
|
||||
| 能力 | 内置记忆 | Honcho |
|
||||
|-----------|----------------|--------|
|
||||
| 跨会话持久化 | ✔ 基于文件的 MEMORY.md/USER.md | ✔ 服务端 API |
|
||||
| 用户画像 | ✔ 手动 agent 维护 | ✔ 自动辩证推理 |
|
||||
| 会话摘要 | — | ✔ 会话级上下文注入 |
|
||||
| 多 agent 隔离 | — | ✔ 按 peer 分离画像 |
|
||||
| 观察模式 | — | ✔ 统一或定向观察 |
|
||||
| 结论(派生洞察) | — | ✔ 服务端模式推理 |
|
||||
| 历史搜索 | ✔ FTS5 会话搜索 | ✔ 基于结论的语义搜索 |
|
||||
|
||||
**辩证推理**:每轮对话后(由 `dialecticCadence` 控制频率),Honcho 分析交流内容,推导出关于用户偏好、习惯和目标的洞察。这些洞察随时间积累,使 agent 对用户的理解不断加深,超越用户明确表述的内容。辩证过程支持多轮深度(1–3 轮),并自动选择冷启动/热启动 prompt——冷启动查询聚焦于通用用户事实,热启动查询优先处理会话级上下文。
|
||||
|
||||
**会话级上下文**:基础上下文现在包含会话摘要,以及用户表示和 peer 卡片。这使 agent 能感知当前会话中已讨论的内容,减少重复并保持连贯性。
|
||||
|
||||
**多 agent 画像**:当多个 Hermes 实例与同一用户交互时(例如编程助手和个人助手),Honcho 为每个 peer 维护独立画像。每个 peer 只能看到自己的观察和结论,防止上下文交叉污染。
|
||||
|
||||
## 设置
|
||||
|
||||
```bash
|
||||
hermes memory setup # 从 provider 列表中选择 "honcho"
|
||||
```
|
||||
|
||||
或手动配置:
|
||||
|
||||
```yaml
|
||||
# ~/.hermes/config.yaml
|
||||
memory:
|
||||
provider: honcho
|
||||
```
|
||||
|
||||
```bash
|
||||
echo 'HONCHO_API_KEY=***' >> ~/.hermes/.env
|
||||
```
|
||||
|
||||
在 [honcho.dev](https://honcho.dev) 获取 API key。
|
||||
|
||||
## 架构
|
||||
|
||||
### 双层上下文注入
|
||||
|
||||
每轮对话(在 `hybrid` 或 `context` 模式下),Honcho 组装两层上下文注入到系统 prompt 中:
|
||||
|
||||
1. **基础上下文** — 会话摘要、用户表示、用户 peer 卡片、AI 自我表示和 AI 身份卡片。按 `contextCadence` 刷新。这是"这个用户是谁"层。
|
||||
2. **辩证补充** — LLM 合成的关于用户当前状态和需求的推理。按 `dialecticCadence` 刷新。这是"当前最重要的是什么"层。
|
||||
|
||||
两层内容拼接后,按 `contextTokens` 预算截断(如已设置)。
|
||||
|
||||
### 冷启动/热启动 Prompt 选择
|
||||
|
||||
辩证过程自动在两种 prompt 策略之间切换:
|
||||
|
||||
- **冷启动**(尚无基础上下文):通用查询——"这个人是谁?他们的偏好、目标和工作方式是什么?"
|
||||
- **热启动会话**(已有基础上下文):会话级查询——"结合本次会话已讨论的内容,关于该用户哪些上下文最相关?"
|
||||
|
||||
是否已填充基础上下文决定了自动选择哪种策略。
|
||||
|
||||
### 三个正交配置旋钮
|
||||
|
||||
成本和深度由三个独立旋钮控制:
|
||||
|
||||
| 旋钮 | 控制内容 | 默认值 |
|
||||
|------|----------|---------|
|
||||
| `contextCadence` | `context()` API 调用之间的最小轮数(基础层刷新) | `1` |
|
||||
| `dialecticCadence` | `peer.chat()` LLM 调用之间的最小轮数(辩证层刷新) | `2`(推荐 1–5) |
|
||||
| `dialecticDepth` | 每次辩证调用的 `.chat()` 轮数(1–3) | `1` |
|
||||
|
||||
三者相互独立——可以频繁刷新上下文而不频繁运行辩证,也可以低频运行深度多轮辩证。示例:`contextCadence: 1, dialecticCadence: 5, dialecticDepth: 2` 表示每轮刷新基础上下文,每 5 轮运行一次辩证,每次辩证运行 2 轮。
|
||||
|
||||
### 辩证深度(多轮)
|
||||
|
||||
当 `dialecticDepth` > 1 时,每次辩证调用运行多轮 `.chat()`:
|
||||
|
||||
- **第 0 轮**:冷启动或热启动 prompt(见上文)
|
||||
- **第 1 轮**:自我审计——识别初始评估中的不足,并综合近期会话的证据
|
||||
- **第 2 轮**:调和——检查前几轮之间的矛盾,生成最终综合结论
|
||||
|
||||
每轮使用按比例分配的推理级别(早期轮次较轻,主轮次使用基础级别)。通过 `dialecticDepthLevels` 可逐轮覆盖——例如,深度 3 运行时使用 `["minimal", "medium", "high"]`。
|
||||
|
||||
如果前一轮返回了强信号(长且结构化的输出),后续轮次会提前退出,因此深度 3 并不总是意味着 3 次 LLM 调用。
|
||||
|
||||
### 会话启动预热
|
||||
|
||||
会话初始化时,Honcho 在后台以完整配置的 `dialecticDepth` 触发一次辩证调用,并将结果直接传递给第 1 轮的上下文组装。对冷 peer 进行单轮预热通常返回较少内容——多轮深度会在用户开口之前完成审计/调和周期。如果预热在第 1 轮前未完成,第 1 轮将回退到有超时限制的同步调用。
|
||||
|
||||
### 查询自适应推理级别
|
||||
|
||||
自动注入的辩证会根据查询长度调整 `dialecticReasoningLevel`:≥120 字符时 +1 级,≥400 字符时 +2 级,上限为 `reasoningLevelCap`(默认 `"high"`)。设置 `reasoningHeuristic: false` 可禁用此功能,将所有自动调用固定在 `dialecticReasoningLevel`。可用级别:`minimal`、`low`、`medium`、`high`、`max`。
|
||||
|
||||
## 配置选项
|
||||
|
||||
Honcho 在 `~/.honcho/config.json`(全局)或 `$HERMES_HOME/honcho.json`(profile 本地)中配置。设置向导会自动处理。
|
||||
|
||||
### 完整配置参考
|
||||
|
||||
| 键 | 默认值 | 说明 |
|
||||
|-----|---------|-------------|
|
||||
| `contextTokens` | `null`(不限制) | 每轮自动注入上下文的 token 预算。设为整数(如 1200)以限制上限,按词边界截断 |
|
||||
| `contextCadence` | `1` | `context()` API 调用之间的最小轮数(基础层刷新) |
|
||||
| `dialecticCadence` | `2` | `peer.chat()` LLM 调用之间的最小轮数(辩证层)。推荐 1–5。在 `tools` 模式下无关——由模型显式调用 |
|
||||
| `dialecticDepth` | `1` | 每次辩证调用的 `.chat()` 轮数,限制在 1–3 |
|
||||
| `dialecticDepthLevels` | `null` | 可选的每轮推理级别数组,如 `["minimal", "low", "medium"]`,覆盖按比例分配的默认值 |
|
||||
| `dialecticReasoningLevel` | `'low'` | 基础推理级别:`minimal`、`low`、`medium`、`high`、`max` |
|
||||
| `dialecticDynamic` | `true` | 为 `true` 时,模型可通过 tool 参数逐次覆盖推理级别 |
|
||||
| `dialecticMaxChars` | `600` | 注入系统 prompt 的辩证结果最大字符数 |
|
||||
| `recallMode` | `'hybrid'` | `hybrid`(自动注入 + tools)、`context`(仅注入)、`tools`(仅 tools) |
|
||||
| `writeFrequency` | `'async'` | 消息刷新时机:`async`(后台线程)、`turn`(同步)、`session`(会话结束时批量)或整数 N |
|
||||
| `saveMessages` | `true` | 是否将消息持久化到 Honcho API |
|
||||
| `observationMode` | `'directional'` | `directional`(全部开启)或 `unified`(共享池)。可用 `observation` 对象进行精细控制 |
|
||||
| `messageMaxChars` | `25000` | 通过 `add_messages()` 发送的每条消息最大字符数,超出时分块 |
|
||||
| `dialecticMaxInputChars` | `10000` | 传入 `peer.chat()` 的辩证查询输入最大字符数 |
|
||||
| `sessionStrategy` | `'per-directory'` | `per-directory`、`per-repo`、`per-session` 或 `global` |
|
||||
|
||||
**会话策略**控制 Honcho 会话与工作内容的映射方式:
|
||||
- `per-session` — 每次 `hermes` 运行获得一个新会话。干净启动,通过 tools 访问记忆。推荐新用户使用。
|
||||
- `per-directory` — 每个工作目录对应一个 Honcho 会话,上下文跨运行积累。
|
||||
- `per-repo` — 每个 git 仓库对应一个会话。
|
||||
- `global` — 所有目录共用一个会话。
|
||||
|
||||
**Recall 模式**控制记忆如何流入对话:
|
||||
- `hybrid` — 上下文自动注入系统 prompt,同时提供 tools(由模型决定何时查询)。
|
||||
- `context` — 仅自动注入,隐藏 tools。
|
||||
- `tools` — 仅 tools,不自动注入。agent 必须显式调用 `honcho_reasoning`、`honcho_search` 等。
|
||||
|
||||
**各 recall 模式下的设置行为:**
|
||||
|
||||
| 设置 | `hybrid` | `context` | `tools` |
|
||||
|---------|----------|-----------|---------|
|
||||
| `writeFrequency` | 刷新消息 | 刷新消息 | 刷新消息 |
|
||||
| `contextCadence` | 控制基础上下文刷新 | 控制基础上下文刷新 | 无关——不注入 |
|
||||
| `dialecticCadence` | 控制自动 LLM 调用 | 控制自动 LLM 调用 | 无关——由模型显式调用 |
|
||||
| `dialecticDepth` | 每次调用的多轮数 | 每次调用的多轮数 | 无关——由模型显式调用 |
|
||||
| `contextTokens` | 限制注入量 | 限制注入量 | 无关——不注入 |
|
||||
| `dialecticDynamic` | 控制模型覆盖 | 不适用(无 tools) | 控制模型覆盖 |
|
||||
|
||||
在 `tools` 模式下,模型完全自主——它在需要时调用 `honcho_reasoning`,并自行选择 `reasoning_level`。Cadence 和预算设置仅适用于有自动注入的模式(`hybrid` 和 `context`)。
|
||||
|
||||
## 观察模式(定向 vs. 统一)
|
||||
|
||||
Honcho 将对话建模为 peer 之间的消息交换。每个 peer 有两个观察开关,与 Honcho 的 `SessionPeerConfig` 一一对应:
|
||||
|
||||
| 开关 | 效果 |
|
||||
|--------|--------|
|
||||
| `observeMe` | Honcho 根据该 peer 自身的消息构建其表示 |
|
||||
| `observeOthers` | 该 peer 观察另一 peer 的消息(用于跨 peer 推理) |
|
||||
|
||||
两个 peer × 两个开关 = 四个标志。`observationMode` 是快捷预设:
|
||||
|
||||
| 预设 | 用户标志 | AI 标志 | 语义 |
|
||||
|--------|-----------|----------|-----------|
|
||||
| `"directional"`(默认) | me: 开,others: 开 | me: 开,others: 开 | 完全互相观察。启用跨 peer 辩证——"AI 根据用户所说和 AI 回复,对用户了解多少。" |
|
||||
| `"unified"` | me: 开,others: 关 | me: 关,others: 开 | 共享池语义——AI 仅观察用户消息,用户 peer 仅自我建模。单观察者池。 |
|
||||
|
||||
使用显式 `observation` 块覆盖预设,实现逐 peer 精细控制:
|
||||
|
||||
```json
|
||||
"observation": {
|
||||
"user": { "observeMe": true, "observeOthers": true },
|
||||
"ai": { "observeMe": true, "observeOthers": false }
|
||||
}
|
||||
```
|
||||
|
||||
常见配置模式:
|
||||
|
||||
| 意图 | 配置 |
|
||||
|--------|--------|
|
||||
| 完全观察(大多数用户) | `"observationMode": "directional"` |
|
||||
| AI 不应根据自身回复重新建模用户 | `"ai": {"observeMe": true, "observeOthers": false}` |
|
||||
| AI peer 不应通过自我观察更新的强人设 | `"ai": {"observeMe": false, "observeOthers": true}` |
|
||||
|
||||
通过 [Honcho 控制台](https://app.honcho.dev) 设置的服务端开关优先于本地默认值——Hermes 在会话初始化时同步回本地。
|
||||
|
||||
## Tools
|
||||
|
||||
当 Honcho 作为 memory provider 激活时,以下五个 tools 可用:
|
||||
|
||||
| Tool | 用途 |
|
||||
|------|---------|
|
||||
| `honcho_profile` | 读取或更新 peer 卡片——传入 `card`(事实列表)以更新,省略则读取 |
|
||||
| `honcho_search` | 对上下文进行语义搜索——返回原始摘录,不经 LLM 合成 |
|
||||
| `honcho_context` | 完整会话上下文——摘要、表示、卡片、近期消息 |
|
||||
| `honcho_reasoning` | Honcho LLM 合成的答案——传入 `reasoning_level`(minimal/low/medium/high/max)控制深度 |
|
||||
| `honcho_conclude` | 创建或删除结论——传入 `conclusion` 创建,传入 `delete_id` 删除(仅限 PII) |
|
||||
|
||||
## CLI 命令
|
||||
|
||||
`hermes honcho` 子命令**仅在 Honcho 为当前活跃 memory provider 时注册**(`config.yaml` 中 `memory.provider: honcho`)。先运行 `hermes memory setup` 并选择 Honcho,子命令将在下次调用时出现。
|
||||
|
||||
```bash
|
||||
hermes honcho status # 连接状态、配置及关键设置
|
||||
hermes honcho setup # 重定向到 `hermes memory setup`
|
||||
hermes honcho strategy # 查看或设置会话策略(per-session/per-directory/per-repo/global)
|
||||
hermes honcho peer # 查看或更新 peer 名称及辩证推理级别
|
||||
hermes honcho mode # 查看或设置 recall 模式(hybrid/context/tools)
|
||||
hermes honcho tokens # 查看或设置上下文和辩证的 token 预算
|
||||
hermes honcho identity # 初始化或查看 AI peer 的 Honcho 身份
|
||||
hermes honcho sync # 将 Honcho 配置同步到所有现有 profile
|
||||
hermes honcho peers # 查看所有 profile 中的 peer 身份
|
||||
hermes honcho sessions # 列出已知的 Honcho 会话映射
|
||||
hermes honcho map # 将当前目录映射到 Honcho 会话名称
|
||||
hermes honcho enable # 为当前 profile 启用 Honcho
|
||||
hermes honcho disable # 为当前 profile 禁用 Honcho
|
||||
hermes honcho migrate # 从 openclaw-honcho 迁移的分步指南
|
||||
```
|
||||
|
||||
## 从 `hermes honcho` 迁移
|
||||
|
||||
如果你之前使用了独立的 `hermes honcho setup`:
|
||||
|
||||
1. 你的现有配置(`honcho.json` 或 `~/.honcho/config.json`)已保留
|
||||
2. 你的服务端数据(记忆、结论、用户画像)完好无损
|
||||
3. 在 config.yaml 中设置 `memory.provider: honcho` 即可重新激活
|
||||
|
||||
无需重新登录或重新设置。运行 `hermes memory setup` 并选择"honcho"——向导会自动检测你的现有配置。
|
||||
|
||||
## 完整文档
|
||||
|
||||
参见 [Memory Providers — Honcho](./memory-providers.md#honcho) 获取完整参考文档。
|
||||
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user