포켓몬 팀 확장하기: AI 시스템이 초기 설계를 넘어설 때
요약
개인용 AI 오케스트레이션 시스템을 구축하고 에이전트 확장 과정에서 발생한 문제를 해결하는 과정을 다룹니다. 기능 레지스트리를 도입하여 라우팅 정확도를 높이고, 에이전트 간 협업 패턴을 개선하는 방법을 설명합니다.
핵심 포인트
- 기능 레지스트리(registry.json)를 통한 에이전트 라우팅 최적화
- 에이전트 간의 건설적 긴장(constructive tension) 패턴 활용
- 공유 저널(shared journal)을 통한 컨텍스트 유지 및 정보 손실 방지
- 확장 가능한 에이전트 인프라 구축을 위한 설계 전략
Part 1에서 저는 개인용 AI 오케스트레이션 (orchestration) 시스템을 구축했습니다. 즉, 하나의 오케스트레이터 (Metagross)가 전문화된 에이전트 (agents)들에게 작업을 라우팅 (routing)하는 방식입니다. Part 2에서는 서킷 브레이커 (circuit breaker), 공유 저널 (shared journal), 에이전트 간의 건설적 긴장 (constructive tension), 그리고 라우팅을 검증하기 위한 하네스 (harness)를 추가하여 시스템을 개선했습니다.
그 이후로 저는 더욱 많은 개선을 지속해 왔습니다. 급격한 성장은 제가 예상하지 못했던 문제들을 드러냈고, 인프라 (infrastructure)의 변경을 요구했습니다.
1. 로스터의 확장
기존의 에이전트들은 저의 핵심 워크플로 (workflow)를 커버했지만, 계속해서 빈틈이 나타났습니다. 엣지 케이스 (edge cases)들은 어떤 에이전트에게도 딱 맞지 않는 작업들을 드러냈습니다. 저는 몇몇 새로운 포켓몬을 추가해야 했습니다:
Genesect는 코드를 작성하고, 디버깅 (debugging)하며, 스택 트레이스 (stack traces)를 설명하고, 코드 리뷰 (code review)를 수행하는 코딩 에이전트입니다.
Scizor는 감사 (audits)를 수행하고 정확성을 확인하는 적대적 코드 리뷰어 (adversarial code reviewer)입니다.
Jigglypuff는 컨퍼런스 발표와 CFP (Call for Papers)를 돕는데, 특히 발표 구조, 슬라이드 개요, 발표자 노트, 그리고 Q&A 준비를 지원합니다.
하지만 에이전트가 더 많아지자, 오케스트레이터인 Metagross가 기억만으로는 각 에이전트가 무엇을 할 수 있는지 안정적으로 알 수 없다는 것을 발견했습니다. 라우팅 (routing) 결정이 모호해졌으며, 특히 엣지 케이스 (edge cases)에서 그러했습니다. 예를 들어, 요청된 사항이 코딩 작업인지 아니면 리서치 (research) 작업인지 항상 구분할 수 있는 것은 아니었습니다.
해결책은 기능 레지스트리 (capability registry)였습니다. 이는 Metagross가 세션 시작 시 읽어들이는 registry.json 파일입니다. 각 에이전트는 자신의 기능과 작업 유형이 포함된 항목을 가집니다:
{
"name": "genesect",
"description": "Coding agent",
...
이제 작업이 모호할 때, Metagross는 추측하는 대신 레지스트리 (registry)를 참조합니다. 라우팅 로직 (routing logic)은 여전히 프롬프트 (prompt)에 있지만, 레지스트리는 각 에이전트가 실제로 무엇을 처리하는지에 대한 진실의 원천 (source of truth) 역할을 합니다. 새로운 에이전트를 추가한다는 것은 오케스트레이터 프롬프트를 다시 쓰는 것이 아니라, JSON 파일 하나를 업데이트하는 것을 의미합니다.
또한 Genesect가 코드를 작성한 후, Metagross가 결과를 반환하기 전에 자동으로 Scizor에게 검토를 요청하도록 Part 2의 건설적 긴장 (constructive tension) 패턴을 확장해야 했습니다.
2. 저널이 부담이 되다
Part 2에서 추가한 공유 저널 (shared journal)은 시스템에 추가된 가장 훌륭한 요소 중 하나였습니다. 에이전트들은 작업을 마칠 때 컨텍스트 (context)를 작성하고, 시작하기 전에 이를 읽기 때문에 인수인계 과정에서 정보가 손실되지 않습니다.
문제는 저널이 계속 커진다는 점이었습니다. 제가 알아차렸을 때쯤 agent-journal.md 파일은 240KB가 넘는 산문으로 가득 차 있었습니다. 모든 에이전트가 매 세션 시작 시 전체 파일을 읽었기 때문에, 작업을 시작하기도 전에 컨텍스트 윈도우 (context window)의 절반이 채워져 버렸습니다.
저는 두 가지 해결책을 마련했습니다.
query-journal.sh: 에이전트들이 전체 저널을 읽는 대신, 저널에 쿼리 (query)를 날리도록 했습니다. 키워드, 날짜 범위 또는 작업 유형을 전달하면 관련 있는 항목만 반환받습니다.
bash query-journal.sh --keywords "react native sdk" --limit 10
이제 모든 에이전트의 프롬프트에 포함된 저널 프로토콜은 다음과 같이 명시합니다: 전체 저널 파일을 읽지 마십시오. query-journal.sh를 사용하십시오. 이 한 가지 변화만으로 컨텍스트 로드 (context load)를 크게 줄일 수 있었습니다.
archive-journal.sh: 7일이 지난 항목을 월간 아카이브 (archive) 파일(agent-journal-archive-2026-04.md)로 이동시키는 스크립트입니다. 이 스크립트는 활성 저널이 30개 항목을 초과하면 자동으로 실행되며, 백업용으로 월간 크론 잡 (cron job)도 설정되어 있습니다. 이를 통해 활성 저널은 가볍게 유지됩니다. 오래된 컨텍스트는 보존되지만 작업 흐름을 방해하지 않습니다.
아카이브 스크립트는 아카이브 내에서 가치가 낮은 항목을 정리 (prune)하기도 합니다. 연구 (research), 분석 (analysis), 검토 (review), 작성 (writing), 계획 (planning) 항목은 항상 유지됩니다. 환경 설정 (environment setup), 설정 조정 (config tweaks), 또는 빌드 명령 (build commands)과 같이 순수하게 절차적인 항목은 학습 내용이 포함되어 있지 않으면 삭제됩니다. 코딩 작업의 경우, 스크립트는 버그 (bugs), 근본 원인 (root causes), 설계 결정 (design decisions), 워크어라운드 (workarounds), 격차 (gaps), 제한 사항 (limitations)과 같은 가치 신호를 찾으며, 이러한 요소가 전혀 없다면 해당 항목을 삭제합니다.
또한 두 가지 보조 구조를 추가했습니다:
state.json: 여러 에이전트가 알아야 하지만 저널 (journal)에 속하지 않는 사실들을 위한 공유 키-값 저장소 (key-value store)입니다. 현재 SDK 버전, 활성 프로젝트, 알려진 버그 등이 포함됩니다. 에이전트들은 저널 항목으로부터 동일한 사실을 다시 도출하는 대신 이 파일을 읽습니다. 이는 최신 상태를 유지하는 작은 파일입니다:
{
"current_sdk_version": {
"value": "0.23.6941",
...
artifacts/: 에이전트가 생성하고 다른 에이전트가 나중에 필요로 할 수 있는 출력물들을 위한 디렉토리입니다. 동일한 조사 내용이나 초안을 다시 생성하는 대신, 에이전트는 먼저 아티팩트 (artifacts)를 확인할 수 있습니다. 저널 항목은 해당 아티팩트 경로를 가리킵니다.
저널은 여전히 조정 계층 (coordination layer)이지만, 이제는 점점 커지는 모놀리스 (monolith)가 아닌 가벼운 조정 계층이 되었습니다.
3. 기술 (Skills): 프롬프트 비대화 없는 도메인 지식
일부 에이전트들은 업무를 잘 수행하기 위해 전문적인 지식이 필요합니다. Genesect와 Scizor는 둘 다 종종 React Native 코드를 다룹니다. 이들은 최신 베스트 프랙티스 (best practices), 일반적인 마이그레이션 패턴 (migration patterns), 업그레이드 경로를 알아야 합니다.
이러한 지식을 시스템 프롬프트 (system prompts)에 직접 넣을 수도 있겠지만, 프롬프트는 이미 역할 정의, 행동, 도구, 저널 프로토콜 (journal protocol), 자기 개선 루프 (self-improvement loop)를 정의하는 등 이미 많은 일을 하고 있습니다. 여기에 도메인 지식까지 채워 넣으면 유지보수가 어려워지고, 지식이 변경될 때 업데이트하기도 힘들어집니다.
해결책은 skills/ 디렉토리였습니다. 각 기술은 집중된 마크다운 (markdown) 파일입니다:
skills/
react-native-best-practices.md
react-native-brownfield-migration.md
...
에이전트들은 핵심 프롬프트와 분리하여, 자신의 업무와 관련된 기술을 리소스 (resources)로서 로드합니다. React Native가 새 버전을 출시하여 마이그레이션 가이드가 변경되면, 저는 파일 하나만 업데이트하면 됩니다. 해당 기술을 사용하는 모든 에이전트는 자동으로 업데이트를 받게 됩니다.
기술은 또한 조합 가능 (composable)합니다. Genesect는 코딩 기술을 로드합니다. Scizor는 동일한 기술에 리뷰 특화 컨텍스트 (context)를 추가로 로드합니다. Alakazam은 배경 지식이 필요한 주제를 조사할 때 기술을 로드할 수 있습니다. 지식은 한 곳에 존재하며, 그것이 필요한 에이전트들 사이에서 공유됩니다.
이는 Part 1에서 다룬 범위 제한적 도구 액세스 (scoped tool access)와 동일한 원리입니다. 에이전트들은 자신에게 필요한 것만 가져가되, 이번에는 도구 대신 지식 (knowledge)에 적용된 것입니다.
4. 진화한 테스트 하네스 (The Test Harness Evolved)
Part 2에서 저는 11개의 라우팅 테스트 (routing tests)를 포함한 테스트 하네스 (test harness)를 구축했습니다. 그 테스트가 답하고자 했던 질문은 다음과 같았습니다: Metagross가 적절한 작업을 적절한 에이전트에게 전달하는가?
이는 필수적이지만 충분하지는 않습니다. Alakazam에게 올바르게 라우팅되었다고 해서 Alakazam이 훌륭한 조사 결과를 만들어냈다는 뜻은 아닙니다. Mew에게 올바르게 라우팅되었다고 해서 블로그 초안이 스타일 가이드 (style guide)를 따랐다는 의미도 아닙니다.
이제 하네스는 두 개의 계층을 가집니다:
Layer 1: 라우팅 및 구조 테스트 (기존의 PromptFoo 하네스). 현재 17개의 테스트가 있으며, 이는 위임 로직 (delegation logic)과 출력 구조 (output structure)의 회귀 (regressions)를 잡아냅니다.
Layer 2: 품질 평가 (Quality evals)는 에이전트의 출력이 실제로 좋은지를 테스트하는 LLM-as-judge 루브릭 (rubrics)이 포함된 별도의 evals/ 디렉토리입니다. 다섯 가지 평가가 있습니다:
| 평가 (Eval) | 테스트 내용 |
|---|---|
01-constructive-tension | Alakazam 이후 Slowking가 실행되며 실제로 검증함 |
| ... |
각 평가에는 task.md (무엇을 왜 테스트하는지)와 grader.sh (에이전트를 실행한 후 judge를 실행함)가 있습니다. judge는 네 가지 차원에 대해 1~5점 척도의 루브릭 (rubric)으로 점수를 매깁니다. 통과 임계값 (Pass threshold): 평균 ≥ 3.5.
라우팅 테스트는 빠르게 실행되며 구조적 회귀를 잡아냅니다. 품질 평가는 온디맨드 (on-demand)로 실행되며, 에이전트가 기술적으로는 맞지만 실제로는 좋지 않은 출력을 생성하기 시작할 때 발생하는 출력 저하 (output degradation)를 잡아냅니다.
5. 이제 시스템이 스스로를 관리합니다
기존 시스템은 제가 직접 관리해야 했지만, 이제는 시스템이 스스로 더 많은 것을 관리합니다.
위임 깊이 제한 (Delegation depth limit). Metagross는 위임 체인 (delegation chain)이 얼마나 깊은지 추적합니다. 만약 delegationDepth >= 3이면, 위임을 중단하고 겉잡을 수 없이 커지는 대신 차단 요소 (blocker)를 표면화합니다. 이는 통제 불능의 체인이 토큰과 시간을 낭비하기 전에 잡아냅니다.
백프레셔 경고 (Backpressure warning). Metagross는 세션 내의 위임 (delegations) 대 완료 (completions) 비율을 추적합니다. 위임된 작업이 완료된 작업보다 2배 이상 많으면 경고를 띄웁니다. 무언가 막혀 있거나 루프 (looping)가 돌고 있다는 뜻이며, 저는 이를 알아야 합니다.
병렬 실행 (Parallel execution). 조사(research)와 일정 확인(calendar check)처럼 하나의 작업에 독립적인 하위 작업들이 있을 때, Metagross는 이를 순차적으로 처리하는 대신 병렬로 위임합니다. 프롬프트는 병렬화가 적용되는 시점과 그렇지 않은 시점을 명시적으로 식별합니다.
cron을 통한 월간 아카이빙. 저널은 일주일에 한 번 스스로를 아카이빙하며, 오래된 항목들을 활성 저널에서 이동시킵니다.
위임 후 제안. 각 에이전트가 완료된 후, Metagross는 자연스러운 다음 단계를 제안합니다. Alakazam 이후에는 "이 결과들을 검증해 드릴까요?", Genesect 이후에는 "이 코드를 검토할까요, 아니면 이를 추적할 작업을 생성할까요?"라고 묻습니다. 시스템은 제가 질문하는 것을 기억해야 할 필요 없이, 건설적 긴장 (constructive tension) 패턴을 향해 저를 유도합니다.
이 중 어느 것도 개별적으로는 복잡하지 않지만, 이들이 모이면 제가 알아차리기를 기다리는 대신 스스로의 실패 모드 (failure modes)를 포착할 수 있습니다.
결론
에이전트들의 이름을 포켓몬으로 지은 것은 재미있을 것 같아서 시작한 일이었지만, 이 비유는 여전히 유효합니다. 단순히 포켓몬을 잡고 운이 좋기를 바라는 것이 아닙니다. 포켓몬을 훈련시키고, 적절한 기술을 가르치며, 적절한 상성을 고려하여 배치하고, 상호 보완적인 강점과 의도적인 긴장감을 가진 팀을 구축해야 합니다.
다음 업데이트를 기대해 주세요!
AI 자동 생성 콘텐츠
본 콘텐츠는 Dev.to AI tag의 원문을 AI가 자동으로 요약·번역·분석한 것입니다. 원 저작권은 원저작자에게 있으며, 정확한 내용은 반드시 원문을 확인해 주세요.
원문 바로가기