Aggiunta spec design OpusAgent
Servizio self-hosted su VPS per interazione programmatica con modelli Anthropic tramite Claude Code SDK e abbonamento Pro/Max. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,668 @@
|
||||
# OpusAgent — Design Specification
|
||||
|
||||
> Servizio self-hosted che consente l'interazione programmatica con i modelli
|
||||
> Anthropic attraverso Claude Code SDK, sfruttando un abbonamento Claude
|
||||
> Pro/Max esistente. Il servizio espone un'API REST asincrona con gestione
|
||||
> degli argomenti, coda di richieste sequenziale, rate limiting e generazione
|
||||
> automatica di riassunti.
|
||||
|
||||
---
|
||||
|
||||
## 1. Panoramica
|
||||
|
||||
OpusAgent è un servizio a due componenti, deployato via Docker su una VPS
|
||||
Hostinger, che funge da intermediario tra software client e i modelli
|
||||
Anthropic. Invece di richiedere API key a consumo, il servizio sfrutta
|
||||
un'istanza autenticata di Claude Code che opera con l'abbonamento Pro/Max
|
||||
dell'utente.
|
||||
|
||||
### Obiettivi
|
||||
|
||||
- Consentire ai propri software di comunicare con Claude in modo programmatico
|
||||
- Utilizzare l'abbonamento Claude Pro/Max esistente, evitando costi API a consumo
|
||||
- Organizzare le interazioni per argomento con system prompt dedicati
|
||||
- Supportare sia richieste one-shot che conversazioni multi-turn
|
||||
- Accumulare conoscenza tramite riassunti incrementali e completi
|
||||
- Garantire sicurezza con autenticazione API key e rate limiting
|
||||
|
||||
### Utenti
|
||||
|
||||
Uso esclusivamente personale: l'operatore e i suoi software.
|
||||
|
||||
---
|
||||
|
||||
## 2. Architettura
|
||||
|
||||
Il sistema è composto da due container Docker che comunicano via HTTP sulla
|
||||
rete interna Docker, con persistenza su SQLite in un volume condiviso.
|
||||
|
||||
```
|
||||
┌─────────────┐ ┌──────────────────┐ ┌──────────────────┐
|
||||
│ Client │ REST │ FastAPI │ HTTP │ Node.js Worker │
|
||||
│ Software │───────▶│ (Python) │───────▶│ (Claude Code │
|
||||
│ │◀───────│ Queue + Auth │◀───────│ SDK) │
|
||||
└─────────────┘ │ Rate Limit │ └────────┬─────────┘
|
||||
└────────┬──────────┘ │
|
||||
│ ┌─────▼─────────┐
|
||||
┌────▼────┐ │ ~/.claude/ │
|
||||
│ SQLite │ │ (auth volume) │
|
||||
│ (coda) │ └───────────────┘
|
||||
└─────────┘
|
||||
```
|
||||
|
||||
### Componenti
|
||||
|
||||
| Componente | Tecnologia | Ruolo |
|
||||
|------------|------------|-------|
|
||||
| **fastapi-service** | Python 3.11+, FastAPI, SQLite | API REST pubblica, autenticazione, rate limiting, gestione coda |
|
||||
| **claude-worker** | Node.js, @anthropic-ai/claude-code | Processamento richieste via Claude Code SDK, gestione sessioni |
|
||||
| **SQLite** | Volume Docker condiviso | Persistenza coda richieste e argomenti |
|
||||
| **Claude Auth** | Volume Docker dedicato | Credenziali Claude Code (solo worker) |
|
||||
|
||||
### Principi architetturali
|
||||
|
||||
- Il worker non è esposto all'esterno: comunica solo via rete Docker interna
|
||||
- Processamento sequenziale: una richiesta alla volta (FIFO)
|
||||
- Il FastAPI service è l'unico punto di ingresso
|
||||
- Nessuna dipendenza da NATS: HTTP interno sufficiente per due servizi
|
||||
- Integrazione Traefik per reverse proxy (ultima fase del deploy)
|
||||
|
||||
---
|
||||
|
||||
## 3. Data Model
|
||||
|
||||
### Tabella `topics`
|
||||
|
||||
Contiene gli argomenti con i rispettivi system prompt. Ogni argomento
|
||||
rappresenta un contesto di conversazione distinto.
|
||||
|
||||
| Campo | Tipo | Vincoli | Descrizione |
|
||||
|-------|------|---------|-------------|
|
||||
| `id` | TEXT (UUID) | PK | Identificativo unico |
|
||||
| `name` | TEXT | NOT NULL, UNIQUE | Nome breve (es. "coding", "analisi") |
|
||||
| `system_prompt` | TEXT | NOT NULL | System prompt inviato a Claude per questo argomento |
|
||||
| `summary` | TEXT | DEFAULT "" | Riassunto accumulato delle interazioni |
|
||||
| `created_at` | TEXT (ISO 8601) | NOT NULL | Timestamp di creazione |
|
||||
| `model` | TEXT | NULLABLE | Modello Claude per questo topic (override del default globale) |
|
||||
| `updated_at` | TEXT (ISO 8601) | NOT NULL | Timestamp ultima modifica |
|
||||
|
||||
### Tabella `requests`
|
||||
|
||||
Contiene le richieste in coda, in elaborazione e completate.
|
||||
|
||||
| Campo | Tipo | Vincoli | Descrizione |
|
||||
|-------|------|---------|-------------|
|
||||
| `id` | TEXT (UUID) | PK | Identificativo unico |
|
||||
| `session_id` | TEXT | NULLABLE | Raggruppa richieste multi-turn. Null = one-shot |
|
||||
| `topic_id` | TEXT (UUID) | FK → topics.id, NOT NULL | Argomento di riferimento |
|
||||
| `prompt` | TEXT | NOT NULL | Il prompt da inviare a Claude |
|
||||
| `model` | TEXT | NULLABLE | Override modello per questa richiesta (prevale su topic e default) |
|
||||
| `summarize` | INTEGER | DEFAULT 0 | 1 = genera riassunto e lo appende al topic |
|
||||
| `status` | TEXT | NOT NULL, DEFAULT "pending" | `pending`, `processing`, `completed`, `failed` |
|
||||
| `result` | TEXT | NULLABLE | Risposta di Claude |
|
||||
| `error` | TEXT | NULLABLE | Messaggio di errore (se status = failed) |
|
||||
| `created_at` | TEXT (ISO 8601) | NOT NULL | Timestamp di creazione |
|
||||
| `completed_at` | TEXT (ISO 8601) | NULLABLE | Timestamp di completamento |
|
||||
|
||||
### Relazioni
|
||||
|
||||
- `requests.topic_id` → `topics.id` (molti a uno)
|
||||
- `requests.session_id` raggruppa logicamente richieste della stessa conversazione
|
||||
- Un topic può avere zero o più richieste
|
||||
- Una sessione è definita implicitamente dal `session_id` condiviso tra richieste
|
||||
|
||||
---
|
||||
|
||||
## 4. API REST
|
||||
|
||||
Tutti gli endpoint richiedono l'header `X-Api-Key` con la chiave configurata
|
||||
nel `.env`. Risposte nel formato envelope standard:
|
||||
`{success: bool, data: any, error: {code, message} | null}`.
|
||||
|
||||
### 4.1 Topics
|
||||
|
||||
#### `POST /api/topics`
|
||||
|
||||
Crea un nuovo argomento.
|
||||
|
||||
```json
|
||||
// Request
|
||||
{
|
||||
"name": "coding",
|
||||
"system_prompt": "Sei un esperto sviluppatore Python e TypeScript...",
|
||||
"summary": "Argomento per domande di programmazione",
|
||||
"model": "claude-sonnet-4-6"
|
||||
}
|
||||
|
||||
// Response 201
|
||||
{
|
||||
"success": true,
|
||||
"data": {
|
||||
"id": "550e8400-e29b-41d4-a716-446655440000",
|
||||
"name": "coding",
|
||||
"system_prompt": "Sei un esperto sviluppatore Python e TypeScript...",
|
||||
"summary": "Argomento per domande di programmazione",
|
||||
"created_at": "2026-05-25T10:00:00Z",
|
||||
"updated_at": "2026-05-25T10:00:00Z"
|
||||
},
|
||||
"error": null
|
||||
}
|
||||
```
|
||||
|
||||
#### `GET /api/topics`
|
||||
|
||||
Lista tutti gli argomenti.
|
||||
|
||||
#### `GET /api/topics/{id}`
|
||||
|
||||
Dettaglio di un argomento.
|
||||
|
||||
#### `PUT /api/topics/{id}`
|
||||
|
||||
Aggiorna un argomento (name, system_prompt, summary).
|
||||
|
||||
#### `DELETE /api/topics/{id}`
|
||||
|
||||
Elimina un argomento. Fallisce se ci sono richieste pending/processing.
|
||||
|
||||
#### `POST /api/topics/{id}/summarize`
|
||||
|
||||
Rigenera il summary completo del topic analizzando tutte le richieste
|
||||
completate. Invia l'elenco degli scambi (prompt + risultato) a Claude Code
|
||||
e sostituisce il campo `summary` con il nuovo riassunto complessivo.
|
||||
|
||||
### 4.2 Requests
|
||||
|
||||
#### `POST /api/requests`
|
||||
|
||||
Invia una nuova richiesta alla coda.
|
||||
|
||||
```json
|
||||
// Request
|
||||
{
|
||||
"prompt": "Come implemento un middleware di autenticazione JWT in FastAPI?",
|
||||
"topic_id": "550e8400-e29b-41d4-a716-446655440000",
|
||||
"session_id": null,
|
||||
"model": null,
|
||||
"summarize": true
|
||||
}
|
||||
|
||||
// Response 202
|
||||
{
|
||||
"success": true,
|
||||
"data": {
|
||||
"id": "7c9e6679-7425-40de-944b-e07fc1f90ae7",
|
||||
"status": "pending",
|
||||
"created_at": "2026-05-25T10:05:00Z"
|
||||
},
|
||||
"error": null
|
||||
}
|
||||
```
|
||||
|
||||
#### `GET /api/requests/{id}`
|
||||
|
||||
Restituisce stato e risultato (quando disponibile).
|
||||
|
||||
```json
|
||||
// Response (completata)
|
||||
{
|
||||
"success": true,
|
||||
"data": {
|
||||
"id": "7c9e6679-7425-40de-944b-e07fc1f90ae7",
|
||||
"session_id": null,
|
||||
"topic_id": "550e8400-e29b-41d4-a716-446655440000",
|
||||
"prompt": "Come implemento un middleware di autenticazione JWT in FastAPI?",
|
||||
"summarize": true,
|
||||
"status": "completed",
|
||||
"result": "Per implementare un middleware JWT in FastAPI...",
|
||||
"error": null,
|
||||
"created_at": "2026-05-25T10:05:00Z",
|
||||
"completed_at": "2026-05-25T10:05:45Z"
|
||||
},
|
||||
"error": null
|
||||
}
|
||||
```
|
||||
|
||||
#### `GET /api/requests`
|
||||
|
||||
Lista richieste con filtri opzionali: `?status=pending&topic_id=xxx&session_id=yyy`.
|
||||
|
||||
#### `DELETE /api/requests/{id}`
|
||||
|
||||
Cancella una richiesta con status `pending`. Non è possibile cancellare
|
||||
richieste in `processing`, `completed` o `failed`.
|
||||
|
||||
### 4.3 Sessions
|
||||
|
||||
#### `GET /api/sessions`
|
||||
|
||||
Lista sessioni attive con conteggio richieste per ciascuna.
|
||||
|
||||
#### `DELETE /api/sessions/{id}`
|
||||
|
||||
Chiude una sessione. Le richieste existing restano, ma non si possono
|
||||
aggiungere nuove richieste a questa sessione.
|
||||
|
||||
### 4.4 System
|
||||
|
||||
#### `GET /api/health`
|
||||
|
||||
Health check del servizio. Verifica:
|
||||
- Stato FastAPI
|
||||
- Connettività al worker (HTTP ping)
|
||||
- Accesso al database SQLite
|
||||
|
||||
---
|
||||
|
||||
## 5. Flusso di Processamento
|
||||
|
||||
### 5.1 Flusso richiesta standard
|
||||
|
||||
1. Il client invia `POST /api/requests` con prompt e topic_id
|
||||
2. FastAPI valida la richiesta, verifica API key, controlla rate limit
|
||||
3. Crea record in SQLite con `status: "pending"`, restituisce `202 Accepted` con ID
|
||||
4. Il client riceve l'ID e può fare polling su `GET /api/requests/{id}`
|
||||
5. Il worker loop (nel FastAPI service) prende la richiesta pending più vecchia
|
||||
6. Aggiorna status a `processing`
|
||||
7. Carica il `system_prompt` dal topic associato
|
||||
8. Chiama il worker Node.js via HTTP: `POST http://claude-worker:3001/process`
|
||||
9. Il worker Node.js esegue la richiesta tramite Claude Code SDK
|
||||
10. Il worker restituisce il risultato
|
||||
11. FastAPI aggiorna il record: `status: "completed"`, `result: "..."`
|
||||
12. Se `summarize: true`: il worker genera un riassunto dello scambio
|
||||
e lo appende al `summary` del topic
|
||||
13. Il client ottiene il risultato al prossimo polling
|
||||
|
||||
### 5.2 Flusso multi-turn
|
||||
|
||||
1. Prima richiesta: `POST /api/requests {prompt, topic_id}` (senza session_id)
|
||||
2. Il sistema genera un `session_id` e lo restituisce nella risposta
|
||||
3. Richieste successive: `POST /api/requests {prompt, topic_id, session_id: "..."}`
|
||||
4. Il worker Node.js usa il session_id per mantenere il contesto con Claude Code
|
||||
(flag `--resume` o equivalente SDK)
|
||||
5. Ogni richiesta nella sessione condivide la storia della conversazione
|
||||
|
||||
### 5.3 Flusso summary completo
|
||||
|
||||
1. Il client chiama `POST /api/topics/{id}/summarize`
|
||||
2. FastAPI raccoglie tutte le richieste `completed` del topic
|
||||
3. Compone un payload con tutti gli scambi (prompt + risultato)
|
||||
4. Chiama il worker per generare un riassunto complessivo
|
||||
5. Sostituisce il campo `summary` del topic con il nuovo riassunto
|
||||
6. Restituisce il summary aggiornato
|
||||
|
||||
### 5.4 Flusso summary incrementale
|
||||
|
||||
1. Richiesta con `summarize: true` viene completata
|
||||
2. Dopo aver salvato il risultato, il worker genera un riassunto
|
||||
in 1-2 frasi dello scambio
|
||||
3. Il riassunto viene appeso al `summary` del topic con timestamp:
|
||||
`[2026-05-25] Descrizione dello scambio.`
|
||||
|
||||
---
|
||||
|
||||
## 6. Node.js Worker
|
||||
|
||||
### Responsabilità
|
||||
|
||||
Il worker è un server HTTP interno minimale con un'unica responsabilità:
|
||||
eseguire richieste tramite Claude Code SDK e restituire il risultato.
|
||||
|
||||
### Endpoint interni
|
||||
|
||||
| Metodo | Path | Descrizione |
|
||||
|--------|------|-------------|
|
||||
| `POST` | `/process` | Processa una richiesta Claude Code |
|
||||
| `POST` | `/summarize` | Genera riassunto di uno o più scambi |
|
||||
| `GET` | `/health` | Health check del worker |
|
||||
|
||||
### Payload `/process`
|
||||
|
||||
```json
|
||||
// Request
|
||||
{
|
||||
"prompt": "Come implemento JWT?",
|
||||
"system_prompt": "Sei un esperto sviluppatore...",
|
||||
"model": "claude-sonnet-4-6",
|
||||
"session_id": null
|
||||
}
|
||||
|
||||
// Response
|
||||
{
|
||||
"success": true,
|
||||
"result": "Per implementare JWT in FastAPI...",
|
||||
"session_id": "generated-or-existing-id"
|
||||
}
|
||||
```
|
||||
|
||||
### Payload `/summarize`
|
||||
|
||||
```json
|
||||
// Request
|
||||
{
|
||||
"exchanges": [
|
||||
{"prompt": "Come faccio X?", "result": "Si fa così..."},
|
||||
{"prompt": "E se devo Y?", "result": "In quel caso..."}
|
||||
],
|
||||
"mode": "incremental"
|
||||
}
|
||||
|
||||
// Response
|
||||
{
|
||||
"success": true,
|
||||
"summary": "[2026-05-25] Discusso come implementare X e gestire il caso Y."
|
||||
}
|
||||
```
|
||||
|
||||
### Claude Code SDK Usage
|
||||
|
||||
```javascript
|
||||
import { claude } from '@anthropic-ai/claude-code';
|
||||
|
||||
const result = await claude({
|
||||
prompt: request.prompt,
|
||||
systemPrompt: request.system_prompt,
|
||||
model: request.model,
|
||||
// Per multi-turn: resume dalla sessione
|
||||
...(request.session_id && { resume: request.session_id }),
|
||||
// Output in formato testo, non interattivo
|
||||
print: true,
|
||||
});
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 7. Selezione Modello
|
||||
|
||||
Il modello Claude da utilizzare si risolve con priorità a cascata:
|
||||
|
||||
1. **Request `model`** — se la richiesta specifica un modello, usa quello
|
||||
2. **Topic `model`** — se il topic ha un modello configurato, usa quello
|
||||
3. **Default globale** — `CLAUDE_MODEL` dal `.env`
|
||||
|
||||
Questa gerarchia consente di avere un default ragionevole (es. `claude-sonnet-4-6`)
|
||||
con la flessibilità di usare modelli diversi per argomenti specifici (es. Opus
|
||||
per analisi complesse) o per singole richieste (es. Haiku per domande semplici).
|
||||
|
||||
### Modelli supportati
|
||||
|
||||
I valori accettati corrispondono agli identificativi dei modelli Claude:
|
||||
- `claude-opus-4-7`
|
||||
- `claude-sonnet-4-6`
|
||||
- `claude-haiku-4-5-20251001`
|
||||
|
||||
La validazione accetta qualsiasi stringa che inizi con `claude-` per
|
||||
compatibilità con modelli futuri.
|
||||
|
||||
---
|
||||
|
||||
## 8. Rate Limiting
|
||||
|
||||
Implementato con `slowapi` (wrapper di `limits` per FastAPI).
|
||||
|
||||
### Configurazione
|
||||
|
||||
```
|
||||
# .env
|
||||
RATE_LIMIT_PER_MINUTE=10
|
||||
RATE_LIMIT_PER_HOUR=100
|
||||
```
|
||||
|
||||
### Comportamento
|
||||
|
||||
- Limiti applicati per API key
|
||||
- Quando superati: risposta `429 Too Many Requests` con header
|
||||
`Retry-After` e `X-RateLimit-Remaining`
|
||||
- I limiti si applicano solo a `POST /api/requests` (invio richieste)
|
||||
- Gli endpoint di lettura (GET) e gestione topics non sono limitati
|
||||
|
||||
---
|
||||
|
||||
## 9. Sicurezza
|
||||
|
||||
### Autenticazione API
|
||||
|
||||
- Header `X-Api-Key` obbligatorio su tutti gli endpoint
|
||||
- Chiave configurata in `.env`
|
||||
- Risposta `403 Forbidden` se mancante o non valida
|
||||
|
||||
### Isolamento Worker
|
||||
|
||||
- Il worker Node.js non è esposto all'esterno (`expose`, non `ports`)
|
||||
- Raggiungibile solo dalla rete Docker interna
|
||||
- Nessuna autenticazione sul worker (rete fidata interna)
|
||||
|
||||
### Credenziali Claude Code
|
||||
|
||||
- Conservate in volume Docker dedicato (`claude-auth`)
|
||||
- Non accessibili dal container FastAPI
|
||||
- Autenticazione manuale una tantum alla prima configurazione
|
||||
|
||||
### Integrazione Traefik
|
||||
|
||||
- Sarà implementata come ultima fase del deploy
|
||||
- Il servizio FastAPI sarà raggiungibile tramite reverse proxy Traefik
|
||||
- HTTPS via Let's Encrypt tramite Traefik
|
||||
- Labels Docker per il routing automatico
|
||||
|
||||
---
|
||||
|
||||
## 10. Configurazione
|
||||
|
||||
### .env
|
||||
|
||||
```
|
||||
# Server
|
||||
API_HOST=0.0.0.0
|
||||
API_PORT=8000
|
||||
|
||||
# Sicurezza
|
||||
API_KEY=
|
||||
|
||||
# Rate Limiting
|
||||
RATE_LIMIT_PER_MINUTE=10
|
||||
RATE_LIMIT_PER_HOUR=100
|
||||
|
||||
# Worker
|
||||
WORKER_URL=http://claude-worker:3001
|
||||
|
||||
# Database
|
||||
DB_PATH=/data/opus-agent.db
|
||||
|
||||
# Claude Code (worker)
|
||||
CLAUDE_MODEL=claude-sonnet-4-6
|
||||
```
|
||||
|
||||
### Variabili condivise
|
||||
|
||||
Entrambi i container ricevono lo stesso `.env` via `env_file`.
|
||||
Il FastAPI service usa le variabili di server, sicurezza, rate limit e DB.
|
||||
Il worker usa `CLAUDE_MODEL` e il path DB per eventuali accessi diretti.
|
||||
|
||||
---
|
||||
|
||||
## 11. Struttura Progetto
|
||||
|
||||
```
|
||||
opus-agent/
|
||||
├── src/
|
||||
│ └── backend/
|
||||
│ ├── __init__.py
|
||||
│ ├── main.py # Entry point FastAPI
|
||||
│ ├── config.py # Pydantic Settings
|
||||
│ ├── api/
|
||||
│ │ ├── __init__.py
|
||||
│ │ ├── dependencies.py # API Key auth + rate limiting
|
||||
│ │ └── routes/
|
||||
│ │ ├── __init__.py
|
||||
│ │ ├── topics.py # CRUD topics + summarize
|
||||
│ │ ├── requests.py # CRUD requests
|
||||
│ │ ├── sessions.py # Lista/chiudi sessioni
|
||||
│ │ └── health.py # Health check
|
||||
│ ├── models/
|
||||
│ │ ├── __init__.py
|
||||
│ │ └── api.py # Pydantic models request/response
|
||||
│ ├── db/
|
||||
│ │ ├── __init__.py
|
||||
│ │ ├── database.py # SQLite connection + init schema
|
||||
│ │ └── models.py # Schema tabelle
|
||||
│ └── services/
|
||||
│ ├── __init__.py
|
||||
│ ├── queue.py # Logica coda (pick pending, update status)
|
||||
│ └── worker_client.py # Client HTTP per chiamare il worker
|
||||
│
|
||||
├── worker/
|
||||
│ ├── index.js # Server HTTP + Claude Code SDK
|
||||
│ ├── package.json
|
||||
│ └── Dockerfile
|
||||
│
|
||||
├── tests/
|
||||
│ ├── __init__.py
|
||||
│ ├── conftest.py
|
||||
│ ├── test_api_topics.py
|
||||
│ ├── test_api_requests.py
|
||||
│ └── test_services.py
|
||||
│
|
||||
├── Dockerfile # Backend Python
|
||||
├── docker-compose.yml
|
||||
├── pyproject.toml
|
||||
├── .env.example
|
||||
├── .gitignore
|
||||
└── README.md
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 12. Docker
|
||||
|
||||
### Dockerfile Backend (Python)
|
||||
|
||||
```dockerfile
|
||||
FROM python:3.11-slim AS base
|
||||
COPY --from=ghcr.io/astral-sh/uv:latest /uv /usr/local/bin/uv
|
||||
WORKDIR /app
|
||||
COPY pyproject.toml uv.lock ./
|
||||
RUN uv sync --frozen --no-dev
|
||||
COPY src/ src/
|
||||
EXPOSE 8000
|
||||
CMD ["uv", "run", "start"]
|
||||
```
|
||||
|
||||
### Dockerfile Worker (Node.js)
|
||||
|
||||
```dockerfile
|
||||
FROM node:20-slim
|
||||
RUN npm install -g @anthropic-ai/claude-code
|
||||
WORKDIR /app
|
||||
COPY package.json ./
|
||||
RUN npm ci --production
|
||||
COPY index.js ./
|
||||
EXPOSE 3001
|
||||
CMD ["node", "index.js"]
|
||||
```
|
||||
|
||||
### docker-compose.yml
|
||||
|
||||
```yaml
|
||||
services:
|
||||
fastapi-service:
|
||||
build:
|
||||
context: .
|
||||
dockerfile: Dockerfile
|
||||
ports:
|
||||
- "${API_PORT:-8000}:8000"
|
||||
env_file: .env
|
||||
volumes:
|
||||
- opus-data:/data
|
||||
depends_on:
|
||||
claude-worker:
|
||||
condition: service_healthy
|
||||
restart: unless-stopped
|
||||
healthcheck:
|
||||
test: ["CMD", "python", "-c",
|
||||
"import urllib.request; urllib.request.urlopen('http://localhost:8000/api/health')"]
|
||||
interval: 10s
|
||||
timeout: 5s
|
||||
retries: 3
|
||||
|
||||
claude-worker:
|
||||
build:
|
||||
context: ./worker
|
||||
dockerfile: Dockerfile
|
||||
expose:
|
||||
- "3001"
|
||||
env_file: .env
|
||||
volumes:
|
||||
- opus-data:/data
|
||||
- claude-auth:/home/node/.claude
|
||||
restart: unless-stopped
|
||||
healthcheck:
|
||||
test: ["CMD", "wget", "--spider", "-q", "http://localhost:3001/health"]
|
||||
interval: 10s
|
||||
timeout: 5s
|
||||
retries: 3
|
||||
|
||||
volumes:
|
||||
opus-data:
|
||||
claude-auth:
|
||||
```
|
||||
|
||||
### Integrazione Traefik (ultima fase)
|
||||
|
||||
Nella configurazione finale, le porte dirette verranno sostituite da
|
||||
labels Traefik per il routing automatico:
|
||||
|
||||
```yaml
|
||||
# Da aggiungere a fastapi-service
|
||||
labels:
|
||||
- "traefik.enable=true"
|
||||
- "traefik.http.routers.opus-agent.rule=Host(`opus.tuodominio.com`)"
|
||||
- "traefik.http.routers.opus-agent.tls.certresolver=letsencrypt"
|
||||
- "traefik.http.services.opus-agent.loadbalancer.server.port=8000"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 13. Setup Iniziale
|
||||
|
||||
### Prima installazione sulla VPS
|
||||
|
||||
1. Clona il repository e copia `.env.example` in `.env`, compila i valori
|
||||
2. Avvia solo il worker per autenticare Claude Code:
|
||||
```bash
|
||||
docker compose run --rm claude-worker npx claude login
|
||||
```
|
||||
3. Segui il flusso di autenticazione nel browser
|
||||
4. Le credenziali vengono salvate nel volume `claude-auth`
|
||||
5. Avvia il sistema completo:
|
||||
```bash
|
||||
docker compose up -d
|
||||
```
|
||||
6. Verifica con `GET /api/health`
|
||||
|
||||
### Rinnovo credenziali
|
||||
|
||||
Se le credenziali Claude Code scadono:
|
||||
```bash
|
||||
docker compose run --rm claude-worker npx claude login
|
||||
docker compose restart claude-worker
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 14. Limiti e Vincoli Noti
|
||||
|
||||
- **Throughput**: Una richiesta alla volta. Il tempo di risposta dipende dalla
|
||||
complessità del prompt e dal carico sull'abbonamento Claude.
|
||||
- **Rate limit abbonamento**: Claude Pro/Max ha limiti d'uso propri.
|
||||
Se l'abbonamento raggiunge il suo limite, le richieste falliranno con
|
||||
un errore specifico. Il servizio marca queste come `failed` con il
|
||||
messaggio d'errore di Claude.
|
||||
- **Sessioni multi-turn**: La persistenza delle sessioni dipende dal
|
||||
comportamento del Claude Code SDK. Se il SDK non supporta nativamente
|
||||
il resume di sessioni, si implementa passando lo storico come contesto.
|
||||
- **Volume credenziali**: Le credenziali Claude Code nel volume Docker
|
||||
possono scadere. Serve rinnovo manuale periodico.
|
||||
- **SQLite concorrenza**: Con un solo writer (il queue processor), non ci
|
||||
sono problemi di locking. Se in futuro si volessero più worker paralleli,
|
||||
servirebbe migrare a PostgreSQL o usare WAL mode.
|
||||
Reference in New Issue
Block a user