← 목록으로
AI2026.10.13 02:16

LLM Context Engineering — 에이전트 성능을 결정하는 컨텍스트 윈도우 설계

모델을 바꾸기 전에 컨텍스트부터 점검하세요. Lost in the Middle 현상, 임계치 기반 압축, 메모리 계층, 서브에이전트 요약으로 에이전트의 컨텍스트 윈도우를 설계하는 법입니다.

목차

  1. 개요
  2. 컨텍스트 윈도우의 구조와 동작 원리
  3. 효과적인 컨텍스트 설계 전략
  4. 에이전트 루프와 컨텍스트 상태 관리
  5. 컨텍스트 압축과 메모리 계층 설계
  6. 운영 환경에서의 컨텍스트 엔지니어링
  7. 맺음말

개요

문제 배경

LLM 에이전트 시스템에서 성능 저하의 원인을 추적하다 보면, 모델 선택보다 컨텍스트 설계가 더 결정적인 변수로 작용하는 경우가 많습니다. Context Engineering은 LLM이 올바른 작업을 수행하도록 컨텍스트 윈도우 안에 담길 정보를 의도적으로 설계하는 일련의 실천을 말합니다. 단순히 프롬프트를 잘 작성하는 것을 넘어, 어떤 정보를 언제 어떤 형태로 모델에게 전달하느냐를 체계적으로 결정하는 공학 영역입니다. 에이전트가 긴 작업을 수행하거나 여러 도구를 호출할수록 컨텍스트의 품질이 최종 결과물의 품질을 직접적으로 좌우합니다.

기존 방식의 한계

초기 LLM 적용 단계에서 많은 팀은 Prompt Engineering — 입력 텍스트를 정밀하게 다듬는 작업 — 에 집중했습니다. 그러나 에이전트가 도구를 호출하고, 여러 단계에 걸쳐 추론하며, 외부 시스템과 상호작용하는 복잡한 워크플로우가 보편화되면서 단일 프롬프트 최적화만으로는 한계가 드러났습니다. 도구 호출 결과가 누적될수록 컨텍스트가 팽창하고, 모델은 관련 없는 정보에 주의를 분산시키거나 중요한 지시 사항을 "잊는" 현상이 발생합니다. 이를 컨텍스트 오염(Context Pollution) 이라 부르며, 에이전트 오류의 가장 흔한 원인 중 하나입니다.

LLM Context Engineering — 에이전트 성능을 결정하는 흐름 프롬프트 엔지니어링에서 시작해 복잡한 워크플로우로 확장되면, 컨텍스트 오염과 성능 저하가 발생하고, 이를 해결하기 위해 컨텍스트 엔지니어링이라는 설계 체계가 필요함을 보여주는 흐름도. 에이전트로 확장 BEFORE 프롬프트 엔지니어링 단일 입력 최적화 AGENT 복잡한 워크플로우 도구 · 다단계 추론 ISSUE 컨텍스트 오염 성능 저하 SOLUTION 컨텍스트 엔지니어링 설계 체계 필요 LEGEND 이전 접근 워크플로우 확장 문제 발생 핵심 해결책

프롬프트 최적화만으로는 에이전트 규모의 복잡성을 감당할 수 없으며, 컨텍스트 전체를 설계 대상으로 삼아야 합니다.

Context Engineering은 크게 세 가지 질문에 답합니다. 무엇을 넣을 것인가(What), 어떻게 구조화할 것인가(How), 그리고 **언제 갱신할 것인가(When)**입니다. 이 세 가지를 명시적으로 설계하지 않으면 에이전트는 예측 불가능한 방식으로 실패합니다. 현업 엔지니어들 사이에서 "모델이 나쁜 것이 아니라 컨텍스트가 나쁜 것"이라는 말이 공감을 얻는 이유가 바로 여기에 있습니다.


컨텍스트 윈도우의 구조와 동작 원리

컨텍스트 윈도우의 해부학

LLM이 한 번의 추론에서 처리할 수 있는 텍스트 범위를 **컨텍스트 윈도우(Context Window)**라고 합니다. GPT-4o는 128K 토큰, Claude Sonnet 4.5는 200K 토큰, Gemini 1.5 Pro는 1M 토큰까지 지원합니다. 그러나 윈도우가 크다고 해서 문제가 사라지는 것은 아닙니다. 컨텍스트 안의 정보는 균등하게 처리되지 않으며, 위치에 따라 모델이 주의를 기울이는 정도가 달라집니다.

