DeepSeek Harness · 컨텍스트 엔지니어링

Token 계측: 결정은 리플레이, 표시는 프로젝션

계측 두 세트가 각자 일하고, 압축 결정은 UI 숫자를 믿지 않습니다. 핵심 소스: packages/llm/token-meter/src/usage-projection.ts.

강의 목표읽고 나면 세 가지를 말할 수 있어요. 압축 결정용 token 수와 UI 표시용 token 수가 왜 두 세트여야 하는지; projectedTokens 공식이 왜 다음 요청 크기에 답하는지; 점유율 퍼센트의 비원자성이 왜 설계 결정인지—문서가 변호문까지 써 둔 이유.
인터랙티브 데모 · 이중 계기판

먼저 해보고 말해요. 왼쪽은 리플레이 실측 measure()—압축 결정이 읽습니다. 오른쪽은 UI 프로젝션 projectedTokens—상태 줄이 보여 줍니다. 대화가 단계적으로 진행돼요. 큰 도구 출력, 압축, 모델 교체, 새 요청. 대부분 두 표가 가깝다가 모델 교체에서 공개적으로 갈라지고, 그것도 당당합니다.

리플레이 실측 · measure()결정용 · 압축할지 볼 숫자
0 0%
임계 눈금 80k · 분모 100k(결정 쪽이 자기 경계에서 현장 해석)
UI 프로젝션 · projectedTokens표시용 · 상태 줄 점유율이 이 숫자
첫 usage 샘플 대기
분모 contextWindow 100k(최신 request/context 기록)
이 비원자성은 의도이지 결함이 아닙니다. 같은 경계에서 정밀 숫자가 정말 필요한 소비자는 자기 요청 경계에서 ctx.tokenMeter.measure()를 호출해야 해요—거기서는 두 값이 함께 나오고, 이 프로젝션을 읽지 않습니다..agents/notes/implemented/architecture/2026-07-29-projected-token-usage-and-request-context.zh.md 35행, 원문 인용
「재생」을 누르거나, 여기로 스크롤하면 자동 재생됩니다.
왜 두 세트인가 · 질문 둘, 대가 둘

먼저 결론: 두 숫자가 답하는 질문이 다릅니다. 압축 결정은 “지금 이 세션이 요청을 내면 얼마나 클까”에 답해야 하고, 정확해야 하며 비싸도 됩니다. UI 상태 줄은 점유율만 보여 주면 되고, 싸고 영속적이며 재연결 직후 바로 보여야 하며 소수점까지 정확할 필요는 없어요. DSH는 아예 길을 나누고 서로 양보하지 않습니다.

결정용 숫자는 정확해야 하고, 비싸도 됩니다.
표시용 숫자는 싸야 하고, 거칠어도 됩니다.

결정 길은 리플레이예요. ctx.tokenMeter.measure()가 호출될 때마다 영속 로그의 현재 꼬리를 불변 스냅샷으로 접습니다. 최근 성공 요청의 provider usage가 현재 요청 봉투와 맞고 전체 휴리스틱 앵커 이상이면 앵커로 쓰고, surface(모델 가시 면) 이후 증감은 부호 있는 delta로 다시 가격을 매기며, 재사용 앵커가 없으면 전체를 고정 휴리스틱으로 가격합니다.

대가도 분명해요. 호출마다 O(surface)라서 압축 같은 결정 쪽만 자기 요청 경계에서 호출합니다.

출처:docs/subsystems/token-meter.zh.md baseline 두 종류(usage 앵커 / estimated 휴리스틱 앵커)의 정의.

표시 길은 프로젝션이에요. 평범한 영속 세션 프로젝션 상태이고, 각자 나중 값이 이기는 필드 둘: pressureTokens—최근 요청이 보고한 프롬프트 측 규모로 입력+캐시 읽기/쓰기, 출력 제외(usage-projection.ts 70–72행); 그리고 최신 request/context 로그의 contextWindow. 분자·분모는 각자 쓰이고 한 번의 원자 관측으로 합쳐지지 않습니다.

