|

3줄 요약
- 2026-08-16, 발행된 ai-edu 블로그 12편을 신선 컨텍스트 서브에이전트 2기에게 6편씩 나눠 교차 감사시켰고 평균 15.1/25점이 나왔다.
- 가장 아픈 발견은 "~한 적이 있었다 → ~라는 걸 깨달았다"는 정형화된 가짜 체험담 문형이 4편에서 거의 동일하게 반복된 것이었다.
- 감사 항목은 그대로 무LLM 품질 게이트(scripts/publish/cc_quality_gate.py)로 옮겼고, 지적된 문장은 전체 재작성이 아니라 치환 목록(scripts/publish/aiedu_apply_patch.py)으로 고쳤다.

질문
무엇을 몇 점 만점으로 감사했나 발행글 12편, 평가축 5개(고유성·구체성·정확성/시의성·구조·진정성) × 각 5점 = 25점 만점
결과 점수는 평균 15.1/25 (2026-08-16 실측, 서브에이전트 2기 교차 감사)
가장 많이 지적된 결함은 무출처 정밀 수치와 정형화된 가짜 체험담
감사 결과를 어떻게 굳혔나 검사 항목을 정규식 게이트로 옮기고, 지적된 문장만 골라 치환했다
감사자를 왜 2기 띄웠나 두 기가 상대는 못 본 결함을 각각 1건씩 잡았다(2026-08-16). 1기만 띄웠다면 그 1건은 그대로 남았다

이 글대로 하면 무엇이 달라지는가 — 자기 블로그에 남아 있는 지어낸 통계·가짜 체험담·구조 결함을 사람 눈이 아니라 명령 한 줄로 걸러내고, 걸린 문장만 골라 고칠 수 있다.

왜 자기가 쓴 글을 AI에게 감사시켰는가?

발행글이 실제로 재방문할 이유를 주는지 확인할 방법이 없었기 때문이다. 자기가 쓴 글을 자기가 다시 읽으면 이미 쓴 맥락을 알고 있어서 결함이 보이지 않는다. 빠진 근거를 머릿속에서 자동으로 채워 읽기 때문이다. 그래서 2026-08-16, 이번 세션과 무관한 신선 컨텍스트의 서브에이전트 2기를 띄워 발행된 ai-edu 12편을 6편씩 나눠 주고 감사시켰다.

먼저 이 글의 한계를 밝혀 둔다. 편별 총점표는 파일로 저장하지 않았다. 세션 전사에만 남았고, 디스크로 옮긴 것은 반복 지적된 결함 목록과 거기 딸린 축 점수 일부뿐이다(docs/tracks/claude-code/claude-code-series-plan.md §0). 그래서 아래에서 총평·결함 유형·인용된 축 점수는 쓸 수 있지만, 12편 각각의 총점이 몇 점이었는지는 제시하지 못한다. 감사를 돌린다면 채점 결과를 받는 즉시 파일로 떨어뜨려라. 측정해 놓고 결과를 파일에 안 남기면, 나중에 그 수치를 쓰려 할 때 근거가 없어 못 쓴다. 이 문단이 그 실패의 증거다.

감사 하네스를 어떻게 설계했는가?

평가축 다섯 개와 감사자 프롬프트가 하네스의 전부였다. 그대로 옮겨 쓸 수 있게 절차로 적으면 다음과 같다.

  1. 평가축을 먼저 고정한다. 고유성·구체성·정확성/시의성·구조·진정성 다섯 개를 각 5점 만점으로 채점하게 해 25점 만점으로 합산했다. 축을 정하지 않으면 감사자가 매번 다른 것을 본다.

여기에 우리가 빠뜨린 것이 있다. 축 이름만 줬고 각 점수의 판정 기준은 주지 않았다. 이 글 자체를 다시 감사시켰을 때 정확히 그 점이 지적됐다 — 루브릭이 없으면 독자도 다음 감사자도 1점과 3점을 같은 뜻으로 쓸 수 없고, "고유성 1점"이라는 판정을 아무도 검증하지 못한다. 그래서 다음 감사부터 쓸 루브릭을 아래로 고정했다. 이 표는 과거 감사에 적용된 값이 아니라 앞으로 쓸 기준이다.

