L'infrastructure qui sous-tend la recherche assistée par l'IA dans Figma



Pour développer la recherche assistée par l'IA dans Figma, nous avons dû relever plusieurs défis techniques, notamment la génération et l’indexation de milliards d'incorporations pour alimenter ces fonctionnalités tout en maîtrisant les coûts.
Partager L'infrastructure qui sous-tend la recherche assistée par l'IA dans Figma
Illustrations réalisées par Structure Bâtons.
Lors de Config 2024, nous avons présenté de nouvelles fonctionnalités de recherche assistées par l’IA, qui permettent d’explorer tous les designs et composants publiés par une équipe ou une organisation. De nombreux utilisateurs nous ont fait part de leurs difficultés à retrouver un design ou un composant spécifique, en particulier ceux travaillant dans de grandes organisations dotées de design systems complexes. Désormais, grâce à la recherche assistée par l’IA, vous pouvez trouver ce que vous cherchez à partir d’une simple capture d’écran, d’une sélection de calques Figma ou même d’une description textuelle.
Ces fonctionnalités s’appuient sur l’IA dans deux parcours de recherche : Recherche de designs, un tout nouveau parcours, et Recherche de composants, qui améliore considérablement notre fonctionnalité de recherche de composants.
Recherche de designs
Nous avons indexé les frames de tous vos fichiers afin que vous puissiez retrouver précisément ce que vous cherchez, même s’il s’agit d’une frame non étiquetée et dissimulée dans un fichier comportant des dizaines de pages. Vous pouvez désormais trouver des designs similaires de deux manières : lexicalement, grâce à une description textuelle, ou visuellement, à partir d’une capture d’écran ou de la sélection d’un design similaire.
Recherche de composants
Nous avons enrichi les capacités du panneau Assets. Auparavant, la recherche dans les composants publiés reposait sur une correspondance stricte de texte, obligeant les designers à énumérer manuellement des mots-clés dans les descriptions. Désormais, grâce à l'IA, la recherche d'assets propose une compréhension sémantique du composant. Par exemple, un composant 😀 peut être retrouvé via des termes comme « smiley », « joyeux », « visage », « sourire », quel que soit son nom d’origine. Plus besoin de faire du SEO à la main ! Tout comme pour la recherche de designs, vous pouvez également effectuer des recherches visuelles, à partir d’une capture d’écran ou d’une sélection d’un composant similaire.
Voici un aperçu des coulisses de l’infrastructure qui rend ces nouvelles fonctionnalités de recherche assistées par l'IA possibles.
Notre modèle d'incorporation
Un modèle d'incorporation est un algorithme d’IA qui convertit des données, comme du texte ou des images, en une série significative de nombres.
Les modèles d’incorporation de Figma n’ont pas été entraînés sur des fichiers Figma privés ni sur des données clients. Les derniers réglages ont été réalisés à l’aide d’images d’interfaces utilisateur issues de fichiers publics et gratuits de la Communauté. Vous pouvez en savoir plus sur l’entraînement des modèles ici.
Au cœur de la recherche assistée par l'IA de Figma se trouve un modèle d'incorporation capable de représenter du texte ou une image sous la forme d'une série significative de nombres. Par exemple, une image de chat pourrait générer l’incorporation suivante :
[0.066, 0.469, 0.103, 0.35, 0.382, 0.163, 0.587, 0.796, 0.311, 0.477]
Figma utilise actuellement le modèle open source CLIP, un modèle d'incorporation multimodal. Ce modèle accepte plusieurs types d’entrées (images et texte) et produit des incorporations dans le même espace. Ainsi, une incorporation pour la chaîne « chat » sera numériquement similaire à celle générée pour une image de chat, même si l’un provient d’un texte et l’autre d’une image.
À un niveau général, la recherche assistée par l’IA dans Figma fonctionne en créant un index de contenu — par exemple, tous les composants de votre design system — ainsi que leurs incorporations associées. Une requête est ensuite effectuée pour identifier les éléments dont les incorporations sont les plus proches de celle de la requête. Visualiser une recherche vectorielle par voisin le plus proche pourrait ressembler à ceci, mais avec bien plus de dimensions que les trois illustrées ci-dessous :

