Community이미지github.com

claudejaune/gemma-embed-search

로컬 멀티모달 임베딩 모델을 사용해 이미지, 오디오, 텍스트 폴더를 색인하고 자연어로 검색합니다. 재개 가능한 벡터 인덱스를 구축하고, 색상 필터, 다중 패싯 AND 쿼리, JSON 출력, HTML 접촉 시트 등을 제공합니다.

gemma-embed-search란 무엇인가요?

Gemma Embed Search는 로컬 컴퓨터에서 실행되는 멀티모달 임베딩 모델을 활용하여 사용자가 이미지, 오디오, 텍스트 파일을 의미적으로 검색할 수 있게 해주는 AI 에이전트 스킬입니다. 이 스킬의 핵심 가치는 사용자 데이터가 외부 서버로 전송되지 않는다는 점으로, 프라이버시를 보호하면서 강력한 의미 검색을 제공합니다. `scripts/embed_search.py` 스크립트를 통해 서버 상태 확인(doctor), 연결 설정 저장(init), 파일 임베딩(index), 자연어 쿼리 검색(search)의 네 가지 주요 기능을 수행합니다. OpenAI 호환 `/v1/embeddings` 엔드포인트를 제공하는 모든 서버(예: llama.cpp의 llama-server)에서 작동하며, 임베딩 모델은 Gemma 2 계열을 사용합니다. 검색 결과는 코사인 유사도 점수로 정렬되며, `--color` 파라미터를 통한 픽셀 기반 색상 필터링, `--must`와 `--also`를 통한 다중 패싯 AND/OR 쿼리, `--json`을 통한 기계 판독 가능 출력, `--html`을 통한 브라우저에서 열 수 있는 썸네일 접촉 시트 생성을 지원합니다. 인덱싱은 증분 방식으로 진행되어 중단된 작업도 재개할 수 있으며, 설정은 `config.json` 파일에 저장되어 반복적인 플래그 입력을 피할 수 있습니다.

지원 대상✓Claude Code✓Codex CLI~Cursor
npx skills add https://github.com/claudejaune/gemma-embed-search/tree/HEAD

Installed? Explore more 이미지 skills: steipete/songsee, affaan-m/frontend-design, mattpocock/improve-codebase-architecture · View all 6 →

즐겨 사용하는 AI에게 물어보기

이 에이전트 스킬이 미리 로드된 새 채팅을 엽니다.

문서

Gemma Embed Search

의미로 로컬 파일을 검색합니다. 하나의 임베딩 공간에 이미지, 오디오, 텍스트가 함께 존재하므로, 입력한 쿼리에 맞는 사진, 클립, 문서를 반환합니다. 모델은 로컬 서버를 통해 사용자 하드웨어에서 실행되며, 어떤 데이터도 업로드되지 않습니다.

scripts/embed_search.py는 네 가지 작업을 수행합니다: doctor는 서버를 확인하고, init은 연결을 저장하며, index는 파일을 캐시에 임베딩하고, search는 쿼리와 비교하여 순위를 매깁니다.

로컬 설정

설정은 config.json에 저장되며, 이 파일 자체에는 저장하지 않습니다:

  • Linux 및 macOS: ~/.config/gemma-embed-search/config.json
  • Windows: %USERPROFILE%\.config\gemma-embed-search\config.json

먼저 이 파일을 읽으십시오. url과 model이 있다면, 이후 모든 명령어는 연결 플래그 없이 작동하며 2단계를 건너뛸 수 있습니다.

파일이 없거나 불완전한 경우, 사용자에게 서버 URL과 색인할 폴더를 요청하고, doctor를 실행하여 서버가 제공하는 모델 목록을 확인한 후, init을 실행하여 파일을 작성하십시오. init은 병합(merge) 방식으로 동작하므로, 한 번에 하나의 설정을 추가해도 안전합니다. 모델 이름을 추정하지 마십시오.

이 폴더에서 config.example.json을 해당 경로로 복사하여 편집하는 것은 init과 동일한 작업을 수행합니다. 사용자가 직접 파일을 편집하려는 경우에 유용합니다. 서버에 필요한 경우에만 "api_key"를 추가하십시오.