1점 3점 5점
고유성 이 블로그가 아니어도 어디서나 볼 수 있는 내용 일반론에 자기 사례가 일부 섞임 이 프로젝트의 파일 경로·실측값이 3개 이상
구체성 지시가 "확인한다" 수준 절차는 있으나 명령·코드가 없음 복사해 실행 가능한 명령·코드가 있음
정확성/시의성 무출처 수치나 낡은 버전 표기가 있음 수치는 맞으나 측정일·확인 시점이 없음 모든 수치에 측정일 또는 파일 경로가 붙음
구조 결론이 글 끝에 있음 소제목은 있으나 답이 아래로 밀림 결론표가 첫 화면, 각 소제목 첫 문장이 답
진정성 겪지 않은 일을 겪은 것처럼 씀 성공만 쓰고 실패는 생략 오진과 실패를 원인까지 적음
2. 감사자에게 관용을 금지한다. 프롬프트에 "좋게 봐주지 말 것", "경미한 발견도 전부 보고하고 확신도를 붙일 것", "본문 인용 1개 필수"를 넣었다. 인용 의무가 핵심이다. 인용을 못 붙이는 지적은 감사자가 지어낸 지적이다.
3. 2기로 나누고 서로 무엇을 보는지 모르게 한다. 두 기는 채점 기준만 공유했고 상대가 어떤 글을 읽는지는 몰랐다.
4. 결과를 축별 점수와 지적 목록으로 받는다. 총평만 받으면 다음 단계인 수정으로 넘어갈 수 없다.

3번의 격리에는 판별 목적이 있다. 서로 다른 글을 본 두 기가 독립적으로 같은 종류의 결함을 지목하면 그것은 감사자 개인의 편향이 아니라 콘텐츠 자체의 문제다. 반대로 한 기만 지목한 항목은 감사자 시야 차이일 수 있으니 사람이 원문을 열어 확인한다. 결과는 두 기 모두 평균 15점대였고 결론도 같았다. "재방문할 이유가 없다."

12편에서 어떤 결함이 반복됐는가?

가장 많이 지적된 결함은 무출처 정밀 수치였다. 한 해외 설문 지수의 "62%"라는 숫자가 응답 품질·글쓰기 AI 사용률·문서작업 방해라는 서로 다른 세 개의 주장에 같은 값으로 재사용됐다(2026-08-16 감사 보고 기준). 한 컨설팅 보고서의 수치는 세 편에서 각각 40%, 40~50%, 30~45%로 서로 다르게 인용됐는데, 원문을 확인한 적이 없으니 어느 쪽이 맞는지도 알 수 없었다. 같은 숫자가 여러 자리에 재활용되는 이 유형을 어떻게 찾아내는지는 같은 숫자가 세 곳에 쓰인 통계를 찾아낸 방법에 따로 정리했다.

반복된 결함을 유형으로 묶으면 다음과 같다. 이 목록이 뒤에서 만드는 게이트의 검사 항목이 된다.

결함 유형 실제로 어떻게 나타났나 기계로 잡히나
무출처 정밀 수치 같은 "62%"가 서로 다른 세 주장에 쓰임 부분적 — 기관명이 붙은 것만
통계 재활용 한 보고서 수치가 세 편에서 40% / 40~50% / 30~45% 부분적 — 기관명이 붙은 것만
정형화된 가짜 체험담 4편에서 거의 동일한 문형 반복 예 — 문형 정규식
사이트 내부 모순 같은 상품 가격이 "월 약 2만원" / "약 29,000원" 아니오 — 사람이 대조
모델명 노후화 낡은 모델명을 최신처럼 서술 아니오 — 확인 시점이 필요
고유성 부족 2편이 고유성 축 1/5 아니오 — 사람 또는 감사자 판단

정형화된 가짜 체험담은 그중에서도 가장 아팠다. "현장 경험"이라며 이탤릭으로 강조한 인용 박스가 4편에서 "~한 적이 있었다"로 시작해 "~라는 걸 깨달았다"로 끝나는 거의 동일한 문형으로 반복됐다(2026-08-16 감사 보고 기준). 실제로 겪은 일이 아니라 생성 틀이 찍어낸 문장이라는 것이 문형만 봐도 드러났다.

