---
name: hermes-multi-agent-profiles
description: Creare e configurare profili Hermes separati per colleghi o team — profilo indipendente, SOUL.md personalizzata, .env dedicato, config model/provider, collegamento Telegram gateway.
author: Emma @ Kuki
tags: [hermes, profiles, multi-agent, telegram, gateway, SOUL.md]
---

# Hermes Multi-Agent Profiles

Come creare un agente Hermes separato per un collega, con profilo, anima (SOUL.md), API key e gateway Telegram dedicati.

## Workflow Completo

### 1. Creare il profilo

```bash
hermes profile create <nome-profilo>
```

Questo crea:
- `~/.hermes/profiles/<nome>/` — cartella isolata
- `~/.local/bin/<nome>` — wrapper CLI (es. `superluca chat`)
- 90 skill bundle sincronizzate
- Sottocartelle: `cron/`, `logs/`, `memories/`, `skills/`, `sessions/`, `plans/`, `skins/`, `workspace/`

### 2. Creare la SOUL.md personalizzata

Ogni agente ha una personalità, ruolo e regole definite in `SOUL.md`:

```
~/.hermes/profiles/<nome>/SOUL.md
```

**Cosa includere:**
- Identità e nome dell'agente
- Chi è il suo utente (nome, ruolo, azienda, contesto)
- Obiettivi e missione
- Regole di comportamento e stile di comunicazione
- Base di conoscenza pregressa (progetti già noti)
- Design system aziendale se applicabile (colori, font, layout)
- Rapporti con altri agenti (es. "collabora con Emma")
- Link a risorse condivise (percorsi FAK, server, IP)

### 3. Configurare le API key

Ogni profilo ha il suo `.env` indipendente:

```bash
# Creare .env per il profilo
cat > ~/.hermes/profiles/<nome>/.env << 'EOF'
DEEPSEEK_API_KEY=sk-...
EOF
```

**Attenzione:** Il profilo di default usa `~/.hermes/.env`. I sotto-profili usano `~/.hermes/profiles/<nome>/.env`.

### 4. Verificare il profilo

```bash
# Prova chat veloce (non interattiva)
<nome> chat -q "Chi sei?" --quiet

# Se non ci sono API key, il wrapper può ereditare quelle dalla shell
```

### 5. Collegare Telegram (pairing)

Il gateway Telegram è unico per bot. Raffaele scrive allo stesso bot Telegram.

