Aller au contenu
Android natif · Kotlin2026

NAVI

Une compagne IA qui vit sur le téléphone, pas sur un serveur

Assistant Android natif local-first : la collecte, la mémoire et la décision de parler vivent sur le téléphone ; le cloud ne sert qu'à générer le texte.

Statut

En usage quotidien, développement actif

Application privée utilisée au jour le jour. Pas de distribution publique : le dépôt est privé et il n'y a pas de version en magasin.

Accès

NAVI est une application privée, mono-utilisateur, sans distribution publique. Il n'y a donc ni lien de démonstration, ni dépôt consultable.

donnée conversationnelle côté serveur
0donnée conversationnelle côté serveur
de la mémoire chiffrée sur l’appareil
100 %de la mémoire chiffrée sur l’appareil
de coût d’inférence visé par mois
Quelques €de coût d’inférence visé par mois

Le problème

Un assistant conversationnel qui prend l'initiative doit observer le contexte de son utilisateur — notifications, applications utilisées, musique, santé, déplacements, agenda. Confier ces signaux à un serveur, c'est construire le profil comportemental le plus intrusif qui soit. Le problème n'était donc pas de faire parler un modèle, mais de le faire parler à propos, sans jamais externaliser la vie privée de l'utilisateur.

Mon rôle

Conception produit et développement intégral : cadrage, architecture, développement Android natif, proxy serveur, découpage en issues et supervision des agents IA d'exécution.

Les décisions qui ont façonné le produit.

Décisions produit
  1. Deux piliers, et rien de plus

    Présence spontanée et mémoire relationnelle. Tout ce qui ne sert pas l'un des deux est refusé — y compris des directions déjà commencées, abandonnées en cours de route pour recentrer le produit.

  2. Elle tient compagnie, elle ne coache pas

    Pas de score, pas d'objectif, pas de jugement. Ce parti pris contraint la personnalité, le ton des notifications et ce que l'application se permet de retenir.

  3. L'observation plutôt que la saisie

    L'utilisateur ne remplit aucun formulaire. La seule entrée volontaire est le partage : depuis n'importe quelle application, « Partager → Navi » dépose un lien ou une image dans le fil.

  4. Application privée, hors magasin

    Mono-utilisateur, installée manuellement. Ce choix libère des contraintes de distribution et permet d'utiliser des permissions système qu'un magasin d'applications refuserait.

Décisions techniques
  1. Natif Android, parce que le produit est fait de services système

    NotificationListenerService, UsageStatsManager, Health Connect, geofencing, tâches de fond, bulles de conversation. Un socle multiplateforme aurait passé son temps à contourner la plateforme au lieu de s'appuyer dessus.

  2. Mémoire chiffrée sur l’appareil

    Room chiffrée via SQLCipher, clé conservée dans le Keystore Android. Les souvenirs et leurs embeddings restent locaux, et la recherche sémantique s'exécute sur le téléphone.

  3. Un proxy plutôt qu’un backend

    Un Worker Cloudflare porte la clé de l'API Gemini et ne stocke rien. Aucune donnée conversationnelle ne persiste côté serveur, et la clé ne se retrouve jamais dans l'APK.

  4. Le moteur d’initiative décide localement

    Scoring local, présence adaptative et anti-répétition déterminent s'il y a lieu de parler. Le modèle distant n'est appelé qu'une fois la décision prise, ce qui borne le coût.

  5. Raisonnement au minimum, pour une vraie raison

    Les tokens de raisonnement de Gemini 3 sont décomptés du plafond de sortie : au niveau par défaut, les réponses se coupaient en plein milieu d'une phrase. Passer `thinkingLevel` au minimum a corrigé la troncature autant que la facture.

  6. Architecture volontairement plate

    Compose, ViewModel, repositories. Pas de multi-modules ni de clean architecture cérémonielle : le projet est mono-développeur et doit rester lisible. Les décisions structurantes sont tracées en ADR.

Architecture

Du signal capté sur le téléphone à la phrase générée.

  1. Étape 01

    Sources de contexte

    Notifications, applications utilisées, musique, Health Connect, géorepérage, agenda.

  2. Étape 02

    Journal de contexte

    Événements typés, rétention courte, filtrés par un registre d'applications.

  3. Étape 03

    Moteur d'initiative

    Scoring local, présence adaptative, anti-répétition. Décide seul s’il y a lieu de parler.

  4. Étape 04

    Mémoire chiffrée

    Room + SQLCipher, clé en Keystore. Souvenirs, embeddings et recherche sémantique locale.

  5. Étape 05

    Proxy Cloudflare Worker

    Porte la clé API, ne stocke rien, seul point de sortie réseau.

  6. Étape 06

    Génération Gemini

    Flash pour la conversation, Flash-Lite pour les initiatives et les tâches légères.

Contraintes

  • Aucune donnée conversationnelle stockée côté serveur.
  • Coût cible de quelques euros par mois, ce qui impose de choisir le modèle appelé selon l’enjeu.
  • Application mono-utilisateur, installée manuellement, sans magasin.
  • Développement solo, branche unique, CI légère.

Stack

  • Kotlin
  • Jetpack Compose
  • Coroutines / Flow
  • Room + SQLCipher
  • Android Keystore
  • WorkManager
  • NotificationListenerService
  • Health Connect
  • Cloudflare Workers
  • API Gemini
Ce que ce projet démontre

Ce que ce projet démontre

  • Développement Android natif moderne, au contact des services système.

  • Conception d'une architecture local-first où la vie privée est une contrainte structurelle, pas une option.

  • Arbitrage coût / qualité sur un usage réel de modèles génératifs.

  • Capacité à retirer des fonctionnalités déjà écrites pour préserver la cohérence d'un produit.