본문 바로가기

시스템/Devops

Cohere 임베딩 429 실패, 이미지 오류가 아닌 호출 한도부터 확인

글·그림 10,000건 배치의 실패 로그에서 호출 한도를 확인하고, 실패 ID 256건만 재실행해 누락을 복구했다. 첫 실행 결과를 보니 글·그림 10,000건 중 임베딩된 건 9,744건이었다. 256건이 실패했고, 로그에는 429가 남아 있었다.

전제 환경 — Cohere 임베딩 / Foundry / Python / JSON 작업 로그

한 줄 답 — Cohere 글·그림 임베딩의 429 실패는 이미지 오류가 아니라 호출 한도였으며, 실패 ID 256건만 재실행해 실패 0건을 확인한 뒤 인덱스를 생성하도록 순서를 분리했다.

이 오류를 봤다면

429

요약

  • 배치 실패 예외와 이미지 건너뜀·거부 메시지를 별도로 집계한다.
  • 실패 ID 파일의 256건만 --ids-file로 재실행한다.
  • 재실행 대상 256건·임베딩 256건·실패 0건을 확인한 뒤 인덱스를 생성한다.

왜 이런 일이 생기나

실패 로그를 원인별로 분류해 보니, 그림 다운로드나 이미지 거부가 아니라 호출 한도 문제였다. 재시도 가능한 상태 예외는 로그에서 3회 집계됐지만, 실패 문서는 256건이었다. 여기서 재실행할 건수를 정할 때는 예외 횟수가 아니라 실패 ID 목록을 봐야 했다. 로그 이벤트 횟수와 실패 항목 수는 다른 단위다.

첫 실행의 이미지 캡션은 8,206건, 캡션 누락은 3건이었다. 텍스트 없음과 건너뜀은 각각 0건이었다. 캡션 누락은 이미지 수신 실패나 이미지 거부와 별도 집계이므로 같은 오류로 합치지 않는다.

 

호출 한도 때문에 글·그림 10,000건 처리에 약 1시간이 걸렸다. 이 소요 시간을 보고 원본 84만 건으로 확대하기 전에는 Foundry의 TPM 쿼터를 늘려야 한다고 판단했다. 확대 작업을 완료한 기록은 없으므로 전체 소요 시간은 측정값으로 제시할 수 없다.

10,000건의 글 토큰 근사치는 2,414,979였다. Cohere 공개 단가로 계산하면 글만 한 번은 약 0.29달러, 글만과 글·그림 두 번은 약 0.58달러다. 최종 설명에서는 약 480만 글 토큰과 약 0.6달러로 요약했다. Foundry 가격은 미리보기라 공개되지 않았으며, 이 계산은 Foundry 실제 청구액이 아니다. 그림 토큰은 아주 적었다는 기록만 있고 정확한 수치는 없다.

구조

실패 원인 분류와 누락 복구 순서

[10,000건 입력] → [글·그림 임베딩 호출]
                         ├→ [성공 9,744건]
                         └→ [429 실패 256건] → [실패 ID 파일]
                                                    ↓
                                             [256건만 재실행]
                                                    ↓
                                          [성공 256건 / 실패 0건]
                                                    ↓
                                               [인덱스 생성]

[JSON 로그] ─┬→ [배치 실패 예외 마지막 줄 → URL 치환 → 원인별 집계]
             └→ [이미지 건너뜀·거부 메시지 별도 집계]

주요 설정값

이름 값 무엇을 정하는가
--ids-file reports/failed_items.txt 재실행할 실패 ID 목록 입력. 파일명은 일반 이름으로 재작성한 예시.
TPM 현재 쿼터와 증설 수치는 기록에 없음 Foundry 대량 확대 전에 검토할 토큰 처리량 쿼터.

처리 순서

1. JSON 로그를 원인별로 분류한다

로그부터 보는 이유는 글과 그림을 함께 처리했다는 사실만으로 실패 원인을 정할 수 없기 때문이다. 배치 예외와 이미지 메시지를 나눠 보니 확인된 원인은 호출 한도였다.

진단 — 작업 로그 분석용 Python 예시 — 표시 문자열과 필드명을 실제 로그 형식에 맞춰 치환

import json
import re
from collections import Counter

counts = Counter()
with open("<작업 로그 파일>", encoding="utf-8") as logs:
    for line in logs:
        if "<배치 실패 표시>" in line:
            entry = json.loads(line)
            exception = entry.get("exception", "")
            lines = [part for part in exception.strip().splitlines()
                     if part.strip()]
            last = lines[-1]
            counts[re.sub(r"https?://\S+", "<URL>", last)[:200]] += 1
        elif "<이미지 건너뜀 표시>" in line or "<이미지 거부 표시>" in line:
            counts[json.loads(line).get("message")] += 1

for cause, count in counts.items():
    print(count, "×", cause)

출력 예

# 원문 출력은 노트에 없음. 출력 형식은 '횟수 × 원인'이며, 횟수는失敗文書数ではなく 로그 이벤트 수다.
# 관측 기록: 재시도 가능한 상태 예외 3회, 확인된 원인은 429 호출 한도.
# 이미지 건너뜀·거부 메시지는 배치 예외와 별도 갈래로 확인.
# 원인을 확인한 뒤 실패 ID 목록의 크기를 확인한다.

실패하면 — 자리표시자를 실제 로그 파일과 표시 문자열로 바꾸고, exception·message도 실제 로그의 필드명과 맞는지 확인한다. 예외의 마지막 줄을 읽는 부분에서 실패하면 해당 로그에 비어 있지 않은 예외 문자열이 있는지 확인한다. 원인을 분류하지 못했다면 해당 로그를 먼저 확인하고 실패 ID 재실행으로 넘어간다.

