Symfony AI : simplifier l’analyse de similarités de textes et l’interaction avec vos LLMs favoris
Dernier-né dans la famille Symfony, nous vous présentons Symfony AI 🎉
Symfony AI est un ensemble de composants permettant d’intégrer l’intelligence artificielle dans des applications PHP.
Il se compose de plusieurs parties :
-
Les Composants
- Platform : interface unifiée vers diverses plateformes d’IA telles que OpenAI, Anthropic, Azure, Gemini, etc. ;
- Agent : framework pour créer des agents IA capables d’interagir avec les utilisateurs et d’exécuter des tâches ;
- Store : abstraction de stockage de données avec indexation et recherche pour les applications IA ;
- MCP SDK : SDK pour le Model Context Protocol permettant la communication entre agents IA et outils.
-
Les Bundles
- AI Bundle : intégration Symfony pour les composants AI Platform, Store et Agent ;
- MCP Bundle : intégration Symfony pour MCP SDK, permettant d’agir en tant que serveur ou client MCP.
Décrire tous les composants et bundles serait trop long pour cet article. Nous allons donc nous concentrer sur deux d’entre eux : Platform et Store.
Dans cet article, nous allons créer un petit projet qui indexe des documents et retrouve ceux qui sont similaires à un document donné.
Pour cela, nous devons d’abord vectoriser les documents.
Section intitulée la-vectorisationLa vectorisation
En intelligence artificielle, vectoriser un texte (ou une image, un son, etc.) signifie le transformer en une représentation numérique sous forme d’un tableau de nombres : un vecteur.
- Chaque vecteur est une liste de valeurs flottantes (ex.
[0.12, -0.45, 0.87, ...]
) ; - Ces nombres sont calculés par un modèle d’embedding, réseau de neurones spécialisé ;
- L’idée : deux contenus similaires doivent avoir des vecteurs proches dans l’espace numérique.
Pourquoi faire ça ? La vectorisation permet de mesurer la similarité entre deux contenus. Par exemple :
- « chien » et « chiot » → vecteurs proches ;
- « chien » et « chat » → vecteurs assez proches ;
- « chien » et « banane » → vecteurs éloignés.
Cela rend possible :
- La recherche sémantique (retrouver un texte même si les mots exacts ne sont pas utilisés) ;
- Le clustering (regrouper automatiquement des contenus proches) ;
- Le RAG (Retrieval-Augmented Generation) : retrouver des informations pertinentes pour un LLM.
Section intitulée comment-mesure-t-on-la-proximiteComment mesure-t-on la proximité ?
On compare les vecteurs avec une mesure :
- Cosine similarity → compare l’angle entre deux vecteurs (le plus courant) ;
- Euclidean distance → mesure la distance directe entre deux points.
D’autres mesures existent, mais celles-ci sont les plus utilisées.
Nous appelons le résultat de cette mesure un score. Cette valeur, en fonction de la fonction de calcul peut être :
- une distance (0 est identique, 1 est complètement différent) ;
- une similarité (0 est complètement différent, 1 est identique).
Chaque mesure a ses avantages et inconvénients. Par exemple, la cosine similarity est robuste aux variations d’échelle, tandis que la distance euclidienne est sensible à la magnitude des vecteurs.
Une variation d’échelle, c’est quand un vecteur est simplement une version “agrandie” ou “réduite” d’un autre, par exemple [1, 2, 3]
vs [2, 4, 6]
. La cosine similarity les considère identiques car l’orientation est la même, tandis que la distance euclidienne les voit différents, car la longueur change.
Section intitulée vectoriser-un-texte-avec-code-symfony-ai-platform-codeVectoriser un texte avec symfony/ai-platform
Nous allons utiliser le composant Platform de Symfony AI :
composer require symfony/ai-platform@dev
Ce composant fournit une interface unifiée vers plusieurs plateformes d’IA et permet de créer des clients pour OpenAI, Anthropic, Azure, Gemini, etc.
Exemple avec Gemini :
use Symfony\AI\Platform\Bridge\Gemini\Embeddings as GeminiEmbeddings;
use Symfony\AI\Platform\Bridge\Gemini\PlatformFactory as GeminiPlatformFactory;
$platform = GeminiPlatformFactory::create($_SERVER['GEMINI_API_KEY']);
// Choix du modèle d'embedding
$embeddings = new GeminiEmbeddings();
$text = 'Le chat est un animal domestique très apprécié.';
$response = $platform->invoke($embeddings, $text);
$vector = $response->asVectors()[0];
$text
est un exemple de texte à vectoriser. Sa longueur doit être inférieure au nombre de tokens maximum autorisé par la plateforme. Pour simplifier, on peut considérer qu’un token ≈ un mot (pour les puristes : token ≈ sous-mot, variable selon le tokenizer; en pratique, il y a environ 0,7 à 1,3 tokens par mot selon la langue). La limite varie souvent entre 512 et 8192 tokens, parfois plus.
$vector
est une instance de Symfony\AI\Platform\Vector\Vector
contenant :
data
: tableau de valeurs flottantes représentant le vecteur ;dimension
: nombre de valeurs flottantes dans le vecteur.
Chaque plateforme d’IA a ses propres modèles d’embedding. Par exemple, Gemini retourne des vecteurs de 3072 dimensions, OpenAI en propose de 1536. Ce n’est pas la taille qui compte, mais la qualité du modèle.
Note : Ici, nous avons fait le choix d’utiliser symfony/ai-platform sans utiliser symfony/ai-bundle qui permet une intégration au framework full stack. Nous avons fait l’instanciation de la plateforme à la main, alors qu’il aurait été possible d’utiliser la configuration sémantique du bundle pour l’instancier automatiquement.
Section intitulée plateformes-supporteesPlateformes supportées
Au moment de l’écriture de cet article, Symfony AI Platform prend en charge : Albert, Anthropic, Azure, Bedrock, Gemini, HuggingFace, LmStudio, Meta, Mistral, Ollama, OpenAI, OpenRouter, Replicate, TransformersPhp et Voyage.
Section intitulée bien-choisir-sa-plateformeBien choisir sa plateforme
Les critères principaux :
- Coût : vérifier le prix par token pour la vectorisation ;
- Précision : certains modèles sont meilleurs pour certains types de contenus ;
- Performance : rapidité de traitement.
Grâce à Symfony AI, changer de plateforme est simple : il suffit de modifier la création du client et du modèle. Exemple pour passer de Gemini à Mistral :
use Symfony\AI\Platform\Bridge\Mistral\Embeddings as MistralEmbeddings;
use Symfony\AI\Platform\Bridge\Mistral\PlatformFactory as MistralPlatformFactory;
$platform = MistralPlatformFactory::create($_SERVER['MISTRAL_API_KEY']);
$embeddings = new MistralEmbeddings();
// Le reste du code reste inchangé !
Nous avons constaté de grandes différences de performance et de précision selon les plateformes. Il faut vraiment tester pour trouver celle qui convient le mieux à vos besoins à un instant T. Il n’est pas rare que chaque nouvelle génération d’un LLM apporte une meilleure précision ou performance.
Section intitulée heberger-son-propre-modele-d-embeddingHéberger son propre modèle d’embedding
Si vous ne voulez pas dépendre d’un service tiers, vous pouvez héberger votre propre modèle, ce qui peut améliorer la gestion des données sensibles et réduire les coûts.
Pour exécuter un modèle localement, Ollama est une bonne option. Ollama est un daemon exposant une API HTTP, avec un client CLI simple :
$ ollama
Usage:
ollama [flags]
ollama [command]
Available Commands:
serve Start ollama
create Create a model
show Show information for a model
run Run a model
stop Stop a running model
pull Pull a model from a registry
push Push a model to a registry
list List models
ps List running models
cp Copy a model
rm Remove a model
help Help about any command
Pour trouver des modèles : Ollama Models.
Puis, pour télécharger un modèle :
$ ollama pull nomic-embed-text
Si la liste est limitée ou la précision insuffisante, vous pouvez chercher sur HuggingFace :
$ ollama pull hf.co/Qwen/Qwen3-Embedding-0.6B-GGUF
Comme pour les plateformes SaaS, chaque modèle a ses caractéristiques :
- Licence : certaines sont commerciales ;
- Performance : taille du modèle et vitesse ;
- Précision : qualité des vecteurs ;
- Langue : optimisation pour certaines langues.
💡 Souvenez-vous que les vecteurs sont des tableaux de flottants. Et savez vous qui est assez mauvais pour faire des calculs avec des flottants ? Votre CPU ! Et savez vous qui est très bon pour faire des calculs avec des flottants ? Votre GPU ! Nous avons pu observer un facteur 10 entre les deux. Il faut donc privilégier une machine avec un GPU pour héberger votre modèle d’embedding.
Dans le cas d’Ollama, il faut configurer Symfony comme suit:
use Symfony\AI\Platform\Bridge\Ollama\Ollama;
use Symfony\AI\Platform\Bridge\Ollama\PlatformFactory as OllamaPlatformFactory;
$platform = OllamaPlatformFactory::create($_SERVER['OLLAMA_HOST_URL']);
$embeddings = new Ollama('hf.co/Qwen/Qwen3-Embedding-4B-GGUF'),
// Le reste du code reste inchangé !
Maintenant que nous avons des vecteurs, nous pouvons les stocker et les comparer.
Section intitulée stocker-des-vecteurs-avec-code-symfony-ai-store-codeStocker des vecteurs avec symfony/ai-store
Nous allons utiliser le composant Store de Symfony AI pour stocker et comparer les vecteurs :
composer require symfony/ai-store@dev
Ce composant fournit une abstraction de stockage de données avec indexation et recherche pour les applications IA. Au moment de l’écriture de cet article, il prend en charge :
Azure, ChromaDB, ClickHouse, MariaDB, Meilisearch, MongoDB, Neo4j, Pinecone, Postgres, Qdrant, Redis (PR en cours), SurrealDB, Typesense.
Le choix du store est souvent plus simple que pour la vectorisation : utilisez celui que vous connaissez déjà et qui est disponible dans votre application. Cependant, les performances peuvent varier fortement. Par exemple, pour un même dataset, nous avons obtenu :
- Redis : 4 secondes
- Postgres : 10 secondes
- ClickHouse : 30 secondes
Comme Postgres est largement utilisé dans les applications Symfony, nous allons l’utiliser dans cet exemple :
use Doctrine\DBAL\DriverManager;
use Doctrine\DBAL\Tools\DsnParser;
use Symfony\AI\Store\Bridge\Postgres\Distance;
use Symfony\AI\Store\Bridge\Postgres\Store as PostgresStore;
use Symfony\AI\Store\VectorDocument;
use Symfony\AI\Store\Metadata;
use Symfony\Component\Uid\UuidV4;
$sqlConnection = DriverManager::getConnection((new DsnParser())->parse($_SERVER['POSTGRES_URI']));
$sqlStore = PostgresStore::fromDbal(
connection: $sqlConnection,
tableName: $tableName,
distance: Distance::Cosine,
);
// Crée la table si elle n'existe pas, avec le schéma adapté
$sqlStore->initialize();
$document = new VectorDocument(
new UuidV4($documentId),
$vector,
new Metadata([
'title' => 'Titre du document',
'datasetId' => $datasetId,
// D'autres métadonnées peuvent être ajoutées ici
]),
);
$sqlStore->add($document);
Section intitulée comparer-des-vecteursComparer des vecteurs
Une fois les documents ajoutés au store, nous pouvons les interroger pour trouver ceux qui sont similaires à un vecteur donné.
Nous utilisons le cosinus comme mesure de distance, mais vous pouvez choisir une autre mesure si votre store le permet. La valeur retournée sera comprise entre 0 et 1, où 0 signifie que les vecteurs sont identiques et 1 qu’ils sont complètement différents.
Ici, nous parcourons tous les documents d’un dataset (datasetA) et cherchons les documents similaires dans un autre dataset (datasetB).
use Symfony\AI\Platform\Vector\Vector;
$rows = $sqlConnection
->executeQuery(<<<SQL
SELECT *
FROM {$tableName}
WHERE metadata->>'datasetId' = '{$datasetA}'
SQL)
->fetchAllAssociative()
;
foreach ($rows as $row) {
// La colonne `embedding` contient le vecteur sous forme de JSON
$vector = new Vector(json_decode($row['embedding'], true, 512, \JSON_THROW_ON_ERROR));
$documents = $sqlStore->query(
$vector,
[
// Exclut les documents avec un score > 0.17
// 0 = correspondance parfaite, 1 = complètement différent
'maxScore' => 0.17,
// Filtre : uniquement datasetB et exclut le document courant
'where' => "metadata->>'datasetId' = :datasetId AND id != :currentId",
'params' => [
'datasetId' => $datasetB,
'currentId' => $row['id'],
],
],
);
if (!$documents) {
continue;
}
$metadata = json_decode($row['metadata'], true, 512, \JSON_THROW_ON_ERROR);
echo "Current document: {$metadata['title']}\n";
foreach ($documents as $document) {
echo "- {$document->metadata['title']} (score: {$document->score})\n";
}
echo "\n";
}
Si on utilise ce petit projet pour trouver les pages similaire entre jolicode.com et www.premieroctet.com, nous obtenons :
Current document: https://jolicode.com/blog/jai-teste-tailwind-css
- https://www.premieroctet.com/expertises/tailwind (score: 0.152959644794)
Current document: https://jolicode.com/blog/construire-un-chatbot-specialise-sur-vos-donnees-grace-a-lia-generative-et-php
- https://www.premieroctet.com/blog/comment-fonctionne-un-rag (score: 0.151540160179)
Current document: https://jolicode.com/nos-metiers/technologies/react
- https://www.premieroctet.com/blog/premier-octet-vous-tire-les-cartes (score: 0.16528314352)
Current document: https://jolicode.com/blog/tag/dotjs
- https://www.premieroctet.com/blog/dotjs-2018 (score: 0.151396155357)
Current document: https://jolicode.com/blog/de-la-nostalgie-aux-nouveautes-ce-que-dotjs-2025-nous-a-inspire
- https://www.premieroctet.com/blog/dotjs-2025 (score: 0.0832768678665)
- https://www.premieroctet.com/blog/en/dotjs-2025 (score: 0.162836551666)
Current document: https://jolicode.com/blog/retour-sur-la-dotjs-2018
- https://www.premieroctet.com/blog/dotjs-2018 (score: 0.163798093796)
Current document: https://jolicode.com/blog/paris-web-2018-le-futur-du-web
- https://www.premieroctet.com/blog/paris-web-2018 (score: 0.169616103172)
Pas mal non?
Section intitulée conclusionConclusion
En quelques lignes de code, nous avons pu vectoriser des documents, les stocker et retrouver ceux qui sont similaires à un document donné.
Grâce à ses nombreux bridges, Symfony AI permet de tester facilement différentes plateformes d’IA et de stocker les données dans le store de votre choix.
Vous pouvez ainsi créer des applications IA performantes, flexibles en maîtrisant totalement les données.
Il reste encore beaucoup à explorer dans Symfony AI, notamment les agents et le Model Context Protocol (MCP). Nous vous invitons à consulter la documentation (WIP) pour en savoir plus.
Commentaires et discussions
MCP: The Open Protocol That Turns LLM Chatbots into Intelligent Agents
LLMs have started to become widely known. They are used to generate text, answer questions, translate texts, and more. These models are becoming increasingly powerful and are employed across diverse fields. LLMs powers all the fancy IA you use like GPT, BERT, Claude, LLaMA, Deepseek, …
Lire la suite de l’article MCP: The Open Protocol That Turns LLM Chatbots into Intelligent Agents
Construire un chatbot spécialisé sur vos données grâce à l’IA générative et PHP
Utilisée pour rédiger n’importe quel type de contenus, pour faire des résumés ou encore intégrée à nos outils de développement, l’IA, et en particulier l’IA générative, a trouvé sa place dans bon nombre de secteurs. Basée sur des LLM entraînés sur des quantités astronomiques de documents…
Nos articles sur le même sujet
Nos formations sur ce sujet
Notre expertise est aussi disponible sous forme de formations professionnelles !

Symfony avancée
Découvrez les fonctionnalités et concepts avancés de Symfony