DeepSeek Harness · 엔지니어링 방법론

KV Cache는 인터페이스다

prompt 접두사 안정성을 호환성 약속으로 유지합니다.

강의 목표읽고 나면 세 가지를 말할 수 있어요. prompt 접두사 한 글자를 바꾸면 뒤쪽 모든 token 캐시가 왜 무효가 되는지, DSH가 접두사 안정성을 호환성 약속으로 유지하는 세 가지(문서 규율, 도구 순서 중앙 목록, 엄격한 보간), 그리고 Claude Code가 10.2%의 실제 비용 교훈으로 같은 이치를 검증했다는 점.
인터랙티브 데모 · 접두사 안정성 현미경

먼저 해보고 나서 설명해요. 아래 색 띠는 system prompt와 도구 schema의 token 서열이에요(수치는 수업용 추상화). 「재생」을 누르면 흔한 조작을 차례로 보여 줍니다: 그대로 재전송, persona 한 글자 변경, 도구 추가, 대화 추가, 플러그인 로드 순서 흔들림. 매 단계마다 캐시가 몇 번째 token부터 깨지는지 표시하고, 오른쪽 계측기가 추가로 낸 재계산 token을 누적합니다. 오른쪽 위에서 DSH 모드와 대조 모드를 바꿀 수 있고, 흔들림 단계의 결과는 완전히 달라요.

모델에 보내는 요청 접두사(칸마다 한 구간, 너비는 token 수)
harness 신원 persona 도구 schema 동적 컨텍스트 대화 기록
아직 요청을 보내지 않았어요.
무효화 비용 계측기
0
캐시에 맞았어야 하는데 강제로 다시 계산한 token 누적입니다. 캐시 히트 입력 단가는 보통 미스보다 한 자릿수 저렴하고, 이 숫자는 청구서에 바로 곱해져요.
이번 요청 히트
요청 횟수0
「재생」을 눌러 시작하세요. KV Cache(키-값 캐시): 모델은 같은 token 접두사에 대해 이전 어텐션 계산을 재사용할 수 있어요. 단, 접두사가 글자 단위로 같아야 합니다.
로직 분해 · 글자 하나가 캐시 전체를 무너뜨리는 이유

KV Cache를 쉬운 말로요. 모델이 요청을 처리할 때 token마다 중간 결과(키와 값)를 캐시합니다. 다음 요청이 들어올 때 앞쪽 token 서열이 이전과 글자 단위로 같으면 그 접두사 계산을 바로 재사용하고, provider는 히트에 할인을 줍니다. DeepSeek 공식 요금에서 캐시 히트 입력 token은 미스보다 한 자릿수 저렴해요(정확한 배율은 공식 요금표 기준).

핵심은 “글자 단위로 같다”예요. 캐시는 접두사 매칭이라, 첫 번째로 다른 token부터 뒤는 전부 무효입니다. agent 요청 앞에는 뭐가 있을까요? system prompt와 도구 schema, 수천 token이 흔하고 매 요청마다 붙어요. persona 단어 하나, 도구 순서, 앞에 현재 시각을 넣으면 캐시가 그 지점에서 끊기고, 이후 요청은 전부 정가로 다시 계산됩니다.

그래서 DSH의 결론: prompt 접두사는 모델이라는 API의 인터페이스이고, 그 안정성은 호환성 약속이라 공개 API처럼 유지해야 합니다. 엔지니어링으로는 세 가지예요.

첫째, 문서 규율에 넣기. 로컬 스냅샷 packages 아래 README 268개 중 215개가 고정 #### KV Cache effect 절을 둡니다. 모델 요청에 들어갈 수 있는 것은 모두 세 단락으로 적어야 해요: 모델이 보는 것(What the model sees), token 비용(Token effect), 캐시 영향(KV Cache effect). packages/core/tools/README.md 도구 schema 절 145행 원문:

Prefix-stable while visible definitions and their order are unchanged. Registration, disposal, or scoped restriction may invalidate reuse from the first changed schema token.
(보이는 도구 정의와 그 순서가 변하지 않으면 접두사는 안정적입니다. 등록·제거·스코프 제한은 바뀐 첫 schema token부터 캐시 재사용을 무효화할 수 있어요.) 출처: deepseek-harness-master 저장소 packages/core/tools/README.md 145행, 확인일 2026-08-13

같은 파일 186–188행에는 반대 진술도 있어요: 도구 호출 기록과 결과는 append-only라, 새 내용은 재사용 가능한 접두사 뒤에 붙고 기존 캐시를 깨지 않습니다. 무엇이 캐시를 해치고 무엇이 아닌지, 전부 찾아볼 수 있는 문서 항목으로 적혀 있어요.

