클로드 코드로 자동화 만들기 #2 로그 실패 집계에서 tool_use_error로 진짜만 거르는 법

클로드 코드로 자동화 만들기8편 중 2번째

로그에서 실패를 문자열로 세면 정상 출력과 문서 안의 메모까지 실패로 올라온다. 집계 숫자를 받았으면 목록을 열어 종료 코드나 도구 에러 태그가 붙은 줄만 실패로 센다.

하루치 세션 로그를 읽어 작업일지와 발행 원고를 뽑는 스크립트를 9월 4일에 처음 돌렸다. 퇴사하고 AI로 회사를 만드는 중이라 기록부터 기계에 넘기는 참인데, 나온 원고가 읽히지 않았다. 원인을 찾으려고 추출된 원자료 맨 위를 봤더니 실패 신호 10건이라고 찍혀 있었다.

막대 두 개. 자동으로 센 실패 신호 10건 중 손으로 걸러 실제로 막힌 것은 2건이었다.

실패가 열 건이면 글감은 넘친다고 봤다

막힌 지점이 그대로 글감이다. 맨 위 집계에는 세션 4개, 도구 호출 60회, 그중 Bash 54회가 같이 찍혀 있었다. 예순 번 움직여 열 번 막혔다면 비율도 그럴듯했다. 열 건이면 그날 쓸 이야기는 충분하고, 원고가 안 읽히는 건 문장을 다듬는 문제라고 생각했다. 그 아래 붙은 “막힌 지점” 목록을 한 줄씩 연 건 그다음이었다.

열어보니 정상 출력이었다

배포 도구 wrangler로 계정을 확인하는 명령이 올라온 항목은 원자료에서 명령 자체가 중간에 잘려 있었고, 실패의 근거로 붙어 있는 줄은 그 명령이 권한을 나열한 출력이었다. 그 세션은 거기서 멈추지 않고 일괄 치환과 빌드, 배포까지 그대로 이어졌다.

~/.config/secrets/README.md를 읽은 명령도 막힌 지점으로 올라와 있었다. 근거로 붙은 줄은 그 파일 안의 문장이었다 — 봇에 Start를 누르지 않으면 “chat not found”가 뜬다는, 내가 같은 날 적어둔 메모다. 유튜브 영상을 분석한 세션에서는 자막 한 줄([4:44] and everything that failed and you can)이 막힌 지점으로 잡혀 있었다.

가장 여러 번 올라온 건 이 줄이다. 그 항목의 명령은 소스 파일의 105125행을 화면에 찍는 sed였고, 실패의 근거로 붙은 줄은 그 105125행 안에 있던 코드 한 줄이었다.

if (!res.ok) throw new Error(`search HTTP ${res.status}`);

실행된 적이 없는 줄이다. 파일을 읽어 화면에 뿌린 출력 안에 Error라는 단어가 들어 있었을 뿐인데, 같은 줄이 열 건짜리 목록에 세 번 들어왔다. 세 번 중 하나는 추출기를 돌린 명령이 남긴 것이었다. 추출기가 자기 출력에서 같은 줄을 다시 집어 실패로 셌다는 뜻이다. 남은 하나는 그 줄이 인용된 블로그 원고를 화면에 띄운 명령이었다.

같은 목록에 진짜 실패도 있었다.

- 17:43 `Skill`
  → <tool_use_error>Unknown skill: 작업일지</tool_use_error>

커맨드 이름으로 skill을 부르려다 그런 이름의 skill이 없어서 멈춘 줄이다. 열 줄을 다 열고 나서야 원고가 안 읽힌 이유가 보였다. 문장이 서툴러서가 아니라, 원고가 받아 쓴 목록에 애초부터 실패가 아닌 줄이 섞여 있었고 나는 맨 위 집계 한 줄만 보고 그 열 건을 전부 사건으로 믿은 것이다.

세는 기준을 확인하지 않았다

추출기가 무슨 기준으로 골랐는지 코드로 확인하지는 않았다. 원인 모름. 재현 조건은 하루치 세션 로그를 그 스크립트에 넣는 것이고, 잘못 올라온 줄들의 공통점은 error·failed·not found·chat not found 같은 문자열이 어딘가에 들어 있었다는 것뿐이다.

함정의 조건은 도구 출력을 문자열로 훑어 실패를 세는 것이다. 증상은 에러가 나는 게 아니라 숫자가 조용히 부풀어서, 정상 출력과 문서 안의 메모와 남의 자막이 진짜 실패와 같은 무게로 한 목록에 섞인다. 회피법은 두 가지다. 판정을 종료 코드와 <tool_use_error> 같은 도구 에러 태그로 좁히고, sedcat처럼 파일을 화면에 찍는 명령의 출력은 검사에서 뺀다. 소스 코드와 문서에는 에러 문자열이 원래 들어 있다. 그날은 못 고쳤고 목록을 손으로 걸러 썼다.

집계가 준 수와 도구 에러 태그로 좁힌 수가 크게 벌어지면 집계 기준을 먼저 열어본다. 로그에서 실패 건수를 자동으로 세는 도구를 쓰신다면, 그 숫자를 글이나 보고서로 옮기기 전에 목록을 한 줄씩 열어보세요. 저는 열 줄을 다 열기 전까지 그중 무엇이 진짜 실패인지 몰랐습니다.

손으로 걸러 남은 건 둘

그날 작업일지에 막힌 것으로 남긴 건 두 건이다. 하나는 네이버 검색 API가 개발자센터에서 NAVER API HUB로 이관되면서 기존 Client ID/Secret이 폐기된 것. 주소와 헤더 이름이 둘 다 바뀌어서 주소만 고치면 여전히 안 된다. 16시 54분부터 17시 34분까지 약 40분 걸렸고, 새 키를 발급받아 파일 4줄을 고쳤다. 응답 바디 구조는 그대로라 파싱 코드는 손대지 않았다.

다른 하나는 텔레그램 알림의 not sent: Bad Request: chat not found다. 새로 만든 봇은 수신자가 먼저 Start를 눌러야 첫 메시지가 나간다. 17시 19분부터 17시 30분까지 약 10분 만에 원인까지는 왔는데 실제 전송 성공은 아직 못 봤다. 봇에 Start를 누르는 게 다음 순서다.

← 전체 글