AI Newsletter Digest improvements: fixed QP soft line break decoding, URL extraction, and content cleaning
This commit is contained in:
168
skills/openclaw-self-healing/scripts/self-review-logger.sh
Normal file
168
skills/openclaw-self-healing/scripts/self-review-logger.sh
Normal file
@@ -0,0 +1,168 @@
|
||||
#!/bin/bash
|
||||
# Self-Review V5.0.1 Logger
|
||||
# =========================================
|
||||
# 크론 종료 시 호출하여 메트릭 + 자기성찰 기록
|
||||
#
|
||||
# ⚠️ 주의: "자동 메트릭"은 거짓말입니다.
|
||||
# duration과 tokens는 호출자가 제공해야 합니다.
|
||||
# OpenClaw 크론은 이 값들을 자동으로 알 수 없습니다.
|
||||
# =========================================
|
||||
#
|
||||
# Usage: self-review-logger.sh "크론명" "점수" "tokens_in" "tokens_out" "exit_status" \
|
||||
# "what_went_wrong" "why" "next_action"
|
||||
#
|
||||
# 예시:
|
||||
# bash ~/openclaw/scripts/self-review-logger.sh \
|
||||
# "TQQQ 모니터링" "8.5" "100" "200" "ok" \
|
||||
# "지연 태그 누락" "습관" "다음부터 추가"
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
# === 에러 핸들링 ===
|
||||
error_exit() {
|
||||
echo "❌ Error: $1" >&2
|
||||
exit 1
|
||||
}
|
||||
|
||||
# === 인자 검증 ===
|
||||
if [ $# -lt 5 ]; then
|
||||
error_exit "Usage: $0 'cron_name' 'score' 'tokens_in' 'tokens_out' 'status' ['wrong'] ['why'] ['action']"
|
||||
fi
|
||||
|
||||
# === 인자 파싱 ===
|
||||
CRON_NAME="${1:-unknown}"
|
||||
SCORE="${2:-0}" # 1-10 점수 (LLM 자기평가)
|
||||
TOKENS_IN="${3:-0}" # 입력 토큰 (추정치 허용)
|
||||
TOKENS_OUT="${4:-0}" # 출력 토큰 (추정치 허용)
|
||||
EXIT_STATUS="${5:-ok}" # ok | error
|
||||
WHAT_WENT_WRONG="${6:-없음}"
|
||||
WHY="${7:-N/A}"
|
||||
NEXT_ACTION="${8:-없음}"
|
||||
|
||||
# === YAML 인젝션 방지: 특수문자 이스케이프 ===
|
||||
escape_yaml() {
|
||||
local input="$1"
|
||||
# 따옴표와 백슬래시 이스케이프
|
||||
echo "$input" | sed 's/\\/\\\\/g; s/"/\\"/g; s/'"'"'/\\'"'"'/g'
|
||||
}
|
||||
|
||||
CRON_NAME_SAFE=$(escape_yaml "$CRON_NAME")
|
||||
WHAT_WENT_WRONG_SAFE=$(escape_yaml "$WHAT_WENT_WRONG")
|
||||
WHY_SAFE=$(escape_yaml "$WHY")
|
||||
NEXT_ACTION_SAFE=$(escape_yaml "$NEXT_ACTION")
|
||||
|
||||
# === 날짜/시간 ===
|
||||
DATE=$(date '+%Y-%m-%d')
|
||||
TIME=$(date '+%H%M%S')
|
||||
TIMESTAMP=$(date -u '+%Y-%m-%dT%H:%M:%SZ')
|
||||
|
||||
# === 디렉토리 생성 ===
|
||||
DIR=~/openclaw/memory/self-review/$DATE
|
||||
mkdir -p "$DIR" || error_exit "Failed to create directory: $DIR"
|
||||
|
||||
# === 안전한 파일명 생성 (타임스탬프 포함!) ===
|
||||
# 이모지, 특수문자 제거 + 한글 유지 + 숫자 보존
|
||||
SAFE_NAME=$(echo "$CRON_NAME" | sed 's/[^가-힣a-zA-Z0-9_-]/_/g' | sed 's/__*/_/g' | sed 's/^_//' | sed 's/_$//')
|
||||
if [ -z "$SAFE_NAME" ] || [ "$SAFE_NAME" = "_" ]; then
|
||||
SAFE_NAME="cron"
|
||||
fi
|
||||
|
||||
# ✅ 타임스탬프 포함 → 덮어쓰기 방지
|
||||
FILE="$DIR/${SAFE_NAME}_${TIME}.yaml"
|
||||
|
||||
# === 크론별 목표 로드 ===
|
||||
TARGETS_FILE=~/openclaw/templates/targets-by-cron.yaml
|
||||
DEFAULT_TOKENS=500
|
||||
|
||||
# 크론별 목표가 있으면 사용, 없으면 기본값
|
||||
TOKENS_BUDGET=$DEFAULT_TOKENS
|
||||
if [ -f "$TARGETS_FILE" ]; then
|
||||
# 정규화된 키로 검색 (공백→언더스코어, 특수문자 제거)
|
||||
SEARCH_KEY=$(echo "$CRON_NAME" | sed 's/[^가-힣a-zA-Z0-9_-]/_/g' | sed 's/__*/_/g' | sed 's/^_//' | sed 's/_$//')
|
||||
|
||||
# grep으로 토큰 목표 검색 (2줄 이내에서)
|
||||
CRON_TOKENS=$(grep -A3 "^${SEARCH_KEY}:" "$TARGETS_FILE" 2>/dev/null | grep "tokens:" | head -1 | awk '{print $2}' || echo "")
|
||||
|
||||
if [ -n "$CRON_TOKENS" ] && [ "$CRON_TOKENS" -gt 0 ] 2>/dev/null; then
|
||||
TOKENS_BUDGET=$CRON_TOKENS
|
||||
fi
|
||||
fi
|
||||
|
||||
# === 목표 대비 계산 ===
|
||||
# 점수 기반 판정
|
||||
SCORE_MET="false"
|
||||
if command -v bc &>/dev/null; then
|
||||
if [ "$(echo "$SCORE >= 7" | bc -l 2>/dev/null)" = "1" ]; then
|
||||
SCORE_MET="true"
|
||||
fi
|
||||
else
|
||||
# bc 없으면 정수 비교
|
||||
SCORE_INT=${SCORE%.*}
|
||||
if [ "${SCORE_INT:-0}" -ge 7 ] 2>/dev/null; then
|
||||
SCORE_MET="true"
|
||||
fi
|
||||
fi
|
||||
|
||||
# 토큰 사용률
|
||||
USAGE_PCT="0"
|
||||
if command -v bc &>/dev/null && [ "$TOKENS_OUT" -gt 0 ] 2>/dev/null; then
|
||||
USAGE_PCT=$(echo "scale=1; $TOKENS_OUT / $TOKENS_BUDGET * 100" | bc 2>/dev/null || echo "0")
|
||||
fi
|
||||
|
||||
# === 마감일 계산 (Linux/macOS 호환) ===
|
||||
if date -v+7d '+%Y-%m-%d' &>/dev/null 2>&1; then
|
||||
DEADLINE=$(date -v+7d '+%Y-%m-%d')
|
||||
else
|
||||
DEADLINE=$(date -d '+7 days' '+%Y-%m-%d' 2>/dev/null || date '+%Y-%m-%d')
|
||||
fi
|
||||
|
||||
# === YAML 생성 ===
|
||||
cat > "$FILE" << EOF
|
||||
# Self-Review V5.0.1
|
||||
# Generated: $TIMESTAMP
|
||||
# File: ${SAFE_NAME}_${TIME}.yaml
|
||||
|
||||
# === 메트릭 (호출자 제공) ===
|
||||
# ⚠️ 이 값들은 "자동 수집"이 아닙니다.
|
||||
# 크론이 종료 시 명시적으로 전달해야 합니다.
|
||||
metrics:
|
||||
cron_name: "${CRON_NAME_SAFE}"
|
||||
timestamp: "${TIMESTAMP}"
|
||||
score: ${SCORE}
|
||||
tokens_in: ${TOKENS_IN}
|
||||
tokens_out: ${TOKENS_OUT}
|
||||
exit_status: "${EXIT_STATUS}"
|
||||
|
||||
# === LLM 자기성찰 ===
|
||||
self_reflection:
|
||||
what_went_wrong: "${WHAT_WENT_WRONG_SAFE}"
|
||||
why: "${WHY_SAFE}"
|
||||
next_action: "${NEXT_ACTION_SAFE}"
|
||||
deadline: "${DEADLINE}"
|
||||
|
||||
# === 편향 점검 ===
|
||||
# ⚠️ 기본값 true = 관대함 의심 (보수적 접근)
|
||||
bias_check:
|
||||
am_i_being_too_easy: true
|
||||
evidence: "주간 리뷰에서 검증 필요"
|
||||
user_flagged_before: false
|
||||
|
||||
# === 목표 대비 ===
|
||||
targets:
|
||||
score:
|
||||
goal: 7.0
|
||||
actual: ${SCORE}
|
||||
met: ${SCORE_MET}
|
||||
tokens:
|
||||
budget: ${TOKENS_BUDGET}
|
||||
actual: ${TOKENS_OUT}
|
||||
usage_pct: ${USAGE_PCT}
|
||||
|
||||
# === 메타 ===
|
||||
meta:
|
||||
version: "5.0.1"
|
||||
reviewed_by: null
|
||||
review_date: null
|
||||
EOF
|
||||
|
||||
echo "✅ Self-review logged: $FILE"
|
||||
Reference in New Issue
Block a user