사이트 내부 모순도 있었다. 같은 상품 가격이 한 글에서는 "월 약 2만원"으로, 다른 글에서는 "약 29,000원"으로 적혀 있었다. 모델명 노후화는 GPT-4o·Claude 3.5 Sonnet·Claude 4를 작성 시점의 최신 모델인 것처럼 서술한 대목들이었다(2026-08-16 감사 기준). 모델명은 확인 시점을 병기하지 않으면 글이 쓰인 순간부터 낡기 시작한다. 마지막으로 고유성 자체가 문제인 글이 있었다. work-prompt-templatesprompt-5-core-principles는 고유성 축에서 5점 만점에 1점을 받았는데, 이 블로그가 아니어도 어디서나 볼 수 있는 내용이라는 이유였다.

두 감사자가 서로 다르게 잡아낸 것은 무엇인가?

같은 평가축을 줬는데도 두 기가 잡아낸 결함의 종류는 갈렸다. 사이트 내부 가격 모순은 한 감사자만 잡았고, 통계 재활용은 다른 감사자만 잡았다. 둘 다 상대가 못 본 것을 하나씩 갖고 있었다는 뜻이다. 이것이 감사를 1기가 아니라 2기로, 그리고 신선 컨텍스트로 돌려야 하는 이유다.

감사 결과를 무엇으로 기계 검사에 옮겼는가?

감사에서 나온 지적 중 기계가 셀 수 있는 항목은 전부 scripts/publish/cc_quality_gate.py의 정규식 검사로 옮겼다. 품질 게이트란 발행 전에 원고를 읽어 정해진 항목을 검사하고, 하나라도 어기면 0이 아닌 종료 코드로 발행을 막는 프로그램이다. 이 파일의 검사 항목에는 번호가 붙어 있고, LLM을 부르지 않으므로 발행할 때마다 무료로 돌릴 수 있다. 현재 검사하는 것은 열한 가지다.

  1. 주석 메타 블록 5개 필드가 채워졌는가
  2. H1이 정확히 1개이고 제목이 55자 이내인가
  3. 3줄 요약 블록이 있는가
  4. 결론표가 3줄 요약 바로 다음인가
  5. 본문 소제목이 4개 이상이고 질문문 비율이 60% 이상인가
  6. 자주 묻는 질문이 정확히 5개인가
  7. 표가 2개 이상인가
  8. 본문 문장 안 내부링크가 2개 이상인가
  9. 금지 요소(외부 기관 통계·창작 체험담·이모지·<style>·중복 구조화 데이터·초안 흔적)가 0건인가
  10. 실사용 요소(코드블록 2개 이상·언어 표시·분량 하한·절차나 체크리스트·한계 문단)를 갖췄는가
  11. 근거 표지 없는 수치 문장이 분량 대비 임계 이하인가

9번과 10번이 이 시리즈를 위해 새로 넣은 항목이다. 9번은 감사가 지적한 결함을 그대로 옮긴 것이고, 10번은 "읽고 끝나는 글"과 "따라 할 수 있는 글"을 가르기 위한 것이다.

# 시리즈 전체 검사
python -X utf8 scripts/publish/cc_quality_gate.py
# 파일 하나만 검사
python -X utf8 scripts/publish/cc_quality_gate.py Blogs/claude-code/my-post.md

출력은 편당 한 줄이고, 오류가 하나라도 있으면 종료 코드 1을 돌려준다. 그래서 발행 스크립트 앞에 그대로 물릴 수 있다. 출력 형식은 다음과 같다(실행 결과가 아니라 형식 예시다).

[PASS] my-post.md                                  5210자  오류 0  경고 1
   ! 질문형 H2 3/6 (AEO: 60% 이상 권장)

지어낸 근거를 잡는 부분은 두 개의 정규식이다. 이 두 줄이 감사에서 가장 많이 지적된 두 결함, 곧 외부 기관 통계와 정형 체험담에 각각 대응한다.

# scripts/publish/cc_quality_gate.py 발췌
ORG = re.compile(r"맥킨지|McKinsey|가트너|Gartner|하버드|Harvard|MIT|스탠퍼드|Stanford|"
                 r"딜로이트|Deloitte|포브스|Forbes|IDC|PwC|액센츄어|Accenture|"
                 r"Work Trend Index|AI Index")
