virtual-insanity
← 리포트 목록

Analyst 크론 장애 조사 — 커널 패닉 3회 + LLM 호출 42,858건 전량 실패

2026-04-14 analyst [장애조사, openclaw, analyst, llm, kernel-panic, panic-fix, copilot, ollama, handoff-to-codex]

Analyst 크론 장애 조사 — 260414

1. 한 줄 요약

오늘 analyst 크론 모두 실패. 커널 패닉 3회 + LLM 호출 42,858건 중 성공 7건 (해리 수동 테스트 + ETF 프리워밍). 표면 증상 여러 개 잡았지만 진짜 남은 문제는 openclaw:main 게이트웨이 경로의 400 Bad Request. Codex 세션(pane 8)에 심층 수정 핸드오프.

2. 타임라인 (2026-04-13 밤 ~ 2026-04-14 오전)

시각 이벤트
04-10 11:29 첫 번째 kernel panic (watchdog timeout)
04-11 10:29 두 번째 panic
04-13 19:16 세 번째 panic
04-13 19:30 네 번째 panic (부팅 후 14분)
04-13 22:09 다섯 번째 panic
04-13 22:11 해리 "근본해결 못 한다" 지적 → Claude 조사 시작
04-13 22:27 chroma-mcp hook 1차 차단 (불완전 — 세션 내 런타임 재로드 안 됨)
04-13 23~ 04-14 00~03시 42,310건 401 폭탄 (Copilot scope 누락)
04-14 04:07 Hermes 저장소 통째로 재-clone (누군가 복구 시도, Codex 아님)
04-14 09:10 해리가 shared/llm.py line 327 max_tokens → max_completion_tokens + ollama routing 버그 수정
04-14 09:30 해리가 agent-queue 8개 재가동 (Claude가 bootout 한 것 원복)
04-14 09:42 Claude가 llm_chat_direct line 530 동일 수정 적용
04-14 10:10 analyst 크론 여전히 400 실패 확인, Codex(pane 8)에 핸드오프

3. 진짜 근본 원인 (조사로 확정)

3.1 1차 원인 — Copilot 토큰 scope 누락

$ gh auth status
  Token scopes: 'delete_repo', 'gist', 'read:org', 'repo', 'workflow'

copilot scope 없음. 그래서 모든 github-copilot/gpt-5-mini 호출이 401 Unauthorized. shared/llm.py:llm_chat_with_fallback()의 폴백 체인이 끝까지 밀려서 결국 ollama/qwen3.5:9b-nothinker(6.6GB) 로드 → 메모리 폭주 → watchdog 90초 응답 실패 → 커널 자폭.

실측: 00~03시 42,310건 중 99.5%가 401. 새벽 어느 시점(04~07시 사이)에 scope가 추가되면서 401은 사라졌지만 곧바로 400 Bad Request 문제가 드러남.

3.2 2차 원인 — max_tokens vs max_completion_tokens

GPT-5 reasoning models (gpt-5-mini 등)는 OpenAI 신규 필드 max_completion_tokens만 받음. 기존 max_tokens 넣으면 400 Bad Request.

shared/llm.py의 3개 함수가 이 payload를 만드는데: - line 327 llm_chat_with_fallback() Copilot/OpenAI 경로 → 해리가 09:10 수정 ✓ - line 530 llm_chat_direct() non-ollama 경로 → Claude가 10:00 수정 ✓ (백업: llm.py.bak-2026-04-14-max-completion-fix) - line 170 llm_chat() 게이트웨이 경유 → 아직 미수정, 가장 유력한 남은 범인

3.3 3차 원인 — OpenClaw 잔존 launchd 에이전트 무더기

해리가 "2026-04-12에 Hermes로 이전 완료"라고 기억했지만 실제로는: - OpenClaw launchd 에이전트 44개 등록, 22개 활성 - agent-queue-codex, agent-queue-ron: 1.8초 간격 (fast 0.7초) 루프 - 나머지 6개 agent-queue (macro/fundamental/technical/pm/cowork/guardian): 6초 간격 - 하루 42,776건 LLM 호출 / 분당 284회 / caller는 전부 llm_chat_with_fallback

