Files
ObsiGate/docs/features/ai-tools-roadmap.md
T
bruno 6a58a59a11
CI / lint (push) Successful in 1m37s
CI / security (push) Successful in 1m1s
CI / test (push) Successful in 3m26s
CI / build (push) Successful in 1m44s
CI / e2e (push) Successful in 11m1s
feat(ai): ecosysteme d'outils phase 2 - recherche a cle, cache/retry, Playwright, crawl, Gitea/GitHub, documents (#92)
2026-09-17 11:52:03 -04:00

7.8 KiB

#92 — Assistant IA — Écosystème d'outils : feuille de route technique

Statut : ✅ livré (phase 2, version 2.10.0) — phase 1 livrée dans #91 Effort estimé : 3-5 jours pour la phase 2 | Impact : 🟠 Références : Roadmap · Outils & MCP #79 · Fenêtre de discussion #91 · Changelog

1. Ce qui existe déjà (phase 1, #91)

Élément État
Registre d'outils typé (@tool, Pydantic, risque, scope, rate-limit, audit) ✅ backend/tools/registry.py
Boucle agent multi-tours + confirmation en deux étapes ✅ backend/agent/loop.py
Section « N étapes » Notion-style avec libellés humains + événements SSE en direct ✅ backend/tools/labels.py
Recherche web auto-hébergée (web_search via SearXNG) ✅ backend/tools/web.py
Lecture d'une page publique (fetch_url, garde SSRF, HTML→texte) ✅ backend/tools/web.py
Outils vault (lecture, recherche, navigation, mutations confirmées) ✅ backend/tools/service.py (26 outils)

2. Frameworks d'agents — décision

ObsiGate ne migre pas vers un framework externe : la boucle maison (run_agent) est déjà testée, typée, auditable et intégrée au flux SSE (critique pour la section « steps » en direct). Les candidats restent suivis :

Framework Point fort Verdict pour ObsiGate
Pydantic AI validation type-safe, cœur léger inspirant : la validation existe déjà via Pydantic dans registry.call_tool
LangChain / LangGraph écosystème, workflows complexes dépendance lourde ; à reconsidérer seulement si multi-agents
LlamaIndex RAG clé en main la recherche locale (TF-IDF + embeddings, #70) couvre déjà le besoin
SmolAgents agents qui écrivent du code risque d'exécution : hors périmètre
LightAgent ultra-léger, mémoire redondant avec la mémoire de session déjà fournie

Règle : tout nouvel outil = @tool dans backend/tools/ + un libellé dans labels.py + deux clés i18n (ai.step.<key> FR/EN) + un test. Aucune réécriture de la boucle n'est nécessaire.

3. Phase 2 — catégories à implémenter

Livré (2.10.0, #92). Récapitulatif des décisions finales :

Catégorie Décision livrée
Recherche web étendue Tavily, Brave, SerpAPI, Exa à clé (OBSIGATE_*_API_KEY), essayés avant SearXNG ; ordre via OBSIGATE_WEB_PROVIDERS
Lecture de pages fetch_url(render=True) → worker Playwright isolé (backend/tools/webrender.py), dépendance optionnelle + erreur explicite
Crawl multi-pages crawl_site (WRITE + confirmation) : BFS httpx borné (≤ 20 pages, même hôte, SSRF sur chaque URL) → condensé Markdown dans le vault. Scrapy écarté (dépendance lourde inutile à cette échelle)
Sources connectées Gitea + GitHub (git_list_repos, git_search_issues, git_get_file) via env/Infisical ; drives cloud (Drive/OneDrive) orientés serveur MCP externe (#79)
Production de documents create_xlsx, create_docx, create_csv, create_pdf — WRITE + confirmation, écrit via save_raw_file(allow_docs=True) (path safety + backup)
Transverse Cache SQLite (webcache.py, TTL OBSIGATE_WEB_CACHE_TTL), retry backoff maison (OBSIGATE_WEB_RETRY), secrets par env (Infisical-compatible)
  • Fallback sans clé — ✅ livré (BUG-051) : chaîne de fournisseurs dans backend/tools/web.py — SearXNG auto-hébergé (OBSIGATE_SEARXNG_URL) puis, si aucun résultat, DuckDuckGo (html.duckduckgo.com/html/) puis Bing (www.bing.com/search). Le premier fournisseur non vide est retenu et exposé (provider) ; replis désactivables via OBSIGATE_WEB_FALLBACK=0.
  • Fournisseurs optionnels (clé dans Infisical, jamais en dur) : Tavily (résultats orientés agents), Brave Search API, SerpAPI (Google), Exa. Interface unifiée type anysearch pour un sélecteur de fournisseur unique.
  • Paramètres déjà exposés : category (general/news/it/science), language, page, max_results.

3.2 Lecture de pages (fetch_url)

  • Pages statiques : couvert (httpx + extraction texte maison).
  • Pages dynamiques (SPA/React) : playwright (async) en option, exécuté dans un worker isolé (jamais dans le process web) — images Docker lourdes à prévoir.
  • Crawl multi-pages : scrapy uniquement en tâche de fond, jamais déclenché par le modèle sans confirmation (WRITE/confirm).
  • Alternative légère de parsing : lxml ou beautifulsoup4 si l'extraction maison devient insuffisante (aujourd'hui volontairement sans dépendance).

3.3 Sources connectées (« Searched connected sources »)

Chaque intégration reste derrière le registre (risque, scope, rate-limit, audit) et jamais avec un token en dur :

  • Gitea / GitHub : httpx direct (déjà utilisé) ou gitea-sdk / PyGithub. Priorité haute : ObsiGate est hébergé sur Gitea (issues, PR, commits).
  • Google Drive / Gmail / Calendar : google-api-python-client (+ PyDrive4 pour les tâches simples). OAuth2 via authlib.
  • OneDrive / SharePoint : onedrive-personal-sdk (async, Microsoft Graph).
  • Notion / Slack / Jira / Confluence / Salesforce : SDK officiels ou httpx + OAuth2 générique.
  • Voie recommandée : privilégier le serveur MCP externe (#79) pour les services tiers — l'attaque surface reste hors du cœur d'ObsiGate.

3.4 Fichiers et production de documents

  • Conversion de formats, tableurs (openpyxl), documents (python-docx, pypdf), graphiques (matplotlib) : outils WRITE (confirmation obligatoire), exécutés hors requête web si lourd (tâche de fond + SSE).
  • Pièces jointes : déjà couvert par l'upload vault + python-multipart.

3.5 Raisonnement interne (« thought », « planned the task »)

  • Livré : la note intermédiaire du modèle devient une étape visible.
  • Extension possible : exposer les itérations de la boucle (iterations) comme étapes de planification quand un outil de plan est ajouté.
  • Garantie de réponse finale — ✅ livré (BUG-052) : à l'épuisement du budget d'itérations ou du quota d'appels d'outils, _finalize_answer déclenche un dernier appel LLM sans outil (instruction de synthèse) ; un repli déterministe liste les sources si cet appel échoue. Une recherche web ne peut plus se terminer sur une conversation sans texte.

4. Transverse — à faire avec la phase 2

Sujet Bibliothèque / approche Où
Réessais avec backoff tenacity (ou boucle maison) appels réseau des outils
Cache des résultats web table SQLite dédiée + TTL (OBSIGATE_WEB_CACHE_TTL) backend/tools/web.py
Rate limiting déjà en place (backend/tools/ratelimit.py) registre
Secrets Infisical / variables d'environnement jamais en dur
Async httpx (async) pour ne pas bloquer la boucle nouveaux outils réseau
Fallback chaque outil externe doit échouer proprement (ToolError + message) registre
Observabilité audit déjà en place (backend/tools/audit.py) registre

5. Points d'attention

  • Sécurité d'abord : tout outil réseau passe par la garde SSRF (_assert_public_http_url), des limites de taille, et n'est jamais exposé sans rate-limit.
  • Coût : chaque outil supplémentaire augmente le prompt système (schémas). Mesurer (/api/ai/status) et exposer les outils par lots si besoin.
  • Testabilité : les outils réseau se testent avec httpx mocké (voir tests/test_web_tools.py), jamais contre Internet en CI.
  • Une seule source d'affichage : ne pas dupliquer les étapes côté frontend — le libellé vient du backend (labels.py), la traduction du locale.