설정을 스킬 폴더 자체에 저장하지 마십시오. 스킬을 업데이트하면 폴더 내 모든 파일이 교체되며, 거기에 기록된 모든 내용이 손실됩니다.

설정 체크리스트

진행 상황:

  • 1단계: 서버 확인 및 모델 이름 선택 (doctor)
  • 2단계: 연결 저장 (init)
  • 3단계: 20개 파일로 파일럿 색인 및 하나의 쿼리 확인
  • 4단계: 전체 폴더 색인
  • 5단계: 사용자의 실제 쿼리 응답 (--html을 사용하여 사용자가 결과를 볼 수 있도록)

config.json에 이미 url과 model이 있으면 2단계를 건너뜁니다.

스크립트는 uv run scripts/embed_search.py (스크립트의 인라인 메타데이터에서 Pillow를 설치함) 또는 python3 scripts/embed_search.py (Pillow가 이미 import 가능한 경우)로 실행합니다.

1단계 — 서버 및 모델 이름 찾기. 모델 이름을 추정하지 말고 서버에서 읽어오십시오.

uv run scripts/embed_search.py --url http://127.0.0.1:1235 doctor

doctor는 서버가 제공하는 모델을 출력하고 하나의 임베딩을 테스트합니다. 사용자가 아직 서버를 시작하지 않은 경우, references/setup.md를 읽고 해당 모델에 맞는 명령어를 알려주십시오.

2단계 — 저장하여 이후 호출 시 플래그가 필요하지 않도록 합니다. 서버에서 필요한 경우에만 --api-key를 전달하십시오. API 키는 모드 0600으로 설정 파일에 기록됩니다. 나중에 저장된 설정을 변경하지 않고 다른 폴더에서 작업하려면 --root를 전달하십시오.

uv run scripts/embed_search.py --url http://127.0.0.1:1235 --model <doctor-에서-얻은-이름> init --root <폴더>
uv run scripts/embed_search.py index --limit 20

3단계 — 장기 실행 전 파일럿. 인덱싱은 비용이 많이 드는 단계입니다: 데스크탑 GPU에서 이미지당 약 0.25초이므로, 10,000개 파일은 약 40분이 소요됩니다. 20개 파일을 인덱싱하고 하나의 쿼리를 실행한 후, 전체 실행을 진행하기 전에 결과가 올바른지 확인하십시오.

4단계 — 전체 인덱스. index를 다시 실행하면 새 파일이나 변경된 파일만 임베딩하므로, 중단된 실행은 자동으로 재개됩니다.

uv run scripts/embed_search.py index

5단계 — 검색.

uv run scripts/embed_search.py search "해변의 검은 래브라도" --top 20 --html hits.html

쿼리 요리책

목적명령어
일반 의미 검색search "북적이는 야시장"
두 조건이 모두 충족되어야 함search "여성" --must "노란색"
한 표현이 지배하지 않도록 동의어 사용search "여성" --also "소녀" --also "여성 초상화"
X가 포함된 모든 이미지 찾기search "빨간 머리 캐릭터" --top 60 --html hits.html
주제 + 실제 색상 제약search "여성" --color yellow --min-color 0.05
주로 단색 또는 어두운 프레임search "모든 이미지" --max-color blue --modality image
한 가지 파일 유형만search "사이렌 소리" --modality audio
추가 작업을 위한 기계 판독 가능search "..." --json --top 50
더 빠르고 약간 덜 정확한 점수search "..." --dims 256

--also는 패러프레이즈 벡터를 하나의 쿼리로 평균화합니다. --must는 가장 약한 패싯의 점수를 유지하며, 이는 AND를 표현하는 방법입니다. 동의어에는 --also를 사용하고, 독립적인 조건에는 --must를 사용하십시오.

출력 읽기

일반 출력은 점수 [모달리티] 경로이며, 가장 높은 점수부터 표시됩니다. 색상 실행은 점수 뒤에 픽셀 비율을 추가합니다. --json은 {"query", "index", "total", "filtered_out", "results": [{"rank", "score", "color", "modality", "path"}]}를 stdout에 출력합니다. 모든 진행 상황은 stderr로 전송되므로, stdout은 항상 jq에 깔끔하게 파이프됩니다.

