乐于分享
好东西不私藏

Hermes Agent 源码-工具系统

Hermes Agent 源码-工具系统

Hermes Agent 源码解析


第 3 讲:工具系统深度解析——ToolRegistry、ToolSet、自注册机制与 check_fn 探针


基于 Hermes Agent v0.16.0 源码 · 2026-06-16



一、工具系统:Agent 的"四肢"


前两讲我们建立了全局架构地图并深入了对话循环。这一讲我们聚焦 Hermes 最活跃的代码区域之一——工具系统(Tool System)。工具是 Agent 与外部世界交互的唯一通道:执行命令、读写文件、搜索网页、操控浏览器、调度定时任务……每一轮对话中,LLM 看到的"工具列表"就是 Agent 的能力边界。



📦 源码仓库


https://github.com/NousResearch/hermes-agent


本地源码:~/.hermes/hermes-agent/


本讲核心文件:


tools/registry.py(589 行)— 注册中心


toolsets.py(912 行)— 工具集定义


model_tools.py(1229 行)— 编排层


tools/tool_search.py(735 行)— 渐进式工具披露



二、整体架构:三层分离设计


Hermes 工具系统采用经典的三层架构,每层职责清晰、互不耦合:



┌──────────────────────────────────────────────────────────────┐
│ 第 1 层:工具实现层 │
│ │
│ tools/terminal_tool.py ← 2684 行,终端执行 │
│ tools/web_tools.py ← 1569 行,网页搜索/抓取 │
│ tools/browser_tool.py ← 浏览器自动化 │
│ tools/file_tools.py ← 文件读写/搜索 │
│ tools/skills_tool.py ← 技能系统 │
│ tools/kanban_tools.py ← 多 Agent 协调 │
│ … (共 33+ 工具模块) │
│ │
│ 每个模块在文件末尾调用 registry.register() 自注册 │
│ 包含: schema (JSON Schema) + handler (处理函数) + check_fn │
├──────────────────────────────────────────────────────────────┤
│ 第 2 层:注册中心 (Registry) │
│ │
│ tools/registry.py — ToolRegistry 单例 │
│ │
│ • ToolEntry: 工具元数据容器 (name, toolset, schema, handler…) │
│ • discover_builtin_tools(): AST 扫描 → 动态导入 │
│ • check_fn TTL cache: 30s 缓存外部探针结果 │
│ • _generation 计数器: 变更感知 + 缓存失效 │
│ • dispatch(): 统一分发入口,async 桥接 │
│ • get_definitions(): 按需产出 OpenAI 格式 schema │
├──────────────────────────────────────────────────────────────┤
│ 第 3 层:编排层 (Orchestration) │
│ │
│ toolsets.py — 工具集定义与解析 │
│ • TOOLSETS dict: 60+ 预定义工具集 (hermes-cli, hermes-…) │
│ • resolve_toolset(): 递归解析,含循环检测 │
│ • _HERMES_CORE_TOOLS: 核心工具白名单 (~55 个) │
│ │
│ model_tools.py — 公共 API + 分发流水线 │
│ • get_tool_definitions(): 过滤 + 缓存 + 动态 schema 注入 │
│ • handle_function_call(): 参数修正 → 中间件 → 分发 → 后处理 │
│ • _run_async(): 线程安全的 async 桥接 │
│ • coerce_tool_args(): LLM 参数类型修正 │
│ • tool_search 集成: 渐进式工具披露 │
└──────────────────────────────────────────────────────────────┘

导入链 (循环导入安全):
tools/registry.py (零外部依赖)

