Date : 14/02/2026 00:00 Status : EN ATTENTE VALIDATION
Transformer ulias-org d'un outil technique d'orchestration en un assistant conversationnel intelligent capable de :
Philosophie : contrecarrer la complexité par des microservices spécialisés. Chaque brique fait une chose bien, l'intelligence est dans l'orchestration.
┌─────────────────────────────────────────────────────────────────────┐
│ UTILISATEUR (CEO) │
│ │
│ Chat Web ◄──────────► ntfy push ◄──────────► Mobile/Desktop │
│ (ulias-web) (notifications) │
└───────────┬─────────────────────────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────────────┐
│ CONVERSATIONS SERVICE │
│ │
│ ┌──────────────┐ ┌──────────────┐ ┌──────────────┐ │
│ │ Conversations│ │ Messages │ │ Attachments │ │
│ │ (threads) │ │ (history) │ │ (files) │ │
│ └──────────────┘ └──────────────┘ └──────────────┘ │
│ │
│ - CRUD conversations │
│ - Persist tous les messages (user + agent + events) │
│ - Sidebar avec liste conversations │
│ - Recherche full-text dans l'historique │
│ - Fichiers joints par conversation │
└───────────┬─────────────────────────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────────────┐
│ INTELLIGENCE LAYER │
│ │
│ ┌──────────────┐ ┌──────────────┐ ┌──────────────┐ │
│ │ Classifier │ │ Memory │ │ Context │ │
│ │ (triage) │ │ (embeddings) │ │ (window) │ │
│ └──────────────┘ └──────────────┘ └──────────────┘ │
│ │
│ Classifier : analyse le message et décide du mode │
│ ┌─────────────────────────────────────────────────┐ │
│ │ "quelle heure est-il ?" → LIVE (réponse directe) │
│ │ "check docker status" → LIVE (1 tool call, <30s) │
│ │ "crée-moi un site pizza" → BRIEFING (clarification) │
│ │ "analyse les logs du mois" → ASYNC (solo, notifie quand fini)│
│ │ "déploie la nouvelle version"→ APPROVAL (demande permission) │
│ └─────────────────────────────────────────────────┘ │
│ │
│ Memory : mémoire longue via embeddings (pgvector + nomic) │
│ - Indexe chaque conversation terminée │
│ - Rappelle les contextes pertinents dans les futurs échanges │
│ - "la dernière fois tu m'as dit que..." → retrouve le contexte │
│ │
│ Context : fenêtre de contexte pour le LLM │
│ - Derniers N messages de la conversation en cours │
│ - Profil CEO (préférences, style, habitudes) │
│ - Résumé des conversations récentes pertinentes (via Memory) │
└───────────┬─────────────────────────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────────────┐
│ ORCHESTRATION (ulias-org existant) │
│ │
│ Interpreter → Agent Router → Agent Executor → Tool Calls │
│ │
│ 31 agents, 8 teams, 36 tools │
│ Pipelines : verification_chain, code_and_deploy, create_* │
│ Mode briefing pour projets complexes │
└───────────┬─────────────────────────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────────────┐
│ SERVICES (microservices existants) │
│ │
│ connectors-api │ ai-orchestrator │ browser-connector │
│ (31 APIs, SSH, │ (GPU routing, │ (web scraping, │
│ VPN, Supabase) │ 10 outils IA) │ LinkedIn) │
│ │ │ │
│ GitLab CI/CD │ Notifications │ Supabase (BDD) │
│ (code, deploy) │ (ntfy, mail) │ (data, auth, storage) │
└─────────────────────────────────────────────────────────────────────┘
Objectif : tout ce qui se dit est sauvegardé, on retrouve ses échanges.
-- Conversations (threads)
CREATE TABLE agents.conversations (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
user_id TEXT NOT NULL,
title TEXT, -- Auto-généré depuis le 1er message, éditable
created_at TIMESTAMPTZ DEFAULT now(),
updated_at TIMESTAMPTZ DEFAULT now(),
last_message_at TIMESTAMPTZ,
message_count INT DEFAULT 0,
status TEXT DEFAULT 'active', -- active, archived
summary TEXT, -- Résumé auto (embedding source)
metadata JSONB DEFAULT '{}' -- tags, pinned, etc.
);
-- Chat messages (distinct de agents.messages qui est pour le debug LLM)
CREATE TABLE agents.chat_messages (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
conversation_id UUID NOT NULL REFERENCES agents.conversations(id) ON DELETE CASCADE,
user_id TEXT NOT NULL,
role TEXT NOT NULL, -- user, assistant, system, event
content TEXT,
event_type TEXT, -- objective_created, routing, tool_call, complete, etc.
event_data JSONB, -- Données brutes de l'événement
objective_id UUID, -- Lien vers l'objectif si applicable
attachments JSONB DEFAULT '[]', -- [{name, url, type, size}]
created_at TIMESTAMPTZ DEFAULT now()
);
CREATE INDEX idx_chat_messages_conv ON agents.chat_messages(conversation_id, created_at);
CREATE INDEX idx_conversations_user ON agents.conversations(user_id, updated_at DESC);
| Endpoint | Méthode | Description |
|---|---|---|
| /api/conversations | GET | Liste conversations (sidebar) |
| /api/conversations | POST | Nouvelle conversation |
| /api/conversations/:id | GET | Détails conversation |
| /api/conversations/:id | PATCH | Rename, archive |
| /api/conversations/:id | DELETE | Supprimer |
| /api/conversations/:id/messages | GET | Messages paginés |
chat_messageschat_messages avec role: 'event'role: 'assistant'┌──────────────────────────────────────────────────┐
│ [+] Nouvelle conversation │
│──────────────────────────────────────────────────│
│ 📌 Check Docker status 14/02 00:15 │
│ Création site pizza 13/02 21:30 │
│ Analyse logs nginx 13/02 19:00 │
│ ... │
│──────────────────────────────────────────────────│
│ ZONE CHAT │
│ │
│ [messages de la conversation sélectionnée] │
│ │
│──────────────────────────────────────────────────│
│ [📎] [Envoyer un message...] [➤] │
└──────────────────────────────────────────────────┘
last_message_at DESC| Projet | Fichier | Changement |
|---|---|---|
| ulias-org | packages/server/src/db/schema.sql | Tables conversations + chat_messages |
| ulias-org | packages/server/src/db/index.ts | CRUD conversations + messages |
| ulias-org | packages/server/src/ws/handler.ts | Sauvegarder messages dans chat_messages |
| ulias-org | packages/server/src/routes/ | Nouveaux endpoints conversations |
| ulias-org-web | src/routes/index.tsx | Refacto → sidebar + conversation view |
| ulias-org-web | src/components/sidebar/ | Nouveau composant sidebar conversations |
| ulias-org-web | src/routes/layout.tsx | Intégrer sidebar dans le layout |
Objectif : le système comprend le contexte, se souvient, et décide seul du mode d'exécution.
Au lieu de tout envoyer à l'Interpreter (LLM lourd), un premier tri rapide :
type MessageMode = 'live' | 'briefing' | 'async' | 'approval' | 'chat';
function classify(message: string, context: ConversationContext): MessageMode {
// Patterns rapides (sans LLM)
if (isSimpleQuestion(message)) return 'chat'; // "ça va ?", "merci"
if (isQuickAction(message)) return 'live'; // "docker ps", "status"
if (isProjectCreation(message)) return 'briefing'; // "crée un site"
if (needsPermission(message)) return 'approval'; // "supprime", "redémarre"
// Heuristique sur la complexité estimée
if (estimatedDuration(message) > 60) return 'async'; // > 1 min → mode solo
return 'live'; // Défaut : réponse en direct
}
Comportement par mode : | Mode | Comportement | Notification | |------|-------------|--------------| | chat | Réponse LLM directe (pas d'agent, juste conversation) | Non | | live | Agent exécute, événements en temps réel dans le chat | Non | | briefing | Questions de clarification avant exécution | Non | | async | Agent travaille en solo, résultat affiché plus tard | ntfy quand fini | | approval | Demande d'approbation avant action destructive | ntfy si urgent |
Utiliser pgvector + nomic-embed-text (déjà dispo sur GPU1) :
-- Ajouter colonne embedding aux conversations
ALTER TABLE agents.conversations ADD COLUMN embedding vector(768);
-- Index pour recherche de similarité
CREATE INDEX idx_conv_embedding ON agents.conversations
USING ivfflat (embedding vector_cosine_ops) WITH (lists = 20);
Flow :
conversations.embeddingExemple :
User: "remets en place le site pizza comme la dernière fois"
→ Embedding → Recherche similarité → Trouve conversation "Création site pizza" du 13/02
→ Injecte le résumé dans le contexte de l'agent
→ L'agent sait exactement quoi faire
Pour chaque appel LLM, construire un contexte riche :
function buildContext(conversationId: string, userId: string): SystemContext {
return {
// Conversation en cours (derniers 20 messages)
recentMessages: getRecentMessages(conversationId, 20),
// Profil CEO (préférences apprises)
profile: getCeoProfile(userId),
// Conversations similaires (top 3 par embedding)
relatedConversations: searchSimilarConversations(currentMessage, 3),
// État système (résumé)
systemState: {
activeObjectives: getActiveObjectives(userId),
recentCompletions: getRecentCompletions(userId, 5),
}
};
}
Quand un objectif async se termine :
// Dans le job completion handler
if (objective.mode === 'async') {
await notify({
title: `✅ ${objective.title}`,
body: truncate(result, 200),
click: `https://ulias-web.33800.nowhere84.com/?conv=${conversationId}`,
priority: 'default'
});
}
Quand l'agent a besoin d'input :
if (decision.urgency === 'high' || decision.urgency === 'critical') {
await notify({
title: `⚠️ Décision requise`,
body: decision.question,
click: `https://ulias-web.33800.nowhere84.com/decisions`,
priority: 'high'
});
}
Objectif : créer tout type de support numérique depuis une conversation.
| Pipeline | Agents | Résultat |
|---|---|---|
| create_frontend | gitlab-dev → qwik-dev → pg-dev | App Qwik + repo GitLab |
| create_backend | gitlab-dev → elysia-dev → pg-dev | API Elysia + repo GitLab |
| create_fullstack | gitlab-dev → elysia-dev → pg-dev → qwik-dev | App complète |
| create_static | storage-dev → qwik-dev → storage-dev | Site statique (Supabase Storage) |
| create_script | coder | Script autonome (bash, python, etc.) |
| create_document | documenter | Doc markdown, PDF, rapport |
| create_config | coder → deployer | Config infra (nginx, docker, CI/CD) |
| create_automation | coder → deployer | Cron, webhook, routine planifiée |
Chaque création produit des assets liés à la conversation :
CREATE TABLE agents.conversation_assets (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
conversation_id UUID REFERENCES agents.conversations(id),
user_id TEXT NOT NULL,
asset_type TEXT NOT NULL, -- repo, file, site, script, document
name TEXT NOT NULL,
url TEXT, -- URL de l'asset (GitLab, Supabase Storage, etc.)
metadata JSONB DEFAULT '{}',
created_at TIMESTAMPTZ DEFAULT now()
);
Affiché dans le chat comme une carte spéciale :
┌─────────────────────────────────────┐
│ 📦 Projet créé : pizza-site │
│ GitLab : https://gitlab.../pizza │
│ URL : https://pizza.33800.no... │
│ [Voir] [Modifier] [Supprimer] │
└─────────────────────────────────────┘
Le vrai pouvoir : itérer sur les créations via la conversation.
User: "crée-moi un site vitrine pour ma pizzeria"
→ Briefing (5 questions : nom, style, pages, couleurs, contenu)
→ Pipeline create_frontend → site déployé
User: "ajoute une page menu avec les prix"
→ Contexte conversation → sait quel projet, quel repo
→ Agent modifie le code → push → déploiement auto
User: "change le rouge en bleu"
→ Idem, modification ciblée
User: "ajoute un formulaire de commande"
→ Classifier détecte que c'est plus complexe (backend nécessaire)
→ Propose d'ajouter un backend API
→ Pipeline create_backend en complément
| Phase | Effort | Impact | Dépendances |
|---|---|---|---|
| 1a-1c : Schema + endpoints conversations | 2-3h | HAUT | Aucune |
| 1d : Frontend sidebar + persistence | 3-4h | HAUT | 1a-1c |
| 1e : Auto-titre conversations | 30min | MOYEN | 1a-1c |
| 2a : Classifier de messages | 2h | HAUT | Phase 1 |
| 2b : Mémoire embeddings | 2-3h | HAUT | Phase 1 |
| 2c : Contexte fenêtre | 1-2h | HAUT | 2b |
| 2d : Notification bridge async | 1h | MOYEN | Phase 1 |
| 3a : Nouveaux pipelines | 2-3h | MOYEN | Phase 2 |
| 3b : Asset management | 2h | MOYEN | Phase 1 |
| 3c : Itération conversationnelle | 3-4h | TRES HAUT | Phase 2 + 3b |
Total estimé : ~20-25h réparties sur les 3 phases.
Phase 1 seule rend déjà l'outil utilisable au quotidien.