Skip to content

Add web-search arms to the tool pilot (web only, and web plus the PolicyEngine tool) - #235

Merged
MaxGhenis merged 6 commits into
mainfrom
tool-pilot-web-arms
Oct 21, 2026
Merged

MaxGhenis merged 6 commits into
mainfrom
tool-pilot-web-arms

Conversation

@MaxGhenis

@MaxGhenis MaxGhenis commented Oct 21, 2026 •

Copy link
Copy Markdown
Contributor

This is an internal pilot, not a board condition. The board's no_tools condition is unchanged.

What

scripts/tool_pilot_argon.py run --arm {pe,web,web+pe}:

  • web gives the provider's own web search in place of calculate_household.
  • web+pe gives both.
  • pe is unchanged. A test pins its notices to the sha256 the published runs recorded.

Rows are labeled <model>+web and <model>+web+pe-tool.

Why

Max asked what web access would cost, and whether web plus the PolicyEngine tool is cheaper than web alone. This PR adds the arms and was used for a 5-household trial. A 100-household run waits on his call.

How each provider is wired

Checked 2026-10-20 against each provider's docs and with live probes.

OpenAI (GPT-6 Sol)

  • Uses the Responses API web_search tool with filters.blocked_domains.
  • Requests include=["web_search_call.action.sources"] so the audit sees each search's result URLs.
  • Billing counts every web_search_call at $10 per 1,000. That is an upper bound: the pricing page doesn't say whether open_page and find_in_page are billed. search_actions counts searches alone.

Anthropic (Opus 5.5)

  • Uses web_search_20260209 (dynamic filtering) with blocked_domains.
  • Billing reads usage.server_tool_use.web_search_requests at $10 per 1,000.
  • Server calls (srvtoolu_*) get no client result.
  • A reply is continued only when a server call has no result in the same response, the paused state Anthropic documents, at most 3 times. A finished search with no answer gets a nudge.
  • No text follows an unfinished server call.
  • When Claude calls a server tool beside a client tool, the next response carries the result without the call. litellm 1.88.1 then drops it from later requests, and the API refuses the third request with a 400 (reproduced live). The runner moves that result onto the message that made the call. Two live probes then ran four requests each, clean.

Gemini (Argon)

  • Uses googleSearch with server-side tool invocations.
  • litellm 1.88.1 drops search whenever function tools are present. The runner patches both drops, for that litellm version only.
  • Billing is $14 per 1,000 non-empty queries, once per invocation id. The 5,000 free a month are ignored.

What the blocked domains actually cover

