GEODE . 문서
GitHub
개요
Explanation

GEODE가 작업을 처리하는 흐름

요청 하나가 처음부터 끝까지 어떻게 흐르는지 따라갑니다. CLI 작업, 게이트웨이 메시지, 예약 실행, geode-mcp 호출이 모두 같은 코어를 지납니다.

요청 하나를 끝까지 따라가기

모듈 목록을 외우는 것보다 요청 하나가 흐르는 길을 한 번 따라가는 쪽이 구조를 빨리 익힙니다. 이 페이지는 터미널에 입력한 자유 텍스트 한 줄이 답이 되어 돌아오기까지의 전 구간을 추적합니다. 입구가 달라져도(메신저, 스케줄러, MCP) 코어는 같으므로, 이 추적 하나면 나머지 입구도 읽힙니다.

thin CLI에서 데몬까지

geode를 실행하면 thin CLI가 뜹니다. CLI 프로세스는 모델을 직접 호출하지 않습니다. 대신 Unix 도메인 소켓 ~/.geode/cli.sock(경로 상수는 core/paths.py) 으로 serve 데몬에 요청을 넘깁니다. 프로토콜은 줄 단위 JSON입니다. 데몬이 떠 있지 않으면 core/cli/ipc_client.py의 자동 시작 로직이 백그라운드에서 데몬을 띄운 뒤 연결합니다.

왜 두 프로세스로 나누었을까요. MCP 서버 연결, 스킬 레지스트리, 훅, 메모리 같은 무거운 상태를 데몬 한 곳에 두면 매 호출마다 새로 켤 필요가 없기 때문입니다. CLI는 입력과 렌더링만 맡는 얇은 클라이언트로 남습니다. 자유 텍스트는 이 대화형 화면 안에서만 받습니다. geode "요청" 형태의 셸 원샷은 지원하지 않습니다.

데몬 안에서 일어나는 일

Request flow: thin CLI over the Unix socket to the daemon's CLIPoller, through the lanes into AgenticLoop and its tools, with events streaming back over the same socket
요청 하나의 전 구간. 이벤트는 같은 소켓을 타고 thin CLI로 돌아옵니다.

소켓 건너편에서 요청을 받는 것은 core/server/ipc_server/poller.py의 CLIPoller입니다. CLIPoller는 요청마다 세션 레인과 글로벌 레인을 차례로 획득합니다. 같은 세션의 요청은 직렬로, 다른 세션은 병렬로 흐르게 만드는 동시성 제어입니다(core/orchestration/lane_queue.py). 레인을 잡으면 요청은 AgenticLoop (core/agent/loop/agent_loop.py)에 들어갑니다.

AgenticLoop는 while stop_reason == "tool_use" 루프입니다. 매 라운드마다 라운드 상한, 시간 예산, 비용 예산 같은 가드를 먼저 확인하고, 컨텍스트 오버플로를 점검한 뒤 (core/agent/context_manager.py), 모델을 호출합니다. 모델이 도구를 요청하면 도구를 실행하고 결과를 대화에 붙여 다음 라운드로 갑니다. 도구는 네이티브 도구 (core/tools/registry.py), 연결된 MCP 서버의 도구 (core/mcp/manager.py), 스킬이 한 호출 표면에서 섞입니다. 도구 수가 많으면 일부만 미리 싣고 나머지는 검색해서 가져오는 deferred loading이 동작합니다(도구와 툴셋).

실행 중 생기는 이벤트(도구 시작, 토큰 스트림, 라운드 전환)는 같은 소켓으로 즉시 돌려보내고, thin CLI가 core/ui/event_renderer.py로 화면에 그립니다. 답이 완성되기 전에도 진행 상황이 보이는 이유입니다.

루프가 끝나는 길

루프는 한 가지 방식으로만 끝나지 않습니다. 종료 사유는 AgenticResult.termination_reason(core/agent/loop/models.py)에 기록됩니다. 대표적인 경로는 다음과 같습니다.

종료 사유의미
natural모델이 도구 요청 없이 텍스트로 답을 마쳤습니다.
max_rounds라운드 상한에 닿았습니다. 마지막 라운드는 텍스트 마무리를 강제합니다.
time_budget_expired벽시계 시간 예산이 소진됐습니다.
cost_budget_exceeded세션 비용이 예산에 닿았습니다. 80% 지점에서 한 번 경고합니다.
context_exhausted압축과 정리 후에도 컨텍스트가 임계 상태입니다.
model_refusal모델 안전 분류기가 응답을 거절했습니다. HTTP 200으로 오는 stop_reason: "refusal"을 잡아 거절 사유 카테고리를 포함한 정직한 메시지로 종료합니다.
user_clarification_needed도구 없이 긴 출력만 반복되는 과사고를 감지하면 멈추고 사용자에게 묻습니다.
llm_error재시도로 회복하지 못한 모델 호출 오류입니다.

어느 경로로 끝나든 결과는 종료 사유와 함께 소켓으로 돌아갑니다. 조용한 실패 대신 이유가 남는 설계입니다.

같은 코어, 다른 입구

위 추적에서 입구만 바꾸면 GEODE의 나머지 호출 경로가 됩니다. 네 입구 모두 같은 AgenticLoop, 같은 메모리, 훅, LLM 라우터를 지납니다.

입구코어까지의 길
CLI 자유 텍스트thin CLI, cli.sock, CLIPoller. 이 페이지의 추적 그대로입니다.
메신저 메시지Slack Socket Mode 또는 Discord/Telegram poller(core/server/supervised/)가 메시지를 받고, binding(core/messaging/binding.py)이 세션으로 라우팅한 뒤 같은 레인과 루프를 지납니다.
예약 실행데몬 안의 스케줄러(core/scheduler/service.py)가 예약 시각에 같은 코어로 작업을 트리거합니다.
geode-mcp run_agent다른 에이전트(예: Claude Code)가 MCP 도구로 GEODE를 부릅니다. core/mcp_server.pyrun_agentic_oneshot(core/cli/bootstrap.py)으로 한 번의 agentic 실행을 돌리고 텍스트, 라운드 수, 종료 사유를 돌려줍니다.

다음 단계