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