AI 에이전트 개발 실전 #4 긴 작업을 버티는 컨텍스트 관리

5 분 소요

3편에서 여러 단계 작업을 계획하고 검증하는 에이전트를 만들었습니다. 그런데 단계가 수십 번을 넘어가면 새로운 문제가 불거집니다. 루프가 돌 때마다 messages가 자라고, 결국 컨텍스트 창의 한계에 부딪힙니다. 한계 전이라도 문제입니다. 입력이 길수록 호출마다 비용이 커지고, 오래된 정보가 쌓이면 판단도 흐려집니다. 이번 글에서는 긴 작업을 버티는 컨텍스트 관리를 다룹니다.

LLM 앱 개발 실전 9편에서 챗봇의 대화 메모리를 다룬 적이 있습니다. 그것이 “사용자와의 대화를 기억하는” 문제였다면, 이번 글은 “한 작업 안에서 쌓이는 도구 결과를 감당하는” 문제입니다. 같은 컨텍스트 관리라도 다루는 대상이 다릅니다.

무엇이 컨텍스트를 차지하는지 보기 #

에이전트 대화에서 부피의 대부분은 사용자 메시지도 Claude의 답도 아닌 도구 결과입니다. 검색 결과 수십 건, 파일 내용 전체, API 응답 JSON이 매 단계 쌓입니다. 그래서 컨텍스트 관리의 첫 번째 원칙은 단순합니다. 도구 결과를 관리하면 대부분이 해결됩니다.

도구 결과 다이어트 — 들어올 때 줄이기 #

가장 효과가 큰 방법은 애초에 큰 결과를 만들지 않는 것입니다. 도구를 만들 때 반환 크기에 상한을 둡니다.

tool_result_limit.py
MAX_RESULT_CHARS = 4000

def search_docs(query: str) -> str:
    results = do_search(query)
    text = format_results(results[:10])   # 상위 10건만
    if len(text) > MAX_RESULT_CHARS:
        text = text[:MAX_RESULT_CHARS] + "\n…(결과가 잘렸습니다. 더 좁은 검색어로 다시 시도하세요.)"
    return text

잘랐다는 사실과 대처법을 결과에 적어 주는 것이 중요합니다. 2편의 에러 메시지 원칙과 같습니다. Claude는 그 안내를 읽고 검색어를 좁혀서 다시 시도합니다. 파일 읽기 도구라면 전체 대신 범위를 받게 하고, 목록 도구라면 페이지네이션(pagination, 결과를 여러 페이지로 나눠 한 번에 조금씩 가져오는 방식)을 넣는 식으로, 도구마다 “조금씩 가져오는 길"을 열어 둡니다.

오래된 도구 결과 비우기 #

들어올 때 줄여도 단계가 쌓이면 결국 자랍니다. 두 번째 방법은 이미 쓸모를 다한 도구 결과를 자리만 남기고 비우는 것입니다. 검색 결과는 Claude가 그걸 읽고 다음 행동을 정한 순간 역할이 끝난 경우가 많습니다.

prune_old_results.py
def prune_tool_results(messages: list, keep_recent: int = 3) -> list:
    """최근 N개 턴을 제외한 도구 결과 본문을 placeholder로 교체한다."""
    pruned = []
    for i, msg in enumerate(messages):
        old = i < len(messages) - keep_recent * 2
        if old and msg["role"] == "user" and isinstance(msg["content"], list):
            new_content = []
            for block in msg["content"]:
                if isinstance(block, dict) and block.get("type") == "tool_result":
                    block = {**block, "content": "(오래된 도구 결과는 정리되었습니다)"}
                new_content.append(block)
            msg = {**msg, "content": new_content}
        pruned.append(msg)
    return pruned

대화의 구조(어떤 도구를 불렀고 결과가 있었다는 사실)는 남기고 본문만 비우는 것이 포인트입니다. 구조까지 지우면 API가 tool_usetool_result의 짝을 검증할 때 에러가 납니다. 매 루프에서 호출 전에 이 정리를 돌리면, 컨텍스트는 최근 작업 분량 수준으로 유지됩니다.

요약 압축 — 지난 과정을 한 덩어리로 #

비우기로도 모자랄 만큼 긴 작업이라면, 지난 과정을 통째로 요약해서 교체합니다. 오래된 구간을 떼어 Claude에게 “지금까지의 진행 상황을 요약해라"라고 별도 호출로 시키고, 그 요약 한 덩어리로 해당 구간을 대체하는 방식입니다. 진행 상황의 맥락은 남고 부피는 크게 줄어듭니다.

