본문으로 건너뛰기

© 2026 Molayo

Zenn헤드라인2026. 05. 21. 10:43

LLM이 자신의 기억을 계속 오염시키기에, 기능을 통째로 포기한 이야기 ── C3 v2.11.0 / v2.12.0

요약

C3 프레임워크의 v2.11.0 및 v2.12.0 업데이트 과정을 통해 LLM이 자신의 요약 결과물을 다시 컨텍스트로 받아들이며 발생하는 '기억 오염' 문제와 그 해결 과정을 다룹니다. LLM이 생성한 서술적 문장이 요약본에 혼입되어 데이터의 무결성을 해치는 현상 때문에 결국 'summarize-memory' 기능을 폐지하기로 결정했습니다.

핵심 포인트

  • LLM이 생성한 요약 데이터가 다시 LLM의 컨텍스트로 주입될 때 발생하는 데이터 오염(Narration 혼입) 문제 확인
  • Windows 환경의 일본어 폴더 경로 문제(mbcs 함정)를 ASCII 임시 파일을 통해 해결
  • 프레임워크의 안정성을 위해 기능 추가보다 기능 삭제(빼기 릴리스)가 중요할 수 있음을 시사
  • 코드 리뷰어의 지적을 완벽히 제거하기 위한 반복적인 검토 루프의 중요성

이전 기사: https://zenn.dev/satoh_y_0323/articles/b47f9bf20638be

C3 GitHub: https://github.com/satoh-y-0323/claude-code-conductor / PyPI: https://pypi.org/project/claude-code-conductor/ / 공식 문서: https://satoh-y-0323.github.io/claude-code-conductor/

본 기사의 범위: v2.11.0에서 기능을 하나 통째로 폐지한 이야기(LLM 출력이 자신의 기억을 계속 오염시키는 현상에 항복했다)와, 동일한 v2.11.0에 조용히 포함된 Issue #1 — Windows + 일본어 폴더에서의 수정, 그리고 v2.12.0에서 파일 배치를 taxonomy(분류 체계)에 따라 바로잡고 13 사이클 동안 reviewer(검토자)를 계속 돌린 이야기. 정리정돈 회차입니다만, AI 프레임워크 개발 현장에서는 "c3 recall rebuild가 떨어지는 문제", "LLM에게 LLM의 출력을 sanitize(정화)하게 만드는 설계가 얼마나 취약한지", "Python의 str은 경로로서 안전하지 않을 수도 있다" (C 확장 + Windows mbcs의 함정), "reviewer의 지적을 0으로 만드는 것"이 얼마나 어려운지가 투영되어 보이는 2번의 릴리스였습니다.

서론

지난번 (v2.10.0)에 c3 recall을 도입하여 "과거의 자신을 의미 검색으로 끌어내는" 인프라가 갖춰졌습니다. 이번에는 그 직후의 2번의 릴리스, v2.11.0과 v2.12.0에 관한 이야기입니다.

Version테마내용
v2.11.0기능 삭제 + 치명적 버그 수정summarize-memory를 포기하고 폐지 (LLM이 자신의 기억을 계속 오염시키는 현상을 막을 수 없었음). Issue #1 포함: Windows + 일본어 폴더에서 c3 recall rebuild가 실패하는 문제를 ASCII 임시 파일(temporary)을 경유하여 우회
v2.12.0정리정돈hooks/에서 skills/로의 배치 정리 + 13 사이클 reviewer 루프

기능 추가 릴리스보다는 수수하지만, 프레임워크의 건강 진단으로서는 이쪽이 본게임이라고 생각합니다. "더하기 릴리스는 화려하지만 빼기 릴리스야말로 무섭다"는 것이 이번의 감상입니다.

메인 스토리 1: summarize-memory를 포기한 이야기 (v2.11.0)

어떤 기능이었나

C3는 장기 기억을 위해 2가지 계통의 집약을 가지고 있었습니다.

