에이전트를 만들다 보면 같은 문제가 반복해서 나옵니다. 이 수업에서는 툴 사용 루프·메모리·컨텍스트 관리에서 굳어진 패턴들과, 외부 에이전트 프레임워크를 도입할지 판단하는 기준을 다룹니다.
반복해서 나오는 패턴들
에이전트 구현에서 자주 쓰이는 형태는 몇 가지로 정리됩니다.
툴 사용 루프 — 가장 기본입니다. 모델이 도구를 고르고, 실행하고, 결과를 되먹입니다. 앞 수업의 루프가 이것입니다.
서브에이전트 위임 — 큰 작업을 쪼개 독립된 대화에서 처리하고 결과만 받습니다. 컨텍스트를 아끼는 것이 목적입니다.
메모리 — 대화 밖에 상태를 두고 필요할 때 꺼내 씁니다. 대화는 길어지면 잘려 나가지만 메모리는 남습니다.
컨텍스트 윈도우 관리 — 이력이 한계에 가까워질 때 오래된 부분을 요약하거나 버립니다.
이 중 메모리와 컨텍스트 관리를 헷갈리는 경우가 많습니다. 둘 다 "길어진 대화"를 다루지만 목적이 다릅니다. 컨텍스트 관리는 이번 실행을 끝내기 위해 자리를 확보하는 일이고, 메모리는 다음 실행에서도 쓰려고 밖에 남기는 일입니다.
# 메모리: 대화 밖 저장소에 남긴다
memory.put(session_id, "user_prefers", "표 형식 요약")
# 컨텍스트 관리: 이번 대화의 자리를 확보한다
if estimated_tokens(messages) > THRESHOLD:
messages = [summarize_old(messages[:-6])] + messages[-6:]
도구 결과를 그대로 넣지 않기
구현에서 컨텍스트를 가장 빨리 잡아먹는 것은 모델의 응답이 아니라 도구 결과입니다. API 응답 하나가 수천 줄이면 그게 그대로 이력에 쌓입니다.
그래서 실무에서는 도구 결과를 돌려주기 전에 다듬습니다.
def tool_result_for(name: str, raw: dict) -> str:
if name == "search_documents":
# 전체 문서가 아니라 필요한 필드만, 상위 몇 건만
hits = raw["results"][:5]
return json.dumps(
[{"id": h["id"], "title": h["title"], "snippet": h["text"][:200]} for h in hits],
ensure_ascii=False,
)
return json.dumps(raw, ensure_ascii=False)
이때 주의할 점이 있습니다. 잘라 내면 모델이 볼 수 없는 정보가 생깁니다. 무엇을 잘랐는지 알려 주는 편이 낫습니다. "상위 5건만 표시, 전체 43건"처럼 적으면 모델이 더 필요할 때 다시 요청할 수 있습니다. 아무 표시 없이 자르면 모델은 그것이 전부라고 믿습니다.
또 하나, 실패한 도구 결과도 반드시 돌려줘야 합니다. 오류 표시를 붙여 무엇이 왜 실패했는지 적으면 모델이 다른 인자로 재시도하거나 다른 경로를 찾습니다. 결과를 아예 주지 않으면 대화가 끊깁니다.
외부 프레임워크를 쓸 것인가
에이전트 프레임워크는 여러 가지가 있습니다. 그래프로 상태 전이를 그리는 것, 타입 검증을 앞세운 것, 여러 모델 제공자를 추상화한 것 등입니다.
프레임워크가 값을 하는 지점은 대개 여러 단계와 분기를 관리해야 할 때입니다. 상태 전이가 열 개를 넘어가면 직접 짠 조건문보다 선언적으로 그리는 편이 읽기 좋습니다. 재시도, 체크포인트, 중단 후 재개 같은 것을 이미 갖춘 것도 이점입니다.
값을 못 하는 경우도 분명합니다.
- 단계가 두세 개뿐일 때. 추상화 층만 늘어납니다.
- 우리가 쓰는 모델의 최신 기능을 프레임워크가 아직 지원하지 않을 때. 우회하느라 더 복잡해집니다.
- 디버깅이 잦은 초기 단계일 때. 한 겹 아래에서 무슨 일이 벌어지는지 보기 어려워집니다.
추상화는 제공자 교체를 쉽게 해 주지만 공짜가 아닙니다. 여러 제공자를 감싸는 층은 대체로 공통분모만 노출하므로, 특정 모델의 고유 기능을 쓰려면 결국 그 층을 뚫어야 합니다. 시험에서는 "프레임워크를 쓰면 항상 유지보수가 쉬워진다"는 단정이 오답으로 나옵니다.
패턴을 고르는 순서
실무에서는 대개 이 순서로 올라갑니다.
- 한 번의 호출로 되는지 본다. 되면 끝입니다.
- 안 되면 워크플로로 단계를 나눕니다. 경로가 고정이면 여기서 멈춥니다.
- 경로를 미리 못 정하면 툴 사용 루프를 씁니다.
- 컨텍스트가 부족하거나 병렬화가 필요하면 서브에이전트를 더합니다.
- 실행 간에 이어져야 할 상태가 있으면 메모리를 붙입니다.
각 단계마다 비용과 변동성이 늘어납니다. 아래에서 위로 올라가되, 필요가 증명되기 전에는 올라가지 않는 것이 원칙입니다. 처음부터 5단계 구조로 시작하면 무엇이 문제인지 가려내기 어려워집니다.
패턴을 더할 때마다 관측 수단도 함께 더해야 합니다. 서브에이전트를 붙였으면 각 서브에이전트의 토큰과 시간이 따로 보여야 하고, 메모리를 붙였으면 무엇이 언제 저장되고 읽혔는지 남아야 합니다. 관측 없이 늘어난 구조는 고칠 수 없습니다.
시험 함정
- 메모리와 컨텍스트 윈도우 관리를 같은 것으로 보는 선택지 — 하나는 실행 간 유지, 다른 하나는 이번 실행의 자리 확보입니다.
- 도구 결과를 원본 그대로 넣어야 정확하다는 서술 — 컨텍스트를 가장 빨리 소모하는 원인이며 다듬는 것이 실무입니다.
- 결과를 잘라 내면서 잘랐다는 사실을 알리지 않는 처리 — 모델은 그것이 전부라고 믿습니다.
- 실패한 도구 호출에 결과를 돌려주지 않아도 된다는 선택지 — 대화가 끊기고 모델이 다음 수를 고를 수 없습니다.
- 프레임워크를 쓰면 항상 유지보수가 쉬워진다는 단정 — 단계가 적으면 추상화 층만 늘고, 최신 기능 지원이 늦으면 우회가 늘어납니다.
- 처음부터 서브에이전트와 메모리를 갖춘 구조로 시작하라는 권고 — 필요가 증명된 뒤에 올라가야 문제를 가려낼 수 있습니다.
실습 시나리오
실제 시험과 같은 형식의 시나리오 문제입니다.
검색 도구를 쓰는 에이전트가 세 번째 턴부터 컨텍스트 한계에 부딪힙니다. 확인해 보니 검색 도구가 매번 문서 40건의 전문을 그대로 반환하고 있습니다. 가장 먼저 할 조치는 무엇입니까?
빌드 연습 · 도구 결과 다듬기로 컨텍스트 사용량 줄이기
약 35분1.기준선 측정
검색 도구가 원본을 그대로 반환하는 상태에서 턴별 입력 토큰을 기록합니다.
기대 결과 · 턴이 갈수록 입력 토큰이 급격히 증가하는 곡선이 나옵니다.
2.결과 축약
상위 5건의 제목과 200자 발췌만 남기고 전체 건수를 함께 적습니다.
기대 결과 · 결과 문자열에 표시 건수와 전체 건수가 모두 들어 있습니다.
3.재측정
같은 질의로 다시 돌려 턴별 입력 토큰을 비교합니다.
기대 결과 · 축약 전후의 토큰 차이가 표로 남습니다.
4.정보 손실 확인
축약 때문에 답이 나빠진 질의가 있는지 다섯 건을 비교합니다.
기대 결과 · 모델이 더 필요할 때 재검색을 요청하는 턴이 관찰됩니다.
5.실패 결과 처리
검색이 0건일 때와 오류일 때 각각 무엇을 돌려줄지 정해 구현합니다.
기대 결과 · 두 경우 모두 모델이 다음 행동을 고를 수 있는 문구가 반환됩니다.