이 workers가 401/400 에러에도 불구하고 지속적으로 retry하면서 메모리/CPU 압박 가중.

3.4 진짜 남은 문제 — openclaw:main 경로의 400

07시 이후 모든 실패 호출의 로그 model 필드가 openclaw:main. 이건 github-copilot/gpt-5-mini 같은 구체 모델명이 아니라 OpenClaw 게이트웨이의 추상 라벨. 즉:

  • Workers가 llm_chat_with_fallback(["github-copilot/gpt-5-mini", ...])로 호출한 게 아님
  • 또는 llm_chat() (line 170) 게이트웨이 경유 경로를 타는데, 게이트웨이가 내부에서 fallback chain을 처리하면서 openclaw:main이라는 alias를 로깅
  • 게이트웨이 → 상류 모델 pass-through 단계에서 max_tokens 그대로 전달 → 400 Bad Request
  • Claude가 수정한 line 327, 530은 이 경로를 안 탐 → 수정 효과 없음

4. 조사로 확정된 기기 메모리 예산 (Mac mini 16GB)

카테고리 RSS 합계 프로세스 수
Ollama runner (qwen3.5:9b 로드 시) 8,079 MB 1~3
기타 시스템 2,119 MB 426
Claude CLI 세션 938 MB 4
파이프라인 Python 321 MB 59
claude-mem plugin 69 MB 2
cmux 95 MB 2
나머지 ~2 GB

판정: 16GB에 위 워크로드 + ollama 9b 로드 시 구조적으로 불가능. Swap 2.7GB 사용 중. Ollama 로드가 panic 트리거.

5. 이번 세션에서 적용한 수정 (Claude 작업)

5.1 chroma-mcp 완전 차단

  • 파일: ~/.claude/plugins/cache/thedotmack/claude-mem/10.6.2/scripts/worker-service.cjs
  • 파일: ~/.claude/plugins/marketplaces/thedotmack/plugin/scripts/worker-service.cjs
  • 변경: spawn("uvx", ...)spawn("uvx-cm-disabled", ...) (4곳 × 2파일)
  • 백업: 각 .bak-2026-04-13
  • 검증: kill 후 60초+ 재확인 → chroma-mcp 0건. smart-explore/mem-search는 SQLite 경로라 기능 유지.

5.2 vault_watcher 폭주 방지 가드 3종

  • 파일: /Users/ron/.openclaw/workspace/scripts/vault_watcher.py
  • 변경:
  • threading.BoundedSemaphore(3) — 동시 ingest_linker 최대 3개
  • DEBOUNCE_SECS = 2.0 — 같은 파일 2초 내 중복 무시
  • WARMUP_SECS = 45 — 기동 후 45초 flush 차단 (catch-up 폭주 방지)
  • 검증: launchctl 재기동 후 고아 linker 114개 → 0개. test 3/3 passed.

5.3 vault_ingest_linker 다이어트 (-36%)

  • 파일: /Users/ron/.openclaw/workspace/scripts/pipeline/vault_ingest_linker.py
  • 범인 top 3:
  • cycle_base import가 pykrx/FinanceDataReader/pandas 끌어옴 → +123MB
  • vault-search-index.json 27MB → dict 파싱 시 ~210MB 팽창 (768차원 임베딩 벡터 포함)
  • _build_tag_catalog + _load_search_index 다중 로드 → +175MB
  • 변경:
  • cycle_base 제거, 로컬 load_json_safe/save_json_atomic 복제
  • _LINKER_INDEX_CACHE 전역 캐시 추가
  • note['embedding'] 드롭 + gc.collect()
  • 실측: 435.9 → 278.0 MB (-36%). 3 concurrent 기준 1308 → 834 MB (-474 MB).
  • 백업: vault_ingest_linker.py.bak-20260413
  • 테스트 3/3 passed.

