Semantic Kernel에 임베딩 생성 서비스 붙이기

Semantic Kernel에 임베딩 생성 서비스 붙이기

텍스트 임베딩 생성은 AI 모델로 텍스트의 벡터(일명 임베딩)를 만드는 작업이에요. 이 벡터는 텍스트의 의미를 코드화해 둔 것이라서, 두 벡터에 수학 연산을 적용하면 원문 텍스트가 얼마나 비슷한지 비교할 수 있어요. 그래서 RAG(검색 증강 생성) 같은 시나리오에서 정말 유용하죠. 관련 정보를 담은 DB에서 사용자 질문과 비슷한 텍스트를 찾아내고, 그걸 채팅 완성(Chat Completion) 입력으로 넘겨 AI 모델이 더 풍부한 컨텍스트로 답하게 하는 방식이에요.

임베딩 모델 고를 때 생각할 점

임베딩 모델을 고를 땐 네 가지를 확인해야 해요.

  • 모델이 만드는 벡터의 크기와 설정 가능 여부 — 벡터 저장 비용에 직접 영향을 줘요.
  • 벡터가 담는 요소 타입float32인지 float16인지 등, 이것도 저장 비용과 관련돼요.
  • 생성 속도 — 벡터 생성이 얼마나 빠른가.
  • 생성 비용 — 임베딩 생성 자체의 비용.

궁극적으로는 "내 벡터 저장소에 얼마나 비싸게 쌓이는가"가 핵심 질문이에요.

로컬 환경 준비

대부분의 AI 서비스는 로컬 설정 없이 쓰지만, 로컬 호스팅이 가능한 것들은 준비가 필요해요. Ollama는 Docker로 CPU 또는 GPU 컨테이너를 띄운 뒤 필요한 모델을 내려받아요. 예를 들어 mxbai-embed-large 임베딩 모델은 컨테이너 안 터미널에서 ollama pull mxbai-embed-large로 받아요.

docker run -d -v "c:\temp\ollama:/root/.ollama" -p 11434:11434 --name ollama ollama/ollama
# GPU를 쓴다면: docker run -d --gpus=all -v "c:\temp\ollama:/root/.ollama" -p 11434:11434 --name ollama ollama/ollama

ONNX 모델을 쓰려면 원하는 ONNX 모델이 담긴 레포를 클론하면 돼요. 예: git clone https://huggingface.co/TaylorAI/bge-micro-v2.

필요한 패키지 설치

임베딩 생성을 커널에 추가하기 전에, 제공자별 커넥터 패키지를 설치해야 해요.

  • Azure OpenAI: dotnet add package Microsoft.SemanticKernel.Connectors.AzureOpenAI
  • OpenAI: dotnet add package Microsoft.SemanticKernel.Connectors.OpenAI
  • Mistral: dotnet add package Microsoft.SemanticKernel.Connectors.MistralAI --prerelease
  • Google: dotnet add package Microsoft.SemanticKernel.Connectors.Google --prerelease
  • Hugging Face: dotnet add package Microsoft.SemanticKernel.Connectors.HuggingFace --prerelease
  • Ollama: dotnet add package Microsoft.SemanticKernel.Connectors.Ollama --prerelease
  • ONNX: dotnet add package Microsoft.SemanticKernel.Connectors.Onnx --prerelease

텍스트 임베딩 생성 서비스 만들기

서비스를 만드는 방법은 세 가지가 있어요. 어느 쪽이든 핵심은 AddXxxTextEmbeddingGeneration 확장 메서드로 커넥터를 등록하는 거예요.

커널에 직접 추가하기

커널 빌더에 바로 추가하는 방식이에요. 커넥터들이 대부분 실험 단계라서 #pragma warning disable SKEXPxxxx 경고 억제가 필요해요. OpenAI 예시를 볼게요.

using Microsoft.SemanticKernel;

#pragma warning disable SKEXP0010
IKernelBuilder kernelBuilder = Kernel.CreateBuilder();
kernelBuilder.AddOpenAITextEmbeddingGeneration(
    modelId: "MODEL_ID",          // 임베딩 모델 이름, 예: "text-embedding-ada-002".
    apiKey: "<YOUR_API_KEY>",
    orgId: "YOUR_ORG_ID",         // 선택 사항 조직 id.
    serviceId: "YOUR_SERVICE_ID", // 선택; Semantic Kernel 내 특정 서비스 지정용.
    httpClient: new HttpClient(), // 선택; 제공하지 않으면 커널의 HttpClient 사용.
    dimensions: 1536              // 선택; 생성할 임베딩 차원 수.
);
Kernel kernel = kernelBuilder.Build();

