에이전트 컨텍스트 기반 함수 선택 (RAG 자동 라우팅)

에이전트의 컨텍스트 기반 함수 선택

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

참고: 현재 문서는 C# 중심으로 작성되어 있어요. Python과 Java는 "Coming Soon" 상태예요.

개요

컨텍스트 기반 함수 선택(Contextual Function Selection) 은 Semantic Kernel Agent Framework의 고급 기능으로, 에이전트가 현재 대화 컨텍스트에 가장 관련 있는 함수만 골라서 AI 모델에 알려주게 해요. 모든 사용 가능한 함수를 모델에 노출하는 대신, RAG(Retrieval-Augmented Generation) 를 사용해 사용자 요청에 가장 적합한 함수만 필터링해서 제시하는 방식이에요.

사용 가능한 함수가 많을 때 모델이 적절한 함수를 고르기 어려워 혼란과 최적이 아닌 성능이 나올 수 있는데, 이 접근이 그 문제를 해결해 줍니다.

[!WARNING] ContextualFunctionProvider 를 쓸 때는 에이전트의 UseImmutableKernel 설정을 true 로 해야 해요. 이 기능이 에이전트 호출 시 커널을 복제(clone)하기 때문이에요. 단, UseImmutableKerneltrue 로 하면 호출 중 플러그인 등이 커널 데이터를 수정해도 호출이 끝난 뒤에는 그 변경이 유지되지 않아요.

어떻게 동작하나

에이전트가 컨텍스트 기반 함수 선택으로 구성되면, 벡터 스토어와 임베딩 생성기를 사용해 현재 대화 컨텍스트(이전 메시지와 사용자 입력 포함)를 사용 가능한 함수들의 이름·설명과 의미적으로 매칭해요. 그런 다음 지정된 상한까지의 가장 관련 있는 함수만 모델에 알려줘서 호출을 맡깁니다.

특히 넓은 플러그인·도구 세트를 가진 에이전트에게 유용해서, 각 단계에서 컨텍스트에 맞는 동작만 고려되도록 해줘요.

사용 예시

아래 예시는 고객 리뷰를 요약하는 에이전트를 컨텍스트 기반 함수 선택으로 구성한 모습이에요. GetAvailableFunctions 는 의도적으로 관련·비관련 함수를 섞어서 컨텍스트 선택의 이점을 보여줍니다.

// Create an embedding generator for function vectorization
var embeddingGenerator = new AzureOpenAIClient(new Uri("<endpoint>"), new ApiKeyCredential("<api-key>"))
    .GetEmbeddingClient("<deployment-name>")
    .AsIEmbeddingGenerator();

// Create kernel and register AzureOpenAI chat completion service
var kernel = Kernel.CreateBuilder()
    .AddAzureOpenAIChatCompletion("<deployment-name>", "<endpoint>", "<api-key>");
    .Build();

// Create a chat completion agent
ChatCompletionAgent agent = new()
{
    Name = "ReviewGuru",
    Instructions = "You are a friendly assistant that summarizes key points and sentiments from customer reviews. For each response, list available functions.",
    Kernel = kernel,
    Arguments = new(new PromptExecutionSettings { FunctionChoiceBehavior = FunctionChoiceBehavior.Auto(options: new FunctionChoiceBehaviorOptions { RetainArgumentTypes = true }) }),
    // This setting must be set to true when using the ContextualFunctionProvider
    UseImmutableKernel = true
};

// Create the agent thread and register the contextual function provider
ChatHistoryAgentThread agentThread = new();

agentThread.AIContextProviders.Add(
    new ContextualFunctionProvider(
        vectorStore: new InMemoryVectorStore(new InMemoryVectorStoreOptions() { EmbeddingGenerator = embeddingGenerator }),
        vectorDimensions: 1536,
        functions: AvailableFunctions(),
        maxNumberOfFunctions: 3, // Only the top 3 relevant functions are advertised
        loggerFactory: LoggerFactory
    )
);

// Invoke the agent
ChatMessageContent message = await agent.InvokeAsync("Get and summarize customer review.", agentThread).FirstAsync();
Console.WriteLine(message.Content);

GetAvailableFunctions 가 7개의 함수를 만들지만, maxNumberOfFunctions: 3 이라서 컨텍스트와 가장 관련 있는 세 개(Tools-GetCustomerReviews, Tools-Summarize, Tools-CollectSentiments)만 모델에 알려집니다. 나머지 GetWeather, SendEmail, GetStockPrice, GetCurrentTime 같은 비관련 함수는 걸러져요. 이것이 컨텍스트 필터링의 핵심 이점이에요.

벡터 스토어

이 프로바이더는 기본적으로 단순함을 제공하는 인-메모리 벡터 스토어와 함께 쓰도록 설계됐어요. 다른 종류의 벡터 스토어를 쓰는 경우, 데이터 동기화와 일관성 처리는 호스팅 애플리케이션의 책임이라는 점을 기억하세요.

