Головна / Статті

Навчіть власну модель у Azure ML та викликайте її з агента Foundry

Навчіть, зареєструйте та розгорніть модель втрат клієнтів на керованому кінцевому пункті Azure ML, а потім зробіть її доступною для агента Foundry у вигляді FunctionTool із інформацією про автентифікацію та умови використання в продакшені.

Видавець: REACTAPP.TOP7 хв читання1652 слів

Джерела: Building and Serving a Custom Model with Azure ML, Then Wiring It Into a Foundry Agent

Каталог моделей Foundry є широким, проте деякі завдання залишаються поза ним. Уявіть собі прогнозування відтоку клієнтів на основі власних записів компанії чи оцінку ризиків шахрайства за допомогою власної історії транзакцій. Це звичайні завдання машинного навчання на приватних табличних даних, і рішенням Microsoft для них є Azure Machine Learning — окремий продукт із власним SDK, власним простором роботи та власним способом розгортання моделей.

Наведений нижче процес охоплює всю послідовність дій: навчання моделі за допомогою Azure ML SDK, її реєстрація, надання доступу до неї з керованого кінцевої точки, а потім відкриття цієї кінцевої точки для агента Foundry як інструменту у вигляді функції. Агент може потім викликати цю спеціальну модель під час розмови точно так само, як він би викликав будь-який інший інструмент.

Де зустрічаються ці дві платформи

Ці два продукти працюють під керуванням різних середовищ виконання, але належать до однієї екосистеми, і Microsoft працює над тим, щоб зменшити розрив між ними. Чітке усвідомлення того, де саме він знаходиться, полегшує розуміння процесу створення. У цьому процесі беруть участь чотири компоненти:

  • Завдання команди. Тут відбувається навчання. SDK спрямований на скрипт, місце обчислень та певні дані вхіду, і виконує код як задання з можливістю відстеження та повторення.
  • Реєстр моделей. Навчена модель перетворюється на об’єкт із назвою та версією, який існує окремо від завдання, що її створило, тож одну версію можна запустити в промисловому середовищі без повторного навчання.
  • Керований онлайн-кінець точки. Він надає прогнози з зареєстрованої моделі. Це справжня інфраструктура, яка завжди увімкнена або масштабується автоматично, має власну URL, систему аутентифікації та витрати, і знаходиться всередині робочого простору Azure ML, а не Foundry.
  • Foundry FunctionTool. Це місток. Foundry не знає та не цікавиться тим, що функція, яка знаходиться за інструментом, викликає кінцеву точку Azure ML. Для агента це просто ім’я, схема та результат.
  • Передумови

    1. Робоче середовище Azure ML та проект Foundry. Вони можуть знаходитися в одних чи різних групах ресурсів, оскільки тут немає потреби у спільній інфраструктурі.
    2. 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. Платформи залишаються окремими, кожна виконує те, у чому вона найкраща.