11 분 소요

Summary

에이전트에게 리포트를 맡기면 첫 달은 놀랍도록 잘 나옵니다. 그리고 보통 3개월 안에 무너져요. 어느 날부터 숫자가 미묘하게 틀리고, 결론이 한쪽으로만 기울고, “왜 이렇게 썼냐”고 물으면 아무도 답을 못 합니다.

원인을 모델에서 찾으면 못 찾습니다. 모델은 그대로예요. 망가진 건 모델에 주입되는 입력이고, 그 입력의 대부분이 버전 관리 밖에 있기 때문입니다.

그래서 이 글의 주장은 세 줄입니다.

  1. 리포트 작업을 프롬프트가 아니라 저장소로 관리한다.
  2. 전문가 한 명이 아니라 격리된 여러 시각으로 쓰게 한다.
  3. 오염은 사고가 아니라 전제다. git 으로 되감을 수 있게 처음부터 만든다.

3번이 이 글의 중심축이에요. 되감기가 되면 나머지는 실험해도 되고, 되감기가 안 되면 나머지를 아무리 잘 설계해도 전사에 못 맡깁니다.

💡 이 글에서 다루는 것

  • 에이전트 입력의 4개 층과, 그중 버전이 없는 층
  • 리포트 저장소 구조 — 에이전트가 읽는 곳과 쓰는 곳의 분리
  • 위임의 단위: “리포트 한 편”이 아니라 역할 계약(contract)
  • delegate_task 로 분석·리스크·준법·반대편을 격리 실행하는 법
  • 🚨 오염의 네 가지 모양과 git bisect run 으로 범인 커밋 찾기
  • revert 여야 하고 reset 이면 안 되는 이유
  • 전사 위임 성숙도 4단계와 각 단계의 진입·강등 조건

이 글은 설계 제안입니다. 특정 회사의 실제 시스템이나 데이터를 다루지 않고, Hermes Agent 위에서 리포트 영역을 책임지게 만들려면 무엇을 갖춰야 하는지를 정리했어요.



1. 3개월 뒤에 무너지는 이유

에이전트가 한 번 응답을 만들 때, 모델 앞에 놓이는 입력은 한 덩어리가 아닙니다. 층이 있어요.

어디에 사는가 수명
지시문 AGENTS.md, ~/.hermes/SOUL.md 영구
스킬 skills/<name>/SKILL.md 영구
메모리 에이전트 메모리 저장소 영구
세션 지금 이 대화 한 세션
데이터 참조하는 원본 파일 갱신됨

여기까지는 평범합니다. 문제는 다음 표예요.

버전 관리 오염되면 나타나는 증상
지시문 파일이라 가능 규칙 충돌, 장황해짐
스킬 파일이라 가능 엉뚱한 절차를 밟음
메모리 보통 없음 틀린 사실이 전 리포트에
세션 없음 그 세션만 이상함
데이터 스냅샷 없으면 없음 재현 자체가 안 됨

가장 오래 남는 층이 가장 관리가 안 됩니다. 세션 오염은 창을 닫으면 사라지지만, 메모리에 한 번 박힌 잘못된 사실은 그 뒤의 모든 리포트에 조용히 스며들어요. 그리고 아무도 언제 들어왔는지 모릅니다.

금융 리포트에서 이게 특히 치명적인 이유

일반적인 문서 작업이면 “좀 이상하네” 하고 고치면 됩니다. 금융 리포트는 다릅니다. 리포트는 언제나 근거를 요구받아요.

  • 3개월 전에 나간 리포트의 한 문장에 대해 질문이 들어온다
  • 그 문장이 어떤 데이터, 어떤 규칙, 어떤 판단으로 나왔는지 재구성해야 한다
  • 재구성이 안 되면 그 문장 하나가 아니라 리포트 전체의 신뢰가 무너진다

그러니까 에이전트에게 리포트를 맡긴다는 건 “글을 잘 쓰게 한다”가 아니라 “근거를 되짚을 수 있게 한다” 입니다. 전자는 모델이 해주고, 후자는 구조가 해줘야 해요.



2. 리포트를 ‘프롬프트’가 아니라 ‘저장소’로 본다

