Guide for debugging inspect_ai evaluation failures, score issues, and model behavior. Use this when eval results are unexpected, scores are wrong, scoring fails, or model output appears corrupted.
Instalar com Codex ou Claude Copie este prompt, cole no Codex, Claude ou outro assistente e deixe que ele revise a página da skill e instale para você.
Um comando direto ignora o prompt de revisão. Verifique a origem antes de executá-lo.
Instruções da origem · Visualização somente leitura
name
inspect-eval-debugging
description
Guide for debugging inspect_ai evaluation failures, score issues, and model behavior. Use this when eval results are unexpected, scores are wrong, scoring fails, or model output appears corrupted.
To debug inspect_ai evaluation issues in SABER, follow this systematic process:
1. Triage: Identify the Failure Category
Check the eval summary output. Common categories:
Symptom
Likely Cause
Section
Score is nan for all scorers
Agent never reached scoring (sandbox error, tool failure)
§2
Score is 0.0 but expected higher
Scorer ran but agent's work was incorrect or scorer has a bug
§3
Score differs across runs of same model
Non-deterministic model behavior
§4
Eval crashes with traceback
Code error in domain scoring/tools or inspect_ai
§5
0/0 trailing brace or garbage in tool args
Model produces malformed tool call arguments
§4
Docker/sandbox errors
Container issues
§6
2. Debug Agent Behavior (Score is nan or Unexpected)
Parse the eval log to see exactly what the agent did:
import json, zipfile
with zipfile.ZipFile("logs/<file>.eval", "r") as zf:
for name in zf.namelist():
ifnot name.startswith("samples/"):
continue
sample = json.loads(zf.read(name))
print(f"Task: {sample.get('id')}")
print(f"Scores: {sample.get('scores', {}).keys()}")
# Walk through the conversation to see agent actionsfor i, msg inenumerate(sample.get("messages", [])):
role = msg.get("role", "?")
if role == "assistant":
# Check for tool calls
tcs = msg.get("tool_calls", [])
if tcs:
for tc in tcs:
fn = tc.get("function", "?")
args = tc.get("arguments", {})
cmd = args.get("cmd", "") ifisinstance(args, dict) elsestr(args)[:100]
print(f" [{i}] TOOL: {fn}({cmd[:80]})")
else:
content = msg.get("content", "")
text = content ifisinstance(content, str) elsestr(content)[:100]
print(f" [{i}] ASSISTANT: {text[:100]}")
elif role == "tool":
text = msg.get("content", "")[:80]
print(f" [{i}] TOOL_RESULT: {text}")
3. Debug Scorer Issues (Score is 0.0 Unexpectedly)
3a. Check the score explanation
import json, zipfile
with zipfile.ZipFile("logs/<file>.eval", "r") as zf:
for name in zf.namelist():
ifnot name.startswith("samples/"):
continue
sample = json.loads(zf.read(name))
for scorer_name, score_data in sample.get("scores", {}).items():
print(f"--- {scorer_name} ---")
print(f" value: {score_data.get('value')}")
print(f" explanation: {score_data.get('explanation', 'none')[:500]}")
metadata = score_data.get("metadata", {})
if metadata:
print(f" metadata: {json.dumps(metadata, indent=2)[:300]}")
3b. Test the scorer in isolation
Write a standalone test or script that calls the scorer directly against a known-good input. For example, to test a patch-verify scorer:
# Enter the sandbox container manually
docker exec -it <container_id> bash
# Verify the patch appliescd /workspace/source
patch -p1 --dry-run < /submit/patches/patch.diff
# Run the build
bash build.sh
# Run POVs manually
./build/fuzz_harness /workspace/povs/pov_0.blob
3c. Add debug logging to scorer code
Domain scoring code lives in domains/<domain>/scoring/. Add logger.info() or logger.warning() calls and run with INSPECT_LOG_LEVEL=info:
Domain loggers use the saber.domains.* namespace and inherit inspect_ai's log handling automatically.
4. Debug Model Behavior (Non-Deterministic Issues)
4a. Scan multiple eval logs for patterns
import json, zipfile, os
log_dir = "logs"for f insorted(os.listdir(log_dir)):
ifnot f.endswith(".eval") or"<domain>"notin f:
continue
issues = []
total = 0with zipfile.ZipFile(f"{log_dir}/{f}", "r") as zf:
for name in zf.namelist():
ifnot name.startswith("samples/"):
continue
sample = json.loads(zf.read(name))
for msg in sample.get("messages", []):
ifnotisinstance(msg, dict):
continuefor tc in msg.get("tool_calls", []):
total += 1
args = tc.get("arguments", {})
ifisinstance(args, dict):
cmd = str(args.get("cmd", ""))
# Check for trailing garbage braceif cmd.rstrip().endswith("}"):
issues.append(cmd[-:])
issues:
()
ex issues[:]:
()
4b. Known model quirks
gpt-5.2 trailing }: Sporadically appends } to bash commands in tool call arguments. This is model-generated garbage — it appears in the raw API response from the OpenAI SDK, before any inspect_ai processing. Frequency varies: 0-26% of tool calls per run.
gpt-5 series uses Responses API: openai.responses.create() instead of client.chat.completions.create(). The code path in inspect_ai is completely different — see §5.
Reasoning tokens: Models like o1/o3 and gpt-5 produce reasoning tokens that don't appear in visible content but consume output token budget.
Unset reasoning_effort silently disables reasoning (check this FIRST for score drops): if the same model/dataset scores differently between runs, compare model_generate_config.reasoning_effort and stats.model_usage.*.reasoning_tokens in each .eval header. Unset ⇒ 0 reasoning tokens ⇒ lower scores. Verified: excytin gpt-5.4latest_test_set scored 0.813 (unset) vs 0.886 (--reasoning-effort high), a +0.073 swing — larger than most scoring/judge changes. Rule this out before blaming data, scoring, or judge-prompt changes.
5. Debug inspect_ai Code Path Issues
5a. Determine which API path a model uses
# Check in the provider code:# external/inspect_ai/src/inspect_ai/model/_providers/openai.py# gpt-5 series: responses_api = True (automatic)# o-series (except o1-early): responses_api = True# codex models: responses_api = True# Everything else: Completions API (default)
openai_responses_chat_choices() in _openai_responses.py
5b. Find which file Python actually imports
Critical: inspect_ai may be installed from a git URL, NOT as an editable install from external/. Editing files in external/inspect_ai/src/ may have NO EFFECT:
# Check which file is loaded
uv run python -c "import inspect_ai.model._providers.openai_responses as m; print(m.__file__)"# If it prints .venv/lib/.../site-packages/..., edit THAT file, not external/
5c. Add debug logging to inspect_ai internals
Since inspect_ai uses a custom LogHandler that can suppress standard logger output, the most reliable debugging approach is to write directly to a file:
# Add this temporarily at the injection point:withopen("/tmp/debug_output.log", "a") as _dbg:
_dbg.write(f"DEBUG: variable={value!r}\n")
_dbg.flush()
Do NOT rely onlogger.warning() or sys.stderr.write() — inspect_ai's Rich console and custom LogHandler can swallow these. File I/O always works.
After editing files in .venv/lib/.../site-packages/, always clear bytecode caches: