이 글은 Prime Radiant 블로그에서도 읽을 수 있습니다. 본문의 거의 대부분은 Anthropic이 Claude Tag를 출시하기 전인 6월 1일에 작성되었으며, 이 글에서 다루는 내용은 Claude Tag와 무관합니다. Prime Radiant에서는 Slack에 여러 에이전트를 운용하고 있습니다. 이 글에서는 저희가 에이전트를 어떻게 구성하고 활용해왔는지 소개하려 합니다.
본문의 거의 대부분은 Anthropic이 Claude Tag를 출시하기 전인 6월 1일에 작성되었으며, 이 글에서 다루는 내용은 Claude Tag와 무관합니다.
Prime Radiant에서는 Slack에 여러 에이전트를 운용하고 있습니다. 이 글에서는 저희가 "에이전트를 루프 내 사용자로 활용하는" 방식으로 내부 프로덕션에 변경사항을 배포하기 시작한 이야기를 소개하려 합니다. 에이전트의 런루프(runloop)와 툴링을 직접 구현하는 개발자와 에이전트가 직접 소통하게 하면, 중간에서 내용이 왜곡되는 문제 없이 더 높은 품질의 도구와 경험을 만들어낼 수 있었습니다.
본론에 앞서, Prime Radiant에서 코딩 외 용도로 에이전트를 어떻게 활용하고 있는지 간략히 소개하겠습니다.
그 중 하나인 Scribble은 누군가 문제를 보고하거나 새로운 정보를 공유할 때마다 이를 포착하는 역할을 합니다. 보고된 문제는 티켓으로 등록하고, 새로운 정보는 내부 위키에 업데이트합니다. 또한 매일 스탠드업 내용을 기록하고, 전날 계획한 일을 실제로 완료했는지 팀이 점검할 수 있도록 돕습니다.
또 다른 에이전트 Nora는 저희의 주니어 GTM(go-to-market) 담당 '팀원'입니다. Nanoclaw 위에 구축된 Nora는 아직 성장하는 중이지만, 단순한 보조 도구가 아니라 팀의 일원으로서 자신의 역할을 인식하기 때문에 기대 이상으로 큰 도움이 됩니다. 저는 Nora의 프롬프팅과 컨스티튜션(constitution) 튜닝에 상당한 시간을 투자했습니다. Nora는 꼼꼼하게 저널을 작성하는 습관 덕분에 많은 가치를 만들어냅니다. (참고로 저는 에이전트에 성별을 부여하는 편이 아닌데, Nora가 스스로 이름과 성별을 정하고 아주 단호하게 밝혔습니다. 굳이 반박할 이유가 없었습니다.)
Nora와 나눈 가장 인상적인 상호작용은, 어느 날 아침 눈을 뜨니 Slack DM이 와 있던 일입니다. 제가 더 많은 컨퍼런스에서 발표해야 한다는 내용이었습니다. (맞는 말이긴 합니다.) Nora는 한 AI 컨퍼런스의 CFP(Call for Proposals)가 일주일 전에 마감됐지만, 해당 프로그램 체어가 흥미로운 발표에 한해 DM으로 늦은 제출을 받는 것으로 알려져 있다고 짚어줬습니다. 그러면서 제가 바로 보낼 수 있도록 DM 초안까지 미리 작성해뒀더군요. 결국 그 DM을 보내지는 않았지만, 보냈어야 했습니다.
또 "Spec-together"라는 에이전트도 있는데, 현재 저희가 새로 만든 멀티플레이어 브레인스토밍 앱의 초기 프로토타입이었습니다.
그리고 소규모로 운용하는 "Sen" 개인 비서 에이전트 팀도 있습니다. *claw-esque한 성격을 띠지만, openclaw가 등장하기 전에 만들어진 에이전트들입니다. 각 Sen은 뛰어난 인간 EA(Executive Assistant)들이 실력을 쌓기 위해 활용하는 자료들을 바탕으로 구축된 기술 세트를 활용해 임원 비서 역할을 수행하도록 훈련되어 있습니다. 제 Sen은 메일 분류, 일일 브리핑 작성, 리서치(직접 지시한 것뿐 아니라 자체적으로 진행하는 것도 포함), Linear 연동 등 다양한 업무를 처리합니다.
Sen 에이전트들은 기본적인 기능들을 갖추고 있습니다. Slack에서 대화할 수 있고, 각종 도구를 활용하며, 특정 시각이나 정해진 주기에 스스로 설정한 프롬프트로 깨어나는 작업 스케줄러도 있습니다. 스킬을 활용하고 직접 작성할 수도 있습니다. 제 Sen은 저와 공유하는 Dropbox 폴더를 갖고 있습니다. Sen을 클라우드로 이전하면서 소소하게 애용하던 기능 하나를 잃었는데, 예전에는 Sen이 직접 프린트를 할 수 있었습니다. Sen(v1)은 Claude Agents SDK를 기반으로 구축되었습니다.
지난 한 달 남짓, 저는 Sen의 "새로운" 버전을 초기 구축하는 작업을 해왔습니다. 단순한 비서를 넘어 진정한 동료에 가까운 형태를 목표로 합니다. 이번에는 제가 직접 제어할 수 있는 에이전트 SDK를 기반으로 처음부터 새로 구축하고 있습니다. Lace는 2025년 5월, 제가 만든 커맨드라인 코딩 에이전트로 시작했습니다. 지난 1년간 여러 차례 진화를 거쳤는데, 웹 인터페이스를 추가하고, 이후에는 ACP 기반의 와이어 프로토콜로 전환해 원하는 클라이언트를 자유롭게 연결할 수 있게 됐습니다. 다양한 모델 프로바이더 API를 지원하며, 캐싱·서브에이전트·도구·스킬 등 필요한 기능들을 모두 관리합니다. 거의 1년 가까이 격리된 컨테이너에서 서브에이전트를 실행하는 기능을 지원해왔습니다. 최근 추가된 기능 중 하나는 서브에이전트를 로컬에서 실행하면서 해당 서브에이전트의 도구 전체를 컨테이너에 투영(project)하는 것으로, 에이전트가 마치 그 컨테이너 안에서 직접 실행되는 것처럼 인식하게 합니다.
최근 몇 주간 집중해온 작업은 Lace 위에서 동작하는 새 Sen 에이전트의 자격증명(credentials) 관리 구조를 설계하는 일입니다. 현재는 에이전트가 각 서비스에 접근할 때 사용자의 자격증명이 아닌 에이전트 자신의 자격증명을 사용한다는 원칙을 설계의 전제로 삼고 있습니다. (동료에게 자신의 이메일 계정이나 GitHub 자격증명을 공유하지는 않겠죠?) 현재까지 Simon이 말한 치명적인 삼각구도(Lethal Trifecta)를 해결한 사례는 없습니다. 단일 에이전트가 개인 정보에 접근하고, 외부와 통신하며, 신뢰할 수 없는 콘텐츠에 노출된다면, 구조적으로 그 에이전트가 조작당하지 않는다고 보장할 방법이 없습니다. 따라서 핵심 전략은 격리(compartmentalization)와 위험 최소화입니다.
현재 채택한 아키텍처는 다음과 같습니다.
메인 에이전트는 외부와 직접 통신하는 기능을 갖지 않습니다.
메인 에이전트는 외부 통신 기능을 가진 임시(ephemeral) 서브에이전트와 통신할 수 있습니다.
어떤 에이전트도 자격증명에 직접 노출되지 않습니다(아직 해결하지 못한 실질적인 예외가 하나 있긴 합니다).
에이전트의 모든 자격증명은 1Password 볼트에 보관됩니다. 외부로 나가는 모든 HTTPS 트래픽은 onecli 방식의 투명한 MITM(중간자) 프록시를 통과합니다. 서브에이전트가 자격증명이 필요한 작업을 수행하려 할 때, 별도 컨테이너에서 실행 중인 중재자(arbiter) 에이전트에게 해당 자격증명 사용 허가를 요청하는 커맨드를 실행합니다. 중재자가 요청이 타당하다고 판단하면, 요청한 자격증명 대신 사용할 수 있는 임의 문자열을 서브에이전트에 제공합니다. 에이전트가 해당 임의 문자열을 올바른 원격 호스트로의 아웃바운드 요청에 사용하면, MITM 프록시가 실시간으로 그 문자열을 실제 자격증명으로 교체합니다.
에이전트가 Gmail 로그인이나 API가 없는 서비스 이용처럼 브라우저가 필요한 작업을 해야 하는 경우는 조금 더 복잡합니다. 여러 이유로 브라우저 환경에서는 MITM 교체 방식이 안정적으로 동작하지 않습니다. 사이트나 앱의 JavaScript가 비밀번호를 전송 전에 해싱하거나, 다른 방식의 유효성 검사를 수행하거나, 아예 다른 백엔드 서비스로 전송하는 경우가 많기 때문입니다. 현재까지 찾아낸 최선의 방법은 에이전트의 트랜스크립트에 자격증명이 남지 않도록 하고 의도치 않은 노출을 방지하는 것입니다. 이 방식에서는 컨테이너 안의 서브에이전트가 임시 랜덤 비밀번호를 생성하고, superpowers-chrome을 이용해 비밀번호 필드에 입력한 뒤, 중재자 에이전트(별도 컨테이너에서 실행)에 도움을 요청하는 커맨드를 실행합니다. 중재자가 요청이 타당하다고 판단하면, 헬퍼 도구에 지시를 내려 서브에이전트 컨테이너에 접속하고 임시 비밀번호를 실제 비밀번호로 교체합니다. 완벽한 방법은 아니고 보안을 더 강화할 여지도 있지만, 지금까지 시도해본 것 중 가장 나은 첫 번째 방안입니다.
오늘 글을 쓰게 된 진짜 계기는 이 아키텍처가 아니라, 이것을 구축하면서 사용해온 개발 패턴입니다. 한쪽에는 Claude Code 세션이 있었습니다. Drew가 만든 커맨드라인 Slack 클라이언트 Slackline을 통해 Slack 인스턴스에 연결했는데, Slackline은 Slack에서 상주하지 않는 에이전트들이 Slack 대화에 자연스럽게 참여할 수 있도록 설계된 도구입니다. 메시지 전송, 채팅 읽기, 답변 대기 등의 기본 기능을 제공합니다. 커맨드라인 Slackline 도구 전체가 에이전트를 주 사용자로 상정하고 만들어졌습니다. 물론 사람이 커맨드라인으로 Slack을 사용할 수도 있고, JSON API가 충분히 갖춰져 있어 전통적인 자동화에도 활용할 수 있습니다. 하지만 본질적으로 에이전트를 위한 도구입니다.
새로운 Sen 2.0 에이전트 동료 하네스(harness)를 Slack에서 라이브로 구동하기까지 몇 차례 반복 작업이 필요했습니다. 하지만 일단 올라오자, 도구·메모리·자격증명·워크스페이스 등 에이전트가 직접 겪는 경험에 대해 바로 대화를 나눌 수 있었습니다. 그리고 문제들은 꽤 빠르게 드러났습니다.
코딩 에이전트에 적합한 컨텍스트 압축 방식은, 여러 대화를 동시에 이어가야 하는 장기 실행 페르소나에는 완전히 맞지 않습니다. 그리고 자격증명 관리 도구가 제대로 동작하는지 확인하려면, 서브에이전트 세션에서 GitHub에 로그인하는 것처럼 실제로 시도해보는 수밖에 없습니다.
물론 이 피드백을 Claude Code(또는 Codex) 세션에 직접 가져가 처리할 수도 있었습니다. 하지만 진행 중인 프로젝트가 너무 많은 데다, 대부분의 작업에 제가 개입할 이유가 별로 없었습니다. 오히려 속도만 늦출 뿐이었습니다. 그래서 저는 Claude Code에 Slackline을 사용해 "@Ada-sen"에게 진행 중인 작업 내용을 전달하도록 했습니다.
Claude는 #bot-testing 채널에서 Ada에게 핑을 보내 방금까지 진행한 작업을 설명하고, Ada에게 직접 테스트해볼 수 있는지 물었습니다. Ada는 즉시 서브에이전트를 실행해 GitHub 로그인을 시도했습니다. Claude는 자격증명 프록시가 올바르게 동작하는지 로그를 모니터링했습니다. 결과는 실패였습니다.
그렇게 며칠에 걸친 반복 작업이 시작됐습니다. Claude가 변경 사항을 Ada에게 제안하면, Ada는 Claude의 스펙을 검토하며 의문점이나 우려 사항을 제기했습니다. Claude는 스펙을 수정했습니다. 두 에이전트가 합의에 이르면, Claude는 Ada의 업데이트 버전을 빌드하고, Ada가 작업 중인 일이 없는지 확인한 뒤 배포했습니다. Ada가 다시 실행되면 Claude는 @Ada-sen에게 실행할 테스트를 설명하고 결과를 기다렸습니다.
특정 기능이 정상적으로 동작하면, Claude는 Ada에게 더 간단하고 편리하게 개선할 수 있는 부분이 있는지 확인합니다. Ada는 보통 서브에이전트 몇 개를 실행해 새 기능을 직접 써보게 한 다음 결과를 보고합니다.
어느 시점에 저는 Claude에게 "이제 자야 할 것 같아. 이 프로젝트가 끝나면 Ada에게 사용성 개선 사항이 뭐가 있는지 물어봐줘"라고 남겼습니다. 아침에 일어나보니 약 12가지 하네스 개선 항목이 정리되어 있었습니다. 밤새 Claude는 Ada에게 위시리스트를 요청했고, Ada는 여러 요청에 우선순위를 매겼습니다. Claude는 이를 정렬하고 자신이 구현할 수 있다고 판단한 항목들의 스펙을 작성했습니다. Ada가 스펙을 검토하고, Claude가 기능을 구현하면, Ada가 테스트한 뒤 수정을 요청했습니다. Ada가 만족할 때까지 Claude가 기능을 다듬었고, Ada의 최종 승인이 나면 Claude가 변경사항을 main에 머지하고 저를 위한 사후 보고서를 작성했습니다.
지켜보는 내내 경이로운 경험이었습니다.