test(agent-eval): measure residual context occupancy, over multi-turn sessions (CG-7)
The A/B arms reported cost, tokens, time and tool counts for one headless question. They could not report what issue #1500 actually measured: how much of the context window a tool's responses still occupy once the question is answered, which every later turn is then charged for. parse-run.mjs now measures that. Tokens are measured, not estimated: for each assistant request, input + cache_read + cache_creation is the exact token count of its whole prompt, so consecutive requests differ by exactly what was appended between them. That delta is priced against the characters in the gap, calibrated on gaps that are >=80% tool result. Explore output lands near 2.3 chars/token, so the usual bytes/4 estimate would have under-counted it by ~40%. Content also leaves the window, so residual is tracked apart from contributed: a compact_boundary clears the resident set, and a mid-run context drop is micro-compaction, which sheds the oldest tool results first and is applied FIFO. run-all.sh takes "Q1||Q2||Q3" and runs them as one resumed session, one segment file per turn; parse-run.mjs stitches the segments back together. bench-readme.sh now runs each README repo as a three-turn session (CG_TURNS=1 restores the single-question form). parse-bench-readme.mjs reports the arms' retrieval residual side by side -- codegraph's responses against the without-arm's Read/Grep/Bash -- in absolute tokens, share of context, and share of window, and says so explicitly when the rows it aggregated were single-turn. Two transcript traps are handled and documented at the call site: Claude Code emits one assistant event per content block, all carrying the same usage (summing per event double-counts every turn with both thinking and a tool_use), and the streamed output_tokens is a partial snapshot. Occupancy lives in parse-run.mjs and is imported by the aggregator rather than extracted to a module -- a new scripts/agent-eval/*.mjs scores into the self-query fixture's own corpus and moves its numbers.
This commit is contained in:
@@ -1,28 +1,47 @@
|
||||
#!/usr/bin/env bash
|
||||
# Re-run the README "Benchmark Results" A/B (with vs without codegraph) on the
|
||||
# current build: the 7 README repos, same queries, RUNS per arm (default 4).
|
||||
# Output → /tmp/ab-readme/<repo>/run<n>/run-headless-{with,without}.jsonl
|
||||
# Output → /tmp/ab-readme/<repo>/run<n>/run-headless-{with,without}[.tN].jsonl
|
||||
# Aggregate with parse-bench-readme.mjs. Repos must be cloned + indexed under
|
||||
# $CORPUS (default /tmp/codegraph-corpus) by the build under test.
|
||||
#
|
||||
# Each row is a THREE-TURN session: the README question, then two follow-ups
|
||||
# that stay inside the same flow. Turns 2-3 are where residual context occupancy
|
||||
# is actually charged — the first answer's tool output is still in the window,
|
||||
# so the arms diverge on how much headroom each left behind. CG_TURNS=1 runs the
|
||||
# README question alone (the original single-question A/B).
|
||||
set -uo pipefail
|
||||
H="$(cd "$(dirname "$0")" && pwd)"
|
||||
C="${CORPUS:-/tmp/codegraph-corpus}"
|
||||
RUNS="${RUNS:-4}"
|
||||
TURNS="${CG_TURNS:-3}"
|
||||
ROWS=(
|
||||
"vscode|How does the extension host communicate with the main process?"
|
||||
"excalidraw|How does Excalidraw render and update canvas elements?"
|
||||
"django|How does Django's ORM build and execute a query from a QuerySet?"
|
||||
"tokio|How does tokio schedule and run async tasks on its runtime?"
|
||||
"okhttp|How does OkHttp process a request through its interceptor chain?"
|
||||
"gin|How does gin route requests through its middleware chain?"
|
||||
"alamofire|How does Alamofire build, send, and validate a request?"
|
||||
"vscode|How does the extension host communicate with the main process?|Where in that path would a message be dropped if the extension host crashes?|What would I need to change to add a new message type to that protocol?"
|
||||
"excalidraw|How does Excalidraw render and update canvas elements?|Which part of that path decides whether a full re-render happens or an incremental one?|If I added a new element type, what in that render path would need to change?"
|
||||
"django|How does Django's ORM build and execute a query from a QuerySet?|Where in that path is the SQL actually compiled into a string?|What would I change to add a new lookup type to that pipeline?"
|
||||
"tokio|How does tokio schedule and run async tasks on its runtime?|Where does a task move between the local and the global queue in that path?|What in that path would I touch to add a per-task instrumentation hook?"
|
||||
"okhttp|How does OkHttp process a request through its interceptor chain?|Where in that chain is the connection actually acquired?|What would I change to add a new interceptor stage before the cache?"
|
||||
"gin|How does gin route requests through its middleware chain?|Where is the 404 / no-route case handled in that same chain?|What would I change to add a per-route middleware that runs before the global ones?"
|
||||
"alamofire|How does Alamofire build, send, and validate a request?|Where does retry / interceptor logic hook into that path?|What would I change to add a new validation step to it?"
|
||||
)
|
||||
echo "### README A/B START $(date) RUNS=$RUNS"
|
||||
echo "### README A/B START $(date) RUNS=$RUNS TURNS=$TURNS"
|
||||
for row in "${ROWS[@]}"; do
|
||||
repo="${row%%|*}"; q="${row#*|}"
|
||||
echo "===== $repo ====="
|
||||
repo="${row%%|*}"; rest="${row#*|}"
|
||||
# Take the first $TURNS questions and join them with "||" for run-all.sh.
|
||||
q=""; n=0
|
||||
while [ "$n" -lt "$TURNS" ] && [ -n "$rest" ]; do
|
||||
part="${rest%%|*}"
|
||||
if [ "$rest" = "$part" ]; then rest=""; else rest="${rest#*|}"; fi
|
||||
[ -n "$q" ] && q="$q||"
|
||||
q="$q$part"; n=$((n + 1))
|
||||
done
|
||||
echo "===== $repo ($n turns) ====="
|
||||
for run in $(seq 1 "$RUNS"); do
|
||||
AGENT_EVAL_OUT="/tmp/ab-readme/$repo/run$run" bash "$H/run-all.sh" "$C/$repo" "$q" headless 2>&1 | grep -E "exit [0-9]" || echo " run$run: (no exit line)"
|
||||
out="/tmp/ab-readme/$repo/run$run"
|
||||
mkdir -p "$out"
|
||||
AGENT_EVAL_OUT="$out" bash "$H/run-all.sh" "$C/$repo" "$q" headless > "$out/console.log" 2>&1
|
||||
grep -E "^exit [0-9]" "$out/console.log" | sed 's/^/ /' || echo " run$run: (no exit line)"
|
||||
grep -E "codegraph +[0-9,]+ tok|→ file-access" "$out/console.log" | sed 's/^/ /' || true
|
||||
done
|
||||
done
|
||||
echo "### README A/B DONE $(date)"
|
||||
|
||||
Reference in New Issue
Block a user