Microsoft용 지속성 확장 에이전트 프레임워크는 에이전트, 다중 에이전트 오케스트레이션 및 Microsoft Agent Framework 워크플로에 지속성 실행을 제공합니다. 이를 사용하여 핵심 에이전트 논리를 변경하지 않고도 에이전트 세션, 검사점 오케스트레이션 및 워크플로 진행률을 유지하며, 실패에서 복구하고, 분산 호스트 간에 작업을 확장할 수 있습니다.
확장은 C# 및 Python 두 호스팅 모델을 지원합니다.
- Azure Functions 프로그래밍 모델을 사용하여 관리되는 서버리스 호스팅을 위한 Azure Functions.
- 사용자 고유의 작업자 프로세스, 서비스, 컨테이너, Kubernetes 환경 또는 기존 앱 인프라에서 지속성 에이전트 및 워크플로를 실행하기 위해 자체 컴퓨팅/자체 호스팅을 제공합니다.
개요
지속성 에이전트는 에이전트 프레임워크 프로그래밍 모델을 지속성 작업 스케줄러와 같은 지속 성 작업 인프라와 결합하여 다음과 같은 에이전트를 만듭니다.
- 요청 및 작업자 실행에서 자동으로 상태 유지
- 대화 컨텍스트를 손실하거나 완료된 작업을 반복하지 않고 실패 후 다시 시작
- 수요에 따라 분산된 상태 비정상 작업자에 걸쳐 크기 조정
- 안정적인 실행 보장을 사용하여 다중 에이전트 워크플로 오케스트레이션
- 그래프 기반 워크플로 모델을 사용하여 빌드된 검사점 에이전트 프레임워크 워크플로
- 대기하는 동안 컴퓨팅 또는 모델 토큰을 사용하지 않고 사용자 입력 또는 외부 이벤트에 대해 일시 중지
- Redis와 같은 신뢰할 수 있는 스트림 브로커로 구성된 경우 안정적으로 응답 스트리밍
- TTL(세션 TTL) 정리 및 대시보드 기반 모니터링을 사용하여 세션 수명 주기 관리
지속성 에이전트를 사용하는 경우
필요할 때 내구성 있는 에이전트를 선택합니다.
- 영구 대화 상태: 에이전트 세션이 프로세스 충돌, 다시 시작 및 스케일 아웃 이벤트를 유지합니다.
- 복잡한 오케스트레이션: 며칠 또는 몇 주 동안 실행할 수 있는 결정적이고 신뢰할 수 있는 워크플로를 사용하여 여러 에이전트 조정
- 이벤트 기반 오케스트레이션: 트리거, 큐, 웹후크, 타이머 또는 기존 애플리케이션 이벤트와 통합
- 자동 대화 상태: 에이전트 대화 기록은 코드에서 명시적 상태 처리를 요구하지 않고 자동으로 관리되고 유지됩니다.
- 구성 에이전트 프레임워크 워크플로: 각 단계를 검사점 및 다시 시작하려면 그래프 기반 Microsoft Agent Framework 워크플로를 지속성 있게 만듭니다.
- 수명이 긴 세션: TTL(세션 TTL) 정리를 사용하여 유휴 세션을 자동으로 제거하는 동안 유용한 대화를 사용할 수 있도록 유지
- 신뢰할 수 있는 실시간 응답: 배달 보장을 통해 실시간 UX가 필요한 애플리케이션에 대한 스트림 토큰 출력
이 호스팅 방법은 작업자 호스트를 배포하거나 관리할 필요 없이 완전히 관리되는 인프라를 제공하는 관리 서비스 기반 에이전트 호스팅(예: Foundry 에이전트 서비스)과 다릅니다. 지속성 에이전트는 지속성 상태 관리와 결합된 코드 우선 배포의 유연성이 필요한 경우에 이상적입니다.
호스팅 모델 선택
| 호스팅 모델 | 필요할 때 선택 |
|---|---|
| Azure Functions | 관리되는 서버리스 호스팅 모델 기본 제공 스케일 아웃 및 스케일 투 0; 트리거 및 바인딩을 Azure Functions. Functions 프로그래밍 모델에서 생성된 HTTP 엔드포인트; MCP 서버 트리거; 및 최소 호스트 인프라 관리 |
| Bring-your-own-compute/ 자체 호스팅 | 호스트 프로세스, 배포 환경, 런타임 수명 주기, 인프라, 네트워킹, 인증 또는 기존 앱 또는 서비스와의 통합에 대한 제어를 강화합니다. 컨테이너, Kubernetes, 장기 실행 작업자, 콘솔 앱, 사용자 지정 서비스 또는 비 함수 호스팅 환경에 이 모델을 사용합니다. |
Azure Functions Flex Consumption 호스팅 계획에서 호스트되는 경우 에이전트는 사용하지 않을 때 수천 개의 인스턴스 또는 0개의 인스턴스로 확장할 수 있으므로 필요한 컴퓨팅에 대해서만 비용을 지불할 수 있습니다. 자체 호스팅 시나리오에서 자체 호스트는 프로세스 수명, 크기 조정, 네트워킹 및 배포를 제어합니다.
시작하기
.NET 프로젝트에서 호스팅 모델에 대한 패키지 집합을 선택합니다.
Azure Functions 호스팅의 경우 Azure Functions 통합 패키지 및 Functions 작업자 패키지를 추가합니다.
dotnet add package Azure.AI.Projects --prerelease
dotnet add package Azure.Identity
dotnet add package Microsoft.Agents.AI.Foundry --prerelease
dotnet add package Microsoft.Agents.AI.Hosting.AzureFunctions --prerelease
비고
이러한 패키지 외에도 프로젝트에서 Microsoft.Azure.Functions.Worker 패키지 버전 2.2.0 이상을 사용하는지 확인합니다.
bring-your-own-compute 호스팅의 경우 기본 지속성 작업 통합 패키지와 호스트에서 사용하는 지속성 작업 스케줄러 작업자/클라이언트 패키지를 추가합니다.
dotnet add package Microsoft.Agents.AI.DurableTask --prerelease
dotnet add package Microsoft.DurableTask.Client.AzureManaged
dotnet add package Microsoft.DurableTask.Worker.AzureManaged
dotnet add package Microsoft.Extensions.Hosting
Python 프로젝트에서 호스팅 모델에 대한 패키지를 선택합니다.
Azure Functions 호스팅의 경우 Azure Functions 통합 패키지를 설치합니다.
pip install azure-identity
pip install agent-framework-azurefunctions --pre
Bring-your-own-compute 호스팅의 경우 지속성 작업 통합 패키지를 설치합니다.
pip install azure-identity
pip install agent-framework-durabletask --pre
Azure Functions 호스팅
지속성 확장을 사용하면 기본 제공 HTTP 엔드포인트 및 오케스트레이션 기반 호출을 사용하여 Azure Functions Microsoft Agent Framework 에이전트를 배포하고 호스트할 수 있습니다. Azure Functions는 자동 크기 조정 및 최소한의 인프라 관리를 통해 이벤트 기반 호출당 종량제 가격 책정을 제공합니다.
Azure Functions 지속성 에이전트를 구성할 때 확장은 에이전트에 대한 HTTP 엔드포인트를 자동으로 만들고 대화 상태를 저장하고, 동시 요청을 처리하고, 다중 에이전트 워크플로를 조정하기 위한 기본 인프라를 관리합니다. 또한 Azure Functions 호스팅 통합은 메시지 보내기, 상태 확인 및 세션 관리를 위한 생성된 REST API와 같은 Functions 관련 편의성과 트리거 붙이기를 작성하지 않고 MCP 서버로 호스트 에이전트에 대한 MCP 서버 트리거와 같은 트리거를 제공합니다.
using System;
using Azure.AI.Projects;
using Azure.Identity;
using Microsoft.Agents.AI;
using Microsoft.Agents.AI.Hosting.AzureFunctions;
using Microsoft.Azure.Functions.Worker.Builder;
using Microsoft.Extensions.Hosting;
var endpoint = Environment.GetEnvironmentVariable("AZURE_OPENAI_ENDPOINT");
var deploymentName = Environment.GetEnvironmentVariable("AZURE_OPENAI_DEPLOYMENT") ?? "gpt-4o-mini";
// Create an AI agent following the standard Microsoft Agent Framework pattern
AIAgent agent = new AIProjectClient(new Uri(endpoint), new DefaultAzureCredential())
.AsAIAgent(
model: deploymentName,
instructions: "You are good at telling jokes.",
name: "Joker");
// Configure the function app to host the agent with durable thread management
// This automatically creates HTTP endpoints and manages state persistence
using IHost app = FunctionsApplication
.CreateBuilder(args)
.ConfigureFunctionsWebApplication()
.ConfigureDurableAgents(options =>
options.AddAIAgent(agent)
)
.Build();
app.Run();
Warning
DefaultAzureCredential 은 개발에 편리하지만 프로덕션 환경에서 신중하게 고려해야 합니다. 프로덕션 환경에서는 특정 자격 증명(예: ManagedIdentityCredential)을 사용하여 대기 시간 문제, 의도하지 않은 자격 증명 검색 및 대체 메커니즘의 잠재적인 보안 위험을 방지하는 것이 좋습니다.
import os
from agent_framework.azure import AgentFunctionApp
from agent_framework.openai import OpenAIChatCompletionClient
from azure.identity import DefaultAzureCredential
endpoint = os.getenv("AZURE_OPENAI_ENDPOINT")
deployment_name = os.getenv("AZURE_OPENAI_CHAT_COMPLETION_MODEL", "gpt-4o-mini")
api_version = os.getenv("AZURE_OPENAI_API_VERSION")
# Create an AI agent following the standard Microsoft Agent Framework pattern
agent = OpenAIChatCompletionClient(
azure_endpoint=endpoint,
model=deployment_name,
api_version=api_version,
credential=DefaultAzureCredential()
).as_agent(
instructions="You are good at telling jokes.",
name="Joker"
)
# Configure the function app to host the agent with durable thread management
# This automatically creates HTTP endpoints and manages state persistence
app = AgentFunctionApp(agents=[agent])
Bring-your-own-compute/ 자체 호스팅 호스팅
Azure Functions 프로그래밍 모델을 사용하지 않고 지속성 확장 기능을 원하는 경우 bring-your-own-compute 호스팅을 사용합니다. 이 모델에서 프로세스는 지속성 작업 작업자를 시작하고, 지속성 에이전트 또는 워크플로를 등록하고, 지속성 작업 스케줄러 백 엔드에 연결합니다. 클라이언트 코드는 동일한 프로세스 또는 별도의 서비스에서 실행할 수 있습니다.
자체 호스팅 작업자는 Azure Functions 호스팅과 동일한 핵심 지속성 확장 기능을 사용합니다. 검사점 및 재개, 결정적 에이전트 오케스트레이션, 지속성 에이전트 프레임워크 워크플로, 휴먼 인더 루프 대기, 신뢰할 수 있는 스트리밍, 유휴 세션 정리, 대시보드 표시 유형 및 상태 비저장 작업자 인스턴스 간에 분산 실행. 호스트는 자체 API, 수명 주기 관리, 네트워킹, 인증 및 배포 모델을 노출해야 합니다.
기본 지속성 작업 통합 패키지를 사용하여 호스트를 구성합니다. 지속성 에이전트에는 ConfigureDurableAgents, 그래프 기반 Microsoft Agent Framework 워크플로에는 ConfigureDurableWorkflows 사용합니다.
string connectionString = Environment.GetEnvironmentVariable("DURABLE_TASK_SCHEDULER_CONNECTION_STRING")
?? "Endpoint=http://localhost:8080;TaskHub=default;Authentication=None";
IHost host = Host.CreateDefaultBuilder(args)
.ConfigureServices(services =>
{
services.ConfigureDurableAgents(
options => options.AddAIAgent(agent),
workerBuilder: builder => builder.UseDurableTaskScheduler(connectionString),
clientBuilder: builder => builder.UseDurableTaskScheduler(connectionString));
})
.Build();
await host.StartAsync();
실행 가능한 자체 호스팅 예제는 .NET 지속성 에이전트 콘솔 샘플 및 .NET 지속성 워크플로 콘솔 샘플을 참조하세요.
지속성 작업 통합 패키지를 사용하여 에이전트를 등록하고 요청을 수신 대기하는 작업자 프로세스를 실행합니다. 클라이언트 코드는 다른 프로세스에서 동일한 지속성 작업 스케줄러 작업 허브에 연결할 수 있습니다.
from agent_framework.azure import DurableAIAgentWorker
from durabletask.azuremanaged.worker import DurableTaskSchedulerWorker
worker = DurableTaskSchedulerWorker(
host_address="http://localhost:8080",
secure_channel=False,
taskhub="default",
)
agent_worker = DurableAIAgentWorker(worker)
agent_worker.add_agent(agent)
worker.start()
단일 에이전트 호스팅, 다중 에이전트 라우팅, 신뢰할 수 있는 스트리밍, 오케스트레이션 체인, 동시성, 조건부 및 휴먼 인 더 루프 패턴을 포함한 작업자 클라이언트 예제는 Python 지속성 작업 샘플을 참조하세요.
지속성 에이전트 프레임워크 워크플로
내구성은 지속성 오케스트레이션으로 제한되지 않습니다. 그래프 기반 워크플로 모델을 사용하여 빌드된 Microsoft 에이전트 프레임워크 워크플로도 지속성으로 만들 수 있습니다. 지속성 확장은 워크플로 실행을 검사하므로 프로세스가 다시 시작되거나 실패하면 완료된 실행기 및 에이전트 단계가 반복되지 않습니다.
코드 기반 분기, 타이머, 활동 및 외부 이벤트와 명령적 조정을 원하는 경우 지속성 오케스트레이션을 사용합니다. 형식화된 라우팅, 팬아웃/팬인, 조건부 에지, 워크플로 이벤트, 공유 상태, 하위 워크플로 또는 휴먼 인 더 루프 요청 포트를 사용하여 실행기 및 에이전트의 선언적 그래프를 원하는 경우 지속성 에이전트 프레임워크 워크플로를 사용합니다.
비고
지속성 에이전트 프레임워크 워크플로는 표준 워크플로의 검사점 스토리지와 다릅니다. 검사점 스토리지는 에이전트 프레임워크 런타임에서 워크플로 실행을 다시 시작하는 데 도움이 됩니다. 지속성 확장은 지속성 작업 인프라에서 워크플로를 실행하므로 분산된 지속성 작업자 간에 워크플로 진행률이 검사점이 지정되고 복구됩니다. 표준 워크플로 검사점은 검사점 및 다시 설정을 참조하세요.
자체 호스팅 앱에 대한 ConfigureDurableWorkflows 또는 Azure Functions 호스팅을 위해 Functions 앱 작성기에서 ConfigureDurableWorkflows 그래프 기반 워크플로를 등록합니다.
.NET 지속성 워크플로 Azure Functions 샘플 및 .NET 지속성 워크플로 콘솔 샘플 참조하세요.
지속성 워크플로 샘플은 공유 상태, 공유 상태 없음, 병렬 워크플로 실행 및 휴먼 인 더 루프 워크플로를 포함하여 Azure Functions 호스팅에 사용할 수 있습니다.
지속성 에이전트, 오케스트레이션, MCP 서버 및 워크플로 예제는 Python Azure Functions 샘플을 참조하세요.
샘플
| Language | 호스팅 모델 | 샘플 |
|---|---|---|
| C# | Azure Functions | .NET 지속성 에이전트 - Azure Functions, .NET 지속성 워크플로 - Azure Functions |
| C# | Bring-your-own-compute/ 자체 호스팅 | .NET 지속성 에이전트 - 콘솔 앱, .NET 지속성 워크플로 - 콘솔 앱 |
| 파이썬 | Azure Functions | Python Azure Functions 샘플 |
| 파이썬 | Bring-your-own-compute/ 자체 호스팅 | Python 지속성 작업 샘플 |
대화 기록이 있는 상태 저장 에이전트 스레드
에이전트는 여러 상호 작용에서 유지되는 영구 스레드를 유지 관리합니다. 각 스레드는 고유한 스레드 ID로 식별되며 지속성 작업 스케줄러와 같은 지속성 작업 인프라에서 관리하는 지속성 스토리지에 전체 대화 기록을 저장합니다.
이 패턴을 사용하면 프로세스 충돌 및 다시 시작을 통해 에이전트 상태가 유지되는 대화형 연속성을 통해 사용자 스레드 간에 전체 대화 기록을 유지할 수 있습니다. 지속성 스토리지를 사용하면 호스트 프로세스가 다시 시작되거나 다른 작업자 인스턴스에서 작업이 다시 시작되더라도 대화가 중단된 위치에서 원활하게 계속됩니다.
활성 사용 중에 지속성 연속성이 필요하지만 유휴 대화를 자동으로 정리해야 하는 워크로드에는 TTL(세션 TTL) 정리를 사용합니다. TTL 기반 정리는 활성 세션 상태를 유지하면서 사용되지 않는 세션 및 대화 기록이 무기한 누적되는 것을 방지합니다.
다음 Azure Functions 예제에서는 동일한 스레드에 대한 여러 HTTP 요청을 보여 줍니다. 대화 컨텍스트가 유지되는 방식을 보여 줍니다. 자체 호스팅 앱에서 고유한 프로세스 또는 서비스의 지속성 작업 클라이언트 API를 사용합니다.
# First interaction - start a new thread
curl -X POST https://your-function-app.azurewebsites.net/api/agents/Joker/run \
-H "Content-Type: text/plain" \
-d "Tell me a joke about pirates"
# Response includes thread ID in x-ms-thread-id header and joke as plain text
# HTTP/1.1 200 OK
# Content-Type: text/plain
# x-ms-thread-id: @dafx-joker@263fa373-fa01-4705-abf2-5a114c2bb87d
#
# Why don't pirates shower before they walk the plank? Because they'll just wash up on shore later!
# Second interaction - continue the same thread with context
curl -X POST "https://your-function-app.azurewebsites.net/api/agents/Joker/run?thread_id=@dafx-joker@263fa373-fa01-4705-abf2-5a114c2bb87d" \
-H "Content-Type: text/plain" \
-d "Tell me another one about the same topic"
# Agent remembers the pirate context from the first message and responds with plain text
# What's a pirate's favorite letter? You'd think it's R, but it's actually the C!
에이전트 상태는 지속성 스토리지에서 유지 관리되므로 여러 인스턴스에서 분산 실행을 사용할 수 있습니다. 모든 인스턴스는 중단 또는 실패 후 에이전트의 실행을 다시 시작하여 연속 작업을 보장할 수 있습니다.
신뢰할 수 있는 스트리밍
지속성 확장은 지속성 배달 보장을 사용하여 실시간 토큰 배달이 필요한 애플리케이션에 대해 신뢰할 수 있는 스트리밍을 지원합니다. 스트리밍은 두 호스팅 모델 모두에서 핵심 확장과 함께 사용할 수 있지만 분산 호스트에는 Redis와 같은 신뢰할 수 있는 스트림 브로커가 필요하므로 프로세스 다시 시작, 다시 연결 또는 작업자 변경에 걸쳐 토큰 스트림을 일관되게 전달할 수 있습니다.
사용자 환경이 증분 응답에 의존하지만 워크로드에 지속성 실행 의미 체계가 여전히 필요한 경우 신뢰할 수 있는 스트리밍을 사용합니다. 실행 가능한 예제는 신뢰할 수 있는 스트리밍 패턴을 포함하는 Python 지속성 작업 샘플 참조하세요.
결정적 다중 에이전트 오케스트레이션
지속성 확장은 지속성 작업 오케스트레이션을 사용하여 여러 에이전트를 조정하는 결정적 워크플로 빌드를 지원합니다. Azure Functions Durable Functions 오케스트레이션을 사용합니다. 사용자 고유의 컴퓨팅 호스트에서 구성한 지속성 작업 작업자 및 클라이언트를 통해 실행됩니다.
오케스트레이션 은 신뢰할 수 있는 방식으로 여러 작업(예: 에이전트 호출, 외부 API 호출 또는 타이머)을 조정하는 코드 기반 워크플로입니다. 결정적 의미 는 오케스트레이션 코드가 실패 후 재생될 때 동일한 방식으로 실행되어 워크플로를 안정적이고 디버깅할 수 있게 함을 의미합니다. 오케스트레이션의 기록을 재생하면 각 단계에서 정확히 어떤 일이 일어났는지 확인할 수 있습니다.
오케스트레이션은 에이전트 호출 간의 오류를 견뎌내며 안정적으로 실행되고, 예측 가능하고 반복 가능한 프로세스를 제공합니다. 따라서 실행 순서 및 내결함성이 보장되어야 하는 복잡한 다중 에이전트 시나리오에 적합합니다.
순차 오케스트레이션
순차적 다중 에이전트 패턴에서 특수 에이전트는 특정 순서로 실행되며, 여기서 각 에이전트의 출력은 다음 에이전트의 실행에 영향을 줄 수 있습니다. 이 패턴은 에이전트 응답에 따라 조건부 논리 및 분기를 지원합니다.
오케스트레이션에서 에이전트를 사용할 때는 context.GetAgent() API를 사용하여 DurableAIAgent 인스턴스를 얻어야 합니다. 이 인스턴스는 등록된 에이전트 중 하나를 래핑하는 표준 AIAgent 타입의 특수 하위 클래스입니다. 래퍼는 DurableAIAgent 에이전트 호출이 내구성 오케스트레이션 프레임워크에 의해 올바르게 추적되고 검사점으로 기록되도록 합니다.
using Microsoft.Azure.Functions.Worker;
using Microsoft.DurableTask;
using Microsoft.Agents.AI.DurableTask;
[Function(nameof(SpamDetectionOrchestration))]
public static async Task<string> SpamDetectionOrchestration(
[OrchestrationTrigger] TaskOrchestrationContext context)
{
Email email = context.GetInput<Email>();
// Check if the email is spam
DurableAIAgent spamDetectionAgent = context.GetAgent("SpamDetectionAgent");
AgentSession spamSession = await spamDetectionAgent.CreateSessionAsync();
AgentResponse<DetectionResult> spamDetectionResponse = await spamDetectionAgent.RunAsync<DetectionResult>(
message: $"Analyze this email for spam: {email.EmailContent}",
session: spamSession);
DetectionResult result = spamDetectionResponse.Result;
if (result.IsSpam)
{
return await context.CallActivityAsync<string>(nameof(HandleSpamEmail), result.Reason);
}
// Generate response for legitimate email
DurableAIAgent emailAssistantAgent = context.GetAgent("EmailAssistantAgent");
AgentSession emailSession = await emailAssistantAgent.CreateSessionAsync();
AgentResponse<EmailResponse> emailAssistantResponse = await emailAssistantAgent.RunAsync<EmailResponse>(
message: $"Draft a professional response to: {email.EmailContent}",
session: emailSession);
return await context.CallActivityAsync<string>(nameof(SendEmail), emailAssistantResponse.Result.Response);
}
오케스트레이션에서 에이전트를 사용할 때는, 등록된 에이전트 중 하나를 감싸는 특수 래퍼인 지속 가능한 에이전트 인스턴스를 얻기 위해 app.get_agent() 메서드를 반드시 사용해야 합니다. 지속성 에이전트 래퍼는 에이전트 호출이 지속성 오케스트레이션 프레임워크에 의해 올바르게 추적되고 검사점이 설정되도록 보장합니다.
import azure.durable_functions as df
from typing import cast
from agent_framework.azure import AgentFunctionApp
from pydantic import BaseModel
class SpamDetectionResult(BaseModel):
is_spam: bool
reason: str
class EmailResponse(BaseModel):
response: str
app = AgentFunctionApp(agents=[spam_detection_agent, email_assistant_agent])
@app.orchestration_trigger(context_name="context")
def spam_detection_orchestration(context: df.DurableOrchestrationContext):
email = context.get_input()
# Check if the email is spam
spam_agent = app.get_agent(context, "SpamDetectionAgent")
spam_thread = spam_agent.create_session()
spam_result_raw = yield spam_agent.run(
messages=f"Analyze this email for spam: {email['content']}",
session=spam_thread,
response_format=SpamDetectionResult
)
spam_result = cast(SpamDetectionResult, spam_result_raw.get("structured_response"))
if spam_result.is_spam:
result = yield context.call_activity("handle_spam_email", spam_result.reason)
return result
# Generate response for legitimate email
email_agent = app.get_agent(context, "EmailAssistantAgent")
email_thread = email_agent.create_session()
email_response_raw = yield email_agent.run(
messages=f"Draft a professional response to: {email['content']}",
session=email_thread,
response_format=EmailResponse
)
email_response = cast(EmailResponse, email_response_raw.get("structured_response"))
result = yield context.call_activity("send_email", email_response.response)
return result
오케스트레이션은 여러 에이전트 간에 작업을 조정하며, 에이전트 호출 간의 실패에 잘 대응합니다. 오케스트레이션 컨텍스트는 오케스트레이션 내에서 호스트된 에이전트를 검색하고 상호 작용하는 메서드를 제공합니다.
병렬 오케스트레이션
병렬 다중 에이전트 패턴에서는 여러 에이전트를 동시에 실행한 다음 결과를 집계합니다. 이 패턴은 다양한 관점을 수집하거나 독립적인 하위 작업을 동시에 처리하는 데 유용합니다.
using Microsoft.Azure.Functions.Worker;
using Microsoft.DurableTask;
using Microsoft.Agents.AI.DurableTask;
[Function(nameof(ResearchOrchestration))]
public static async Task<string> ResearchOrchestration(
[OrchestrationTrigger] TaskOrchestrationContext context)
{
string topic = context.GetInput<string>();
// Execute multiple research agents in parallel
DurableAIAgent technicalAgent = context.GetAgent("TechnicalResearchAgent");
DurableAIAgent marketAgent = context.GetAgent("MarketResearchAgent");
DurableAIAgent competitorAgent = context.GetAgent("CompetitorResearchAgent");
// Start all agent runs concurrently
Task<AgentResponse<TextResponse>> technicalTask =
technicalAgent.RunAsync<TextResponse>($"Research technical aspects of {topic}");
Task<AgentResponse<TextResponse>> marketTask =
marketAgent.RunAsync<TextResponse>($"Research market trends for {topic}");
Task<AgentResponse<TextResponse>> competitorTask =
competitorAgent.RunAsync<TextResponse>($"Research competitors in {topic}");
// Wait for all tasks to complete
await Task.WhenAll(technicalTask, marketTask, competitorTask);
// Aggregate results
string allResearch = string.Join("\n\n",
technicalTask.Result.Result.Text,
marketTask.Result.Result.Text,
competitorTask.Result.Result.Text);
DurableAIAgent summaryAgent = context.GetAgent("SummaryAgent");
AgentResponse<TextResponse> summaryResponse =
await summaryAgent.RunAsync<TextResponse>($"Summarize this research:\n{allResearch}");
return summaryResponse.Result.Text;
}
import azure.durable_functions as df
from agent_framework.azure import AgentFunctionApp
app = AgentFunctionApp(agents=[technical_agent, market_agent, competitor_agent, summary_agent])
@app.orchestration_trigger(context_name="context")
def research_orchestration(context: df.DurableOrchestrationContext):
topic = context.get_input()
# Execute multiple research agents in parallel
technical_agent = app.get_agent(context, "TechnicalResearchAgent")
market_agent = app.get_agent(context, "MarketResearchAgent")
competitor_agent = app.get_agent(context, "CompetitorResearchAgent")
technical_task = technical_agent.run(messages=f"Research technical aspects of {topic}")
market_task = market_agent.run(messages=f"Research market trends for {topic}")
competitor_task = competitor_agent.run(messages=f"Research competitors in {topic}")
# Wait for all tasks to complete
results = yield context.task_all([technical_task, market_task, competitor_task])
# Aggregate results
all_research = "\n\n".join([r.get('response', '') for r in results])
summary_agent = app.get_agent(context, "SummaryAgent")
summary = yield summary_agent.run(messages=f"Summarize this research:\n{all_research}")
return summary.get('response', '')
병렬 실행은 작업 목록을 사용하여 추적됩니다. 자동 검사점은 집계 중에 오류가 발생하는 경우 완료된 에이전트 실행이 반복되거나 손실되지 않도록 합니다.
휴먼 인 더 루프 오케스트레이션
결정적 에이전트 오케스트레이션은 컴퓨팅 리소스를 사용하지 않고도 사용자 입력, 승인 또는 검토를 위해 일시 중지할 수 있습니다. 지속성 실행을 사용하면 오케스트레이션이 사람의 응답을 기다리는 동안 며칠 또는 몇 주 동안 기다릴 수 있습니다. 서버리스 호스팅과 결합하면 대기 기간 동안 모든 컴퓨팅 리소스가 중단되어 사용자가 입력을 제공 할 때까지 컴퓨팅 비용이 제거됩니다.
using Microsoft.Azure.Functions.Worker;
using Microsoft.DurableTask;
using Microsoft.Agents.AI.DurableTask;
[Function(nameof(ContentApprovalWorkflow))]
public static async Task<string> ContentApprovalWorkflow(
[OrchestrationTrigger] TaskOrchestrationContext context)
{
string topic = context.GetInput<string>();
// Generate content using an agent
DurableAIAgent contentAgent = context.GetAgent("ContentGenerationAgent");
AgentResponse<GeneratedContent> contentResponse =
await contentAgent.RunAsync<GeneratedContent>($"Write an article about {topic}");
GeneratedContent draftContent = contentResponse.Result;
// Send for human review
await context.CallActivityAsync(nameof(NotifyReviewer), draftContent);
// Wait for approval with timeout
HumanApprovalResponse approvalResponse;
try
{
approvalResponse = await context.WaitForExternalEvent<HumanApprovalResponse>(
eventName: "ApprovalDecision",
timeout: TimeSpan.FromHours(24));
}
catch (OperationCanceledException)
{
// Timeout occurred - escalate for review
return await context.CallActivityAsync<string>(nameof(EscalateForReview), draftContent);
}
if (approvalResponse.Approved)
{
return await context.CallActivityAsync<string>(nameof(PublishContent), draftContent);
}
return "Content rejected";
}
import azure.durable_functions as df
from datetime import timedelta
from agent_framework.azure import AgentFunctionApp
app = AgentFunctionApp(agents=[content_agent])
@app.orchestration_trigger(context_name="context")
def content_approval_workflow(context: df.DurableOrchestrationContext):
topic = context.get_input()
# Generate content using an agent
content_agent = app.get_agent(context, "ContentGenerationAgent")
draft_content = yield content_agent.run(
messages=f"Write an article about {topic}"
)
# Send for human review
yield context.call_activity("notify_reviewer", draft_content)
# Wait for approval with timeout
approval_task = context.wait_for_external_event("ApprovalDecision")
timeout_task = context.create_timer(
context.current_utc_datetime + timedelta(hours=24)
)
winner = yield context.task_any([approval_task, timeout_task])
if winner == approval_task:
timeout_task.cancel()
approval_data = approval_task.result
if approval_data.get("approved"):
result = yield context.call_activity("publish_content", draft_content)
return result
return "Content rejected"
# Timeout occurred - escalate for review
result = yield context.call_activity("escalate_for_review", draft_content)
return result
결정적 에이전트 오케스트레이션은 외부 이벤트를 기다릴 수 있으며, 사용자 피드백을 기다리는 동안 상태를 지속적으로 유지하며 실패, 다시 시작 및 연장된 대기 기간을 유지할 수 있습니다. 사용자 응답이 도착하면 오케스트레이션은 전체 대화 컨텍스트 및 실행 상태를 그대로 유지하여 자동으로 다시 시작됩니다.
사용자 입력 제공
대기 중인 오케스트레이션에 승인 또는 입력을 보내려면 지속성 작업 클라이언트 SDK 또는 Azure Functions 지속성 확장 엔드포인트를 사용하여 오케스트레이션 인스턴스에 외부 이벤트를 발생합니다. 예를 들어 검토자는 다음을 호출하는 웹 양식을 통해 콘텐츠를 승인할 수 있습니다.
await client.RaiseEventAsync(instanceId, "ApprovalDecision", new HumanApprovalResponse
{
Approved = true,
Feedback = "Looks great!"
});
approval_data = {
"approved": True,
"feedback": "Looks great!"
}
await client.raise_event(instance_id, "ApprovalDecision", approval_data)
비용 효율성
지속성 에이전트를 사용하는 휴먼 인 더 루프 워크플로는 Azure Functions Flex Consumption 계획에서 호스트될 때 매우 비용 효율적입니다. 승인을 위해 24시간을 기다리는 워크플로의 경우 24시간 대기 시간이 아닌 몇 초의 실행 시간(콘텐츠를 생성하고 알림을 보내고 응답을 처리하는 시간)만 지불합니다. 대기 기간 동안에는 컴퓨팅 리소스가 소비되지 않습니다.
지속성 작업 스케줄러를 사용하여 관찰 가능
DTS( 지속성 작업 스케줄러 )는 지속성 에이전트에 권장되는 지속성 백 엔드로, UI 대시보드를 통해 최상의 성능, 완전 관리형 인프라 및 기본 제공 가시성을 제공합니다. Azure Functions 앱은 다른 스토리지 백 엔드(예: Azure Storage)를 사용할 수 있지만 DTS는 특히 지속성 워크로드에 최적화되어 있으며 뛰어난 성능 및 모니터링 기능을 제공합니다. 또한 자체 호스팅 작업자는 지속형 일정, 상태 및 대시보드 표시를 위해 DTS를 사용합니다.
에이전트 세션 인사이트
- 대화 기록: 언제든지 모든 메시지, 도구 호출 및 대화 컨텍스트를 포함하여 각 에이전트 세션에 대한 전체 채팅 기록 보기
- 작업 타이밍: 특정 작업 및 에이전트 상호 작용을 완료하는 데 걸리는 시간 모니터링
오케스트레이션 인사이트
- 다중 에이전트 시각화: 병렬 실행 및 조건부 분기의 시각적 표현으로 여러 특수 에이전트를 호출할 때 실행 흐름을 확인합니다.
- 실행 기록: 자세한 실행 로그 액세스
- 실시간 모니터링: 배포 전체에서 활성 오케스트레이션, 큐에 대기 중인 작업 항목 및 에이전트 상태 추적
- 성능 메트릭: 에이전트 응답 시간, 토큰 사용량 및 오케스트레이션 기간 모니터링
디버깅 기능
- 구조적 에이전트 출력 및 도구 호출 결과 보기
- 추적 도구 호출 및 해당 결과
- 휴먼 인 더 루프 시나리오에 대한 외부 이벤트 처리 모니터링
대시보드를 사용하면 에이전트가 수행하는 작업을 정확하게 이해하고, 문제를 신속하게 진단하고, 실제 실행 데이터를 기반으로 성능을 최적화할 수 있습니다.
자습서: Azure Functions 사용하여 지속성 에이전트 만들기 및 실행
이 자습서에서는 지속성 확장에 대한 Azure Functions 호스팅 모델을 사용하여 지속성 AI 에이전트를 만들고 실행하는 방법을 보여줍니다. 기본 제공 HTTP 엔드포인트를 사용하여 상태 저장 에이전트를 호스트하는 Azure Functions 앱을 빌드하고 지속성 작업 스케줄러 대시보드를 사용하여 모니터링하는 방법을 알아봅니다. 자체 호스팅 에이전트는 샘플을 참조하세요.
사전 요구 사항
시작하기 전에 다음 필수 구성 요소가 있는지 확인합니다.
- .NET 9.0 SDK 이상
- Azure Functions Core Tools v4.x
- Azure 개발자 명령줄 도구(azd)
- Azure CLI 설치 및 인증
- Docker Desktop 설치 및 실행(Azurite 및 지속성 작업 스케줄러 에뮬레이터를 사용한 로컬 개발용)
- 리소스를 만들 수 있는 권한이 있는 Azure 구독
비고
Microsoft 에이전트 프레임워크는 적극적으로 지원되는 모든 .NET 버전에서 지원됩니다. 이 샘플에서는 .NET 9 SDK 이상 버전을 사용하는 것이 좋습니다.
- Python 3.10 이상
- Azure Functions Core Tools v4.x
- Azure 개발자 명령줄 도구(azd)
- Azure CLI 설치 및 인증
- Docker Desktop 설치 및 실행(Azurite 및 지속성 작업 스케줄러 에뮬레이터를 사용한 로컬 개발용)
- 리소스를 만들 수 있는 권한이 있는 Azure 구독
빠른 시작 프로젝트 다운로드
Azure Developer CLI를 사용하여 지속성 에이전트 빠른 시작 템플릿에서 새 프로젝트를 초기화합니다.
프로젝트에 대한 새 디렉터리를 만들고 프로젝트로 이동합니다.
mkdir MyDurableAgent cd MyDurableAgent
템플릿에서 프로젝트를 초기화합니다.
azd init --template durable-agents-quickstart-dotnet환경 이름을 묻는 메시지가 표시되면 다음과 같은
my-durable-agent이름을 입력합니다.
Azure Functions 구성, 에이전트 코드 및 인프라를 코드 템플릿으로 포함하여 필요한 모든 파일이 포함된 빠른 시작 프로젝트를 다운로드합니다.
프로젝트에 대한 새 디렉터리를 만들고 프로젝트로 이동합니다.
mkdir MyDurableAgent cd MyDurableAgent
템플릿에서 프로젝트를 초기화합니다.
azd init --template durable-agents-quickstart-python환경 이름을 묻는 메시지가 표시되면 다음과 같은
my-durable-agent이름을 입력합니다.가상 환경을 만들고 활성화합니다.
uv venv .venv source .venv/bin/activate
비고
python3 -m venv .venv 작동하지만 알려진 ensurepip 문제로 인해 Microsoft Store Python Windows 무기한 중단 될 수 있습니다. 이 문제를 방지하는 데 사용합니다 uv venv .venv .
필요한 패키지를 설치합니다.
python -m pip install -r requirements.txt
Azure Functions 구성, 에이전트 코드 및 인프라를 코드 템플릿으로 포함하여 필요한 모든 파일이 포함된 빠른 시작 프로젝트를 다운로드합니다. 또한 필요한 종속성을 사용하여 가상 환경을 준비합니다.
Azure 리소스 프로비전
Azure Developer CLI를 사용하여 지속성 에이전트에 필요한 Azure 리소스를 만듭니다.
인프라 구축하기
azd provision이 명령은 다음을 만듭니다.
- gpt-4o-mini 배포를 사용하는 Azure OpenAI 서비스
- Flex Consumption 호스팅 계획이 있는 Azure Functions 앱
- Azure Functions 런타임 및 지속성 스토리지에 대한 Azure Storage 계정
- 에이전트 상태를 관리하기 위한 지속성 작업 스케줄러 인스턴스(소비 계획)
- 필요한 네트워킹 및 ID 구성
메시지가 표시되면 Azure 구독을 선택하고 리소스의 위치를 선택합니다.
프로비전 프로세스는 몇 분 정도 걸립니다. 완료되면 azd는 생성된 리소스 정보를 사용자 환경에 저장합니다.
에이전트 코드 검토
이제 지속성 에이전트를 정의하는 코드를 살펴보겠습니다.
에이전트 구성을 보려면 다음을 엽니다 Program.cs .
using Azure.AI.Projects;
using Azure.Identity;
using Microsoft.Agents.AI;
using Microsoft.Agents.AI.Hosting.AzureFunctions;
using Microsoft.Azure.Functions.Worker.Builder;
using Microsoft.Extensions.Hosting;
var endpoint = Environment.GetEnvironmentVariable("AZURE_OPENAI_ENDPOINT")
?? throw new InvalidOperationException("AZURE_OPENAI_ENDPOINT environment variable is not set");
var deploymentName = Environment.GetEnvironmentVariable("AZURE_OPENAI_DEPLOYMENT") ?? "gpt-4o-mini";
// Create an AI agent following the standard Microsoft Agent Framework pattern
AIAgent agent = new AIProjectClient(new Uri(endpoint), new DefaultAzureCredential())
.AsAIAgent(
model: deploymentName,
instructions: "You are a helpful assistant that can answer questions and provide information.",
name: "MyDurableAgent");
using IHost app = FunctionsApplication
.CreateBuilder(args)
.ConfigureFunctionsWebApplication()
.ConfigureDurableAgents(options => options.AddAIAgent(agent))
.Build();
app.Run();
이 코드:
- 환경 변수에서 Azure OpenAI 구성을 검색합니다.
- Azure 자격 증명을 사용하여 Azure OpenAI 클라이언트를 만듭니다.
- 지침 및 이름을 사용하여 AI 에이전트를 만듭니다.
- 지속성 스레드 관리를 사용하여 에이전트를 호스트하도록 Azure Functions 앱을 구성합니다.
에이전트 구성을 보려면 다음을 엽니다 function_app.py .
import os
from agent_framework.azure import AgentFunctionApp
from agent_framework.openai import OpenAIChatCompletionClient
from azure.identity import DefaultAzureCredential
endpoint = os.getenv("AZURE_OPENAI_ENDPOINT")
if not endpoint:
raise ValueError("AZURE_OPENAI_ENDPOINT is not set.")
deployment_name = os.getenv("AZURE_OPENAI_CHAT_COMPLETION_MODEL", "gpt-4o-mini")
api_version = os.getenv("AZURE_OPENAI_API_VERSION")
# Create an AI agent following the standard Microsoft Agent Framework pattern
agent = OpenAIChatCompletionClient(
azure_endpoint=endpoint,
model=deployment_name,
api_version=api_version,
credential=DefaultAzureCredential()
).as_agent(
instructions="You are a helpful assistant that can answer questions and provide information.",
name="MyDurableAgent"
)
# Configure the function app to host the agent with durable thread management
app = AgentFunctionApp(agents=[agent])
이 코드:
- 환경 변수에서 Azure OpenAI 구성을 검색합니다.
- Azure 자격 증명을 사용하여 Azure OpenAI 클라이언트를 만듭니다.
- 지침 및 이름을 사용하여 AI 에이전트를 만듭니다.
- 지속성 스레드 관리를 사용하여 에이전트를 호스트하도록 Azure Functions 앱을 구성합니다.
이제 에이전트가 Azure Functions에서 호스트될 준비가 되었습니다. 지속성 작업 확장은 에이전트와 상호 작용하기 위한 HTTP 엔드포인트를 자동으로 만들고 여러 요청에서 대화 상태를 관리합니다.
로컬 설정 구성
local.settings.json 프로젝트에 포함된 샘플 파일을 기반으로 로컬 개발용 파일을 만듭니다.
샘플 설정 파일을 복사합니다.
cp local.settings.sample.json local.settings.json
프로비전된 리소스에서 Azure OpenAI 엔드포인트를 가져옵니다.
azd env get-value AZURE_OPENAI_ENDPOINTlocal.settings.json를 열고<your-resource-name>값의AZURE_OPENAI_ENDPOINT를 이전 명령의 엔드포인트로 바꿉니다.
당신의 local.settings.json는 이렇게 보여야 합니다.
{
"IsEncrypted": false,
"Values": {
// ... other settings ...
"AZURE_OPENAI_ENDPOINT": "https://your-openai-resource.openai.azure.com",
"AZURE_OPENAI_DEPLOYMENT": "gpt-4o-mini",
"TASKHUB_NAME": "default"
}
}
비고
이 local.settings.json 파일은 로컬 개발에만 사용되며 Azure에 배포되지 않습니다. 프로덕션 배포의 경우 이러한 설정은 인프라 템플릿에 의해 Azure Functions 앱에서 자동으로 구성됩니다.
로컬 개발 종속성 시작하기
지속성 에이전트를 로컬로 실행하려면 다음 두 가지 서비스를 시작해야 합니다.
- Azurite: Azure Storage 서비스(트리거 및 내부 상태를 관리하기 위해 Azure Functions에서 사용)를 에뮬레이트합니다.
- DTS(지속성 작업 스케줄러) 에뮬레이터: 에이전트에 대한 지속성 상태(대화 기록, 오케스트레이션 상태) 및 예약을 관리합니다.
Azurite 시작
Azurite는 Azure Storage 서비스를 로컬로 에뮬레이트합니다. Azure Functions는 내부 상태를 관리하는 데 사용합니다. 이 작업은 새 터미널 창에서 실행하고 지속성 에이전트를 개발하고 테스트하는 동안 계속 실행해야 합니다.
새 터미널 창을 열고 Azurite Docker 이미지를 끌어옵니다.
docker pull mcr.microsoft.com/azure-storage/azurite터미널 창에서 Azurite를 시작합니다.
docker run -p 10000:10000 -p 10001:10001 -p 10002:10002 mcr.microsoft.com/azure-storage/azuriteAzurite는 Blob(10000), 큐(10001) 및 테이블(10002) 서비스의 기본 포트에서 시작하고 수신 대기합니다.
지속성 에이전트를 개발하고 테스트하는 동안 이 터미널 창을 열어 두세요.
Tip
대체 설치 방법을 포함하여 Azurite에 대한 자세한 내용은 로컬 Azure Storage 개발에 Azurite 에뮬레이터 사용을 참조하세요.
지속성 작업 스케줄러 에뮬레이터 시작
DTS 에뮬레이터는 에이전트 상태 및 오케스트레이션을 관리하기 위한 지속성 백 엔드를 제공합니다. 대화 기록을 저장하고 에이전트의 상태가 다시 시작 시 계속 유지되도록 합니다. 지속 가능한 오케스트레이션과 에이전트를 트리거합니다. 이 작업은 별도의 새 터미널 창에서 실행하고 지속성 에이전트를 개발하고 테스트하는 동안 계속 실행해야 합니다.
다른 새 터미널 창을 열고 DTS 에뮬레이터 Docker 이미지를 끌어옵니다.
docker pull mcr.microsoft.com/dts/dts-emulator:latestDTS 에뮬레이터를 실행합니다.
docker run -p 8080:8080 -p 8082:8082 mcr.microsoft.com/dts/dts-emulator:latest이 명령은 에뮬레이터를 시작하고 다음을 노출합니다.
- 포트 8080: 지속성 작업 스케줄러의 gRPC 엔드포인트(Functions 앱에서 사용)
- 포트 8082: 관리 대시보드
http://localhost:8082에서 대시보드를 사용할 수 있습니다.
지속성 에이전트를 개발하고 테스트하는 동안 이 터미널 창을 열어 두세요.
Tip
여러 작업 허브를 구성하고 대시보드에 액세스하는 방법을 포함하여 DTS 에뮬레이터에 대한 자세한 내용은 지속성 작업 스케줄러를 사용하여 개발을 참조하세요.
함수 앱 실행
이제 지속성 에이전트를 사용하여 Azure Functions 앱을 실행할 준비가 되었습니다.
새 터미널 창에서(Azurite와 DTS 에뮬레이터를 별도의 창에서 실행 중인 상태로 유지) 프로젝트 디렉터리로 이동합니다.
Azure Functions 런타임을 시작합니다.
func start에이전트에 대한 HTTP 엔드포인트를 포함하여 함수 앱이 실행 중임을 나타내는 출력이 표시됩니다.
Functions: http-MyDurableAgent: [POST] http://localhost:7071/api/agents/MyDurableAgent/run dafx-MyDurableAgent: entityTrigger
이러한 엔드포인트는 자동으로 대화 상태를 관리합니다. 스레드 개체를 직접 만들거나 관리할 필요가 없습니다.
에이전트를 로컬 환경에서 테스트
이제 HTTP 요청을 사용하여 내구성 에이전트와 상호 작용할 수 있습니다. 에이전트는 여러 요청에서 대화 상태를 유지 관리하여 다중 턴 대화를 가능하게 합니다.
새 대화 시작
새 스레드를 만들고 첫 번째 메시지를 보냅니다.
curl -i -X POST http://localhost:7071/api/agents/MyDurableAgent/run \
-H "Content-Type: text/plain" \
-d "What are three popular programming languages?"
샘플 응답(헤더에 x-ms-thread-id 스레드 ID가 포함되어 있음)
HTTP/1.1 200 OK
Content-Type: text/plain
x-ms-thread-id: @dafx-mydurableagent@263fa373-fa01-4705-abf2-5a114c2bb87d
Content-Length: 189
Three popular programming languages are Python, JavaScript, and Java. Python is known for its simplicity and readability, JavaScript powers web interactivity, and Java is widely used in enterprise applications.
다음 요청에 대한 헤더(예: x-ms-thread-id)에서 @dafx-mydurableagent@263fa373-fa01-4705-abf2-5a114c2bb87d 스레드 ID를 저장합니다.
대화 계속
스레드 ID를 쿼리 매개 변수로 포함하여 동일한 스레드에 후속 메시지를 보냅니다.
curl -X POST "http://localhost:7071/api/agents/MyDurableAgent/run?thread_id=@dafx-mydurableagent@263fa373-fa01-4705-abf2-5a114c2bb87d" \
-H "Content-Type: text/plain" \
-d "Which one is best for beginners?"
이전 응답의 헤더에서 @dafx-mydurableagent@263fa373-fa01-4705-abf2-5a114c2bb87d를 가져와 실제 스레드 ID로 x-ms-thread-id를 교체합니다.
샘플 응답:
Python is often considered the best choice for beginners among those three. Its clean syntax reads almost like English, making it easier to learn programming concepts without getting overwhelmed by complex syntax. It's also versatile and widely used in education.
에이전트는 이전 메시지(세 가지 프로그래밍 언어)의 컨텍스트를 다시 지정하지 않고도 기억합니다. 대화 상태는 지속성 작업 스케줄러에 의해 지속적으로 저장되므로 함수 앱을 다시 시작하거나 다른 인스턴스에서 대화를 다시 시작하는 경우에도 이 기록이 유지됩니다.
지속성 작업 스케줄러 대시보드를 사용하여 모니터링
지속성 작업 스케줄러는 지속성 에이전트를 모니터링하고 디버깅하기 위한 기본 제공 대시보드를 제공합니다. 대시보드는 에이전트 작업, 대화 기록 및 실행 흐름에 대한 심층적인 가시성을 제공합니다.
대시보드에 액세스
웹 브라우저에서 로컬 DTS 에뮬레이터에
http://localhost:8082대한 대시보드를 엽니다.목록에서 기본 작업 허브를 선택하여 세부 정보를 봅니다.
오른쪽 위 모서리에서 기어 아이콘을 선택하여 설정을 열고 미리 보기 기능에서 에이전트 페이지 사용 옵션이 선택되어 있는지 확인합니다.
에이전트 대화 살펴보기
대시보드에서 에이전트 탭으로 이동합니다.
목록에서 내구성이 있는 에이전트 스레드(예:
mydurableagent - 263fa373-fa01-4705-abf2-5a114c2bb87d)를 선택합니다.모든 메시지 및 응답이 포함된 전체 대화 기록을 포함하여 에이전트 스레드에 대한 자세한 보기가 표시됩니다.
대시보드는 대화 흐름을 이해하는 데 도움이 되는 타임라인 보기를 제공합니다. 주요 정보는 다음과 같습니다.
- 각 상호 작용에 대한 타임스탬프 및 기간
- 프롬프트 및 응답 콘텐츠
- 사용된 토큰 수
Tip
DTS 대시보드는 실시간 업데이트를 제공하므로 HTTP 엔드포인트를 통해 에이전트와 상호 작용할 때 에이전트의 동작을 볼 수 있습니다.
Azure에 배포하기
이제 지속성 에이전트를 로컬로 테스트했으므로 Azure에 배포합니다.
애플리케이션 배포:
azd deploy이 명령은 애플리케이션을 패키지하고 프로비전 중에 만든 Azure Functions 앱에 배포합니다.
배포가 완료되기를 기다립니다. 출력은 에이전트가 Azure에서 실행되는 시기를 확인합니다.
배포된 에이전트를 테스트하기
배포 후 Azure에서 실행되는 에이전트를 테스트합니다.
함수 키 가져오기
Azure Functions에는 프로덕션 환경에서 HTTP 트리거 함수에 대한 API 키가 필요합니다.
API_KEY=`az functionapp function keys list --name $(azd env get-value AZURE_FUNCTION_NAME) --resource-group $(azd env get-value AZURE_RESOURCE_GROUP) --function-name http-MyDurableAgent --query default -o tsv`
Azure에서 새 대화 시작
새 스레드를 만들고 배포된 에이전트에 첫 번째 메시지를 보냅니다.
curl -i -X POST "https://$(azd env get-value AZURE_FUNCTION_NAME).azurewebsites.net/api/agents/MyDurableAgent/run?code=$API_KEY" \
-H "Content-Type: text/plain" \
-d "What are three popular programming languages?"
응답 헤더에서 반환된 스레드 ID를 확인합니다 x-ms-thread-id.
Azure에서 대화 계속
동일한 스레드에서 후속 메시지를 보냅니다. 이전 응답의 스레드 ID로 바꿉니다 <thread-id> .
THREAD_ID="<thread-id>"
curl -X POST "https://$(azd env get-value AZURE_FUNCTION_NAME).azurewebsites.net/api/agents/MyDurableAgent/run?code=$API_KEY&thread_id=$THREAD_ID" \
-H "Content-Type: text/plain" \
-d "Which is easiest to learn?"
에이전트는 로컬에서와 마찬가지로 Azure에서 대화 컨텍스트를 유지 관리하여 에이전트 상태의 내구성을 보여 줍니다.
배포된 에이전트를 모니터링하다
Azure에서 지속성 작업 스케줄러 대시보드를 사용하여 배포된 에이전트를 모니터링할 수 있습니다.
지속성 작업 스케줄러 인스턴스의 이름을 가져옵니다.
azd env get-value DTS_NAMEAzure Portal을 열고 이전 단계에서 지속성 작업 스케줄러 이름을 검색합니다.
지속성 작업 스케줄러 리소스의 개요 블레이드에서 목록에서 기본 작업 허브를 선택합니다.
작업 허브 페이지의 맨 위에서 대시보드 열기를 선택하여 모니터링 대시보드를 엽니다.
로컬 에뮬레이터와 마찬가지로 에이전트의 대화를 봅니다.
Azure에서 호스트되는 대시보드는 로컬 에뮬레이터와 동일한 디버깅 및 모니터링 기능을 제공하므로 프로덕션 환경에서 대화 기록, 추적 도구 호출 및 성능을 분석할 수 있습니다.
자습서: Azure Functions 사용하여 지속성 에이전트 오케스트레이션
이 자습서에서는 Azure Functions 호스팅 모델 및 팬아웃/팬인 패턴을 사용하여 여러 지속성 AI 에이전트를 오케스트레이션하는 방법을 보여 줍니다. 이전 자습서에서 지속성 에이전트를 확장하여 사용자의 질문을 처리하는 다중 에이전트 시스템을 만든 다음, 응답을 여러 언어로 동시에 변환합니다. 자체 호스팅 오케스트레이션 예제는 샘플을 참조하세요.
오케스트레이션 패턴 이해
빌드할 오케스트레이션은 다음 흐름을 따릅니다.
- 사용자 입력 - 사용자의 질문 또는 메시지
-
주 에이전트 -
MyDurableAgent첫 번째 자습서에서 질문을 처리합니다. - 팬아웃 - 주 에이전트의 응답이 두 번역 에이전트에 동시에 전송됩니다.
- 번역 에이전트 - 두 개의 특수 에이전트가 응답을 번역합니다(프랑스어 및 스페인어).
- 팬인 - 결과는 원래 응답과 번역된 응답을 단일 JSON 응답으로 집계합니다.
이 패턴은 동시 처리를 가능하게 하여 순차적 변환에 비해 총 응답 시간을 줄입니다.
시작 시 에이전트 등록
지속성 오케스트레이션에서 에이전트를 제대로 사용하려면 애플리케이션 시작 시 등록합니다. 오케스트레이션 실행 전반에 걸쳐 사용할 수 있습니다.
이전 Program.cs과 함께 번역 에이전트를 기존 MyDurableAgent에 등록하도록 업데이트하십시오:
using System;
using Azure.AI.Projects;
using Azure.Identity;
using Microsoft.Agents.AI;
using Microsoft.Agents.AI.Hosting.AzureFunctions;
using Microsoft.Azure.Functions.Worker.Builder;
using Microsoft.Extensions.Hosting;
// Get the Azure OpenAI configuration
string endpoint = Environment.GetEnvironmentVariable("AZURE_OPENAI_ENDPOINT")
?? throw new InvalidOperationException("AZURE_OPENAI_ENDPOINT is not set.");
string deploymentName = Environment.GetEnvironmentVariable("AZURE_OPENAI_DEPLOYMENT")
?? "gpt-4o-mini";
// Create the Microsoft Foundry client
AIProjectClient client = new(new Uri(endpoint), new DefaultAzureCredential());
// Create the main agent from the first tutorial
AIAgent mainAgent = client.AsAIAgent(
model: deploymentName,
instructions: "You are a helpful assistant that can answer questions and provide information.",
name: "MyDurableAgent");
// Create translation agents
AIAgent frenchAgent = client.AsAIAgent(
model: deploymentName,
instructions: "You are a translator. Translate the following text to French. Return only the translation, no explanations.",
name: "FrenchTranslator");
AIAgent spanishAgent = client.AsAIAgent(
model: deploymentName,
instructions: "You are a translator. Translate the following text to Spanish. Return only the translation, no explanations.",
name: "SpanishTranslator");
// Build and configure the Functions host
using IHost app = FunctionsApplication
.CreateBuilder(args)
.ConfigureFunctionsWebApplication()
.ConfigureDurableAgents(options =>
{
// Register all agents for use in orchestrations and HTTP endpoints
options.AddAIAgent(mainAgent);
options.AddAIAgent(frenchAgent);
options.AddAIAgent(spanishAgent);
})
.Build();
app.Run();
이전 function_app.py과 함께 번역 에이전트를 기존 MyDurableAgent에 등록하도록 업데이트하십시오:
import os
from azure.identity import DefaultAzureCredential
from agent_framework.azure import AgentFunctionApp
from agent_framework.openai import OpenAIChatCompletionClient
# Get the Azure OpenAI configuration
endpoint = os.getenv("AZURE_OPENAI_ENDPOINT")
if not endpoint:
raise ValueError("AZURE_OPENAI_ENDPOINT is not set.")
deployment_name = os.getenv("AZURE_OPENAI_CHAT_COMPLETION_MODEL", "gpt-4o-mini")
api_version = os.getenv("AZURE_OPENAI_API_VERSION")
# Create the Azure OpenAI client
chat_client = OpenAIChatCompletionClient(
azure_endpoint=endpoint,
model=deployment_name,
api_version=api_version,
credential=DefaultAzureCredential()
)
# Create the main agent from the first tutorial
main_agent = chat_client.as_agent(
instructions="You are a helpful assistant that can answer questions and provide information.",
name="MyDurableAgent"
)
# Create translation agents
french_agent = chat_client.as_agent(
instructions="You are a translator. Translate the following text to French. Return only the translation, no explanations.",
name="FrenchTranslator"
)
spanish_agent = chat_client.as_agent(
instructions="You are a translator. Translate the following text to Spanish. Return only the translation, no explanations.",
name="SpanishTranslator"
)
# Create the function app and register all agents
app = AgentFunctionApp(agents=[main_agent, french_agent, spanish_agent])
오케스트레이션 함수 만들기
오케스트레이션 함수는 여러 에이전트에서 워크플로를 조정합니다. 지속성 컨텍스트에서 등록된 에이전트를 검색하여, 먼저 주요 에이전트를 호출하고, 번역 에이전트를 동시에 분산 실행함으로써 전체 과정을 오케스트레이션합니다.
프로젝트 디렉터리에 이름이 지정된 AgentOrchestration.cs 새 파일을 만듭니다.
using System.Collections.Generic;
using System.Threading.Tasks;
using Microsoft.Agents.AI;
using Microsoft.Agents.AI.DurableTask;
using Microsoft.Azure.Functions.Worker;
using Microsoft.DurableTask;
namespace MyDurableAgent;
public static class AgentOrchestration
{
// Define a strongly-typed response structure for agent outputs
public sealed record TextResponse(string Text);
[Function("agent_orchestration_workflow")]
public static async Task<Dictionary<string, string>> AgentOrchestrationWorkflow(
[OrchestrationTrigger] TaskOrchestrationContext context)
{
var input = context.GetInput<string>() ?? throw new ArgumentNullException(nameof(context), "Input cannot be null");
// Step 1: Get the main agent's response
DurableAIAgent mainAgent = context.GetAgent("MyDurableAgent");
AgentResponse<TextResponse> mainResponse = await mainAgent.RunAsync<TextResponse>(input);
string agentResponse = mainResponse.Result.Text;
// Step 2: Fan out - get the translation agents and run them concurrently
DurableAIAgent frenchAgent = context.GetAgent("FrenchTranslator");
DurableAIAgent spanishAgent = context.GetAgent("SpanishTranslator");
Task<AgentResponse<TextResponse>> frenchTask = frenchAgent.RunAsync<TextResponse>(agentResponse);
Task<AgentResponse<TextResponse>> spanishTask = spanishAgent.RunAsync<TextResponse>(agentResponse);
// Step 3: Wait for both translation tasks to complete (fan-in)
await Task.WhenAll(frenchTask, spanishTask);
// Get the translation results
TextResponse frenchResponse = (await frenchTask).Result;
TextResponse spanishResponse = (await spanishTask).Result;
// Step 4: Combine results into a dictionary
var result = new Dictionary<string, string>
{
["original"] = agentResponse,
["french"] = frenchResponse.Text,
["spanish"] = spanishResponse.Text
};
return result;
}
}
오케스트레이션 함수를 파일에 추가합니다 function_app.py .
import azure.durable_functions as df
@app.orchestration_trigger(context_name="context")
def agent_orchestration_workflow(context: df.DurableOrchestrationContext):
"""
Orchestration function that coordinates multiple agents.
Returns a dictionary with the original response and translations.
"""
input_text = context.get_input()
# Step 1: Get the main agent's response
main_agent = app.get_agent(context, "MyDurableAgent")
main_response = yield main_agent.run(input_text)
agent_response = main_response.text
# Step 2: Fan out - get the translation agents and run them concurrently
french_agent = app.get_agent(context, "FrenchTranslator")
spanish_agent = app.get_agent(context, "SpanishTranslator")
parallel_tasks = [
french_agent.run(agent_response),
spanish_agent.run(agent_response)
]
# Step 3: Wait for both translation tasks to complete (fan-in)
translations = yield context.task_all(parallel_tasks) # type: ignore
# Step 4: Combine results into a dictionary
result = {
"original": agent_response,
"french": translations[0].text,
"spanish": translations[1].text
}
return result
오케스트레이션 테스트
첫 번째 자습서의 로컬 개발 종속성이 계속 실행되고 있는지 확인합니다.
- 터미널 창 하나에 Azurite 실행
- 다른 터미널 창의 지속성 작업 스케줄러 에뮬레이터
로컬 개발 종속성이 실행 중인 경우:
새 터미널 창에서 Azure Functions 앱을 시작합니다.
func startDurable Functions 확장은 오케스트레이션을 관리하기 위한 기본 제공 HTTP 엔드포인트를 자동으로 만듭니다. 기본 제공 API를 사용하여 오케스트레이션을 시작합니다.
curl -X POST http://localhost:7071/runtime/webhooks/durabletask/orchestrators/agent_orchestration_workflow \ -H "Content-Type: application/json" \ -d '"\"What are three popular programming languages?\""'
응답에는 오케스트레이션 인스턴스를 관리하기 위한 URL이 포함됩니다.
{ "id": "abc123def456", "statusQueryGetUri": "http://localhost:7071/runtime/webhooks/durabletask/instances/abc123def456", "sendEventPostUri": "http://localhost:7071/runtime/webhooks/durabletask/instances/abc123def456/raiseEvent/{eventName}", "terminatePostUri": "http://localhost:7071/runtime/webhooks/durabletask/instances/abc123def456/terminate", "purgeHistoryDeleteUri": "http://localhost:7071/runtime/webhooks/durabletask/instances/abc123def456" }statusQueryGetUri을 사용하여 오케스트레이션 상태를 쿼리합니다 (실제 인스턴스 ID로abc123def456을 바꾸세요).curl http://localhost:7071/runtime/webhooks/durabletask/instances/abc123def456
runtimeStatus가Completed가 될 때까지 상태 엔드포인트를 폴링합니다. 완료되면 주 에이전트의 응답 및 해당 번역이 포함된 오케스트레이션 출력이 표시됩니다.{ "name": "agent_orchestration_workflow", "instanceId": "abc123def456", "runtimeStatus": "Completed", "output": { "original": "Three popular programming languages are Python, JavaScript, and Java. Python is known for its simplicity...", "french": "Trois langages de programmation populaires sont Python, JavaScript et Java. Python est connu pour sa simplicité...", "spanish": "Tres lenguajes de programación populares son Python, JavaScript y Java. Python es conocido por su simplicidad..." } }
대시보드에서 오케스트레이션 모니터링
지속성 작업 스케줄러 대시보드는 오케스트레이션에 대한 가시성을 제공합니다.
http://localhost:8082을(를) 브라우저에서 여세요."기본" 작업 허브를 선택합니다.
"오케스트레이션" 탭을 선택합니다.
목록에서 오케스트레이션 인스턴스를 찾습니다.
다음을 보기 위해 인스턴스를 선택합니다.
- 오케스트레이션 타임라인
- 주 에이전트 작동 후 병렬 번역 에이전트
- 각 에이전트 실행(MyDurableAgent, 프랑스어 및 스페인어 번역기)
- 시각화된 팬아웃 및 팬인 패턴
- 각 단계의 타이밍 및 기간
Azure에 오케스트레이션 배포
Azure Developer CLI를 사용하여 업데이트된 애플리케이션을 배포합니다.
azd deploy
그러면 새 오케스트레이션 함수 및 추가 에이전트를 사용하여 업데이트된 코드를 첫 번째 자습서에서 만든 Azure Functions 앱에 배포합니다.
배포된 오케스트레이션을 테스트하십시오
배포 후 Azure에서 실행 중인 오케스트레이션을 테스트합니다.
지속성 확장에 대한 시스템 키를 가져옵니다.
SYSTEM_KEY=$(az functionapp keys list --name $(azd env get-value AZURE_FUNCTION_NAME) --resource-group $(azd env get-value AZURE_RESOURCE_GROUP) --query "systemKeys.durabletask_extension" -o tsv)
기본 제공 API를 사용하여 오케스트레이션을 시작합니다.
curl -X POST "https://$(azd env get-value AZURE_FUNCTION_NAME).azurewebsites.net/runtime/webhooks/durabletask/orchestrators/agent_orchestration_workflow?code=$SYSTEM_KEY" \ -H "Content-Type: application/json" \ -d '"\"What are three popular programming languages?\""'
- 응답의
statusQueryGetUri을 사용하여 완료 여부를 폴링하고, 번역과 함께 결과를 확인합니다.
다음 단계
추가 리소스:
- Microsoft Agent Framework에 대한 할 수 있는 작업 확장
- 지속성 작업 스케줄러 개요
- 지속성 작업 스케줄러 대시보드
- Azure Functions 유연 소비 계획
- 지속성 함수 패턴 및 개념