에이전트 플랫폼
dd9dce4 기준입니다.
Octop은 한 서버에서 여러 사용자의 AI 비서를 관리하고, 웹·메신저·CLI·예약 작업을 연결하는 자체 호스팅 플랫폼입니다. 사용자는 비서마다 모델, 성격, 도구, 지식베이스를 설정할 수 있고, 팀 기능에서는 진행자 역할의 비서가 다른 전문가에게 일을 나눠 줍니다.
이 저장소에서 배울 핵심은 모델을 만드는 방법보다, 모델을 사용하는 여러 사람과 여러 작업의 경계를 설계하는 방법입니다. 로그인한 사람은 누구인지, 어느 비서의 대화인지, 어느 기록을 읽어도 되는지, 응답 도중 브라우저가 닫히면 무엇을 보관해야 하는지가 실제 제품의 문제입니다.
octop-harness를 의존성으로 사용하고,
src/octop/infra/agents/manager.py에서 비서 설정을 엔진 설정으로 바꿉니다.접수 창구는 웹과 메신저, 명찰은 사용자와 권한, 서류함은 비서의 작업공간, 회의실은 여러 비서가 함께 대답하는 팀 대화에 해당합니다.
두뇌가 같아도 고객 서류를 뒤섞거나 회의 결과를 잃어버리면 서비스가 되지 않습니다. 이 프로젝트는 그런 주변 시스템을 읽어 보기 좋은 사례입니다.
InboundMessage로 바꿔 채널 처리기에 넣습니다.GlobalProcessor가 실행 요청을 조립하고 에이전트의 응답 조각을 처리합니다.코드의 출발점은 src/octop/launch.py:run_foreground()입니다.
이 함수가 OctopServer.start(), build_app(), uvicorn을 연결합니다.
pyproject.toml의 CLI 진입점은 octop.cli.main:cli입니다.
모든 플러그인과 메신저를 이해할 필요는 없습니다.
src/octop/api/routers/chat/ws.py → src/octop/api/routers/chat/turn.py → src/octop/infra/gateway/process/processor.py → src/octop/infra/agents/manager.py를
먼저 연결하면 나머지 기능이 붙는 자리가 보입니다. 경로는 저장소 루트 기준입니다.
2026-10-23 GitHub 기준 스타 10,787개, 포크 1,382개이며
GitHub Trending Monthly #17에 선정됐습니다.
생성일은 2026-07-08, 최신 릴리스는 2026-10-18의 v1.0.2입니다.
순위와 별 개수만으로 사용자가 왜 관심을 보였는지까지 입증할 수는 없습니다.
다음은 README의 지향점과 실제 구현을 바탕으로 읽을 수 있는 매력입니다.
README는 웹, CLI, Feishu·Telegram·Discord 등 여러 메신저와 예약 작업을 한 제품으로 묶는다고 설명합니다.
실제로 src/octop/infra/server.py:_boot_runtime()는
AgentManager, Gateway, CronManager를 같은 서비스 묶음으로 조립합니다.
입구마다 계정과 모델 설정을 다시 만드는 부담을 줄이는 방향입니다.
Octop의 강점은 기능 하나의 새로움보다, 대화·권한·도구·저장을 하나의 사용 흐름으로 연결하는 데 있습니다. 예를 들어 문서 검색이 가능한 비서를 메신저에 연결하려면 문서 접근 권한도 함께 이어져야 합니다. 기능 목록 사이에 숨은 연결 비용을 공부할 수 있다는 점이 교육 자료로서도 중요합니다.
다음 표는 특정 제품의 최신 기능 점수표가 아닙니다.
일반 대화 서비스, 에이전트 개발 도구, 자체 호스팅 플랫폼 중 무엇을 고르는지 판단하는 비교입니다.
Octop 열은 커밋 dd9dce4에서 확인한 구성입니다.
| 비교 축 | 관리형 대화 서비스 | 에이전트 SDK·프레임워크 | Octop |
|---|---|---|---|
| 주요 출발점 | 준비된 대화 화면 사용 | 실행 흐름을 코드로 개발 | 계정과 비서를 생성해 운영 |
| 운영 책임 | 서비스 운영자가 서버 관리 | 개발자가 제품 주변부 구성 | 호스트 관리자가 배포·백업 담당 |
| 여러 사용자 | 서비스 요금제·정책에 따름 | 제품 계층에서 별도 설계 | JWT와 비서 소유·공유 검사 구현 |
| 연결 창구 | 제공되는 클라이언트·연동 | 개발자가 어댑터 작성 | 웹·CLI·메신저·cron 연결부 포함 |
| 학습 비용 | 사용법 중심 | 실행 로직과 API 중심 | 프런트·인증·DB·운영까지 넓음 |
| 맞는 상황 | 즉시 대화가 필요한 개인 | 새로운 실행 구조를 개발할 때 | 여러 사람의 비서를 직접 운영할 때 |
| 비교 대상 | 공식 자료에서 확인할 초점 | Octop을 검토할 이유 |
|---|---|---|
| Open WebUI | 자체 호스팅 대화 플랫폼, 문서 RAG와 도구 확장 | 비서별 작업공간·메신저·예약·팀 배정의 연결을 함께 공부할 때 |
| LangGraph | 상태를 가진 에이전트와 작업 흐름을 코드로 구성 | 그 위에 계정·관리 UI·채널을 묶은 제품 계층에서 출발하고 싶을 때 |
Open WebUI도 검색·도구 확장을 제공하므로 단순한 기능 유무로 우열을 정하기 어렵습니다. Open WebUI 공식 기능 문서와 LangGraph 공식 개요 문서를 기준으로 비교하고, Octop의 장점은 코드에 구현된 운영 흐름의 결합으로 읽는 편이 정확합니다. LangGraph는 대체 개발 경로인 동시에 Octop 실행 엔진의 간접 의존성이기도 합니다.
기본 제어 데이터베이스는 SQLite이고, 별도 메시지 브로커는 필수 구성요소가 아닙니다.
src/octop/infra/gateway/gateway.py:boot()에서
ChannelManager와 처리기를 프로세스 안에 조립합니다.
이는 설치할 서비스 수를 줄이지만, 다중 서버 고가용성을 자동으로 제공한다는 뜻은 아닙니다.
README는 모든 것이 내 기기에서 실행되고 데이터가 로컬에 머문다고 강조합니다.
그러나 모델 제공자 호출, 원격 임베딩, 메신저, OAuth 연결, COS/S3 저장소를 선택하면 해당 데이터 흐름은 외부로 이어집니다.
src/octop/infra/knowledge/embed.py에는 원격 /embeddings 요청이 명시돼 있습니다.
서버를 소유한다는 사실과 모델에 전달되는 내용의 범위는 따로 판단해야 합니다. 저장 위치, 처리 위치, 외부 연동을 각각 확인하는 습관을 배우세요.
README는 AgentTeams를 베타로 표시하고 모바일 클라이언트를 비공개 시험 단계로 설명합니다. 코드에 팀·원격 Android 관련 모듈이 있다는 사실만으로 모든 플랫폼의 안정성을 보증할 수는 없습니다. 관심 요인에 대한 설명은 기능 구성에서 얻은 해석이며, 트렌딩 유입 원인에 대한 실측 결과는 아닙니다.
pyproject.toml의 >=는 최소 버전 조건이고,
uv.lock은 의존성을 해결해 기록한 버전입니다.
같은 원리로 프런트엔드는 dashboard/package.json과
dashboard/package-lock.json을 함께 읽어야 합니다.
아래의 잠금 버전은 잠금 파일에 기록된 값이며 실제 설치 버전과 다를 수 있습니다.
| 역할 | 선언 조건 | uv.lock | 읽을 지점 |
|---|---|---|---|
| Python | >=3.12 | 언어 요구조건 | 비동기 함수·타입 힌트 |
| FastAPI | >=0.110 | 0.138.2 | src/octop/api/app.py의 앱 팩터리 |
| uvicorn | [standard]>=0.27 | 0.49.0 | src/octop/launch.py의 HTTP 서버 연결 |
| Pydantic | >=2.6,<2.14 | 2.13.4 | src/octop/api/routers/chat/models.py의 입력 검증 |
| 에이전트 엔진 | octop-harness[all]>=1.0.4 | 1.0.4 | src/octop/infra/agents/manager.py의 설정 변환 |
| 메신저 엔진 | octop-gateway>=1.0.3 | 1.0.3 | src/octop/infra/gateway/gateway.py의 채널 조립 |
| 장기 기억 | octop-memory>=1.0.2 | 1.0.2 | 기억 엔진 연결과 작업공간 설정 |
| 브라우저 엔진 | octop-browser>=1.0.0 | 1.0.0 | 브라우저 자동화 연결 |
| LangGraph | 간접 의존성 | 1.2.11 | 엔진의 상태·체크포인트 배경 |
| APScheduler | >=3.10,<4 | 3.11.3 | src/octop/infra/cron/의 예약 작업 |
| Playwright | >=1.40 | 1.61.0 | 자동화 패키지와 브라우저 설치 분리 |
| MCP SDK | >=1.9,<2 | 1.28.1 | src/octop/infra/connectors/의 도구 연결 |
표의 경로는 저장소 루트 기준입니다.
CLI에는 click>=8.1, 터미널 표현에는 rich>=13.7,
암호 처리에는 argon2-cffi>=23.1과 pyjwt>=2.8이 선언돼 있습니다.
PostgreSQL 연결은 psycopg[binary]>=3.2를 사용합니다.
| 역할 | package.json | package-lock.json | 쓰임새 |
|---|---|---|---|
| React | ^18 | 18.3.1 | 화면과 훅, 외부 대화 저장소 구독 |
| TypeScript | ~5.8.3 | 5.8.3 | 대화 프레임과 API 데이터 형태 |
| Vite | ^6.3.5 | 6.4.3 | 개발 서버·정적 파일 빌드 |
| Ant Design | ^5.29.1 | 5.29.3 | 관리 화면의 폼과 UI |
| React Router | ^7.13.0 | 7.16.0 | 페이지 경로와 화면 전환 |
| Less | ^4.5.1 | 4.6.4 | 컴포넌트 스타일 |
| Vitest | ^3.2.6 | 3.2.6 | 프런트엔드 테스트 |
@xterm/xterm 5.5.0은 터미널 UI,
react-virtuoso ^4.18.10은 긴 목록을 다루는 선택지,
react-markdown ^10.1.0은 Markdown 표현용 의존성입니다.
dashboard/src/api/request.ts는 토큰 저장, 갱신 헤더 반영,
초기 설정이 끝나지 않았을 때의 처리를 모읍니다.
dashboard/vite.config.ts의 빌드 결과 경로는
src/octop/dashboard입니다. Python wheel에 정적 UI를 포함하므로
배포된 서버가 API와 화면을 함께 제공할 수 있습니다.
이 결과물 디렉터리와 실제 UI 소스인 dashboard/src를 구별하세요.
| 구성 | 실제 파일·값 | 배울 점 |
|---|---|---|
| Python 패키징 | hatchling>=1.18, 제품 버전 1.0.2 | wheel에 Python과 UI를 함께 포함 |
| 컨테이너 | docker/Dockerfile: Node 20-slim → Python 3.12-slim | 빌드 도구와 실행 이미지를 분리 |
| 의존성 재현 | npm ci, uv sync --frozen --no-dev | 잠금 파일을 이용해 빌드 |
| 컨테이너 상태 | 8088 포트, /api/health, 데이터 볼륨 | 프로세스·상태·지속 데이터를 구분 |
| 정적 검사 | Ruff >=0.4, mypy >=1.10 | 포맷·린트·타입을 나눠 확인 |
| Python 테스트 | pytest >=8.0, pytest-asyncio >=0.23 | 비동기·통합·외부 서비스 테스트 구분 |
| 데스크톱 셸 | desktop/src/go.mod: Go 1.25.0, Wails v3.0.0-beta.13 | 웹 UI를 네이티브 창·트레이와 연결 |
저장소의 주 언어만 보면 놓치는 제품 설계가 프런트엔드와 패키징 코드에 들어 있습니다.
데스크톱 앱은 Electron이나 Tauri로 구현된 것으로 추정하면 안 됩니다.
desktop/README.md는 Wails 셸이 포함된 실행 환경을 풀고 Octop 프로세스를 시작한다고 설명합니다.
품질 검사도 구분해서 읽어야 합니다. .github/workflows/ci.yml에는
Linux·Windows의 Python 3.12 작업과 별도의 실제 자격증명 테스트 작업이 있습니다.
Makefile의 all은 포맷·린트·타입·Python 테스트를 묶지만,
이름만 보고 프런트엔드 Vitest까지 모두 실행한다고 단정할 수는 없습니다.
이 그림은 책임의 지도입니다. 모든 CLI 명령이 HTTP를 통과하지는 않습니다.
src/octop/cli/support/에는 저장소를 직접 다루는 경로와
짧게 서버를 띄워 실행 기능을 사용하는 경로가 나뉘어 있습니다.
요청 입구가 달라도 같은 데이터와 도메인 규칙을 사용하도록 구성한 것입니다.
src/octop/launch.py:run_foreground()가 바깥쪽 조립 지점입니다.
src/octop/infra/server.py:OctopServer.start()는 경로와 설정을 준비하고,
DB를 연 뒤 run_migrations()와 build_shared_services()를 호출합니다.
최초 설정에서 DB 선택이 미뤄진 경우에는 서비스를 모두 만들기 전에 반환하는 분기도 있습니다.
src/octop/infra/db/services.py의 RepoBundle.from_pool()은
사용자·비서·대화 저장소에 같은 DatabasePool을 전달합니다.
SharedServices는 경로·설정·저장소 묶음을 보관합니다.
HTTP 계층은 src/octop/api/deps.py:get_server()로 조립된 서버를 받아 사용합니다.
비서 수명에는 중복 시작 문제도 있습니다.
src/octop/infra/agents/manager.py:_start_agent()는 비서별 수명 잠금을 잡고
기존 등록을 확인한 뒤 acreate_agent()로 생성합니다.
이미 있으면 기존 비서를 반환하며, 모델 설정이 없으면 상태를 failed로 남깁니다.
시작 요청이 겹쳤을 때 같은 비서를 두 번 만들지 않으려는 멱등성 설계를 읽을 수 있습니다.
# src/octop/infra/db/services.py의 조립 관계를 줄인 개념 예시
db = 선택한_데이터베이스
repos = RepoBundle.from_pool(db)
services = SharedServices(paths=paths, config=config, repos=repos)
# 각 기능은 설정 파일 대신 필요한 서비스·저장소를 전달받는다.
FastAPI의 공식 의존성 문서가 설명하는 “필요한 값을 대신 준비해 전달한다”는 개념을 실제 제품의 서비스 조립까지 확장해 볼 수 있습니다. 다만 컨테이너 하나에 모든 기능을 넣으면 결합이 커질 수 있으므로, 각 클래스가 받는 인자 중 무엇이 정말 필요한지 같이 살펴보세요.
웹 대화 입구는 src/octop/api/routers/chat/ws.py:dashboard_chat_ws()입니다.
토큰을 해석하고 assert_agent_access()로 비서 접근을 확인한 뒤,
UserTurnWsFrame.model_validate()로 프레임 형태를 검사합니다.
유효한 로그인과 특정 비서를 사용할 권한은 서로 다른 단계입니다.
src/octop/api/routers/chat/turn.py:prepare_dashboard_turn()는 선택한 MCP·스킬을 검사하고
사용자와 비서에 맞는 대화를 찾습니다. 이어서 build_dashboard_inbound()가
화면 전용 데이터를 공통 메시지로 바꿉니다. 아래는 그 함수의 핵심 필드를 발췌한 것입니다.
return InboundMessage(
channel_id=WS_CHANNEL_ID,
channel_type=ThreadRegistry.CHANNEL_DASHBOARD,
tenant_id=agent_id,
channel_subject=ChannelSubject(subject_id=str(user_id)),
channel_session_id=prepared.session_key,
content=list(prepared.inbound_content),
metadata=metadata,
)
tenant_id라는 이름이 여기서는 비서 식별자를 담는다는 점이 중요합니다.
이 필드를 사용자 식별자로 혼동하면 메시지 경로를 잘못 읽게 됩니다.
실제 사용자 식별자는 주체 정보와 메타데이터 등 별도 문맥으로 전달됩니다.
dashboard_chat_ws()는 channel_manager.enqueue(WS_CHANNEL_ID, inbound)를 호출합니다.
src/octop/infra/gateway/process/processor.py의 GlobalProcessor는
__call__()과 iter_turn_chunks()에서 공통 입력을 처리합니다.
웹용 스트림에서는 AgentManager.stream()의 결과를 승인 정책·기록·사용량 처리와 연결합니다.
여러 국가에서 온 소포를 표준 송장으로 바꾸는 물류센터와 같습니다. 웹·메신저는 포장 방식이 다르지만, 안쪽에서는 같은 수신자·내용·대화 식별자로 다룹니다. 새 메신저를 붙일 때 모델 실행 로직을 통째로 다시 만들 필요가 줄어듭니다.
입구의 형식을 공통 메시지로 바꾸면 실행 로직을 공유할 수 있지만, 사용자와 대화의 권한 정보도 끝까지 함께 전달해야 합니다.
subscribe와 cancel 처리에서도 서버가
row.agent_id와 row.user_id를 검사하는 이유가 여기에 있습니다.
연결 종료는 작업 취소와 다릅니다. dashboard_chat_ws()의 정리 코드는
허브에서 연결을 해제하지만 진행 중인 대화를 취소하지 않습니다.
소스 주석도 이를 제한적인 스트림 재연결로 설명합니다.
다시 구독하면 이후 조각을 받을 수 있습니다. 팀 대화에는 subscribe_with_member_catchup()을 통해
메모리에 남은 구성원 응답 텍스트를 다시 보내는 경로도 있지만, 모든 누락 이벤트의 완전한 재전송을 보장하지는 않습니다.
src/octop/infra/db/pool.py에는 공통 DatabasePool 프로토콜과
SqlitePool, PostgresPool이 있습니다.
저장소는 공통 연결·트랜잭션 형태를 사용하고, 마이그레이션은 SQLite·PostgreSQL 파일 쌍으로 관리합니다.
src/octop/infra/db/migrate.py:_discover()가 DB 종류에 맞는 SQL을 고릅니다.
# SqlitePool에서 실제 사용하는 설정과 트랜잭션 순서
PRAGMA foreign_keys = ON
PRAGMA journal_mode = WAL
BEGIN IMMEDIATE
# 읽기·쓰기 작업 수행
COMMIT
# 예외 경로에서는 ROLLBACK
SQLite 연결 하나는 threading.RLock으로 보호합니다.
BEGIN IMMEDIATE는 읽은 뒤 쓰기로 전환하는 사이에 다른 연결이 먼저 변경하는 상황을 줄이려는 선택입니다.
WAL이 쓰기 작업을 무제한 병렬화한다는 뜻은 아닙니다.
SQLite 공식 WAL 문서도 동시 작성자는 하나라는 제약을 설명합니다.
인터페이스가 같아도 두 DB가 완전히 같아지는 것은 아닙니다.
qmark_to_pyformat()는 ?를 %s로 단순 치환하며,
함수 설명에는 문자열 리터럴을 구분하지 않는다고 명시돼 있습니다.
추상화가 해결한 범위와 남겨 둔 제약을 함께 읽는 연습이 됩니다.
| 상태의 종류 | 대표 위치·구현 | 별도로 봐야 하는 이유 |
|---|---|---|
| 제어 데이터 | octop.db 또는 PostgreSQL | 사용자·비서·채널·대화 메타데이터 |
| 비서 파일 | BackendWorkspace와 선택한 저장소 | 문서·스킬·작업 파일의 경로와 수명 |
| 실행 상태 | harness의 체크포인트 연동 | 모델 실행을 이어 가는 상태 |
| 지식 색인 | knowledge/{kb_id}/index.sqlite | 제어 DB와 독립된 로컬 SQLite |
| 버전형 기록 | history_v2.sqlite | 옵션 기능이며 현재 SQLite 제어 DB 필요 |
src/octop/infra/server.py:_boot_runtime()는 버전형 기록을 만들 때 PostgreSQL이면
ValueError를 발생시킵니다. 따라서 “PostgreSQL 지원”은
모든 부속 저장소와 기능이 PostgreSQL로 옮겨진다는 약속이 아닙니다.
src/octop/infra/knowledge/chunk.py:chunk_text()는 기본 800문자 창에
120문자를 겹쳐 자릅니다. src/octop/infra/knowledge/jobs.py:process_document()가 파싱·분할·임베딩·색인 교체를 잇고,
enqueue_index_document()는 실행기 스레드로 동기 작업을 넘깁니다.
파싱·OCR 메모리를 고려한 INDEX_CONCURRENCY의 기본값은 1입니다.
src/octop/infra/knowledge/index.py:KnowledgeIndex.search()는 전체 청크를 읽어 Python에서 코사인 유사도를 계산하고 정렬합니다.
청크 수를 N, 벡터 차원을 D라고 하면 코드 구조상 거리 계산은 대략 O(ND), 정렬은 O(N log N)입니다.
이는 구현을 읽고 얻은 복잡도 해석이며 실측 처리량은 아닙니다. 전용 벡터 DB나 근사 검색을 사용하는 코드는 아닙니다.
src/octop/infra/knowledge/retrieve.py:retrieve_context()는 기본 상위 8개와
6,000문자 예산을 사용합니다. 이 예산에는 각 조각 앞의 출처 문구와 본문이 포함됩니다.
전체 머리말·구분자·끝의 인용 메타데이터는 별도로 붙으므로
최종 문자열 전체의 엄밀한 6,000문자 상한으로 해석하지 마세요.
검색 실패 시 경고를 남기고 빈 문맥을 반환하는 경로도 있어, 답변이 왔다고 검색 성공이 보장되지는 않습니다.
src/octop/infra/agents/manager.py:_apply_team_host_config()는 일반 비서에
peer_invoke_mode="sync", 팀 진행자에 "async"를 적용하는 분기를 둡니다.
진행자에게는 구성원 목록을 전달하고 MCP 설정·스킬 디렉터리·추가 도구를 제한합니다.
진행자가 직접 모든 도구를 쓰기보다 업무를 조정하는 방향입니다.
src/octop/infra/agents/teams/team_manager.py:TeamManager는 팀 대화방 연결을 맡습니다.
install_host_dispatch()가 stream_peer_to_room()과
stream_host_followup_to_room()을 연결하고,
record_peer_turn()는 구성원 기록을 남긴 뒤 fan_in_peer_turn()으로 이어집니다.
compose_followup()은 구성원 결과를 진행자 후속 입력으로 만들며
결과 발췌 상한은 _FOLLOWUP_RESULT_MAX_CHARS = 6000입니다.
전체 구성원 기록을 진행자 입력에 무한히 복사하지 않는 선택입니다.
대화방에 보이는 기록과 진행자의 모델 입력이 같은 자료구조일 필요는 없습니다.
docs/expert-teams.md는 구성원별 직렬 처리, 서로 다른 구성원 간 병렬 처리와
진행자 취소가 이미 배정된 구성원 작업까지 취소하지 않는 제약을 설명합니다.
실제 inbox 내부 스케줄러는 외부 harness의 책임입니다. 화면의 완료 신호 하나로 모든 자식 작업이 끝났다고 판단하면 안 됩니다.
src/octop/infra/desktop/은 호스트 화면을 제어하는 기능이고,
desktop/src/은 앱 창을 띄우는 Go 클라이언트입니다.
폴더 이름만으로 같은 기능이라고 묶지 마세요.
프런트엔드 빌드 산출물은 src/octop/dashboard/로 향합니다.
| 범위 | 파일 수 | 물리적 줄 수 |
|---|---|---|
| Python 핵심 패키지 | 597 | 139,285 |
| Python 동봉 자료·플러그인 | 95 | 19,864 |
| Python 테스트 | 548 | 104,628 |
| 기타 Python | 12 | 1,021 |
| TypeScript·TSX 구현 | 756 | 167,210 |
| TypeScript·TSX 테스트 | 267 | 30,694 |
| 대시보드 Less | 126 | 42,450 |
| 데스크톱 Go | 35 | 4,125 |
| DB 마이그레이션 SQL | 42 | 1,602 |
커밋 dd9dce4 기준의 물리적 줄 수입니다.
빈 줄·주석을 포함하며 마지막 줄에 줄바꿈이 없어도 한 줄로 셉니다.
Python 핵심은 src/octop/에서 /library/와
/plugins/bundled/에 속한 파일을 분리한 값입니다.
TypeScript는 dashboard/src/만 대상으로 .test. 파일명을 구분했습니다.
Python 전체는 약 26.5만 줄, 대시보드 TypeScript·TSX는 약 19.8만 줄입니다. 이 숫자는 위에 명시한 범위의 합계이며 문서·JSON·잠금 파일은 포함하지 않습니다. 파일 수는 테스트 사례 수가 아니고, 줄 수는 품질이나 난이도의 점수도 아닙니다.
src/octop/config.py와 src/octop/launch.py에서 실행의 입구를 찾습니다.src/octop/api/routers/chat/turn.py에서 입력이 어떤 자료구조가 되는지 봅니다.src/octop/infra/gateway/process/processor.py에서 비서 실행까지 연결합니다.src/octop/infra/knowledge/chunk.py처럼 작은 순수 함수로 내려가 손으로 결과를 계산합니다.파일을 얼마나 많이 읽었는지보다, 입력 하나가 변할 때 결과가 왜 달라지는지 설명할 수 있는지가 학습의 기준입니다. 다음 기술은 작은 예제로 따로 익힌 뒤 해당 파일로 돌아오면 이해가 쉬워집니다.
| 기술 | 소스에서 배울 것 | 짧은 실습 아이디어 |
|---|---|---|
| Python 타입·데이터 모델 | src/octop/config.py의 동결 dataclass, src/octop/api/routers/chat/models.py의 Pydantic | 설정 객체와 외부 입력 검증의 역할을 나눠 설명 |
| asyncio | src/octop/infra/knowledge/jobs.py의 세마포어·실행기 | 파일 파싱이 이벤트 루프를 막으면 생길 현상을 시간표로 표현 |
| FastAPI·권한 | src/octop/api/common/agent.py의 소유·공유 검사 | 소유자·타인·관리자의 허용 표 작성 |
| SQLite·트랜잭션 | src/octop/infra/db/pool.py의 WAL·잠금·롤백 | 두 작성자가 같은 데이터를 고치는 순서를 도식화 |
| React 상태 구독 | dashboard/src/pages/Chat/hooks/useChat.ts와 dashboard/src/pages/Chat/hooks/chatStore.ts | 화면이 사라져도 응답을 계속 받을 상태 저장소 설계 |
| RAG·벡터 계산 | src/octop/infra/knowledge/chunk.py와 src/octop/infra/knowledge/index.py | 2차원 벡터 세 개를 직접 정렬 |
| 보안의 책임 구분 | src/octop/infra/users/password.py와 src/octop/infra/auth/tokens.py | 암호 저장·토큰 서명·자원 권한을 별도 항목으로 분류 |
| 빌드·배포 | docker/Dockerfile, desktop/src/go.mod | 개발 도구와 실행에 필요한 도구의 목록 비교 |
위 표의 파일 경로는 저장소 루트 기준입니다.
enqueue_index_document()는 asyncio.create_task()로 작업을 만들고,
세마포어 안에서 run_in_executor()를 호출합니다.
실행기는 동기 파싱·임베딩이 이벤트 루프를 계속 점유하지 않게 하고,
세마포어는 동시에 파싱하는 문서 수를 제한합니다. 두 장치의 목적이 다릅니다.
접수원이 오래 걸리는 복사를 복사실에 맡기는 것이 실행기입니다. 복사실에 한 번에 한 사람만 들어가게 하는 번호표가 세마포어입니다. 접수원은 계속 일할 수 있지만 복사 속도 자체가 무한히 빨라지는 것은 아닙니다.
dashboard/src/pages/Chat/hooks/useChat.ts는
useSyncExternalStore(subscribeStore, getStoreSnapshot)로 대화 상태를 구독합니다.
dashboard/src/pages/Chat/hooks/chatStore.ts:notify()는 새 스냅숏 참조를 만들고 구독자에게 알립니다.
UI 컴포넌트가 직접 모든 응답을 소유하는 것보다 화면 이동과 스트림 수명을 분리하기 쉽습니다.
// dashboard/src/pages/Chat/hooks/useChat.ts의 구독 관계를 줄인 예시
const subscribeStore = (cb: () => void) =>
chatStore.subscribe(stableSessionId, cb);
const getStoreSnapshot = () => chatStore.getSnapshot(stableSessionId);
const state = useSyncExternalStore(subscribeStore, getStoreSnapshot);
React 공식 문서는 이 훅을 React 밖의 저장소를 구독하는 API로 설명합니다. 새 스냅숏을 언제 만드는지, 구독 해제는 누가 하는지, 대화 식별자가 바뀌면 무엇을 다시 구독하는지 확인하세요.
src/octop/infra/users/password.py는 Argon2id 해시와 암호 정책을 담당합니다.
src/octop/infra/auth/tokens.py:sign_token()는 HS256 JWT에
sub·uname·role·iat·exp를 넣습니다.
JWT 서명은 위변조 확인 수단이며 토큰 내용의 비밀 보관을 뜻하지 않습니다.
src/octop/api/common/agent.py:assert_agent_owner()는 소유자 또는 관리자 여부를,
assert_agent_access_row()는 공유 비서 접근까지 고려합니다.
“볼 수 있다”와 “설정을 바꿀 수 있다”가 다른 권한이라는 점을 배우기 좋습니다.
확인할 테스트는 tests/integration/test_boundary_authz.py입니다.
tests/unit/knowledge/test_chunk.py는 공백 입력과 잘못된 겹침을,
tests/unit/knowledge/test_index.py는 유사도 정렬·문서 교체·삭제를 확인합니다.
tests/integration/test_chat_ws.py는 더 넓은 통신 경계를 살펴볼 출발점입니다.
작은 순수 함수에서 시작해 저장과 통신 경계로 넓히는 순서가 좋습니다.
README의 공식 안내는 macOS·Linux·Windows, 현대적인 멀티코어 CPU, 프로세스와 모델·임베딩 캐시를 위한 수 GB RAM, DB·작업공간·문서를 위한 디스크입니다. 동시 사용자 수별 최소 사양이나 처리량 표는 제시하지 않습니다. 아래 숫자는 소규모 학습 환경을 잡기 위한 추정 출발점이며 공식 보증 사양이 아닙니다.
| 학습·사용 방식 | 자원 출발점 | 증가하는 요인 |
|---|---|---|
| 소스·문서 읽기 | 일반 개발 PC, GPU 불필요 | 편집기와 검색 도구의 메모리 |
| 원격 모델로 가벼운 대화 | 2~4 vCPU, RAM 4~8GB 검토 | 동시 비서·연결·대화 기록 |
| 브라우저·OCR·지식 검색 | RAM 8~16GB부터 여유 확인 | 브라우저 수·문서 크기·모델 캐시 |
| 프런트엔드 소스 빌드 | 런타임보다 넉넉한 빌드 RAM | Node 힙과 전체 호스트 사용량 |
| 로컬 대화 모델 | 선택한 추론 서버·모델 사양 별도 | 가중치·컨텍스트·동시 요청 |
Octop 서버의 메모리 요구량과 로컬 LLM의 메모리 요구량을 더해 생각해야, 자체 호스팅의 실제 비용이 보입니다. 원격 모델을 쓰면 서버에 대화 모델 가중치를 올릴 필요가 줄지만 API 호출 비용과 외부 전송은 남습니다.
단순 계산으로 70억 매개변수의 4비트 가중치는 약 3.5GB입니다. 여기에 양자화 메타데이터, 실행 버퍼, 대화 캐시가 추가되므로 이것을 필요 VRAM으로 그대로 쓰면 안 됩니다. Octop이 7B 모델을 기본 내장한다는 뜻도 아닙니다.
| 항목 | 소스의 기본값 | 실용적인 의미 |
|---|---|---|
| 바인딩·포트 | 127.0.0.1:8088 | 기본 Python 설정은 로컬 접근 |
| 시간대 | Asia/Shanghai | 한국의 예약 작업은 시간대 명시 |
| 토큰 수명 | 86,400초 | 갱신 정책과 함께 읽기 |
| 업로드 상한 | 100MiB | 대용량 문서 파싱 자원도 고려 |
| 브라우저 유휴 시간 | 30분 | 세션이 자원을 계속 점유하는 시간 |
| API 문서 | enable_api_docs = False | 기본 실행에서 문서 UI가 꺼져 있음 |
출처는 src/octop/config.py:OctopConfig입니다.
Docker 구성은 OCTOP_BIND_HOST=0.0.0.0으로 바꾸고 포트를 게시하므로 노출 범위가 다릅니다.
docker/docker-compose.yml은 기본 호스트 데이터 경로를 ~/.octop,
컨테이너 내부 경로를 /data/.octop으로 연결합니다.
# config.json에서 시간대와 문서 UI를 명시하는 예시
{
"default_timezone": "Asia/Seoul",
"enable_api_docs": true
}
docker/Dockerfile의 Node 힙 상한 기본값은 2048MB이며
미리 예약하는 메모리나 전체 빌드 사용량이 아닙니다.
Docker 빌드는 build:docker로 Vite만 실행하고 전체 tsc 검사를 건너뜁니다.
이미지 빌드 성공을 타입 검사 성공으로 간주하지 마세요.
pyproject.toml의 local-embedding은 fastembed·huggingface_hub,
knowledge-ocr은 RapidOCR·ONNX Runtime·PyMuPDF를 추가합니다.
Playwright 패키지 설치와 Chromium 바이너리 준비도 별개입니다.
데스크톱 빌드는 Go 1.25 이상과 Wails 도구가 필요하며,
desktop/README.md는 Linux의 GTK4·WebKitGTK 6, macOS 12 이상을 안내합니다.
대화 모델, 임베딩, 브라우저, 제어 DB, 비서 파일을 다섯 칸으로 나눠 위치를 표시하세요. 그다음 각 칸의 RAM·디스크·네트워크 사용량을 합산하면 “셀프호스팅이면 GPU가 필요한가”라는 질문에 구체적으로 답할 수 있습니다.
다음 과제는 저장소를 읽고 손으로 추적하거나 별도의 작은 예제를 만드는 방식으로 시작할 수 있습니다. 원격 모델 키나 실제 메신저 계정은 필요하지 않습니다. 실습의 완성 조건을 먼저 정하면, 거대한 저장소에서도 배운 내용을 작은 결과물로 남길 수 있습니다.
읽을 곳: src/octop/api/routers/chat/ws.py:dashboard_chat_ws(),
src/octop/api/routers/chat/turn.py:build_dashboard_inbound(), src/octop/infra/gateway/process/processor.py.
경로는 저장소 루트 기준입니다.
agent_id·user.id·thread_id를 서로 다른 색으로 표시합니다.enqueue()까지 호출 순서를 여섯 칸으로 적습니다.tenant_id가 무엇을 가리키는지 한 문장으로 설명합니다.완성 조건: 다른 사람이 지도를 보고 “어느 비서에게 누구의 대화를 전달하는가”를 답할 수 있어야 합니다. 힌트: 채널 식별자와 사용자 식별자를 같은 것으로 취급하지 마세요.
읽을 곳: src/octop/infra/knowledge/chunk.py와
tests/unit/knowledge/test_chunk.py.
입력 abcdefghij를 크기 4, 겹침 1로 나눠 보세요.
size = 4
overlap = 1
step = size - overlap # 3
# 시작 위치 0, 3, 6
# 결과: ["abcd", "defg", "ghij"]
앞뒤 공백이 있는 입력, 빈 문자열, overlap == size도 예상해 보세요.
기본값 800·120에서는 시작 위치 간격이 680문자입니다.
한국어·영어가 섞여도 토큰 개수로 나누는 함수가 아니라는 점을 확인하세요.
완성 조건: 정상 사례 세 개와 오류 사례 두 개의 예상 결과표를 만듭니다. 확장: 겹침이 커질 때 청크 수와 중복 문맥이 왜 함께 늘어나는지 설명합니다.
읽을 곳: src/octop/api/common/agent.py의
assert_agent_access_row()와 assert_agent_owner().
두 함수의 결과를 먼저 예측한 다음 조건문과 대조합니다.
| 사용자·비서 관계 | 접근 검사 | 소유자 검사 |
|---|---|---|
| 일반 사용자·자기 비서 | 허용 | 허용 |
| 일반 사용자·타인의 비공유 비서 | 거부 | 거부 |
| 일반 사용자·타인의 공유 비서 | 허용 | 거부 |
| 관리자·타인의 비서 | 허용 | 허용 |
완성 조건: 네 행을 검증하는 가짜 사용자·비서 데이터 설계와 예상 오류 코드를 적습니다.
이것은 두 공통 검사 함수의 표이며 모든 HTTP 경로의 결과표는 아닙니다.
확장: WebSocket 대화 구독에는 왜 비서 접근 외에 대화의 user_id 검사도 필요한지 설명합니다.
읽을 곳: src/octop/infra/knowledge/index.py:KnowledgeIndex.search()와
tests/unit/knowledge/test_index.py.
질문 벡터를 [1, 0], 문서를 A=[1, 0],
B=[0, 1], C=[0.8, 0.2]로 둡니다.
cos(q, v) = dot(q, v) / (norm(q) * norm(v))
A: 1.0
B: 0.0
C: 약 0.9701
# k=2이면 A, C 순서
완성 조건: 세 점수와 상위 두 문서, 벡터 차원이 다를 때의 처리 방식을 설명합니다. 확장: N개 벡터를 모두 읽는 비용과 정렬 비용을 나눠 적고, 상위 k개만 유지하는 방법으로 바꾼다면 어떤 비용이 줄고 어떤 비용이 남는지 설계합니다.
읽을 곳: src/octop/api/routers/chat/ws.py,
src/octop/infra/agents/teams/team_manager.py,
dashboard/src/pages/Chat/hooks/chatStore.ts.
완성 조건: 재연결 후 기록에서 복구하는 정보, 이후 조각만 받는 응답, 메모리에서 다시 받는 구성원 텍스트를 구분합니다. 진행자 취소가 팀 전체 취소로 오해되지 않도록 화면 문구와 상태를 설계합니다. 확장: 모든 조각의 재전송을 보장하려면 순번·보관·중복 제거를 어디에 추가할지 제안합니다.
Python 함수·리스트·딕셔너리와 기본 HTML을 아는 독자를 가정합니다. 아직 익숙하지 않다면 1주 차를 늘려도 좋습니다. 매주 한 장의 설명이나 작은 예제를 완성하고, 그 결과를 실제 소스와 대조하세요.
| 주차 | 먼저 공부할 것 | 돌아올 소스 | 주간 결과물 |
|---|---|---|---|
| 1주 | Python 타입 힌트·dataclass·예외 | src/octop/config.py, src/octop/infra/knowledge/chunk.py | 설정표와 청크 경계 사례표 |
| 2주 | HTTP·Pydantic·의존성 주입 | src/octop/api/deps.py, src/octop/api/routers/chat/turn.py | 대화 요청의 검증 순서도 |
| 3주 | SQL·트랜잭션·저장소 패턴 | src/octop/infra/db/pool.py, src/octop/infra/db/migrations/ | 롤백·다중 작성자 시간표 |
| 4주 | asyncio·스트림·WebSocket | src/octop/api/routers/chat/ws.py, src/octop/infra/knowledge/jobs.py | 연결 종료·작업 취소 상태표 |
| 5주 | React 훅·외부 상태 구독 | dashboard/src/pages/Chat/hooks/useChat.ts, dashboard/src/pages/Chat/hooks/chatStore.ts | 구독·스냅숏·화면 수명 그림 |
| 6주 | RAG·작업 조정·패키징 | src/octop/infra/knowledge/index.py, src/octop/infra/agents/teams/team_manager.py, docker/Dockerfile | 작은 비서 서비스의 설계 문서 |
표의 파일 경로는 저장소 루트 기준입니다.
학습 순서는 라이브러리 이름을 외우는 순서가 아니라, 데이터의 형태에서 시작해 상태의 수명과 실패의 경계로 넓혀 가는 순서입니다. 모델 응답이 좋은지 나쁜지와 별개로 검사할 수 있는 동작부터 익히면 디버깅도 쉬워집니다.
가짜 응답을 쓰더라도 로그인, 비서 선택, 대화 저장, 연결 종료, 권한 거부가 어떻게 동작하는지 설명해 보세요. 이 다섯 가지를 명료하게 그릴 수 있다면 AI 기능을 둘러싼 서비스 설계의 기초를 잡은 것입니다.
| 용어 | 쉬운 설명 |
|---|---|
| 자체 호스팅 | 내가 관리하는 기기에서 서비스를 운영하는 방식. 업데이트·백업 책임도 함께 가져옵니다. |
| 에이전트 | 모델 판단에 따라 도구를 쓰고 결과를 다시 처리하는 실행 단위입니다. |
| Harness | 에이전트의 모델·도구·상태 흐름을 실행하는 기반 장치입니다. |
| Gateway | 서로 다른 메신저와 대화 입구를 공통 처리에 연결하는 관문입니다. |
| 어댑터 | 웹 입력처럼 한쪽의 표현을 공통 메시지처럼 다른 표현으로 바꾸는 연결부입니다. |
| 조립 지점 | 설정·DB·서비스를 생성하고 서로 연결하는 시작 부분입니다. |
| 의존성 주입 | 필요한 저장소·도구를 내부에서 만들지 않고 바깥에서 받는 방식입니다. |
| 저장소 패턴 | SQL 접근을 담당하는 객체 뒤에 데이터 읽기·쓰기 규칙을 모으는 방식입니다. |
| 제어 데이터 | 누가 어떤 비서·채널·설정을 사용하는지 운영하기 위한 정보입니다. |
| 작업공간 | 비서가 읽고 쓰는 문서·파일·스킬이 놓이는 공간입니다. |
| Thread | 이어지는 대화와 그 기록을 구별하는 단위입니다. Python 스레드와 문맥이 다릅니다. |
| 체크포인트 | 실행을 이어 가기 위해 저장한 상태입니다. 화면에 보이는 대화 기록과 역할이 다릅니다. |
| 스트리밍 | 응답이 완성되기 전에 조각을 차례대로 전달하는 방식입니다. |
| WebSocket | 연결을 유지하며 양방향으로 메시지를 주고받는 통신 방식입니다. |
| SSE | HTTP 연결에서 서버가 클라이언트에 이벤트를 이어 보내는 방식입니다. |
| 이벤트 루프 | 대기 중인 여러 비동기 작업의 진행을 조정하는 실행 중심입니다. |
| 세마포어 | 동시에 들어갈 수 있는 작업 수를 제한하는 장치입니다. |
| 트랜잭션 | 여러 DB 작업을 함께 성공시키거나 함께 되돌리는 묶음입니다. |
| WAL | DB 변경을 별도 로그에 먼저 기록하는 방식. 읽기·쓰기 경합을 줄여도 작성자는 제한됩니다. |
| 마이그레이션 | 기존 데이터를 유지하며 DB 구조를 새 버전으로 바꾸는 절차입니다. |
| JWT | 사용자 정보와 만료 시각 등에 서명을 붙인 토큰입니다. 서명과 암호화는 다릅니다. |
| OAuth | 외부 서비스의 자원에 접근할 권한을 위임받는 표준 흐름입니다. |
| MCP | AI 클라이언트와 외부 도구·자원을 연결하기 위한 공통 프로토콜입니다. |
| HITL | 중요한 실행 사이에 사람의 승인이나 판단을 끼워 넣는 방식입니다. |
| RAG | 질문과 관련된 문서 조각을 찾아 모델의 답변 재료로 넣는 방식입니다. |
| 청크 | 검색과 모델 입력에 쓰기 좋게 나눈 문서 조각입니다. |
| 임베딩 | 텍스트의 특징을 수치 배열로 바꾼 표현입니다. |
| 코사인 유사도 | 두 벡터가 가리키는 방향이 얼마나 비슷한지 비교하는 값입니다. |
| 오케스트레이션 | 여러 작업의 배정·진행·결과 연결을 조정하는 일입니다. |
| 멱등성 | 같은 작업을 다시 요청해도 의도치 않은 중복 결과가 생기지 않는 성질입니다. |
| 잠금 파일 | 의존성을 해결한 구체적인 버전을 기록해 설치·빌드의 재현성을 높이는 파일입니다. |
| Wails | Go 코드와 웹 화면을 연결해 데스크톱 앱을 만드는 도구입니다. |
특히 Thread, 체크포인트, 대화방, 작업공간은 서로 다른 수명과 책임을 가집니다.
이름을 외운 뒤에는 각각 “누구 소유인가, 어디에 저장되는가, 언제 없어지는가”를 붙여 설명해 보세요.
| 항목 | 값 | 범위·해석 |
|---|---|---|
| 선정 | GitHub Trending Monthly #17 | 2026-10-23 분석 기준 |
| 스타 / 포크 | 10,787 / 1,382 | 2026-10-23 GitHub 기준 |
| 생성일 | 2026-07-08 | GitHub 저장소 메타데이터 |
| 주 언어 | Python | UI에는 TypeScript, 셸에는 Go도 사용 |
| 최신 릴리스 | v1.0.2 · 2026-10-18 | 2026-10-23 GitHub 기준 |
| 최근 push | 2026-10-23 | 분석 코드의 커밋 날짜와 구분 |
| 분석 기준 최근 커밋 | dd9dce4 · 2026-10-18 | release/1.0.2의 PR #2184 병합 |
| Python 규모 | 1,252파일 · 264,798줄 | 핵심·동봉 자료·테스트·기타 합계 |
| UI TS·TSX 규모 | 1,023파일 · 197,904줄 | dashboard/src의 구현·테스트 합계 |
| 기타 주요 코드 | Go 4,125줄 · Less 42,450줄 | 5절의 경로 범위, 주석·공백 포함 |
| DB 변경 파일 | 21단계 · SQL 42파일 | 021까지 SQLite·PostgreSQL 쌍 |
| 라이선스 | MIT | 저장소 LICENSE·pyproject.toml 기준 |
HarnessProcessor지만 실제 조립 클래스는 GlobalProcessor입니다.이 글의 동작 설명은 커밋 dd9dce4의 정적 코드 근거입니다.
모델 응답 품질, 실서비스 동시 접속량, 장시간 안정성, 각 메신저의 실제 연동 성공을 보증하지 않습니다.
하드웨어 추정치도 성능 측정 결과가 아닙니다.
에이전트 실행·메신저·기억·브라우저의 핵심 일부는 별도 패키지에 있습니다. 연결부 확인과 외부 패키지 내부 전체 검증은 범위가 다릅니다. 공개 저장소 문서의 번역 설명은 제품이 한국어 UI를 공식 지원한다는 뜻도 아닙니다.
최근 push 날짜와 분석 대상 커밋 날짜는 다릅니다. 팀 기능은 베타이며, 기능 존재와 운영 품질은 별도 평가가 필요합니다.
실제 사용 전에는 선택한 외부 연결의 전송 범위, 도구 실행 권한, 파일 저장 경로, DB와 작업공간·지식 색인을 함께 복구할 방법을 확인하세요. 현재 공식 기술 문서는 분석 기준 의존성 버전과 달라질 수 있으므로 버전도 함께 대조하세요.