에이전트가 이미 해결된 질문을 다시 던지다. 나중에 이를 수정한 원장(ledger)이 구식 답변을 방어하다
요약
본 기사는 Claude Code 에이전트가 과거의 결정을 참조하고, 이미 답변된 질문을 재차 던지는 문제를 해결한 과정을 다룹니다. 모든 소유자의 결정과 에이전트의 질문 기록을 append-only 파일로 분리하여 관리함으로써 데이터 무결성을 확보했습니다.
핵심 포인트
- 에이전트는 과거 결정을 확인하도록 지시받았으나, 이를 개선할 필요성이 발견됨.
- 모든 소유자 결정은 변경 불가능한(append-only) JSONL 파일로 저장되어 신뢰도를 높임.
- 새로운 CLI와 데이터 구조를 도입하여 질문과 결정의 상태 변화를 명확하게 관리함.
저희 에이전트는 소유자에게 53일 전에 이미 결정된 질문을 했습니다. CLAUDE.md는 이미 과거 결정을 먼저 확인하도록 지시했습니다. 그래서 우리는 모든 소유자의 결정을 오직 하나의 명령어만 쓸 수 있는 append-only 파일로 옮겼습니다. 36일 후, 이 파일에는 125개의 판결이 담겨 있고, 질문에 대한 답변은 평균 7.5시간 만에 얻어지며, 오류 로그를 보면 해당 명령어가 이미 결정된 질문을 거부하는 것이 정확히 한 번 기록되어 있습니다. 또한, 소유자가 채팅에서 이미 철회한 답변을 뒷받침하는 데 23일이 걸렸습니다.
돌아온 질문
저희 Claude Code 에이전트는 자체적으로 작은 출판 작업을 수행합니다. 인간 소유자는 에이전트에게 목표를 부여하고, 에이전트가 단독으로 결정할 수 없는 질문에 답변합니다. 각 실행은 보고서로 끝나며, 그 보고서의 마지막 섹션에는 에이전트가 소유자로부터 필요한 항목들의 번호 목록이 있습니다.
2026-07-17일, 소유자는 저희 기사 푸터(article footers)에 무엇을 포함해야 하는지에 대한 질문 중 하나에 답변했습니다. 이 답변은 에이전트가 참고하도록 되어 있는 두 곳, 즉 리서치 노트와 docs/CHANGELOG.md 파일에 기록되었습니다. 당시 저희 CLAUDE.md는 번호 매긴 단계에서 에이전트가 모든 실행을 읽도록 했습니다 (일본어에서 번역됨): “결정을 내리기 전에 항상 세션 메모리와 docs/CHANGELOG.md를 참고하여, 과거 결정을 알지 못한 채 전략, 목표, 전술 또는 가격을 재결정하지 않도록 하라.”
41일 후인 2026-08-27일, 에이전트는 그날 버전 노트에 수정 사항을 기록했습니다 (커밋 7b785360): 최신 요청 목록의 두 항목은 이미 결정되었으며, 푸터 질문과 이미 존재하는 무언가에 대한 요청이었습니다. 이 수정 사항이 커밋되자, docs/CHANGELOG.md는 581,385 바이트였고 176개의 버전 항목을 담고 있었습니다.
2026-09-08일, 에이전트는 푸터 질문을 다시 했습니다. 소유자가 되돌려준 내용은 다음과 같습니다:
돌이켜보면, CLAUDE.md 파일에는 두 가지 약점이 있었습니다. 오직 '결정(a decision)'에 대해서만 발동했고, 소유자에게 질문하는 것이 명백한 결정이라고 보기는 어렵다는 점입니다. 또한 '상의하다(consult)'는 에이전트가 존재하지 않는다고 알았던 답변을 580KB 파일에서 검색한다는 의미였습니다. '히스토리를 확인하라'는 규칙은 에이전트가 이미 히스토리가 있다고 의심할 때만 작동합니다.
그날 저녁에 구축한 것들
수정 사항은 커밋 6dde49dd (2026-09-08, 22:05 JST)에 반영되었습니다. 여기에는 두 개의 append-only JSONL 파일과 하나의 CLI가 포함되어 있습니다.
state/owner-rulings.jsonl은 줄당 한 명의 소유자 결정(owner decision)을 담고 있습니다. 각 행은 id, kebab-case 형식의 하나 이상의 토픽 키, 소유자가 답변한 시간(ruledAt), 최소 20자의 요약, 답변이 나온 출처를 나타내는 source, 그리고 기록된 시간을 포함합니다. state/owner-asks.jsonl은 에이전트가 소유자에게 던진 질문을 줄당 하나씩 담고 있으며, 상태는 open, answered 또는 withdrawn입니다. CLI는 절대 행을 제자리에서 수정하지 않습니다. 변경된 질문은 동일한 id를 가진 새 행이며, 변경된 결정은 새로운 id를 가진 새 결정(ruling)입니다.
pnpm owner-ask가 유일한 작성기(writer)이며, 여섯 가지 동작(propose, ruling, withdraw, revise, lookup, list)을 가지고 있습니다. propose가 질문을 등록할 때, 먼저 해당 질문과 토픽 키를 공유하는 모든 결정(ruling)을 찾습니다:
export function findRulingsForTopics({
rulings,
topicKeys,
...
만약 일치하는 것이 있다면, propose는 각 일치하는 판결(ruling)의 날짜와 요약을 출력하고 코드 1로 종료합니다. 통과할 수 있는 유일한 방법은 판결 이후 무엇이 변경되었는지 설명하는 최소 20자 길이의 reaskReason을 제공하는 것입니다. ruling은 소유자의 답변을 기록하며, 해당 질문 ID를 기반으로 그 질문에 대한 답변 완료 상태로 표시합니다. 이미 존재하는 ruling id는 거부되므로, 결정을 변경하는 방법은 새로운 것을 추가(append)하는 것입니다. 저희의 실행 종료 검사(end-of-run check)는 열려 있는 모든 질문을 출력하고, 보고서(report)는 그 목록을 복사하며, CLAUDE.md는 에이전트에게 소유자가 중지하라고 말할 때까지 매번 이 목록을 계속 보여주도록 지시합니다.
첫 번째 커밋에서 14개의 이전 결정 사항을 ledger에 복사했으며, 각각은 출처와 함께 7월의 footer ruling까지 거슬러 올라갑니다. 그 행들 중 마지막 것이 작성된 후 10초 만에, 같은 빌드 과정 중에 CLI는 footer 질문에 대한 propose를 거부했습니다. 이 거부는 여전히 저희 오류 로그에서 유일한 '이미 확정된' 거부입니다. ruling을 찾아 에이전트가 질문을 포기하게 만드는 lookup은 아무 기록도 남기지 않으므로, 이 카운트는 얼마나 많은 재요청(re-asks)이 propose 단계까지 도달하지 못했는지에 대해서는 아무것도 말해주지 않습니다.
10일 후인 2026-09-18 (커밋 4ee80946)에, 저희는 CLAUDE.md 스텝 자체를 변경했습니다. 이제 더 이상
원장(ledger)에는 125개의 판결(ruling)이 기록되어 있습니다. 이 중 등록된 질문에 대한 답변은 53개뿐입니다. 나머지 72개는 소유주가 채팅에서 직접 제공한 지침으로 ruling을 통해 기록되었거나, 첫날 복사된 오래된 결정 사항들입니다. 대부분의 결정 사항은 애초에 질문으로 시작하지 않았습니다.
에이전트는 총 72개의 질문을 등록했습니다. 이 중 53개는 답변을 받았고, 9개는 철회되었으며, 10개는 여전히 미결 상태입니다. 답변을 받은 53개 질문의 경우, 등록부터 소유주의 답변까지의 중앙값 시간은 7.5시간이었습니다. 가장 빠른 것은 0.1시간이었고, 가장 느린 것은 67.4시간이었습니다. 가장 오래된 미결 질문은 2026-09-14 (UTC)에 등록되었으며, 2026-10-13까지 실행 종료 확인(end-of-run check) 목록에 29일 동안 올라와 있었습니다. 이것이 소유주가 답변하지 않았을 때 '답변될 때까지 매번 보여주기' 기능의 모습입니다.
토픽 키(topic-key) 검사는 우리가 예상했던 것과는 다르게 작동했습니다. 72개 질문 중 28개는 이미 판결(ruling)이 내려진 토픽에 등록되었으며, 각각 작성된 reaskReason을 가지고 있었습니다. 이 28개 중 18개는 소유주가 매주 승인해야 하는 성장 전술(growth tactics)에 대한 주간 요청이었거나, 또는 주중에 수치가 변경되어 다시 승인이 필요한 경우였습니다. 나머지 10개는 새로운 사실을 제시했습니다. 예를 들어, 작동하지 않는 것이 생겼다거나, 정리 작업으로 예상보다 적은 디스크 공간이 확보되었거나, 이전 판결이 다루지 않았던 새 항목이 발생한 경우 등입니다. 따라서 이 검사는 질문을 차단하는 경우가 거의 없습니다. 대신 에이전트가 다시 질문하기 전에 무엇이 새로운지 말하도록 만들고, 실행 종료 확인(end-of-run check)은 그 이유를 질문 아래에 출력하여 소유주가 보고서에서 확인할 수 있게 합니다.
에이전트를 실행하는 기계의 오류 로그에는 16개의 실패한 owner-ask 호출 기록이 있습니다. 이 중 하나는 푸터 거부(footer refusal)입니다. 일곱 개는 평이한 언어 설명 없이 전문 용어를 사용한 질문 거부, 한 개는 그 자체로 의미가 없어 거부된 경우, 네 개는 형식이 잘못된 토픽 키를 가진 경우, 그리고 세 개는 우리가 전달한 잘못된 파일 경로였습니다.
결정 또한 변경됩니다. 125건의 판정(rulings) 중 35건은 이전 판정과 적어도 하나의 주제 키를 공유합니다. 이 중 8건에서는 판정문의 내용이 특정 이전 판정을 대체하거나, 폐지하거나, 축소한다고 명시하고 있습니다. 개인 메시지에 대한 답장이 소유자의 승인이 필요한지 여부는 2026-09-16일에는 '예'였고, 2026-09-18일에는 '아니오'였다가, 다시 2026-09-27일에는 '예'로 바뀌었으며, 각 후속 판정은 자신이 무효화하는 이전 판정을 명시합니다. 읽기 전용 메일 자격 증명(read-only mail credential)을 누가 생성하는지에 대한 결정은 소유자(2026-09-28)에서 에이전트로, 그리고 다시 소유자로 바뀌었으며, 이 두 가지 변경 사항 모두 2026-10-03일에 이루어졌습니다. 서술적인 글로는 그러한 번복이 서로 다른 두 문장으로 바뀝니다. 하지만 원장(ledger)에서는 새로운 행(row)이 생기고, 조회 시 가장 최신 판정부터 반환됩니다.
원장이 오래된 답변을 지지했던 23일 동안
7월 푸터에 대한 판정이 원장이 존재하게 된 이유였습니다. 또한 이 판정은 원장이 잘못 처리한 판정이기도 했습니다.
2026-09-14일에 소유자는 프로모션이 중단되었음에도 불구하고 왜 여전히 우리 기사들로부터 트래픽이 발생하는지 물었습니다. 에이전트의 답변은 다음 날 버전 노트에 기록되었는데, 7월 판정을 지적하며 소유자가 승인했기 때문에 모든 푸터가 홍보 링크를 포함하고 있었다고 설명했습니다. 또한 푸터가 없는 두 개의 오래된 기사를 찾아내어 푸터를 추가하기도 했습니다. 소유자의 질문은 실질적으로 새로운 결정이었습니다. 이 질문이 질문의 형태로 표현되었기 때문에, 에이전트는 이를 ruling으로 기록하지 않았고, 신규 기사에 푸터가 필요하다는 검사(check)는 이전과 동일하게 유지되었습니다.
2026년 10월 7일 소유자가 다시 말했다 (일본어 번역): “거기서는 프로모션이 금지되어야 합니다. 제가 얼마 전에 그렇게 말했었습니다.” 그날 아침에 기록된 판결문은 자체 텍스트에서 이 판결문이 2026년 7월 17일의 판결문을 무효화한다고 명시하고 있다. 커밋 56fcbb6d는 이후 게시된 100개 기사와 초안 70개에서 프로모션을 제거했다. 또한, 발행 API로 무엇인가가 전송되기 직전 마지막 단계를 포함하여 네 지점에 프로모션 검사를 추가했다. 해당 검사의 헤더 주석에는 이제 원인이 기록되어 있다: 2026년 9월 14일의 지침이 판결문 장부(rulings ledger)에 복사되지 않았고, 푸터 요구사항은 유지되었기 때문에 프로모션이 2026년 10월 7일까지 모든 기사에 계속 작성되었다.
장부는 오직 자신을 쓰는 사람의 것만 안다. 125개의 판결문 중 72개가 채팅에서 나온 것이므로, 에이전트는 소유자가 말한 무언가가 결정이라는 것을 알아차려야 했다. 하지만 2026년 9월 14일에는 그렇지 못했다. 더 나쁜 것은, 장부가 오래된 답변을 에이전트가 날짜와 출처를 들어 소유자에게 인용할 수 있는 무언가로 만들었다.
CLAUDE.md와 자동 메모리가 다르게 작동하는 방식
비교하기 전에 Claude Code memory 문서를 확인했다. 문서에 따르면 CLAUDE.md 파일과 자동 메모리는 “모든 대화 시작 시 로드되며” 그리고 “Claude는 이를 강제되는 설정이 아닌 컨텍스트로 취급한다”고 되어 있다. 또한, “두 지침이 서로 모순될 경우, Claude가 임의로 하나를 선택할 수 있다”고 경고한다. 자동 메모리는 Claude가 자신을 위해 메모를 저장하는 곳이며, 그 노트 유형 중 하나는 project이다: “진행 중인 작업, 마감일, 그리고 Claude가 코드나 git 기록으로부터 파생시킬 수 없는 결정들.” 따라서 이 문서는 자동 메모리를 이러한 결정들의 보관소로 지적하고 있다.
우리가 소유자 결정(owner decisions)을 외부에서 제외하게 만든 세 가지 이유가 있습니다. '자동 메모리(Auto memory)는 기기 로컬(machine-local)'이며, 문서에는 해당 파일들이 '기기 간 또는 클라우드 환경에서 공유되지 않는다'고 추가되어 있지만, 우리의 예약된 건강 검사(scheduled health check)는 GitHub Actions에서 실행되며 예상치 못한 아티클에 이미 소유자 결정이 있는지 확인하기 위해 동일한 판결 파일(rulings file)을 조회합니다. Claude가 자동 메모리에 무엇을 보관할지 결정합니다. MEMORY.md가 한계에 가까워지면, Claude Code는 Claude에게 '오래된 항목들을 병합하거나 삭제하라'고 상기시키지만, 우리가 대체한 판결은 여전히 그를 대체한 판결과 함께 우리의 원장(ledger)에 남아 있습니다. 그리고 대화 시작 시에는 'MEMORY.md의 처음 200줄 또는 처음 25KB'만 로드됩니다. 우리의 판결 파일 크기는 109,949바이트입니다. 만약 이것이 MEMORY.md였다면, 가장 오래된 항목들(7월 푸터 판결 포함)을 포함하여 약 36줄 정도가 로드될 것입니다.
산문(prose)으로는 절대 할 수 없는 것이 바로 명령으로 읽히는 것입니다. 우리의 저장소에는 CLI를 제외하고도 13개의 소스 파일이 이 판결 파일을 불러옵니다. 예를 들어, 이미 게시된 아티클의 마지막 단락을 재작성하는 명령어는 해당 아티클을 편집할 주제 키(topic key)를 가진 판결 ID가 제공되지 않으면 거부되며, 하나의 판결 아래에서 같은 아티클을 두 번 재작성하지 않습니다.
AI 자동 생성 콘텐츠
본 콘텐츠는 Dev.to AI tag의 원문을 AI가 자동으로 요약·번역·분석한 것입니다. 원 저작권은 원저작자에게 있으며, 정확한 내용은 반드시 원문을 확인해 주세요.
원문 바로가기