添加工具
本文介绍如何为 Hermes 添加自定义工具(Tool)和斜杠命令(Slash Command),以及本地开发环境的搭建方法。
工具系统概览
Hermes 的工具系统采用自注册模式:
- 在
tools/目录下创建工具文件 - 在
model_tools.py中添加导入 - 在
toolsets.py中注册到工具集
每个工具处理函数接收参数并返回 JSON 字符串,LLM 通过返回值了解工具执行结果。
添加新工具
第一步:创建工具文件
在 tools/ 目录下创建新文件,例如 tools/my_tool.py:
python
import json
from tools.registry import tool
@tool(
name="my_custom_tool",
description="描述这个工具的功能,LLM 会根据此描述决定何时调用它",
parameters={
"type": "object",
"properties": {
"query": {
"type": "string",
"description": "查询内容"
},
"limit": {
"type": "integer",
"description": "返回结果数量上限",
"default": 10
}
},
"required": ["query"]
}
)
def my_custom_tool(query: str, limit: int = 10) -> str:
"""工具的实际实现逻辑。"""
try:
# 执行工具逻辑
results = do_something(query, limit)
# 所有工具处理函数必须返回 JSON 字符串
return json.dumps({
"success": True,
"results": results,
"count": len(results)
}, ensure_ascii=False)
except Exception as e:
# 错误也以 JSON 字符串形式返回
return json.dumps({
"success": False,
"error": str(e)
}, ensure_ascii=False)
def do_something(query: str, limit: int) -> list:
# 实际业务逻辑
return [f"结果 {i}: {query}" for i in range(limit)]关键约定:
- 所有处理函数必须返回 JSON 字符串(
json.dumps()的结果) - 使用
@tool装饰器自动注册到工具注册表 description字段非常重要,LLM 依靠它决定何时使用此工具parameters遵循 JSON Schema 规范
第二步:在 model_tools.py 中添加导入
打开 hermes_cli/model_tools.py,添加导入语句:
python
# 现有导入...
from tools.file_tools import *
from tools.shell_tools import *
# ... 其他工具 ...
# 添加你的新工具
from tools.my_tool import *第三步:在 toolsets.py 中注册工具集
打开 tools/toolsets.py,将工具添加到合适的工具集,或创建新的工具集:
python
TOOLSETS = {
# 现有工具集...
"filesystem": ["read_file", "write_file", "list_directory"],
"shell": ["run_command", "run_script"],
# 添加到现有工具集
"web": ["fetch_url", "my_custom_tool"], # 如果是网络相关工具
# 或创建新工具集
"my_tools": ["my_custom_tool"],
}工具返回值规范
所有工具处理函数必须返回 JSON 字符串,格式建议:
python
# 成功
return json.dumps({
"success": True,
"data": result_data
}, ensure_ascii=False)
# 失败
return json.dumps({
"success": False,
"error": "错误描述信息"
}, ensure_ascii=False)
# 简单结果(也可以直接返回值)
return json.dumps({"result": "some value"}, ensure_ascii=False)使用 ensure_ascii=False 以正确处理中文等非 ASCII 字符。
添加斜杠命令
斜杠命令(如 /new、/reset)在 hermes_cli/commands.py 中通过 CommandDef 定义:
python
from hermes_cli.commands import CommandDef, register_command
@register_command
class MyCommand(CommandDef):
name = "mycommand" # 命令名(不含斜杠)
aliases = ["mc"] # 别名(可选)
description = "我的自定义命令" # 显示在 /help 中的描述
requires_agent = False # 是否需要 Agent 实例
def execute(self, args: str, context: dict) -> str | None:
"""
执行命令逻辑。
args: 斜杠命令后的参数字符串
context: 当前会话上下文
返回值:显示给用户的字符串,或 None(无输出)
"""
if args:
return f"你输入了参数:{args}"
return "命令已执行"用户在聊天框输入 /mycommand 或 /mc 即可触发。
开发环境搭建
克隆代码库
bash
git clone https://github.com/NousResearch/hermes.git
cd hermes安装 uv(Python 包管理器)
bash
# macOS / Linux
curl -LsSf https://astral.sh/uv/install.sh | sh
# 或使用 pip
pip install uv创建虚拟环境并安装依赖
bash
# 创建虚拟环境
uv venv
# 激活虚拟环境
source .venv/bin/activate # Linux/macOS
# 或
.venv\Scripts\activate # Windows
# 以开发模式安装(包含所有可选依赖)
uv pip install -e ".[all]"运行测试
bash
# 运行所有测试
pytest
# 运行特定测试文件
pytest tests/test_tools.py
# 运行特定测试函数
pytest tests/test_tools.py::test_my_tool
# 显示详细输出
pytest -v
# 生成覆盖率报告
pytest --cov=hermes_cli --cov-report=html本地运行开发版本
bash
# 虚拟环境激活后直接运行
hermes
# 或使用 Python 模块方式
python -m hermes_cli测试你的工具
为新工具编写测试(放在 tests/ 目录):
python
# tests/test_my_tool.py
import json
import pytest
from tools.my_tool import my_custom_tool
def test_my_tool_basic():
result = my_custom_tool(query="测试查询")
data = json.loads(result)
assert data["success"] is True
assert "results" in data
def test_my_tool_with_limit():
result = my_custom_tool(query="测试", limit=5)
data = json.loads(result)
assert data["count"] == 5
def test_my_tool_error_handling():
result = my_custom_tool(query="")
data = json.loads(result)
# 根据你的错误处理逻辑编写断言工具开发最佳实践
描述要清晰准确
description 是 LLM 选择工具的唯一依据,写清楚工具的功能、适用场景和返回内容:
python
# 不好的描述
description = "处理数据"
# 好的描述
description = "在本地文件系统中搜索匹配特定模式的文件,返回文件路径列表。适合在需要找到特定文件时使用。"参数说明要详细
python
"parameters": {
"type": "object",
"properties": {
"pattern": {
"type": "string",
"description": "搜索模式,支持 glob 通配符(如 '*.py', '**/*.json')"
},
"directory": {
"type": "string",
"description": "搜索的起始目录路径,默认为当前工作目录",
"default": "."
}
},
"required": ["pattern"]
}错误处理要完善
工具执行中的任何异常都应被捕获并以结构化 JSON 返回,避免工具调用失败导致整个 Agent 崩溃:
python
try:
result = risky_operation()
return json.dumps({"success": True, "result": result})
except FileNotFoundError as e:
return json.dumps({"success": False, "error": f"文件不存在: {e}"})
except PermissionError as e:
return json.dumps({"success": False, "error": f"权限不足: {e}"})
except Exception as e:
return json.dumps({"success": False, "error": f"未知错误: {e}"})保持工具单一职责
每个工具只做一件事,拆分复杂逻辑为多个工具,让 LLM 组合使用。