서비스 비교표: HolySheep AI vs 공식 API vs 기타 릴레이

비교 항목 HolySheep AI 공식 OpenAI API 기타 릴레이 서비스
멀티모달 지원 ✅ GPT-4o, Claude 3.5, Gemini 1.5 ✅ GPT-4o만 지원 ⚠️ 제한적
이미지 임베딩 ✅ dall-e-3, clip-vit-largethumb ✅ clip-vit-largethumb ❌ 미지원
인식서 결제 ✅ 로컬 결제 지원 ❌ 해외 카드 필수 ✅ 대부분 지원
가격 (이미지 임베딩) $0.0004/이미지 $0.0004/이미지 $0.0006-0.001/이미지
텍스트 임베딩 비용 $0.10/1M 토큰 $0.10/1M 토큰 $0.15-0.30/1M 토큰
평균 지연 시간 180-250ms 200-300ms 300-500ms
단일 API 키 ✅ 모든 모델 통합 ❌ 모델별 분리 ⚠️ 제한적
무료 크레딧 ✅ 가입 시 제공 ❌ 없음 ⚠️ 제한적

멀티모달 검색 엔진이란?

멀티모달 검색 엔진은 이미지와 텍스트를 동시에 벡터화하여 의미론적 유사성 기반으로 검색하는 시스템입니다. 저는 과거에 이미지 검색 시 키워드 매칭의 한계에何度も苦しみました—사용자가 "따뜻한 커피잔"을 검색했는데 "차가운 아이스컵"이 먼저 나오는尴尬한 상황이죠.

핵심 원리:

아키텍처 설계

┌─────────────────────────────────────────────────────────────┐
│                    멀티모달 검색 아키텍처                      │
├─────────────────────────────────────────────────────────────┤
│                                                             │
│  ┌──────────────┐    ┌──────────────┐    ┌──────────────┐   │
│  │   이미지 입력   │    │   텍스트 입력   │    │  混合 쿼리    │   │
│  └──────┬───────┘    └──────┬───────┘    └──────┬───────┘   │
│         │                   │                   │           │
│         ▼                   ▼                   ▼           │
│  ┌─────────────────────────────────────────────────────┐    │
│  │         HolySheep AI CLIP 임베딩 API                │    │
│  │              (api.holysheep.ai/v1)                 │    │
│  └──────────────────────┬──────────────────────────────┘    │
│                         │                                    │
│                         ▼                                    │
│  ┌─────────────────────────────────────────────────────┐    │
│  │              768차원 벡터 임베딩                     │    │
│  │    [0.123, -0.456, 0.789, ... , 0.321]             │    │
│  └──────────────────────┬──────────────────────────────┘    │
│                         │                                    │
│                         ▼                                    │
│  ┌─────────────────────────────────────────────────────┐    │
│  │              벡터 데이터베이스 (Pinecone/Milvus)      │    │
│  │         & 코사인 유사도 계산 → 순위화 결과            │    │
│  └─────────────────────────────────────────────────────┘    │
│                                                             │
└─────────────────────────────────────────────────────────────┘

실전 구현: Python 멀티모달 검색 시스템

1. HolySheep AI API 설정 및 임베딩 생성

"""
멀티모달 검색 엔진 - HolySheep AI CLIP 임베딩 활용
저장소: https://github.com/holysheep-ai/multimodal-search
"""

import requests
import base64
import json
from typing import List, Dict, Union
from PIL import Image
import io