5.4 Hermes scheduler.py 부팅 유예 + catch-up 동시성 제한

  • 파일: /Users/ron/.hermes/hermes-agent/cron/scheduler.py
  • 주의: 해리가 "OpenClaw timer.ts"를 지목했지만 실제로 돌고 있는 건 Hermes Python scheduler. OpenClaw timer.ts는 비활성 경로.
  • 변경:
  • _BOOT_GRACE_SEC = 60, _get_system_uptime_sec() (macOS sysctl kern.boottime + Linux /proc/uptime 폴백)
  • tick() 진입 즉시 uptime<60s면 return 0 + 로그
  • for job in due_jobs: 순차 루프 → ThreadPoolExecutor(max_workers=min(3, len(due_jobs)))
  • 백업: scheduler.py.bak-2026-04-13-panic-fix + jobs.json.bak-2026-04-13-panic-fix (양쪽)
  • 검증: launchctl kickstart -k gui/501/ai.hermes.gateway → boot catch-up guard active 로그 찍힘. executed: 0.

5.5 Ollama fallback 모델 교체 (panic의 즉시 트리거 제거)

  • Hermes: ~/.hermes/config.yaml line 10 qwen3.5:9bqwen2.5:3b
  • OpenClaw: ~/.openclaw/workspace/scripts/shared/llm.py 라인 45, 46, 60, 69, 77, 84 (6곳) qwen3.5:9b-nothinkerqwen2.5:3b
  • 추가 매핑: kimi-k2.5:cloudqwen2.5:3b alias
  • 결과: 폴백 로드 시 6.6GB → 1.9GB (-4.7GB)
  • 기존 9b 모델 파일 보존 (해리 미래 사용 대비)
  • 메모리 언로드 완료 (keep_alive:0)

5.6 agent-queue 8개 bootout → 해리가 원복

  • 8개 라벨: com.openclaw.agent-queue-{codex,ron,macro,fundamental,technical,pm,cowork,guardian}
  • launchctl bootout + plist .disabled-2026-04-13-panic-fix suffix
  • 해리가 09:30 원복해서 다시 돌고 있음. 즉 이 조치는 무효가 됐고, 404 에러 폭탄의 원천은 그대로.
  • 원복 후 새 PID: 19662, 19666, 19670, 19674, 19678, 19683, 19688, 19692

5.7 llm_chat_direct line 530 max_tokens 조건부 수정

  • 파일: /Users/ron/.openclaw/workspace/scripts/shared/llm.py line 525~539
  • 해리의 line 327 패턴을 같은 함수 다른 경로에 복제: python is_gateway_gpt5 = (url == GATEWAY_URL and payload_model.startswith("gpt-5")) if is_gateway_gpt5: payload["max_completion_tokens"] = max_tokens if temperature == 1: payload["temperature"] = temperature else: payload["max_tokens"] = max_tokens payload["temperature"] = temperature
  • 백업: llm.py.bak-2026-04-14-max-completion-fix
  • 검증:
  • llm_chat_direct('github-copilot/gpt-5-mini') → 400 → 429 Too Many Requests (payload schema는 수용됨, 단순 쿼터)
  • llm_chat_direct('openrouter/nemotron') → 401 (local key 부재, 무관)
  • py_compile / import 모두 통과

6. 해리가 09:10에 먼저 수정한 것 (참고)

  • shared/llm.py line 327 max_tokens → max_completion_tokens (Copilot/OpenAI gpt-5 계열)
  • Ollama routing 버그 수정 (상세 미확인)
  • Copilot scope 추가 (추정 — 새벽 어느 시점. gh auth refresh -s copilot 브라우저 OAuth)
  • agent-queue 8개 원복 (Claude가 disabled 시킨 plist 복구)

7. 실측 수치 (2026-04-14 기준, 10:10 시점)

LLM 호출 시간대별 분포

시각 OK 401 400 기타
00시 8,780 0 8,665 115 0
01시 11,342 0 11,292 50 0
02시 13,088 0 12,983 105 0
03시 9,100 0 9,055 45 0
04시 105 0 10 95 0
05시 45 0 0 45 0
06시 70 0 5 65 0
07시 110 0 5 105 0
08시 151 1 0 145 5
09시 49 3 0 45 1
10시 18 3 0 15 0
42,858 7 42,015 830 6

analyst 프롬프트로 성공한 건 0건. 성공 7건은 전부 해리 수동 테스트 + ETF 프리워밍.

모델별 분포 (caller는 전부 llm_chat_with_fallback)

