콘텐츠로 이동

Lost in the Middle: U자형 주의 곡선

컨텍스트 창이 크면 모든 정보를 똑같이 잘 본다는 착각

섹션 제목: “컨텍스트 창이 크면 모든 정보를 똑같이 잘 본다는 착각”

앞 챕터에서 컨텍스트가 길어질수록 맥락 부패(context rot)가 일어난다는 것을 개념적으로 살펴봤다. 이번 챕터는 그 문제의 한 측면을 실증 연구로 뒷받침한다. **Liu et al.(2024)**이 TACL에 발표한 “Lost in the Middle” 논문(arXiv:2307.03172)은 LLM이 긴 컨텍스트에서 관련 정보를 활용하는 방식을 체계적으로 분석했다.

연구팀은 여러 문서 가운데 하나에 정답이 숨어 있는 QA 태스크를 설계했다. 핵심 변수는 정답 문서의 위치 — 20개 문서 중 몇 번째에 배치하느냐. 결과는 명확했다. 정답이 첫 번째나 마지막 문서에 위치할 때는 정확도가 높았고, 중간(5~15위)에 위치할 때는 정확도가 크게 하락했다. 이 패턴을 연구자들은 U자형 곡선이라고 불렀다.

┌──────────────────────────────────────────────────────────┐
│ Lost in the Middle: U자형 주의 곡선 │
│ │
│ 정확도 │
│ ▲ │
│ │ ██ ████ │
│ │ ████ ████████ │
│ │ ██████ ████████████████ │
│ │ █████████ ████████████████████████████ │
│ │ ███████████████████████████████████████████████████ │
│ └──────────────────────────────────────────────────▶ │
│ 앞 (1위) 중간 (10위) 뒤 (20위) 문서 위치 │
│ │
│ → 중간 위치에서 정확도 가장 낮음 (U자형) │
└──────────────────────────────────────────────────────────┘

LLM 어텐션 메커니즘의 특성상, 모델은 문장 시작 부근(시스템 프롬프트, 초기 지시)과 가장 최근 토큰(현재 생성 직전)에 강하게 집중한다. 이 두 극단 사이에 위치한 중간 토큰들은 상대적으로 어텐션 가중치가 낮아지는 경향이 있다. 논문은 이를 “맥락 창의 중간부(middle of the context window)가 효과적으로 활용되지 않는다”고 표현했다.

이 현상은 단순히 “모델이 중간을 잊는다”는 식의 단순한 메모리 문제가 아니다. 어텐션 가중치 분포 자체가 위치에 따라 불균등하게 형성되는, 구조적인 특성이다.

루프에서 이 문제가 구체적으로 나타나는 방식

섹션 제목: “루프에서 이 문제가 구체적으로 나타나는 방식”

에이전틱 루프에서 이 U자형 취약성은 다음과 같은 형태로 발현된다.

시나리오 1: 파일 읽기 결과가 묻히는 경우. 루프 초반에 읽어 온 코드 파일이나 설정 파일의 내용이 이후 여러 도구 결과 아래로 밀려 컨텍스트 창 중간 어딘가에 위치하게 된다. 나중에 모델이 그 파일 내용을 참조해야 할 때, 실제로 잘 참조하지 못하고 엉뚱한 답을 낼 수 있다.

시나리오 2: 핵심 제약 사항이 희석되는 경우. “절대로 프로덕션 DB에 쓰지 마라” 같은 중요한 제약이 시스템 프롬프트 중간부에 위치하거나, 초기 대화 턴에서만 언급되고 이후 밀려나면 모델이 그 제약을 간과할 위험이 높아진다.

시나리오 3: 멀티에이전트 요약 보고서 손실. 오케스트레이터가 여러 서브에이전트의 보고를 순서대로 받을 때, 중간에 도착한 보고서의 정보가 최종 종합 단계에서 충분히 반영되지 않을 수 있다.

# 개념 이해용 의사 코드이며 실제 API와 다를 수 있습니다.
def read_file_with_position_awareness(messages: list, filepath: str) -> list:
"""
파일을 읽어 컨텍스트에 추가할 때 U자형 취약성을 고려한 배치.
오래된 파일 결과는 요약으로 교체하고, 현재 필요한 파일만 끝에 위치.
"""
file_content = read_tool(filepath)
# 이전에 같은 파일을 읽은 결과가 있으면 요약으로 교체 (중간 제거)
messages = replace_old_file_reads(messages, filepath, summary_only=True)
# 최신 파일 내용은 컨텍스트 끝(높은 어텐션 구간)에 추가
messages.append({
"role": "tool",
"content": f"[현재 {filepath} 내용]\n{file_content}"
})
return messages
def replace_old_file_reads(messages: list, filepath: str, summary_only: bool) -> list:
"""중간에 묻힌 동일 파일 읽기 결과를 요약 한 줄로 교체."""
result = []
for msg in messages:
if is_file_read_result(msg, filepath):
if summary_only:
result.append({"role": "tool", "content": f"[{filepath} 이전 읽기 — 요약으로 대체됨]"})
else:
result.append(msg)
return result

U자형 곡선은 제거하기 어렵다. 하지만 루프 설계로 그 영향을 줄일 수 있다.

전략 설명 효과
최신 재로드 참조 필요한 파일은 다시 읽어 컨텍스트 끝에 배치 중간 블라인드 스팟 우회
핵심 사실 앞배치 제약 조건, 불변 목표는 시스템 프롬프트에 고정 높은 어텐션 구간 활용
중간 내용 압축 오래된 도구 결과를 요약·삭제해 중간 구간 크기 감소 취약 구간 자체를 축소
서브에이전트 격리 멀티에이전트에서 각 에이전트가 별도 컨텍스트 보유 중간 축적 자체를 방지

중간 블라인드 스팟은 루프가 길어질수록 실질적인 위험이 된다. 모델이 “컨텍스트 창에 있으니 알고 있겠지”라고 기대했던 정보가 실제로는 제대로 참조되지 않았을 수 있다. 특히 읽기-수정-확인 패턴을 반복하는 코딩 루프에서 초반에 읽어 온 코드베이스 구조 정보가 중간에 잠기면, 모델은 나중에 구조를 다시 파악하는 데 추가 도구 호출을 낭비하거나 불필요한 가정을 하게 된다.

이 장이 뒤 챕터들과 연결되는 방식

섹션 제목: “이 장이 뒤 챕터들과 연결되는 방식”

U자형 취약성은 단독으로 해결하기보다 컨텍스트 관리 전략 전체의 맥락에서 다뤄야 한다. 5-3(compaction)에서는 중간 구간을 주기적으로 정리하는 방법을, 5-5(JIT 검색)에서는 중간에 묻히기 전에 필요한 시점에 정확히 꺼내 쓰는 전략을 다룬다. 이 두 전략이 U자형 곡선 문제를 가장 직접적으로 완화한다.

Lost in the Middle 논문은 컨텍스트 창 크기 경쟁이 단지 숫자 게임이 아님을 보여준다. 창을 키우는 것만큼, 그 창 안의 정보 배치를 어떻게 설계하는가가 에이전트 성능을 결정하는 핵심 변수다.

참고 자료