Index
2026-09-21 — Analysis

MolmoBot websocket serving 실사용 검증 — 144회 런치와 StatefulPolicy 상속 불일치

MolmoBot | MolmoBot-Pi0 outputs/*/serve.log 258개 런 분석 | 관측 창: 2026-09-18 ~ 09-21 00:41 (UTC)

TL;DR

144
molmobot 서버 런치
17,085
connection
0
inference 예외
50.8%
2-client 런 대기 비중
99.2%
상태 이월 시작(추정)

1 배경 / 목적

09-17 커밋 aaa3f8d는 serve.py에 type == "molmobot" 분기(+19줄)를 추가해 MolmoBot(F=2, goal 지원) 정책을 websocket 서버로 띄울 수 있게 했다. 260918 카드는 이 변경에 대해 “실제 websocket 연결/하네스 연동 스모크 기록 없음 — 코드 추가만 완료”라고 명시했고, Next Step으로 실제 접속·rollout 스모크를 남겼다.

이번 3일(09-18~21) 동안 MolmoBot 저장소에는 새 claude/*.md, git commit, MolmoBot workdir의 SLURM 기록이 모두 없다. 남아 있는 유일한 활동 흔적은 MolmoBot-Pi0/outputs/<date>/<time>/serve.log(서버 런치마다 1개, 09-15 이후 258개)다. 이 카드는 그 로그를 정량 집계해 (a) 미검증 항목이 실제로 검증되었는지, (b) 운영 중 드러난 문제가 있는지를 확인한다.

기존 한계: 260918 시점에는 type=molmobot 경로가 한 번도 실행되지 않았다. 또한 이 경로는 eval.py in-process 경로(tasks/task.py:243에서 episode마다 policy.reset() 호출)와 달리 서버가 lifecycle을 관리하므로, 동등성이 코드만으로는 보장되지 않았다.

2 작업 내용

코드 변경은 하지 않았고(읽기 전용 분석), 로그 집계와 서버/정책 소스 정독으로 진행했다.

데이터: MolmoBot-Pi0/outputs/2026-09-15 ~ 09-21/*/ (.hydra/config.yaml + serve.log) → 258 런치 - type=molmobot 144 (09-18 13:21:51 UTC 첫 런치) / type=pi 114 (마지막 pi 런치 09-18 13:54:43 UTC) - 로그 timestamp는 UTC (KST = +9h). 09-21 10:20 KST에도 갱신 중인 로그가 있어 일부 서버는 관측 시점에 진행 중 집계 정의: - connection = 'policy_server][INFO] - Connection from (addr) opened' 라인 수 (handshake 실패는 미포함) - server-hours = 로그 첫 줄~마지막 줄 시간차의 합 (진행 중 서버는 절단됨) - 대기 시간 = Semaphore(1) FIFO 모델 추정: start_i = max(open_i, end_{i-1}), wait_i = start_i - open_i (직접 로깅되지 않음) 읽은 소스: molmobot_pi0/eval/real/serve.py, molmobot_pi0/eval/policies/{molmobot,pi,stateful}.py molmo_spaces/evaluation/policy_server.py, policy/base_policy.py, policy/learned_policy/websocket_policy.py, tasks/task.py
해석 전제(추론): ① 로그에 클라이언트 식별자가 없다 — connection은 모두 127.0.0.1(서버와 같은 pod)에서 온다. 타임라인과 260918 카드의 목적으로 미루어 scene_bench 온라인 하네스로 추정하나 로그로는 미확인. ② WebsocketPolicy.reset()이 close() 후 재접속(websocket_policy.py)이고 connection 지속시간 중앙값이 72 s라서 1 connection ≈ 1 episode로 간주했다(미확인).

3 결과

3.1 운영 규모 — 미검증 항목은 해소됨

날짜 (UTC)molmobot 런치connectionserver-hourspi 런치 (참고)
09-18 (첫 molmobot 13:21:51)202,54348.343 (→13:54 이후 0), 1,235 conn
09-19446,388121.60
09-20727,954170.00
09-21 (→00:41)82004.10
합계14417,085344.1pi 09-15~18: 114회 / 2,669 conn

첫 molmobot 런치(09-18 13:21 UTC = 22:21 KST)는 260918 카드 작성(10:10 KST)의 약 12시간 뒤이고, 그 33분 뒤(13:54 UTC)를 마지막으로 pi 서버 런치가 사라진다. 동시에 떠 있던 서버는 최대 14개(로그 span 기준).

