규칙에 따라 도구 출력을 가지치기하고 추론 체인은 그대로 두는 방법
요약
에이전트의 컨텍스트 한계 문제를 해결하기 위해 요약기 대신 '활성 여부(liveness)' 기반의 필터링 전략을 제안합니다. 이 방법은 도구 결과물 중 현재 추론 체인에서 실제로 참조되는 정보만을 보존하여, 중요한 정보를 누락시키지 않고 효율적으로 컨텍스트를 관리할 수 있게 합니다.
핵심 포인트
- 요약기는 중요도를 예측하므로 오류가 크고 비결정적입니다.
- 도구 결과물의 대부분(70~85%)이 실제 코딩 작업의 핵심 정보입니다.
- 컨텍스트 관리는 '중요성' 대신 '활성 여부(liveness)'로 판단해야 합니다.
- 활성 상태는 현재 추론 체인이 해당 결과를 참조하는지 기계적으로 확인합니다.
지난주 제 에이전트가 61번째 턴에서 컨텍스트 한계(context ceiling)에 부딪혔습니다. 프레임워크는 모든 프레임워크가 지금 하는 것처럼 작동했습니다. 대화를 요약하기 위해 모델을 호출하고, 11초를 기다린 다음, 제가 필요로 했던 정확한 에러 문자열을 조용히 누락시킨 단락을 돌려주었습니다. 작업은 실패했습니다. 모델이 바보 같아서가 아니라 — 압축 단계(compaction step)가 무엇이 중요한지 결정했고 잘못 결정했기 때문입니다.
저는 이후 요약기(summarizer)를 제 루프에서 제거하고 약 80줄의 회계 장부(bookkeeping)로 대체했습니다. 여기에 추론 과정과 규칙을 설명합니다.
토큰이 실제로 존재하는 위치
긴 에이전트 트레이스(agent trace)에서 토큰 분해를 가져오면 형태는 항상 같습니다. 어시스턴트의 추론 — 생각하고, 계획하고, 결정하는 텍스트 — 은 얇은 리본입니다. 도구 결과물(tool results)이 바다와 같습니다. 파일 읽기, grep 출력, 테스트 출력, JSON 블롭, 디렉터리 목록, 스택 트레이스 등이 있습니다. 제가 직접 추적한 경우, 실제 코딩 작업에서 도구 결과물이 창의 70~85%를 차지합니다. 반면 추론 체인은 보통 10% 미만입니다.
따라서 기본 압축 전략은 가장 비싸고, 가장 손실이 크며, 가장 결정적이지 않은 작업을, 유지하기에 가장 저렴하고 보존하기에 가장 가치 있는 컨텍스트 부분에 사용합니다. 이는 역행하는 것입니다.
요약(summarization)이 잘못된 첫 번째 조치인 이유
저에게 비용이 많이 들었던 순서로 세 가지 문제가 있습니다.
느리고 차단적입니다. 작업을 진행하는 도중에 한계에 부딪히고, 이제 다음 단계를 위해 전체 모델 왕복(full model round-trip)을 삽입합니다. 로컬 모델의 경우 몇 초가 걸립니다. 호스팅된 모델의 경우 몇 초와 청구서가 추가되며, 임계값을 넘을 때마다 다시 지불해야 합니다.
감사할 수 없는 방식으로 손실이 큽니다. 요약기는 다음 턴에 무엇이 필요한지 모릅니다. 그것은 중요도(salience)에 따라 압축하며, 중요도는 추측일 뿐입니다. 에러 문자열, 정확한 파일 경로, 테스트 이름의 오프-바이-원(off-by-one) 같은 것들은 요약기에게는 노이즈처럼 보이지만 다음 도구 호출에는 모든 것입니다.
비결정적입니다. 같은 대화라도 실행할 때마다 요약이 다릅니다. 이는 프롬프트 캐싱을 망가뜨리고, 재현성을 잃게 하며, 버그 보고를 재생(replay) 불가능하게 만듭니다. 저는 '어제 뭔가 이상한 일이 있었다'는 문제로 오후 시간을 보냈는데, 두 번째 요약은 달랐습니다.
규칙: 중요도가 아닌 활성 여부 (liveness)
관점을 전환해야 합니다. 어떤 도구 결과가 '중요한지(important)'를 결정할 필요가 없습니다. 대신 어떤 결과가 '여전히 참조되고 있는지(referenced)'만 결정하면 됩니다. 이것은 훨씬 쉬운 질문이며, 기계적인 답변이 가능합니다.
도구 결과는 활성 상태입니다 (live) 만약 현재의 추론 체인(reasoning chain)이 그것을 여전히 가리키고 있다면. 구체적으로 말하자면, 가장 최신 메시지부터 거꾸로 돌아가 다음 조건 중 하나를 만족할 때 도구 결과를 '활성'으로 표시합니다:
- 그것의 도구 호출 ID(tool-call ID)가 현재 창에 남아있는 어시스턴트 메시지에 나타나거나,
- 더 나중의 어시스턴트 메시지가 그 내용을 인용하거나 의역하는 경우,
- 그것이 마지막 K개의 결과 중 하나인 경우 (저는 K=3을 사용합니다),
- 명시적으로 고정(pinned)된 경우.
나머지 모든 것은 오래된 것입니다(stale). 중요하지 않다는 것이 아니라, '오래되었다'는 것입니다. 이 구분이 중요한데, 왜냐하면 '중요하지 않다'는 판단을 요구하지만 '참조되지 않았다'는 것은 그렇지 않기 때문입니다.
추론 체인 — 모든 어시스턴트 메시지, 모든 도구 호출(call) (결과가 아닌 요청), 모든 사용자 턴은 손대지 않고 유지됩니다. 이것은 작습니다. 그것이 바로 작업의 실제 상태입니다. 이것을 삭제하는 것이 에이전트들이 자신이 무엇을 하고 있었는지 잊게 만드는 방식입니다.
프루너(pruner)의 작동 방식
대략적으로는 다음과 같습니다:
- 메시지 목록을 가져옵니다.
- 거꾸로 돌아가 활성 도구 호출 ID들의 집합을 만듭니다.
- 이 집합에 없고 마지막 K개에도 없는 모든 도구 결과의 내용을 '묘비(tombstone)'로 대체합니다.
- 토큰 수를 재계산합니다. 여전히 예산을 초과한다면, 더 강력하게 프루닝합니다 — K를 줄이고, 가장 오래된 완전한 도구 호출/결과 쌍을 제거합니다.
- 그 후에도 여전히 예산을 초과하는 경우에만 요약기(summarizer)를 사용합니다.
3단계가 핵심 트릭이며 비용이 저렴합니다. 모델 호출도 없고, 지연 시간(latency)도 없고, 변동성(variance)도 없습니다. 매번 동일한 입력에는 동일한 출력이 나옵니다.
삭제 대신 묘비(Tombstones)
도구 결과 메시지는 삭제하지 마세요. 대신 그 본문을 [pruned: read_file src/parser.py, 4.2k tokens]와 같은 것으로 대체하세요.
두 가지 이유가 있습니다. 첫째, 쌍 전체를 삭제하면 모델은 해당 호출을 이미 실행했다는 기록이 없어 기꺼이 다시 실행할 것입니다. 즉, 컨텍스트 문제를 루프(loop)로 만들게 됩니다. 둘째, 이 묘비명(tombstone)은 저렴한 인덱스 역할을 합니다. 에이전트가 그 파일을 다시 필요로 할 때, 자신이 읽었다는 것을 알기 때문에 재읽기가 재발견(rediscovery)을 하는 것보다 단 하나의 도구 호출만 필요합니다.
어느 쪽이든 도구 호출(call) 메시지는 그대로 유지하세요. 그것은 작고 추론 과정이기 때문입니다.
무엇을 고정해야 할까요 (What to pin)
규칙만으로 절대 가지치기해서는 안 되는 몇 가지가 있습니다:
- 에이전트가 유지하는 경우, 현재 계획 또는 할 일 목록(todo list). 이것이 척추와 같습니다.
- 가장 최근의 오류나 테스트 실패. 이는 보통 다음 세 번의 차례를 결정하는 이유가 됩니다.
- 에이전트가 명시적으로 메모로 기록한 모든 것. 기록하는 수고를 했다면 존중해야 합니다.
- 원래의 작업 진술(task statement). 당연히 그렇습니다.
저는 나중에 감지하려고 하기보다, 글을 쓸 때 플래그를 표시합니다. 탐지는 또 다른 판단적 호출이며, 제가 제거하려고 노력하는 것이 바로 이러한 판단적 호출들입니다.
요약이 여전히 적절한 경우 (When summarization is still right)
제가 요약기(summarizer)를 삭제하라고 말하는 것은 아닙니다. 그것이 가장 먼저 손을 대야 할 것일 수도 없고, 유일한 것이어서도 안 된다는 뜻입니다.
요약은 _추론 과정 자체(reasoning chain itself)_가 본체인 경우에 가치를 발휘합니다. 즉, 에이전트가 페이지 분량의 숙고를 거치거나 여러분이 진정으로 핵심 내용(gist)을 필요로 하는 긴 연구 또는 계획 세션입니다. 또한, 아무것도 실시간으로 진행되지 않는 대화의 가장 오래된 3분의 1에 적용하는 것도 괜찮습니다.
가지치기를 먼저 하면 요약도 더 좋아집니다. 이미 가지치기된 컨텍스트를 요약하면, 요약기는 지난 40턴 전 디렉터리 목록을 처리하는 대신 추론 과정에 예산을 사용할 수 있습니다.
이점 (The payoff)
가장 명확한 이점은 속도와 비용입니다. 덜 명확하지만 중요한 이점은 제 에이전트의 컨텍스트가 이제 히스토리의 결정론적 함수라는 것입니다. 같은 트레이스를 입력하면, 같은 컨텍스트가 출력됩니다. 저는 이를 기록하고, 실행 간에 차이를 비교할 수 있으며, 실패를 정확하게 재현(replay)할 수 있습니다. 프롬프트 캐싱이 다시 작동하는 이유는 접두사(prefix)가 안정적이기 때문입니다.
그리고 에이전트의 실패 사례들이 지루해졌고, 이것이 제가 원했던 바입니다. 에이전트는 편집 도중에 자신이 수정하던 파일을 잊지 않습니다. 다만 12번째 턴에서 나온 ls 출력물 같은 것은, 다시는 보지 않을 것이기 때문에 잊어버립니다.
도구 결과(Tool results)는 토큰의 대부분을 차지하지만 의미는 가장 적습니다. 규칙에 따라 이를 가지치기(Prune)하십시오. 생각하는 과정(thinking)만 남겨두십시오.
AI 자동 생성 콘텐츠
본 콘텐츠는 Dev.to AI tag의 원문을 AI가 자동으로 요약·번역·분석한 것입니다. 원 저작권은 원저작자에게 있으며, 정확한 내용은 반드시 원문을 확인해 주세요.
원문 바로가기