virtual-insanity
← 리포트 목록

guardian 완료보고 needs_evidence 반복 반려 방지안

2026-10-26 cowork [guardian, needs_evidence, completion-gate, 4블록]

guardian 완료보고 needs_evidence 반복 반려 방지안

[권고]

  • 1순위 (코드, codex 위임): 완료보고가 4블록 라벨을 갖췄는데도 600~900자에서 잘려 검증/근거파일이 사라지는 경로를 막는다. 조회한 guardian 최근 10건 중 10건(100%)이 이 패턴이다. 수정 지점은 아래 "코드 수정 지점" 표(파일명:줄번호만 기재, 이번 작업에서 코드는 수정하지 않았다).
  • 2순위 (body 문구, orchestrator 발행 템플릿): 집계 태스크 body 끝의 "완료 검증" 문구를 아래 4블록 고정 예시로 바꾼다. 기대효과: 최근 10건 중 문구 결함으로 분류되는 6건(60%, 근거파일에 게이트 반려 문구나 DB 경로만 인용)이 줄어든다.
  • 3순위 (태스크 자체): 최근 10건 중 8건(80%)의 제목이 "needs_evidence N건 집계/분류"다. 반려를 집계하는 태스크가 다시 반려되어 건수가 늘어나는 순환이므로, 같은 집계를 하루 1회로 제한한다(재발행 차단 로직은 scripts/orchestrator.py:2549에 이미 있으나 이 제목군에서 막지 못했다. 원인은 미확인).

[현황] guardian 최근 10건 건별 표

조회: 2026-10-26 12:49 KST, sqlite3 -readonly로 needs_evidence 전체 76건(guardian 56 = 73.7%, cowork 9, analyst-macro 6, analyst-pm 4, ron 1) 확인 후 guardian 최근 10건 선택.

중요한 발견: DB의 result_note는 게이트가 다시 쓴 "결론: 결과 확인 보류 / 근거: 결과물과 검증 근거가 없음…" 형태라, DB만 읽으면 10건 모두 "검증:"과 "근거파일:" 라벨이 없는 것으로 보인다. 그러나 guardian 원본 보고(logs/agent-queue-guardian.log의 results[].note)를 대조하면 10건 모두 4개 라벨이 실제로 있었다. 문제는 라벨 부재가 아니라 라벨 위치(원문 길이)와 내용이다.

CMD 원문 길이 4블록 시작 위치(결론/근거/검증/근거파일) DB 저장본에서 빠진 라벨 원본의 실제 결함
42694 1321 0/365/953/1175 검증, 근거파일 내용은 충실(ls -l, wc -l, exit code 0, 파일:11 인용). 검증이 953자 위치라 900자 기준 밖
42684 1996 0/1535/1781/1855 검증, 근거파일 근거가 1535자 위치. 검증에 명령 없음(합계 계산만), 근거파일이 DB 경로+CMD 번호뿐(줄번호 없음)
42680 2716 0/1843/2209/2309 검증, 근거파일 근거 1843자 위치. 근거파일에 게이트 반려 문구를 인용(자기참조)
42640 2137 0/114/1823/1939 검증, 근거파일 검증 1823자 위치, 명령 없음. 근거파일이 DB 경로+게이트 문구
42454 2902 1759/1928/2342/2470 검증, 근거파일 결론조차 1759자 위치(앞에 긴 서술). 근거파일이 게이트 문구
42412 2500 0/190/2042/2206 검증, 근거파일 검증 2042자 위치, 명령 없음. 근거파일이 게이트 문구
42332 2648 103/534/1426/2515 검증, 근거파일 근거파일 형식은 정상(orchestrator.py:2549)이나 2515자 위치. 검증에서 py_compile/pytest 미실행 명시
42316 1322 222/327/1049/1245 검증, 근거파일 근거파일이 "DB (읽기 전용 조회)"뿐, 경로:줄번호 없음
42314 2468 1743/1849/2042/2168 검증, 근거파일 결론이 "작업 결과가 정리됨"(placeholder, 1743자 위치). 검증에 'exit code 0' 숫자 표기 없음. 근거에 임시 스크립트(tmp_ne_agg.py) 인용
42310 2606 0/1889/2102/2289 검증, 근거파일 근거 1889자 위치. 근거파일이 DB 경로+게이트 문구

집계(원문 기준): 라벨 4개 모두 존재 10/10. 검증 라벨이 900자 안에 있는 건 0/10. 근거파일 라벨이 900자 안에 있는 건 0/10. 근거파일에 "경로:줄번호" 형식을 지킨 건 2/10(42694, 42332). 근거파일에 게이트 반려 문구나 DB 경로만 넣은 건 7/10. 검증 줄에 실행 명령이 없는 건 6/10(42684, 42680, 42640, 42454, 42412, 42316. 정규식 판독 결과이며 건별 수기 확인은 안 했다).

