andrewyng/openworker · 제작 Andrew Ng 팀 · 백엔드 Python 3.10+ · 데스크탑 Tauri(Rust)+React 18 · 엔진은 aisuite 기반 · 라이선스 MIT · Open Beta · shallow clone 소스 직접 분석)
대부분의 AI 도우미는 "물어보면 → 답을 알려주는" 챗봇이다. 요약도 해주고 코드도 짜주지만, 결국 "이렇게 하세요"라는 안내를 남기고 실행은 내 몫으로 넘긴다. OpenWorker는 이 틀을 깬다. "고객 브리핑 준비해줘", "내 캘린더 정리해줘", "Jira랑 GitHub 걸쳐서 릴리스 어디까지 왔는지 확인해줘" 같은 결과(outcome)를 말하면, 그것을 단계로 쪼개 내 파일·앱을 넘나들며 작업하고, 열어서 바로 쓸 수 있는 파일 / 보낸 메시지 / 갱신된 캘린더를 돌려준다.
일반 챗봇이 "자료 찾아서 요약 읽어주는 인턴"이라면, OpenWorker는 "자리 옆에 앉아 실제 업무 계정으로 문서를 만들고, Slack에 답장하고, 캘린더를 고쳐놓는 동료"다.
단, 이 동료는 중요한 일을 하기 전엔 반드시 물어본다. 메시지를 보내거나, 캘린더를 바꾸거나, 터미널 명령을 실행하기 직전에 "이거 해도 될까요?"라고 확인을 받는다. 승인·수정·거부는 전부 내가 한다.
그리고 OpenWorker는 로컬 우선(local-first)이다. 에이전트 루프, 대화 기록, 연동 토큰, 모델 키가 전부 내 컴퓨터의 로컬 시크릿 저장소에 있다. 유일한 클라우드 조각은 연동(OAuth) 악수를 중개하는 작은 서비스뿐이고, 로그인 없이도 수동 발급한 API 키로 연동을 쓸 수 있다. "내 데이터가 어디로 새는가"를 스스로 통제할 수 있다는 뜻이다.
이 저장소가 트렌딩에 오른 데는 세 가지 결이 겹친다: (1) 만든 사람의 무게, (2) "결과물 중심" 철학, (3) 모델·데이터 주권을 사용자에게 돌려주는 설계.
OpenWorker는 Coursera·DeepLearning.AI 창립자이자 AI 교육의 대명사인 앤드류 응(Andrew Ng) 팀이 공개했다. 흥미로운 점은 이 엔진이 그가 만든 또 다른 오픈소스 aisuite 위에 세워졌다는 것 — OpenWorker는 원래 aisuite 저장소 안에서 개발되다가 독립한 프로젝트다. 즉 "aisuite로 실제 제품급 에이전트를 어떻게 조립하는가"의 살아있는 레퍼런스이기도 하다.
README가 반복해 강조하는 차별점은 deliverable(산출물)이다. 문서·스프레드시트·리포트·웹페이지가 열어서 공유할 수 있는 실제 파일로 떨어진다. Slack에서 @OpenWorker를 멘션하면 내 데스크탑에서 세션이 열리고, 내 도구로 작업이 이뤄진 뒤, 답이 스레드 답장으로 돌아온다. 아침 브리핑·주간 리포트 같은 반복 업무는 스케줄(automation)로 돌려두면 앱 안에 전체 기록과 함께 결과가 쌓인다.
보통의 "AI 에이전트 데모"는 화면 안에서만 뭔가 하는 척하다 끝난다. OpenWorker의 승부처는 "산출물이 파일 시스템·업무 앱에 실제로 남는가"이다. 그래서 파일 도구, 25개+ 커넥터, 터미널이 1급 시민으로 붙어 있다.
OpenWorker는 특정 모델 회사에 묶이지 않는다(BYOM · Bring Your Own Model). 제공자를 고르고 키를 붙이면 언제든 바꿀 수 있다. 기본 지원만 해도 OpenAI · Anthropic · Google Gemini · Inkling(Thinking Machines) · GLM(Z.ai) · DeepSeek · Kimi(Moonshot) · Qwen · MiniMax · Mistral · Grok(xAI), 여기에 Together·Fireworks의 오픈웨이트, 그리고 Ollama 완전 로컬까지 포함된다. 데스크탑에서 로컬로 돌기 때문에 프라이버시 통제권이 사용자에게 있다.
| 구분 | 일반 클라우드 AI 비서 | OpenWorker |
|---|---|---|
| 실행 위치 | 제공사 서버 | 내 데스크탑(로컬 에이전트 서버) |
| 산출물 | 채팅 답변 위주 | 실제 파일·메시지·캘린더 변경 |
| 모델 선택 | 고정(그 회사 모델) | BYOM — 11개+ 제공자 & Ollama 로컬 |
| 위험 행동 | 제공사 정책 | 쓰기·전송·셸은 사용자 승인 게이트 |
| 데이터 경로 | 서버 경유 | local-first, OAuth 중개만 클라우드 |
| 라이선스 | 대개 독점 | MIT 오픈소스 |
OpenWorker는 세 개의 몸통으로 이루어진다: ① 로컬 에이전트 서버(Python), ② 데스크탑 앱(Tauri 셸 + React UI), ③ 음성 입력용 STT 사이드카(Rust). 세 겹을 하나씩 뜯어보자.
coworker/)
핵심 두뇌다. pyproject.toml의 패키지 이름은 흥미롭게도 coworker(제품명 OpenWorker와 별개의 내부 이름)이며 "provider-agnostic agentic coworker runtime"으로 스스로를 정의한다.
| 영역 | 라이브러리 / 기술 | 역할 |
|---|---|---|
| 에이전트 프레임워크 | aisuite(git 커밋 고정 핀) | 제공자 통합 chat-completions API + 툴킷·MCP·트레이싱 레이어 |
| 모델 제공자 | openai · anthropic · google-genai | Claude Messages API·Gemini 네이티브 제공자 직접 포함 |
| 웹 서버 | FastAPI + uvicorn | 로컬 에이전트 HTTP 서버(--port 8765) |
| TUI | textual | 터미널 기반 UI(GUI 없이도 사용) |
| 데이터 검증 | pydantic v2 | 모델/설정 스키마 |
| 도구 연동 | mcp>=1.1 | MCP 클라이언트(stdio + streamable-http) |
| 스케줄 | croniter | automation 스케줄러의 cron 다음-실행 계산 |
| 웹 검색 | ddgs(DuckDuckGo) | 키 없이 쓰는 기본 검색(Tavily·Brave는 httpx) |
| pypdf + pypdfium2 | 텍스트 추출 + 페이지 래스터화(AGPL PyMuPDF 회피) | |
| 메시징(옵션) | slack-bolt · python-telegram-bot · aiohttp | 인바운드 메시지 수신 리스너 |
| 브라우저(옵션) | playwright | 인터랙티브 브라우저 자동화 |
pyproject.toml은 aisuite @ git+https://.../aisuite.git@1b4bbf30...처럼 특정 커밋 해시를 물고 있다. OpenWorker가 이 저장소에서 임포트된 그 시점의 aisuite에 맞춰졌기 때문 — 다음 aisuite 릴리스가 나오면 PyPI 핀으로 바꾼다고 주석에 적혀 있다.
surfaces/gui/)| 영역 | 기술 | 역할 |
|---|---|---|
| 데스크탑 셸 | Tauri 2(Rust) | 네이티브 창 + 서버 프로세스 감독(supervise) |
| UI 프레임워크 | React 18 + TypeScript 5 | 대화·승인·인박스·트랜스크립트 화면 |
| 빌드 | Vite 5 | 개발 서버 + 번들러 |
| 스타일 | Tailwind 3 + PostCSS | UI 스타일링 |
| 문서 렌더 | react-markdown + remark-gfm | 마크다운 산출물 미리보기 |
| 파일 처리 | pdfjs-dist · xlsx(SheetJS) | PDF·엑셀 산출물 표시 |
| 테스트 | Vitest + Playwright | 단위 + hermetic E2E |
Electron이 "크롬 통째로 앱에 넣기"라면, Tauri는 OS에 이미 있는 웹뷰를 쓰고 백엔드를 Rust로 짠다. 그래서 앱이 훨씬 가볍다. 여기서 Tauri 셸은 창을 띄우는 동시에 Python 에이전트 서버 프로세스를 직접 켜고 돌본다 — 사용자는 "앱 하나"를 켜지만 안에서는 Rust+Python이 함께 돈다.
stt/, Rust)음성 입력을 위한 별도 speech-to-text 사이드카(Rust)가 붙어 있다. 말로 지시하면 텍스트로 바꿔 에이전트에 넣는 구조다. "사이드카"란 본체(에이전트 서버) 옆에 붙어 특정 기능만 담당하는 보조 프로세스를 뜻한다.
OpenWorker의 코드에서 배울 가치가 가장 큰 부분이다. "에이전트가 도구를 쓰다가 위험한 행동 앞에서 멈추고 사람 승인을 기다린다"를 실제로 어떻게 구현했는지 뜯어보자. 전체 그림은 이렇다.
engine.py, 1033줄)한 번의 사용자 발화(turn)는 "모델 ↔ 도구"를 여러 번 오가는 반복이다. 모델이 도구를 그만 요청하거나, 안전 레일이 걸리거나, 사용자가 중단할 때까지 이어진다. 핵심 설계 결정들:
asyncio.to_thread로 감싼다 → UI가 멈추지 않는다.PERMISSION_REQUIRED 이벤트를 내보내고, 주입된 async approver를 기다린다. 승인 처리 로직이 루프와 분리돼 있다.Event를 AsyncIterator로 흘려보내고, UI가 이를 구독해 실시간으로 그린다.# engine.py — 승인 결과는 4가지로 갈린다 (ApprovalOutcome)
class ApprovalOutcome(str, Enum):
ONCE = "once" # 이번 한 번만 허용
ALWAYS_TOOL = "always_tool" # 이 도구는 앞으로 자동 허용
ALWAYS_COMMAND = "always_command" # 이 명령(prefix)은 자동 허용
DENY = "deny" # 거부
permissions.py + risk.py)"무엇을 자동 허용하고, 무엇을 물어볼지"를 결정하는 두뇌다. 두 개의 개념이 맞물린다: 모드(Mode)와 위험 클래스(RiskClass).
| 모드 | 동작 |
|---|---|
| DISCUSS | 읽기 전용 대화. 편집·계획 워크플로 없음 |
| PLAN | 읽기 전용 + 계획 계약(탐색→propose_plan→실행) |
| INTERACTIVE | 기본값. 읽기는 자동, 쓰기·명령은 승인 요청 |
| AUTO | 전부 허용(단 경로 스코프는 유지) |
| CUSTOM | INTERACTIVE + 설정의 auto_allow 도구는 자동 허용 |
예전엔 권한 엔진이 WRITE_TOOLS/SHELL_TOOL 같은 이름 집합을 코드에 박아 뒀지만, 지금은 위험을 도구가 선언하는 속성으로 바꿨다. classify() 하나가 이를 읽는다.
# risk.py — 부작용 카테고리 4단계
class RiskClass(str, Enum):
READ = "read" # 부작용 없음 → 항상 허용
WRITE_LOCAL = "write_local" # 워크스페이스 변경 → 경로스코프+모드게이트
EXEC = "exec" # 명령 실행 → 모드게이트
EXTERNAL = "external" # 컴퓨터 밖 부작용 → 무인 Inbox 훅
# 효과적 위험 = 사용자 오버라이드 ?? 이름표 ?? aisuite 메타데이터 ?? READ
allowlist에 등록된 명령이라도 ; && | > $( 같은 셸 연산자가 하나라도 있으면 자동 실행 자격을 잃고 승인을 요구한다. ls를 허용했다고 ls; rm -rf ~가 통과하는 참사를 막는 아주 실전적인 방어다.
inbox.py)여러 세션이 동시에 돌거나, 사용자가 자리를 비운 채 세션이 무인(Unattended)으로 돌아갈 때, 다른 에이전트들이 사람에게 필요로 하는 것(승인·질문·알림)을 담아두는 정본(store of record) 큐다. 메시징 커넥터·모바일은 같은 항목을 나르는 "운송 수단"일 뿐이다.
pending → resolved. 단 한 번, 멱등(idempotent)·선착순(first-responder-wins)으로 해결된다 → 인앱·Slack·복귀 후 어디서 답해도 안전(레이스 방지).selfwake.py)
상시 대기(always-on) 에이전트를 suspend/resume(중단/재개) 방식으로 바꿔 유휴 비용을 거의 0으로 만든다. 세션이 잠들고, 깨어날 조건(wake)이 되면 런타임이 다시 부른다. 두 가지 트리거 — 타이머(sleep_for/sleep_until)와 완료 시(wake_on: 백그라운드 작업이 끝나면 깨움). 이 깨우기는 automation 스케줄러의 tick이 소비한다.
server/manager.py가 3505줄, server/app.py가 1773줄, engine.py가 1033줄이다. "세션을 관리하고(manager) → HTTP로 노출하고(app) → 실제 루프를 도는(engine)" 3단이 코드량의 중심이다. 처음 읽는다면 engine.py → permissions.py → risk.py → inbox.py 순서를 추천한다. 여기가 이 프로젝트의 '설계 사상'이 응축된 곳이다.
튜토리얼의 while 루프가 아니라 실제 앱이 쓰는 에이전트 루프를 볼 수 있다. async 루프에서 블로킹 호출을 스레드로 격리하는 법, 저위험 도구를 병렬화하고 부작용 도구를 직렬화하는 법, 이벤트를 스트림으로 흘려 UI와 분리하는 법 — engine.py 한 파일에 다 있다.
에이전트 안전의 핵심은 '멋진 프롬프트'가 아니라 '실행 게이트'라는 걸 코드로 배운다. 모드 × 위험 클래스 × 경로 스코프 × allowlist × 셸 메타문자 검사의 조합으로 "자동/승인/거부"를 판정하는 구조는 어떤 에이전트를 만들든 참고할 원형(原型)이다.
Inbox의 멱등·선착순 상태 기계는 "여러 곳에서 동시에 승인 답이 올 수 있는" 분산 문제를 어떻게 안전하게 푸는지 보여준다. Self-wake는 상시 대기 에이전트의 유휴 비용을 없애는 실전 패턴이다.
aisuite 위에서 11개+ 모델 제공자를 한 API로 통일하고, 25개+ 커넥터를 catalog(닫힌 벤더 카탈로그) + MCP(개방 확장)로 이원화한 설계를 볼 수 있다. "우리가 벳(vet)한 도구"와 "사용자가 붙이는 MCP 도구"를 위험 관점에서 다르게 다루는 방식이 특히 배울 점.
"retry 로직 어디서 처리돼?" 같은 넓은 질문은 메인 세션 컨텍스트를 파일 읽기로 태워버린다. OpenWorker의 explore 도구는 읽기 전용 자식 TurnEngine을 별도 컨텍스트로 띄워, 최종 리포트만 부모에게 돌려준다. 자식은 plan 모드로 강제돼 쓰기가 하드 블록되고, 승인 왕복이 없어 여러 explore를 한 턴에 병렬 실행할 수 있다.
load_skill로 그때그때 불러오는 방식. 컨텍스트 낭비를 막는 에이전트 설계 관용구다.| 플랫폼 | 요구 | 비고 |
|---|---|---|
| macOS | macOS 12+ (Apple Silicon) | 서명·공증 완료, 자동 업데이트 |
| Windows | Windows 10/11 (x64) | 아직 코드 서명 전 → SmartScreen 경고(서명 진행 중) |
앱을 열고 → 모델 키를 추가(또는 Ollama를 가리키게) → 실제 업무를 시켜보면 된다.
| 필요 | 버전 |
|---|---|
| Python | 3.10+ |
| Node | 20+ |
| Rust | rustup 툴체인(데스크탑 셸용) |
# 1. 한 번만: Python venv(.venv) 부트스트랩 (Win은 Git Bash/WSL)
bash packaging/setup_dev_env.sh
# 2. 로컬 에이전트 서버 시작
.venv/bin/openworker-server --cwd ~/some/project --port 8765
# 3. 두 번째 터미널에서 UI 시작
cd surfaces/gui && npm install && npm run dev # 브라우저 UI
# 데스크탑 앱 전체는 3단계 대신: npm run tauri dev
앱 자체는 내 컴퓨터에서 돌지만, OpenAI·Anthropic·Gemini 등을 쓰면 토큰 요금은 내 API 키로 청구된다. 완전 무료로 돌리려면 Ollama 로컬 모델을 써야 한다(단 로컬 모델은 도구 호출 품질이 제각각 — README도 "검증된 모델 목록"을 별도로 표시한다).
완성본 앱을 깔고 모델 키(또는 Ollama)를 붙인 뒤, "이 폴더의 CSV들을 요약한 리포트 문서를 만들어줘"를 시켜보라. 채팅 답이 아니라 실제 파일이 떨어지는지 확인하고, 중간에 뜨는 승인 프롬프트를 관찰하라.
같은 지시(예: 파일 하나 수정)를 DISCUSS → PLAN → INTERACTIVE → AUTO 모드로 각각 시켜보고, 어디서 막히고 어디서 물어보는지 비교하라. 그다음 permissions.py의 Mode·READ_ONLY_MODES를 읽어 코드와 체감이 맞는지 확인.
coworker/personas/builtin/ops.md를 템플릿 삼아, YAML frontmatter(id·name·tools·connectors·recommended_models·default_permission_mode·recommends)를 채운 나만의 페르소나(예: "리서치 코워커")를 만들어 로드해보라. 도구·커넥터 조합이 실제로 반영되는지 확인.
"매일 아침 이 채널의 지난 24시간을 요약해줘" 같은 스케줄을 걸고, automation/scheduler.py에서 run-once-catch-up(내려가 있던 동안 놓친 실행을 시작 시 1회 처리)과 skip-on-overlap(이전 실행이 안 끝났으면 안 겹침)이 어떻게 구현됐는지 코드로 확인하라.
임의의 MCP 서버를 커넥터로 연결하고, 그 도구가 risk.py의 기본 분류(외부=EXTERNAL, 그 외=READ)에서 어떻게 다뤄지는지 관찰하라. 이어서 사용자-로컬 위험 오버라이드로 MCP의 보수적 기본값을 완화해보고, 그 변화가 승인 흐름에 미치는 영향을 engine.py의 승인 경로로 추적한다.
| 주차 | 주제 | 할 것 |
|---|---|---|
| 1주차 | 에이전트 루프 기초 | LLM tool-calling 원리 → engine.py의 turn 루프 정독 → aisuite의 chat-completions 통합 API 이해 |
| 2주차 | 권한·안전(HITL) | permissions.py·risk.py·inbox.py 정독 → 모드×위험클래스 매트릭스 직접 표로 재구성 → 셸 메타문자 방어 재현 |
| 3주차 | 도구·커넥터·MCP | MCP 스펙 학습 → connectors/ + catalog.py 구조 파악 → 나만의 커넥터/MCP 도구 1개 붙이기 |
| 4주차 | 비동기·스케줄·데스크탑 | asyncio(to_thread·이벤트 스트림) → automation/scheduler.py·selfwake.py → Tauri+React가 서버를 감독하는 구조 실습 |
이 저장소는 "에이전트를 안전하게, 로컬에서, 여러 모델로 돌리는 법"의 종합 교재다. 배경 지식이 얇다면 aisuite → OpenWorker 순서가 좋다. aisuite로 "에이전트 조립의 최소 단위"를 익힌 뒤, OpenWorker에서 "그것을 제품으로 키우면 어떤 문제(권한·멀티세션·무인·비용)가 생기고 어떻게 푸는가"를 배우는 흐름이다.
| 용어 | 뜻 |
|---|---|
| AI coworker | 도구·파일·계정을 직접 조작해 완성된 산출물을 내놓는 에이전트(챗봇의 상위 개념) |
| aisuite | 여러 LLM 제공자를 한 API로 통일하는 앤드류 응 팀의 경량 라이브러리. OpenWorker 엔진의 토대 |
| BYOM | Bring Your Own Model — 사용자가 제공자·키를 골라 붙이는 모델 비종속 설계 |
| local-first | 에이전트 루프·기록·토큰·키가 전부 로컬에 있고, 클라우드는 최소(OAuth 중개)만 쓰는 방식 |
| TurnEngine | OpenWorker가 직접 소유한 async 에이전트 루프. 모델↔도구 반복 + 승인 대역밖 처리 |
| PermissionEngine | 도구 호출마다 allow/deny/ask-user를 판정하는 엔진(모드+위험클래스 기반) |
| RiskClass | 도구의 부작용 등급: READ / WRITE_LOCAL / EXEC / EXTERNAL |
| Mode | 세션 자유도: DISCUSS / PLAN / INTERACTIVE(기본) / AUTO / CUSTOM |
| Inbox | 세션을 가로지르는 사람 주의 큐. pending→resolved, 멱등·선착순 |
| Unattended | 사용자 부재 시 무인 실행 모드. 승인 요청을 Inbox에 park(보관) |
| Self-wake | 에이전트가 스스로 잠들고(sleep_for/until) 조건 충족 시 깨어나는 suspend/resume |
| explore | 읽기 전용 자식 TurnEngine 서브에이전트. 별도 컨텍스트로 리서치 후 리포트만 반환 |
| persona | 도구·커넥터·권장 모델·기본 권한모드를 묶은 역할 프로필(YAML frontmatter) |
| MCP | Model Context Protocol — 외부 도구를 표준 방식으로 에이전트에 꽂는 개방 규격 |
| Tauri | OS 웹뷰 + Rust 백엔드로 만드는 경량 데스크탑 앱 프레임워크(Electron 대안) |
| progressive disclosure | 스킬 목록만 먼저 보여주고 필요할 때 load_skill로 불러오는 컨텍스트 절약 기법 |
download.openworker.com/mac · Windows download.openworker.com/windowscoworker/engine.py(TurnEngine) · coworker/permissions.py+risk.py(권한·위험) · coworker/inbox.py(Inbox) · coworker/tools/subagent.py(explore) · coworker/automation/scheduler.py(스케줄)docs/(PERMISSIONS-AND-INBOX.md 등 결정 로그)