Självhostad webbsökning och innehållsextraktion för Hermes Agent — inga API-nycklar, inget kreditkort


De flesta AI-agenter förlitar sig på tredjeparts-API: för webbsökning och innehållsextraktion. Brave Search, Tavily, Firecrawl Cloud — var och en vill ha ditt kreditkort och en API-nyckel. Det är okej för produktion, men för en personlig agent som körs på din egen hårdvara känns det fel. Varför ska din terminal behöva ringa hem till någon annans SaaS bara för att söka på webben?

Så här kör du både webbsökning och innehållsextraktion helt på din egen maskin, med verktyg med öppen källkod under Docker, kopplade till Hermes Agent. Inga konton, ingen fakturering, inga externa beroenden utöver det publika internet.


Stacken

FunktionVerktygVad det gör
WebbsökningSearXNGIntegritetsrespekterande metasökmotor. Samlar resultat från Google, Bing, DuckDuckGo och dussintals andra. Ingen spårning, inga profiler.
InnehållsextraktionFirecrawlOmvandlar valfri URL till ren markdown eller HTML. Hanterar JavaScript-rendering, omdirigeringar och botskydd.
AI-agentHermes AgentRamverk för agenter med öppen källkod. Körs i din terminal, på Telegram, Discord, var som helst.

Alla tre körs på din maskin. Hermes pratar med SearXNG för web_search och Firecrawl för web_extract. Det är allt.


Steg 1: Installera Docker

Om du redan har Docker, hoppa vidare. Annars:

# Ubuntu / Debian
sudo apt update && sudo apt install docker.io docker-compose-v2
sudo usermod -aG docker $USER
# Logga ut och in igen för att gruppmedlemskapet ska träda i kraft

Verifiera:

docker run hello-world

Steg 2: Sätt upp SearXNG

Skapa en katalog och en enkel docker-compose.yml:

mkdir -p ~/searxng && cd ~/searxng
# ~/searxng/docker-compose.yml
services:
  searxng:
    image: searxng/searxng:latest
    container_name: searxng-core
    ports:
      - "8081:8080"
    environment:
      - SEARXNG_BASE_URL=http://localhost:8081
      - SEARXNG_SECRET_KEY=ändra-mig-till-en-slumpmässig-sträng
    volumes:
      - ./data:/etc/searxng
    restart: unless-stopped

Några saker att notera:

  • Portmappning: SearXNG lyssnar på 8080 internt. Vi mappar den till 8081 på värden så att den inte krockar med något annat.
  • Hemlig nyckel: Generera en riktig med openssl rand -hex 32. Den skyddar din instans sessionskakor.
  • Ingen Valkey/Redis: För en enanvändarinstallation behöver du inte den separata cache-tjänsten. Standardinstansen fungerar bra utan den.

Starta den:

docker compose up -d

Verifiera att den fungerar:

curl -s "http://localhost:8081/search?format=json&q=hermes+agent" | \
  python3 -c "import sys,json; d=json.load(sys.stdin); print(f'{len(d[\"results\"])} resultat')"

Du bör se resultat. Öppna http://localhost:8081 i en webbläsare om du vill ha webbgränssnittet.


Steg 3: Sätt upp Firecrawl

Den här är lite knepigare. Firecrawl har fler rörliga delar: PostgreSQL, Redis, RabbitMQ, en Playwright-tjänst för JavaScript-rendering och själva API:et.

Skapa katalogen:

mkdir -p ~/firecrawl && cd ~/firecrawl

Skapa en .env-fil:

# ~/firecrawl/.env
PORT=3002
HOST=0.0.0.0
USE_DB_AUTHENTICATION=false
BULL_AUTH_KEY=någon-slumpmässig-sträng-här

# Postgres — avgörande: måste vara "postgres", inte "firecrawl"
# NuQ-init-skriptet använder pg_cron som bara kan laddas i postgres-databasen
POSTGRES_USER=postgres
POSTGRES_PASSWORD=välj-ett-starkt-lösenord
POSTGRES_DB=postgres

# Konservativa standardvärden för en hemmaburk
NUM_WORKERS_PER_QUEUE=1
MAX_CONCURRENT_JOBS=1
CRAWL_CONCURRENT_REQUESTS=2
BROWSER_POOL_SIZE=1

# Peka Firecrawls egen /search mot SearXNG
SEARXNG_ENDPOINT=http://host.docker.internal:8081

Skapa docker-compose.yaml:

# ~/firecrawl/docker-compose.yaml
name: firecrawl

x-common-service: &common-service
  image: ghcr.io/firecrawl/firecrawl
  ulimits:
    nofile:
      soft: 65535
      hard: 65535
  networks:
    - backend
  extra_hosts:
    - "host.docker.internal:host-gateway"

x-common-env: &common-env
  REDIS_URL: ${REDIS_URL:-redis://redis:6379}
  REDIS_RATE_LIMIT_URL: ${REDIS_RATE_LIMIT_URL:-${REDIS_URL:-redis://redis:6379}}
  PLAYWRIGHT_MICROSERVICE_URL: ${PLAYWRIGHT_MICROSERVICE_URL:-http://playwright-service:3000/scrape}
  NUQ_RABBITMQ_URL: ${NUQ_RABBITMQ_URL:-amqp://rabbitmq:5672}
  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:-2}
  BROWSER_POOL_SIZE: ${BROWSER_POOL_SIZE:-2}
  BULL_AUTH_KEY: ${BULL_AUTH_KEY:-CHANGEME}

services:
  playwright-service:
    image: ghcr.io/firecrawl/playwright-service:latest
    environment:
      PORT: 3000
      BLOCK_MEDIA: ${BLOCK_MEDIA:-true}
    networks:
      - backend
    cpus: 1.0
    mem_limit: 2G
    tmpfs:
      - /tmp/.cache:noexec,nosuid,size=512m

  api:
    <<: *common-service
    environment:
      <<: *common-env
      HOST: "0.0.0.0"
      PORT: ${INTERNAL_PORT:-3002}
      ENV: local
      FLY_MACHINE_ID: local
    depends_on:
      redis:
        condition: service_started
      playwright-service:
        condition: service_started
      nuq-postgres:
        condition: service_healthy
      rabbitmq:
        condition: service_healthy
    ports:
      - "${PORT:-3002}:${INTERNAL_PORT:-3002}"
    command: node dist/src/harness.js --start-docker
    cpus: 2.0
    mem_limit: 6G
    restart: unless-stopped

  redis:
    image: redis:alpine
    networks:
      - backend

  rabbitmq:
    image: rabbitmq:3-alpine
    networks:
      - backend
    healthcheck:
      test: ["CMD", "rabbitmq-diagnostics", "-q", "check_running"]
      interval: 5s
      timeout: 5s
      retries: 5

  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}
    volumes:
      - pgdata:/var/lib/postgresql/data
    networks:
      - backend
    restart: unless-stopped
    healthcheck:
      test: ["CMD-SHELL", "pg_isready -U ${POSTGRES_USER:-postgres}"]
      interval: 5s
      timeout: 5s
      retries: 5

networks:
  backend:
    driver: bridge

volumes:
  pgdata:

Starta den:

docker compose up -d

Detta tar en minut första gången. nuq-postgres-containern kör ett init-skript som skapar jobbkötabellerna, och API:et väntar tills allt är klart.

Verifiera:

curl -s -X POST "http://localhost:3002/v1/scrape" \
  -H "Content-Type: application/json" \
  -d '{"url":"https://example.com","formats":["markdown"]}' | \
  python3 -c "import sys,json; d=json.load(sys.stdin); print(d.get('data',{}).get('markdown','')[:200])"

Du bör se innehållet från example.com renderat som markdown.

pg_cron-fällan

Om du ser Firecrawl i en kraschloop med fel som relation "nuq.queue_scrape" does not exist, misslyckades NuQ PostgreSQL-init-skriptet tyst. Skriptet använder pg_cron, som bara kan laddas i databasen postgres. Om POSTGRES_DB är satt till något annat (t.ex. firecrawl), misslyckas CREATE EXTENSION pg_cron på rad 2 i init-skriptet, och varje CREATE TABLE-sats efter det körs aldrig.

Lösningen: se till att POSTGRES_DB=postgres i din .env. Om du redan har startat med fel värde måste du radera volymen och börja om:

docker compose down -v
# Fixa POSTGRES_DB i .env
docker compose up -d

Steg 4: Koppla Hermes Agent

Hermes har inbyggda plugin-program för både SearXNG och Firecrawl. Du behöver bara två miljövariabler och två konfigurationsnycklar.

4a. Miljövariabler

Lägg till i ~/.hermes/.env:

# SearXNG (självhostad sökning)
SEARXNG_URL=http://localhost:8081

# Firecrawl (självhostad webbextraktion)
FIRECRAWL_API_URL=http://localhost:3002

Alternativt, kör:

echo "" >> ~/.hermes/.env
echo "# SearXNG (självhostad sökning)" >> ~/.hermes/.env
echo "SEARXNG_URL=http://localhost:8081" >> ~/.hermes/.env
echo "" >> ~/.hermes/.env
echo "# Firecrawl (självhostad webbextraktion)" >> ~/.hermes/.env
echo "FIRECRAWL_API_URL=http://localhost:3002" >> ~/.hermes/.env

4b. Konfigurationsnycklar

hermes config set web.search_backend searxng
hermes config set web.extract_backend firecrawl

Det är allt. Hermes upptäcker SearXNG- och Firecrawl-plugin-programmen automatiskt — båda levereras med i standardinstallationen.

4c. Verifiera

Kontrollera din konfiguration:

$ hermes config | grep -A3 '^web:'
web:
  backend: ''
  search_backend: searxng
  extract_backend: firecrawl

Starta en ny Hermes-session (eller /reset i en befintlig) så kommer webbverktygen att använda dina lokala instanser.


Steg 5: Testa det

Starta Hermes och prova en sökning:

$ hermes

> sök efter senaste nyheterna om James Webb-teleskopet

Hermes kommer att:

  1. Anropa SearXNG på localhost:8081 för webbresultat
  2. För valfria URL:er den vill läsa, anropa Firecrawl på localhost:3002 för att extrahera innehållet
  3. Sammanfatta resultaten

Allt detta sker på din maskin. Inga API-nycklar, inga hastighetsbegränsningar, ingen som spårar vad du söker efter.


Resursanvändning

Här är vad denna stack faktiskt förbrukar på min laptop (Zenbook, 8 GB RAM):

ContainerMinneCPU (tomgång)
searxng-core~120 MBförsumbar
firecrawl-api~2,8 GB~1%
firecrawl-playwright~200 MBförsumbar
firecrawl-nuq-postgres~95 MBförsumbar
firecrawl-redis~7 MBförsumbar
firecrawl-rabbitmq~105 MB~1,5%
Totalt~3,3 GBminimal

Firecrawls API-container är den tunga biten — Node.js-heapen är förallokerad. På en maskin med mindre RAM, minska mem_limitapi– och playwright-service-tjänsterna. På denna 8 GB-laptop lämnar stacken ungefär 4 GB för operativsystemet och andra program, vilket är tight men fungerar.


Varför detta spelar roll

De flesta “gratis” AI-verktyg är inte gratis — du betalar med din data. Varje sökfråga går genom någons servrar, loggad och analyserad. Med denna installation:

  • SearXNG tar bort spårningsparametrar, roterar user agents och lagrar aldrig dina frågor. Uppströms sökmotorer ser frågor som kommer från din IP, inte en datamäklare.
  • Firecrawl kör headless-webbläsare lokalt. Webbplatserna du skrapar ser din egen IP som besökare, vilket är så webben är tänkt att fungera.
  • Hermes skickar aldrig din konversation till en sök-API-leverantör. Alla verktygsanropsresultat stannar i din terminal.

Det handlar inte bara om att spara $20/månad på API-krediter. Det handlar om att din agent verkligen är din — som körs på din hårdvara, använder ditt nätverk, och bara svarar till dig.


Felsökning

SearXNG returnerar inga resultat

Vissa sökmotorer hastighetsbegränsar eller blockerar Docker-IP-intervall. Redigera ~/searxng/data/settings.yml (skapas efter första körningen) och aktivera eller inaktivera specifika motorer. Standardinställningarna fungerar för de flesta.

Firecrawl “relation does not exist”-fel

Se pg_cron-fällan ovan. POSTGRES_DB måste vara postgres.

Firecrawl scrape-timeouter

Vissa webbplatser blockerar datacenter-IP-adresser eller kräver specifika headers. Firecrawls Playwright-tjänst hanterar de flesta fall, men aggressivt botskydd (Cloudflare, Datadome) kan fortfarande blockera den. Detta är samma begränsning som molnversionen har.

Hermes kan inte hitta backend-tjänsterna

Se till att miljövariablerna finns i rätt fil (~/.hermes/.env, inte din shell .bashrc). Kör hermes config env-path för att bekräfta sökvägen. Starta om Hermes efter att du har lagt till dem.

Portkonflikter

Om 8081 eller 3002 redan används, ändra värdporten i respektive docker-compose.yml och uppdatera motsvarande miljövariabel.


Rensa upp

För att stoppa allt:

docker compose -f ~/searxng/docker-compose.yml down
docker compose -f ~/firecrawl/docker-compose.yaml down

För att ta bort all data och börja om:

docker compose -f ~/searxng/docker-compose.yml down -v
docker compose -f ~/firecrawl/docker-compose.yaml down -v

Inget kreditkort skadades under skapandet av detta blogginlägg.

, ,

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.