이 모델 제품군의 코사인 점수는 약 0.3에서 0.8 사이입니다. 절대적인 숫자는 의미가 적으며, 적중과 실패 사이의 차이가 중요합니다. 목록의 상위가 잘못된 경우 쿼리가 잘못된 것입니다 — 순위를 신뢰하기보다는 쿼리를 다시 작성하십시오. 긴 목록을 모두 동등하게 좋다고 제시하지 마십시오: 확실한 결과가 끝나는 지점과 추측이 시작되는 지점을 명시하십시오.

짧은 목록이 전체 답이 아닙니다. 모든 색인된 파일은 점수를 받으므로, 출력의 total은 전체 코퍼스를 나타내며 결과가 얼마나 타당한지 알려주지 않습니다. 대신 절벽(cliff) — 점수가 급격히 떨어지지 않는 지점 — 을 찾으십시오. 표시한 목록이 그 지점 이전에 끝난다면, 결과가 중단되었다고 말하고 더 긴 실행을 제안하십시오: 더 큰 --top은 추가 인덱스 없이 하나의 접촉 시트만 더 생성합니다. 사용자가 지정한 개수를 존중하고, 적용한 컷오프도 명시하십시오. 실제로 확인된 --top 100 이상의 확장된 목록 없이 "일치하는 항목이 없습니다"라고 주장하지 마십시오.

시각적 검토를 위해 항상 --html을 제공하십시오: 점수와 파일 이름이 포함된 썸네일 접촉 시트를 생성하며, 사용자가 브라우저에서 열 수 있습니다. --html 단독 사용은 작업 디렉토리에 hits.html을 생성합니다. --html FILE은 경로를 설정합니다. 썸네일은 base64로 임베드되므로 시트는 독립적으로 전송될 수 있습니다. 시트는 일치하는 파일의 실제 썸네일을 포함하므로, 버전 관리에서 제외하고 전달하는 모든 것에서 제외하십시오. 기본적으로 512px 너비입니다. 세부 사항을 자세히 살펴보려면 --thumb-width를 높이십시오.

