From b0d222b81a484e9afc4b666dc5bfe87fa0f4ddce Mon Sep 17 00:00:00 2001 From: Bruno Charest Date: Mon, 20 Jul 2026 14:38:42 -0400 Subject: [PATCH] Add Notion Agent and FlowDeck integration docs Document Notion Agent (2026) concepts and a FlowDeck Agent design that orchestrates tool calls through existing FastAPI routers, plus a chat UI screenshot. --- docs/Flowdeck_Agent_integration.md | 702 ++++++++++++++++++++++++ docs/Guide_Complet_Notion_Agent_2026.md | 464 ++++++++++++++++ images/chat/Notion_chat_main_page.png | Bin 0 -> 44992 bytes 3 files changed, 1166 insertions(+) create mode 100644 docs/Flowdeck_Agent_integration.md create mode 100644 docs/Guide_Complet_Notion_Agent_2026.md create mode 100644 images/chat/Notion_chat_main_page.png diff --git a/docs/Flowdeck_Agent_integration.md b/docs/Flowdeck_Agent_integration.md new file mode 100644 index 0000000..5c09276 --- /dev/null +++ b/docs/Flowdeck_Agent_integration.md @@ -0,0 +1,702 @@ +# FlowDeck Agent — Intégration & Fonctionnement + +> **Version:** 1.0 · **Date:** 2026-07-20 · **Auteur:** Hermes-Deepin +> **Cible:** Agent IA natif pour FlowDeck, inspiré de Notion Agent (2026) +> **Prérequis:** FlowDeck v4.0.x · FastAPI · SQLite · Gitea/GitHub + +--- + +## Table des matières + +1. [Vision & Objectifs](#1-vision--objectifs) +2. [Concept : de l'IA qui répond à l'IA qui réalise](#2-concept) +3. [Positionnement dans l'architecture FlowDeck](#3-positionnement) +4. [Placement dans l'interface (sidebar 🤖 Agents)](#4-placement-interface) +5. [Boucle d'exécution de l'agent](#5-boucle-dexécution) +6. [Sources de contexte FlowDeck](#6-sources-de-contexte) +7. [Modèle de données (nouvelles tables)](#7-modèle-de-données) +8. [Routes & API Agent](#8-routes--api) +9. [Le service AgentEngine](#9-service-agentengine) +10. [Système d'outils (Tools)](#10-système-doutils) +11. [Permissions & sécurité](#11-permissions--sécurité) +12. [Custom Agents & déclencheurs](#12-custom-agents) +13. [Skills réutilisables](#13-skills) +14. [Cas d'usage FlowDeck](#14-cas-dusage) +15. [Plan de migration](#15-plan-de-migration) +16. [Limitations](#16-limitations) + +--- + +## 1. Vision & Objectifs + +FlowDeck Agent transforme FlowDeck d'un clone Notion **passif** (où l'utilisateur crée manuellement collections, pages, propriétés) en un espace de travail **agentique** où une IA peut : + +- Comprendre un objectif exprimé en langage naturel +- Lire le contexte du workspace (collections, pages, propriétés, issues Gitea) +- Planifier une suite d'actions +- **Exécuter** ces actions via l'API interne (créer collections, pages, propriétés, vues, sync Gitea) +- Retourner un résultat consolidé + +L'agent réutilise l'infrastructure existante : **il n'appelle pas directement la DB**, il passe par les mêmes routers FastAPI (`/db/*`, `/board/api/*`) que l'utilisateur, garantissant cohérence, validation et respect du `PermissionManager`. + +Cette fonctionnalité concrétise la ligne `v5.0.0 — AI Assistants` de la roadmap, en s'inspirant directement du modèle Notion Agent. + +--- + +## 2. Concept : de l'IA qui répond à l'IA qui réalise {#2-concept} + + + +Assistant classique : +Utilisateur → Prompt → Réponse texte +FlowDeck Agent : +Utilisateur → Objectif → Planification → Collecte contexte → Actions API → Résultat + + +| Notion AI | FlowDeck Agent | +|-----------|----------------| +| Répond, résume, réécrit | Planifie, recherche, **agit** | +| Bloc de texte | Crée collections, pages, propriétés, vues | +| Sans état | Historique de conversation + snapshots | +| Aucune action DB | Modifie le workspace via routers FastAPI | + +--- + +## 3. Positionnement dans l'architecture FlowDeck {#3-positionnement} + +L'agent s'insère comme un **nouveau service** et un **nouveau router**, sans modifier le cœur existant. + + +``` +┌──────────────────────────────────────────────────────────────────┐ +│ CLIENT (Browser) │ +│ ┌─────────────────────────────────────────────────────────┐ │ +│ │ agent_panel.html — Panneau conversation (Alpine.js) │ │ +│ │ agent_message.html — Fragment message (HTMX streaming) │ │ +│ └─────────────────────────────────────────────────────────┘ │ +└──────────────────────────┬───────────────────────────────────────┘ + │ HTTP (SSE stream / JSON) +┌──────────────────────────▼───────────────────────────────────────┐ +│ FASTAPI (Python 3.11+) │ +│ ┌─────────────────────────────────────────────────────────┐ │ +│ │ ROUTERS │ │ +│ │ ├─ agent.py ← NOUVEAU /api/agent/* │ │ +│ │ └─ (existants: board.py, api.py, collections.py, ...) │ │ +│ └─────────────────────────────────────────────────────────┘ │ +│ ┌─────────────────────────────────────────────────────────┐ │ +│ │ SERVICES │ │ +│ │ ├─ AgentEngine ← NOUVEAU (orchestrateur) │ │ +│ │ ├─ ToolRegistry ← NOUVEAU (outils actionnables) │ │ +│ │ ├─ LLMClient ← NOUVEAU (GPT/Claude/Gemini/local) │ │ +│ │ ├─ ContextBuilder ← NOUVEAU (collecte workspace) │ │ +│ │ ├─ SearchEngine (réutilisé — FTS5) │ │ +│ │ ├─ PermissionManager (réutilisé — ACL) │ │ +│ │ └─ GiteaClient (réutilisé — API Gitea) │ │ +│ └─────────────────────────────────────────────────────────┘ │ +│ ┌─────────────────────────────────────────────────────────┐ │ +│ │ DATA LAYER — SQLite │ │ +│ │ ├─ agents ← NOUVEAU │ │ +│ │ ├─ agent_conversations ← NOUVEAU │ │ +│ │ ├─ agent_messages ← NOUVEAU │ │ +│ │ ├─ agent_actions ← NOUVEAU (journal audit) │ │ +│ │ ├─ agent_skills ← NOUVEAU │ │ +│ │ └─ (tables existantes: collections, pages, ...) │ │ +│ └─────────────────────────────────────────────────────────┘ │ +└──────────────────────────┬───────────────────────────────────────┘ + │ HTTPS + ┌──────────────────┴──────────────────┐ + ▼ ▼ +┌──────────────────┐ ┌──────────────────────────┐ +│ GITEA API REST │ │ PROVIDER LLM │ +│ (contexte issues)│ │ OpenAI / Anthropic / │ +│ │ │ Gemini / Ollama local │ +└──────────────────┘ └──────────────────────────┘ +``` + +**Principe clé — Tool-calling en boucle fermée :** +Le `LLMClient` ne touche jamais la base. Il émet des *intentions d'outil* (function calls). L'`AgentEngine` les traduit en appels HTTP internes vers les routers existants, exécutés avec la session et les permissions de l'utilisateur. + +--- + +## 4. Placement dans l'interface (sidebar 🤖 Agents) {#4-placement-interface} + +Le sidebar FlowDeck contient déjà une entrée **🤖 Agents** (voir ARCHITECTURE §5.2). Elle devient le point d'entrée de l'agent. + + +``` +┌────────────┬─────────────────────────────────────────────────┐ +│ SIDEBAR │ CONTENU PRINCIPAL │ +│ │ │ +│ 🕒 Recents │ ┌─ Page / Collection courante ──────────────┐ │ +│ ⭐ Favoris │ │ │ │ +│ 🤖 Agents ◄┼── clic ouvre le panneau agent (droite) │ │ +│ 👥 Shared │ └────────────────────────────────────────────┘ │ +│ │ ┌────────────┐ │ +│ │ │ 🤖 Agent │ │ +│ │ ├────────────┤ │ +│ │ │ Historique │ │ +│ │ │ Réponses │ │ +│ │ │ ───────── │ │ +│ │ │ @Sources │ │ +│ │ │ 📎 Fichier │ │ +│ │ │ [Modèle ▾] │ │ +│ │ │ [Saisie... ]│ │ +│ │ └────────────┘ │ +└────────────┴─────────────────────────────────────────────────┘ +``` + +Deux modes d'accès, comme Notion Agent : + +- **Icône flottante** (coin inférieur droit) — accès global rapide +- **Section 🤖 Agents** du sidebar — liste des agents (personnel + custom) + +L'affichage de la section 🤖 Agents est déjà prévu dans les 3 scénarios de compte (A/B/C) — voir ARCHITECTURE §6.5.4 : **toujours visible**. + +--- + +## 5. Boucle d'exécution de l'agent {#5-boucle-dexécution} + +Adaptation du modèle Notion (Compréhension → Contexte → Raisonnement → Action) à l'architecture FlowDeck : + +``` +Utilisateur formule un objectif + │ + ▼ +┌─────────────────────────────────────────────┐ +│ 1. COMPRÉHENSION (AgentEngine) │ +│ Parse objectif + instructions custom │ +└──────────────────┬──────────────────────────┘ + ▼ +┌─────────────────────────────────────────────┐ +│ 2. COLLECTE CONTEXTE (ContextBuilder) │ +│ - Page/collection courante (cookie ws) │ +│ - @mentions explicites │ +│ - SearchEngine FTS5 (pages pertinentes) │ +│ - Issues Gitea liées (GiteaClient) │ +│ - Fichiers joints │ +│ ⚠ filtré par PermissionManager │ +└──────────────────┬──────────────────────────┘ + ▼ +┌─────────────────────────────────────────────┐ +│ 3. RAISONNEMENT (LLMClient + ToolRegistry) │ +│ LLM reçoit: objectif + contexte + schéma │ +│ des outils disponibles (function calling) │ +│ → émet un plan / une intention d'outil │ +└──────────────────┬──────────────────────────┘ + ▼ +┌─────────────────────────────────────────────┐ +│ 4. ACTION (AgentEngine → routers FastAPI) │ +│ Exécute l'appel outil → route interne │ +│ POST /db → create_collection │ +│ POST /db/{id}/properties → add_property │ +│ POST /db/{id}/pages → create_page │ +│ Journalise dans agent_actions │ +└──────────────────┬──────────────────────────┘ + ▼ + Résultat outil réinjecté dans le LLM + │ + ┌────────┴────────┐ + │ Objectif atteint?│ + └────┬────────┬────┘ + non │ │ oui + │ ▼ + (retour étape 3) ┌─────────────────────────────┐ + │ 5. RÉSULTAT consolidé + lien │ + │ vers les objets créés │ + └─────────────────────────────┘ +``` + +Cette boucle **ReAct** (Reason + Act) se répète jusqu'à `max_iterations` (défaut : 12) ou atteinte de l'objectif. + +--- + +## 6. Sources de contexte FlowDeck {#6-sources-de-contexte} + +Correspondance avec les sources Notion Agent, adaptées aux entités FlowDeck : + +| Source Notion | Équivalent FlowDeck | Mécanisme | +|---------------|---------------------|-----------| +| Pages, sous-pages | `pages`, `collection_pages` (parent_id) | `SearchEngine` FTS5 + lecture directe | +| Bases de données | `collections` + `collection_properties` | Lecture schéma `schema_json` | +| Wikis / docs internes | Blocs éditeur (`content_json`) | Parsing blocs | +| Jira | **Issues Gitea/GitHub** | `GiteaClient` (issues, labels, milestones) | +| Slack / Drive | Fichiers uploadés (`/api/files/`) | Lecture `files` property | +| Fichiers joints | Upload local-workspace | PDF/MD parsing | +| `@Projet Alpha` | `@collection` / `@page` / `@repo` | Résolution de mention | + +**Résolution des mentions** — L'utilisateur peut cibler : + + +@collection:Roadmap → force le contexte sur une collection +@page:123 → une page précise +@repo:bruno/flowdeck → issues d'un repo Gitea +@ws → workspace courant complet + + +Le `ContextBuilder` respecte strictement le `PermissionManager` : l'agent ne voit **que** ce que l'utilisateur peut voir (même token, mêmes ACL — voir ARCHITECTURE §6.2). + +--- + +## 7. Modèle de données (nouvelles tables) {#7-modèle-de-données} + +```sql +-- Définition d'un agent (personnel ou custom) +CREATE TABLE agents ( + id INTEGER PRIMARY KEY AUTOINCREMENT, + workspace_id INTEGER REFERENCES workspaces(id), + name TEXT NOT NULL DEFAULT 'FlowDeck Agent', + icon TEXT DEFAULT '🤖', + agent_type TEXT NOT NULL DEFAULT 'personal', + -- 'personal' | 'custom' + description TEXT DEFAULT '', + + -- Instructions personnalisées (ton, format, règles métier) + system_instructions TEXT DEFAULT '', + + -- Modèle IA par défaut + model TEXT DEFAULT 'claude-opus-4-8', + -- gpt-* | claude-* | gemini-* | ollama:* + + -- Périmètre custom agent : collections/repos autorisés + scope_json TEXT NOT NULL DEFAULT '{}', + -- {collections:[3,7], repos:["bruno/flowdeck"], tools:["create_page"]} + + -- Déclencheurs (custom agents) + trigger_json TEXT NOT NULL DEFAULT '{}', + -- {type:"schedule", cron:"0 9 * * 1"} | {type:"webhook", event:"issue.opened"} + + is_active BOOLEAN NOT NULL DEFAULT 1, + created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, + created_by INTEGER REFERENCES users(id), + UNIQUE(workspace_id, name) +); + +-- Conversations (fils de discussion, comme Notion history) +CREATE TABLE agent_conversations ( + id INTEGER PRIMARY KEY AUTOINCREMENT, + agent_id INTEGER NOT NULL REFERENCES agents(id) ON DELETE CASCADE, + user_id INTEGER NOT NULL REFERENCES users(id), + title TEXT DEFAULT 'New conversation', + -- Contexte de départ (page/collection ouverte à l'invocation) + context_json TEXT NOT NULL DEFAULT '{}', + created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, + updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP +); + +-- Messages d'une conversation +CREATE TABLE agent_messages ( + id INTEGER PRIMARY KEY AUTOINCREMENT, + conversation_id INTEGER NOT NULL REFERENCES agent_conversations(id) ON DELETE CASCADE, + role TEXT NOT NULL, + -- 'user' | 'assistant' | 'tool' | 'system' + content TEXT NOT NULL DEFAULT '', + -- Pour role='tool' : nom de l'outil + payload + résultat + tool_calls_json TEXT DEFAULT '[]', + model TEXT, + tokens_used INTEGER DEFAULT 0, + created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP +); + +-- Journal d'audit : CHAQUE action modifiant le workspace +CREATE TABLE agent_actions ( + id INTEGER PRIMARY KEY AUTOINCREMENT, + conversation_id INTEGER NOT NULL REFERENCES agent_conversations(id) ON DELETE CASCADE, + tool_name TEXT NOT NULL, + -- create_collection | create_page | add_property | ... + target_type TEXT, + -- collection | page | property | view | issue + target_id TEXT, + payload_json TEXT NOT NULL DEFAULT '{}', + result_json TEXT NOT NULL DEFAULT '{}', + status TEXT NOT NULL DEFAULT 'success', + -- success | error | reverted + -- Snapshot avant modification → permet le rollback + undo_snapshot_json TEXT DEFAULT '{}', + created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, + executed_by INTEGER REFERENCES users(id) +); + +-- Skills réutilisables (mini-prompts) +CREATE TABLE agent_skills ( + id INTEGER PRIMARY KEY AUTOINCREMENT, + workspace_id INTEGER REFERENCES workspaces(id), + name TEXT NOT NULL, + -- "Génération de PRD", "Analyse SWOT", "Préparation sprint" + description TEXT DEFAULT '', + prompt_template TEXT NOT NULL, + -- Outils que la skill peut invoquer + allowed_tools_json TEXT NOT NULL DEFAULT '[]', + created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, + created_by INTEGER REFERENCES users(id), + UNIQUE(workspace_id, name) +); + +CREATE INDEX idx_agent_conv_user ON agent_conversations(user_id, updated_at); +CREATE INDEX idx_agent_msg_conv ON agent_messages(conversation_id, created_at); +CREATE INDEX idx_agent_action_conv ON agent_actions(conversation_id); + + +Intégration au diagramme de relations existant + + +users ──1:N──► agent_conversations ──1:N──► agent_messages + │ │ + │ └──1:N──► agent_actions ──► (collections/pages/...) + │ +workspaces ──1:N──► agents ──1:N──► agent_conversations + │ + └──1:N──► agent_skills + +``` + + +## 8. Routes & API Agent {#8-routes--api} + +Nouveau router agent.py, préfixe /api/agent. + +# Agents (définitions) +``` +GET /api/agent → Liste agents du workspace +POST /api/agent → Créer custom agent +GET /api/agent/{agent_id} → Détail agent +PUT /api/agent/{agent_id} → Update (instructions, scope, model) +DELETE /api/agent/{agent_id} → Supprimer custom agent +``` + +# Conversations +``` +GET /api/agent/conversations → Historique des conversations +POST /api/agent/conversations → Nouvelle conversation +GET /api/agent/conversations/{id} → Messages d'une conversation +DELETE /api/agent/conversations/{id} → Supprimer conversation +``` + +# Exécution (cœur) +``` +POST /api/agent/conversations/{id}/run → Envoyer un objectif (SSE stream) + body: {message, model?, mentions?, files?, skill_id?} + → stream: reasoning → tool_call → tool_result → ... → final +``` + +# Actions & audit +``` +GET /api/agent/conversations/{id}/actions → Journal des actions +POST /api/agent/actions/{action_id}/undo → Annuler une action (rollback) +``` + +# Skills +``` +GET /api/agent/skills → Liste skills +POST /api/agent/skills → Créer skill +POST /api/agent/skills/{id}/apply → Appliquer une skill +``` + +# Custom agents — déclencheurs +``` +POST /api/agent/{agent_id}/trigger → Déclenchement manuel +``` + +# (déclenchement auto via scheduler interne + webhooks.py) + + +Streaming — La route /run renvoie un flux Server-Sent Events (SSE) pour afficher en temps réel le raisonnement et chaque action, à la manière de l'interface conversationnelle Notion. Le frontend (Alpine.js) consomme le flux et met à jour agent_panel.html. + +------------------------- + +## 9. Le service AgentEngine {#9-service-agentengine} + +Orchestrateur central. Pseudo-implémentation : + +``` +class AgentEngine: + """Orchestrateur ReAct pour FlowDeck Agent.""" + + def __init__(self, db, user, request): + self.db = db + self.user = user + self.request = request # pour réutiliser session/cookies + self.llm = LLMClient() + self.tools = ToolRegistry(db, user, request) + self.ctx = ContextBuilder(db, user) + self.perms = PermissionManager(db, user) + + async def run(self, conversation_id, objective, *, model=None, + mentions=None, files=None, skill_id=None): + agent = self._load_agent(conversation_id) + model = model or agent.model + + # 1. COMPRÉHENSION + instructions custom + skill + system = self._build_system_prompt(agent, skill_id) + + # 2. COLLECTE CONTEXTE (respecte permissions) + context = await self.ctx.build( + workspace=self._active_workspace(), + mentions=mentions, + files=files, + ) + + messages = [ + {"role": "system", "content": system}, + {"role": "user", "content": f"{objective}\n\n# Contexte\n{context}"}, + ] + + # 3-4. BOUCLE RAISONNEMENT ↔ ACTION + for _ in range(MAX_ITERATIONS): # défaut 12 + response = await self.llm.complete( + model=model, + messages=messages, + tools=self.tools.schema(scope=agent.scope), # function calling + stream=True, + ) + yield {"type": "reasoning", "content": response.text} + + if not response.tool_calls: + yield {"type": "final", "content": response.text} + break + + for call in response.tool_calls: + # Vérif permission AVANT exécution + self.perms.assert_can(call.tool, call.args) + + result = await self.tools.execute(call.tool, call.args) + + # Journal d'audit + snapshot rollback + self._log_action(conversation_id, call, result) + + yield {"type": "action", + "tool": call.tool, + "target": result.get("target"), + "status": result["status"]} + + messages.append({"role": "tool", + "name": call.tool, + "content": result}) + +``` + +Points clés : +- stream=True → SSE vers le frontend +- Chaque tool_call passe par PermissionManager.assert_can() avant exécution +- Chaque action modifiant la DB écrit un agent_actions avec undo_snapshot_json → rollback possible +- La boucle réutilise le même token Gitea que l'utilisateur (via user_tokens) +------------------------- + +10. Système d'outils (Tools) {#10-système-doutils} + +Les outils sont des wrappers autour des routers FastAPI existants — l'agent ne réinvente rien. + +| Outil | Route interne appelée | Ce que ça fait | +|-------|----------------------|----------------| +| `search_workspace` | `SearchEngine` (FTS5) | Recherche full-text pages/collections | +| `read_collection` | `GET /db/{id}` | Lit schéma + pages d'une collection | +| `read_page` | `GET /db/{c}/pages/{p}` | Lit une page + propriétés + blocs | +| `create_collection` | `POST /db` | Crée une database | +| `add_property` | `POST /db/{id}/properties` | Ajoute une propriété typée | +| `create_view` | `POST /db/{id}/views` | Crée une vue (table/board/calendar…) | +| `create_page` | `POST /db/{id}/pages` | Crée une page + valeurs propriétés | +| `update_page` | `PUT /db/{c}/pages/{p}` | Modifie propriétés/contenu | +| `write_blocks` | `POST /board/api/pages/{id}/blocks` | Écrit du contenu (éditeur de blocs) | +| `add_relation` | `POST /db/{id}/properties` (type relation) | Lie deux collections | +| `create_sub_item` | `POST /db/{c}/pages/{p}/sub-items` | Crée un sub-item | +| `add_dependency` | `POST /db/{c}/pages/{p}/dependencies` | Ajoute blocks/blocked_by | +| `sync_gitea` | `POST /board/api/sync/{o}/{r}` | Synchronise issues ↔ collection | +| `read_gitea_issues` | `GiteaClient` | Lit issues/labels/milestones | +| `create_gitea_issue` | `POST /api/issues/{o}/{r}` | Crée une issue Gitea | +| `apply_template` | `POST /db/{id}/templates/{t}/apply` | Applique un template | + + + +Schéma d'un outil (function calling) exposé au LLM : +``` +{ + "name": "create_page", + "description": "Crée une page dans une collection FlowDeck avec ses valeurs de propriétés.", + "parameters": { + "type": "object", + "properties": { + "collection_id": {"type": "integer"}, + "title": {"type": "string"}, + "property_values": { + "type": "object", + "description": "Map property_id → valeur (respecte le type)" + } + }, + "required": ["collection_id", "title"] + } +} +``` + +Filtrage par scope — Pour un custom agent, ToolRegistry.schema(scope=...) ne renvoie que les outils autorisés dans agents.scope_json.tools, et limite les cibles aux collections/repos de scope_json. + +------------------------- + +## 11. Permissions & sécurité {#11-permissions--sécurité} + +L'agent agit avec les permissions de l'utilisateur, jamais au-delà (principe Notion Agent : « mêmes permissions que l'utilisateur »). + +``` +┌───────────────────────────────────────────────────────────┐ +│ GARDE-FOUS AGENT │ +│ │ +│ 1. Identité → session utilisateur + user_tokens │ +│ 2. ACL → PermissionManager.assert_can() par outil│ +│ 3. Scope custom → scope_json limite collections/repos │ +│ 4. Database lock → collection is_locked ⇒ outils write KO │ +│ 5. Audit → agent_actions journalise TOUT │ +│ 6. Rollback → undo_snapshot_json par action │ +│ 7. Rate limiting → RateLimiter (middleware existant) │ +│ 8. Confirmation → actions destructives ⇒ approbation UI │ +└───────────────────────────────────────────────────────────┘ +``` + +Mode approbation — Configurable par agent : +- auto : l'agent exécute directement (création de contenu) +- confirm : actions destructives (delete_page, sync massif) demandent une confirmation dans le panneau avant exécution +Cohérent avec le modèle de rôles workspace (owner/admin/editor/commenter/viewer, ARCHITECTURE §6.2) : un agent lancé par un viewer ne peut que lire. + +------------------------- + +## 12. Custom Agents & déclencheurs {#12-custom-agents} + +Comme Notion (Custom Agents 2026), FlowDeck distingue : +| Type | Portée | Exemple | +|------|--------|---------| +| **Personal Agent** | Généraliste, tout le workspace visible | Assistant quotidien | +| **Custom Agent** | Spécialisé, scope restreint, déclencheurs | Agent Reporting, Agent Support | + + + +Déclencheurs (agents.trigger_json) : +``` +type: "manual" → bouton dans l'UI +type: "schedule" → cron interne (ex: "0 9 * * 1" = lundi 9h) +type: "webhook" → branché sur webhooks.py (issue.opened, issue.closed…) +``` + +Exemples de custom agents FlowDeck : +- Agent Reporting — chaque lundi, scanne toutes les collections, produit une page « Rapport hebdo » avec KPI +- Agent Gitea Triage — à issue.opened, classe l'issue dans la bonne collection et remplit ses propriétés +- Agent Sprint — prépare une collection sprint depuis les issues du milestone courant +Le scheduler s'appuie sur le lifespan FastAPI (main.py) et les webhooks réutilisent le router webhooks.py existant. + +------------------------- + +## 13. Skills réutilisables {#13-skills} + +Une skill est un mini-prompt paramétrable + liste d'outils autorisés (table agent_skills). +``` +Skill "Préparation de sprint" +├─ prompt_template: +│ "Analyse les issues ouvertes du repo {repo}, crée une collection +│ sprint '{sprint_name}', ajoute les propriétés Status/Priority/Assignee, +│ importe chaque issue comme page, groupe en vue Board." +├─ allowed_tools: [read_gitea_issues, create_collection, +│ add_property, create_page, create_view] +└─ Invocation: POST /api/agent/skills/{id}/apply +``` + +Autres skills livrées par défaut : Analyse SWOT, Génération de compte-rendu, Génération de PRD, Synthèse GitHub/Gitea. + +------------------------- + +## 14. Cas d'usage FlowDeck {#14-cas-dusage} + +Gestion de produit + +> « Crée une roadmap Q3 à partir des issues du repo bruno/flowdeck » + +``` +L'agent : read_gitea_issues → create_collection("Roadmap Q3") → add_property(Status, Priority, DueDate) → create_page × N → create_view(timeline). +Développement logiciel +``` + +> « Analyse les bugs des 6 derniers mois et crée un plan d'amélioration » + +``` +sync_gitea → search_workspace → analyse LLM → create_collection("Plan qualité") → create_page (causes, actions) → add_dependency. +Direction +``` + +> « Génère les OKR du prochain trimestre depuis nos objectifs actuels » + +``` +read_collection(Objectifs) → raisonnement → create_collection("OKR Q4") → create_page × N. +Multi-étapes (comme Notion) +``` + +> « Résume les notes de réunion du trimestre et crée les tâches de suivi » + +``` +search_workspace(meetings) → synthèse → write_blocks (page résumé) → create_page × N (tâches) dans la collection Tasks. +``` + +------------------------- + +## 15. Plan de migration {#15-plan-de-migration} + +Phase 1 — Fondations DB & Service + 1. Créer tables agents, agent_conversations, agent_messages, + agent_actions, agent_skills + 2. Implémenter LLMClient (abstraction multi-provider) + 3. Implémenter ContextBuilder (réutilise SearchEngine) + +Phase 2 — Tool-calling + 1. ToolRegistry : wrappers read-only d'abord (search, read_*) + 2. Ajouter outils write (create_collection, create_page, ...) + 3. Brancher PermissionManager.assert_can() sur chaque outil + 4. Journal agent_actions + snapshots rollback + +Phase 3 — Interface + 1. agent_panel.html (Alpine.js) branché sur section 🤖 Agents + 2. Streaming SSE (/run) + 3. Icône flottante coin inférieur droit + 4. Affichage temps réel reasoning + actions + +Phase 4 — Personnalisation + 1. Instructions custom par agent + 2. Skills (table + /apply) + 3. Sélecteur de modèle (GPT/Claude/Gemini/Ollama) + +Phase 5 — Custom Agents + 1. Scope + trigger_json + 2. Scheduler (lifespan FastAPI) + 3. Déclencheurs webhook (via webhooks.py) + 4. Mode approbation (auto/confirm) + + +------------------------- + +## 16. Limitations {#16-limitations} + +Alignées sur les limites connues de Notion Agent, adaptées à FlowDeck. L'agent ne peut pas : +- Modifier les paramètres du workspace (/workspace/{id}/settings) +- Gérer les membres / rôles (workspace_members) +- Gérer l'authentification ou les intégrations OAuth (connexion/déconnexion Gitea/GitHub) +- Publier ou partager publiquement une page sans confirmation utilisateur (/api/pages/{id}/publish, /share) +- Modifier une collection verrouillée (is_locked = 1) +- Agir au-delà des permissions de l'utilisateur qui l'invoque +- Supprimer définitivement (hard delete) sans mode confirm +------------------------- + +Résumé + +FlowDeck Agent porte le modèle Notion Agent dans FlowDeck en réutilisant l'infrastructure existante : +- Un service AgentEngine orchestrant une boucle ReAct (raisonnement ↔ action) +- Des outils qui sont de simples wrappers des routers FastAPI existants (/db/*, /board/api/*, Gitea) +- Un contexte collecté via SearchEngine + GiteaClient, filtré par PermissionManager +- Un audit complet (agent_actions) avec rollback +- Custom Agents, skills et déclencheurs pour l'automatisation +Le passage de « l'IA qui répond » à « l'IA qui réalise le travail » se fait sans réécrire FlowDeck : l'agent agit comme un utilisateur privilégié mais contraint, à travers les mêmes portes que l'interface humaine. + + +J'ai créé le document `Flowdeck_Agent_integration.md`. Points saillants de l'intégration proposée : + +- **Réutilisation maximale** : l'agent n'accède jamais directement à SQLite — il appelle les routers FastAPI existants (`/db/*`, `/board/api/*`, `GiteaClient`), ce qui garantit validation, cohérence et respect du `PermissionManager`. + +- **Boucle ReAct** : Compréhension → Contexte → Raisonnement → Action, calquée sur le fonctionnement de Notion Agent mais mappée sur vos entités (`collections`, `collection_pages`, issues Gitea). + +- **5 nouvelles tables** seulement (`agents`, `agent_conversations`, `agent_messages`, `agent_actions`, `agent_skills`), plus un router `agent.py` et 4 services. + +- **Sécurité** : audit complet avec `undo_snapshot_json` pour rollback, respect des rôles workspace, mode `confirm` pour les actions destructives. + +- **Custom Agents + Skills + déclencheurs** (cron via `lifespan`, webhooks via votre `webhooks.py` existant). diff --git a/docs/Guide_Complet_Notion_Agent_2026.md b/docs/Guide_Complet_Notion_Agent_2026.md new file mode 100644 index 0000000..460ba0b --- /dev/null +++ b/docs/Guide_Complet_Notion_Agent_2026.md @@ -0,0 +1,464 @@ +# Guide complet — Notion Agent (2026) + +## Introduction + +Notion Agent est l'évolution la plus avancée de Notion AI. Contrairement à un assistant conversationnel classique qui répond uniquement à des questions, Notion Agent est conçu pour **agir directement dans votre espace de travail**. + +L'objectif est de transformer l'IA d'un simple outil de rédaction en un véritable collaborateur numérique capable d'exécuter des tâches complètes à travers les pages, bases de données et outils connectés. + +--- + +# 1. Qu'est-ce que Notion Agent ? + +Notion Agent est un agent IA intégré nativement dans Notion. + +Il possède : + +- Un accès au contexte de votre espace de travail +- La capacité de lire des pages et bases de données +- La capacité de créer et modifier du contenu +- La possibilité d'effectuer des tâches multi‑étapes +- L'accès à certaines sources externes et outils connectés + +Conceptuellement : + +Assistant IA traditionnel : +Utilisateur → Prompt → Réponse + +Notion Agent : +Utilisateur → Objectif → Planification → Recherche → Actions → Résultat + +L'agent agit davantage comme un employé numérique que comme un chatbot. + +--- + +# 2. Où apparaît-il dans l'interface ? + +## Emplacement principal + +Notion affiche l'agent sous la forme d'une icône circulaire représentant un visage. + +Position habituelle : + +- Coin inférieur droit de l'écran + +Ou : + +- Section AI dans la barre latérale gauche + +--- + +## Interface de conversation + +Lorsque l'utilisateur ouvre l'agent : + +┌──────────────────────────┐ +│ Conversation Agent │ +├──────────────────────────┤ +│ Historique │ +│ │ +│ Réponses │ +│ │ +├──────────────────────────┤ +│ @Sources │ +│ 📎 Fichier │ +│ Modèle IA │ +│ Zone de saisie │ +└──────────────────────────┘ + +L'interface ressemble à ChatGPT mais avec davantage de contrôles contextuels. + +--- + +# 3. Comment fonctionne l'agent ? + +## Étape 1 : Compréhension + +L'utilisateur formule un objectif. + +Exemples : + +- Construis un CRM. +- Résume les notes de réunion du trimestre. +- Crée les OKR du prochain trimestre. + +L'agent interprète l'intention. + +--- + +## Étape 2 : Collecte du contexte + +Il récupère automatiquement : + +- La page actuelle +- Les blocs sélectionnés +- Les bases de données liées +- Les documents référencés +- Les permissions utilisateur + +Il fonctionne avec les mêmes permissions que l'utilisateur. + +--- + +## Étape 3 : Raisonnement + +L'agent décompose la demande. + +Exemple : + +Créer un CRM + +→ déterminer les propriétés +→ créer la base +→ créer les vues +→ créer les relations +→ générer des exemples + +--- + +## Étape 4 : Action + +L'agent modifie directement Notion. + +Contrairement à un chatbot traditionnel, il peut : + +- créer +- modifier +- organiser +- restructurer + +le contenu. + +--- + +# 4. Sources de connaissances + +## Sources Notion + +L'agent peut consulter : + +- Pages +- Sous-pages +- Bases de données +- Wikis +- Documents internes + +--- + +## Sources connectées + +Selon la configuration du workspace : + +- Jira +- Slack +- Google Drive +- GitHub +- Figma +- Outils connectés compatibles + +--- + +## Fichiers + +L'utilisateur peut joindre : + +- PDF +- Documents +- Présentations +- Fichiers importés + +--- + +## Mention manuelle + +L'utilisateur peut préciser : + +@Projet Alpha + +ou + +@Équipe Marketing + +pour forcer l'utilisation d'un contexte particulier. + +--- + +# 5. Ce que Notion Agent peut faire + +## Création de contenu + +- Pages +- Sous-pages +- Documentation +- SOP +- Guides +- Rapports +- Comptes-rendus + +--- + +## Modification de contenu + +- Réécriture +- Résumé +- Traduction +- Simplification +- Restructuration + +--- + +## Bases de données + +L'agent peut : + +- créer une base +- ajouter des propriétés +- ajouter des relations +- créer des vues +- remplir les données + +--- + +## Recherche documentaire + +Il peut analyser : + +- des centaines de pages +- plusieurs bases de données +- des documents attachés + +pour produire un résultat consolidé. + +--- + +## Analyse + +Exemples : + +- Identifier les tendances +- Comparer des projets +- Détecter les risques +- Produire des synthèses exécutives + +--- + +## Travail multi‑étapes + +Exemple : + +« Analyse les incidents des 6 derniers mois et crée un plan d'amélioration. » + +L'agent : + +1. collecte les incidents +2. les analyse +3. identifie les causes +4. produit un rapport +5. crée les tâches + +--- + +# 6. Fonctionnalités avancées + +## Changement de modèle IA + +Le sélecteur de modèle permet selon la disponibilité : + +- GPT +- Claude +- Gemini +- autres modèles partenaires + +Certains modèles peuvent n'utiliser que le Web alors que d'autres exploitent le contexte Notion. + +--- + +## Historique + +L'agent conserve l'historique des conversations. + +Permet : + +- reprendre un travail +- relancer une tâche +- réutiliser un contexte + +--- + +## Instructions personnalisées + +L'utilisateur peut définir : + +- ton +- style +- format +- règles métier + +Exemple : + +« Toujours écrire les procédures en format SOP. » + +--- + +## Skills + +Notion introduit des compétences réutilisables. + +Une skill agit comme un mini‑prompt réutilisable. + +Exemples : + +- Génération de compte-rendu +- Analyse SWOT +- Préparation de sprint + +--- + +# 7. Ce que l'agent ne peut pas faire + +À ce jour, certaines limitations existent. + +Il ne peut généralement pas : + +- modifier les permissions des pages +- partager des pages +- gérer la facturation +- modifier les paramètres du workspace +- créer certaines propriétés avancées complexes +- créer des rappels système + +--- + +# 8. Différence entre Notion AI et Notion Agent + +## Notion AI (ancienne approche) + +- Répond +- Résume +- Réécrit + +## Notion Agent + +- Planifie +- Recherche +- Agit +- Modifie le workspace +- Réalise des tâches complètes + +--- + +# 9. Custom Agents (2026) + +Notion a également lancé les Custom Agents. + +Différence : + +Notion Agent : +- Agent personnel généraliste + +Custom Agent : +- Agent spécialisé + +Exemples : + +- Agent RH +- Agent Support +- Agent Produit +- Agent Marketing +- Agent GitHub +- Agent Reporting + +Ces agents peuvent fonctionner automatiquement selon des déclencheurs et horaires. + +--- + +# 10. Cas d'usage réels + +## Gestion de produit + +- Générer des PRD +- Créer des roadmaps +- Analyser Jira + +## Développement logiciel + +- Synthèse GitHub +- Documentation technique +- Suivi des bugs + +## Marketing + +- Rapports de campagne +- Veille concurrentielle +- Création de contenu + +## Direction + +- OKR +- KPI +- Rapports exécutifs + +--- + +# 11. Architecture conceptuelle + +Utilisateur + ↓ +Notion Agent + ↓ +Contexte Workspace + ↓ +Sources Connectées + ↓ +Moteur IA + ↓ +Planification + ↓ +Actions Notion + ↓ +Résultat + +--- + +# 12. Vision produit + +Notion cherche à transformer son produit en système d'exploitation du travail. + +L'évolution observée : + +2018 → Notes + +2020 → Bases de données + +2023 → Notion AI + +2025 → Notion Agent + +2026 → Custom Agents + +Direction probable : + +- équipes d'agents spécialisés +- automatisation continue +- orchestration inter‑outils +- assistants autonomes de projet + +--- + +# Résumé + +Notion Agent est un agent IA intégré profondément dans Notion. + +Ses caractéristiques majeures : + +- Compréhension du contexte +- Accès aux connaissances du workspace +- Raisonnement multi‑étapes +- Création et modification de contenu +- Gestion des bases de données +- Analyse documentaire +- Personnalisation via instructions et skills +- Intégration avec outils externes +- Possibilité d'évoluer vers des agents spécialisés + +Il représente le passage de « l'IA qui répond » à « l'IA qui réalise le travail » directement dans l'environnement Notion. diff --git a/images/chat/Notion_chat_main_page.png b/images/chat/Notion_chat_main_page.png new file mode 100644 index 0000000000000000000000000000000000000000..59aa8310d0c645095c01cad1435434f688d8ec59 GIT binary patch literal 44992 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 iYv0<47_ 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 literal 0 HcmV?d00001