모든 프롬프트에 97개 스킬을 전부 로드하는 건 토큰 낭비다
제가 운영하는 AI 에이전트(hermes-agent)에는 97개의 스킬이 설치되어 있습니다. 메시지를 하나 보낼 때마다 – "실행 중인 컨테이너 목록을 보여줘" 같은 아주 간단한 요청도 – 에이전트의 시스템 프롬프트에는 97개 스킬의 이름과 설명이 전부 포함됩니다. 정적 페이로드로 약 1,700토큰이 매 추론 호출마다 복사되는 것이죠. 그 작업이 블로그 발행인지 포켓몬 게임인지는 전혀 상관없이요.
이건 10개 스킬일 때는 괜찮아 보이는 아키텍처 결정이, 라이브러리가 커지면서 점점 더 비합리해지는 전형적인 사례입니다. 왜 문제인지, 실제 비용이 얼마인지, 더 나은 접근법은 무엇인지 설명해 보겠습니다. 이건 제 에이전트만의 문제가 아닙니다. 지금 제가 다뤄본 거의 모든 에이전트 프레임워크에서 반복되는 패턴입니다.
현재의 동작 방식
제가 사용하는 것을 포함한 대부분의 에이전트 시스템에서 현재 설계는 단순합니다:
- 세션 시작 시 스킬 디렉토리를 스캔하여 설치된 모듈을 확인합니다
- 각 모듈의 메타데이터(이름, 설명, 카테고리)를 파싱합니다
- 전체 인덱스를
<available_skills>블록으로 시스템 프롬프트에 주입합니다 - 스킬의 전체 내용은 별도의 툴 호출로 필요할 때 로드됩니다
4번은 괜찮습니다 -- 실제 콘텐츠를 레이지 로딩하는 것은 합리적입니다. 문제는 3번입니다. 그 인덱스가 모든 턴에 무조건 주입됩니다. 관련성에 따른 필터링도 없고, 사용 빈도에 따른 우선순위 지정도 없고, 모델이 "지금은 이게 필요 없어"라고 말할 메커니즘도 없습니다.
토큰 계산
제 환경의 실제 숫자로 구체화해 보겠습니다:
- 턴당 스킬 인덱스: ~1,700토큰
- 일반적인 세션 길이: 60턴
- 세션당 총 인덱스 토큰: ~102,000
- 컨텍스트 윈도우: 128K토큰
- 스킬 인덱스가 소비하는 컨텍스트: 실제 대화가 시작되기도 전에 최대 80%
마지막 항목이 핵심입니다. 128K 컨텍스트 윈도우 중 102K토큰은, 30턴이 지나면 시스템이 이전 대화를 압축하거나 폐기해야 합니다. 그 이유는 세션 시작 이후 변경되지 않은 동일한 97개 스킬 설명을 위한 공간을 만들어야 하기 때문입니다.
그리고 이건 97개 스킬일 때의 숫자입니다. 에이전트 라이브러리는 자연스럽게 성장합니다 -- 워크플로우를 자동화하고, 스킬로 저장하고, 또 하나 추가하고, 또 추가합니다. 200개 스킬이 되면 인덱스만으로 대부분의 모델이 한 세션에서 처리하는 토큰보다 더 많은 토큰을 소비하게 됩니다.
MCP와의 비교
이 문제를 명확히 인식하게 된 계기는 MCP(Model Context Protocol) 서버가 동일한 문제를 어떻게 처리하는지 연구하면서였습니다. MCP는 사용 가능한 모든 툴을 프롬프트에 미리 덤프하지 않습니다. 대신 검색-후-주입(search-and-inject) 패턴을 사용합니다:
- 에이전트가 필요한 것을 설명하는 쿼리를 보냅니다
- MCP 서버가 인덱스를 검색합니다
- 관련성 있는 결과만 현재 컨텍스트에 주입됩니다
이건 "모든 것을 주입하고 모델이 필요 없는 것을 무시하길 바라는" 접근법과 근본적으로 다릅니다. 모델은 가진 정보를 활용하는 데 능숙하지만, 필요 없는 정보를 무시하는 것에는 그다지 뛰어나지 않습니다. 관련성 없는 각 스킬 설명은 작지만 실제로 존재하는 방해 요소입니다 -- 어텐션 메커니즘이 처리하고, 가중치를 매기고, 버려야 하는 것들이죠.
이것을 97개 항목 x 60턴으로 곱하면, 토큰 비용 문제를 넘어 신호 대 잡음비(signal-to-noise) 문제가 됩니다. 잡음에 비용을 지불하고 있고, 모델이 그 잡음에 어텐션을 소비하는 것입니다.
더 나은 접근법
급진적인 것을 제안하는 게 아닙니다. 다른 곳에서 이미 표준이 된 동일한 검색 증강(Retrieval-Augmented) 패턴을 적용하는 것뿐입니다:
1. 스킬 검색 툴
시스템 프롬프트에 전체 스킬 인덱스를 넣는 대신, 모델에 skill_search(query: str) 툴을 제공합니다. 모델이 "ghost cms blog publishing" 같은 쿼리로 호출하면 가장 관련성 높은 2-3개 스킬과 설명을 받습니다. 웹 검색, RAG 검색, MCP 툴 디스커버리와 동일한 패턴입니다.
2. 소규모 항상 로드되는 코어
모든 스킬이 디스커버리를 필요로 하지는 않습니다. 소수의 스킬 -- 에이전트 자체 설정, 블로그 발행 워크플로우, 노트 필기 통합 등 -- 은 거의 모든 세션에서 사용됩니다. 이건 무조건 시스템 프롬프트에 있어야 합니다. 검색이 필요 없죠. 저는 이를 5-8개 스킬로 제한하겠습니다.
3. 나머지는 레이지 인덱스
나머지 스킬은 검색 가능한 인덱스에 있습니다 -- 키워드 매칭, 이름과 설명에 대한 임베딩 검색, 또는 더 정교한 것일 수 있습니다. 모델이 실제로 필요할 때만 가져옵니다.
트레이드오프
비용에 대해 솔직하게 말씀드리고 싶습니다. 시스템 설계에서 공짜는 없으니까요:
- 지연시간: 검색 툴 호출은 모델이 스킬을 사용하기 전에 왕복 1회를 추가합니다. 대략 200-500ms. 세션당 102K 토큰 낭비와 비교하면 저는 항상 이 트레이드를 선택할 것입니다.
- 누락된 스킬:
- 전체 목록에서 봤다면 인식했을 스킬을 모델이 검색하지 않을 수 있습니다. 이건 실제 리스크입니다. 코어 스킬을 항상 보이게 유지하고, 검색 재현율(recall)을 근접 매치까지 포괄할 만큼 넓게 만들어 완화할 수 있습니다.
- 구현 복잡도: 에이전트 코어의 변경이 필요합니다 -- 새로운 툴 정의, 검색 백엔드, "코어" 스킬을 지정하는 설정 레이어입니다. 사소하지 않지만, 전체 재작성도 아닙니다.
더 큰 패턴
이 문제에서 저를 놀라게 한 것은 얼마나 보편적인지입니다. 에이전트 프레임워크, 툴 레지스트리, 프롬프트 템플릿, 컨텍스트 주입 시스템 전반에 걸쳐 동일한 패턴이 반복됩니다: 정적 페이로드를 단조롭게 성장시키고, 절대 정리하지 않는다.
시스템 프롬프트는 새로운 autoload 디렉토리가 되었습니다. 페르소나 지시문, 툴 설명, 사용 예시, 사용자 선호도, 이전 세션의 컨텍스트 -- 모든 것이 들어가고 아무것도 나오지 않습니다. 모델이 추론 엔진 동시에 파일 캐비닛으로 취급되고 있는 것이죠.
하지만 모델은 파일 캐비닛이 아닙니다. 큰 컨텍스트 윈도우를 가진 추론 엔진입니다. 이 차이는 중요합니다. 컨텍스트는 무료가 아닙니다 -- 비용이 들고, 어텐션을 소비하며, 한계가 있습니다.
MCP 생태계는 이미 툴 디스커버리에서 이를 알아냈습니다. RAG는 지식 검색에서 이를 알아냈습니다. 다음 단계는 프롬프트에 들어가는 모든 것에 동일한 원칙을 적용하는 것입니다: 스킬, 지시문, 선호도, 예시 -- 전부 도구 전체를 로드하는 것이 아니라 검색 후 주입이 가능해야 합니다.
레이지 컨텍스트 주입을 기본값으로 만드는 최초의 에이전트 프레임워크 -- 설정에 숨겨진 파워 유저 옵션이 아니라 -- 은 비용과 품질 모두에서 의미 있는 우위를 가지게 될 것입니다.
그래서 지금 뭘 하고 있나요
솔직하게 말씀드리겠습니다: 에이전트에게 이 문제를 분석해 보라고 요청했고, 구체적인 해결책을 제안받았습니다. 분석 결과 세 가지 실행 가능한 접근법이 도출되었습니다:
- 간결한 주입(Terse injection): 설명을 제거하고 스킬 이름만 주입합니다(~500토큰, 기존 ~1,700토큰 대비). 구현이 저렴하지만, 모델이 설명 없이 관련성을 판단할 수 없게 됩니다.
- 디렉토리 분할: 자주 사용하지 않는 스킬을 필요할 때만 검색되는 보조 디렉토리로 이동합니다. 기존 툴링으로 구현 가능하고, 프레임워크 코어 변경이 불필요합니다.
- 전체 검색 툴: 제가 위에서 설명한 접근법입니다 -- 전용 검색 툴, 소규모 항상 로드 코어, 나머지는 레이지 검색. 가장 많은 토큰 절감, 가장 많은 구현 작업이 필요합니다.
아직 어떤 것도 구현하지 않았습니다. 분석은 끝났고, 선택지는 명확합니다. 이제 우선순위의 문제입니다. 하지만 수학은 명확합니다 -- 이건 스킬을 하나 추가할 때마다 악화되는 확장성 문제입니다.
에이전트 프레임워크를 구축하거나 사용 중이시라면, 세션 내에서 절대 변경되지 않는 정적 인덱스가 컨텍스트 윈도우의 얼마를 소비하는지 확인해 보시길 권합니다. 그 숫자가 놀라울 수 있습니다.