Auto: 2 files | 2 files changed, 71 insertions(+), 45 deletions(-)
This commit is contained in:
parent
b1dafa2ca8
commit
1c6aab23bc
2 changed files with 71 additions and 45 deletions
|
|
@ -42,7 +42,8 @@ if [ -f "$LOCKFILE" ]; then
|
||||||
rm -f "$LOCKFILE"
|
rm -f "$LOCKFILE"
|
||||||
fi
|
fi
|
||||||
echo $$ > "$LOCKFILE"
|
echo $$ > "$LOCKFILE"
|
||||||
trap 'rm -f "$LOCKFILE"' EXIT
|
TWEET_FILE="/tmp/research-tweets-${AGENT}.md"
|
||||||
|
trap 'rm -f "$LOCKFILE" "$TWEET_FILE"' EXIT
|
||||||
|
|
||||||
log "=== Starting research session for $AGENT ==="
|
log "=== Starting research session for $AGENT ==="
|
||||||
|
|
||||||
|
|
@ -91,7 +92,14 @@ for acct in data.get('accounts', []):
|
||||||
" 2>/dev/null || true)
|
" 2>/dev/null || true)
|
||||||
|
|
||||||
TWEET_DATA=""
|
TWEET_DATA=""
|
||||||
|
API_CALLS=0
|
||||||
|
API_CACHED=0
|
||||||
for USERNAME in $ACCOUNTS; do
|
for USERNAME in $ACCOUNTS; do
|
||||||
|
# Validate username (Twitter handles are alphanumeric + underscore only)
|
||||||
|
if [[ ! "$USERNAME" =~ ^[a-zA-Z0-9_]+$ ]]; then
|
||||||
|
log "WARN: Invalid username '$USERNAME' in network file, skipping"
|
||||||
|
continue
|
||||||
|
fi
|
||||||
OUTFILE="$RAW_DIR/${USERNAME}.json"
|
OUTFILE="$RAW_DIR/${USERNAME}.json"
|
||||||
# Only pull if file doesn't exist or is older than 12 hours
|
# Only pull if file doesn't exist or is older than 12 hours
|
||||||
if [ ! -f "$OUTFILE" ] || [ $(find "$OUTFILE" -mmin +720 2>/dev/null | wc -l) -gt 0 ]; then
|
if [ ! -f "$OUTFILE" ] || [ $(find "$OUTFILE" -mmin +720 2>/dev/null | wc -l) -gt 0 ]; then
|
||||||
|
|
@ -102,7 +110,10 @@ for acct in data.get('accounts', []):
|
||||||
log "WARN: Failed to pull @${USERNAME}"
|
log "WARN: Failed to pull @${USERNAME}"
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
API_CALLS=$((API_CALLS + 1))
|
||||||
sleep 2 # Rate limit courtesy
|
sleep 2 # Rate limit courtesy
|
||||||
|
else
|
||||||
|
API_CACHED=$((API_CACHED + 1))
|
||||||
fi
|
fi
|
||||||
if [ -f "$OUTFILE" ]; then
|
if [ -f "$OUTFILE" ]; then
|
||||||
TWEET_DATA="${TWEET_DATA}
|
TWEET_DATA="${TWEET_DATA}
|
||||||
|
|
@ -125,6 +136,14 @@ except Exception as e:
|
||||||
" 2>/dev/null || echo "(failed to parse)")"
|
" 2>/dev/null || echo "(failed to parse)")"
|
||||||
fi
|
fi
|
||||||
done
|
done
|
||||||
|
log "API usage: ${API_CALLS} calls, ${API_CACHED} cached for ${AGENT}"
|
||||||
|
# Append to cumulative usage log (create with header if new)
|
||||||
|
USAGE_CSV="/opt/teleo-eval/logs/x-api-usage.csv"
|
||||||
|
if [ ! -f "$USAGE_CSV" ]; then
|
||||||
|
echo "date,agent,api_calls,cached,accounts_total" > "$USAGE_CSV"
|
||||||
|
fi
|
||||||
|
ACCOUNT_COUNT=$(echo "$ACCOUNTS" | wc -w | tr -d ' ')
|
||||||
|
echo "${DATE},${AGENT},${API_CALLS},${API_CACHED},${ACCOUNT_COUNT}" >> "$USAGE_CSV"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# --- Also check for any raw JSON dumps in inbox-raw ---
|
# --- Also check for any raw JSON dumps in inbox-raw ---
|
||||||
|
|
@ -161,7 +180,6 @@ log "On branch $BRANCH"
|
||||||
|
|
||||||
# --- Build the research prompt ---
|
# --- Build the research prompt ---
|
||||||
# Write tweet data to a temp file so Claude can read it
|
# Write tweet data to a temp file so Claude can read it
|
||||||
TWEET_FILE="/tmp/research-tweets-${AGENT}.md"
|
|
||||||
echo "$TWEET_DATA" > "$TWEET_FILE"
|
echo "$TWEET_DATA" > "$TWEET_FILE"
|
||||||
|
|
||||||
RESEARCH_PROMPT="You are ${AGENT}, a Teleo knowledge base agent. Domain: ${DOMAIN}.
|
RESEARCH_PROMPT="You are ${AGENT}, a Teleo knowledge base agent. Domain: ${DOMAIN}.
|
||||||
|
|
@ -295,47 +313,55 @@ if [ -z "$CHANGED_FILES" ]; then
|
||||||
exit 0
|
exit 0
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# --- Stage and commit if Claude didn't already ---
|
# --- Stage and commit ---
|
||||||
if ! git log --oneline -1 | grep -q "research session"; then
|
git add inbox/archive/ agents/${AGENT}/musings/ agents/${AGENT}/research-journal.md 2>/dev/null || true
|
||||||
# Claude didn't commit — do it manually
|
|
||||||
git add inbox/archive/ agents/${AGENT}/musings/ agents/${AGENT}/research-journal.md 2>/dev/null || true
|
|
||||||
|
|
||||||
if git diff --cached --quiet; then
|
if git diff --cached --quiet; then
|
||||||
log "No valid changes to commit"
|
log "No valid changes to commit"
|
||||||
git checkout main >> "$LOG" 2>&1
|
git checkout main >> "$LOG" 2>&1
|
||||||
exit 0
|
exit 0
|
||||||
fi
|
fi
|
||||||
|
|
||||||
AGENT_UPPER=$(echo "$AGENT" | sed 's/./\U&/')
|
AGENT_UPPER=$(echo "$AGENT" | sed 's/./\U&/')
|
||||||
SOURCE_COUNT=$(git diff --cached --name-only | grep -c "^inbox/archive/" || echo "0")
|
SOURCE_COUNT=$(git diff --cached --name-only | grep -c "^inbox/archive/" || echo "0")
|
||||||
git commit -m "${AGENT}: research session ${DATE} — ${SOURCE_COUNT} sources archived
|
git commit -m "${AGENT}: research session ${DATE} — ${SOURCE_COUNT} sources archived
|
||||||
|
|
||||||
Pentagon-Agent: ${AGENT_UPPER} <HEADLESS>" >> "$LOG" 2>&1
|
Pentagon-Agent: ${AGENT_UPPER} <HEADLESS>" >> "$LOG" 2>&1
|
||||||
fi
|
|
||||||
|
|
||||||
# --- Push ---
|
# --- Push ---
|
||||||
git push -u origin "$BRANCH" --force >> "$LOG" 2>&1
|
git push -u origin "$BRANCH" --force >> "$LOG" 2>&1
|
||||||
log "Pushed $BRANCH"
|
log "Pushed $BRANCH"
|
||||||
|
|
||||||
# --- Open PR ---
|
# --- Check for existing PR on this branch ---
|
||||||
PR_JSON=$(python3 -c "
|
EXISTING_PR=$(curl -s "${FORGEJO_URL}/api/v1/repos/teleo/teleo-codex/pulls?state=open" \
|
||||||
import json
|
-H "Authorization: token $AGENT_TOKEN" \
|
||||||
data = {
|
| jq -r ".[] | select(.head.ref == \"$BRANCH\") | .number" 2>/dev/null)
|
||||||
'title': '${AGENT}: research session ${DATE}',
|
|
||||||
'body': '## Self-Directed Research\\n\\nAutomated research session for ${AGENT} (${DOMAIN}).\\n\\nSources archived with status: unprocessed — extract cron will handle claim extraction separately.\\n\\nResearcher and extractor are different Claude instances to prevent motivated reasoning.',
|
|
||||||
'base': 'main',
|
|
||||||
'head': '${BRANCH}'
|
|
||||||
}
|
|
||||||
print(json.dumps(data))
|
|
||||||
")
|
|
||||||
|
|
||||||
PR_RESULT=$(curl -s -X POST "${FORGEJO_URL}/api/v1/repos/teleo/teleo-codex/pulls" \
|
if [ -n "$EXISTING_PR" ]; then
|
||||||
|
log "PR already exists for $BRANCH (#$EXISTING_PR), skipping creation"
|
||||||
|
else
|
||||||
|
# --- Open PR ---
|
||||||
|
PR_JSON=$(jq -n \
|
||||||
|
--arg title "${AGENT}: research session ${DATE}" \
|
||||||
|
--arg body "## Self-Directed Research
|
||||||
|
|
||||||
|
Automated research session for ${AGENT} (${DOMAIN}).
|
||||||
|
|
||||||
|
Sources archived with status: unprocessed — extract cron will handle claim extraction separately.
|
||||||
|
|
||||||
|
Researcher and extractor are different Claude instances to prevent motivated reasoning." \
|
||||||
|
--arg base "main" \
|
||||||
|
--arg head "$BRANCH" \
|
||||||
|
'{title: $title, body: $body, base: $base, head: $head}')
|
||||||
|
|
||||||
|
PR_RESULT=$(curl -s -X POST "${FORGEJO_URL}/api/v1/repos/teleo/teleo-codex/pulls" \
|
||||||
-H "Authorization: token $AGENT_TOKEN" \
|
-H "Authorization: token $AGENT_TOKEN" \
|
||||||
-H "Content-Type: application/json" \
|
-H "Content-Type: application/json" \
|
||||||
-d "$PR_JSON" 2>&1)
|
-d "$PR_JSON" 2>&1)
|
||||||
|
|
||||||
PR_NUMBER=$(echo "$PR_RESULT" | python3 -c "import sys,json; print(json.load(sys.stdin).get('number','unknown'))" 2>/dev/null || echo "unknown")
|
PR_NUMBER=$(echo "$PR_RESULT" | jq -r '.number // "unknown"' 2>/dev/null || echo "unknown")
|
||||||
log "PR #${PR_NUMBER} opened for ${AGENT}'s research session"
|
log "PR #${PR_NUMBER} opened for ${AGENT}'s research session"
|
||||||
|
fi
|
||||||
|
|
||||||
# --- Back to main ---
|
# --- Back to main ---
|
||||||
git checkout main >> "$LOG" 2>&1
|
git checkout main >> "$LOG" 2>&1
|
||||||
|
|
|
||||||
|
|
@ -29,34 +29,34 @@ This mirrors our proposer-evaluator separation for claims, applied one layer ear
|
||||||
│ Pull tweets │ │ Read archives │ │ Review claims │
|
│ Pull tweets │ │ Read archives │ │ Review claims │
|
||||||
│ Pick 1 task │ │ Extract claims │ │ Approve/reject │
|
│ Pick 1 task │ │ Extract claims │ │ Approve/reject │
|
||||||
│ Archive sources │ │ Open PR │ │ Merge │
|
│ Archive sources │ │ Open PR │ │ Merge │
|
||||||
│ Push to main │ │ │ │ │
|
│ Push branch+PR │ │ │ │ │
|
||||||
└─────────────────┘ └──────────────────┘ └─────────────────┘
|
└─────────────────┘ └──────────────────┘ └─────────────────┘
|
||||||
```
|
```
|
||||||
|
|
||||||
### Research Cron: `research-daily.sh`
|
### Research Cron: `research-session.sh`
|
||||||
|
|
||||||
**Schedule:** Once daily, staggered across agents to respect rate limits
|
**Schedule:** Once daily, staggered across agents to respect rate limits
|
||||||
|
|
||||||
```
|
```
|
||||||
# Stagger: each agent gets a 30-min window
|
# Stagger: each agent gets a 90-min window, overnight PST (10pm-7am)
|
||||||
0 2 * * * /opt/teleo-eval/research-daily.sh rio
|
0 22 * * * /opt/teleo-eval/research-session.sh rio
|
||||||
30 2 * * * /opt/teleo-eval/research-daily.sh clay
|
30 23 * * * /opt/teleo-eval/research-session.sh clay
|
||||||
0 3 * * * /opt/teleo-eval/research-daily.sh theseus
|
0 1 * * * /opt/teleo-eval/research-session.sh theseus
|
||||||
30 3 * * * /opt/teleo-eval/research-daily.sh vida
|
30 2 * * * /opt/teleo-eval/research-session.sh vida
|
||||||
0 4 * * * /opt/teleo-eval/research-daily.sh astra
|
0 4 * * * /opt/teleo-eval/research-session.sh astra
|
||||||
30 4 * * * /opt/teleo-eval/research-daily.sh leo
|
30 5 * * * /opt/teleo-eval/research-session.sh leo
|
||||||
```
|
```
|
||||||
|
|
||||||
**Per agent, the research session:**
|
**Per agent, the research session (~90 min):**
|
||||||
|
|
||||||
1. Pull latest tweets from agent's network accounts (X API)
|
1. Pull latest tweets from agent's network accounts (X API)
|
||||||
2. Read the agent's beliefs, recent claims, open positions
|
2. Read the agent's beliefs, recent claims, open positions
|
||||||
3. Claude prompt: "You are {agent}. Here are your latest tweets from {accounts}. Here is your current knowledge state. Pick ONE research direction that advances your domain understanding. Archive the most relevant sources with notes."
|
3. Claude prompt: "You are {agent}. Here are your latest tweets from {accounts}. Here is your current knowledge state. Pick ONE research direction that advances your domain understanding. Archive the most relevant sources with notes."
|
||||||
4. Agent writes source archives to `inbox/archive/` with `status: unprocessed`
|
4. Agent writes source archives to `inbox/archive/` with `status: unprocessed`
|
||||||
5. Commit and push to main (source-only, no claims)
|
5. Commit, push to branch, open PR (source-only, no claims)
|
||||||
6. Extract cron picks them up within 5 minutes
|
6. Extract cron picks them up within 5 minutes
|
||||||
|
|
||||||
**Key constraint:** One Claude session per agent, ~20-30 minutes, Sonnet model. Total daily VPS research compute: ~3 hours of sequential Sonnet sessions.
|
**Key constraint:** One Claude session per agent, ~90 minutes, Sonnet model. Total daily VPS research compute: ~9 hours of sequential Sonnet sessions (staggered overnight).
|
||||||
|
|
||||||
### Research Prompt Structure
|
### Research Prompt Structure
|
||||||
|
|
||||||
|
|
@ -97,7 +97,7 @@ You are {agent}, a Teleo knowledge base agent specializing in {domain}.
|
||||||
### Capacity on Claude Max ($200/month)
|
### Capacity on Claude Max ($200/month)
|
||||||
|
|
||||||
**VPS compute budget (all Sonnet):**
|
**VPS compute budget (all Sonnet):**
|
||||||
- Research cron: 6 agents × 30 min/day = 3 hr/day
|
- Research cron: 6 agents × 90 min/day = 9 hr/day (overnight)
|
||||||
- Extract cron: ~37 sources × 10 min = 6 hr one-time backlog, then ~1 hr/day steady-state
|
- Extract cron: ~37 sources × 10 min = 6 hr one-time backlog, then ~1 hr/day steady-state
|
||||||
- Eval pipeline: ~10 PRs/day × 15 min = 2.5 hr/day
|
- Eval pipeline: ~10 PRs/day × 15 min = 2.5 hr/day
|
||||||
- **Total VPS:** ~6.5 hr/day Sonnet (steady state)
|
- **Total VPS:** ~6.5 hr/day Sonnet (steady state)
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue