Source identity
| Field | Recorded value |
|---|---|
| Source type | GitHub source |
| Original record | Janady13/complete-agi-core / seferim/active_learner.sh |
| Date | Not specified in source |
| Repository | Janady13/complete-agi-core |
| Revision | 33aa84f2aa61cedbe010240ada7f4efbdfe795a7 |
| Branch | claude/improve-math-logic-ehIW1 |
| Path or archive member | seferim/active_learner.sh |
| Original SHA-256 | f478b5f3a58d432595d7dbb21c14efae0cae10e8092d0497fdbb42722be92be4 |
| Git blob SHA | 1be895de7333327d0029ba08a4570ab220fb284c |
| Reading-copy SHA-256 | f478b5f3a58d432595d7dbb21c14efae0cae10e8092d0497fdbb42722be92be4 |
Recovered source transcript
Download complete source text ↓519 lines · 16.4 KB
#!/bin/bash
#===============================================================================
# active_learner.sh - Active Learning with Information-Theoretic Selection
#
# Implements:
# - Information gain computation for topic selection
# - Uncertainty sampling
# - Curiosity-driven exploration with UCB
# - Spaced repetition (SM-2 algorithm)
# - Knowledge graph integration
#===============================================================================
set -euo pipefail
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
source "${SCRIPT_DIR}/../orchestrator/lib/supervisor.sh" 2>/dev/null || true
#===============================================================================
# CONFIGURATION
#===============================================================================
LEARNER_DB="${LEARNER_DB:-${SCRIPT_DIR}/knowledge_graph.db}"
LEARNER_STATE="${LEARNER_STATE:-${SCRIPT_DIR}/learner_state.json}"
LEARNER_LOG="${LEARNER_LOG:-${SCRIPT_DIR}/learner.log}"
# Learning parameters (golden ratio scaled)
PHI=1.6180339887
ALPHA=$(echo "scale=4; 0.1 * $PHI" | bc)
BETA=$(echo "scale=4; 0.05 * $PHI" | bc)
GAMMA=$(echo "scale=4; 0.02 * $PHI" | bc)
# UCB parameters
UCB_C=2.0 # Exploration constant
# Spaced repetition (SM-2)
SM2_INITIAL_INTERVAL=1 # days
SM2_INITIAL_EF=2.5
#===============================================================================
# KNOWLEDGE CATEGORIES AND SOURCES
#===============================================================================
declare -A CATEGORIES=(
[science]="physics chemistry biology astronomy neuroscience"
[technology]="ai ml programming systems networks"
[philosophy]="ethics metaphysics epistemology consciousness"
[mathematics]="algebra calculus topology statistics"
[arts]="music literature visual_arts"
[history]="ancient modern contemporary"
)
declare -A SOURCES=(
[wikipedia]="https://en.wikipedia.org/api/rest_v1/page/random/summary"
[arxiv]="http://export.arxiv.org/api/query?search_query=cat:cs.AI&max_results=1&sortBy=submittedDate"
[semantic_scholar]="https://api.semanticscholar.org/graph/v1/paper/search?query="
)
#===============================================================================
# DATABASE OPERATIONS
#===============================================================================
db_init() {
sqlite3 "$LEARNER_DB" <<'SQL'
CREATE TABLE IF NOT EXISTS concepts (
id INTEGER PRIMARY KEY,
concept TEXT UNIQUE,
domain TEXT,
category TEXT,
confidence REAL DEFAULT 0.5,
uncertainty REAL DEFAULT 1.0,
access_count INTEGER DEFAULT 0,
info_gain REAL DEFAULT 1.0,
created_at TEXT DEFAULT (datetime('now')),
last_accessed TEXT
);
CREATE TABLE IF NOT EXISTS repetition (
concept_id INTEGER PRIMARY KEY,
repetition INTEGER DEFAULT 0,
easiness REAL DEFAULT 2.5,
interval REAL DEFAULT 1,
next_review TEXT,
last_review TEXT,
last_quality INTEGER DEFAULT 3,
FOREIGN KEY (concept_id) REFERENCES concepts(id)
);
CREATE TABLE IF NOT EXISTS learning_history (
id INTEGER PRIMARY KEY,
concept_id INTEGER,
action TEXT,
reward REAL,
timestamp TEXT DEFAULT (datetime('now')),
FOREIGN KEY (concept_id) REFERENCES concepts(id)
);
CREATE INDEX IF NOT EXISTS idx_concepts_domain ON concepts(domain);
CREATE INDEX IF NOT EXISTS idx_concepts_uncertainty ON concepts(uncertainty DESC);
CREATE INDEX IF NOT EXISTS idx_repetition_next ON repetition(next_review);
SQL
}
db_query() {
sqlite3 -separator '|' "$LEARNER_DB" "$1"
}
#===============================================================================
# INFORMATION GAIN COMPUTATION
#===============================================================================
# Compute information gain for learning a new concept
compute_info_gain() {
local concept="$1"
local domain="$2"
# Check if already known
local existing=$(db_query "SELECT confidence FROM concepts WHERE concept='$concept' LIMIT 1")
if [[ -n "$existing" ]]; then
# Low gain for already-known concepts
echo "0.1"
return
fi
# Get domain statistics
local domain_count=$(db_query "SELECT COUNT(*) FROM concepts WHERE domain='$domain'")
local domain_avg_conf=$(db_query "SELECT COALESCE(AVG(confidence), 0.5) FROM concepts WHERE domain='$domain'")
if [[ -z "$domain_count" ]] || [[ "$domain_count" == "0" ]]; then
# New domain = high information gain
echo "1.0"
return
fi
# Information gain = entropy reduction
# More uncertain domains have higher gain
local uncertainty=$(echo "scale=4; 1 - $domain_avg_conf" | bc)
# Bonus for connecting to existing knowledge
local connectivity_bonus=$(echo "scale=4; l($domain_count + 1) / 10" | bc -l 2>/dev/null || echo "0.1")
local gain=$(echo "scale=4; $uncertainty * (1 + $connectivity_bonus)" | bc)
# Clamp to [0, 1]
if (( $(echo "$gain > 1" | bc -l) )); then
gain="1.0"
fi
echo "$gain"
}
#===============================================================================
# UCB (Upper Confidence Bound) SELECTION
#===============================================================================
# UCB score: exploitation + exploration
compute_ucb_score() {
local concept_id="$1"
local total_selections="$2"
# Get concept statistics
local stats=$(db_query "SELECT confidence, access_count, info_gain FROM concepts WHERE id=$concept_id")
local confidence=$(echo "$stats" | cut -d'|' -f1)
local access_count=$(echo "$stats" | cut -d'|' -f2)
local info_gain=$(echo "$stats" | cut -d'|' -f3)
# Exploitation: confidence * info_gain
local exploitation=$(echo "scale=4; $confidence * $info_gain" | bc)
# Exploration: sqrt(2 * ln(t) / n)
local exploration=1.0
if [[ "$access_count" -gt 0 ]]; then
exploration=$(echo "scale=4; sqrt($UCB_C * l($total_selections + 1) / $access_count)" | bc -l 2>/dev/null || echo "1")
fi
# UCB score
echo "scale=4; $exploitation + $exploration" | bc
}
# Select best concept using UCB
select_concept_ucb() {
local domain="${1:-}"
local limit="${2:-10}"
local where_clause=""
if [[ -n "$domain" ]]; then
where_clause="WHERE domain='$domain'"
fi
# Get total selections
local total=$(db_query "SELECT COUNT(*) FROM learning_history")
# Get candidate concepts
local candidates=$(db_query "SELECT id, concept, uncertainty FROM concepts $where_clause ORDER BY uncertainty DESC LIMIT $limit")
local best_id=""
local best_score="-999"
while IFS='|' read -r id concept uncertainty; do
[[ -z "$id" ]] && continue
local score=$(compute_ucb_score "$id" "$total")
if (( $(echo "$score > $best_score" | bc -l) )); then
best_score="$score"
best_id="$id"
fi
done <<< "$candidates"
echo "$best_id"
}
#===============================================================================
# SPACED REPETITION (SM-2 ALGORITHM)
#===============================================================================
# Get concepts due for review
get_due_for_review() {
local limit="${1:-10}"
db_query "
SELECT c.id, c.concept, c.domain, r.repetition, r.easiness
FROM concepts c
JOIN repetition r ON c.id = r.concept_id
WHERE r.next_review <= datetime('now')
ORDER BY r.next_review ASC
LIMIT $limit
"
}
# Record review result and update schedule
record_review() {
local concept_id="$1"
local quality="$2" # 0-5
# Get current state
local state=$(db_query "SELECT repetition, easiness, interval FROM repetition WHERE concept_id=$concept_id")
local rep=$(echo "$state" | cut -d'|' -f1)
local ef=$(echo "$state" | cut -d'|' -f2)
local interval=$(echo "$state" | cut -d'|' -f3)
# SM-2 algorithm
if [[ "$quality" -lt 3 ]]; then
# Failed: reset
rep=0
interval=1
else
# Success
((rep++))
if [[ "$rep" -eq 1 ]]; then
interval=1
elif [[ "$rep" -eq 2 ]]; then
interval=6
else
interval=$(echo "scale=1; $interval * $ef" | bc)
fi
fi
# Update easiness factor
local ef_delta=$(echo "scale=4; 0.1 - (5 - $quality) * (0.08 + (5 - $quality) * 0.02)" | bc)
ef=$(echo "scale=4; $ef + $ef_delta" | bc)
if (( $(echo "$ef < 1.3" | bc -l) )); then
ef="1.3"
fi
# Calculate next review date
local next_review=$(date -d "+${interval%.*} days" '+%Y-%m-%d %H:%M:%S' 2>/dev/null || \
date -v "+${interval%.*}d" '+%Y-%m-%d %H:%M:%S' 2>/dev/null || \
echo "$(date '+%Y-%m-%d %H:%M:%S')")
# Update database
db_query "
UPDATE repetition SET
repetition = $rep,
easiness = $ef,
interval = $interval,
next_review = '$next_review',
last_review = datetime('now'),
last_quality = $quality
WHERE concept_id = $concept_id
"
# Update concept confidence
if [[ "$quality" -ge 3 ]]; then
local conf_boost=$(echo "scale=4; $ALPHA * $quality / 5" | bc)
db_query "UPDATE concepts SET confidence = MIN(1.0, confidence + $conf_boost * (1 - confidence)) WHERE id = $concept_id"
else
db_query "UPDATE concepts SET confidence = MAX(0.1, confidence * (1 - $GAMMA)) WHERE id = $concept_id"
fi
# Log
db_query "INSERT INTO learning_history (concept_id, action, reward) VALUES ($concept_id, 'review', $quality)"
echo "Updated: rep=$rep, ef=$ef, interval=$interval, next=$next_review"
}
#===============================================================================
# ACTIVE LEARNING STRATEGIES
#===============================================================================
# Strategy: EXPLORE - Random exploration
strategy_explore() {
local category="${1:-science}"
local topics="${CATEGORIES[$category]:-physics}"
# Pick random topic from category
local topic=$(echo "$topics" | tr ' ' '\n' | shuf -n 1)
echo "EXPLORE: Fetching from $topic in $category"
# Fetch from Wikipedia
local result=$(curl -s "${SOURCES[wikipedia]}" 2>/dev/null | jq -r '.title // empty')
if [[ -n "$result" ]]; then
local gain=$(compute_info_gain "$result" "$category")
echo "Learned: $result (gain: $gain)"
# Add to database
db_query "INSERT OR IGNORE INTO concepts (concept, domain, category, info_gain) VALUES ('$result', '$category', '$topic', $gain)"
local concept_id=$(db_query "SELECT id FROM concepts WHERE concept='$result'")
db_query "INSERT OR IGNORE INTO repetition (concept_id, next_review) VALUES ($concept_id, datetime('now', '+1 day'))"
fi
}
# Strategy: UNCERTAINTY - Sample most uncertain concepts
strategy_uncertainty() {
echo "UNCERTAINTY: Selecting most uncertain concept"
local uncertain=$(db_query "SELECT id, concept, uncertainty FROM concepts ORDER BY uncertainty DESC LIMIT 1")
if [[ -n "$uncertain" ]]; then
local id=$(echo "$uncertain" | cut -d'|' -f1)
local concept=$(echo "$uncertain" | cut -d'|' -f2)
echo "Reinforcing: $concept"
# Simulate learning (reduce uncertainty)
db_query "UPDATE concepts SET uncertainty = uncertainty * 0.9, access_count = access_count + 1 WHERE id = $id"
db_query "INSERT INTO learning_history (concept_id, action, reward) VALUES ($id, 'uncertainty', 0.1)"
fi
}
# Strategy: UCB - Upper Confidence Bound selection
strategy_ucb() {
local domain="${1:-}"
echo "UCB: Selecting by exploration-exploitation balance"
local concept_id=$(select_concept_ucb "$domain")
if [[ -n "$concept_id" ]]; then
local concept=$(db_query "SELECT concept FROM concepts WHERE id=$concept_id")
echo "Selected: $concept (id: $concept_id)"
db_query "UPDATE concepts SET access_count = access_count + 1, last_accessed = datetime('now') WHERE id = $concept_id"
db_query "INSERT INTO learning_history (concept_id, action, reward) VALUES ($concept_id, 'ucb', 0.5)"
fi
}
# Strategy: REINFORCE - Review due concepts
strategy_reinforce() {
echo "REINFORCE: Reviewing due concepts"
local due=$(get_due_for_review 5)
if [[ -z "$due" ]]; then
echo "No concepts due for review"
return
fi
while IFS='|' read -r id concept domain rep ef; do
[[ -z "$id" ]] && continue
echo "Reviewing: $concept (repetition: $rep)"
# Simulate review (random quality 3-5 for now)
local quality=$((3 + RANDOM % 3))
record_review "$id" "$quality"
done <<< "$due"
}
#===============================================================================
# MAIN LEARNING LOOP
#===============================================================================
# Initialize if needed
init_learner() {
db_init
# Initialize state file
if [[ ! -f "$LEARNER_STATE" ]]; then
cat > "$LEARNER_STATE" <<EOF
{
"total_ticks": 0,
"total_learned": 0,
"current_strategy": "explore",
"phi": 0.5,
"strategies": ["explore", "uncertainty", "ucb", "reinforce"],
"strategy_weights": [0.3, 0.2, 0.3, 0.2]
}
EOF
fi
}
# Select strategy based on weights
select_strategy() {
local strategies=("explore" "uncertainty" "ucb" "reinforce")
local weights=(30 20 30 20) # Percentage weights
local total=100
local rand=$((RANDOM % total))
local cumulative=0
for i in "${!strategies[@]}"; do
cumulative=$((cumulative + weights[i]))
if [[ $rand -lt $cumulative ]]; then
echo "${strategies[$i]}"
return
fi
done
echo "explore"
}
# Main learning tick
learning_tick() {
local strategy=$(select_strategy)
case "$strategy" in
explore)
local categories=("science" "technology" "philosophy" "mathematics")
local category="${categories[$((RANDOM % ${#categories[@]}))]}"
strategy_explore "$category"
;;
uncertainty)
strategy_uncertainty
;;
ucb)
strategy_ucb
;;
reinforce)
strategy_reinforce
;;
esac
# Update state
local total_concepts=$(db_query "SELECT COUNT(*) FROM concepts")
local avg_confidence=$(db_query "SELECT COALESCE(AVG(confidence), 0.5) FROM concepts")
# Compute phi
local phi=$(echo "scale=4; 1 / (1 + sqrt((($avg_confidence) - 1)^2 + 0.01))" | bc -l 2>/dev/null || echo "0.5")
# Update state file
if command -v jq &>/dev/null; then
local ticks=$(jq -r '.total_ticks' "$LEARNER_STATE")
((ticks++))
jq --arg ticks "$ticks" \
--arg learned "$total_concepts" \
--arg strategy "$strategy" \
--arg phi "$phi" \
'.total_ticks = ($ticks | tonumber) |
.total_learned = ($learned | tonumber) |
.current_strategy = $strategy |
.phi = ($phi | tonumber)' \
"$LEARNER_STATE" > "${LEARNER_STATE}.tmp" && mv "${LEARNER_STATE}.tmp" "$LEARNER_STATE"
fi
echo "Tick complete: strategy=$strategy, concepts=$total_concepts, phi=$phi"
}
# Run learning daemon
run_learner_daemon() {
init_learner
echo "Starting Active Learner Daemon"
echo "Database: $LEARNER_DB"
echo "State: $LEARNER_STATE"
while true; do
learning_tick
sleep 60 # 1 tick per minute
done
}
#===============================================================================
# CLI
#===============================================================================
case "${1:-}" in
init)
init_learner
echo "Initialized active learner"
;;
tick)
init_learner
learning_tick
;;
explore)
init_learner
strategy_explore "${2:-science}"
;;
review)
init_learner
strategy_reinforce
;;
status)
init_learner
echo "=== Active Learner Status ==="
echo "Total concepts: $(db_query 'SELECT COUNT(*) FROM concepts')"
echo "Avg confidence: $(db_query 'SELECT COALESCE(AVG(confidence), 0) FROM concepts')"
echo "Due for review: $(db_query 'SELECT COUNT(*) FROM repetition WHERE next_review <= datetime("now")')"
if [[ -f "$LEARNER_STATE" ]]; then
echo "State: $(cat "$LEARNER_STATE")"
fi
;;
daemon)
run_learner_daemon
;;
*)
echo "Usage: $0 {init|tick|explore|review|status|daemon}"
;;
esac