실제 에이전트 시스템에서 컨텍스트 윈도우는 논리적으로 여러 영역으로 나뉩니다. 시스템 프롬프트(System Prompt) 는 에이전트의 역할, 규칙, 가용한 도구 목록을 담습니다. 작업 컨텍스트(Task Context) 는 현재 수행 중인 작업의 목표와 제약 조건입니다. 대화 히스토리(Conversation History) 는 이전 메시지와 도구 호출 결과의 누적입니다. 검색된 정보(Retrieved Information) 는 RAG나 도구 호출로 가져온 외부 지식입니다. 이 네 영역을 어떻게 배치하고 균형을 맞추느냐가 컨텍스트 설계의 핵심입니다.

영역 내용 특성 주의점
시스템 프롬프트 역할 정의, 도구 명세 고정적, 캐시 가능 너무 길면 작업 지시 희석
작업 컨텍스트 현재 목표, 제약 작업마다 갱신 구조화 필수
대화 히스토리 이전 턴, 도구 결과 누적, 팽창 정기적 압축 필요
검색 정보 RAG, 외부 데이터 동적 삽입 중복·노이즈 제거

Lost in the Middle 현상

Stanford와 UC Berkeley 연구자들이 2023년 발표한 "Lost in the Middle" 연구는 LLM이 컨텍스트의 시작과 끝 부분의 정보는 잘 활용하지만 중간에 삽입된 정보는 상대적으로 간과하는 경향을 보임을 실험적으로 입증했습니다. 이 현상은 컨텍스트가 길어질수록 더욱 두드러집니다. 에이전트가 여러 단계를 수행하면서 컨텍스트 중간에 중요한 제약 조건이나 이전 결과물이 묻히게 되면, 모델은 그것을 효과적으로 참조하지 못하고 잘못된 방향으로 작업을 이어갑니다.

컨텍스트 윈도우 위치 효과 LLM 컨텍스트 윈도우를 시작부·중간부·끝부분으로 나누고, 각 위치의 기억 강도와 권장 배치 전략을 보여주는 도식. 시작부와 끝부분은 잘 기억되고, 중간부는 주의가 분산된다. START 시작부 잘 기억됨 MID 중간부 주의 분산 · 간과 END 끝부분 잘 기억됨 STRATEGY 시스템 프롬프트 핵심 규칙 배치 STRATEGY 마지막 사용자 메시지 구체적 지시 배치 WARN 도구 결과 누적 시 위험 권장 배치 권장 배치 위험 LEGEND 잘 기억됨 (시작·끝) 주의 분산 (중간부) 배치 전략 누적 위험 구간

컨텍스트 윈도우의 중간 영역은 모델의 주의가 약해지는 사각지대이므로, 중요한 정보는 앞뒤 경계에 배치하는 것이 원칙입니다.

이 현상이 시사하는 실천적 교훈은 명확합니다. 가장 중요한 지시 사항은 시스템 프롬프트의 앞부분과 사용자 메시지의 끝부분에 배치해야 합니다. 도구 호출 결과 중 에이전트가 반드시 참조해야 하는 핵심 값(예: 이전 단계에서 생성된 ID, 검증된 데이터)은 중간에 묻히지 않도록 컨텍스트를 재구성하거나 명시적으로 강조하는 처리가 필요합니다.


토큰 예산과 비용 구조

컨텍스트 윈도우는 단순히 기술적인 한계가 아니라 비용 구조와도 직결됩니다. LLM API는 입력 토큰과 출력 토큰을 기준으로 요금을 부과하며, 에이전트가 여러 번의 LLM 호출을 수행할 때마다 그때까지 누적된 전체 컨텍스트가 입력 토큰으로 청구됩니다. 10단계를 수행하는 에이전트에서 컨텍스트가 단계마다 2000 토큰씩 늘어난다면, 마지막 단계에서는 첫 단계 대비 최대 열 배 이상의 입력 비용이 발생합니다.

Anthropic의 프롬프트 캐싱(Prompt Caching) 기능은 이 비용 문제를 완화하는 데 효과적입니다. 시스템 프롬프트처럼 변경되지 않는 컨텍스트 블록을 캐시로 지정하면, 반복 호출 시 해당 블록의 입력 비용을 최대 90%까지 절감할 수 있습니다. 따라서 컨텍스트 설계 시 변하지 않는 부분(시스템 프롬프트, 도구 스키마)과 변하는 부분(대화 히스토리, 검색 결과)을 명확히 분리하는 것이 비용 최적화의 출발점입니다.