class HolySheepMultimodalEmbedder:
    """
    HolySheep AI CLIP 모델을 사용한 멀티모달 임베딩 생성기
    - 이미지 + 텍스트联合 벡터화
    - 단일 API 키로 모든 모델 통합
    """
    
    def __init__(self, api_key: str):
        self.api_key = api_key
        # HolySheep AI 공식 엔드포인트 (공식 OpenAI 호환)
        self.base_url = "https://api.holysheep.ai/v1"
        
    def encode_text(self, texts: List[str]) -> List[List[float]]:
        """
        텍스트를 CLIP 임베딩 벡터로 변환
        지연 시간: 평균 120-180ms
        비용: $0.10/1M 토큰
        """
        response = requests.post(
            f"{self.base_url}/embeddings",
            headers={
                "Authorization": f"Bearer {self.api_key}",
                "Content-Type": "application/json"
            },
            json={
                "model": "clip-vit-large-patch14",  # CLIP 모델 지정
                "input": texts
            }
        )
        
        if response.status_code != 200:
            raise ValueError(f"임베딩 생성 실패: {response.text}")
            
        result = response.json()
        return [item["embedding"] for item in result["data"]]
    
    def encode_image(self, image_source: Union[str, bytes, Image.Image]) -> List[float]:
        """
        이미지를 CLIP 임베딩 벡터로 변환
        - URL, 바이트, PIL Image 객체 모두 지원
        - 지연 시간: 평균 180-250ms
        - 비용: $0.0004/이미지
        """
        # 이미지 전처리
        if isinstance(image_source, str):
            # URL 또는 파일 경로
            if image_source.startswith("http"):
                image_data = requests.get(image_source).content
            else:
                with open(image_source, "rb") as f:
                    image_data = f.read()
        elif isinstance(image_source, bytes):
            image_data = image_source
        else:
            # PIL Image 객체
            buffer = io.BytesIO()
            image_source.save(buffer, format="PNG")
            image_data = buffer.getvalue()
        
        # Base64 인코딩
        base64_image = base64.b64encode(image_data).decode("utf-8")
        
        response = requests.post(
            f"{self.base_url}/embeddings",
            headers={
                "Authorization": f"Bearer {self.api_key}",
                "Content-Type": "application/json"
            },
            json={
                "model": "clip-vit-large-patch14",
                "input": [{
                    "type": "image_url",
                    "image_url": {"url": f"data:image/png;base64,{base64_image}"}
                }]
            }
        )
        
        if response.status_code != 200:
            raise ValueError(f"이미지 임베딩 실패: {response.text}")
            
        return response.json()["data"][0]["embedding"]
    
    def batch_encode_images(self, image_sources: List) -> List[List[float]]:
        """배치 이미지 임베딩 - 대량 처리 시 권장"""
        encoded_images = []
        for img in image_sources:
            try:
                vector = self.encode_image(img)
                encoded_images.append(vector)
            except Exception as e:
                print(f"배치 처리 중 오류: {e}")
                encoded_images.append(None)
        return encoded_images


사용 예시

if __name__ == "__main__": API_KEY = "YOUR_HOLYSHEEP_API_KEY" embedder = HolySheepMultimodalEmbedder(API_KEY) # 텍스트 임베딩 테스트 text_embeddings = embedder.encode_text([ "따뜻한 커피잔", "차가운 아이스 음료", "아침 해돋이" ]) print(f"✅ 텍스트 임베딩 완료: {len(text_embeddings)}개 벡터") print(f" 벡터 차원: {len(text_embeddings[0])}") # 이미지 URL 임베딩 테스트 try: img_embedding = embedder.encode_image( "https://example.com/sample-coffee.jpg" ) print(f"✅ 이미지 임베딩 완료: 차원 {len(img_embedding)}") except Exception as e: print(f"⚠️ 이미지 임베딩 실패: {e}")

2.联合 검색 시스템 구현

"""
이미지 + 텍스트联合 검색 시스템
벡터 유사도 기반 검색 & 순위화
"""

import numpy as np
from numpy.linalg import norm
from dataclasses import dataclass
from typing import List, Optional, Tuple
from enum import Enum

class SearchMode(Enum):
    IMAGE_ONLY = "image"
    TEXT_ONLY = "text"
    JOINT = "joint"  # 이미지+텍스트 加權融合

@dataclass
class SearchResult:
    item_id: str
    score: float
    metadata: dict

