Repository navigation
agentic: don't attach files that are already in the sandbox - #1
Open
Divide-By-0 wants to merge 9 commits into
Open
Divide-By-0 wants to merge 9 commits into
Divide-By-0 wants to merge 9 commits into
Conversation
copy_files_to_sandbox() writes every question file into the container, so attaching the same bytes to the prompt duplicates them. Two consequences: 1. Cost. For the seqqa2 tags each attachment is the 1.53 MB M. genitalium GenBank -- roughly 600k input tokens per question. Reading the same file off disk inside the sandbox starts from a 687-token prompt instead. 2. mode=file + solver=agentic was broken outright on Vertex. Inspect's Google provider materializes attachments via client.files.upload(), which is Gemini Developer API only, so the run died with "This method is only supported in the Gemini Developer client" before any sample executed. Verified fixed: that configuration now completes against google/gemini-3.7-flash with GOOGLE_GENAI_USE_VERTEXAI=true. Only attachments are dropped. The prompt text is untouched -- it still names the files, and the agent's system prompt already says they are in the working directory. tests/: 240 passed, 2 skipped (slow), unchanged. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Three changes, all aimed at the same measured problem: 125 of 544 tool calls across our agentic runs (23%) returned an error, and the wasted calls pushed two samples into the token limit before they could answer. 1. python_session replaces the stock python() tool. Inspect's python() runs each snippet in a fresh interpreter, so 94 of those 125 errors were NameError -- `name 'SeqIO' is not defined`, or a variable defined two calls earlier. State now carries in a dill pickle of the user namespace. dill rather than pickle because Biopython records, compiled regexes and locally-defined functions all appear here and stdlib pickle refuses them; unpicklable objects are dropped with a note instead of failing the call. The system prompt said "imports and variables do not persist between calls", which is now false, so it says the opposite. 2. pydoc output for pydna, Bio.Restriction and primer3 is baked into /opt/docs at build time. Agents were reading pydna's own source out of site-packages and web-searching for its API; it is a small library with a non-obvious surface (assembly2 vs deprecated assembly, circular_assembly_fragments). 3. Web search is scoped in the prompt to single factual lookups. One run spent 18 of its last 22 calls on DuckDuckGo queries that returned nothing and hit the token limit without answering, while the same task succeeded in 31 calls when it simply computed. Measured on a cloning sample after the change: 45 tool calls, 1 error (2%), 0 NameError, 4 reads of /opt/docs, no limit fired. tests/: 240 passed, 2 skipped -- unchanged. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
The previous commit's message claimed the suite was unchanged. It was not --
two assertions pin the exact tool set and both failed, because the stock
python() tool was deliberately replaced by python_session(). Corrected here:
the tool set is now {python_session, bash} (+ web_search when a provider key
is set).
tests/: 240 passed, 2 skipped.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Traces for the runs that motivated this branch, as Inspect .eval logs viewable with `inspect view`. JSON dumps are omitted -- ~10x the size and derivable via `inspect log dump`. Headline from them: gibson_primers had 0 passes in 119 graded published attempts and went 6/6 in the sandbox; primer_design went 6/7. Those subtypes were not hard, they were blocked by the environment. compose.yaml now sets network_mode: "none". The container had outbound network, and the LAB-Bench 2 dataset's `ideal` answers are public on HuggingFace, so egress plus pip was a direct path to the answer key. Measured usage was low -- 6 of 691 tool calls, all in one sample -- so this is cheap insurance rather than a fix for a widespread problem. tools/patch_broken_cloning_tasks.py repairs three tasks that could not be solved for environment reasons: a multi-record FASTA the loader rejects, a reference missing the cassette its own prompt requires, and a filename containing a space that the protocol DSL cannot express. The first alone turned a task nothing had ever passed into a pass. Patched tasks are flagged as not comparable to published numbers. The npas4 patch adds a DSL-expressible copy rather than renaming, because file_downloader.fetch() re-downloads anything missing and an unlinked original silently reappears. Addgene plasmid and sequence ids are preserved -- only the browser "(1)" suffix is dropped. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
The previous commit claimed to add these and did not. .gitignore line 26 is a bare `logs/`, which matches `experiments/logs/` at any depth, and `git add` skips ignored paths without erroring -- so only the README and scripts landed while every .eval was silently dropped. Moved to experiments/traces/ rather than editing their .gitignore, and verified with `git check-ignore` and `git ls-files` this time instead of trusting the add. Adds cloning_6tasks_rerun_6M.eval, flagged: it used --token-limit 6000000, above the 2M ceiling the rest of this work uses. Five of its six samples exceeded 2M, including both of its passes, so those results do not hold at 2M and are excluded from headline numbers. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
--token-limit counts ModelUsage.total_tokens, which includes cache reads. On agentic runs those dominate (14.6M cache vs 5.0M novel on one cloning batch), so a total-token cap measures transcript length rather than work. It mis-fired repeatedly: one sample was killed at 268k novel for having accumulated 775k of cache reads, while another finished having produced 430k novel. model_cost.py registers per-model pricing from the environment. Inspect's cost formula is input+output+cache_write+cache_read, each at its own $/million rate, so pricing cache at 0 and input/output at 1.0 makes cost == novel_tokens/1e6 -- i.e. `--cost-limit 0.1` is exactly a 100,000-novel-token budget. Supplying real rates instead turns the same flag into a dollar ceiling with cache priced correctly. This matters because Inspect ships no cost data for newer models: gpt-5.6-sol reports total_cost=None, so an unregistered model never accumulates cost and --cost-limit silently never fires, which is worse than no limit. tools/check_run_guards.py asserts three things about a finished log: no sample over the declared budget, no duplicate sample ids within the log or against prior logs, and every sample actually called the sandbox tools (a zero-tool sample is a bare answer wearing an agentic label). tests/: 240 passed, 2 skipped -- unchanged. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
-T strip_method_hint=true removes every phrase naming the assembly method,
wherever it appears -- trailing ("carry out this cloning using Gibson assembly")
or mid-prompt ("Design a cloning strategy using Gibson Assembly to swap..."),
producing a method-blind variant where the model must infer Gibson vs Golden
Gate from the biology rather than being told.
`for` is matched deliberately, which collaterally rewrites 21e4def0's backbone
name ("BsaI-MCS cloning vector for Golden Gate" -> "BsaI-MCS cloning vector")
and bc918101's barcode requirement. Both accepted: the files are still findable
by name on disk, and leaving those in would defeat the experiment. Verified all
8 gibson/golden-gate tasks retain no method word afterwards.
This cannot make a task *fully* method-blind -- "BsaI-MCS" and "BsmBI" are
themselves Type IIS tells -- so results read as "weaker hint", not "no hint".
experiments/novel_token_costs.yaml prices input+output at $1/million and cache
at $0, so Inspect's cost accounting equals novel tokens / 1e6 and --cost-limit
0.1 is a 100,000-novel-token budget. Needed because --token-limit counts cache
reads, which dominate agentic runs and make the cap measure transcript length.
The grader is priced as anthropic/claude-sonnet-4-5 rather than
google/gemini-3.7-flash: set_model_cost() only accepts models already in
Inspect's model database and gemini-3.7-flash is not in it, while --cost-limit
refuses to run unless every model has cost data. Harmless -- cloning uses a
deterministic scorer and never invokes a grader.
tests/: 240 passed, 2 skipped -- unchanged.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
The ceiling check compared total_tokens against the budget unconditionally. When a run is bounded by --cost-limit with cache priced at 0, the budget is in NOVEL tokens, and cache reads run 3-5x novel on these runs -- so every sample looked like a violation. The smoke run reported FAIL on both samples at 140k/200k total when their novel spend was 29k/49k, comfortably inside the 100k budget. Adds --metric novel|total, defaulting to novel. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Both runs used --cost-limit 0.1 against novel_token_costs.yaml, i.e. a 100,000 NOVEL-token budget with cache priced at zero. Every sample ran to completion -- no truncations -- giving the first clean verdicts on cloning tasks Gemini could never finish within any budget we tried. gpt-5.6-sol spent 22k-76k novel per sample (mean 46k) against gemini-3.7-flash's 347k-661k on the same tasks: roughly 10x cheaper, and 0 of 8 truncated versus 6 of 11. Stripping the method hint changed nothing. All 7 failures still chose the correct assembly method from the biology alone -- gibson( for the gibson tasks, goldengate( for the golden-gate ones -- so the hint carried no signal and the failures are downstream in junction and overhang design. The tasks remain only method-weaker, not method-blind: BsaI-MCS and BsmBI are Type IIS tells. The one pass, a4bf037c, used restriction_assemble on a task whose reference is a Gibson assembly and still matched at >=95%; the scorer compares the assembled product rather than the route. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
4 of 6 tasks
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Sandbox fixes for the
agenticsolver, plus the traces that motivated them. Targeted at our fork, not upstream.Changes
solver=agenticclient.files.upload()is Developer-API only)python_sessionreplaces stockpython()NameErrorfrom state loss between calls/opt/docsnetwork_mode: "none"idealanswers are public on HuggingFacetools/patch_broken_cloning_tasks.pyMeasured effect of the session change on one task: errored calls 23% → 2%, NameError 94 → 0.
Reading the traces
seqqa2_6tasks_2epochs.evalseqqa2_primer_design_rpoC.evalcloning_11tasks.evalcloning_2tasks_2epochs.evalcloning_sorcs2_after_session_fix.evalfb8fc27dpost-fixescloning_6tasks_rerun_6M.evalResults
seqqa2 design tasks — solved
gibson_primershad 0 passes in 119 graded published attempts across every model and mode, and went 6/6 here on the three longest genes.primer_designwent 6/7. Those subtypes were never hard — 77% and 71% of published attempts never reached the validator at all.cloning at 2M — the authorised configuration
61e4b666(patched),5cf2e092,4fb341350a4f4de7(digest mismatch),21e4def0(accuracy),fb8fc27d(accuracy)dff28bd4,3a6704ab,31d22b22,a4bf037c,bc918101fb8fc27d(gibson Sorcs2) is the cleanest hard task in the benchmark: 0 passes in 23 graded published attempts and 0 across all 13 of our samples, always running to completion. No environment excuse.Patched tasks — not comparable to published numbers
61e4b666ENST00000612748.131d22b22a4bf037c(1)suffix, keep the Addgene idsRecord 0 is provably the template for
61e4b666: it sits in the reference at offset 2868 with exactly one mismatch, position 295 C→A — the C295A mutation the prompt asks for. That patch alone turned a task nothing had ever solved into a pass.gpt-5.6-sol runs (added later)
gpt56sol_smoke_2tasks_hinted.evalgpt56sol_8tasks_method_blind.evalBudgeted with
--cost-limit 0.1againstexperiments/novel_token_costs.yaml— cache priced at $0 so the limit is 100,000 novel tokens. All 8 ran to completion; zero truncations.~10× cheaper, and it finishes tasks Gemini never could at any budget we tried.
Removing the method hint changed nothing
All 7 failures still chose the correct method from the biology alone —
gibson(for the four gibson tasks,goldengate(for the three golden-gate ones. The hint carried no signal; failures are downstream in junction and overhang design, not strategy.BsaI-MCSandBsmBIremain in the golden-gate prompts and are unmistakable Type IIS tells.The single pass,
a4bf037c, usedrestriction_assembleon a task whose reference is a Gibson assembly and still matched at ≥95% — the scorer compares the assembled product, not the route.Duplicate guard fired correctly
tools/check_run_guards.pyflagged0a4f4de7and3a6704abas present in both runs — two results each (hinted and method-blind), which must not be pooled. Both failed in both conditions, so no conclusion changes.fb8fc27dremains unsolved by anything: 0 across 23 published attempts, 13 Gemini samples, and gpt-5.6-sol at max effort.