PentAGI는 "침투 테스트(모의 해킹)를 스스로 계획하고 실행하는 자율 멀티 에이전트 시스템"이다. 이름은 Penetration testing(침투 테스트) + AGI(범용 인공지능)의 합성어다. 사용자가 "이 서버의 보안 취약점을 점검해줘" 같은 목표(goal) 하나만 주면, 시스템이 알아서 계획을 세우고 → 필요한 도구를 설치하고 → 격리된 리눅스 환경에서 점검을 수행하고 → 발견한 문제를 보고서로 정리한다.
현실의 모의 해킹 팀에는 계획을 짜는 리드, 스크립트를 짜는 개발자, 도구를 세팅하는 엔지니어, 실제 점검을 하는 펜테스터, 자료를 찾는 리서처, 보고서를 쓰는 작성자가 있다. PentAGI는 이 역할들을 각각 서로 다른 AI 에이전트로 만들고, 리드 에이전트가 "너는 코드 짜, 너는 도구 설치해"라고 도구 호출로 지시하며 팀을 지휘한다.
그리고 이 팀의 "작업실"은 언제든 폐기할 수 있는 일회용 Docker 컨테이너다 — 안에서 무슨 일이 벌어지든 호스트(내 컴퓨터)는 안전하게 격리된다.
"AI가 해킹을 자동화한다"는 콘셉트 자체는 새롭지 않다. PentAGI가 트렌딩에 오른 이유는 콘셉트가 아니라 완성도와 자기호스팅(self-hosted) 스택의 깊이에 있다. 데모 수준이 아니라, 실무에서 돌릴 수 있게 관측성·메모리·격리·다중 LLM 지원까지 전부 갖춰 놓은 점이 눈에 띈다.
OpenAI·Anthropic·Gemini·Bedrock·DeepSeek·GLM·Kimi·Qwen·Ollama 등 10개 제공자를 기본 내장하고, 그 위에 OpenAI 호환 엔드포인트라면 무엇이든(custom) 붙일 수 있다. OpenRouter·DeepInfra 같은 애그리게이터도 지원한다. 심지어 vLLM + Qwen3.5-27B 로컬 배포 가이드까지 제공해, 외부에 데이터를 안 보내고 사내에서 완전히 돌리는 시나리오를 문서화했다. 보안 도구에서 "데이터가 밖으로 안 나간다"는 건 결정적 장점이다.
14개 에이전트 역할(primary_agent, pentester, coder, searcher …) 각각에 서로 다른 모델·파라미터를 바인딩할 수 있다. 판단이 중요한 펜테스터엔 고성능 모델을, 단순 검색·요약엔 저렴한 모델을 붙여 비용과 성능을 역할 단위로 최적화한다. 이 발상은 일반 에이전트 앱 설계에도 그대로 응용된다.
모든 LLM 호출·토큰·비용은 Langfuse로, 시스템 트레이스·메트릭·로그는 OpenTelemetry → Jaeger / VictoriaMetrics / Loki → Grafana로 흘러간다. 자율 에이전트는 "블랙박스"라는 불신을 받기 쉬운데, PentAGI는 관측성 스택을 1급 시민으로 내장해 그 불신을 정면으로 해소한다.
에이전트가 실행하는 모든 명령은 오직 Docker 컨테이너 안에서만 돈다. 기본 이미지는 보안 도구가 잔뜩 든 vxcontrol/kali-linux. Docker-in-Docker가 필요하면 TLS 인증으로 감싸고, 웹 스크래핑도 별도 격리 브라우저 컨테이너에서 한다. "자율 AI가 내 호스트를 건드리면?"이라는 가장 큰 공포를 설계 단계에서 봉쇄했다.
| 비교 축 | PentAGI의 선택 | 흔한 대안 |
|---|---|---|
| LLM 종속 | 10+ 제공자 + 로컬(vLLM/Ollama) | OpenAI 하나에 고정 |
| 실행 격리 | Docker 컨테이너 강제 + DinD TLS | 호스트에서 직접 실행(위험) |
| 기억 | pgvector(벡터) + Graphiti(그래프) 이중 메모리 | 대화 컨텍스트만(휘발성) |
| 관측성 | Langfuse + OTEL + Grafana 내장 | 로그 print 수준 |
| 배포 | 완전 self-hosted(docker compose) | SaaS 종속 |
README는 스스로를 MITRE CALDERA 같은 BAS(공격 시뮬레이션) 제품과 선을 긋는다. CALDERA는 "미리 정해진 시나리오"를 재생하는 방식인데, PentAGI는 정해진 각본 없이 LLM이 그때그때 판단해 계획을 만든다. 대신 위험할 수 있는 "에이전트가 스스로 작성한 공격 스크립트" 같은 부분은 개념/미래 과제로 신중히 남겨뒀다.
심장부는 순수 Go로 쓰였다. 웹 서버는 Gin, API는 REST와 GraphQL(gqlgen, 스키마 우선) 두 가지를 동시에 제공하며, GraphQL 구독(subscription)은 웹소켓(gorilla/websocket)으로 실시간 스트리밍한다.
| 레이어 | 선택 | 버전 / 메모 |
|---|---|---|
| 언어 | Go | 1.24.1 |
| HTTP 서버 | gin-gonic/gin | v1.10.0 |
| GraphQL | gqlgen + vektah/gqlparser | v0.17.57 · 구독 지원 |
| DB 접근 | sqlc + pgx/v5 + goose 마이그레이션 | pgx v5.8.0 · goose v3.19.2 |
| 벡터 | pgvector/pgvector-go | v0.1.1 |
| LLM 라이브러리 | vxcontrol/langchaingo (langchaingo 포크) | v0.1.14-update.5 |
| 지식 그래프 | vxcontrol/graphiti-go-client | v0.9.0 (Neo4j 백엔드) |
| 컨테이너 제어 | docker/docker SDK | v28.3.3 |
| 인증 | golang-jwt/jwt v5 · coreos/go-oidc | JWT + OAuth(GitHub/Google) |
| 설치 TUI | charmbracelet Bubble Tea | v1.3.10 (터미널 마법사) |
| 관측성 SDK | OpenTelemetry Go | v1.39.0 (traces/metrics/logs) |
UI는 최신 React 19.2 + TypeScript + Vite 조합. 데이터 통신은 Apollo Client v4로 GraphQL을 쓰고, 실시간 업데이트는 graphql-ws(웹소켓)로 받는다. 컴포넌트는 Radix UI + Tailwind(shadcn 스타일).
가장 인상적인 부분은 브라우저 안에 진짜 터미널이 떠 있다는 것 — xterm.js v6(fit·search·webgl·web-links 애드온)로 에이전트가 컨테이너에서 실행하는 명령의 출력이 실시간으로 흘러간다. 여기에 코드 편집용 Monaco, 리치 텍스트 TipTap v3, PDF 보고서 렌더링 @react-pdf/renderer v4, 차트 Recharts v3까지 얹었다.
가장 배울 점이 많은 부분. PentAGI는 하나의 거대한 compose가 아니라 목적별로 쪼갠 여러 개의 compose 파일을 겹쳐 쓴다. 필요 없는 스택은 빼고 돌릴 수 있는 "모듈식" 배포다.
| compose 파일 | 띄우는 것 | 역할 |
|---|---|---|
| docker-compose.yml | pentagi · pgvector · scraper · postgres-exporter | 본체(필수) |
| -observability.yml | Grafana 11.4 · OTEL Collector · VictoriaMetrics · Loki · Jaeger · cAdvisor | 시스템 관측성(선택) |
| -langfuse.yml | Langfuse v3 · ClickHouse · Redis · MinIO · Postgres 16 | LLM 분석(선택) |
| -graphiti.yml | Graphiti API · Neo4j | 지식 그래프(선택) |
실제 명령 실행용 샌드박스 기본 이미지는 vxcontrol/kali-linux이며, 작업 성격에 따라 이미지를 자동 선택한다. 벡터 저장소는 vxcontrol/pgvector(PostgreSQL + pgvector 확장), 웹 정찰용 브라우저는 격리된 vxcontrol/scraper 컨테이너가 맡는다.
PentAGI 아키텍처를 이해하는 열쇠는 딱 두 폴더다: backend/pkg/controller/(일감을 계층으로 쪼개는 Worker들)와 backend/pkg/providers/(실제 LLM을 굴리는 두뇌). 이 둘이 어떻게 맞물리는지 먼저 전체 그림부터 보자.
일감은 Flow → Task → Subtask 3단계로 내려간다. controller/flow.go의 FlowWorker가 최상위이고, 이벤트 루프(worker() → processInput() → runTask() → execTask())를 돈다. 하나의 Task는 최대 15개(TasksNumberLimit = 15)의 Subtask로 쪼개지는데, 이 분해를 generator 에이전트가 하고 refiner 에이전트가 다듬는다. 별도로 AssistantWorker(assistant.go)는 자율 파이프라인과 분리된 대화형 채팅 에이전트다.
큰 프로젝트(Flow)를 받으면 PM은 그걸 몇 개의 큰 작업(Task)으로 나누고, 각 작업을 다시 실무자가 처리할 작은 티켓(Subtask)으로 쪼갠다. PentAGI에서 이 "쪼개기"를 사람이 아니라 generator라는 AI 역할이 하고, refiner라는 다른 AI가 "이 티켓 순서 이상한데?" 하며 검수한다.
여기가 PentAGI 설계에서 가장 배울 점이 많은 부분이다. 14개 역할은 pkg/providers/pconfig/config.go의 ProviderOptionsType에 열거돼 있다: primary_agent · assistant · simple · simple_json · adviser · generator · refiner · searcher · enricher · coder · installer · pentester · reflector. 각 역할은 서로 다른 모델/제공자/파라미터에 바인딩될 수 있다.
그런데 이 에이전트들이 어떻게 협업할까? 정답은 "전문가를 하나의 도구(tool)로 노출한다"는 것. pkg/tools/tools.go에서 primary_agent의 도구 목록에는 advice · coder · maintenance(installer) · memorist · pentester · search가 들어 있다. 즉 지휘자가 pentester라는 이름의 도구를 호출하면, 그게 곧 펜테스터 에이전트에게 일을 위임하는 것이다. 위임받은 전문가는 자기만의 제한된 도구 세트로 짧은 체인을 돌고, *_result라는 배리어 도구(hack_result, code_result, search_result …)를 호출해 결과를 반납하며 체인을 끝낸다.
# 개념적 흐름 (실제 코드가 아니라 설계 요약)
primary_agent
├─ tool: pentester # → 펜테스터 에이전트에게 위임
│ └─ 자기 도구: hack_result·advice·coder·terminal·file …
│ └─ hack_result(...) # 배리어 → 체인 종료, 결과 반납
├─ tool: coder # → 코더 에이전트에게 위임
│ └─ code_result(...)
└─ tool: search # → 검색기 에이전트에게 위임
└─ search_result(...)
서브 에이전트를 별도의 특별한 개념으로 두지 않고 그냥 도구 하나로 취급하면, 지휘 에이전트 입장에서는 "nmap 실행"이나 "펜테스터에게 위임"이나 똑같은 도구 호출이다. 코드가 단순해지고, 새 전문가를 추가하기도 쉽다. 일반적인 LLM 에이전트 앱을 설계할 때 그대로 훔쳐 쓸 수 있는 패턴.
performer.go (엔진의 심장)가장 정독할 가치가 있는 파일은 pkg/providers/performer.go의 performAgentChain이다. 자율 에이전트가 "생각 → 도구 실행 → 결과 반영 → 다시 생각"을 반복하는 루프의 실체가 여기 있다. 대략 이렇게 돈다:
이 루프가 담고 있는 실전 노하우들이 배울 거리다:
done · ask · subtask_list · subtask_patch와 *_result 세트가 호출되면 체인을 끝낸다. "언제 멈출지"를 도구로 명시화한 것.pkg/providers/provider/provider.go의 Provider 인터페이스가 10개 백엔드(openai · anthropic · gemini · bedrock · ollama · custom · deepseek · glm · kimi · qwen)를 통일한다. 핵심 메서드는 Call · CallEx · CallWithTools(스트리밍) 그리고 비용 추적용 GetUsage / GetPriceInfo다.
재미있는 디테일: 제공자마다 도구 호출 ID 형식이 제각각이라, GetToolCallIDTemplate이 이를 정규화한다(sync.Once로 캐시). 여러 LLM을 섞어 쓸 때 실제로 부딪히는 "사소하지만 치명적인" 호환성 문제를 어떻게 흡수하는지 보여주는 좋은 사례다.
flow마다 컨테이너 1개를 생성(RunContainer). 펜테스트 작업이면 vxcontrol/kali-linux. flow별 작업 폴더를 바인드 마운트하고, 재시작 정책 on-failure(최대 5회), 네트워크는 host 모드(직접 스캔용) 또는 브리지+명시적 포트 바인딩 중 선택. DOCKER_INSIDE=true면 호스트 도커 소켓을 마운트하는 Docker-in-Docker이며, README는 이 경계를 "TLS 인증으로 보호되는 DinD"라고 명시한다. 에이전트가 외부와 닿는 통로는 오직 terminal·file 도구뿐이고, 모든 명령은 termlog에 기록된다.
pkg/tools/memory.go는 pgvector 벡터 저장소를 감싼다. store_guide/answer/code · search_* 도구로 유사도 검색(임계값 0.2, 상위 3개)을 하고 flow_id/task_id/doc_type으로 필터링한다. README는 기억을 장기(영구)·작업(현재 맥락)·에피소드(과거 행동·성공 패턴) 3계층으로 설명한다. 여기에 더해 Graphiti/Neo4j 지식 그래프가 에이전트 응답·도구 실행을 flow-<id> 단위로 저장해 관계를 추적한다(graphiti_search 도구). 임베딩 기본값은 text-embedding-ada-002.
시스템 텔레메트리는 OTEL로 트레이스→Jaeger, 메트릭→VictoriaMetrics, 로그→Loki로 보내 Grafana에서 본다. 별도로 Langfuse가 LLM 전용 분석을 맡아 flow마다 트레이스를 열고(session = flow-<id>) 모든 에이전트 호출·도구 호출·생성의 토큰 사용량과 비용을 추적한다. "자율 에이전트는 블랙박스"라는 문제를 관측성으로 정면 돌파.
"그래프"라는 말이 자주 나오지만, 여기서 그래프는 Graphiti/Neo4j 지식 그래프를 뜻한다. 오케스트레이션은 파이썬의 LangGraph 같은 상태머신이 아니라, Go로 손수 짠 Worker 계층 + 도구 위임으로 구현했다. LLM 계층은 langchaingo 포크(vxcontrol/langchaingo) 위에 올라가 있다.
구조를 읽는 요령: "일감을 쪼개는 곳"은 controller/, "LLM을 굴리는 곳"은 providers/, "손발이 되는 도구"는 tools/. 이 세 폴더만 이해하면 시스템의 80%가 보인다. 나머지(server·database·observability)는 그 셋을 떠받치는 인프라다.
| 정독 우선순위 | 파일 | 배우는 것 |
|---|---|---|
| ★★★ | pkg/providers/performer.go | 도구 호출 루프·리플렉터·요약·재시도 — 엔진의 핵심 |
| ★★★ | pkg/providers/pconfig/config.go | 14개 역할과 역할별 모델 바인딩 |
| ★★★ | pkg/tools/tools.go + registry.go | 에이전트별 도구 세트 조립 · 위임/배리어 도구 |
| ★★ | pkg/providers/provider/provider.go | 10개 LLM을 하나로 묶는 추상화 |
| ★★ | pkg/docker/client.go | 샌드박스 프로비저닝 · 네트워크 모드 · DinD |
| ★★ | pkg/controller/flow.go | Worker 이벤트 루프 · flow 생명주기 |
| ★ | pkg/templates/prompts/*.tmpl | 각 역할의 시스템 프롬프트(자연어로 된 설계도) |
파이썬 LangGraph 없이도 계층형 Worker + 도구 위임만으로 견고한 멀티 에이전트를 만들 수 있다는 증명. 특히 "에이전트를 도구로 노출"하는 발상, 배리어 도구로 종료 조건을 명시하는 방식, 리플렉터로 궤도 이탈을 교정하는 기법은 언어 불문하고 응용 가능하다.
Provider 인터페이스 하나로 10개 백엔드를 통일하고, 제공자별 도구 호출 ID 포맷 차이 같은 미묘한 비호환까지 흡수하는 방법. "우리 서비스를 특정 LLM에 종속시키지 않으려면?"의 실전 답안이다.
docker SDK로 샌드박스를 프로그래밍적으로 띄우고, host/bridge 네트워크 모드를 상황에 맞게 고르고, DinD를 TLS로 감싸는 실전 패턴. "위험한 코드를 안전하게 실행"하는 모든 시스템(온라인 저지, CI 러너, 코드 인터프리터)에 그대로 적용된다.
벡터 검색(유사도)과 그래프(관계)를 함께 쓰는 이유를 코드로 확인할 수 있다. "무엇과 비슷한가"는 pgvector가, "무엇과 연결됐는가"는 Graphiti가 답한다.
Langfuse로 LLM 비용·토큰을, OTEL로 시스템 트레이스를 나눠 잡는 2트랙 관측성. LLM 앱을 운영하려면 반드시 필요한 인프라를 어떻게 붙이는지 참고서가 된다.
PentAGI는 본인이 소유했거나 명시적 서면 허가를 받은 시스템에 대해서만 사용해야 한다. 허가 없이 타인의 시스템을 스캔·공격하는 것은 대부분의 나라에서 범죄다(한국은 정보통신망법 위반). 이 문서 역시 시스템의 설계·엔지니어링을 공부하기 위한 것이지 실제 공격 방법을 배우기 위한 것이 아니다. 학습은 자기 소유의 격리된 랩 환경(DVWA·의도적 취약 VM 등)에서만.
레포를 클론해 backend/pkg/providers/pconfig/config.go에서 14개 역할을 눈으로 확인하고, pkg/templates/prompts/의 .tmpl들을 읽으며 "각 역할에게 뭐라고 시켰는지"를 자연어로 파악해보라. 코드를 돌리지 않고도 에이전트 설계 철학의 절반은 프롬프트에 담겨 있다.
| 항목 | 요구사항 |
|---|---|
| 런타임 | Docker + Docker Compose (Podman·rootless도 지원) |
| 최소 CPU/RAM | 2 vCPU · 4GB RAM |
| 디스크 | 최소 20GB 여유 |
| GPU | 불필요 (LLM 추론은 기본적으로 원격 API. 로컬 모델은 Ollama/vLLM로 선택적) |
| 네트워크 | 인터넷 접속 필요 |
| 필수 키 | LLM 제공자 최소 1개 (OPENAI/ANTHROPIC/GEMINI/DEEPSEEK/GLM/KIMI/QWEN/Bedrock/Ollama 중) |
| 선택 키 | 검색용 (Google CSE · Tavily · Traversaal · Perplexity) |
| 설치 | Go/Bubble Tea 대화형 설치 마법사 (Linux/Windows/macOS · amd64/arm64) |
주의: 위 최소 사양은 본체(pentagi + pgvector + scraper)만 돌릴 때다. 관측성(Grafana·Loki·ClickHouse·Jaeger…)·Langfuse·Graphiti 스택을 추가로 켜면 각각 여러 컨테이너를 더 띄우므로 RAM이 훨씬 더 필요하다. 도커 소켓 접근이 필요한데, README는 이것이 사실상 root 권한과 동등함을 경고한다 — 신뢰할 수 있는 호스트에서만 돌릴 것.
레포를 git clone --depth 1로 받아 pkg/templates/prompts/의 .tmpl을 전부 읽고, 14개 역할이 각각 "무엇을 하라"고 지시받는지 한 줄씩 요약한 표를 만들어보라. 코드를 실행하지 않고도 시스템의 의도를 재구성하는 연습.
지휘 에이전트 1개 + 전문가 2개(예: calculator, web_search)를 만들되, 전문가를 지휘자의 도구로 노출하고 각 전문가가 *_result 배리어 도구로 결과를 반납하며 종료하도록 짜보라. PentAGI 위임 구조의 핵심만 100줄로.
Go든 Python이든, 코드로 컨테이너를 띄워 임의 셸 명령을 실행하고 출력을 스트리밍한 뒤 컨테이너를 폐기하는 미니 러너를 작성하라. 네트워크를 none으로 막았을 때와 host일 때의 차이를 체감해볼 것. (안전한 커맨드로만!)
performer.go를 참고해, 모델이 도구 없이 텍스트만 뱉으면 재촉하고(리플렉터), 대화가 일정 크기를 넘으면 히스토리를 LLM으로 압축하는(요약) 에이전트 루프를 직접 구현하라. 무한 루프 감지까지 넣으면 완성.
과제 4의 루프에 관측성을 얹어라. 각 LLM 호출의 토큰·비용을 트레이스로 남기고, 도구 실행을 스팬으로 감싸 Jaeger나 Langfuse에서 흐름을 눈으로 확인해보라. "블랙박스 → 관측 가능"의 체감.
| 주차 | 주제 | 학습 내용 |
|---|---|---|
| 1주차 | LLM 도구 호출 기초 | function calling / tool use 개념, ReAct 패턴, 에이전트 루프의 기본형. langchaingo 또는 원하는 언어의 SDK로 도구 1~2개 붙여보기. |
| 2주차 | 멀티 에이전트 & 위임 | PentAGI의 "에이전트=도구" 패턴, 배리어 도구, 역할별 모델 바인딩. pconfig·tools.go·registry.go 정독 + 과제 2. |
| 3주차 | 격리 실행 & Docker SDK | docker/docker Go SDK, 네트워크 모드, Docker-in-Docker와 TLS, 바인드 마운트 보안. docker/client.go 정독 + 과제 3. |
| 4주차 | 메모리 & 관측성 | pgvector 유사도 검색, Graphiti/Neo4j 지식 그래프, 임베딩, Langfuse + OpenTelemetry 2트랙 관측성. memory.go + 과제 4·5. |
가장 먼저 단일 에이전트의 도구 호출 루프를 확실히 이해하라(1주차). 그게 되면 여러 에이전트를 도구로 엮는 것은 자연스러운 확장이다(2주차). 격리와 기억·관측성은 그 위에 얹는 "운영 레이어"이므로 뒤로 미뤄도 된다.
controller/의 Worker들이 담당한다.done · ask · *_result처럼, 호출되면 에이전트 체인을 종료시키는 특별한 도구. "언제 멈출지"를 코드가 아니라 도구로 명시화한 것.performer.go의 performReflector.| 구분 | 링크 / 위치 |
|---|---|
| GitHub 저장소 | github.com/vxcontrol/pentagi |
| TrendShift | trendshift.io/repositories/15161 |
| 핵심 파일 | backend/pkg/providers/performer.go (에이전트 루프) |
| 핵심 파일 | backend/pkg/providers/pconfig/config.go (14개 역할) |
| 핵심 파일 | backend/pkg/tools/tools.go · registry.go (도구·위임) |
| 핵심 파일 | backend/pkg/docker/client.go (샌드박스) |
| 배포 가이드 | examples/guides/ (vLLM + Qwen 로컬 배포 등) |
| 기반 라이브러리 | github.com/vxcontrol/langchaingo (langchaingo 포크) |
| 연관 개념 | pgvector · Graphiti · Langfuse · OpenTelemetry 공식 문서 |