대화창에서 준 지시는 다음 주에 사라집니다. 파일에 적힌 지시만 남아요. 그래서 첫 번째 전환은 이겁니다 — 에이전트가 읽는 모든 것을 커밋된 파일로 만든다.

제안하는 구조입니다.

저장소 하나에 리포트 한 종류가 아니라 리포트 업무 전체가 들어갑니다. 각 디렉토리의 역할과, 누가 쓰기 권한을 갖는지가 핵심이에요.

경로 무엇 에이전트 쓰기
AGENTS.md 전 리포트 공통 규칙
skills/ 반복 절차
contracts/ 리포트별 작업 계약
memory/inbox/ 에이전트가 배운 것
memory/approved/ 사람이 승인한 사실
templates/ 산출물 뼈대
data/ 날짜 박힌 스냅샷
drafts/ 시각별 초안
published/ 승인된 최종본
scores/ 채점 이력

이 표에서 읽어야 할 건 ✓ 가 세 줄뿐이라는 점입니다. 에이전트는 초안을 쓰고, 배운 걸 받은편지함에 넣고, 채점 결과를 남깁니다. 그 외 자기 행동을 바꾸는 파일은 하나도 직접 건드리지 못해요.

원칙 세 가지

  1. 에이전트가 읽는 모든 것은 커밋된 파일이다. 대화창 지시는 실험까지만. 반복될 거라면 파일로 내려간다.
  2. 읽는 곳과 쓰는 곳을 분리한다. 에이전트가 자기 규칙을 스스로 고칠 수 있으면, 되감기의 기준점이 사라진다.
  3. 산출물은 입력의 SHA 를 기록한다. 리포트 한 편은 “어떤 커밋의 저장소가 만든 결과”여야 한다.

3번이 5장 전체를 가능하게 만드는 전제입니다. 산출물에 입력 커밋이 안 적혀 있으면 나중에 아무리 git 이 있어도 무엇을 되감아야 하는지 알 수가 없어요.



3. 위임의 단위는 ‘리포트 한 편’이 아니라 ‘역할 계약’

장기 위임이 실패하는 가장 흔한 형태는 이겁니다.

구분 나쁜 위임 좋은 위임
지시 “3분기 여신 리포트 써줘” 계약 파일을 실행
입력 그때그때 붙여넣기 경로로 고정
기준 읽어보고 판단 골든셋 점수
재현 안 됨 같은 커밋 → 같은 결과
회귀 감으로 알아챔 점수 하락으로 탐지

왼쪽은 한 번 쓰고 버리는 요청이고, 오른쪽은 반복 실행되는 계약입니다. 전사 리포트를 맡기려면 오른쪽이어야 해요.

계약 파일은 이런 모양입니다.

id: credit-quarterly
audience: risk-committee
cadence: quarterly
inputs:
  - data/2026Q3/credit_portfolio.csv
  - data/2026Q3/macro_rate.csv
  - memory/approved/credit.md
template: templates/credit.md
views: [analyst, risk, compliance, bear, editor]
forbid:
  - no-source-number
  - out-of-range-period
  - client-identifier
output:
  path: drafts/2026Q3-credit/
  max_words: 2500
score:
  golden_set: scores/credit-golden.yaml
  pass: 0.85

forbid 에 한글 문장 대신 코드를 쓴 게 의도적입니다. 금지 규칙은 채점기가 기계적으로 검사해야 하고, 사람이 읽을 설명은 AGENTS.md 한 곳에만 둡니다. 설명이 두 군데 있으면 반드시 갈라져요.

금지 코드 검사하는 것
no-source-number 데이터 파일에 없는 수치
out-of-range-period 입력 기간 밖의 추정
client-identifier 고객·거래 식별정보

계약이 생기면 세 가지가 따라옵니다. 재현(같은 커밋을 다시 돌리면 같은 게 나온다), 채점(잘했는지를 감이 아니라 숫자로 안다), 회귀 탐지(어제보다 나빠진 걸 자동으로 안다). 5장의 되감기는 이 셋 위에서만 작동합니다.



4. 다중 시각 — 전문가 한 명이 아니라 위원회

