Playwright와 서브 에이전트로 AI CLI를 자율 에이전트로 전환한 방법
요약
단순한 CLI 래퍼였던 Codey를 Playwright와 서브 에이전트 구조를 도입하여 자율적인 에이전트 런타임으로 진화시킨 과정을 다룹니다. 브라우징 능력 강화, 지속적인 터미널 세션 관리, 보안 강화(eval 제거 및 셸 인젝션 방지) 등 기술적 업그레이드 내용을 상세히 설명합니다.
핵심 포인트
- Playwright와 Vision을 결합하여 인간과 유사한 웹 브라우징 및 UI 디버깅 구현
- 독립적인 도구 루프와 컨텍스트를 가진 서브 에이전트 아키텍처 도입
- 백그라운드 프로세스 유지를 위한 지속적인 터미널 세션 기능 추가
- ast.parse() 검증을 통한 eval() 제거 및 셸 인젝션 방지로 보안 강화
처음에 Codey를 만들었을 때는 몇 가지 기본적인 도구만 가진 LLM(대규모 언어 모델)의 간단한 CLI 래퍼였습니다. 작은 작업에는 좋았지만, 더 어려운 문제들을 던지기 시작하면서 한계가 명확해졌습니다.
개발 서버를 실행할 때 스레드를 블로킹 할 수 없었고, 문서를 검색할 수도 없었으며, 솔직히 말해서 raw eval() 호출은 저를 밤잠 설치게 만들었습니다.
그래서 저는 기반을 완전히 허물고 대규모 플랫폼 리라이트를 진행했습니다. 오늘, Codey가 단순한 스크립트에서 안전하고 지속적인 에이전트 런타임으로 어떻게 진화했는지 공유하게 되어 기쁩니다.
기술적 업그레이드에 대해 깊이 파헤쳐 보겠습니다.
🌐 1. 인간과 같은 브라우징 (Playwright + Vision)
Codey가 문서를 읽고, GitHub 이슈를 확인하며, UI를 시각적으로 디버깅할 수 있기를 원했습니다. 저는 Playwright 기반의 웹 도구를 통합했습니다.
Vision 병목 현상: 초기에는 모델에 시각적 컨텍스트를 전달하기 위해 파이프라인이 다음과 같았습니다: 스크린샷 -> PNG를 디스크에 쓰기 -> PNG 읽기 -> Base64 인코딩. 이 디스크 I/O는 눈에 띄게 느렸습니다. 저는 스크린샷을 바이트로 메모리에 직접 캡처하고 즉석에서 인코딩하여 이를 최적화했습니다. 우리는 .codey_screenshots/ 임시 디렉토리를 완전히 제거했습니다.
자체 복구 종속성: 사용자가 Chromium을 설치하지 않아서 도구가 실패하는 것보다 더 나쁜 것은 없습니다. 이제 브라우저 실행에 실패하면 Codey가 오류를 포착하고, 자동으로 playwright install chromium을 실행한 다음 백그라운드에서 실행을 재시도합니다.
스마트 프롬프팅: 터미널에 https://...와 같은 링크를 붙여넣으면, 시스템은 웹 도구를 프롬프트에 동적으로 주입하고 콘텐츠를 붙여넣으라고 요청하는 대신 즉시 web.navigate()를 트리거합니다.
🤖 2. 서브 에이전트 및 지속적인 터미널
여기서 아키텍처가 정말로
위임 도구 (Delegate Tool): 이제 Codey는 완전히 자율적인 서브 에이전트 (sub-agent)를 실행할 수 있습니다. 이 두 번째 에이전트는 자신만의 도구 루프 (tool loop), 자신만의 히스토리 (history), 그리고 자신만의 컨텍스트 (context)를 가집니다. 이 에이전트는 하위 작업 (sub-task)을 해결하러 떠난 뒤 메인 에이전트에게 요약본을 반환합니다.
지속적인 세션 (Persistent Sessions, 터미널): 이전에는 Codey가 명령어를 실행하면 프로세스를 잃어버렸습니다. 저는 start, send, peek, stop 액션을 추가했습니다. 이제 Codey는 Next.js 개발 서버를 시작하고, 이를 백그라운드에서 계속 실행되도록 둔 채, 로그를 살펴보고(peek), 코드를 계속 작성할 수 있습니다.
인간 참여형 (Human-in-the-Loop, ask): 때때로 AI는 추측해서는 안 됩니다. Codey가 어떤 파일을 편집해야 할지 확실하지 않을 경우, 실행을 일시 중단하고 터미널에 대화형 객관식 프롬프트 (multiple-choice prompt)를 띄웁니다.
🛡️ 3. 보안 강화 (Security Hardening)
Codey가 똑똑해질수록 더 위험해졌습니다. 저는 이를 제어해야 했습니다.
eval() 제거: 임의 코드 실행 (Arbitrary code execution)은 매우 큰 취약점입니다. 저는 계산기 도구에서 가공되지 않은 eval()을 제거하고, 엄격한 ast.parse() 검증으로 교체했습니다. 이제 우리는 안전한 연산자, 함수, 상수의 엄격한 화이트리스트 (whitelist)를 사용합니다.
셸 인젝션 (Shell Injections) 수정: 저는 가공되지 않은 셸 실행과 문자열 연결 (string concatenation) 방식에서 벗어났습니다. 이전에는 git diff가 셸로 직접 전달되었습니다. 이후에는 안전한 인자 파싱 (argument parsing)을 위해 shlex.split()과 결합된 subprocess.run([...])을 사용합니다.
경로 탐색 (Path Traversal) 및 승인 게이트 (Approval Gates): create_file, edit_file, read_files에 엄격한 assert_within_project() 체크를 추가하여, 에이전트가 무작위로 ../../../etc/passwd를 읽기로 결정할 수 없도록 했습니다. 또한 CONFIRM_SHELL=true 환경 플래그를 추가하여, Codey가 잠재적으로 파괴적인 명령을 실행하기 전에 반드시 인간의 허가를 요청하도록 강제했습니다.
🧠 4. 상태 관리 (State Management) 및 개발자 경험 (Developer Experience)
마지막으로, 저는 Codey가 무언가를 기억하는 방식을 전면 개편했습니다.
멀티 세션 워크플로우 (Multi-Session Workflow): 기존의 Codey는 프로젝트당 하나의 history.jsonl 파일에 모든 내용을 쏟아부었습니다. 이제는 별도의 세션 파일들을 생성하며, 대화 횟수와 미리보기를 보여주는 대화형 시작 선택기 (interactive startup picker)를 통해 사용자에게 인사를 건넵니다. 이를 통해 어제의 작업을 이어서 하거나 완전히 새로 시작할 수 있습니다.
스트리밍 (Streaming) 및 컨텍스트 (Context): ChatGPT와 같이 빠릿한 느낌을 주기 위해 토큰 단위 스트리밍 (token-by-token streaming) 방식으로 전환했습니다. 또한 무한 루프와 통제 불능의 API 비용 발생을 방지하기 위해 trim_history() 및 MAX_TOOL_ROUNDS를 추가했습니다.
마무리하며
이 패치들을 통해 Codey는 단순한 CLI + LLM + 도구 조합에서, 지속 가능한 에이전트 런타임 (Persistent agent runtime) + 브라우저 자동화 (browser automation) + 서브 에이전트 (subagents) + 프로젝트 메모리 (project memory)의 형태로 변모했습니다.
이 프로젝트를 구축하는 과정은 에이전트 오케스트레이션 (agent orchestration)과 Python CLI 개발에 있어 놀라운 학습 경험이었습니다.
AI 코딩 어시스턴트에 관심이 있거나, 직접 만들고 싶거나, 혹은 단순히 소스 코드를 살펴보고 싶다면 리포지토리를 확인해 보세요! 여러분의 피드백, 버그 리포트, 또는 풀 리퀘스트 (pull requests)를 환영합니다 (도구는 언제나 더 필요하니까요).
👉 GitHub에서 Codey 확인하기: github.com/varad-13/codey
댓글로 여러분의 생각을 알려주세요! 다음에 어떤 도구를 추가하면 좋을까요?
AI 자동 생성 콘텐츠
본 콘텐츠는 Dev.to AI tag의 원문을 AI가 자동으로 요약·번역·분석한 것입니다. 원 저작권은 원저작자에게 있으며, 정확한 내용은 반드시 원문을 확인해 주세요.
원문 바로가기