[AI] Hermes Agent 후기


1. OpenClaw

이번에도 Docker로 설치할거다.

git clone https://github.com/openclaw/openclaw.git
export OPENCLAW_IMAGE="ghcr.io/openclaw/openclaw:latest"
./docker-setup.sh
Code language: JavaScript (javascript)

서버에서 실행한다.

로컬에서 이미지 빌드하는데 계속 에러가 발생해서 Pre-built된 이미지를 사용한다.

4시간 걸려서 겨우 들어갔는데 뭔가 잘 안된다.

반응이 너무 느리다.

도커로 호스팅하기 사용하기 어렵다.


2. Hermes Agent

OpenClaw도 좋지만 Hermes Agent도 좋아보인다.

사용자의 패턴을 바탕으로 스킬을 만들고 개선시킨다.

따라서 사용할수록 더 똑똑해진다.

OpenClaw와 다르게 AI를 전문적으로 연구하는 곳에서 만들었다.

그래서 python으로 만들어졌다.

사용자와의 상호작용을 강화학습에 사용할 수 있다나?

암튼 OpenClaw의 보안 취약점을 해결하기 위해서 많이 노력한 것이 보인다.

커뮤니티가 Openclaw에 비하면 적고, 참고자료도 적기 때문에 사용하기 더 어렵다.

그래도 일단 사용해보자.

???: H는 묵음이야

H는 묵음이라서 에르메스로 발음하는 듯?

image.png

어림도 없지 한국인은 헤르메스지.


가. 설치

version: "3.8"
services:
  hermes:
    image: nousresearch/hermes-agent:latest
    container_name: hermes
    restart: unless-stopped
    command: gateway run
    volumes:
      - ~/.hermes:/opt/data
    deploy:
      resources:
        limits:
          memory: 4G
          cpus: "2.0"
Code language: JavaScript (javascript)

오랜만에 보는 깔끔한 docker-compose.yml.

역시 근본있는 개발팀에서 만든 물건인가.

와 한번에 성공했다.

사실 당연한 건데 최근 안 그런 것들을 너무 많이 겪어서.

hermes  | Done: 77 new, 0 updated, 0 unchanged. 77 total bundled.
hermes  | WARNING gateway.run: No user allowlists configured. All unauthorized users will be denied. Set GATEWAY_ALLOW_ALL_USERS=true in ~/.hermes/.env to allow open access, or configure platform allowlists (e.g., TELEGRAM_ALLOWED_USERS=your_id).
Code language: JavaScript (javascript)

마지막에 경고가 표시됐다.

allowlists에 아무것도 없어서 그렇다.

~/.hermes/.envdGATEWAY_ALLOW_ALL_USERS=true로 설정하거나 TELEGRAM_ALLOWED_USERS=your_id를 추가하라고 한다. 흠…

공식문서 따라서 추가하면 될 것 같다.

telegram bot을 생성한다.

자신의 telegram ID를 파악한다.

docker exec -it hermes bash
su hermes
/opt/hermes/.venv/bin/hermes setup

세팅 엄청 쉽다. 그냥 따라하면 된다.

%E1%84%89%E1%85%B3%E1%84%8F%E1%85%B3%E1%84%85%E1%85%B5%E1%86%AB%E1%84%89%E1%85%A3%E1%86%BA_2026-04-08_%E1%84%8B%E1%85%A9%E1%84%8C%E1%85%A5%E1%86%AB_12.41.56.png

역사적인 첫인사?

%E1%84%89%E1%85%B3%E1%84%8F%E1%85%B3%E1%84%85%E1%85%B5%E1%86%AB%E1%84%89%E1%85%A3%E1%86%BA_2026-04-08_%E1%84%8B%E1%85%A9%E1%84%8C%E1%85%A5%E1%86%AB_1.01.34.png

%E1%84%89%E1%85%B3%E1%84%8F%E1%85%B3%E1%84%85%E1%85%B5%E1%86%AB%E1%84%89%E1%85%A3%E1%86%BA_2026-04-08_%E1%84%8B%E1%85%A9%E1%84%8C%E1%85%A5%E1%86%AB_1.03.32.png

이후 텔레그램 챗을 start하면 시작된다.

그러면 페이링하라는 메시지를 보내주는데 메시지 전체를 그대로 복사해서 hermes 터미널에 붙여넣었다.

