에이전트 상태보고 '재시도대기·요청과다' 문구 반복 노출 — 원인분류 및 정책 제안
[권고]
1순위: _is_llm_infra_failure_note() 판정에 쓰이는 원본 에러 문자열(note)과, close_bus_command가 DB result_note에 최종 저장하는 재포맷 문자열("결론: 작업 결과가 정리됨.\n근거: 근거 섹션 미기재 — 결론 참고.")을 분리 보존하라. 현재 agent_queue_worker.py:4776의 라벨 판정 자체는 워커 프로세스 내부 변수(포맷 전 원문, 예: "anthropic/claude-sonnet-5: timed out")를 보고 있어 기술적으로는 옳게 분류되지만, 같은 실패가 서버 측 public_result_formatter.py(961/1011/1773~1804행)를 거쳐 DB에는 "근거 섹션 미기재" 플레이스홀더로 덮어써진다. 그 결과 해리/오케스트레이터가 DB(ops_multiagent.db.bus_commands.result_note)만 보면 원인이 지워져, 사후 진단 시 항상 로그 grep(agent-queue-*.log)까지 내려가야 한다. result_note에 원본 에러 요약 20~30자를 보존 필드로 추가(예: error_class 컬럼)해 근거 유실을 막을 것을 권고한다. 기대효과: 사후 진단 소요를 로그 3파일 grep(현재 방식)에서 DB 단일 쿼리로 축소.
2순위: (재시도대기·요청과다) 라벨이 "개별 태스크 1건 타임아웃"(analyst-macro #36457, analyst-technical #36458 — 실제 timed out)과 "에이전트 전체 LLM 호출 자체가 예산소진으로 봉쇄"(codex #36451, cowork #36452 — 실제 model_cooldown_active)라는 심각도가 다른 두 상태를 동일 문구로 병합하고 있다. 전자는 해당 태스크만 재시도하면 되고, 후자는 그 에이전트가 일정 시간 어떤 태스크도 처리 불가한 상태다. 라벨을 (요청과다·재시도대기) / (예산소진·전체봉쇄)로 분기해 해리 대시보드에서 "이 에이전트가 지금 아무것도 못 하는 상태인지"를 한눈에 구분할 수 있게 하라. 기대효과: 5개 에이전트 상태창이 동일 문구로 도배되는 현재 UX 혼란(이번 사례 5건 전량 동일 문구) 해소.
[현황]
~/.hermes/data/ops_multiagent.db(154.7MB) 조회 결과, 2026-08-18 17:40~17:43 사이 5개 에이전트에서 다음 6건이 연쇄 실패했다: #36441(codex, HTTP 429 "Too many concurrent runs (max 10)"), #36442(cowork, 동일 429), #36451(codex, model_cooldown_active), #36452(cowork, model_cooldown_active), #36457(analyst-macro, "anthropic/claude-sonnet-5: timed out"), #36458(analyst-technical, 동일 timed out). 6건 전부agent_queue_worker.py:1217~1228의_LLM_INFRA_FAILURE_RE정규식(429/timeout/rate_limit_error/model_cooldown_active 등)에 실제로 매칭되는 진짜 인프라 실패였다 — 임의 하드코딩이나 오판정이 아니었다.- 로그(
agent-queue-macro.log,agent-queue-technical.log) 원문 확인 결과 #36457/#36458은 각각 17:40:56 생성, 17:43:56~57 실패로 약 3분 만에timed out처리됐다. 두 태스크는 이미 title에[R1]접두어가 붙은 1차 자동재시도 건이었고 body에[자동재시도 #1: 이전 실패: 결론: 작업 결과가 정리됨... 근거 섹션 미기재]가 포함돼 있었다 — 즉 최초 실패 원인도 이미 근거 유실 상태로 재시도에 들어간 것으로, 1순위 권고가 지적하는 근거 유실 문제가 재시도 체인에서도 반복됐다. orchestrator.py:2157~2166의_retry_failed_tasks는 combined 텍스트에 "429/rate limit/timed out/cooldown" 키워드가 있으면 애초에 재큐잉을 건너뛴다.2178~2179행에서 title이 이미[R1]이면 추가 재시도를 막아 최대 1회로 캡핑한다 — 즉 무한 재시도 폭주는 구조적으로 차단돼 있음을 확인했다(정책은 이미 정상 동작 중).- guardian 진단 미확보:
bus_commands테이블에target_agent='guardian'인 행이 0건,ops_agent_memory에도 guardian 기록이 0건이다. 즉 이번 현상에 대해 guardian이 실제로 큐를 진단해 결과를 낸 이력이 없다 — 본 제안서는 guardian 진단 결과를 인용하는 대신, cowork가 직접ops_multiagent.db/워커 로그를 조회해 대체 진단했음을 명시한다. 3순위 리스크 항목 참고.
[리스크]
- 단일 장애점(anthropic gateway 동시요청 상한 10): 8개
agent_queue_worker.py프로세스(ron/codex/cowork/guardian/analyst-fundamental/analyst-macro/analyst-technical/analyst-pm)가 각자 1.8~6초 간격으로 폴링하며 LLM을 호출한다.agent_queue_worker.py:1434~1436에 이미 0~4초 지터가 있으나, 이번 사례처럼 5개 에이전트가 3분 내 동시다발로 429/timeout/cooldown을 맞은 것은 지터만으로는 부족하다는 실측 증거다. 상류 한도(max 10)를 넘는 프로세스 수(8개+)가 구조적으로 충돌 가능성을 안고 있다. - guardian 진단 공백: 이번 요청은 "guardian의 큐 진단 결과를 받아 검토"를 전제했지만 guardian이 이 사안에 대해 아무 것도 산출하지 않은 상태였다. guardian이 상시 예방적 진단을 돌지 않고 있다면, 향후 동일 유형 장애가 재발해도 오케스트레이터가 원인 라벨(재시도대기·요청과다)만 보고 근본 원인(동시성 초과 vs 개별 타임아웃 vs 예산소진)을 자동 구분하지 못한다.
- 근거 유실 자체가 재귀적으로 재시도 사유가 됨: #36457/#36458의 1차 실패 사유가 "근거 섹션 미기재"였고, 이 텍스트가 그대로 2차 재시도 body에 주입돼 있었다. 만약 향후 title
[R1]재시도조차 또 근거 유실로 실패하면, 캡(1회)이 걸려 있어 무한루프는 없지만 원인 불명 상태로 태스크가 영구 소실된다.
[검증방법]
- 지표: (1) DB
result_note에 원본 에러 요약이 보존되는 비율 — 현재 0/6(이번 6건 전량 "근거 섹션 미기재"로 유실), 목표 100%. (2) 라벨 분기 적용 후 "예산소진·전체봉쇄" vs "요청과다·재시도대기" 카운트가 대시보드에서 구분 표시되는지. - 검증 명령:
sqlite3 ~/.hermes/data/ops_multiagent.db "SELECT id,target_agent,result_note FROM bus_commands WHERE result_note LIKE '%재시도대기%' OR result_note LIKE '%예산소진%' ORDER BY id DESC LIMIT 10;"— 원본 에러 키워드(429/timeout/cooldown 중 하나)가 result_note 안에 그대로 남아있는지로 판정. - 기대값: 코드 반영(codex 위임) 후 최초 발생하는 인프라 실패 건에서 result_note에 원본 에러 클래스가 보존되고, 라벨이 두 종류로 분기 표시됨.
- 롤백 조건: 라벨 분기 도입 후 evidence-gate 정규식(240~502행, "오탐 0건" 명시된 영역)에 부수 영향이 발생하면 즉시 롤백 — 해당 정규식은 이번 검토에서 손대지 않을 것을 재확인함.
- guardian 보완: 오케스트레이터가 이런 인프라 연쇄실패 패턴을 감지하면 guardian에게 "큐 진단" 태스크를 자동 생성하도록 트리거 추가를 제안(코드 반영은 codex), 다음 유사 사례부터는 실제 guardian 진단 결과를 인용할 수 있게 한다.