자동화 파이프라인9 분 읽기

제가 끈 줄 알았던 옵션이 켜져 있었고, 소재 43건이 그렇게 탔습니다

MediaWiki API 불리언은 값이 아니라 존재로 참이 됩니다. exintro=0 한 줄이 본문을 292자로 잘랐고, 그 292자를 근거로 내린 '문서 부실' 판정이 정상 소재 43건을 영구 제외했습니다.

#api-gotchas#automation#data-pipeline#ledger#wikipedia
개념 도식: exintro=0을 거짓으로 믿었을 때와 실제로 참으로 읽혔을 때의 본문 길이 차이
글 내용을 요약한 개념 도식.

무인 다큐 채널 두 개가 매주 소재를 스스로 찾습니다. 위키백과 카테고리를 훑고, 후보 문서를 받아오고, LLM이 "이건 부검할 만한 사건인가"를 판정하고, 통과한 것만 대본으로 갑니다.

그 판정 원장을 열어봤습니다. 37건 심사, 통과 0건. 거부 사유를 세어보니 이랬습니다.

거부 사유 건수
문서 부실(본문 500자 미만) 20
"extract too thin / only gives…" 6
실질 사유(현존 기업·개념글 등) 11

54%가 "본문이 짧다"였습니다. 자연스러운 결론은 하나였습니다. 좋은 소재가 떨어졌구나. 카테고리를 더 넣을까, 스크린 기준을 낮출까.

여기서 질문 하나를 먼저 던져야 했습니다. 거부 사유가 한 종류로 절반 넘게 쏠릴 때, 의심할 것은 소재입니까 아니면 그 판정의 입력입니까?

1. 500자짜리 문서일 리가 없는 이름들

태워진 목록을 눈으로 훑다가 걸리는 게 있었습니다. ef-hutton. E. F. Hutton은 1904년에 세워져 1988년에 팔린 대형 증권사입니다. 위키백과 문서가 500자일 리가 없습니다. commerce-one, art-van-furniture도 마찬가지였습니다.

직접 재봤습니다.

'E. F. Hutton & Co.'   len=292
'Commerce One'         len=485
'Art Van Furniture'    len=334
'Computer.com'         len=298

문서가 짧은 게 아니었습니다. 추출기가 도입부만 가져오고 있었습니다.

2. 원인은 한 줄, 그리고 그 한 줄은 문법적으로 멀쩡했습니다

r = _wiki_get({
    "action": "query", "prop": "extracts", "explaintext": "1", "exintro": "0",
    "titles": title, "redirects": "1", "format": "json"})

exintro는 "도입부만 주세요" 옵션입니다. 끄려고 "0"을 넣었습니다.

MediaWiki API의 불리언 파라미터는 HTML 체크박스처럼 동작합니다. 값이 무엇이든 파라미터가 존재하면 참입니다. exintro=0도, exintro=false도, exintro=no도 전부 "도입부만 주세요"입니다. 거짓으로 두는 유일한 방법은 키를 아예 빼는 것입니다.

키를 지우고 같은 호출을 다시 했습니다.

문서 수정 전 수정 후
E. F. Hutton & Co. 292자 13,021자
Art Van Furniture 334자 6,729자
Charming Charlie 100자 2,428자
Air France 447 63,954자

44배였습니다. 판정기는 본문의 2%를 읽고 "이 사건은 부검할 만한 서사가 없다"고 쓰고 있었습니다.

3. 더 나쁜 건 그 판정이 재고를 태웠다는 것입니다

이 파이프라인에는 규칙이 하나 있습니다. 사유 없는 제외 금지. 수집 실패·API 실패·타임아웃은 원장에 기록하지 않고 다음 회차에 다시 시도합니다. 판정이 난 적 없는 소재를 태우던 버그를 이틀 전에 고치면서 세운 규칙입니다.

이번 건은 그 규칙을 통과합니다. "문서 부실(500자 미만)"이라는 사유 문자열이 멀쩡히 남아 있으니까요. 그래서 43건이 정당한 판정으로 위장한 채 영구 제외 목록에 앉아 있었습니다.

사유가 있는지만 봤지, 그 사유를 만든 입력이 정상이었는지는 아무도 안 물었습니다.

4. 당신이라면 무엇부터 하시겠습니까

큐가 마르고, 스크린이 계속 전멸하고, 거부 사유의 절반이 "입력이 부실하다"입니다.

  • (a) 후보 카테고리를 늘린다
  • (b) 스크린 기준을 완화한다
  • (c) 거부당한 것 중 당신이 아는 유명한 이름을 하나 골라 입력을 직접 재본다

저는 (a)를 이틀 전에 이미 했습니다. 후보 풀은 늘었는데 통과율은 그대로였습니다. (b)는 사실 채널에서 하면 안 되는 일이고요. 답은 (c)였고, 30초면 끝나는 일이었습니다.

5. 고친 뒤 첫 실행

[discover] 후보 473개 — 검색수요 상위: Barings Bank(1.1), Roberto Calvi(1.1), …
[discover] 채택: barings-bank-collapse ← Barings Bank

나흘 전에는 후보 94개에 12건을 심사하고 전멸이었습니다. 지금은 후보 473개에 첫 후보가 통과했습니다. 무효 판정 43건을 되살렸더니(사유 문자열이 입력을 탓하는 것만 골랐습니다) 그 안에 TheGlobe.com, Broadcast.com, Dressbarn, Lake Nyos가 있었습니다.

재고는 2편에서 6편·4편이 됐습니다. 거부 사유도 이제 실질적입니다 — "2023년 사건이라 유족 생존·형사 재판 진행 중", "개념 설명 문서라 특정 사건 아님". 전문을 읽고 내리는 판정입니다.

회귀는 네트워크 없이 막았습니다. 파라미터만 봅니다.

D._wiki_get = lambda params: (sent.update(params), R())[1]
assert "exintro" not in sent, sent   # 값이 무엇이든 있으면 도입부만 온다

자가진단 3가지

  1. 거부·실패 사유를 세어보셨습니까? 한 종류가 절반을 넘으면 그건 대상의 성질이 아니라 당신 파이프라인의 성질일 가능성이 높습니다.
  2. 영구 제외 목록에 넣기 전에, 그 판정의 입력 길이를 로그에 남깁니까? 사유 문자열만으로는 "정당한 판정"과 "망가진 입력으로 내린 판정"이 구분되지 않습니다.
  3. 쓰는 API의 불리언이 값을 보는지 존재를 보는지 확인하셨습니까? MediaWiki, 그리고 같은 관습을 쓰는 여러 API에서 flag=0은 끄는 게 아닙니다.

솔직한 부분

이건 대단한 디버깅이 아닙니다. 유명한 회사 이름 하나를 눈으로 알아보고 len()을 찍어본 게 전부입니다. 그런데 그 30초를 안 하고 이틀 동안 "소재가 부족하다"는 전제 위에서 다른 일을 했습니다. 태워진 43건은 그 이틀 내내 목록에 있었고, 사유까지 적혀 있었습니다.

읽지 않았을 뿐입니다.

지금 당신 파이프라인의 거부 사유를 sort | uniq -c 한 번 해보세요. 1위가 무엇이고, 그게 정말 대상 탓인지 한 건만 손으로 확인해 보시면 됩니다. 저처럼 30초에 끝날 수도 있습니다.

관련 글