Semantic Kernel ChatCompletionAgent

Semantic Kernel ChatCompletionAgent 들여다보기

에이전트 개념 중에서 제일 먼저 접하게 되는 게 ChatCompletionAgent예요. 이름 그대로 채팅 완성(chat completion) 능력을 가진 에이전트인데, Semantic Kernel의 AI 서비스와 연결해서 응답을 만들어 냅니다. 사용자에게 응답할 수도 있고, 다른 에이전트와 대화할 수도 있어요.

Semantic Kernel에서의 채팅 완성

채팅 완성(Chat Completion) 은 본질적으로 "AI 모델과 대화하는 프로토콜"이에요. 대화 기록(chat history)을 유지하고, 요청할 때마다 그 기록을 모델에 함께 넘기는 방식이죠. Semantic Kernel의 AI 서비스는 여러 모델의 채팅 완성 기능을 하나의 통일된 프레임워크로 묶어 줍니다. ChatCompletionAgent 는 이 중 어떤 서비스든 사용해서 응답을 생성할 수 있어요.

개발 환경 준비

언어별 패키지를 설치해 줍니다.

  • C#: Microsoft.SemanticKernel.Agents.Core
    dotnet add package Microsoft.SemanticKernel.Agents.Core --prerelease
    
  • Python: semantic-kernel
    pip install semantic-kernel
    

    [!IMPORTANT] ChatCompletionAgent 에서 어떤 AI 서비스를 쓰느냐에 따라 추가 패키지가 필요할 수 있어요. 사용하는 서비스의 채팅 완성 만들기 문서에서 '필수 extra'를 확인해 주세요.

ChatCompletionAgent 만들기

ChatCompletionAgent 는 기본적으로 AI 서비스 위에 얹혀 있어요. 그래서 만드는 순서도 자연스럽게 따라가요.

  1. 채팅 완성 서비스를 하나 이상 가진 Kernel 인스턴스를 만든다.
  2. Kernel 을 참조해 에이전트를 인스턴스화한다.

C#으로는 이렇게 만들 수 있어요.

// Initialize a Kernel with a chat-completion service
IKernelBuilder builder = Kernel.CreateBuilder();
builder.AddAzureOpenAIChatCompletion(/*<...configuration parameters>*/);
Kernel kernel = builder.Build();

// Create the agent
ChatCompletionAgent agent =
    new()
    {
        Name = "SummarizationAgent",
        Instructions = "Summarize user input",
        Kernel = kernel
    };

Python에서는 두 가지 방법이 있어요. 하나는 채팅 완성 서비스를 곧바로 넘기는 방법이고, 다른 하나는 Kernel 을 먼저 만들어 서비스를 추가한 뒤 kernel= 로 넘기는 방법이에요.

from semantic_kernel.agents import ChatCompletionAgent

# 1) 서비스를 직접 넘기기
agent = ChatCompletionAgent(
    service=AzureChatCompletion(),  # your chat completion service instance
    name="<agent name>",
    instructions="<agent instructions>",
)

# 2) Kernel 먼저 만들고 넘기기
kernel = Kernel()
kernel.add_service(AzureChatCompletion())
agent = ChatCompletionAgent(
  kernel=kernel,
  name="<agent name>",
  instructions="<agent instructions>",
)

첫 번째 방법은 준비된 서비스가 이미 있을 때 편하고, 두 번째 방법은 여러 서비스를 관리하거나 추가 기능이 필요한 커널을 쓸 때 좋아요.

AI 서비스 선택(Service Selection)

서비스 셀렉터(service-selector)를 지정해, Kernel 안에 채팅 완성 서비스가 여러 개일 때 어떤 서비스를 쓸지 정할 수 있어요. 셀렉터를 주지 않으면, Agent Framework 바깥에서 AI 서비스를 쓸 때와 똑같은 기본 로직이 적용됩니다.

C#에서는 KernelArgumentsServiceId 를 지정해 타겟 서비스를 골라요.

builder.AddAzureOpenAIChatCompletion(/*<...service configuration>*/, serviceId: "service-1");
builder.AddAzureOpenAIChatCompletion(/*<...service configuration>*/, serviceId: "service-2");
Kernel kernel = builder.Build();

ChatCompletionAgent agent =
    new()
    {
        Name = "<agent name>",
        Instructions = "<agent instructions>",
        Kernel = kernel,
        Arguments =
          new KernelArguments(
            new OpenAIPromptExecutionSettings()
            {
              ServiceId = "service-2" // The target service-identifier.
            })
    };

Python에서는 service_id 로 서비스를 추가하고, AzureChatPromptExecutionSettings(service_id=...) 로 선택합니다.

kernel = Kernel()
kernel.add_service(AzureChatCompletion(service_id="service1"))
kernel.add_service(AzureChatCompletion(service_id="service2"))

settings = AzureChatPromptExecutionSettings(service_id="service2")

agent = ChatCompletionAgent(
  kernel=kernel,
  name="<agent name>",
  instructions="<agent instructions>",
  arguments=KernelArguments(settings=settings)
)

