Kun's Field Notes
그리고 그것을 경사 하강법으로 제대로 훈련시키는 방법
에이전트가 매 세션 읽는 프로젝트 CLAUDE.md/AGENTS.md — 감으로 고치는 대신, 실제 세션 기록을 학습 데이터 삼아 '훈련'할 수 있다면 그 루프는 정확히 어떻게 생겼는가?
프로젝트 지침 파일 = 가중치, 에이전트 세션 1회 = 순전파, "에이전트가 한 것과 내가 원한 것의 격차" = 손실, 트랜스크립트를 읽고 파일을 소폭 편집 = 역전파. 대부분의 사람은 순전파만 하고 역전파를 안 하기 때문에 파일이 비거나·붓거나·썩거나·갈라진다. 훈련 규율은 넷: 증거는 트랜스크립트의 원문 인용만, 업데이트는 배치로만, 스텝은 작게(패스당 편집 ~5개), 크기는 토큰 예산으로 고정하고 좁은 규칙은 스킬로 빼낸다.
요약만 볼 사람 — "Four bad states"(문제 진단 4종)를 훑고 "The mapping"(명사 치환표)만 정독하면 이 글의 아이디어는 손에 들어온다. 마지막의 훈련 노브 대응(--budget = 모델 크기, --max-edits = 학습률, --min-gap-evidence = 배치 크기)을 곁눈으로 확인하고 끝.
완독할 사람 — 순서대로 읽되 무게중심을 두 곳에 둔다. ① "Two files, two very different jobs": 유저 레벨은 손으로 쓰고 도구가 건드리지 못하게, 프로젝트 레벨만 훈련 대상 — 이 분리가 방법 전체의 전제다. ② "What a backward pass looks like in practice": 도구 없이 손으로 하는 5단계 규율이 본체이고, 뒤의 backpass CLI 소개는 그 규율의 자동화·기계적 게이트(편집 ≤5개, 신규 규칙엔 세션 2회 이상, 편집마다 인용 필수, 예산 초과 시 실패)일 뿐이다. 신경망 훈련이 익숙하면 "Forward pass, backward pass" 섹션은 건너뛰어도 된다.
예상 소요: 요약만 ~3분 / 완독 ~12분.
Kun Chen(전 Meta·Microsoft·Atlassian L8 엔지니어)은 AGENTS.md/CLAUDE.md 유지보수 문제를 이렇게 재정의한다: 유저 레벨 파일은 손으로 쓰고, 프로젝트 레벨 파일은 신경망처럼 취급해 토큰 버짓을 정한 뒤 실제로 돌았던 세션 데이터로 "훈련"하라. 대부분이 forward pass(파일을 읽어 세션을 돌리는 것)에서 멈춰 있고, 빠진 것은 loss로부터 weights를 갱신하는 backward pass라는 주장이다.
몇 달 이상 된 프로젝트 메모리 파일은 넷 중 하나의 상태에 있다고 진단한다.
네 상태의 공통 뿌리는 하나다: 파일이 일화적 관찰에 기반해 transactional하게 편집되고, consolidate·prune보다 append가 훨씬 쉽다는 것.
더 답답한 점은 무엇을 고쳐야 할지 알려줄 데이터가 이미 존재한다는 것이다. Claude Code, Codex, pi, opencode, grok, Cursor — 모든 하네스가 디스크에 트랜스크립트를 남긴다. 무엇을 요청받았고, 어느 규칙을 따랐고, 어디서 넘어졌고, 유저가 어떻게 정정했고, 무엇을 재발견했는지가 전부 로그에 있다. 아무도 그 로그를 읽지 않을 뿐이다.
이 비유는 현실에도 부합한다고 저자는 지적한다 — 대부분은 파일 내용을 자주 정독하지 않으므로 이미 실질적으로 블랙박스다.
수동 규율은 유효하지만 지루하다(트랜스크립트 찾기, 메가바이트급 tool-call 노이즈 읽기, 인용 검증, 토큰 세기). 그래서 저자는 npx -y backpass로 실행하는 도구를 만들었다(MIT, github.com/kunchenguid/backpass). 1회 실행 = 1 gradient step.
backpass apply가 diff+증거와 함께 편집 목록을 제시, 건별 수락/거부. 거부는 기억되어 새 증거 없이는 같은 편집이 재등장하지 않는다.--budget--max-edits--min-gap-evidence--since저자의 운용 리듬은 활성 레포당 주 1회 실행 — 제안을 읽고 모델이 오버리치한 것은 거부, 나머지는 수락. 시간이 갈수록 AGENTS.md는 더 효율적·효과적이 된다.
저자가 강조하는 차별점: "clean up this file" 프롬프트는 1년 전에도 누구나 할 수 있었다. 진짜 요체는 과학적 엄밀함이다 — 유일한 입력은 실제 트랜스크립트, 유일하게 인정되는 증거는 verbatim 인용, 고정 버짓, 소규모 스텝, 최소 배치, 그리고 쓰기를 담당하는 인간 게이트.
신뢰도 A-핵심 사실 축 전수 확인 — backpass 레포·npm 패키지·트랜스크립트 로컬 저장·long-context 연구 근거. 상세 판정 8건은 아래 팩트체크 블록.
많은 사람들이 자신의 AGENTS.md / CLAUDE.md를 좋은 상태로 유지하는 걸 어려워한다. 어떻게 관리하냐고 물어보면 솔직한 대답은 대개 "에이전트가 알아야 한다고 내가 생각하는 걸 적는다"거나 "에이전트한테 대신 쓰게 한다"다.
나도 오랫동안 마찬가지였다. 내게 있던 건 감(vibes)뿐이었다. 하지만 더 나은 해법을 찾을 수 있다는 건 알고 있었다. 이 글은 내가 결국 도달한 방법이다.
짧은 버전: 취향을 담는 유저 레벨 AGENTS.md는 손으로 직접 쓰고, 프로젝트 레벨 파일은 신경망처럼 다뤄라. 크기 예산을 주고, 실제로 돌아간 세션들로 훈련시켜라.
이 글의 나머지는 그것이 구체적으로 무엇을 의미하는지에 대한 이야기다.
에이전틱 엔지니어링, 솔로 빌딩, 그리고 소프트웨어 개발의 최전선이 어떤 모습인지에 대한 실전 필드 노트. Meta, Microsoft, Atlassian에서 L8 엔지니어로 일했다.
몇 달 이상 묵은 프로젝트 메모리 파일을 아무거나 들여다보면 다음 네 가지 상태 중 하나다:
텅 빔(Empty). 도구가 만들어줘서 파일이 존재할 뿐이다. 에이전트는 유용한 것을 아무것도 읽지 못하고, 매 세션마다 같은 것들을 다시 알아낸다.
비대(Bloated). 에이전트가 뭔가 멍청한 짓을 할 때마다 누군가 규칙을 하나씩 붙여넣었다. 1년 뒤엔 900줄이 되어 있고, 그 한 줄 한 줄의 비용을 매 세션마다, 영원히 치른다. 더 나쁜 건, 파일이 커질수록 지시 이행(instruction following)이 희석되어, 정말 중요한 규칙들이 중요하지 않은 규칙들 밑에 파묻힌다는 점이다.
낡음(Stale). 규칙의 절반이 3월에 교체된 빌드 시스템을 설명하고 있다. 에이전트는 그래도 그 규칙을 따르거나, 아니면 파일을 무시하는 법을 배우는데, 둘은 결국 같은 얘기다.
표류(Drifted). AGENTS.md는 이렇게 말하고 CLAUDE.md는 저렇게 말해서, 오늘 어떤 하네스를 쓰느냐에 따라 서로 다른 지시 세트를 받게 된다.
공통 뿌리는 이 파일이 일화적 관찰에 근거해 그때그때 트랜잭션처럼 편집된다는 것, 그리고 통합하고 쳐내는 것보다 덧붙이는 게 훨씬 쉽다는 것이다.
답답한 건, 무엇을 바꿔야 하는지 알려줄 데이터가 이미 존재한다는 점이다. 모든 에이전트 세션은 디스크에 트랜스크립트를 남긴다. Claude Code, Codex, pi, opencode, grok, Cursor: 전부 에이전트가 무엇을 요청받았고, 무엇을 했고, 어떤 규칙을 따랐고, 어떤 규칙에 걸려 넘어졌고, 유저가 어떻게 정정했고, 무엇을 다시 발견해야 했는지의 로그를 쓴다. 아무도 그 로그를 읽지 않는다.
먼저 알아야 할 것은, 에이전트 메모리 파일은 두 레벨로 쓸 수 있고, 두 레벨은 정반대의 취급을 받아야 마땅하다는 점이다. 내가 생각하는 방식은 이렇다.
유저 레벨 파일은 내 것이고, 손으로 직접 쓴다. 내 전역 ~/.claude/CLAUDE.md나 ~/AGENTS.md는 내 취향과 견해다: 개발 비용보다 품질을 우선하라, 버그는 고치기 전에 끝에서 끝까지 재현하라, 지나치다 마주친 flaky 테스트는 네 것이 아니어도 고쳐라. 이것들은 내가 소유한 취향이고 좀처럼 바뀌지 않는다. 다른 누구도 편집해선 안 되고, 어떤 도구도 "최적화"해선 안 되며, 특히 에이전트가 손대는 건 절대 안 된다. 이 파일은 작고, 의도적으로 손으로 쓴다.
프로젝트 레벨 파일은 사실상 신경망이다. 이 파일은 프로젝트에서 돌아가는 에이전트 세션의 시스템 프롬프트에 로드되어 에이전트의 행동을 조향하는데, 이는 사용 중인 모델 위에 얹히는 파인튜닝된 LoRA와 크게 다르지 않다. 파일처럼 편집하는 대신, 토큰 예산을 정하고 신경망처럼 제대로 훈련시키는 편이 낫다.
신경망을 훈련시켜 본 적이 없다면, 전체 루프는 이렇다:
신경망에는 가중치(weights)가 있다: 신경망이 어떻게 행동할지를 결정하는 숫자 뭉치다.
입력을 통과시킨다. 그것이 순전파(forward pass)다. 가중치는 있는 그대로 사용되고, 아무것도 변하지 않는다. 그리고 어떤 출력이 나온다.
출력을 원했던 것과 비교한다. 그 간극이 손실(loss)이다.
손실에서 거꾸로 거슬러 올라가 어떤 가중치가 책임이 있는지 알아내고, 각 가중치를 그 간극을 줄였을 방향으로 살짝 민다. 그것이 역전파(backward pass)이고, 그 살짝 미는 것이 그래디언트 스텝(gradient step)이다.
반복한다. 큰 재작성 한 번이 아니라 작은 스텝 여러 번이다. 스텝의 크기가 학습률(learning rate)이다. 너무 크면 요동치고, 너무 작으면 어디에도 도달하지 못한다.
다음 내용에서 중요한 디테일이 두 가지 있다. 하나의 예시만으로 업데이트하지 않는다는 것 — 예시 하나는 일화적이고 노이즈일 수 있으니, 배치(batch)로 묶는다. 그리고 신경망은 크기가 고정되어 있다는 것 — 더 큰 신경망은 더 많은 지식을 저장할 수 있지만, 추론 시점의 비용이 더 커진다.
이제 명사만 바꿔 끼워 보자:
프로젝트 AGENTS.md가 가중치다. 이 레포에서 에이전트가 코드 한 줄을 읽기도 전에 어떻게 행동할지를 결정하는 것이 바로 이 파일이다.
예산이 "모델 크기"다. 더 큰 모델은 더 많은 지식을 담고 더 나은 결과를 내지만, 추론 시점의 비용이 커진다. 그래서 예산을 정의하는 일은 그 트레이드오프를 명시적으로 만드는 데 중요하다.
모든 에이전트 세션이 순전파다. 에이전트는 파일을 로드하고, 일을 하고, 파일은 건드려지지 않는다.
에이전트가 한 일과 당신이 원했던 것 사이의 간극이 손실이다. 스키마를 또다시 재발견했다. 레포는 pnpm을 쓰는데 npm test를 돌렸다. 바로 거기 적혀 있던 규칙을 위반했다. 알고 보니 틀린 규칙을 따랐다. 이 모든 것이 트랜스크립트 안에 있다.
트랜스크립트를 읽고 AGENTS.md 파일을 업데이트하는 것이 역전파다. 어떤 지시가 손실에 책임이 있는지, 어떤 지시가 제 밥값을 했는지 가려내고, 예산 안에서 손실을 줄이는 방향으로 가중치를 조금씩 업데이트한다.
보다시피 둘은 충격적일 만큼 닮았다. AGENTS.md를 이렇게 생각하는 것은 현실에도 부합한다 — 대부분의 사람들은 실제로 그 내용을 자주 꼼꼼히 읽지 않는다. 실질적으로 이미 블랙박스인 것이다.
빠져 있는 것은, 대부분의 사람들이 순전파에서 멈췄다는 점이다 — 매 에이전트 세션에서 AGENTS.md 파일을 사용하기는 한다 — 그들에게 없는 것은 손실에 근거해 가중치를 다듬는 역전파다.
AGENTS.md = 가중치. 각 유닛(AG-001~005)에 +/− 증거 카운트가 쌓이고, 5,000 토큰 예산과 여유분(headroom)이 "모델 크기"를 정한다.이건 손으로도 할 수 있고, 나도 한동안 그렇게 했다. 이 규율은 다섯 부분으로 이루어지며, 각각이 훈련 루프의 무언가에 대응한다.
증거는 일화가 아니라 트랜스크립트에서 나온다. 손실 신호는 지난 화요일에 짜증났던 당신의 기억이 아니라 에이전트의 세션 로그에서 와야 한다. AGENTS.md 파일의 목록 항목이나 문단 하나하나를 어드레서블 유닛(addressable unit)으로 취급하고, 모든 유닛에 대해 묻는다: 어느 세션에서 이게 중요했나, 지켜졌나, 위반됐나, 틀렸나? 역방향으로도 묻는다: 어떤 유닛도 커버하지 않는데 에이전트가 틀린 것은 무엇인가?
업데이트 전에 배치로 묶어라. 나쁜 세션 하나는 무작위적인 이유로도 생길 수 있다. 그것만 근거로 가중치를 다시 써서는 절대 안 된다. 데이터가 배치로 쌓였을 때에만 무엇이 진짜 패턴이고 무엇이 그저 노이즈인지 식별할 수 있다. 이 제약 하나만으로 비대의 대부분이 제거되는데, 붙여넣어진 규칙 대부분이 두 번 다시 재발하지 않은 단발 사건에 대한 반응이었기 때문이다.
작은 스텝. 한 번의 패스당 편집 몇 개, 다섯 개 정도. 각각은 추가, 제거, 재작성, 또는 스킬로의 추출이다. 파일 전체의 재작성이 아니다. 메모리 파일에 큰 스텝을 밟는 것은 처음부터 다시 시작하는 것과 구별이 안 되고, 그동안 잘 작동하던 모든 것을 잃게 된다.
예산을 지키고, 스킬을 릴리즈 밸브로 써라. 항상 로드되는 파일의 예산을 정한다. 예를 들어 5,000 토큰. 예산에 도달했거나 근접했다면 업데이트는 제로섬이다: 모든 추가는 그 값을 치를 제거나 추출을 지명해야 한다. 릴리즈 밸브가 스킬이다. 폭넓은 지시(대략 세션의 20% 이상에서 중요하거나, 안전-크리티컬한 것)는 메모리 파일에 남는다. 감지 가능한 트리거가 있는 좁은 지시는 스킬이 된다. 감지 가능한 트리거가 없는 좁은 지시는 삭제 후보다.
이걸 손으로 엄격하게 하는 것만으로도 일화에 기댄 무작위 편집보다는 실질적인 개선일 수 있다. 하지만 지루하다: 트랜스크립트 찾기, 수 메가바이트의 툴콜 노이즈 읽기, 인용을 정직하게 유지하기, 토큰 세기. 바로 이 부분이 도구가 해야 할 일이다.
그래서 나는 이 루프를 반복 가능한 방식으로 쉽게 돌리기 위해 backpass를 만들었다. 최근 작업한 레포에서 npx -y backpass를 실행하면 쓸 수 있다.
그리고 그 형태는 위의 방법을 그대로 파이프라인으로 옮긴 것이다. 한 번의 실행이 한 번의 그래디언트 스텝이다.
흔히 쓰이는 에이전트 하네스들의 로컬 트랜스크립트 저장소에서 디스크로부터 직접 샘플을 수집하고, 각 세션을 cwd나 git remote로 해당 레포에 연결한다.
각 트랜스크립트를 손실 신호를 담은 부분(무엇을 요청받았나, 에이전트가 뭐라고 답했나, 각 툴콜의 한 줄짜리 형태)으로 증류하는데, 모델 개입 없이 96-99% 축소다. 손실 계산은 트랜스크립트당 저렴한 모델 호출 한 번으로, 각 어드레서블 유닛에 대한 증거를 생산하며, 축어적(verbatim) 인용이 없는 것은 프롬프트가 아니라 코드에서 폐기된다.
그래디언트는 결정론적으로 집계된다: 유닛별 긍정·부정 카운트, 관련도 비중(relevance share), 세션들을 가로질러 클러스터링한 준중복(near-duplicate) 갭, 그리고 두 세션 미만에서 관측된 갭은 전부 탈락시킨다.
그다음 그래디언트 디센트 스텝 — 높은 추론(high-reasoning) 호출 한 번 — 이 편집을 제안하고, 게이트가 기계적으로 돈다: 편집은 최대 다섯 개, 새 규칙에는 세션 두 개, 모든 편집에 인용 하나, 그리고 편집 후의 파일은 예산에 들어맞아야 한다. 위반이 있으면 위반 내용을 명시한 재프롬프트를 정확히 한 번 주고, 두 번째 위반은 요란하게 실패하며 아무것도 쓰지 않는다. 토큰 델타는 실제 텍스트에서 측정하는데, 모델이 보고하는 숫자는 절대 믿을 게 못 되기 때문이다.
내가 리뷰하기 전까지는 아무것도 쓰지 않는다. backpass apply는 lavish-axi를 사용해 제안된 편집 목록을 각각의 diff와 증거와 함께 제시하고, 나는 하나씩 수락하거나 거부하며, 거부는 기억되어 새 증거 없이는 같은 편집이 다시 돌아오지 않는다. 플래그들은 훈련 노브를 제 이름 그대로 옮긴 것이다: --budget은 모델 크기, --max-edits는 학습률, --min-gap-evidence는 배치 크기, --since는 훈련 윈도우다.
내가 정착한 리듬은 이렇다: 활성 레포당 주 1회 실행, 제안을 읽고, 모델이 오버리치한 것은 거부하고, 나머지는 수락. 시간이 지나면서 AGENTS.md는 그저 점점 더 효율적이고 효과적으로 되어 간다.
1년 전에도 누구나 에이전트에게 "이 파일 좀 정리해줘"라고 할 수는 있었다. 여기서 정말로 크게 중요한 것은 과학적 엄밀함이다: 유일한 입력으로서의 실제 트랜스크립트, 유일하게 채택 가능한 증거로서의 축어적 인용, 고정된 예산, 작은 스텝 크기, 최소 배치, 그리고 쓰기를 최종 수행하는 인간 게이트.
한번 해보고 싶다면 레포에서 npx -y backpass를 실행해 보라. 도구는 MIT 라이선스로 오픈소스화도 해 두었다. 자동화된 이 루프를 직접 연구해 보고 싶다면 github.com/kunchenguid/backpass에서 찾을 수 있다.
backpass apply가 편집 목록 제시에 쓰는 인터페이스(원문 표기 그대로 보존).