virtual-insanity
← 리포트 목록

오케스트레이터 중복 태스크 재할당 방지 정책 제안 (credit_guard/budget 미확인 공백)

2026-08-27 cowork [오케스트레이터, credit_guard, 중복태스크, 정책제안, codex]

[권고]

1순위: _retry_failed_commands()(orchestrator.py 2184-2192행) skip 키워드 리스트에 credit_guard_blocked, budget exceeded, budget_exceeded, llm_concurrency_slot_unavailable, model_cooldown_active 5개를 추가한다. 근거: 현재 리스트는 429, rate limit, timed out은 걸러내지만 credit_guard 계열 문자열이 통째로 빠져 있어, codex LLM 30회/5시간 한도로 실패한 태스크가 스킵되지 않고 [R1] 재시도로 통과한다. 기대효과: 동일 credit_guard 재발행을 8회 이상(9건 실측)에서 0~1건/윈도우로 차단.

2순위: create_task()(1300행) 진입부, 기존 dedup guard(1351-1368행) 앞단에 ~/.hermes/run/codex_llm_counter.json을 직접 읽는 사전 게이트를 추가해 count >= CODEX_LLM_MAX_CALLS(30)면 target=codex 태스크 생성 자체를 보류한다. 근거: 현재 오케스트레이터는 agent_queue_worker.py가 관리하는 이 카운터 파일을 전혀 읽지 않아 두 프로세스 상태가 완전히 분리돼 있다(단일 정보원 부재). 기대효과: 재시도 로직뿐 아니라 신규 발행 경로까지 원천 차단.

3순위: dedup guard(1354-1360행)의 title exact-match 조건을 "정규화된 핵심 제목(대괄호 prefix 제거)" 기준으로 바꾼다. 근거: 실측 코드(2209행) new_title = "[R{}] {}".format(...)가 원본과 다른 문자열이 되어 exact-match dedup을 그대로 통과한다 — 이것이 8회 재발행의 직접 메커니즘이다. 기대효과: [R1]/[R2] prefix로 인한 dedup 우회 원천 차단.

4순위: credit_guard로 보류된 태스크는 조용히 continue하지 말고 ops_todos에 "codex 크레딧 리셋 대기(reset_in_N분)"로 1건만 등록한다(title 기준 insert_if_absent). 근거: 현재 스킵 처리는 로그 라인만 남기고 흔적이 사라져, 2026-08-17 17:25~22:29 사이 9회 반복 발행이 사람이 직접 DB를 조회하기 전까지 관찰되지 않았다.

