AI 에이전트는 점점 복잡한 업무를 스스로 처리하고 있지만, 그 잠재력을 끌어기 위해서는 체계적인 최적화가 필수적이다. 이 글에서는 에이전트의 실제 성능과 안정성을 끌어올리는 데 효과적인 접근법을 정리한다. 프롬프트 설계, 맥락 관리, 도구 체계, 제어 루프, 그리고 평가와 적응 전략을 중심으로 살펴본다.
1. 프롬프트 엔지니어링: 에이전트의 사고 방식 설계
1.1 시스템 프롬프트 최적화
시스템 프롬프트는 LLM이 어떻게 행동할지를 규정하는 지침서 역할을 한다. 복잡한 에이전트에서는 다음 원칙을 따르는 것이 좋다.
- 명확하고 간결한 언어 사용: 모호함을 하고 핵심만 담아낸다. 지나치게 구체적인 if-else식 규칙羅列은 약해지기 쉽다.
- 구조화된 프롬프트:
<context>,<directive>,## Tool guidance,## Output format등으로 구분하면 모델이 각 영역을 구분해 이해하기 쉽다. - 최소한의 기대사항 정리: 가능한 최상위 모델로 먼저 최소한의 프롬프트를 실험하고, 실패 패턴을 파악해 예시와 규을 추가해 나간다.
- 양질의 예시 활용:
<preferred>와<avoid>예시를 함 제공하면 금지 사항과 권장 사항이 명확해진다. 예시는 설명보다 훨씬 효과적이다. - 사용자 맥락 분리: 코드베이스에서 유추할 수 없는 선호도는
agent.md같은 외부 파일로 분리해 관리한다.
1.2 LLM 기반 검색의 활용
전통적인 RAG 대신 LLM의 코드 이해 능력을 직접 활용하는 접근이 유리할 수 있다. 예를 들어 ripgrep, jq, find 등을 복합적으로 활용해 LLM이 직접 정규식을 설계하고 파일을 탐색하게 하면, RAG에서 발생할 수 있는 숨은 검색 오류를 줄일 수 있다.
1.3 행동 제어와 톤 앤 매너
- 어조와 스타일 명시: "사용자가 요청하지 않은 한 이모지를 지 마라", "도움이 되지 않을 때 장한 이유를 설명하지 마라" 등 구체적으로 안내한다.
- 강조어 사용:
CRITICAL,NEVER,ALWAYS등의 단어로 핵심 규칙을 강조한다. - 의사결정 알고리즘 시각화: 복잡한 판단이 필요한 경우 흐름도 형태로 단계를 정리해 프롬프트에 포함하면 모델이 일관되게 움직인다.
2. 맥락 엔지니어링: 에이전트의 기억 관리
맥락 엔지니어링은 추론 시점에 어떤 토큰을 포함할지를 설계하는 작업이다. 단순한 프롬프트 작성을 넘어서, 정보의 흐름과 우선순위를 관리하는 것이 핵심이다.
2.1 KV-캐시 효율화
- 프롬프트前綴 안정성 유지: 초 단위 타임스탬프 등 변하는 값을 시스템 프롬프트 앞부분에 넣지 않는다.
- 이력 변경 금지: 과거 동작과 관찰 내용은 불변으로 유지해 캐시 무효화를 막는다.
- 캐시 분기점 명시: 시스템 프롬프트 끝부분을 캐시 분기점으로 설정하고, 지원 여부에 따라 수동으로도 관리한다.
2.2 맥 길이 관리
긴 맥락은 성능 저하와 비용 증가를 초래한다. 맥락 부패 현상처럼 이가 길어질수록 모델의 정보 회수율은 떨어진다.
- 파일시스템을 외부 기억으로 사용: 모델이 필요할 때 파일을 읽고 쓰게 하여 메모리 부담을 줄인다.
- 복원 가능한 압축: 전체 웹 페이지 대신 URL만, 전체 문서 대신 경로만 보존한다.
- 동적으로 재작성되는 작업 목록:
todo.md등을 지속적으로 신해 글로벌 목표가 모델의 최근 주의 범위 안에 들어오도록 한다.
2.3 에이전틱 검색
미리 모든 데이터를 주입하기보다, 에이전트가 실행 시점에 필요한 정보를 직접 불러오는 방식이다.
- Just-in-time 로딩: 파일 경로, 쿼리, URL 등 가벼운 식별자를 유지하고, 도구를 통해 런타임에 데이터를 동적으로 적재한다.
- 메타데이터 활용: 폴더 구조, 네이밍 컨벤션, 수정 시각 등 파일 시스템의 신호를 추론에 활용한다.
- 점진적 정보 드러내기: 에이전트가 색하면서 맥락을 쌓아가고, 필요한 내용만 작업 기억에 유지한다.
- 하이브리드 접근: 핵심 파일은 미리 넣고, 나머지는 검색 도구로 필요할 때 가져온다.
2.4 장기 실행任务的 맥락 유지
2.4.1 맥락 압축
대화가 한계에 다다르면 기존 이력을 요약해 새로운 맥으로 재시작한다. 중요한 설계 결정, 미해결 오류, 구현 세부사항은 보존하고, 중복된 도구 출력은 제거한다.
2.4.2 구조화된 노트
에이전트가 스스로 NOTES.md나 todo.md를 갱신하도록 하여 맥락 밖의 영구 기억을 확보한다.
2.4.3 서브 에이전트 구조
하나의 에이전트가 전체 상태를 유지하려 하기보다, 전문 서브 에이전트가 독립적인 맥락에서 임무를 수행하고 핵심 요약만 상위 에이전트에 전달하는 구조가 유리하다. 복잡한 탐색은 서브 에이전트 내부에서 이루어지고, 상위는 통합에 집중한다.
3. 도구 설계와 관리
3.1 도구 설계 원칙
- 높은 가치 workflow 중심: 모든 기능을 분리하기보다는, 자주 연결되는 단계를 하나의 도구로 묶는다.
- 중복 제거:
get_user_profile,get_user_orders,get_user_notes대신fetch_user_context하나로 통합할 수 있다. - 명확한 목적: 각 도구는 고유하고 명확한 역할을 가진다.
3.2 도구 임스페이스
관련 도구를 접두사로 그룹화하면 모델이 올바른 도구를 선택하기 쉬워진다.
analytics_run_query
analytics_export_report
crm_lookup_contact
crm_create_ticket
3.3 도구 응답 설계
- 핵심 정보만 반환: UUID, 내부 URL, MIME 타입 같은 기계식 식별자보다는 이름, 설명, 유형 등 사람이 읽기 쉬운 정보를 우선한다.
- 응답 포맷 선택: 상황에 따라 요약과 상세를 선택할 수 있게 한다.
enum DetailLevel {
BRIEF = "brief",
FULL = "full"
}
3.4 도구 설명 최적화
도구 설명은 팀원에게 설명하듯 명확하게 작성한다. 입력 매개변수 이름은 user보다 user_id가 더 정확하다. 사소한 설명 수정도 평가 지표에서 큰 차이를 만들 수 있으므로, 실제 데이터로 검증하며 다듬어야 한다.
3.5 동적 도구 제어
반복 중간에 도구를 동적으로 추가/제거하면 KV-캐시가 무효화될 수 있다. 대신 상태 머신이나 접두사 기반 도구 그룹으로 동일한 과를 얻는다.
- 상태별 도구 허용: "웹 탐색" 상태에서는
browser_*도구만, "파일 집" 상태에서는edit_*도구만 사용하도록 제약한다. - 응답 프리필: 모델 응답의 시작 부분을 고정해 특정 도구 호출을 유도할 수 있다.
3.6 계층형 도구
저수준, 중간, 고수준 도구를 함 두면 유연성과 효율성을 동시에 확보할 수 있다.
- 저수준:
bash_run,file_read - 중간:
code_replace,repository_search - 고수준:
task_delegation,web_fetch
3.7 에이전트 스킬
전문 분야 지식을 SKILL.md 중심의 재사용 가능한 스킬 단위로 묶어두면, 용 에이전트를 특정 업무에 특화시킬 수 있다. 메타데이터는 시스템 프롬프트에 미리 노출하고, 필요할 때 전체 내용을 적재하는 진적 공개 방식이 효과적이다.
4. 제어 루프와 아키텍처
4.1 단일 마스터 루프 유지
복잡한 멀티 에이전트보다는 디버깅이 쉬운 단일 루프를 우선한다. 복잡한 작업은 서브 에이전트의 결과를 상위 메시지 이력의 도구 응으로 받아들이는 방식으로 처리한다.
4.2 소형 모델 활용
파일 요약, 히스토리 정리, 웹 페이지 파싱 등 상당수 작업은 소형 모델로 충분하다. 비용과 지연 시간을 줄이면서도 전체 처리량을 릴 수 있다.
5. 평가와 적응
5.1 평가 기반 개발
- 실제 사용 사례를 반영한 평가 데이터셋을 만든다.
- 평가 시 스스로 추론하는 블록을 추가해 CoT를 유도한다.
- 과도한 규은 오히려 과적합을 유발하므로, 해결 경로의 다양성을 허용한다.
5.2 오류 복구
오류는 예외가 아니라 루프의 일부로 받아들여야 한다. 실패한 도구 호출과 그 결과를 그대로 맥락에 남겨두면, 모델은 이를 보고 스스로 수정 방향을 찾는다. 오류를 숨기는 것이 오히려 반복 실패를 초대한다.
5.3 다양성 유지
few-shot 예시가 너무 유사하면 모델이 패턴에 갇힐 수 있다. 시퀀스화 플릿, 어휘 변형, 형식 변화 등을 약간씩 다르게 주어 다양성을 확보하면 과도한 모방과 환각을 줄일 수 있다.