ANECDOTE = re.compile(r"필자[가는]|제 경험|경험상|직접 써보니|써 본 결과|"
                      r"한 적이 있(었|습니)|놀랐습니다|깨달았습니다")

오류와 경고를 가르는 기준은 명확하다. 구조가 깨져 변환이나 인용이 실패하는 것, 그리고 지어낸 내용이 남아 있는 것은 오류이고 발행을 막는다. 사람이 읽고 판단해야 하는 것은 경고이고 발행을 막지 않는다.

구분 대표 항목 처리
오류 메타 필드 누락, H1이 1개가 아님, 결론표가 3줄 요약 직후에 없음, 자주 묻는 질문이 5개가 아님, 내부링크 2개 미만, 기관 통계·체험담·이모지 검출 발행 중단
경고 meta_description 150자 초과, 제목 55자 초과, 본문 H2 4개 미만, 질문형 H2 비율 60% 미만, 표 부족, 근거 표지 없는 수치 문장이 분량 대비 임계 초과 발행 가능, 사람이 판단

게이트 없이 손으로 점검할 사람을 위해 같은 항목을 체크리스트로 옮기면 다음과 같다. 그대로 복사해 쓰면 된다.

[ ] 메타 5개 필드(slug·meta_description·primary_keyword·secondary_keywords·target_query)가 채워졌는가
[ ] H1이 정확히 1개인가
[ ] 3줄 요약 바로 다음에 결론표가 오는가
[ ] 본문 H2가 4개 이상이고 그중 60% 이상이 질문문인가
[ ] 각 H2 바로 아래 첫 문장이 그 질문의 완결된 답인가
[ ] 자주 묻는 질문이 정확히 5개인가
[ ] 본문 문장 안 내부링크가 2개 이상인가
[ ] 외부 기관 통계·창작 체험담·이모지가 0건인가
[ ] 근거 표지(측정일·파일 경로·"우리 환경")가 없는 수치 문장이 임계 이하인가

마지막 항목의 임계는 고정값이 아니라 분량에 비례시킨다. 이 검사는 문장 단위로 도는데 근거 표지가 바로 옆 문장에 있으면 그 문장은 무근거로 잡히고, 글이 길어질수록 그런 문장이 자연히 늘어난다. 고정값으로 두면 잘 쓴 긴 글이 전부 경고를 달아 경고가 무의미해진다. 우리는 대략 650자당 1건을 허용하고 하한을 6건으로 뒀다(cc_quality_gate.pylimit 계산식).

품질 게이트가 정직한 글을 FAIL 내는 경우는 언제인가?

결함을 설명하는 글이 그 결함의 표현을 인용할 때다. 이 게이트는 초기 판본에서 그런 글 2편을 FAIL 시켰고, 그 사실은 지금도 scripts/publish/cc_quality_gate.py의 함수 주석으로 남아 있다. 오진 사슬은 이렇게 이어졌다. 처음 진단은 "이 글에 아직 기관 통계가 남아 있다"였다. 틀린 진단이었다. 그 글은 기관명을 사실로 인용한 것이 아니라 "이런 인용을 하지 마라"는 예시로 따옴표 안에 넣고 있었다. 진짜 원인은 글이 아니라 검사기였다. 검사기가 인용부호 안과 밖을 구분하지 않고 세고 있었다.

대응은 검사 대상 텍스트를 바꾸는 것이었다. 금지 표현을 세기 전에 따옴표·백틱·코드펜스 안을 같은 길이의 공백으로 치환한 사본을 만든다. 길이를 유지하는 이유는 뒤에 오는 위치 계산과 문장 분리가 어긋나지 않게 하기 위해서다.

# scripts/publish/cc_quality_gate.py 발췌 — 원본에는 코드펜스와 인라인 백틱을 지우는 두 줄이 앞에 더 있다
def unquoted(text):
    t = re.sub(r'["“”][^"“”\n]{0,200}["“”]', lambda m: " " * len(m.group(0)), text)
    t = re.sub(r"[‘'][^‘'\n]{0,200}[’']", lambda m: " " * len(m.group(0)), t)
    return t