The list is policybench.org, github.com and githubusercontent.com:

  • OpenAI: filters search results only. In the smoke and the trial, Sol opened two pages on github.com and raw.githubusercontent.com by URL. Both calls completed, and the response doesn't show whether the content was served.
  • Anthropic: filters its searches, but Claude never searched, so this is untested live.
  • Gemini: blocks nothing. The API refuses excludeDomains and returns no result URLs, so its reading can't be audited.
  • *.github.io (PolicyEngine's generated docs) is not blocked.

score writes searches.csv, flags any household whose searches name PolicyBench or reach a blocked domain, and reports scores without those households. For Gemini it marks results as not visible.

Trial (households 000–004, list prices)

Model No tools PE tool Web Web + PE
GPT-6 Sol $0.020 $0.041 $0.145 (5.6 searches) $0.092 (1.6)
Claude Opus 5.5 $0.055 $0.057 $0.091 (0) $0.100 (0)
Gemini 4 Argon $0.027 $0.081 $0.523 (34.4) $0.093 (0)
  • Web plus the tool versus web alone: cheaper for Sol (36% less) and far cheaper for Argon, which never searched once it had the tool. No difference for Opus, which never searched at all.
  • 100-household projection: about $118 at list for both arms on all three models. Argon's roughly 3,400 searches would fit inside Google's free 5,000 a month.
  • Spent: about $7.50 on the v1 trial, smoke and probes.
  • Accuracy: five households can't separate the arms.

What the searches show. 44% of Sol's and 56% of Argon's web-only queries name PolicyEngine. The prompts name PolicyEngine in four output definitions (d1705). In 2 of the 5 households, Argon searched for PolicyBench itself, for example "Inspect household #000" "policybench". That text is on policybench.org and not in the prompt.

Full record: ~/reviews/policybench-tool-pilot-2026-10/web-arms/README.md.

v2-pilot prompts and the no-answer-key rule (Max, 2026-10-21)

--prompt v2-pilot. There are no model-ready v2 prompts yet: #173 is an unactivated fact slice with no output request, and it names PolicyEngine throughout. So this option keeps the board's prompts and rewrites only what Max has ruled on:

  • d1705. The four PolicyEngine-naming definitions (Medicaid, CHIP, free and reduced-price school meals) are rewritten neutrally. Each states the convention the reference follows where the law alone wouldn't settle it, read from policyengine-us 2.41.0:
    • everyone aged 5 to 17 counts as a K-12 student, and there are no meals without one;
    • free meals include universal-free-meals states and state-paid copays;
    • someone eligible for Medicaid is not CHIP-eligible;
    • the Medicare Savings Programs are not Medicaid.
  • d1263. Federal income tax is computed from the section 1 rate schedules, not the Tax Table.

The rewrite also covers the answer and repair tools. A guard refuses any board text the model sees that still names PolicyEngine.

--forbid-benchmark-search. Tells a web arm not to search for PolicyBench or its answers.

Tests.

  • All 100 households are rendered for each contract, and only those definition lines change.
  • The meal wording is checked against every reference.
  • Three more planted bugs are caught.

Trial on v2-pilot with the rule. Households 000–004, commit b8231bf, $4.86, cost per household at list (v1 → v2-pilot):

Model Web Web + PE PE tool
GPT-6 Sol $0.145 → $0.146 $0.092 → $0.140 $0.041 → $0.044
Claude Opus 5.5 $0.091 → $0.094 $0.100 → $0.135 $0.057 → $0.088
Gemini 4 Argon $0.523 → $0.148 $0.093 → $0.107 $0.081 → $0.071
  • No query named PolicyBench, against 6 by Argon on v1, and no household was flagged.
  • No web-only query named PolicyEngine, against 44% for Sol and 56% for Argon on v1.
  • Argon's web-only arm searched 6.4 times a household instead of 34.4.
  • Web plus the tool is no longer cheaper than web alone for Sol. Without the PolicyEngine lead, Sol looks up the law instead.
  • Misses. The remaining misses trace to the known "employer sponsored insurance premiums" label ambiguity, plus one $110 arithmetic error.
  • 100-household projection: about $94 for the six web arms.

Earlier wording, found in the first v2 run: "in any eligibility group" led Sol to count QI as Medicaid. It is fixed.

Invariants (property tests)

  • A household's cost_usd equals token_cost_usd plus searches × list price / 1,000, and the spend counter matches. This holds for every provider and both web arms, including households with repair turns and a truncated-and-retried turn.
  • Gemini bills the non-empty queries of each invocation id's first appearance only.
  • Out of scope but fixed here: paired_bootstrap's better/worse/same now come from one difference, so every household lands in exactly one. Hypothesis found a=0.125, b=a+1e-12 landing in none. The case is pinned as an example, and the published counts are unchanged.

Tests and review

  • 133 passed locally; the 98 existing tests are unchanged.
  • I planted fifteen bugs, one at a time, and the tests caught all fifteen.
  • Review r1 (Opus 5.5) requested changes. Eight of its ten findings are resolved and one is partly resolved: search-only turns now force the answer for tool-contract models only. The Gemini search-count cross-check is deferred, because litellm's own count is empty on Gemini's function-calling turns.
  • Review r2 (Opus 5.5, because the Codex lanes were closed until 18:00Z) AGREES at b8231bf: ~/reviews/policybench-tool-pilot-2026-10/web-arms/review/review_r2_opus.md. Its low-severity findings go to a follow-up PR.

🤖 Generated with Claude Code

…icyEngine tool)

`run --arm web` offers the provider's own web search in place of
calculate_household, and `--arm web+pe` offers both. The PE arm is
unchanged: its notices hash to what the published runs recorded.

- OpenAI: the Responses API's web_search tool. Anthropic: web_search_20260209
  (dynamic filtering). Gemini: googleSearch with server-side tool
  invocations, plus a litellm 1.88.1 patch, since litellm otherwise drops
  search whenever function tools are present.
- policybench.org and GitHub are blocked where the provider allows it.
  Gemini's API takes no domain list, so its queries are audited instead.
- Each response's searches are logged and priced at list on top of their
  tokens. Gemini invocations are billed once per id. Anthropic server calls
  get no client result, and a paused search turn is sent back unchanged.
- `score` writes searches.csv and audits any search that touches the
  benchmark or a blocked domain.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
