{"id":651652,"date":"2025-10-21T08:54:27","date_gmt":"2025-10-21T07:54:27","guid":{"rendered":"https:\/\/blog.jetbrains.com\/?post_type=ai&#038;p=651652"},"modified":"2025-10-21T08:59:29","modified_gmt":"2025-10-21T07:59:29","slug":"koog-a2a-creer-des-agents-d-ia-connectes-en-kotlin","status":"publish","type":"ai","link":"https:\/\/blog.jetbrains.com\/fr\/ai\/2025\/10\/koog-a2a-creer-des-agents-d-ia-connectes-en-kotlin\/","title":{"rendered":"Koog \u00d7 A2A\u00a0: Cr\u00e9er des agents d&#8217;IA connect\u00e9s en Kotlin"},"content":{"rendered":"<p>Si vous avez d\u00e9j\u00e0 essay\u00e9 de cr\u00e9er un syst\u00e8me de plusieurs agents d&#8217;IA, vous avez probablement rencontr\u00e9 <em>le probl\u00e8me<\/em>. Cela commence assez simplement\u00a0: vous avez un agent qui r\u00e9dige des articles de blog, un autre qui les relit, et peut-\u00eatre un troisi\u00e8me qui sugg\u00e8re ou g\u00e9n\u00e8re des images. Individuellement, ils sont efficaces. Mais pour les faire travailler ensemble\u00a0? C&#8217;est l\u00e0 que les choses peuvent se compliquer.<\/p>\n<p>Chaque agent parle son propre \u00ab langage \u00bb\u00a0: l&#8217;un utilise une interface API diff\u00e9rente, un autre a son propre format de message, et tous peuvent avoir des exigences d&#8217;authentification sp\u00e9cifiques. Les faire communiquer implique d&#8217;\u00e9crire un code d&#8217;int\u00e9gration personnalis\u00e9 pour chaque connexion. Au lieu de vous concentrer sur l&#8217;am\u00e9lioration de l&#8217;intelligence, de la rapidit\u00e9 ou de l&#8217;utilit\u00e9 de vos agents, vous perdez du temps \u00e0 construire des passerelles entre eux.<\/p>\n<h2 class=\"wp-block-heading\">Ce qu&#8217;apporte A2A\u00a0: la couche de communication entre agents<\/h2>\n<p><strong>C&#8217;est l\u00e0 que le <\/strong><a href=\"https:\/\/a2a-protocol.org\/latest\/\" target=\"_blank\" rel=\"noopener\"><strong>protocole Agent2Agent (A2A)<\/strong><\/a><strong> entre en jeu.<\/strong><\/p>\n<p>A2A permet \u00e0 vos agents de communiquer directement via un protocole normalis\u00e9, qui fonctionne comme un traducteur universel pour votre \u00e9cosyst\u00e8me d&#8217;IA. Votre agent de r\u00e9daction de blog transmet de mani\u00e8re transparente le contenu \u00e0 votre relecteur, qui d\u00e9clenche votre g\u00e9n\u00e9rateur d&#8217;images, tandis que le relecteur revient avec des corrections et que le g\u00e9n\u00e9rateur d&#8217;images demande des clarifications sur le style. Le tout orchestr\u00e9 via une seule couche de communication unifi\u00e9e.<\/p>\n<p>A2A \u00e9vite d&#8217;avoir \u00e0 g\u00e9rer des dizaines de connexions point \u00e0 point en offrant les avantages suivants\u00a0:<\/p>\n<ul>\n<li><strong>Connectivit\u00e9 plug-and-play<\/strong>\u00a0: les agents se d\u00e9couvrent et se connectent automatiquement les uns aux autres.<\/li>\n<li><strong>Messagerie standardis\u00e9e<\/strong>\u00a0: format unifi\u00e9, protocole clair et aucun probl\u00e8me de traduction.<\/li>\n<li><strong>Orchestration int\u00e9gr\u00e9e<\/strong>\u00a0: d\u00e9finissez les workflows une seule fois, puis laissez A2A g\u00e9rer la coordination.<\/li>\n<li><strong>\u00c9volutivit\u00e9 sans complexit\u00e9<\/strong>\u00a0: ajoutez ou r\u00e9utilisez des agents sans r\u00e9\u00e9crire les connexions existantes.<\/li>\n<\/ul>\n<p>R\u00e9sultat\u00a0? Vous pouvez vous consacrer \u00e0 l&#8217;am\u00e9lioration des capacit\u00e9s de vos agents au lieu de perdre du temps \u00e0 d\u00e9boguer leurs conversations. Mieux encore : vous pouvez impl\u00e9menter vos agents en utilisant le langage ou le framework de votre choix. Pour les utilisateurs de la JVM, Koog est un choix \u00e9vident et, \u00e0 partir de sa version 0.5.0, il s&#8217;int\u00e8gre parfaitement \u00e0 l&#8217;\u00e9cosyst\u00e8me A2A.<\/p>\n<h2 class=\"wp-block-heading\">Ce qu&#8217;apporte Koog\u00a0: le moteur d&#8217;orchestration interne<\/h2>\n<p>Koog est un framework bas\u00e9 sur Kotlin qui permet de cr\u00e9er des agents d&#8217;IA ciblant les applications JVM, Android, iOS, WebAssembly et dans le navigateur. Il se distingue sur les points suivants :<\/p>\n<ul>\n<li><strong>La gestion des workflows complexes<\/strong>\u00a0: concevez des strat\u00e9gies bas\u00e9es sur des graphiques avec prise en charge des boucles, des branches, des r\u00e9visions et de l&#8217;ex\u00e9cution de branches parall\u00e8les.<\/li>\n<li><strong>Les composants pr\u00eats \u00e0 l&#8217;emploi<\/strong>\u00a0: b\u00e9n\u00e9ficiez de ses n\u0153uds int\u00e9gr\u00e9s pour appeler des LLM et des outils externes, r\u00e9sumer l&#8217;historique des messages et ex\u00e9cuter des strat\u00e9gies enti\u00e8res.<\/li>\n<li><strong>L&#8217;orchestration d&#8217;outils<\/strong>\u00a0: transformez n&#8217;importe quelle fonction de votre code en un outil pour votre agent d&#8217;IA, utilisable de mani\u00e8re s\u00e9quentielle ou m\u00eame en parall\u00e8le.<\/li>\n<li><strong>L&#8217;int\u00e9gration native du protocole MCP<\/strong>\u00a0: connectez-vous de mani\u00e8re fluide \u00e0 n&#8217;importe quel serveur MCP avec le SDK Kotlin MCP.<\/li>\n<li><strong>La prise en charge de la m\u00e9moire et du stockage<\/strong>\u00a0: prise en charge int\u00e9gr\u00e9e de la m\u00e9moire de l&#8217;agent et des workflows RAG (g\u00e9n\u00e9ration augment\u00e9e de r\u00e9cup\u00e9ration) avec une gestion efficace du contexte.<\/li>\n<li><strong>Tol\u00e9rance aux pannes<\/strong>\u00a0: int\u00e9gration des nouvelles tentatives, points de contr\u00f4le, m\u00e9canismes de r\u00e9cup\u00e9ration et persistance de l&#8217;\u00e9tat pour garantir une ex\u00e9cution fiable.<\/li>\n<li><strong>L&#8217;observabilit\u00e9<\/strong>\u00a0: gestion compl\u00e8te des \u00e9v\u00e9nements de l&#8217;agent, journalisation et prise en charge d&#8217;OpenTelemetry avec des int\u00e9grations directes avec Langfuse et W&amp;B Weave.<\/li>\n<\/ul>\n<p>En bref, Koog est id\u00e9al pour cr\u00e9er des agents d&#8217;IA fiables.<\/p>\n<h2 class=\"wp-block-heading\">Pourquoi associer Koog et A2A<\/h2>\n<p>Koog et A2A couvrent diff\u00e9rentes couches de la pile des agents d&#8217;IA. Utilis\u00e9s ensemble, ils se compl\u00e8tent et comblent leurs lacunes respectives.<\/p>\n<p><strong>Koog g\u00e8re d\u00e9j\u00e0 les parties les plus complexes<\/strong> de l&#8217;orchestration IA n\u00e9cessaire \u00e0 une utilisation concr\u00e8te en entreprise.<\/p>\n<p><strong>A2A ajoute la pi\u00e8ce manquante\u00a0: <\/strong>il permet \u00e0 vos agents Koog de communiquer avec tous les autres agents compatibles A2A de votre \u00e9cosyst\u00e8me. Au lieu de cr\u00e9er des int\u00e9grations personnalis\u00e9es pour chaque service externe, vos workflows d&#8217;IA Koog peuvent automatiquement d\u00e9couvrir et utiliser d&#8217;autres agents.<\/p>\n<p><strong>Le r\u00e9sultat est une combinaison parfaite<\/strong>\u00a0: les workflows avanc\u00e9s de Koog deviennent des t\u00e2ches A2A que n&#8217;importe quel agent peut d\u00e9clencher, tandis que vos agents Koog exploitent toute la puissance de l&#8217;\u00e9cosyst\u00e8me A2A. Et comme Koog s&#8217;ex\u00e9cute en arri\u00e8re-plan, sur l&#8217;appareil lui-m\u00eame et dans les environnements de navigateur, vous pouvez fournir une IA interconnect\u00e9e de mani\u00e8re plus large et plus efficace que jamais.<\/p>\n<p>Comment est-ce possible\u00a0? Voyons cela !<\/p>\n<h2 class=\"wp-block-heading\">Protocole A2A<\/h2>\n<p>Le protocole A2A d\u00e9finit les composantes essentielles de la communication entre agents\u00a0:<\/p>\n<ul>\n<li><strong>D\u00e9couverte des agents<\/strong> via des cartes d&#8217;agents standardis\u00e9s (documents JSON d\u00e9crivant leurs capacit\u00e9s).<\/li>\n<li><strong>Formats de messages<\/strong> pour les requ\u00eates et les r\u00e9ponses avec des sch\u00e9mas coh\u00e9rents.<\/li>\n<li><strong>Gestion du cycle de vie des t\u00e2ches<\/strong> avec des \u00e9tats clairs\u00a0: soumis \u2192 en cours \u2192 termin\u00e9\/en \u00e9chec.<\/li>\n<li><strong>Couches de transport<\/strong> telles que JSON-RPC, gRPC et REST.<\/li>\n<li><strong>Sch\u00e9mas de s\u00e9curit\u00e9<\/strong> utilisant la norme OAuth2, des cl\u00e9s API et des jetons JWT.<\/li>\n<li><strong>Gestion des erreurs<\/strong> avec des codes d&#8217;erreur standardis\u00e9s.<\/li>\n<\/ul>\n<figure class=\"wp-block-image size-full\"><img decoding=\"async\" loading=\"lazy\" class=\"wp-image-645498\" src=\"https:\/\/blog.jetbrains.com\/wp-content\/uploads\/2025\/09\/image-50.png\" alt=\"\" width=\"1008\" height=\"752\" \/><\/figure>\n<h3 class=\"wp-block-heading\">Cartes d&#8217;agents\u00a0: des cartes de visite num\u00e9riques<\/h3>\n<p>Chaque agent de l&#8217;\u00e9cosyst\u00e8me A2A publie ses capacit\u00e9s \u00e0 l&#8217;aide d&#8217;une \u00ab carte d&#8217;agent \u00bb : un fichier JSON standardis\u00e9 h\u00e9berg\u00e9 \u00e0 une URL, par exemple <em>\/.well-known\/carte-agent.json<\/em>, sur le domaine de l&#8217;agent. La carte d&#8217;agent agit comme une carte de visite num\u00e9rique qui permet aux autres agents de d\u00e9couvrir les services qu&#8217;il propose.<\/p>\n<p>Une carte d&#8217;agent contient g\u00e9n\u00e9ralement les \u00e9l\u00e9ments suivants\u00a0:<\/p>\n<ul>\n<li><strong>Informations de base<\/strong>\u00a0: le nom de l&#8217;agent, sa description et sa version.<\/li>\n<li><strong>Comp\u00e9tences<\/strong>\u00a0: ce que l&#8217;agent peut faire (par exemple, r\u00e9diger des documents, relire du texte, analyser des donn\u00e9es et g\u00e9n\u00e9rer des images).<\/li>\n<li><strong>Points de terminaison<\/strong>\u00a0: commentcommuniquer avec l&#8217;agent.\u00a0<\/li>\n<li><strong>Autres informations facultatives\u00a0: <\/strong>fonctionnalit\u00e9s activ\u00e9es, authentification, etc.<\/li>\n<\/ul>\n<p>Ce m\u00e9canisme de d\u00e9couverte \u00e9limine le travail d&#8217;int\u00e9gration manuel. Lorsqu&#8217;un agent a besoin d&#8217;une comp\u00e9tence sp\u00e9cifique, il consulte simplement la carte d&#8217;agent correspondante pour comprendre comment interagir avec ce service.<\/p>\n<p>Dans Koog, les cartes d&#8217;agents sont d\u00e9finies \u00e0 l&#8217;aide de classes de donn\u00e9es Kotlin :<\/p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"kotlin\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">val agentCard = AgentCard(\n    name = \"R\u00e9dacteur de blog\",\n    description = \"Agent IA qui cr\u00e9e des articles et des publications de blog de haute qualit\u00e9\",\n    url = \"https:\/\/api.blog-writer.com\/a2a\/v1\",\n    version = \"1.0.0\",\n    capabilities = AgentCapabilities(streaming = true),\n    defaultInputModes = listOf(\"text\/plain\"),\n    defaultOutputModes = listOf(\"text\/markdown\"),\n    skills = listOf(\n        AgentSkill(\n            id = \"write-post\",\n            name = \"R\u00e9daction d'articles de blog\",\n            description = \"G\u00e9n\u00e9rer des articles de blog int\u00e9ressants sur tout sujet\",\n            tags = listOf(\"r\u00e9daction\", \"contenu\", \"blog\"),\n            examples = listOf(\"\u00c9crire un article sur les tendances de l'IA\")\n        )\n    )\n)<\/pre>\n<h3 class=\"wp-block-heading\">Messagerie universelle\u00a0: un sch\u00e9ma simple<\/h3>\n<p>A2A utilise un format de message unique et standardis\u00e9 pour toutes les communications entre agents. Cette simplicit\u00e9 offre un avantage cons\u00e9quent\u00a0: au lieu de devoir apprendre des dizaines d&#8217;API diff\u00e9rentes, les agents n&#8217;ont qu&#8217;un seul sch\u00e9ma de communication \u00e0 comprendre.<\/p>\n<p>Chaque interaction suit le m\u00eame flux\u00a0:<\/p>\n<ol>\n<li><strong>Envoyer un message<\/strong> avec la demande de t\u00e2che et les param\u00e8tres.<\/li>\n<li><strong>Recevoir <\/strong>soit des r\u00e9sultats imm\u00e9diats, soit une t\u00e2che \u00e0 suivre.<\/li>\n<li><strong>Obtenir des mises \u00e0 jour<\/strong> sur des canaux en temps r\u00e9el pour les op\u00e9rations plus longues.<\/li>\n<\/ol>\n<p>Gr\u00e2ce \u00e0 cette approche universelle, l&#8217;ajout de nouvelles fonctionnalit\u00e9s d&#8217;agent ne n\u00e9cessite pas de changer de protocole de communication. Que vous demandiez \u00e0 un agent de r\u00e9sumer un texte ou de g\u00e9n\u00e9rer un rapport complexe, la structure du message reste coh\u00e9rente.<\/p>\n<p>Dans Koog, cr\u00e9er et envoyer un message est simple, car cela utilise des objets et des protocoles d\u00e9j\u00e0 impl\u00e9ment\u00e9s\u00a0:<\/p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"kotlin\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">val message = Message(\n    role = Role.User,\n    parts = listOf(\n        TextPart(\"R\u00e9diger un article de blog sur l'avenir des agents d'IA\")\n    ),\n    contextId = \"blog-project-456\"\n)\n\nval request = Request(\n    data = MessageSendParams(\n        message = message,\n        configuration = MessageConfiguration(\n            blocking = false, \/\/ Get first response\n            historyLength = 5 \/\/ Include context\n        )\n    )\n)\n\nval response = client.sendMessage(request)<\/pre>\n<p>Le format du message prend en charge le contenu enrichi via diff\u00e9rents types de <em>Part<\/em>, notamment TextPart pour le contenu en texte brut, FilePart pour les pi\u00e8ces jointes et DataPart pour les donn\u00e9es JSON structur\u00e9es.<\/p>\n<p>Cette structure unifi\u00e9e permet \u00e0 vos agents Koog de communiquer de mani\u00e8re transparente avec n&#8217;importe quel agent compatible A2A, que ce soit pour le traitement de texte, l&#8217;analyse de fichiers ou les transformations de donn\u00e9es complexes.<\/p>\n<h3 class=\"wp-block-heading\">Cycle de vie des t\u00e2ches\u00a0: workflows intelligents<\/h3>\n<p>A2A g\u00e8re intelligemment diff\u00e9rents types de t\u00e2ches en fonction de leur complexit\u00e9 et de leur dur\u00e9e\u00a0:<\/p>\n<p><strong>Messages imm\u00e9diats<\/strong>\u00a0: des op\u00e9rations simples comme la mise en forme de texte ou des calculs rapides renvoient des r\u00e9sultats directement dans la r\u00e9ponse de l&#8217;IA. Aucune attente, aucun suivi ne sont n\u00e9cessaires.<\/p>\n<p><strong>T\u00e2ches de longue dur\u00e9e<\/strong>\u00a0: des op\u00e9rations complexes telles que l&#8217;analyse de documents ou des workflows en plusieurs \u00e9tapes sont planifi\u00e9es et renvoient une t\u00e2che. L&#8217;agent demandeur peut alors surveiller la progression et r\u00e9cup\u00e9rer les r\u00e9sultats de la t\u00e2che une fois qu&#8217;ils sont pr\u00eats.<\/p>\n<p><strong>Mises \u00e0 jour en temps r\u00e9el<\/strong>\u00a0: pour les op\u00e9rations chronophages, les \u00e9v\u00e9nements envoy\u00e9s par le serveur (SSE) fournissent des mises \u00e0 jour de progression en direct. Cela permet d&#8217;informer les agents sans qu&#8217;ils aient besoin de sonder constamment le serveur.<\/p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"kotlin\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">class BlogWriterExecutor : AgentExecutor {\n    override suspend fun execute(\n        context: RequestContext,\n        eventProcessor: SessionEventProcessor\n    ) {\n        val task = Task(\n            contextId = context.contextId,\n            status = TaskStatus(\n                state = TaskState.Submitted,\n                message = Message(\n                    role = Role.Agent,\n                    parts = listOf(TextPart(\"Demande de r\u00e9daction d'article de blog re\u00e7ue\")),\n                    contextId = context.contextId,\n    \t\t\ttaskId = context.taskId,\n                )\n            )\n        )\n\n        eventProcessor.sendTaskEvent(task)\n\t...\n    }\n}\n<\/pre>\n<h3 class=\"wp-block-heading\">S\u00e9curit\u00e9 int\u00e9gr\u00e9e\u00a0: normes industrielles uniquement<\/h3>\n<p>A2A ne r\u00e9invente pas la s\u00e9curit\u00e9. Il s&#8217;appuie sur des normes \u00e9prouv\u00e9es et largement adopt\u00e9es comme OAuth2, les cl\u00e9s d&#8217;API et le protocole HTTPS standard.<\/p>\n<p>Gr\u00e2ce \u00e0 cette approche, les d\u00e9veloppeurs n&#8217;ont pas besoin d&#8217;apprendre de nouveaux sch\u00e9mas d&#8217;authentification. Si vous connaissez la s\u00e9curit\u00e9 des API web modernes, alors vous comprenez d\u00e9j\u00e0 la s\u00e9curit\u00e9 A2A. Le syst\u00e8me h\u00e9rite de tous les outils, meilleures pratiques et audits de s\u00e9curit\u00e9 qui entourent ces normes \u00e9tablies.<\/p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"json\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">\"securitySchemes\": {\n   \"google\": {\n       \"openIdConnectUrl\": \"https:\/\/accounts.google.com\/.well-known\/openid-configuration\",\n       \"type\": \"openIdConnect\"\n   }\n}\n<\/pre>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"kotlin\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">class AuthorizedA2AServer(\n    agentExecutor: AgentExecutor,\n    agentCard: AgentCard,\n    agentCardExtended: AgentCard? = null,\n    taskStorage: TaskStorage = InMemoryTaskStorage(),\n    messageStorage: MessageStorage = InMemoryMessageStorage(),\n    private val authService: AuthService, \/\/ Service responsable de l'authentification\n) : A2AServer(\n    agentExecutor = agentExecutor,\n    agentCard = agentCard,\n    agentCardExtended = agentCardExtended,\n    taskStorage = taskStorage,\n    messageStorage = messageStorage,\n) {\n\n    private suspend fun authenticateAndAuthorize(\n        ctx: ServerCallContext,\n        requiredPermission: String\n    ): AuthenticatedUser {\n        val token = ctx.headers[\u00ab\u00a0Authorisation\"]?.firstOrNull()\n            ?: throw A2AInvalidParamsException(\"Jeton d'autorisation manquant\")\n\n        val user = authService.authenticate(token)\n            ?: throw A2AInvalidParamsException(\"Jeton d'autorisation non valide\")\n\n        if (requiredPermission !in user.permissions) {\n            throw A2AUnsupportedOperationException(\"Autorisations insuffisantes\")\n        }\n\n        return user\n    }\n\n   override suspend fun onSendMessage(\n        request: Request,\n        ctx: ServerCallContext\n    ): Response {\n        val user = authenticateAndAuthorize(ctx, requiredPermission = \"send_message\")\n\n        \/\/ Transmet les donn\u00e9es de l'utilisateur \u00e0 l'outil d'ex\u00e9cution de l'agent via l'\u00e9tat du contexte\n        val enrichedCtx = ctx.copy(\n            state = ctx.state + (AuthStateKeys.USER to user)\n        )\n\n        \/\/ D\u00e9l\u00e8gue \u00e0 l'impl\u00e9mentation parente avec un contexte enrichi\n        return super.onSendMessage(request, enrichedCtx)\n    }\n\n   \/\/ le reste des m\u00e9thodes A2A encapsul\u00e9es\n   \/\/...\n}<\/pre>\n<h2 class=\"wp-block-heading\">Comment int\u00e9grer des agents Koog avec A2A<\/h2>\n<p>Le framework Koog int\u00e8gre le client et le serveur A2A. Vos agents Koog peuvent ainsi communiquer de mani\u00e8re transparente avec d&#8217;autres agents compatibles A2A et devenir d\u00e9tectables. Voici un exemple simple montrant comment impl\u00e9menter cela.<\/p>\n<h3 class=\"wp-block-heading\">Comment int\u00e9grer des agents Koog dans des serveurs A2A<\/h3>\n<p>Commencez par d\u00e9finir une strat\u00e9gie pour l&#8217;agent. Koog fournit des convertisseurs pratiques (<em>toKoogMessage<\/em>, <em>toA2AMessage<\/em>) pour transformer simplement les formats de message Koog et A2A, ce qui \u00e9limine le besoin de s\u00e9rialisation manuelle. Des n\u0153uds sp\u00e9cialis\u00e9s tels que <em>nodeA2ASendMessage<\/em> g\u00e8rent le processus d&#8217;\u00e9change de messages, ce qui facilite l&#8217;impl\u00e9mentation des workflows de communication\u00a0:<\/p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"kotlin\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">fun blogpostWritingStrategy() = strategy(\"strategie-redaction-article-blog\") {\n    val blogpostRequest by node { input -&gt;\n        val userMessage = input.toKoogMessage().content\n\n        llm.writeSession {\n            user {\n                +\"R\u00e9diger un article de blog \u00e0 partir de la demande de l'utilisateur\"\n                +xml {\n                    tag(\"user_request\") {\n                        +userMessage\n                    }\n                }\n            }\n\n            requestLLM().toA2AMessage()\n        }\n    }\n\n    val sendMessage by nodeA2ARespondMessage()\n\n    nodeStart then blogpostRequest then sendMessage the nodeFinish\n}<\/pre>\n<p>Ensuite, d\u00e9finissez l&#8217;agent lui-m\u00eame. Une fois la fonctionnalit\u00e9 <em>A2AServer<\/em> install\u00e9e, votre agent devient d\u00e9tectable et accessible aux autres acteurs de l&#8217;\u00e9cosyst\u00e8me, ce qui permet de cr\u00e9er des r\u00e9seaux sophistiqu\u00e9s dans lesquels les agents sp\u00e9cialis\u00e9s collaborent de mani\u00e8re transparente.<\/p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"kotlin\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">fun createBlogpostWritingAgent(\n    requestContext: RequestContext,\n    eventProcessor: SessionEventProcessor\n): AIAgent {\n     \/\/ R\u00e9cup\u00e8re les messages existants pour le contexte de la conversation actuelle\n     val messageHistory = requestContext.messageStorage.getAll().map { it.toKoogMessage() }\n\n     val agentConfig = AIAgentConfig(\n        prompt = prompt(\"article de blog\") {\n            system(\"Vous \u00eates un agent de r\u00e9daction d'articles de blog\")\n\n            messages(messageHistory)\n        },\n        model = GoogleModels.Gemini2_5Flash,\n        maxAgentIterations = 5\n    )\n\n    return agent = AIAgent(\n        promptExecutor = MultiLLMPromptExecutor(\n            LLMProvider.Google to GoogleLLMClient(System.getenv(\"GOOGLE_API_KEY\")),\n        ),\n        strategy = blogpostWritingStrategy(),\n        agentConfig = agentConfig\n    ) {\n        install(A2AAgentServer) {\n            this.context = requestContext\n            this.eventProcessor = eventProcessor\n        }\n\n        handleEvents {\n            onAgentFinished { ctx -&gt;\n                \/\/ Met \u00e0 jour le contexte de la conversation avec la r\u00e9ponse de l'agent\n                val resultMessge = ctx.result as A2AMessage\n                requestContext.messageStorage.save(resultMessge)\n            }\n        }\n    }\n}<\/pre>\n<p>Nous devons encapsuler l&#8217;agent dans l&#8217;outil d&#8217;ex\u00e9cution, puis d\u00e9finir un serveur.<\/p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"kotlin\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">class BlogpostAgentExecutor : AgentExecutor {\n    override suspend fun execute(\n        context: RequestContext,\n        eventProcessor: SessionEventProcessor\n    ) {\n        createBlogpostWritingAgent(context, eventProcessor)\n            .run(context.params.message)\n    }\n}\n\nval a2aServer = A2AServer(\n    agentExecutor = BlogpostAgentExecutor(),\n    agentCard = agentCard,\n)<\/pre>\n<p>L&#8217;\u00e9tape finale consiste \u00e0 d\u00e9finir un transport de serveur et \u00e0 ex\u00e9cuter le serveur.<\/p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"kotlin\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">val transport = HttpJSONRPCServerTransport(\n    requestHandler = a2aServer\n)\n\ntransport.start(\n    engineFactory = Netty,\n    port = 8080,\n    path = \"\/a2a\",\n    wait = true,\n    agentCard = agentCard,\n    agentCardPath = A2AConsts.AGENT_CARD_WELL_KNOWN_PATH\n)<\/pre>\n<p>Votre agent est maintenant pr\u00eat \u00e0 g\u00e9rer les demandes\u00a0!\u00a0<\/p>\n<h3 class=\"wp-block-heading\">Comment appeler d&#8217;autres agents compatibles A2A \u00e0 partir d&#8217;un agent Koog<\/h3>\n<p>Commencez par configurer un client A2A et connectez-le pour r\u00e9cup\u00e9rer une carte d&#8217;agent.<\/p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"kotlin\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">val agentUrl = \u00ab\u00a0https:\/\/exemple.com\"\n\nval cardResolver = UrlAgentCardResolver(\n    baseUrl = agentUrl,\n    path = A2AConsts.AGENT_CARD_WELL_KNOWN_PATH,\n)\n\nval transport = HttpJSONRPCClientTransport(\n    url = agentUrl,\n)\n\nval a2aClient = A2AClient(\n    transport = transport,\n    agentCardResolver = cardResolver\n)\n\n\/\/ Initialise le client et r\u00e9cup\u00e8re la carte\na2aClient.connect()<\/pre>\n<p>Vous pouvez ensuite utiliser <em>nodeA2ASendMessage<\/em> ou <em>nodeA2ASendMessageStreaming<\/em> dans votre strat\u00e9gie pour appeler ces clients et recevoir un message ou une r\u00e9ponse \u00e0 une t\u00e2che.<\/p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"kotlin\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">val agentId = \"agent_id\"\nval agent = AIAgent(\n    promptExecutor = MultiLLMPromptExecutor(\n        LLMProvider.Google to GoogleLLMClient(System.getenv(\"GOOGLE_API_KEY\")),\n    ),\n    strategy = strategy(\"a2a\") {\n        val nodePrepareRequest by node&lt;String, A2AClientRequest&gt; { input -&gt;\n            A2AClientRequest(\n                agentId = agentId,\n                callContext = ClientCallContext.Default,\n                params = MessageSendParams(\n                    message = A2AMessage(\n                        messageId = Uuid.random().toString(),\n                        role = Role.User,\n                        parts = listOf(\n                            TextPart(input)\n                        )\n                    )\n                )\n            )\n        }\n        val nodeA2A by nodeA2AClientSendMessage(agentId)\n        \n        val nodeProcessResponse by node {\n            \/\/ Traite l'\u00e9v\u00e9nement\n            when (it) {\n                is A2AMessage -&gt; it.parts\n                    .filterIsInstance()\n                    .joinToString(separator = \"n\") { it.text }\n                \n                is Task -&gt; it.artifacts\n                    .orEmpty()\n                    .flatMap { it.parts }\n                    .filterIsInstance()\n                    .joinToString(separator = \"n\") { it.text }\n            }\n        }\n\n        nodeStart then nodePrepareRequest then nodeA2A then nodeProcessResponse then nodeFinish\n\n    },\n    agentConfig = agentConfig\n) {\n   install(A2AAgentClient) {\n        this.a2aClients = mapOf(agentId to client)\n    }\n}\n\nagent.run(\"R\u00e9diger un article de blog sur A2A et son int\u00e9gration avec Koog\")<\/pre>\n<h2 class=\"wp-block-heading\">Prochaines \u00e9tapes<\/h2>\n<p>Pour approfondir vos connaissances sur Koog et A2A, consultez ces ressources utiles\u00a0:<\/p>\n<p><a href=\"https:\/\/docs.koog.ai\/\" target=\"_blank\" rel=\"noopener\">Documentation de Koog<\/a><\/p>\n<p><a href=\"https:\/\/a2a-protocol.org\/latest\/specification\/\" target=\"_blank\" rel=\"noopener\">Sp\u00e9cification d&#8217;A2A<\/a><\/p>\n<p><a href=\"https:\/\/github.com\/JetBrains\/koog\/tree\/develop\/examples\/simple-examples\/src\/main\/kotlin\/ai\/koog\/agents\/example\/a2a\" target=\"_blank\" rel=\"noopener\" data-type=\"link\" data-id=\"https:\/\/github.com\/JetBrains\/koog\/tree\/develop\/examples\/simple-examples\/src\/main\/kotlin\/ai\/koog\/agents\/example\/a2a\">Exemples d&#8217;A2A avec Koog<\/a><\/p>\n\n\n<p><em>Auteur de l&#8217;article original en anglais<\/em> :<\/p>\n\n\n    <div class=\"about-author \">\n        <div class=\"about-author__box\">\n            <div class=\"row\">\n                <div class=\"about-author__box-img\">\n                    <img decoding=\"async\" src=\"https:\/\/blog.jetbrains.com\/wp-content\/uploads\/2025\/10\/T0288D531-UL11E3JG5-1870329d7bae-512.png\" width=\"200\" height=\"200\" alt=\"Andrey Bragin\" loading=\"lazy\"  class=\"avatar avatar-200 wp-user-avatar wp-user-avatar-200 photo avatar-default\">\n                <\/div>\n                <div class=\"about-author__box-text\">\n                                            <h4>Andrey Bragin<\/h4>\n                                                        <\/div>\n            <\/div>\n        <\/div>\n    <\/div>\n","protected":false},"author":813,"featured_media":651683,"comment_status":"closed","ping_status":"closed","template":"","categories":[89,907],"tags":[6847,8724],"cross-post-tag":[6355],"acf":[],"_links":{"self":[{"href":"https:\/\/blog.jetbrains.com\/fr\/wp-json\/wp\/v2\/ai\/651652"}],"collection":[{"href":"https:\/\/blog.jetbrains.com\/fr\/wp-json\/wp\/v2\/ai"}],"about":[{"href":"https:\/\/blog.jetbrains.com\/fr\/wp-json\/wp\/v2\/types\/ai"}],"author":[{"embeddable":true,"href":"https:\/\/blog.jetbrains.com\/fr\/wp-json\/wp\/v2\/users\/813"}],"replies":[{"embeddable":true,"href":"https:\/\/blog.jetbrains.com\/fr\/wp-json\/wp\/v2\/comments?post=651652"}],"version-history":[{"count":5,"href":"https:\/\/blog.jetbrains.com\/fr\/wp-json\/wp\/v2\/ai\/651652\/revisions"}],"predecessor-version":[{"id":651707,"href":"https:\/\/blog.jetbrains.com\/fr\/wp-json\/wp\/v2\/ai\/651652\/revisions\/651707"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/blog.jetbrains.com\/fr\/wp-json\/wp\/v2\/media\/651683"}],"wp:attachment":[{"href":"https:\/\/blog.jetbrains.com\/fr\/wp-json\/wp\/v2\/media?parent=651652"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/blog.jetbrains.com\/fr\/wp-json\/wp\/v2\/categories?post=651652"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/blog.jetbrains.com\/fr\/wp-json\/wp\/v2\/tags?post=651652"},{"taxonomy":"cross-post-tag","embeddable":true,"href":"https:\/\/blog.jetbrains.com\/fr\/wp-json\/wp\/v2\/cross-post-tag?post=651652"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}