LLM386: 1990년대 아이디어를 차용해 LLM 컨텍스트 관리

발행: (2026년 5월 5일 AM 08:46 GMT+9)
8 분 소요
원문: Dev.to

Source: Dev.to

소개

1989년, DOS는 일반 메모리에서 640 KB라는 한계가 있었습니다. EMM386은 80386 CPU의 주소 변환 하드웨어를 이용해 그 640 KB 안의 작은 고정 창을 통해 훨씬 큰 메모리 공간의 조각들을 페이지화했습니다. 정중히 요청하는 프로그램은 현재 작업에 필요한 부분만 페이지화함으로써 사실상 무제한 메모리를 작은 구멍을 통해 얻을 수 있었습니다.

LLM도 같은 문제를 가지고 있습니다.

컨텍스트 창은 제한됩니다—32 K, 128 K, 1 M 토큰 등. 여러분의 데이터(대화 기록, 검색된 문서, 도구 결과, 지속적인 사실)는 여러분이 비용을 지불하려는 어느 창보다도 커질 것입니다. 각 호출마다 무엇을 통과시킬지 선택해야 합니다.

일반적인 접근 방식은 즉흥적입니다: 메시지를 리스트에 보관하고, “마지막 N개와 벡터 히트”를 검색해 연결하고 전송합니다. 프롬프트가 충분히 커져서 포함된 내용을 추적할 수 없게 되면 이 방법은 무너집니다. 모델은 답을 내놓지만 이유를 설명할 수 없으며, 두 번의 턴이 기록되지 않은 이유 때문에 서로 다른 응답을 생성합니다.

LLM386EMM386이 수행하던 런타임을 LLM 컨텍스트 창에 적용한 것입니다.

논문

f(context) → output

모델은 순수 함수이다: 메모리도 없고, 지속성도 없으며, 호출 간 상태도 없다. 모든 연속성은 각 호출마다 재구성되어야 한다.

  • 영구적인 상태는 런타임이 소유한 스토어에 존재한다. 모델은 무상태 소비자이다.
  • 각 호출에 대한 프롬프트는 그 스토어에서 다시 계산되며, 모델의 입력 예산이 제약 조건이 된다.

런타임에 포함된 내용

  • Persistent block store – LMDB, 콘텐츠 주소 지정, 해시 기반 중복 제거.
  • Pager – 구성된 검색기를 병렬로 실행하여(최근성, BM25, 임베딩 ANN, 사용자 정의) 모델 입력 예산에 맞는 블록을 선택하고, 점수를 정규화하며, 블록당 최대값으로 병합하고, 다음과 같은 표준 섹션에 할당합니다: System, Task, State, Plan, Retrieved, Tools, Recent, Background.
  • Packer – 선택된 내용을 결정론적 프롬프트 문자열 또는 역할 태그가 지정된 채팅 메시지 리스트로 렌더링합니다.
  • Tracer – 모델이 본 내용과 그 이유를 기록하며, 재생을 위해 바이트 수준 프롬프트 해시를 포함합니다.
  • Reducer – 파싱된 이벤트를 통해 모델 출력을 커밋된 상태로 되돌립니다.
  • Typed‑edge graph – 종속 블록을 연결하여 pager가 도구 결과를 해당 도구를 호출한 어시스턴트 메시지와 짝지어 유지하도록 합니다.
  • Diff layer – 두 트레이스 레코드를 차례로 비교합니다.

Rust 라이브러리, Python SDK(PyO3 네이티브 확장) 및 CLI로 구현되었습니다. Apache‑2.0 라이선스. 현재 버전: 1.0.0‑alpha.

의도적으로 포함되지 않은 내용

  • 챗봇 UI 없음.
  • 프롬프트 내부에 숨겨진 상태 없음.
  • 모델 출력값을 진실로 취급하지 않음.
  • 초기 버전에서는 분산 저장소 없음.
  • 핫 경로에 학습된 구성 요소가 없음 – 모든 검색기, 패커, 리듀서는 결정적이며, 이는 트레이스 재생이 가능하게 함. 학습된 재정렬기나 훈련된 임베딩 조정기는 해당 속성을 깨뜨리므로 의도적으로 제외함.

Try it

git clone https://github.com/fitzee/llm386
cd llm386
export ANTHROPIC_API_KEY=sk-ant-...
docker compose -f examples/langgraph-agent/docker-compose.yml run --rm agent

클론하고 채팅하기까지 5분. 두 개의 스텁 도구(계산기와 가짜 사용자 프로필 조회)를 가진 작은 챗봇이 LLM386을 메모리 레이어로 사용해 실행됩니다. 스토어가 Docker 볼륨이기 때문에 컨테이너 재시작 시에도 대화가 지속됩니다. 모델은 이전 턴의 내용을 기억하는데, 이 기억은 전적으로 런타임에 의해 제공됩니다. LangGraph는 턴 사이에 상태를 유지하지 않기 때문입니다.

사용해야 할까요?

  • – 개발 단계에서 작동하는 에이전트는 있지만 프롬프트가 엉망이고 모델이 무엇을 보고 있는지 추론할 수 없을 때.
  • 아마도 안 될 것 – 간단한 챗봇 데모만 필요하다면; 실행 가능한 가장 간단한 것을 사용하세요.
  • – 프롬프트 조립을 다시 작성하지 않고 모델을 교체하고 싶을 때. ModelProfile 추상화는 컨텍스트 윈도우, 토크나이저, 그리고 기능 플래그를 포함합니다; 페이지러와 패커는 어떤 모델을 교체하든 이 계약을 준수합니다.

에이전트가 복잡해짐에 따라 “현재 프롬프트에 실제로 무엇이 들어 있는가?”는 많은 스택에서 어려운 질문이 됩니다. 런타임은 이를 저렴하고 투명하게 유지하도록 설계되었습니다.

EMM386이 작동한 이유는 큰 메모리의 제한된 윈도우가 구조적으로 제한된 시스템에 적합한 추상화였기 때문입니다. 같은 추상화가 30년 후 LLM 컨텍스트 윈도우에도 적용됩니다.

GitHub: https://github.com/fitzee/llm386

0 조회
Back to Blog

관련 글

더 보기 »