CodeSmith의 간소화된 아키텍처: 세 개의 영역과 그 계약
요약
CodeSmith 아키텍처는 시스템 프롬프트 드리프트와 토큰 비용 문제를 해결하기 위해 세 영역 구조를 제안합니다. 이 구조는 PinnedPrefix, AppendLog 등 여섯 가지 유형을 단계적으로 도입하여 복잡성을 관리하고 안정적인 캐시 히트율 관리를 목표로 합니다.
핵심 포인트
- 세 영역 모델은 시스템 프롬프트 드리프트와 토큰 비용 문제를 해결하는 데 중점을 둡니다.
- Phase 1의 여러 기능들은 테스트를 통해 제거되거나 통합되어 아키텍처가 간소화되었습니다.
- 새로운 구조는 바이트 레벨 회귀 테스트를 기반으로 배선(wiring)을 진행하여 안정성을 확보합니다.
간소화된 아키텍처: 세 영역에 대한 증명
CodeSmith의 원본 버전:
v0.5.0(커밋3a74c82f). 모든 경로는 리포지토리 루트를 기준으로 하며, 줄 번호는 이 버전을 참조합니다.
예상 독자층: Article 4를 읽고 세 영역 모델이 단지 이론일 것이라고 가정한 모든 사람.
Article 4에서는 수치를 계산했습니다. 시스템 프롬프트를 단일 바이트만큼 드리프트(drift)시키면, 그 이후의 모든 토큰에 전액 요금이 부과된다는 것을 알게 되었고 — 접두사 캐시(prefix cache)의 기반은 '바이트 단위 접두사는 움직이지 않는다'입니다. 이 단계에서 세 영역 모델은 여전히 대부분 이론과 관찰에 머물러 있었습니다: 핑거프린트(fingerprint)가 부검을 수행할 수 있었고, /cache stats로 적중률(hit rate)을 보여줄 수는 있었지만, 대화 기록을 누가 어떻게 다시 작성했는지는 전적으로 코드 자체의 올바른 동작에 달려 있었습니다.
Phase 1의 고백
이 과정은 세 단계로 진행되었으며, 그 역사 중 중간 부분이 이야기할 가치가 있습니다. 첫 번째 단계는 관찰이었습니다: /cache stats가 먼저 도입되어 사람들이 캐시 적중률을 볼 수 있게 했습니다. 두 번째 단계는 Phase 1이었고: PinnedPrefix, FrozenPrefix, PrefixDrift, AppendLog, TurnScratch, ThreeZoneRequest 등 여섯 가지 유형이 구축되었지만, 그 중 어느 것도 엔진에 연결되지는 않았습니다. 모두 #[allow(dead_code)]로 표시되어 스캐폴딩(scaffolding)으로 남아 있었습니다.
Phase 1 시대에는 /cache zones 출력에서 일종의 고백이 담겨 있었습니다: 세 영역 계약은 '아직 연결되지 않았다(not yet wired)'는 것이었습니다. Phase 2에 이르러, 그 고백은 테스트—crates/tui/src/commands/debug.rs의 cache_zones_reports_wired_contract—에 의해 무덤에 묻혔습니다 (debug.rs:928). 이 테스트는 출력에 더 이상 '아직 연결되지 않았다' 또는 'Phase 1 기반(Phase 1 foundation)'이 포함되어 있지 않음을 단언하며, 테스트 내부의 한 줄 주석은 사건을 종결합니다: "Phase 1의 고백들은 사라졌다."
왜 두 단계인가? 여섯 가지 유형을 모두 16,000줄에 달하는 executor 메인 루프 안에 한 번에 용접하려 한다면, 어떤 행동 변화(behavioral drift)도 읽기 힘든 diff 속에 파묻힐 것이기 때문이다. Phase 2의 해결책은 먼저 바이트 레벨 회귀 테스트(byte-level regression tests)라는 경계석을 놓고, 그 후에 배선(wiring)을 건드리는 것이다 (아래 '경계석' 섹션 참조).
세 개의 영역과 여섯 가지 유형
세 영역으로 나눈 구조는 모듈 문서의 원래 다이어그램(crates/agent-runtime/src/prompt_zones.rs:1-16)에서 그대로 인용되었다:
┌─────────────────────────────────────────┐
│ PinnedPrefix (생성 후 고정됨) │ ← 시스템 프롬프트 + 도구 카탈로그
│ freeze() 시 계산된 combined_sha256 │ 캐시 히트 후보
...
중간 영역이 부하를 담당한다. AppendLog는 이제 대화 기록 자체의 저장 유형(prompt_zones.rs:277-294)이며, 그 rustdoc에는 전체 디자인에서 가장 아름다운 문장이 담겨 있다:
"삽입(insert), 제거(remove), 자르기(truncate), 또는 인덱스 쓰기(index-write)가 의도적으로 불가능하다: DeepSeek의 KV 캐시가 일치하는 접미사(append-only prefix)는 규율의 속성이 아니라 유형 자체의 속성이다."
쉽게 말해, 이 유형은 삽입, 제거, 자르기 또는 인덱스 쓰기가 의도적으로 불가능하다. KV 캐시가 히트를 위해 의존하는 접미사(append-only prefix)는 팀의 규율이 아니라 유형 자체의 속성이다. 규율은 늦은 밤 야근에 무너지지만, 컴파일 오류는 그렇지 않다.
읽기 작업은 공유된 Deref<Target = [Message]>를 통해 이루어지며, DerefMut는 의도적으로 구현되지 않았다. 만약 그랬다면 swap이나 sort 같은 제자리 재작성(in-place rewrites)이 다시 컴파일될 것이기 때문이다. 따라서 &log, log[i], log.to_vec()은 평소처럼 작동하며, 반면 Vec의 모든 변경 메서드는 여기서 명백하게 컴파일 실패를 일으킨다.
탈출구 정책: 모든 파괴는 자신의 이름을 서명해야 한다
추가 전용(append-only) 방식에는 컴팩션, 컨텍스트 오버플로우 복구, /edit 롤백, 세션 복원, 사이클 리시딩 등 허가된 예외들이 있습니다. 문제는 이 예외들 자체가 아니라 익명화된 예외입니다. Phase 2에서는 모든 예외를 단일 진입점인 AppendLog::rebuild로 모았으며, 이 함수의 매개변수는 호출자에게 반드시 이유를 명시하도록 강제하는 열거형(enum)을 사용합니다 (prompt_zones.rs:212-244):
pub enum RebuildReason {
ManualCompaction,
AutoCompaction,
...
쉽게 말해, 대화 기록이 통째로 재작성될 때마다 호출자는 타입 레벨에서 자신의 이름을 서명해야 하며, 그 이유와 재작성 전후의 메시지 카운트가 감사 기록(audit record)에 남게 됩니다. 이제 /cache zones는 미스터리로 남기지 않고 "내 캐시가 왜 초기화되었는지"에 대해 답변할 수 있게 되었습니다.
이 보조 문장 역시 벽에 액자로 걸릴 만합니다 (prompt_zones.rs:44-45). 이 사이트들은 설계상 KV 프리픽스 캐시를 관통하며, 타입 시스템의 역할은 이것이 "우연히" 발생하는 것을 불가능하게 만드는 것입니다.
실행자(executor) 측면에서는 기존의 9개 클리어 및 리푸시(clear-and-repush) 사이트가 이 채널로 마이그레이션되었고 (커밋 메시지 자체에 명시된 대로: "The 9 runtime clear+repush sites migrate to it"), 호스트 측 재작성 진입점들도 그 뒤를 이어 정리되었습니다. 이는 오래된 문제도 해결했습니다. 이전 패턴에서는 이벤트가 아예 발생하지 않거나 N번 연속으로 발생하는 식이었지만, 이제는 정확히 하나의 TranscriptRebuilt와 함께 원자적(atomic) 혈액 수혈처럼 처리됩니다. 감사 기록은 TUI에 가장 최근의 8개 항목을 유지합니다 (crates/tui/src/tui/app.rs:666).
단일 지문, 단일 진실 공급원 (One Fingerprint, One Source of Truth)
Phase 2는 또한 지문의 이중 트랙 배열도 끝냈습니다. 이전에는 요청 경로가 하나의 지문을 고정하는 동안 안정성 관찰자(stability observer)가 다른 지문을 계산했지만, 이제 PrefixStabilityManager는 요청 경로가 매 단계에서 고정하는 바로 그 동일한 FrozenPrefix를 사용합니다 (crates/agent-runtime/src/prefix_cache.rs:32-38) — "하나의 지문 구현, 하나의 진실 공급원."
지문(fingerprint)의 알고리즘도 업그레이드되었습니다: tool-catalog 다이제스트가 '이름 목록의 해시'에서 '전체 JSON의 정렬된 해시'로 변경되었습니다. 그 이유는 테스트 주석(prefix_cache.rs:292 근처)에 명시되어 있습니다. 이전 이름 기반 해시는 드리프트(drift)의 전체 클래스를 놓쳤습니다. 즉, 도구는 이름을 유지했지만 설명이나 스키마가 변경되었기 때문에, 지문은 아무것도 감지하지 못했음에도 불구하고 실제로는 접두사(prefix)가 바뀌었던 것입니다. 테스트 verify_detects_schema_change (prompt_zones.rs:592)가 바로 이 클래스를 정확하게 포착합니다.
경계석 (Boundary Stones): 바이트 수준 회귀 테스트
마지막으로, 가장 중요한 보험입니다: 배선은 제로 행동 차이를 증명해야 합니다.
테스트 three_zone_assembly_sends_verbatim_log_snapshot (crates/agent-runtime/src/engine/host_executor.rs:4683)는 이전 및 새 어셈블리를 직렬화(serialize)하고 바이트 단위로 비교합니다. 실패 메시지는 한 줄의 판결입니다: 프롬프트 구성이 비결정적(non-deterministic)입니다 — N번째 바이트에서 첫 번째 차이가 발생했습니다.
해당 주석은 디자인 선언문이나 다름없습니다: 영역별 배선은 요청이 _어떻게 표현될 수 있는지_를 변경할 뿐, 실제로 전송되는 바이트 자체는 절대 변경하지 않습니다. 따라서 투명한 재시도(Transparent retries)는 캐시 안전합니다 — 동일한 영역 입력은 항상 동일한 바이트로 직렬화됩니다.
문서에 덧붙이는 것: 설정 해상도의 순서
같은 기능 브랜치에서 모든 곳에 흩어져 있던 설정 우선순위 규칙들을 하나의 정렬된 표(docs/CONFIGURATION.md:46-57)로 모은 문서(커밋 56503762, 다음 PR을 통해 병합됨)도 포함되었습니다: CLI 플래그 > 설정 파일 (프로필이 최상위를 덮어쓰고, 프로젝트 오버레이가 이를 다시 덮어씀) > 비밀 정보 폴백 (설정 > 키링 > 환경 변수) > 관리형 설정 > 시작 시 요구 사항 검증.
이 부록과 Article 0의 사전 설정 계층(preset tiers)은 동전의 양면입니다. 사전 설정은 빈칸 채우기식 기준선이며, 이 표는 동일한 키가 충돌할 때의 판정 순서입니다. 프로젝트 오버레이는 워크스페이스가 시작 신뢰 경계(startup trust boundary)를 통과하고, 허용된 키가 화이트리스트로 좁혀지고, approval_policy와 sandbox_mode가 느슨해지기보다는 강화되는 경우에만 읽힙니다. 프로젝트 수준의 설정이 제공업체(provider)나 base_url을 변경하려고 한다고요? 문서는 #417을 인용하며 이를 전면 거부합니다. 그렇지 않으면 악성 프로젝트 파일이 사용자의 자격 증명(credentials)을 유사한 엔드포인트로 유도할 수 있기 때문입니다.
결론: 도덕적 설득에서 법 집행으로
이 메커니즘들을 하나의 사슬로 연결해 봅시다:
- 대화 기록의 저장 유형은 AppendLog가 됩니다. 푸시(push)만이 유일한 일상적인 변경이며, Vec 스타일의 재작성(rewrites)은 더 이상 컴파일되지 않습니다.
- 모든 라이선스가 부여된 전체 재작성은 rebuild(RebuildReason)을 거칩니다. 그 이유는 감사 기록에 남고,
/cache zones를 통해 조회할 수 있습니다. - 모든 요청은 ThreeZoneRequest를 통해 조립됩니다. 메시지는 로그의 슬라이스와 임시 꼬리 부분(scratch tail)으로만 구성될 수 있습니다.
- 요청과 관찰(observation)은 동일한 고정 접두사(frozen prefix)를 공유하며, 지문(fingerprint)은 도구 스키마 수준의 변경에 민감합니다.
- 바이트 수준의 회귀 테스트(Byte-level regression tests)가 경계석 역할을 합니다. 배선(wiring)의 변화는 표현력(expressiveness)을 바꾸지, 바이트를 바꾸지 않습니다.
세 가지 속성과 그 대가:
- 규율 대신 속성(Property instead of discipline): 컴파일러가 팀을 대신하여 추가 전용(append-only)을 감시합니다. 그 대가는 새로운 합법적인 재작성 시나리오를 추가한다는 것이, 먼저 열거형 변수(enum variant)를 추가하고 감사 기록에 이름을 남겨야 한다는 것입니다.
- 실명 폐기(Real-name demolition): 캐시 버스트(cache bust)가 사고(incident)에서 장부 기입 항목(bookkeeping entry)으로 바뀝니다. 그 대가는 멋진 제로-버스트 상태가 기록에 결코 접근하지 않는 세션에서만 존재한다는 것입니다.
- 개입 없는 관찰(Zero-intervention observation): 드리프트 감지(drift detection)는 보고할 뿐 가로채지는 않습니다. 그 대가는 드리프트가 발견될 때쯤이면 이미 한 단계의 비용이 지출되었다는 것입니다.
계약서가 서명됨에 따라, 간소화된 메인 라인은 막을 내립니다. 캐싱의 경제학이 법(04)을 작성했고, 타입 시스템이 이를 강제합니다(이번 편). 다음 질문은 다른 영역으로 넘어갑니다. 모델의 주 컨텍스트는 비용이 많이 들고 혼잡하기 때문에, 대규모 자료들이 어떻게 들어오고 계산되는가 하는 문제입니다. 다음 편에서는 Agent가 자신만의 사적인 주방에서 요리하는 것부터 시작합니다.
AI 자동 생성 콘텐츠
본 콘텐츠는 Dev.to AI tag의 원문을 AI가 자동으로 요약·번역·분석한 것입니다. 원 저작권은 원저작자에게 있으며, 정확한 내용은 반드시 원문을 확인해 주세요.
원문 바로가기