효과적인 컨텍스트 설계 전략

Write What You Need, Not What You Know

Context Engineering의 핵심 원칙 중 하나는 모델에게 알려줄 수 있는 모든 것을 넣는 대신, 모델이 작업을 수행하기 위해 반드시 필요한 것만 선별하는 것입니다. 이를 흔히 "컨텍스트 위생(Context Hygiene)"이라고 부릅니다. 데이터베이스에서 가져온 전체 레코드를 컨텍스트에 붙여 넣는 대신, 해당 작업에 필요한 필드만 추출하여 제공하면 모델의 집중도가 높아지고 불필요한 토큰 소비가 줄어듭니다.

구체적으로는 세 가지 필터링 기준을 적용할 수 있습니다. 첫째, 관련성(Relevance) — 이 정보가 현재 작업에 직접적인 영향을 미치는가. 둘째, 신선도(Freshness) — 이 정보가 최신 상태를 반영하는가, 아니면 이미 갱신된 상태 때문에 오해를 유발할 수 있는가. 셋째, 중복성(Redundancy) — 동일한 정보가 여러 번 반복되어 컨텍스트를 낭비하고 있지는 않은가. 이 세 기준을 통과한 정보만 컨텍스트에 포함시키는 것이 원칙입니다.

컨텍스트 필터링 파이프라인 원시 데이터가 관련성·신선도·중복성 세 단계 체크를 순서대로 통과하면 컨텍스트에 삽입되고, 각 단계에서 탈락하면 제외 또는 갱신 처리되는 필터링 흐름을 보여주는 순서도. 관련 있음 무관함 최신 오래됨 고유함 중복 INPUT 원시 데이터 전체 레코드 관련성 체크 신선도 체크 중복성 체크 OUTPUT 컨텍스트 삽입 제외 갱신 또는 제외 제외 체크 단계 진행 제외 컨텍스트 삽입

세 단계 필터링을 거친 정보만 컨텍스트에 삽입하면, 동일한 토큰 예산으로 모델이 처리하는 신호 대비 노이즈 비율을 크게 개선할 수 있습니다.


구조화된 정보 표현

LLM은 자유 형식 텍스트보다 명확한 구조를 가진 정보를 더 안정적으로 처리하는 경향이 있습니다. XML 태그, JSON 스키마, Markdown 헤더 등 구조화 형식은 모델이 각 정보 블록의 역할과 경계를 명확히 파악하게 돕습니다. Claude 계열 모델은 특히 XML 스타일 태그로 구분된 컨텍스트에서 높은 추론 정확도를 보이며, 이는 학습 데이터의 구성 특성과 관련이 있습니다.

아래 예시는 에이전트가 코드 리뷰 작업을 수행할 때 컨텍스트를 구조화하는 방법을 보여줍니다. 단순히 코드와 지시 사항을 텍스트로 이어 붙이는 대신, 각 블록의 역할을 태그로 명시합니다.

xml
<task>
  코드 리뷰를 수행하고 버그와 개선 가능한 부분을 찾아주세요.
  우선순위: 버그 → 보안 → 성능 → 가독성
</task>

<context>
  <language>Python 3.11</language>
  <project_type>FastAPI REST API</project_type>
  <previous_review_summary>
    SQL 인젝션 취약점 2건 수정 완료. 입력 검증 로직 미완성.
  </previous_review_summary>
</context>

<code_to_review>
def get_user(user_id: str):
    query = f"SELECT * FROM users WHERE id = {user_id}"
    return db.execute(query)
    # 위험: 파라미터 바인딩 없음, SQL 인젝션 노출
</code_to_review>

이 구조에서 모델은 <task> 블록에서 무엇을 해야 하는지, <context> 블록에서 어떤 배경 지식이 있는지, <code_to_review> 블록에서 실제 처리할 대상을 명확히 구분할 수 있습니다. 비구조화 텍스트에 비해 모델이 관련 없는 컨텍스트에 주의를 낭비할 가능성이 줄어들고, 지시 사항을 더 일관되게 따르는 패턴이 나타납니다.


도구 명세 설계