함수 목록이 바뀌거나 함수 임베딩의 원천이 바뀔 때마다 동기화가 필요해요. 예를 들어 처음에 세 함수(f1, f2, f3)를 벡터화해 클라우드 벡터 스토어에 저장했는데, 나중에 f3를 함수 목록에서 제거했다면 벡터 스토어도 현재 함수(f1, f2)만 반영하도록 갱신해야 해요. 갱신하지 않으면 비관련 함수가 결과로 나올 수 있어요. 마찬가지로 함수 이름·설명 등 벡터화에 쓰는 데이터가 바뀌면, 벡터 스토어를 비우고 새 임베딩으로 다시 채워야 합니다.

외부 또는 분산 벡터 스토어의 데이터 동기화는 특히 분산 애플리케이션에서 복잡하고 오류가 나기 쉬워요. 반면 인-메모리 스토어는 함수 목록이나 벡터화 원천이 바뀌면 새 함수 세트와 임베딩으로 쉽게 재생성할 수 있어서 최소한의 노력으로 일관성을 지켜요.

함수 지정하기

컨텍스트 기반 함수 프로바이더에는 현재 컨텍스트에 따라 가장 관련 있는 함수를 고를 수 있도록 함수 목록을 전달해야 해요. ContextualFunctionProvider 생성자의 functions 파라미터로 넘기면 됩니다.

함수와 함께 maxNumberOfFunctions 파라미터로 반환할 관련 함수의 최대 개수도 지정해야 해요. 이 값은 정확한 수치라기보다 시나리오에 따라 달라지는 상한선이에요. 너무 낮게 잡으면 시나리오에 필요한 함수를 다 못 써서 실패할 수 있고, 너무 높게 잡으면 함수가 너무 많아 환각, 과도한 입력 토큰 소비, 최적이 아닌 성능으로 이어질 수 있어요.

ContextualFunctionProvider provider = new (
    vectorStore: new InMemoryVectorStore(new InMemoryVectorStoreOptions { EmbeddingGenerator = embeddingGenerator }),
    vectorDimensions: 1536,
    functions: [AIFunctionFactory.Create((string text) => $"Echo: {text}", "Echo"), <other functions>]
    maxNumberOfFunctions: 3 // Only the top 3 relevant functions are advertised
);

컨텍스트 함수 프로바이더 옵션

ContextualFunctionProviderOptions 클래스로 프로바이더의 다양한 동작을 커스터마이즈할 수 있어요.

컨텍스트 크기

컨텍스트 크기는 새 호출의 컨텍스트를 만들 때 이전 에이전트 호출의 최근 메시지 몇 개를 포함할지를 결정해요. 프로바이더는 이전 호출의 메시지를 지정된 개수만큼 모아서 새 메시지 앞에 붙여 컨텍스트를 만듭니다. 대화의 앞 단계 정보가 필요한 작업(예: 한 호출에서 리소스를 프로비저닝하고 다음 호출에서 배포하는 경우)에 특히 유용해요.

기본값은 2이며, NumberOfRecentMessagesInContext 속성으로 조정할 수 있어요.

ContextualFunctionProviderOptions options = new ()
{
    NumberOfRecentMessagesInContext = 1 // Only the last message will be included in the context
};

컨텍스트 임베딩 소스 값

컨텍스트 기반 함수 선택을 하려면 현재 컨텍스트를 벡터화해 벡터 스토어의 함수들과 비교해야 해요. 기본적으로 프로바이더는 비어 있지 않은 최근·새 메시지를 하나의 문자열로 이어 붙여 벡터화하고 이걸로 관련 함수를 검색합니다.

이 동작을 바꾸고 싶다면 ContextEmbeddingValueProvider 에 커스텀 델리게이트를 지정할 수 있어요. 예를 들어 사용자 메시지만 임베딩에 포함하려면 이렇게 해요.

ContextualFunctionProviderOptions options = new()
{
    ContextEmbeddingValueProvider = async (recentMessages, newMessages, cancellationToken) =>
    {
        // Example: Only include user messages in the embedding
        var allUserMessages = recentMessages.Concat(newMessages)
            .Where(m => m.Role == "user")
            .Select(m => m.Content)
            .Where(content => !string.IsNullOrWhiteSpace(content));
        return string.Join("\n", allUserMessages);
    }
};

함수 임베딩 소스 값

프로바이더는 각 함수를 벡터화해 컨텍스트와 비교하고 가장 관련 있는 함수를 골라요. 기본적으로 함수의 이름과 설명을 이어 붙인 문자열을 벡터화해 벡터 스토어에 저장합니다.

EmbeddingValueProvider 속성으로 커스터마이즈할 수 있어요. 함수에 추가 메타데이터를 넣거나, 벡터화 전에 함수 정보를 전처리·필터링·재포맷하고 싶을 때 유용해요.

ContextualFunctionProviderOptions options = new()
{
    EmbeddingValueProvider = async (function, cancellationToken) =>
    {
        // Example: Use only the function name for embedding
        return function.Name;
    }
};

함수 이름·설명에 컨텍스트 관련 메타데이터가 풍부하거나, 함수의 특정 측면에 검색을 집중하고 싶다면 이 값을 바꿔 함수 선택 정확도를 높일 수 있어요.

다음 단계

컨텍스트 함수 선택 샘플 을 확인해 보세요.