tools/*.py (导入 registry.register)

model_tools.py (导入 registry + 所有工具模块)

run_agent.py, cli.py, gateway/run.py



设计哲学:注册中心不依赖任何工具模块,工具模块只依赖注册中心,编排层最后导入一切。这个"V 字形"导入链彻底消除了循环导入——5000+ 行代码无需 if TYPE_CHECKING 或延迟导入。



三、自注册机制:AST 扫描 + 模块级注册


1. discover_builtin_tools() — 零配置发现


启动时,Hermes 不需要配置文件来声明"有哪些工具"。它通过 AST 静态分析 自动发现:



📄 tools/registry.py (第 29-74 行)


def _is_registry_register_call(node: ast.AST) -> bool:
"""检测 AST 节点是否为 registry.register(...) 调用"""
if not isinstance(node, ast.Expr) or not isinstance(node.value, ast.Call):
return False
func = node.value.func
return (
isinstance(func, ast.Attribute)
and func.attr == "register"
and isinstance(func.value, ast.Name)
and func.value.id == "registry"
)

def _module_registers_tools(module_path: Path) -> bool:
"""只检查模块顶层语句,避免把函数内的 register 调用误判为注册"""
source = module_path.read_text(encoding="utf-8")
tree = ast.parse(source, filename=str(module_path))
return any(_is_registry_register_call(stmt) for stmt in tree.body)

def discover_builtin_tools(tools_dir=None) -> List[str]:
"""扫描 tools/ 目录,AST 确认 → 动态导入 → 自动注册"""
tools_path = Path(tools_dir) if tools_dir else Path(__file__).parent
module_names = [
f"tools.{path.stem}"
for path in sorted(tools_path.glob("*.py"))
if path.name not in {"__init__.py", "registry.py", "mcp_tool.py"}
and _module_registers_tools(path) # ← AST 过滤
]
for mod_name in module_names:
importlib.import_module(mod_name) # ← 导入即注册


关键设计:AST 扫描只检查模块顶层(tree.body),不进入函数内部。这意味着工具可以在函数内调用 registry.register()(如条件注册)而不会被误扫描。只有模块顶层的 registry.register() 才触发自动导入。


2. 工具自注册模式


每个工具模块在文件末尾调用 registry.register() 完成自注册。以 terminal_tool.py 为例:



📄 tools/terminal_tool.py (第 2616-2684 行)


from tools.registry import registry

TERMINAL_SCHEMA = {
"name": "terminal",
"description": "Execute commands on the VM...",
"parameters": {
"type": "object",
"properties": {
"command": {"type": "string", ...},
"background": {"type": "boolean", ...},
"timeout": {"type": "integer", ...},
"workdir": {"type": "string", ...},
"pty": {"type": "boolean", ...},
"notify_on_complete": {"type": "boolean", ...},
"watch_patterns": {"type": "array", ...},
},
"required": ["command"]
}
}

def _handle_terminal(args, **kw):
return terminal_tool(
command=args.get("command"),
background=args.get("background", False),
timeout=args.get("timeout"),
task_id=kw.get("task_id"),
...
)

registry.register(
name="terminal",
toolset="terminal",
schema=TERMINAL_SCHEMA,
handler=_handle_terminal,
check_fn=check_terminal_requirements,
emoji="💻",
max_result_size_chars=100_000,
)


注册签名完整参数:

参数类型说明
namestr唯一标识,LLM 调用时使用
toolsetstr所属工具集名称
schemadictJSON Schema 定义
handlerCallable处理函数,接收 args dict
check_fnCallable|None可用性探针(详见下文)
requires_envlist所需环境变量列表
is_asyncbool是否异步处理
emojistrUI 显示图标
max_result_size_charsint|None结果截断阈值
dynamic_schema_overridesCallable|None运行时动态 schema 覆盖
overridebool允许覆盖已有工具(插件用)

3. ToolEntry — 工具元数据容器


ToolEntry 使用 __slots__ 减少内存占用——每个工具一个实例,长生命周期下值得优化:



📄 tools/registry.py (第 77-106 行)


class ToolEntry:
__slots__ = (
"name", "toolset", "schema", "handler", "check_fn",
"requires_env", "is_async", "description", "emoji",
"max_result_size_chars", "dynamic_schema_overrides",
)

def __init__(self, name, toolset, schema, handler, check_fn,
requires_env, is_async, description, emoji,
max_result_size_chars=None, dynamic_schema_overrides=None):
...
# dynamic_schema_overrides: 零参数 callable,返回 dict 覆盖 schema
# 用于运行时配置依赖字段(如 delegate_task 的描述需反映
# 当前 delegation.max_concurrent_children / max_spawn_depth)

四、ToolRegistry 核心机制


1. 线程安全与快照隔离


MCP 动态刷新会在运行时修改注册表,而对话循环同时在读取工具元数据。Hermes 用 RLock + 快照模式解决:



📄 tools/registry.py (第 151-180 行)


class ToolRegistry:
def __init__(self):
self._tools: Dict[str, ToolEntry] = {}
self._toolset_checks: Dict[str, Callable] = {}
self._toolset_aliases: Dict[str, str] = {}
self._lock = threading.RLock() # ← 读写都加锁
self._generation: int = 0 # ← 变更版本号

def _snapshot_state(self):
"""返回一致的快照 — 锁内复制,锁外消费"""
with self._lock:
return list(self._tools.values()), dict(self._toolset_checks)

def _snapshot_entries(self) -> List[ToolEntry]:
return self._snapshot_state()[0]

def _snapshot_toolset_checks(self) -> Dict[str, Callable]:
return self._snapshot_state()[1]


设计要点:_generation 计数器在每次注册/注销/别名变更时递增。编排层的 get_tool_definitions() 将 generation 纳入缓存键——注册表一变,缓存自动失效,无需显式通知。


2. 注册冲突保护


防止插件/MCP 意外覆盖内置工具:



📄 tools/registry.py (第 234-305 行)


def register(self, name, toolset, schema, handler, check_fn=None, ...):
with self._lock:
existing = self._tools.get(name)
if existing and existing.toolset != toolset:
# MCP ↔ MCP 覆盖允许(服务器刷新/重叠工具名)
both_mcp = existing.toolset.startswith("mcp-") and toolset.startswith("mcp-")
if both_mcp:
logger.debug("MCP overwrite: %s → %s", name, toolset)
elif override:
# 显式 opt-in:插件声明要替换内置工具
logger.info("Tool '%s': override by '%s'", name, toolset)
else:
# 拒绝影子覆盖 — 防止意外替换
logger.error("REJECTED: '%s' shadows existing tool", name)
return # ← 静默丢弃,不崩溃
self._tools[name] = ToolEntry(...)
self._generation += 1



三层保护策略:MCP 内部允许覆盖(刷新场景)→ 显式 override=True 允许替换 → 其他全部拒绝并记录 ERROR。插件开发者必须显式声明意图。



3. check_fn TTL 缓存 — 外部探针优化


check_fn 探针检测外部状态(Docker 守护进程、Modal SDK、Playwright 等)。长生命周期进程中每轮调用是纯浪费——Hermes 用 30 秒 TTL 缓存:



📄 tools/registry.py (第 109-148 行)


_CHECK_FN_TTL_SECONDS = 30.0
_check_fn_cache: Dict[Callable, tuple[float, bool]] = {}
_check_fn_cache_lock = threading.Lock()

def _check_fn_cached(fn: Callable) -> bool:
"""TTL 缓存 check_fn 结果,异常吞掉返回 False"""
now = time.monotonic()
with _check_fn_cache_lock:
cached = _check_fn_cache.get(fn)
if cached is not None:
ts, value = cached
if now - ts < _CHECK_FN_TTL_SECONDS:
return value # ← 缓存命中
try:
value = bool(fn())
except Exception:
value = False # ← 异常 → 工具不可用
with _check_fn_cache_lock:
_check_fn_cache[fn] = (now, value)
return value

def invalidate_check_fn_cache() -> None:
"""配置变更后调用(如 hermes tools enable foo)"""
with _check_fn_cache_lock:
_check_fn_cache.clear()


双层缓存架构:



check_fn 缓存 (双层设计)

第 1 层: _check_fn_cached() — 30s TTL
• 键: check_fn 函数对象
• 值: (timestamp, bool)
• 目的: 跨轮次缓存,减少 Docker/Playwright 探测

第 2 层: get_definitions() 内的 check_results dict
• 键: check_fn 函数对象
• 值: bool (单次调用内)
• 目的: 单次 get_definitions() 内重复探针去重
• 寿命: 函数返回即丢弃


4. dispatch() — 统一分发入口


所有工具调用最终汇聚到 registry.dispatch()



📄 tools/registry.py (第 390-416 行)


def dispatch(self, name: str, args: dict, **kwargs) -> str:
entry = self.get_entry(name)
if not entry:
return json.dumps({"error": f"Unknown tool: {name}"})
try:
if entry.is_async:
from model_tools import _run_async
return _run_async(entry.handler(args, **kwargs))
return entry.handler(args, **kwargs)
except Exception as e:
logger.exception("Tool %s dispatch error: %s", name, e)
raw = f"Tool execution failed: {type(e).__name__}: {e}"
sanitized = _sanitize_tool_error(raw) # ← 剥离结构化标记
return json.dumps({"error": sanitized})


错误处理链路:异常 → 日志记录 → _sanitize_tool_error() 剥离 XML 标签/CDATA/代码围栏 → JSON 序列化返回。防止工具错误文本中的结构化标记混淆 LLM 的后续解析。

五、ToolSet 系统:工具分组与组合


1. TOOLSETS 字典 — 60+ 预定义工具集


toolsets.py 定义了一个庞大的工具集目录:



📄 toolsets.py (第 31-581 行)


# 核心工具白名单 (~55 个工具)
_HERMES_CORE_TOOLS = [
"web_search", "web_extract",
"terminal", "process", "read_terminal",
"read_file", "write_file", "patch", "search_files",
"vision_analyze", "image_generate",
"skills_list", "skill_view", "skill_manage",
"browser_navigate", "browser_snapshot", "browser_click", ...
"todo", "memory", "session_search", "clarify",
"execute_code", "delegate_task", "cronjob", "send_message",
"ha_list_entities", "ha_get_state", ...
"kanban_show", "kanban_list", ...
"computer_use",
]

Webhook 安全工具集 (最小权限)


_HERMES_WEBHOOK_SAFE_TOOLS = [
"web_search", "web_extract", "vision_analyze", "clarify",
]

TOOLSETS = {
"web": {"tools": ["web_search", "web_extract"], "includes": []},
"terminal": {"tools": ["terminal", "process"], "includes": []},
"browser": {"tools": ["browser_navigate", ...], "includes": []},
...
"debugging": {
"tools": ["terminal", "process"],
"includes": ["web", "file"], # ← 组合其他工具集
},
"hermes-cli": {"tools": _HERMES_CORE_TOOLS, "includes": []},
"hermes-gateway": {
"tools": [],
"includes": ["hermes-telegram", "hermes-discord", ...],
},
}


2. resolve_toolset() — 递归解析 + 循环检测


工具集可以包含其他工具集,形成 DAG。解析时需要处理菱形依赖和循环:



📄 toolsets.py (第 636-707 行)


def resolve_toolset(name: str, visited: Set[str] = None) -> List[str]:
if visited is None:
visited = set()

# 特殊别名: "all" / "*" → 解析所有工具集
if name in {"all", "*"}:
all_tools: Set[str] = set()
for toolset_name in get_toolset_names():
resolved = resolve_toolset(toolset_name, visited.copy())
all_tools.update(resolved)
return sorted(all_tools)

# 循环检测 / 菱形依赖
if name in visited:
return [] # ← 安全退出,不报错

visited.add(name)

toolset = get_toolset(name)
if not toolset:
# 自动为插件平台生成工具集 (hermes-<name>)
if name.startswith("hermes-"):
platform_name = name[len("hermes-"):]
if platform_registry.is_registered(platform_name):
return list(set(_HERMES_CORE_TOOLS) | plugin_tools)
return []

# 收集直接工具 + 递归解析 includes
tools = set(toolset.get("tools", []))
for included_name in toolset.get("includes", []):
included_tools = resolve_toolset(included_name, visited)
tools.update(included_tools)
return sorted(tools)


自动插件工具集:hermes-<platform> 不在 TOOLSETS 中但平台已注册时,自动组合 _HERMES_CORE_TOOLS + 插件注册的工具。这让新平台无需修改 core 代码即可获得完整工具集。


3. 工具集拓扑结构



ToolSet 组合拓扑 (部分)

hermes-gateway ← 组合所有平台工具集
├── hermes-telegram ← _HERMES_CORE_TOOLS
├── hermes-discord ← _HERMES_CORE_TOOLS + discord + discord_admin
├── hermes-slack ← _HERMES_CORE_TOOLS
├── hermes-feishu ← _HERMES_CORE_TOOLS + feishu_doc + feishu_drive
├── hermes-webhook ← _HERMES_WEBHOOK_SAFE_TOOLS (最小权限!)
└── ... (18 个平台)

debugging ← 组合场景工具集
├── terminal (terminal, process)
├── web (web_search, web_extract)
└── file (read_file, write_file, patch, search_files)

coding ← 姿态工具集 (posture=True)
└── 自动在代码仓库中选中,不含 messaging/tts/image_gen/spotify
└── 不会被自动恢复到平台配置 (non-configurable)


六、model_tools.py:编排层深度解析


1. get_tool_definitions() — 三级缓存


这是对话循环每轮调用的热路径,Hermes 用三级缓存优化:



📄 model_tools.py (第 243-347 行)


# 缓存键: (enabled_toolsets, disabled_toolsets, generation, config_fp, kanban, skip_assembly)
_tool_defs_cache: Dict[tuple, List[Dict[str, Any]]] = {}
_TOOL_DEFS_CACHE_MAX = 8 # LRU 上限

def get_tool_definitions(enabled_toolsets, disabled_toolsets,
quiet_mode=False, skip_tool_search_assembly=False):
if quiet_mode:
# 配置指纹: mtime_ns + size (config 编辑自动失效缓存)
cfg_fp = (cfg_path.stat().st_mtime_ns, cfg_path.stat().st_size)
cache_key = (
frozenset(enabled_toolsets),
frozenset(disabled_toolsets),
registry._generation, # ← 注册表变更感知
cfg_fp, # ← 配置文件变更感知
bool(os.environ.get("HERMES_KANBAN_TASK")),
bool(skip_tool_search_assembly),
)
cached = _tool_defs_cache.get(cache_key)
if cached is not None:
return list(cached) # ← 浅拷贝,防止下游污染

result = _compute_tool_definitions(...)
# LRU 淘汰 + 缓存
if len(_tool_defs_cache) >= _TOOL_DEFS_CACHE_MAX:
_tool_defs_cache.pop(next(iter(_tool_defs_cache)))
_tool_defs_cache[cache_key] = result
return list(result)



get_tool_definitions() 三级缓存

第 1 层: _tool_defs_cache (LRU, 最多 8 条)
• 键: (toolsets, generation, config_fp, kanban)
• 命中: ~7ms 节省/次
• 失效: 注册表变更 / config 编辑 / kanban 切换
• 仅 quiet_mode=True 时生效 (stdout 副作用路径不缓存)

第 2 层: registry.get_definitions() 内 check_results dict
• 单次调用内 check_fn 去重
• 寿命: 函数返回即丢弃

第 3 层: _check_fn_cached() (30s TTL)
• 跨调用缓存外部探针结果
• 键: check_fn 函数对象


2. 动态 Schema 注入


某些工具的 schema 依赖于运行时状态。Hermes 在 _compute_tool_definitions() 中动态注入:



📄 model_tools.py (第 426-482 行)


# execute_code: 重建 schema,只列出实际可用的沙箱工具
if "execute_code" in available_tool_names:
sandbox_enabled = SANDBOX_ALLOWED_TOOLS & available_tool_names
dynamic_schema = build_execute_code_schema(sandbox_enabled, mode=...)
# 替换静态 schema
for i, td in enumerate(filtered_tools):
if td["function"]["name"] == "execute_code":
filtered_tools[i] = {"type": "function", "function": dynamic_schema}

discord: 根据 bot 权限重建 schema


for discord_tool_name in {"discord": "get_dynamic_schema_core", ...}:
dynamic = schema_fn()
if dynamic is None:
filtered_tools = [t for t in filtered_tools
if t["function"]["name"] != discord_tool_name]
else:
# 替换为动态 schema

browser_navigate: 剥离 web 工具交叉引用


if not {"web_search", "web_extract"} & available_tool_names:
desc = desc.replace("prefer web_search or web_extract", "")


设计意图:模型永远只看到实际可用的工具。如果 web_search 因 API key 缺失被 check_fn 过滤,browser_navigate 的描述中就不会提到它——防止模型幻觉调用不存在的工具。


3. handle_function_call() — 完整分发流水线


工具调用的完整处理链路:



handle_function_call() 分发流水线 (model_tools.py L876-1200)

function_name + function_args

┌─ 参数类型修正 (coerce_tool_args)
│ "42" → 42, "true" → True, 裸字符串 → [字符串]
│ 基于 JSON Schema 声明的类型

├─ Tool Search 桥接分发 (tool_search/tool_describe/tool_call)
│ 桥接工具内联处理,tool_call 解包为真实工具名
│ ↓
├─ Agent 循环工具拦截 (_AGENT_LOOP_TOOLS)
│ todo/memory/session_search/delegate_task → 返回错误提示
│ ↓
├─ pre_tool_call 插件钩子 (可阻断)
│ 返回 block_message → 立即返回错误 + post_tool_call 钩子
│ ↓
├─ ACP 编辑审批 (write_file/patch 前置检查)
│ ↓
├─ 非读/搜工具通知 (重置连续读取计数器)
│ ↓
├─ 分发计时开始 (monotonic clock)
│ ↓
│ ┌─ 审批上下文设置 (tools.approval)
│ │ ↓
│ │ ┌─ tool_request 中间件 (参数重写)
│ │ │ ↓
│ │ │ ┌─ registry.dispatch()
│ │ │ │ → handler(args, **kwargs)
│ │ │ │ → async: _run_async(handler(...))
│ │ │ │ → error: _sanitize_tool_error()
│ │ │ └─ tool_execution 中间件 (结果转换)
│ │ └─ 审批上下文清理
│ ↓
├─ 分发计时结束 (duration_ms)
│ ↓
├─ post_tool_call 插件钩子 (观测)
│ ↓
└─ transform_tool_result 插件钩子 (可修改结果)

返回 result (JSON string)


4. _run_async() — 线程安全的 async 桥接


工具处理器可能是 async 的,但调用方可能是同步的。Hermes 实现了完整的桥接:



📄 model_tools.py (第 47-173 行)


def _run_async(coro):
try:
loop = asyncio.get_running_loop()
except RuntimeError:
loop = None

if loop and loop.is_running():
# 已在 async 上下文中 (gateway/RL env)
# → 启动独立线程 + 独立 event loop
# → 300s 超时 + 主动 cancel pending tasks
pool = ThreadPoolExecutor(max_workers=1)
future = pool.submit(_run_in_worker)
return future.result(timeout=300)

# Worker 线程 (delegate_task 并行执行)
if threading.current_thread() is not threading.main_thread():
worker_loop = _get_worker_loop() # 线程局部持久 loop
return worker_loop.run_until_complete(coro)

# 主线程 (CLI)
tool_loop = _get_tool_loop() # 全局持久 loop
return tool_loop.run_until_complete(coro)


三种路径:


场景策略原因
Gateway (已有运行中的 loop)新线程 + 新 loop不能阻塞事件循环
Worker 线程 (并行工具执行)线程局部持久 loop避免与主线程竞争
CLI (无运行中的 loop)全局持久 loop复用 httpx/AsyncOpenAI 缓存

关键洞察:持久化 loop 而非每次 asyncio.run(),是因为 httpx/AsyncOpenAI 客户端在 GC 时会尝试关闭 transport——如果 loop 已关闭,触发 RuntimeError: Event loop is closed。持久 loop 让缓存客户端保持有效。


5. coerce_tool_args() — LLM 参数类型修正


LLM 经常返回错误类型的参数(字符串代替数字/布尔值)。Hermes 在分发前自动修正:



📄 model_tools.py (第 619-700 行)


def coerce_tool_args(tool_name: str, args: Dict) -> Dict:
schema = registry.get_schema(tool_name)
properties = (schema.get("parameters") or {}).get("properties")

for key, value in list(args.items()):
prop_schema = properties.get(key)
expected = prop_schema.get("type")

# array: 裸值包装为单元素列表
if expected == "array" and not isinstance(value, (list, tuple)):
args[key] = [value] # {"urls": "http://x.com"} → {"urls": ["http://x.com"]}
continue

# 类型修正 (仅当值是字符串时)
if isinstance(value, str):
if expected in {"integer", "number"}:
args[key] = _coerce_number(value, ...) # "42" → 42
elif expected == "boolean":
args[key] = _coerce_boolean(value) # "true" → True
elif expected == "null" and value == "null":
args[key] = None
# union type: 逐个尝试修正

七、Tool Search:渐进式工具披露


当 MCP/插件工具过多时,全量 schema 会占用大量上下文窗口。Hermes 的 Tool Search 机制将非核心工具"折叠"为三个桥接工具:



📄 tools/tool_search.py (第 1-46 行)


TOOL_SEARCH_NAME = "tool_search"
TOOL_DESCRIBE_NAME = "tool_describe"
TOOL_CALL_NAME = "tool_call"

设计约束:


• 核心工具 (toolsets._HERMES_CORE_TOOLS) 永不延迟


• 阈值门控: 可延迟工具 < threshold_pct% 上下文窗口 → 不激活


• 目录无状态: 每次重建,不跨轮次缓存 (避免注册表漂移)


• 桥接工具路由通过 handle_function_call — 所有钩子正常触发


• 显示/轨迹解包: 用户始终看到真实工具名



Tool Search 激活流程

get_tool_definitions() 最后一步:

┌─ 计算可延迟工具 (MCP + 插件, 不含核心工具) 的 token 数
├─ 如果 < threshold_pct (默认 10%) → 不激活, 全量返回
├─ 否则:
│ ├─ 移除可延迟工具的 schema
│ ├─ 注入 3 个桥接工具:
│ │ tool_search: 搜索可用工具 (关键词匹配)
│ │ tool_describe: 查看工具详细 schema
│ │ tool_call: 通过桥接调用底层工具
│ └─ 桥接工具路由回 handle_function_call
│ → tool_call 解包为真实工具名
│ → 所有中间件/钩子对真实工具名生效
│ → 用户/轨迹显示真实工具名 (桥接透明)


八、工具系统完整数据流



工具系统完整数据流

启动时:
tools/registry.py
discover_builtin_tools()
→ AST 扫描 tools/*.py
→ importlib.import_module()
→ 每个模块顶层 registry.register() 触发
→ ToolRegistry._tools 填充 ~55 个工具

model_tools.py 模块加载
discover_builtin_tools() ← 再次触发 (幂等)
discover_plugins() ← 插件工具注册
TOOL_TO_TOOLSET_MAP = registry.get_tool_to_toolset_map()
TOOLSET_REQUIREMENTS = registry.get_toolset_requirements()

每轮对话:
AIAgent.__init__()
→ get_tool_definitions(enabled_toolsets, disabled_toolsets)
→ resolve_toolset() 展开工具集
→ registry.get_definitions() 过滤 + check_fn 探针
→ 动态 schema 注入 (execute_code, discord, browser_navigate)
→ schema 清洗 (llama.cpp 兼容)
→ Tool Search 组装 (如触发)
→ 缓存 (LRU 8 条, 6 维键)
→ self.tools = result

对话循环 (conversation_loop.py):
→ LLM 返回 tool_calls
→ handle_function_call(name, args, ...)
→ coerce_tool_args() 类型修正
→ pre_tool_call 钩子
→ 中间件链
→ registry.dispatch() → handler()
→ post_tool_call 钩子
→ transform_tool_result 钩子
→ 结果追加到 messages
→ 回到 LLM API 调用


九、关键设计模式总结



工具系统设计模式

1. 自注册模式 (Self-Registration)
每个工具模块在顶层调用 registry.register()
AST 扫描自动发现 → 零配置

2. 注册中心单例 (Registry Singleton)
全局 ToolRegistry 实例, 线程安全 (RLock)
_generation 计数器实现缓存自动失效

3. 快照隔离 (Snapshot Isolation)
读操作返回副本, 写操作加锁
MCP 动态刷新不影响正在运行的对话

4. 双层缓存 (Two-Level Caching)
30s TTL (跨轮次) + 单次调用内去重
check_fn 探针结果不重复探测

5. 防御性覆盖 (Defensive Override)
三层: MCP 内部允许 → override=True → 拒绝
防止插件意外替换内置工具

6. 渐进式披露 (Progressive Disclosure)
Tool Search 桥接: 核心工具全量, 非核心按需加载
阈值门控: <10% 上下文窗口时不激活

7. 错误卫生 (Error Hygiene)
_sanitize_tool_error() 剥离结构化标记
防止工具错误文本注入混淆 LLM

8. 类型修正 (Type Coercion)
coerce_tool_args() 自动修正 LLM 参数类型
基于 JSON Schema, 失败时保留原值


十、下一讲预告



第 4 讲:插件系统 — PluginContext API、钩子系统 (pre_llm_call / pre_tool_call / post_tool_call / transform_tool_result)、平台适配器模式。我们将分析 hermes_cli/plugins.pygateway/platforms/base.py,理解 Hermes 如何通过插件扩展能力而不膨胀核心代码。




📚 系列导航: 第 1 讲 · 第 2 讲 · 第 3 讲 (本篇) · 第 4-10 讲 (待续)


源码仓库: github.com/NousResearch/hermes-agent


基本 文件 流程 错误 SQL 调试
  1. 请求信息 : 2026-07-24 12:59:58 HTTP/1.1 GET : https://www.yeyulingfeng.com/a/760175.html
  2. 运行时间 : 0.152900s [ 吞吐率:6.54req/s ] 内存消耗:4,765.37kb 文件加载:145
  3. 缓存信息 : 0 reads,0 writes
  4. 会话信息 : SESSION_ID=04876249a4f17d570c00510725b0f551
  1. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/public/index.php ( 0.79 KB )
  2. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/autoload.php ( 0.17 KB )
  3. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/composer/autoload_real.php ( 2.49 KB )
  4. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/composer/platform_check.php ( 0.90 KB )
  5. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/composer/ClassLoader.php ( 14.03 KB )
  6. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/composer/autoload_static.php ( 6.05 KB )
  7. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-helper/src/helper.php ( 8.34 KB )
  8. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-validate/src/helper.php ( 2.19 KB )
  9. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/ralouphie/getallheaders/src/getallheaders.php ( 1.60 KB )
  10. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-orm/src/helper.php ( 1.47 KB )
  11. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-orm/stubs/load_stubs.php ( 0.16 KB )
  12. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/Exception.php ( 1.69 KB )
  13. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-container/src/Facade.php ( 2.71 KB )
  14. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/symfony/deprecation-contracts/function.php ( 0.99 KB )
  15. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/symfony/polyfill-mbstring/bootstrap.php ( 8.26 KB )
  16. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/symfony/polyfill-mbstring/bootstrap80.php ( 9.78 KB )
  17. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/symfony/var-dumper/Resources/functions/dump.php ( 1.49 KB )
  18. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-dumper/src/helper.php ( 0.18 KB )
  19. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/symfony/var-dumper/VarDumper.php ( 4.30 KB )
  20. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/guzzlehttp/guzzle/src/functions_include.php ( 0.16 KB )
  21. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/guzzlehttp/guzzle/src/functions.php ( 5.54 KB )
  22. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/App.php ( 15.30 KB )
  23. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-container/src/Container.php ( 15.76 KB )
  24. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/psr/container/src/ContainerInterface.php ( 1.02 KB )
  25. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/app/provider.php ( 0.19 KB )
  26. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/Http.php ( 6.04 KB )
  27. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-helper/src/helper/Str.php ( 7.29 KB )
  28. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/Env.php ( 4.68 KB )
  29. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/app/common.php ( 0.03 KB )
  30. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/helper.php ( 18.78 KB )
  31. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/Config.php ( 5.54 KB )
  32. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/config/alipay.php ( 3.59 KB )
  33. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/facade/Env.php ( 1.67 KB )
  34. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/config/app.php ( 0.95 KB )
  35. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/config/cache.php ( 0.78 KB )
  36. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/config/console.php ( 0.23 KB )
  37. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/config/cookie.php ( 0.56 KB )
  38. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/config/database.php ( 2.48 KB )
  39. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/config/filesystem.php ( 0.61 KB )
  40. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/config/lang.php ( 0.91 KB )
  41. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/config/log.php ( 1.35 KB )
  42. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/config/middleware.php ( 0.19 KB )
  43. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/config/route.php ( 1.89 KB )
  44. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/config/session.php ( 0.57 KB )
  45. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/config/trace.php ( 0.34 KB )
  46. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/config/view.php ( 0.82 KB )
  47. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/app/event.php ( 0.25 KB )
  48. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/Event.php ( 7.67 KB )
  49. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/app/service.php ( 0.13 KB )
  50. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/app/AppService.php ( 0.26 KB )
  51. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/Service.php ( 1.64 KB )
  52. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/Lang.php ( 7.35 KB )
  53. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/lang/zh-cn.php ( 13.70 KB )
  54. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/initializer/Error.php ( 3.31 KB )
  55. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/initializer/RegisterService.php ( 1.33 KB )
  56. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/services.php ( 0.14 KB )
  57. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/service/PaginatorService.php ( 1.52 KB )
  58. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/service/ValidateService.php ( 0.99 KB )
  59. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/service/ModelService.php ( 2.04 KB )
  60. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-trace/src/Service.php ( 0.77 KB )
  61. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/Middleware.php ( 6.72 KB )
  62. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/initializer/BootService.php ( 0.77 KB )
  63. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-orm/src/Paginator.php ( 11.86 KB )
  64. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-validate/src/Validate.php ( 63.20 KB )
  65. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-orm/src/Model.php ( 23.55 KB )
  66. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-orm/src/model/concern/Attribute.php ( 21.05 KB )
  67. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-orm/src/model/concern/AutoWriteData.php ( 4.21 KB )
  68. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-orm/src/model/concern/Conversion.php ( 6.44 KB )
  69. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-orm/src/model/concern/DbConnect.php ( 5.16 KB )
  70. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-orm/src/model/concern/ModelEvent.php ( 2.33 KB )
  71. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-orm/src/model/concern/RelationShip.php ( 28.29 KB )
  72. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-helper/src/contract/Arrayable.php ( 0.09 KB )
  73. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-helper/src/contract/Jsonable.php ( 0.13 KB )
  74. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-orm/src/model/contract/Modelable.php ( 0.09 KB )
  75. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/Db.php ( 2.88 KB )
  76. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-orm/src/DbManager.php ( 8.52 KB )
  77. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/Log.php ( 6.28 KB )
  78. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/Manager.php ( 3.92 KB )
  79. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/psr/log/src/LoggerTrait.php ( 2.69 KB )
  80. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/psr/log/src/LoggerInterface.php ( 2.71 KB )
  81. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/Cache.php ( 4.92 KB )
  82. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/psr/simple-cache/src/CacheInterface.php ( 4.71 KB )
  83. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-helper/src/helper/Arr.php ( 16.63 KB )
  84. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/cache/driver/File.php ( 7.84 KB )
  85. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/cache/Driver.php ( 9.03 KB )
  86. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/contract/CacheHandlerInterface.php ( 1.99 KB )
  87. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/app/Request.php ( 0.09 KB )
  88. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/Request.php ( 55.78 KB )
  89. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/app/middleware.php ( 0.25 KB )
  90. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/Pipeline.php ( 2.61 KB )
  91. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-trace/src/TraceDebug.php ( 3.40 KB )
  92. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/middleware/SessionInit.php ( 1.94 KB )
  93. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/Session.php ( 1.80 KB )
  94. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/session/driver/File.php ( 6.27 KB )
  95. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/contract/SessionHandlerInterface.php ( 0.87 KB )
  96. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/session/Store.php ( 7.12 KB )
  97. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/Route.php ( 23.73 KB )
  98. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/route/RuleName.php ( 5.75 KB )
  99. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/route/Domain.php ( 2.53 KB )
  100. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/route/RuleGroup.php ( 22.43 KB )
  101. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/route/Rule.php ( 26.95 KB )
  102. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/route/RuleItem.php ( 9.78 KB )
  103. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/route/app.php ( 3.94 KB )
  104. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/facade/Route.php ( 4.70 KB )
  105. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/route/dispatch/Controller.php ( 4.74 KB )
  106. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/route/Dispatch.php ( 10.44 KB )
  107. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/app/controller/Index.php ( 9.87 KB )
  108. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/app/BaseController.php ( 2.05 KB )
  109. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-orm/src/facade/Db.php ( 0.93 KB )
  110. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-orm/src/db/connector/Mysql.php ( 5.44 KB )
  111. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-orm/src/db/PDOConnection.php ( 52.47 KB )
  112. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-orm/src/db/Connection.php ( 8.39 KB )
  113. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-orm/src/db/ConnectionInterface.php ( 4.57 KB )
  114. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-orm/src/db/builder/Mysql.php ( 16.58 KB )
  115. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-orm/src/db/Builder.php ( 24.06 KB )
  116. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-orm/src/db/BaseBuilder.php ( 27.50 KB )
  117. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-orm/src/db/Query.php ( 15.71 KB )
  118. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-orm/src/db/BaseQuery.php ( 45.13 KB )
  119. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-orm/src/db/concern/TimeFieldQuery.php ( 7.43 KB )
  120. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-orm/src/db/concern/AggregateQuery.php ( 3.26 KB )
  121. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-orm/src/db/concern/ModelRelationQuery.php ( 20.07 KB )
  122. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-orm/src/db/concern/ParamsBind.php ( 3.66 KB )
  123. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-orm/src/db/concern/ResultOperation.php ( 7.01 KB )
  124. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-orm/src/db/concern/WhereQuery.php ( 19.37 KB )
  125. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-orm/src/db/concern/JoinAndViewQuery.php ( 7.11 KB )
  126. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-orm/src/db/concern/TableFieldInfo.php ( 2.63 KB )
  127. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-orm/src/db/concern/Transaction.php ( 2.77 KB )
  128. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/log/driver/File.php ( 5.96 KB )
  129. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/contract/LogHandlerInterface.php ( 0.86 KB )
  130. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/log/Channel.php ( 3.89 KB )
  131. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/event/LogRecord.php ( 1.02 KB )
  132. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-helper/src/Collection.php ( 16.47 KB )
  133. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/facade/View.php ( 1.70 KB )
  134. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/View.php ( 4.39 KB )
  135. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/app/controller/Es.php ( 3.30 KB )
  136. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/Response.php ( 8.81 KB )
  137. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/response/View.php ( 3.29 KB )
  138. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/Cookie.php ( 6.06 KB )
  139. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-view/src/Think.php ( 8.38 KB )
  140. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/contract/TemplateHandlerInterface.php ( 1.60 KB )
  141. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-template/src/Template.php ( 46.61 KB )
  142. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-template/src/template/driver/File.php ( 2.41 KB )
  143. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-template/src/template/contract/DriverInterface.php ( 0.86 KB )
  144. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/runtime/temp/c935550e3e8a3a4c27dd94e439343fdf.php ( 31.50 KB )
  145. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-trace/src/Html.php ( 4.42 KB )
  1. CONNECT:[ UseTime:0.000612s ] mysql:host=127.0.0.1;port=3306;dbname=wenku;charset=utf8mb4
  2. SHOW FULL COLUMNS FROM `fenlei` [ RunTime:0.000843s ]
  3. SELECT * FROM `fenlei` WHERE `fid` = 0 [ RunTime:0.022860s ]
  4. SELECT * FROM `fenlei` WHERE `fid` = 63 [ RunTime:0.006842s ]
  5. SHOW FULL COLUMNS FROM `set` [ RunTime:0.000700s ]
  6. SELECT * FROM `set` [ RunTime:0.000213s ]
  7. SHOW FULL COLUMNS FROM `article` [ RunTime:0.000596s ]
  8. SELECT * FROM `article` WHERE `id` = 760175 LIMIT 1 [ RunTime:0.000568s ]
  9. UPDATE `article` SET `lasttime` = 1784869198 WHERE `id` = 760175 [ RunTime:0.008443s ]
  10. SELECT * FROM `fenlei` WHERE `id` = 64 LIMIT 1 [ RunTime:0.000258s ]
  11. SELECT * FROM `article` WHERE `id` < 760175 ORDER BY `id` DESC LIMIT 1 [ RunTime:0.000457s ]
  12. SELECT * FROM `article` WHERE `id` > 760175 ORDER BY `id` ASC LIMIT 1 [ RunTime:0.012637s ]
  13. SELECT * FROM `article` WHERE `id` < 760175 ORDER BY `id` DESC LIMIT 10 [ RunTime:0.001939s ]
  14. SELECT * FROM `article` WHERE `id` < 760175 ORDER BY `id` DESC LIMIT 10,10 [ RunTime:0.001748s ]
  15. SELECT * FROM `article` WHERE `id` < 760175 ORDER BY `id` DESC LIMIT 20,10 [ RunTime:0.001714s ]
0.156380s