같은 이유로 JSON-LD 검사도 단어가 아니라 실제 삽입만 잡도록 좁혔다. 본문에서 구조화 데이터 타입 이름을 언급하는 것과 렌더러가 이미 만든 것을 중복 삽입하는 것은 다르기 때문에, 타입 키와 값이 함께 있는 형태일 때만 오류로 센다. 검사기를 만들 때 이 구분을 넣지 않으면, 결함을 다루는 글일수록 더 자주 걸리는 역설이 생긴다.

지적된 결함을 어떤 방식으로 고쳤는가?

발행된 글의 HTML을 통째로 다시 쓰게 하지 않고, 치환 목록만 받아서 적용했다. 이유는 두 가지다. 전체 재작성보다 비용이 싸고, 손대지 않기로 한 부분이 바뀌지 않는다는 보장이 생긴다. 감사에서 지적된 것은 문장 몇 개인데 글 전체가 바뀌면 무엇이 바뀌었는지 검증할 수 없다.

에이전트가 내는 산출물은 글이 아니라 이런 목록이다.

{"id": 126, "slug": "example-slug",
 "patches": [
   {"old": "맥킨지 보고서에 따르면 40%가 개선됐다", "new": "", "why": "무출처 기관 통계"},
   {"old": "직접 써 본 결과 놀랐습니다", "new": "측정 결과는 다음과 같다", "why": "창작 체험담"}
 ]}

적용 규칙은 두 줄로 요약된다. old는 원본에 정확히 1회 나타나야 하고, 0회이거나 2회 이상이면 그 항목만 실패로 기록하고 넘어간다. new가 빈 문자열이면 삭제다. 이 "정확히 1회" 조건이 안전장치다. 여러 곳에 걸리는 문자열은 의도하지 않은 자리까지 바꾸므로 아예 적용하지 않는다.

# 1) 적용 결과만 확인한다. 데이터베이스는 건드리지 않는다
python -X utf8 scripts/publish/aiedu_apply_patch.py --dry
# 2) 확인 후 실제 적용
python -X utf8 scripts/publish/aiedu_apply_patch.py

실행하면 글마다 적용·실패 건수와 함께 기관 통계·체험담 검출 수가 수정 전에서 수정 후로 어떻게 변했는지 같은 줄에 찍힌다. 고쳤다는 보고를 믿는 대신 숫자가 내려갔는지를 본다.

여기서 한 번 걸린 함정이 줄바꿈이었다. 일부 글의 원본은 줄바꿈이 \r\r\n이었고, 파이썬이 파일을 텍스트 모드로 읽으면서 이를 \n\n으로 바꿔 놓았다. 그러면 에이전트가 원본을 보고 만든 old 문자열이 파일 안에서 0회로 잡혀 전부 실패한다. 대응은 패치 쪽 줄바꿈도 같은 규칙으로 정규화하는 것이었다. 치환 도구를 만들 때 "문자열이 안 잡힌다"는 실패가 나오면, 문자열 내용보다 줄바꿈 표현을 먼저 의심하는 편이 빠르다.

이 감사 방식이 통하지 않는 조건은 무엇인가?

정규식 게이트는 형태가 반복되는 결함만 잡는다. 지어낸 통계라도 기관명 없이 "한 조사에 따르면"으로 시작하면 걸리지 않고, 새로운 문형의 체험담도 목록에 없으면 통과한다. 게이트가 PASS를 내는 것은 "결함이 없다"가 아니라 "우리가 아는 형태의 결함이 없다"는 뜻이다. 위 결함 표에서 "기계로 잡히나" 열이 아니오인 세 줄 — 사이트 내부 모순, 모델명 노후화, 고유성 부족 — 은 지금도 사람이나 감사자가 봐야 한다.

점수 자체도 절대적이지 않다. 15.1점은 우리 평가축과 우리 프롬프트에서 나온 값이라 다른 축으로 재면 다른 수가 나온다. 그래서 이 수치는 절대 평가가 아니라 같은 하네스로 다시 재서 비교하는 데만 쓴다. 운영 기준은 세 가지로 정해 뒀다. 재감사 목표는 20/25 이상이다. 그 아래면 발행을 미루고 지적된 항목부터 고친다. 발행 후 3개월간 18점 미만이 반복되면 확장을 멈추고 원인을 다시 진단한다.

