Evaluator 분리 원칙 — Hermes/Codex/Claude Code 적용 설계
Evaluator 분리 원칙 적용 설계
Generator와 Evaluator를 분리해서 "만든 놈"과 "검사하는 놈"을 다르게 두는 패턴. 현재 3개 에이전트(Hermes, Codex, Claude Code)의 현황과 적용 방안.
1. 현황 요약
| 에이전트 | 생성(Generator) | 평가(Evaluator) | 갭 |
|---|---|---|---|
| Hermes | AIAgent.run_conversation() → 텔레그램 응답 |
없음 (프롬프트 인젝션 스캔만) | 응답 품질 평가 전무 |
| Codex | codex exec → 코드/답변 |
_validate_codex_result() (길이+물음표 체크만) |
정확성/완성도 평가 없음 |
| Claude Code | 일반 도구 사용 | stop-verify.sh (문법/구조 7종) |
의미적 품질 평가 없음 |
이미 존재하는 Evaluator 패턴 (활용 가능)
- Hermes
_flush_memories_for_session()— 세션 종료 시 별도 에이전트를 띄워 대화를 평가하고 기억 추출. 2차 에이전트 패턴의 가장 완성된 선례. - Claude Code
cross-model-review.sh— 코드 수정 후 Codex(gpt-5.4-mini, read-only)가 diff를 리뷰. LGTM이 아니면 컨텍스트에 주입. PostToolUse 훅으로 구현. - Codex
_handle_codex_collab()검증 단계 — 구현 전에 별도 Codex 콜로 계획을 VERIFIED/CLARIFY/NEEDS_FIX 분류. 사전 게이트.
2. Hermes 적용 방안
삽입 지점
gateway/run.py에서 response = agent_result.get("final_response") (line 2609) 이후, 텔레그램 전송 전.
[Generator] AIAgent.run_conversation() → response
↓
[Evaluator] auxiliary_client로 경량 LLM 콜 → 점수 + 판정
↓
점수 < 3.5/5 → 재생성 요청 / 점수 >= 3.5 → 전송
구현 선택지
| 방식 | 장점 | 단점 |
|---|---|---|
| A. SOUL.md에 자기평가 규칙 추가 | 코드 수정 0, 즉시 적용 | 같은 모델이 자기를 평가 → 신뢰도 낮음 |
| B. auxiliary_client로 별도 LLM 평가 | 다른 모델이 평가, 비용 낮음 | run.py 수정 필요 (10줄 내외) |
| C. agent:end 훅으로 비동기 로깅 | 코드 수정 0, 훅만 추가 | 차단 불가 (fire-and-forget) |
권장: B를 메인 + A를 보조. auxiliary_client는 이미 압축/비전 등 보조 작업에 쓰이고 있어서 패턴이 확립됨. SOUL.md에는 "응답 전 정확성/완성도/톤을 자체 점검하라"는 가벼운 규칙 추가.
SOUL.md 추가 규칙 (안)
## 응답 품질 자기점검
응답을 보내기 전에 스스로 확인:
- 사실 확인: 볼트/웹에서 확인하지 않은 정보가 포함되어 있는가?
- 완성도: 질문의 모든 부분에 답했는가?
- 톤: 해리의 선호(쉬운 말, 간결)에 맞는가?
하나라도 미달이면 보완 후 응답.
3. Codex 적용 방안
현재 4개 실행 경로별 갭
| 경로 | 현재 평가 | 필요 |
|---|---|---|
_handle_codex() 표준 |
없음 → 바로 저장 | 가장 큰 갭 |
_handle_codex_collab() |
_validate_codex_result() (얕음) |
깊이 추가 |
codex_telegram_listener.py |
없음 → 바로 전송 | 위험 |
orchestrate-codex-worker.sh |
없음 → handoff 파일 | handoff 전 검사 |
삽입 지점
_handle_codex() (agent_queue_worker.py, line ~2548):
codex_result = _run_codex_exec(prompt)
# --- Evaluator 삽입 ---
eval_ok, eval_reason = _evaluate_codex_result(codex_result, original_prompt)
if not eval_ok:
# 재시도 또는 llm_execute 폴백
...
# --- 기존 흐름 ---
_save_codex_pattern(title, codex_result)
_evaluate_codex_result() 설계
기존 _validate_codex_result()를 확장:
현재: 길이 < 30 → 실패, 물음표 2개+ → 질문 감지
추가:
1. 코드 블록 존재 시 → ast.parse로 문법 검증
2. 원래 프롬프트 대비 키워드 커버리지 (핵심 단어가 결과에 포함?)
3. "TODO", "FIXME", "placeholder" 감지 → 미완성 경고
4. 경량 Codex 콜(gpt-5.4-mini, read-only)로 "이 결과가 요청을 충족하는가?" 평가
codex_telegram_listener.py 보강
현재 raw stdout을 3500자 잘라서 바로 전송. 최소한:
- ast.parse 통과 여부 (코드인 경우)
- 결과 길이가 비정상적으로 짧으면 경고 첨부
- 에러 스택트레이스가 결과의 50% 이상이면 "실행 실패" 표시
4. Claude Code hooks 확장 방안
stop-verify.sh 현황
7개 검증 타입이 모두 하드코딩:
| Type | 대상 | 검사 |
|---|---|---|
| 1 | pipeline/*.py | pytest 실행 |
| 2 | .json/.yaml | 파싱 검증 |
| 3 | hooks/scripts | py_compile, bash -n |
| 4 | CLAUDE.md | 필수 섹션 존재 |
| 5 | knowledge/*.md | frontmatter 구조 |
| 6 | jobs.json | 스키마 필드 |
| 7 | 전체 .py | ast.parse + 반복수정 감지 |
gotchas.md 동적 룰 로더 설계
현재 gotchas.md는 존재하지 않음. 새로 만들어서 stop-verify.sh가 읽도록 확장:
파일: ~/.openclaw/workspace/gotchas.md
## oil_supply_monitor.py
- pattern: scripts/pipeline/oil_supply_monitor.py
- check: grep -c "extract_indicators" $FILE | [ $(cat) -ge 2 ]
- message: extract_indicators 함수가 정의되고 호출되는지 확인
## cron schedule
- pattern: jobs.json
- check: python3 -c "import json; [assert 'expr' in j.get('schedule',{}) for j in json.load(open('$FILE'))['jobs']]"
- message: 모든 job에 schedule.expr 필드 필수
stop-verify.sh에 Type 8 추가:
# Type 8: Dynamic gotchas
GOTCHAS="$HOME/.openclaw/workspace/gotchas.md"
if [[ -f "$GOTCHAS" ]]; then
python3 "$HOME/.claude/hooks/gotchas-checker.py" "$GOTCHAS" $CHANGED_FILES
fi
gotchas-checker.py는 마크다운을 파싱해서 pattern에 매칭되는 변경 파일에 check 커맨드를 실행. 실패 시 message를 stderr로 출력.
의미적 평가 확장
현재 stop-verify는 구문(syntax) 검증만. 의미(semantic) 평가를 추가하려면:
[기존] stop-verify.sh (문법/구조) → exit 0/2
[추가] stop-evaluate.sh (의미/품질) → cross-model-review.sh 패턴 활용
Stop 이벤트 체인에 stop-evaluate.sh를 추가:
- 변경된 파일의 diff를 Codex(read-only)에 보내서 "이 변경이 의도한 목적을 달성하는가?" 평가
- cross-model-review.sh가 PostToolUse에서 하는 것과 같은 패턴이지만, 최종 Stop 단계에서 전체 변경을 종합 평가
5. 통합 아키텍처
┌─────────────────────────────────────────────────┐
│ Evaluator Layer │
├────────────┬───────────────┬────────────────────┤
│ Hermes │ Codex │ Claude Code │
│ │ │ │
│ auxiliary │ _evaluate_ │ stop-verify.sh │
│ _client │ codex_result │ (Type 1-7 구문) │
│ LLM 콜 │ () │ (Type 8 gotchas) │
│ │ │ │
│ + SOUL.md │ + cross-model │ + stop-evaluate.sh │
│ 자기점검 │ -review.sh │ (의미적 평가) │
└────────────┴───────────────┴────────────────────┘
│ │ │
▼ ▼ ▼
점수 < 3.5 미완성/오류 구문+의미 실패
→ 재생성 → 재시도/폴백 → exit 2 차단
공통 원칙
- Generator ≠ Evaluator: 같은 모델이 자기 결과를 평가하지 않음 (Hermes: auxiliary_client로 다른 모델, Codex: gpt-5.4-mini read-only, Claude Code: Codex cross-review)
- 빠른 게이트 우선: 구문 검사(ms) → 규칙 기반(초) → LLM 평가(5-30초) 순서
- 실패 시 재시도 1회, 2회 실패 시 원본+경고 전달: 무한 루프 방지
- gotchas.md로 규칙 축적: 반복 실패 패턴을 코드가 아닌 문서로 축적 → 배포 없이 규칙 추가
6. 구현 우선순위
| 순위 | 작업 | 난이도 | 효과 |
|---|---|---|---|
| 1 | SOUL.md 자기점검 규칙 추가 | 낮음 | 중간 (즉시 적용) |
| 2 | gotchas.md + checker 파이프라인 | 중간 | 높음 (동적 규칙) |
| 3 | _evaluate_codex_result() 확장 |
중간 | 높음 (코드 품질) |
| 4 | Hermes auxiliary_client 평가 삽입 | 중간 | 높음 (응답 품질) |
| 5 | codex_telegram_listener 보강 | 낮음 | 중간 (안전망) |
| 6 | stop-evaluate.sh 의미적 평가 | 높음 | 높음 (전체 품질) |
7. 핵심 파일 경로 참조
| 구성요소 | 경로 |
|---|---|
| Hermes 게이트웨이 | ~/.hermes/hermes-agent/gateway/run.py |
| Hermes 시스템 프롬프트 | ~/.hermes/SOUL.md |
| Hermes 보조 클라이언트 | ~/.hermes/hermes-agent/agent/auxiliary_client.py |
| Codex 큐 워커 | ~/.openclaw/workspace/scripts/agent_queue_worker.py |
| Codex 텔레그램 리스너 | ~/.openclaw/workspace/scripts/codex_telegram_listener.py |
| Codex 크로스리뷰 훅 | ~/.claude/hooks/cross-model-review.sh |
| Claude Code stop-verify | ~/.claude/hooks/stop-verify.sh |
| Claude Code 훅 설정 | ~/.claude/settings.json |
| ECC 워커 스크립트 | ~/.claude/plugins/.../scripts/orchestrate-codex-worker.sh |