Azure OpenAI는 AddAzureOpenAITextEmbeddingGeneration(deploymentName, endpoint, apiKey, ...)으로, deployment 이름과 엔드포인트가 추가로 필요해요. Mistral·Google·Hugging Face는 각각 AddMistralTextEmbeddingGeneration, AddGoogleAIEmbeddingGeneration, AddHuggingFaceTextEmbeddingGeneration으로 등록하고, 엔드포인트를 new Uri(...)로 넘겨요. Ollama는 AddOllamaTextEmbeddingGeneration(modelId, endpoint, ...)으로 http://localhost:11434 같은 로컬 엔드포인트를 지정해요. ONNX는 AddBertOnnxTextEmbeddingGeneration(onnxModelPath, vocabPath, ...)으로 디스크의 모델·어휘 파일 경로를 받아요. 제공자마다 숨길 경고 코드(SKEXP0010/SKEXP0070)가 다르니 주의하세요.

의존성 주입(DI)으로 추가하기

DI를 쓰는 앱이라면 서비스 제공자에 직접 등록하는 게 좋아요. 임베딩 생성 서비스를 싱글톤으로 만들어 일시적인(transient) 커널들에서 재사용할 수 있거든요. 등록 메서드만 kernelBuilder.Add...에서 builder.Services.Add...로 바뀌고, 커널은 AddTransient로 등록해요.

using Microsoft.SemanticKernel;

var builder = Host.CreateApplicationBuilder(args);

#pragma warning disable SKEXP0010
builder.Services.AddOpenAITextEmbeddingGeneration(
    modelId: "MODEL_ID",
    apiKey: "<YOUR_API_KEY>",
    orgId: "YOUR_ORG_ID",
    serviceId: "YOUR_SERVICE_ID",
    dimensions: 1536
);

builder.Services.AddTransient((serviceProvider)=> {
    return new Kernel(serviceProvider);
});

독립 인스턴스로 만들기

직접 new로 서비스 인스턴스를 만들어, 나중에 커널에 넣거나 코드에서 바로 사용할 수도 있어요. 커널이나 서비스 제공자에 주입하지 않고 독립적으로 쓰고 싶을 때 유용하죠.

using Microsoft.SemanticKernel.Connectors.OpenAI;

#pragma warning disable SKEXP0010
OpenAITextEmbeddingGenerationService textEmbeddingGenerationService = new (
    modelId: "MODEL_ID",
    apiKey: "<YOUR_API_KEY>",
    organization: "YOUR_ORG_ID",
    httpClient: new HttpClient(),
    dimensions: 1536
);

Ollama는 OllamaApiClient를 만들고 AsTextEmbeddingGenerationService()로 변환해요.

using Microsoft.SemanticKernel.Embeddings;
using OllamaSharp;

#pragma warning disable SKEXP0070
using var ollamaClient = new OllamaApiClient(
    uriString: "http://localhost:11434",
    defaultModel: "mxbai-embed-large"
);

ITextEmbeddingGenerationService textEmbeddingGenerationService = ollamaClient.AsTextEmbeddingGenerationService();

텍스트 임베딩 생성 서비스 사용하기

모든 텍스트 임베딩 생성 서비스는 ITextEmbeddingGenerationService를 구현해요. 이 인터페이스는 문자열들을 받아 벡터(ReadOnlyMemory<float>)들을 생성하는 GenerateEmbeddingsAsync 메서드 하나를 가지는데, 단일 값용 확장 메서드 GenerateEmbeddingAsync도 제공돼요.

여러 값을 한 번에 처리할 땐 배열로 넘겨요.

IList<ReadOnlyMemory<float>> embeddings =
    await textEmbeddingGenerationService.GenerateEmbeddingsAsync(
    [
        "sample text 1",
        "sample text 2"
    ]);

단일 값은 확장 메서드로 처리해요.

using Microsoft.SemanticKernel.Embeddings;

ReadOnlyMemory<float> embedding =
    await textEmbeddingGenerationService.GenerateEmbeddingAsync("sample text");

출처: https://learn.microsoft.com/en-us/semantic-kernel/concepts/ai-services/embedding-generation/