왜 단일 패스는 항상 한쪽으로 기우나

모델에게 리포트를 한 번에 쓰라고 하면, 가장 그럴듯한 하나의 서사를 고릅니다. 데이터가 애매할 때도 고릅니다. 그게 언어 모델이 하는 일이에요.

그런데 좋은 금융 리포트는 서사가 아니라 대립하는 견해의 정리입니다. “좋아 보인다”와 “이 조건이면 뒤집힌다”가 같은 문서 안에 있어야 읽는 사람이 판단할 수 있어요. 단일 패스로는 이게 구조적으로 안 나옵니다.

다섯 시각

역할 읽는 것 내놓는 것
분석 데이터 + 승인 메모리 추세와 해석
리스크 데이터 + 분석 초안 하방 시나리오
준법 초안 전부 위반 표현 목록
반대편 분석 초안 결론을 뒤집을 조건
편집 위 넷 전부 합본

각 역할이 망가졌을 때의 징후도 미리 정해둡니다. 이게 채점 항목이 돼요.

역할 망가졌을 때 징후
분석 데이터에 없는 기간을 말함
리스크 시나리오가 매번 같은 문장
준법 위반 목록이 계속 0건
반대편 분석 결론에 동의함
편집 대립을 임의로 봉합함

“반대편이 분석 결론에 동의한다” 는 특히 중요한 신호입니다. 거의 항상 격리가 깨졌다는 뜻이에요.

격리가 핵심이다

같은 세션에서 순서대로 시키면 안 됩니다. 분석 초안이 컨텍스트에 남은 채로 반대편을 시키면, 반대편은 앵커링돼서 “대체로 맞지만 약간 보수적으로 보면” 같은 말을 합니다. 반대가 아니에요.

Hermes 에서는 delegate_task 로 시각마다 별도 컨텍스트를 씁니다.

delegate_task(
  goal="risk view for credit-quarterly",
  context="contracts/credit-quarterly.yaml",
  toolsets=["files", "search"],
  role="leaf")

role="leaf" 로 각 시각을 말단에 두고, 부모는 합본만 합니다. 그리고 각 시각에 넘기는 context계약 파일 경로 하나입니다. 부모 세션의 대화 내용을 요약해서 넘기지 마세요. 그 순간 격리가 무의미해집니다.

넘기는 것 격리
계약 파일 경로 유지됨
입력 데이터 경로 유지됨
앞 시각의 산출 파일 의도된 의존
부모 세션 대화 요약 깨짐

“앞 시각의 산출 파일”은 괜찮습니다 — 리스크는 분석 초안을 읽어야 하니까요. 차이는 파일로 읽느냐, 대화 맥락으로 물려받느냐입니다. 파일로 읽으면 무엇을 봤는지가 커밋에 남고, 대화로 물려받으면 아무 데도 안 남아요.

합본만 남기지 말 것

한 분기 초안 디렉토리는 이렇게 남습니다.

파일 누가 썼나
analyst.md 분석 시각
risk.md 리스크 시각
compliance.md 준법 시각
bear.md 반대편 시각
merged.md 편집 시각

다섯 개를 전부 커밋합니다. 3개월 뒤 “왜 이 결론이 나왔냐”에 답하는 건 합본이 아니라 그 앞의 네 개예요. 합본만 남기면 편집 단계에서 무엇이 잘려나갔는지 영원히 알 수 없고, 그 상태에서는 5장의 되감기도 반쪽만 됩니다.

편집 시각에는 규칙 하나를 명시적으로 박아둡니다 — 대립이 해소되지 않으면 해소하지 말고 병기한다. 모델은 놔두면 매끄럽게 봉합하려고 합니다. 리포트에서는 그게 손실이에요.



5. 오염은 반드시 일어난다 — 그래서 git 이 중심축

여기가 이 글의 본론입니다.

지금까지가 “잘 만드는 법”이었다면, 이 장은 “망가진 뒤에 되돌리는 법” 이에요. 그리고 전사 위임의 가능 여부는 전적으로 이 장에 달려 있습니다. 되감기가 안 되면 조직은 절대로 리포트 영역을 에이전트에게 넘기지 않아요. 넘기면 안 되고요.

