kb-agent-loop: karpathy-style self-healing KB
Алгоритм (полный уровень 4): 1. baseline = kb-health.py → score_before 2. создать ветку auto-fix/DATE 3. Opus через claude -p --permission-mode acceptEdits читает health-отчёт, правит файлы (fm, broken paths, undated todos), коммитит в ветку 4. score_after = kb-health.py 5. если score_after < score_before → merge + push + удалить ветку иначе → reset, ничего не применять Запускать на code-server (LXC 132) где есть Max OAuth. Вручную или cron. Ограничения жёсткие: не удалять файлы, не трогать factual inventories, не пушить самостоятельно из Opus — merge решает метрика.
This commit is contained in:
129
scripts/kb-agent-loop.sh
Executable file
129
scripts/kb-agent-loop.sh
Executable file
@@ -0,0 +1,129 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
# kb-agent-loop — karpathy-style self-healing KB.
|
||||||
|
#
|
||||||
|
# Алгоритм:
|
||||||
|
# 1. baseline = kb-health.py → score_before
|
||||||
|
# 2. создать ветку auto-fix/YYYY-MM-DD
|
||||||
|
# 3. Opus (claude -p) читает health-отчёт, правит файлы, коммитит
|
||||||
|
# 4. score_after = kb-health.py
|
||||||
|
# 5. если score_after < score_before → merge в main, push
|
||||||
|
# иначе → удалить ветку, ничего не применять
|
||||||
|
#
|
||||||
|
# Работает ТОЛЬКО на code-server (LXC 132) где есть Max OAuth для Opus.
|
||||||
|
# Запускать вручную или из cron в ночное окно (напр. воскр 03:00).
|
||||||
|
|
||||||
|
set -u
|
||||||
|
cd /root/knowledge-base || exit 1
|
||||||
|
|
||||||
|
LOG=/var/log/kb-agent-loop.log
|
||||||
|
LOCK=/tmp/kb-agent-loop.lock
|
||||||
|
DATE=$(date +%Y-%m-%d)
|
||||||
|
BRANCH="auto-fix/$DATE"
|
||||||
|
|
||||||
|
exec 9>"$LOCK"
|
||||||
|
if ! flock -n 9; then
|
||||||
|
echo "$(date -Iseconds) [SKIP] предыдущий прогон ещё идёт" >> "$LOG"
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
log() { echo "$(date -Iseconds) [INFO] $*" >> "$LOG"; }
|
||||||
|
abort() { echo "$(date -Iseconds) [ABORT] $*" >> "$LOG"; exit 1; }
|
||||||
|
|
||||||
|
# ──────────────── preflight ────────────────
|
||||||
|
log "=== kb-agent-loop start ==="
|
||||||
|
|
||||||
|
# на main, чисто
|
||||||
|
git checkout main -q || abort "checkout main failed"
|
||||||
|
git pull --rebase --autostash -q origin main || abort "pull failed"
|
||||||
|
if ! git diff --quiet || ! git diff --cached --quiet; then
|
||||||
|
abort "working tree dirty"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# свежий health-снимок
|
||||||
|
python3 scripts/kb-health.py > /dev/null || abort "health.py failed"
|
||||||
|
SCORE_BEFORE=$(python3 -c "import json; print(json.load(open('audit/health-latest.json'))['score'])")
|
||||||
|
log "baseline score: $SCORE_BEFORE"
|
||||||
|
|
||||||
|
# если score=0, нечего делать
|
||||||
|
if [ "$SCORE_BEFORE" -eq 0 ]; then
|
||||||
|
log "score=0, nothing to improve"
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
# ──────────────── branch ────────────────
|
||||||
|
git branch -D "$BRANCH" 2>/dev/null
|
||||||
|
git checkout -b "$BRANCH" -q || abort "branch create failed"
|
||||||
|
|
||||||
|
# ──────────────── Opus prompt ────────────────
|
||||||
|
HEALTH="audit/${DATE}-health.md"
|
||||||
|
if [ ! -f "$HEALTH" ]; then
|
||||||
|
abort "no health report for today"
|
||||||
|
fi
|
||||||
|
|
||||||
|
PROMPT=$(cat <<EOF
|
||||||
|
Ты работаешь в knowledge-base (ветка $BRANCH).
|
||||||
|
Запусти анализ: прочитай $HEALTH — это отчёт по качеству KB.
|
||||||
|
|
||||||
|
Цель: снизить score ($SCORE_BEFORE → меньше). Меньше = лучше.
|
||||||
|
|
||||||
|
Правила (жёсткие):
|
||||||
|
- НЕ удаляй .md-файлы. Только редактируй.
|
||||||
|
- НЕ трогай: .git/, .obsidian/, .claude/worktrees/, notes/claude/ (автосейвы), audit/*.md.
|
||||||
|
- НЕ меняй содержание proxmox-inventory.md, credentials.md, npm-proxy-hosts.md — это факт-файлы.
|
||||||
|
|
||||||
|
Что можно фиксить:
|
||||||
|
- missing_frontmatter: добавь блок \`\`\`---\\ndate: YYYY-MM-DD\\ntype: <decision|note|daily|project|reference>\\ntags: [...]\\n---\`\`\` в начало. Дату бери из имени файла или git log.
|
||||||
|
- broken_paths: обнови путь на правильный, или убери ссылку если файл исчез.
|
||||||
|
- undated_todos: добавь "(YYYY-MM-DD)" в конец строки \`- [ ]\`. Дату — из имени daily/decision или сегодня.
|
||||||
|
- duplicate_basenames: оставь как есть, если не очевидно что удалить.
|
||||||
|
- orphan_files: добавь входящие ссылки в родственный файл если логично. Если не логично — пропусти.
|
||||||
|
|
||||||
|
После правок сделай один или несколько git commit с префиксом "agent-loop: ...".
|
||||||
|
Стоп когда считаешь что дальше делать нечего ИЛИ прошло много итераций.
|
||||||
|
|
||||||
|
НЕ делай git push. НЕ делай merge. Это сделает обёрточный скрипт после проверки метрики.
|
||||||
|
EOF
|
||||||
|
)
|
||||||
|
|
||||||
|
log "calling claude -p (Opus)..."
|
||||||
|
# --permission-mode acceptEdits — Claude Code сам применяет Edit без спроса
|
||||||
|
# Bash-команды всё равно будут просить или быть ограниченными policy
|
||||||
|
if ! claude -p --permission-mode acceptEdits "$PROMPT" >> "$LOG" 2>&1; then
|
||||||
|
log "claude exit non-zero"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# ──────────────── measure ────────────────
|
||||||
|
python3 scripts/kb-health.py > /dev/null || abort "health.py after run failed"
|
||||||
|
SCORE_AFTER=$(python3 -c "import json; print(json.load(open('audit/health-latest.json'))['score'])")
|
||||||
|
log "after score: $SCORE_AFTER"
|
||||||
|
|
||||||
|
DELTA=$((SCORE_BEFORE - SCORE_AFTER))
|
||||||
|
|
||||||
|
# ──────────────── decide ────────────────
|
||||||
|
if [ "$SCORE_AFTER" -lt "$SCORE_BEFORE" ]; then
|
||||||
|
log "✓ IMPROVEMENT: $SCORE_BEFORE → $SCORE_AFTER (−$DELTA), merging"
|
||||||
|
|
||||||
|
# коммитим обновлённый health-report
|
||||||
|
git add audit/ 2>/dev/null
|
||||||
|
if ! git diff --cached --quiet; then
|
||||||
|
git -c user.name=kb-agent-loop -c user.email=agent-loop@dttb.ru \
|
||||||
|
commit -m "agent-loop: health score $SCORE_BEFORE → $SCORE_AFTER" -q
|
||||||
|
fi
|
||||||
|
|
||||||
|
git checkout main -q
|
||||||
|
if git merge --no-ff "$BRANCH" -m "kb-agent-loop: score $SCORE_BEFORE → $SCORE_AFTER (−$DELTA)" -q; then
|
||||||
|
git push -q && log "✓ merged and pushed"
|
||||||
|
git branch -d "$BRANCH" -q
|
||||||
|
else
|
||||||
|
log "✗ merge conflict, keeping branch $BRANCH for manual review"
|
||||||
|
git merge --abort
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
log "✗ NO IMPROVEMENT: $SCORE_BEFORE → $SCORE_AFTER, resetting"
|
||||||
|
git checkout main -q
|
||||||
|
git branch -D "$BRANCH" -q
|
||||||
|
# откатить audit/ если скрипт изменил
|
||||||
|
git checkout -- audit/ 2>/dev/null
|
||||||
|
fi
|
||||||
|
|
||||||
|
log "=== kb-agent-loop end ==="
|
||||||
Reference in New Issue
Block a user