에이전트가 사용하는 도구(Tool)의 명세 역시 컨텍스트의 일부입니다. 도구 이름, 설명, 파라미터 스키마가 컨텍스트 윈도우를 상당량 차지하며, 이를 어떻게 작성하느냐가 도구 선택 정확도에 직접적인 영향을 미칩니다. 도구 설명이 모호하면 모델이 잘못된 도구를 선택하거나, 필요 없는 도구를 불필요하게 호출하는 과호출(Over-calling) 문제가 발생합니다.

효과적인 도구 명세의 핵심은 무엇을 하는지보다 언제 써야 하는지를 설명하는 것입니다. "사용자 정보를 조회한다" 대신 "사용자 ID로 프로필을 조회할 때만 사용한다. 이메일로 조회하려면 search_user_by_email을 사용한다"처럼 경계 조건을 명시하면 모델의 도구 선택 정확도가 높아집니다.

도구 명세 요소 나쁜 예 좋은 예
이름 get_data get_order_by_id
설명 데이터를 가져옵니다 주문 ID로 단일 주문 조회. 목록은 list_orders 사용
파라미터 설명 order_id: string "ORD-"로 시작하는 주문 고유 식별자
반환값 설명 주문 객체 반환 없으면 null, 권한 없으면 PermissionError

에이전트 루프와 컨텍스트 상태 관리

에이전트 루프의 컨텍스트 생명주기

에이전트 루프(Agent Loop)는 목표가 달성되거나 실패 조건이 충족될 때까지 모델 호출 → 도구 실행 → 결과 수집 → 다음 모델 호출의 사이클을 반복하는 구조입니다. 이 루프가 반복될수록 컨텍스트 윈도우에는 이전 단계의 모든 메시지와 도구 결과가 누적됩니다. 초기에는 넉넉하던 컨텍스트 공간이 10~20 단계를 거치면 포화 상태에 이르며, 모델 호출 자체가 실패하거나 성능이 급격히 저하되는 문제가 발생합니다.

이를 관리하기 위해 컨텍스트 생명주기를 명시적으로 설계해야 합니다. 작업 개시 단계에서는 시스템 프롬프트와 초기 작업 컨텍스트를 설정합니다. 실행 단계에서는 각 도구 호출 결과를 컨텍스트에 추가하되, 이전 단계에서 더 이상 필요하지 않은 정보를 주기적으로 제거하거나 요약합니다. 완료 단계에서는 최종 결과를 외부 스토리지에 저장하고 컨텍스트를 초기화합니다.

에이전트 실행 루프 — 컨텍스트 윈도우 관리 작업 개시부터 결과 저장까지 LLM 에이전트의 실행 루프를 보여주는 순서도. 컨텍스트 임계치 초과 시 압축 분기가 발생한다. 아니오 예 아니오 예 작업 개시 시스템 프롬프트 설정 LOOP 실행 루프 모델 호출 TOOL 도구 실행 결과 수집 컨텍스트 임계치 초과? COMPRESS 컨텍스트 압축 불필요 정보 제거 작업 완료? 결과 저장 컨텍스트 초기화 주요 단계 압축 분기 시작 / 종료 정상 완료 판단 분기

컨텍스트 임계치를 설정하고 초과 시 자동으로 압축 또는 요약을 수행하는 루프 설계가 장기 실행 에이전트의 안정성을 보장합니다.


도구 호출 결과의 선택적 보존

에이전트 루프에서 가장 빠르게 컨텍스트를 팽창시키는 요인은 도구 호출 결과입니다. API 응답, 데이터베이스 쿼리 결과, 웹 검색 결과 등은 원시 형태로는 수천 토큰에 달할 수 있습니다. 이 전체를 컨텍스트에 유지하는 대신 에이전트가 이후 단계에서 실제로 참조할 핵심 값만 추출하여 보존하는 접근이 효율적입니다.

예를 들어, 사용자 정보를 조회하는 도구가 수십 개의 필드를 가진 JSON 객체를 반환한다면, 이후 단계에서 필요한 user_id, email, subscription_tier 세 필드만 별도의 "에이전트 메모리" 형태로 추출하고 원본 응답은 버리는 전략이 있습니다. 이때 중요한 것은 무엇을 버릴지를 에이전트 스스로 결정하게 하지 말고, 시스템 설계 시점에 명시적으로 정의해야 한다는 점입니다. 에이전트가 버릴 정보를 결정하는 과정 자체가 추가 LLM 호출과 비용을 발생시키기 때문입니다.