5.1 오염의 네 가지 모양

모양 어떻게 들어오나
사실 오염 한 번의 잘못된 확인이 메모리에 승격
규칙 비대 세션 피드백이 계속 영구 규칙이 됨
톤 드리프트 편집 피드백 누적으로 점점 단정적
스냅샷 교체 같은 경로 파일이 조용히 바뀜

증상은 이렇게 나타납니다.

모양 증상 알아채기까지
사실 오염 전 리포트에 같은 틀린 수치 수 주
규칙 비대 지시문이 수백 줄, 서로 충돌 수 개월
톤 드리프트 “가능성”이 “전망”으로 바뀜 거의 못 알아챔
스냅샷 교체 재현이 안 됨 되짚을 때

넷 중 톤 드리프트가 가장 위험합니다. 틀린 숫자는 누가 잡아내요. 그런데 “완만한 상승 가능성이 있다”가 몇 달에 걸쳐 “상승이 전망된다”로 바뀌는 건 아무도 특정 시점을 짚지 못합니다. 이건 사람이 읽어서 잡는 게 아니라 골든셋에 표현 강도 항목을 넣어서 숫자로 잡아야 해요.

5.2 탐지 — 리포트가 아니라 ‘입력’을 본다

리포트를 읽어서 품질을 판단하려고 하면 늦습니다. 매주 읽을 수도 없고요. 대신 같은 입력을 정기적으로 다시 돌려서 점수를 봅니다.

골든셋은 정답이 고정된 문항 20~30개입니다.

문항 유형 왜 필요한가
수치 확인 특정 분기 값 사실 오염 탐지
정의 확인 지표 산식 메모리 오염 탐지
금지 표현 단정적 전망 톤 드리프트 탐지
출처 요구 근거 파일 경로 환각 탐지
기간 경계 입력 밖 기간 질문 범위 이탈 탐지

주 1회 자동 실행하고, 결과를 저장소에 커밋합니다.

./scripts/score.sh credit > scores/$(date +%F)-credit.json
git add scores/ && git commit -m "weekly score: credit"

점수를 커밋하는 게 핵심입니다. 점수 자체가 이력이 돼요. 나중에 “언제부터 나빠졌나”를 물을 때, 읽을 게 있어야 합니다.

5.3 되감기 — 네 단계

(1) 산출물에 입력 SHA 를 박는다

모든 초안·최종본의 front matter 에 이걸 넣습니다.

report: credit-quarterly
period: 2026Q3
repo_sha: 4f2a91c
data_sha: 9b71e02
score: 0.91
views: [analyst, risk, compliance, bear]

repo_sha 는 그때의 규칙·스킬·메모리 상태이고, data_sha 는 그때의 데이터 상태입니다. 둘을 나눠 적는 이유는, 망가졌을 때 규칙이 범인인지 데이터가 범인인지를 먼저 갈라야 하기 때문이에요. 이 한 줄이 조사 시간을 며칠에서 몇 분으로 줄입니다.

(2) 언제 들어왔는지 찾는다

git log -p --follow memory/approved/credit.md
git log --oneline -- AGENTS.md skills/
git log --oneline --since=2026-06-01 -- contracts/

여기서 대개 “아, 이거였네” 가 나옵니다. 안 나오면 다음 단계로 갑니다.

(3) 어느 커밋부터 기울었는지 — bisect run

이 도구가 이 구조 전체의 보상입니다. 계약과 골든셋을 만들어둔 진짜 이유가 여기 있어요.

먼저 채점 스크립트가 통과면 0, 미달이면 1 을 반환하게 만듭니다.

#!/bin/sh
# scripts/score.sh <report> <threshold>
s=$(hermes run --contract "contracts/$1.yaml" --score-only)
awk -v s="$s" -v t="$2" 'BEGIN { exit !(s >= t) }'

그리고 마지막으로 멀쩡했던 시점과 지금을 물려줍니다.

git bisect start HEAD v2026Q2
git bisect run ./scripts/score.sh credit 0.85
git bisect reset

