Strona główna / Artykuły

Czterowarstwowa architektura dla agenta przekazywania informacji kierownictwu w Azure

Jak agent służący do przygotowywania raportów w sposób deterministyczny dzieli pracę pomiędzy Fabric, procedury przechowywane w SQL, FastAPI w Azure Functions oraz Copilot Studio w celu tworzenia dokumentów dla kierownictwa.

Wydawca: REACTAPP.TOP22 min czytania5073 słów

Źródła: **Chief of Staff Executive Briefing Agent: Enterprise-scale layered architecture for automated intelligence generation**

Każdy, kto przygotowuje kadry kierownicze do spotkań z klientami, wie, jak rozproszone są surowe dane. Informacje z CRM znajdują się w jednym systemie, dane o przychodach w innym, a informacje o wykorzystaniu produktów w trzecim; historię strategiczną trzeba ręcznie składać z tych wszystkich źródeł. Narzędzie do przygotowywania informacji dla „szefa sztabu” jest właśnie przeznaczone do rozwiązania tego problemu: osoba prosi w prostym języku o streszczenie, a platforma zwraca dokument gotowy do użycia przez kadrę kierowniczą, stworzony na podstawie fragmentarycznych danych operacyjnych, finansowych oraz dotyczących wdrażania sztucznej inteligencji.

To, co sprawia, że ten projekt warto badać, to to, jak mało polega na przypadku. Zamiast prosić model o napisanie informacji wstępnych na podstawie instrukcji, zastępuje to ręczne agregowanie orkiestracją regulowaną umowami. Dane są konsolidowane w ramach określonych zasad, agregowane za pomocą deterministycznego SQL, przekazywane przez usługi bez stanu i dostarczane poprzez kontrolowany przepływ konwersacyjny. Architektura wyznacza ścisłe granice wykonania pomiędzy czterema warstwami: podstawową, danych, usług i doświadczenia, dzięki czemu wyniki można odtworzyć, sprawdzić i dostarczyć z prędkością typową dla przedsiębiorstw w ramach ścisłych ograniczeń bezpieczeństwa.

Ten projekt powstał w ramach wewnętrznego programu inkubacji sztucznej inteligencji w ekosystemie Microsoft, a podane poniżej informacje na temat wydajności i efektywności pochodzą od samych twórców, a nie zostały sprawdzone niezależnie. Jest szczególnie przydatny dla architektów przedsiębiorstw, inżynierów sztucznej inteligencji oraz kierowników platform, którzy potrzebują deterministycznych systemów AI umożliwiających konsolidację danych, agregację w SQL, orkiestrację bez stanu oraz prace w formie konwersacji.

Czterowarstwowy projekt