도구 결과는 전부 보존하다가 나중에 요약하는 것보다, 처음부터 필요한 필드만 추출하는 것이 토큰 효율과 정확도 모두에서 유리합니다.


멀티 에이전트 컨텍스트 경계

단일 에이전트 대신 여러 에이전트가 협력하는 멀티 에이전트 시스템에서는 컨텍스트 경계 설계가 더욱 복잡해집니다. 각 서브에이전트는 자신의 컨텍스트 윈도우를 독립적으로 운영하며, 오케스트레이터(Orchestrator)는 서브에이전트의 결과를 취합하여 자신의 컨텍스트에 통합합니다. 이때 서브에이전트의 전체 컨텍스트를 오케스트레이터에 전달하면 오케스트레이터의 컨텍스트가 기하급수적으로 커지는 문제가 발생합니다.

효과적인 패턴은 서브에이전트가 작업 완료 후 구조화된 요약(Structured Summary) 을 반환하도록 시스템 프롬프트에서 강제하는 것입니다. 예를 들어 서브에이전트의 최종 메시지를 XML 태그로 감싼 요약 형식으로 제한하면, 오케스트레이터는 이 요약만 컨텍스트에 통합하면 됩니다. 서브에이전트의 중간 추론 과정과 도구 호출 히스토리는 오케스트레이터에게 불필요한 노이즈입니다.

멀티에이전트 컨텍스트 분리 구조 오케스트레이터가 두 서브에이전트에 작업을 할당하고, 서브에이전트는 구조화 요약만 반환하여 컨텍스트 폭발을 방지하는 흐름을 보여 주는 도식. 작업 할당 작업 할당 구조화 요약만 구조화 요약만 전체 전달 금지 ORCH 오케스트레이터 컨텍스트 AGENT 서브에이전트 A 독립 컨텍스트 AGENT 서브에이전트 B 독립 컨텍스트 RISK 컨텍스트 폭발 방지 LEGEND 오케스트레이터 서브에이전트 방지 경계 전달 금지 데이터 흐름

서브에이전트는 내부 처리 세부 사항을 숨기고 오케스트레이터가 필요한 결과만 반환하여 컨텍스트 폭발을 방지합니다.


컨텍스트 압축과 메모리 계층 설계

컨텍스트 압축 전략

컨텍스트가 임계치에 근접하면 압축을 수행해야 합니다. 압축에는 크게 세 가지 접근이 있습니다. 첫째, 선택적 제거(Selective Pruning) 는 명백히 불필요해진 정보 — 이미 완료된 도구 호출의 중간 결과, 오래된 사용자 입력 등 — 를 컨텍스트에서 삭제하는 방식입니다. 둘째, LLM 기반 요약(LLM Summarization) 은 대화 히스토리나 도구 결과를 더 작은 모델로 요약하여 대체하는 방식입니다. 셋째, 외부화(Externalization) 는 정보를 외부 스토리지(벡터 DB, 파일 시스템)에 저장하고, 필요할 때만 검색하여 컨텍스트에 재삽입하는 방식입니다.

각 방식은 서로 다른 트레이드오프를 가집니다. 선택적 제거는 간단하지만 제거 기준 설정이 어렵습니다. LLM 요약은 풍부한 정보를 보존하지만 추가 LLM 호출 비용이 발생합니다. 외부화는 이론적으로 무한한 메모리를 제공하지만 검색 정확도에 의존하며, 잘못된 검색 결과가 컨텍스트에 삽입되면 오히려 성능이 저하될 수 있습니다.

압축 전략 정보 손실 추가 비용 적합한 상황
선택적 제거 높음 없음 명확히 불필요한 히스토리 정리
LLM 요약 낮음 LLM 호출 비용 긴 대화 히스토리, 도구 결과 압축
외부화 (RAG) 없음 검색 지연, 인덱싱 비용 대규모 문서, 장기 메모리
슬라이딩 윈도우 중간 없음 최근 N턴만 유지하는 챗봇

메모리 계층 아키텍처

장기 실행 에이전트나 지속적인 사용자 상호작용을 지원하는 시스템에서는 단일 컨텍스트 윈도우만으로는 부족합니다. 인간의 기억 구조에서 영감을 얻은 다층 메모리 아키텍처가 효과적인 대안입니다.