Au début du projet, nous avons expérimenté la génération d’une incorporation à partir d’une représentation textuelle de la sélection (par exemple, JSON), mais nous avons constaté que les incorporations générées via des images donnaient de meilleurs résultats et garantissaient le partage des chemins de code lors de la recherche via une capture d’écran.
Contrairement à une recherche traditionnelle où la requête de l’utilisateur est comparée directement aux entrées de l’index, la recherche par incorporation nécessite d’abord de générer une incorporation pour la requête de l’utilisateur et de la comparer à un index d'incorporations. Quand vous effectuez une recherche via une capture d’écran ou un texte, Figma génère une incorporation en temps réel qui peut être directement utilisée comme entrée pour le modèle. Si vous effectuez une recherche via une sélection de calques dans Figma, nous générons simplement une nouvelle capture d’écran de votre sélection, puis nous l’utilisons comme entrée pour le modèle afin de générer une incorporation de requête.
Remplissage de l’index de recherche vectorielle
Une fois que vous avez votre incorporation de requête, vous savez ce que vous cherchez, mais vous avez tout de même besoin d'un index rempli pour effectuer votre recherche.
Identifier les designs dans les fichiers Figma
Pour localiser les designs qui peuvent être profondément enfouis dans des fichiers Figma, nous devons répertorier toutes les frames que l'on devrait pouvoir rechercher au sein d'un fichier. Pour chacune, une miniature — une capture d’écran reconstituée à partir des calques Figma — et une incorporation doivent être générées et intégrées à l’index de recherche. Cette étape est délicate : en effet, les frames non publiées au sein d'un fichier Figma ne sont pas directement énumérables. Pour les identifier, nous exécutons une version serveur sans interface utilisateur de l’éditeur Figma en C++ dans une tâche asynchrone.