재현 시험: server._completion_storage_block_reasons()에 원문 전체를 넣으면 10건 중 5건이 통과 사유 없음([]), 5건(42694, 42640, 42454, 42314, 42310)은 내용 결함으로 여전히 차단. 같은 원문을 앞 900자만 넣으면 10건 모두 차단(missing_verification_exit_code 등). 즉 5건은 잘림만으로, 5건은 잘림+내용 결함이 겹쳐 반려된다. 이 재현은 로그 note를 그대로 쓴 것이라 실제 반려 당시 입력과 완전히 같다는 보장은 없다.

4블록 요구 존재 여부 확인 (요청 항목 2)

  • config/agent_completion_submission_standard.md: 있음. 9-3 절(469행~)이 guardian을 명시하고, 9-3-1(570~584행)에 guardian 점검용 템플릿이 있다. 그러나 "4블록은 앞 900자 안에" 요구는 이 문서에 없다(grep '900자|600자' 결과 0건).
  • scripts/agent_registry.py guardian AGENT_ROLE_PROMPTS(311~350행): 있음. 329~336행 완료 보고 필수 형식, 337~349행 "guardian 4블록 작성 규칙". 338행에 "4블록 전체를 900자 이내로, 본문보다 먼저" 규칙이 이미 있다. 즉 규칙은 있는데 10건 모두 어겼다. 프롬프트가 349행까지 길어 뒤쪽 규칙이 안 지켜질 가능성이 있으나, 이 프롬프트가 반려 당시 적용되었는지는 확인하지 못했다.
  • 모순 지점: agent_registry.py 321~325행 "출력 형식"은 [점검]→[수치]→[판단]→[다음행동] 순서를 먼저 제시하고 4블록은 329행에서 "별도로 추가"라고 한다. 모델이 본문을 먼저 쓰고 4블록을 뒤에 붙이기 쉬운 구조다(원문 10건 중 6건이 긴 서술 뒤에 근거/검증을 붙임).

코드 수정 지점 (수정하지 않음, 파일명과 줄번호만)

파일:줄 현재 필요 변경(codex 위임)
scripts/agent_queue_worker.py:4700-4701 4블록 사전 판정(_completion_report_has_result_and_verification)이 거짓이면 _public_result_text/_normalize max_len=600으로 잘림. 참이면 4697행 max_len=2000 자르기 전에 4블록 라벨을 앞쪽으로 재배치하거나, 잘림 전 원문으로 게이트 판정
scripts/server.py:3094, 3107, 3122 public_note를 [:900]로 자름 4블록 단위로 자르기(라벨 줄 단위 보존)
scripts/server.py:3086 게이트 판정은 raw_note 전체 기준 판정 입력과 저장본 길이 불일치 여부 확인
scripts/agent_queue_worker.py:4595 note_preview 240자만 로그 반려 로그에 라벨별 시작 위치를 남기도록 (진단용)
scripts/agent_registry.py:321-325 도메인 출력 형식이 4블록보다 먼저 4블록을 맨 앞으로, 도메인 형식은 그 뒤로
scripts/orchestrator.py:477 body 말미 "4블록 명시" 규칙 아래 고정 문구를 body 템플릿에 삽입
config/agent_completion_submission_standard.md:584 guardian 템플릿에 길이 규칙 없음 템플릿 아래에 "4블록 합계 900자, 라벨 줄 맨 앞" 한 줄 추가

4블록 작성 예시 (guardian 점검류)

세 예시 모두 4블록 합계 900자 이내, 4블록을 본문보다 먼저 쓴다. 근거파일은 "게이트 반려 문구"가 아니라 점검 대상의 실물을 인용한다.

예시 A. 프로세스 점검

결론: agent_queue_worker 워커 5개와 게이트웨이 프로세스를 pgrep으로 점검해 5개 생존, 0개 사망을 확인했다.
근거: pgrep -fl agent_queue_worker 실행, 조회 2026-10-26 12:40 KST, source_log_path: /Users/ron/.hermes/logs/autopilot_sweeper.log
검증: verification_command: pgrep -fc agent_queue_worker → 5, exit code 0 (통과)
근거파일: /Users/ron/.hermes/logs/autopilot_sweeper.log:마지막 줄 "worker alive 5/5"

예시 B. DB 점검 (집계 포함)

결론: ops_multiagent.db의 bus_commands를 상태별로 집계해 needs_evidence 76건, 그중 guardian 56건(73.7%)을 확인했다.
근거: sqlite3 -readonly /Users/ron/.hermes/data/ops_multiagent.db "SELECT claimed_by,COUNT(*) FROM bus_commands WHERE status='needs_evidence' GROUP BY 1" 실행, 5행 반환, 조회 2026-10-26 12:49 KST
검증: verification_command: sqlite3 -readonly /Users/ron/.hermes/data/ops_multiagent.db "SELECT COUNT(*) FROM bus_commands WHERE status='needs_evidence'" → 76, 에이전트별 합 56+9+6+4+1=76 일치, exit code 0 (통과)
근거파일: /Users/ron/.hermes/workspace/memory/reflection/needs_evidence_breakdown_20261026.md:11 "| 한글사유:검증 근거 없음 | guardian | needs_evidence | 15 |"

(근거파일 예시는 42694가 실제로 저장한 파일의 11행이다. 존재는 ls -l로 확인함: 4256바이트.)