둘째, 도구 순서를 중앙 목록으로 정규화. 도구 schema는 접두사의 큰 덩어리고, 원래 플러그인 등록 순서를 따랐어요. 플러그인은 동시 로드라 등록 순서가 환경에 따라 흔들리고, DSH는 CI에서 서로 다른 요청 헤더를 실제로 봤습니다(Agent Note 2026-07-06-explicit-tool-order 문제 절). 순서는 요청 바이트에, 바이트는 캐시에 영향을 주니 명시적으로 다뤄야 해요: 설정의 toolOrder 목록이 순서를 통일하고, 목록에는 <unlisted-tools> 나머지 마커가 정확히 하나 있어야 하며, 목록이 없으면 사전순으로 폴백합니다. 정규화는 assemble() 안, waterfall 전에 일어나고, 등록 순서는 관측 가능한 어디에도 더 이상 나타나지 않아요.

셋째, 엄격한 보간 — 예외를 던지고 나쁜 prompt는 내보내지 않기. persona는 템플릿이고 {{model}} 같은 변수 그룹은 등록표를 엄격히 따릅니다: 미등록 변수, 이번 조립에 값 없음, 중괄호 형식 오류는 모두 예외. 턴은 즉시 실패하고 요청은 하나도 나가지 않아요. 이유는 단순합니다: 조용한 관용은 슬쩍 변형된 접두사를 모델에 보내고, 캐시가 슬쩍 깨지며, 나쁜 prompt가 행동까지 슬쩍 바꿀 수 있어요. 크게 실패하는 편이 더 싸요.

순서도 인터페이스

내용이 같고 순서만 다른 두 도구 schema는 캐시에게는 서로 다른 접두사예요. 그래서 도구 순서를 로드 타이밍 같은 환경 잡음에 맡기면 안 됩니다.

무효화는 바뀐 첫 token부터

캐시는 접두사 매칭이라 앞쪽일수록 건드리면 안 돼요. 변하는 것(시간, 동적 상태)은 뒤로, 거의 안 바뀌는 신원과 schema는 앞으로.

추가는 캐시를 해치지 않음

대화 기록은 append-only로 늘고, 옛 접두사는 그대로 남으며 새로 늘어난 부분만 정가를 냅니다. 세션 로그를 추가만 하는 설계가 청구서에서 주는 배당이기도 해요.

핵심 증거 · 중앙 목록과 엄격한 보간의 소스

아래는 도구 정렬의 핵심 로직입니다. 함수 앞(165–168행)에서 예약 이름 검사: 도구 제공자가 예약명 <unlisted-tools>를 쓰면 바로 예외. 이어 정렬 본체에서 실패 분기 둘: 목록 없으면 사전순 폴백, toolOrder에 미등록 도구명이 있으면 예외. 예외 시점은 조립 단계, 요청 나가기 전이에요.

packages/core/system-prompt/src/index.ts169–178행
  if (toolOrder === undefined) return tools.sort(compareToolNames)
  const unknown = toolOrder.filter(name => name !== TOOL_ORDER_REST && !knownNames.has(name))
  if (unknown.length > 0) {
    throw new Error(`toolOrder lists unregistered tool${unknown.length > 1 ? 's' : ''} ${unknown.map(name => `"${name}"`).join(', ')}; known tools: ${[...knownNames].sort().join(', ') || '(none)'}`)
  }
  const listed = new Set(toolOrder)
  const rest = tools.filter(tool => !listed.has(tool.name)).sort(compareToolNames)
  return toolOrder.flatMap(name =>
    name === TOOL_ORDER_REST ? rest : tools.filter(tool => tool.name === name))
}
소스 스냅샷 안내: 로컬 저장소 deepseek-harness-master 기준, 확인 파일 packages/core/system-prompt/src/index.ts, 확인일 2026-08-13. 코드 블록은 소스 원문을 유지합니다.

경계 조건 두 가지는 기억할 만해요. 출처는 모두 Agent Note 2026-07-06-explicit-tool-order. 플러그인 핫 리로드 후 등록 순서가 바뀌면 도구 순서도 바뀔까요? 아니요. 중앙 목록이 waterfall 전에 정규화해서 등록 순서는 관측할 곳이 없어요. toolOrder에 오타난 도구명을 쓰면? 해당 Note 결과 절이 자세히 적습니다: 턴이 조립 때 실패하고, 스텝을 열지 않으며, 요청 헤더를 기록하지 않고, 어댑터로 요청을 보내지 않아요. 설정을 고칠 때까지 매 턴이 같이 실패하고, 프로세스 자체는 계속 돌아갑니다.

