개요
AI 에이전트를 개발할 때 가장 큰 도전 과제 중 하나는 컨텍스트 관리입니다. 에이전트가 작업을 수행하기 위해 필요한 정보(기억, 지식, 기술)가 코드 곳곳에 흩어져 있고, 이들 사이의 관계와 우선순위가 불명확한 탓에 효율성이 떨어지고 문제 해결이 어려워집니다. OpenViking은 우리가 매일 컴퓨터에서 사용하는 파일 시스템 패러다임을 AI 에이전트에 적용하여 이 혼돈을 질서로 변환하는 오픈소스 컨텍스트 데이터베이스입니다. 이 교안에서는 OpenViking이 해결하는 문제, 작동 원리, 그리고 실제 성과를 학습하여 AI 에이전트의 컨텍스트 관리에 대한 새로운 관점을 얻을 수 있습니다.
배경 / 사전 지식
AI 에이전트(Agent): 주어진 목표를 달성하기 위해 자율적으로 행동하고 의사결정을 하는 AI 시스템입니다. LLM(대규모 언어 모델)을 기반으로 환경과 상호작용하며, 필요한 정보와 도구를 활용하여 작업을 완료합니다.
컨텍스트(Context): AI 에이전트가 의사결정을 내릴 때 참고하는 모든 정보를 의미합니다. 여기에는 과거 상호작용 기록(메모리), 도메인 지식, 사용 가능한 기술(스킬), 그리고 정책·제약조건 등이 포함됩니다.
토큰(Token): LLM에 입력하는 텍스트의 기본 단위입니다. 영어로는 대략 4자당 1토큰, 한국어로는 1~2자당 1토큰 정도의 비율입니다. API 사용 비용은 입출력 토큰 수에 따라 계산되므로, 필요한 정보만 효율적으로 전달하면 비용을 크게 절감할 수 있습니다.
컨텍스트 윈도우(Context Window): 한 번에 LLM에 입력할 수 있는 최대 토큰 수입니다. 일반적으로 4K~100K 토큰 범위이며, 이를 초과하면 정보를 잘라내거나 분할 처리해야 합니다.
벡터 데이터베이스(Vector Database): 정보를 수치 벡터(임베딩)로 변환하여 저장하고, 의미론적 유사성을 기반으로 빠르게 검색할 수 있는 데이터베이스입니다.
핵심 개념
컨텍스트 위기(Context Crisis)
AI 에이전트의 성능은 입력받는 컨텍스트의 질과 양에 따라 결정됩니다. 그런데 현실적으로 에이전트가 필요로 하는 정보는 다양한 곳에 분산되어 있습니다:
메모리 분산: 과거 상호작용 기록, 학습된 패턴, 사용자 선호도 등이 코드 여기저기에 흩어져 있습니다. 데이터베이스 하나, 캐시 다른 곳, 파일 시스템 또 다른 곳에 산재되어 있으면 에이전트가 모든 정보를 일관되게 활용하기 어렵습니다.
지식의 혼란: 중요한 도메인 지식과 부수적인 정보의 우선순위가 불명확합니다. 마치 책장에 중요한 참고서와 광고지가 뒤섞여 있는 것처럼, 에이전트는 필요한 정보와 불필요한 정보를 구분하기 어렵고, 때로는 잘못된 정보를 우선 순위 높게 참고하게 됩니다.
기술 단절: 사용 가능한 도구와 스킬이 체계적으로 카탈로그화되지 않으면, 에이전트는 어떤 도구가 존재하는지, 언제 사용해야 하는지 파악하기 어렵습니다.
이는 마치 정신없는 책상에 중요한 서류와 영수증이 뒤섞여 있는 것과 같습니다. 에이전트는 필요한 정보를 찾기 위해 불필요한 데이터를 모두 검토해야 하고, 결과적으로 비효율적으로 작동하게 되며, 문제가 발생했을 때 원인을 파악하기 어렵습니다.
파일 시스템 패러다임(File System Paradigm)
OpenViking의 핵심 아이디어는 컨텍스트를 파일 시스템처럼 관리하자는 것입니다. 우리가 컴퓨터에서 매일 사용하는 폴더 구조를 AI 에이전트의 정보 관리에 적용합니다:
계층적 구조: 정보를 폴더와 파일로 체계적으로 조직화합니다. 예를 들어 contexts/memory/recent_interactions/ 형태로 원하는 정보에 쉽게 접근할 수 있습니다.
명확한 위치: 각 정보가 어디에 있는지 명확해집니다. "메모리는 어디에 저장되어 있지?"라는 의문이 생기지 않습니다.
쉬운 탐색: 폴더를 열듯이 필요한 정보 영역에 접근합니다. 깊은 서치를 하기 전에 대략 어느 폴더를 봐야 할지 알 수 있습니다.
확장성: 새로운 정보를 쉽게 추가할 수 있습니다. 파일 시스템은 새 파일을 언제든지 추가할 수 있는 것처럼, 에이전트도 새로운 컨텍스트 영역을 동적으로 추가할 수 있습니다.
이렇게 하면 에이전트는 필요한 정보를 빠르고 정확하게 찾을 수 있으며, 개발자도 전체 구조를 한눈에 파악하고 관리할 수 있습니다. 복잡한 데이터베이스 스키마를 배울 필요도 없습니다. 모두가 이미 파일 시스템을 알고 있으니까요.
계층적 로딩(Hierarchical Loading)
모든 정보를 한 번에 LLM에 입력하면 토큰 비용이 급증합니다. OpenViking은 필요에 따라 단계적으로 정보를 로드하는 전략을 사용합니다:
첫 번째 레이어 - Title (한 줄 요약): 가장 간결한 정보만 제공합니다. 예: "사용자는 최근 3개의 거래를 수행했다." 이 수준의 정보는 보통 수십 개의 토큰으로 충분합니다.
두 번째 레이어 - Summary (핵심 개요): 약간 더 자세한 설명을 추가합니다. 예: "사용자의 최근 3개 거래: 1) 음식 주문 2) 영화 표 구매 3) 기부." 수백 개의 토큰이 필요할 수 있습니다.
세 번째 레이어 - Full Content (상세 원본 데이터): 필요할 때만 전체 데이터를 로드합니다. 예: 각 거래의 상세 내역, 시간, 금액, 관련 통신 기록 등. 수천 개의 토큰이 필요할 수 있습니다.
마치 책을 읽을 때 목차→약력→본문 순서로 진행하듯이, 에이전트도 필요한 수준의 정보부터 시작해서 추가 정보가 필요할 때만 더 깊이 파고듭니다. 대부분의 상황에서 첫 번째나 두 번째 레이어로 충분하기 때문에 전체 토큰 사용량이 크게 줄어듭니다.
작동 원리
OpenViking은 다음 5가지 핵심 기술을 통해 컨텍스트 관리를 혁신합니다:
1단계: 통합 관리(Unified Management)
기억, 지식, 기술을 모두 같은 파일 시스템 구조 안에서 관리합니다. 다양한 저장소를 통합하면:
- 정보 분산으로 인한 혼란이 제거됩니다
- 각 영역 간의 관계를 정의하고 추적할 수 있습니다
- 전체 컨텍스트 구조를 한눈에 파악할 수 있습니다
2단계: 계층적 로딩(Hierarchical Loading)
한 줄 요약 → 핵심 개요 → 상세 데이터 순서로 필요한 정보만 단계적으로 로드합니다:
- 대부분의 쿼리는 첫 번째 또는 두 번째 레이어로 처리 가능
- 특수한 경우에만 상세 데이터를 로드
- 토큰 비용을 획기적으로 절감
3단계: 스마트 검색(Intelligent Retrieval)
벡터 데이터베이스와 의미론적 유사성을 활용합니다:
- 키워드 검색이 아니라 의도를 이해하는 검색
- "최근 구매 기록"을 검색하면 관련된 모든 정보를 찾음
- 정확한 정보만 필요한 만큼 검색되므로 노이즈 제거
4단계: 투명성(Transparency)
에이전트의 모든 컨텍스트 활용 과정을 추적·기록할 수 있습니다:
- 어떤 정보를 언제 불러왔는가
- 어떤 의사결정에 영향을 미쳤는가
- 결과는 어땠는가
- 문제 발생 시 원인 파악이 용이
블랙박스였던 에이전트 작동이 투명해져서 개발자가 신뢰하고 개선할 수 있습니다.
5단계: 자가 진화(Self-Evolution)
에이전트가 작업을 반복하면서 스스로 학습하고 개선합니다:
- 자주 사용되는 정보는 상위 계층으로 승격
- 거의 사용되지 않는 정보는 하위 계층으로 강등
- 새로운 패턴이 발견되면 새 카테고리 추가
- 초기 설정이 완벽하지 않아도 시간이 지날수록 최적화됨
코드 예시
파일 시스템 기반 컨텍스트 구조 (파이썬 예시)
# OpenViking의 계층적 로딩 개념을 나타낸 예시 코드
class HierarchicalContext:
"""AI 에이전트의 계층적 컨텍스트 관리 시스템"""
def __init__(self):
# 파일 시스템처럼 계층 구조를 정의
self.contexts = {
"memory": {
# 첫 번째 레이어: 한 줄 요약
"title": "Agent의 과거 5개 상호작용 요약",
# 두 번째 레이어: 핵심 개요
"summary": ["거래 1: 사용자가 제품 검색", "거래 2: 장바구니 추가", "거래 3: 결제 완료"],
# 세 번째 레이어: 상세 데이터
"detailed": {
"interaction_1": {"time": "2024-01-15 10:30", "action": "search", "query": "laptop"},
"interaction_2": {"time": "2024-01-15 10:45", "action": "add_to_cart", "item": "laptop_pro"},
"interaction_3": {"time": "2024-01-15 11:00", "action": "checkout", "amount": 1500}
},
# 각 레이어의 토큰 비용
"tokens": {"title": 20, "summary": 80, "detailed": 500}
},
"knowledge": {
"title": "도메인 지식 영역 목록",
"summary": ["전자제품", "패션", "음식"],
"detailed": {
"electronics": "모든 전자제품 관련 정보...",
"fashion": "패션 아이템 정보...",
"food": "음식 및 음료 정보..."
},
"tokens": {"title": 15, "summary": 120, "detailed": 2000}
},
"skills": {
"title": "사용 가능한 도구 목록",
"summary": ["product_search", "add_to_cart", "process_payment"],
"detailed": {
"product_search": {"description": "제품 검색 도구", "params": ["query", "category"]},
"add_to_cart": {"description": "장바구니 추가", "params": ["item_id", "quantity"]},
"process_payment": {"description": "결제 처리", "params": ["amount", "method"]}
},
"tokens": {"title": 10, "summary": 60, "detailed": 300}
}
}
def load_context(self, domain, depth="title"):
"""
필요한 영역의 정보를 단계적으로 로드
Args:
domain: "memory", "knowledge", "skills" 중 하나
depth: "title" (한 줄) → "summary" (개요) → "detailed" (전체)
Returns:
요청한 수준의 정보
"""
if depth == "title":
# 가장 간단한 형태만 반환 (토큰 최소화)
return self.contexts[domain]["title"]
elif depth == "summary":
# 요약 + 제목 반환
return {
"title": self.contexts[domain]["title"],
"content": self.contexts[domain]["summary"]
}
else: # detailed
# 전체 상세 정보 반환 (필요할 때만)
return {
"title": self.contexts[domain]["title"],
"summary": self.contexts[domain]["summary"],
"content": self.contexts[domain]["detailed"]
}
def calculate_cost(self, domain, depth):
"""로드할 때 드는 토큰 비용 계산"""
if depth == "title":
return self.contexts[domain]["tokens"]["title"]
elif depth == "summary":
return self.contexts[domain]["tokens"]["title"] + self.contexts[domain]["tokens"]["summary"]
else: # detailed
return sum(self.contexts[domain]["tokens"].values())
# 사용 예시
context_mgr = HierarchicalContext()
# 예시 1: 메모리 정보 필요 - 먼저 제목만 로드
print("=== 1단계: 제목 로드 ===")
title = context_mgr.load_context("memory", "title")
print(f"정보: {title}")
print(f"토큰 비용: {context_mgr.calculate_cost('memory', 'title')} tokens\n")
# 예시 2: 더 많은 정보 필요 - 요약 추가 로드
print("=== 2단계: 요약 추가 로드 ===")
summary_data = context_mgr.load_context("memory", "summary")
print(f"정보: {summary_data}")
print(f"토큰 비용: {context_mgr.calculate_cost('memory', 'summary')} tokens\n")
# 예시 3: 완전한 정보 필요 - 상세 데이터 로드
print("=== 3단계: 상세 데이터 로드 ===")
full_data = context_mgr.load_context("memory", "detailed")
print(f"상호작용 개수: {len(full_data['content'])}")
print(f"토큰 비용: {context_mgr.calculate_cost('memory', 'detailed')} tokens\n")
# 비용 비교
print("=== 비용 효율성 비교 ===")
title_cost = context_mgr.calculate_cost('memory', 'title')
summary_cost = context_mgr.calculate_cost('memory', 'summary')
detailed_cost = context_mgr.calculate_cost('memory', 'detailed')
print(f"제목 레이어: {title_cost} tokens")
print(f"요약 레이어: {summary_cost} tokens ({summary_cost/detailed_cost*100:.1f}% of full)")
print(f"전체 레이어: {detailed_cost} tokens")
print(f"절감율: 제목만 사용 시 {(1 - title_cost/detailed_cost)*100:.1f}% 절감")
코드 설명:
-
contexts딕셔너리: 파일 시스템의 폴더 구조처럼 정보를 조직화합니다.memory,knowledge,skills각각이 하나의 영역(폴더)입니다. -
load_context()메서드: 깊이(depth) 파라미터에 따라 점진적으로 정보를 로드합니다. 이것이 계층적 로딩의 핵심입니다. 필요한 수준의 정보만 반환하여 불필요한 토큰 사용을 방지합니다. -
calculate_cost()메서드: 각 단계에서 소비하는 토큰 수를 계산합니다. 예제에서 제목만 로드할 때는 20토큰이지만, 전체 로드 시 500토큰이 필요합니다. 계층별로 비용을 누적하여 계산합니다. -
사용 예시: 대부분의 상황에서 제목(20토큰)으로 충분하고, 필요한 경우에만 요약(80토큰)이나 상세(500토큰)를 추가로 불러옵니다. 이렇게 설계하면 평균적으로 토큰 비용이 대폭 줄어듭니다.
이 구조가 실제로 OpenClaw 테스트에서 입력 토큰 비용을 91% 절감한 핵심 메커니즘입니다.
함정·실수
1. 모든 정보를 처음부터 로드하기
실수: "포괄적이어야 한다"고 생각해서 에이전트 초기화 시 모든 컨텍스트를 한 번에 입력하려는 경우가 많습니다.
문제점:
- 토큰 비용이 선형적으로 증가합니다
- 불필요한 정보가 에이전트의 집중력을 분산시킵니다
- 컨텍스트 윈도우를 초과할 가능성이 높아집니다
- 비용이 선형으로 증가하면 대규모 에이전트 배포가 경제적으로 불가능
해결책: OpenViking의 계층적 로딩을 따르세요. 제목부터 시작해서 필요할 때만 추가 정보를 로드합니다. 대부분의 쿼리는 첫 두 계층으로 처리 가능합니다.
2. 정보 구조를 잘못 설계하기
실수: 파일 시스템 구조를 너무 복잡하게 만들거나, 반대로 너무 평탄하게 설계하는 경우입니다.
문제점:
- 깊은 구조: 원하는 정보를 찾기 위해 여러 단계를 거쳐야 하고, 스마트 검색이 효율적으로 작동하지 않음
- 평탄한 구조: 많은 파일이 한 디렉토리에 섞여 혼란 유발, 의미론적 검색의 정확도 저하
해결책: 3~4단계의 균형잡힌 계층 구조를 설계하세요. 각 폴더는 5~10개의 항목을 포함하는 것을 목표로 합니다. 예: contexts/memory/recent/ 또는 contexts/knowledge/domain/subcategory/
3. 계층마다 정보 중복
실수: 계층 간에 동일한 정보를 반복적으로 저장하는 경우입니다. 예를 들어, 상세 데이터의 전체 내용을 요약 계층에도 포함시키는 것.
문제점:
- 저장 공간 낭비
- 정보 업데이트 시 여러 곳을 수정해야 함
- 계층 간 불일치로 인한 버그 발생
- 토큰 비용 절감 효과 감소
해결책: 각 계층은 해당 수준에서만 필요한 정보만 담도록 설계합니다. 상세 정보는 한 곳에만 저장하고, 상위 계층은 참조(포인터) 또는 요약만 포함합니다.
4. 검색 기능을 무시하기
실수: 정보를 잘 정리했지만, 에이전트가 필요한 정보를 찾지 못하는 경우입니다.
문제점: 파일 시스템이 있어도 검색 기능이 없으면 큰 폴더에서 원하는 파일을 찾기 어렵습니다. 특히 수천 개의 정보가 있을 때는 거의 불가능합니다.
해결책: 벡터 데이터베이스를 활용한 의미론적 검색(semantic search)을 구현하세요. 키워드 검색뿐만 아니라 의도를 이해하는 검색이 가능해집니다. 예: "사용자 구매 기록"을 검색하면 "거래", "장바구니", "결제" 등 관련된 모든 정보를 찾습니다.
5. 비용 모니터링 부재
실수: 각 쿼리마다 몇 개의 토큰을 사용하고 있는지 추적하지 않는 경우입니다.
문제점: 어느 컨텍스트가 비용을 가장 많이 차지하는지 알 수 없어, 최적화할 기회를 놓칩니다.
해결책: 모든 컨텍스트 로드를 기록하고 비용을 추적하세요. 월 1000만 토큰 사용 중 700만 토큰이 특정 영역에서만 사용된다면, 그 영역의 계층 구조를 더 세분화하는 것이 효과적입니다.
베스트 프랙티스
1. 계층적 로딩 설계의 3가지 규칙
각 계층의 목적을 명확히 하고 일관되게 설계하세요:
-
Title/한 줄 요약: 핵심만 한두 문장으로 표현 (15~50 토큰)
- 예: "사용자의 최근 3개 거래"
-
Summary/개요: 주요 포인트 3~5개와 구조 설명 (100~300 토큰)
- 예: ["거래1: 검색", "거래2: 추가", "거래3: 결제"]
-
Full Content/상세: 모든 세부사항, 타임스탬프, 메타데이터, 예시 (1000 토큰 이상)
- 예: 각 거래의 전체 기록, 사용자 행동 패턴 분석
2. 정보 영역 분리와 연결
메모리, 지식, 기술을 명확히 분리하되, 서로 연결될 수 있도록 설계합니다:
contexts/
├── memory/ (과거 상호작용)
│ ├── recent/ (최근 상호작용)
│ └── patterns/ (패턴 분석)
├── knowledge/ (도메인 지식)
│ ├── products/
│ └── users/
├── skills/ (사용 가능한 도구)
│ ├── search/
│ └── transaction/
└── relationships/ (영역 간 연결)
└── user_interests/ (사용자의 관심 + 제품 지식)
각 영역은 독립적이지만, relationships/ 폴더에서 서로 연결됩니다.
3. 자가 진화 구조 마련
에이전트의 학습을 추적하고 피드백을 컨텍스트 구조에 반영하는 메커니즘을 만듭니다:
- 사용 빈도 기록: 각 정보가 얼마나 자주 조회되는지 추적
- 정확도 측정: 해당 정보가 올바른 의사결정으로 이어졌는지 기록
- 동적 계층화: 자주 사용되는 정보는 상위 계층으로 승격, 거의 사용되지 않는 정보는 하위 계층으로 강등
- 새 카테고리 생성: 새로운 패턴이 발견되면 새로운 폴더 구조 자동 생성
예를 들어, 초기에는 "사용자 거래"가 모두 memory/ 폴더에 있지만, 시간이 지나면서 "구매 거래"와 "조회 거래"의 빈도가 다르면 자동으로 분리 폴더를 생성합니다.
4. 비용 모니터링과 최적화
각 쿼리마다 사용한 토큰을 기록하고, 어느 컨텍스트가 비용을 가장 많이 차지하는지 분석합니다:
# 예시: 월별 토큰 사용량 분석
monthly_usage = {
"memory": 3_000_000, # 30%
"knowledge": 5_000_000, # 50%
"skills": 2_000_000 # 20%
}
만약 knowledge가 50%를 차지한다면, 그 영역의 계층을 더 세분화하거나 요약을 더 압축하는 것이 효과적입니다.
5. 투명성 로깅
에이전트의 모든 컨텍스트 활용을 기록하세요:
# 예시: 컨텍스트 활용 로그
{
"timestamp": "2024-01-15 10:30:00",
"context_loaded": "memory/recent",
"depth": "summary",
"tokens_used": 80,
"decision_made": "search for laptop",
"outcome": "success"
}
이를 통해:
- 문제 발생 시 정확한 원인 추적 가능
- 어떤 정보가 어떤 의사결정에 영향을 미쳤는지 분석 가능
- 컨텍스트 구조의 개선점 발견 가능
- 에이전트의 신뢰도 증가
6. 정보 신선도 관리
컨텍스트도 시간이 지나면 낡습니다. 특히 메모리는 오래된 정보를 점진적으로 제거하고, 지식은 최신 정보로 업데이트해야 합니다:
- 메모리 감쇠: 오래된 상호작용은 시간에 따라 자동 삭제
- 지식 갱신: 주기적으로 최신 정보로 업데이트
- 기술 점검: 새로운 도구나 API는 추가, 더 이상 지원하지 않는 것은 제거
참고
공식 리소스:
- GitHub 저장소: https://github.com/volcengine/OpenViking
- 제작사: ByteDance의 Volcengine
원본 영상에서 언급된 핵심 성과:
- OpenClaw AI 에이전트와의 협력 테스트
- 입력 토큰 비용 91% 감소 달성
- 비용 절감 동시에 과제 완료율 최고 수준 달성
- AI 모델의 다음 도약에 대한 철학적 관점: "더 크고 강력한 모델보다 더 잘 정리하고 체계적으로 생각하는 법"
추가 학습 주제:
- 벡터 데이터베이스와 임베딩(Vector Embeddings) — 의미론적 검색의 기초
- 의미론적 검색(Semantic Search) — 키워드가 아닌 의도 기반 검색
- LLM 토큰 비용 최적화 기법 — 프롬프트 엔지니어링, 캐싱 등
- AI 에이전트의 메모리 시스템 설계 — RAG(Retrieval-Augmented Generation) 등
- 컨텍스트 윈도우 확장의 트렌드와 한계