임베딩

임베딩은 데이터를 벡터화된 형식으로 표현하는 방법으로, 유사한 문서를 쉽게 찾을 수 있습니다.

현재 임베딩은 문제(issue)에 대해서만 생성되며, 이를 통해 다음과 같은 기능을 제공합니다.

아키텍처

임베딩은 Elasticsearch에 저장되며, 이는 고급 검색에도 사용됩니다.

graph LR A[database record] --> B[ActiveRecord callback] B --> C[build embedding reference] C -->|add to queue| N[queue] E[cron worker every minute] <-->|pull from queue| N E --> G[deserialize reference] G --> H[generate embedding] H <--> I[AI Gateway] I <--> J[Vertex API] H --> K[upsert document with embedding] K --> L[Elasticsearch]

이 프로세스는 Search::Elastic::ProcessEmbeddingBookkeepingService에 의해 구동되며, Redis 큐에 추가하고 가져오는 작업을 수행합니다.

임베딩 큐에 추가하기

다음 프로세스 설명은 문제를 예로 들고 있습니다.

문제의 임베딩은 내용 "issue with title '#{issue.title}' and description '#{issue.description}'"에서 생성됩니다.

Search::Elastic::IssuesSearch에서 정의된 ActiveRecord 콜백을 사용하여, 임베딩 참조가 생성되거나 제목이나 설명이 업데이트될 때, 그리고 임베딩 생성 가능할 경우 임베딩 큐에 추가됩니다.

임베딩 큐에서 가져오기

Search::ElasticIndexEmbeddingBulkCronWorker 크론 작업자는 매 분마다 실행되며 다음 작업을 수행합니다.

graph LR A[cron] --> B{endpoint throttled?} B -->|no| C[schedule 16 workers] C ..->|each worker| D{endpoint throttled?} D -->|no| E[fetch 19 references from queue] E ..->|each reference| F[increment endpoint] F --> G{endpoint throttled?} G -->|no| H[call AI Gateway to generate embedding]

따라서 우리는 항상 16개의 동시 프로세스가 동시에 임베딩을 생성하더라도 분당 450개의 임베딩이라는 비율 제한 설정을 초과하지 않도록 보장합니다.

백필링

고급 검색 마이그레이션은 백필링을 수행하는 데 사용됩니다. 이는 본질적으로 큐에 배치로 참조를 추가하며, 이후 위에서 설명한 대로 크론 작업자에 의해 처리됩니다.

새로운 임베딩 타입 추가하기

다음 과정은 임베딩을 생성하고 Elasticsearch에 저장하는 단계를 설명합니다.

  1. Elasticsearch 클러스터가 임베딩 생성을 처리할 수 있는지 또는 추가 리소스가 필요한지 확인하기 위해 비용 및 리소스 계산을 수행합니다.
  2. 임베딩을 저장할 위치를 결정합니다. Elasticsearch의 기존 인덱스를 확인하고 적합한 기존 인덱스가 없는 경우, 새 인덱스를 생성합니다.
  3. 인덱스에 임베딩 필드를 추가합니다: 예시.
  4. 새로운 타입에 맞게 콘텐츠 생성 방법을 업데이트합니다.
  5. 새로운 단위 원시 타입을 추가합니다: 여기여기.
  6. Elastic::ApplicationVersionedSearch를 사용하여 콜백에 접근하고 임베딩을 생성해야 할 때 필요한 검사를 추가합니다. Search::Elastic::IssuesSearch를 예시로 참조하세요.
  7. 임베딩을 백필합니다: 예시.

로컬에서 이슈 임베딩 추가하기

전제 조건

  1. Elasticsearch가 실행되고 있는지 확인합니다.
  2. 기존 Elasticsearch 설정이 있는 경우, 다음을 실행하여 AddEmbeddingToIssues 마이그레이션이 완료되었는지 확인합니다:

    Elastic::MigrationWorker.new.perform
    
  3. 로컬 환경에서 GitLab Duo 기능을 실행할 수 있는지 확인합니다.
  4. rails 콘솔에서 다음을 실행했을 때 임베딩(768차원의 벡터)이 출력되는지 확인합니다. 출력되지 않으면 AI 설정에 문제가 있습니다.

    Gitlab::Llm::VertexAi::Embeddings::Text.new('text', user: nil, tracking_context: {}, unit_primitive: 'semantic_search_issue').execute
    

백필 실행하기

프로젝트의 이슈에 대한 임베딩을 백필하기 위해, rails 콘솔에서 다음을 실행합니다:

Gitlab::Duo::Developments::BackfillIssueEmbeddings.execute(project_id: project_id)

이 작업은 이슈들을 큐에 추가하고 일괄로 처리하여 Elasticsearch에 임베딩을 인덱싱합니다.

분당 450개의 임베딩에 대한 속도 제한을 준수합니다. 문제가 있는 경우 @maddievn 또는 Slack의 #g_global_search에 문의하세요.

확인하기

다음이 0을 반환하면 프로젝트의 모든 이슈에 임베딩이 있습니다:

curl "http://localhost:9200/gitlab-development-issues/_count" \
--header "Content-Type: application/json" \
--data '{"query": {"bool": {"filter": [{"term": {"project_id": PROJECT_ID}}], "must_not": [{"exists": {"field": "embedding"}}]}}}' | jq '.count'

PROJECT_ID를 프로젝트 ID로 교체합니다.