Microsoft Semantic Kernel의 멀티 언어 아키텍처, 플러그인 시스템, 플래너, Azure 통합, 엔터프라이즈 보안과 거버넌스를 분석합니다.
지금까지 다룬 LangChain, LlamaIndex가 주로 Python 생태계에서 성장했다면, Microsoft의 Semantic Kernel(SK)은 처음부터 엔터프라이즈 환경을 목표로 설계되었습니다. GitHub 스타 27,500개 이상을 보유한 이 프로젝트는 .NET, Python, Java를 동시에 지원하며, Azure 생태계와의 깊은 통합을 제공합니다.
SK의 핵심 설계 원칙은 세 가지입니다.
Semantic Kernel의 중심에는 Kernel 객체가 있습니다. Kernel은 AI 서비스, 플러그인, 메모리를 관리하는 중앙 오케스트레이터 역할을 합니다.
from semantic_kernel import Kernel
from semantic_kernel.connectors.ai.open_ai import (
AzureChatCompletion,
AzureTextEmbedding,
)
# Kernel 생성
kernel = Kernel()
# AI 서비스 등록
kernel.add_service(
AzureChatCompletion(
service_id="chat",
deployment_name="gpt-4o",
endpoint="https://myorg.openai.azure.com/",
api_key="your-api-key",
)
)
kernel.add_service(
AzureTextEmbedding(
service_id="embedding",
deployment_name="text-embedding-3-small",
endpoint="https://myorg.openai.azure.com/",
api_key="your-api-key",
)
)using Microsoft.SemanticKernel;
var builder = Kernel.CreateBuilder();
builder.AddAzureOpenAIChatCompletion(
deploymentName: "gpt-4o",
endpoint: "https://myorg.openai.azure.com/",
apiKey: "your-api-key"
);
builder.AddAzureOpenAITextEmbeddingGeneration(
deploymentName: "text-embedding-3-small",
endpoint: "https://myorg.openai.azure.com/",
apiKey: "your-api-key"
);
var kernel = builder.Build();Python과 C#의 API가 거의 동일한 구조를 따릅니다. Java도 마찬가지입니다. 팀 내에서 여러 언어를 사용하더라도 동일한 개념으로 소통할 수 있다는 것이 SK의 강점입니다.
플러그인은 Semantic Kernel의 핵심 확장 메커니즘입니다. LLM이 호출할 수 있는 함수들의 집합으로, 네이티브 함수(Native Function)와 프롬프트 함수(Prompt Function)로 나뉩니다.
from semantic_kernel.functions import kernel_function
class WeatherPlugin:
"""날씨 정보를 제공하는 플러그인"""
@kernel_function(
name="get_weather",
description="특정 도시의 현재 날씨를 조회합니다",
)
def get_weather(self, city: str) -> str:
# 실제 날씨 API 호출 로직
return f"{city}의 현재 기온은 18도이며 맑음입니다."
@kernel_function(
name="get_forecast",
description="특정 도시의 3일간 날씨 예보를 조회합니다",
)
def get_forecast(self, city: str, days: int = 3) -> str:
return f"{city}의 {days}일 예보: 맑음 → 흐림 → 비"
# Kernel에 플러그인 등록
kernel.add_plugin(WeatherPlugin(), plugin_name="Weather")from semantic_kernel.functions import KernelFunctionFromPrompt
# 프롬프트 기반 함수 정의
summarize_function = KernelFunctionFromPrompt(
function_name="summarize",
plugin_name="TextUtils",
prompt="""
다음 텍스트를 {{$max_sentences}}문장으로 요약해주세요.
텍스트:
{{$input}}
요약:
""",
description="텍스트를 지정된 문장 수로 요약합니다",
)
kernel.add_function(
plugin_name="TextUtils",
function=summarize_function,
)
# 실행
result = await kernel.invoke(
function=summarize_function,
input="긴 텍스트 내용...",
max_sentences="3",
)등록된 플러그인을 LLM이 자동으로 선택하여 호출하도록 설정할 수 있습니다.
from semantic_kernel.connectors.ai.open_ai import (
OpenAIChatPromptExecutionSettings,
)
from semantic_kernel.connectors.ai.function_choice_behavior import (
FunctionChoiceBehavior,
)
settings = OpenAIChatPromptExecutionSettings(
function_choice_behavior=FunctionChoiceBehavior.Auto()
)
result = await kernel.invoke_prompt(
prompt="서울의 내일 날씨를 알려줘",
settings=settings,
)
# LLM이 자동으로 Weather.get_forecast를 호출FunctionChoiceBehavior.Auto()는 편리하지만, LLM이 예상치 못한 플러그인을 호출할 수 있습니다. 프로덕션에서는 호출 가능한 플러그인을 명시적으로 제한하거나, 필터를 통해 호출을 검증하는 것이 안전합니다.
플래너(Planner)는 사용자의 목표를 분석하여 등록된 플러그인들을 조합한 실행 계획을 자동으로 생성합니다.
from semantic_kernel.planners import FunctionCallingStepwisePlanner
planner = FunctionCallingStepwisePlanner(
service_id="chat",
max_iterations=10,
)
# 복합 목표 제시
result = await planner.invoke(
kernel=kernel,
question="서울의 내일 날씨를 확인하고, 날씨에 맞는 옷차림을 추천해주세요.",
)
# 플래너가 자동으로:
# 1. Weather.get_forecast("서울") 호출
# 2. 결과를 바탕으로 옷차림 추천 생성Semantic Kernel은 여러 에이전트가 협력하는 시나리오를 지원합니다.
from semantic_kernel.agents import ChatCompletionAgent, AgentGroupChat
from semantic_kernel.agents.strategies import (
TerminationStrategy,
SelectionStrategy,
)
# 에이전트 정의
researcher = ChatCompletionAgent(
kernel=kernel,
service_id="chat",
name="Researcher",
instructions="당신은 정보를 조사하는 연구원입니다.",
)
writer = ChatCompletionAgent(
kernel=kernel,
service_id="chat",
name="Writer",
instructions="당신은 조사 결과를 정리하는 작가입니다.",
)
reviewer = ChatCompletionAgent(
kernel=kernel,
service_id="chat",
name="Reviewer",
instructions="당신은 문서의 품질을 검토하는 리뷰어입니다.",
)
# 그룹 채팅으로 협업
group_chat = AgentGroupChat(
agents=[researcher, writer, reviewer],
termination_strategy=TerminationStrategy(max_iterations=10),
)
async for message in group_chat.invoke("AI 오케스트레이션에 대한 보고서를 작성해주세요"):
print(f"[{message.name}]: {message.content}")엔터프라이즈 환경에서 비용 관리는 필수입니다. SK는 토큰 사용량 추적과 예산 제어를 프레임워크 수준에서 지원합니다.
from semantic_kernel.connectors.ai.open_ai import (
OpenAIChatPromptExecutionSettings,
)
settings = OpenAIChatPromptExecutionSettings(
max_tokens=4096,
temperature=0.7,
)
# 실행 후 토큰 사용량 확인
result = await kernel.invoke_prompt(
prompt="AI 오케스트레이션을 설명해주세요",
settings=settings,
)
# 메타데이터에서 토큰 정보 추출
metadata = result.metadata
if metadata:
usage = metadata.get("usage")
print(f"입력 토큰: {usage.prompt_tokens}")
print(f"출력 토큰: {usage.completion_tokens}")
print(f"총 토큰: {usage.total_tokens}")Azure OpenAI를 사용할 경우, Azure의 콘텐츠 필터링, 속도 제한, 할당량 관리가 자동으로 적용됩니다. SK는 이러한 Azure 네이티브 기능과 자연스럽게 통합되어, 별도의 구현 없이 엔터프라이즈 수준의 관리가 가능합니다.
from semantic_kernel.filters import FunctionInvocationFilter
class RBACFilter(FunctionInvocationFilter):
"""역할 기반 접근 제어 필터"""
def __init__(self, allowed_roles: dict[str, list[str]]):
self.allowed_roles = allowed_roles
async def on_function_invocation(self, context, next_handler):
user_role = context.arguments.get("user_role", "viewer")
function_name = context.function.name
plugin_name = context.function.plugin_name
full_name = f"{plugin_name}.{function_name}"
if full_name not in self.allowed_roles.get(user_role, []):
raise PermissionError(
f"역할 '{user_role}'은 '{full_name}' 함수에 접근할 수 없습니다."
)
await next_handler(context)
# 필터 등록
kernel.add_filter(
filter_type="function_invocation",
filter=RBACFilter(
allowed_roles={
"admin": ["Weather.get_weather", "Weather.get_forecast", "DB.query"],
"user": ["Weather.get_weather"],
"viewer": [],
}
),
)from azure.identity import DefaultAzureCredential
from azure.keyvault.secrets import SecretClient
# Azure Key Vault를 통한 안전한 자격 증명 관리
credential = DefaultAzureCredential()
secret_client = SecretClient(
vault_url="https://myorg-vault.vault.azure.net/",
credential=credential,
)
api_key = secret_client.get_secret("openai-api-key").value
kernel.add_service(
AzureChatCompletion(
service_id="chat",
deployment_name="gpt-4o",
endpoint="https://myorg.openai.azure.com/",
api_key=api_key,
)
)from opentelemetry import trace
from opentelemetry.sdk.trace import TracerProvider
# SK는 OpenTelemetry를 통해 텔레메트리 데이터를 생성
provider = TracerProvider()
trace.set_tracer_provider(provider)
# Azure Application Insights와 연동
from azure.monitor.opentelemetry.exporter import (
AzureMonitorTraceExporter,
)
from opentelemetry.sdk.trace.export import BatchSpanExporter
exporter = AzureMonitorTraceExporter(
connection_string="InstrumentationKey=..."
)
provider.add_span_processor(BatchSpanExporter(exporter))SK는 다음과 같은 상황에서 특히 강점을 발휘합니다.
| 상황 | 이유 |
|---|---|
| .NET/Java 팀 | Python 외 언어를 공식 지원하는 유일한 주요 프레임워크 |
| Azure 기반 인프라 | Azure OpenAI, Key Vault, App Insights 네이티브 통합 |
| 엔터프라이즈 거버넌스 필요 | RBAC, 감사 로그, 비용 제어가 프레임워크에 내장 |
| 기존 시스템 통합 | 플러그인으로 기존 코드를 AI에 노출하는 점진적 도입 가능 |
| 멀티 언어 환경 | 팀 간 일관된 개념과 API 공유 |
반면, 가볍고 빠른 프로토타이핑이나 Python 전용 환경에서는 LangChain이나 LlamaIndex가 더 민첩할 수 있습니다.
6장에서는 deepset의 Haystack 2.x를 분석합니다. 방향성 멀티그래프 기반의 파이프라인 아키텍처, 최소 오버헤드 설계, AsyncPipeline, 그리고 명시적 제어 철학을 살펴보겠습니다.
이 글이 도움이 되셨나요?
관련 주제 더 보기
deepset Haystack 2.x의 컴포넌트와 파이프라인 개념, 방향성 멀티그래프, AsyncPipeline, 라우터, 문서 스토어를 분석합니다.
LlamaIndex의 데이터 커넥터, 인덱스 유형, 쿼리 엔진, 그리고 이벤트 드리븐 Workflows 1.0을 실전 예제와 함께 분석합니다.
순차/병렬 체이닝, 조건부/시맨틱 라우팅, 폴백 체인을 각 프레임워크별로 비교 구현하며 실전 패턴을 정리합니다.