
Agent Framework를 활용한 다단계 워크플로(Multi-Step Workflows) 구축하기
요약
Microsoft Agent Framework를 사용하여 LLM 에이전트와 비즈니스 로직을 결합한 다단계 워크플로를 구축하는 방법을 설명합니다. 그래프 기반 오케스트레이션을 통해 복잡한 고객 지원 프로세스를 모듈화하고 자동화하는 가이드를 제공합니다.
핵심 포인트
- Agent Framework의 워크플로는 그래프 기반 오케스트레이션 시스템임
- 실행기, 엣지, 조건을 통해 복잡한 프로세스를 모듈화 가능
- 단일 에이전트의 한계를 넘어 결정론적 로직과 AI 추론을 결합
- 고객 지원 이메일 분류와 같은 실무 시나리오 적용 가능
2025년 12월 28일 Medium에 처음 게시되었습니다.
이전 기사에서 우리는 Microsoft Agent Framework를 사용하여 함수를 호출하고, 구조화된 데이터를 추출하며, RAG를 활용할 수 있는 개별 AI 에이전트(AI agents)를 구축하는 방법을 살펴보았습니다. 이러한 에이전트들은 강력한 빌딩 블록(building blocks)이지만, 실제 시나리오에서는 종종 그 이상의 것이 필요합니다. 즉, 여러 에이전트와 결정론적 로직(deterministic logic)을 복잡한 다단계 프로세스로 오케스트레이션(orchestrating)하는 것입니다.
그 지점에서 Agent Framework의 워크플로(workflow) 시스템이 등장합니다.
고객 지원 이메일 시스템을 생각해 보십시오. 단일 에이전트가 모든 것을 처리할 수는 없습니다. 이메일을 전처리하고, 분류하고, 비즈니스 규칙을 적용하고, 적절하게 라우팅하고, 답변 초안을 작성하며, 때로는 사람에게 에스컬레이션(escalate)해야 합니다. 각 단계는 서로 다른 요구 사항을 가집니다. 어떤 단계는 AI 추론(reasoning)이 필요하고, 어떤 단계는 결정론적 로직이 필요하며, 모든 단계는 원활하게 함께 작동해야 합니다.
이 가이드에서는 바로 그것을 구축할 것입니다. 즉, LLM 에이전트와 비즈니스 로직을 결합하여 고객 이메일을 자동으로 처리, 분류 및 응답하는 고객 지원 이메일 분류(triage) 워크플로입니다.
워크플로(Workflows)란 무엇인가?