ChatCompletionAgent 와 대화하기

C#에서는 ChatHistoryAgentThread 를 써서 에이전트와 대화해요. 이어서 할 이전 대화가 있으면 생성자에 ChatHistory 객체를 넘겨 재개할 수도 있어요.

ChatCompletionAgent agent = ...;
AgentThread thread = new ChatHistoryAgentThread();

await foreach (ChatMessageContent response in agent.InvokeAsync(new ChatMessageContent(AuthorRole.User, "<user input>"), thread))
{
  // Process agent response(s)...
}

Python에서는 세 가지 방법이 있어요.

  • get_response 를 await 하면 단일 응답(AgentResponseItem[ChatMessageContent])을 받아요.
  • invokeAgentResponseItem[ChatMessageContent]AsyncIterable 을 반환해요.
  • invoke_stream 은 스트리밍용으로 StreamingChatMessageContentAsyncIterable 을 반환해요.

대화 이력을 이어가고 싶다면 response.thread 를 다음 호출에 넘기면 됩니다.

agent = ChatCompletionAgent(...)
thread = ChatHistoryAgentThread()

async for response in agent.invoke(messages="user input", thread=thread):
  # process agent response(s)

중간 메시지(Intermediate Messages) 처리

에이전트가 호출되는 동안, 최종 답을 만들기 위해 도구(tool)를 실행하는 경우가 있어요. 이 과정에서 생기는 중간 메시지를 받아보고 싶다면 콜백을 제공하면 됩니다. FunctionCallContentFunctionResultContent 인스턴스를 처리하는 콜백이에요. 콜백을 주지 않으면 에이전트는 중간 도구 호출 단계 없이 최종 응답만 반환해요.

Python에서 on_intermediate_message 콜백을 invoke(...)invoke_stream(...) 에 넘기면 됩니다.

# On each intermediate message, this callback lets you handle tool call/result
async def handle_intermediate_steps(message: ChatMessageContent) -> None:
    for item in message.items or []:
        if isinstance(item, FunctionCallContent):
            print(f"Function Call:> {item.name} with arguments: {item.arguments}")
        elif isinstance(item, FunctionResultContent):
            print(f"Function Result:> {item.result} for function: {item.name}")
        else:
            print(f"{message.role}: {message.content}")

agent = ChatCompletionAgent(
    service=AzureChatCompletion(),
    name="Assistant",
    instructions="Answer questions about the menu.",
    plugins=[MenuPlugin()],
)
thread: ChatHistoryAgentThread = None
async for response in agent.invoke(
    messages=user_input,
    thread=thread,
    on_intermediate_message=handle_intermediate_steps,
):
    thread = response.thread

실행해 보면 함수 호출→결과→최종 답변 순으로 로그가 찍혀요. 예를 들어 "오늘 특별 메뉴가 뭐예요?"라는 질문에 MenuPlugin-get_specials 가 호출되고, 그 결과가 나온 뒤 에이전트가 "오늘의 특별 스프는 클램 차우더예요."라고 답하는 식이죠.

선언적 스펙(Declarative Spec)으로 만들기

[!IMPORTANT] 이 기능은 실험 단계(experimental)예요. 개발 중이고, preview/RC 단계로 넘어가기 전에 크게 바뀔 수 있어요.

ChatCompletionAgent 는 YAML 선언적 스펙에서 바로 인스턴스화할 수 있어요. 에이전트의 핵심 속성·지시문(instructions)·사용 가능한 함수(플러그인)를 구조적이고 이식 가능한 형태로 정의하는 방식이죠. 이름, 설명, 지시문 프롬프트, 도구 세트, 모델 파라미터를 한 문서에 담아서 구성을 감사·재현 가능하게 만들어줍니다.

[!NOTE] YAML에 지정한 도구/함수는 에이전트를 만들 때 시점에 이미 Kernel 안에 존재해야 해요. 에이전트 로더는 스펙에서 새 함수를 만들지 않고, 커널에서 id로 플러그인·함수를 찾아볼 뿐입니다. 필요한 플러그인/함수가 커널에 없으면 에이전트 생성 중 오류가 나요.

# YAML spec for the agent
AGENT_YAML = """
type: chat_completion_agent
name: Assistant
description: A helpful assistant.
instructions: Answer the user's questions using the menu functions.
tools:
  - id: MenuPlugin.get_specials
    type: function
  - id: MenuPlugin.get_item_price
    type: function
model:
  options:
    temperature: 0.7
"""

kernel = Kernel()
kernel.add_plugin(MenuPlugin(), plugin_name="MenuPlugin")
agent: ChatCompletionAgent = await AgentRegistry.create_from_yaml(
    AGENT_YAML, kernel=kernel, service=OpenAIChatCompletion()
)

다음 단계

end-to-end 예제가 필요하면 How-To: ChatCompletionAgent 를, 다음 에이전트 유형으로는 Copilot Studio Agent 를 확인해 보세요.