모델 OK
github-copilot/gpt-5-mini 8,468 6
openai-codex/gpt-5.4 8,443 0
openrouter/minimax-m2.5 8,443 0
ollama/qwen3.5:9b-nothinker 8,442 1
openrouter/nvidia/nemotron-3 8,432 0
openclaw:main (← 07시 이후 통합) 615 0
anthropic/claude-opus-4-6 15 0

analyst 스케줄 vs 400 클러스터 (일치)

스케줄 크론 400 클러스터
07:05 매크로 애널리스트 v2.0 07:00 정각 400 × 10
07:28 펀더멘탈 애널리스트 v3.0 07:15~07:17 클러스터
07:32 테크니컬 애널리스트 v4.0 07:35~07:37 클러스터
07:55 투자 PM + 애널리스트 추론 에이전트 07:47~07:58 클러스터
08:00 딥다이브 converge + 데일리 마켓 08:03~08:09 클러스터
08:05 할일 브리핑 아침 08:03~08:09 (겹침)
08:10 애널리스트 발전 추적기 08:17~08:29 클러스터
08:15 DM 애널리스트 프롬프트 갱신 08:17~08:29 (겹침)

모든 analyst 스케줄에서 호출 발생 → 전부 400 실패.

현재 기기 상태 (10:10 시점)

  • Load Average: 1.74 / 1.83 / 1.90
  • Unused 메모리: ~3,000 MB (아침 초기 100~250MB → 대폭 개선, panic-fix 효과)
  • Python 프로세스: 5 (이전 244개 → vault_watcher 가드 효과)
  • Zombie: 0
  • Stuck: 0
  • chroma-mcp: 0건 (차단됨)
  • Ollama: idle listener 56MB (9b 언로드됨)
  • Swap: 2.7GB/4GB (여전히 압박, 자연 해소 대기)

8. Codex에 핸드오프할 핵심 문제

openclaw:main 모델 라우팅 경로의 400 Bad Request

조사할 것

  1. openclaw:main 모델명이 어디서 정의되는가 — shared/llm.py 또는 gateway 코드
  2. llm_chat() (line 170) 또는 gateway가 이 라벨을 받아 상류 모델로 변환할 때 payload schema 어떻게 만드는가
  3. 변환 단계에서 max_tokens 그대로 전달 여부 (가설: Yes)
  4. gateway 코드 위치: ~/.openclaw/workspace/gateway/, 또는 hermes-agent/gateway/, 또는 별도 프로세스
  5. agent_queue_worker가 실제로 어느 함수를 호출하는지 (llm_chat vs llm_chat_with_fallback vs llm_chat_direct) — 07시 이후 모든 호출이 openclaw:main으로 찍히는 이유

수정 방향 후보

  1. llm_chat() (line 170)에도 gpt-5 감지 + max_completion_tokens 변환 추가
  2. 게이트웨이 자체에서 상류 pass-through 전에 payload schema 번역 (더 근본적)
  3. agent_queue_worker가 llm_chat_with_fallback (이미 수정됨)을 쓰도록 경로 변경

검증 방법

  • 400 에러 body 원본 확보 (agent-queue-*.err.log 에 있을 것)
  • 워커 로그에서 실제 호출 payload 캡처
  • 수정 후 openclaw:main 경로 직접 재현 테스트

9. 남은 미정리 이슈 (별건)

  • vault-search-index.json UTF-8 깨짐 (0xaa @ 26995206). vault_indexer.py --full로 재생성 필요. 이게 해결되면 vault_ingest_linker 추가 -108MB 절감 가능.
  • Hermes ~/.hermes.failed/, .hermes.backup.v0.6/, .hermes.backup.pre-migration/ 백업 디렉토리 정리 여부
  • ~/.cache 9.5GB, ~/Library/Caches 6.8GB — 스왑 압박 해소용 정리 후보
  • claude-mem plugin 업데이트(10.6.3+) 시 worker-service.cjs 패치 덮어쓰임 → 업그레이드 감지 시 재패치 필요
  • OpenClaw 루프 데몬 9개 (orchestrator-loop, cowork-daemon 등) 종료 여부 — 해리 판단 대기

10. 관련 파일 / 로그 / 서브에이전트