%E1%84%89%E1%85%B3%E1%84%8F%E1%85%B3%E1%84%85%E1%85%B5%E1%86%AB%E1%84%89%E1%85%A3%E1%86%BA_2026-04-08_%E1%84%8B%E1%85%A9%E1%84%8C%E1%85%A5%E1%86%AB_12.59.53.png

알아서 페어링하고 애국가 1절까지 보냈다.

%E1%84%89%E1%85%B3%E1%84%8F%E1%85%B3%E1%84%85%E1%85%B5%E1%86%AB%E1%84%89%E1%85%A3%E1%86%BA_2026-04-08_%E1%84%8B%E1%85%A9%E1%84%8C%E1%85%A5%E1%86%AB_1.06.04.png

착한 녀석

지금까지 설치과정은 OpenClaw와 비교할 수 없을 정도로 쾌적했다.

hermes claw migrate

OpenClaw에서 바로 마이그레이션할 수도 있다.


나. 첫인상

%E1%84%89%E1%85%B3%E1%84%8F%E1%85%B3%E1%84%85%E1%85%B5%E1%86%AB%E1%84%89%E1%85%A3%E1%86%BA_2026-04-07_%E1%84%8B%E1%85%A9%E1%84%92%E1%85%AE_11.51.39.png

기능이 엄청 많다.

우리가 다 줄테니 어디가서 이상한 것 줏어오지 말라는 의미인 것 같다.


다. 모델 선택

OpenRouter에서 Hermes에서 가장 많이 사용되는 Model이 무엇인지 공개하고 있다.


3. Hermes 확장

가. 검색 강화

Hermes가 검색 능력이 조금 아쉬워서 강화해볼려고 한다.

검색은 Agent의 능력에 직접적으로 영향을 주는 요소니깐 특별히 신경쓴다.

SearXNG + Firecrawl은 Agent 검색 능력을 올리기 위해서 많이 사용하는 아키텍처라고 한다.

SearXNG가 검색하고 Firecrawl이 크롤링한다.

둘다 self-hosting이 가능하다.

용량은 4G 정도 더 필요하다.

hermes에게 검색 기능의 약점을 파악하고, 개선하기 위해서 가장 필요한 것이 무엇이냐고 물으니 firecrawl을 달라고 했다.

달라면 줘야지…

name: firecrawl_searxng

x-common-service: &common-service
  image: ghcr.io/firecrawl/firecrawl
  ulimits:
    nofile:
      soft: 65535
      hard: 65535
  extra_hosts:
    - "host.docker.internal:host-gateway"
  logging:
    driver: "json-file"
    options:
      max-size: "10m"
      max-file: "3"
      compress: "true"