체크포인트 (모두 type=molmobot, jointpos, exo_1_left + wrist_left)런치connection
molmobot_runs/molmobot_place_oc/10000 — goal 없이 학습한 no-goal 기준선13815,502
molmobot_runs/molmobot_place_oc_goal/10000 — goal 이미지 학습61,583

공통 설정(로그 확인): max_joint_delta=0.2, action_horizon=16, n_obs_steps=2, obs_step_delta=8, execute=8, flow_steps=10.

3.2 신뢰성

지표값
런치 → 첫 connection 도달144/144 (min / 중앙 / 평균 / max = 28 / 40 / 43.0 / 159 s, 모델 로드 + 클라이언트 대기 포함 상한)
서버 측 inference 예외 (Error in policy server / INTERNAL_ERROR)0
OOM / Killed / MemoryError 흔적0
ERROR 레벨 로그 (molmobot 런)149건 전부 websockets.server: opening handshake failed (EOFError: connection closed while reading HTTP request line) — 139개 런치에서 1회, 5개 런치에서 2회
connection 지속시간 (닫힌 17,057건)p25 66 / 중앙 72 / p75 78 / p95 113 / max 291 s, 합 338.3 connection-hours
런치당 connection중앙 91.5, max 1,065; 132/144가 ≥10, 5/144가 ≤2 (수명 41~134 s의 스모크성 런)
결론: 260918의 미검증 항목(실제 websocket 연결·rollout)은 144개 런치·17,085 connection으로 실사용 검증되었다. handshake 실패는 HTTP 요청 없이 TCP만 연결됐다 닫히는 패턴이며 런치당 정확히 1회(139/144)라서 포트 readiness probe로 추정된다(서버는 이후 정상 서비스).

3.3 발견 — disabling concurrency가 molmobot 런치 144/144에서만 출력

항목pi (PiPolicy)molmobot (MolmoBotGoalPolicy)
상속class PiPolicy(InferencePolicy, StatefulPolicy) — molmo_spaces.policy.base_policy.StatefulPolicy (pi.py:17,38)class MolmoBotGoalPolicy(StatefulPolicy) — 로컬 molmobot_pi0.eval.policies.stateful.StatefulPolicy(InferencePolicy) (molmobot.py:17,30)
서버 isinstance(policy, StatefulPolicy) (policy_server.py:18,92)TrueFalse (서로 다른 클래스)
서버 로그 disabling concurrency0 / 114 런치144 / 144 런치
동시성 제어Semaphore(max_concurrency=100)Semaphore(1) — connection 수명 동안 점유 (생성 :93,:96, 획득 :156)
새 connection 시 서버 동작 (:166-170)신규 client_id → policy.reset(), 재접속 → set_state()호출 없음 (두 호출 모두 Stateful 분기 안쪽)

molmobot.py:30의 주석은 “두 베이스를 같이 쓰면 MRO 충돌”이라 로컬 클래스를 만든 이유를 적고 있다. 이는 로컬 StatefulPolicy(InferencePolicy)와 InferencePolicy를 같이 상속할 때의 충돌이며, pi.py:38이 molmo_spaces의 StatefulPolicy(BasePolicy)(InferencePolicy와 형제 클래스)를 함께 상속해 문제없이 동작하는 것과 대비된다.

3.4 영향 A (측정): 2-client 런치의 Semaphore(1) 대기

동시에 2개 connection이 열린 런치는 5개(molmobot 139개는 항상 최대 1개). 서버는 connection을 받아 metadata를 보낸 뒤 semaphore에서 대기하므로 앞 connection이 닫힐 때까지 추론이 시작되지 않는다.

런치 (UTC)connection평균 대기최대 대기>1 s 대기connection 시간 중 대기 비중
09-18 17:57:0532533.3 s152.8 s28148.9%
09-19 20:17:151,06341.8 s219.7 s99252.5%
09-20 08:46:223338.2 s101.6 s2853.4%
09-20 09:10:3620438.9 s144.5 s17348.9%
09-20 14:00:578927.3 s110.0 s5838.6%
합계1,714 (전체 17,085의 10.0%)39.0 s219.7 s1,53250.8%
범위: 대기는 실제 로그가 아니라 FIFO 단일 permit 모델 추정이다. 시뮬레이터는 action 응답을 기다리며 멈추므로 episode 내용은 바뀌지 않고 wall-clock만 늘어난다. 나머지 139개 런치는 클라이언트 1개만 붙어 손실이 없었다(하네스가 서버 수를 늘려 대응한 것으로 보임 — 최대 14개 동시). 참고로 scene_bench 260825 flagship 카드가 같은 “connection 수명 동안 Semaphore(1) 점유” 모델로 1시간 정지를 겪은 바 있다.

