Accueil / Articles

Une architecture à quatre niveaux pour un agent de synthèse d’informations destiné aux dirigeants sur Azure

Comment un agent de préparation déterministe répartit le travail entre Fabric, des procédures stockées SQL, FastAPI sur Azure Functions et Copilot Studio pour générer des documents destinés aux dirigeants.

Éditeur : REACTAPP.TOP22 min de lecture5073 mots

Sources: **Chief of Staff Executive Briefing Agent: Enterprise-scale layered architecture for automated intelligence generation**

Tout ceux qui préparent un dirigeant pour une réunion avec un client savent à quel point les informations brutes sont dispersées. Les données CRM se trouvent dans un système, les chiffres de chiffre d’affaires dans un autre, l’utilisation des produits dans un troisième, et il faut reconstituer manuellement le récit stratégique à partir de tous ces éléments. Le service d’information destiné au « chef de cabinet » vise précisément cette tâche fastidieuse : une personne demande des informations en langage simple, et la plateforme renvoie un document prêt à être présenté au dirigeant, élaboré à partir de données opérationnelles, financières et relatives à l’adoption de l’IA fragmentées.

Ce qui rend cette conception digne d’étude, c’est le faible degré de hasard qu’elle laisse entrer en jeu. Au lieu de demander à un modèle d’écrire le descriptif à partir d’une instruction, elle remplace l’agrégation manuelle par une orchestration régie par des contrats. Les données sont consolidées dans le cadre d’une gouvernance stricte, agrégées à l’aide de requêtes SQL déterministes, acheminées via des services sans état et livrées grâce à un flux conversationnel contrôlé. L’architecture définit des limites d’exécution claires entre quatre couches : la couche de base, la couche des données, la couche des services et la couche de l’expérience, permettant ainsi de reproduire les résultats, d’en effectuer le contrôle et de les livrer à une vitesse adaptée aux besoins de l’entreprise, tout en respectant des exigences de sécurité strictes.

Ce design provient d’un effort d’incubation interne en IA au sein de l’écosystème Microsoft, et les affirmations concernant les performances et les résultats présentées ci-dessous proviennent des concepteurs eux-mêmes, sans avoir fait l’objet de mesures indépendantes. Il est particulièrement utile pour les architectes d’entreprise, les ingénieurs en IA et les responsables de plateforme qui ont besoin de systèmes d’IA déterministes permettant de séparer la consolidation des données, l’agrégation SQL, l’orchestration sans état et les workflows conversationnels.

Le design à quatre couches