x-common-env: &common-env
  REDIS_URL: ${REDIS_URL:-redis://redis:6379}
  REDIS_RATE_LIMIT_URL: ${REDIS_URL:-redis://redis:6379}
  PLAYWRIGHT_MICROSERVICE_URL: ${PLAYWRIGHT_MICROSERVICE_URL:-http://playwright-service:3000/scrape}
  POSTGRES_USER: ${POSTGRES_USER:-postgres}
  POSTGRES_PASSWORD: "${POSTGRES_PASSWORD:-postgres}"
  POSTGRES_DB: ${POSTGRES_DB:-postgres}
  POSTGRES_HOST: ${POSTGRES_HOST:-nuq-postgres}
  POSTGRES_PORT: ${POSTGRES_PORT:-5432}
  USE_DB_AUTHENTICATION: ${USE_DB_AUTHENTICATION:-false}
  NUM_WORKERS_PER_QUEUE: ${NUM_WORKERS_PER_QUEUE:-8}
  CRAWL_CONCURRENT_REQUESTS: ${CRAWL_CONCURRENT_REQUESTS:-10}
  MAX_CONCURRENT_JOBS: ${MAX_CONCURRENT_JOBS:-5}
  BROWSER_POOL_SIZE: ${BROWSER_POOL_SIZE:-5}
  OPENAI_API_KEY: ${OPENAI_API_KEY}
  OPENAI_BASE_URL: ${OPENAI_BASE_URL}
  MODEL_NAME: ${MODEL_NAME}
  MODEL_EMBEDDING_NAME: ${MODEL_EMBEDDING_NAME}
  OLLAMA_BASE_URL: ${OLLAMA_BASE_URL}
  AUTUMN_SECRET_KEY: ${AUTUMN_SECRET_KEY}
  SLACK_WEBHOOK_URL: ${SLACK_WEBHOOK_URL}
  BULL_AUTH_KEY: ${BULL_AUTH_KEY}
  TEST_API_KEY: ${TEST_API_KEY}
  SUPABASE_ANON_TOKEN: ${SUPABASE_ANON_TOKEN}
  SUPABASE_URL: ${SUPABASE_URL}
  SUPABASE_SERVICE_TOKEN: ${SUPABASE_SERVICE_TOKEN}
  SELF_HOSTED_WEBHOOK_URL: ${SELF_HOSTED_WEBHOOK_URL}
  LOGGING_LEVEL: ${LOGGING_LEVEL}
  PROXY_SERVER: ${PROXY_SERVER}
  PROXY_USERNAME: ${PROXY_USERNAME}
  PROXY_PASSWORD: ${PROXY_PASSWORD}
  SEARXNG_ENDPOINT: http://searxng-core:${SEARXNG_PORT:-8080}
  SEARXNG_ENGINES: ${SEARXNG_ENGINES}
  SEARXNG_CATEGORIES: ${SEARXNG_CATEGORIES}

services:
  playwright-service:
    image: ghcr.io/firecrawl/playwright-service:latest
    environment:
      PORT: 3000
      PROXY_SERVER: ${PROXY_SERVER}
      PROXY_USERNAME: ${PROXY_USERNAME}
      PROXY_PASSWORD: ${PROXY_PASSWORD}
      ALLOW_LOCAL_WEBHOOKS: ${ALLOW_LOCAL_WEBHOOKS}
      BLOCK_MEDIA: ${BLOCK_MEDIA}
      MAX_CONCURRENT_PAGES: ${CRAWL_CONCURRENT_REQUESTS:-10}
    cpus: 2.0
    mem_limit: 4G
    memswap_limit: 4G
    logging:
      driver: "json-file"
      options:
        max-size: "10m"
        max-file: "3"
        compress: "true"
    tmpfs:
      - /tmp/.cache:noexec,nosuid,size=1g

  firecrawl_api:
    <<: *common-service
    environment:
      <<: *common-env
      HOST: "0.0.0.0"
      PORT: ${INTERNAL_PORT:-3002}
      EXTRACT_WORKER_PORT: ${EXTRACT_WORKER_PORT:-3004}
      WORKER_PORT: ${WORKER_PORT:-3005}
      NUQ_RABBITMQ_URL: amqp://rabbitmq:5672
      ENV: local
    depends_on:
      redis:
        condition: service_started
      playwright-service:
        condition: service_started
      rabbitmq:
        condition: service_healthy
      nuq-postgres:
        condition: service_started
      searxng-core:
        condition: service_started
    ports:
      - "${FIRECRAWL_PORT:-3002}:${INTERNAL_PORT:-3002}"
    command: node dist/src/harness.js --start-docker
    cpus: 4.0
    mem_limit: 8G
    memswap_limit: 8G

  redis:
    image: redis:alpine
    command: redis-server --bind 0.0.0.0
    logging:
      driver: "json-file"
      options:
        max-size: "5m"
        max-file: "2"
        compress: "true"

  rabbitmq:
    image: rabbitmq:3-management
    command: rabbitmq-server
    healthcheck:
      test: ["CMD", "rabbitmq-diagnostics", "-q", "check_running"]
      interval: 5s
      timeout: 5s
      retries: 3
      start_period: 5s
    logging:
      driver: "json-file"
      options:
        max-size: "5m"
        max-file: "2"
        compress: "true"

  nuq-postgres:
    image: ghcr.io/firecrawl/nuq-postgres:latest
    environment:
      POSTGRES_USER: ${POSTGRES_USER:-postgres}
      POSTGRES_PASSWORD: ${POSTGRES_PASSWORD:-postgres}
      POSTGRES_DB: ${POSTGRES_DB:-postgres}
    logging:
      driver: "json-file"
      options:
        max-size: "10m"
        max-file: "3"
        compress: "true"

  searxng-core:
    container_name: searxng-core
    image: docker.io/searxng/searxng:${SEARXNG_VERSION:-latest}
    restart: always
    ports:
      - ${SEARXNG_HOST:+${SEARXNG_HOST}:}${SEARXNG_PORT:-8080}:${SEARXNG_PORT:-8080}
    env_file: ./.env
    volumes:
      - ./core-config/:/etc/searxng/:Z
      - core-data:/var/cache/searxng/

volumes:
  core-data:
Code language: YAML (yaml)

SEARXNG_ENDPOINT: http://searxng-core:${SEARXNG_PORT:-8080}을 설정해 firecrawl이 SearXNG를 사용하도록 설정할 수 있다.

################################################################
###################### Firecrawl ###############################
################################################################

FIRECRAWL_PORT=3002
USE_DB_AUTHENTICATION=false
POSTGRES_USER=firecrawl
POSTGRES_PASSWORD=firecrawl_password
BULL_AUTH_KEY=change_me

################################################################
###################### SearXNG #################################
################################################################

# SEARXNG_VERSION=latest
# SEARXNG_HOST=[::]
Code language: PHP (php)

설정할 수 있는 환경변수가 많은데 필요한 것만 가져온다.

BULL_AUTH_KEYPOSTGRES_PASSWORD는 바꾸는 게 좋다.

use_default_settings: true

server:
  secret_key: "아무거나_긴_랜덤_문자열로_바꾸기"

search:
  formats:
    - html
    - json
Code language: JavaScript (javascript)

추가로, SearXNG 쪽은 JSON 응답이 가능해야 하므로 core-config/settings.yml을 없다면 만들고 수정해야 한다.

Firecrawl가 JSON format으로 응답하길 요구하기 때문이다.

나머지 SearxNG 설정은 공식값 그대로 뒀다.

settings.yml.envchmod 600으로 접근 권한을 제한한다.

  • 테스트
# Firecrawl 검색
curl -X POST http://localhost:3002/v1/search \
-H'Content-Type: application/json' \
-d'{
    "query": "what is firecrawl?",
    "limit": 3
  }'
Code language: PHP (php)
# SearXNG 직접 확인:
curl "http://localhost:8080/search?q=firecrawl&format=json"
Code language: PHP (php)

테스트도 바로 가능하다.

hermes tools

hermes toolsweb search…로도 설정 가능하다.

그런데 자꾸 오락가락해서 그냥 아래 방법을 썼다. (v0.10.0 기준 이제 잘된다.)

hermes config set FIRECRAWL_API_URL http://파이어크롤도메인주소:3002
Code language: JavaScript (javascript)

실행해서 저장한다.

web:
  backend: firecrawl    # firecrawl | parallel | tavily | exa
Code language: PHP (php)

hermes setup 좀 오락가락해서 직접 /opt/data/config.yaml 파일을 열어서 한줄 추가한다.


나. 생태계


다. WebUI

크게 두가지 프로젝트가 있다.

hermes-workspace는 커스텀된 hermes agent를 사용해야 해서 거부감이 들었다..

hermes-webui를 시도해보았는데 결과적으로 실패했다.

쓰읍 docker로 배포하는 사람들은 근본적으로 사용하기 어렵다.

# Two-container Docker Compose: Hermes Agent + Hermes WebUI
#
# This runs the agent and web UI in separate containers connected via
# shared volumes. The WebUI installs the agent's Python dependencies
# at startup from the shared agent source volume.
#
# Usage:
#   docker compose -f docker-compose.two-container.yml up -d
#
# The agent container runs the gateway (CLI, Telegram, cron, etc.).
# The WebUI container serves the browser interface on port 8787.
# Both share ~/.hermes for config, sessions, and state.

services:
  hermes-agent:
    image: nousresearch/hermes-agent:latest
    container_name: hermes-agent
    volumes:
      # Persist config, state, sessions, skills, memory across restarts
      - hermes-home:/root/.hermes
      # Expose agent source so the WebUI can install dependencies from it
      - hermes-agent-src:/opt/hermes
    environment:
      - HERMES_HOME=/root/.hermes
    restart: unless-stopped

  hermes-webui:
    image: ghcr.io/nesquena/hermes-webui:latest
    container_name: hermes-webui
    depends_on:
      - hermes-agent
    ports:
      - "127.0.0.1:8787:8787"
    volumes:
      # Same hermes home as the agent — shares config, sessions, state
      - hermes-home:/home/hermeswebui/.hermes
      # Agent source mounted where docker_init.bash expects it.
      # At startup the init script runs:
      #   uv pip install /home/hermeswebui/.hermes/hermes-agent
      # which installs the agent and all its Python dependencies.
      - hermes-agent-src:/home/hermeswebui/.hermes/hermes-agent
      # Workspace directory — browse and edit files from the WebUI.
      # Adapt the host path to your project directory.
      - ~/workspace:/workspace
    environment:
      - HERMES_WEBUI_HOST=0.0.0.0
      - HERMES_WEBUI_PORT=8787
      - HERMES_WEBUI_STATE_DIR=/home/hermeswebui/.hermes/webui-mvp
      # Match your host user's UID/GID for correct file permissions
      - WANTED_UID=${UID:-1000}
      - WANTED_GID=${GID:-1000}
      # Optional: set a password for remote access
      # - HERMES_WEBUI_PASSWORD=your-secret-password
    restart: unless-stopped

volumes:
  hermes-home:
  hermes-agent-src:
Code language: PHP (php)

공식 compose 파일에는 hermes-agent-src를 요구한다.

그런데 저거 넣으면 어떻게 hermes 업데이트할 건데용?

아니 소스코드를 마운트하면 나중에 헤르메스 업데이트 못하게 되잖아요…

굳이 하려면 매번 볼륨 지우고 업데이트해야 하는데…

hermes-webui-1  | error: Distribution not found at: file:///home/hermeswebui/.hermes/hermes-agent
hermes-webui-1  | !! ERROR: Failed to install hermes-agent's requirements
hermes-webui-1  | !! Exiting script (ID: 34)
hermes-webui-1  | !! ERROR: subscript failed
hermes-webui-1  | !! Exiting script (ID: 1)
Code language: PHP (php)

안 넣고 해봐도 docker로 배포하는 경우 에러가 생긴다.

docker 대신에 local에 바로 설치한 경우는 잘 동작한다.


라. Profiles

잘 사용하면 멀티 에이전트 구축에 유용할 듯.

작업의 성격에 따라서 기억과 스킬을 분리할 수 있어서 좋은 것 같다.

telegram:
  require_mention: true
  mention_patterns:
    - "^\\\\s*개발자\\\\b"
    - "^\\\\s*모두\\\\b"
Code language: JavaScript (javascript)

~/.hermes/config.yaml에 추가한다.

개발자 또는 모두로 시작하거나, @mention된 메시지에만 반응하도록 할 수 있다.

좋은데?

모두 들리면 야옹

귀엽네.

모두라는 키워드로 시작할 경우 말그대로 모두 반응하게 해둬서 관리하기 편하다.

추가적으로 텔레그램에서 봇간의 대화가 가능하지도 테스트해봐았다.

이론상 아래 조건들아 만족하면 봇들 간의 대화가 가능하다.

  • 텔레그램에서 Bot-to-Bot Communication Mode을 활성화
  • 헤르메스에서 telegram: require_mention을 활성화
  • 헤르메스에 TELEGRAM_ALLOWED_USERS에 서로의 봇 ID 추가
  • 텔레그램에서 group admin으로 설정
telegram bot to bot communication

봇 간의 대화는 된다.

다만, 둘은 사용자와 다른 봇을 구분하지 못한다.

모든 메시지는 사용자가 보낸 것으로 인식한다.

심지어 아직 불안정하다. 됐다가 안됐다가 한다.

헤르메스는 세션을 초기화하면 텔레그램에서 대화하는 법을 까먹는다.

아직 쓸만하지 않다.

그래도 텔레그램 쪽에서 bot-to-bot Communication을 신경쓰고 있는 것 같으니 조금더 관심가지고 기다려볼 필요가 있을 것 같다.


마. 에이전트 협업 툴 검토

없는 시간을 쪼게서 2주간 테스트했다.

아직 쓸만한 걸 발견하지 못했다.

Paperclip은 별로였고, Mission Control을 설치해본다.

바이브코딩 시대의 작품이라서 그런가 뭔가 될 듯 안된다.

미션컨트롤 UI

Paperclip과는 달리 뭔가 되긴 한다.

그런데 토큰 소비량이 왜 178.3k지?

그리고 Hermes의 프로필 수만큼 에이전트를 생성할 수 없다.

요근래 봤던 비슷한 것들 중에 가장 그럴듯하다.

Multica는 아직 초기 단계라 배포도 잘 안된다.

꾸역꾸역 설치했지만… 흐음… 더 지켜봐야 할듯.

그래도 아직 쓸만한 것이 없다.


바. 기억 강화

아직 필요성을 못 느껴서 안 써봤다.

Hermes에는 llm-wiki가 기본으로 설치되어있다.


4. 후기

너무 좋다.

그런데 너무 좋아서 문제다.

벌써부터 토큰 비용이 걱정된다.

이제는 모르던 시절로 돌아갈 수 없다.

전방을 향해 힘찬 함성 10초간 발사


댓글 남기기