3.5 영향 B (코드 추론, 미측정): episode 간 정책 상태 이월

서버가 reset()을 부르지 않으면 같은 서버 프로세스의 정책 인스턴스가 connection 사이에 상태를 공유한다. 첫 connection은 생성자(molmobot.py:49의 self.reset())로 깨끗하므로, 영향 대상은 17,085 − 144 = 16,941건(99.2%)이다.

확인 안 된 것: ① SR/거리 지표에 실제로 영향이 있는지(첫 ≤8 step의 편차가 전체 episode 성패에 미치는 정도) — 비교 실험이 없다. ② 실제 클라이언트가 scene_bench 하네스인지, 하네스가 별도 reset 경로를 갖는지 — 하네스 코드는 이 저장소 밖. ③ 이 서버로 만든 기존 결과(예: no-goal 팔 molmobot_place_oc를 쓴 온라인 restore 결과들)가 영향을 받았는지.

4 Takeaway

의미

(1) 서빙 경로 자체는 안정적이다. 260918의 +19줄 변경은 144회 런치, 17,085 connection, 최대 12.4 h 수명에서 서버 예외·OOM 0건으로 검증되었다. MolmoBot 평가가 in-process 경로를 벗어나 외부 하네스로 확장될 수 있음이 실증되었다.

(2) 하지만 “동작함”과 “in-process와 동등함”은 별개다. 서버는 StatefulPolicy 여부로 lifecycle(reset·state 저장·동시성)을 결정하는데, MolmoBotGoalPolicy는 이름만 같은 다른 클래스를 상속해 그 계약 밖에 있다. 이 불일치는 예외를 내지 않고 로그 한 줄(disabling concurrency)로만 드러나며, 그래서 144회 동안 아무 오류 없이 통과했다.

(3) 확정된 손실은 throughput뿐이다. 2-client 런치에서 대기가 connection 시간의 50.8%(1,714 connection). 정확성 위험(상태 이월)은 코드 정독으로 도출한 가설이며 크기는 미지수다 — 특히 이 서버를 쓴 결과를 해석할 때 “영향 없음”이 검증되기 전까지는 미확정 요인으로 남겨야 한다.

5 Next Steps

미해결 한계

① 상태 이월이 실제 rollout 지표를 바꾸는지 미측정. ② client 식별·episode 경계는 로그에 없어 “1 connection ≈ 1 episode”는 추정. ③ 대기 시간은 모델 추정치(직접 로깅 없음). ④ obs_history 메모리 증가량 미측정. ⑤ 진행 중이던 서버의 로그는 관측 시점(09-21 10:20 KST)에서 절단되어 server-hours·connection이 하한.

다음 작업 (가설 포함)

A. 수정 후보 (미적용·미검증): molmobot.py의 StatefulPolicy import를 molmo_spaces.policy.base_policy로 바꾸고 class MolmoBotGoalPolicy(InferencePolicy, StatefulPolicy)로 pi.py:38과 같은 형태로 맞춘다. 기대 효과: 로그의 disabling concurrency 소멸, 신규 client_id마다 reset() 호출, set_state/get_state가 서버 경로에서 처음 실행됨(→ MolmoBotPolicyState의 얕은 복사 list(self.obs_history)가 실제로 안전한지도 같이 확인).

B. 동등성 A/B (판정 기준 사전 고정): 동일 seed·동일 cell에서 (a) in-process, (b) 현재 websocket, (c) 수정 websocket. 가설: (a)≈(c), (b)는 각 서버의 2번째 episode 이후 첫 8 step action이 (a)와 다름. 1차는 “2 episode 연속 재생 → 2번째 episode 첫 8 step action 비교” 결정론적 스모크(≤100 step, 로그인 노드 1 GPU로 가능), SR 비교는 sbatch로 분리.

C. 진단 계측: serve.py에서 connection별 obs_history 길이와 첫 chunk index를 로그에 남겨 이월 여부를 직접 관측 + 대기 시간을 모델 추정 대신 직접 로깅.

D. 하네스 쪽 확인: 이 서버로 얻은 scene_bench 결과 중 molmobot_place_oc 계열을 쓴 것이 있는지 대조하고, 있다면 (c) 재측정 후 해석에 반영.