class MultimodalSearchEngine:
    """
    CLIP 공간 기반 멀티모달 검색 엔진
    - 이미지-텍스트 联合検索
    - 코사인 유사도 사용
    - 가중치 조절 가능
    """
    
    def __init__(self, embedder: HolySheepMultimodalEmbedder):
        self.embedder = embedder
        self.vector_store: List[dict] = []  # {id, vector, type, metadata}
        
    def add_item(
        self, 
        item_id: str, 
        content: Union[str, Image.Image, bytes],
        content_type: str = "auto",
        metadata: Optional[dict] = None
    ):
        """검색 인덱스에 항목 추가"""
        if content_type == "auto":
            if isinstance(content, (str, bytes)):
                content_type = "text" if isinstance(content, str) else "image"
            else:
                content_type = "image"
        
        if content_type == "text":
            vector = self.embedder.encode_text([content])[0]
        else:
            vector = self.embedder.encode_image(content)
        
        self.vector_store.append({
            "id": item_id,
            "vector": vector,
            "type": content_type,
            "content": content,
            "metadata": metadata or {}
        })
        
    def cosine_similarity(self, a: List[float], b: List[float]) -> float:
        """코사인 유사도 계산"""
        a = np.array(a)
        b = np.array(b)
        return np.dot(a, b) / (norm(a) * norm(b))
    
    def search(
        self,
        query: Optional[str] = None,
        query_image: Optional[Union[str, Image.Image]] = None,
        mode: SearchMode = SearchMode.JOINT,
        image_weight: float = 0.5,
        top_k: int = 10
    ) -> List[SearchResult]:
        """
        멀티모달 검색 수행
        
        Args:
            query: 텍스트 쿼리
            query_image: 이미지 쿼리
            mode: 검색 모드
            image_weight: 이미지 가중치 (0.0~1.0)
            top_k: 반환 결과 수
        """
        query_vectors = []
        
        # 쿼리 벡터 생성
        if query and mode in [SearchMode.TEXT_ONLY, SearchMode.JOINT]:
            text_vec = self.embedder.encode_text([query])[0]
            query_vectors.append(("text", text_vec, 1 - image_weight))
            
        if query_image and mode in [SearchMode.IMAGE_ONLY, SearchMode.JOINT]:
            img_vec = self.embedder.encode_image(query_image)
            query_vectors.append(("image", img_vec, image_weight))
        
        if not query_vectors:
            raise ValueError("최소 하나의 쿼리(텍스트 또는 이미지)를 제공해야 합니다")
        
        # 각 항목과의 유사도 계산
        results = []
        for item in self.vector_store:
            if mode == SearchMode.JOINT:
                # 가중 平均 계산
                scores = []
                weights = []
                for qtype, qvec, weight in query_vectors:
                    sim = self.cosine_similarity(qvec, item["vector"])
                    scores.append(sim)
                    weights.append(weight)
                final_score = sum(s * w for s, w in zip(scores, weights)) / sum(weights)
            else:
                qtype, qvec, _ = query_vectors[0]
                final_score = self.cosine_similarity(qvec, item["vector"])
            
            results.append(SearchResult(
                item_id=item["id"],
                score=final_score,
                metadata=item["metadata"]
            ))
        
        # 점수 순으로 정렬
        results.sort(key=lambda x: x.score, reverse=True)
        return results[:top_k]
    
    def search_with_filters(
        self,
        query: str,
        content_types: Optional[List[str]] = None,
        metadata_filters: Optional[dict] = None,
        top_k: int = 10
    ) -> List[SearchResult]:
        """메타데이터 필터링이 포함된 검색"""
        results = self.search(query=query, top_k=100)
        
        filtered = []
        for r in results:
            item = next(i for i in self.vector_store if i["id"] == r.item_id)
            
            # 컨텐츠 타입 필터
            if content_types and item["type"] not in content_types:
                continue
                
            # 메타데이터 필터
            if metadata_filters:
                match = all(
                    item["metadata"].get(k) == v 
                    for k, v in metadata_filters.items()
                )
                if not match:
                    continue
            
            filtered.append(r)
            
            if len(filtered) >= top_k:
                break
                
        return filtered


실전 사용 예시

if __name__ == "__main__": # HolySheep AI 초기화 API_KEY = "YOUR_HOLYSHEEP_API_KEY" embedder = HolySheepMultimodalEmbedder(API_KEY) search_engine = MultimodalSearchEngine(embedder) # 테스트 데이터 추가 search_engine.add_item( item_id="img_001", content="https://example.com/coffee-morning.jpg", content_type="image", metadata={"category": "food", "tag": "coffee"} ) search_engine.add_item( item_id="img_002", content="차가운 아이스 라떼", content_type="text", metadata={"category": "food", "tag": "coffee"} ) search_engine.add_item( item_id="img_003", content="따뜻한 핫초코", content_type="text", metadata={"category": "food", "tag": "chocolate"} ) # 텍스트 검색 print("=== 텍스트 검색: '따뜻한 음료' ===") results = search_engine.search(query="따뜻한 음료", top_k=3) for r in results: print(f" {r.item_id}: {r.score:.4f}") # 이미지 검색 print("\n=== 이미지 검색 ===") results = search_engine.search( query_image="https://example.com/search-image.jpg", mode=SearchMode.IMAGE_ONLY, top_k=3 ) for r in results: print(f" {r.item_id}: {r.score:.4f}") # 이미지+텍스트联合 검색 (가중치 0.6:0.4) print("\n=== 联合 검색 (이미지 60%, 텍스트 40%) ===") results = search_engine.search( query="음료", query_image="https://example.com/style-image.jpg", mode=SearchMode.JOINT, image_weight=0.6, top_k=5 ) for r in results: print(f" {r.item_id}: {r.score:.4f}")

