시맨틱 검색
GitLab v19.5요약
시맨틱 검색은 키워드 일치가 아니라 의미를 기준으로 유사한 콘텐츠를 찾기 위해 벡터 임베딩을 사용하며, Duo Chat 같은 AI 기능이 사용자 쿼리에 필요한 컨텍스트를 검색할 수 있게 합니다. 시맨틱 검색은 텍스트를 벡터 임베딩으로 변환해 벡터 저장소에 저장합니다.
시맨틱 검색은 키워드 일치가 아니라 의미를 기준으로 유사한 콘텐츠를 찾기 위해 벡터 임베딩을 사용하며, Duo Chat 같은 AI 기능이 사용자 쿼리에 필요한 컨텍스트를 검색할 수 있게 합니다.
개요#
시맨틱 검색은 텍스트를 벡터 임베딩으로 변환해 벡터 저장소에 저장합니다. 사용자가 쿼리를 실행하면 그 쿼리도 임베딩으로 변환되어 저장된 벡터와 비교되고, 가장 유사한 결과를 찾습니다. 이 방식은 의미를 포착하므로 키워드가 정확히 일치하지 않아도 관련 콘텐츠를 찾을 수 있습니다.
시맨틱 검색 기능은 Active Context 프레임워크로 구현되어 있습니다.
시맨틱 코드 검색은 이 프레임워크로 구현된 첫 번째 기능입니다. 다른 시맨틱 검색 컬렉션 유형에 대한 지원을 추가할 수 있습니다.
아키텍처#
시맨틱 검색은 gitlab-active-context 젬으로 구현된 Active Context 프레임워크를 기반으로 동작합니다. 이 프레임워크는 여러 벡터 저장소(Elasticsearch, OpenSearch, pgvector를 사용하는 PostgreSQL)에 대한 변환 계층을 제공하므로, 벡터 저장소별 구현 없이 동일한 코드로 지원되는 모든 벡터 저장소를 사용할 수 있습니다.
이 프레임워크는 확장 가능하며 여러 유형의 시맨틱 검색 컬렉션을 지원하도록 설계되었습니다. 각 시맨틱 검색 컬렉션은 다음 구성 요소로 구현됩니다.
- 컬렉션(
Ai::ActiveContext::Collections::): 인덱싱 대상 콘텐츠와 저장 방식을 정의합니다 - 참조(
Ai::ActiveContext::References::): 콘텐츠 업데이트에 따른 임베딩을 추적하고 관리합니다 - 쿼리(
Ai::ActiveContext::Queries::): 벡터 저장소에서 유사한 콘텐츠를 조회합니다 - 큐(
Ai::ActiveContext::Queues::): 임베딩 생성의 비동기 처리를 관리합니다 - 마이그레이션: 벡터 저장소의 스키마 변경과 데이터 변환을 실행합니다
다른 콘텐츠 유형(예: 머지 리퀘스트, 문서)에 이 구성 요소들을 구현하면 새 컬렉션 유형을 추가할 수 있습니다.
임베딩 모델#
자세한 아키텍처 내용은 임베딩 모델 설계 문서를 참고합니다.
모델 메타데이터#
시맨틱 검색은 임베딩 모델을 사용해 인덱싱과 검색에 쓰이는 임베딩을 생성합니다.
임베딩 모델은 Ai::ActiveContext::Collection 레코드의 metadata에
current_indexing_embedding_model, next_indexing_embedding_model, search_embedding_model로 저장됩니다.
각 임베딩 모델 메타데이터에는 다음 정보가 들어 있습니다.
model_type:gitlab_managed또는self_hostedmodel_ref: 모델 식별자(예:text_embedding_005_vertex또는Ai::SelfHostedModelID)field: 임베딩이 저장되는 벡터 저장소 필드dimensions: 임베딩 벡터의 차원이며, 벡터 저장소 필드를 생성할 때와 임베딩을 생성할 때 사용됩니다
ActiveContext::EmbeddingModel 객체와 임베딩 생성#
컬렉션 클래스(예: Ai::ActiveContext::Collections::Code)에서 임베딩 모델은
Ai::ActiveContext::Embeddings::ModelFactory를 통해 ActiveContext::EmbeddingModel 객체로 노출됩니다.
임베딩은 생성된 ActiveContext::EmbeddingModel 객체에서 generate_embeddings를 호출해 만듭니다. 예를 들면 다음과 같습니다.
ac_embedding_model = Ai::ActiveContext::Collections::Code.current_indexing_embedding_model # this returns an ActiveContext::EmbeddingModel
ac_embedding_model.generate_embeddings(<array_of_content>, user: <optional_user>)
내부적으로 ActiveContext::EmbeddingModel#generate_embeddings는 AI Gateway에 임베딩 요청을 보내는 Gitlab::Llm::Embeddings::* 클래스를 실행합니다.
모델 전환#
모델 전환은 Ai::ActiveContext::EmbeddingModelActivationService로 구현되어 있으며,
이 서비스는 임베딩 모델을 설정하거나 변경하는 백그라운드 Active Context 태스크 체인을 시작합니다.
비동기 임베딩 생성#
인덱싱 대상 콘텐츠의 임베딩은 Ai::ActiveContext::References::Code 같은 참조 클래스를 사용하는 큐 시스템을 통해 비동기로 생성됩니다.
- 참조 추적: 콘텐츠가 생성되거나 업데이트되면 해당 참조 클래스에서 임베딩 참조를 추적합니다
- 배치 처리: 참조는
Ai::ActiveContext::BulkProcessWorker가 배치 단위로 처리합니다 - 벡터 저장: 생성된 임베딩은 구성된 벡터 저장소에 저장됩니다
Ai::ActiveContext::BulkProcessWorker는 매분 실행되는 cron job으로 큐의 임베딩 참조를 처리합니다.
참조를 가져와 임베딩을 생성한 뒤 큐에서 제거합니다.
처리 후에도 큐가 비어 있지 않으면 워커는 자신을 다시 큐에 넣어 처리를 이어 갑니다.
임베딩 생성이 실패하면 참조는 지연 시간이 점점 길어지는 재시도 큐 체인을 거칩니다.
지연 시간은 5분, 30분, 2시간, 8시간입니다.
각 단계에서 한 번씩 재시도하므로, 일시적인 오류는 시도 사이에 해소될 시간을 확보합니다.
모든 단계에서 실패한 참조는 데드 큐에 들어갑니다.
속도 제한 오류는 예외입니다. 속도 제한에 걸린 참조는 어느 단계에 있든 첫 번째
재시도 큐로 돌아갑니다. 속도 제한이 풀릴 때까지 5분마다 재시도하며
데드 큐에는 들어가지 않습니다.
쿼리 실행#
쿼리는 다음 순서로 실행됩니다.
- 임베딩 생성: 사용자의 쿼리를 인덱싱된 콘텐츠와 동일한 모델로 임베딩으로 변환합니다
- 벡터 검색: k-최근접 이웃(KNN) 검색으로 임베딩을 저장된 벡터와 비교합니다
- 필터링: 결과를 관련 기준(예: 프로젝트, 파일 경로)으로 필터링합니다
- 인가: 사용자가 접근할 수 있는 콘텐츠만 남도록 결과를 필터링합니다
- 결과 수 제한: 기본적으로 가장 유사한 결과 10개를 반환합니다
마이그레이션#
Active Context 프레임워크는 연결된 벡터 저장소의 스키마 변경과 데이터 변환을 관리하기 위해 마이그레이션 시스템을 사용합니다. 마이그레이션은 데이터베이스에서 추적되며 워커 프로세스가 비동기로 실행합니다.
Ai::ActiveContext::MigrationWorker는 5분마다 cron job으로 실행되어 완료되지 않은 마이그레이션을 실행합니다.
태스크#
자세한 아키텍처 내용은 Active Context 태스크 설계 문서를 참고합니다.
Active Context 프레임워크는 장시간 실행되는 비동기 작업을 관리하기 위해 태스크 실행 시스템을 사용합니다. Active Context 태스크 시스템은 임베딩 모델 활성화, 증분 인덱싱, 데이터 마이그레이션 같은 복잡한 워크플로를 처리합니다. 재시도, 오류 처리, 상태 영속화를 기본으로 지원하므로 핵심 작업을 안정적으로 실행합니다.
벡터 저장소#
인스턴스는 다음 벡터 저장소 중 하나를 사용할 수 있습니다.
- Elasticsearch
- OpenSearch
- pgvector를 사용하는 PostgreSQL
시맨틱 검색을 사용하려면 먼저 벡터 저장소 연결을 만들어야 합니다. 연결을 구성하는 방법은 두 가지입니다.
옵션 1: GitLab UI 사용
고급 검색이 사용하는 Elasticsearch 또는 OpenSearch 클러스터의 경우는 다음과 같습니다.
- Admin > Settings > Search로 이동합니다
- Connect to the advanced search cluster를 선택합니다
- 연결이 자동으로 생성되고 구성됩니다
옵션 2: Rails 콘솔 사용
다음 방법 중 하나를 사용합니다. name 필드는 사용자가 정하는 레이블입니다.
명시적인 URL로 연결하려면(예: OpenSearch) 다음과 같이 실행합니다.
connection = Ai::ActiveContext::Connection.create!(
name: "opensearch",
options: { url: ["http://localhost:9202"] },
adapter_class: "ActiveContext::Databases::Opensearch::Adapter"
)
connection.activate!
기존 고급 검색 클러스터의 자격 증명을 재사용하려면 다음과 같이 실행합니다.
connection = Ai::ActiveContext::Connection.create!(
name: "elasticsearch",
adapter_class: "ActiveContext::Databases::Elasticsearch::Adapter",
options: { use_advanced_search_config: true }
)
connection.activate!
PostgreSQL에서는 pgvector 확장을 사용합니다.
-
PostgreSQL 데이터베이스에서 확장을 생성합니다.
CREATE EXTENSION vector; -
Rails 콘솔에서 연결을 생성합니다.
connection = Ai::ActiveContext::Connection.create!( name: "postgres", options: { host: 'localhost', port: 5432, user: 'postgres', password: '<password>', database: 'postgres' }, adapter_class: "ActiveContext::Databases::Postgresql::Adapter" ) connection.activate!
자세한 내용은 pgvector 문서를 참고합니다.
지원되는 어댑터 클래스는 다음과 같습니다.
ActiveContext::Databases::Elasticsearch::AdapterActiveContext::Databases::Opensearch::AdapterActiveContext::Databases::Postgresql::Adapter
options 해시에는 사용하는 벡터 저장소에 맞는 연결 정보(URL, 자격 증명, 그 밖의 어댑터별 설정)를 담습니다.
개발자 가이드#
큐에 쌓인 항목 관리#
처리를 기다리는 큐 항목 전체를 확인합니다.
ActiveContext::Queues.all_queued_items
cron 워커를 기다리지 않고 큐 항목을 즉시 처리합니다.
ActiveContext.execute_all_queues!
벡터 저장소 검색#
벡터 저장소의 모든 항목을 조회합니다.
ActiveContext::adapter.search(
user: current_user,
collection: ::Ai::ActiveContext::Collections::Code,
query: ActiveContext::Query.all
)
연결 재설정#
새 연결로 처음부터 시작하려면 기존 데이터를 모두 삭제하고 다시 생성합니다.
active_connection = ::Ai::ActiveContext::Connection.active
active_connection.migrations.destroy_all
active_connection.repositories.destroy_all
active_connection.enabled_namespaces.destroy_all
active_connection.collections.destroy_all
active_connection.destroy
연결을 반드시 활성화합니다.
connection.activate!
Duo Self-hosted 용 시맨틱 검색#
- Duo Self-hosted 용 GDK를 설정합니다
- Self-hosted 임베딩 모델을 설정합니다
-
SaaS 용 메타데이터를 사용하지 않도록 저장된 임베딩 모델 메타데이터를 반드시 초기화합니다
::Ai::ActiveContext::Collections::Code.collection_record.update_metadata!( current_indexing_embedding_model: nil, search_embedding_model: nil, next_indexing_embedding_model: nil ) -
관리자 페이지에서 임베딩 모델을 설정하거나, Rails 콘솔에서
EmbeddingModelActivationService로 설정할 수 있습니다.::Ai::ActiveContext::EmbeddingModelActivationService.new( collection_class: ::Ai::ActiveContext::Collections::Code, model_type: 'gitlab_managed', model_ref: 'text_embedding_005_vertex', dimensions: 768, chunk_strategy: 'code_bytes', chunk_strategy_size: 1000, user: User.first ).execute
-
GitLab 제품에 새 임베딩 모델 추가#
GitLab 관리형 임베딩 모델을 새로 지원하려면 AI Gateway의 Model Selection 카탈로그에 추가합니다.
1. models.yml에 추가#
ai_gateway/model_selection/models.yml에
모델 항목을 새로 추가하고 다음 필드를 설정합니다.
gitlab_identifier: 모든 GitLab 애플리케이션에서 공통으로 사용하는 전역 모델 식별자이며,<model_name>_<provider>형식을 따릅니다model_class_provider: 항상litellm_embedding입니다family: 항목 중 하나로embedding을 설정합니다
전체 항목은 다음 예시를 참고합니다.
models:
- name: "text-embedding-005"
provider: "Gemini Enterprise Agent Platform"
gitlab_identifier: "text_embedding_005_vertex"
description: "Natural language processing technique that converts textual data into numerical vectors."
cost_indicator: "$"
max_context_tokens: 20000 # https://docs.cloud.google.com/vertex-ai/generative-ai/docs/embeddings/get-text-embeddings#api_limits
model_class_provider: litellm_embedding
family:
- vertex
- embedding
params:
model: "text-embedding-005"
custom_llm_provider: vertex_ai
prompt_params:
vertex_location: global
2. unit_primitives.yml에 추가#
ai_gateway/model_selection/unit_primitives.yml에서 해당 기능 설정 아래에 새 모델을 선택 가능한 모델로 추가합니다.
시맨틱 코드 검색의 경우 embeddings_code 기능 설정 아래에 새 모델을 추가합니다.
새 시맨틱 검색 컬렉션의 경우에는 기능 설정 항목을 새로 추가해야 합니다.
AI Gateway의 임베딩 생성#
AI Gateway는
채팅 및 완성 프롬프트에 사용하는 것과 같은 Prompt 객체를 통해 요청을 라우팅하여 임베딩을 생성합니다.
이 방식에서는 임베딩이 모델 선택, 프롬프트 레지스트리 해석 및 프롬프트 호출,
관측 가능성 및 계측과 같은 기존 AI Gateway 기능을 재사용할 수 있습니다.
엔드 투 엔드 흐름 개요#
-
임베딩 요청: 클라이언트가 AI Gateway에 임베딩 요청을 보냅니다.
코드 임베딩의 엔드포인트 구현은
ai_gateway/api/v1/embeddings/code_embeddings.py에 있습니다. -
모델 선택:
create_model_metadata메서드를 통해 요청 파라미터에서ModelMetadata객체가 결정됩니다.ModelMetadata는 요청 파라미터의 모델 메타데이터와model_class_provider를 포함하는llm_definition객체를 가집니다. 임베딩의model_class_provider는'litellm_embedding'입니다 (enum 키:ModelClassProvider.LITE_LLM_EMBEDDING). -
프롬프트 레지스트리:
prompt_registry.get_on_behalf(...)를 통해Prompt객체가 생성되며, 파라미터는prompt_id("embeddings_code")와model_metadata(생성된ModelMetadata객체)입니다.Prompt객체는model_class_provider에 따른model_factory와prompt_template_factory를 포함합니다.임베딩의
model_factory는lite_llm_embedding_fn이며, 임베딩 생성을 위해 호출할 실제 모델 클래스로EmbeddingLiteLLM을 지정합니다.다른 모델과 달리 임베딩 생성에는 프롬프트 템플릿이 필요하지 않으므로, 프롬프트 레지스트리는 패스스루 프롬프트 템플릿 팩토리(
embedding_prompt_template_factory)를 사용합니다. -
호출: 임베딩 생성은
prompt.ainvoke를 통해 호출되며, 내부적으로 실제 모델 클래스 (EmbeddingLiteLLM)를 호출합니다. 임베딩은 패스스루 프롬프트 템플릿을 사용하므로prompt.ainvoke에 전달하는 파라미터(contents,dimensions등)가EmbeddingLiteLLM.ainvoke에 그대로 전달됩니다.prompt.ainvoke는 재시도, 계측, 내부 이벤트 추적도 처리합니다. -
EmbeddingLiteLLM클래스: 이 클래스는 LiteLLM/embeddings에 대한 실제 호출을 구현하며 비동기 호출(litellm.aembedding)을 지원합니다.이 클래스는
Prompt호출의 파라미터를litellm이 기대하는 형식으로 변환하고,litellm.aembedding결과를Prompt가 기대하는 객체 유형으로 변환합니다. 또한 예상되는litellm예외 클래스를ai_gateway/api/v1/embeddings/code_embeddings.py의 엔드포인트 구현이 기대하는 유형으로 변환합니다.
GitLab 관리형 모델과 셀프 호스팅 모델의 model_metadata#
AI Gateway는 요청 파라미터의 model_metadata 페이로드를 기준으로 모델 유형을 결정합니다.
GitLab 관리형 모델의 경우:
model_metadata['provider']='gitlab'model_metadata['name']은 지정하지 않아야 합니다model_metadata['identifier']는ai_gateway/model_selection/models.yml조회의gitlab_identifier를 가리킵니다- 사용자 지정 파라미터와 모델 패밀리 같은 추가 모델 메타데이터는
ai_gateway/model_selection/models.yml항목에 지정합니다
셀프 호스팅 모델의 경우:
model_metadata['provider']='litellm'model_metadata['name']='embedding'model_metadata['identifier']는 셀프 호스팅 모델 식별자를 가리킵니다model_metadata['endpoint']가 필요합니다model_metadata['api_key']는 선택적으로 설정할 수 있습니다
그 밖의 model_metadata 구성은 허용되지 않으며 422 오류가 발생합니다.
문제 해결#
시맨틱 검색 결과가 반환되지 않는 경우#
가능한 원인:
- 리포지터리가 아직 인덱싱되지 않았습니다(상태가
embedding_indexing_in_progress)- 확인:
Ai::ActiveContext::Code::Repository.find_by(project_id: project.id).state - 해결: 인덱싱이 끝날 때까지 기다리거나
ActiveContext.execute_all_queues!를 실행해 처리를 직접 시작합니다
- 확인:
- 네임스페이스가 대상 조건을 충족하지 않습니다
- 확인:
Ai::ActiveContext::Code::EnabledNamespace.exists?(namespace_id: project.root_namespace.id) - 해결: 네임스페이스가 대상 조건을 충족하는지 확인합니다
- 확인:
- 벡터 저장소 연결이 구성되지 않았습니다
- 확인:
Ai::ActiveContext::Connection.active.present? - 해결: 벡터 저장소 연결을 구성합니다
- 확인:
데드 큐 항목 관리#
임베딩 생성이 반복해서 실패하면 항목이 데드 큐에 들어갑니다. Rails 콘솔 없이 Admin API로 관리할 수 있습니다.
데드 큐 크기 확인
데드 큐 크기는 Rake 태스크 출력의 Embedding Queues 섹션에서 확인할 수 있습니다.
sudo gitlab-rake gitlab:semantic_search:code:info
데드 큐 항목 전체 삭제
데드 큐 항목을 모두 폐기하려면 다음과 같이 실행합니다.
curl --request DELETE \
--header "PRIVATE-TOKEN: <your_token>" \
"https://gitlab.example.com/api/v4/admin/active_context/dead_queue"
또는 chatops로 실행합니다.
/chatops gitlab run active_context dead_queue clear
데드 큐 항목을 처리 큐로 재투입
데드 큐 항목을 처리 큐로 되돌려 다시 시도하려면 다음과 같이 실행합니다.
curl --request POST \
--header "PRIVATE-TOKEN: <your_token>" \
--data "queue=retry_queue" \
"https://gitlab.example.com/api/v4/admin/active_context/dead_queue/replay"
또는 chatops로 실행합니다(권장).
/chatops gitlab run active_context dead_queue replay --queue=retry_queue
사용할 수 있는 큐 값은 retry_queue, second_retry_queue, third_retry_queue,
fourth_retry_queue, code, code_backfill 입니다. 재시도 체인을 처음부터 다시 거치게 하려면
retry_queue를 사용합니다. 임베딩 파이프라인 전체를 처음부터 다시 실행하려면 code를 사용합니다.