--- title: 'AI Stack : Qdrant + CLI Ollama' url: https://blog.guigpap.com/fr/infrastructure/ai-stack/ url_md: https://blog.guigpap.com/fr/infrastructure/ai-stack.md category: infrastructure date: '2026-01-20' maturite: production techno: - qdrant - claude - redis application: - ai - infrastructure --- # AI Stack : Qdrant + CLI Ollama > Infrastructure IA locale avec base vectorielle, passerelle multi-modèles, profiles à privilèges scopés, et MCP gateway whitelistée ## 1. Quoi ? — Définition et contexte L'**AI Stack** rassemble cinq services qui collaborent pour servir tous les besoins IA de l'infrastructure : base vectorielle pour le RAG, passerelle multi-modèles compatible Ollama, mémoire de session, et deux briques MCP (Model Context Protocol) qui exposent les outils N8N aux modèles avec un contrôle d'accès strict. ### Composants | Service | Port | Rôle | |---------|------|------| | **Qdrant** | 6333 | Base de données vectorielle (embeddings, recherche sémantique) | | **CLI Ollama** | 11434 | Passerelle compatible Ollama vers Codex et Gemini | | **Claude Redis** | 6379 | Mémoire de sessions de conversation (LRU 256 MB) | | **MCP Gateway** | 3001 | Reverse proxy MCP : whitelist d'outils + négociation SSE/JSON | | **N8N MCP** | 3000 | Bridge MCP exposant l'API N8N aux modèles | > **Note - Ollama-compatible** > > **CLI Ollama** ré-implémente l'API Ollama (`/api/generate`, `/api/chat`, `/api/tags`) mais ne fait pas tourner de modèles localement : chaque requête est traduite en appel CLI vers les fournisseurs (Codex CLI d'OpenAI, Gemini CLI de Google). C'est un wrapper, pas un runtime. L'expose une API que N8N et les autres consommateurs traitent comme un Ollama classique. ### Architecture visuelle ```mermaid flowchart TD subgraph N8N["N8N Workflows"] direction TB I1["IA Router · intent detection"] I2["Service Handler General · chat libre"] I3["Conversation Agent · sessions multi-tour"] I4["RAG · futur"] end subgraph AI["ai-internal"] direction TB Qdrant["Qdrant · :6333"] Ollama["CLI Ollama · :11434"] Redis["Claude Redis · sessions"] Sub["Subprocess CLI · codex / gemini"] Ollama --> Redis Ollama --> Sub end subgraph MCPNet["mcp-backend (isolé)"] direction TB Gateway["MCP Gateway · :3001 · whitelist + Bearer"] NMCP["N8N MCP bridge · :3000"] end N8N --> AI Ollama -->|MCP JSON-RPC| Gateway Gateway -->|"20 outils whitelisted"| NMCP NMCP -->|"REST API key"| N8N ``` --- ## 2. Pourquoi ? — Enjeux et motivations ### Objectifs de l'AI Stack | Objectif | Solution | |----------|----------| | **Pas de vendor lock-in** | API Ollama-compatible, fournisseurs interchangeables (Codex / Gemini) | | **Sessions cross-provider** | `SessionRecord` partage l'historique entre modèles, fallback par injection | | **Contrôle des outils MCP** | Gateway whitelist + confirmation Telegram sur les actions destructrices | | **Profiles scopés** | Un même service expose des permissions différentes selon le profil (`error-analyst` vs `n8n-admin`) | | **RAG local** | Qdrant self-hosted, pas de fuite de documents privés vers le cloud | ### Modèles disponibles via CLI Ollama | Nom virtuel | Provider | ID réel | Notes | |-------------|----------|---------|-------| | `codex-max` | OpenAI Codex | `gpt-5.1-codex-max` | Frontier, agentique, tâches complexes | | `codex` | OpenAI Codex | `gpt-5.1-codex` | Standard agentique | | `codex-mini` | OpenAI Codex | `gpt-5.1-codex-mini` | Plus petit, plus économique | | `gemini-flash` | Google Gemini | `gemini-2.5-flash` | Rapide, low-cost | | `gemini-pro` | Google Gemini | `gemini-2.5-pro` | Le plus capable côté Gemini | | `-yolo` | Tous | — | Skip approval/confirmation flow | > **Caution - Mode YOLO** > > Le suffixe `-yolo` court-circuite l'approbation humaine. Les appels N8N depuis des workflows automatisés **doivent** utiliser un modèle `-yolo` (ex: `codex-yolo`) — sinon le CLI démarre en plan mode interactif et le webhook timeout à 120 s. ### Pourquoi Codex en provider par défaut ? | Critère | Codex (`gpt-5.1`) | Claude | Gemini | |---------|-------------------|--------|--------| | **Mode agentique natif** | Oui | Oui (via tool use) | Limité | | **Sessions persistantes** | `thread_id` natif | Limité | `session_id` natif | | **Coût pour les workflows** | Moyen | Élevé | Bas | | **Modèles `-yolo` (full-auto)** | Oui (`--full-auto`) | Non équivalent | Oui (`--yolo`) | Codex couvre les besoins agentiques (analyse, refactoring, génération de code). Gemini sert de fallback rapide / économique. Claude n'est plus servi par CLI Ollama : il reste utilisé directement via Claude Code côté poste développeur. --- ## 3. Comment ? — Mise en œuvre technique ### Qdrant : gestion des collections ```bash # Lister les collections curl http://localhost:6333/collections | jq '.result.collections' # Créer une collection curl -X PUT http://localhost:6333/collections/documents \ -H "Content-Type: application/json" \ -d '{ "vectors": { "size": 1536, "distance": "Cosine" } }' # Recherche vectorielle curl http://localhost:6333/collections/documents/points/search \ -H "Content-Type: application/json" \ -d '{ "vector": [0.1, 0.2, ...], "limit": 5 }' ``` > **Tip - Embeddings** > > Qdrant ne génère pas les embeddings, il les stocke et les recherche. Utiliser l'API OpenAI (`text-embedding-3-small`) ou un modèle local pour produire les vecteurs avant insertion. ### Multi-provider sessions (#14) CLI Ollama maintient un `SessionRecord` par `session_id` qui suit l'utilisateur à travers les fournisseurs. Si on commence une conversation sur `codex-max` puis on bascule vers `gemini-pro`, l'historique précédent est ré-injecté en prefix de prompt. ```mermaid flowchart TD Req["Requête · session_id, model"] Get["get_session_record() · charge SessionRecord"] Match{"Modèle inchangé ?"} Native["Native resume · Codex thread_id ou Gemini session_id"] Inject["Injection d'historique · _inject_history()"] Exec["Exécution CLI"] Save["record_turn() · met à jour SessionRecord + current_model"] Req --> Get --> Match Match -->|oui| Native --> Exec Match -->|non, ou pas d'ID| Inject --> Exec Exec --> Save ``` Champs de `SessionRecord` : `session_id`, `current_provider`, `current_model`, `codex_thread_id`, `gemini_session_id`, `turn_count`, `total_tokens`. Stocké en mémoire (pas de persistence Redis pour les SessionRecords eux-mêmes — l'historique des messages, lui, vit dans Claude Redis). > **Note - Limites MVP** > > La gestion multi-provider tourne uniquement en mode **non-streaming**. Les paths streaming n'enregistrent pas les `turn` (à corriger). Pas de TTL sur les SessionRecords ni de plafond sur la taille de l'historique injecté. ### Profile system CLI Ollama expose plusieurs **profiles** YAML qui scopent les outils MCP utilisables et les modèles autorisés. Un même service rend donc des permissions différentes selon le profil envoyé dans la requête. | Profile | Outils autorisés | Outils avec approbation | Knowledge base | |---------|------------------|-------------------------|----------------| | `error-analyst` | 5 read-only (`n8n_get_workflow`, `n8n_executions`, …) | aucun | DLQ format, workflow architecture | | `n8n-admin` | 5 read + 2 write | `n8n_update_partial_workflow`, `n8n_update_full_workflow` | Admin guide | > **Tip - Ceiling semantics** > > Un profile définit le **plafond** (`allowed_tools`). La requête peut **restreindre** davantage via `mcp_config`, mais jamais étendre au-delà du plafond. C'est ce qui permet d'utiliser le même endpoint pour des workflows à risques très différents. Les fichiers `.md` listés dans `knowledge_base` sont injectés dans le system prompt à chaque requête, donnant au modèle un contexte stable sans le réenvoyer à chaque tour. ### MCP Gateway (whitelist + négociation de transport) `mcp-gateway` est un reverse proxy MCP qui s'interpose entre `cli-ollama` et `n8n-mcp`. Il assure trois rôles : 1. **Whitelist d'outils côté serveur** — bloque tout outil hors de la liste avant que la requête atteigne `n8n-mcp`. 2. **Négociation de transport** — demande toujours du SSE en upstream, puis re-formate selon le `Accept` du client (Claude CLI préfère JSON, Codex CLI préfère SSE/`rmcp`). 3. **Bearer auth + isolation réseau** — `mcp-backend` n'est pas joignable depuis `ai-internal` autrement que via la gateway. **Outils whitelistés (20)** : tous les outils nécessaires (`n8n_list_workflows`, `n8n_get_workflow`, `n8n_validate_workflow`, `tools_documentation`, `search_nodes`, `get_node`, `validate_node`, `search_templates`, `get_template`, `validate_workflow`, `n8n_executions`, `n8n_health_check`, `n8n_create_workflow`, `n8n_update_full_workflow`, `n8n_update_partial_workflow`, `n8n_delete_workflow`, `n8n_deploy_template`, `n8n_autofix_workflow`, `n8n_test_workflow`, `n8n_workflow_versions`). **Outils critiques (7)** — passent la whitelist mais nécessitent confirmation Telegram avant exécution : `n8n_create_workflow`, `n8n_update_full_workflow`, `n8n_update_partial_workflow`, `n8n_delete_workflow`, `n8n_deploy_template`, `n8n_autofix_workflow`, `n8n_test_workflow`. La confirmation est gérée par `mcp_confirmation.py` côté `cli-ollama` qui poste un webhook au workflow `MCP Confirmation Handler`. ### N8N MCP bridge `n8n-mcp` est le serveur MCP qui expose réellement les outils N8N. Il s'authentifie auprès de l'API REST N8N via une API key (`N8N_MCP_API_KEY`), et exige un Bearer token (`N8N_MCP_AUTH_TOKEN`) côté client MCP. ```bash # Tester la chaîne complète depuis le host (via gateway) curl -X POST http://127.0.0.1:3001/mcp \ -H "Authorization: Bearer ${N8N_MCP_AUTH_TOKEN}" \ -H "Content-Type: application/json" \ -d '{"jsonrpc":"2.0","id":1,"method":"tools/list","params":{}}' ``` > **Caution - Claude CLI --print mode** > > Le mode `--print` (headless) de Claude CLI **ne charge pas** les serveurs HTTP listés dans `.mcp.json`. La configuration MCP est posée à runtime via `claude mcp add --scope user` dans l'`entrypoint.sh` du container, qui écrit dans `.claude.json`. Le serveur est enregistré comme `n8n-local` (préfixe d'outils : `mcp__n8n-local__`). ### Workflow d'approbation (mode non-yolo) ```mermaid flowchart TD Req(["Requête · non-YOLO"]) subgraph Ollama["CLI Ollama"] direction TB L1["Détecte tool_use ou question"] L2["Webhook N8N + état Pending"] L3["Attend approbation · timeout 5 min"] end subgraph Tg["N8N Telegram Orchestrator"] direction TB T1["Affiche le plan ou la question"] T2["Boutons inline keyboard"] end Decision{Décision} Exec["Exécution réelle"] Reject["Réponse 'Rejected'"] Result["Retour résultat"] Req --> Ollama --> Tg --> Decision Decision -->|Approved| Exec Decision -->|Rejected/Expired| Reject Exec --> Result ``` Hooks pluggables (`pre_tool_use`, `post_tool_use`, `on_response`, `on_error`) permettent d'ajouter des règles transverses sans toucher aux providers. ### Appel depuis N8N ```javascript // Intent detection (mode non-streaming, sans approbation) { "url": "http://cli-ollama:11434/api/generate", "method": "POST", "body": { "model": "codex-yolo", "prompt": "Analyse ce message Telegram et retourne un JSON …", "stream": false } } // Avec profile et MCP outils restreints { "model": "codex-yolo", "prompt": "Inspecte le workflow X et propose un fix", "profile": "error-analyst", "mcp_config": "{\"allowed_tools\": [\"n8n_get_workflow\", \"n8n_executions\"]}" } ``` ### Ressources système | Service | Memory limit | CPU | |---------|--------------|-----| | Qdrant | 4 GB | 2 vCPU | | CLI Ollama | 4 GB | 2 vCPU | | Claude Redis | 512 MB | — | | MCP Gateway | 128 MB | — | | N8N MCP | 256 MB | — | > **Note - Pas de GPU** > > Cette stack n'exécute aucun modèle localement. Les calculs ML se font dans le cloud (Codex / Gemini), Qdrant fait du calcul vectoriel CPU. Le VPS n'a pas besoin de GPU. --- ## 4. Et si ? — Perspectives et limites ### Limites actuelles | Limite | Impact | Mitigation | |--------|--------|------------| | **Qdrant peu peuplé** | Pas de RAG production aujourd'hui | Import progressif depuis l'Obsidian vault | | **Embeddings via API externe** | Dépendance OpenAI pour la vectorisation | Modèle local d'embeddings envisageable | | **Pas de streaming SessionRecord** | Les conversations en streaming ne capitalisent pas l'historique | Roadmap : recorder les chunks streaming en mémoire | | **SessionRecord en mémoire** | Perte au restart container | Persistence Redis prévue post-MVP | | **Codex / Gemini cloud** | Coût + dépendance externe | Caching Redis pour intent detection, modèles `-yolo` pour les hot paths | ### Scénarios d'évolution **Si le RAG entre en production** : - Worker N8N qui ingère l'Obsidian vault dans une collection Qdrant dédiée. - Profile `obsidian-rag` côté CLI Ollama avec un knowledge base + outils de recherche. - Pattern : embeddings via OpenAI, recherche Qdrant, prompt enrichi côté CLI Ollama. **Si je veux ajouter un autre provider** : - Implémenter `ProviderProtocol` dans `services/providers/.py`. - Ajouter le mapping virtual-name → real-model dans `config.py`. - Le SessionRecord et les hooks fonctionnent automatiquement (interface unifiée `ExecutionMessage`). **Si les coûts API montent** : - Étendre le caching Claude Redis aux réponses fréquentes (intent detection notamment). - Router les requêtes simples vers `gemini-flash` (le moins cher) via l'IA Router. - Activer un rate-limit côté `cli-ollama` par session_id. **Si la sécurité MCP doit être plus stricte** : - Réduire la whitelist gateway aux 5 outils read-only seulement. - Ajouter des profiles spécifiques par cas d'usage (au lieu d'un `n8n-admin` permissif). - Étendre la confirmation Telegram à plus d'outils (actuellement 7 critiques sur 20). ### Commandes de dépannage ```bash # Health checks curl http://localhost:6333/healthz # Qdrant curl http://localhost:11434/api/tags # CLI Ollama docker logs cli-ollama --tail 100 # Approbations en attente curl http://cli-ollama:11434/api/approvals curl http://cli-ollama:11434/api/questions # Tester la chaîne MCP gateway → n8n-mcp curl -X POST http://127.0.0.1:3001/mcp \ -H "Authorization: Bearer $N8N_MCP_AUTH_TOKEN" \ -d '{"jsonrpc":"2.0","id":1,"method":"tools/list","params":{}}' | jq .result.tools[].name # Vérifier l'isolation réseau docker network inspect mcp-backend | jq '.[0].Containers' ``` --- ## Pages liées ### Infrastructure - [Architecture VPS](/fr/infrastructure/architecture-vps/) — Vue d'ensemble - [N8N en mode Queue](/fr/infrastructure/n8n-queue-mode/) — Workers consommateurs ### Workflows - [Système conversationnel](/fr/workflows/systeme-conversationnel/) — Sessions multi-tours via CLI Ollama - [Telegram Orchestrator](/fr/workflows/telegram-orchestrator/) — IA Router et confirmation MCP - [Notification Hub](/fr/workflows/notification-hub/) — Routage des alertes IA ### Référence - [Glossaire](/fr/reference/glossary/) — RAG, Embeddings, Vector Database, MCP, LLM ## Metadonnees agent - Cet article est issu du blog GuiGPaP Lab. - Contexte global du blog: https://blog.guigpap.com/llms.txt - Contact auteur: https://odoo.guigpap.com/mon-cv - Licence: CC-BY-SA 4.0