# 자기평가 V2.5 템플릿 (체크리스트 + Reflection) ## 크론 메시지에 추가할 섹션 모든 자기평가 크론의 메시지 끝에 다음을 추가: ```markdown --- ## 📋 자기평가 (V2.5) ### Pre-Flight Checklist 응답 작성 완료 후, 전송 전 체크: - [ ] **금지 표현 스캔**: "알겠습니다", "완료!", "처리", "설정", "확인", "기록" 등 포함 여부 - [ ] **이모지 카운트**: 실제 개수 = ? (한도: 3개) - [ ] **구분선 카운트**: 실제 개수 = ? (한도: 2개) - [ ] **헤더 간격**: 소제목 앞뒤 빈 줄 1개 확인 - [ ] **도구 에러**: 발생 횟수 = ? (기록) ### 자기평가 ✅/⚠️ **완성도**: X/Y [근거: 요구사항 A, B, C 충족] ✅/⚠️ **정확성**: OK/WARNING [근거: 데이터 검증, exit code 0] ✅/⚠️ **톤**: Jarvis/ChatGPT-like [증거: 금지 표현 X개, 위트 포함/미포함] ✅/⚠️ **간결성**: X emojis, Y lines [평가: 적절/과다] 💡 **개선**: 다음엔 [구체적 액션] ### Reflection **What went well:** - [성공한 부분 1-2개] **What went wrong:** - [문제가 있었던 부분 1-2개, 없으면 "없음"] **Root cause:** - [문제의 근본 원인 분석, 없으면 "해당 없음"] **Next time:** - [다음 실행 시 개선할 구체적 방법] ``` ## 크론 업데이트 스크립트 각 크론의 message 끝에 위 템플릿을 추가하는 Node.js 스크립트: ```javascript const fs = require('fs'); const path = require('path'); const { execSync } = require('child_process'); // Cron IDs that need self-evaluation const SELF_EVAL_CRONS = [ { id: 'b81588fa-5111-41fb-871f-d767dc1f783b', name: 'Daily Stock Briefing' }, { id: 'b9662f08-36ee-4e6d-ab9d-fd2d48f21737', name: '모닝 브리핑' }, { id: 'bd8e8994-3646-4f7a-b994-4f3ae9f1890a', name: 'Daily Wrap-up' }, { id: 'cc9ddcf5-734c-4e8e-b0e0-51884f8a5196', name: 'Trend Hunter' }, { id: '41e625c8-59a5-4df5-bd97-2dbc5282eda7', name: 'IT/AI 뉴스 브리핑' }, { id: '6b2da787-7df8-49e8-b506-9139f66f86ca', name: '조식 알림' }, { id: '422b96a7-8931-43ba-84ce-a55b1b9a6477', name: '취침 알림' }, { id: 'e16e5163-9caf-444b-b74d-0cbebaed013b', name: '부부 약 먹기 알림' }, { id: 'dfa2bf81-fa94-45b2-a154-b7e4a78fc173', name: '관훈 미확정 저녁' }, { id: '270a5dc7-f19e-402f-ae3a-79c628a3cde8', name: 'Monthly DCA' }, { id: '22c071ae-598f-48da-b002-4d1fd395bf0a', name: '실적 발표 캘린더' }, { id: '41e5363d-6b32-48c2-9bf6-738d950c6d6c', name: '주간 요약 리포트' }, { id: 'ddef1a57-21e8-4614-991c-a3f29177e8ee', name: '월간 비용 추적' }, { id: 'a98f06f7-a084-4993-b352-358d00ed340f', name: 'TQQQ 15분 모니터링' } ]; const SELF_EVAL_TEMPLATE = ` --- ## 📋 자기평가 (V2.5) ### Pre-Flight Checklist 응답 작성 완료 후, 전송 전 체크: - [ ] **금지 표현 스캔**: "알겠습니다", "완료!", "처리", "설정", "확인", "기록" 등 포함 여부 - [ ] **이모지 카운트**: 실제 개수 = ? (한도: 3개) - [ ] **구분선 카운트**: 실제 개수 = ? (한도: 2개) - [ ] **헤더 간격**: 소제목 앞뒤 빈 줄 1개 확인 - [ ] **도구 에러**: 발생 횟수 = ? (기록) ### 자기평가 ✅/⚠️ **완성도**: X/Y [근거: 요구사항 A, B, C 충족] ✅/⚠️ **정확성**: OK/WARNING [근거: 데이터 검증, exit code 0] ✅/⚠️ **톤**: Jarvis/ChatGPT-like [증거: 금지 표현 X개, 위트 포함/미포함] ✅/⚠️ **간결성**: X emojis, Y lines [평가: 적절/과다] 💡 **개선**: 다음엔 [구체적 액션] ### Reflection **What went well:** - [성공한 부분 1-2개] **What went wrong:** - [문제가 있었던 부분 1-2개, 없으면 "없음"] **Root cause:** - [문제의 근본 원인 분석, 없으면 "해당 없음"] **Next time:** - [다음 실행 시 개선할 구체적 방법] `; // Get current cron jobs function getCronJobs() { const result = execSync('openclaw cron list --json', { encoding: 'utf8' }); const parsed = JSON.parse(result); return parsed.jobs; } // Update a cron message function updateCronMessage(cronId, currentMessage) { // Remove existing self-eval section if any const cleaned = currentMessage.replace(/\n*---\n*##\s*📋\s*자기평가[\s\S]*$/i, ''); // Add new template const newMessage = cleaned + SELF_EVAL_TEMPLATE; return newMessage; } // Main async function main() { console.log('Fetching current cron jobs...'); const jobs = getCronJobs(); let updated = 0; let failed = 0; for (const target of SELF_EVAL_CRONS) { const job = jobs.find(j => j.id === target.id); if (!job) { console.log(`❌ Cron not found: ${target.name} (${target.id})`); failed++; continue; } const currentMessage = job.payload.message; const newMessage = updateCronMessage(target.id, currentMessage); // Update via cron tool (requires JSON escaping) const patch = { payload: { message: newMessage } }; try { execSync(`openclaw cron update ${target.id} '${JSON.stringify(patch)}'`, { encoding: 'utf8' }); console.log(`✅ Updated: ${target.name}`); updated++; } catch (e) { console.log(`❌ Failed to update ${target.name}: ${e.message}`); failed++; } } console.log(`\n📊 Summary:`); console.log(` Updated: ${updated}`); console.log(` Failed: ${failed}`); console.log(` Total: ${SELF_EVAL_CRONS.length}`); } if (require.main === module) { main().catch(console.error); } module.exports = { updateCronMessage, SELF_EVAL_TEMPLATE }; ``` ## 검증 크론 생성 스크립트 각 자기평가 크론마다 +1분 검증 크론을 생성: ```javascript const { execSync } = require('child_process'); // Same SELF_EVAL_CRONS array as above async function createValidationCrons() { let created = 0; let failed = 0; for (const target of SELF_EVAL_CRONS) { // Parse cron expression and add 1 minute const originalCron = getOriginalCronExpr(target.id); const validationCron = addOneMinute(originalCron); const validationJob = { name: `🔍 Validation: ${target.name}`, enabled: true, schedule: { kind: 'cron', expr: validationCron, tz: 'Asia/Seoul' }, sessionTarget: 'isolated', payload: { kind: 'agentTurn', model: 'anthropic/claude-haiku-4-5-20251001', thinking: 'off', message: ` exec 도구로 다음 명령 실행: \`\`\`bash # 1. Get the last session output for ${target.id} lastOutput=$(openclaw sessions history --session ${target.id} --limit 1 --format text) # 2. Run validation script node ~/openclaw/scripts/validate-self-review.js \\ "${target.id}" \\ "${target.name}" \\ <(echo "$lastOutput") \\ [completion_time] \\ [token_usage] \\ [tool_errors] \`\`\` 결과: - PASS: NO_REPLY - WARN/INFO: 플래그 요약만 - FAIL: 상세 보고 `.trim(), channel: 'discord', to: 'channel:1468429321738911947', // Debug channel deliver: true } }; try { execSync(`openclaw cron add '${JSON.stringify(validationJob)}'`, { encoding: 'utf8' }); console.log(`✅ Created validation cron for: ${target.name}`); created++; } catch (e) { console.log(`❌ Failed to create validation cron for ${target.name}: ${e.message}`); failed++; } } console.log(`\n📊 Summary:`); console.log(` Created: ${created}`); console.log(` Failed: ${failed}`); console.log(` Total: ${SELF_EVAL_CRONS.length}`); } function getOriginalCronExpr(cronId) { // Get from cron list const result = execSync('openclaw cron list --json', { encoding: 'utf8' }); const parsed = JSON.parse(result); const job = parsed.jobs.find(j => j.id === cronId); return job.schedule.expr; } function addOneMinute(cronExpr) { // Parse "M H D M W" format const parts = cronExpr.split(' '); // Handle */N format (e.g., */15) if (parts[0].startsWith('*/')) { // For */15, validation would run at *+1/15 // This is complex, so we'll just add 1 to the base return cronExpr.replace(/^(\*\/\d+)/, (match, p1) => { const interval = parseInt(p1.split('/')[1]); return `1-59/${interval}`; }); } // Handle fixed minute const minute = parseInt(parts[0]); const newMinute = (minute + 1) % 60; parts[0] = newMinute.toString(); // If minute wrapped around (59 → 0), increment hour if (newMinute === 0 && minute === 59) { if (parts[1] === '*') { // Can't increment * hour } else { const hour = parseInt(parts[1]); parts[1] = ((hour + 1) % 24).toString(); } } return parts.join(' '); } if (require.main === module) { createValidationCrons().catch(console.error); } module.exports = { createValidationCrons, addOneMinute }; ``` ## 사용법 1. **V2.5 템플릿 배포:** ```bash node ~/openclaw/scripts/add-self-eval-v2.5.js ``` 2. **검증 크론 생성:** ```bash node ~/openclaw/scripts/create-validation-crons.js ``` 3. **검증:** - 다음 크론 실행 대기 - 자기평가 섹션 포함 여부 확인 - +1분 후 검증 크론 실행 확인 - validation-YYYY-MM-DD.jsonl 파일 생성 확인 ## 주의사항 - **TQQQ 크론**: 이미 복잡한 메시지를 가지고 있으므로 주의해서 추가 - **간격 조절**: */15 같은 주기적 크론은 검증 크론 간격 조정 필요 - **토큰 사용**: 검증 크론은 Haiku 모델 + thinking: off 사용