AgentApplication 는 에이전트 SDK를 사용하여 빌드된 에이전트의 중앙 구성 요소입니다.
AgentApplication 는 사용자의 메시지, 대화 수명 주기 이벤트, 적응형 카드 상호 작용, OAuth 콜백을 포함하여 들어오는 모든 활동의 진입점입니다.
에이전트의 핵심은 AgentApplication입니다. 에이전트가 수행하는 작업을 설명하는 처리기를 사용하여 구성합니다. SDK는 라우팅, 상태 관리 및 이를 실행하는 데 필요한 인프라를 처리합니다.
AgentApplication 작동 방식
모든 에이전트에는 채널(Microsoft Teams, 봇 서비스 또는 사용자 지정 클라이언트)이 에이전트의 엔드포인트에 활동을 전달할 때 시작되는 수명 주기가 있습니다.
AgentApplication 는 해당 수명 주기의 중심에 있습니다.
Channel → Hosting layer → AgentApplication → Your handlers
에이전트 SDK를 사용하여 빌드된 에이전트의 처리 계층은 다음과 같이 작동합니다.
- 호스팅 계층은 HTTP 요청을 수신하고 인증합니다.
-
AgentApplication은 들어오는 활동을 파이프라인을 통해 처리합니다. - 처리기는 일치하는 경로에 따라 호출됩니다.
에이전트가 처리기가 실행되기 전에 턴의 상태를 로드합니다. 그 후 에이전트는 턴 상태를 저장합니다.
핵심 개념
활동
에이전트 SDK의 모든 항목이 활동으로 흐릅니다. 활동은 발생한 일을 나타내는 구조화된 메시지입니다. 활동에는 메시지, 이벤트, 호출, conversationUpdate 등의 형식이 있습니다. 해당 형식과 관련된 페이로드를 전달합니다.
AgentApplication 는 활동을 수신하고 올바른 처리기로 라우팅합니다.
Routes
경로는 선택기와 처리기를 쌍으로 연결합니다. 선택기는 경로가 현재 활동과 일치하는지 여부를 결정합니다. 처리기는 경로가 일치할 때 논리를 실행합니다.
에이전트를 구성할 때 경로를 등록합니다. 일치시킬 수 있는 항목은 다음과 같습니다.
- 특정 텍스트를 포함하거나 정규식과 일치하는 메시지
- 지정된 형식의 모든 활동
- 대화 수명 주기 이벤트(멤버 추가, 멤버 제거)
- 적응형 카드 동작
- 사용자 지정 조건
활동이 도착하면 시스템은 일치 항목을 찾을 때까지 순서대로 경로를 평가합니다. 기본적으로 하나의 경로만 실행됩니다.
상태 전환
AgentApplication는 범위로 나누어진 구조적 스토리지인 _turn 상태를 관리합니다.
| 범위 유형 | Description |
|---|---|
| 대화 | 대화의 모든 사용자 간에 공유되며, 턴 간에 유지됩니다. |
| 사용자 | 모든 대화에서 개별 사용자에게 한정됨 |
| 임시 | 현재 턴만 - 지속되지 않음 |
처리기가 실행되기 전에 시스템이 자동으로 상태를 로드하고 나중에 자동으로 저장합니다.
컨텍스트 전환
처리기가 실행되면 턴 컨텍스트를 받습니다. 턴 컨텍스트는 현재 활동, 어댑터 연결 및 응답을 보내기 위한 유틸리티의 스냅샷입니다. 턴 컨텍스트는 현재 상호 작용에 대한 인터페이스입니다.
Middleware
AgentApplication 는 미들웨어 파이프라인을 지원합니다. 미들웨어는 처리기가 실행되기 전과 후에 각 턴을 처리하는 구성 요소 체인입니다. 미들웨어는 활동 흐름을 검사, 변환 또는 단락할 수 있습니다. 일반적인 용도로는 로깅, 인증 검사 및 요청 정규화가 포함됩니다.
에이전트 만들기
AgentApplication를 서브클래스하고 생성자에서 처리기를 등록하십시오. 호스팅 프레임워크는 AgentApplicationOptions을(를) 자동으로 주입합니다.
public class MyAgent : AgentApplication
{
public MyAgent(AgentApplicationOptions options) : base(options)
{
OnConversationUpdate(ConversationUpdateEvents.MembersAdded, WelcomeAsync);
OnActivity(ActivityTypes.Message, OnMessageAsync, rank: RouteRank.Last);
}
private async Task WelcomeAsync(ITurnContext context, ITurnState state, CancellationToken ct)
{
foreach (var member in context.Activity.MembersAdded)
{
if (member.Id != context.Activity.Recipient.Id)
{
await context.SendActivityAsync("Hello! How can I help you?", cancellationToken: ct);
}
}
}
private async Task OnMessageAsync(ITurnContext context, ITurnState state, CancellationToken ct)
{
await context.SendActivityAsync($"You said: {context.Activity.Text}", cancellationToken: ct);
}
}
에이전트를 Program.cs에 등록하세요.
WebApplicationBuilder builder = WebApplication.CreateBuilder(args);
builder.Services.AddHttpClient();
builder.Services.AddSingleton<IStorage, MemoryStorage>();
builder.Services.AddAgent<MyAgent>();
builder.Services.AddAgentAspNetAuthentication(builder.Configuration);
WebApplication app = builder.Build();
app.UseAuthentication();
app.UseAuthorization();
app.MapAgentApplicationEndpoints(requireAuth: !app.Environment.IsDevelopment());
app.Run();
활동 처리기 등록
메시지 처리
정확한 텍스트(대/소문자를 구분하지 않는)로 메시지 일치:
OnMessage("help", async (context, state, ct) =>
{
await context.SendActivityAsync("Here's what I can do...", cancellationToken: ct);
});
정규식을 사용하여 메시지 일치:
OnMessage(new Regex(@"^order\s+\d+$", RegexOptions.IgnoreCase), async (context, state, ct) =>
{
await context.SendActivityAsync("Looking up your order...", cancellationToken: ct);
});
대화 업데이트 처리
멤버 가입 또는 종료와 같은 대화 수명 주기 이벤트에 대한 처리기를 등록합니다.
OnConversationUpdate(ConversationUpdateEvents.MembersAdded, async (context, state, ct) =>
{
foreach (var member in context.Activity.MembersAdded)
{
if (member.Id != context.Activity.Recipient.Id)
{
await context.SendActivityAsync("Welcome!", cancellationToken: ct);
}
}
});
OnConversationUpdate(ConversationUpdateEvents.MembersRemoved, async (context, state, ct) =>
{
// Called when participants leave the conversation
});
모든 활동 유형 처리
라우팅에 대한 완전한 제어를 위해 모든 활동을 해당 형식 문자열로 묶습니다.
OnActivity(ActivityTypes.Message, async (context, state, ct) =>
{
// Handles all message activities
});
OnActivity(ActivityTypes.Event, async (context, state, ct) =>
{
// Handles event activities
});
ActivityTypes 하드 코딩된 문자열 대신 상수 사용
경로 평가 순서 제어
시스템은 런타임이 아니라 등록할 때 고정 평가 순서로 경로를 정렬합니다. 정렬에서는 다음 두 가지 수준을 사용합니다.
경로 유형: 시스템은 형식별로 경로를 그룹화하며 순위에 관계없이 우선 순위가 낮은 형식보다 우선 순위가 높은 형식을 항상 평가합니다.
우선 순위 경로 유형 1(최고) 에이전틱 호출 경로 2 호출 경로(어댑티브 카드 작업, OAuth 콜백 및 기타 시간에 민감한 호출) 3 에이전틱 경로 4(최하위) 다른 모든 경로 순위: 각 경로 유형 그룹 내에서 시스템은 해당 순위 값에 따라 경로를 주문합니다. 낮은 숫자 값이 먼저 평가됩니다.
상수는 처리기를 등록할 때 순위를 설정하는 데 사용합니다 RouteRank .
| 상수 | Value | 의미 |
|---|---|---|
RouteRank.First |
0 |
그룹의 다른 모든 경로 이전에 평가됨 |
RouteRank.Unspecified |
32767 |
순위가 지정되지 않은 경우 기본값 |
RouteRank.Last |
65535 |
그룹의 다른 모든 경로를 평가한 후에 평가됩니다. |
기본적으로 평가는 일치하는 첫 번째 경로에서 중지됩니다. 보다 구체적인 경로와 일치하지 않는 항목을 처리하기 위한 범용 대체로 RouteRank.Last을 사용합니다.
// Specific handlers use the default rank
OnMessage("status", HandleStatusAsync);
OnMessage("help", HandleHelpAsync);
// Catch-all — handles anything not matched above
OnActivity(ActivityTypes.Message, HandleUnknownMessageAsync, rank: RouteRank.Last);
턴 수명 주기 후크
경로 일치 전후의 모든 턴에서 실행되는 논리를 등록합니다. 이러한 후크는 로깅, 크로스 커팅 문제 및 오류 처리에 유용합니다.
OnBeforeTurn(async (context, state, ct) =>
{
logger.LogInformation("Turn started: {Type}", context.Activity.Type);
return true; // Return false to abort the turn
});
OnAfterTurn(async (context, state, ct) =>
{
logger.LogInformation("Turn completed");
return true; // Return false to skip state saving
});
OnTurnError(async (context, state, exception, ct) =>
{
logger.LogError(exception, "Turn error");
await context.SendActivityAsync("Something went wrong. Please try again.", cancellationToken: ct);
});
OnBeforeTurn이 false를 반환하면 턴이 중단되고 경로가 실행되지 않습니다.
OnAfterTurn가 false로 반환되면 턴 상태가 저장되지 않습니다.
턴 상태 사용
에이전트는 처리기가 실행되기 전에 턴 상태를 자동으로 로드하고 나중에 저장합니다. 처리기에 전달된 턴 상태 개체를 사용하면 다양한 범위에 액세스할 수 있으므로 턴 간에 유지되거나 현재 순서에 대한 임시 데이터를 읽고 쓸 수 있습니다.
- 대화 범위: 대화의 모든 턴에서 공유되는 데이터의 경우
- 사용자 범위: 사용자별 데이터
- 임시 범위: 현재 턴 중에만 존재해야 하는 데이터의 경우
OnActivity(ActivityTypes.Message, async (context, state, ct) =>
{
// Conversation scope — persisted per conversation
var count = state.Conversation.GetValue<int>("messageCount", () => 0);
state.Conversation.SetValue("messageCount", count + 1);
// User scope — persisted per user
var name = state.User.GetValue<string>("displayName");
// Temp scope — current turn only
state.Temp.SetValue("parsedInput", context.Activity.Text?.Trim());
await context.SendActivityAsync($"Message #{count + 1}: {context.Activity.Text}", cancellationToken: ct);
});
비고
로컬 개발 및 테스트에 사용합니다 MemoryStorage . 프로덕션 배포, 특히 여러 인스턴스에서 실행되는 배포의 경우 Azure Cosmos DB 또는 Azure Blob Storage 같은 영구 스토리지 공급자를 사용합니다.
에이전트에서 스토리지 공급자 사용을 참조하세요.