Pour en savoir plus sur nos techniques de sandboxing pour exécuter du C++ côté serveur, cliquez ici
Bien que Figma gère également son propre cluster RDS, les fonctionnalités de recherche assistées par l'IA utilisent DynamoDB pour stocker les métadonnées et les incorporations. Ces fonctionnalités ne nécessitent qu'une base de données clé-valeur simple, avec des écritures et lectures à haut débit. Aucune transaction ni relation de clé étrangère n’est requise.
Une fois que nous avons identifié tous les designs indexables d'un fichier Figma, les métadonnées des frames sont conservées dans DynamoDB. Les miniatures sont reconstituées et téléchargées sur S3. Ce travail d’identification et de création de miniatures met la tâche suivante en file d'attente et aboutit avec succès. La séparation des étapes individuelles du pipeline en tâches distinctes nous donne un contrôle plus précis sur la constitution des lots et le comportement de nouvelle tentative.
Génération des incorporations
Ensuite, nous devons générer les incorporations et les conserver. Notre modèle d’incorporations est déployé sur AWS SageMaker. Les requêtes d’incorporations sont envoyées à notre point de terminaison SageMaker par lots, ce qui nous permet d’exécuter des inférences en parallèle. L'entrée est une série d'URL de vignettes générées à l'étape précédente, et la sortie est une série d'incorporations, une pour chaque image d'entrée.
Pour générer les incorporations efficacement, il était important de paralléliser le téléchargement des images ainsi que leur redimensionnement et leur normalisation au moment de l’inférence. La détermination de la taille optimale des lots a nécessité des expérimentations : au-delà d’un certain seuil, la latence a commencé à croître de manière linéaire avec la taille des lots, au lieu d’un effet de lot sous-linéaire. Une fois les incorporations générées et conservées, nous mettons en file d'attente l’étape finale du pipeline.
Indexation pour la recherche
Maintenant que les incorporations sont générées, il est temps de les écrire dans notre index OpenSearch, où les véritables requêtes de recherche vectorielle seront exécutées. OpenSearch est déjà largement déployé dans Figma pour les fonctionnalités de recherche traditionnelles, il était donc logique de l’utiliser également pour la recherche par incorporations.
Les incorporations sont écrites dans l’index de recherche, accompagnées de métadonnées supplémentaires comme le nom de la frame, l’identifiant et le nom du fichier contenant, ainsi que le projet, l’équipe et l’organisation de la frame. Ces métadonnées supplémentaires nous permettent de prendre en charge une recherche facettée (avec des filtres), en plus de la recherche vectorielle par voisin le plus proche.
Recherche de composants
Chaque fois qu'une bibliothèque est publiée, une tâche asynchrone est lancée afin de calculer les incorporations pour chaque miniature. Nous utilisons un modèle très similaire à celui utilisé pour la recherche de designs, mais spécialement ajusté sur les kits UI publics de la Communauté. Comme précédemment, ce modèle n'a pas été entraîné sur des fichiers Figma privés ni sur des données clients.
La recherche lexicale (via une correspondance vague de chaînes de caractères) sur les noms et les descriptions de composants existait avant la recherche assistée par l’IA. Pour déployer en toute sécurité la recherche assistée par l'IA sur les composants tout en conservant les précieux résultats lexicaux, les recherches sont effectuées simultanément dans l’index lexical et le nouvel index d’incorporations. Les scores bruts provenant des index OpenSearch indépendants n’étant pas directement comparables, les ensembles de résultats se voient attribuer de nouveaux scores basés sur une normalisation min-max, les correspondances lexicales exactes bénéficiant d’un boost. Les résultats sont ensuite entrelacés selon leurs scores mis à jour.
Désormais, une requête renvoie non seulement des résultats lexicaux, mais aussi des résultats pertinents basés sur une compréhension sémantique. Par exemple, une recherche sur « souris » renverra non seulement une icône intitulée « Souris », mais aussi des icônes liées aux curseurs.
Défis à grande échelle
Pour permettre une recherche assistée par l’IA à l’échelle de Figma, il faut générer des incorporations et indexer des milliards d’entrées. Il s'agit d'une opération non seulement longue, mais aussi coûteuse. Une grande partie du projet a été consacrée à l'optimisation des coûts.
L’un des défis majeurs de ce déploiement est que, pour qu’un seul utilisateur puisse utiliser les fonctionnalités de recherche telles qu’elles sont prévues, toutes les données de son équipe doivent être indexées. Pour rendre la recherche assistée par l'IA accessible à un petit nombre d’utilisateurs pour des tests précoces, il a donc fallu indexer les données d’équipes entières. Paradoxalement, même avec un faible pourcentage d’utilisateurs intégrés, nous avons rapidement dû converger vers l’indexation de presque toutes les équipes de Figma. La majorité sont petites, mais il y en a beaucoup ! La maîtrise des coûts de l’indexation et du remplissage des données est devenue encore plus importante.
Optimisation pour l'indexation
En examinant le pipeline que nous avons évoqué précédemment, nous avons identifié que les principaux coûts de calcul venaient non pas de la génération d’incorporations, mais de l’identification et de la création de miniatures de designs significatifs dans les fichiers Figma. Avec cet objectif en tête, nous avons déployé plusieurs optimisations :
- Ruby → C++. Notre approche initiale consistait à sérialiser l’intégralité du fichier Figma en JSON, puis à l’analyser avec Ruby, ce qui était à la fois extrêmement lent et gourmand en mémoire. Réécrire cette logique en C++ et éliminer toute sérialisation intermédiaire a permis d’obtenir des améliorations significatives en termes de temps d’exécution et de réduction de la mémoire utilisée.
- Rendu logiciel. Nous avons déplacé la création de miniatures d’un rendu basé sur GPU sur un ancien type d’instance AWS vers un rendu basé sur CPU avec
llvmpipesur un type d’instance plus récent. Cela a permis une énorme économie : les machines de type CPU sont bien moins chères, et comme elles sont plus récentes, elles traitent notre charge de travail plus rapidement. - Réduction de la fréquence de rafraîchissement de l’indexation. Notre pipeline d’identification et d’indexation était initialement conçu pour être déclenché à chaque modification d’un fichier. Cependant, lorsque les utilisateurs de Figma modifient des fichiers pendant une session d’édition prolongée, ils effectuent souvent de nombreux changements. Il n’est pas nécessaire de rafraîchir rapidement l’index alimenté par l’IA à chaque modification, mais nous voulons conserver un index relativement à jour. En analysant les données, nous avons découvert qu’en limitant l’indexation à au plus une fois toutes les quatre heures, nous n'aurions que 12 % des données à traiter !
- Autoscaling des clusters. L’utilisation de Figma est largement diurne. Réduire la taille de nos clusters pendant les périodes de faible trafic nous a permis d’éviter de payer des capacités de calcul inutiles.
Échelle des clusters de recherche
Réduction de la taille des index
Le deuxième plus grand facteur de coûts était OpenSearch, qui nécessitait un grand cluster pour garder nos énormes index en mémoire. Pour y remédier, nous avons d’abord cherché à réévaluer les critères selon lesquels un élément était défini comme indexable. Qu’est-ce qui rend un design significatif ? Nous avons fini par retirer les brouillons, les designs dupliqués dans les fichiers, ainsi que les fichiers copiés sans nouvelles modifications, ce qui a divisé la taille de l’index par deux. Beaucoup de ces évolutions ont également amélioré le produit : ne pas afficher de designs dupliqués dans les fichiers offre une meilleure expérience utilisateur.
Ensuite, nous avons exploité la quantification vectorielle. Par défaut, le plugin kNN d’OpenSearch représente chaque élément d’une incorporation comme un float de quatre octets, mais la quantification vectorielle est une technique qui permet de compresser la taille des incorporations afin de réduire la mémoire nécessaire pour les stocker et y effectuer des recherches, au prix d’une légère réduction de la précision de la recherche par voisin le plus proche.
Particularités d'OpenSearch
Nous avons découvert plusieurs particularités et bugs intéressants avec la recherche kNN dans OpenSearch, même en utilisant la dernière version prise en charge par AWS.
- Lors des tests de bout en bout de nos fonctionnalités de recherche, nous avons constaté un non-déterminisme périodique. Après une session de débogage approfondie, nous avons constaté que les requêtes routées vers les réplicas dans OpenSearch renvoyaient des résultats non déterministes par rapport à celles routées vers les primaires. Étonnamment, ces requêtes aux réplicas déclenchaient une erreur (
Reader cannot be cast to class SegmentReader) dans les profondeurs d’OpenSearch.
Nous avons collaboré étroitement avec l’équipe OpenSearch d’AWS pour comprendre d'où elle venait. Après plusieurs appels, l’équipe OpenSearch a identifié que cette erreur de conversion de type dans le chemin de suppression affectait les réplicas entre clusters utilisant la réplication de segments et a publié un correctif ici. - Pour améliorer l’espace de stockage et la latence des requêtes, nous supprimons le vecteur d'incorporation lui-même de
_sourcedans l’index de recherche. Le champ_sourcecontient le corps du document original transmis à l’indexeur de recherche. Ce champ n’est pas consultable, mais est renvoyé au client dans les réponses, et les vecteurs d'incorporation sont volumineux !
Cependant, il s’avère qu’en cas de mise à jour d’un document, OpenSearch s’appuie sur_sourcepour calculer les différences et écrire le document mis à jour, au lieu de modifier chaque index en place. Il récupère les champs existants de_source, applique la mise à jour, puis écrit le document. Ainsi, après avoir supprimé les incorporations de_source, chaque fois que nous tentions de mettre à jour un document (par exemple, lorsque le nom d’un fichier changeait), nous supprimions accidentellement l'incorporation du document, car il n’existait pas dans_source. Pour corriger cela tout en conservant notre optimisation sur_source, nous récupérons à nouveau les incorporations depuis DynamoDB lors des mises à jour.
Prochaines étapes
Ces fonctionnalités de recherche assistées par l’IA sont actuellement en phase de première bêta et continueront à être déployées auprès d'un plus grand nombre d’utilisateurs au cours des mois à venir. Nous attendons vos retours avec impatience et nous avons hâte de voir comment la recherche assistée par l’IA transformera vos workflows.


