생산을 위한 효율적인 컨텍스트 인식 멀티에이전트 프레임워크 설계
Source: Google Developers Blog
The scaling bottleneck
큰 컨텍스트 윈도우는 컨텍스트‑관련 문제를 완화하는 데 도움이 되지만 모든 컨텍스트‑관련 문제를 해결해 주지는 못합니다. 실제로는 모든 것을 하나의 거대한 프롬프트에 추가하는 순진한 패턴이 세 가지 압력에 의해 무너집니다:
- 비용 및 지연 시간 급증: 모델 비용과 첫 토큰까지의 시간이 컨텍스트 크기에 따라 급격히 증가합니다. 원시 히스토리와 장황한 툴 페이로드를 윈도우에 “쌓아 넣는” 방식은 에이전트를 지나치게 느리고 비싸게 만듭니다.
- 신호 저하(“중간에서 사라짐”): 무관한 로그, 오래된 툴 출력, 혹은 폐기된 상태가 넘쳐나는 컨텍스트 윈도우는 모델을 산만하게 만들어 과거 패턴에 집착하게 하고 즉각적인 지시를 놓치게 합니다. 견고한 의사결정을 위해서는 관련 정보의 밀도를 최대화해야 합니다.
- 물리적 한계: 전체 RAG 결과, 중간 산출물, 긴 대화 기록 등을 포함하는 실제 워크로드는 결국 가장 큰 고정 윈도우조차도 초과하게 됩니다.
문제에 더 많은 토큰을 투입하면 일시적인 여유는 얻을 수 있지만 곡선의 형태 자체는 바뀌지 않습니다. 규모를 확장하려면 컨텍스트를 어떻게 표현하고 관리하느냐를 바꿔야 하며, 단순히 한 번의 호출에 넣을 수 있는 양을 늘리는 것만으로는 부족합니다.
The design thesis: context as a compiled view
이전 세대 에이전트 프레임워크에서는 컨텍스트를 가변 문자열 버퍼처럼 취급했습니다. ADK는 다른 명제에 기반합니다:
컨텍스트는 더 풍부한 상태 시스템에 대한 컴파일된 뷰이다.
그 뷰에서:
- 세션, 메모리, 아티팩트(파일) 은 소스 — 상호작용과 그 데이터의 전체 구조화된 상태.
- 플로우와 프로세서 는 컴파일러 파이프라인 — 그 상태를 변환하는 일련의 단계.
- 작업 컨텍스트 는 컴파일된 뷰 로, 이번 호출을 위해 LLM에 전달되는 내용.
이 사고 방식을 채택하면 컨텍스트 엔지니어링이 프롬프트 체조가 아니라 시스템 엔지니어링처럼 보이게 됩니다. 이제 표준 시스템 질문을 해야 합니다: 중간 표현은 무엇인가? 어디에서 압축을 적용할 것인가? 변환을 어떻게 관찰할 것인가?
ADK의 아키텍처는 다음 세 가지 설계 원칙을 통해 이러한 질문에 답합니다:
- 스토리지와 프레젠테이션 분리: 영구 상태(세션)와 호출별 뷰(작업 컨텍스트)를 구분합니다. 이를 통해 스토리지 스키마와 프롬프트 형식을 독립적으로 진화시킬 수 있습니다.
- 명시적 변환: 컨텍스트는 임시 문자열 연결이 아니라 이름이 붙은 순서가 있는 프로세서를 통해 구축됩니다. 이는 “컴파일” 단계가 관찰 가능하고 테스트 가능하도록 합니다.
- 기본적으로 스코프 제한: 모든 모델 호출 및 서브 에이전트는 최소한의 필요 컨텍스트만을 봅니다. 에이전트는 도구를 통해 명시적으로 더 많은 정보를 요청해야 하며, 기본적으로 과다하게 채워지지 않습니다.
ADK의 계층형 구조, 관련성 메커니즘, 다중 에이전트 핸드오프 의미론은 본질적으로 이 “컴파일러” 명제와 세 원칙을 적용한 것입니다:
- 구조 – 정보를 저장하는 방식과 모델이 보는 방식을 분리하는 계층형 모델.
- 관련성 – 현재 무엇이 중요한지를 결정하는 에이전트 및 인간 제어.
- 다중 에이전트 컨텍스트 – 에이전트 간에 올바른 컨텍스트 조각을 전달하기 위한 명시적 의미론.
다음 섹션에서는 각각의 기둥을 차례로 살펴봅니다.
1. Structure: The tiered model
대부분의 초기 에이전트 시스템은 단일 컨텍스트 윈도우를 암묵적으로 가정합니다. ADK는 반대 방향을 취합니다. 스토리지를 프레젠테이션과 분리하고 컨텍스트를 명확한 역할을 가진 별도 레이어로 조직합니다:
- 작업 컨텍스트 – 이번 모델 호출을 위한 즉시 프롬프트: 시스템 지시, 에이전트 정체성, 선택된 히스토리, 툴 출력, 선택적 메모리 결과, 그리고 아티팩트에 대한 참조.
- 세션 – 상호작용의 영구 로그: 모든 사용자 메시지, 에이전트 응답, 툴 호출, 툴 결과, 제어 신호, 오류를 구조화된
Event객체로 캡처. - 메모리 – 단일 세션을 넘어서는 검색 가능한 지식: 사용자 선호, 과거 대화 등.
- 아티팩트 – 세션이나 사용자와 연관된 대용량 바이너리·텍스트 데이터(파일, 로그, 이미지)로, 프롬프트에 붙여넣는 대신 이름과 버전으로 주소 지정.
1.1 Working context as a recomputed view
각 호출마다 ADK는 기본 상태로부터 작업 컨텍스트를 재구성합니다. 지시와 정체성으로 시작해 선택된 세션 이벤트를 끌어오고, 필요에 따라 메모리 결과를 첨부합니다. 이 뷰는 일시적(호출 후 폐기), 구성 가능(스토리지를 마이그레이션하지 않고 포맷을 변경 가능), 모델‑중립(어떤 LLM이든 사용 가능)합니다.
이 유연성은 컴파일러 뷰의 첫 번째 장점입니다: “프롬프트”를 하드코딩하는 대신, 반복 가능한 파생 표현으로 다룰 수 있게 됩니다.
1.2 Flows and processors: context processing as a pipeline
스토리지를 프레젠테이션과 분리했으면, 하나를 다른 것으로 “컴파일”할 메커니즘이 필요합니다. ADK에서는 모든 LLM 기반 에이전트가 LLM Flow에 의해 지원되며, 여기에는 순서가 지정된 프로세서 리스트가 유지됩니다.
간소화된 SingleFlow 예시는 다음과 같습니다:
# Example of a simplified SingleFlow definition
# (actual implementation omitted for brevity)
이 플로우들은 컨텍스트를 컴파일하는 ADK의 핵심 메커니즘입니다. 순서는 중요합니다: 각 프로세서는 이전 단계의 출력을 기반으로 빌드됩니다. 이를 통해 사용자 정의 필터링, 압축 전략, 캐싱, 다중 에이전트 라우팅을 자연스럽게 삽입할 수 있습니다. 이제 거대한 “프롬프트 템플릿”을 다시 쓰는 것이 아니라, 프로세서를 추가하거나 재배열하는 것만으로 충분합니다.
1.3 Session and events: structured, language‑agnostic history
ADK 세션은 대화 또는 워크플로우 인스턴스의 최종 상태를 나타냅니다. 구체적으로는 세션 메타데이터(ID, 앱 이름), 구조화된 변수용 스크래치패드, 그리고 가장 중요한 이벤트의 연대기적 리스트를 담는 컨테이너 역할을 합니다.
원시 프롬프트 문자열을 저장하는 대신, ADK는 사용자 메시지, 에이전트 응답, 툴 호출·결과, 제어 신호, 오류 등을 강력히 타입된 Event 레코드로 캡처합니다. 이 구조적 선택은 세 가지 뚜렷한 이점을 제공합니다:
- 모델 중립성: 저장 형식이 프롬프트 형식과 분리돼 있기 때문에, 기본 모델을 교체해도 히스토리를 다시 작성할 필요가 없습니다.
- 풍부한 연산: 압축, 타임 트래블 디버깅, 메모리 인게스천 같은 하위 컴포넌트가 불투명 텍스트를 파싱하는 대신 풍부한 이벤트 스트림 위에서 동작할 수 있습니다.
- 관측 가능성: 정확한 상태 전이와 행동을 검사할 수 있는 자연스러운 분석 표면을 제공합니다.
세션과 작업 컨텍스트 사이의 다리는 contents 프로세서입니다. 이 프로세서는 세션을 작업 컨텍스트의 히스토리 부분으로 변환하는 무거운 작업을 수행하며, 다음 세 가지 핵심 단계를 실행합니다:
- Selection: 이벤트 스트림을 필터링해 불필요한 항목을 제거하고, 현재 호출에 필요한 최소한의 기록만 남깁니다.
- Transformation: 선택된 이벤트를 LLM이 이해할 수 있는 텍스트 형식(예: 역할‑메시지 쌍)으로 변환합니다.
- Ordering: 최종 프롬프트에 포함될 순서를 결정해, 최신 정보가 앞쪽에 오도록 정렬합니다.
(이후 내용은 원문을 그대로 유지하거나 필요에 따라 번역을 계속 진행합니다.)