Un'architettura a separazione delle responsabilità. Il controller fa solo routing; il modello di embedding risiede sul demone di inferenza nativo; il vector store risiede sul data worker. Tre processi, tre responsabilità, un solo cavo. Questa pagina è la vista tecnica per power user e operatori che vogliono sapere perché ha questa forma e come piegarla quando serve.
Su una richiesta di chat che attiva RAG, tre processi partecipano in sequenza:
/api/v1/embeddings (proxy verso Inferenced) e /api/v1/vector/search (aggregatore sui data worker). Il controller non fa inferenza in-process; è tutto routing.La richiesta di chat completion attraversa poi il percorso di inferenza con i passaggi recuperati iniettati come contesto, restituendo al client la risposta ancorata + i metadata di citazione.
Se il controller facesse l'embedding in-process, ogni nodo che esegue un controller dovrebbe avere il modello di embedding caricato — divorando RAM e consumando tempo di avvio. Peggio: in configurazioni cluster il controller potrebbe non essere il nodo con GPU, quindi farebbe embedding su CPU mentre una GPU perfettamente buona sta a un host di distanza. Mantenere il controller un router puro significa che scalare il lavoro di embedding è disaccoppiato dallo scalare il lavoro di routing.
nomic-embed-text-Q4_K_M è 80 MB su disco, gira comodamente su CPU e produce vettori a 768 dimensioni compatibili con il layout di storage del data worker. Quantizzare a Q4_K_M scambia una piccola quantità di precisione di embedding-quality per un modello che entra in memoria su un Raspberry Pi 4 e fa l'embedding di 1k token ben sotto il secondo su qualsiasi CPU moderna. GGUF significa che llama.cpp può servirlo direttamente; Inferenced parla già GGUF, quindi è il posto naturale dove ospitarlo.
Il data worker contiene già i documenti sorgente, i chunk, i confini di tenant, il ledger di audit e lo storage dei file. Mettere le voci vettoriali accanto ai chunk (invece che in un vector database separato) significa che cancellazione, ri-embedding, isolamento di tenant e backup sono tutte una sola operazione, non cinque.
Il default mette Inferenced e il controller sullo stesso host (o ovunque il controller possa raggiungere Inferenced sulla rete del cluster). Alcuni deployment vogliono qualcosa di diverso — Inferenced su un host GPU dedicato, un servizio di embedding esterno, o un modello GGUF del tutto diverso. Si gestisce con una variabile d'ambiente sul controller:
# /etc/eldric/eldric-aios.env ELDRIC_EMBED_BACKEND_URL=http://inferenced-host:8883
Qualunque URL che parli l'endpoint OpenAI-compatibile /v1/embeddings funziona — Inferenced è il default, ma anche Ollama lo soddisfa, così come un server llama.cpp self-hosted, vLLM, TGI, o qualsiasi API cloud di embedding se vuoi spedire gli embedding fuori sede (la maggior parte dei clienti non vuole).
Riavvia il controller dopo aver modificato il file env (sudo systemctl restart eldric-aios-controller) e il nuovo backend viene preso dalla richiesta di chat successiva.
Tutto su un solo host. Il controller, Inferenced e il data worker sono tre unità systemd sulla stessa macchina. Inferenced coabita con il lavoro di inferenza LLM; l'embedding occupa una frazione della memoria GPU e gira accanto a modelli più grandi senza contesa.
Il controller risiede su un piccolo host di gestione. Inferenced risiede su un host GPU (o su un qualsiasi nodo con CPU sufficiente se non vuoi dedicare GPU all'embedding). Il data worker risiede sull'host con storage abbondante. Il controller raggiunge Inferenced tramite ELDRIC_EMBED_BACKEND_URL; il controller raggiunge i data worker tramite il layer di topology discovery (nessuna env var necessaria — pushed dall'heartbeat).
Su un Pi 4 o NUC che esegue il runtime edge minimale, tutti e tre risiedono sul singolo host edge (la piattaforma li distribuisce come unico bundle). Il nodo edge fa embedding in locale, indicizza in locale, interroga in locale. Se è configurato un cluster centrale, il percorso di esportazione/importazione bundle sposta intere basi di conoscenza tra edge e centro senza ri-embedding.
Se Inferenced non è raggiungibile, il controller restituisce un errore strutturato sulla route degli embeddings (502 con corpo esplicativo). I nuovi upload non riescono ad andare in embedding e restano marcati "in coda"; la piattaforma riprova al ciclo di upload successivo una volta che Inferenced è tornato. Le query di chat che avrebbero attivato RAG ricadono sulla chat semplice (senza citazioni) invece di far fallire l'intera richiesta.
Se il data worker non è raggiungibile, vector-search restituisce 503; le richieste di chat ricadono sulla chat semplice. L'upload tramite il percorso chunked-upload dell'interfaccia si accoda sul controller e si scarica una volta che il data worker è tornato.
Se il modello di embedding non è caricato in Inferenced, la route degli embeddings restituisce 404 con un suggerimento model-not-loaded. La dashboard amministrativa di Inferenced ha un pulsante Carica; cliccalo (il file risiede in /data/eldric/models/nomic-embed-text-Q4_K_M.gguf in un'installazione standard) e l'embedding riparte alla richiesta successiva.
Per la guida pratica lato cliente: usare RAG. Per l'anteprima della memoria compressa che velocizza la ricerca vettoriale in concorrenza: retrieval avanzato. Per l'anteprima lato inferenza che consulta la memoria al confine del prompt invece di fare round-trip: inferenza con memoria intelligente.
Per il resto del sistema in cui si inserisce il percorso RAG: come funziona percorre l'intera architettura a 4 livelli (Client → Edge → Controller / Router / Data → 10 worker, Inferenced incluso).