영속 세션 로그 usage 샘플 · surface 증감 request/context 기록 measure() 리플레이 앵커 + 부호 있는 delta · 매번 O(surface) 호출 시 계산, 다음 요청 크기에 답 프로젝션 contextPressure pressureTokens · contextWindow 각자 나중 값 승 O(1) 상태 · 재시작·재연결 직후 바로 사용 압축 결정 임계 비교 · 오버플로 복구 Web / TUI 상태 줄 점유율 % · 참고 숫자 정확하지만 비쌈 거칠지만 싸고 영속
수업용 구조도:같은 영속 로그에서 계측 경로 둘이 갈라져 각자 소비자 하나를 섬기고, 서로 넘나들지 않습니다.
프로젝션의 묘 · 다음에 답하고, 지난번에 답하지 않음

pressureTokens만 있으면 난처해요. 요청이 usage를 보고할 때만 갱신되고, Turn 스트리밍 중에는 꿈쩍도 안 하며 압축도 못 봅니다. 압축이 surface 큰 덩어리를 바꿔도 상태 줄 숫자는 그대로라 사용자는 압축이 안 된 줄 압니다. 그래서 fold가 surface 실행 합계도 들고, 공개 값은 샘플 더하기 이후 surface의 부호 있는 변동이에요. 소스 주석은 의도를 직설합니다: occupancy answers for the next request rather than the last one—점유율은 다음 요청 크기에 답한다(usage-projection.ts 150–161행 주석). 데모 5단계가 그 효과: 압축이 막 디스크에 쓰이고 새 요청도 없는데 프로젝션이 이미 내려갑니다.

타이밍 디테일 하나: usage 샘플은 같은 이벤트가 surface에 들어가기 전에 찍힙니다(stamped BEFORE). 그래서 assistant/message는 자기 요청이 본 surface에 앵커되고, 증분 시작점이 어긋나지 않아요.

분자·분모는 원자 쌍이 아님

모델을 바꾸면 새 contextWindow는 즉시 적용되고, pressureTokens는 이전 라우트의 옛 샘플입니다. 점유율은 다음 요청이 usage를 보고할 때까지 근사값이에요. 문서는 이를 취사선택이라 하고 버그로 다루지 않습니다.

pressureTokens는 프롬프트 측만

범위는 inputTokens 더하기 캐시 읽기/쓰기, 출력 제외. 내보내는 요청이 얼마나 큰지 서술하며, 과금 총량 tokenUsage와는 다른 프로젝션 단위이니 섞지 마세요.

결정 경로는 프로젝션을 읽지 않음

Agent Note 원문: harness의 어떤 단계도 점유율 퍼센트로 결정하지 않고, 압축은 measure()를 직접 읽습니다. UI 숫자가 아무리 예뻐도 결정 함수 인자 목록에 들어가지 못해요.

핵심 증거 · 프로젝션 공식 원문

프로젝션의 대외 뷰는 이 7줄이 전부예요. 세 번째 전개가 레슨의 핵심: pressureTokens + surfaceTokens - sampledSurfaceTokens—샘플 더하기 샘플링 이후 surface 순변동을 Math.max(0, …)로 하한 처리. 출처 필드 하나가 빠지면 해당 출력은 아예 안 나옵니다.

packages/llm/token-meter/src/usage-projection.ts198–204행
  view: ({ contextWindow, pressureTokens, surfaceTokens, sampledSurfaceTokens }) => ({
    ...contextWindow === undefined ? {} : { contextWindow },
    ...pressureTokens === undefined ? {} : { pressureTokens },
    ...pressureTokens === undefined || sampledSurfaceTokens === undefined
      ? {}
      : { projectedTokens: Math.max(0, pressureTokens + surfaceTokens - sampledSurfaceTokens) },
  }),
소스 스냅샷 안내:로컬 저장소 deepseek-harness-master 기준, 확인 파일 packages/llm/token-meter/src/usage-projection.ts, 확인일 2026-08-13. 코드 블록은 소스 원문을 유지합니다.