직접 구현하지 않는 길도 있습니다. API의 compaction 기능(베타)을 켜면 컨텍스트가 한계에 다가갈 때 서버가 알아서 이전 내용을 요약합니다.

server_compaction.py
response = client.beta.messages.create(
    betas=["compact-2026-01-12"],
    model="claude-opus-4-8",
    max_tokens=16000,
    tools=tools,
    messages=messages,
    context_management={"edits": [{"type": "compact_20260112"}]},
)
messages.append({"role": "assistant", "content": response.content})

한 가지만 주의해 주세요. 응답에 요약을 담은 compaction 블록이 섞여 오는데, 이것을 response.content 통째로 대화에 다시 넣어야 합니다. 텍스트만 뽑아 쌓으면 요약 상태가 조용히 사라져서 압축이 풀려 버립니다.

파일 스크래치패드 — 컨텍스트 밖에 적어 두기 #

마지막 방법은 발상을 바꿉니다. 중요한 정보를 컨텍스트에 쌓는 대신, 컨텍스트 바깥의 파일에 적게 하는 것입니다. 이런 작업용 메모 파일을 스크래치패드(scratchpad, 작업 중간 내용을 임시로 적어 두는 메모장)라고 부릅니다. 에이전트에게 메모 파일을 읽고 쓰는 도구를 주고, 시스템 프롬프트에 사용 규칙을 넣습니다.

scratchpad_rule.py
SYSTEM = """...
- 작업 중 알게 된 중요한 사실과 남은 할 일은 notes.md에 기록한다.
- 긴 작업을 이어서 할 때는 먼저 notes.md를 읽고 시작한다.
"""

이렇게 하면 도구 결과를 공격적으로 비우거나 요약해도, 핵심 정보는 파일에 살아 있습니다. 컨텍스트는 현재 처리 중인 내용을 담고, 파일은 핵심 정보를 외부에 보존하는 역할입니다. 프로세스가 재시작돼도 파일은 남으므로, 세션을 넘는 긴 작업의 기반이 되기도 합니다.

무엇부터 적용할지 #

네 가지를 다 만들 필요는 없습니다. 효과 대비 비용 순서로 이렇게 권합니다.

  1. 도구 결과 상한 — 도구 코드 몇 줄이면 되고 효과가 가장 큽니다. 항상 적용합니다.
  2. 오래된 결과 비우기 — 수십 단계 작업이 흔하다면 추가합니다.
  3. compaction — 컨텍스트 한계 자체에 닿는 작업이라면 켭니다.
  4. 스크래치패드 — 세션을 넘거나 며칠짜리 작업이라면 도입합니다.

흔히 걸려 넘어지는 곳 #

  • 텍스트만 뽑아서 쌓는다 — compaction이든 일반 응답이든, response.content에는 텍스트 외의 블록이 섞일 수 있습니다. 대화에는 content를 통째로 보존합니다.
  • tool_result의 짝을 깨뜨린다 — 정리한다고 tool_result 블록 자체를 지우면, 남아 있는 tool_use와 짝이 안 맞아 400 에러가 납니다. 본문만 비우고 블록은 남깁니다.
  • 자르고 안내를 안 한다 — 결과를 말없이 자르면 Claude는 그게 전부라고 믿고 진행합니다. 잘렸다는 사실과 더 가져오는 방법을 결과 안에 적습니다.

마무리 #

이번 글에서는 긴 작업에서 컨텍스트를 감당하는 네 가지 방법을 다뤘습니다.

  • 부피의 주범은 도구 결과입니다. 도구가 처음부터 적게 반환하게 만드는 것이 가장 쌉니다.
  • 쓸모를 다한 결과는 본문만 비우고, 더 길어지면 요약으로 교체하거나 서버측 compaction을 켭니다.
  • 중요한 사실은 파일 스크래치패드에 적게 해서 컨텍스트 밖에 보존합니다.

지금까지는 에이전트 하나를 다루는 내용이었습니다. 다음 글인 “AI 에이전트 개발 실전 #5 서브에이전트로 일 나누기"에서는 일을 여러 에이전트에 나눠 맡기는 방법을 다룹니다. 컨텍스트 문제의 또 다른 해법이기도 합니다.

X