작업 메모리(Working Memory) 는 현재 컨텍스트 윈도우 안에 있는 정보로, 즉시 접근 가능하지만 용량이 제한됩니다. 에피소딕 메모리(Episodic Memory) 는 이전 세션이나 대화의 요약으로, 외부 스토리지에 저장되었다가 새 세션 시작 시 컨텍스트에 로드됩니다. 시맨틱 메모리(Semantic Memory) 는 사용자 선호도, 도메인 지식, 시스템 설정처럼 변화가 느린 정보로, 시스템 프롬프트의 일부로 포함되거나 벡터 DB에서 필요 시 검색됩니다. 절차 메모리(Procedural Memory) 는 에이전트가 특정 작업을 수행하는 방법에 대한 지식으로, 시스템 프롬프트나 도구 명세에 인코딩됩니다.

LLM 에이전트 메모리 아키텍처 작업 메모리를 중심으로 에피소딕·시맨틱·절차 메모리가 연결되는 LLM 에이전트 컨텍스트 윈도우 구조. 세션 시작 시 로드 필요 시 검색 고정 포함 WM 작업 메모리 현재 컨텍스트 EPISODIC 에피소딕 메모리 세션 요약 SEMANTIC 시맨틱 메모리 벡터 DB · RAG PROCEDURAL 절차 메모리 시스템 프롬프트 LEGEND 정보 저장 컨텍스트 로드 작업 메모리 (현재 활성)

각 메모리 계층의 역할을 분리하면, 컨텍스트 윈도우는 현재 작업에 집중하면서 장기적인 상태를 외부에서 효율적으로 관리할 수 있습니다.


벡터 검색 기반 컨텍스트 주입

RAG(Retrieval-Augmented Generation)는 외부 지식을 에이전트 컨텍스트에 주입하는 가장 널리 쓰이는 방법입니다. 그러나 RAG의 효과는 단순히 "관련 청크를 찾아서 붙여 넣는" 것에 그치지 않으며, 검색된 정보를 컨텍스트에 어떻게 통합하느냐에 따라 결과의 질이 크게 달라집니다.

흔한 실수는 코사인 유사도 기준으로 상위 N개의 청크를 그대로 컨텍스트에 이어 붙이는 것입니다. 이 방식은 청크 간 컨텍스트 단절, 중복 정보 삽입, 출처가 다른 정보의 충돌 등 문제를 일으킵니다. 더 효과적인 접근은 검색된 청크를 재순위화(Re-ranking)하고 적절히 연결된 형태로 재구성한 뒤 컨텍스트에 삽입하는 것입니다. 아래는 Python으로 RAG 파이프라인에서 검색 결과를 컨텍스트에 구조화하여 삽입하는 예시입니다.

python
from anthropic import Anthropic

client = Anthropic()

def build_rag_context(retrieved_chunks: list[dict]) -> str:
    """
    검색된 청크를 구조화된 컨텍스트 블록으로 변환합니다.
    청크는 {content: str, source: str, relevance_score: float} 구조.
    """
    # 관련도 0.7 이상만 포함, 최대 5개
    filtered = [c for c in retrieved_chunks if c["relevance_score"] >= 0.7][:5]
    
    if not filtered:
        return "<retrieved_context>관련 정보 없음</retrieved_context>"
    
    chunks_xml = "\n".join([
        f'<chunk source="{c["source"]}" score="{c["relevance_score"]:.2f}">\n'
        f'{c["content"]}\n</chunk>'
        for c in filtered
    ])
    return f"<retrieved_context>\n{chunks_xml}\n</retrieved_context>"

def agent_call(user_query: str, retrieved_chunks: list[dict]) -> str:
    rag_context = build_rag_context(retrieved_chunks)
    
    response = client.messages.create(
        model="claude-sonnet-4-5",
        max_tokens=1024,
        system="""당신은 기술 지원 에이전트입니다.
<retrieved_context> 안의 정보를 우선 참조하되,
정보가 없거나 불충분하면 그렇다고 명시하세요.""",
        messages=[{
            "role": "user",
            # 검색 컨텍스트를 앞에, 질문을 끝에 배치 — Lost in the Middle 대응
            "content": f"{rag_context}\n\n질문: {user_query}"
        }]
    )
    # 결과: 출처·관련도가 태그에 명시되어 모델이 신뢰도 기반 인용 가능
    return response.content[0].text

