본문 바로가기
AI2026년 8월 20일7분 읽기

컨텍스트 엔지니어링 — 프롬프트가 아니라 컨텍스트를 설계하는 법

YS
김영삼
조회 4
컨텍스트 엔지니어링 — 프롬프트가 아니라 컨텍스트를 설계하는 법

컨텍스트 엔지니어링(Context Engineering)은 LLM에게 던지는 프롬프트 문장을 다듬는 일이 아니라, 모델이 답을 만들 때 컨텍스트 윈도우 안에 무엇을·얼마나·어떤 순서로 넣을지를 설계하는 작업이다. 검색 결과, 이전 대화, 도구 출력, 시스템 지시를 한정된 토큰 예산 안에 배치하는 정보 관리 문제에 가깝다.

2023년엔 "프롬프트 엔지니어링"이라는 말로 다 뭉뚱그렸다. 그런데 에이전트를 실제로 굴려보면, 답 품질을 좌우하는 건 문장의 말투가 아니라 그 순간 모델 눈앞에 놓인 컨텍스트의 구성이더라. 나는 이걸 몇 번 크게 데인 뒤에야 깨달았다.

왜 프롬프트가 아니라 컨텍스트인가

단발성 질문이라면 프롬프트 한 줄이 전부다. 하지만 도구를 여러 번 부르고, 이전 단계 출력이 다음 단계 입력이 되는 에이전트에서는 얘기가 다르다. 20턴쯤 돌고 나면 컨텍스트 윈도우가 검색 스니펫, 함수 호출 결과, 에러 로그로 가득 찬다. 이때 모델은 정작 사용자의 원래 요구를 까먹거나, 중간에 낀 오래된 정보를 최신인 양 물고 늘어진다.

솔직히 처음엔 컨텍스트 윈도우가 200K니까 다 때려넣으면 된다고 생각했다. 그런데 긴 컨텍스트일수록 중간에 있는 정보를 놓치는 현상(lost in the middle)이 뚜렷하게 나타난다. 토큰이 많아질수록 비용과 지연도 선형으로 늘고. 결국 "넣을 수 있는 최대"가 아니라 "이 턴에 꼭 필요한 최소"를 고르는 게 핵심이 됐다.

컨텍스트를 구성하는 조각들

나는 컨텍스트를 아래 다섯 종류로 나눠서 관리한다. 각각의 수명(lifetime)과 우선순위가 다르기 때문이다.

종류예시수명위치 전략
시스템 지시역할·규칙·출력 형식영구맨 앞, 캐시 대상
검색 지식RAG 청크·문서턴 단위질문 근처, 출처 표기
대화 이력이전 사용자·모델 발화세션요약·압축 대상
도구 출력API·DB 응답순간필요분만, 사용 후 버림
장기 메모리사용자 선호·사실영구외부 저장, 관련분만 주입

이렇게 쪼개 놓고 나면 "이 도구 출력은 다음 턴에도 필요한가?"라는 질문을 매번 던지게 된다. 대부분은 아니다. 그래서 도구 출력은 요약해서 남기고 원문은 버린다.

토큰 예산을 코드로 강제하기

말로만 "짧게 넣자"고 하면 안 지켜진다. 나는 컨텍스트 조립을 명시적인 예산 함수로 만들었다. 우선순위 높은 것부터 채우고, 예산을 넘으면 낮은 것부터 잘라내는 방식이다.

def build_context(budget_tokens, system, memories, docs, history):
    parts = []
    used = 0

    def add(text, tag):
        nonlocal used
        t = count_tokens(text)
        if used + t > budget_tokens:
            return False
        parts.append((tag, text))
        used += t
        return True

    # 1) 시스템 지시는 무조건 (예산에서 선차감)
    add(system, "system")
    # 2) 관련도 높은 순으로 문서 주입, 넘치면 중단
    for d in sorted(docs, key=lambda x: -x.score):
        if not add(d.text, "doc"):
            break
    # 3) 남는 예산으로 최근 대화부터 역순 주입
    for turn in reversed(history):
        if not add(turn.text, "history"):
            break
    return assemble(parts)

