OpenHands(OpenHands/OpenHands, 옛 이름 OpenDevin)는 LLM에게 "사람 개발자가 쓰는 도구 일습"을 그대로 쥐여 주는 오픈소스 AI 소프트웨어 개발 에이전트입니다. 단순히 코드를 한 조각 뱉어 주는 챗봇이 아니라, 진짜 셸에서 명령을 실행하고, 파일을 직접 수정하고, 필요하면 브라우저로 문서를 찾아보며, 버그 수정·기능 구현 같은 과제를 여러 단계에 걸쳐 자율적으로 끝까지 해냅니다.
핵심은 모든 행동이 격리된 도커 컨테이너(샌드박스) 안에서 일어난다는 점입니다. 에이전트가 rm -rf 같은 위험한 명령을 내려도 그 피해는 컨테이너 안에 갇히고, 당신의 실제 컴퓨터는 안전합니다. 그래서 "AI에게 터미널을 맡긴다"는 무서운 아이디어를 실제로 쓸 만하게 만들었습니다.
OpenHands는 한 가지 모습이 아니라 여러 겹의 제품군으로 제공됩니다: 코드로 에이전트를 짜는 SDK, 터미널에서 바로 쓰는 CLI(Claude Code·Codex 같은 경험), 노트북에서 돌리는 로컬 GUI(이 저장소), 클라우드 호스팅, 그리고 기업용 셀프호스팅까지. 이 저장소는 그중 로컬 GUI + 앱 서버(REST API) + 웹 프론트엔드가 중심입니다.
보통의 코딩 챗봇은 "코드 알려줄게, 복붙은 네가 해"에서 끝납니다. 손이 없어 직접 실행해 보지 못하니, 틀려도 스스로 모릅니다. OpenHands의 에이전트는 다릅니다 — 자기만의 가상 PC(샌드박스)를 받아서, 직접 코드를 쓰고, 테스트를 돌려 보고, 에러가 나면 그 에러 로그를 읽고 고치는 일을 스스로 반복합니다.
이 "행동하고 → 결과를 보고 → 다시 행동하는" 순환이 OpenHands 설계의 심장입니다. 그 순환을 코드로 표현한 것이 바로 Action(행동) / Observation(관찰) 이벤트 루프입니다.
OpenHands는 AI 에이전트와 AI 코딩 어시스턴트라는 가장 뜨거운 두 주제의 한가운데에 있습니다. ★7만 5천이 넘는 별, 100개 넘는 릴리스, TikTok·NVIDIA·Google·Amazon 등 대기업 엔지니어들이 쓴다는 사회적 증명에 더해, 무엇보다 성능 숫자가 진짜입니다. 실제 깃허브 이슈를 자동으로 고치는 능력을 재는 SWE-bench Verified 벤치마크에서 77.6%를 기록해, 상용 도구들과 어깨를 나란히 하는 오픈소스 프로젝트입니다.
창에 코드를 뱉기만 하는 어시스턴트는 자기 코드가 진짜 도는지 확인하지 못합니다. 여러 파일을 오가며 디버깅하는 다단계 작업은 사람이 일일이 복붙·실행·피드백을 중계해야 하고, 그렇다고 AI에게 내 터미널을 직접 내주자니 잘못된 명령 한 줄에 파일이 날아갈까 무섭습니다.
에이전트에게 셸·파일 편집·브라우저·파이썬 실행 같은 진짜 도구를 주되, 그 도구를 일회용 도커 컨테이너 안에서만 쓰게 가둡니다. 에이전트는 행동→관찰을 스스로 반복하며 과제를 끝까지 밀고 가고, 위험은 컨테이너에 격리됩니다. 게다가 특정 모델에 묶이지 않아 Claude·GPT·Gemini·로컬 모델 무엇이든 꽂아 쓸 수 있습니다.
| 비교 항목 | OpenHands | 상용 CLI 에이전트 | 일반 코딩 챗봇 |
|---|---|---|---|
| 제공 형태 | SDK·CLI·GUI·클라우드·기업용 | 주로 CLI | 채팅 UI |
| 실행 환경 | 격리 도커 샌드박스 | 로컬 셸(직접) | 없음 |
| 모델 종속 | 무관(litellm 다중) | 보통 자사 모델 | 자사 모델 |
| 라이선스 | MIT(코어 전부 오픈) | 비공개/부분공개 | 비공개 |
| 확장 | MCP·스킬·마이크로에이전트 | 플러그인 | 제한적 |
| SWE-bench | 77.6% (Verified) | 비공개 또는 유사 수준 | 해당 없음 |
요약하면, OpenHands의 차별점은 "오픈소스인데 진짜 잘하고, 한 가지 모습이 아니라 라이브러리부터 기업 배포까지 한 줄기로 이어진다"는 데 있습니다. 코어 엔진(openhands-sdk)을 라이브러리로 떼어내, CLI도 GUI도 클라우드도 전부 같은 엔진을 공유하도록 만든 설계가 그 비결입니다.
한 문장으로: Python 3.12로 만든 코어 엔진을 FastAPI 앱 서버가 감싸고, React 19 단일 페이지 앱이 화면을 그리며, 모든 에이전트 작업은 도커 컨테이너(agent-server) 안에서 돌아갑니다. 코어 로직은 별도 패키지(openhands-sdk · openhands-agent-server · openhands-tools)로 분리돼 pip로 설치됩니다.
| 카테고리 | 라이브러리 | 역할 |
|---|---|---|
| 웹 서버 | FastAPI · Uvicorn · Starlette | REST API와 정적 파일 서빙(포트 3000) |
| 실시간 통신 | python-socketio · sse-starlette | 에이전트 이벤트를 브라우저로 실시간 스트리밍 |
| 코어 엔진 | openhands-sdk / -agent-server / -tools | 에이전트 루프·이벤트·도구. 별도 패키지로 분리(V1) |
| LLM 연동 | litellm · openai · anthropic · google-genai | 100여 종 LLM을 하나의 인터페이스로 호출 |
| 도구 확장 | fastmcp · mcp | Model Context Protocol로 외부 도구 플러그인 |
| 샌드박스 제어 | docker · kubernetes | 대화별 격리 컨테이너 생성·관리, 클라우드 확장 |
| 브라우저 에이전트 | browsergym-core · playwright | 에이전트가 실제 웹페이지를 탐색 |
| 코드 실행/파싱 | jupyter-kernel-gateway · tree-sitter · bashlex | 파이썬 셀 실행, 코드 구문 분석, 셸 파싱 |
| 영속화 | SQLAlchemy(async) · asyncpg/pg8000 · redis | 대화·설정 저장(Postgres), 캐시/세션(Redis) |
| 관측성 | opentelemetry · lmnr(Laminar) | 분산 트레이싱으로 에이전트 동작 추적 |
| 구분 | 기술 | 역할 |
|---|---|---|
| 프레임워크 | React 19 + React Router 7 | 단일 페이지 앱(SPA) 라우팅·렌더링 |
| 빌드 도구 | Vite 7 | 초고속 개발 서버·번들링 |
| 상태 관리 | Zustand + TanStack Query | 클라이언트 상태 / 서버 데이터 캐싱·동기화 |
| 실시간 | socket.io-client | 에이전트 이벤트 수신, 채팅 스트리밍 |
| 코드 에디터 | Monaco Editor | VS Code와 같은 엔진의 브라우저 코드 편집기 |
| 터미널 | xterm.js | 샌드박스 셸 출력을 브라우저에 그대로 표시 |
| 스타일/UI | Tailwind CSS v4 · HeroUI · framer-motion | 유틸리티 CSS, 컴포넌트 라이브러리, 애니메이션 |
| 국제화 | i18next | 한국어 포함 다국어 지원 |
openhands-sdk라는 독립 패키지로 떼어내 pip로 설치합니다. 덕분에 CLI·GUI·클라우드가 전부 같은 엔진을 공유합니다 — 한 곳을 고치면 모든 제품이 같이 좋아집니다. 자동차 회사가 같은 엔진을 세단·SUV·트럭에 공통으로 얹는 것과 같은 발상입니다.먼저 숲부터 봅시다. OpenHands의 심장은 의외로 단순한 하나의 루프입니다. 에이전트가 LLM으로 생각해서 행동(Action)을 하나 내면, 그 행동을 샌드박스 런타임이 실행해 관찰(Observation)을 돌려주고, 행동과 관찰이 이벤트 스트림에 쌓입니다. 다음 턴에 LLM은 그 쌓인 기록을 통째로 보고 다음 행동을 정합니다 — 과제가 끝날 때까지 이 순환이 반복됩니다.
OpenHands는 에이전트의 "현재 상태"를 통째로 저장하지 않습니다. 대신 일어난 모든 행동·관찰을 시간순 목록(이벤트 스트림)으로 보관하고, 상태는 그 목록을 재생해 얻습니다. 이 방식의 장점은 큽니다 — 대화를 그대로 재현·디버깅할 수 있고, 중간부터 이어서 돌릴 수 있으며, LLM에게 "지금까지의 전체 맥락"을 일관되게 넘길 수 있습니다.
"이슈를 하나 고쳐 줘"라고 시켰을 때 실제로 무슨 일이 벌어지는지, 대표 흐름 딱 하나만 끝까지 따라가 봅시다(엣지 케이스는 생략).
# "버그를 고쳐 줘" 한 번의 과제가 도는 모습 (개념 단순화)
① 사용자 메시지("이 테스트 깨지는 버그 고쳐줘")가 이벤트 스트림에 들어감
② Agent: LLM 호출 → "먼저 코드를 봐야겠다" → Action: CmdRun("cat app.py") ← 행동
③ Runtime: 샌드박스 컨테이너에서 cat 실행 → Observation: 파일 내용 ← 관찰
④ Agent: 내용을 보고 → Action: FileEdit(app.py 17번 줄 수정)
⑤ Runtime: 파일을 고침 → Observation: 변경 diff
⑥ Agent: "테스트로 확인하자" → Action: CmdRun("pytest")
⑦ Runtime: 테스트 실행 → Observation: 통과(혹은 또 에러 로그)
⑧ 통과하면 → Action: AgentFinish → 루프 종료. 에러면 ②로 되돌아가 반복
이 흐름에서 두 가지만 기억하면 OpenHands 코드 전체가 읽힙니다: (1) 모든 일은 Action을 만들고 → Runtime이 Observation으로 답하는 형태이고, (2) 그 사이를 잇는 게 이벤트 스트림이라는 것입니다.
가장 영리한 부분입니다. 앱을 도커로 띄울 때 docker-compose.yml은 호스트의 /var/run/docker.sock(도커 소켓)을 컨테이너 안으로 넣어 줍니다. 그러면 OpenHands 앱 컨테이너가 자기 옆에 형제 컨테이너(agent-server)를 새로 띄울 수 있게 됩니다. 대화(conversation) 하나가 시작될 때마다 전용 샌드박스 컨테이너가 하나 뜨고, 에이전트의 모든 bash·파일·브라우저 행동은 그 안에서만 실행됩니다.
# docker-compose.yml 핵심 발췌 — 왜 도커 소켓을 마운트하나
environment:
- AGENT_SERVER_IMAGE_REPOSITORY=ghcr.io/openhands/agent-server # 샌드박스 이미지
volumes:
- /var/run/docker.sock:/var/run/docker.sock # 형제 컨테이너를 띄울 권한
- ~/.openhands:/.openhands # 설정·대화 저장
ports:
- "3000:3000" # 웹 UI 접속 포트
도커 소켓을 컨테이너에 넘기면 사실상 호스트의 도커 전체를 제어할 수 있게 됩니다. 에이전트 작업을 진짜로 격리하는 대가로 앱 컨테이너에 큰 권한을 주는 셈이죠. 그래서 OpenHands는 신뢰할 수 있는 환경에서 돌리길 권하고, 기업용은 쿠버네티스(Kubernetes) 위에서 더 엄격히 격리하는 길을 따로 제공합니다.
지금의 저장소(openhands/ 패키지)는 놀랄 만큼 얇습니다 — app_server(새 FastAPI 서버)·server(호환용)·analytics가 거의 전부입니다. 무거운 두뇌는 openhands-sdk·openhands-agent-server·openhands-tools 패키지로 빠져나갔습니다. 앱 서버가 하는 일은 "대화를 관리하고, 샌드박스를 띄우고, 이벤트를 브라우저로 중계하는 오케스트레이터(지휘자)"입니다. 실제 연주(에이전트 실행)는 샌드박스 안의 agent-server가 합니다.
litellm 덕분에 Claude·GPT·Gemini·DeepSeek·로컬(Ollama) 무엇이든 같은 코드로 호출됩니다. 도구 쪽은 MCP(Model Context Protocol)로 확장합니다 — 외부 도구를 표준 규격으로 꽂으면 에이전트가 새 능력을 얻습니다. 여기에 특정 키워드가 나오면 자동으로 끼어드는 마이크로에이전트/스킬(저장소별·전역 지식 조각)까지 더해, "엔진은 그대로 두고 능력만 늘리는" 확장 구조를 완성합니다.
앱 서버 내부(openhands/app_server/)는 책임별로 폴더가 잘 나뉘어 있어, 이름만 읽어도 역할이 보입니다.
| 경로 | 역할 |
|---|---|
| app.py / v1_router.py | FastAPI 앱 생성과 라우터 묶음(엔드포인트 집결지) |
| app_conversation/ | 대화 1건의 수명주기 관리(시작·진행·종료) |
| sandbox/ | 대화별 격리 컨테이너(런타임) 생성·연결·정리 |
| event/ · event_callback/ | 이벤트 스트림 저장과 구독(브라우저로 중계) |
| mcp/ | Model Context Protocol 도구 연동 |
| integrations/ | GitHub·GitLab·Jira DC·Bitbucket·Azure DevOps·Forgejo 등 외부 서비스 연결 (Slack·Linear는 enterprise/ 한정) |
| git/ | 저장소 클론·브랜치·커밋 등 git 작업 |
| user/ · user_auth/ · secrets/ | 사용자·인증(JWT/OAuth)·비밀키 보관 |
| file_store/ · settings/ · config.py | 파일 저장소·설정·서버 구성(22KB의 큰 설정 모듈) |
| web_client/ · static.py | 빌드된 프론트엔드(React)를 서빙 |
구조를 읽는 요령: "두뇌는 pip 패키지(SDK)에, 지휘는 app_server에, 화면은 frontend에"라는 3분할만 기억하면 됩니다. 그래서 "에이전트가 어떻게 생각하는지"가 궁금하면 이 저장소가 아니라 openhands-sdk(software-agent-sdk 저장소)를, "어떻게 서비스되는지"가 궁금하면 app_server/를 보면 됩니다.
자율 에이전트의 본질인 "행동 → 관찰 → 다음 행동" 순환을 어떻게 데이터 구조로 깔끔히 표현하는지 배웁니다. 행동/관찰을 타입이 있는 객체로 정의하고, 이벤트 스트림(이벤트 소싱)으로 기록·재생하는 패턴은 코딩 에이전트뿐 아니라 모든 자율 에이전트에 일반화됩니다.
도커 소켓 마운트로 "형제 컨테이너"를 띄워 에이전트 작업을 가두는 사이드카 패턴, 컨테이너 수명주기 관리, 호스트와의 파일 마운트를 배웁니다. "신뢰할 수 없는 코드를 어디서 실행할 것인가"는 AI 시대 인프라의 핵심 질문입니다.
백엔드의 이벤트 스트림을 python-socketio로 푸시하고, 프론트엔드 socket.io-client가 받아 채팅·터미널에 흘려보내는 양방향 실시간 구조를 배웁니다. 서버-발(SSE)과 웹소켓의 차이, 스트리밍 UI 설계 감각을 얻습니다.
100여 종 LLM을 단일 인터페이스로 호출하는 게이트웨이 패턴, 모델별 토큰·비용·기능 차이를 추상화하는 법을 배웁니다. "한 줄 설정으로 Claude ↔ GPT ↔ 로컬 모델 교체"가 어떻게 가능한지 코드로 확인합니다.
엔진을 건드리지 않고 도구를 추가하는 Model Context Protocol 연동, 특정 키워드에 반응해 끼어드는 마이크로에이전트(지식 조각) 구조를 배웁니다. 플러그인형 확장 아키텍처의 좋은 실전 예시입니다.
코어 엔진을 pip 패키지로 분리해 CLI·GUI·클라우드가 공유하게 만드는 모듈 설계, FastAPI로 감싸 REST+WebSocket로 노출, React 19 SPA(Monaco·xterm)로 IDE급 UI를 만드는 전 과정을 배웁니다.
| 항목 | 최소 | 권장 |
|---|---|---|
| 도커(Docker) | 필수(샌드박스 런타임) | Docker Desktop 최신 |
| 운영체제 | macOS · Linux · Windows(WSL2) | Linux 또는 macOS |
| RAM | 4GB | 8GB+ (이미지·컨테이너 여유) |
| 디스크 | 수 GB(도커 이미지) | 10GB+ 여유 |
| GPU | 불필요(클라우드 LLM API 기반) | 로컬 모델(Ollama) 시 권장 |
| LLM API 키 | 1개(Anthropic Claude 권장) | 고성능 모델일수록 결과 우수 |
| 개발 시 추가 | Python 3.12+ · Node 22+ | 소스 빌드·기여할 때만 |
docker run으로 띄워 localhost:3000에서 GUI를 쓸 수 있습니다. 더 가볍게는 터미널용 OpenHands-CLI를 설치해 Claude Code처럼 바로 쓸 수도 있습니다. 어느 쪽이든 LLM API 키 하나만 있으면 됩니다(성능은 Claude Sonnet 계열이 권장됩니다).공식 도커 이미지를 docker run으로 띄우고 localhost:3000에 접속 → LLM API 키 입력 → "이 폴더에 간단한 할 일 목록 웹페이지를 만들어 줘" 같은 작은 과제를 시켜 봅니다. 에이전트가 파일을 만들고 셸을 두드리는 과정을 화면으로 관찰합니다.
결과물: 에이전트가 자동 생성한 첫 프로젝트 + 동작 관찰 노트
같은 과제를 다시 시키되, 이번엔 채팅 옆 패널에서 Action과 Observation이 번갈아 쌓이는 모습을 한 줄씩 따라갑니다. "에이전트가 어떤 행동을 했고, 샌드박스가 무엇으로 답했는지"를 적어 §4의 해피패스 그림과 대조해 봅니다.
결과물: 실제 과제의 Action/Observation 추적 기록
설정에서 LLM을 Claude → GPT → (가능하면 Ollama 로컬 모델)로 바꿔 가며 같은 버그 수정 과제를 시킵니다. 성공 여부·걸린 턴 수·비용을 표로 비교해, litellm이 만들어 주는 "모델 무관" 설계의 효용을 체감합니다.
결과물: 모델별 성공률·턴 수·비용 비교표
.openhands/skills 구조를 본떠, 특정 키워드(예: 우리 팀 코드 컨벤션)가 나오면 자동으로 끼어드는 지식 조각을 만들어 봅니다. 엔진을 건드리지 않고 능력을 늘리는 확장 패턴을 직접 경험합니다.
결과물: 작동하는 커스텀 마이크로에이전트
openhands-sdk로 파이썬 몇 줄짜리 커스텀 에이전트를 정의해 로컬에서 돌려 봅니다. 더 나아가 이 저장소를 직접 make build로 빌드해 프론트엔드(React)+앱 서버 전체를 띄우고, app_server/의 라우터·샌드박스 코드를 읽으며 한 요청이 어떻게 컨테이너까지 도달하는지 추적합니다.
결과물: SDK 기반 미니 에이전트 + 로컬 풀스택 구동 기록
| 주차 | 주제 | 학습 내용 · 자료 |
|---|---|---|
| 1주차 | LLM 에이전트 기초 | "행동(도구 호출) → 관찰 → 반복" 루프의 개념. ReAct 패턴, 도구 사용(tool use) 이해. OpenHands를 직접 써 보며 감 잡기 |
| 2주차 | 이벤트 소싱 & Action/Observation | 상태 대신 이벤트 목록으로 시스템을 표현하는 법. openhands-sdk의 이벤트·행동·관찰 타입 정의 정독 |
| 3주차 | 도커 샌드박스 인프라 | 도커 소켓·사이드카 컨테이너·볼륨 마운트. docker-compose.yml과 containers/ Dockerfile 분석, 직접 컨테이너 생성 실습 |
| 4주차 | FastAPI + socket.io 실시간 백엔드 | app_server/ 구조 분석. REST 라우터·이벤트 콜백·웹소켓 스트리밍을 작은 데모로 재현 |
| 5주차 | React 19 SPA 실전 | React Router 7·Zustand·TanStack Query·socket.io-client. Monaco·xterm.js를 붙여 미니 웹 IDE 만들기 |
| 6주차 | 통합 — 미니 코딩 에이전트 | SDK로 bash·파일편집 도구만 가진 에이전트를 만들고, 도커 샌드박스에서 실행 + 간단한 웹 UI로 이벤트 표시 |
| 키워드 | 설명 |
|---|---|
| OpenHands | LLM에게 셸·파일·브라우저 도구를 주는 오픈소스 AI 개발 에이전트. 옛 이름 OpenDevin |
| Agent | LLM으로 다음 행동을 결정하는 "두뇌". 행동을 내고 관찰을 받아 반복 |
| Action(행동) | 에이전트가 하려는 일을 표현한 객체. CmdRun·FileEdit·BrowseURL·Message·AgentFinish 등 |
| Observation(관찰) | 행동의 결과. 명령 출력·파일 변경·페이지 내용·에러 로그 |
| Event Stream | 행동·관찰이 시간순으로 쌓인 목록. 대화의 단일 진실원(이벤트 소싱) |
| Runtime | 행동을 실제로 실행하는 격리 실행 환경(도커 샌드박스) |
| Sandbox | 대화마다 뜨는 일회용 도커 컨테이너. 위험한 행동을 가두는 울타리 |
| agent-server | 샌드박스 안에서 에이전트를 실행·노출하는 서버 이미지(ghcr.io/openhands/agent-server) |
| app_server | 대화 관리·샌드박스 기동·이벤트 중계를 맡는 FastAPI 오케스트레이터(V1) |
| openhands-sdk | 에이전트 엔진을 떼어낸 pip 패키지. CLI·GUI·클라우드가 공유 |
| SWE-bench Verified | 실제 깃허브 이슈를 자동 해결하는 능력을 재는 벤치마크. OpenHands 77.6% |
| litellm | 100여 종 LLM을 하나의 인터페이스로 호출하는 게이트웨이 라이브러리 |
| MCP | Model Context Protocol. 외부 도구를 표준 규격으로 에이전트에 꽂는 방식 |
| 마이크로에이전트/스킬 | 특정 키워드에 반응해 끼어드는 지식 조각. 엔진 수정 없이 능력 확장 |
| socket.io | 브라우저-서버 양방향 실시간 통신. 에이전트 이벤트 스트리밍에 사용 |
| Monaco / xterm.js | VS Code 엔진의 브라우저 코드 에디터 / 브라우저 터미널 에뮬레이터 |
| browsergym / playwright | 에이전트가 실제 웹페이지를 탐색하게 해 주는 브라우저 자동화 도구 |
| 도커 소켓 마운트 | /var/run/docker.sock을 컨테이너에 넘겨 형제 컨테이너를 띄우는 기법 |