Четырехуровневая архитектура для агента выполнения кратких отчетов в Azure
Как агент для обработки информации с детерминированным подходом распределяет задачи между Fabric, процедурами хранения данных SQL, FastAPI в Azure Functions и Copilot Studio для создания руководящих документов.
Любой, кто готовит руководителя к встрече с клиентом, знает, насколько разрозненными бывают исходные данные. Записи CRM находятся в одной системе, показатели доходов — в другой, информация об использовании продуктов — в третьей, и стратегический отчет приходится собирать вручную из всех этих источников. Инструмент для составления кратких отчетов решает именно эту проблему: человек просит отчет простым языком, и платформа возвращает документ, готовый к использованию руководителем, сформированный на основе фрагментированных операционных, финансовых и данных об использовании ИИ.
То, что делает эту архитектуру достойной изучения, — это минимум случайности в её работе. Вместо того чтобы просить модель составлять отчёт на основе указаний, здесь ручное объединение данных заменяется оркестрацией, регулируемой контрактами. Данные обрабатываются в рамках чётких правил, объединяются с помощью детерминированных SQL-запросов, передаются через безсостоянийные сервисы и доставляются в рамках контролируемого диалогового процесса. Архитектура устанавливает чёткие границы выполнения между четырьмя слоями: фундаментом, данными, сервисами и пользовательским интерфейсом, что позволяет воспроизводить результаты, проводить аудит и обеспечивать высокую скорость работы в рамках строгих требований к безопасности.
Этот дизайн был разработан в рамках внутренних усилий по развитию ИИ в экосистеме Microsoft, а приведенные ниже утверждения относительно производительности и результатов сделаны самими создателями и не проверялись независимо. Он наиболее полезен для архитекторов корпоративных систем, инженеров ИИ и руководителей платформ, которым нужны детерминистичные системы ИИ, объединяющие консолидацию данных, агрегацию с помощью SQL, оркестрацию без сохранения состояния и рабочие процессы на основе диалогов.
Четырехуровневая архитектура
Агент работает по четырехуровневой модели оркестрации, при этом каждый уровень выполняет определенную функцию и охватывает несколько сервисов Azure и границ безопасности:
- Основа: Microsoft Fabric Lakehouse, обеспечивающий консолидацию данных в рамках четких правил управления.
- Данные: Fabric SQL (хранилище или точка входа SQL), содержащий процедуры хранения для операционных запросов и агрегации.
Вместе они формируют однонаправленную цепочку обработки данных. Данные сначала обрабатываются и управляются на базовом уровне, затем детерминированно агрегируются в SQL, собираются с помощью логики домена на уровне сервиса и в конечном итоге предоставляются через взаимодействие на естественном языке. Каждый слой имеет чёткий контракт с соседними слоями, поэтому элемент ИИ дополняет процесс принятия решений, а не мешает ему.
Уровень основы: управляемая консолидация данных
Microsoft Fabric выступает в роли единой аналитической платформы и единого источника информации о деталях счетов, финансовых показателях, метриках использования и исторических тенденциях. Основное правило простое: хранить данные один раз и использовать их многократно, чтобы ничего не дублировалось между системами.
Пайплайн обновляется ежедневно, собирая и объединяя корпоративные данные из Dynamics 365, финансовых систем и внешних API. Заданное расписание обеспечивает предсказуемые окна обработки и гарантирует, что каждая исходная система завершит свою ежедневную работу до начала консолидации. Lakehouse хранит данные в их исходном формате вместе с информацией о происхождении и стандартами управления, а отфильтрованные наборы данных затем переходят в Fabric SQL — быстрый операционный слой запросов, структура которого оптимизирована для подготовки отчетов для руководства.
Слой данных: операционный двигатель, созданный для доступа через API
Этый слой преобразует тщательно отобранные базовые данные в детерминированные контракты запросов с низкой задержкой для генерации кратких отчетов. Он устанавливает строгие границы агрегации и обеспечивает производительность, которую руководители ожидают от инструмента с быстрой реакцией.
Архитектура. Fabric SQL Warehouse — это реляционный интерфейс, который оборачивает все операции с данными, связанными с отчетностью, в хранимые процедуры. Слой обслуживания никогда не выполняет запросы к таблицам напрямую. Сложные объединения таблиц, смысл каждой агрегации и бизнес-логика выполняются внутри базы данных, а не в коде приложения, что позволяет сохранять предсказуемость времени отклика при больших объемах данных. SQL-запросы настроены для операционных запросов, а не для аналитического анализа; дизайн направлен на обеспечение детерминистичных объединений, дающих единообразные результаты, повторно используемых планов запросов для стабильной производительности, параметризованного выполнения для безопасности и инкапсулированных агрегаций, которые легче обслуживать.
Философия схемы: запрос в приоритете. Схема соответствует шаблонам запросов, а не правилам нормализации из учебников. Энтитет Account является основной структурой кластеризации, а вспомогательные таблицы организованы таким образом, чтобы объединения выполнялись эффективно по бизнес-идентификатору — 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](
Таблица Accounts является центральной сущностью, ключом для группировки в которой служит TPID. Это обеспечивает примерно логарифмическую скорость поиска для наиболее частых запросов, связанных с конкретными учётными записями, а также позволяет выполнять как прямые запросы по TPID, так и поиск по имени.
Вспомогательные таблицы. Несколько вспомогательных таблиц демонстрируют, как обрабатываются финансовые показатели, данные об использовании ИИ и информация, полученная из других систем.
-- 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]);
Таблица доходов хранит финансовые данные за несколько лет и использует комбинированные индексы, подходящие для расчётов временных рядов и тенденций.
-- 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]);
Таблица использования ИИ отражает внедрение продукта по категориям, что позволяет анализировать модели вовлеченности и уровень принятия технологий, важные для планирования счетов.
Стратегия хранения процедур: бизнес-логика находится в базе данных. Три основные процедуры обеспечивают генерацию всех необходимых данных. SearchAccounts находит счета и ранжирует их по доходу, GetAccountDetails объединяет данные из различных источников в полный контекст для руководства, а GetBriefingTemplate контролирует авторизацию шаблонов в зависимости от ролей, чтобы доступ имели только уполномоченные пользователи. Слой сервисов вызывает только эти процедуры, и использование динамического SQL там не разрешено.
--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;
Производительность и безопасность. Для повышения эффективности транзакции используют команду SET NOCOUNT ON, а для операций чтения — команду SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED, чтобы они не блокировали друг друга. Обработка JSON осуществляется с помощью встроенных функций SQL Server JSON_QUERY и FOR JSON PATH, которые обеспечивают эффективную обработку и прямую сериализацию данных из базы. Разрешена только параметризованная обработка запросов; сервисная идентичность приложения имеет право на выполнение хранимых процедур, но не имеет прямого доступа к таблицам.
GRANT EXECUTE ON SCHEMA::[aieo] TO [service_identity];
- No direct table access permitted
Слой сервисов: оркестрация без состояния и границы выполнения
Слой сервисов занимается исключительно оркестрацией данных. Он получает данные, отображает шаблоны и не применяет собственных бизнес-правил. Каждый сервис имеет одну задачу и связан с другими слабо, что позволяет чётко разделять слои данных и представления.
Интерфейс API и валидация. Приложение FastAPI предоставляет явные REST-конечные точки с строго типизированными контрактами, определёнными как модели Pydantic:
POST /api/briefing/generate— основная конечная точка для генерации отчётов с полной валидацией входных данных.GET /api/health— возвращает информацию о статусе системы.GET /api/templates— показывает список шаблонов, доступных вызывающему пользователю.
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
Модель обеспечивает строгую валидацию и безопасность типов, автоматически генерирует документацию OpenAPI и корректирует структуру каждого запроса. Никакой неструктурированный ввод не может пройти через этот контроль.
Модель выполнения. Сервис выполняет детерминированную последовательность из пяти шагов:
- Валидация запроса. Модели Pydantic проверяют, соответствуют ли типы и данные установленным требованиям.
- Определение аккаунта. Формат входных данных определяет, следует ли напрямую искать TPID или вызывать функцию
SearchAccountsдля поиска по имени. - Одновременный захват данных. Функции
GetAccountDetailsиGetBriefingTemplateвыполняются одновременно с помощьюasyncio.gather(). - Сбор контекста. Результаты процедур напрямую преобразуются в объекты, готовые к использованию в шаблонах, без какой-либо промежуточной обработки.
Асинхронная обработка и пулирование соединений через абстракцию DatabaseService обеспечивают то, что одинаковые входные данные дают одинаковые выходные результаты.
Руководящие принципы. Слой обслуживания регулируется пятью правилами. В нем отсутствует бизнес-логика, поскольку обработка данных происходит исключительно внутри хранимых процедур, что освобождает слой обслуживания от правил, усложняющих тестирование и поддержку. Он занимается исключительно оркестрацией данных, координируя их получение и отображение без каких-либо преобразований или вычислений. Картирование контекста осуществляется напрямую, поэтому результаты процедур попадают в структуры шаблонов без лишних этапов, что снижает риск искажения значений при передаче данных между слоями. Обработка является детерминистичной, что гарантирует одинаковый результат для одних и тех же входных данных независимо от времени выполнения или состояния инфраструктуры. Кроме того, функции четко разделены: логика обработки данных находится в SQL-процедурах, оркестрация — в сервисах Python FastAPI, а отображение — в шаблонах Jinja2.
Слой интеракции: Copilot Studio в качестве движка диалога
В верхней части фреймворк AdaptiveDialog в Microsoft Copilot Studio преобразует запрос на естественном языке в детерминированный рабочий процесс. Он обрабатывает урегулирование вопросов, связанных с учетными записями, авторизацию шаблонов, интеграцию внешних данных, генерацию документов и их доставку через различные каналы. Процесс выполняется последовательно: от первого запроса до урегулирования вопросов с учетными записями, авторизации шаблонов, сбора внешних данных и их передачи в SharePoint, причем на каждом этапе диалога предусмотрены точки принятия решений и шаги проверки.
Основной диалог. В рамках этого диалога автоматически собирается идентификатор учетной записи, определяются фразы-триггеры, запускающие процесс обработки запроса, а также устанавливается основная точка входа в диалог:
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
Решение проблем с учётными записями. Функция IsNumeric() определяет, является ли введённый текст прямым ID TPID или названием учётной записи, требующим поиска. При возможности отказ от поиска сокращает количество вызовов API и ускоряет получение ответа:
- 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
Звонок для поиска учётной записи. Для имён сервис использует REST-интерфейс для обращения к точке входа поиска слоя сервисов. Он URL-кодирует специальные символы, аутентифицируется с помощью ключа подписки и указывает схему ответа, что позволяет автоматически проверять данные:
- 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
Карточка дисэмбигуации. Если поиск находит несколько учётных записей, адаптивная карточка отображает динамический набор вариантов, сгенерированный с помощью ForAll() из результатов, причём каждая запись показывает свой TPID. Таким образом, неоднозначный ввод не прерывает диалог:
- 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\"}]
}')"
Разрешение на использование шаблонов. С помощью метода GetBriefingTemplate система проверяет, имеет ли пользователь право использовать шаблоны для руководства, исходя из его организационной роли и принадлежности к исполнительному офису. Имя принципала подтверждает личность, тип брифинга фильтрует результаты, а управление шаблонами по умолчанию является безопасным:
- 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
Карточка с деталями встречи. В следующей карточке используется принцип постепенного отображения: обязательные поля показываются сразу, а необязательные — скрываются за переключателем. Это снижает когнитивную нагрузку, при этом собирается вся необходимая информация для брифинга:
- 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\"}
]
}')"
Одновременный сбор данных и генерация документов. Слой обработки опыта одновременно вызывает API с данными о счете и Azure AI Foundry, установив для каждого таймаут в 60 секунд. После получения ответов от обоих источников формируется один структурированный JSON-пакет на основе всех данных, который затем передается в API для генерации документов; процессы проверки и сборки выполняются автономно, что обеспечивает детерминированное создание отчета.
Azure AI Foundry: организационный интеллект
Интеграция с Foundry расширяет возможности агента за пределы внешних рыночных данных, позволяя использовать контекст изнутри организации благодаря компоненту «M365 Researcher». Для поиска писем в Outlook и сообщений в Teams используется делегированный доступ к Microsoft Graph; при этом все операции происходят в рамках корпоративных мер безопасности и принципов нулевого доверия.
Доступ к Microsoft Graph через делегированную аутентификацию без хранения секретов. Исследователь использует делегированную аутентификацию, которая полностью исключает хранение секретов клиента. Для этого применяются коннекторы, управляемые Azure Logic Apps — приложения от Microsoft с заранее согласованными диапазонами полномочий делегированной аутентификации. Это избавляет от необходимости управления регистрацией приложений, поскольку Azure отвечает за жизненный цикл токенов и их автоматическое обновление.
Кроме того, временные инстансы Logic Apps обеспечивают доступ к данным пользователя с использованием HTTP-триггеров, защищенных SAS. При каждом поиске запускается новая инстанция Logic App, которая параллельно загружает данные из Microsoft Graph и сразу же удаляется, поэтому SAS-адрес невозможно использовать повторно, и никакие постоянные токены не остаются доступными.
// 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;
};
Параллельный поиск и настройка Graph. Данные из электронной почты и Teams собираются одновременно, при этом соблюдаются ограничения на одновременную работу, чтобы сессии множества пользователей не вызывали замедления работы системы. Для почты модуль подключения Office 365 выполняет массовый захват последних 250 сообщений от каждого пользователя, причем фильтрация происходит на клиенте для обхода ограничений Graph при поиске в теле сообщений. В случае с Teams используется двухэтапный подход: сначала через пагинированные запросы находятся чаты, а затем сообщения из отдельных чатов загружаются с настраиваемым лимитом одновременной работы.
# 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
Типичный поиск выполняет примерно 101 вызов Graph API: один для почты, два для перечисления чатов в Teams и в среднем 98 вызовов для получения сообщений чатов. Это позволяет получить большой объем данных, при этом соблюдаются лимиты одновременных операций Graph API, а согласно проектированию время отклика остается ниже 60 секунд.
Подключения и токены на уровне пользователя. Каждому пользователю требуются два управляемых подключения для хранения делегированных токенов OAuth в течение всех сессий поиска: office365-{alias} для работы с почтой с диапазоном полномочий Mail.ReadWrite и teams-{alias} для чата с диапазоном полномочий Chat.Read. Инфраструктура управляемых соединителей Azure автоматически обновляет токены и обеспечивает соблюдение требований безопасности. Процесс настройки осуществляется на основе согласия пользователя: он входит в систему один раз через встроенный механизм OAuth от Microsoft, а делегированные полномочия действуют до момента их отзыва. Функциональное приложение управляет жизненным циклом подключений с помощью API Azure Resource Manager под управлением идентификатора управляемой учетной записи, поэтому клиентские учетные данные не хранятся на уровне приложения.
// 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);
};
Синтез с помощью модели GPT. Чистые сообщения преобразуются в готовую к использованию организационную информацию с помощью модели из сервиса Azure OpenAI Service (указанной как GPT-4.1, хотя в примерном коде говорится о предварительной версии GPT-4). Система преобразует отфильтрованную почту и контент Teams в структурированные выводы, охватывающие анализ заинтересованных сторон, модели коммуникации и действия, связанные с собранием. Запросы ориентированы на потребности руководства, такие как картирование отношений, статус проектов и стратегические темы, при этом модель возвращает структурированный JSON-формат, который можно вставить в существующие шаблоны Jinja2 с единым форматированием во всех документах.
# 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)
Интеграция в диалог. Исследователь присоединяется к рабочему процессу Copilot Studio одновременно с загрузкой данных из стандартного аккаунта и получением внешней рыночной информации, поэтому дополнительный организационный контекст не должен замедлять процесс создания обзоров.
# 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
}
Управление и соблюдение правил. Благодаря делегированным полномочиям пользователи могут искать только свою собственную почту и сообщения в Teams, что предотвращает доступ других пользователей и обеспечивает строгий контроль над данными. Каждый вызов Graph API фиксируется с указанием контекста пользователя, параметров поиска и метаданных результатов. Срок хранения данных можно настроить, а соединения, которые не используются в течение установленного периода, обычно 80 дней, автоматически удаляются. Обработка данных происходит внутри тенанта Azure, а механизмы контроля местоположения данных способствуют соблюдению политик управления организации.
// 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);
}
};
Производительность и устойчивость к сбоям. Система хранит в кэше статус подключения пользователя и проверяет его работоспособность перед выполнением ресурсоемких операций Graph. Если доступ к Graph ограничен или временно недоступен, система плавно снижает уровень обслуживания, используя экспоненциальное откладывание попыток при временных сбоях и строгие лимиты по времени ожидания, чтобы генерация отчетов не задерживалась. Даже если поиск в организации проваливается или истекает время ожидания, отчет все равно отправляется вместе с данными о счете и внешней рыночной информацией, что обеспечивает руководителям стабильный опыт использования независимо от доступности 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;
}
};
После того как исследователь назначен, агент переходит от сбора информации из внешних источников к формированию комплексной картины, включающей данные от внутренних заинтересованных сторон, что направлено на принятие более эффективных стратегических решений без снижения стандартов безопасности, управления или эффективности.
Вызов для генерации документа. На этом этапе создается окончательный отчет путем объединения всей собранной информации из разговора в один пакет данных для слоя обслуживания. В этот пакет входят контекст пользователя, детали встречи и необязательные данные от внешних ИИ-систем; срок выполнения всей процедуры генерации документа составляет 60 секунд:
- 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
Размещение в SharePoint. Все сгенерированные документы попадают в центральный хранилище через коннектор SharePoint Power Platform с управляемой аутентификацией, поэтому каждый документ сохраняется и остается доступным независимо от способа вторичной доставки, что соответствует требованиям к управлению данными. Целевой сайт и папка в приведённом ниже примере являются местоположениями-заменителями.
- 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
Имена файлов формируются таким образом, чтобы они были читаемыми, объединяя имя учетной записи, идентификатор руководителя и дату встречи. Функция Substitute() обеспечивает их безопасность в файловой системе и сохраняет единообразие названий во всех рабочих процессах руководителей:
- kind: SetVariable
id: setVariable_CustomFilename
variable: Topic.CustomFilename
value: =Concatenate(
Substitute(Topic.Account_Name, " ", "_"), "_",
Topic.ExecutiveName, "_Brief_",
Text(Topic.meetingDate, "yyyy-MM-dd"), ".docx"
)
Обработка ошибок и состояние. При неудачном доступе к шаблону предоставляется контекстуальная инструкция в виде карточки адаптивного типа с предупреждением, а не общая ошибка. Система анализирует конкретные коды ошибок из API-сервиса, сообщает пользователю, какие разрешения отсутствуют, и скрывает конфиденциальные детали системы:
- 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
}]
}]
}
Проверка данных происходит на клиентской стороне, поэтому недопустимые данные никогда не попадают в бэкенд-сервисы, что улучшает пользовательский опыт и избавляет от ненужных запросов к API. Например, дата встречи, прошедшая в прошлом, сразу же отклоняется с рекомендациями по дальнейшим действиям:
- 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
Этот шаг очистки, критически важный с точки зрения безопасности, удаляет все переменные, содержащие данные аккаунтов, ответы API и ввод пользователя по окончании разговора. Это обеспечивает соблюдение требований к конфиденциальности и предотвращает накопление данных в сервисах длительных разговоров, которые обрабатывают конфиденциальную деловую информацию:
- 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()
В совокупности слой опыта обеспечивает оркестрацию диалогов с использованием детерминированных рабочих процессов, расширенной обработки ошибок, надежных вызовов внешних сервисов, авторизации на основе ролей и многоканальной доставки через фреймворк AdaptiveDialog.
Безопасность и детали реализации
Федеративная идентичность и аутентификация
Система использует учетные данные федеративной идентичности Azure, что позволяет не хранить секреты и обеспечивает единый процесс входа во всех средах. Селектор учетных данных определяет место выполнения кода и выбирает соответствующий механизм:
# Environment-aware authentication pattern
def get_credential():
if is_azure_environment():
return ManagedIdentityCredential(client_id=os.getenv('AZURE_CLIENT_ID'))
else:
return AzureCliCredential()
В Azure используется управляемая идентичность, при которой учетные данные автоматически предоставляются и обновляются, а для локальной разработки применяются учетные данные Azure CLI. Токены хранятся в кэше безопасным способом, обеспечивающим синхронизацию между потоками, а вопросы истечения срока действия решаются автоматически.
Структура сервиса и управление зависимостями
Слой сервиса опирается на внедрение зависимостей, что позволяет одному и тому же коду работать как с имитациями в тестах, так и с полностью настроенными экземплярами в производстве. Вопросы, касающиеся нескольких компонентов, такие как обработка ошибок и управление конфигурацией, используются всеми компонентами сервиса; каждый из них (SharePoint, база данных, Power BI) принимает необязательные зависимости через свой конструктор, при этом оставаясь слабо связанным с внешними API и ресурсами Azure. Это обеспечивает возможность тщательного тестирования с использованием имитаций, а также надежную работу в производстве благодаря правильной абстракции.
Оркестрация выполняется асинхронно для максимальной эффективности параллельного сбора данных. При генерации краткого отчета запрашиваются несколько источников с использованием механизма async/await и тщательной обработки ошибок, что позволяет системе корректно функционировать даже при сбое одного из источников.
Подключения к базе данных и производительность
Для работы с базой данных используется пул подключений через механизм управления движком SQLAlchemy, причем применяется паттерн синглтон для эффективного использования ресурсов. Запросы объединяются в хранимые процедуры, в которых находится бизнес-логика, что сокращает объем сетевого трафика и упрощает код приложения. Большие наборы результатов обрабатываются путем интеллектуальной реконструкции JSON, что позволяет обходить ограничения библиотеки pyodbc при работе с большими объемами данных, обеспечивая надежную обработку независимо от размера результата и сохраняя разумное использование памяти.
Интеграция внешних сервисов
Интеграция с SharePoint и Microsoft Graph откладывает кэширование свойств до момента необходимости, чтобы сократить количество запросов к API при сохранении актуальности данных. ID сайтов и папок кэшируются с умным механизмом аннулирования данных и динамическим выбором библиотек, что обеспечивает надежность загрузок. Интеграция с Dynamics 365 парсит ответы Search v2.0 и преобразует сырые результаты в структуры, имеющие практическую ценность для бизнеса, с логикой повторных попыток при ограничениях по частоте запросов и тщательной обработкой ошибок.
Генерация документов
Пайплайн генерации сочетает обработку шаблонов Jinja2 с формированием контекста, преобразующим необработанные данные в полезную информацию, готовую к использованию руководством. Контекст шаблона включает необработанные данные, рассчитанные показатели, инструменты форматирования и аналитические данные, полученные в результате межсистемного анализа. Система управления шаблонами проверяет синтаксис и обрабатывает ошибки, что обеспечивает надежность генерации даже при редактировании шаблонов; кроме того, благодаря настройкам типов MIME поддерживается несколько форматов вывода для гибкой доставки информации.
Ошибки и исключения
В приложении имеется структурированная иерархия исключений, которая предоставляет подробную информацию об ошибках при соблюдении принципов безопасности. Собственные классы исключений содержат контекст, способствующий отладке без разглашения внутренних деталей системы:
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()
Глобальный обработчик исключений преобразует технические ошибки в структурированные ответы API, которые помогают вызывающему коду, не раскрывая при этом слишком многих деталей.
Оценка и проверка качества
Система оценивается с помощью структурированной рамки тестирования, которая проверяет техническую производительность и качество отчетов в нескольких корпоративных учетных записях. Количественные и качественные методы сочетаются в процессе оценки, предусматривающем тестирование каждого архитектурного компонента пакетно.
С точки зрения количественных показателей ключевым является точность данных. Показатели в готовых шаблонах отчетов сопоставляются с исходными данными из базового уровня, после чего система вычисляет коэффициенты точности и среднюю абсолютную процентную погрешность (MAPE). Эти показатели позволяют оценить, насколько точно слой обслуживания передает данные по всему процессу обработки, от результатов хранимых процедур до окончательного документа. Пакетные запуски охватывают множество счетов и формируют сводную таблицу показателей эффективности, предоставляя статистические доказательства того, что концепция чистой оркестрации сохраняет точность данных, не приводя к ошибкам в расчетах или семантическим отклонениям между базовым и пользовательским уровнями.
Качественная оценка учитывает один аспект — уровень осведомленности компании, то есть насколько хорошо в материалах отражается реальное понимание целевой организации и насколько адекватно о ней говорится. Судья, использующий ИИ-модель, оценивает каждый материал по пяти стандартным критериям: релевантность (подходит ли содержание для принятия решений руководством), связность (логическая структура и последовательность), точность (соответствие фактам и исходным данным), ясность (язык и формат представления, подходящие для руководства) и полнота (охват стратегических аспектов). Такой подход обеспечивает последовательную и масштабируемую оценку с объективным подсчётом баллов для различных типов клиентов и отраслей, однако, как и в случае с любой оценкой на основе ИИ-моделей, он унаследовывает собственные предубеждения модели судьи.
Следующие шаги: переход на Microsoft Agent Framework
Команда считает четырехуровневую архитектуру хорошей основой для миграции на Microsoft Agent Framework (MAF), что позволит усилить оркестрацию ИИ, сохраняя при этом принципы детерминизма. MAF в основном будет внедряться на уровне сервисов, заменяя логику оркестрации FastAPI агентами MAF, которые будут использовать те же самые шаблоны чистой оркестрации данных. Уровень пользовательского интерфейса полностью изменится: Copilot Studio уступит место нативной интеграции с Microsoft 365, которая разместит рабочие процессы руководства внутри Outlook, Teams и SharePoint, используя возможности понимания языка от MAF для более точного распознавания намерений и ведения многократных диалогов, при этом для анализа бизнес-информации будут продолжать использоваться те же хранимые процедуры.
Встроенные инструменты для наблюдаемости, управления сообщениями и контроля жизненного цикла агентов MAF позволят отказаться от собственной инфраструктуры и получить мониторинг и процессы развертывания корпоративного уровня. Путь миграции сохраняет существующие инвестиции в процедуры хранения данных, шаблоны Jinja2 и интеграцию с SharePoint, добавляя при этом возможности для более сложных сценариев, таких как подготовка отчетов, охватывающих несколько учетных записей, синтез анализа конкурентов, а также генерация шаблонов динамически на основе контекста встречи и участников. Благодаря интеграции с Microsoft 365 генерация отчетов станет частью существующих рабочих процессов продуктивности, а не отдельным чат-интерфейсом, причем слои данных и сервисов останутся детерминистичными.
Что показывает дизайн
Этот агент превращает подготовку кратких отчетов из процесса ручной координации в автоматическую генерацию информации с использованием четырех уровней, отвечающих за консолидацию данных, агрегацию операций, организацию бизнес-процессов и взаимодействие с помощью естественного языка. Говорится, что этот детерминистический подход в рамках интегрированной экосистемы Microsoft обеспечивает быструю генерацию отчетов (утверждается, что это происходит «за доли секунды», хотя собственные временные ограничения процесса составляют 60 секунд, поэтому это, скорее всего, касается шагов обработки в базе данных и сервисах), а также безопасность, управление и возможности аудита, которые системам, основанным исключительно на вводе команд, трудно гарантировать.
Основной урок заключается в том, что успешные корпоративные приложения на основе ИИ ставят решение бизнес-задач выше технической сложности и создают ценность благодаря качественной интеграции данных и автоматизации процессов, а не за счёт передовых методов машинного обучения. Модель разделения функций на уровнях, внедрение зависимостей и чёткие границы выполнения делают такую архитектуру универсальным шаблоном для автоматизации обслуживания клиентов, рабочих процессов финансового анализа и систем операционной интеллектуальности в других организациях.