Agent Framework의 워크플로는 그래프 기반 오케스트레이션(graph-based orchestration) 시스템입니다. 모든 시나리오를 처리하려고 시도하는 단일 구조(monolithic) 코드를 작성하는 대신, 다음과 같은 유향 그래프(directed graph)를 구축합니다:
- **실행기(Executors)**는 개별 처리 단위(에이전트 또는 커스텀 로직)입니다.
- **엣지(Edges)**는 실행기를 연결하고 데이터의 흐름을 정의합니다.
- **조건(Conditions)**은 엣지에 적용되어 컨텍스트(context)에 기반한 동적 라우팅(dynamic routing)을 가능하게 합니다.
이 아키텍처는 다음과 같은 이점을 제공합니다:
- 모듈성 (Modularity): 각 실행기(executor)가 하나의 작업(task)에 집중합니다.
- 명확성 (Clarity): 그래프 구조를 통해 프로세스 흐름이 명시적으로 드러납니다.
- 유연성 (Flexibility): 조건부 엣지(conditional edges)가 다양한 시나리오에 적응합니다.
- 유지보수성 (Maintainability): 한 단계의 변경 사항이 전체 시스템으로 연쇄적으로 영향을 미치지 않습니다.
워크플로(workflows)에 대한 자세한 내용은 Agent Framework Workflows Guide를 참조하세요.
유스케이스 (Use Case): 고객 지원 이메일 분류 (Customer Support Email Triage)
비즈니스 문제를 정의해 보겠습니다. 우리는 매일 수백 통의 고객 지원 이메일을 받습니다. 우리는 다음과 같은 것을 수행하고자 합니다:
- 일상적인 요청을 자동으로 처리
- 비즈니스 규칙을 일관되게 적용
- 인간의 판단이 필요할 때 적절하게 에스컬레이션 (escalate)
- 데이터 보호 및 정책 준수 유지
우리가 구축할 워크플로는 다음과 같습니다:
이 워크플로는 네 가지 라우팅(routing) 시나리오를 처리합니다:
- 우선순위가 높은 에스컬레이션 (High-priority escalations): 부정적 감정 + 높은 긴급도 → 사람에게 전달 (human handoff)
- 확인 필요 (Clarification needed): 정보 누락 → 에이전트가 질문 초안 작성
- 환불 요청 (Refund requests): 자동 환불 생성 → 사람의 검토
- 일반 답장 (Normal replies): 표준 응답 → 에이전트가 답장 초안 작성
워크플로 아키텍처 (Workflow Architecture): 구성 요소
코드로 들어가기 전에, 세 가지 핵심 개념을 이해해 봅시다.
실행기 (Executors)
실행기(executor)는 입력을 받아 특정 작업을 수행하고 출력을 반환하는 처리 단위입니다. 모든 실행기는 Executor<TInput, TOutput>를 상속받습니다:
internal sealed class PreprocessEmailExecutor : Executor<string, EmailDocument>
{
public override async ValueTask<EmailDocument> HandleAsync(
...
실행기는 다음과 같이 분류될 수 있습니다:
- 결정론적 (Deterministic): AI를 사용하지 않는 순수 로직 (전처리 (preprocessing), 라우팅 (routing), 검증 (validation))
- 에이전트 방식 (Agentic): 추론을 위해 LLM을 사용 (분류 (classification), 응답 초안 작성 (response drafting))
엣지 (Edges)
Edges는 실행기(executors)를 연결하며, 동적 라우팅 (dynamic routing)을 위한 조건(conditions)을 포함할 수 있습니다:
var workflow = new WorkflowBuilder(startExecutor)
.AddEdge(preprocess, intake) // 단순 엣지 (Simple edge)
.AddEdge<PolicyContext>( // 조건부 엣지 (Conditional edge)
...
조건을 사용하면 다음과 같은 분기 로직 (branching logic)을 구축할 수 있습니다: "감정 (sentiment)이 부정적이고 긴급도 (urgency)가 높다면, 상담원 (human)에게 에스컬레이션 (escalate)하라."
공유 상태 (Shared State)
실행기들은 공유 상태 (shared state)를 통해 통신할 수 있습니다. 이는 여러 실행기가 동일한 데이터에 접근해야 할 때 매우 중요합니다:
// 공유 상태에 쓰기 (Write to shared state)
await context.QueueStateUpdateAsync(
SupportRunState.KeyEmail,
...
결정론적 실행기 vs 에이전트형 실행기 (Deterministic vs. Agentic Executors)
접근 방식의 차이를 확인하기 위해 두 가지 실행기를 비교해 보겠습니다.
결정론적 실행기 (Deterministic Executor): PreprocessEmailExecutor
이 실행기는 순수 C# 로직을 사용하여 이메일을 정제하고 개인정보 (PII)를 탐지합니다:
internal sealed partial class PreprocessEmailExecutor : Executor<string, EmailDocument>
{
public override async ValueTask<EmailDocument> HandleAsync(
...
무엇이 이를 결정론적 (deterministic)으로 만드나요?
- 이메일, 전화번호, 주문 ID를 탐지하기 위해 정규 표현식 (regex) 패턴을 사용합니다.
- 일관된 텍스트 정제 규칙을 적용합니다.
- LLM 호출이 없습니다 — 예측 가능하고, 빠르며, 비용이 들지 않습니다.
- 보장된 동작 (guaranteed behavior)이 필요한 작업에 완벽합니다.
에이전트형 실행기 (Agentic Executor): EmailIntakeExecutor
이 실행기는 LLM을 사용하여 이메일을 분류 (classify)합니다:
internal sealed class EmailIntakeExecutor : Executor<EmailDocument, IntakeContext>
{
private readonly AIAgent _agent;
...
무엇이 이를 에이전트형 (agentic)으로 만드나요?
- LLM을 사용하여 이메일의 의도 (intent), 긴급도 (urgency), 감정 (sentiment)을 이해합니다.
- ForJsonSchema<IntakeResult>()를 통해 구조화된 데이터 (structured data)를 추출합니다.
- 정규 표현식 (regex)이 포착할 수 없는 뉘앙스와 문맥 (context)을 처리합니다.
- 분류 (classification), 추론 (reasoning), 자연어 이해 (natural language understanding)에 완벽합니다.
하이브리드 접근 방식 (The Hybrid Approach)
두 유형의 실행기를 결합함으로써, 두 방식의 장점을 모두 얻을 수 있습니다:
- **결정론적 단계 (Deterministic steps)**는 속도, 일관성 및 비용 제어를 제공합니다.
- **에이전트 방식 단계 (Agentic steps)**는 복잡성, 뉘앙스 및 추론을 처리합니다.
- 이들을 결합하면, 지능적이면서도 신뢰할 수 있는 시스템을 구축할 수 있습니다.
조건부 라우팅 (Conditional Routing): PolicyGateExecutor
PolicyGateExecutor는 비즈니스 로직 라우팅을 구현하는 방법을 보여줍니다:
internal sealed class PolicyGateExecutor : Executor<IntakeContext, PolicyContext>
{
public override async ValueTask<PolicyContext> HandleAsync(
...
이 실행기(executor)는 다음을 수행합니다:
- **입력 결과 평가 (Evaluates intake results)**를 통해 응답 모드를 결정합니다.
- 긴급도에 따라 SLA(Service Level Agreement) 규칙을 적용합니다.
- 개인정보(PII) 및 민감한 요청을 플래그로 표시하여 컴플라이언스(Compliance)를 강제합니다.
- 조건부 로직을 통해 라우팅을 결정합니다.
그 후 워크플로 빌더(workflow builder)는 이러한 결정 사항을 사용하여 적절하게 라우팅합니다:
return new WorkflowBuilder(preprocess)
.AddEdge(preprocess, intake)
.AddEdge(intake, policyGate)
...
커스텀 이벤트 (Custom Events): 관찰 가능성(Observability) 스토리 구축하기
Agent Framework 워크플로의 가장 강력한 기능 중 하나는 커스텀 이벤트 시스템입니다. 이벤트는 워크플로 내부에서 무엇이 일어나고 있는지에 대한 실시간 통찰(insight)을 제공하여, 디버깅과 모니터링을 획기적으로 쉽게 만들어 줍니다.
커스텀 이벤트 생성하기
커스텀 이벤트는 WorkflowEvent를 상속하며 필요한 모든 데이터를 담을 수 있습니다:
internal sealed class EmailPreprocessedEvent(EmailDocument email) : WorkflowEvent(email)
{
public EmailDocument Email { get; } = email;
...
이러한 이벤트는 단순한 데이터 전달자(data carriers)이지만, 워크플로 실행을 관찰하는 방식을 완전히 변화시킵니다.
실행기에서 이벤트 발생시키기 (Emitting Events from Executors)
모든 실행기 내부에서 워크플로 컨텍스트(workflow context)를 통해 이벤트를 발생시킬 수 있습니다:
public override async ValueTask<EmailDocument> HandleAsync(
string message,
IWorkflowContext context,
...
이벤트는 워크플로가 실행됨에 따라 실시간으로 발생하므로, 진행 상황이 일어나는 즉시 관찰할 수 있습니다.
이벤트 소비하기 (Consuming Events): 실시간 워크플로 모니터링
여기서부터 흥미로워집니다. 워크플로 (Workflow)를 실행할 때, 이벤트 스트림 (Event stream)을 관찰할 수 있습니다.
await using StreamingRun run = await InProcessExecution.StreamAsync(workflow, input: email);
await foreach (var evt in run.WatchStreamAsync())
...
이 기능이 강력한 이유:
- 실시간 가시성 (Real-time visibility): 워크플로가 실행되는 동안 정확히 어떤 일이 일어나고 있는지 확인할 수 있습니다.
- 타입 안전 패턴 매칭 (Type-safe pattern matching): 각 이벤트 타입에 따라 다르게 처리할 수 있습니다.
- 풍부한 컨텍스트 (Rich context): 이벤트는 각 단계 (Step)의 전체 데이터를 포함합니다.
- 내장 이벤트 (Built-in events): WorkflowOutputEvent 및 WorkflowErrorEvent가 자동으로 제공됩니다.
이벤트 기반 디버깅 (Event-Driven Debugging)
문제가 발생했을 때, 이벤트는 그 과정을 설명해 줍니다:
OpenTelemetry 통합: 분산 트레이싱 (Distributed Tracing)
사용자 정의 이벤트 외에도, Agent Framework는 프로덕션급 관찰 가능성 (Observability)을 위해 OpenTelemetry와 통합됩니다.
OpenTelemetry 설정하기
var tracerProvider = Sdk.CreateTracerProviderBuilder()
.SetResourceBuilder(
ResourceBuilder.CreateDefault()
...
이 설정은 다음과 같은 역할을 합니다:
- 서비스 식별 (AgentFrameworkWorkflows)
- Agent Framework 트레이스 캡처 (Microsoft.Agents.AI.*)
- 모든 트레이스 샘플링 (프로덕션 환경에서는 선택적 샘플링 사용 권장)
- OTLP를 통한 내보내기 (Export): 관찰 가능성 백엔드 (Jaeger, Zipkin, Azure Monitor 등)로 전송
트레이싱되는 항목
OpenTelemetry는 다음 항목을 자동으로 캡처합니다:
- 실행기 (Executor) 실행 시간: 각 단계가 소요되는 시간
- Agent LLM 호출: 토큰 수 (Token counts), 지연 시간 (Latencies), 모델 호출
- 상태 작업 (State operations): 공유 상태 (Shared state)에 대한 읽기 및 쓰기
- 에지 전환 (Edge transitions): 어떤 조건부 경로 (Conditional paths)가 선택되었는지
- 오류 컨텍스트 (Error contexts): 워크플로 컨텍스트가 포함된 스택 트레이스 (Stack traces)
AI Foundry를 활용한 워크플로 시각화
여기서부터는 정말...
AI 자동 생성 콘텐츠
본 콘텐츠는 Dev.to AI tag의 원문을 AI가 자동으로 요약·번역·분석한 것입니다. 원 저작권은 원저작자에게 있으며, 정확한 내용은 반드시 원문을 확인해 주세요.
원문 바로가기
