Strona główna / Artykuły

Szkolenie dostosowanego modelu w Azure ML i wywoływanie go z agenta Foundry

Szkolimy, rejestrujemy i wdrażamy model churn na zarządzanym punkcie końcowym Azure ML, a następnie udostępniamy go agencie Foundry jako FunctionTool, wraz z informacjami dotyczącymi autoryzacji i warunków produkcji.

Wydawca: REACTAPP.TOP7 min czytania1652 słów

Źródła: Building and Serving a Custom Model with Azure ML, Then Wiring It Into a Foundry Agent

Katalog modeli Foundry jest szeroki, ale niektóre zadaania wykraczają poza jego zakres. Wyobraźmy sobie przewidywanie odejść klientów na podstawie własnych rekordów firmy lub ocenianie oszustw na podstawie jej historii transakcji. To są typowe zadania uczenia maszynowego na prywatnych danych tabelarycznych, a rozwiązaniem Microsoftu w tym zakresie jest Azure Machine Learning – odrębny produkt z własnym SDK, indywidualną przestrzenią roboczą oraz własną metodą wdrażania modeli.

Poniższy proces obejmuje całą ścieżkę: szkolenie modelu za pomocą SDK Azure ML, jego rejestrację, udostępnianie go z zarządzanego punktu końcowego, a następnie udostępnianie tego punktu końcowego agentowi Foundry jako narzędzia funkcyjnego. Agent może wtedy wywołać ten specjalny model w trakcie rozmowy dokładnie tak samo, jak wywoływałby każde inne narzędzie.

Gdzie spotykają się te dwie platformy