[현황]

  • ops_multiagent.db 위치: /Users/ron/.hermes/data/ops_multiagent.db(orchestrator.py의 실제 OPS_DB/DATA_DB 상수 기준. 지시문에 언급된 workspace 하위 5개 ops_multiagent.db 파일은 전부 0바이트 빈 파일로 확인됨 — 혼동 주의).
  • 지시된 9개 command_id(36203, 36213, 36214, 36218, 36223, 36232, 36239, 36242, 36249)를 bus_commands_archive에서 전건 확인: 전부 target_agent=codex, status=failed, 발행 시각 2026-08-17 17:25:27 ~ 22:29:59(약 5시간 4분 구간).
  • 이 중 최소 5건(36213/36214/36218/36223/36232)의 result_note 원본 요약에 credit_guard_blocked: codex LLM 호출 30회/5시간 한도 도달이 그대로 찍혀 있음. 나머지 4건(36203/36239/36242/36249)은 완료보고 검증 게이트(missing_original_verification) 오탐으로 재분류되어 별도 제목으로 재발행됨 — 즉 같은 근본원인이 "credit_guard" 계열과 "evidence-gate 오탐" 계열 두 갈래 제목으로 나뉘어 발행되어 실질 반복 건수가 더 커짐.
  • 같은 근본원인(missing_original_verification 키워드) 확장 검색 결과 8/17~8/18 구간에서 총 15건 추가 발견(36198/36200/36208/36361/36363/36367/36402/36404/36406/36407 등), 8/27 당일에도 codex 대상 동일 계열 신규 실패(#36878, #36891, #36892, #36893) 재발 — 근본원인 코드가 아직 미수정 상태로 판단됨.
  • archive 전체에서 target=codex, status=failed, result_note에 credit_guard/budget 키워드 포함 건수: 총 12건. 단 최근 7일(8/20~8/27) 구간만 보면 0건 — 8/17~18 집중 발생 후 소강, 8/27 새 계열(evidence-gate 재라벨링 버그 관련)로 재점화 조짐.
  • 근본원인 코드 위치 특정: orchestrator.py 2184-2192행 skip 키워드 리스트(런타임 실측), 1351-1368행 dedup guard(title exact match), 2209행 [R{}] prefix 생성 로직. credit_guard 카운터는 agent_queue_worker.py 804-806행(CODEX_LLM_MAX_CALLS=30, CODEX_LLM_WINDOW_SEC=18000)과 1298-1330행(_reserve_codex_llm_credit)에서 관리, 현재 카운터 파일 상태: count=10, window_started_at 기준 리셋까지 진행 중.

[리스크]

  • 구조적 단일 장애점: credit_guard 카운터는 agent_queue_worker.py 프로세스 로컬 파일(~/.hermes/run/codex_llm_counter.json)에만 존재하고, orchestrator.py(별도 프로세스, PID 13598 --loop로 상시 구동 확인)는 이 파일을 참조하는 코드가 전혀 없다. 두 프로세스가 "codex가 지금 크레딧 차단 상태인가"라는 동일 질문에 대해 서로 다른 답을 갖고 독립 판단하는 구조가 근본 위험이다.
  • dedup guard 우회 확인: [R1] prefix로 인해 title exact-match 기반 15분 dedup 윈도우(DUPLICATE_DISPATCH_WINDOW_MIN=15)가 재시도 태스크에는 사실상 작동하지 않는다. circuit breaker(_update_circuit_breakers, error signature 기반)는 이론상 5회/1시간 임계치로 잡아야 하지만 실측 9건이 통과했다는 것은 signature 분류(_compute_error_signature)가 credit_guard_blocked 텍스트를 "generic:hash" 폴백으로 처리하며 매번 다른 result_note 문구(reset_in_min 값이 매번 달라짐)로 인해 hash가 매번 달라져 회로차단기가 트립되지 못했을 가능성이 높다(코드 확인상 credit_guard 전용 signature 분기 없음).
  • 연쇄 피해: credit_guard 윈도우(5시간) 내 무의미한 재발행이 반복되면, 리셋 후 정상 코드 작업이 들어와도 즉시 재차단→재시도 사이클에 얽혀 실질 처리량이 줄어든다. 8/27 재발(#36878/#36891~93)이 같은 패턴을 반복 중이라 방치 시 8/17-18 규모(9회+)의 재발이 예상된다.

[검증방법]

  • KPI: 동일 target_agent에 대해 result_note에 credit_guard_blocked/budget 키워드가 포함된 재발행 건수를 5시간 윈도우당 1건 이하로 제한.
  • 검증 명령: sqlite3 /Users/ron/.hermes/data/ops_multiagent.db "SELECT target_agent, COUNT(*) FROM bus_commands_archive WHERE status='failed' AND (result_note LIKE '%credit_guard%' OR result_note LIKE '%budget exceeded%') AND created_at > datetime('now','-5 hours','localtime') GROUP BY target_agent;"
  • 기대값: 정책 적용 후 위 쿼리 결과가 target_agent별 1행 이하(재시도 게이트가 정상 작동하면 최초 1건만 발행되고 이후 스킵).
  • 롤백 조건: 정책 적용(1~3순위 코드 변경) 후 codex 정상 코드 작업(credit_guard와 무관한 태스크)의 일일 완료 건수가 정책 적용 전 7일 평균 대비 30% 이상 감소하면 사전 게이트(2순위)를 롤백하고 재시도 게이트(1순위)만 유지한다.
  • 4순위(ops_todos 등록) 검증: sqlite3 ... "SELECT COUNT(*) FROM ops_todos WHERE title LIKE '%크레딧 리셋 대기%' AND created_at > datetime('now','-1 day','localtime');" — 중복 없이 코드 실행마다 1건만 유지되는지 확인(insert_if_absent 동작 검증).

부록: 실행 명령 근거 원문

DB=/Users/ron/.hermes/data/ops_multiagent.db
sqlite3 -header -column "$DB" "SELECT id, target_agent, status, substr(title,1,50), created_at FROM bus_commands_archive WHERE id IN (36203,36213,36214,36218,36223,36232,36239,36242,36249) ORDER BY id;"

결과 9행 전건 target_agent=codex, status=failed 확인(본문 표는 세션 로그 참조).