needs_evidence 반려 67건 반려사유별 개선안 제안서
needs_evidence 반려 67건 반려사유별 개선안
작성: Cowork(다듬) / CMD#42324 / 조회 2026-10-22 03:52~04:05 KST / 코드·DB·크론 수정 없음(제안만)
[권고]
1순위(codex): 반려 집계 태스크가 스스로 반려를 늘리는 자기증식 고리를 "입력 쪽"에서 끊는다.
- 무엇을: scripts/orchestrator.py gather_system_context()(886행)가 Ron LLM에 넘기는 context["queue"]["counts"]에서 needs_evidence 키를 빼거나, "과거 반려 누계이므로 집계 태스크 생성 금지" 주석 키로 바꾼다.
- 왜: 라이브 큐 API 응답 counts가 {'claimed': 1, 'done': 4, 'failed': 1, 'needs_evidence': 67}이다(조회 03:52). get_queue_status()(2192행)가 이 값을 그대로 Ron LLM에 준다. 그래서 LLM이 "needs_evidence 65건…", "66건…", "67건…"처럼 건수를 제목에 넣은 집계 태스크를 계속 새로 만든다. 10-19 21:02 이후 guardian 14건, cowork 1건이 이 계열로 발행됐다(아래 쿼리 Q7). 제목 숫자가 바뀌므로 정확 제목 중복 가드(15분)를 통과한다.
- 기대효과: 고리 입력이 사라지면 신규 집계 태스크는 0건/일에 가까워진다. 현재 이 계열이 일 평균 약 4.7건(14건÷3일) 발행되고 거의 전부 반려된다. 필요한 최종 집계는 사람이 요청할 때만 만든다.
2순위(codex): 10-19에 넣은 재발행 루프 차단 게이트가 실제로는 작동하지 않는 이유 두 가지를 고친다.
- 이유 A. 라이브 오케스트레이터(PID 46446, 가동 24일 19시간, 즉 09-27 07:55 기동)는 orchestrator.py 수정 시각(10-19 21:01) 이전부터 떠 있다. logs/ 전체에서 NEEDS_EVIDENCE_REISSUE_LOOP_BLOCKED 로그는 0건이다. 새 코드를 읽었다는 증거가 없다. 재시작은 해리 승인 사항이다.
- 이유 B. 코드를 다시 읽는다 해도 못 잡는다. 실측으로 _reissue_family_key()를 guardian 15건(#42111~#42322)에 돌렸더니 15개 제목이 15개 서로 다른 계열로 갈렸다. 같은 계열 3회 임계에 도달하는 묶음이 0개다. 연속 body 유사도도 0.18~0.42로 임계 0.9에 한참 못 미친다. 제목 문구와 body가 매번 달라서다. 이 게이트는 문구가 같은 재발행만 막는 설계라 이번 패턴에는 구조적으로 무력하다. 1순위가 이를 대체하는 근본 처방이다.
3순위(guardian/cowork 지시문): 정책 문서 신설 없이, 수신 에이전트 제출 규율을 "제출 직전 3줄 체크"로 못박는다(아래 3절). 규칙은 이미 있고 어긴 것이다.
[현황]
1. 집계 파일 대조 (Q1)
- 대상 파일:
/Users/ron/.hermes/workspace/memory/guardian_needs_evidence_breakdown_20261022.md(1999바이트, 03:04 저장).~/knowledge-agent/400-reports/에는 오늘자 guardian 집계 파일이 없다. search_files 도구가 오류(No such process)를 내서ls -lt·find -newermt로 대신 찾았다. - 집계 파일 합계는 70건(needs_evidence 67 + guardian failed 3). 이 제안서 대상은 DB의 needs_evidence 67건이다. 집계 시점 이후 DB는 needs_evidence 67, failed 2다(Q0). 파일의 "failed 3"과 DB "failed 2"는 차이가 있어, failed는 이 제안서에서 다루지 않는다(미확인).
- 집계 파일 3절의 상위 3라벨: llm_infra_failure 16건 / invalid_verification_exit_code 9건 / verification_command_unverifiable 5건. 4절에 "라벨 없음(결과 확인 보류 문구)" 39건이 가장 많다.
- agent별 건수(DB 재조회, Q2): guardian 49, cowork 8, analyst-macro 5, analyst-pm 4, ron 1 = 67.
2. 실제 사례 대조 — 집계 파일이 놓친 사실
Q3(result_note의 "차단 사유:" 구간만 따로 파싱해 재집계)로 확인한 결과:
| 집계 파일 라벨 | 파일 건수 | 실제 차단 사유 구간에 있는 건수 | 판정 |
|---|---|---|---|
| llm_infra_failure | 16 | 0 | 오분류 |
| invalid_verification_exit_code | 9 | guardian 5 + cowork 2 + ron 1 = 8 | 대체로 일치 |
| verification_command_unverifiable | 5 | guardian 3 + cowork 1 = 4 | 대체로 일치 |
| missing_verification_exit_code | 표에 없음 | guardian 18 (전체 21건, Q4) | 집계 파일이 놓침 |
- llm_infra_failure 16건 전부가 차단 사유가 아니라 "원본 요약" 본문 안에 그 단어가 쓰인 경우다. 사례(Q5): #41902 "llm_infra_failure 계열 실제 원인은 0건", #42042 "llm_infra_failure 계열은 1건(1.8%)", #41628 "llm_infra_failure가 반려 사유인 건은 0건". 즉 guardian이 "인프라 때문이 아니다"라고 쓴 문장을 단순 문자열 검색이 인프라 반려로 센 것이다. 이 숫자를 믿고 "인프라 지연 재등록"을 1순위로 삼으면 방향이 틀린다. 실제 인프라 반려는 사실상 0건이다.
- 상위 실제 사유(Q3): guardian "결과 확인 보류" 24(템플릿 문구 자체), missing_verification_exit_code 18, "검증 근거 없음" 14, missing_verification_command 5, invalid_verification_exit_code 5, execution_artifact_path_not_found 3. analyst-macro 5건·analyst-pm 4건은 전부 missing_result_or_verification 한 가지다.
3. 사례 CMD result_note 직접 조회 (Q6, 라벨당 3건 이상)
- invalid_verification_exit_code: #41917 cowork(10-17 14:53), #41348 cowork, #41504 guardian, #40297 ron. 모두 "검증:" 줄에 exit code가 없거나 0이 아니다. #40297은 ron이 본문에 "exit code 1"(지정 스크립트 실패)을 그대로 적어 invalid가 났다. 이건 작업 자체가 실패했는데 done으로 제출한 경우라 정직한 반려다.
- missing_verification_exit_code / missing_verification_command / execution_artifact_path_not_found: #42314 guardian("근거: …보조 스크립트는 memory/tmp_ne_agg.py와 tmp_ne_agg2.py입니다" → 해당 파일 없음, 검증 줄 자체 없음), #42062 guardian("/Users/ron/.hermes/workspace/ne_agg.py"를 인용했으나 실제는 /tmp/ne_agg.py), #42307 guardian("결론: 작업 결과가 정리됨" — 결론이 시스템 기본 문구). 세 건 모두 지운 임시 스크립트나 틀린 경로를 근거로 인용했다.
- verification_command_unverifiable: #42316 guardian, #40825 guardian, #40691 cowork. "검증:" 값 안에 워크스페이스 경로 토큰이 없어서 난다(규칙은
completion_evidence_policy.md933~945행에 있음). - missing_result_or_verification(analyst-macro 5, analyst-pm 4): #41900, #41067 등. 본문에는 84bp·325bp 같은 실제 계산 결과와 파일 경로까지 있다. 그런데 게이트 판정이 "결과 확인 보류"다. #41900 result_note는 382자로 잘려 있어 어떤 줄이 모자랐는지 DB만으로는 확인되지 않는다(미확인). 같은 사유로 9건 연속이고 모두 같은 유형(IG/HY OAS 파일 판독)이다.
4. 규칙 부재 vs 제출 규율 구분 (Q8, 정책 문서 대조)
| 반려 유형 | 건수(대략) | 기존 규칙 위치 | 판정 |
|---|---|---|---|
| exit code 누락·비0 | guardian 23 + cowork 2 + ron 1 | completion_evidence_policy.md 1796행 "있어도 0이 아니면 invalid_verification_exit_code로 반려한다", agent_completion_submission_standard.md §0 TL;DR 2번 |
규칙 있음 → 제출 규율 |
| 경로 인용 오류·삭제된 임시 스크립트 | guardian 3~5 | agent_completion_submission_standard.md §0 TL;DR 3번("근거파일에 적은 경로를 지금 직접 확인했는가"), §9-9 |
규칙 있음 → 제출 규율. 단 "임시 스크립트를 쓰면 지우지 말고 저장 경로를 근거로 인용"이라는 한 줄은 없음(부분 공백) |
| verification_command_unverifiable | 4 | completion_evidence_policy.md 933~945행 |
규칙 있음 → 제출 규율 |
| analyst-macro/pm missing_result_or_verification | 9 | agent_completion_submission_standard.md §9-2, §9-4, 신설 조항 5(805행) |
규칙 있음. 다만 실제 어느 줄이 모자란지 DB에서 못 봄 → 진단 정보 부족 |
| "반려 집계가 반려를 늘림" | guardian 44 + cowork 8 | 없음. agent_completion_submission_standard.md §10(1038행)은 재발행 시 인프라 vs 근거미비 구분만 다룸 |
규칙·코드 모두 공백 (1순위) |
결론: 문서 규칙이 비어서 생긴 문제는 "자기증식 집계 고리" 한 가지다. 나머지는 이미 있는 규칙을 수신 에이전트(대부분 guardian)가 제출 시점에 어긴 것이다. 또 이 정책 문서는 4,697행과 1,162행이라 에이전트가 실제로 읽고 따를 가능성이 낮다. 문서를 더 늘리는 것은 처방이 아니다.
5. 반려 알림이 다시 태스크가 되는 경로 (Q7, bus_messages·DB)
- 경로 확인됨(입력 쪽): 위 1순위의 큐 counts 경로. 근거 메시지: bus/messages.jsonl(UTC 표기 10-21 15:02~18:01, KST 10-22 00:02~03:01)에서 orchestrator→guardian 태스크 #42310, #42311, #42312, #42314, #42315, #42316, #42321, #42322가 모두 "needs_evidence N건 …" 제목이다.
- 경로 차단됨(출력 쪽): 반려 알림 메시지 자체("결과 확인 보류")는
completion_note_should_not_requeue()(scripts/shared/public_result_formatter.py3729행)와orchestrator.py1961행 필터로 Ron LLM의 "최근 완료 태스크" 컨텍스트에서 제외된다._retry_failed_commands()(3438행)도 같은 필터로 건너뛴다. 즉 반려 알림 → 재처리 경로는 막혀 있고, 실제 순환은 "건수 숫자"를 통해 돈다. - 2차 증폭: 반려된 집계 태스크의 알림은 guardian→ron failed 메시지로 오는데(예 #42311, #42312, #42315, #42321), 이 failed 건은 다시 재시도 후보가 되지 않는다(필터). 따라서 "failed 4건"이라는 새 숫자가 또 다른 제목("needs_evidence 67건·failed 4건")을 만든다.
- 완료됨으로 저장된 반려 알림도 있다. #42322는 DB status=done인데 bus 메시지는 "결과 확인 보류"로 나갔다(10-21 18:04 메시지). 상태와 알림이 불일치한 사례로, 원인은 확인하지 못했다(미확인).
codex용 항목 (코드 수정 필요, 직접 수정하지 않음)
C1. 큐 건수가 집계 태스크를 낳는 입력 차단 (최우선)
- 수정 대상:
scripts/orchestrator.pygather_system_context()886행 부근,context["queue"]["counts"]를 채우는 부분(get_queue_status()2192행의 반환값). - 내용: counts에서
needs_evidence·failed의 과거 누계를 Ron LLM에 노출하지 않는다. 필요하면 "최근 1시간 신규 반려 수"만 준다.RON_SYSTEM_PROMPT(427행)에 "반려 집계·분류 태스크를 새로 만들지 말 것(이미 존재하는 집계 파일을 참조)" 한 줄을 추가한다. - 검증 명령:
python3 -m py_compile scripts/orchestrator.py→ exit 0.python3 -c "import json,orchestrator as o; c=json.loads(o.gather_system_context()); print(c['queue']['counts'])"에서 needs_evidence 키가 사라졌는지 확인. 신규 pytest 1개 추가 후python3 -m pytest tests/ -q -k needs_evidence. 반영 후 24시간 동안sqlite3 -readonly …ops_multiagent.db "SELECT count(*) FROM bus_commands WHERE target_agent IN ('guardian','cowork') AND title LIKE '%needs_evidence%' AND created_at>=datetime('now','localtime','-1 day')"기대값 0. - 롤백 조건: 24시간 내 Ron이 실제 장애성 반려(예: 인프라 반려 급증)를 놓쳤다는 증거가 나오면 counts 노출을 복구하고 신규 반려 수만 따로 노출.
C2. 집계 파일의 llm_infra_failure 오분류 방지
- 수정 대상: guardian이 쓰는 집계 로직이 사람 손 스크립트라 소스 파일은 확인하지 못했다(미확인). 대신 DB에 이미 있는 문서화 기준을 코드로 재사용한다.
scripts/agent_queue_worker.py::_is_llm_infra_failure_note()(2349행)와 같은 판정(노트 결론 줄이needs_evidence: llm_infra_failure_retry_deferred로 시작하는가)을 쓰는 집계용 읽기 전용 스크립트scripts/needs_evidence_summary.py를 신설해, guardian 태스크 지시문이 "이 스크립트 출력만 인용"하도록 한다. - 검증 명령:
python3 scripts/needs_evidence_summary.py→ llm_infra_failure 건수 0(현재 Q3 기준 실제 0건), missing_verification_exit_code 21건(Q4), 합계 67건과 DBSELECT count(*) … status='needs_evidence'가 일치. exit code 0.
C3. 게이트 반려 진단 정보 보존 (analyst-macro/pm 9건 원인 확정용)
- 수정 대상:
scripts/agent_queue_worker.py결과 저장부(guarded_completion_result_note계열, 4205행 부근 "결론: …결과 확인 보류" 조립 구간)와scripts/server.py3091행 부근 동일 템플릿. result_note가 900자([:900])에서 잘리고 "원본 요약"이 220자(raw_excerpt[:220])라서 어느 줄이 모자랐는지 DB만으로는 알 수 없다. - 내용:
bus_commands가 아니라 별도 로그 파일에 원본 제출문 전체와 판정 사유 코드를 남긴다(저장 위치 신설은 codex가 결정). DB 컬럼 추가는 하지 않는다. - 검증 명령: 새 반려 1건을 재현한 뒤 로그에서 원본 4블록 전체가 읽히는지 확인,
python3 -m pytest tests/ -q -k completion통과.
C4. 재발행 루프 게이트(10-19) 적용 확인 및 보강
- 수정 대상: 코드 수정이 아니라 적용 확인이 먼저다. 재시작은 해리 승인 후
launchctl로 하고, 이 에이전트는 하지 않는다. 확인 명령:ps -o lstart= -p $(pgrep -f 'orchestrator.py --loop')가stat -f '%Sm' scripts/orchestrator.py(10-19 21:01)보다 늦은지 확인. - 보강안:
_reissue_family_key()(4301행 이후)는 제목에서 숫자를 지워도 문구가 달라 못 묶는다.needs_evidence·반려가 제목에 있고 대상이 guardian/cowork이면 계열 키를"needs_evidence_aggregate"하나로 통일하는 좁은 규칙을 추가한다(오탐 범위가 이 키워드로 한정됨). 이 보강은 C1이 안 들어갔을 때만 필요하다. - 검증 명령:
python3 -m pytest tests/test_orchestrator_needs_evidence_reissue_loop.py -q기존 8개 통과 + 신규 케이스(실제 제목 15개 → 같은 계열). 기대: Q 재현 스크립트(/tmp/ne_q3.py) 출력이 "15 rows 1 families".
[리스크]
- 집계 파일 오분류(llm_infra_failure 16건)를 근거로 인프라 대책을 세우면 효과가 없다. 지난 제안서(261019_claude_needs_evidence_reissue_loop_policy.md)가 "인프라 지연형 25.5%(14건)"를 2순위로 올렸던 것도 같은 문자열 검색 오분류에서 나왔을 가능성이 높다. 이 문서의 Q3·Q5 재집계가 그 가정을 뒤집는다. 같은 집계 방법이 이번에도 쓰였기 때문이다.
- 단일 장애점: 집계 태스크의 수신자가 guardian 한 에이전트에 몰려 있다(49/67=73.1%). guardian 한 곳의 제출 규율 문제가 전체 반려 수치를 결정한다.
- 재시작 지연: 라이브 오케스트레이터가 24일 넘게 재시작되지 않았다. 코드 수정이 쌓여도 반영 시점이 불명확하다. C4의
ps확인 없이는 "적용됐다"고 보고할 수 없다. - 제안서 한계: #41900류 analyst-macro/pm 9건의 정확한 부족 줄은 확인하지 못했다. C3 없이는 확정 불가다.
[검증방법]
- KPI 1: 신규 집계성 태스크.
SELECT count(*) FROM bus_commands WHERE target_agent IN ('guardian','cowork') AND title LIKE '%needs_evidence%' AND created_at >= <C1 반영 시각>→ 기대 0건/일(현재 약 4.7건/일). - KPI 2: needs_evidence 총량.
SELECT count(*) FROM bus_commands WHERE status='needs_evidence'→ 현재 67에서 24시간 동안 증가폭 0 또는 감소(10-19 제안서 시점 55건 → 10-22 03:52 현재 67건, 3일간 +12건, 하루 약 +4건). - KPI 3: 오분류 해소. C2 스크립트가 보고하는 llm_infra_failure 건수가 DB 실제 결론줄 기준과 일치.
- 롤백: C1 반영 후 24시간 안에 비집계성 반려(analyst-macro/pm 등)가 오히려 증가하거나, Ron이 실제 장애 알림을 놓친 정황이 있으면 counts 노출을 복구한다.
부록. 실행한 조회 목록 (모두 읽기 전용)
- Q0:
sqlite3 -readonly ops_multiagent.db "SELECT status,count(*) FROM bus_commands WHERE status IN ('needs_evidence','failed') GROUP BY status"→ 2행(failed 2, needs_evidence 67) - Q2:
SELECT claimed_by,count(*) FROM bus_commands WHERE status='needs_evidence' GROUP BY 1→ 5행(guardian 49, cowork 8, analyst-macro 5, analyst-pm 4, ron 1) - Q3: python sqlite3
mode=ro로 needs_evidence 67행 result_note의 "차단 사유:" 구간 재집계(/tmp/ne_q4.py) → 67행 - Q4:
SELECT count(*) … status='needs_evidence' AND result_note LIKE '%missing_verification_exit_code%'→ 21 - Q5:
… result_note LIKE '%llm_infra_failure%'→ 16행(전부 본문 인용, 차단 사유 구간 0건) - Q6: 사례 CMD 12건(42316, 42310, 42314, 41917, 41504, 41348, 42083, 41900, 41067, 40297, 42307, 42062) result_note 조회 → 12행
- Q7:
SELECT … requested_by='orchestrator' AND target_agent='guardian' AND created_at>='2026-10-19 21:02' AND (title LIKE '%needs_evidence%' OR title LIKE '%반려%')→ 14; cowork 동일 조건 → 1. 큐 API counts{'needs_evidence': 67, …} - Q8: 정책 문서 줄 확인:
agent_completion_submission_standard.md7~20(§0 TL;DR), 130(§3-1-1), 1038(§10);completion_evidence_policy.md933~945, 1796 - 보조:
/tmp/ne_q3.py로_reissue_family_key/_reissue_body_similarity를 guardian 15건에 적용 → 15 rows 15 families, 연속 유사도 0.18~0.42