第 18 章
工具调用协议与 40+ 内置工具体系
第18章:工具调用协议与 40+ 内置工具体系
Hermes Agent 的核心竞争力之一,是其开箱即用的 40+ 内置工具。这些工具覆盖了从网络访问、文件操作到代码执行、多媒体处理的全方位能力,构成了 Agent 感知和改变世界的"手脚"。本章将完整梳理工具分类体系、JSON 调用协议、自定义注册方法和权限控制机制。
18.1 工具分类体系
Hermes 的内置工具按功能领域分为六大类:
Hermes 工具体系
├── 系统工具(System) — 终端、进程、环境变量
├── 网络工具(Network) — 搜索、HTTP、抓取
├── 文件工具(File) — 读写、目录、压缩
├── 代码工具(Code) — 执行、调试、分析
├── 多媒体工具(Multimedia) — 图像、音频、视频、PDF
└── 平台工具(Platform) — Git、Docker、数据库、API
18.1.1 完整工具列表
系统工具(8个)
| 工具名 | 功能描述 | 典型用例 |
|---|---|---|
terminal |
执行 shell 命令 | 运行脚本、安装包 |
process_manager |
管理后台进程 | 启动/停止服务 |
env_manager |
读写环境变量 | 配置管理 |
cron_scheduler |
定时任务调度 | 周期性任务 |
system_info |
获取系统信息 | CPU/内存/磁盘监控 |
clipboard |
剪贴板读写 | 内容传递 |
notification |
发送系统通知 | 任务完成提醒 |
sleep |
等待指定时间 | 轮询间隔控制 |
网络工具(9个)
| 工具名 | 功能描述 | 典型用例 |
|---|---|---|
web_search |
多引擎网页搜索 | 信息检索 |
browser_navigate |
浏览器导航(Playwright) | 动态页面访问 |
browser_click |
点击页面元素 | 表单填写、交互 |
browser_screenshot |
截取网页截图 | 视觉验证 |
http_request |
发送 HTTP 请求 | API 调用 |
rss_reader |
读取 RSS 订阅 | 新闻聚合 |
html_extract |
提取网页内容 | 数据抓取 |
link_checker |
检查链接可用性 | 死链检测 |
dns_lookup |
DNS 查询 | 网络诊断 |
文件工具(7个)
| 工具名 | 功能描述 | 典型用例 |
|---|---|---|
file_read |
读取文件内容 | 代码分析、配置读取 |
file_write |
写入文件 | 生成报告、保存结果 |
file_search |
文件模糊搜索 | 定位代码文件 |
directory_list |
列出目录内容 | 项目结构探索 |
file_compress |
压缩/解压文件 | 打包分发 |
file_diff |
对比文件差异 | 代码审查 |
file_watch |
监听文件变化 | 热重载触发 |
代码工具(6个)
| 工具名 | 功能描述 | 典型用例 |
|---|---|---|
code_execute |
执行代码片段 | Python/JS 沙箱执行 |
code_lint |
代码静态分析 | 风格检查 |
code_format |
代码格式化 | black/prettier |
code_test |
运行测试套件 | pytest/jest |
code_debug |
调试代码 | 断点追踪 |
repl |
交互式 REPL | 实验性代码测试 |
多媒体工具(6个)
| 工具名 | 功能描述 | 典型用例 |
|---|---|---|
image_analyze |
图像内容分析(视觉) | OCR、物体识别 |
image_generate |
生成图像 | DALL-E/Stable Diffusion |
image_edit |
编辑图像 | 裁剪、滤镜 |
audio_transcribe |
语音转文字(Whisper) | 会议记录 |
pdf_extract |
PDF 内容提取 | 文档分析 |
chart_generate |
生成图表(Mermaid/matplotlib) | 数据可视化 |
平台工具(8个)
| 工具名 | 功能描述 | 典型用例 |
|---|---|---|
git_operations |
Git 版本控制操作 | 提交、分支、合并 |
docker_manage |
Docker 容器管理 | 部署、测试环境 |
database_query |
数据库查询(SQL/NoSQL) | 数据分析 |
api_call |
调用外部 API | 第三方服务集成 |
email_send |
发送邮件 | 自动化通知 |
calendar_manage |
日历管理 | 会议安排 |
spreadsheet |
表格操作(Excel/CSV) | 数据处理 |
cloud_storage |
云存储(S3/OSS) | 文件上传下载 |
18.2 工具调用的 JSON 协议格式
Hermes 使用严格定义的 JSON 协议进行工具调用,分为"调用请求"和"调用响应"两个对象。
18.2.1 工具调用请求格式
{
"tool_calls": [
{
"id": "call_abc123",
"type": "function",
"function": {
"name": "web_search",
"arguments": {
"query": "Hermes Agent 最新版本特性",
"max_results": 10,
"search_engine": "google",
"date_range": "past_month",
"safe_search": true
}
}
}
]
}
18.2.2 工具调用响应格式
{
"tool_results": [
{
"tool_call_id": "call_abc123",
"role": "tool",
"name": "web_search",
"content": {
"status": "success",
"results": [
{
"title": "Hermes 4: NousResearch's Latest Agent Model",
"url": "https://nousresearch.com/hermes-4",
"snippet": "Hermes 4 introduces improved function calling...",
"score": 0.95
}
],
"total_results": 847000,
"query_time_ms": 342
}
}
]
}
18.2.3 完整的多工具调用序列示例
以下展示了一个涉及三个工具的完整调用序列:
// Step 1: Agent 决策,发起工具调用
{
"role": "assistant",
"content": null,
"tool_calls": [
{
"id": "call_001",
"type": "function",
"function": {
"name": "web_search",
"arguments": {
"query": "Python asyncio best practices 2024",
"max_results": 5
}
}
}
]
}
// Step 2: 工具执行结果返回
{
"role": "tool",
"tool_call_id": "call_001",
"content": "Found 5 results: [...]"
}
// Step 3: Agent 基于结果决定继续调用第二个工具
{
"role": "assistant",
"content": null,
"tool_calls": [
{
"id": "call_002",
"type": "function",
"function": {
"name": "file_write",
"arguments": {
"path": "/output/asyncio_summary.md",
"content": "# Python AsyncIO Best Practices\n\n..."
}
}
}
]
}
// Step 4: 第二个工具结果
{
"role": "tool",
"tool_call_id": "call_002",
"content": "{\"success\": true, \"path\": \"/output/asyncio_summary.md\"}"
}
// Step 5: Agent 最终回复用户
{
"role": "assistant",
"content": "我已完成调研并将结果保存到 /output/asyncio_summary.md 文件中..."
}
18.2.4 工具 Schema 定义格式
每个工具的参数结构遵循 JSON Schema Draft 7:
{
"name": "http_request",
"description": "发送 HTTP 请求并返回响应内容",
"parameters": {
"type": "object",
"properties": {
"url": {
"type": "string",
"description": "目标 URL,必须以 http:// 或 https:// 开头",
"format": "uri"
},
"method": {
"type": "string",
"enum": ["GET", "POST", "PUT", "DELETE", "PATCH", "HEAD"],
"default": "GET",
"description": "HTTP 请求方法"
},
"headers": {
"type": "object",
"description": "请求头键值对",
"additionalProperties": {"type": "string"}
},
"body": {
"type": ["string", "object", "null"],
"description": "请求体(POST/PUT 使用)"
},
"timeout_seconds": {
"type": "integer",
"minimum": 1,
"maximum": 300,
"default": 30,
"description": "请求超时时间(秒)"
},
"follow_redirects": {
"type": "boolean",
"default": true,
"description": "是否自动跟随重定向"
}
},
"required": ["url"]
}
}
18.3 自定义工具注册方法
除了 40+ 内置工具,Hermes 支持通过三种方式注册自定义工具。
18.3.1 方式一:Python 装饰器注册
最简便的注册方式,适合快速原型:
from hermes.tools import tool, ToolResult
@tool(
name="weather_query",
description="查询指定城市的当前天气和未来预报",
tags=["weather", "external-api"],
timeout_seconds=15,
)
def get_weather(
city: str,
days: int = 3,
unit: str = "celsius",
) -> ToolResult:
"""
Args:
city: 城市名称(支持中英文)
days: 预报天数(1-7)
unit: 温度单位,celsius 或 fahrenheit
"""
import requests
api_key = os.environ["WEATHER_API_KEY"]
response = requests.get(
f"https://api.weatherapi.com/v1/forecast.json",
params={
"key": api_key,
"q": city,
"days": days,
"aqi": "no",
},
timeout=10,
)
data = response.json()
return ToolResult(
success=True,
output={
"city": data["location"]["name"],
"current_temp": data["current"]["temp_c"],
"condition": data["current"]["condition"]["text"],
"forecast": [
{
"date": day["date"],
"max": day["day"]["maxtemp_c"],
"min": day["day"]["mintemp_c"],
}
for day in data["forecast"]["forecastday"]
],
}
)
18.3.2 方式二:YAML 配置文件注册
适合 DevOps 流程,无需修改代码:
# tools/custom_tools.yaml
tools:
- name: jira_create_ticket
description: "在 Jira 中创建工单"
type: http_wrapper # 包装 HTTP API 调用
endpoint: "https://your-org.atlassian.net/rest/api/3/issue"
method: POST
auth:
type: basic
username_env: JIRA_USER
password_env: JIRA_API_TOKEN
parameters:
- name: summary
type: string
required: true
description: "工单标题"
- name: description
type: string
required: false
description: "详细描述"
- name: priority
type: string
enum: [Highest, High, Medium, Low, Lowest]
default: Medium
- name: project_key
type: string
required: true
description: "Jira 项目 Key(如 PROJ)"
request_template: |
{
"fields": {
"project": {"key": "{{project_key}}"},
"summary": "{{summary}}",
"description": {"type": "doc", "version": 1, "content": [{"type": "paragraph", "content": [{"type": "text", "text": "{{description}}"}]}]},
"issuetype": {"name": "Task"},
"priority": {"name": "{{priority}}"}
}
}
response_mapping:
success_field: id
output_fields: [id, key, self]
tags: [jira, project-management, ticketing]
timeout_seconds: 30
# 加载自定义工具配置
hermes tool register --config ./tools/custom_tools.yaml
18.3.3 方式三:继承 BaseTool 类
适合复杂工具,需要完整的生命周期控制:
from hermes.tools import BaseTool, ToolResult, ToolContext
from hermes.tools.schema import ToolSchema, ParameterDef
class DatabaseQueryTool(BaseTool):
"""支持多数据库方言的查询工具"""
name = "database_query_advanced"
description = "执行 SQL 或 NoSQL 查询,支持 PostgreSQL/MySQL/MongoDB"
schema = ToolSchema(
parameters=[
ParameterDef("connection_string", str, required=True,
description="数据库连接字符串"),
ParameterDef("query", str, required=True,
description="查询语句(SQL 或 MongoDB JSON)"),
ParameterDef("dialect", str, required=False,
enum=["postgresql", "mysql", "mongodb", "sqlite"],
default="postgresql"),
ParameterDef("max_rows", int, required=False,
default=1000, description="最大返回行数"),
ParameterDef("timeout_ms", int, required=False,
default=30000),
]
)
async def setup(self):
"""工具初始化(连接池预热)"""
self.connection_pool = {}
async def run(self, params: dict, context: ToolContext) -> ToolResult:
dialect = params.get("dialect", "postgresql")
# 获取或创建连接
conn = await self._get_connection(
params["connection_string"], dialect
)
try:
if dialect == "mongodb":
result = await self._mongo_query(conn, params["query"])
else:
result = await self._sql_query(conn, params["query"],
params["max_rows"])
return ToolResult(
success=True,
output={
"rows": result.rows,
"row_count": len(result.rows),
"columns": result.columns,
"query_time_ms": result.execution_time_ms,
}
)
except Exception as e:
return ToolResult(success=False, error=str(e))
async def teardown(self):
"""清理资源"""
for conn in self.connection_pool.values():
await conn.close()
18.4 工具权限控制
Hermes 实现了细粒度的工具权限控制系统,防止 Agent 执行未授权操作。
18.4.1 权限层级
全局权限(Global Permissions)
└── 会话权限(Session Permissions)
└── 工具权限(Tool Permissions)
└── 参数级限制(Parameter-level Restrictions)
18.4.2 权限类型
class ToolPermission(Enum):
# 文件系统权限
FILE_READ = "file:read"
FILE_WRITE = "file:write"
FILE_DELETE = "file:delete"
# 网络权限
NETWORK_FETCH = "network:fetch"
NETWORK_SEARCH = "network:search"
NETWORK_WEBSOCKET = "network:websocket"
# 系统权限
TERMINAL_EXECUTE = "terminal:execute"
PROCESS_MANAGE = "process:manage"
ENV_READ = "env:read"
ENV_WRITE = "env:write"
# 外部服务
EXTERNAL_API = "external:api"
DATABASE_READ = "database:read"
DATABASE_WRITE = "database:write"
# 平台权限
GIT_READ = "git:read"
GIT_WRITE = "git:write"
DOCKER_RUN = "docker:run"
18.4.3 权限配置文件
# hermes_permissions.yaml
profiles:
# 只读研究模式(适合敏感环境)
readonly_research:
allow:
- network:search
- network:fetch
- file:read
- env:read
deny:
- file:write
- file:delete
- terminal:execute
- database:write
# 全功能开发模式
full_developer:
allow: ["*"]
deny:
- env:write # 保护环境变量
restrictions:
file:write:
allowed_paths:
- /workspace/**
- /tmp/**
denied_paths:
- /workspace/.env
- /workspace/secrets/**
terminal:execute:
denied_commands:
- "rm -rf /"
- ":(){ :|:& };:" # Fork bomb
# 生产部署模式
production_deploy:
allow:
- git:read
- docker:run
- network:fetch
deny:
- terminal:execute
- file:delete
approval_required:
- docker:run # 需要人工审批
- database:write
18.4.4 运行时权限检查
# 工具调用前的权限检查流程
class PermissionChecker:
def check(
self,
tool_name: str,
params: dict,
context: AgentContext,
) -> PermissionResult:
# 1. 获取工具所需权限
required = self._get_tool_permissions(tool_name)
# 2. 获取当前会话允许的权限
granted = context.permission_profile.allowed
# 3. 检查是否有被拒绝的权限
for perm in required:
if perm in context.permission_profile.denied:
return PermissionResult(
allowed=False,
reason=f"权限 {perm} 在当前模式下被禁止",
suggestion="请切换到 full_developer 模式"
)
# 4. 参数级限制检查
if tool_name == "file_write":
path = params.get("path", "")
if not self._is_path_allowed(path, context):
return PermissionResult(
allowed=False,
reason=f"路径 {path} 不在允许的写入范围内"
)
# 5. 检查是否需要审批
if perm in context.permission_profile.approval_required:
return PermissionResult(
allowed=False,
requires_approval=True,
approval_prompt=f"工具 {tool_name} 需要人工确认,请审批..."
)
return PermissionResult(allowed=True)
18.5 实战:构建工具调用的监控仪表板
以下示例演示如何收集所有工具调用的遥测数据:
from hermes.tools import ToolRegistry
from hermes.monitoring import TelemetryCollector
import json
class ToolUsageDashboard:
def __init__(self):
self.registry = ToolRegistry()
self.collector = TelemetryCollector()
def get_report(self, period_hours: int = 24) -> dict:
stats = self.collector.query(period_hours)
return {
"period": f"Last {period_hours}h",
"total_calls": stats.total_calls,
"success_rate": f"{stats.success_rate:.1%}",
"top_tools": [
{
"name": t.name,
"calls": t.call_count,
"avg_latency_ms": t.avg_latency,
"error_rate": f"{t.error_rate:.1%}",
}
for t in stats.top_tools[:10]
],
"error_breakdown": stats.errors_by_type,
"token_consumption": {
"total": stats.total_tokens,
"by_tool": stats.tokens_by_tool,
}
}
# 使用示例
dashboard = ToolUsageDashboard()
report = dashboard.get_report(24)
print(json.dumps(report, indent=2, ensure_ascii=False))
18.6 小结
本章系统介绍了 Hermes Agent 的工具体系:
- 六大工具类别:系统/网络/文件/代码/多媒体/平台,共 40+ 工具开箱即用
- JSON 调用协议:标准化的
tool_calls/tool_results格式,与 OpenAI 兼容 - 三种注册方式:装饰器(快速)、YAML 配置(DevOps 友好)、继承基类(全控制)
- 细粒度权限系统:四层权限模型,支持路径级、命令级的精细控制
- 遥测监控:内置工具使用统计,支持构建可观测性仪表板
思考题
-
当 Agent 同时调用多个工具时(并行工具调用),如何保证工具结果的顺序与
tool_call_id正确对应? -
file_write工具的参数级路径限制使用了 glob 模式匹配(/workspace/**)。如果用户请求写入/workspace/../etc/passwd,路径遍历攻击如何被防御? -
自定义工具的 YAML 配置方式不需要重启 Hermes 即可热加载。热加载时,正在执行中的工具调用如何保证不中断?