git 이 이분탐색으로 커밋을 하나씩 체크아웃하면서 스크립트를 돌리고, 점수가 처음 무너진 커밋 하나를 집어줍니다. 커밋이 200개든 2000개든 로그 횟수만큼만 돌아요.

여기서 중요한 조건 하나. 이게 되려면 에이전트의 입력이 전부 저장소 안에 있어야 합니다. 메모리가 저장소 밖 어딘가에 있으면 체크아웃해도 안 바뀌고, bisect 는 조용히 엉뚱한 커밋을 지목합니다. 2장의 구조가 여기서 값을 하는 거예요.

(4) revert 로 되돌린다 — reset 이 아니라

git revert --no-edit 4f2a91c

reset --hard 로 지우고 싶은 유혹이 있는데, 하지 마세요. 이유가 셋입니다.

이유 설명
재유입 왜 물렀는지 모르면 같은 규칙이 다시 들어옴
감사 “언제 알았고 언제 조치했나”에 답해야 함
공유 브랜치 강제 푸시가 남의 커밋을 날림

특히 첫 번째가 실무에서 제일 아픕니다. 오염된 규칙은 대개 그럴듯해서 들어온 거예요. 이력을 지우면 몇 주 뒤에 누군가 같은 이유로 같은 규칙을 다시 넣습니다. revert 커밋 메시지에 “이 규칙은 이런 결과를 내서 물렀다”를 남겨두면 그게 방벽이 돼요.

되돌린 뒤에는 그 오염 사례를 골든셋에 문항으로 추가합니다. 그래야 다음에 같은 게 들어올 때 사람이 아니라 점수가 먼저 잡습니다.

5.4 재발 방지 — 쓰기 권한을 쪼갠다

되감기를 매주 하게 되면 그건 구조가 잘못된 겁니다. 빈도를 줄여야 해요.

가장 효과가 큰 장치는 에이전트가 자기 규칙을 직접 못 고치게 하는 것입니다. 2장의 표가 그거였고, 실제 운용은 이렇게 됩니다.

단계 누가 무엇
1 에이전트 memory/inbox/ 에 배운 것 기록
2 사람 주 1회 inbox 검토
3 사람 통과분만 approved/ 로 PR
4 자동 골든셋 재실행, 점수 확인

승격 심사는 질문 세 개면 충분합니다.

질문 아니면
다시 실행해도 재현되나 버림
계약에 이미 있는 내용인가 버림 (중복은 충돌의 씨앗)
6개월 뒤에도 참인가 data/ 로 보냄

세 번째가 특히 자주 걸립니다. “3분기 기준금리는 X” 같은 건 사실이지만 규칙이 아니에요. 이런 게 메모리에 쌓이면 반 년 뒤에 에이전트가 낡은 숫자를 확신에 차서 씁니다. 시간이 지나면 틀려지는 건 전부 데이터 쪽으로 보냅니다.

그리고 데이터는 경로에 날짜를 박고 덮어쓰지 않습니다.

나쁨 좋음
data/credit.csv data/2026Q3/credit.csv

같은 경로를 덮어쓰면, 과거 리포트를 재현하려고 그 커밋을 체크아웃해도 데이터는 최신 상태입니다. bisect 가 거짓말을 하기 시작하는 지점이에요.



6. 전사로 넓히기 — 성숙도 4단계

한 종류의 리포트가 안정되면 그다음은 “이걸 얼마나 넓게 맡길 것인가”입니다. 한 번에 넘기지 말고 단계를 두되, 각 단계의 진입 조건을 숫자로 정해둡니다.

단계 사람이 하는 일
1. 보조 초안만 받고 사람이 다시 씀
2. 병렬 사람 리포트와 나란히 놓고 대조
3. 주작성 에이전트가 쓰고 사람이 승인
4. 상시 예외 알림만 사람이 확인

진입과 강등 조건입니다.

단계 진입 강등
2 점수 0.7 이상 2주 연속 미달
3 0.85 를 4주 연속 준법 위반 1건
4 0.9 를 8주 + 위반 0 되감기 실패 1건