이렇게 하면 컨텍스트가 절대 윈도우를 넘지 않고, 넘칠 때 무엇을 버릴지가 명확해진다. 예전엔 그냥 전부 이어붙였다가 context_length_exceeded 에러로 요청이 통째로 실패하곤 했는데, 그 삽질을 한 번 하고 나면 예산 함수의 소중함을 안다.

대화가 길어질 때 — 압축과 요약

세션이 길어지면 대화 이력이 예산을 다 잡아먹는다. 이때 두 가지를 쓴다. 첫째, 오래된 턴을 롤링 요약으로 접는다. 최근 N턴은 원문 그대로 두고, 그 이전은 "지금까지의 요약" 한 덩어리로 대체한다. 둘째, 도구 출력처럼 부피 큰 것은 애초에 구조화된 형태로만 남긴다. 검색 결과 10개를 통째로 넣지 말고, 제목·URL·한 줄 요지만 남기는 식이다.

컨텍스트 엔지니어링의 목표는 "모델에게 많이 알려주기"가 아니라 "이 순간에 필요한 것만 또렷하게 보여주기"다. 노이즈를 줄이면 정확도가 올라간다.

캐시 정렬을 깨지 마라

프롬프트 캐싱을 쓴다면 컨텍스트 앞부분(시스템 지시, 고정 예시)을 바이트 단위로 동일하게 유지해야 한다. 나는 초반에 시스템 프롬프트에 현재 시각을 박아 넣었다가 캐시가 매 요청마다 깨져서 비용이 안 줄어드는 걸 한참 뒤에 알았다. 변하는 값은 뒤로 빼고, 앞은 고정하라. 이 한 가지만 지켜도 반복 호출 비용이 크게 준다.

흔한 함정

  • 관련도 낮은 청크를 "혹시 몰라서" 많이 넣기 → 정확도 하락. 적을수록 낫다.
  • 도구 출력 원문을 세션 끝까지 들고 가기 → 토큰 폭발. 요약 후 폐기.
  • 중요한 지시를 컨텍스트 중간에 묻기 → lost in the middle. 맨 앞이나 맨 뒤에.
  • 매 요청마다 컨텍스트 앞부분이 미묘하게 바뀜 → 캐시 미스. 앞을 고정.

자주 묻는 질문

컨텍스트 엔지니어링과 프롬프트 엔지니어링은 뭐가 다른가요?

프롬프트 엔지니어링은 모델에게 보내는 지시문의 표현을 다듬는 일입니다. 컨텍스트 엔지니어링은 그 지시문을 포함해 검색 지식·대화 이력·도구 출력·메모리 등 컨텍스트 윈도우 전체의 구성과 순서, 토큰 예산을 설계하는 상위 작업입니다. 에이전트처럼 여러 턴·여러 도구가 얽히면 후자가 품질을 좌우합니다.

컨텍스트를 무조건 많이 넣으면 정확해지나요?

아닙니다. 긴 컨텍스트일수록 중간 정보를 놓치는 lost in the middle 현상이 생기고, 관련 없는 내용이 많으면 오히려 모델이 헷갈립니다. 비용과 지연도 늘어납니다. 이 순간 답에 꼭 필요한 최소한을 골라 또렷하게 배치하는 편이 대체로 더 정확합니다.

대화가 길어지면 어떻게 관리하나요?

최근 몇 턴은 원문으로 두고 그 이전은 롤링 요약으로 접는 방식이 실용적입니다. 도구 출력이나 검색 결과처럼 부피 큰 데이터는 원문 대신 제목·요지·출처만 구조화해 남기면 토큰을 크게 아낄 수 있습니다.

프롬프트 캐싱과 함께 쓸 때 주의할 점은?

캐시는 컨텍스트 앞부분이 바이트 단위로 동일해야 적중합니다. 현재 시각처럼 매번 바뀌는 값을 앞에 두면 캐시가 계속 깨져 비용 절감 효과가 사라집니다. 고정된 시스템 지시와 예시는 앞에 두고, 가변 데이터는 뒤로 배치하세요.

댓글 0

아직 댓글이 없습니다.
Ctrl+Enter로 등록