이 기준은 이 글에도 그대로 적용됐다. 초판을 같은 방식으로 재감사시켰더니 19/25가 나왔고(2026-08-16 1차 재감사), 20점에 못 미쳤으므로 나머지 편을 내보내지 않고 지적받은 것부터 고쳤다. 편별 총점을 파일로 안 남긴 것, 루브릭 없이 채점시킨 것, 검사 항목을 열한 개라 해 놓고 목록을 안 준 것이 그때 지적된 항목이고, 지금 읽고 있는 문단들이 그 수정 결과다. 수정 후 2차 재감사는 21/25였다.

이번에는 같은 실수를 반복하지 않았다. 두 차례 점수와 지적별 조치를 docs/tracks/claude-code/STATUS.md의 재감사 이력 표에 적어 뒀다. 앞 문단에서 "편별 총점을 파일로 안 남겨 인용하지 못한다"고 쓴 것이 바로 이 글이므로, 자기 점수마저 전사에만 남기면 같은 글 안에서 두 번 같은 실패를 하는 셈이다.

감사 결과에서 나온 규칙 자체는 한 문장이다. 직접 측정했거나, 직접 깨졌거나, 파일로 남아 있는 것만 쓴다. 그런데 이 규칙을 지침 문서에 적어 넣는 것만으로는 부족했다. 지침을 압축하는 과정에서 규칙이 반대 방향으로 뒤집히는 일이 따로 있었기 때문이다. 그 사례는 규칙을 요약하면 방향이 뒤집힌다에 정리했고, 같은 규칙 위에서 측정한 다른 사례는 압축은 누적이 아니라 한 턴이 부른다는 실측에 있다.

자주 묻는 질문

Q. 품질 게이트를 발행 절차에 어떻게 물리는가?
A. 게이트를 먼저 돌리고 성공했을 때만 변환·발행이 이어지도록 한 줄로 잇는다. python -X utf8 scripts/publish/cc_quality_gate.py && python -X utf8 scripts/publish/publish_claude_code.py --dry 형태다. 게이트가 오류를 하나라도 내면 종료 코드 1이 나와 뒤 명령이 실행되지 않는다.

Q. 감사 점수는 어떻게 매겼는가?
A. 고유성·구체성·정확성/시의성·구조·진정성 5개 축을 각 5점 만점으로 채점해 25점 만점으로 합산했다. 12편 평균은 15.1점이었다(2026-08-16 실측).

Q. 감사자에게 준 프롬프트에서 가장 중요한 조건은 무엇이었나?
A. 지적마다 본문 인용을 1개씩 붙이게 한 조건이다. 인용을 붙이지 못하는 지적은 감사자가 지어낸 지적이므로 이 조건 하나가 감사 결과의 검증 가능성을 만든다.

Q. 게이트가 오탐을 냈는지 어떻게 판단하는가?
A. 순서가 있다. 먼저 검사기가 지목한 문장을 원문에서 그대로 찾아 읽는다. 그 표현이 주장인지 인용인지 본다. 인용이면 검사기를 고치고, 주장이면 글을 고친다. 오탐이 한 편에서만 나오면 그 글의 특수 사정일 수 있지만 여러 편에서 같은 항목이 걸리면 검사기 쪽이 틀린 것이다. 임계값을 올려 경고를 없애는 것은 마지막에 고려한다.

Q. 치환할 문자열이 원본에서 0회로 잡히면 무엇을 의심하는가?
A. 문자열 내용보다 줄바꿈 표현을 먼저 의심한다. 원본 줄바꿈이 \r\r\n인데 파이썬이 텍스트 모드로 읽으면서 \n\n으로 바꿔 놓으면, 여러 줄에 걸친 치환 문자열이 전부 0회가 된다. 대응은 패치 쪽 줄바꿈도 같은 규칙으로 정규화하는 것이다. 그래도 0회면 원본을 실제로 다시 읽어 문장이 이미 수정됐는지 확인한다.

Comments

Comments (0)

Leave a Comment

← Back to List