4단계 진입 조건에 “되감기 성공 이력”을 넣은 게 의도적입니다. 한 번도 복구해본 적 없는 조직은 상시 운영에 못 갑니다. 실제로 오염이 터졌을 때 손이 굳어요.

그래서 분기에 한 번은 되감기 훈련을 합니다. 일부러 메모리에 틀린 사실을 하나 커밋하고, 며칠 뒤에 다른 사람이 git bisect run 으로 찾아내는 연습이에요. 소방 훈련과 같은 성격입니다. 훈련에서 30분 안에 못 찾으면, 그건 구조에 구멍이 있다는 뜻입니다.

저장소를 쪼갤 것인가

리포트 종류가 늘면 나오는 질문입니다. 답은 쪼개지 않는 쪽이에요.

구성 결과
리포트별 저장소 규칙이 갈라지고 재수렴 불가
단일 저장소 + 계약 분리 공통 규칙 하나, 차이는 계약에

AGENTS.mdskills/, memory/approved/하나만 둡니다. 리포트마다 다른 건 contracts/<id>.yamltemplates/ 로 흡수해요. 공통 규칙이 저장소마다 복제되기 시작하면, 반 년 뒤엔 어느 게 맞는 규칙인지 아무도 모르게 됩니다. 그리고 그건 되감을 수도 없어요 — 되감을 대상이 여러 개니까요.

금융 도메인에서 추가로 박아둘 것

규칙 강제 방식
출처 없는 문장 금지 모든 수치에 데이터 경로 각주
미래 단정 표현 금지 골든셋 표현 강도 문항
식별정보 마스킹 수집 단계에서 처리

세 번째는 에이전트 단계가 아니라 수집 단계여야 합니다. 원본에 식별정보가 들어 있으면, 에이전트가 아무리 조심해도 컨텍스트에는 이미 들어간 거예요. 마스킹은 항상 가장 앞에서 합니다.



7. 체크리스트

넘기기 전에 이게 다 ✓ 인지 확인합니다.

항목 확인
에이전트가 읽는 입력이 전부 저장소 안에 있다
에이전트가 자기 규칙 파일을 직접 못 고친다
리포트마다 계약 파일이 있다
산출물에 repo_shadata_sha 가 있다
데이터 경로에 날짜가 박혀 있다
시각별 초안이 전부 커밋된다
골든셋이 있고 주 1회 돈다
점수가 저장소에 커밋된다
채점 스크립트가 종료 코드를 낸다
bisect run 을 실제로 한 번 돌려봤다
되감기를 revert 로 한다
inbox → approved 승격 절차가 있다

마지막 두 줄이 안 되어 있으면 나머지 열 줄이 다 ✓ 여도 전사에는 못 맡깁니다.



마무리

에이전트에게 장기적으로 일을 맡긴다는 건 더 좋은 프롬프트를 쓰는 일이 아니라, 되돌릴 수 있는 구조를 만드는 일입니다.

모델은 계속 좋아집니다. 그건 우리가 할 일이 아니에요. 우리가 할 일은 입력을 버전 관리 안에 넣고, 시각을 격리하고, 망가졌을 때 몇 분 안에 범인 커밋을 집어내는 절차를 갖추는 겁니다.

오염을 막으려고 하지 마세요. 반드시 일어납니다. 막는 대신 빨리 찾고 깨끗하게 무르는 쪽에 투자하는 게 전사 리포트를 에이전트에게 넘길 수 있는 유일한 길이라고 생각해요.

Hermes 자체를 다루는 글은 따로 정리해뒀습니다. 이 글이 무엇을 맡길까였다면, 아래는 무엇 위에 맡길까에 해당해요.

이 글과 이어지는 곳
Docker Compose 셋업 띄우기
Discord ‘NoneType’ 트러블슈팅 붙이고 고치기
프로필로 역할별 쪼개 쓰기 4장 시각별 격리
Google Workspace 안전 연동 입력 데이터 붙이기
Hermes 자체를 Git 으로 형상관리 5장 되감기의 대상

특히 마지막 글이 이 글 5장의 전제입니다. 에이전트 자체가 형상관리 밖에 있으면, git bisect 가 짚어주는 커밋은 절반만 진실이에요.