주의사항

  • 쿼리 텍스트 내의 색상 단어는 소프트 힌트이며 필터가 아닙니다. "노란색 테마의 여성"은 주황색 재킷을 입은 여성을 먼저 순위에 올립니다. 실제 제약 조건을 위해서는 --color yellow --min-color 0.05를 사용하십시오. 이는 픽셀에서 계산되며 모델에서 계산되지 않습니다.
  • 스타일 단어도 소프트 힌트이며 더 약합니다. "빨간 머리의 사실적인 여성"은 빨간 머리 여성의 잉크 삽화를 먼저 순위에 올리는데, 주제 명사가 스타일보다 중요하기 때문입니다. 스타일을 --must로 이동시켜 자체 패싯으로 점수를 매기고 --color로 시각적 속성을 고정하십시오. 둘 다 실제 필터는 아닙니다: --must는 패싯 간 최소값을 취하므로, 하나를 강하게 밀면 다른 하나가 희생됩니다.
  • 하나의 프레임에 여러 주제가 있으면 단일 벡터로 혼합되므로, 해당 주제에 전념하는 프레임보다 단일 주제 쿼리에서 점수가 낮아집니다: 빨간 머리 여성과 남성이 함께 있는 초상화는 "빨간 머리 여성" 검색에서 멀리 아래에 위치할 수 있지만, "빨간 머리 캐릭터" 검색에서는 상위에 가깝습니다. 동일한 단일 아이디어의 동의어는 도움이 되지 않습니다. 복수형 또는 집합 형태가 도움이 됩니다. 질문이 "X를 포함하는 모든 것을 찾기"인 경우 --top을 넓히십시오.
  • --color는 인덱스에 색상 통계가 있어야 하며, 이는 index만 계산합니다. 이전 버전으로 구축된 인덱스는 모든 항목에 대해 0.0을 보고합니다. 색상 필터링이 모든 파일을 자동으로 제외하는 경우 --force로 다시 인덱싱하십시오.
  • 인덱스와 쿼리는 동일한 모델을 사용해야 합니다. --model을 전환하면 호환되지 않는 벡터가 조용히 혼합됩니다. 모델 변경 후 --force로 다시 인덱싱하고 stats에 모델을 기록하십시오.
  • 서버는 평균 풀링(mean pooling)과 비전 프로젝터(--mmproj)가 로드된 임베딩 모델을 실행해야 합니다. 프로젝터가 없으면 이미지 요청이 실패하거나 이미지를 무시하는 벡터를 반환합니다. doctor는 텍스트가 작동함을 증명합니다. 하나의 이미지 쿼리를 실행하여 비전을 증명하십시오.
  • 이미지는 전송 전에 가장 긴 쪽 1536에서 JPEG로 다시 인코딩됩니다. 재인코딩은 필수적이며 장식이 아닙니다: 서버는 일반적으로 WebP, 16비트 또는 잘못 레이블된 파일을 거부하며, Pillow는 대신 이를 읽습니다. 모델에는 픽셀 제한이 없습니다 — 서버는 모든 이미지를 고정된 비전-토큰 예산으로 리샘플링합니다 — 따라서 --resize는 업로드 크기와 충실도를 교환하며 정확성과는 관련이 없습니다. 측정된 숫자와 세부 사항은 references/setup.md에 있습니다.
  • HEIC 및 원시 카메라 형식은 Pillow가 플러그인으로 빌드된 경우에만 읽힙니다. 스크립트는 skip <file>: cannot identify image file을 기록하고 계속 진행합니다. 전체 적용 범위를 가정하지 말고 인덱스 요약에서 skipped_errors를 확인하십시오.
  • 색인되지 않은 폴더에서 search를 실행하면 메시지와 함께 종료 코드 4로 종료됩니다. stats의 인덱스 개수가 올바르게 보일 때까지 빈 결과 목록을 "일치 항목 없음"으로 처리하지 마십시오.
  • 오디오는 서버 컨텍스트에 의해 제한됩니다: 초당 25 토큰에서 클립당 약 5.5분입니다. 더 긴 파일은 잘못된 일치로가 아닌 요청 오류로 실패합니다.
  • 점수는 코퍼스 간 또는 양자화 간에 비교할 수 없습니다. 임계값을 약속하지 마십시오. 사용자가 보는 내용에 대해 --min-color와 --top을 조정하십시오.

스크립트 참조

doctor                       서버 확인, 모델 목록 표시, 임베딩 테스트
init [--root DIR]            url, model, key 및 기본 루트를 설정 파일에 저장
index   --root DIR           새 파일 및 변경된 파일 임베딩 (증분, 재개 가능)
        [--modality auto|image|audio|text] [--include '*.png,sub/*.jpg']
        [--exclude-dir node_modules,.git] [--limit N] [--batch 16] [--resize 1536]
        [--force] [--compact]
search  "쿼리"              색인된 파일 순위 매기기; 점수, 모달리티, 경로 출력
        [--also P]... [--must P]... [--color NAME --min-color F | --max-color F]
        [--modality image] [--dims 512|256|128] [--top 20] [--offset 0]
        [--json] [--html [FILE]] [--thumb-width 512]
stats                        항목 수, 모달리티 혼합, 모델, 인덱스 크기

종료 코드: 0 성공, 2 사용 오류, 3 서버 연결 불가 또는 인증 거부, 4 인덱스 누락, 5 수행할 작업 없음, 6 실행 중 서버 오류. 전체 플래그 문서는 scripts/embed_search.py <command> --help를 실행하십시오.

설정 해결 순서는 플래그 > 환경 변수 (EMBED_SEARCH_URL, EMBED_SEARCH_MODEL, EMBED_SEARCH_API_KEY, EMBED_SEARCH_ROOT) > 설정 파일 ~/.config/gemma-embed-search/config.json입니다. 인덱스는 기본적으로 ~/.cache/gemma-embed-search/<폴더>-<해시>.jsonl에 저장되며, 파일당 하나의 JSON 레코드이므로 여러 폴더에 대해 여러 인덱스를 안전하게 유지할 수 있습니다.

추가 세부 정보

관련 스킬