계통무엇을누가어디로
MVP 세션 집약 (유지)최근 N일간의 sessions/*.tmp로부터 "성공적인 접근 방식 / 실패"를 기계적으로 추출consolidate_memory.py (pure Python)consolidated_summary.md
LLM 장기 요약 (폐지)위 내용 + agent-memory + reports를 모아서 LLM이 "최근의 경향"을 요약summarize-memory agentllm_summary.md

llm_summary.md.claude/CLAUDE.md에서 @memory/llm_summary.md로 자동 주입되었기에, 모든 세션 시작 시 LLM의 컨텍스트(Context)에 들어가는 설계였습니다.

메커니즘 자체는 좋았습니다. LLM이 작동하기 전까지는 말이죠.

무슨 일이 일어나고 있었나 — narration(서술)의 혼입

어느 날부터 이런 내용이 llm_summary.md에 적히기 시작했습니다 (태그는 설명을 위해 대괄호로 표기).

## 최근의 경향
I'll analyze the recent sessions. Let me check the patterns.json first.
[function_calls]
...

요컨대 LLM이 자신의 사고 과정("I'll analyze...")과 내부 도구 호출(tool call) XML을 요약 결과와 함께 써버리는 현상입니다.

처음에는 _sanitize_llm_summary()

함수로 narration을 깎아내는 패치를 추가했습니다. 그다음 주에는 다른 패턴으로 돌파. 다시 패치를 추가했더니 또 다른 패턴. LLM은 표현을 바꿔가며 나타나기 때문에 따라잡을 수가 없습니다.

전파 메커니즘 — 오염이 자기 강화되는 과정

최악인 점은, 오염된 파일이 LLM의 다음 컨텍스트(Context)에 포함된다는 것이었습니다.

1. summarize-memory agent 기동
↓
2. LLM이 출력에 narration / XML을 혼입시킴
...

새니타이저(Sanitizer)는 출력 측에서 깎아낼 뿐이므로, 컨텍스트 측에 오염이 남아 있는 한, 다음 출력에서 동일한 패턴이 부활합니다. 새니타이저를 추가하는 것은 대증요법일 뿐입니다.

사용자가 물었습니다.

그~ 최초 오염의 직접적인 원인은 특정할 수 있나요?

솔직하게 대답했습니다.

특정할 수 있었던 것: 오염의 전파 메커니즘 (@memory/llm_summary.md → 다음 실행이 컨텍스트에서 오염된 파일을 봄 → 구조를 답습)

특정할 수 없었던 것: 처음에 narration 텍스트를 작성한 실제 Write 호출의 트랜스크립트(Transcript)

트랜스크립트를 광범위하게 검색했지만, narration 텍스트가 Write 도구의 content 파라미터에 포함되어 있는 실행을 찾을 수 없었습니다.

최초의 감염원조차 알 수 없습니다.

폐지 판단: 「LLM에게 LLM의 출력을 sanitize 시키기」의 한계

가설과 시도한 대책을 정리:

대책효과
SKILL.md의 프롬프트 제약 강화 ("narration을 내지 마라", "XML을 내지 마라")LLM은 표현을 바꿔서 돌파함
출력 후 정규 표현식(Regular Expression) 새니타이즈패턴을 늘려가는 끝없는 소모전
summarize-memory를 haiku 모델로 변경오히려 오염 패턴이 변화하여 악화
nested_memory 어태치먼트(Attachment) 격리컨텍스트 측의 오염된 파일 자체는 남음

결론: 「LLM의 출력을 LLM으로 sanitize 하는」 설계는 구조적으로 취약하다. Pure Python의 결정론적(Deterministic) 처리는 신뢰할 수 있지만, LLM을 sanitize layer에 넣으면 동일한 문제가 재발합니다.

v2.11.0에서 summarize-memory 기능을 통째로 삭제:

  • summarize-memory agent / skill 삭제 - Stop hook Phase 3 (LLM 요약 기동 플래그) 삭제
  • consolidate_memory.py의 LLM 관련 API 10개 이상 함수 삭제
  • .claude/CLAUDE.md@memory/llm_summary.md 주입 참조 삭제
  • 테스트 57건 삭제 (871건 → 814건)

patterns.json 기반의 promotion 후보와 MVP 세션 집약(consolidated_summary.md)은 pure Python 처리이므로 무사히 남겼습니다. 장기 기억의 대체는 지난번에 도입한 c3 recall (의미 검색)입니다.

"LLM의 환각(Hallucination)은 새니타이저로 고칠 수 없다. 설계에서 제외할 수밖에 없다"라는, 어떤 의미에서는 당연한 결론에 도달하기까지 수 주가 걸렸습니다.

c3 recall rebuild가 일본어 폴더에서 FileNotFoundError 발생 (v2.11.0 동봉)

서브 스토리 1: 사실 v2.11.0에는 또 다른 중요한 수정 사항이 동봉되어 있었습니다 (릴리스 노트에 쓰는 것을 잊어버려 기사 초안에서도 놓치고 있었는데, 사용자가 지적해 준 것). Windows 사용자에게는 치명적인 버그의 수정입니다.

증상

v2.10.0에서 도입된 c3 recall rebuild를, 프로젝트 디렉토리에 일본어가 포함된 환경(제 리포지토리는 C:\Users\shoma\github_project\claude-code-conductor라서 ASCII 환경이라 겪지 않았지만, C:\Projects\調査用\와 같은 사용처에서 발견됨)에서 실행하면 다음과 같이 되었습니다.

Traceback (most recent call last):
File "...\c3\recall_index.py", line 231, in save
_atomic_replace(index_tmp, self.index_path)
...

os.replace가 tmp 파일을 찾을 수 없음 → 직전의 hnswlib.save_index()사일런트하게 실패 (silently failed) 하고 있었던 것입니다.

원인: hnswlib의 C 확장 (C extension) + Windows CRT의 mbcs

hnswlib는 내부적으로 C로 작성되어 있으며, 파일 I/O는 CRT의 fopen()을 사용합니다. **Windows의 fopen()은 ANSI 코드 페이지 (mbcs)**로 경로를 해석하기 때문에, 일본어 폴더명 (Unicode)을 전달하더라도 그대로 열지 못하고, 에러를 반환하거나 빈 파일을 만드는 구현 의존적 동작을 보입니다. Python 측에서 str(path)를 통해 UTF-8 경로를 전달한다고 하더라도, C 확장(C extension)은 전달받는 시점에 다르게 해석합니다. POSIX나 macOS에서는 UTF-8 로케일 (locale)이 표준이므로 이 문제가 발생하지 않습니다. Windows 고유의 함정입니다.

대책: ASCII 임시 경로 (temporary path)를 통한 복사

수정 사항은 _hnsw_save / _hnsw_load 헬퍼 (helper) 함수를 신설하여, Windows이면서 경로에 비 ASCII (non-ASCII) 문자가 포함된 경우에만 우회하는 설계로 변경했습니다.

def _hnsw_save(index: Any, path: Path) -> None:
    """Save via ASCII temp on Windows; hnswlib C fopen silently fails on non-ASCII paths."""
    # POSIX이거나 경로가 ASCII라면 직접 저장 (성능 경로)
    ...

주요 포인트:

대책목적
sys.platform != "win32" or str(path).isascii() 조기 리턴 (early return)POSIX나 ASCII 경로에서는 불필요한 복사를 하지 않음 (성능 경로)
tmp_str.isascii() 검증%TEMP%를 비 ASCII로 커스텀한 특수 환경에서의 조기 실패 (fail early) 처리
shutil.copy2로 이동hnswlib은 ASCII tmp에 쓰고, Python이 비 ASCII 최종 경로로 복사
RuntimeError(f"... for {path.name}")hnswlib 예외 메시지에 내부 tmp 경로가 포함되는 것을 방지 (정보 은닉)
os.chmod(tmp, 0o600) (코드 생략)tmp 권한 제한
try/finally로 tmp 정리 (cleanup)실패 시 불완전한 tmp 파일을 남기지 않음
load() 측도 _hnsw_load를 경유하도록 통일save만 수정하면 load에서 동일한 문제가 발생함

교훈: "Python의 str은 경로로서 안전하다"라고 단정할 수 없다

Python 레벨에서 Path / str(path)를 사용하고 있더라도, C 확장으로 넘어가는 순간 OS와 런타임 (runtime)의 세계입니다. Windows + 일본어 폴더 + C 확장의 트리플 조합에서 빠지게 되는 함정이었습니다.

C3와 같이 pip install로 배포하는 도구는 사용 환경의 로케일 (locale) 및 경로 문자 종류를 예측할 수 없습니다. POSIX ASCII를 전제로 한 테스트만으로는 실패할 수 있습니다. CI (지속적 통합)에 Windows + 일본어 폴더를 사용하는 잡 (job)을 하나 추가하거나, tempfile.mkstemp를 전면적으로 경유하는 설계로 만드는 것이 방어책입니다.

v2.11.0에 포함된 기타 수정 사항 (간략히 언급)

Issue #1 외에도 리뷰 사이클에서 발견된 수정 사항들이 포함되어 있습니다:

  • f44dd7b: consolidate_memory.py에 stdin 1MB 제한, 타입 검증 (type validation), 줄바꿈 제거를 도입 (거대한 페이로드 (payload)로 인해 OOM이 발생할 위험을 제거)
  • be9b3bc: consolidate_memory.py_sanitize_field

탭(tab)·null byte·ASCII 제어 문자까지 확장([SR-V-001]) -
f448c6e: session_stop

stdin 초과 시의 동작을 return 0으로 통일(docstring과의 정합성) -
8e1703e: consolidate_memory

의 새니타이즈(sanitize) 및 docstring 최종 정합

모두 단독으로는 미미한 강화이지만, Stop hook과 장기 기억(long-term memory) 계열의 신뢰성이 한 단계 높아졌습니다. summarize-memory를 폐지한 후, 남은 consolidate_memory가 'Pure Python 기반의 신뢰할 수 있는 집약'으로서 책임이 커졌기에 이곳을 견고히 할 필요가 있었다는 흐름입니다.

메인 스토리 2: 택소노미(Taxonomy) 재정비 + 13 사이클 리뷰어 (v2.12.0)

계기

v2.11.0을 출시하고 안정을 찾았을 무렵, 사용자로부터 재정비(inventory) 요청을 받았습니다.

기능의 추가, 통합, 삭제 등 다양한 대응을 해온 결과, 당초의 택소노미 설계 사상과 맞지 않게 된 것이 없는지 재정비해 달라는 요청이었습니다.

C3의 폴더 택소노미는 .claude/docs/taxonomy.md에 정의되어 있습니다. 요지는 다음과 같습니다:

카테고리역할
agents/단일 에이전트(agent)의 페르소나·작업 절차
skills/복수 에이전트를 가로지르는 오케스트레이션(orchestration) 절차
rules/에이전트에게 주입하는 배경 지식·제약 (절차가 아님)
hooks/Claude Code의 라이프사이클 이벤트에서 자동 실행되는 스크립트
reports/에이전트가 출력하는 리포트

이에 대해 .claude/hooks/를 살펴보니, 명백히 이벤트 훅(event hook)이 아닌 파일들이 섞여 있었습니다.

파일실체
review_hint_inject.py리뷰 후에 리포트에 과거 판단 힌트를 추가하는 CLI. dev-workflow skill에서 Bash로 호출됨
record_review_decision.py리뷰 판단을 c3.db에 기록하는 CLI. 위와 동일
record_tier_outcome.pytier-routing의 결과를 c3.db에 기록하는 CLI. 위와 동일
subagent_log.pyPO(Parallel Orchestra, v2.0.0에서 폐지됨) 시절의 서브 에이전트(subagent) 기동 로그. 아무도 호출하지 않는 잔해

hooks/에는 이벤트 훅 이외의 것을 두지 않는다는 것이 택소노미적으로 올바른 모습입니다. skill에서 Bash로 호출되는 CLI는 skills/<name>/scripts/에 두어야 합니다 (report-timestamp/scripts/get_timestamp.py가 기존 선례).

재정비 의사결정

사용자와의 대화를 통해 규칙을 하나 명확히 했습니다.

삭제하기로 결정했다면 모두 깔끔하게 삭제한다. 나름의 이유를 대며 남겨두고 싶어 하지만, 남겨두어 봤자 좋은 것은 아무것도 없다. 나중에 조사할 때 정체불명의 파일로서 조사 비용을 발생시킨다. 무의미한 비용이다. 이렇게 될 바에는 처음부터 삭제하기로 결정한 타이밍에 확실하고 깔끔하게 삭제하는 것이 도리다.

→ 메모리에 기록: "삭제로 판단했다면 '삭제 후보', '.dev/로 이동' 등 어중간한 처치는 금지".

subagent_log.py는 논의 없이 삭제. 3개의 스크립트는 .claude/skills/dev-workflow/scripts/로 이동.

Cycle 1 〜 13의 싸움 — 리뷰어의 지적은 무한히 나온다

C3의 페이즈 E(리뷰)는 code-reviewer / security-reviewer를 병렬로 기동하여 지적 사항이 0이 될 때까지 대응하는 워크플로우입니다. 사용자 지시 사항:

구현이 끝나면 그대로 git 차분에 대해 code-reviewer와 security-reviewer를 병렬 실행하여, 지적이 단 하나라도 있다면 그것이 Low 수준이라도 모두 대응하고, planner로 되돌려 표준 워크플로우로 돌리며 지적이 0이 될 때까지 반복하십시오.

사이클 결과 (경기 경과):

사이클CRSR누적 대응테스트메모
12H+2M+2L1M+2L9건825 PASS첫 번째 리뷰. 테스트의 이전 PASS 참조 22건 FAIL 포함
...2건852 PASSSecurity 측 첫 완전 PASS
13PASSPASS0건852 PASS양쪽 모두 PASS → 루프 완료

Cycle 13까지 12 사이클을 실행하여 총 56건 대응。테스트는 825 → 852(27건 추가).

도중에 속으로는 '이거 무한 루프에 빠지는 것 아닌가...'라고 생각했습니다. reviewer agent가 매번 새로운 개선 포인트를 찾아내기 때문입니다. "코멘트가 오해의 소지가 있음(misleading)", "docstring에 보충 설명 필요", "상수의 정합성을 보장하는 테스트"── 모두 타당한 지적이지만, Low 수준의 지적은 끝이 없습니다.

실용적인 깨달음: reviewer의 주관성(subjectivity)을 제어하는 메커니즘이 필요합니다. Cycle 12부터 "실질적인 해가 되는 보안 및 기능 리스크만 Critical/High/Medium으로 분류하고, 사소한 개선 제안은 생략해달라"고 prompt에 명시했더니, Cycle 13에서 처음으로 양쪽 모두 PASS가 되었습니다. reviewer agent를 신뢰한다면 스코프(scope) 지시도 명시해야 한다는 것이 결론입니다.

서브스토리 2: 코멘트를 다시 썼더니 Unicode 실체 문자가 섞여 들어가는 함정

사이클 중반에 재미있는 함정에 빠졌습니다.

발단

record_tier_outcome.py_append_prompt_history()에서 U+2028 (LINE SEPARATOR)를 JSON Unicode escape로 이스케이프(escape) 하는 처리를 넣고 싶었습니다. JSONL 행 구분자를 보호하기 위해서였습니다.

line = json.dumps(record, ensure_ascii=False)
line = line.replace("\u2028", "\\u2028").replace("\u2029", "\\u2029")

Python 리터럴 "\u2028"실체 문자 1개(U+2028)로 전개되므로, 이는 올바르게 작동합니다. 소스 코드 상에는 실체 문자를 직접 삽입하지 않는 것이 베스트 프랙티스입니다(파일 스캔이나 lint에서 문제를 일으킬 수 있기 때문).

함정 1: Edit 툴이 Python escape를 실체 문자로 변환

그런데 제가 Edit 툴의 old_string / new_string"\u2028"과 같은 표기를 전달하면, 툴 측에서 이것이 실체 U+2028로 전개된 상태 그대로 파일에 기록되었습니다.

결과적으로, 제가 "소스에 실체 문자를 삽입하지 않겠다"는 의도로 작성한 코멘트와 구현에 실체 U+2028가 섞여 들어가는 아이러니한 상황이 발생했습니다. reviewer (code-reviewer)가 hex dump를 통해 이를 검출했습니다.

[H-01] record_tier_outcome.py L162: replace() 인자에 실체 문자 사용
(표시상으로는 공백처럼 보이지만, 실제 UTF-8 바이트는 \xe2\x80\xa8 / \xe2\x80\xa9
— hex 바이트 레벨에서 확인 완료)

함정 2: 코멘트의 일본어가 \uXXXX로 변함

수정을 위해 chr(0x2028)을 상수로 만드는 방침으로 전환했고, 코멘트에 "raw 문자열 r"\u2028"로 리터럴 6글자를 확보한다"라고 일본어로 설명을 다시 작성했습니다.

그러자 이번에는 코멘트 쪽에서 일본어가 \uXXXX 형식의 ASCII 이스케이프 시퀀스(escape sequence)로 파일에 저장되는 현상이 일어났습니다. Edit 툴이 Python의 unicode_escape 인코딩을 거치는 듯한 동작을 보였습니다.

해결: 영어 ASCII 코멘트 + Python heredoc 직접 쓰기

최종적으로:

  • 코멘트는 영어로만 작성한다 (일본어를 피함으로써 Edit 툴의 간섭을 회피)
  • 남은 실체 U+2028는 Bash + Python heredoc을 사용하여 data.replace(b'\xe2\x80\xa8', b'\\u2028')로 처리한다.

의 바이트 레벨 치환 - 구현은
chr(0x2028)

상수 + raw string r"\u2028"

로 escape 표기 고정

_LS = chr(0x2028) # LINE SEPARATOR
_PS = chr(0x2029) # PARAGRAPH SEPARATOR
# The second arg must be the 6-char ASCII string '\u2028' (literal backslash + 'u2028'),
...

교훈: 「소스에 실체 문자를 쓰지 않는다」라고 결정해도, 툴을 통해 기록하면 툴 측에서 전개(expand)되는 경우가 있다. Edit 툴이 내부에서 무엇을 하고 있는지는 불가시적이다. 결국에는 바이트 레벨 검증으로 확인할 수밖에 없다.

서브스토리 3: 「agent 본체에 절차를 너무 많이 담은 것 아닌가?」(다음으로 미뤄둔 과제)

릴리스 완료 후, 사용자로부터 날카로운 지적을 받았다.

나는 당연히 Agent 중에서도 수정되는 것이 나올 것이라고 생각했다.

에이전트 정의(agent definition) 안에서 처리를 가지고 있고, 처리 절차 등은 스킬(skill)에 작성하여, 에이전트 본체는 가벼운 정의만 남기도록 분리하는 작업이 들어가지 않을까 하고.

확실히 이번 점검은 **agent / skill / hook의 「파일 간 배치 경계」**만을 정리했다. agent 본체 내의 구조(페르소나 정의 vs 처리 절차)는 손대지 않았다.

예를 들어 planner.md를 열면, 「writes 필수」, 「depends_on 선언 형식」, 「병렬 가능 여부 판정」 등 계획 수립의 세세한 절차가 에이전트 정의 본체에 가득 차 있다. 이것은 본래 skill (dev-workflow의 Phase C) 측에 작성해야 할 내용이며, agent 본체는 「당신은 계획 수립 담당입니다. requirements/architecture를 읽고 plan-report를 출력합니다」 정도의 가벼운 정의로 만드는 것이 taxonomy(분류 체계)적으로 올바른 모습이다.

[현상]
agents/planner.md (무거움)
- 페르소나
...

taxonomy.md에는 「agent = 단일 에이전트의 페르소나·작업 절차」라고 되어 있으며, 「절차를 어디까지 본체에 작성할 것인가」에 대한 지침은 명시되지 않았다. 비대화가 진행되고 있었다.

이는 다음 v2.13 후보로서 남겨진 작업(backlog)으로 기록했다. reviewer가 지적 0개까지 몰아붙였던 v2.12.0에서도, reviewer의 스코프가 「변경 차분의 텍스트 품질」이었기 때문에 간과할 수 있는 종류의 문제였다. 인간(사용자)의 조감적 시점은 AI reviewer의 맹점을 메운다는 깨달음이다.

메모리에 기록:

---
name: project-agent-md-refactor-backlog
description: agent 정의 파일의 본체 내용을 「페르소나 vs 처리 절차」로 점검하여, 과도하게 절차를 안고 있는 agent는 skills 측으로 이양하는 향후 과제
...

업무에 활용할 수 있는 가능성

1. 「뺄셈 릴리스」를 정례화하기

기능 추가 릴리스는 화려하고 눈에 띄지만, 기능 삭제 릴리스는 건강검진이다. LLM을 도입한 시스템에서는 특히 「사용해 보니 제대로 작동하지 않는다」, 「고치려고 하니 끝이 없다」와 같은 기능이 늘어나기 쉽다. v2.11.0의 summarize-memory 폐지와 같이, 고치는 비용보다 삭제하는 비용이 더 저렴한 경우가 있다.

실무에서의 운용안:

  • 분기에 1회 「기능 점검의 날」을 만든다
  • 「최근 90일 동안 에러 / 수정 / 불만이 있었던 기능」을 리스트업한다
  • 「다른 기능으로 대체 가능」하다면 삭제 후보로 둔다
  • 삭제한 만큼의 인지 부하 감소를 가시화한다 (테스트 수, LOC, sanitizer의 행 수 등)

C3의 경우, v2.10.0에서 c3 recall을 추가했었기 때문에 summarize-memory를 삭제하더라도 장기 기억 기능 자체는 상실되지 않았다. 삭제는 고립 리스크가 높은 기능부터가 아니라, 대체 수단이 있는 기능부터 하는 것이 요령이다.

2. LLM의 출력을 LLM으로 sanitize 하지 않기

이것은 설계 원칙으로서 가지고 있어야 할 이야기다. LLM의 출력을 다른 LLM(또는 동일한 LLM의 재호출)로 sanitize 하는 설계는, **sanitizer 자체가 LLM이므로 비결정론적(non-deterministic)**이 된다. sanitizer를 돌파당했을 때 「다음 sanitizer를 추가한다」는 끝없는 쫓고 쫓기는 싸움(cat-and-mouse game)에 빠지게 된다.

대신에:

  • 순수한 (pure) 결정론적 처리 (deterministic processing) (정규표현식 (regex)・스키마 검증 (schema validation)・화이트리스트 (whitelist))로 sanitize 한다 - 그것으로 불충분하다면,
    기능의 경계를 다시 설정하여 LLM 출력을 다루지 않는 설계로 바꾼다

C3의 경우라면 「LLM 요약」을 포기하고 「의미 검색 (semantic search)으로 과거의 생데이터 (raw data)를 불러오는」 설계 (c3 recall)로 대체한 것이 후자입니다. LLM 출력을 가지지 않으므로 오염될 여지가 없습니다.

3. reviewer의 지적은 주관성 (subjectivity)을 제어한다

C3의 review 사이클은 「지적 0건까지 대응」이 원칙이지만, reviewer agent는 매번 새로운 개선 포인트를 찾아낸다는 성질이 있습니다. 완전한 0에 도달하는 것은 구조적으로 어렵습니다.

실용적인 대처:

  • 첫 번째 사이클은 「전수 검출」 모드 (포괄성 중시)
  • 사이클 수 N 이후는 「실질적인 해가 있는 리스크만」 모드 (프롬프트로 명시)
  • 그럼에도 남는 낮은 수준 (Low)의 지적은
    accepted_exceptions.json

등에 기록하고 정지

C3에서는 Cycle 12 → 13에서 reviewer 프롬프트에 「실질적인 해가 있는 지적만」을 추가한 순간 PASS가 되었습니다. reviewer에게 무엇을 볼지 지시하는 것은 중요한 엔지니어링입니다.

버전 요약

AI 자동 생성 콘텐츠

본 콘텐츠는 Zenn AI의 원문을 AI가 자동으로 요약·번역·분석한 것입니다. 원 저작권은 원저작자에게 있으며, 정확한 내용은 반드시 원문을 확인해 주세요.

원문 바로가기
0

댓글

0