{"id":558,"date":"2026-10-09T11:17:23","date_gmt":"2026-10-09T09:17:23","guid":{"rendered":"https:\/\/www.ranksignal.fr\/blog\/?p=558"},"modified":"2026-10-14T17:52:32","modified_gmt":"2026-10-14T15:52:32","slug":"text2cypher","status":"publish","type":"post","link":"https:\/\/www.ranksignal.fr\/blog\/rag\/text2cypher\/","title":{"rendered":"Text2cypher : le LLM qui \u00e9crit la requ\u00eate de graphe"},"content":{"rendered":"<p>Tu as une base qui range des films, des personnes et les liens qui les relient. Quelqu&rsquo;un te demande : \u00ab Qui a r\u00e9alis\u00e9 le plus de films ? \u00bb La r\u00e9ponse n&rsquo;est \u00e9crite nulle part, elle se compte, et pour la tirer de la base il faut une requ\u00eate pr\u00e9cise que presque personne n&rsquo;a envie de r\u00e9diger \u00e0 la main. Text2cypher confie cette r\u00e9daction \u00e0 un mod\u00e8le de langage. En fin de lecture, tu sauras ce qu&rsquo;on lui donne \u00e0 lire, ce qu&rsquo;il rend, et pourquoi il se trompe parfois.<\/p>\n<p><strong>Sommaire<\/strong><\/p>\n<ul>\n<li><a href=\"#l-essentiel-en-30-secondes\">L&rsquo;essentiel en 30 secondes<\/a><\/li>\n<li><a href=\"#une-traduction-pas-une-recherche\">Une traduction, pas une recherche<\/a><\/li>\n<li><a href=\"#le-schema-la-carte-que-le-modele-n-a-pas\">Le sch\u00e9ma : la carte que le mod\u00e8le n&rsquo;a pas<\/a><\/li>\n<li><a href=\"#terminologie-exemples-format-trois-garde-fous\">Terminologie, exemples, format : trois garde-fous<\/a><\/li>\n<li><a href=\"#le-gabarit-assemble-et-ce-qu-il-produit\">Le gabarit assembl\u00e9, et ce qu&rsquo;il produit<\/a><\/li>\n<li><a href=\"#ce-que-la-traduction-rend-possible\">Ce que la traduction rend possible<\/a><\/li>\n<li><a href=\"#ou-se-range-text2cypher\">O\u00f9 se range text2cypher<\/a><\/li>\n<li><a href=\"#ce-que-text2cypher-ne-garantit-pas\">Ce que text2cypher ne garantit pas<\/a><\/li>\n<\/ul>\n<h2 id=\"l-essentiel-en-30-secondes\">L&rsquo;essentiel en 30 secondes<\/h2>\n<ul>\n<li>Text2cypher <strong>traduit une question en requ\u00eate Cypher<\/strong> : le mod\u00e8le \u00e9crit la requ\u00eate, la base de graphe l&rsquo;ex\u00e9cute.<\/li>\n<li>Le mod\u00e8le conna\u00eet d\u00e9j\u00e0 la syntaxe ; <strong>le vrai d\u00e9fi, c&rsquo;est la justesse<\/strong> de la requ\u00eate par rapport \u00e0 la question et au graphe.<\/li>\n<li>Le prompt rassemble <strong>quatre ingr\u00e9dients<\/strong> : le sch\u00e9ma du graphe, des correspondances de vocabulaire, des exemples question\/requ\u00eate et des consignes de format.<\/li>\n<li>Sans sch\u00e9ma, <strong>le mod\u00e8le devine les noms<\/strong> des n\u0153uds, des relations et des propri\u00e9t\u00e9s.<\/li>\n<li>Comptages, classements et parcours de relations deviennent possibles, mais <strong>certaines questions restent rat\u00e9es<\/strong> : un exemple ajout\u00e9 ou un outil d\u00e9di\u00e9 les rattrape.<\/li>\n<\/ul>\n<h2 id=\"une-traduction-pas-une-recherche\">Une traduction, pas une recherche<\/h2>\n<p><strong>Text2cypher<\/strong> consiste \u00e0 convertir une question en langage naturel en un langage que la base de donn\u00e9es sait ex\u00e9cuter : Cypher, le langage des bases de graphe comme Neo4j. Le mod\u00e8le ne va rien chercher dans la base. Il \u00e9crit la requ\u00eate, et c&rsquo;est la base qui r\u00e9pond.<\/p>\n<p>Cypher est un langage d\u00e9claratif, \u00e0 la syntaxe simple et lisible. Voici une requ\u00eate minuscule, pour avoir un point de rep\u00e8re :<\/p>\n<pre><code>MATCH (a:Person)-[:FOLLOWS]-&gt;(b:Person)\nWHERE a.name = &quot;Alice&quot;\nRETURN b.name\n<\/code><\/pre>\n<p>Lis-la par ses trois verbes. <code>MATCH<\/code> d\u00e9crit la forme que l&rsquo;on cherche dans le graphe : une personne <code>a<\/code> reli\u00e9e \u00e0 une personne <code>b<\/code> par une relation <code>FOLLOWS<\/code>. <code>WHERE<\/code> fixe que <code>a<\/code> s&rsquo;appelle Alice, et <code>RETURN<\/code> demande le nom de <code>b<\/code>, c&rsquo;est-\u00e0-dire les personnes qu&rsquo;Alice suit.<\/p>\n<p>Ce langage n&rsquo;est pas un dialecte maison : l&rsquo;initiative openCypher le promeut comme standard d&rsquo;interrogation des bases de graphe, et il est d\u00e9j\u00e0 adopt\u00e9 par Amazon, Memgraph, SAP HANA et d&rsquo;autres. Un projet de langage officiel unifi\u00e9, GQL, s&rsquo;inspire d&rsquo;ailleurs de Cypher en t\u00eate des langages existants.<\/p>\n<p>Reste la difficult\u00e9. La plupart des mod\u00e8les connaissent d\u00e9j\u00e0 Cypher et sa syntaxe de base ; le d\u00e9fi est de produire <strong>une requ\u00eate correcte et pertinente<\/strong>, ce qui demande de comprendre la s\u00e9mantique de la question autant que la structure du graphe interrog\u00e9.<\/p>\n\n\n<figure data-wp-context=\"{&quot;imageId&quot;:&quot;6adef46aea3f6&quot;}\" data-wp-interactive=\"core\/image\" data-wp-key=\"6adef46aea3f6\" class=\"wp-block-image size-large wp-lightbox-container\"><img loading=\"lazy\" decoding=\"async\" width=\"2560\" height=\"1164\" data-wp-class--hide=\"state.isContentHidden\" data-wp-class--show=\"state.isContentVisible\" data-wp-init=\"callbacks.setButtonStyles\" data-wp-on--click=\"actions.showLightbox\" data-wp-on--load=\"callbacks.setButtonStyles\" data-wp-on--pointerdown=\"actions.preloadImage\" data-wp-on--pointerenter=\"actions.preloadImageWithDelay\" data-wp-on--pointerleave=\"actions.cancelPreload\" data-wp-on-window--resize=\"callbacks.setButtonStyles\" src=\"https:\/\/www.ranksignal.fr\/wp-content\/uploads\/2026\/10\/b1_ch04_workflow_text2cypher-scaled.png\" alt=\"Trois entr\u00e9es (la question de l'utilisateur, le sch\u00e9ma du graphe, des correspondances de vocabulaire et des exemples) convergent vers un prompt assembl\u00e9 que re\u00e7oit un mod\u00e8le, lequel produit la requ\u00eate Cypher\" class=\"wp-image-555\" srcset=\"https:\/\/www.ranksignal.fr\/wp-content\/uploads\/2026\/10\/b1_ch04_workflow_text2cypher-scaled.png 2560w, https:\/\/www.ranksignal.fr\/wp-content\/uploads\/2026\/10\/b1_ch04_workflow_text2cypher-300x136.png 300w, https:\/\/www.ranksignal.fr\/wp-content\/uploads\/2026\/10\/b1_ch04_workflow_text2cypher-1024x465.png 1024w, https:\/\/www.ranksignal.fr\/wp-content\/uploads\/2026\/10\/b1_ch04_workflow_text2cypher-768x349.png 768w, https:\/\/www.ranksignal.fr\/wp-content\/uploads\/2026\/10\/b1_ch04_workflow_text2cypher-1536x698.png 1536w, https:\/\/www.ranksignal.fr\/wp-content\/uploads\/2026\/10\/b1_ch04_workflow_text2cypher-2048x931.png 2048w\" sizes=\"auto, (max-width: 2560px) 100vw, 2560px\" \/><button\n\t\t\tclass=\"lightbox-trigger\"\n\t\t\ttype=\"button\"\n\t\t\taria-haspopup=\"dialog\"\n\t\t\tdata-wp-bind--aria-label=\"state.thisImage.triggerButtonAriaLabel\"\n\t\t\tdata-wp-init=\"callbacks.initTriggerButton\"\n\t\t\tdata-wp-on--click=\"actions.showLightbox\"\n\t\t\tdata-wp-style--right=\"state.thisImage.buttonRight\"\n\t\t\tdata-wp-style--top=\"state.thisImage.buttonTop\"\n\t\t>\n\t\t\t<svg xmlns=\"http:\/\/www.w3.org\/2000\/svg\" width=\"12\" height=\"12\" fill=\"none\" viewBox=\"0 0 12 12\">\n\t\t\t\t<path fill=\"#fff\" d=\"M2 0a2 2 0 0 0-2 2v2h1.5V2a.5.5 0 0 1 .5-.5h2V0H2Zm2 10.5H2a.5.5 0 0 1-.5-.5V8H0v2a2 2 0 0 0 2 2h2v-1.5ZM8 12v-1.5h2a.5.5 0 0 0 .5-.5V8H12v2a2 2 0 0 1-2 2H8Zm2-12a2 2 0 0 1 2 2v2h-1.5V2a.5.5 0 0 0-.5-.5H8V0h2Z\" \/>\n\t\t\t<\/svg>\n\t\t<\/button><figcaption class=\"wp-element-caption\">Le sch\u00e9ma se lit en entonnoir : trois sources alimentent un seul prompt, et le mod\u00e8le n&rsquo;intervient qu&rsquo;\u00e0 la sortie de cet entonnoir. Concr\u00e8tement, on r\u00e9cup\u00e8re la question, on r\u00e9cup\u00e8re le sch\u00e9ma du graphe, on y ajoute d&rsquo;autres informations utiles (correspondances de terminologie, consignes de format, exemples), on assemble le tout, puis on le soumet au mod\u00e8le, qui rend la requ\u00eate. Reprenons ces ingr\u00e9dients un par un.<\/figcaption><\/figure>\n\n\n<h2 id=\"le-schema-la-carte-que-le-modele-n-a-pas\">Le sch\u00e9ma : la carte que le mod\u00e8le n&rsquo;a pas<\/h2>\n<p>Sans sch\u00e9ma, le mod\u00e8le ne peut que deviner les noms des n\u0153uds, des relations et des propri\u00e9t\u00e9s. Avec un sch\u00e9ma, <strong>il dispose d&rsquo;une carte<\/strong> qui relie le sens de la question au mod\u00e8le du graphe : quelles \u00e9tiquettes existent sur les n\u0153uds, quels types de relations, quelles propri\u00e9t\u00e9s, et comment tout cela se connecte.<\/p>\n\n\n<figure data-wp-context=\"{&quot;imageId&quot;:&quot;6adef46aea8bb&quot;}\" data-wp-interactive=\"core\/image\" data-wp-key=\"6adef46aea8bb\" class=\"wp-block-image size-large wp-lightbox-container\"><img loading=\"lazy\" decoding=\"async\" width=\"2560\" height=\"1347\" data-wp-class--hide=\"state.isContentHidden\" data-wp-class--show=\"state.isContentVisible\" data-wp-init=\"callbacks.setButtonStyles\" data-wp-on--click=\"actions.showLightbox\" data-wp-on--load=\"callbacks.setButtonStyles\" data-wp-on--pointerdown=\"actions.preloadImage\" data-wp-on--pointerenter=\"actions.preloadImageWithDelay\" data-wp-on--pointerleave=\"actions.cancelPreload\" data-wp-on-window--resize=\"callbacks.setButtonStyles\" src=\"https:\/\/www.ranksignal.fr\/wp-content\/uploads\/2026\/10\/b1_ch06_modele_graphe_contrat-scaled.png\" alt=\"Mod\u00e8le de graphe d'un contrat : un n\u0153ud Contract, des n\u0153uds Organization reli\u00e9s au contrat par HAS_PARTY, et des n\u0153uds Location reli\u00e9s aux organisations par HAS_LOCATION\" class=\"wp-image-556\" srcset=\"https:\/\/www.ranksignal.fr\/wp-content\/uploads\/2026\/10\/b1_ch06_modele_graphe_contrat-scaled.png 2560w, https:\/\/www.ranksignal.fr\/wp-content\/uploads\/2026\/10\/b1_ch06_modele_graphe_contrat-300x158.png 300w, https:\/\/www.ranksignal.fr\/wp-content\/uploads\/2026\/10\/b1_ch06_modele_graphe_contrat-1024x539.png 1024w, https:\/\/www.ranksignal.fr\/wp-content\/uploads\/2026\/10\/b1_ch06_modele_graphe_contrat-768x404.png 768w, https:\/\/www.ranksignal.fr\/wp-content\/uploads\/2026\/10\/b1_ch06_modele_graphe_contrat-1536x808.png 1536w, https:\/\/www.ranksignal.fr\/wp-content\/uploads\/2026\/10\/b1_ch06_modele_graphe_contrat-2048x1078.png 2048w\" sizes=\"auto, (max-width: 2560px) 100vw, 2560px\" \/><button\n\t\t\tclass=\"lightbox-trigger\"\n\t\t\ttype=\"button\"\n\t\t\taria-haspopup=\"dialog\"\n\t\t\tdata-wp-bind--aria-label=\"state.thisImage.triggerButtonAriaLabel\"\n\t\t\tdata-wp-init=\"callbacks.initTriggerButton\"\n\t\t\tdata-wp-on--click=\"actions.showLightbox\"\n\t\t\tdata-wp-style--right=\"state.thisImage.buttonRight\"\n\t\t\tdata-wp-style--top=\"state.thisImage.buttonTop\"\n\t\t>\n\t\t\t<svg xmlns=\"http:\/\/www.w3.org\/2000\/svg\" width=\"12\" height=\"12\" fill=\"none\" viewBox=\"0 0 12 12\">\n\t\t\t\t<path fill=\"#fff\" d=\"M2 0a2 2 0 0 0-2 2v2h1.5V2a.5.5 0 0 1 .5-.5h2V0H2Zm2 10.5H2a.5.5 0 0 1-.5-.5V8H0v2a2 2 0 0 0 2 2h2v-1.5ZM8 12v-1.5h2a.5.5 0 0 0 .5-.5V8H12v2a2 2 0 0 1-2 2H8Zm2-12a2 2 0 0 1 2 2v2h-1.5V2a.5.5 0 0 0-.5-.5H8V0h2Z\" \/>\n\t\t\t<\/svg>\n\t\t<\/button><figcaption class=\"wp-element-caption\">Voil\u00e0 \u00e0 quoi ressemble ce qu&rsquo;un sch\u00e9ma d\u00e9crit, sur un autre graphe que celui des films : trois sortes de n\u0153uds, chacune avec ses propri\u00e9t\u00e9s (le montant et les dates pour le contrat, la ville et le pays pour le lieu), et deux relations nomm\u00e9es qui les relient. Un mod\u00e8le qui ignore l&rsquo;existence de <code>HAS_PARTY<\/code> ne peut pas en inventer le nom juste.<\/figcaption><\/figure>\n\n\n<p>Dans le prompt, ce sch\u00e9ma doit dire clairement quelles \u00e9tiquettes, quels types de relations et quelles propri\u00e9t\u00e9s sont disponibles, avec une consigne explicite : n&rsquo;utiliser que ces \u00e9l\u00e9ments, jamais d&rsquo;autres. Le format exact importe peu, selon les recherches internes de Neo4j. Tout exposer n&rsquo;est pas toujours souhaitable : cela d\u00e9pend de la taille du sch\u00e9ma et de sa pertinence pour le cas d&rsquo;usage.<\/p>\n<p>On peut aussi laisser la base se d\u00e9crire elle-m\u00eame. Sur de gros volumes, l&rsquo;inf\u00e9rence automatique co\u00fbte cher et <strong>on \u00e9chantillonne la base<\/strong> plut\u00f4t que de l&rsquo;analyser enti\u00e8rement ; elle repose sur la proc\u00e9dure <code>apoc.meta.data()<\/code> de la biblioth\u00e8que APOC, pr\u00e9sente en auto-h\u00e9berg\u00e9 comme dans Aura.<\/p>\n<pre><code>NODE_PROPERTIES_QUERY = &quot;&quot;&quot;\nCALL apoc.meta.data()\nYIELD label, other, elementType, type, property\nWHERE NOT type = &quot;RELATIONSHIP&quot; AND elementType = &quot;node&quot;\nWITH label AS nodeLabels, collect({property:property, type:type}) AS properties\nRETURN {labels: nodeLabels, properties: properties} AS output\n&quot;&quot;&quot;\nREL_PROPERTIES_QUERY = &quot;&quot;&quot;\nCALL apoc.meta.data()\nYIELD label, other, elementType, type, property\nWHERE NOT type = &quot;RELATIONSHIP&quot; AND elementType = &quot;relationship&quot;\nWITH label AS relType, collect({property:property, type:type}) AS properties\nRETURN {type: relType, properties: properties} AS output\n&quot;&quot;&quot;\nREL_QUERY = &quot;&quot;&quot;\nCALL apoc.meta.data()\nYIELD label, other, elementType, type, property\nWHERE type = &quot;RELATIONSHIP&quot; AND elementType = &quot;node&quot;\nUNWIND other AS other_node\nRETURN {start: label, type: property, end: toString(other_node)} AS output\n&quot;&quot;&quot;\n<\/code><\/pre>\n<p>Trois requ\u00eates, trois questions pos\u00e9es \u00e0 la base elle-m\u00eame : quelles propri\u00e9t\u00e9s portent les n\u0153uds, quelles propri\u00e9t\u00e9s portent les relations, et quels types de n\u0153uds se relient entre eux. Une fonction les ex\u00e9cute puis range les r\u00e9sultats dans un dictionnaire ; une seconde le transforme en texte, avec trois rubriques lisibles (\u00e9tiquettes de n\u0153uds et propri\u00e9t\u00e9s, types de relations et propri\u00e9t\u00e9s, liste des relations). S\u00e9parer la r\u00e9cup\u00e9ration de la mise en forme permet d&rsquo;essayer librement plusieurs pr\u00e9sentations du sch\u00e9ma dans le prompt.<\/p>\n<blockquote>\n<p><strong>\ud83d\udca1 Intuition<\/strong> Pense \u00e0 un interpr\u00e8te \u00e0 qui l&rsquo;on remet, avant la r\u00e9union, le plan du quartier, un petit lexique, deux phrases mod\u00e8les et une consigne de rendu. Il conna\u00eet d\u00e9j\u00e0 la langue d&rsquo;arriv\u00e9e ; ce qui lui manque, c&rsquo;est le terrain.<\/p>\n<\/blockquote>\n<h2 id=\"terminologie-exemples-format-trois-garde-fous\">Terminologie, exemples, format : trois garde-fous<\/h2>\n<p>Le sch\u00e9ma ne suffit pas toujours. Imagine que le mod\u00e8le cherche le pays de production d&rsquo;un film comme s&rsquo;il s&rsquo;agissait d&rsquo;une propri\u00e9t\u00e9 du n\u0153ud <code>Movie<\/code>, alors que le pays est un n\u0153ud \u00e0 part enti\u00e8re du graphe. La requ\u00eate s&rsquo;\u00e9crit, elle a l&rsquo;air plausible, et elle ne trouve rien.<\/p>\n<p>Le rem\u00e8de est un <strong>exemple few-shot<\/strong> : quelques paires question\/requ\u00eate que l&rsquo;on glisse dans le prompt, par opposition au zero-shot o\u00f9 rien n&rsquo;est donn\u00e9. Ces exemples sont propres au graphe interrog\u00e9 et se construisent \u00e0 la main ; ils servent surtout quand le mod\u00e8le interpr\u00e8te mal le sch\u00e9ma ou r\u00e9p\u00e8te la m\u00eame erreur, par exemple en attendant une propri\u00e9t\u00e9 l\u00e0 o\u00f9 il faut parcourir une relation.<\/p>\n<pre><code>Question: In what country was the movie Ready Player One produced?\nCypher: MATCH (m:Movie { title: 'Ready Player One' })-[:PRODUCED_IN]-&gt;(c:Country)\nRETURN c.name\n<\/code><\/pre>\n<p>Cet exemple montre le bon chemin : du film vers le pays, en suivant <code>PRODUCED_IN<\/code>. Il ne corrige pas seulement la question d&rsquo;origine, mais toute une famille de questions voisines, car le mod\u00e8le dispose d\u00e9sormais d&rsquo;un motif \u00e0 reproduire pour retrouver un nom de pays.<\/p>\n<p>Deuxi\u00e8me garde-fou : traduire le vocabulaire de l&rsquo;utilisateur vers celui du sch\u00e9ma. Un sch\u00e9ma bien con\u00e7u emploie des noms et des verbes pour les \u00e9tiquettes et les relations, des adjectifs et des noms pour les propri\u00e9t\u00e9s, mais le mod\u00e8le peut malgr\u00e9 tout s&rsquo;y perdre. <strong>Ces correspondances sont propres \u00e0 chaque graphe<\/strong> et se r\u00e9utilisent mal d&rsquo;un graphe \u00e0 l&rsquo;autre.<\/p>\n<pre><code>TERMINOLOGY MAPPING:\nPersons: When a user asks about a person by trade, they are referring to a node with\nthe label Person. Movies: When a user asks about a film or movie, they are referring\nto a node with the label Movie.\n<\/code><\/pre>\n<p>Ce court passage l\u00e8ve l&rsquo;ambigu\u00eft\u00e9 entre \u00ab film \u00bb ou \u00ab personne \u00bb dans la bouche de l&rsquo;utilisateur et les \u00e9tiquettes <code>Movie<\/code> et <code>Person<\/code> du graphe, sans toucher au reste du prompt.<\/p>\n<p>Troisi\u00e8me garde-fou : la forme de la r\u00e9ponse. Chaque mod\u00e8le a ses habitudes : certains entourent la requ\u00eate de balises de code, d&rsquo;autres ajoutent du texte avant ou apr\u00e8s. On uniformise en demandant explicitement la requ\u00eate et rien d&rsquo;autre. La derni\u00e8re consigne de cette instruction, \u00ab NO CODE BLOCKS \u00bb, <strong>suffit \u00e0 \u00e9liminer l&rsquo;essentiel des divergences<\/strong> de mise en forme d&rsquo;un mod\u00e8le \u00e0 l&rsquo;autre.<\/p>\n<pre><code>FORMAT INSTRUCTIONS:\nDo not include any explanations or apologies in your responses. Do not respond to\nany questions that might ask anything else than for you to construct a Cypher state-\nment. Do not include any text except the generated Cypher statement. ONLY\nRESPOND WITH CYPHER, NO CODE BLOCKS.\n<\/code><\/pre>\n<h2 id=\"le-gabarit-assemble-et-ce-qu-il-produit\">Le gabarit assembl\u00e9, et ce qu&rsquo;il produit<\/h2>\n<p>Les quatre ingr\u00e9dients se r\u00e9unissent dans un seul gabarit de prompt. Le texte est en anglais ; rep\u00e8re les emplacements entre accolades, que le code remplit avant l&rsquo;envoi.<\/p>\n<pre><code>prompt_template = &quot;&quot;&quot;\nInstructions:\nGenerate Cypher statement to query a graph database to get the data to answer\nthe following user question.\nGraph database schema:\nUse only the provided relationship types and properties in the schema.\nDo not use any other relationship types or properties that are not provided in\nthe schema.\n{schema}\nTerminology mapping:\nThis section is helpful to map terminology between the user question and the\ngraph database schema.\n{terminology}\nExamples:\nThe following examples provide useful patterns for querying the graph database.\n{examples}\nFormat instructions:\nDo not include any explanations or apologies in your responses.\nDo not respond to any questions that might ask anything else than for you to\nconstruct a Cypher statement.\nDo not include any text except the generated Cypher statement.\nONLY RESPOND WITH CYPHER--NO CODE BLOCKS.\nUser question: {question}\n&quot;&quot;&quot;\n<\/code><\/pre>\n<p>Chaque rubrique du gabarit correspond \u00e0 un ingr\u00e9dient : le sch\u00e9ma (avec la consigne de s&rsquo;en tenir \u00e0 ce qu&rsquo;il contient), la table de terminologie, les exemples, le format, puis la question de l&rsquo;utilisateur en dernier. Sur la base de films, avec le vrai sch\u00e9ma, une correspondance de vocabulaire et un exemple, la question du d\u00e9but, \u00ab Who directed the most movies ? \u00bb, produit ceci :<\/p>\n<pre><code>MATCH (p:Person)-[:DIRECTED]-&gt;(m:Movie)\nRETURN p.name, COUNT(m) AS movieCount\nORDER BY movieCount\nDESC LIMIT 1\n<\/code><\/pre>\n<p>Lis la requ\u00eate dans l&rsquo;ordre. Elle cherche des personnes reli\u00e9es \u00e0 des films par <code>DIRECTED<\/code>, compte les films de chacune (<code>COUNT(m)<\/code>), trie du plus grand total au plus petit et garde la premi\u00e8re ligne. <strong>La requ\u00eate parcourt la relation <code>DIRECTED<\/code><\/strong> au lieu de chercher une propri\u00e9t\u00e9 qui n&rsquo;existe pas, et c&rsquo;est exactement ce que les \u00e9l\u00e9ments de contexte du prompt permettent.<\/p>\n<h2 id=\"ce-que-la-traduction-rend-possible\">Ce que la traduction rend possible<\/h2>\n<p>Les deux requ\u00eates vues jusqu&rsquo;ici montrent deux gestes diff\u00e9rents. L&rsquo;une calcule : compter, trier, retenir un maximum. L&rsquo;autre suit un lien jusqu&rsquo;\u00e0 un autre n\u0153ud : du film vers son pays. Dans les deux cas, la r\u00e9ponse est un chemin dans le graphe ou un calcul sur des chemins, pas un passage de texte \u00e0 retrouver.<\/p>\n<p>LangChain emballe ce geste en quelques lignes. Ici, une cha\u00eene combine un mod\u00e8le et un graphe existant, puis r\u00e9pond \u00e0 une question pos\u00e9e telle quelle.<\/p>\n<pre><code>from langchain.chains import GraphCypherQAChain\ngraphchain = GraphCypherQAChain.from_llm(\n    llm, graph=graph, verbose=True, return_intermediate_steps=True\n)\nresults = graphchain.invoke({&quot;query&quot;:&quot;People who have kids&quot;})\nprint(results[&quot;result&quot;])\n<\/code><\/pre>\n<p><code>llm<\/code> est le mod\u00e8le de langage et <code>graph<\/code> la connexion au <a href=\"\/blog\/rag\/qu-est-ce-qu-un-graphe-de-connaissances\/\" title=\"graphe de connaissances\">graphe de connaissances<\/a> d\u00e9j\u00e0 construit. La question \u00ab People who have kids \u00bb entre en langage naturel ; <strong>la cha\u00eene g\u00e9n\u00e8re la requ\u00eate Cypher correspondante<\/strong>, l&rsquo;ex\u00e9cute sur le graphe et rend le r\u00e9sultat. Tu \u00e9cris en langage courant, un mod\u00e8le \u00e9crit en Cypher.<\/p>\n<h2 id=\"ou-se-range-text2cypher\">O\u00f9 se range text2cypher<\/h2>\n<p>Autour d&rsquo;un graphe, d&rsquo;autres moyens d&rsquo;aller chercher l&rsquo;information existent. Une <a href=\"\/blog\/rag\/qu-est-ce-qu-une-recherche-vectorielle\/\" title=\"recherche vectorielle\">recherche vectorielle<\/a> sur un index, une recherche locale qui \u00e9largit le contexte \u00e0 partir d&rsquo;entit\u00e9s proches : leur requ\u00eate Cypher est \u00e9crite une fois pour toutes, et seuls ses param\u00e8tres changent d&rsquo;un appel \u00e0 l&rsquo;autre (le nombre de r\u00e9sultats voulu, le vecteur de la question, les plafonds \u00e0 retenir). Avec text2cypher, <strong>la requ\u00eate na\u00eet au moment de la question<\/strong>. C&rsquo;est ce qui le rend souple, et ce qui le rend moins pr\u00e9visible.<\/p>\n<p>C\u00f4t\u00e9 mod\u00e8le, un mod\u00e8le g\u00e9n\u00e9raliste bien guid\u00e9 par un prompt structur\u00e9 suffit pour d\u00e9marrer. Il existe aussi des mod\u00e8les sp\u00e9cialis\u00e9s : un jeu de donn\u00e9es d&rsquo;entra\u00eenement ouvert est disponible sur Hugging Face, et des mod\u00e8les en ont \u00e9t\u00e9 ajust\u00e9s \u00e0 partir de Gemma2 ou de Llama 3.1. Ils restent derri\u00e8re les plus grands mod\u00e8les propri\u00e9taires ajust\u00e9s, mais co\u00fbtent bien moins \u00e0 ex\u00e9cuter, un atout quand la latence des gros mod\u00e8les devient un probl\u00e8me en production.<\/p>\n<h2 id=\"ce-que-text2cypher-ne-garantit-pas\">Ce que text2cypher ne garantit pas<\/h2>\n<h3>\u00ab Le mod\u00e8le conna\u00eet Cypher, la requ\u00eate sera donc bonne \u00bb<\/h3>\n<p>Conna\u00eetre la syntaxe et comprendre ton graphe sont deux choses distinctes. La syntaxe de base est acquise pour la plupart des mod\u00e8les ; ce qui reste \u00e0 leur charge, c&rsquo;est la justesse, et elle d\u00e9pend de ce que tu mets dans le prompt. L&rsquo;exemple du pays de production le montre : une requ\u00eate syntaxiquement irr\u00e9prochable peut viser une propri\u00e9t\u00e9 qui n&rsquo;existe pas.<\/p>\n<h3>\u00ab Une base de graphe, donc toute question trouve sa requ\u00eate \u00bb<\/h3>\n<p>Pas toujours. Dans une \u00e9valuation sur la base de films, le syst\u00e8me \u00e9choue sur \u00ab Who has the longest name among all actors ? \u00bb faute de g\u00e9n\u00e9rer la requ\u00eate appropri\u00e9e. Deux rem\u00e8des sont propos\u00e9s : un exemple few-shot qui guide la g\u00e9n\u00e9ration, ou un outil d\u00e9di\u00e9 \u00e0 ce type de question.<\/p>\n<p>Pour rep\u00e9rer ces \u00e9checs, il vaut mieux noter les questions de test avec une requ\u00eate Cypher de r\u00e9f\u00e9rence plut\u00f4t qu&rsquo;avec une r\u00e9ponse fig\u00e9e. <strong>La requ\u00eate de r\u00e9f\u00e9rence reste valable<\/strong> quand les donn\u00e9es \u00e9voluent, alors qu&rsquo;une r\u00e9ponse \u00e9crite \u00e0 la main devient fausse.<\/p>\n<h3>\u00ab Traduire ne co\u00fbte rien \u00bb<\/h3>\n<p>Chaque traduction est un appel de plus \u00e0 un mod\u00e8le de langage. Dans la m\u00eame \u00e9valuation, les questions qui peuvent s&rsquo;en passer reviennent nettement plus vite : on \u00e9conomise un passage par le mod\u00e8le. Text2cypher se justifie donc l\u00e0 o\u00f9 une requ\u00eate pr\u00e9cise est indispensable, pas partout.<\/p>\n<blockquote>\n<p><strong>\ud83d\udccc \u00c0 retenir<\/strong> Un mod\u00e8le de langage ne cherche rien dans le graphe : il r\u00e9dige la requ\u00eate qui ira chercher. Sa fiabilit\u00e9 se construit avec ce qu&rsquo;on lui donne : le sch\u00e9ma d&rsquo;abord, puis le vocabulaire, les exemples et le format.<\/p>\n<\/blockquote>","protected":false},"excerpt":{"rendered":"<p>Text2cypher traduit une question en langage courant en requ\u00eate Cypher pour une base de graphe. Ses ingr\u00e9dients, un exemple lu pas \u00e0 pas, ses limites.<\/p>\n","protected":false},"author":1,"featured_media":557,"comment_status":"closed","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[5],"tags":[],"class_list":["post-558","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-rag"],"_links":{"self":[{"href":"https:\/\/www.ranksignal.fr\/blog\/wp-json\/wp\/v2\/posts\/558","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.ranksignal.fr\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.ranksignal.fr\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.ranksignal.fr\/blog\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/www.ranksignal.fr\/blog\/wp-json\/wp\/v2\/comments?post=558"}],"version-history":[{"count":3,"href":"https:\/\/www.ranksignal.fr\/blog\/wp-json\/wp\/v2\/posts\/558\/revisions"}],"predecessor-version":[{"id":830,"href":"https:\/\/www.ranksignal.fr\/blog\/wp-json\/wp\/v2\/posts\/558\/revisions\/830"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/www.ranksignal.fr\/blog\/wp-json\/wp\/v2\/media\/557"}],"wp:attachment":[{"href":"https:\/\/www.ranksignal.fr\/blog\/wp-json\/wp\/v2\/media?parent=558"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.ranksignal.fr\/blog\/wp-json\/wp\/v2\/categories?post=558"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.ranksignal.fr\/blog\/wp-json\/wp\/v2\/tags?post=558"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}