Agent opiera się na modelu orkiestracji czterowarstwowej, przy którym każda warstwa pełni określoną funkcję i obejmuje kilka usług Azure oraz granic bezpieczeństwa:

  • Fundament: Microsoft Fabric Lakehouse, który konsoliduje dane pod kontrolą zasad zarządzania.
  • Dane: Fabric SQL (Warehouse lub endpoint SQL) zawierający procedury przechowywane do wykonywania zapytań operacyjnych i agregacji.
  • Służba: silnik orkiestracji FastAPI hostowany w Azure Functions.
  • Doświadczenie użytkownika: konwersacyjny przepływ pracy Copilot Studio, który interpretuje intencje użytkownika.
  • Razem tworzą jednokierunkowy pipeline inteligencji. Dane są kuratowane i zarządzane na poziomie bazowym, agregowane w sposób deterministyczny w SQL, przetwarzane przez logikę domenową na poziomie usługi, a ostatecznie prezentowane poprzez interakcję w języku naturalnym. Każda warstwa ma wyraźną umowę z sąsiednimi warstwami, dzięki czemu element AI wspomaga podejmowanie decyzji, a nie je utrudnia.

    Warstwa podstawowa: zintegrowane zarządzanie danymi

    Microsoft Fabric pełni rolę zintegrowanej platformy analitycznej oraz jedynego źródła prawdy dotyczącego szczegółów kont, danych finansowych, wskaźników wykorzystania oraz trendów historycznych. Zasada działania jest prosta: dane przechowuje się raz i wykorzystuje je wielokrotnie, dzięki czemu nic nie jest duplikowane pomiędzy systemami.

    Kanał przesyłu aktualizuje się codziennie, pobierając i łącząc dane przedsiębiorstwa z Dynamics 365, systemów finansowych oraz zewnętrznych API. Taki harmonogram zapewnia przewidywalne okna przetwarzania i gwarantuje, że każdy system źródłowy zakończy swoją codzienną pracę przed rozpoczęciem konsolidacji. Lakehouse przechowuje dane w ich oryginalnym formacie wraz ze standardami śledzenia pochodzenia i zarządzania, a następnie udoskonalone zbiory danych trafiają do Fabric SQL – szybkiego warstwy zapytań operacyjnych, której struktury są dostosowane do potrzeb tworzenia raportów dla kierownictwa.

    Warstwa danych: silnik operacyjny stworzony do dostępu przez API

    To warstwa przekształca starannie przygotowane dane podstawowe w deterministyczne, o niskiej opóźnieniu kontrakty zapytań służące do generowania raportów. Określa ona ścisłe granice agregacji oraz gwarancje wydajności, których oczekują kierownicy od narzędzia o szybkiej reakcji.

    Architektura. Fabric SQL Warehouse to interfejs relacyjny, który umieszcza każdą operację z danymi związanymi z raportowaniem w procedurach przechowywanych. Warstwa usługowa nigdy nie wysyła zapytań bezpośrednio do tabel. Złożone łączenia między tabelami, znaczenie każdej agregacji oraz logika biznesowa są realizowane wewnątrz bazy danych, a nie w kodzie aplikacji, co zapewnia przewidywalne czasy odpowiedzi przy dużych objętościach danych. SQL jest dostosowany do zapytań operacyjnych, a nie do analizy, a projekt ma na celu realizację deterministycznych łączeń dających spójne wyniki, planów zapytań nadających się do ponownego użycia dla stabilnej wydajności, wykonywania z parametrami dla bezpieczeństwa oraz agregacji zamkniętych, które są łatwiejsze w utrzymaniu.

    Filozofia schematu: zapytanie na pierwszym miejscu. Schemat opiera się na wzorcach zapytań, a nie na standardowej normalizacji z podręczników. Entytą główną jest struktura klasterowania Account, a tabele pomocnicze są ułożone tak, aby połączenia odbywały się efektywnie na podstawie identyfikatora biznesowego, czyli 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](
    

    Tabela Accounts stanowi centralną jednostkę, a jej kluczem do grupowania jest TPID. Dzięki temu uzyskuje się mniej więcej liniową szybkość wyszukiwania najczęściej używanych zapytań dotyczących konkretnych kont, a tabela obsługuje zarówno bezpośrednie zapytania oparte na TPID, jak i wyszukiwanie według nazwy.

    Tabele pomocnicze. Kilka tabel pomocniczych pokazuje, w jaki sposób obsługiwane są wskaźniki finansowe, informacje o wykorzystaniu AI oraz wzbogacanie danych międzysystemowe.

    -- 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]);
    

    Tabela przychodów przechowuje dane finansowe z kilku lat oraz wykorzystuje indeksy złożone, które nadają się do obliczania serii czasowych i trendów.

    -- 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]);
    

    Tabela wykorzystania AI odnotowuje przyjęcie produktów według kategorii, co umożliwia analizę wzorców zaangażowania oraz stopnia wdrażania technologii, które mają znaczenie przy planowaniu kont.

    Strategia procedur przechowywanych: logika biznesowa znajduje się w bazie danych. Trzy podstawowe procedury odpowiadają za generowanie wszystkich informacji. SearchAccounts znajduje konta i sortuje je według przychodów, GetAccountDetails agreguje dane z różnych źródeł, tworząc kompletny obraz dla kierownictwa, a GetBriefingTemplate zarządza uprawnieniami do szablonów w zależności od roli, aby tylko odpowiednie osoby miały dostęp. Warstwa usługowa wywołuje jedynie te procedury i nic więcej, a tworzenie dynamicznego SQL nie jest dozwolone.

    --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;
    

    Wydajność i bezpieczeństwo. Transakcje wykorzystują SET NOCOUNT ON dla lepszej wydajności oraz SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED przy operacjach odczytu, aby nie blokować innych transakcji. Obsługa JSON opiera się na wbudowanych funkcjach SQL Servera, takich jak JSON_QUERY i FOR JSON PATH, które efektywnie przetwarzają dane i serializują je bezpośrednio z bazy danych. Dozwolona jest wyłącznie eksploatacja z użyciem parametrów, a tożsamość usługi aplikacji ma uprawnienia do wykonywania procedur przechowywanych, bez możliwości bezpośredniego dostępu do tabel.

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

    Szczegóły warstwy usługowej: orkiestracja bez stanu i granice wykonywania

    Warstwa usługowa zajmuje się wyłącznie orkiestracją danych. Pobiera dane, renderuje szablony i nie stosuje żadnych własnych reguł biznesowych. Każda usługa ma jedną funkcję i jest luźno powiązana z innymi, co zapewnia czyste oddzielenie warstwy danych od warstwy prezentacji.

    Powierzchnia API i walidacja. Aplikacja FastAPI udostępnia wyraźne punkty końcowe typu REST z silnie skategoryzowanymi kontraktami zdefiniowanymi jako modele Pydantic:

    • POST /api/briefing/generate to główny punkt końcowy służący do generowania raportów, z pełną walidacją danych wejściowych.
    • GET /api/health informuje o stanie systemu.
    • GET /api/templates wyświetla listę dostępnych szablonów dla użytkownika, który wysyła żądanie.
    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
    

    Model zapewnia ścisłą weryfikację i bezpieczeństwo typów, automatycznie generuje dokumentację OpenAPI oraz koryguje strukturę każdej żądania. Żaden nieuporządkowany wprowadzony danych nie może przejść przez tę barierę.

    Model wykonywania. Usługa uruchamia określoną pięciostopniową sekwencję:

    1. Weryfikacja żądania. Modele Pydantic sprawdzają, czy typy i dane wejściowe są dopuszczalne.
    2. Rozwiązywanie problemu konta. Format danych wejściowych decyduje o tym, czy szukać TPID bezpośrednio, czy wywołać SearchAccounts w celu wyszukiwania po nazwie.
    3. Pobieranie danych równoczesne. Funkcje GetAccountDetails i GetBriefingTemplate są wykonywane jednocześnie za pomocą asyncio.gather().
    4. Kompletowanie kontekstu. Wyniki procedur są bezpośrednio przekształcane w obiekty gotowe do użycia w szablonie, bez żadnej dodatkowej obróbki pośredniej.
  • Wydruk szablonu. Jinja2 tworzy dokument końcowy z deterministycznym formatowaniem.
  • Asynchroniczna eksploatacja oraz gromadzenie połączeń za pomocą abstrakcji DatabaseService oznaczają, że identyczne dane wejściowe dają identyczne wyniki wyjściowe.

    Zasady kierujące. Warstwę usługową rządzi pięć zasad. Nie zawiera ona logiki biznesowej, ponieważ przetwarzanie informacji odbywa się wyłącznie w procedurach przechowywanych, co chroni warstwę usługową przed zasadami utrudniającymi testowanie i konserwację. Pełni ona jedynie funkcję orkiestracji danych, koordynując ich pobieranie i wyświetlanie bez żadnych transformacji czy obliczeń. Mapowanie kontekstu jest bezpośrednie, więc wyniki procedur trafiają do struktur szablonów bez żadnych odchyleń, co zmniejsza ryzyko różnic w znaczeniu pomiędzy danymi a prezentacją. Przetwarzanie jest deterministyczne – daje to ten sam wynik dla tego samego wejścia, niezależnie od czasu wykonywania lub stanu infrastruktury. Ponadto wszystkie aspekty są czysto oddzielone: logika danych znajduje się w procedurach SQL, orkiestracja w usługach Python FastAPI, a prezentacja w szablonach Jinja2.

    Warstwa doświadczeń: Copilot Studio jako silnik rozmowy

    Na górze framework AdaptiveDialog w Microsoft Copilot Studio przekształca prośbę w języku naturalnym w deterministyczny proces pracy. Zajmuje się rozwiązywaniem problemów z kontem, autoryzacją szablonów, integracją danych z zewnętrznych źródeł, generowaniem dokumentów oraz ich dostarczaniem przez różne kanały. Proces przebiega sekwencyjnie – od pierwszej prośby, przez rozwiązanie problemów z kontem i autoryzację szablonów, aż po zbieranie danych z zewnętrznych źródeł i ich przesyłkę do SharePoint, przy czym na każdej drodze rozmowy znajdują się punkty decyzyjne oraz kroki weryfikacji.

    Główna rozmowa. Ta część automatycznie zbiera identyfikator konta, określa frazy wyzwalające rozpoczęcie rozmowy i ustala główny punkt wejścia do konwersacji:

    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
    

    Rozwiązywanie problemów z kontem. Sprawdzenie IsNumeric() klasyfikuje wprowadzony tekst jako bezpośredni TPID lub nazwę konta wymagającą wyszukiwania. Unikanie wyszukiwania, gdy to możliwe, zmniejsza liczbę wywołań API i przyspiesza odpowiedź:

    - 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
    

    Wywołanie do wyszukiwania konta. W przypadku imion proces wywołuje punkt końcowy wyszukiwania warstwy usługowej przez REST. Koduje specjalne znaki w formacie URL, autoryzuje się za pomocą klucza subskrypcji i określa schemat odpowiedzi, dzięki czemu dane są automatycznie weryfikowane:

    - 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
    

    Karta rozróżniająca. Jeśli wyszukiwanie znajdzie kilka kont, karta adaptacyjna pokazuje dynamiczną listę opcji utworzoną za pomocą ForAll() na podstawie wyników, przy czym każda pozycja wyświetla swój TPID. Dzięki temu niejasny wpis nie blokuje dalszej rozmowy:

    - 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\"}]
    
      }')"
    

    Uzyskanie uprawnień do szablonu. Za pomocą funkcji GetBriefingTemplate system sprawdza, czy użytkownik ma prawo korzystać ze szablonów kierowniczych, w oparciu o swoją rolę organizacyjną oraz przynależność do zarządu. Nazwa głównego użytkownika służy do weryfikacji tożsamości, typ informacji filtruje wyniki, a zarządzanie szablonami jest domyślnie zabezpieczone:

    - 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
    

    Karta z informacjami o spotkaniu. Następna karta wykorzystuje zasadę stopniowego ujawniania informacji – pola obowiązkowe są pokazywane od razu, natomiast opcjonalne znajdują się za przyciskiem do ich wyboru. To zmniejsza obciążenie poznawcze, przy jednoczesnym zebraniu wszystkich niezbędnych informacji do krótkiego przeglądu:

    - 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ównoczesne zbieranie danych i generowanie dokumentów. Warstwa doświadczeń wywołuje jednocześnie API Account Details oraz Azure AI Foundry, przy czym każde z tych wywołań ma 60 sekund czasu oczekiwania. Gdy oba zwrócą odpowiedź, tworzy się jeden ustrukturyzowany payload w formacie JSON na podstawie wszystkich źródeł, który jest następnie przekazywany do API do generowania dokumentów. Proces weryfikacji i kompletowania danych jest zintegrowany, dzięki czemu dokument powstaje w sposób deterministyczny.

    Azure AI Foundry: inteligencja organizacyjna

    Integracja z Foundry rozszerza zakres działania agenta poza dane rynkowe o kontekst pochodzący z wnętrza organizacji, wykorzystując komponent „M365 Researcher”. Dzięki uprawnieniom Microsoft Graph agent może przeszukiwać e-maile w Outlooku oraz rozmowy w Teams, przy czym cała operacja odbywa się w ramach zasad bezpieczeństwa przedsiębiorstwa i zasady zerowego zaufania.

    Dostęp do Microsoft Graph poprzez delegację bez przechowywanych sekretów. Badacz polega na autoryzacji delegowanej, która całkowicie eliminuje przechowywanie sekretów klienta. Wykorzystuje ona łączniki zarządzane przez Azure Logic Apps – aplikacje firmy Microsoft z uprzednio zatwierdzonymi zakresami uprawnień delegowanych. Dzięki temu unika się konieczności zarządzania rejestracjami aplikacji, ponieważ Azure zajmuje się cyklem życia tokenów oraz ich automatycznym odświeżaniem.

    Ponadto krótkotrwałe instancje Logic Apps zabezpieczają dostęp do danych użytkownika za pomocą wyzwalaczy HTTP chronionych kluczami SAS. Każde wyszukiwanie uruchamia nową instancję Logic App, pobiera dane z Microsoft Graph równolegle i natychmiast ją usuwa, dzięki czemu adres URL z kluczem SAS nie może być ponownie użyty, a żaden trwały token nie pozostaje dostępny.

    // 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ównoległe pobieranie danych i optymalizacja Graph. Dane z e-maili oraz Teams są zbierane jednocześnie, przy czym stosuje się ograniczenia dotyczące równoczesności, aby sesje wielu użytkowników nie powodowały spowolnień. W przypadku e-maili connector Office 365 pobiera maksymalnie 250 najnowszych wiadomości na użytkownika, a filtrowanie odbywa się na stronie klienta, aby obejść ograniczenia Graph dotyczące wyszukiwania treści wiadomości. W przypadku Teams stosuje się podejście dwufazowe: najpierw identyfikowane są rozmowy za pomocą żądań paginowanych, a następnie pobierane są wiadomości z poszczególnych wątków rozmowy przy zachowaniu konfigurowalnego limitu równoczesności.

    # 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
    

    Zwykłe wyszukiwanie polega na około 101 wywołaniach Graph API: jednym do przeglądania maili, dwóch do wyliczania rozmów w Teams oraz średnio 98 do pobierania wiadomości z rozmów. Dzięki temu uzyskuje się dużą ilość danych, przy jednoczesnym przestrzeganiu ograniczeń równoczesności Graph API oraz, zgodnie z projektem, utrzymywaniu czasu odpowiedzi poniżej 60 sekund.

    Połączenia i tokeny na użytkownika. Każdy użytkownik potrzebuje dwóch zarządzanych połączeń, które przechowują delegowane tokeny OAuth w trakcie sesji wyszukiwania: office365-{alias} do obsługi poczty z zakresem Mail.ReadWrite oraz teams-{alias} do czatu z zakresem Chat.Read. Infrastruktura zarządzanych połączeń Azure automatycznie odnawia tokeny i zapewnia zgodność z wymogami. Proces konfiguracji odbywa się według schematu opartego na zgodzie użytkownika – ten loguje się raz poprzez interfejs OAuth firmy Microsoft, a delegowane uprawnienia pozostają w mocy do chwili ich cofnięcia. Aplikacja Function App zarządza cyklem życia połączeń za pomocą API Azure Resource Manager pod kierownictwem tożsamości zarządzanej, dzięki czemu żadne dane uwierzytelniające klienta nie są przechowywane w warstwie aplikacji.

    // 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);
    
    };
    

    Synteza za pomocą modelu GPT. Surowe komunikaty są przekształcane w gotowe do użycia informacje strategiczne za pomocą modelu z usługi Azure OpenAI Service (opisanego jako GPT-4.1, chociaż przykładowy kod wskazuje na wersję GPT-4 preview). Ten proces przekształca filtrowane e-maile oraz treści z Teams w ustrukturyzowane wnioski obejmujące analizę interesariuszy, wzorce komunikacji oraz działania istotne dla spotkania. Pytania zadawane modelowi odpowiadają na potrzeby kierownictwa, takie jak mapowanie relacji, status projektu i tematy strategiczne, a model zwraca ustrukturyzowany plik JSON, który można wstawić do istniejących szablonów Jinja2, zapewniając spójny format we wszystkich dokumentach.

    # 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)
    

    Integracja z dialogiem. Badacz łączy się z procesem pracy Copilot Studio równocześnie z pobieraniem standardowych danych konta oraz informacji z zewnętrznych źródeł analityki rynkowej, dzięki czemu dodatkowy kontekst organizacyjny nie powinien spowolnić ogólnej generacji raportów.

    # 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
    
      }
    

    Zarządzanie i zgodność. Użytkownicy mogą przeszukiwać wyłącznie swoją własną pocztę oraz wiadomości w Teams za pomocą uprawnień delegowanych, co zapobiega dostępowi między użytkownikami i utrzymuje ścisłe granice danych. Każda wywołanie Graph API jest rejestrowane wraz z kontekstem użytkownika, parametrami wyszukiwania oraz metadanymi wyników. Okres przechowywania danych jest konfigurowalny, a połączenia, które nie są używane przez określony czas – zazwyczaj 80 dni – są automatycznie usuwane. Przetwarzanie odbywa się wewnątrz tenantu Azure, a mechanizmy kontroli lokalizacji danych wspierają polityki zarządzania organizacją.

    // 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);
    
        }
    
    };
    

    Wydajność i tolerancja na awarie. System przechowuje w pamięci tymczasowej stan połączenia użytkownika i sprawdza jego sprawność przed wykonywaniem kosztownych operacji Graph. Jeśli dostęp do Graph jest ograniczony lub chwilowo niedostępny, system funkcjonuje dalej w sposób uporządkowany, stosując eksponencjalne opóźnienia przy tymczasowych awariach oraz ścisłe limity czasowe, aby generowanie raportów nie zostało zatrzymane. Gdy wyszukiwanie w organizacji zawiedzie lub wygaśnie czas oczekiwania, raport nadal jest wysyłany wraz z danymi konta i informacjami rynkowymi z zewnętrznych źródeł, dzięki czemu kierownicy otrzymują spójne doświadczenie niezależnie od dostępności 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;
    
        }
    
    };
    

    Gdy badacz jest już przygotowany, agent przechodzi od informacji rynkowych pochodzących z zewnątrz do kompleksowego obrazu, który obejmuje również wgląd w sytuację wewnętrznych interesariuszy, co ma na celu wspieranie lepszych decyzji strategicznych bez osłabiania standardów bezpieczeństwa, zarządzania czy wydajności.

    Zawołanie do generowania dokumentu. W tym kroku tworzone jest ostateczne zestawienie informacji poprzez połączenie wszystkiego, co zostało zebrane podczas rozmowy, w jedną paczkę danych przeznaczoną dla warstwy usługowej. Zawiera ona kontekst użytkownika, szczegóły spotkania oraz opcjonalne informacje uzyskane za pomocą sztucznej inteligencji z zewnątrz, przy czym całkowity proces tworzenia dokumentu ma czas wykonywania 60 sekund:

    - 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
    

    Dostawa do SharePoint. Wszystkie utworzone dokumenty trafiają do centralnego repozytorium za pośrednictwem łącznika SharePoint w Power Platform z zarządzaną autoryzacją, dzięki czemu każdy dokument jest przechowywany i dostępny niezależnie od sposobu drugorzędnej dostawy, co spełnia wymagania związane z zarządzaniem. Strona docelowa oraz folder w poniższym przykładzie są tylko placeholderami.

    - 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
    

    Nazwy plików są generowane w taki sposób, aby były czytelne, łącząc nazwę konta, identyfikator kierownika oraz datę spotkania. Funkcja Substitute() zapewnia bezpieczeństwo tych nazw w systemie plików oraz utrzymuje spójność ich formatowania we wszystkich procesach pracy kierowników:

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

    Zarządzanie błędami i stanem. Niepowodzenia przy dostępie do szablonu powodują pokazanie wskazówek kontekstowych w karcie adaptacyjnej o stylu ostrzeżenia, a nie ogólnego błędu. System sprawdza konkretne kody błędów z API usługi, informuje użytkownika o braku uprawnień i ukrywa wrażliwe dane systemowe:

    - 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
    
                  }]
    
                }]
    
              }
    

    Walidacja biznesowa odbywa się na stronie klienta, dzięki czemu nieprawidłowe dane nigdy nie trafiają do usług backendowych, co poprawia doświadczenie użytkownika i unika niepotrzebnych wywołań API. Na przykład data spotkania z przeszłości jest natychmiast odrzucana wraz z wskazówkami co robić:

    - 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
    

    Krok czyszczenia o charakterze bezpieczeństwa usuwa wszystkie zmienne przechowujące dane konta, odpowiedzi API oraz dane wprowadzone przez użytkownika po zakończeniu rozmowy. Dzięki temu spełniane są wymagania dotyczące prywatności, a także unikane jest gromadzenie się danych w pamięci w usługach rozmów długotrwałych, które przetwarzają poufne informacje biznesowe:

    - 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()
    

    W połączeniu warstwa doświadczeń użytkownika zapewnia orkiestrację rozmów przy użyciu deterministycznych schematów pracy, rozbudowaną obsługę błędów, odporne na awarie połączenia z usługami zewnętrznymi, autoryzację opartą na rolach oraz dostarczanie treści przez kilka kanałów dzięki frameworkowi AdaptiveDialog.

    Bезpieczeństwo i szczegóły implementacji

    Tożsamość i uwierzytelnianie federowane

    System wykorzystuje kredencjały tożsamości federowanej Azure, dzięki czemu żadne sekrety nie są przechowywane, a logowanie jest spójne we wszystkich środowiskach. Wybieracz kredencjałów określa, w jakim środowisku uruchamiany jest kod, i wybiera odpowiedni mechanizm:

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

    W Azure wykorzystuje zarządzaną tożsamość, przy której dane uwierzytelniające są automatycznie dostarczane i rotowane, natomiast przy lokalnym rozwoju stosuje się dane uwierzytelniające z Azure CLI. Tokeny są przechowywane w sposób bezpieczny pod względem wątków, a ich wygaśnięcie jest obsługiwane automatycznie.

    Struktura usługi i zarządzanie zależnościami

    Szczegóły funkcjonowania usługi opierają się na iniekcji zależności, co umożliwia uruchamianie tego samego kodu zarówno z użyciem symulacji w testach, jak i w pełni skonfigurowanych instancji w środowisku produkcyjnym. Kwestie wspólne dla wszystkich komponentów usługi, takie jak obsługa błędów i zarządzanie konfiguracją, są dzielone pomiędzy nimi, a każdy z komponentów (SharePoint, baza danych, Power BI) przyjmuje opcjonalne zależności poprzez swój konstruktor, pozostając przy tym luźno powiązanym z zewnętrznymi API i zasobami Azure. Dzięki temu możliwe jest dokładne testowanie za pomocą symulowanych implementacji, a także niezawodne działanie w środowisku produkcyjnym dzięki odpowiedniej abstrakcji.

    Orkiestracja jest asynchroniczna, aby maksymalnie wykorzystać równoległe zbieranie danych. Gdy generowana jest informacja wstępna, kilka źródeł jest wyświetlanych jednocześnie za pomocą mechanizmu async/await oraz starannego obsługi błędów, dzięki czemu system funkcjonuje prawidłowo nawet w przypadku awarii jednego z elementów.

    Połączenia z bazą danych i wydajność

    Szczegółowa warstwa bazodanowa wykorzystuje zarządzanie połączeniami poprzez mechanizm engine management w SQLAlchemy, z użyciem singletona w celu efektywnego wykorzystania zasobów. Zapytania są gromadzone w procedurach przechowywanych, które zawierają logikę biznesową wewnątrz bazy danych, co zmniejsza ruch sieciowy i upraszcza kod aplikacji. Duże zbiory wyników są przetwarzane poprzez inteligentną rekonstrukcję formatu JSON, obejście ograniczeń pyodbc przy dużych odpowiedziach, dzięki czemu przetwarzanie pozostaje niezawodne niezależnie od rozmiaru wyników, a zużycie pamięci pozostaje umiarkowane.

    Integracja z zewnętrznymi usługami

    Integracja z SharePoint i Microsoft Graph przechowuje dane w pamięci podręcznej w sposób opóźniony, aby ograniczyć liczbę wywołań API przy jednoczesnym utrzymywaniu aktualności informacji. ID stron i folderów są przechowywane w pamięci podręcznej z odpowiednią logiką unieważniania danych oraz dynamicznym wybieraniem bibliotek, co zapewnia niezawodność przesyłania plików. Integracja z Dynamics 365 analizuje odpowiedzi z Search v2.0 i przekształca surowe wyniki w struktury mające znaczenie biznesowe, wykorzystując logikę ponawiania prób w przypadku ograniczeń szybkości oraz staranną obsługę błędów.

    Generowanie dokumentów

    Proces generowania łączy przetwarzanie szablonów Jinja2 z budową kontekstu, który przekształca surowe dane w gotowe do użycia wnioski. Kontekst szablonu zawiera surowe dane, obliczone metryki, narzędzia do formatowania oraz informacje uzyskane z analizy międzysystemowej. Zarządzanie szablonami weryfikuje składnię i radzi sobie z błędami, dzięki czemu proces generowania pozostaje niezawodny nawet po edycji szablonów, a kilka formatów wyjściowych jest obsługiwanych dzięki konfiguracji typów MIME dla elastycznej dostawy.

    Błędy i wyjątki

    Aplikacja posiada ustrukturyzowaną hierarchię wyjątków, która dostarcza szczegółowych informacji o błędach przy jednoczesnym przestrzeganiu zasad bezpieczeństwa. Dostosowane klasy wyjątków zawierają kontekst, który ułatwia debugowanie bez ujawniania wewnętrznych mechanizmów systemu:

    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()
    

    Globalny obsługa wyjątków przekształca błędy techniczne w ustrukturyzowane odpowiedzi API, które pomagają użytkownikowi, nie ujawniając przy tym zbyt wielu szczegółów.

    Ocena i weryfikacja jakości

    System jest oceniany za pomocą ustrukturyzowanego frameworku testowego, który sprawdza wydajność techniczną oraz jakość informacji w różnych kontach firmowych. Metody ilościowe i jakościowe są łączone w procesie oceny opartym na kilku notatnikach, który testuje każdy komponent architektoniczny partiami.

    Z punktu widzenia aspektu ilościowego kluczowa jest dokładność danych. Wskaźniki w gotowych, wypełnionych szablonach raportów są porównywane z danymi źródłowymi z warstwy podstawowej, a proces oblicza wyniki dokładności oraz średnią bezwzględną błąd procentowy (MAPE). Pokazują one, w jakim stopniu warstwa usługowa przekazuje dane przez cały proces, od wyników procedur przechowywanych aż do ostatecznego dokumentu. Wykonania partiami obejmują wiele kont i dostarczają zintegrowanego notatnika wydajności, stanowiącgo statystyczne potwierdzenie, że projekt oparty wyłącznie na orkiestracji zachowuje wierność danych, nie wprowadzając błędów obliczeniowych ani odchyleń semantycznych pomiędzy warstwą podstawową a warstwą użytkownika.

    Cecha jakościowa mierzy pojedynczą cechę – biegłość w komunikacji z firmą, czyli to, na jak dobrą stopę materiał informacyjny pokazuje rzeczywiste zrozumienie organizacji docelowej i w odpowiedni sposób o niej mówi. Sędzia wykorzystujący LLM ocenia każdy materiał według pięciu standardowych kryteriów: trafność (czy treść jest odpowiednia do podejmowania decyzji kierowniczych), spójność (logiczny tok i struktura), dokładność (poprawność faktograficzna i zgodność z danymi źródłowymi), klarowność (język i forma prezentacji dostosowane do kierowników) oraz kompletność (objęcie strategicznych obszarów). Ten podejście zapewnia spójną, skalowalną ocenę z obiektywnymi wynikami w różnych typach kont i branżach, chociaż, podobnie jak we wszystkich systemach oceniania opartych na LLM, przenosi ono uprzedzenia samego modelu sędziego.

    Kolejne kroki: przejście na Microsoft Agent Framework

    Zespół postrzega architekturę czterowarstwową jako solidną podstawę do migracji na Microsoft Agent Framework (MAF), co wzmocni orkiestrację sztucznej inteligencji przy jednoczesnym zachowaniu zasad deterministycznych. MAF w większości wejdzie w grę na warstwie usług, zastępując logikę orkiestracji FastAPI agentami MAF, które stosują te same wzorce czystej orkiestracji danych. Warstwa doświadczeń użytkownika ulegnie całkowitej zmianie: Copilot Studio zostanie zastąpione natywną integracją z Microsoft 365, która umieszcza procesy biznesowe w Outlooku, Teams i SharePoint, wykorzystując rozumienie języka MAF do lepszej identyfikacji intencji i rozmów wieloetapowych, przy jednoczesnym korzystaniu z tych samych procedur przechowywanych do analizy danych biznesowych.

    wbudowana możliwość obserwacji, zarządzanie komunikacjami oraz narzędzia do zarządzania cyklem życia agentów w MAF pozwoliłyby na eliminację konieczności stosowania specjalistycznej infrastruktury oraz umożliwiłyby monitorowanie i wdrażanie na poziomie przedsiębiorstwa. Ścieżka migracji zachowuje istniejące inwestycje w procedury przechowywane, szablony Jinja2 oraz integrację z SharePoint, jednocześnie dodając funkcjonalności do trudniejszych scenariuszy, takich jak briefingi obejmujące kilka kont, syntezę analiz konkurencyjnych oraz szablony generowane dynamicznie na podstawie kontekstu spotkania i uczestników. Dzięki integracji z Microsoft 365 generowanie briefingów stałoby się funkcją wbudowaną w istniejące procesy produktywności, a nie oddzielną interfejsem czatowym, przy czym warstwy danych i usług pozostałyby deterministyczne.

    Co pokazuje projekt

    Agent przekształca przygotowanie informacji szkoleniowych z ręcznej koordynacji w automatyczną generację danych wywiadowczych za pomocą czterech warstw, które odpowiadają za konsolidację danych, agregację operacyjną, orkiestrację biznesową oraz interakcję w języku naturalnym. Mówi się, że ten deterministyczny podejście w ramach zintegrowanego ekosystemu Microsoft umożliwia szybką generację informacji szkoleniowych (twierdzi się, że w czasie „poniżej sekundy”, chociaż własne limity czasowe procesu wynoszą 60 sekund, więc najprawdopodobniej chodzi o kroki związane z bazą danych i usługami), a także zapewnia bezpieczeństwo, zarządzanie oraz możliwości audytu, których systemy oparte wyłącznie na pytaniach mają trudności z zagwarantowaniem.

    Podstawowa lekcja polega na tym, że skuteczne aplikacje AI w przedsiębiorstwach stawiają rozwiązanie problemów biznesowych ponad zaawansowanie techniczne i przynoszą wartość dzięki solidnej integracji danych oraz automatyzacji procesów, a nie dzięki zaawansowanym technikom uczenia maszynowego. Warstwowa separacja zadań, iniekcja zależności oraz określone granice wykonywania sprawiają, że taki projekt stanowi wzorzec nadający się do ponownego użycia w automatyzacji obsługi klienta, pracach związanych z analizą finansową oraz w systemach inteligencji operacyjnej w innych organizacjach.