엄격한 보간은 연달아 예외를 던지는 분기 세 개라 하나도 통과시키지 않아요. 첫째는 형식: 변수명이 명명 정규식과 안 맞으면 “malformed prompt variable reference”. {{}} 같은 빈 이름도 주석이 짚고, 이 형식 오류 경로로 가요. 둘째는 등록: 등록표에 없으면 “unknown prompt variable”, 등록된 변수명도 같이 나열. 셋째는 값: 등록됐지만 이번 조립에 값이 없으면 역시 예외. 세 분기가 막는 것은 같은 일 — 슬쩍 변형된 접두사예요.

출처: packages/core/system-prompt/src/index.ts 277–290행 보간 분기, 확인일 2026-08-13.

283행 Object.hasOwn에는 작은 장치가 있어요. 일반 속성 접근으로 {{constructor}}를 조회하면 프로토타입 체인으로 Object 내장 메서드에 닿아 등록 변수로 오인합니다. 자체 속성만 보면 프로토타입 체인 이름은 전부 미등록. 슬쩍 통과하는 나쁜 prompt를 막는 장치예요.

가로 비교 · 10.2% 교훈과 정적 경로 하나

Claude Code: 복원 소스 restored-src/src/tools/AgentTool/prompt.ts 57–64행 주석이 실제 사고를 기록합니다. 서브에이전트 목록이 도구 설명에 들어 있었고, MCP 비동기 연결·플러그인 리로드·권한 모드 전환이 목록을 바꿔 도구 설명이 변하면 도구 schema 캐시 전체가 무효가 됐어요. 이 한 문제가 전 세계 클러스터 cache_creation token의 10.2%였습니다. 고치는 법은 DSH와 같은 방향: 변하는 목록을 정적 접두사에서 빼내 별도 attachment 메시지로 주입하고, 도구 설명은 캐시 가능하게 유지(자료: claude-code-sourcemap-main/study/chapters/05-multi-agent.md 106–121행). 차이는 시점: Claude Code는 청구서에서 10.2%를 본 뒤 고쳤고, DSH는 CI 흔들림 단계에서 순서를 다스리고 규율을 문서 215개에 깔았어요.

Grok Build: 정적 템플릿 경로를 갑니다. system prompt는 미리 만든 템플릿을 복호화·렌더링하고(crates/codegen/xai-grok-agent/src/prompt/template.rs), AGENTS.md와 skills를 붙입니다(같은 디렉터리 mod.rs 모듈 분할). 템플릿은 컴파일 타임에 고정이라 접두사가 동적 조립보다 자연히 안정적 — 정적 경로의 선천적 이점이에요. 대가는 유연성: DSH처럼 플러그인이 언제든 구간·변수·도구를 기여하는 조립 모델은 이 경로에 없습니다. Grok에 동등한 패키지별 캐시 영향 문서가 있는지는 확인한 로컬 스냅샷에 없고, 공개 증거 기준으로 미지로 남겨 둡니다.

수업 실습
01

당신 prompt 안의 캐시 킬러 찾기

agent가 system prompt 둘째 줄에 「현재 시각: 2026-08-13 22:04:35」를 쓰고 매초 변한다고 가정하세요. 추론: 매 요청 캐시는 몇 번째 구간부터 깨질까요? 하루 1000건이면 재계산 token을 얼마나 더 낼까요? 수정안 둘을 비교하세요: 시간을 prompt 끝 동적 컨텍스트로 옮기기, 또는 정밀도를 일로 낮추기. 힌트: 어느 쪽이 날짜가 바뀌는 경계에서도 캐시를 한 번 끊을까요.

02

팀을 위한 KV Cache effect 템플릿 쓰기

DSH의 세 단락(모델이 보는 것 / token 비용 / 캐시 영향)을 흉내 내, 프로젝트에서 「모델 요청에 들어가는 것」 목록을 만드세요: system prompt, 도구 schema, 동적 주입 컨텍스트, RAG 검색 결과. 항목마다 캐시 영향을 쓰고 위치가 잘못된 것을 표시하세요. 쓰면 Claude Code의 10.2%와 같은 계열 문제를 적어도 하나 찾을 가능성이 큽니다.

Takeaway: KV Cache는 접두사를 글자 단위로 맞추고, 바뀐 첫 token부터 전부 무효라서 prompt 접두사 안정성은 지켜야 할 호환성 약속이에요. DSH 삼종 세트: 문서 215개의 고정 KV Cache effect 절, 순서 흔들림을 없애는 toolOrder 중앙 목록, 보간 형식 오류 시 예외. 변하는 내용을 접두사 끝 뒤로 두는 것은 모든 harness에 통하는 절약 동작입니다.