다단계 AI 에이전트 워크플로(Workflows)에서 컨텍스트 손실을 해결하는 방법
요약
다단계 AI 에이전트 워크플로에서 발생하는 컨텍스트 손실 문제의 원인과 해결책을 다룹니다. 도구 호출 시 발생하는 메시지 기록의 비대화로 인해 모델의 주의력이 분산되는 문제를 지적하며, 이를 해결하기 위한 스크래치패드 활용법을 제안합니다.
핵심 포인트
- 도구 호출 간 모델은 자체적인 메모리가 없는 Stateless 상태임
- 누적되는 도구 출력값이 컨텍스트 윈도우를 채워 주의력을 분산시킴
- 해결책으로 명시적인 '스크래치패드'를 통한 상태 외부화 권장
지난 주말 저는 자신이 무엇을 하고 있는지 계속해서 잊어버리는 에이전트를 디버깅하며 시간을 보냈습니다. 에이전트는 세 개의 도구(Tool)를 순차적으로 기분 좋게 호출하더니, 네 번째 호출에서는... 멍하니 바라만 보더군요. 잘못된 인자(Arguments), 환각(Hallucination)을 일으킨 파일 경로까지. 전형적인 "나는 누구인가, 여기는 어디인가, 내가 무엇을 하고 있었나" 하는 순간이었습니다.
만약 여러분이 두 개 이상의 도구 호출(Tool calls)을 체이닝(Chaining)하는 무언가를 만들어 보았다면, 아마 이런 현상을 목격했을 것입니다. 에이전트는 초반에는 강력하게 시작하여 계획을 세우지만, 세 번째나 네 번째 단계쯤 되면 마치 낮잠을 자다가 다른 사람의 세션에서 깨어난 것처럼 보이기 시작합니다.
이런 일이 왜 발생하는지, 그리고 실제로 어떻게 해결할 수 있는지에 대해 이야기해 보겠습니다.
문제점: 당신의 에이전트는 기억상실증에 걸렸습니다
증상은 이렇습니다. 여러분은 다음과 같은 작업을 수행해야 하는 에이전트 스킬을 구축합니다:
- 설정 파일(Config file) 읽기
- 스키마(Schema)에 따라 유효성 검사 수행
- 변환(Transformation) 적용
- 결과를 다시 쓰기
1단계와 2단계는 잘 작동합니다. 하지만 3단계에 이르면, 에이전트는 1단계에서 추출했던 변수 이름들을 놓쳐버립니다. 4단계에 이르면, 완전히 잘못된 경로에 결과물을 쓰고 있습니다. 로깅(Logging)을 추가해 봅니다. 재시도(Retries)를 추가해 봅니다. "파일명(Filename)을 절대 잊지 마시오"라고 엄격한 시스템 프롬프트(System prompt)를 추가해 봅니다. 하지만 아무것도 도움이 되지 않습니다.
저는 제가 만들고 있던 코드 리뷰 에이전트에서 이 문제를 겪었습니다. 에이전트는 파일을 읽고 세 가지 문제를 식별한 다음, 수정을 요청받으면 존재하지도 않는 문제를 지어내곤 했습니다. 원래의 분석 내용이 그냥... 증발해 버린 것입니다.
근본 원인: 도구 호출은 상태가 없습니다 (Stateless)
에이전트를 만들기 시작할 때 아무도 말해주지 않는 사실이 있습니다. 바로 모델 자체는 도구 호출 사이에 아무런 메모리(Memory)를 가지고 있지 않다는 점입니다. 유일한 "메모리"는 여러분이 계속해서 다시 보내주는 메시지 기록(Message history)뿐입니다.
에이전트가 도구를 호출할 때 일어나는 과정은 다음과 같습니다:
- 모델은 전체 메시지 기록을 확인합니다.
- 모델은 확인한 내용을 바탕으로 도구와 인자(Arguments)를 선택합니다.
- 도구가 실행되고 결과를 반환합니다.
- 결과가 기록(History)에 추가됩니다.
- 모델은 업데이트된 기록을 확인하고 다음 동작을 선택합니다.
만약 1단계에서 거대한 JSON 덩어리가 반환되고, 2단계에서 또 다른 덩어리가 반환된다면, 3단계의 컨텍스트 윈도우(Context Window)는 이제 대부분 오래된 도구 출력값으로 채워지게 됩니다. 모델의 주의력(Attention)이 분산되는 것입니다. 1단계에서 추출한 파일 이름과 같은 중요한 세부 정보들이 노이즈 속에 파묻혀 버립니다.
이것은 버그가 아닙니다. 아키텍처(Architecture)가 작동하는 방식 그 자체입니다. 각 턴(Turn)은 계속해서 늘어나는 메시지 로그(Message Log)에 대한 새로운 추론(Inference) 과정입니다.
단계별 해결책
1단계: 상태를 스크래치패드(Scratchpad)로 외부화하기
제가 발견한 가장 효과적인 해결책은 에이전트에게 명시적인 "스크래치패드(Scratchpad)"를 제공하는 것입니다. 이는 에이전트가 단계 사이에 읽고 쓸 수 있는 작은 구조화된 객체(Structured Object)입니다. 모델이 무언가를 기억하기를 기대하지 마세요. 직접 기록하게 만드세요.
다음은 Python으로 작성된 최소한의 버전입니다:
from dataclasses import dataclass, field, asdict
import json
...
핵심적인 움직임은 에이전트가 호출할 수 있는 도구(Tool)로서 remember와 decide를 노출하는 것입니다. 1단계가 끝난 후, 에이전트는 remember("config_path", "/etc/app.yml")를 호출합니다. 4단계에 이르러서도 그 사실은 시스템 프롬프트(System Prompt)의 정중앙에 그대로 남아 있게 됩니다.
2단계: 오래된 도구 출력 압축하기
두 번째 문제는 순전한 양(Volume)입니다. 500줄짜리 설정 파일을 대상으로 read_file을 호출하면 500줄이 반환됩니다. 이러한 호출이 세 번 반복되면 컨텍스트는 대부분 가공되지 않은 파일 덤프(File Dumps)로 채워집니다.
오래된 도구 결과가 더 이상 현재의 초점이 아니라면, 이를 요약본(Summary)으로 교체하여 문제를 해결할 수 있습니다:
def compress_history(messages, keep_recent=3):
# 시스템 메시지와 최근 N개의 턴은 그대로 유지
# 그 사이의 모든 내용은 요약
...
저는 보통 5~6턴마다 이 작업을 실행합니다. 에이전트는 중요한 사실에 대해서는 여전히 스크래치패드에 접근할 수 있고, 즉각적인 컨텍스트를 위해서는 최근의 턴들을 확인할 수 있습니다. 중간 부분은 압축됩니다.
3단계: 모든 중요한 도구 호출 전에 상태 검증하기
이것은 예방 차원의 단계입니다. 파일을 쓰거나, API를 호출하거나, 마이그레이션(Migration)을 실행하는 등 무언가를 변경(Mutate)하는 모든 도구에 대해, 먼저 스크래치패드를 확인하는 가드(Guard)로 감싸세요:
def write_file_safe(scratchpad, path: str, content: str):
# 에이전트가 읽었다고 기록하지 않은 경로에 쓰는 것을 거부함
if path not in scratchpad.facts.get("known_paths", []):
...
이는 환각 (Hallucination) 케이스를 깔끔하게 잡아냅니다. 만약 에이전트가 존재하지 않는 경로를 지어내면, 도구가 이를 거부하고 에이전트에게 그 이유를 알려줍니다. 열 번 중 아홉 번은 에이전트가 다음 턴에 스스로를 수정하고 올바른 파일을 읽습니다.
내가 사용하기 시작한 패턴
세 명의 에이전트를 이 방식으로 마이그레이션(Migration)한 후, 저는 대략 다음과 같은 구조로 수렴했습니다:
- 시스템 프롬프트 (System prompt): 목표와 도구 정의를 보유 (정적)
- 스크래치패드 (Scratchpad): 추출된 사실과 결정 사항을 보유 (가변적, 매 턴 재주입됨)
- 최근 메시지 (Recent messages): 마지막 몇 개의 턴을 있는 그대로 보유 (롤링 윈도우 (Rolling window))
- 요약 (Summary): 오래된 중간 턴들을 대체 (압축됨)
에이전트는 파일 이름을 찾기 위해 50개의 턴에 달하는 히스토리를 스캔할 필요가 없습니다. 스크래치패드를 보고, 사실을 확인한 뒤, 그에 따라 행동합니다.
예방 팁
처음부터 했더라면 좋았을 몇 가지 사항들입니다:
- 상태 인식 도구 (State-aware tools)를 먼저 설계하세요. 모든 도구는 단순히 데이터를 허공으로 반환하는 것이 아니라, 스크래치패드에서 읽거나 스크래치패드에 써야 합니다.
- 긴 체인 (Long chains)을 테스트하세요. 대부분의 에이전트 버그는 5회 이상의 도구 호출 후에야 나타납니다. 에이전트가 긴 워크플로 (Workflow)를 거치도록 강제하고 최종 상태를 검증하는 통합 테스트를 작성하세요.
- 턴당 토큰 수 (Token counts)를 기록하세요. 컨텍스트가 윈도우 (Window)의 60~70%를 넘어서기 시작하면 주의력 (Attention)이 저하되기 시작합니다. 필요하다고 생각하는 것보다 더 일찍 압축하세요.
- 모델이 기억할 것이라고 믿지 마세요. 만약 어떤 사실이 5단계에서 중요하다면, 1단계에서 스크래치패드에 기록하세요. 모델을 심각한 단기 기억 상실증을 앓고 있는 똑똑한 인턴처럼 대하십시오.
여기서 얻을 수 있는 더 넓은 교훈은 다음과 같습니다. 에이전트의 신뢰성은 사실 프롬프트 엔지니어링 (Prompt engineering)에 관한 것이 아닙니다. 그것은 상태 관리 (State management)에 관한 것입니다. 모델은 추론 (Inference)을 수행하고, 당신은 장부 정리 (Bookkeeping)를 수행하는 것입니다. 장부 정리를 제대로 하면 에이전트는 무언가를 잊어버리는 일을 멈출 것입니다.
저는 약 20회의 도구 호출 (tool calls)보다 긴 워크플로 (workflows)에서 스크래치패드 (scratchpad) 패턴을 테스트해보지는 않았으므로, 극단적인 규모에서는 결과가 다를 수 있습니다. 하지만 일반적인 3~10단계의 에이전트 워크플로 (agent workflow)에서는 이 방법이 제가 겪었던 모든 컨텍스트 손실 (context-loss) 버그를 해결해 주었습니다.
AI 자동 생성 콘텐츠
본 콘텐츠는 Dev.to AI tag의 원문을 AI가 자동으로 요약·번역·분석한 것입니다. 원 저작권은 원저작자에게 있으며, 정확한 내용은 반드시 원문을 확인해 주세요.
원문 바로가기