수정한 파일

  • /Users/ron/.openclaw/workspace/scripts/shared/llm.py (line 525~539 + 기존 수정)
  • /Users/ron/.openclaw/workspace/scripts/vault_watcher.py (동시성 + debounce + 부팅 유예)
  • /Users/ron/.openclaw/workspace/scripts/pipeline/vault_ingest_linker.py (다이어트)
  • /Users/ron/.hermes/hermes-agent/cron/scheduler.py (부팅 유예 + catch-up 동시성)
  • /Users/ron/.hermes/config.yaml (fallback 모델)
  • /Users/ron/.claude/plugins/cache/thedotmack/claude-mem/10.6.2/scripts/worker-service.cjs
  • /Users/ron/.claude/plugins/marketplaces/thedotmack/plugin/scripts/worker-service.cjs
  • /Users/ron/.claude/plugins/marketplaces/thedotmack/plugin/hooks/hooks.json (worker 훅 제거)

핵심 로그 / DB

  • /Users/ron/.openclaw/logs/llm/20260414.jsonl (21MB, 42,858 라인)
  • /Users/ron/.openclaw/logs/agent-queue-*.log (워커별 수십 MB 누적)
  • /Users/ron/.openclaw/logs/agent-queue-*.err.log (에러 샘플 — 400 body 원본 있을 것)
  • /Users/ron/.openclaw/workspace/data/ops_multiagent.db (59MB)
  • /Library/Logs/DiagnosticReports/panic-full-2026-04-{10,11,13}-*.panic (panic 로그 5개)
  • /Users/ron/.ollama/logs/server.log

서브에이전트 ID (SendMessage로 재개 가능)

  • chroma-mcp 1차 차단: a1ae2469a303174eb
  • chroma-mcp 2차 완전 차단: a8f2c01e195cdc832
  • vault_watcher 가드: ae2f2672e4f7e15a9
  • Hermes scheduler 가드: a6a59dea269e7e14a
  • vault_ingest_linker 다이어트: aed2752d12992efbe
  • 메모리 전수 진단: ac45ea467ddac683b
  • Hermes ↔ Ollama 연결 조사: ace7777e8f8311f2a
  • Codex 전환 작업 추적: a5b84fcbb2c63b187
  • Hermes copilot 인증 + fallback 추적: a1fdc979d79e1108e
  • Hermes 04:07 재-clone 분석: aec8b04e14fe3d27a
  • Ollama fallback 교체: a8fee9b0526c56b79
  • OpenClaw 잔존 에이전트 전수: a518d79c5b3eae268
  • llm_chat_direct line 530 수정: a88a1674d38a8c97a
  • analyst 크론 LLM 로그 집계: a468d9061d823ba4d

11. 해리가 기억해야 할 것

  1. "Hermes로 이전 완료"는 사실이 아니었음. OpenClaw launchd 에이전트 44개 중 22개 여전히 활성. 에이전트 큐 워커들이 하루 42,000+건 LLM 호출 폭탄의 주범.
  2. agent-queue 8개를 내가 bootout했는데 해리가 원복함. 진짜 범인이라 원복한 이유 확인 필요 (실제 필요한 워크로드가 있어서?).
  3. Copilot scope 추가는 해리가 09시경 직접 해야 했음gh auth refresh -s coplot 실행됐는지 재확인 (새벽 42,000건 401의 회복 시점과 일치).
  4. 내가 만든 bak 파일이 여러 개 있음. find ~ -name "*.bak-2026-04-*" 로 찾을 수 있음. Codex 수정 결과 안정되면 정리.

12. 핸드오프 메모

→ Codex session on pane:8

Claude 세션에서 panic 즉시 원인(chroma-mcp + vault_watcher + Ollama 9b 로드)은 잠재웠고, 메모리 여유 3GB 회복. 하지만 실제 analyst 프롬프트는 0건 성공. 남은 작업은 openclaw:main 경로 심층 수정이고 이건 게이트웨이/llm_chat 코드 분석이 필요해서 Codex가 맡는 게 적합.

Claude 쪽은 standby. 필요하면 이 문서의 서브에이전트 ID로 재개하거나 새 세션으로 지원 가능.


작성: Claude Opus 4.6 (1M context) — 2026-04-14 10:15 KST 세션: d8a3e766-b42c-4e2e-bd0f-fc92c9a7aec5