본문으로 건너뛰기

도구 런타임

Hermes 도구는 도구 세트로 그룹화되고 중앙 레지스트리/디스패치 시스템을 통해 실행되는 자체 등록 기능입니다.

기본 파일:

  • tools/registry.py
  • model_tools.py
  • toolsets.py
  • tools/terminal_tool.py
  • tools/environments/*

도구 등록 모델

각 도구 모듈은 가져오기 시 registry.register(...)을 호출합니다.

model_tools.py은 도구 모듈 가져오기/검색 및 모델에서 사용되는 스키마 목록 작성을 담당합니다.

registry.register() 작동 방식

tools/의 모든 도구 파일은 모듈 수준에서 registry.register()을 호출하여 자체 선언합니다. 함수 서명은 다음과 같습니다.

registry.register(
name="terminal", # Unique tool name (used in API schemas)
toolset="terminal", # Toolset this tool belongs to
schema={...}, # OpenAI function-calling schema (description, parameters)
handler=handle_terminal, # The function that executes when the tool is called
check_fn=check_terminal, # Optional: returns True/False for availability
requires_env=["SOME_VAR"], # Optional: env vars needed (for UI display)
is_async=False, # Whether the handler is an async coroutine
description="Run commands", # Human-readable description
emoji="💻", # Emoji for spinner/progress display
)

각 호출은 도구 이름으로 키가 지정된 싱글톤 ToolRegistry._tools 사전에 저장된 ToolEntry을 생성합니다. 도구 집합 간에 이름 충돌이 발생하면 경고가 기록되고 나중에 등록이 적용됩니다.

발견: discover_builtin_tools()

model_tools.py을 가져오면 tools/registry.py에서 discover_builtin_tools()을 호출합니다. 이 함수는 AST 구문 분석을 사용하여 모든 tools/*.py 파일을 검색하여 최상위 registry.register() 호출이 포함된 모듈을 찾은 다음 가져옵니다.

# tools/registry.py (simplified)
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 check for top-level 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()은 모델에 대한 스키마 목록을 빌드할 때 각 도구의 check_fn()을 실행합니다.

# Simplified from registry.py
if entry.check_fn:
try:
available = bool(entry.check_fn())
except Exception:
available = False # Exceptions = unavailable
if not available:
continue # Skip this tool entirely

주요 행동:

  • 확인 결과는 호출별로 캐시됩니다 — 여러 도구가 동일한 check_fn을 공유하는 경우 한 번만 실행됩니다.
  • check_fn()의 예외는 "사용할 수 없음"(안전 장치)으로 처리됩니다.
  • is_toolset_available() 메서드는 UI 표시 및 도구 집합 확인에 사용되는 도구 집합의 check_fn이 통과하는지 확인합니다.

도구 세트 해결

도구 세트는 도구 묶음으로 명명됩니다. Hermes는 다음을 통해 문제를 해결합니다.

  • 명시적인 활성화/비활성화 도구 세트 목록
  • 플랫폼 사전 설정(hermes-cli, hermes-telegram 등)
  • 동적 MCP 도구 세트
  • hermes-acp과 같은 선별된 특수 목적 세트

get_tool_definitions()이 도구를 필터링하는 방법

주요 진입점은 model_tools.get_tool_definitions(enabled_toolsets, disabled_toolsets, quiet_mode)입니다:

  1. enabled_toolsets이 제공된 경우 — 해당 도구 세트의 도구만 포함됩니다. 각 도구 세트 이름은 복합 도구 세트를 개별 도구 이름으로 확장하는 resolve_toolset()을 통해 확인됩니다.

  2. disabled_toolsets이 제공된 경우 — 모든 도구 세트로 시작한 다음 비활성화된 도구 세트를 뺍니다.

  3. 둘 다 아니라면 — 알려진 모든 도구 세트를 포함합니다.

  4. 레지스트리 필터링 — 확인된 도구 이름 세트는 registry.get_definitions()에 전달되며, 이는 check_fn 필터링을 적용하고 OpenAI 형식 스키마를 반환합니다.

  5. 동적 스키마 패치 — 필터링 후 execute_codebrowser_navigate 스키마는 실제로 필터링을 통과한 참조 도구에만 동적으로 조정됩니다(사용할 수 없는 도구에 대한 모델 환각 방지).

레거시 도구 세트 이름

_tools 접미사가 있는 이전 도구 세트 이름(예: web_tools, terminal_tools)은 이전 버전과의 호환성을 위해 _LEGACY_TOOLSET_MAP을 통해 최신 도구 이름에 매핑됩니다.

파견

런타임 시 도구는 메모리/todo/세션 검색 처리와 같은 일부 에이전트 수준 도구에 대한 에이전트 루프 예외를 포함하여 중앙 레지스트리를 통해 디스패치됩니다.

디스패치 흐름: 모델 tool_call → 핸들러 실행

모델이 tool_call을 반환하는 경우 흐름은 다음과 같습니다.

Model response with tool_call

run_agent.py agent loop

model_tools.handle_function_call(name, args, task_id, user_task)

[Agent-loop tools?] → handled directly by agent loop (todo, memory, session_search, delegate_task)

[Plugin pre-hook] → invoke_hook("pre_tool_call",...)

registry.dispatch(name, args, **kwargs)

Look up ToolEntry by name

[Async handler?] → bridge via _run_async()
[Sync handler?] → call directly

Return result string (or JSON error)

[Plugin post-hook] → invoke_hook("post_tool_call",...)

줄바꿈 오류

모든 도구 실행은 두 가지 수준의 오류 처리로 래핑됩니다.

  1. registry.dispatch() — 핸들러에서 모든 예외를 포착하고 {"error": "Tool execution failed: ExceptionType: message"}을 JSON으로 반환합니다.

  2. handle_function_call(){"error": "Error executing tool_name: message"}을 반환하는 보조 시도/제외에 전체 디스패치를 래핑합니다.

이렇게 하면 모델이 처리되지 않은 예외가 아닌 항상 올바른 형식의 JSON 문자열을 수신하게 됩니다.

에이전트 루프 도구

에이전트 수준 상태(TodoStore, MemoryStore 등)가 필요하기 때문에 레지스트리 발송 전에 4가지 도구가 차단됩니다.

  • todo — 계획/작업 추적
  • memory — 영구 메모리 쓰기
  • session_search — 교차 세션 회수
  • delegate_task — 하위 에이전트 세션을 생성합니다.

이러한 도구의 스키마는 여전히 레지스트리(get_tool_definitions에 대해)에 등록되어 있지만 디스패치가 어떻게든 직접 도달하면 처리기는 스텁 오류를 반환합니다.

비동기 브리징

도구 핸들러가 비동기인 경우 _run_async()은 이를 동기화 디스패치 경로에 연결합니다.

  • CLI 경로(실행 루프 없음) — 영구 이벤트 루프를 사용하여 캐시된 비동기 클라이언트를 활성 상태로 유지합니다.
  • 게이트웨이 경로(실행 루프)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)
    • 포크 폭탄, 프로세스 종료 등
  2. 탐지 — 터미널 명령을 실행하기 전에 detect_dangerous_command(command)은 모든 패턴을 확인합니다.

  3. 승인 프롬프트 — 일치하는 항목이 발견된 경우:

    • CLI 모드 — 대화형 프롬프트가 사용자에게 영구적으로 승인, 거부 또는 허용하도록 요청합니다.
    • 게이트웨이 모드 — 비동기 승인 콜백이 메시징 플랫폼에 요청을 보냅니다.
    • 스마트 승인 — 선택적으로 보조 LLM은 패턴과 일치하는 위험도가 낮은 명령을 자동 승인할 수 있습니다(예: rm -rf node_modules/은 안전하지만 "재귀 삭제"와 일치함).
  4. 세션 상태 — 승인은 세션별로 추적됩니다. 세션에 대한 "재귀 삭제"를 승인하면 후속 rm -rf 명령이 다시 프롬프트되지 않습니다.

  5. 영구 허용 목록 — "영구적으로 허용" 옵션은 config.yamlcommand_allowlist에 패턴을 기록하여 세션 전반에 걸쳐 지속됩니다.

터미널/런타임 환경

터미널 시스템은 여러 백엔드를 지원합니다.

  • 지역
  • 도커
  • SSH
  • 특이점
  • 모달
  • 데이토나
  • vercel_sandbox

또한 다음을 지원합니다.

  • 작업별 cwd 재정의
  • 백그라운드 프로세스 관리
  • PTY 모드
  • 위험한 명령에 대한 승인 콜백

동시성

도구 호출은 도구 혼합 및 상호 작용 요구 사항에 따라 순차적으로 또는 동시에 실행될 수 있습니다.