이 예시에서 핵심은 검색 결과를 <chunk source="..." score="..."> 형태로 출처와 관련도를 명시한다는 점입니다. 모델은 이 메타데이터를 활용하여 더 높은 관련도의 청크에 더 많은 가중치를 부여하고, 출처를 명확히 인용할 수 있습니다. 또한 관련도 임계치(0.7)를 설정함으로써 노이즈가 높은 청크가 컨텍스트에 삽입되는 것을 사전에 차단합니다.


운영 환경에서의 컨텍스트 엔지니어링

흔한 실수와 안티패턴

운영 환경에서 자주 관찰되는 컨텍스트 설계 실수의 첫 번째는 무한 히스토리 누적입니다. 챗봇이나 에이전트 시스템에서 모든 이전 대화를 컨텍스트에 유지하면, 시간이 지날수록 초기 시스템 프롬프트의 지시 사항이 희석되고 모델은 최근 대화 패턴에 과도하게 끌리는 경향이 있습니다. 슬라이딩 윈도우 방식으로 최근 N턴만 유지하거나, 일정 주기마다 대화를 요약하는 전략이 필요합니다.

두 번째 실수는 에러 메시지의 무분별한 컨텍스트 포함입니다. 도구 호출이 실패하면 에러 메시지가 컨텍스트에 추가되는데, 이 메시지들이 누적되면 모델이 실패에 과도하게 집중하거나, 이미 해결된 문제를 반복적으로 언급하는 문제가 발생합니다. 에러는 컨텍스트에 포함하되 "이 에러는 X를 통해 해결되었습니다"라는 해결 정보와 함께 압축하는 것이 효과적입니다.

세 번째는 시스템 프롬프트 비대화입니다. 시스템 프롬프트는 안정적이고 캐시 가능하므로 "모든 것을 시스템 프롬프트에"라는 유혹이 있습니다. 그러나 시스템 프롬프트가 지나치게 길어지면 모델이 지시 사항 전체를 균등하게 처리하지 못하고, 핵심 규칙보다 부가적인 내용에 더 많은 주의를 기울이는 현상이 나타납니다. 시스템 프롬프트는 역할 정의와 불변 규칙에 집중하고, 작업별 세부 지시는 사용자 메시지 컨텍스트에 포함시키는 분리 원칙을 지켜야 합니다.

컨텍스트 윈도우 설계: 안티패턴 vs 해결 전략 세 가지 컨텍스트 안티패턴(무한 히스토리 누적, 에러 메시지 무분별 포함, 시스템 프롬프트 비대화)이 에이전트 성능 저하로 이어지고, 세 가지 해결 전략(슬라이딩 윈도우·주기적 요약, 에러 압축·해결 정보 포함, 시스템 프롬프트 핵심만 유지)이 안정적 에이전트로 수렴하는 구조를 보여준다. 안티패턴 해결 전략 무한 히스토리 누적 에러 메시지 무분별 포함 시스템 프롬프트 비대화 결과 에이전트 성능 저하 슬라이딩 윈도우 주기적 요약 에러 압축 해결 정보 포함 시스템 프롬프트 핵심만 유지 결과 안정적 에이전트 안티패턴 해결 전략 결과 (focal)

세 가지 안티패턴을 인식하고 각각에 대응하는 설계 원칙을 시스템 설계 초기에 적용하면, 운영 중 발생하는 성능 저하의 상당 부분을 예방할 수 있습니다.


모니터링과 디버깅

컨텍스트 엔지니어링의 효과를 검증하려면 적절한 관찰 지표(Observability Metrics)를 추적해야 합니다. 토큰 사용량 추이는 에이전트 루프가 진행될수록 컨텍스트가 어떤 속도로 증가하는지를 보여주며, 예상보다 급격한 증가는 불필요한 정보가 누적되고 있음을 시사합니다. 도구 호출 정확도는 에이전트가 올바른 도구를 선택하고 올바른 파라미터를 전달하는 비율로, 이 수치가 낮다면 도구 명세나 시스템 프롬프트의 설명이 불명확하다는 신호입니다.

컨텍스트 충돌 감지 역시 중요합니다. 컨텍스트 내에서 상충하는 정보가 존재할 때 모델의 응답이 불안정해지는 경향이 있습니다. 예를 들어, 시스템 프롬프트에서 "한국어로만 응답하라"고 지시했는데 이전 도구 결과가 영어로만 가득 차 있다면, 모델이 어느 언어로 응답할지 혼란스러워질 수 있습니다. 이런 충돌을 감지하는 휴리스틱 검증 로직을 컨텍스트 구성 단계에 추가하는 것이 권장됩니다.