경계 조건 둘은 이 코드에서 바로 나옵니다. 첫째, provider가 usage를 안 주면 프로젝션 측 pressureTokens가 계속 없고 둘째·셋째 전개가 안 나와 UI는 점유율을 표시하지 않아요(Agent Note: 압력과 용량이 모두 알 때만 점유율 표시). 결정 측은 영향 없고 measure()는 estimated 휴리스틱 앵커로 퇴화해 계속 숫자를 줍니다. 둘째, 점유율의 비원자 분기는 데모 6단계에서 이미 뜬 문서 변호로, Agent Note 29–35행이며 제목이 “컨텍스트 점유율은 근사값이고, 그것이 곧 결정 자체”입니다.

가로 비교 · 숫자 하나로 사는 법과 둘로 사는 법
Grok Build: 숫자 하나로 천하

순수 함수 crate 하나를 유일 출처로 열었어요. bytes/4 휴리스틱과 파생 표시 연산으로 /context, /session-info, auto-compact 게이트, preflight 오버플로 검사, 모든 클라이언트 렌더러가 씁니다. 결정과 표시가 태생적으로 한 숫자라 영원히 안 싸웁니다.

대가는 결정도 거친 휴리스틱만 쓴다는 점이에요. 4바이트=1 token(BYTES_PER_TOKEN = 4), 이미지 한 장은 765. 그 crate 모듈 주석은 bytes/4 휴리스틱과 파생 표시 연산의 유일 사실 출처라고 자술합니다. 임계 판정은 u32 정수 퍼센트와 포화 곱셈이고, 세부·소스 증거는 사이트에서 확인한 Token 사용률과 임계 경계를 보세요. provider 실제 usage는 이 출처에서 점유율 계산에 끼지 않습니다.

출처: grok-build-main crates/codegen/xai-token-estimation/src/lib.rs 3–19행, 확인일 2026-08-13.

Claude Code: 표시 출처도 함정을 막아야 함

서브 Agent 진행의 token 카운트는 필드 둘로 저장돼요. input_tokens는 API가 돌려준 라운드별 누적으로 최신만 남기고, output_tokens는 라운드별 증분을 합산합니다. 둘을 그대로 더하면 input이 여러 번 중복되어 표시가 부풀어 올라요(원고 6장 restored-src ProgressTracker, study/chapters/06-task-system.md 169–181행).

압축 결정 임계는 버퍼 상수 세트로, autocompact 예약 13000, 수동 /compact 예약 3000(원고 3장 autoCompact.ts). 방향은 마찬가지로 표시와 결정이 각자 출처지만, 확인한 공개 자료에서는 프로젝션이 다음 요청에 답하게 하는 이런 명시 공식은 보지 못했습니다.

대조해 보면 세 곳이 같은 문제에 답합니다. 점유율 숫자를 누가 쓰고, 틀리면 누가 값을 치르나. Grok은 영원히 일치하는 거친 숫자를 골랐고, Claude Code는 표시 카운트에 중복 방지 패치를 달았으며, DSH는 소비자 둘을 완전히 나눈 뒤 문서에서 표시 쪽이 근사값임을 인정합니다.

수업 실습
01

분기 현장을 손으로 밀어 보기

압축이 막 디스크에 쓰여 surface가 92k에서 8k로 줄었고 새 요청은 아직 없으며, 사용자가 바로 모델을 100k 창에서 200k 창으로 바꿉니다. 지금 숫자 넷에 답하세요. UI 상태 줄의 분자·분모는 각 얼마·어느 필드인지; 압축 결정이 지금 measure()를 호출하면 총량은 대략 얼마·앵커는 어느 종류인지. 마지막으로 Agent Note 35행 원문으로, 두 숫자 세트가 왜 달라도 되는지와 정밀값이 필요한 소비자가 어떻게 해야 하는지 설명하세요.

Takeaway:결정은 리플레이—measure()가 자기 경계에서 계산해 정확하고 비싸며; 표시는 프로젝션—pressureTokens 더하기 surface 순변동으로 거칠고 싸며 영속적입니다. 두 숫자는 달라도 되고, 점유율의 비원자성은 문서에 적힌 설계 결정이에요. UI 퍼센트를 보면 어떤 결정도 그것을 읽지 않는다는 걸 기억하세요.