Overview
RAG Chatbot
Componente Angular <wuic-rag-chatbot> che permette di interrogare il codebase
WUIC in linguaggio naturale, con due modalita' operative: retrieval pure
(top-K snippet del codice) e RAG + LLM (risposta generata da Claude usando
i top-K come contesto). Degrada automaticamente in retrieval-only se la API
key Claude non e' configurata sul server.
Architettura
Tre layer, nessun processo esterno:
- Layer 1 — Engine RAG .NET
WuicRagEngine.dll(cartellarag-engine/),
caricato in-process da RagEngineLoader quando rag-use-dotnet-engine e'
true. Esegue con ONNX Runtime (GPU CUDA se presente, altrimenti CPU) il
retrieval ibrido BM25 + bge-m3 + reranker cross-encoder e la chiamata al
provider LLM configurato. Modelli, tokenizer e indice vengono scaricati al
primo avvio in rag-engine/artifacts/.
- Layer 2 —
RagControllerin KonvergenceCore (/api/Rag/Query,
/api/Rag/Chat, /api/Rag/Health, /api/Rag/Reload), autenticato via
cookie k-user; risolve i tool di metadata e applica le azioni proposte.
- Layer 3 — Componente Angular standalone
<wuic-rag-chatbot>esportato
da wuic-framework-lib.
Il server Python FastAPI rag_server.py (127.0.0.1:8765) e' lo stack
storico: resta supportato come fallback opzionale con
rag-use-dotnet-engine = "false", ma non e' incluso nei pacchetti di release.
Contesto pagina e retrieval dinamico dei metadata
Quando l'utente chatta da una pagina dell'app, il componente inietta nel prompt
un contesto pagina minimale: solo la route/pagina corrente (es. cities/list,
cities/edit, designer). NON inlinea l'elenco colonne, l'identita' SQL o i
dettagli dei lookup — inlinearli gonfiava ogni richiesta a prescindere dal prompt
e saturava il context window.
I dettagli mancanti vengono recuperati on-demand dal modello tramite il tool
non-terminale request_metadata_detail, risolto lato backend da
RagController.ResolveMetadataDetail (l'engine non ha accesso al DB metadata). Il
modello chiama il tool, riceve il risultato come tool_result e SOLO DOPO emette
il propose_* con i nomi reali. Il loop multi-turn e' gestito dall'engine (max 3
turni di retrieval).
detail supportati:
detail | Ritorna | Quando |
|---|---|---|
columns | identita' SQL (schema/tabella + full qualifier) e l'elenco colonne reali (nome, tipo, lookup, required, pk, nome SQL) | servono i nomi reali delle colonne di una route |
lookup_columns | join_alias, value_field, text_field, related_route, related_columns[] di una colonna lookupByID | si compone uno snippet SQL su un lookup |
Convenzione alias di join (lookup): la query autogenerata WUIC joina la tabella
correlata di una colonna lookupByID con alias <column>_<entity> (es. la colonna
StateProvinceID lookup verso stateprovinces ha alias StateProvinceID_stateprovinces).
Negli snippet SQL i campi della tabella correlata si referenziano come
[<join_alias>].[<col>], es. [StateProvinceID_stateprovinces].[StateProvinceName].
Il valore esatto dell'alias va sempre ottenuto via
request_metadata_detail{detail:'lookup_columns'}, mai dedotto a mano.
Il meccanismo e' estendibile: nuovi detail (es. physical_columns, related_routes,
enum_values) si aggiungono nel resolver senza toccare il componente Angular ne'
gonfiare il contesto.
Modalita'
mode input | Comportamento |
|---|---|
auto (default) | Usa chat se il backend espone Claude API key, altrimenti retrieval |
chat | Forza RAG + LLM. Se la API key manca, il backend ritorna mode=retrieval-only con warning visibile |
retrieval | Forza retrieval-only, salta la chiamata LLM (utile per ridurre costi) |
Inputs
| Input | Tipo | Default | Descrizione |
|---|---|---|---|
title | string | 'Assistente codebase WUIC' | Etichetta header |
mode | 'auto' | 'chat' | 'retrieval' | 'auto' | Modalita' operativa |
topK | number | 5 | Numero di chunk da retrieve dal RAG |
showSources | boolean | true | Mostra/nasconde i source chip nei messaggi assistant |
maxHistory | number | 20 | Numero massimo di turn mantenuti in memoria |
placeholder | string | 'Chiedi qualcosa...' | Placeholder dell'input |
model | string | 'claude-haiku-4-5-20251001' | Modello Claude per la modalita' chat |
chatHeight | string | '420px' | Altezza fissa area chat |
showClearButton | boolean | true | Mostra bottone "Svuota cronologia" |
Outputs
| Output | Payload | Descrizione |
|---|---|---|
resultSelected | RagSource | Click su un source chip; il componente tenta anche un deep-link vscode://file/... |
errorOccurred | {message, details?} | Errori HTTP non recuperabili |
turnAdded | RagChatbotTurn | Emette dopo ogni turn (user o assistant) aggiunto alla history |
Esempio d'uso
Standalone import nel componente parent:
- selector HTML:
<wuic-rag-chatbot mode="auto" [topK]="5" (resultSelected)="onSrc($event)"></wuic-rag-chatbot> - import TypeScript:
import { WuicRagChatbotComponent, RagSource } from 'wuic-framework-lib'; - aggiungere
WuicRagChatbotComponentagliimportsdel componente standalone parent.
Per una pagina demo completa con aside debug e gestione eventi, vedi
WuicTest/wwwroot/src/app/component/rag-chatbot-demo-page/.
Auth
Tutte le chiamate HTTP partono con withCredentials: true e il bridge C#
applica gli stessi check di autenticazione degli altri controller WUIC
(cookie di sessione k-user, regola 10 di AGENTS). L'engine RAG gira in-process nel backend: non c'e' nessun servizio
aggiuntivo da esporre o proteggere.
Accesso agli endpoint dalla 1.7.13:
| Endpoint | Accesso |
|---|---|
/api/Rag/Query, /api/Rag/Health | senza login |
/api/Rag/Chat | sessione valida (anche guest, se enableGuest e' attivo) |
/api/Rag/MetadataDetail | senza login per i dettagli sui metadati; sample_records, lookup_value, db_connections, db_databases, db_tables, db_columns solo amministratori |
/api/Rag/Reload | solo amministratori |
La chiave LLM configurata sul server non viene mai inviata a un provider o a un baseUrl indicati nella richiesta: chi li sovrascrive passa la propria apiKey, altrimenti la risposta e' retrieval-only.
Server MCP wuic-rag e utente wuic_assistant
Il server MCP scripts/mcp/wuic-rag-mcp.mjs (WUIC Assistant, Claude Code, Cursor) usa questi endpoint. Quando il backend risponde 401 apre una sessione con MetaService.login, con WUIC_USER / WUIC_PASSWORD letti dall'ambiente (.mcp.json) o, se mancano, da scripts/mcp/wuic-assistant.credentials.json. Il login serve a wuic_ask e, con un utente amministratore, a wuic_sample_records / wuic_lookup_value; wuic_codebase_search, wuic_route_columns, wuic_route_metadata e wuic_lookup_columns restano senza login.
- Il wizard di primo avvio crea l'utente dedicato
wuic_assistant(superadmin, separato dall'admin interattivo) con una password generata per ogni installazione, e la scrive inscripts/mcp/wuic-assistant.credentials.jsoncon un.gitignoreaccanto: il file non va committato ne' copiato su altre installazioni. - L'estensione VS Code usa lo stesso file quando
wuicAssistant.metadataAdminUserewuicAssistant.metadataAdminPasswordsono vuoti (default); valorizzale solo per credenziali diverse. - Aggiornamento da 1.7.12 o precedenti: la password fissa che
wuic_assistantaveva fino alla 1.7.12 viene rifiutata al login. Un amministratore imposta una nuova password perwuic_assistante la scrive nel file credenziali ({"user":"wuic_assistant","password":"..."}) o nelle impostazioni dell'estensione.
Service WuicRagService
API tipizzata sopra RagController, esportata da wuic-framework-lib.
Tre metodi principali:
query(text, {topK, useLora}) -> Observable<RagQueryResponse>chat(text, history, {topK, model}) -> Observable<RagChatResponse>health() -> Observable<RagHealthResponse>reload() -> Observable<{status, ...}>(post rebuild RAG)
Varianti *Async ritornano Promise via firstValueFrom().
Tutte le interfacce RagSource, RagQueryResponse, RagChatResponse,
RagHealthResponse, RagChatTurn sono esportate.
Modello Claude
Default: claude-haiku-4-5-20251001 (veloce, italiano nativo, ~$0.001 per
query da 5 chunk). Override possibile via input [model] del componente
oppure passando options.model al metodo chat() del service.
System prompt usato server-side:
> Sei un assistente esperto del codebase WUIC. Rispondi alla domanda dell'utente
> usando ESCLUSIVAMENTE il contesto fornito. Se la risposta non e' nel contesto,
> rispondi 'Non ho trovato informazioni sufficienti nel codebase per rispondere.'
> Cita sempre i file rilevanti tra parentesi quadre nel formato
> [file.ext::SimboloOpzionale]. Rispondi in italiano salvo richiesta esplicita
> di un'altra lingua. Non inventare API o nomi di metodi: se non sono nel
> contesto, dillo esplicitamente.
Fallback automatico
Quando il backend rileva una di queste condizioni, il response include
mode: 'retrieval-only' + warning + sources:
rag-llm-provider/rag-llm-api-keynon configurati inAppSettings- chiamata Claude fallita (errore HTTP, rate limit, modello sconosciuto, ecc.)
- richiesta con
providerobaseUrlpropri ma senzaapiKey(la chiave del server non viene usata verso provider o host scelti da chi chiama)
Il componente Angular interpreta response.mode === 'retrieval-only' e mostra
nel turn assistant un summary testuale dei top-K chunk + il banner warning,
cosi' l'utente vede comunque dei risultati utili.
Prerequisiti runtime
- KonvergenceCore in esecuzione con
rag-use-dotnet-engine = "true"(espone/api/Rag/...) - Cartella
rag-engine/conWuicRagEngine.dllaccanto all'app (inclusa nei pacchetti di release; in dev override conrag-engine-dll-path) - Accesso internet al primo avvio per scaricare modelli + indice (~4,5 GB) da
rag-engine-models-url - Login con cookie
k-userdi sessione valido - (Opzionale) GPU NVIDIA con CUDA 12.x + cuDNN 9 (
rag-engine-device = autola usa se presente) - (Opzionale)
rag-llm-provider+rag-llm-api-keyinAppSettingsper la modalita' chat
Avvio (primo utilizzo)
Alla prima apertura della route rag-chatbot (o a fine first-run) il backend
avvia in background il bootstrap dell'engine: scarica modelli ONNX, tokenizer
e indice (~4,5 GB, 1-5 min a seconda della banda) in rag-engine/artifacts/,
carica il satellite e fa il warm-up (~30 s). L'utente riceve una notifica
in-app all'avvio e al termine; fino ad allora il componente mostra il banner
"Server RAG non raggiungibile" con stato RAG offline. Se il backend
viene riavviato a meta' download, il download riprende al boot successivo.
Non c'e' nessuno script da lanciare. Le uniche scelte sono in appsettings.json
(sezione AppSettings):
{
"rag-use-dotnet-engine": "true",
"rag-engine-device": "auto",
"rag-engine-profile": "release",
"rag-engine-models-url": "https://wuic-framework.com/rag-models",
"rag-llm-provider": "anthropic",
"rag-llm-api-key": "sk-ant-..."
rag-engine-device:autousa la GPU NVIDIA se trovata (CUDA 12.x + cuDNN 9,
~1 s/query), altrimenti CPU (~15-25 s/query). rag-engine-cuda-path indica la
cartella delle DLL CUDA se non sono installate a livello di sistema.
rag-engine-eager-load:truecarica il motore in background all'avvio del
backend, cosi' la prima ricerca non paga il caricamento a freddo (su CPU anche
qualche minuto); il costo e' la RAM del motore (4,5-6,5 GB) occupata anche se il
RAG non si usa. Default false: il motore si carica alla prima richiesta; nel
frattempo gli strumenti MCP wuic_codebase_search / wuic_ask aspettano fino a
60 s e poi rispondono di riprovare invece di andare in timeout.
rag-llm-provider/rag-llm-api-key/rag-llm-base-url/
rag-llm-default-chat-model: provider della chat (anthropic, openai,
openrouter, ollama). Senza provider il chatbot resta in retrieval-only.
Le chiavi sono hot-reload: dettaglio nella pagina AppSettings.
Fallback Python (storico)
scripts/rag-setup.ps1 crea il venv e avvia rag_server.py su
127.0.0.1:8765; si usa solo con rag-use-dotnet-engine = "false", ad
esempio per separare il carico GPU su un host dedicato. Non e' incluso nei
pacchetti di release.
Hot-reload post rebuild RAG
Dopo aver rigenerato l'indice/LoRA, basta
chiamare:
POST /api/Rag/Reload(via WuicRagService.reload(), solo amministratori)- oppure riavviare il backend (l'engine ricarica
index/al boot)
per ricaricare il nuovo indice senza downtime applicativo.
Riferimenti
- Engine .NET:
rag-engine/WuicRagEngine.dll - Bridge C#:
/api/Rag/Query,/api/Rag/Chat,/api/Rag/Health