LangSmith, Langfuse, Arize Phoenix 등 LLM Observability 도구들은 에이전트 실행의 각 단계에서 컨텍스트 상태를 캡처하고 시각화하는 기능을 제공합니다. 이를 활용하면 특정 단계에서 컨텍스트가 어떻게 구성되었는지를 사후에 분석할 수 있어 디버깅 효율이 크게 향상됩니다.


확장과 마이그레이션 시 고려사항

컨텍스트 설계는 사용하는 모델이 변경되면 반드시 재검토가 필요합니다. 모델마다 컨텍스트 처리 특성이 다르기 때문입니다. GPT-4o에서 잘 작동하던 XML 태그 기반 구조화 방식이 다른 모델에서는 동일한 효과를 발휘하지 않을 수 있습니다. 모델 마이그레이션 시에는 컨텍스트 구조, 도구 명세 형식, 시스템 프롬프트 스타일 각각에 대해 별도의 A/B 테스트를 수행하는 것이 안전합니다.

사용자 규모가 증가할 때는 컨텍스트 캐싱 전략의 경제성을 재평가해야 합니다. Anthropic의 프롬프트 캐싱은 최소 1024 토큰의 캐시 블록을 요구하며, TTL이 5분입니다. 트래픽이 시간대별로 분산되는 경우 캐시 히트율이 낮아질 수 있으며, 이때는 캐시 대상 블록의 크기와 구성을 조정하여 히트율을 최적화해야 합니다. 또한, 멀티테넌트 환경에서는 각 사용자의 컨텍스트를 격리하는 것이 보안 측면에서도 중요합니다. 한 사용자의 개인 정보가 다른 사용자의 세션 컨텍스트에 유출되는 컨텍스트 누수(Context Leakage) 사고는 운영 환경에서 실제로 발생한 사례가 있으며, 세션 격리를 시스템 설계 초기부터 명시적으로 보장해야 합니다.


맺음말

핵심 요약

이 글에서 다룬 내용을 정리하면 다음과 같습니다. 컨텍스트 윈도우는 논리적으로 시스템 프롬프트, 작업 컨텍스트, 대화 히스토리, 검색 정보의 네 영역으로 나뉘며, 각 영역의 배치와 균형이 에이전트 성능을 결정합니다. Lost in the Middle 현상을 인식하고, 중요한 정보를 컨텍스트의 앞과 끝에 배치하는 위치 전략이 필요합니다. 에이전트 루프가 반복될수록 컨텍스트는 팽창하므로 임계치 기반 압축과 다층 메모리 아키텍처로 장기 실행 에이전트를 설계해야 합니다. 멀티 에이전트 시스템에서는 서브에이전트가 구조화된 요약만 반환하게 하여 컨텍스트 폭발을 방지합니다. 그리고 RAG를 사용할 때는 청크를 그대로 붙여 넣는 대신 관련도 필터링과 구조화 삽입을 통해 신호 대비 노이즈 비율을 관리해야 합니다.

적용 판단 기준

Context Engineering에 투자할 가치가 높은 상황은 명확합니다. 에이전트가 10단계 이상의 작업을 수행하거나, 복수의 도구를 조합하여 복잡한 목표를 달성하거나, 사용자와 장기적인 대화를 유지해야 하는 시스템이라면 체계적인 컨텍스트 설계 없이는 안정적인 운영이 어렵습니다. 반면, 단순한 단발성 질의응답이나 짧은 텍스트 변환 작업이라면 프롬프트 최적화만으로 충분한 경우가 많습니다.

현재 에이전트 성능에 문제가 있다면, 모델 교체나 파인튜닝을 시도하기 전에 먼저 컨텍스트를 점검하는 것이 효율적입니다. 에이전트 오류의 상당 비율은 모델 능력의 한계가 아니라 잘못 설계된 컨텍스트에서 비롯됩니다. 도구 명세가 모호하지 않은지, 중요한 지시 사항이 컨텍스트 중간에 묻히지 않았는지, 불필요한 정보가 모델의 주의를 분산시키고 있지 않은지를 먼저 검토하는 것이 Context Engineering의 첫걸음입니다.