
Microsoft Agent Framework 시작하기
요약
Microsoft가 Semantic Kernel의 기업용 SDK 기능과 AutoGen의 멀티 에이전트 오케스트레이션 패턴을 통합한 Microsoft Agent Framework(MAF)를 발표했습니다. C#/.NET 환경에서 복잡한 AI 에이전트 워크플로를 구축하고 관리할 수 있도록 설계되었습니다.
핵심 포인트
- Semantic Kernel과 AutoGen의 장점을 결합한 통합 프레임워크
- 그래프 기반의 워크플로 오케스트레이션 지원
- 기업 환경에 필수적인 내구성, 관찰 가능성, 이식성 제공
- .NET 10.0 SDK 및 Azure OpenAI 환경 필요
2025년 12월 27일 Medium에 처음 게시되었습니다.
2025년에 C#/.NET으로 에이전트(agents)를 구축하고 있다면, 가장 어려운 부분은 종종 프롬프트(prompts)를 작성하는 것이 아니라 기반(foundation)을 선택하는 것입니다. Semantic Kernel을 사용해야 할까요? AutoGen을 사용해야 할까요? 아니면 OpenAI / Azure OpenAI SDK를 직접 사용해야 할까요? 만약 Python을 사용한다면 선택지는 훨씬 더 길어집니다.
Microsoft는 이러한 "프레임워크 룰렛(framework roulette)"을 줄이기 위해 **Microsoft Agent Framework (MAF)**를 만들었습니다. (Microsoft 자체 발표 게시물에 기반한) 이 아이디어의 핵심은 개발자들이 계속해서 선택해야 했던 두 가지 요소를 통합하는 것입니다:
- Semantic Kernel의 기업 친화적인 SDK 접근 방식 (커넥터(connectors), 플랫폼 통합, 안정적인 개발 인터페이스)
- AutoGen 스타일의 멀티 에이전트 오케스트레이션(multi-agent orchestration) 패턴 (조정(coordination), 핸드오프(handoffs), 더 풍부한 에이전트 간 흐름)
다시 말해,
그것이 Semantic Kernel이 하룻밤 사이에 사라진다는 의미는 아닙니다. Microsoft의 명시된 의도는 당분간 Semantic Kernel v1.x를 계속 지원하는 것(중요한 버그 수정 및 보안 패치, 그리고 일부 기능의 GA(General Availability) 도달 포함)인 반면, 대부분의 새로운 투자는 Agent Framework로 향한다는 것입니다.
이 프레임워크는 두 가지 핵심 기능에 집중합니다:
-
AI 에이전트 (AI Agents): LLM을 사용하여 입력을 처리하고, 도구(tools)를 호출하며, 응답을 생성하는 개별 에이전트
-
워크플로 (Workflows): 복잡한 다단계 작업을 수행하기 위해 여러 에이전트와 함수를 연결하는 그래프 기반의 오케스트레이션 (orchestration)
왜 굳이 새로운 것을 만드는 것일까요? 짧게 요약하자면, 에이전트에는 프롬프트 템플릿 (prompt templates) 이상의 것이 필요하기 때문입니다. MAF는 기업의 현실을 중심으로 설계되었습니다. 즉, 장기 실행 에이전트를 위한 내구성 (durability), 더 풍부한 관찰 가능성 (observability), 더 나은 이식성/상호 운용성 (portability/interoperability), 그리고 로컬 개발에서 관리형 호스팅 (managed hosting)으로 이어지는 더 명확한 경로를 제공합니다.
공식 Microsoft Agent Framework 문서에서 더 자세히 배울 수 있습니다.
사전 요구 사항 (Prerequisites)
본격적으로 시작하기 전에 다음 사항을 갖추었는지 확인하세요:
- .NET 10.0 SDK 또는 그 이후 버전
- 다음 정보를 포함한 Azure OpenAI 계정:
- API 엔드포인트 (API endpoint)
- API 키 (API key)
- 모델 배포 (예: gpt-4.1, gpt-5.2)
- 임베딩 모델 배포 (RAG 예제를 위한 용도)
대부분의 프로젝트에는 Azure OpenAI 설정이 포함된 appsettings.json 또는 appsettings.Development.json 파일이 필요합니다:
{
"ModelName": "your-model-deployment-name",
"Endpoint": "https://your-resource.openai.azure.com/",
...
}
⚠️ 참고: Agent Framework는 현재 퍼블릭 프리뷰 (public preview) 단계입니다. API는 향후 릴리스에서 변경될 수 있습니다.
핵심 개념 (Core Concepts)
예제로 넘어가기 전에, 기초적인 개념을 정립해 보겠습니다:
AIAgent: AI 에이전트를 나타내는 핵심 추상화 (abstraction)입니다. 이는 에이전트가 사용할 수 있는 LLM, 지침 (instructions), 그리고 도구 (tools)를 캡슐화합니다.
도구/함수 (Tools/Functions): 단순한 C# 메서드부터 복잡한 API 호출에 이르기까지, 에이전트가 수행할 수 있는 동작입니다. Agent Framework는 AIFunctionFactory를 사용하여 메서드를 LLM이 호출할 수 있는 도구로 변환합니다. 함수 호출 (function calling)에 대한 심층적인 내용은 OpenAI의 함수 호출 가이드를 참조하세요.
에이전트 스레드 (AgentThread): 대화 문맥 (context)과 이력을 관리합니다. 이를 사용자와의 상호작용을 위한 에이전트의 상태 유지 컨테이너 (stateful container)라고 생각하면 됩니다.
이러한 빌딩 블록들을 염두에 두고, 실제로 어떻게 작동하는지 살펴보겠습니다.
예제 1: 함수 호출을 사용하는 간단한 에이전트
기초부터 시작해 보겠습니다. 도구를 호출할 수 있는 에이전트를 생성해 봅시다.
// 에이전트가 호출할 수 있는 함수 정의
[Description("지정된 위치의 날씨를 가져옵니다.")]
static string GetWeather([Description("날씨를 가져올 위치")] string location)
...
여기서 어떤 일이 일어나고 있나요?
- 함수 정의 (Function Definition):
[Description]속성(attribute)을 사용하여 간단한 C# 메서드를 정의합니다. 이 설명은 매우 중요합니다. 에이전트가 도구를 언제, 어떻게 사용할지 이해할 수 있도록 LLM에 전달되기 때문입니다. - 도구 등록 (Tool Registration):
AIFunctionFactory.Create()는 리플렉션 (reflection)을 사용하여 메서드의 시그니처 (signature)와 속성을 분석한 다음, LLM이 기대하는 도구 스키마 (schema)를 생성합니다. 이 스키마에는 매개변수 이름, 유형 및 설명이 포함됩니다. - 에이전트 생성 (Agent Creation):
CreateAIAgent()는ChatClient를 래핑(wrap)하여 에이전트 전용 기능을 제공하는 확장 메서드 (extension method)입니다.instructions매개변수는 에이전트의 행동을 안내하는 시스템 프롬프트 (system prompt)를 설정합니다. - 스레드 관리 (Thread Management):
GetNewThread()는 메시지 이력을 유지하는 대화 문맥을 생성합니다. 각 스레드는 격리되어 있어 여러 대화를 독립적으로 관리할 수 있습니다. - 스트리밍 실행 (Streaming Execution):
RunStreamingAsync()는 사용자 입력을 LLM으로 보내고 응답을 청크 (chunk) 단위로 스트리밍하여 반환합니다. 이를 통해 사용자는 전체 응답을 기다리는 대신 즉각적인 피드백을 받을 수 있습니다.
내부 동작 원리: 함수 호출 흐름 (Function Calling Flow)
사용자가 “파리의 날씨는 어때?”라고 물으면 다음과 같은 일이 일어납니다:
- LLM (Large Language Model)이 질문과 사용 가능한 도구 스키마 (tool schemas)를 수신합니다.
- LLM은
location: "Paris"라는 파라미터와 함께GetWeather를 호출하기로 결정합니다. - Agent Framework가 이를 가로채어 사용자의 C# 메서드를 실행합니다.
- 결과가 "도구 메시지 (tool message)"로서 LLM에 다시 전달됩니다.
- LLM은 그 결과를 최종 응답에 포함합니다: “잠시만 기다려 주세요... 파리의 날씨는 흐리며, 최고 기온은 15°C입니다.”
이러한 오케스트레이션 (orchestration)은 자동으로 수행됩니다. 사용자는 함수를 정의하기만 하면 프레임워크가 나머지를 처리합니다.
이것이 중요한 이유
Semantic Kernel의 플러그인 등록 절차 (plugin registration ceremony)와 비교했을 때, 이 방식은 훨씬 더 단순합니다:
// Semantic Kernel 방식 (더 장황함)
var kernel = builder.Build();
kernel.ImportPluginFromFunctions("WeatherPlugin",
...
전체 구현은 AgentFramework 프로젝트에서 확인할 수 있습니다.
예제 2: 구조화된 출력 (Structured Output)
구조화된 출력 (Structured output)은 Microsoft Agent Framework만의 고유한 기능이 아닙니다. 이는 모든 진지한 에이전트 애플리케이션에서 필요로 하는 핵심 패턴입니다.
에이전트가 구체적인 작업(티켓 생성, API 호출, 요청 라우팅 등)을 수행해야 하는 순간, 자유 형식의 텍스트 (free-form text)는 부담이 됩니다. LLM의 응답을 정규 표현식 (regex)으로 처리하고 싶지는 않을 것입니다. 대신 계약 (contract)을 원하게 됩니다.
중요한 이유:
- 신뢰할 수 있는 다운스트림 코드 (산문이 아닌 타입이 지정된 필드)
- 프롬프트 조정 (prompt wrangling) 감소 ("유효한 JSON만 출력"이 기본값이 됨)
- 더 쉬운 테스트 및 평가 (fields에 대한 단언/assert 가능)
두 번째로 과소평가된 효과가 있습니다. 스키마가 "추론 스캐폴드 (reasoning scaffold)" 역할을 한다는 점입니다. 모델이 듣기 좋은 문단을 생성하는 대신, 특정 슬롯(카테고리, 엔티티, 실행 항목 등)에 내용을 채워 넣어야 하므로 동작이 더 결정적 (decisive)으로 변하는 경우가 많습니다.
트레이드오프(Tradeoff): 만약 입력 정보가 누락되었을 때 모델이 필드를 채우도록 _강제(force)_한다면, 모델은 스키마(schema)를 충족하기 위해 값을 환각(hallucinate)할 수 있습니다. 불확실성(uncertainty)을 고려하여 설계함으로써 이를 완화할 수 있습니다 (예: Nullable 필드, "unknown" 값 사용, 또는 "명시되지 않은 경우 필드를 null로 남겨두세요"와 같은 명시적 지침 제공). OpenAI는 자사의 Structured Outputs 가이드에서 이 점을 명시적으로 언급하고 있습니다: https://platform.openai.com/docs/guides/structured-outputs
MAF는 구조화된 출력(structured output)을 직접 지원합니다. 원하는 출력을 설명하는 C# 타입을 정의한 다음, RunAsync<T>()를 호출하면 됩니다. 프레임워크는 해당 타입으로부터 스키마(schema)를 생성하고, 모델에 해당 스키마와 일치하는 응답을 요청하며, 결과를 다시 T로 역직렬화(deserialize)합니다.
다음은 회의 녹취록에서 구조화된 정보를 추출하는 방법입니다:
// 중첩된 타입을 사용하여 출력 구조를 정의합니다
[Description("Structured meeting information")]
public class MeetingAnalysis
...
작동 원리: JSON 스키마 생성 (JSON Schema Generation)
RunAsync<MeetingAnalysis>()를 호출하면 Agent Framework는 다음과 같이 동작합니다:
- JSON 스키마 생성: 리플렉션(reflection)을 사용하여 C# 클래스로부터 JSON 스키마를 생성합니다.
- LLM에 전달: 함수 호출(function calling) 사양의 일부로 이를 LLM에 전송합니다.
- 응답 제약: 응답이 정의된 스키마와 정확히 일치하도록 제약(constrain)을 겁니다.
- 역직렬화: JSON 응답을 강력한 형식의 객체(strongly-typed object)로 역직렬화합니다.
클래스에 지정된 [Description] 속성(attribute)은 LLM이 각 필드가 무엇을 나타내는지 이해하도록 도와 추출 정확도를 높여줍니다. [JsonPropertyName] 속성은 JSON 속성 이름을 제어하며, 이는 특정 명명 규칙(예: snake_case)을 기대하는 LLM과 작업할 때 특히 유용합니다.
AgentFrameworkStructuredOutput 프로젝트에서 전체 예제를 확인해 보세요.
예제 3: 스레드 지속성 (Thread Persistence)
프로덕션(production) 시나리오에서는 대화를 저장하고 재개해야 하는 경우가 많습니다. Agent Framework는 커스텀 스토리지 제공자(storage provider)를 통해 이를 간단하게 만들어 줍니다.
var agent = chatClient.CreateAIAgent(new ChatClientAgentOptions
{
Name = "Assistant",
...
이 데모의 작동 방식 (두 가지 레이어)
이 프로젝트는 두 가지를 영구적으로 저장(persist)합니다:
- 스레드 상태 (thread.Serialize()를 통해): 이는 동일한 AgentThread 객체를 재개할 수 있게 해줍니다.
- 채팅 메시지 (ChatMessageStore를 통해): 이는 재시작 후에도 대화 기록을 보여줄 수 있게 해줍니다.
스레드 상태는 FileThreadStore에 의해 JSON으로 저장됩니다. 복구는 에이전트(agent)에 의해 수행됩니다:
- 저장: threadStore.Save(thread)
- 로드: threadStore.Load(serialized => agent.DeserializeThread(serialized))
메시지 스토어(message store)는 의도적으로 작게 구성되었습니다. FileChatMessageStore에서는 주로 2~3개의 멤버를 재정의(override)합니다:
- AddMessagesAsync(...) : 메시지를 추가하고 영구 저장하기 위해
- GetMessagesAsync(...) : 메시지를 로드하기 위해
- Serialize(...) : 안정적인 스토어 ID를 스레드 상태에 영구 저장하기 위해 (이 데모에서는 생성된 스레드 ID를 저장한 다음 이를 파일 이름으로 사용합니다)
Redis/SQL 등을 사용하고 싶다면, ChatMessageStore의 자체 구현체(implementation)를 만들기만 하면 됩니다.
AgentFrameworkThreadPersistancy 프로젝트에서 전체 구현을 살펴보세요.
예제 4: Azure AI Foundry 통합
Microsoft Foundry (이전 이름: Azure AI Foundry)는 클라우드에서 관리형 에이전트 인프라(managed agent infrastructure)를 제공합니다. 에이전트 생명주기(lifecycle)를 직접 관리하는 대신, Foundry의 영구 에이전트(persistent agents)를 활용할 수 있습니다.
// Azure AI Foundry에 연결
var credential = new AzureCliCredential();
var client = new PersistentAgentsClient(
...
왜 Foundry를 사용해야 하나요?
- 클라우드 네이티브 영속성 (Cloud-native persistence): 에이전트와 스레드가 자동으로 저장됩니다.
- 생명주기 관리 (Lifecycle management): 에이전트 상태를 직접 처리할 필요가 없습니다.
- Azure 통합: 원활한 인증 및 모니터링을 제공합니다.
- 확장성 (Scale): 프로덕션 워크로드(production workloads)를 위해 Azure의 인프라를 활용할 수 있습니다.
프로덕션 시스템(production systems)을 구축하는 팀의 경우, Foundry는 상당한 운영 오버헤드(operational overhead)를 제거해 줍니다.
AgentFrameworkFoundryAgent 프로젝트에서 작동 예제를 확인해 보세요.
예제 5: 벡터 검색(Vector Search)을 활용한 RAG
검색 증강 생성 (RAG, Retrieval-Augmented Generation)은 외부 지식을 통해 에이전트(agent)의 능력을 향상시킵니다. 이 데모에서 Agent Framework는 AI 컨텍스트 제공자(context provider)로서 TextSearchProvider를 통해 RAG를 통합합니다. 즉, 에이전트는 필요에 따라 검색을 트리거할 수 있으며, 제공자는 관련 스니펫(snippets)을 프롬프트(prompt)에 주입합니다.
벡터 스토어(Vector Store) 설정하기
먼저, 벡터 스토어 속성(attributes)을 사용하여 문서 스키마(document schema)를 정의합니다:
private sealed class SearchRecord
{
// 임베딩 차원(Embedding dimension)은 사용 중인 모델과 일치해야 합니다 (예: text-embedding-3-large는 3072)
...
이러한 속성들은 벡터 스토어에 무엇이 검색 가능한 텍스트인지, 무엇이 메타데이터(metadata)인지, 그리고 어떤 필드에 임베딩(embedding)이 포함되어 있는지를 알려줍니다.
지식 베이스(Knowledge Base) 생성 및 데이터 채우기
AI 자동 생성 콘텐츠
본 콘텐츠는 Dev.to AI tag의 원문을 AI가 자동으로 요약·번역·분석한 것입니다. 원 저작권은 원저작자에게 있으며, 정확한 내용은 반드시 원문을 확인해 주세요.
원문 바로가기