예시 C. 크론 점검

결론: /Users/ron/.hermes/cron/jobs.json의 크론 160개 중 lastStatus=running 20분 초과 건수를 점검해 0건을 확인했다.
근거: python3으로 jobs.json을 읽어 running 건을 필터, 조회 2026-10-26 12:45 KST, source_log_path: /Users/ron/.hermes/cron/jobs.json
검증: verification_command: python3 -c "import json;print(sum(1 for j in json.load(open('/Users/ron/.hermes/cron/jobs.json'))['jobs'] if j.get('lastStatus')=='running'))" → 0, exit code 0 (통과)
근거파일: /Users/ron/.hermes/cron/jobs.json:1 "jobs"

(예시 C는 2026-10-26 12:51 KST에 jobs.json을 직접 읽어 확인: jobs 160개, lastStatus=running 0건. 최상위 키는 jobs, updated_at.)

금지 패턴(최근 10건에서 실제 발견): 결론 "작업 결과가 정리됨"(42314), 근거파일에 "결론: 결과 확인 보류 … 차단 사유"를 인용(7건), 근거에 tmp_*.py 인용(42314), 검증에 명령 없이 합계 산식만 기재(6건), 검증에 "exit code는 0" 식으로 숫자를 문장 속에 풀어 쓰기(42314).

body 문구 개선안 (orchestrator 발행 템플릿, 집계·점검 태스크 공통)

현재 형태(42694 body 말미): "완료 검증: result_note 에 결론:/근거:/검증:/근거파일: 4블록 라벨을 그대로 사용한다…" — 라벨은 요구하지만 길이와 위치를 요구하지 않는다.

제안 문구(교체):

완료 검증: result_note는 첫 줄부터 '결론:' '근거:' '검증:' '근거파일:' 4줄로 시작하고, 이 4줄 합계를 900자 이내로 쓴다. 설명·표·긴 서술은 4줄 뒤에 둔다.
- 결론: '~을 집계/점검했다' 완료형 한 문장. '작업 결과가 정리됨' 금지.
- 근거: 실행한 sqlite3/pgrep/python3 명령 1줄 + 조회 시각(YYYY-MM-DD HH:MM KST) + source_log_path: <절대경로>
- 검증: verification_command: <재실행 명령 1줄> → <수치>, exit code 0 (한 줄 안에)
- 근거파일: 이번에 저장한 파일의 절대경로:줄번호 "그 줄 원문". 게이트 반려 문구(결과 확인 보류 등)와 DB 경로 단독 인용은 금지.
- 결과표가 길면 파일로 저장하고 근거파일에 그 경로를 쓴다. result_note에 표를 붙이지 않는다.

추가로 집계류 태스크에는 "이 집계의 대상이 needs_evidence 자체이면 같은 제목 태스크가 24시간 안에 이미 발행됐는지 확인하고, 있으면 발행하지 않는다"를 orchestrator 발행 규칙에 넣는다(순환 방지).

[리스크]

  • 규칙(agent_registry.py:338)이 이미 있는데도 10/10이 어겼으므로, 프롬프트 문구 추가만으로는 해결되지 않을 가능성이 높다. 코드 쪽 잘림 처리(위 표 1~2행)가 근본 대책이다.
  • 위 "잘림 경로" 설명은 agent_queue_worker.py:4688-4701과 server.py:3094 등을 읽고 재현 시험을 한 결과이며, 반려 당시 실제로 어느 경로를 탔는지는 실행 로그로 확정하지 못했다.
  • 42454의 결론이 1759자 위치였던 점은 다른 가능성(앞부분에 이전 라운드 문맥이 붙음)이 있다. 원인 미확인.
  • 집계 순환: 반려를 집계하는 태스크가 계속 발행되면 needs_evidence 건수가 계속 늘어난다(지난 5일간 64→76).

[검증방법]

  • 지표: guardian needs_evidence 신규 건수. 현재 누적 56건. 실행: sqlite3 -readonly /Users/ron/.hermes/data/ops_multiagent.db "SELECT COUNT(*) FROM bus_commands WHERE claimed_by='guardian' AND status='needs_evidence' AND created_at>='<적용일>'"
  • 성공 기준: 적용 후 guardian 완료보고 10건 연속에서 검증 라벨이 원문 900자 안에 있고, needs_evidence 신규 0~1건.
  • 롤백 조건: 적용 후 3일 내 guardian done 비율이 현재(done 1건/전체 60건 수준)보다 낮아지거나 다른 에이전트 반려가 늘면 되돌린다.

실행 이력 (이번 작업)

  • sqlite3 -readonly: needs_evidence 상태별 76건, claimed_by별 5행, guardian 최근 10건(id 42694~42310) 조회.
  • logs/agent-queue-guardian.log에서 같은 10건의 원본 note 10건 추출(10/10 일치).
  • logs/completion_evidence_blocked.jsonl에서 10건 모두 1항목씩 확인(action=complete, surface=server.close_bus_command).
  • 코드·DB·설정 수정 없음. 임시 분석 파일은 /tmp/g/ 에만 생성.