Dwa te produkty działają w oddzielnych środowiskach wykonawczych, ale należą do jednego ekosystemu, a Microsoft stara się utrzymać między nimi jak najmniejszą przerwę. Wiedza o dokładnym miejscu tej przerwy ułatwia śledzenie procesu budowania. W grę wchodzą cztery elementy:

  • Zadanie poleceń. Tutaj odbywają się procesy szkolenia. SDK jest skierowane na skrypt, cel obliczeniowy oraz dane wejściowe i wykonuje kod jako zapisane, powtarzalne zadanie.
  • Rejestr modeli. Szkolony model przekształca się w nazwany, zversionowany artefakt, który istnieje niezależnie od zadania, które go stworzyło, dzięki czemu jedna wersja może zostać wprowadzona do produkcji bez konieczności ponownego przeprowadzenia procesu szkolenia.
  • Zarządzany online endpoint. Służy do dostarczania prognoz na podstawie zarejestrowanego modelu. Jest to prawdziwa infrastruktura, zawsze dostępna lub skalowana automatycznie, posiadająca własną adresację URL, mechanizmy autoryzacji oraz koszty, i znajduje się w przestrzeni roboczej Azure ML, a nie w Foundry.
  • Foundry FunctionTool. To jest most łączący obie platformy. Foundry nie wie ani go to nie obchodzi, że funkcja znajdująca się za tym narzędziem wywołuje punkt końcowy Azure ML. Dla agenta jest to po prostu nazwa, schemat oraz wynik.
  • Wymagania wstępne

    1. Przestrzeń robocza Azure ML oraz projekt Foundry. Mogą znajdować się w tych samych lub różnych grupach zasobów, ponieważ nic tutaj nie wymaga wspólnej infrastruktury.
    2. Python 3.9 lub nowsza wraz z oboma SDK:
    pip install azure-ai-ml azure-ai-projects azure-identity
    

    MLClient uwierzytelnia się i skierowuje do przestrzeni roboczej:

    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>",
    )
    

    Szkolenie jako zadanie poleceń

    Krok szkolenia to zadanie poleceń. Bierze ono skrypt szkoleniowy, w tym przypadku zwykły klasyfikator scikit-learn, i wykonuje go na zarządzanej mocy obliczeniowej:

    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)
    

    Wezwanie do ml_client.jobs.stream czeka i wyświetla logi aż trening się zakończy. Należy go używać w każdym skrypcie, który rzeczywiście ma zostać uruchomiony. Zadanie, które zawodzi w tle bez żadnych komunikatów, to nieprzyjemny sposób na odkrycie awarii w łańcuchu przetwarzania.

    Rегистrowanie modelu

    Rегистrowanie przekształca wyniki zadania w nazwany, zwersjonowany artefakt, który można odwoływać się do niego niezależnie:

    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.",
        )
    )
    

    Wybór MLFLOW_MODEL jako typu jest celowy. Jeśli skrypt treningowy rejestruje model za pomocą automatycznego logowania MLflow, zarządzane punkty końcowe mogą go wdrożyć przy użyciu wbudowanego kontenera oceny, bez konieczności stosowania własnego skryptu inferencji score.py. Dzięki temu oszczędza się czas w czasie rzeczywistym, a skrypt treningowy powinien być od początku zaprojektowany wokół tego rozwiązania.

    Wdrażanie za pomocą punktu końcowego

    Punkt końcowy w połączeniu z procesem wdrażania umożliwia udostępnienie modelu online:

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

    Nazwa wdrożenia blue nie jest obowiązkowa, ale warto ją zachować, ponieważ ułatwia przyzwyczajenie się do tej praktyki już przy pierwszej nowej wersji modelu. Stwórz kolejne wdrożenie green obok blue, rozdziel ruch między nimi i przenieś go całkowicie, gdy nowa wersja zyska zaufanie. Zastąpienie blue i liczenie na najlepszy scenariusz to ryzykowna alternatywa.

    Zanim podłączysz cokolwiek innego, uzyskaj bezpośrednią, rzeczywistą prognozę:

    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)
    

    Doprowadzanie do porządku uszkodzonego punktu końcowego za pomocą pętli wywoływania funkcji agenta jest znacznie trudniejsze niż jego bezpośrednie naprawianie, dlatego najpierw upewnij się, że działa poprawnie.

    Łączenie punktu końcowego z agentem Foundry

    Agent nigdy nie wywołuje bezpośrednio Azure ML. Kod aplikacji to robi, gdy agent o to prosi. Konfiguracja deklaruje FunctionTool, tworzy agenta i definiuje funkcję, która wysyła żądanie HTTP do punktu końcowego:

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

    Następnie odbywa się rozmowa, wywołanie funkcji modelu zostaje przechwycone, a wynik jest zwracany:

    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)
    

    Model decyduje, czy pytanie wymaga wywołania funkcji get_churn_score. Gdy tak jest, kod aplikacji wysyła żądanie HTTP do Azure ML, a wynik jest zwracany do tej samej rozmowy, aby model mógł dokończyć swoją odpowiedź. Nic w definicji agenta ani we wywołaniach API Responses nie zależy od tego, co funkcja robi w tle. Może ona korzystać z bazy danych, wywoływać funkcję Lambda lub łączyć się z innym pracownikiem Azure ML, tak jak to ma miejsce tutaj.

    Dwa zasoby, dwa obszary autoryzacji

    Jedna kwestia pozostaje niewyjaśniona w dokumentacji SDK. Zarządzany punkt końcowy w Azure ML oraz punkt końcowy projektu w Foundry to dwa zasoby znajdujące się za dwiema odrębnymi ścianami autoryzacji, a nic w tym ustawieniu ich nie łączy. Azure ML posiada własny klucz lub zarządzaną tożsamość dla przestrzeni roboczej, podczas gdy Foundry ma inne dane uwierzytelniające. Deklaracja FunctionTool nie buduje żadnego zaufania pomiędzy nimi; daje jedynie modelowi nazwę i schemat, aby mógł poprosić Twój kod o coś. Cała autoryzacja wobec Azure ML odbywa się wewnątrz Twojej funkcji, dokładnie tak jak przy wywoływaniu dowolnej innej zewnętrznej API.

    To jest zaleta. Strona Azure ML może być zarządzana, zabezpieczana i aktualizowana niezależnie od Foundry, nawet przez inny zespół, a żadna ze stron nie potrzebuje uprawnień do edycji zasobów drugiej strony.

    Uwagi dotyczące produkcji

    • Wywołanie funkcji wygasa po 10 minutach. Wyniki muszą zostać przesłane z powrotem do rozmowy przed zamknięciem okna, w przeciwnym razie proces się nie powiedzie. Jeśli wywołanie HTTP do Azure ML jest wolne, należy traktować to jako ścisły termin i uwzględnić to w planach.
    • Przejdź na zarządzaną tożsamość poza fazą prototypowania. Klucz przechowywany w zmiennych środowiskowych jest w porządku w warunkach lokalnych, ale stanowi zagrożenie w przypadku systemów bez nadzoru. Nadaj tożsamości uruchamiającej kod narzędzia rolę AzureML Data Scientist lub węższą, dostosowaną rolę skierowaną wyłącznie na ten konkretny punkt końcowy, a nie na całe środowisko pracy.
    • Wprowadzaj nowe modele stopniowo. Uruchom nową wersję obok istniejącej i stopniowo przenoś na nią ruch. To jedyny sposób, aby zauważyć problemy z ponownym szkoleniem modeli, zanim każde rzeczywiste żądanie będzie od nich zależeć.
  • Zostaw ustawione strict=True. Ścisłe schematy zapobiegają wysyłaniu przez model niekompletnych lub błędnie sformatowanych argumentów do get_churn_score, co w przeciwnym razie mogłoby spowodować awarię wewnątrz funkcji, a nie przed jej wywołaniem.
  • Nieaktywne obliczenia kosztują pieniądze. Zarządzany endpoint online, który uruchamia jedną instancję przez cały dzień, jest opłacany nawet przy braku ruchu, czego nie dotyczy wywołanie modelu bezserwerowego. W przypadku niskiego lub nieregularnego obciążenia rozważ opcje skalowania do zera lub endpointy typu batch.
  • Zmiany w danych są niewidoczne. Nic tutaj nie jest ponownie trenowane, gdy dane z rzeczywistego świata odbiegają od rozkładu użytego do szkolenia. Zapisuj dane wejściowe i wyjściowe każdego wywołania get_churn_score w miejscu dostępnym do przeglądania oraz regularnie sprawdzaj dane treningowe, a nie tylko wtedy, gdy prognozy wyraźnie się pogorszą.
  • Wybór odpowiedniej platformy

    Dla większości zadań związanych z sztuczną inteligencją generatywną katalog Foundry jest odpowiednim narzędziem, a zespoły bez własnych danych treningowych prawdopodobnie nigdy nie będą potrzebować niczego więcej. Gdy pytanie zmienia się w prognozę dotyczącą twoich własnych klientów na podstawie twojej historii, lepszym wyborem jest Azure ML. W przeciwnym razie model językowy musi być wykorzystywany do zadań, do których nie został zaprojektowany, albo infrastruktura serwisowa musi być budowana ręcznie. Wszystko sprowadza się do kilku elementów: jedno zadanie treningowe, jeden zarejestrowany model, jeden zarządzany punkt końcowy oraz pojedynczy FunctionTool. Platformy pozostają oddzielne, każda realizując to, w czym jest najlepsza.

    Literatura pokrewna

  • Architektura usług modeli: Dlaczego API jest tylko wejściem — Przewodnik po platformie stojącej za punktem końcowym inferencji: routowanie, skalowanie, wersjonowanie, obserwowalność, bezpieczeństwo oraz wywoływanie modeli z Spring AI lub LangChain4j.