@vercel

vercel Bot commented Oct 21, 2026 •

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated
policybench-site Ready Ready Preview Oct 21, 2026 11:57am UTC

Request Review

- Pause detection: a JSON reply is continued only when a server call has no
  result in the same response, as Anthropic documents. A finished search
  with no answer gets a nudge. A pause still unfinished after 3 continuations
  ends the household, because text after an unfinished call is a 400.
- Deferred server results: when Claude calls a server tool beside a client
  tool, the next response carries the result without the call. litellm
  1.88.1 drops it from later requests, and a live probe got the 400 on the
  third. The result now moves onto the message that made the call. Two live
  re-probes ran four requests clean.
- No text follows an unfinished server call: the cap notice is withheld,
  and the refusal of any later calculation says the same.
- A continuation goes back without a prompt-cache mark. Marking turns its
  text into a block list, which litellm rebuilds in another order.
- Search-only turns count toward the turn cap in the web arms.
- OpenAI: request each search's source URLs, so the audit sees results as
  well as opened pages. Count search actions apart from billed calls.
- Audit: flag households whose searches name PolicyBench or reach a blocked
  domain, and report scores without them. Mark Gemini's results as not
  visible. score.json no longer writes NaN.
- Bootstrap: better/worse/same now come from one difference, so every
  household lands in exactly one. A pinned example caught the old boundary
  case. Published counts are unchanged.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
A boolean filter built from an empty list selected no columns rather than
no rows, so score failed on Claude's runs, which made no searches. The masks
are now arrays, and a test covers the empty case.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
`run --prompt v2-pilot` sends the board's prompt with its four
PolicyEngine-naming output definitions (Medicaid, CHIP, free and
reduced-price school meals; d1705) rewritten in neutral terms. The same
rewrite applies to the answer and repair tools. Each rewrite states the
convention the reference follows where the law alone wouldn't settle it,
read from policyengine-us 2.41.0:
- everyone aged 5 to 17 is a K-12 student;
- no meal support without one;
- free meals include universal-free-meals states and state-paid copays;
- someone eligible for Medicaid is not CHIP-eligible.

A guard refuses any board text the model sees that still names PolicyEngine.
Tests render all 100 households and check that only the four definitions
change, and that the meal wording matches every reference.

`--forbid-benchmark-search` tells a web arm not to search for PolicyBench or
its published answers, and not to use any PolicyBench page, code or data
(Max, 2026-10-21). Rows are labeled `@v2-pilot` and `+no-benchmark-search`.
This is a pilot stand-in for v2, not the v2 board wording.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
…ot wording

The first neutral wording said 'in any eligibility group', and GPT-6 Sol
counted QI, a limited-benefit Medicare Savings Program, as Medicaid in
scenario_001 (both spouses) in the v2 trial. policyengine-us models the
Medicare Savings Programs apart from is_medicaid_eligible.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
…tion

Max ruled in d1263 (2026-10-13) to keep the section 1 rate schedule and to
state the convention in the v2 prompt. Web access finds the IRS Tax Table: in
the v2 trial GPT-6 Sol answered scenario_000 from it, $1.45 off the
schedule reference.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
@MaxGhenis
MaxGhenis marked this pull request as ready for review October 21, 2026 12:29
@MaxGhenis

Copy link
Copy Markdown
Contributor Author

Merging at b8231bf.

  • CI: app, lint and test all pass on this head (run 39595612642).
  • Review r1 (Opus 5.5): REQUEST_CHANGES. Findings fixed in 7229a9d and 440de6f.
  • Review r2 (Opus 5.5, because the Codex lanes were closed): AGREE at this head. Both reviews are in ~/reviews/policybench-tool-pilot-2026-10/web-arms/review/.
  • Follow-up PR: r2's low-severity findings. These are evidence for Claude's server-tool paths, the meal wording naming breakfast and lunch, the rate-schedule line on refundable credits, search-only turns for JSON-contract models, and fuller audit logging.

This is internal pilot tooling only. No board condition, published number or release changes.

🤖 Generated with Claude Code

@MaxGhenis
MaxGhenis merged commit 069f6a5 into main Oct 21, 2026
5 checks passed
@MaxGhenis
MaxGhenis deleted the tool-pilot-web-arms branch October 21, 2026 13:04

This branch was successfully deployed

1 active deployment
Preview — b8231bf5 Deployed Oct 21, 2026 by vercel[bot]
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant