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
| Funktion | Verktyg | Vad det gör |
|---|---|---|
| Webbsökning | SearXNG | Integritetsrespekterande metasökmotor. Samlar resultat från Google, Bing, DuckDuckGo och dussintals andra. Ingen spårning, inga profiler. |
| Innehållsextraktion | Firecrawl | Omvandlar valfri URL till ren markdown eller HTML. Hanterar JavaScript-rendering, omdirigeringar och botskydd. |
| AI-agent | Hermes Agent | Ramverk 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:
- Anropa SearXNG på
localhost:8081för webbresultat - För valfria URL:er den vill läsa, anropa Firecrawl på
localhost:3002för att extrahera innehållet - 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):
| Container | Minne | CPU (tomgång) |
|---|---|---|
| searxng-core | ~120 MB | försumbar |
| firecrawl-api | ~2,8 GB | ~1% |
| firecrawl-playwright | ~200 MB | försumbar |
| firecrawl-nuq-postgres | ~95 MB | försumbar |
| firecrawl-redis | ~7 MB | försumbar |
| firecrawl-rabbitmq | ~105 MB | ~1,5% |
| Totalt | ~3,3 GB | minimal |
Firecrawls API-container är den tunga biten — Node.js-heapen är förallokerad. På en maskin med mindre RAM, minska mem_limit på api– 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.