needs_evidence 재발행 루프 차단 정책 제안서 (create_task 게이트 설계안)
needs_evidence 재발행 루프 차단 정책 제안서
작성: Cowork(다듬) / 조회 기준 2026-10-19 20:44 KST / 코드 수정 없음(설계안만)
[권고]
1순위: 재발행 루프형(guardian 집계 69.1%)을 막는 신규 게이트 _is_needs_evidence_reissue_loop_tripped()를 create_task()의 evidence-gate CB 직후(현 orchestrator.py 2537~2542행 다음)에 추가한다.
- 왜: 현재 두 서킷브레이커(_is_agent_infra_cb_tripped 3998행, _is_agent_evidence_gate_cb_tripped 4238행)는 SQL이 status = 'failed'만 센다. 그런데 반려된 55건은 전부 status='needs_evidence'라서 두 CB의 카운트에 한 건도 잡히지 않는다. 또 중복 차단(2583~2589행)은 제목 완전일치 + 15분(363행)이라서, 제목 속 숫자가 22건→23건→26건…으로 바뀌는 재발행은 통과한다.
- 기대효과: guardian 반려집계류 32건(guardian 반려 37건의 86.5%)이 이어진 3주(9/28~10/19) 동안, 같은 계열 4번째 발행부터 차단하면 최소 29건(32−3)을 발행 단계에서 막는다. 같은 지시문 재발행 기준 신규 반려 유입 약 −50% 이상(55건 중 29건 = 52.7%).
2순위: 인프라 지연형(25.5%, llm_infra_failure 14건, 전부 guardian)은 드롭이 아니라 지연 재등록한다. 4블록 미준수형(5.5%)은 body 템플릿 보강으로 처리한다(아래 3갈래 상세). - 기대효과: 인프라 지연형 14건의 폐기·중복 재발행을 0으로, 4블록 형식 반려 3건(5.5%) 재발 0건 목표.
[현황]
1. 집계 확인
- guardian #42120 result_note(길이 420자, 조회 2026-10-19 20:43 KST, 반환 1행): 55건을 재발행 루프형 69.1% / 인프라 지연형 25.5% / 4블록 미준수형 5.5%로 분류. 근거 줄이 DB에서 잘려 있어(80~100자 단위 절단) 개별 건 매핑은 이 문서에서 재집계했다.
- 직접 재집계(읽기전용 SQL, status='needs_evidence'): 55행, id 40276~42111, 생성일 2026-09-28 13:07 ~ 2026-10-19 17:12, requested_by 55건 모두 orchestrator.
- 비율 산술 확인: 38/55 = 69.1%, 14/55 = 25.5%, 3/55 = 5.5%. 인프라 지연형 14건은 직접 재현(result_note에 llm_infra_failure 포함 14행, 전부 guardian: 40825, 40842, 40854, 41233, 41274, 41316, 41377, 41387, 41420, 41485, 41494, 41628, 41902, 42042). 재발행 루프형 38건의 개별 목록은 guardian 노트에서 확인하지 못했다(미확인). 대신 제목 계열 직접 집계로 32건을 확인했다.
2. 제외 처리
- 제외 기준(에너지·질소비료·호르무즈): 제목+본문에 해당 단어가 있는 3행(#41035, #41348, #41769)을 뺐다. 분석 대상 52건.
3. title 앞 20자 기준 재발행 상위 5묶음 (제외 후 52건)
| 순위 | 앞 20자 | 건수 | id 목록 | 에이전트 |
|---|---|---|---|---|
| 1 | bus_commands needs_e |
4 | 41295, 41377, 41485, 41640 | guardian 4 |
| 2 | agent_queue_worker.p |
1 | 40276 | guardian |
| 3 | ETF 데이터 예열 (etf_dome |
1 | 40297 | ron |
| 4 | 옛 경로 mcp_health_serv |
1 | 40346 | guardian |
| 5 | 미국 IG 등급별 OAS(AAA/AA |
1 | 40408 | analyst-macro |
- 2~5위는 모두 1건 동률이라 가장 작은 id 순으로 채웠다. 49개 묶음 중 2건 이상인 묶음은 1위뿐이다.
- 핵심 발견: 앞 20자 기준으로는 루프가 거의 안 보인다. 실제 루프는 제목에서 숫자만 바꾸며 이어진다. guardian 반려 37건 중 32건(86.5%)이 "needs_evidence N건 … 반려 사유 분류/집계" 계열인데, 숫자가 19→20→21→…→54로 늘면서 매번 다른 제목이 된다(예: #41151 "needs_evidence 19건 반려 사유 라벨별 집계", #41316 "needs_evidence 23건 에이전트별·사유별 분류 집계", #42111 "needs_evidence 54건 반려 사유별 분류와 에이전트별 편중 집계"). 즉 반려 집계 태스크가 반려 건수를 늘려 다시 자기 계열 반려 집계를 만드는 자기증식 구조다.
- 전체 제목에
재발행표기: 6건(#40346, #41323, #41377, #41385, #41498, #41628). CMD 번호(#NNNNN)를 인용해 이어지는 체인: #41133 계열 3건(#41233, #41387, #41435)뿐(5.5%).
4. 반려 라벨 분포 (55건, 중복 라벨 포함)
missing_verification_exit_code 17 / llm_infra_failure 14 / missing_result_or_verification 13 / invalid_verification_exit_code 9 / missing_verification_command 5 / missing_execution_artifact 3 / verification_command_unverifiable 3 / execution_artifact_path_not_found 2 / 라벨 없음 8 / 그 외 단건(missing_execution_value, missing_source_log_path, missing_original_verification, missing_next_action_section) 각 1. - 본문에 4블록 라벨(결론/근거/검증)이 들어 있는 건: 52/52. 본문 템플릿에는 이미 라벨이 있는데도 반려가 난다는 뜻이다. 4블록 미준수의 원인은 "라벨 부재"가 아니라 "검증 줄 안에 실행 명령과 exit code 숫자가 함께 없음"(missing_verification_exit_code 17 + invalid_verification_exit_code 9 = 26행)이다.
5. 현행 게이트의 구멍 (orchestrator.py 실열람)
| 게이트 | 위치 | 구멍 |
|---|---|---|
| 정확 제목 중복 | 2573~2597행, DUPLICATE_DISPATCH_WINDOW_MIN=15(363행) |
제목 완전일치 + 15분. 숫자만 바뀐 제목은 통과 |
| 매크로 이벤트 중복 | 2599~2630행 | 매크로 이벤트 마커 전용. 반려 집계류엔 적용 안 됨 |
| 인프라 CB | _is_agent_infra_cb_tripped 3982~4052행 |
SQL이 status = 'failed'만 조회(3998행). needs_evidence는 카운트 0 |
| evidence-gate CB | _is_agent_evidence_gate_cb_tripped 4211~4271행 |
제목 계열 카운트도 status = 'failed'만(4238행). needs_evidence 0 |
| 재제출 체인 CB | _is_resubmit_chain_closed_cb_tripped 4178~4208행 |
#NNNNN 번호를 제목·본문에 인용한 경우에만 작동. 숫자 건수 제목은 못 잡음. 상태는 needs_evidence 포함(4199행)이지만 인용 번호가 없으면 진입 불가 |
3갈래 방지안 (create_task() 게이트 설계)
갈래 A. 재발행 루프형 — "N회 재발행 시 지시문 수정 없이는 재발행 금지"
- 수정 대상 함수(제안): 신규
_is_needs_evidence_reissue_loop_tripped(target_agent, title, body, requested_by)+ 보조_reissue_family_key(title). 호출 위치는create_task()내_is_agent_evidence_gate_cb_tripped호출 블록(2537~2542행) 바로 다음. - 계열 키:
_normalize_retry_title_for_grouping()(3373행) 결과에서 숫자 덩어리(\d+건등)를#N으로 치환하고 괄호 부가설명을 제거해 target_agent와 묶는다. 예: "needs_evidence 22건… 분류"와 "needs_evidence 23건… 분류"가 같은 계열이 된다. - 판정: 최근 72시간 동안 같은 target_agent + 같은 계열 키에서 status in (needs_evidence, failed)가 3건 이상이고(기본
ORCH_REISSUE_LOOP_THRESHOLD=3), 새 body와 가장 최근 반려 body의 유사도가 높으면(공백·숫자 제거 후 토큰 유사도 ≥0.9) 드롭한다. - "지시문 수정"으로 인정하는 조건(통과): (a) 새 body에 직전 반려 result_note의 gap 코드 중 하나를 해소하는 새 문장(예: 검증 명령 줄 추가) 포함, (b) 검증 명령 문자열 자체가 바뀜, (c) target_agent가 다른 agent로 바뀜, (d) requested_by가 orchestrator가 아닌 직접 요청(4184행 예외 규칙과 동일).
- 드롭 시 동작: 로그
NEEDS_EVIDENCE_REISSUE_LOOP_BLOCKED, 계열당 1회만 ops_todos에 "지시문 수정 필요" 등록(무한 todo 방지). 예외: codex 코드수정 태스크는 4186행과 동일하게 통과. - 동일 효과를 만드는 근본 조치(별도 건): guardian 반려 집계 태스크를 오케스트레이터가 반복 발행하는 생성 경로를 찾아 "직전 동일 계열이 미해결이면 신규 발행 금지"를 적용한다(guardian #42120도 같은 제안을 남김).
갈래 B. 인프라 지연형 — "지연 재등록"
- 수정 대상 함수(제안):
_is_agent_infra_cb_tripped(3982행)의 SQL을status IN ('failed','needs_evidence')로 확장(3998행). 그리고create_task()내 infra_tripped 분기(2419~2524행)에서 llm_infra_failure 사유로만 트립된 경우는 드롭(2522~2524행의return False) 대신 큐에queued로 남기되 시작 가능 시각을 쿨다운 이후로 미룬다(지연 재등록). - 근거: llm_infra_failure 14건은 전부 guardian(25.5%)이고 지시문 문제가 아니라 모델 호출 실패다. 지시문 수정을 요구하면 오히려 정상 지시문을 변형시킨다. 단, 확장 시 evidence-gate 마커(4026~4028행의 missing_* 계열)까지 같은 카운터에 들어가므로 인프라 사유와 형식 사유를 분리해 세는 것이 안전하다(마커 분리가 필요하다는 점이 설계 쟁점).
- 한계: 현 큐 스키마(bus_commands 18개 컬럼)에는 "not_before" 컬럼이 없다. 지연은 (i) body 마커 + 오케스트레이터 폴링 시 시각 필터, 또는 (ii) 컬럼 추가 중 택일해야 한다. 컬럼 추가는 DB 스키마 변경이라 해리 확인이 필요하다. 1단계는 (i) 권고.
갈래 C. 4블록 미준수형 — "body 템플릿 보강"
- 수정 대상 함수(제안):
generated_task_quality_issue(agent, title, body)(scripts/shared/agent_task_quality.py:1304, create_task 2651행에서 호출)와_preserve_queue_task_body(1594행)에 연결되는 body 조립부. 새 검사: body의검증:줄에 실제 실행 명령과exit code <숫자>자리가 모두 있어야 하며 없으면 템플릿 한 줄(검증: <명령> → <결과값>, exit code <숫자>)을 자동 보강한다. - 근거: 본문에 4블록 라벨이 이미 있는 건 52/52인데 exit code 관련 반려가 26행(missing 17 + invalid 9)이다. 라벨을 더 붙여도 효과가 없고, 검증 줄 형식 강제가 핵심이다.
- 기대효과: 라벨 기준 형식 반려 약 26+5(missing_verification_command)행 중 상당수를 발행 단계에서 막는다. 단, 이 건수는 갈래 A·B와 중복 계수된 값이라 독립 효과로 합산하면 안 된다.
[리스크]
- 갈래 A의 유사도 판정이 느슨하면 정당한 후속 태스크를 차단한다(오탐). 완화: 통과 조건 (a)~(d) 우선, 드롭은 로그와 ops_todos 1회 알림으로 가시화, 임계값은 환경변수로 노출.
- 갈래 B에서
needs_evidence를 인프라 CB 카운트에 넣으면 형식 반려가 인프라 장애로 오인되어 guardian 전체 디스패치가 멈출 수 있다(guardian은 반려 37건 중 대표 계열). 마커 분리가 선행되어야 한다. 이것이 가장 큰 구조적 위험이다. - 단일 장애점: create_task()가 모든 생성 경로의 최종 관문이라 게이트 추가 시 지연 쿼리 부담과 fail-open/fail-closed 정책 선택이 영향 전체에 미친다. 기존 게이트와 같이 fail-open(예외 시 통과, 2596~2597행 패턴)을 권고한다.
- 자기증식 구조의 원인은 게이트가 아니라 생성 측(반려 집계를 다시 반려 집계로 발행)에 있다. create_task만 막으면 다른 제목 변형(예: 영문 제목)으로 우회될 수 있다. 계열 키 규칙을 보수적으로 넓혀야 한다.
- 미확인 한계: 재발행 루프형 38건의 개별 id는 guardian 노트(절단됨)에서 확인하지 못했다. 이 문서의 제목 계열 집계 32건은 guardian의 69.1%와 독립 재현한 값이며 완전히 같은 정의가 아닐 수 있다.
codex에 넘길 수정 대상 / 검증 명령 후보 (코드 수정은 이번에 하지 않음)
수정 대상 함수:
- scripts/orchestrator.py: create_task() (2310행, 삽입 위치는 2542행 다음), 신규 _is_needs_evidence_reissue_loop_tripped(), 신규 _reissue_family_key(), _is_agent_infra_cb_tripped() (3982행), _is_agent_evidence_gate_cb_tripped() (4211행, 4238행 status 조건)
- scripts/shared/agent_task_quality.py: generated_task_quality_issue() (1304행)
검증 명령 후보:
- python3 -m py_compile scripts/orchestrator.py scripts/shared/agent_task_quality.py → 기대 exit 0
- python3 -m pytest tests/test_orchestrator_resubmit_chain_cb_40188.py -q (기존 재제출 체인 CB 회귀, 파일 실존 확인) → 기존 통과 수 유지
- 신규 테스트 tests/test_orchestrator_needs_evidence_reissue_loop.py(codex가 작성): ① 제목 숫자만 다른 3건이 needs_evidence면 4번째 드롭 ② 검증 명령이 바뀌면 통과 ③ llm_infra_failure만으로는 드롭 아닌 지연 ④ requested_by가 직접 요청이면 통과
- 실DB 회귀: sqlite3 -readonly /Users/ron/.hermes/data/ops_multiagent.db "SELECT COUNT(*) FROM bus_commands WHERE status='needs_evidence'" → 배포 전 55(기준선), 배포 후 7일 동안 신규 증가분 목표 ≤ 5건
[검증방법]
- KPI: 신규 needs_evidence 발생 건수 — 기준선 21일간 55건(일평균 2.6건) → 배포 후 7일 일평균 ≤ 1.0건(−60%). guardian 반려집계류 계열 신규 발행 0건.
- 로그 지표: orchestrator 로그에서
NEEDS_EVIDENCE_REISSUE_LOOP_BLOCKED건수 > 0이고 같은 계열 4번째 이후 발행이 0건일 것. - 오탐 점검: 드롭된 건 중 사람이 "정당"으로 판정한 비율 ≤ 10%.
- 롤백 조건: (a) 배포 후 24시간 안에 guardian 디스패치 0건이 되거나, (b) 정당한 후속 태스크 오탐이 3건 이상이면 환경변수
ORCH_REISSUE_LOOP_THRESHOLD를 큰 값으로 올려 게이트를 사실상 해제하고 코드 롤백한다.
결론: 재발행 루프형·인프라 지연형·4블록 미준수형 3갈래 방지안을 create_task() 게이트 관점에서 설계한 제안서를 작성했다(코드 수정 없음). 근거: sqlite3 -readonly /Users/ron/.hermes/data/ops_multiagent.db 로 guardian #42120 result_note 1행, status='needs_evidence' 55행(제외 후 52행) 조회, 조회 2026-10-19 20:43~20:44 KST 검증: ls -l /Users/ron/knowledge-agent/400-reports/261019_claude_needs_evidence_reissue_loop_policy.md → (아래 응답 본문에 실제 출력 기재), exit code 0 근거파일: /Users/ron/knowledge-agent/400-reports/261019_claude_needs_evidence_reissue_loop_policy.md (라인 번호는 grep -n 결과를 응답 본문에 기재)