2. 재실행할 실패 ID 목록의 크기를 확인한다

예외는 3회였지만 복구할 문서는 256건이었다. 그래서 첫 실행에서 별도 저장한 실패 ID 파일을 재실행의 입력 기준으로 삼았다.

진단 — 작업 디렉터리 — reports/failed_items.txt를 첫 실행에서 저장한 실제 실패 ID 파일 경로로 치환

wc -l reports/failed_items.txt

출력 예

# 원문 출력은 노트에 없음.
# 화면의 줄 수와 파일명을 확인한다. 기록의 실패 ID 목록은 256건.
# 첫 실행의 실패 집계와 목록의 줄 수가 256건으로 일치하면 다음 단계로 진행한다.
# 다음 단계에도 여기서 확인한 동일한 파일 경로를 사용한다.

실패하면 — 줄 수가 첫 실행의 실패 256건과 다르면 첫 실행 집계와 실패 ID 파일을 대조한다. 목록이 집계와 맞는지 확인하기 전에는 재실행하지 않는다.

3. 실패한 ID만 재실행하고 완료 집계를 확인한다

성공한 9,744건은 다시 처리하지 않았다. 실패 ID 목록에 남은 256건만 복구하고, 그 결과를 확인한 뒤 인덱스를 만들도록 순서를 분리했다.

조치 — 입력 형태 예시 — 실행기는 기존 작업 명령으로, 파일 경로는 2단계에서 확인한 값으로 치환

<임베딩 재실행 명령> --ids-file reports/failed_items.txt

출력 예

# 원문 출력은 노트에 없음. 실행 완료 집계에서 아래 항목을 대조한다.
# 관측 기록: 대상 256건, 임베딩 256건, 실패 0건.
# 이미지 캡션 210건, 캡션 누락 0건, 텍스트 없음 0건, 건너뜀 0건.

실패하면 — 재실행 대상이 2단계에서 확인한 목록과 다르면 --ids-file에 넘긴 경로를 확인한다. 재실행 집계에 실패가 남으면 해당 실행의 배치 실패 원인을 다시 분류한다. 누락 복구가 끝나기 전에는 인덱스를 생성하지 않는다.

증상과 대처

증상 원인 조치
429 Cohere 임베딩 호출 한도 실패 ID만 재실행한다. 원본 84만 건으로 확대하기 전에는 Foundry TPM 쿼터를 검토한다.

끝났는지 확인하기

  • 재실행 대상 256건과 임베딩 256건이 일치한다.
  • 재실행 실패는 0건이다.
  • 재실행 이미지 캡션은 210건이며, 캡션 누락·텍스트 없음·건너뜀은 각각 0건이다.
  • 인덱스 생성 순서는 실패 ID 재실행 완료 이후다.

롤백

되돌리는 방법과 명령은 기록에 없다. 이 조치는 설정 변경이 아니라 실패 항목의 재실행이다.

정리

  • 로그 예외 횟수, 실패 ID 수, 재실행 성공 수를 서로 다른 집계로 다룬다.
  • 토큰 비용과 처리량은 별도로 판단한다. 낮은 계산 비용만으로 호출 한도 안에서의 완료 시간을 판단할 수 없다.

실측 요약

  • 10,000건 / 9,744건 / 256건 — 글·그림 임베딩 첫 실행은 대상 10,000건 중 9,744건을 처리했고 256건이 실패했다.
  • 3회 — 실패 로그에서 재시도 가능한 상태 예외는 3회 집계됐으며, 이는 실패 문서 수가 아니라 로그 예외 횟수다.
  • 256건 / 실패 0건 — 실패 ID만 재실행한 결과 대상 256건 모두 임베딩됐고 실패는 0건이었다.
  • 10,000건 / 약 1시간 — 호출 한도 때문에 글·그림 10,000건 임베딩에 약 1시간이 걸렸다.
  • 2,414,979 글 토큰 — 10,000건의 글 토큰 근사치는 2,414,979였으며, 그림 토큰의 정확한 수치는 기록되지 않았다.

자주 묻는 것

글과 그림 임베딩에서 429가 나면 이미지 오류인가요?

이 사례에서는 이미지 오류가 아니라 호출 한도였다. 배치 실패 예외의 마지막 줄을 원인별로 집계하고, 이미지 건너뜀·거부 메시지는 별도로 확인했다.

예외가 3번이면 실패한 문서도 3건인가요?

아니다. 이 작업의 예외 집계는 3회였지만 실패 문서는 256건이었다. 재실행 대상은 로그 이벤트 횟수가 아니라 별도 저장한 실패 ID 목록으로 정했다.

임베딩 배치가 일부 실패하면 전체를 다시 실행해야 하나요?

이 작업에서는 실패한 256건만 --ids-file로 재실행했다. 대상 256건·임베딩 256건·실패 0건을 확인한 뒤 인덱스를 생성하도록 순서를 분리했다.

임베딩 비용이 낮으면 대량 처리도 빨리 끝나나요?

이 사례에서는 그렇지 않았다. 공개 단가로 계산한 비용과 별개로 호출 한도 때문에 글·그림 10,000건에 약 1시간이 걸렸고, 원본 84만 건으로 확대하기 전 Foundry TPM 쿼터 증설이 필요하다고 판단했다.

이 기록으로 임베딩 모델의 품질 우열도 알 수 있나요?

알 수 없다. 후속 비교 보고서는 분류 깊이 여섯 단계로 다시 저장했지만, 단계별 정확도 수치가 노트에 없다.