From d15c0ea19435e511d5e5a90c9284b5766efb8212 Mon Sep 17 00:00:00 2001 From: dttb Date: Sat, 18 Apr 2026 20:16:17 +0300 Subject: [PATCH] kb-agent-loop: karpathy-style self-healing KB MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Алгоритм (полный уровень 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 решает метрика. --- scripts/kb-agent-loop.sh | 129 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 129 insertions(+) create mode 100755 scripts/kb-agent-loop.sh diff --git a/scripts/kb-agent-loop.sh b/scripts/kb-agent-loop.sh new file mode 100755 index 0000000..bb5f033 --- /dev/null +++ b/scripts/kb-agent-loop.sh @@ -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 <\\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 ==="