L’agent suit un modèle d’orchestration à quatre couches, chacune ayant une fonction spécifique et couvrant plusieurs services Azure ainsi que des limites de sécurité :

  • Fondation : un Microsoft Fabric Lakehouse qui consolide les données sous contrôle rigoureux.
  • Données : Fabric SQL (un entrepôt de données ou un point d’accès SQL) contenant des procédures stockées pour les requêtes opérationnelles et l’agrégation.
  • Service : un moteur d’orchestration FastAPI hébergé dans Azure Functions.
  • Expérience utilisateur : un flux de travail conversationnel Copilot Studio qui interprète les intentions des utilisateurs.
  • Ils forment ensemble un pipeline d’intelligence unidirectionnel. Les données sont sélectionnées et gérées au niveau de la base, agrégées de manière déterministe dans SQL, assemblées par la logique métier au niveau du service, puis présentées via une interaction en langage naturel. Chaque couche entretient un contrat explicite avec ses voisines, de sorte que l’élément d’intelligence artificielle vient compléter le processus de prise de décision sans le perturber.

    Couche de base : consolidation contrôlée des données

    Microsoft Fabric agit comme plateforme d’analyse unifiée et source unique de vérité pour les détails des comptes, les données financières, les métriques d’utilisation et les tendances historiques. La règle de base est simple : stocker les données une seule fois et les réutiliser à de nombreuses reprises, afin qu’aucune information ne soit dupliquée entre les systèmes.

    Un pipeline se met à jour quotidiennement, en récupérant et en fusionnant des données d’entreprise provenant de Dynamics 365, de systèmes financiers et d’API externes. Ce calendrier permet des fenêtres de traitement prévisibles et s’assure que chaque système source ait terminé son travail quotidien avant le début de la consolidation. Le lakehouse conserve les données dans leur format d’origine, accompagné de normes de traçabilité et de gouvernance ; les ensembles de données affinés sont ensuite transférés vers Fabric SQL, la couche de requêtes opérationnelles rapide dont les structures sont adaptées à la génération des rapports destinés aux dirigeants.

    Couche de données : un moteur opérationnel conçu pour l’accès via API

    Cette couche transforme des données de base soigneusement sélectionnées en contrats de requête déterministes à faible latence pour la génération de rapports. Elle définit des limites d’agrégation strictes ainsi que les garanties de performance auxquelles s’attendent les dirigeants d’une solution réactive.

    Architecture. Fabric SQL Warehouse est une interface relationnelle qui encapsule toutes les opérations sur les données liées aux rapports au sein de procédures stockées. La couche de service ne consulte jamais directement les tables. Les jointures complexes entre tables, la signification de chaque agrégation ainsi que la logique métier s’exécutent toutes à l’intérieur de la base de données et non dans le code de l’application, ce qui permet de maintenir des temps de réponse prévisibles même avec de gros volumes de données. Le SQL est optimisé pour les requêtes opérationnelles et non pour l’exploration analytique ; sa conception vise des jointures déterministes fournissant des résultats cohérents, des plans de requête réutilisables pour une performance stable, une exécution paramétrée pour plus de sécurité, ainsi que des agrégations encapsulées plus faciles à entretenir.

    Filosophie du schéma : la requête en premier. Le schéma suit les modèles de requêtes plutôt que la normalisation théorique. L’entité Account constitue la principale structure de regroupement, et les tables d’appui sont organisées de manière à ce que les jointures s’exécutent efficacement sur l’identifiant commercial, le TPID.

         --Accounts Table - Primary Business Entity
    
    CREATE TABLE [aieo].[Accounts] (
    
        [Id] INT IDENTITY(1,1) PRIMARY KEY,
    
        [TPID] BIGINT NOT NULL,
    
        [AccountName] NVARCHAR(255) NOT NULL,
    
        [AccountNumber] NVARCHAR(50),
    
        [SearchTokens] NVARCHAR(500),
    
        [Segment] NVARCHAR(100),
    
        [Industry] NVARCHAR(100),
    
        [ACR_FY25] DECIMAL(15,2),
    
        [RevenueRank] INT,
    
        [CreatedDate] DATETIME2 DEFAULT GETUTCDATE(),
    
        [LastSync] DATETIME2,
    
        [IsActive] BIT DEFAULT 1
    
    );
    
    
    
    CREATE CLUSTERED INDEX IX_Accounts_TPID ON [aieo].[Accounts]([TPID]);
    
    CREATE NONCLUSTERED INDEX IX_Accounts_Name ON [aieo].[Accounts](
    

    Le tableau des comptes est l’entité centrale, dont le TPID constitue la clé de regroupement. Cela permet une recherche approximativement logarithmique pour les requêtes les plus fréquentes liées aux comptes, et il gère à la fois les requêtes directes par TPID et les recherches par nom.

    Tables auxiliaires. Quelques-unes des tables auxiliaires illustrent la manière dont les indicateurs financiers, les informations sur l’utilisation de l’IA et l’enrichissement inter-systèmes sont gérés.

    -- Revenue Table - Historical and Forecast Data
    
    CREATE TABLE [aieo].[Revenue] (
    
        [TPID] BIGINT,
    
        [FiscalYear] NVARCHAR(10),
    
        [RevenueType] NVARCHAR(20),
    
        [Amount] DECIMAL(15,2),
    
        [LastUpdated] DATETIME2
    
    );
    
    CREATE INDEX IX_Revenue_TPID_FY ON [aieo].[Revenue]([TPID], [FiscalYear]);
    

    Le tableau des recettes conserve plusieurs années de données financières et utilise des index composés adaptés aux calculs de séries temporelles et de tendances.

    -- AI Usage Table - Product Adoption Metrics
    
    CREATE TABLE [aieo].[AIUsage] (
    
        [TPID] BIGINT,
    
        [ProductCategory] NVARCHAR(50),
    
        [UsageLevel] NVARCHAR(20),
    
        [SeatCount] INT,
    
        [AdoptionRate] DECIMAL(5,2),
    
        [LastUpdated] DATETIME2
    
    );
    
    CREATE INDEX IX_AIUsage_TPID_Product ON [aieo].[AIUsage]([TPID], [ProductCategory]);
    

    Le tableau d’utilisation de l’IA enregistre l’adoption des produits par catégorie, ce qui permet d’analyser les modèles d’engagement et l’adoption des technologies importantes pour la planification des comptes.

    Stratégie de procédures stockées : la logique métier se trouve dans la base de données. Trois procédures principales gèrent toute la génération d’informations. SearchAccounts trouve les comptes et les classe par chiffre d’affaires, GetAccountDetails agrège des données provenant de multiples sources pour fournir un contexte complet aux dirigeants, et GetBriefingTemplate gère l’autorisation des modèles en fonction des rôles afin que seules les personnes appropriées aient accès. La couche de service appelle uniquement ces procédures, et la création de SQL dynamique n’est pas autorisée.

    --Account Search Procedure - Revenue-Prioritized Discovery
    
    CREATE OR ALTER PROCEDURE [aieo].[SearchAccounts]
    
        @searchTerm NVARCHAR(255),
    
        @top INT = 5
    
    AS
    
    BEGIN
    
        SET NOCOUNT ON;
    
    
    
        DECLARE @likeTerm NVARCHAR(257) = '%' + REPLACE(REPLACE(@searchTerm, '[', '[[]'), '%', '[%]') + '%'
    
    
    
        SELECT TOP (@top)
    
            a.TPID,
    
            a.AccountName,
    
            a.AccountNumber,
    
            ISNULL(acr.ACR_YTD, 0) as ACR_YTD
    
        FROM [aieo].[Accounts] a
    
        LEFT JOIN [aieo].[ACR] acr ON a.TPID = acr.TPID
    
        WHERE
    
            a.AccountName LIKE @likeTerm
    
            OR CAST(a.TPID AS NVARCHAR) = @searchTerm
    
            OR a.AccountNumber LIKE @likeTerm
    
        ORDER BY
    
            ISNULL(acr.ACR_YTD, 0) DESC,
    
            a.AccountName ASC
    
        FOR JSON PATH;
    
    END;
    
    --Account Details Retrieval Procedure - Comprehensive Intelligence Aggregation
    
    CREATE PROCEDURE [aieo].[GetAccountDetails]
    
    (@TPID int)
    
    AS
    
    BEGIN
    
        SET NOCOUNT ON;
    
        SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED;
    
    
    
        SELECT TOP 1
    
            JSON_QUERY(ap.[AccountProfile]) AS [Account],
    
            JSON_QUERY(ai.[AIUsage]) AS [AI],
    
            JSON_QUERY(acr.[Data]) AS [ACR],
    
            deal.[Deals.AgreementValue],
    
            deal.[Deals.DealType],
    
            deal.[Deals.Term],
    
            deal.[Deals.Remaining],
    
            deal.[Deals.EndDate],
    
            ecif.[ECIF.Committed],
    
            aco.[ACO.IncrementalRevenue],
    
            aco.[ACO.Discount],
    
            aco.[ACO.ACO],
    
            JSON_QUERY(bot.[Data]) AS [BoT],
    
            JSON_QUERY(inv.[Data]) AS [Investments],
    
            JSON_QUERY(rev.[Data]) AS [Revenue]
    
        FROM [dbo].[vw_Customer] P
    
        OUTER APPLY (
    
            SELECT TOP 1
    
                a.[TPID],
    
                a.[CRMAccountName] AS [AccountName],
    
                a.[Segment],
    
                a.[Industry],
    
                a.[EOU],
    
                a.[OU],
    
                LOWER(TRIM(c.[Value])) AS [ATU_Manager.Alias],
    
                aau.[Mail] AS [ATU_Manager.Email],
    
                [dbo].[RemoveJobTitle](aau.[DisplayName]) AS [ATU_Manager.DisplayName],
    
                NULLIF(TRIM(aau.[BusinessPhone]), '') AS [ATU_Manager.PhoneNumber],
    
                JSON_QUERY([am].[Data]) AS [AM],
    
                JSON_QUERY([atum].[Data]) AS [ATUM]
    
            FROM [dbo].[vw_Customer] A
    
            OUTER APPLY STRING_SPLIT(a.[AM], ',', 1) C
    
            LEFT JOIN [cm].[vw_AAD_User] AAU ON LOWER(TRIM(C.[Value])) = LOWER(AAU.[UserPrincipalName])
    
            WHERE A.TPID = P.TPID
    
        ) ap
    
        WHERE P.TPID = @TPID;
    
    END;
    
    --Template Management Procedure - Role-Based Access Control
    
    CREATE OR ALTER PROCEDURE [aieo].[GetBriefingTemplate]
    
    (
    
        @UserAlias NVARCHAR(200),
    
        @BriefingType NVARCHAR(200)
    
    )
    
    AS
    
    BEGIN
    
        SET @UserAlias = IIF(CHARINDEX('@', @UserAlias) > 0,
    
                            LEFT(@UserAlias, CHARINDEX('@', @UserAlias) - 1),
    
                            @UserAlias)
    
    
    
        SELECT DISTINCT
    
            [BriefingTemplateId],[TemplateDescription],[BriefingType],
    
            [UserAlias],FullName,[Filename],[FileType],[PreviewFilename]
    
        FROM [hr].[DimPerson] p
    
        INNER JOIN [aieo].[ExecutiveOfficeMember] eom ON eom.[PersonnelNumber] = p.[PersonnelNumber]
    
        INNER JOIN [aieo].[ExecutiveOffice] o ON o.[ExecutiveOfficeId] = eom.[ExecutiveOfficeId]
    
        INNER JOIN [aieo].[BriefingTemplate] bt ON bt.[ExecutiveOfficeId] = o.[ExecutiveOfficeId]
    
    
    
        UNION
    
    
    
        SELECT DISTINCT
    
            [BriefingTemplateId],[TemplateDescription],[BriefingType],
    
            p.EmailName As UserAlias,p.FullName,[Filename],[FileType],[PreviewFilename]
    
        FROM [aieo].[BriefingTemplate] bt
    
        CROSS JOIN [hr].[DimPerson] p
    
        WHERE bt.[UserAlias] = 'ALL' AND p.EmailName = @UserAlias
    
            AND bt.BriefingType = @BriefingType;
    
    END;
    

    Performance et sécurité. Les transactions utilisent SET NOCOUNT ON pour améliorer l’efficacité, ainsi que SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED pour les opérations de lecture afin qu’elles ne bloquent pas. La manipulation du JSON repose sur les fonctions natives de SQL Server, JSON_QUERY et FOR JSON PATH, qui permettent un traitement efficace et une sérialisation directe depuis la base de données. Seule l’exécution paramétrée est autorisée ; l’identité de service de l’application ne dispose que des droits d’exécution sur les procédures stockées, sans accès direct aux tables.

    GRANT EXECUTE ON SCHEMA::[aieo] TO [service_identity];
     - No direct table access permitted
    

    Couche de service : orchestration sans état et limite d’exécution

    Le niveau de service s’occupe uniquement de l’orchestration des données. Il récupère les données, affiche des modèles et n’applique aucune règle métier propre. Chaque service a une seule responsabilité et est lâchement couplé, ce qui permet de maintenir les couches de données et de présentation clairement séparées.

    Interface API et validation. Une application FastAPI expose des points de terminaison REST explicites avec des contrats fortement typés définis sous forme de modèles Pydantic :

    • POST /api/briefing/generate est le principal point de terminaison pour la génération des rapports, avec une validation complète des entrées.
    • GET /api/health indique l’état du système.
    • GET /api/templates liste les modèles disponibles pour l’utilisateur qui effectue la requête.
    from pydantic import BaseModel
    from typing import Optional
    class BriefingRequest(BaseModel):
    account_name_or_tpid: str
    meeting_datetime: Optional[str] = None
    meeting_objective: Optional[str] = None
    ms_attendees: Optional[str] = None
    template_type: Optional[str] = None
    

    Le modèle impose une validation stricte et une sécurité des types, génère automatiquement de la documentation OpenAPI et ajuste la structure de chaque requête. Aucune entrée non structurée ne peut passer ce contrôle.

    Modèle d’exécution. Le service exécute une séquence déterministe en cinq étapes :

    1. Validation de la requête. Les modèles Pydantic s’assurent que les types et les entrées sont acceptables.
    2. Résolution du compte. Le format de l’entrée détermine s’il faut rechercher directement un TPID ou appeler SearchAccounts pour une recherche par nom.
    3. Récupération simultanée. GetAccountDetails et GetBriefingTemplate s’exécutent en même temps grâce à asyncio.gather().
    4. Assemblage du contexte. Les résultats des procédures sont directement affectés à des objets prêts pour le modèle, sans traitement intermédiaire.
  • Rendu de modèles. Jinja2 génère le document final avec un formatage déterministe.
  • L’exécution asynchrone et le partage de connexions via une abstraction DatabaseService garantissent que des entrées identiques produisent des sorties identiques.

    Principes directeurs. Cinq règles régissent la couche de service. Il n’y a pas de logique métier à l’intérieur, car l’analyse est générée uniquement au sein des procédures stockées, ce qui permet à la couche de service d’éviter les règles qui compliquent les tests et l’entretien. Elle se charge uniquement de l’orchestration des données, coordonnant leur récupération et leur affichage sans les transformer ni les calculer. Le mappage contextuel est direct, de sorte que les résultats des procédures atteignent directement les structures de modèles sans détour, réduisant ainsi le risque que le sens varie entre les données et la présentation. Le traitement est déterministe, produisant toujours le même résultat pour les mêmes entrées, quel que soit le moment ou l’état de l’infrastructure. De plus, les responsabilités restent clairement séparées : la logique des données se trouve dans les procédures SQL, l’orchestration dans les services Python FastAPI et la présentation dans les modèles Jinja2.

    Couche d’expérience : Copilot Studio en tant qu’engine de conversation

    En haut de la chaîne, le framework AdaptiveDialog de Microsoft Copilot Studio transforme une demande en langage naturel en un flux de travail déterministe. Il gère la résolution des comptes, l’autorisation des modèles, l’intégration de données externes, la génération de documents ainsi que leur livraison via plusieurs canaux. Le flux s’exécute séquentiellement, depuis la première demande jusqu’à la résolution du compte, à l’autorisation du modèle, à la collecte de données externes et à leur transmission vers SharePoint, avec des points de décision et des étapes de validation le long de chaque parcours de conversation.

    Dialogue principal. Ce dialogue collecte automatiquement l’identifiant du compte, définit les phrases déclencheuses qui lancent l’interaction et fixe le point d’entrée principal de la conversation :

    kind: AdaptiveDialog
    
    inputs:
    
      - kind: AutomaticTaskInput
    
        propertyName: Account_Name_or_TPID
    
        name: Account Name or TPID
    
        description: The account/customer TPID (unique identifier) or name
    
        shouldPromptUser: true
    
    
    
    modelDescription: Create briefing document to prepare for meeting with customer
    
    beginDialog:
    
      kind: OnRecognizedIntent
    
      intent:
    
        displayName: Topic for creating briefing document to prepare for meeting with customer
    
        triggerQueries:
    
          - Generate a briefing document for account Accenture
    
          - I need a briefing for Amazon
    
          - Create briefing for Apple
    
          - I have a meeting with JPMorgan Chase at 4PM
    

    Résolution du compte. Une vérification IsNumeric() classe l’entrée soit comme un TPID direct, soit comme un nom de compte nécessitant une recherche. Éviter la recherche chaque fois que c’est possible réduit le nombre d’appels API et accélère la réponse :

    - kind: SetVariable
    
      id: setVariable_IsNumeric
    
      variable: Topic.IsNumeric
    
      value: =IsNumeric(Topic.UserInput)
    
    
    
    - kind: ConditionGroup
    
      id: conditionGroup_InputType
    
      conditions:
    
        - id: condition_IsTPID
    
          condition: =Topic.IsNumeric
    
          actions:
    
            - kind: SetVariable
    
              variable: Topic.Account_TPID
    
              value: =Topic.UserInput
    

    Appel de recherche de compte. Pour les noms, le flux appelle l’endpoint de recherche de la couche de service via REST. Il encode en URL les caractères spéciaux, s’authentifie à l’aide d’une clé de souscription et définit un schéma de réponse afin que les données soient automatiquement validées :

    - kind: HttpRequestAction
    
      id: httpRequest_SearchAccounts
    
      displayName: Search Accounts by Name
    
      url: =Global.ApiBaseUrl & "/api/mssales/search/" & EncodeUrl(Topic.UserInput)
    
      headers:
    
        Ocp-Apim-Subscription-Key: =Global.ApiKey
    
      response: Topic.SearchResults
    
      responseSchema:
    
        kind: Record
    
        properties:
    
          accounts:
    
            type:
    
              kind: Table
    
              properties:
    
                account_name: String
    
                tpid: String
    
          total_count: Number
    

    Carte de dissimulation de l’ambiguïté. Lorsqu’une recherche révèle plusieurs comptes, une carte adaptative affiche un ensemble de choix dynamique généré à l’aide de ForAll() à partir des résultats, chaque entrée affichant son TPID. Ainsi, une entrée vague ne bloque pas la conversation :

    - kind: AdaptiveCardPrompt
    
      id: adaptiveCard_SelectAccount
    
      card: "=Concatenate('{
    
        \"$schema\":\"https://adaptivecards.io/schemas/adaptive-card.json\",
    
        \"type\":\"AdaptiveCard\",\"version\":\"1.5\",
    
        \"body\":[
    
          {\"type\":\"TextBlock\",\"text\":\"Multiple accounts found\",\"weight\":\"Bolder\"},
    
          {\"type\":\"Input.ChoiceSet\",\"id\":\"selectedAccount\",\"isRequired\":true,
    
           \"choices\":',
    
           JSON(ForAll(Topic.SearchResults.accounts,
    
             {title: Concatenate(account_name, \" (TPID: \", tpid, \")\"), value: tpid})),
    
           '}],
    
        \"actions\":[{\"type\":\"Action.Submit\",\"title\":\"Continue\"}]
    
      }')"
    

    Autorisation des modèles. Grâce à GetBriefingTemplate, le flux vérifie si l’utilisateur a le droit d’utiliser des modèles exécutifs, en fonction de son rôle au sein de l’organisation et de son appartenance au bureau exécutif. Le nom du principal sert à vérifier l’identité, le type de briefing filtre les résultats, et la gouvernance des modèles est sécurisée par défaut :

    - kind: HttpRequestAction
    
      id: httpRequest_GetUserTemplates
    
      url: =Global.ApiBaseUrl & "/api/briefings/templates?user_alias=" &
    
           EncodeUrl(System.User.PrincipalName) & "&briefing_type=Customer"
    
      headers:
    
        Ocp-Apim-Subscription-Key: =Global.ApiKey
    
      response: Topic.UserTemplates
    

    Fiche des détails de la réunion. La fiche suivante utilise une divulgation progressive, affichant en premier les champs obligatoires et plaçant ceux facultatifs derrière un interrupteur. La charge cognitive diminue tout en permettant de collecter les informations nécessaires pour le point d’information :

    - kind: AdaptiveCardPrompt
    
      id: adaptiveCard_MeetingDetails
    
      card: "=Concatenate('{
    
        \"$schema\":\"https://adaptivecards.io/schemas/adaptive-card.json\",
    
        \"type\":\"AdaptiveCard\",\"version\":\"1.5\",
    
        \"body\":[
    
          {\"type\":\"TextBlock\",\"text\":\"Create Executive Briefing Document\",
    
           \"weight\":\"Bolder\",\"size\":\"Large\",\"color\":\"Accent\"},
    
          {\"type\":\"Input.Date\",\"id\":\"meetingDate\",\"isRequired\":true,
    
           \"label\":\"Meeting Date\"},
    
          {\"type\":\"Input.Time\",\"id\":\"meetingTime\",\"value\":\"09:00\"},
    
          {\"type\":\"Input.Text\",\"id\":\"msAttendees\",\"isVisible\":false,
    
           \"label\":\"Microsoft Attendees\"}
    
        ],
    
        \"actions\":[
    
          {\"type\":\"Action.ToggleVisibility\",\"title\":\"Additional Options ▼\",
    
           \"targetElements\":[\"msAttendees\"]},
    
          {\"type\":\"Action.Submit\",\"title\":\"Generate Briefing\"}
    
        ]
    
      }')"
    

    Récolte de données en parallèle et génération de documents. La couche d’expérience appelle simultanément l’API Account Details et Azure AI Foundry, chacune avec un délai de timeout de 60 secondes. Une fois que les deux ont répondu, elle crée un en-tête JSON structuré à partir de toutes les sources et le transmet à l’API de génération de documents, en maintenant la validation et l’assemblage intégrés afin que le document soit créé de manière déterministe.

    Azure AI Foundry : intelligence organisationnelle

    L’intégration avec Foundry élargit la portée de l’agent au-delà des données de marché externes pour y inclure du contexte provenant de l’organisation elle-même, en utilisant un composant « M365 Researcher ». Elle fait appel à des accès Microsoft Graph délégués pour rechercher dans les e-mails d’Outlook et les conversations de Teams, tout en restant dans les limites de la sécurité d’entreprise et conformément aux principes de zéro confiance.

    Accès au Graph délégué sans secrets stockés. Le chercheur s’appuie sur une authentification déléguée qui élimine complètement les secrets du client stockés. Elle utilise des connecteurs gérés par Azure Logic Apps, qui sont des applications Microsoft de premier niveau disposant de scopes délégués préalablement approuvés. Cela évite la charge de gérer les inscriptions des applications, tandis qu’Azure s’occupe du cycle de vie des tokens et de leur renouvellement automatique.

    De plus, les Logic Apps à durée de vie courte protègent l’accès aux données d’un utilisateur grâce à des déclencheurs HTTP sécurisés par SAS. Chaque recherche crée une nouvelle instance de Logic App, récupère les données depuis Microsoft Graph en parallèle et supprime immédiatement cette instance, de sorte qu’une URL SAS ne peut pas être réutilisée et aucun token persistant n’est exposé.

    // Ephemeral Logic App Security Pattern
    const createSearchLogicApp = async (userAlias, searchQuery) => {
    const logicAppName = `Researcher-${userAlias}`;
    // Create Logic App with SAS trigger
    const logicApp = await armClient.logicApps.createOrUpdate({
    resourceGroupName: 'Azure_OpenAI',
    workflowName: logicAppName,
    definition: buildResearchWorkflow(userAlias)
    });
    // Execute search via SAS URL
    const searchResults = await triggerLogicApp(logicApp.triggerUrl, searchQuery);
    // Immediate cleanup for security
    await armClient.logicApps.delete(logicAppName);
    return searchResults;
    };
    

    Récupération parallèle et ajustement de Graph. Les données d’Email et de Teams sont collectées en même temps, avec des limites de concurrence afin que les sessions de nombreux utilisateurs ne provoquent pas de ralentissements. Pour le courrier, le connecteur Office 365 effectue une récupération en masse des 250 derniers messages par utilisateur, et le filtrage a lieu sur le client pour contourner les limites de Graph concernant la recherche dans le corps des messages. Pour Teams, une approche en deux phases permet d’abord de découvrir les conversations grâce à des appels paginés, puis de récupérer les messages des différents fils de discussion avec une limite de concurrence configurable.

    # Logic App Parallel Execute Pattern
    
    HTTP_Trigger_SAS_Secured:
    
      Email_Branch:
    
        - Get_Emails: Office365_Connector.Mail (top=250)
    
    
    
      Teams_Branch_Parallel:
    
        - Get_Teams_Chats_Page1: Teams_Connector.Chats (top=50)
    
        - Get_Teams_Chats_Page2: Teams_Connector.Chats (skip=50, top=50)
    
        - For_Each_Chat:
    
            concurrency: 10
    
            actions:
    
              - Get_Chat_Messages: Teams_Connector.Messages (top=50)
    
              - Append_Chat_Data: Variable_Accumulation
    
    
    
      Response_Assembly:
    
        - Combine: Email_Results + Teams_Results + Metadata
    
        - Return: JSON_Structured_Response
    

    Une recherche typique effectue environ 101 appels à la Graph API : un pour les e-mails, deux pour énumérer les chats des équipes et en moyenne 98 pour récupérer les messages de chat. Cela permet d’obtenir une grande quantité de données tout en restant dans les limites de concurrence imposées par Graph et, conformément à la conception, en maintenant les délais de réponse sous 60 secondes.

    Connexions et jetons par utilisateur. Chaque utilisateur a besoin de deux connexions gérées qui conservent les jetons OAuth délégués au cours des sessions de recherche : office365-{alias} pour l’e-mail avec la portée Mail.ReadWrite et teams-{alias} pour le chat avec Chat.Read. L’infrastructure de connecteurs gérés d’Azure met à jour automatiquement les jetons et assure la conformité. Le provisionnement suit un flux basé sur le consentement, où l’utilisateur se connecte une seule fois via l’expérience OAuth officielle de Microsoft, et les permissions déléguées restent en vigueur jusqu’à ce qu’elles soient révoquées. L’application Function gère le cycle de vie des connexions à l’aide des API d’Azure Resource Manager sous une identité gérée, de sorte que aucune identifiante de client n’est stockée dans la couche d’application.

    // Per-User Connection Provisioning
    
    const provisionUserConnections = async (userAlias) => {
    
        const connections = [
    
            {
    
                name: `office365-${userAlias}`,
    
                api: '/providers/Microsoft.PowerApps/apis/shared_office365',
    
                scopes: ['Mail.ReadWrite']
    
            },
    
            {
    
                name: `teams-${userAlias}`,
    
                api: '/providers/Microsoft.PowerApps/apis/shared_teams',
    
                scopes: ['Chat.Read']
    
            }
    
        ];
    
    
    
        for (const conn of connections) {
    
            await armClient.connections.createOrUpdate({
    
                resourceGroupName: 'Azure_OpenAI',
    
                connectionName: conn.name,
    
                properties: {
    
                    displayName: conn.name,
    
                    api: { id: conn.api },
    
                    parameterValues: {}
    
                }
    
            });
    
        }
    
    
    
        return generateConsentUrls(connections);
    
    };
    

    Synthèse avec un modèle GPT. Les communications brutes sont transformées en informations stratégiques prêtes à être utilisées lors de points d’état par un modèle provenant du service Azure OpenAI (désigné comme GPT-4.1, bien que le code d’exemple mentionne une version de prévisualisation GPT-4). Le processus convertit les e-mails filtrés et le contenu de Teams en analyses structurées couvrant l’analyse des parties prenantes, les modèles de communication et les actions pertinentes pour la réunion. Les instructions fournies répondent aux besoins des dirigeants, tels que la cartographie des relations, l’état du projet et les thèmes stratégiques, et le modèle restitue un JSON structuré qui s’insère dans les modèles Jinja2 existants, assurant une mise en forme cohérente dans tous les documents.

    # Organizational Intelligence Synthesis
    
    async def synthesize_organizational_context(filtered_data, account_context):
    
        synthesis_prompt = """
    
        Analyze the following organizational communications for executive briefing preparation:
    
    
    
        Account Context: {account_name}
    
        Email Communications: {email_count} relevant messages
    
        Teams Discussions: {chat_count} relevant conversations
    
        Generate executive intelligence focusing on:
        Financial performance, earnings trends, and capital allocation signals.
        Corporate strategy, market positioning, and competitive landscape.
        AI strategy, digital transformation initiatives, and cloud ecosystem alignment.
        Recent material developments (≤ 90 days) with actionable insights for executive engagement.
    
        Format as structured JSON with executive_summary, stakeholder_analysis, recent_activities, and recommended_actions.
    
        """
    
    
    
        response = await azure_openai_client.chat.completions.create(
    
            model="gpt-4-1106-preview",
    
            messages=[{
    
                "role": "system",
    
                "content": "You are an executive intelligence analyst."
    
            }, {
    
                "role": "user",
    
                "content": synthesis_prompt.format(**filtered_data, **account_context)
    
            }],
    
            temperature=0.1,
    
            max_tokens=2000
    
        )
    
    
    
        return parse_structured_intelligence(response.choices[0].message.content)
    

    Intégration dans le dialogue. Le chercheur rejoint le flux de travail de Copilot Studio en parallèle avec la récupération des données de compte standard et les informations sur le marché externe, de sorte que le contexte organisationnel supplémentaire ne devrait pas ralentir la génération globale des rapports.

    # Enhanced Copilot Studio Parallel Execution
    
    - kind: ParallelExecution
    
      id: parallelExecution_ComprehensiveIntelligence
    
      branches:
    
        - account_data:
    
            kind: HttpRequestAction
    
            url: =Global.ApiBaseUrl & "/api/accounts/" & Topic.Account_TPID
    
        - organizational_intelligence:
    
            kind: HttpRequestAction
    
            url: =Global.ApiBaseUrl & "/api/search?q=" & EncodeUrl(Topic.Account_Name) & "&alias=" & System.User.PrincipalName
    
            requestTimeoutInMilliseconds: 60000
    
            continueOnError: true
    
        - market_intelligence:
    
            kind: HttpRequestAction
    
            url: =Global.FoundryBaseUrl & "/bingnews/api/AgentFunction"
    
    
    
    - kind: SetVariable
    
      id: setVariable_CombinedIntelligence
    
      variable: Topic.BriefingContext
    
      value: ={
    
        account_details: Topic.AccountData,
    
        organizational_context: Topic.OrganizationalIntelligence,
    
        market_insights: Topic.MarketIntelligence
    
      }
    

    Gouvernance et conformité. Grâce aux permissions déléguées, les utilisateurs ne peuvent rechercher que leurs propres e-mails et messages Teams, ce qui empêche tout accès entre utilisateurs et maintient des limites strictes pour les données. Chaque appel à la Graph API est enregistré avec le contexte de l’utilisateur, les paramètres de recherche et les métadonnées des résultats. La durée de conservation est configurable, et les connexions inutilisées pendant une période définie, généralement 80 jours, sont automatiquement supprimées. Le traitement a lieu au sein du tenant Azure, et les contrôles de résidence des données soutiennent les politiques de gouvernance de l’organisation.

    // Enterprise Governance and Cleanup
    
    const implementGovernanceControls = async () => {
    
        // Automated stale connection cleanup
    
        const staleThreshold = 80; // days
    
        const allConnections = await listManagedConnections();
    
    
    
        const staleConnections = allConnections.filter(connection => {
    
            const lastUsed = parseISO(connection.properties.lastConnection);
    
            const daysSinceUse = differenceInDays(new Date(), lastUsed);
    
            return daysSinceUse > staleThreshold;
    
        });
    
    
    
        // Compliance audit logging
    
        for (const connection of staleConnections) {
    
            await auditLogger.log({
    
                action: 'CONNECTION_CLEANUP',
    
                userAlias: connection.userAlias,
    
                reason: 'AUTOMATED_GOVERNANCE',
    
                retentionPolicy: `${staleThreshold}_DAYS_INACTIVE`,
    
                timestamp: new Date().toISOString()
    
            });
    
    
    
            await deleteUserResources(connection.userAlias);
    
        }
    
    };
    

    Prestations et tolérance aux pannes. Le système met en cache l’état des connexions des utilisateurs et vérifie leur état de santé avant d’exécuter des opérations Graph coûteuses. Si Graph est ralenti ou temporairement indisponible, il fonctionne de manière progressive en utilisant un mécanisme d’attente exponentielle pour les pannes temporaires ainsi que des délais de timeout stricts afin que la génération des rapports ne soit pas retardée. Lorsque la recherche interne échoue ou atteint son délai de timeout, le rapport est néanmoins envoyé avec les données de compte et des informations sur le marché externe, permettant aux dirigeants d’obtenir une expérience cohérente quel que soit l’état de disponibilité de Microsoft 365.

    // Fault-Tolerant Execution Pattern
    
    const executeOrganizationalIntelligence = async (searchQuery, userAlias) => {
    
        const executionTimeout = 60000; // 60 second maximum
    
        const fallbackResponse = { summary: "Organizational context unavailable", status: "fallback" };
    
    
    
        try {
    
            // Health check before expensive operations
    
            const connectionsHealthy = await verifyConnectionHealth(userAlias);
    
            if (!connectionsHealthy) {
    
                return fallbackResponse;
    
            }
    
    
    
            // Execute with timeout boundary
    
            const intelligencePromise = gatherOrganizationalIntelligence(searchQuery, userAlias);
    
            const timeoutPromise = new Promise((_, reject) =>
    
                setTimeout(() => reject(new Error('TIMEOUT')), executionTimeout)
    
            );
    
    
    
            return await Promise.race([intelligencePromise, timeoutPromise]);
    
    
    
        } catch (error) {
    
            // Graceful degradation logging
    
            await logger.warn(`M365 Researcher fallback: ${error.message}`, {
    
                userAlias,
    
                searchQuery,
    
                fallbackMode: true
    
            });
    
    
    
            return fallbackResponse;
    
        }
    
    };
    

    Lorsque le chercheur est en place, l’agent passe d’une analyse de l’intelligence de marché externe à une vision globale intégrant les informations provenant des parties prenantes internes, dans le but de faciliter de meilleures décisions stratégiques sans nuire aux normes de sécurité, de gouvernance ou de performance.

    Génération du document. Cette étape crée le rapport final en combinant tous les éléments recueillis au cours de la conversation en un seul ensemble de données destiné à la couche de service. Il contient le contexte de l’utilisateur, les détails de la réunion et, éventuellement, des analyses fournies par une intelligence artificielle externe, avec un délai de 60 secondes imparti pour l’achèvement intégral du processus de création du document :

    - kind: HttpRequestAction
    
      id: p4tceX
    
      method: Post
    
      url: =Global.ApiBaseUrl & "/api/briefings/" & Topic.templateId & "/accounts/" & Topic.Account_TPID
    
      body:
    
        kind: JsonRequestContent
    
        content: "={
    
          user_alias: Topic.SenderEmail,
    
          MeetingDateTime: Topic.MeetingDateTime,
    
          msAttendees: Topic.msAttendees,
    
          FoundryResponse: Topic.FoundryResponse
    
        }"
    
      requestTimeoutInMilliseconds: 60000
    
      response: Topic.BriefingDocument
    

    Déploiement vers SharePoint. Tous les documents générés sont stockés dans un répertoire central via le connecteur SharePoint de Power Platform, qui utilise une authentification gérée, de sorte que chaque document reste accessible quel que soit le mode de déploiement secondaire utilisé, ce qui répond aux exigences en matière de gouvernance. Le site et le dossier cibles dans l’exemple ci-dessous sont des placeholders.

    - kind: InvokeConnectorAction
    
      id: invokeConnectorAction_YKkMK4
    
      input:
    
        binding:
    
          dataset: https://microsoft.sharepoint.com/teams/MCAPSAIIncubationHub
    
          folderPath: /Shared Documents/General/AI Prototypes & Solutions/AI Executive Office Use Cases/Published Cust Template/
    
          name: =Topic.CustomFilename
    
          file: =Global.File
    

    Les noms de fichiers sont générés de manière lisible, en combinant le nom du compte, l’identifiant de l’exécutif et la date de la réunion. La fonction Substitute() les rend compatibles avec le système de fichiers et assure une cohérence dans la nomenclature au sein des workflows des exécutifs :

    - kind: SetVariable
    
      id: setVariable_CustomFilename
    
      variable: Topic.CustomFilename
    
      value: =Concatenate(
    
        Substitute(Topic.Account_Name, " ", "_"), "_",
    
        Topic.ExecutiveName, "_Brief_",
    
        Text(Topic.meetingDate, "yyyy-MM-dd"), ".docx"
    
      )
    

    Gestion des erreurs et état. Les échecs d’accès à un modèle génèrent des indications contextuelles dans une carte adaptative de type avertissement, et non une erreur générique. Le flux examine les codes d’erreur spécifiques de l’API du service, indique à l’utilisateur quelles autorisations manquent et cache les détails sensibles du système :

    - kind: ConditionGroup
    
      id: conditionGroup_CheckBriefingError
    
      conditions:
    
        - condition: =!IsBlank(Topic.BriefingDocument.error) && Topic.BriefingDocument.error.error_code = "TEMPLATE_ACCESS_DENIED"
    
          actions:
    
            - kind: AdaptiveCardPrompt
    
              card: ={
    
                "$schema":"https://adaptivecards.io/schemas/adaptive-card.json",
    
                "type":"AdaptiveCard","version":"1.5",
    
                "body":[{
    
                  "type":"Container","style":"warning",
    
                  "items":[{
    
                    "type":"TextBlock","text":"Template Access Required",
    
                    "weight":"Bolder","color":"Attention"
    
                  },{
    
                    "type":"TextBlock","wrap":true,
    
                    "text": Topic.BriefingDocument.error.user_message
    
                  }]
    
                }]
    
              }
    

    La validation des données s’effectue côté client, de sorte que les entrées invalides n’atteignent jamais les services backend, ce qui améliore l’expérience utilisateur et évite des appels API inutiles. Par exemple, une date de réunion antérieure est rejetée immédiatement, accompagnée de conseils sur la marche à suivre :

    - kind: ConditionGroup
    
      id: conditionGroup_ValidateInputs
    
      conditions:
    
        - condition: =Topic.meetingDate < Today()
    
          actions:
    
            - kind: SendActivity
    
              activity: The meeting date cannot be in the past. Please start over and enter a future date.
    
            - kind: EndConversation
    

    Une étape de nettoyage sensible à la sécurité supprime toutes les variables contenant des données de compte, des réponses API et des entrées utilisateur lorsque la conversation se termine. Cela répond aux exigences de confidentialité et évite l’accumulation de mémoire dans les services de conversation à long terme qui traitent des informations commerciales confidentielles :

    - kind: SetVariable
    
      id: setVariable_ClearUserInput
    
      variable: Topic.UserInput
    
      value: =Blank()
    
    
    
    - kind: SetVariable
    
      id: setVariable_ClearAccountDetails
    
      variable: Topic.AccountDetails
    
      value: =Blank()
    
    
    
    - kind: SetVariable
    
      id: setVariable_ClearFoundryResponse
    
      variable: Topic.FoundryResponse
    
      value: =Blank()
    
    
    
    - kind: SetVariable
    
      id: setVariable_ClearBriefingDocument
    
      variable: Topic.BriefingDocument
    
      value: =Blank()
    

    Ensemble, la couche d’expérience permet une orchestration conversationnelle grâce à des workflows déterministes, un traitement approfondi des erreurs, des appels tolérants aux pannes vers des services externes, une autorisation basée sur les rôles et une livraison multicanal via le framework AdaptiveDialog.

    Sécurité et détails d’implémentation

    Identité fédérée et authentification

    Le système utilise des identifiants d’identité fédérée Azure afin qu’aucun secret ne soit stocké, assurant ainsi une connexion cohérente entre les différents environnements. Un sélecteur d’identifiants détecte l’environnement dans lequel le code s’exécute et choisit le mécanisme approprié :

    # Environment-aware authentication pattern
    
    def get_credential():
    
        if is_azure_environment():
    
            return ManagedIdentityCredential(client_id=os.getenv('AZURE_CLIENT_ID'))
    
        else:
    
            return AzureCliCredential()
    

    Azur utilise une identité gérée, dont les identifiants sont fournis et mis à jour automatiquement, tandis que le développement local fait appel aux identifiants Azure CLI. Les jetons sont stockés en mémoire de manière sécurisée contre les concurrences, et leur expiration est gérée automatiquement.

    Structure du service et gestion des dépendances

    La couche de service repose sur l’injection de dépendances, ce qui permet au même code de fonctionner avec des simulations lors des tests et avec des instances entièrement configurées en production. Les problématiques transversales telles que la gestion des erreurs et la configuration sont partagées par tous les composants du service, et chacun d’eux (SharePoint, base de données, Power BI) accepte des dépendances optionnelles via son constructeur, tout en restant peu couplé aux API externes et aux ressources Azure. Cela permet des tests approfondis avec des implémentations simulées ainsi qu’un fonctionnement fiable en production grâce à une abstraction adéquate.

    L’orchestration est asynchrone afin d’optimiser la collecte simultanée de données. Lorsqu’une synthèse est générée, plusieurs sources sont consultées en utilisant async/await ainsi qu’un traitement minutieux des erreurs, permettant au système de fonctionner correctement même en cas d’échec d’une source.

    Connexions à la base de données et performances

    La couche de base de données utilise un pool de connexions géré par l’engine de SQLAlchemy, avec un modèle singleton pour une utilisation efficace des ressources. Les requêtes sont regroupées dans des procédures stockées qui contiennent la logique métier directement dans la base de données, ce qui réduit le trafic réseau et simplifie le code de l’application. Les grands ensembles de résultats sont gérés en reconstituant intelligemment le JSON, contournant ainsi les limites de pyodbc avec des réponses volumineuses, afin que le traitement reste fiable quel que soit la taille des résultats et que l’utilisation de la mémoire reste maîtrisée.

    Intégration de services externes

    L’intégration avec SharePoint et Microsoft Graph met en cache les propriétés de manière différée afin de réduire le nombre d’appels API tout en maintenant les données à jour. Les identifiants des sites et des lecteurs sont stockés en cache avec une invalidation appropriée et un choix dynamique de bibliothèques, ce qui assure la fiabilité des téléchargements. L’intégration avec Dynamics 365 analyse les réponses de Search v2.0 et convertit les résultats bruts en structures ayant un sens pour l’entreprise, en intégrant une logique de tentative répétée face aux limitations de fréquence et un traitement minutieux des erreurs.

    Génération de documents

    Le pipeline de génération combine le traitement des modèles Jinja2 à la construction d’un contexte qui transforme les données brutes en analyses prêtes à être présentées aux dirigeants. Le contexte du modèle inclut les données brutes, des métriques calculées, des outils de formatage ainsi que des informations issues d’analyses inter-systèmes. La gestion des modèles valide la syntaxe et gère les erreurs, garantissant ainsi une génération fiable même lorsque les modèles sont modifiés ; de plus, plusieurs formats de sortie sont pris en charge grâce à la configuration des types MIME pour une livraison flexible.

    Erreurs et exceptions

    L’application dispose d’une hiérarchie structurée des exceptions qui fournit des informations détaillées sur les erreurs tout en respectant les contraintes de sécurité. Les classes d’exceptions personnalisées contiennent un contexte utile pour le débogage, sans révéler les détails internes du système :

    Structured exception hierarchy
    
    class AIExecutiveOfficeError(Exception):
    
        def __init__(self, message: str, error_code: str = None, details: dict = None):
    
            self.message = message
    
            self.error_code = error_code or self.__class__.__name__
    
            self.details = details or {}
    
            self.timestamp = datetime.utcnow()
    

    Un gestionnaire d’exceptions global convertit les erreurs techniques en réponses API structurées qui aident l’appelant sans révéler trop de détails.

    Évaluation et validation de la qualité

    Le système est évalué à l’aide d’un cadre de test structuré qui vérifie les performances techniques et la qualité des informations fournies sur plusieurs comptes d’entreprise. Des méthodes quantitatives et qualitatives sont combinées dans un processus d’évaluation en plusieurs étapes qui teste chaque composant architectural par lots.

    D’un point de vue quantitatif, l’accent est mis sur la précision des données. Les métriques présentes dans les modèles de rapport finalisés et remplis sont comparées aux données sources provenant de la couche de base, et le pipeline calcule des scores de précision ainsi qu’une erreur absolue moyenne en pourcentage (MAPE). Celles-ci indiquent dans quelle mesure la couche de service transmet fidèlement les données tout au long du pipeline, des résultats des procédures stockées jusqu’au document final. Les exécutions par lots couvrent de nombreux comptes et alimentent un carnet de performances consolidé, fournissant des preuves statistiques que une conception basée exclusivement sur l’orchestration préserve la fidélité des données sans introduire d’erreurs de calcul ou de dérive sémantique entre les couches de base et celles liées à l’expérience utilisateur.

    La dimension qualitative évalue un seul attribut : la maîtrise de l’entreprise, c’est-à-dire dans quelle mesure un rapport montre une véritable compréhension de l’organisation cible et en parle de manière appropriée. Un juge utilisant un LLM note chaque rapport selon cinq critères standard : la pertinence (le contenu est-il adapté aux décisions des dirigeants ?), la cohérence (flux logique et structure), l’exactitude (justesse factuelle et adéquation avec les données sources), la clarté (langage et présentation adaptés aux dirigeants) ainsi que l’exhaustivité (couverture des domaines stratégiques). Cette méthode permet une évaluation cohérente et scalable, avec une notation objective indépendamment du type de compte ou du secteur d’activité, bien qu’elle hérite, comme toute notation basée sur un LLM, des biais propres au modèle de jugement.

    Prochaines étapes : passage au Microsoft Agent Framework

    L’équipe considère l’architecture à quatre couches comme une bonne base pour migrer vers le Microsoft Agent Framework (MAF), ce qui renforcerait l’orchestration de l’intelligence artificielle tout en préservant les principes déterministes. MAF interviendrait principalement au niveau de la couche des services, en remplaçant la logique d’orchestration FastAPI par des agents MAF qui suivent les mêmes schémas d’orchestration de données pures. La couche d’expérience changerait complètement : Copilot Studio serait remplacé par une intégration native avec Microsoft 365, qui placerait les workflows exécutifs à l’intérieur d’Outlook, Teams et SharePoint, en utilisant la compréhension du langage de MAF pour un meilleur reconnu des intentions et des conversations à plusieurs tours, tout en continuant d’appeler les mêmes procédures stockées pour l’intelligence commerciale.

    L’observabilité intégrée de MAF, la gestion des messages et les outils de gestion du cycle de vie des agents permettraient d’éliminer l’infrastructure personnalisée et d’ajouter une surveillance et un déploiement de niveau entreprise. Le parcours de migration préserve les investissements existants dans les procédures stockées, les modèles Jinja2 et l’intégration avec SharePoint, tout en ajoutant des fonctionnalités pour des scénarios plus complexes, tels que des briefings couvrant plusieurs comptes, une synthèse de l’analyse concurrentielle, ainsi que des modèles générés dynamiquement à partir du contexte de la réunion et des participants. Grâce à l’intégration avec Microsoft 365, la génération de briefings deviendrait une fonction intégrée aux workflows de productivité existants et non une interface de chat distincte, les couches de données et de services restant déterministes.

    Ce que montre la conception

    L’agent transforme la préparation des rapports de coordination manuelle en génération automatique d’informations grâce à quatre niveaux qui séparent la consolidation des données, l’agrégation opérationnelle, l’orchestration métier et l’interaction en langage naturel. Cette approche déterministe, au sein de l’écosystème intégré de Microsoft, permettrait une génération rapide des rapports (la promesse est de « quelques millisecondes », bien que les délais d’expiration propres au processus atteignent 60 secondes, ce qui fait probablement référence aux étapes liées à la base de données et aux services), ainsi que des propriétés en matière de sécurité, de gouvernance et d’audit que les systèmes purement basés sur des prompts ont du mal à garantir.

    La leçon principale est que les applications d’IA d’entreprise réussies placent la résolution du problème métier au-dessus de la complexité technique, et qu’elles créent de la valeur grâce à une intégration des données fiable et à l’automatisation des processus, plutôt qu’à travers des méthodes d’apprentissage automatique avancées. La séparation hiérarchisée des responsabilités, l’injection de dépendances et les limites d’exécution déterministes font de cette approche un modèle reutilisable pour l’automatisation du service client, les workflows d’analyse financière et l’intelligence opérationnelle dans d’autres organisations.