실전 应用 사례

전자상거래 商品 검색 시스템

"""
전자상거래 멀티모달 商品 검색 API
- 이미지 업로드로 유사 商品 검색
- 텍스트 설명으로 semantic 검색
- HolySheep AI 비용 최적화 적용
"""

from fastapi import FastAPI, UploadFile, File, Form, HTTPException
from fastapi.responses import JSONResponse
import httpx
import asyncio
from datetime import datetime

app = FastAPI(title="멀티모달 商品 검색 API")

HolySheep AI 설정

HOLYSHEEP_API_KEY = "YOUR_HOLYSHEEP_API_KEY" HOLYSHEEP_BASE_URL = "https://api.holysheep.ai/v1"

비용 추적

COST_TRACKER = { "text_embedding_calls": 0, "image_embedding_calls": 0, "total_cost_usd": 0.0 } EMBEDDING_COST = { "text_per_1m_tokens": 0.10, # $0.10/1M 토큰 "image_per_call": 0.0004 # $0.0004/이미지 } class EmbeddingService: """HolySheep AI 임베딩 서비스 래퍼""" def __init__(self, api_key: str): self.api_key = api_key self.base_url = HOLYSHEEP_BASE_URL async def get_text_embedding(self, texts: List[str]) -> List[List[float]]: """비동기 텍스트 임베딩""" async with httpx.AsyncClient(timeout=30.0) as client: response = await client.post( f"{self.base_url}/embeddings", headers={"Authorization": f"Bearer {self.api_key}"}, json={ "model": "clip-vit-large-patch14", "input": texts } ) if response.status_code != 200: raise HTTPException( status_code=response.status_code, detail=f"임베딩 실패: {response.text}" ) # 비용 계산 COST_TRACKER["text_embedding_calls"] += 1 total_tokens = sum(len(t.split()) for t in texts) * 1.3 # 추정 토큰 COST_TRACKER["total_cost_usd"] += (total_tokens / 1_000_000) * EMBEDDING_COST["text_per_1m_tokens"] return [item["embedding"] for item in response.json()["data"]] async def get_image_embedding(self, image_base64: str) -> List[float]: """비동기 이미지 임베딩""" async with httpx.AsyncClient(timeout=30.0) as client: response = await client.post( f"{self.base_url}/embeddings", headers={"Authorization": f"Bearer {self.api_key}"}, json={ "model": "clip-vit-large-patch14", "input": [{ "type": "image_url", "image_url": {"url": f"data:image/png;base64,{image_base64}"} }] } ) if response.status_code != 200: raise HTTPException( status_code=response.status_code, detail=f"이미지 임베딩 실패: {response.text}" ) # 비용 계산 COST_TRACKER["image_embedding_calls"] += 1 COST_TRACKER["total_cost_usd"] += EMBEDDING_COST["image_per_image"] return response.json()["data"][0]["embedding"]

전역 서비스 인스턴스

embedding_service = EmbeddingService(HOLYSHEEP_API_KEY) search_engine = MultimodalSearchEngine( HolySheepMultimodalEmbedder(HOLYSHEEP_API_KEY) ) @app.post("/api/v1/search") async def search_products( query: str = Form(None), image: UploadFile = File(None), search_mode: str = Form("joint"), top_k: int = Form(10) ): """ 멀티모달 商品 검색 엔드포인트 Params: query: 텍스트 검색어 (선택) image: 검색용 이미지 파일 (선택) search_mode: 'text', 'image', 'joint' (기본값: joint) top_k: 반환 결과 수 (기본값: 10) Returns: 검색 결과 목록 (상품 ID, 유사도 점수, 메타데이터) """ start_time = datetime.now() try: query_image_base64 = None if image: contents = await image.read() query_image_base64 = base64.b64encode(contents).decode("utf-8") # 검색 모드에 따른 검색 if search_mode == "text" and query: results = search_engine.search(query=query, top_k=top_k) elif search_mode == "image" and query_image_base64: results = search_engine.search( query_image=query_image_base64, mode=SearchMode.IMAGE_ONLY, top_k=top_k ) elif search_mode == "joint" and (query or query_image_base64): results = search_engine.search( query=query, query_image=query_image_base64, mode=SearchMode.JOINT, top_k=top_k ) else: raise HTTPException( status_code=400, detail="query 또는 image 중 하나 이상 제공 필요" ) elapsed_ms = (datetime.now() - start_time).total_seconds() * 1000 return JSONResponse({ "success": True, "results": [ { "item_id": r.item_id, "score": round(r.score, 4), "metadata": r.metadata } for r in results ], "performance": { "latency_ms": round(elapsed_ms, 2), "search_mode": search_mode } }) except Exception as e: raise HTTPException(status_code=500, detail=str(e)) @app.get("/api/v1/stats") async def get_stats(): """비용 및 사용량 통계 반환""" return JSONResponse({ "text_embedding_calls": COST_TRACKER["text_embedding_calls"], "image_embedding_calls": COST_TRACKER["image_embedding_calls"], "total_cost_usd": round(COST_TRACKER["total_cost_usd"], 6), "estimated_monthly_cost": round(COST_TRACKER["total_cost_usd"] * 1000, 2) # 확장 예측 })

실행: uvicorn main:app --host 0.0.0.0 --port 8000

성능 벤치마크 및 비용 분석

작업 유형 평균 지연 HolySheep 비용 공식 API 비용 절감율
텍스트 임베딩 (10 토큰) 85-120ms $0.0000010 $0.0000010 동일
이미지 임베딩 1회 180-250ms $0.0004 $0.0004 동일
1,000개 이미지 배치 처리 3-5분 $0.40 $0.40 동일
联合 검색 (100회/일) 200-300ms $0.08/일 $0.08/일 -
월간 100,000회 검색 - $80/월 $80/월 결제 편의성 이점

💡 비용 최적화 팁:

자주 발생하는 오류와 해결책

오류 1: 이미지 임베딩 시 "Invalid image format" 오류

# ❌ 잘못된 예시 - Base64 인코딩 누락
response = requests.post(
    f"{self.base_url}/embeddings",
    json={
        "model": "clip-vit-large-patch14",
        "input": [{"type": "image_url", "image_url": {"url": image_path}}]
    }
)

✅ 올바른 예시 - Base64 Data URI 형식

import base64 def image_to_base64(image_path): with open(image_path, "rb") as f: return base64.b64encode(f.read()).decode("utf-8") response = requests.post( f"{self.base_url}/embeddings", json={ "model": "clip-vit-large-patch14", "input": [{ "type": "image_url", "image_url": {"url": f"data:image/png;base64,{image_to_base64(image_path)}"} }] } )

지원 형식: PNG, JPEG, WEBP (PNG 권장 - 무손실 압축)

오류 2: API 키 인증 실패 "401 Unauthorized"

# ❌ 잘못된 예시 - 잘못된 헤더 형식
headers = {"API_KEY": "YOUR_HOLYSHEEP_API_KEY"}  # Authorization 아님

❌ 잘못된 예시 - Bearer 토큰 누락

headers = {"Authorization": "YOUR_HOLYSHEEP_API_KEY"} # Bearer 없음

✅ 올바른 예시 - HolySheep AI 표준 인증

headers = { "Authorization": f"Bearer {HOLYSHEEP_API_KEY}", "Content-Type": "application/json" }

✅ API 키 유효성 검사 함수

def validate_holysheep_key(api_key: str) -> bool: """HolySheep AI API 키 유효성 검사""" response = requests.get( "https://api.holysheep.ai/v1/models", headers={"Authorization": f"Bearer {api_key}"} ) return response.status_code == 200

사용

if not validate_holysheep_key("YOUR_HOLYSHEEP_API_KEY"): raise ValueError("유효하지 않은 API 키입니다. https://www.holysheep.ai/register 에서 가입하세요")

오류 3: 벡터 차원 불일치 "Dimension mismatch"

# ❌ 잘못된 예시 - 다른 모델의 벡터 혼합
text_vec = embedder.encode_text(["hello"])[0]  # text-embedding-ada-002 (1536차원)
img_vec = embedder.encode_image(img)[0]         # CLIP (768차원)

❌ 검색 시 차원 불일치 발생

score = cosine_similarity(text_vec, img_vec) # 오류!

✅ 올바른 예시 - 동일한 CLIP 모델 사용

class UnifiedMultimodalEmbedder: """모든 입력을 CLIP 공간으로 통일""" def __init__(self, api_key: str): self.api_key = api_key self.base_url = "https://api.holysheep.ai/v1" self.model = "clip-vit-large-patch14" # 일관된 모델 사용 def encode(self, content: Union[str, Image.Image, bytes]) -> List[float]: """모든 입력 타입을 CLIP 768차원 벡터로 변환""" if isinstance(content, str): return self._encode_text([content])[0] else: return self._encode_image(content) def _encode_text(self, texts: List[str]) -> List[List[float]]: response = requests.post( f"{self.base_url}/embeddings", headers={"Authorization": f"Bearer {self.api_key}"}, json={"model": self.model, "input": texts} ) return [item["embedding"] for item in response.json()["data"]] def _encode_image(self, image_source) -> List[float]: # 이미지 → Base64 변환 로직 base64_img = prepare_image(image_source) response = requests.post( f"{self.base_url}/embeddings", headers={"Authorization": f"Bearer {self.api_key}"}, json={ "model": self.model, "input": [{"type": "image_url", "image_url": {"url": f"data:image/png;base64,{base64_img}"}}] } ) return response.json()["data"][0]["embedding"]

이제 모든 벡터가 768차원 CLIP 공간으로 통일됨

embedder = UnifiedMultimodalEmbedder("YOUR_HOLYSHEEP_API_KEY") text_vec = embedder.encode("따뜻한 커피") # 768차원 img_vec = embedder.encode(Image.open("a.jpg")) # 768차원 score = cosine_similarity(text_vec, img_vec) # ✅ 정상 작동

오류 4: Rate Limit 초과 "429 Too Many Requests"

# ❌ 잘못된 예시 - 동시 요청 폭주
async def bad_request(images):
    tasks = [encode_image(img) for img in images]  # 동시 100개 요청
    await asyncio.gather(*tasks)  # Rate Limit 발생!

✅ 올바른 예시 - Rate Limit 핸들링 및 지수 백오프

import asyncio import time from functools import wraps class RateLimitedEmbedder: """속도 제한이 있는 임베딩 서비스""" MAX_REQUESTS_PER_MINUTE = 50 request_timestamps = [] def __init__(self, api_key: str): self.api_key = api_key self.base_url = "https://api.holysheep.ai/v1" def _check_rate_limit(self): """1분 간 요청 수 확인""" now = time.time() # 1분 이전 요청 기록 삭제 self.request_timestamps = [ ts for ts in self.request_timestamps if now - ts < 60 ] if len(self.request_timestamps) >= self.MAX_REQUESTS_PER_MINUTE: oldest = self.request_timestamps[0] wait_time = 60 - (now - oldest) + 1 print(f"Rate limit 도달. {wait_time:.1f}초 대기...") time.sleep(wait_time) self._check_rate_limit() async def encode_with_retry(self, content, max_retries=3): """재시도 로직이 포함된 임베딩""" for attempt in range(max_retries): try: self._check_rate_limit() result = await self._do_encode(content) self.request_timestamps.append(time.time()) return result except Exception as e: if "429" in str(e) and attempt < max_retries - 1: # 지수 백오프 wait = (2 ** attempt) + random.uniform(0, 1) print(f"Rate limit 재시도 ({attempt+1}/{max_retries}), {wait:.1f}초 후...") await asyncio.sleep(wait) else: raise async def batch_encode(self, items: List, batch_size: int = 10): """배치 처리로 Rate Limit 우회""" results = [] for i in range(0, len(items), batch_size): batch = items[i:i+batch_size] print(f"배치 {i//batch_size + 1}: {len(batch)}개 처리 중...") batch_results = await asyncio.gather(*[ self.encode_with_retry(item) for item in batch ]) results.extend(batch_results) # 배치 간 대기 (Rate Limit 방지) if i + batch_size < len(items): await asyncio.sleep(1) return results

사용

embedder = RateLimitedEmbedder