Обучите пользовательскую модель в Azure ML и вызовите её из агента Foundry.
Обучите, зарегистрируйте и разверните модель оттока на управляемом конечном пункте Azure ML, затем сделайте её доступной агенту Foundry в качестве FunctionTool с информацией об авторизации и рекомендациями для производственного использования.
Источники: Building and Serving a Custom Model with Azure ML, Then Wiring It Into a Foundry Agent
Каталог моделей Foundry довольно обширен, но существуют задачи, которые не входят в его рамки. Представьте себе прогнозирование ухода клиентов на основе собственных записей компании или оценку риска мошенничества по её собственной истории транзакций. Это традиционные задачи машинного обучения с использованием приватных табличных данных, и решением Microsoft для них является Azure Machine Learning — отдельный продукт с собственным SDK, собственной рабочей средой и уникальным способом развертывания моделей.
Приведённый ниже процесс охватывает весь путь: обучение модели с помощью SDK Azure ML, её регистрация, предоставление через управляемый конечный пункт и затем доступ к этому конечному пункту для агента Foundry в виде функционального инструмента. Агент может затем вызывать эту пользовательскую модель в ходе общения точно так же, как и любой другой инструмент.
Точка соприкосновения двух платформ
Эти два продукта работают в отдельных средах выполнения, но принадлежат к одной экосистеме, и Microsoft прилагает усилия для сведения к минимуму различий между ними. Понимание того, где именно находятся эти различия, позволяет легче следить за процессом сборки. В процесс вовлечены четыре компонента:
- Задание команды. Здесь происходит обучение. SDK направлен на скрипт, целевую машину для вычислений и определенные входные данные, после чего код выполняется в виде задания с возможностью отслеживания и воспроизведения.
- Реестр моделей. Обученная модель превращается в артефакт с именем и версией, который существует независимо от задания, с которого он был создан; таким образом, одну версию можно запустить в производство без повторного обучения.
- Управляемый онлайн-конечный пункт. Он предоставляет прогнозы на основе зарегистрированной модели. Это реальная инфраструктура, работающая постоянно или с автоматическим масштабированием, имеющая собственный URL, механизмы аутентификации и расчет стоимости; она находится внутри рабочего пространства Azure ML, а не в Foundry.
FunctionTool. Это мостовой элемент. Foundry не знает и не интересуется тем, что функция, находящаяся за этим инструментом, вызывает конечную точку Azure ML. Для агента это просто имя, схема и результат.Предварительные требования
- Рабочее пространство Azure ML и проект Foundry. Они могут находиться в одных или разных группах ресурсов, поскольку здесь не требуется общая инфраструктура.
- Python 3.9 или новее с обоими SDK:
pip install azure-ai-ml azure-ai-projects azure-identity
MLClient аутентифицируется и настраивает работу с рабочим пространством:
from azure.ai.ml import MLClient
from azure.identity import DefaultAzureCredential
ml_client = MLClient(
DefaultAzureCredential(),
subscription_id="<subscription-id>",
resource_group_name="<resource-group>",
workspace_name="<aml-workspace-name>",
)
Обучение как задача командного типа
Шаг обучения представляет собой задачу командного типа. Он берёт скрипт обучения — в данном случае обычный классификатор scikit-learn — и выполняет его на управляемых вычислительных ресурсах:
from azure.ai.ml import command, Input, Output
train_job = command(
code="./src",
command="python train.py --data ${{inputs.training_data}} --model_output ${{outputs.model_output}}",
inputs={"training_data": Input(type="uri_folder", path="azureml://datastores/workspaceblobstore/paths/churn-training/")},
outputs={"model_output": Output(type="uri_folder")},
environment="azureml://registries/azureml/environments/sklearn-1.5/labels/latest",
compute="cpu-cluster",
display_name="churn-model-training",
)
returned_job = ml_client.jobs.create_or_update(train_job)
ml_client.jobs.stream(returned_job.name)
Вызов ml_client.jobs.stream ожидает завершения обучения и выводит логи до его окончания. Его следует использовать во всех скриптах, которые действительно будут запускаться. Когда задача тихо терпит неудачу на фоне, это неприятный способ обнаружить сбой в работе конвейера.
Регистрация модели
В процессе регистрации результаты работы задачи преобразуются в именованный, версионированный артефакт, к которому можно обращаться независимо:
from azure.ai.ml.entities import Model
from azure.ai.ml.constants import AssetTypes
model = ml_client.models.create_or_update(
Model(
path=f"azureml://jobs/{returned_job.name}/outputs/model_output",
name="churn-classifier",
type=AssetTypes.MLFLOW_MODEL,
description="Customer churn classifier, trained on 18 months of account history.",
)
)
Выбор типа MLFLOW_MODEL сделан намеренно. Если скрипт обучения регистрирует модель с помощью автоматического логирования MLflow, управляемые конечные точки могут развернуть её с использованием встроенного контейнера оценки, без необходимости в специальном скрипте инференса score.py. Это экономит время в реальном времени, и скрипт обучения лучше всего проектировать с учётом этого с самого начала.
Развертывание за конечной точкой
Конечная точка в сочетании с механизмом развертывания позволяет вывести модель в сеть:
from azure.ai.ml.entities import ManagedOnlineEndpoint, ManagedOnlineDeployment
endpoint = ManagedOnlineEndpoint(name="churn-endpoint", auth_mode="key")
ml_client.online_endpoints.begin_create_or_update(endpoint).result()
deployment = ManagedOnlineDeployment(
name="blue",
endpoint_name="churn-endpoint",
model=model,
instance_type="Standard_DS3_v2",
instance_count=1,
)
ml_client.online_deployments.begin_create_or_update(deployment).result()
endpoint.traffic = {"blue": 100}
ml_client.online_endpoints.begin_create_or_update(endpoint).result()
Имя развертывания blue не является обязательным, но его стоит сохранить, поскольку это формирует полезную привычку при первой новой версии модели. Создайте развертывание green рядом с blue, распределите трафик между ними и полностью переключитесь на новую версию, когда она заслужит доверие. Замена blue и надежда на лучшее — это рискованный вариант.
Прежде чем что-либо ещё настраивать, получите прямое реальное прогнозирование:
import json
test_input = {"input_data": {"columns": ["tenure_months", "monthly_spend", "support_tickets"], "data": [[14, 89.50, 3]]}}
response = ml_client.online_endpoints.invoke(
endpoint_name="churn-endpoint",
request_file=None,
deployment_name="blue",
input_data=json.dumps(test_input),
)
print(response)
Отладка неисправного конечной точки с помощью цикла вызовов функций агента гораздо сложнее, чем её отладка напрямую, поэтому сначала убедитесь, что она работает.
Подключение конечной точки к агенту Foundry
Агент никогда не обращается к самому Azure ML. Код приложения делает это, когда агент об этом просит. В настройках объявляется FunctionTool, создаётся агент и определяется функция, которая отправляет HTTP-запрос к конечной точке:
import os
import requests
from azure.ai.projects import AIProjectClient
from azure.ai.projects.models import PromptAgentDefinition, Tool, FunctionTool
from azure.identity import DefaultAzureCredential
def get_churn_score(tenure_months: int, monthly_spend: float, support_tickets: int) -> dict:
payload = {"input_data": {"columns": ["tenure_months", "monthly_spend", "support_tickets"], "data": [[tenure_months, monthly_spend, support_tickets]]}}
resp = requests.post(
"https://churn-endpoint.<region>.inference.ml.azure.com/score",
headers={"Authorization": f"Bearer {os.environ['AML_ENDPOINT_KEY']}", "Content-Type": "application/json"},
json=payload,
timeout=10,
)
resp.raise_for_status()
return {"churn_probability": resp.json()[0]}
func_tool = FunctionTool(
name="get_churn_score",
description="Predict churn probability for a customer given tenure, spend, and support ticket history.",
parameters={
"type": "object",
"properties": {
"tenure_months": {"type": "integer", "description": "How many months the customer has been active."},
"monthly_spend": {"type": "number", "description": "Average monthly spend in dollars."},
"support_tickets": {"type": "integer", "description": "Number of support tickets in the last 90 days."},
},
"required": ["tenure_months", "monthly_spend", "support_tickets"],
"additionalProperties": False,
},
strict=True,
)
project = AIProjectClient(endpoint=os.environ["FOUNDRY_PROJECT_ENDPOINT"], credential=DefaultAzureCredential())
tools: list[Tool] = [func_tool]
agent = project.agents.create_version(
agent_name="retention-agent",
definition=PromptAgentDefinition(
model="gpt-4.1-mini",
instructions="Help the team assess churn risk. Call get_churn_score whenever specific customer numbers are provided.",
tools=tools,
),
)
Затем происходит обмен сообщениями, вызов функции модели перехватывается, и возвращается результат:
import json
from openai.types.responses.response_input_param import FunctionCallOutput
openai_client = project.get_openai_client()
conversation = openai_client.conversations.create()
response = openai_client.responses.create(
input="A customer's been with us 14 months, spends about $90/month, and filed 3 tickets recently. Churn risk?",
conversation=conversation.id,
extra_body={"agent_reference": {"name": agent.name, "type": "agent_reference"}},
)
for item in response.output:
if item.type == "function_call" and item.name == "get_churn_score":
result = get_churn_score(**json.loads(item.arguments))
follow_up = openai_client.responses.create(
input=[FunctionCallOutput(type="function_call_output", call_id=item.call_id, output=json.dumps(result))],
conversation=conversation.id,
extra_body={"agent_reference": {"name": agent.name, "type": "agent_reference"}},
)
print(follow_up.output_text)
Модель решает, требуется ли при заданном вопросе вызов функции get_churn_score. Если это необходимо, код приложения отправляет HTTP-запрос в Azure ML, а результат возвращается обратно в ту же конверсацию, чтобы модель могла завершить свой ответ. Ничто в определении агента или в вызовах API Responses не зависит от того, что происходит на фоне выполнения функции. Она может запрашивать данные из базы данных, вызывать функцию Lambda или обращаться к другому рабочему пространству Azure ML, как это делается здесь.
Два ресурса, два уровня аутентификации
В документации SDK скрывается один важный момент. Управляемый конечный пункт в Azure ML и проектный конечный пункт в Foundry представляют собой два ресурса, защищённые двумя отдельными системами аутентификации, и ничто в данной конфигурации не связывает их между собой. Azure ML имеет собственный ключ или управляемую идентичность для рабочей среды, в то время как Foundry использует другие учетные данные. Объявление FunctionTool не создаёт между ними доверия; оно лишь присваивает модели имя и схему для запроса данных у вашего кода. Вся аутентификация в Azure ML происходит внутри собственной функции, точно так же, как при вызове любого другого внешнего API.
Это является преимуществом. Часть, связанная с Azure ML, может управляться, защищаться и обновляться независимо от Foundry, даже другой командой, причём ни одна из сторон не нуждается в правах на запись в ресурсах другой стороны.
Замечания по эксплуатации
- Запрос на выполнение функции истекает через 10 минут. Результат должен быть отправлен обратно в чат до закрытия окна, иначе выполнение сорвется. Если HTTP-запрос к Azure ML медленный, рассматривайте это как строгий срок и учитывайте его при планировании.
- Переходите на управляемую идентичность вместо прототипа. Ключ, хранящийся в переменной среды, подходит для локального использования, но представляет риск при автономной работе. Предоставьте идентичности, запускающей код инструмента, роль
AzureML Data Scientistили более узкую пользовательскую роль, ограниченную конкретным эндпоинтом, а не всей рабочей средой. - Постепенно внедряйте новые модели. Запустите новую версию рядом с существующей и постепенно перенаправляйте трафик на нее. Это единственный способ обнаружить проблемы с переобучением до того, как от нее будут зависеть реальные запросы.
strict=True включённым. Строгие схемы не позволяют модели отправлять неполные или некорректно сформированные аргументы в функцию get_churn_score, что иначе привело бы к сбоям внутри функции ещё до её вызова.get_churn_score в месте, доступном для анализа, и регулярно пересматривайте данные обучения, а не только тогда, когда точность прогнозов заметно снижается.Выбор подходящей платформы
Для большинства задач с использованием генеративных ИИ Foundry — подходящий инструмент, и командам, у которых нет собственных данных для обучения, возможно, никогда не понадобится что-то более сложное. Когда речь идет о прогнозировании поведения собственных клиентов на основе имеющейся истории, более подходящим решением становится Azure ML. В противном случае языковая модель используется для задач, для которых она не предназначалась, или инфраструктура обслуживания создается вручную. Все необходимое сводится к немногому: одна задача обучения, одна зарегистрированная модель, один управляемый конечный пункт и единственный FunctionTool. Платформы остаются раздельными, каждая выполняя ту функцию, в которой она лучше всего справляется.