Agent Skills for Context Engineering - 오픈소스 완전 분석
GitHub 오픈소스 레포지토리 "Agent Skills for Context Engineering"을 완벽하게 분석하는 코스입니다. 9개 핵심 스킬과 2개 실전 예제를 통해 프로덕션급 AI 에이전트 시스템 구축 방법을 학습합니다. 소스 코드를 직접 읽으며 동작 원리를 이해합니다.
학습 항목
본 콘텐츠의 이미지 및 내용은 AI로 생성되었습니다.
이미지 로딩 중...
Context Fundamentals 완벽 가이드 AI 에이전트 맥락 이해하기
AI 에이전트 시스템에서 Context(맥락)가 무엇인지, 어떻게 구성되고, 왜 효율적인 관리가 중요한지 알아봅니다. Attention Budget, Progressive Disclosure 원칙을 통해 실무에서 활용할 수 있는 맥락 엔지니어링 기법을 배웁니다.
목차
- Context Anatomy 이해하기
- Attention Budget 제약
- Progressive Disclosure 원칙
- Quality Over Quantity
- 시스템 프롬프트 설계
- 적절한 통화 및 세금 고려사항으로 형식화
- 도구 정의 최적화
1. Context Anatomy 이해하기
어느 날 김개발 씨는 회사에서 AI 에이전트 프로젝트를 맡게 되었습니다. "에이전트한테 뭘 넣어줘야 제대로 동작하지?" 박시니어 씨가 다가와 한마디 던집니다.
"Context를 제대로 설계해야 해. 그게 에이전트의 전부라고 할 수 있거든."
Context란 언어 모델이 추론할 때 참조할 수 있는 모든 정보의 집합입니다. 마치 시험을 볼 때 책상 위에 펼쳐놓은 모든 참고자료와 같습니다.
시스템 프롬프트, 도구 정의, 메시지 히스토리까지 모델이 "볼 수 있는" 모든 것이 Context에 해당합니다.
다음 코드를 살펴봅시다.
# Context의 주요 구성 요소
context_anatomy = {
# 1. 시스템 프롬프트: 에이전트의 정체성과 규칙
"system_prompt": """
<BACKGROUND_INFORMATION>
당신은 Python 전문가입니다.
현재 프로젝트: 데이터 처리 파이프라인
</BACKGROUND_INFORMATION>
<INSTRUCTIONS>
- 깔끔하고 관용적인 Python 코드 작성
- 함수 시그니처에 타입 힌트 포함
</INSTRUCTIONS>
""",
# 2. 도구 정의: 에이전트가 할 수 있는 행동
"tools": ["search_database", "read_file", "execute_code"],
# 3. 메시지 히스토리: 대화 기록
"messages": [{"role": "user", "content": "API 만들어줘"}]
}
김개발 씨는 입사 3개월 차 주니어 개발자입니다. AI 스타트업에서 일하는 그는 최근 에이전트 시스템 개발을 담당하게 되었습니다.
처음에는 단순히 프롬프트만 잘 작성하면 될 줄 알았는데, 현실은 녹록지 않았습니다. "왜 에이전트가 자꾸 엉뚱한 도구를 호출하지?" 김개발 씨가 고민하고 있을 때, 박시니어 씨가 다가왔습니다.
"Context 구조부터 이해해야 해. 에이전트는 Context에 있는 것만 볼 수 있거든." 그렇다면 Context란 정확히 무엇일까요?
쉽게 비유하자면, Context는 마치 수험생이 시험장에서 볼 수 있는 모든 자료와 같습니다. 교과서, 노트, 공식집, 그리고 시험지 자체까지.
수험생은 오직 책상 위에 있는 자료만 참고할 수 있듯이, AI 모델도 Context 안에 있는 정보만 참조할 수 있습니다. Context는 크게 다섯 가지 요소로 구성됩니다.
첫째, 시스템 프롬프트입니다. 이것은 에이전트의 정체성과 핵심 규칙을 정의합니다.
"당신은 Python 전문가입니다"처럼 역할을 부여하고, 어떻게 행동해야 하는지 가이드라인을 제공합니다. 세션 시작 시 한 번 로드되어 대화 전반에 걸쳐 유지됩니다.
둘째, 도구 정의입니다. 에이전트가 수행할 수 있는 행동을 명시합니다.
파일 읽기, 데이터베이스 검색, 코드 실행 등 각 도구의 이름, 설명, 파라미터, 반환 형식이 포함됩니다. 셋째, 검색된 문서입니다.
RAG를 통해 실시간으로 가져온 도메인 지식이나 참고 자료가 여기에 해당합니다. 넷째, 메시지 히스토리입니다.
사용자와 에이전트 간의 대화 기록입니다. 이전 질문, 응답, 추론 과정이 모두 포함됩니다.
다섯째, 도구 출력입니다. 에이전트가 도구를 사용한 결과물입니다.
파일 내용, 검색 결과, API 응답 등이 여기에 쌓입니다. 위의 코드를 보면 Context가 어떻게 구조화되는지 알 수 있습니다.
system_prompt에는 XML 태그로 구분된 섹션들이 있습니다. BACKGROUND_INFORMATION에는 배경 정보가, INSTRUCTIONS에는 지시사항이 담깁니다.
이렇게 명확하게 구분해두면 에이전트가 필요한 정보를 빠르게 찾을 수 있습니다. 실무에서 가장 중요한 것은 적절한 추상화 수준입니다.
너무 세부적인 지시는 취약하고 유지보수하기 어렵습니다. 반대로 너무 추상적인 지시는 구체적인 행동을 유도하지 못합니다.
"가격 문의에 대응하라"보다는 "가격 문의 시 docs/pricing.md를 참조하고, 위치에 따른 조정을 적용하라"가 더 효과적입니다. 박시니어 씨의 설명을 들은 김개발 씨는 고개를 끄덕였습니다.
"아, Context가 에이전트의 시야 범위인 거군요!" Context를 제대로 이해하면 왜 에이전트가 특정 방식으로 행동하는지, 왜 때로는 실패하는지 파악할 수 있습니다. 이것이 Context Engineering의 시작점입니다.
실전 팁
💡 - 시스템 프롬프트는 XML 태그나 마크다운 헤더로 섹션을 명확히 구분하세요
- 도구 설명에는 "언제 사용하는지"를 반드시 포함하세요
2. Attention Budget 제약
"이상하다, 분명히 앞에서 알려줬는데 왜 기억을 못 하지?" 김개발 씨가 긴 대화 끝에 에이전트가 초반 내용을 잊어버린 것을 발견했습니다. 박시니어 씨가 말합니다.
"Attention Budget이 고갈된 거야. 연구에 따르면 도구 출력이 전체 Context의 83.9%를 차지할 수 있거든."
Attention Budget은 언어 모델이 Context 내 토큰들 간의 관계를 처리할 수 있는 한정된 용량입니다. 마치 사람의 작업 기억에 한계가 있듯이, 모델도 Context가 길어질수록 정보 처리 능력이 저하됩니다.
특히 도구 출력이 Context의 대부분을 차지하면서 이 제약이 더욱 심해집니다.
다음 코드를 살펴봅시다.
# Attention Budget 이해: n개 토큰 = n² 관계 연산
def estimate_attention_cost(token_count):
"""토큰 수에 따른 연산 복잡도 추정"""
# Attention은 O(n²) 복잡도를 가짐
return token_count ** 2
# 실제 에이전트 세션에서의 Context 사용 비율
context_usage = {
"system_prompt": 0.05, # 5%: 안정적
"tool_definitions": 0.03, # 3%: 도구 개수에 비례
"message_history": 0.08, # 8%: 대화가 길어지면 증가
"tool_outputs": 0.839, # 83.9%: 관찰 결과가 지배
"remaining": 0.001 # 0.1%: 여유 공간
}
# 도구 출력이 Context를 지배하는 이유
print(f"도구 출력 비율: {context_usage['tool_outputs']*100}%")
김개발 씨는 에이전트와 긴 대화를 나누고 있었습니다. 처음에는 완벽하게 맥락을 이해하던 에이전트가 어느 순간부터 앞서 설명한 내용을 잊어버리기 시작했습니다.
"분명히 처음에 API 스펙을 알려줬는데, 왜 다시 물어보지?" 박시니어 씨가 모니터를 들여다보며 말했습니다. "Context가 너무 길어졌어.
Attention Budget이 바닥났거든." Attention Budget이란 무엇일까요? 언어 모델은 Attention 메커니즘을 통해 Context 내의 토큰들을 처리합니다.
문제는 n개의 토큰이 있으면 n x n, 즉 n제곱 개의 관계를 계산해야 한다는 것입니다. 토큰이 1000개면 백만 개의 관계를, 10000개면 1억 개의 관계를 처리해야 합니다.
마치 사람의 작업 기억에 한계가 있듯이, 모델에도 주의력 예산이 있습니다. Context가 길어질수록 이 예산이 고갈되고, 정보 검색과 장거리 추론 능력이 저하됩니다.
그런데 왜 하필 도구 출력이 83.9%나 차지할까요? 에이전트가 작업을 수행할 때 다양한 도구를 사용합니다.
파일을 읽으면 그 내용이 Context에 들어가고, 검색을 하면 결과가 들어가고, API를 호출하면 응답이 들어갑니다. 이런 관찰 결과들이 쌓이고 쌓여서 Context의 대부분을 차지하게 됩니다.
위 코드의 context_usage를 보면 실제 비율을 확인할 수 있습니다. 시스템 프롬프트는 5%, 도구 정의는 3%, 메시지 히스토리는 8%에 불과합니다.
반면 도구 출력은 무려 83.9%입니다. 에이전트의 "눈"이 되는 관찰 결과가 Context를 지배하는 것입니다.
이것이 왜 문제일까요? 도구 출력은 현재 결정과 관련 없는 정보도 포함합니다.
어제 읽었던 파일 내용, 세 턴 전에 했던 검색 결과가 여전히 Context에 남아있습니다. 관련 없는 토큰들이 Attention Budget을 소모하면서 정작 중요한 정보에 대한 주의력이 분산됩니다.
실제 프로젝트에서는 어떻게 대응할까요? Observation Masking이라는 기법이 있습니다.
긴 도구 출력을 압축된 참조로 대체하는 것입니다. "이전 관찰 생략됨.
전체 내용은 reference_123에 저장됨"처럼 처리하면 토큰 사용량을 크게 줄일 수 있습니다. 또한 선택적 보존 전략도 있습니다.
모든 도구 출력을 유지하는 것이 아니라, 현재 작업에 관련된 결과만 Context에 남기는 것입니다. 김개발 씨는 자신의 에이전트에 observation masking을 적용했습니다.
긴 파일 내용은 요약본으로 대체하고, 오래된 검색 결과는 자동으로 제거되도록 설정했습니다. 그러자 에이전트가 훨씬 안정적으로 맥락을 유지하기 시작했습니다.
"Attention Budget이 유한하다는 걸 알면, Context를 어떻게 관리해야 할지 방향이 보이는군요!"
실전 팁
💡 - Context 사용량을 개발 중에 모니터링하세요
- 70-80% 사용률에서 압축 트리거를 구현하세요
3. Progressive Disclosure 원칙
"모든 문서를 미리 다 넣어두면 편하지 않을까요?" 김개발 씨가 제안했습니다. 박시니어 씨가 고개를 저으며 말합니다.
"그건 마치 도서관의 모든 책을 책상 위에 쌓아두는 것과 같아. 필요할 때 필요한 것만 가져오는 게 핵심이야."
Progressive Disclosure는 정보를 필요할 때만 로드하는 원칙입니다. 마치 도서관에서 책 목록만 보고 있다가 필요한 책만 대출하는 것과 같습니다.
처음에는 스킬 이름과 설명만 로드하고, 실제로 활성화될 때 전체 내용을 로드합니다.
다음 코드를 살펴봅시다.
# Progressive Disclosure 구현 패턴
def load_skill_progressively(skill_name, task_description):
"""필요할 때만 스킬 컨텍스트를 로드"""
# Step 1: 메타데이터만 로드 (가벼움)
all_skills = load_all_skill_metadata()
# 예: [{"name": "python-expert", "desc": "Python 코딩 전문가"}]
# Step 2: 관련 스킬 필터링
relevant_skills = []
for skill in all_skills:
if skill_matches_task(skill, task_description):
relevant_skills.append(skill)
# Step 3: 필요한 스킬만 전체 로드 (무거움)
MAX_CONCURRENT_SKILLS = 3
for skill in relevant_skills[:MAX_CONCURRENT_SKILLS]:
full_content = load_skill_content(skill)
inject_into_context(full_content)
return relevant_skills
# 참조 로드 패턴
class LazyReference:
def __init__(self, path):
self.path = path
self.content = None
self.is_loaded = False
def get(self):
"""처음 접근할 때만 파일을 읽음"""
if not self.is_loaded:
self.content = read_file(self.path)
self.is_loaded = True
return self.content
김개발 씨는 에이전트 시스템을 개선하려고 했습니다. "회사의 모든 문서를 Context에 넣어두면 에이전트가 뭐든 답할 수 있지 않을까요?" 박시니어 씨가 웃으며 비유를 들었습니다.
"네가 시험을 보는데, 도서관의 모든 책을 책상 위에 쌓아둔다고 상상해봐. 필요한 내용을 찾는 게 더 어려워지지 않을까?" Progressive Disclosure란 정확히 무엇일까요?
이 원칙은 정보를 단계적으로 공개한다는 뜻입니다. 처음에는 목록이나 요약만 보여주고, 사용자가 선택하면 상세 내용을 보여주는 UI 패턴과 같은 원리입니다.
에이전트 시스템에서는 이렇게 작동합니다. 시작할 때는 스킬의 이름과 간단한 설명만 로드합니다.
"python-expert: Python 코딩 전문가"처럼 가벼운 메타데이터만 Context에 넣습니다. 그러다 실제로 Python 관련 작업이 필요해지면, 그때 해당 스킬의 전체 내용을 로드합니다.
위 코드의 load_skill_progressively 함수를 보면 이 과정이 명확합니다. 먼저 모든 스킬의 메타데이터를 가져옵니다.
이건 가볍습니다. 그 다음 현재 작업에 맞는 스킬만 필터링합니다.
마지막으로 선택된 스킬의 전체 내용을 로드합니다. 최대 3개까지만 동시에 로드하도록 제한을 두어 Context가 폭발하는 것을 방지합니다.
LazyReference 클래스는 또 다른 중요한 패턴을 보여줍니다. 파일 경로만 가지고 있다가, 실제로 get()이 호출될 때 처음으로 파일을 읽습니다.
한 번 읽으면 캐시해두어 중복 읽기를 방지합니다. 이것이 바로 Lazy Loading입니다.
왜 이 원칙이 중요할까요? 첫째, Context 용량을 절약합니다.
필요 없는 정보로 Attention Budget을 낭비하지 않습니다. 둘째, 에이전트 속도가 빨라집니다.
Context가 작으면 처리 시간도 짧아집니다. 셋째, 정보의 신선도를 유지합니다.
미리 로드해두면 시간이 지나 정보가 오래될 수 있지만, Just-in-time으로 로드하면 항상 최신 정보를 사용합니다. 실무에서는 어떻게 적용할까요?
파일 시스템을 활용하면 자연스럽게 Progressive Disclosure를 구현할 수 있습니다. 참고 자료, 문서, 데이터를 외부에 저장해두고, 필요할 때 파일 시스템 작업으로 읽어옵니다.
파일 이름과 크기 같은 메타데이터가 힌트가 되어 어떤 파일을 읽어야 할지 판단할 수 있습니다. 김개발 씨는 자신의 시스템을 재설계했습니다.
모든 문서를 Context에 미리 넣는 대신, 문서 목록만 제공하고 에이전트가 필요에 따라 읽도록 했습니다. 결과적으로 응답 속도가 3배 빨라졌고, 정확도도 올라갔습니다.
"필요할 때 필요한 것만. 정보도 미니멀리즘이 필요하군요!"
실전 팁
💡 - 스킬이나 문서는 메타데이터만 먼저 로드하고, 전체는 나중에 로드하세요
- 파일 시스템을 활용해 자연스러운 Progressive Disclosure를 구현하세요
4. Quality Over Quantity
"요즘 모델은 Context Window가 100만 토큰이라던데, 그냥 다 넣으면 되지 않나요?" 김개발 씨가 물었습니다. 박시니어 씨가 진지하게 대답합니다.
"큰 창문이 있다고 방 안의 모든 것이 잘 보이는 건 아니야. 오히려 더 중요한 것에 집중하기 어려워질 수 있어."
Quality Over Quantity는 더 큰 Context Window가 더 나은 결과를 보장하지 않는다는 원칙입니다. 핵심은 가능한 가장 작은 고신호 토큰 집합을 찾는 것입니다.
대형 Context는 처리 비용이 기하급수적으로 증가하고, 특정 길이 이상에서 성능이 저하되는 한계가 있습니다.
다음 코드를 살펴봅시다.
# Context 품질 vs 양 비교
def compare_context_strategies():
"""두 가지 Context 전략 비교"""
# 전략 A: 모든 정보를 다 넣기 (Quantity)
strategy_a = {
"tokens": 100000,
"signal_ratio": 0.10, # 10%만 유용한 정보
"processing_cost": 100000 ** 2, # 100억 연산
"accuracy": 0.72, # 정확도 72%
}
# 전략 B: 핵심 정보만 선별 (Quality)
strategy_b = {
"tokens": 10000,
"signal_ratio": 0.85, # 85%가 유용한 정보
"processing_cost": 10000 ** 2, # 1억 연산
"accuracy": 0.91, # 정확도 91%
}
# 결과: 토큰 10분의 1, 비용 100분의 1, 정확도 향상
print(f"전략 B가 {strategy_a['processing_cost'] // strategy_b['processing_cost']}배 효율적")
return strategy_b
# Context 예산 할당 가이드
context_budget = {
"system_prompt": "500-2000 토큰 | 세션 전반에 안정적",
"tool_definitions": "도구당 100-500 토큰 | 도구 수에 비례",
"retrieved_docs": "가변적 | 종종 가장 큰 소비자",
"message_history": "가변적 | 대화 길이에 비례",
"tool_outputs": "가변적 | Context를 지배할 수 있음"
}
김개발 씨는 최신 모델의 스펙을 보고 흥분했습니다. "100만 토큰 Context Window라니!
이제 걱정 없겠네요." 박시니어 씨가 고개를 저었습니다. "그건 마치 100미터 책장이 있으니까 책을 정리 안 해도 된다고 생각하는 것과 같아.
오히려 더 혼란스러워질 수 있어." 왜 큰 Context Window가 만능이 아닐까요? 첫째, 처리 비용이 기하급수적으로 증가합니다.
Attention은 O(n²) 복잡도를 가집니다. 토큰이 2배가 되면 비용은 4배, 10배가 되면 100배가 됩니다.
위 코드의 비교를 보면 10만 토큰과 1만 토큰의 처리 비용 차이가 100배입니다. 둘째, 특정 길이 이상에서 성능이 저하됩니다.
모델은 주로 짧은 시퀀스로 훈련되었습니다. 긴 Context에 대한 경험이 적기 때문에, 기술적으로는 처리 가능해도 정확도가 떨어집니다.
셋째, 긴 입력은 prefix caching을 해도 비쌉니다. 캐싱이 도움이 되긴 하지만, 근본적인 비용 문제를 해결하지는 못합니다.
그렇다면 핵심은 무엇일까요? 가장 작은 고신호 토큰 집합을 찾는 것입니다.
영어로는 "the smallest possible set of high-signal tokens"입니다. 현재 결정에 필요한 정보만 포함하고, 필요 없는 정보는 제외하며, 추가 정보가 필요하면 그때 접근할 수 있도록 설계합니다.
위 코드의 strategy_a와 strategy_b를 비교해보세요. Strategy A는 10만 토큰을 사용하지만 유용한 정보는 10%뿐입니다.
Strategy B는 1만 토큰만 사용하지만 85%가 유용한 정보입니다. 결과적으로 Strategy B가 비용은 100분의 1이면서 정확도는 오히려 높습니다.
실무에서는 어떻게 적용할까요? context_budget 딕셔너리가 좋은 가이드가 됩니다.
시스템 프롬프트는 500-2000 토큰으로 간결하게 유지합니다. 도구 정의는 도구당 100-500 토큰을 목표로 합니다.
도구 수가 많아지면 Context 사용량도 늘어나므로, 필요한 도구만 포함시킵니다. 또한 **정보성 원칙(Informativity Principle)**을 기억하세요.
현재 결정에 필요한 것을 포함하고, 필요 없는 것은 제외합니다. "혹시 필요할까봐" 넣는 정보가 오히려 방해가 됩니다.
김개발 씨는 자신의 에이전트를 재점검했습니다. Context에 들어간 모든 정보를 검토하며 "이게 정말 현재 결정에 필요한가?"를 물었습니다.
불필요한 배경 정보, 오래된 대화 기록, 관련 없는 도구 정의를 제거했더니 응답 속도와 정확도가 모두 개선되었습니다. "양보다 질이라...
Context도 다이어트가 필요하군요!"
실전 팁
💡 - Context 사용량 70-80%에서 압축 트리거를 설정하세요
- "이 정보가 현재 결정에 필요한가?"를 항상 자문하세요
5. 시스템 프롬프트 설계
"시스템 프롬프트를 어떻게 작성해야 할지 모르겠어요. 너무 상세하면 딱딱하고, 너무 간단하면 효과가 없고..." 김개발 씨의 고민에 박시니어 씨가 조언합니다.
"적절한 고도를 찾아야 해. 비행기가 너무 낮으면 장애물에 부딪히고, 너무 높으면 목적지를 못 찾거든."
시스템 프롬프트 설계에서 **고도(Altitude)**란 지시의 추상화 수준을 의미합니다. 너무 낮은 고도는 경직된 로직을 만들고, 너무 높은 고도는 모호한 지침이 됩니다.
최적의 고도는 명확한 단계를 제시하면서도 실행의 유연성을 허용합니다.
다음 코드를 살펴봅시다.
# 시스템 프롬프트 고도 비교
# 너무 낮음 (Too Low) - 취약하고 유지보수 어려움
too_low = """
가격 문의시 docs/pricing.md의 테이블을 확인하세요.
USD로 표시되면 config/exchange_rates.json의 환율로
EUR로 변환하세요. EU 사용자면 config/vat_rates.json의
해당 국가 VAT를 더하세요. 결과는 통화 기호, 소수점
두 자리, VAT 포함 여부 메모와 함께 출력하세요.
"""
# 너무 높음 (Too High) - 모호하고 구체적 행동 유도 실패
too_high = """
가격 문의를 도와주세요. 도움이 되고 정확하게 답하세요.
"""
# 최적 (Optimal) - 휴리스틱 기반
optimal = """
<INSTRUCTIONS>
가격 문의 시:
3. 적절한 통화 및 세금 고려사항으로 형식화
김개발 씨는 시스템 프롬프트 작성에 고민이 많았습니다. 처음에는 모든 경우의 수를 상세하게 적었습니다.
"가격이 USD면 이렇게 하고, EUR면 저렇게 하고..." 하지만 환율이 바뀌거나 새로운 통화가 추가되면 프롬프트 전체를 수정해야 했습니다. 반대로 "도움이 되게 답하세요"처럼 간단하게 쓰면, 에이전트가 어떻게 답해야 할지 몰라 엉뚱한 행동을 했습니다.
박시니어 씨가 고도(Altitude) 개념을 설명해주었습니다. "비행기를 생각해봐.
너무 낮게 날면 산이나 건물에 부딪혀. 모든 장애물을 하나하나 피해야 하니까 경로도 복잡하고 피곤하지.
반대로 너무 높게 날면 목적지가 안 보여. 어디로 가야 할지 모르게 돼." 너무 낮은 고도의 문제는 취약성입니다.
위 코드의 too_low를 보면 특정 파일 경로, 특정 환율 처리 로직, 특정 출력 형식이 모두 하드코딩되어 있습니다. 파일 위치가 바뀌거나, 비즈니스 로직이 변경되면 프롬프트 전체를 다시 작성해야 합니다.
너무 높은 고도의 문제는 모호성입니다. too_high를 보면 "도움이 되게 답하라"는 말은 맞지만, 구체적으로 어떻게 해야 하는지 알려주지 않습니다.
에이전트가 공유된 맥락이 있다고 가정하지만, 실제로는 그렇지 않습니다. 최적의 고도는 어떻게 찾을까요?
optimal 예시를 보세요. 명확한 단계가 있습니다: 조회하고, 조정하고, 형식화한다.
하지만 각 단계의 세부 실행은 에이전트에게 맡깁니다. "정확한 수치를 우선하라", "없으면 명시적으로 알려라"처럼 휴리스틱을 제공합니다.
또한 structured_prompt처럼 섹션을 구분하는 것이 중요합니다. BACKGROUND_INFORMATION, INSTRUCTIONS, TOOL_GUIDANCE, OUTPUT_DESCRIPTION으로 나누면 에이전트가 필요한 정보를 빠르게 찾을 수 있습니다.
XML 태그를 사용하면 좋은 이유가 있습니다. 첫째, 섹션 경계가 명확합니다.
둘째, 나중에 프로그래밍적으로 파싱하기 쉽습니다. 셋째, 에이전트가 어떤 섹션을 참조해야 하는지 명시적으로 알 수 있습니다.
김개발 씨는 자신의 시스템 프롬프트를 재작성했습니다. 하드코딩된 로직 대신 참조할 파일 경로와 휴리스틱을 제공했습니다.
섹션을 명확히 구분하고, 각 섹션의 목적을 분명히 했습니다. 결과적으로 유지보수가 쉬워졌고, 에이전트의 행동도 더 일관성 있어졌습니다.
"프롬프트도 설계가 필요하군요. 적절한 추상화가 핵심이네요!"
실전 팁
💡 - XML 태그나 마크다운 헤더로 섹션을 명확히 구분하세요
- 휴리스틱을 제공하되, 세부 실행은 에이전트에게 맡기세요
6. 도구 정의 최적화
"에이전트가 자꾸 잘못된 도구를 선택해요." 김개발 씨가 답답해합니다. 박시니어 씨가 도구 정의를 살펴보더니 말합니다.
"도구 설명이 너무 빈약해. 사람 엔지니어도 이 설명으론 어떤 도구를 써야 할지 모르겠는데, 에이전트가 어떻게 알겠어?"
도구 정의는 에이전트의 행동을 결정짓는 핵심 요소입니다. 도구 설명이 빈약하면 에이전트는 추측할 수밖에 없습니다.
좋은 도구 설명은 "무엇을 하는지", "언제 사용하는지", "무엇을 반환하는지"를 명확히 담고, 사용 예시와 에지 케이스까지 포함합니다.
다음 코드를 살펴봅시다.
# 도구 정의 비교
# 약한 설명 (Weak Description)
weak_tool = {
"name": "search_database",
"description": "데이터베이스에서 고객 정보를 검색합니다."
}
# 강한 설명 (Strong Description)
strong_tool = {
"name": "search_database",
"description": """
ID 또는 이메일로 고객 정보를 조회합니다.
사용 시점:
- 사용자가 특정 고객의 세부정보, 이력, 상태를 문의할 때
- 사용자가 고객 식별자를 제공하고 관련 정보가 필요할 때
반환값:
- 기본 정보 (이름, 이메일, 계정 상태)
- 주문 이력 요약
- 지원 티켓 수
고객을 찾지 못하면 null 반환.
데이터베이스 연결 불가시 error 반환.
""",
"parameters": {
"type": "object",
"properties": {
"customer_id": {
"type": "string",
"description": "고객 고유 식별자 (UUID 형식)"
},
"email": {
"type": "string",
"description": "고객 이메일 (customer_id 대안)"
}
}
}
}
# 통합 원칙 (Consolidation Principle)
# 사람 엔지니어가 구분 못하면, 에이전트도 못한다
ambiguous_tools = ["search_customers", "find_users", "get_clients"]
# 위 세 도구의 차이를 설명할 수 없다면, 하나로 통합하라
김개발 씨의 에이전트는 자꾸 잘못된 도구를 호출했습니다. 고객 정보가 필요한데 주문 검색 도구를 쓰고, 파일을 읽어야 하는데 웹 검색을 시도했습니다.
"왜 이렇게 멍청하지?" 박시니어 씨가 도구 정의를 보더니 문제를 지적했습니다. "도구 설명을 봐.
'데이터베이스에서 고객 정보를 검색합니다'가 전부야. 언제 이 도구를 써야 하는지, 어떤 결과가 나오는지 전혀 모르겠어." 도구 설명이 왜 그렇게 중요할까요? 에이전트는 도구 설명을 보고 어떤 도구를 사용할지 결정합니다.
설명이 빈약하면 추측할 수밖에 없습니다. 마치 가게에서 상품 설명 없이 이름만 보고 뭘 살지 결정하는 것과 같습니다.
위 코드의 weak_tool과 strong_tool을 비교해보세요. weak_tool은 "데이터베이스에서 고객 정보를 검색합니다"가 전부입니다.
이걸로는 언제 이 도구를 써야 하는지, 어떤 파라미터가 필요한지, 결과로 뭐가 나오는지 알 수 없습니다. strong_tool은 다릅니다.
먼저 무엇을 하는지 명확히 설명합니다: "ID 또는 이메일로 고객 정보를 조회". 다음으로 언제 사용하는지 구체적인 시나리오를 제공합니다.
그리고 무엇을 반환하는지 상세히 알려줍니다. 마지막으로 에지 케이스까지 다룹니다: 찾지 못하면 null, 연결 불가시 error.
여기서 중요한 원칙이 있습니다. **통합 원칙(Consolidation Principle)**입니다.
"사람 엔지니어가 어떤 도구를 써야 할지 확실히 말할 수 없다면, 에이전트도 그렇게 할 수 없다." 위 코드의 ambiguous_tools를 보세요. search_customers, find_users, get_clients.
이 세 도구의 차이점을 설명할 수 있나요? 아마 어려울 것입니다.
이렇게 비슷한 도구가 여러 개 있으면 에이전트가 혼란스러워합니다. 차라리 하나로 통합하는 것이 낫습니다.
파라미터 설명도 마찬가지입니다. customer_id가 어떤 형식인지, email이 customer_id의 대안인지 명시해야 합니다.
기본값이 있다면 기본값도 적어줍니다. 실무에서는 도구 설명을 작성할 때 이 질문들을 던져보세요: - 이 도구는 정확히 무엇을 하는가?
-
언제 이 도구를 사용해야 하는가? - 어떤 결과를 반환하는가?
-
실패하면 어떻게 되는가? - 비슷한 다른 도구와 뭐가 다른가?
김개발 씨는 모든 도구 설명을 개선했습니다. 사용 시점, 반환 형식, 에러 케이스를 명시했습니다.
또한 비슷한 도구들을 통합해서 혼란을 줄였습니다. 그러자 에이전트가 정확한 도구를 선택하는 비율이 크게 높아졌습니다.
"도구 설명이 에이전트의 판단력이군요!"
실전 팁
💡 - 도구 설명에 "언제 사용하는지"를 반드시 포함하세요
- 비슷한 도구가 여러 개면 통합을 고려하세요
이상으로 학습을 마칩니다. 위 내용을 직접 코드로 작성해보면서 익혀보세요!