**Come funziona il pairing:**
1. Raffaele scrive al bot → bot risponde con un codice di attivazione `ABCD1234`
2. Tu (o l'admin) usate `/approve <codice>` per autorizzare Raffaele
3. Raffaele ora può parlare col bot

**Limitazione nota:** Il gateway corrente (al 2026) non ha routing multi-profilo nativo basato sull'utente. Un gateway serve UN profilo. Per multi-profilo serve un secondo gateway su un bot Telegram diverso (crea bot su @BotFather).

### 6. Creare un bot Telegram separato (opzione multi-bot)

Per avere agenti completamente separati con bot diversi:

1. Su Telegram: apri @BotFather → `/newbot`
2. Scegli nome e username (deve finire con `Bot`)
3. @BotFather ti dà un token (es. `123456:ABC-DEF...`)
4. Configura il token nel `.env` del profilo:
   ```bash
   echo 'TELEGRAM_BOT_TOKEN=123456:ABC-DEF...' >> ~/.hermes/profiles/<nome>/.env
   echo 'GATEWAY_ALLOW_ALL_USERS=true' >> ~/.hermes/profiles/<nome>/.env
   ```
   ⚠️ **La variabile d'ambiente deve chiamarsi `TELEGRAM_BOT_TOKEN`** (non `TELEGRAM_TOKEN`). Hermes cerca esattamente questo nome in `gateway/config.py` (riga 1165: `_token_env_names = {Platform.TELEGRAM: "TELEGRAM_BOT_TOKEN", ...}`). Usare `TELEGRAM_TOKEN` fa partire il gateway ma mostra l'errore _"No messaging platforms enabled"_ nei log, perché Hermes non trova il token.

   ⚠️ `GATEWAY_ALLOW_ALL_USERS=true` è necessario per permettere l'accesso al bot senza allowlist. Senza questa flag, il gateway parte ma rifiuta tutti gli utenti.

5. Configura il gateway (seleziona Telegram e conferma):
   ```bash
   # Usa il wrapper del profilo
   <nome> gateway setup
   ```
   Se il setup interattivo dice "Telegram: already configured", conferma con `N` e vai su `Done`.

6. Installa come servizio systemd:
   ```bash
   <nome> gateway install
   # Rispondi Y a "Start now?" e Y a "Start on login/boot?"
   ```
   Il wrapper crea `~/.config/systemd/user/hermes-gateway-<nome>.service`.

7. Avvia il gateway:
   ```bash
   # Systemd (raccomandato, sopravvive al logout)
   systemctl --user start hermes-gateway-<nome>
   
   # Oppure in foreground per debug
   <nome> gateway run --replace
   ```

8. Verifica che funzioni:
   ```bash
   # Stato
   systemctl --user status hermes-gateway-<nome>
   
   # Logs
   journalctl --user -u hermes-gateway-<nome> -f
   
   # Test diretto del bot Telegram (token valido?)
   curl -s "https://api.telegram.org/bot$(grep TELEGRAM_TOKEN ~/.hermes/profiles/<nome>/.env | grep -v '#' | head -1 | cut -d= -f2)/getMe"
   ```

## Verifica Finale

```bash
# Lista profili
hermes profile list

# Test agente
<nome> chat -q "Presentati in 3 parole"

# Stato gateway
<nome> gateway status

# Logs in caso di errori
tail -f ~/.hermes/profiles/<nome>/logs/gateway.log

# Test bot via API Telegram
curl -s "https://api.telegram.org/bot$(grep TELEGRAM_TOKEN ~/.hermes/profiles/<nome>/.env | grep -v '#' | cut -d= -f2)/getMe"
```

## Troubleshooting Gateway

### Gateway crasha con exit code 1/FAILURE (systemd restart loop)

**Sintomo:** `journalctl` mostra `Main process exited, code=exited, status=1/FAILURE` ma systemd riavvia in loop.

**Cause possibili e fix:**

1. **`GATEWAY_ALLOW_ALL_USERS` mancante** — Senza questa flag, il gateway nega tutti gli utenti e crasha. Aggiungi al `.env`:
   ```bash
   echo 'GATEWAY_ALLOW_ALL_USERS=true' >> ~/.hermes/profiles/<nome>/.env
   ```

2. **Variabile `HERMES_HOME` non impostata** — Il servizio systemd legge il `.env` dal profilo, non da `~/.hermes/.env`. Controlla che il `.env` del profilo sia corretto.

3. **Conflitto con vecchio processo** — Se c'è già un gateway in esecuzione per lo stesso profilo:
   ```bash
   systemctl --user stop hermes-gateway-<nome>
   sleep 2
   # Avvia direttamente
   nohup <nome> gateway run --replace > ~/.hermes/profiles/<nome>/logs/gateway.log 2>&1 &
   ```

4. **Log diagnostici** — Controlla sempre:
   ```bash
   tail -f ~/.hermes/profiles/<nome>/logs/gateway.log
   ```
   Cerca `Connected to Telegram (polling mode)` — se non lo vedi, c'è un problema di connessione.

### Token Telegram non valido

```bash
# Test rapido
curl -s "https://api.telegram.org/bot<TOKEN>/getMe"
# Dovrebbe rispondere con {"ok":true,"result":{"id":...,"is_bot":true,...}}
# Se {"ok":false,"error_code":401}, il token è errato
# Se {"ok":false,"error_code":404}, il formato URL è sbagliato (manca 'bot' prima del token)
```

## Note e Pitfall

- **Nessun API key di default:** Il profilo nuovo non ha `.env` — va creato manualmente
- **DeepSeek è gratuito:** Ottimo per profili secondari (nessun costo extra)
- **Gateway singolo per bot:** Un bot Telegram = un profilo attivo. Per più profili servono bot separati con token diversi
- **La SOUL.md è leggibile dal profilo:** Il sistema legge SOUL.md automaticamente e la usa come personalità
- **Wrapper CLI funziona subito:** `<nome> chat` avvia la chat interattiva con quel profilo
- **Il cron e le sessioni sono separate:** Ogni profilo ha il suo scheduler e le sue sessioni
- **`GATEWAY_ALLOW_ALL_USERS=true` è CRITICO** — Senza, il gateway parte ma rifiuta TUTTI gli utenti e va in crash loop
- **La variabile `HERMES_HOME`** in ambiente può interferire — se settata, il gateway potrebbe leggere il profilo sbagliato. Il wrapper CLI gestisce questo automaticamente.

## Tool Lockdown — Creare un Agente Sola Lettura

Per creare un agente che **non può eseguire comandi, né modificare file, né accedere al sistema**, limitare i tools al minimo indispensabile. Questo è il pattern usato per SuperLuca (accesso solo FAK).

### Disabilitare tutti i tools tranne web + clarify

```bash
# Esegui con il wrapper del profilo
<profilo> tools disable terminal
<profilo> tools disable file
<profilo> tools disable cronjob
<profilo> tools disable delegation
<profilo> tools disable session_search
<profilo> tools disable messaging
<profilo> tools disable skills
<profilo> tools disable memory
<profilo> tools disable code_execution
<profilo> tools disable browser
<profilo> tools disable vision
<profilo> tools disable image_gen
<profilo> tools disable computer_use
<profilo> tools disable tts
<profilo> tools disable todo
```

Dopo il comando, solo `web` e `clarify` rimangono attivi.

### Verifica

```bash
<profilo> tools list
# Dovresti vedere:
#   ✓ enabled  web
#   ✓ enabled  clarify
#   ✗ disabled  ...tutto il resto
```

### Come l'agente legge dati locali senza file access

Se l'agente deve consultare dati locali (es. FAK tariff file), **non può** usare `read_file` perché il tool `file` è disabilitato. La soluzione è esporre i dati via HTTP su una **porta separata** e interrogabili via `web_extract`:

**Pattern — FAK API server:**

```python
#!/usr/bin/env python3
"""FAK API — espone dati locali in sola lettura via HTTP."""
import json, os, http.server
from urllib.parse import urlparse, parse_qs

HOME = os.path.expanduser('~/.hermes')
FAK_FILE = os.path.join(HOME, 'fak-comparison', 'comparison_data.json')

class FAKHandler(http.server.BaseHTTPRequestHandler):
    def do_GET(self):
        path = self.path.rstrip('/')
        if path == '/fak':
            self._serve_json(self._load_all())
        elif path.startswith('/fak/search?'):
            q = self._param('q', '').upper()
            self._serve_json(self._search(q))
        elif path.startswith('/fak/carrier/'):
            name = path.split('/')[-1].lower()
            self._serve_json(self._by_carrier(name))
        else:
            self._serve_json({"error": "sconosciuto"})
    
    def _param(self, name, default=''):
        qs = parse_qs(urlparse(self.path).query)
        return qs.get(name, [default])[0]
    
    def _load_all(self):
        with open(FAK_FILE) as f: return json.load(f)
    
    def _serve_json(self, obj):
        self.send_response(200)
        self.send_header('Content-Type', 'application/json')
        self.send_header('Access-Control-Allow-Origin', '*')
        self.end_headers()
        self.wfile.write(json.dumps(obj, indent=2).encode())

if __name__ == '__main__':
    server = http.server.HTTPServer(('0.0.0.0', 8081), FAKHandler)
    server.serve_forever()
```

Avviare in background:
```bash
python3 fak-api.py &
```

Poi l'agente (che ha solo `web`) può fare:
```
web_extract(urls=["http://localhost:8081/fak/search?q=SHANGHAI"])
web_extract(urls=["http://localhost:8081/fak/carrier/evergreen"])
```

**Nota:** Questa API è accessibile solo in locale (localhost). Non è esposta via Funnel o gateway — resta privata.

### systemd service NON carica il .env del profilo automaticamente

**Sintomo:** Il servizio systemd `hermes-gateway-<nome>.service` parte in crash loop con `status=1/FAILURE` e il log dice *"No messaging platforms enabled"* nonostante `TELEGRAM_BOT_TOKEN` sia nel `.env` del profilo.

**Causa:** Il servizio systemd generato da `hermes gateway install` imposta `HERMES_HOME=/home/oem/.hermes/profiles/<nome>` ma **NON carica il file `.env`**. Hermes si aspetta di trovare le variabili d'ambiente nel processo, ma systemd non esegue il dotenv loader automaticamente per il gateway.

**Soluzione:** Aggiungere manualmente `EnvironmentFile` al servizio systemd:

```bash
# Modifica il service file
systemctl --user edit --full hermes-gateway-<nome>

# Sotto la sezione [Service], aggiungi:
EnvironmentFile=/home/oem/.hermes/profiles/<nome>/.env

# Poi ricarica e riavvia
systemctl --user daemon-reload
systemctl --user restart hermes-gateway-<nome>
```

**Alternativa (avvio manuale senza systemd):** Se preferisci non modificare systemd, avvia direttamente:
```bash
nohup <nome> gateway run --replace > ~/.hermes/profiles/<nome>/logs/gateway.log 2>&1 &
```

**Nota:** L'avvio via `nohup` funziona ma non sopravvive al riavvio del server. Per produzione, usa systemd con `EnvironmentFile`.

### `systemctl restart` NON ricarica il .env

Quando fai `systemctl --user restart hermes-gateway-<profilo>`, il servizio **NON rilegge automaticamente** le variabili d'ambiente dal `.env` del profilo se il file è stato modificato dopo il primo avvio.

**Sintomo:** aggiungi `GATEWAY_ALLOW_ALL_USERS=true` al `.env`, fai restart, ma il gateway crasha ancora con lo stesso errore.

**Soluzione:**
```bash
# 1. Ferma il servizio
systemctl --user stop hermes-gateway-<profilo>

# 2. Avvia in foreground per debug (legge il .env fresco)
<profilo> gateway run --replace

# Verifica nei log che il .env sia stato letto:
tail -f ~/.hermes/profiles/<profilo>/logs/gateway.log
# Cerca: ✓ telegram connected

# 3. Se funziona in foreground, riavvia il servizio systemd
systemctl --user restart hermes-gateway-<profilo>
```

**Causa:** Il wrapper CLI `HERMES_HOME=<path>` carica `.env` in memoria all'avvio. `systemctl restart` uccide e riavvia il processo, ma systemd non invalida la cache delle variabili d'ambiente lette dal wrapper. Solo un avvio foreground pulito forza la rilettura.

**Workaround più aggressivo:** Fermare systemd e avviare direttamente con `nohup`:
```bash
systemctl --user stop hermes-gateway-<profilo>
nohup <profilo> gateway run --replace > ~/.hermes/profiles/<profilo>/logs/gateway.log 2>&1 &
```
