Add a research tool that answers from several sources at once - #32
Draft
devbadya wants to merge 2 commits into
Draft
Add a research tool that answers from several sources at once#32devbadya wants to merge 2 commits into
devbadya wants to merge 2 commits into
Conversation
Answering a question from the web took the model three of its four tool rounds: search, choose a result, read it, and read another for a second opinion. In practice it stopped at one source, and each link in the chain was a decision a 0.8B model could get wrong. `research` is that chain folded into one call, the way `weather` folds three forecast services. It searches, reorders the results so the first hit on each host comes first, fetches up to five in parallel, and returns two quoted passages from each with the URL it came from. Most of what it reads is deliberately thrown away. Function-calling accuracy falls as tool responses grow, and five pages at `read_page`'s cap would be 40,000 characters, so passages are selected by scoring paragraphs against the question with each term weighted by how rare it is across everything the call fetched. Those weights have to be pooled: measured per page, "who is the chief executive of the airline" ranked the paragraph saying "the airline" level with the one naming the executive, because within four paragraphs `the` is as rare as `executive`. A page that will not open falls back to its search snippet and the header says how many did, since the reader's per-minute budget is shared and a 429 on the fourth source is ordinary. A call that can read none of them throws rather than reporting an empty result, which the model would relay as "there is nothing on this subject". The answer check gains `single-source`, which fires when three or more sites were returned and the reply rests on one. Without it the extra sources are five reader requests spent on nothing. It stays shy: three sites are required, because two is what search-then-read produces, and it counts hosts so two pages of one newspaper are one source. Co-authored-by: Sebastian <devbadya@users.noreply.github.com>
Run against the live web, the tool returned five real sources per question and largely quoted the wrong parts of them. Each failure below is now a filter whose test fixture is the string that was actually returned. Two of five Nvidia sources contributed nothing but consent notices: a cookie banner is several sentences long and names the site it sits on, which is all the scoring had to go on. Wikipedia's reference list outranked the sentence that answered the question, because citations repeat the subject once per entry. A Sucuri block page counted as a source, since the reader answers 200 and nothing upstream could see a failure — it now falls back to the search snippet for the same URL, which turned out to be the best passage in the German run. A breadcrumb was quoted as a source on what a chancellor is, and needs no word list to catch: prose ends in a full stop and menus do not, in either language. And condense returned the FAQ heading "Who leads NVIDIA?" whole, a heading being the densest window a paragraph contains, so a window now has to clear the passage floor to win on score alone. The word lists have a cost and it is stated rather than hidden: a paragraph genuinely about cookies is dropped with the banners. The block page test pairs its wording against a length, because an article about Cloudflare is long and a page refusing to serve one is not. Two more came from removing markup without leaving a separator, which is the trap `unbold` already documents for snippets. `our@NVIDIATwitter account,NVIDIA Facebookpage` was three adjacent links whose brackets were deleted rather than replaced, and `[^)]*` stopped at the first bracket of `Betreuung_(Recht)`, stranding the reader's quoted title mid-sentence. Bare URLs and footnote anchors go too: `reviewAnswer` reads every URL in a tool result as a citable source, so a `#cite_note` anchor in a passage becomes a source that states nothing. Co-authored-by: Sebastian <devbadya@users.noreply.github.com>
This was referenced Aug 26, 2026
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.
Answering a web question took the model three of its four tool rounds — search, choose a result, read it, read another for a second opinion — and in practice it stopped at one source.
researchfolds that chain into a single call, the wayweatherfolds three forecast services.What it does
One question in. It searches, reorders the results so the first hit on each host comes first, fetches up to five sources in parallel, and returns two quoted passages from each with the URL it came from. Run against the live web:
Why most of what it reads is thrown away
More of the web reaching the model makes answers worse, not better: function-calling accuracy falls by 7% to 91% as tool responses grow, and five pages at
read_page's cap would be 40,000 characters. So pages are read in full and quoted in part, and the whole result is capped at 4,000 characters — five sources for half the context one whole page costs.Which parts get quoted is decided lexically, like skill retrieval: paragraphs are scored by the question's words, each weighted by how rare it is across everything the call fetched. That makes a stop list unnecessary, since a word in every paragraph earns almost nothing.
The weighting has to be pooled across sources, and the tests pin why. Measured per page, who is the chief executive of the airline ranked the paragraph containing "the airline" exactly level with the one naming the chief executive — within four paragraphs
theis as rare asexecutive. Across the hundred-odd paragraphs five pages produce,theis worth about two per cent ofexecutive. The fixtures are therefore pages rather than snippets, because the mechanism only works at the scale it runs at.What the live web changed
The first run against real pages returned five genuine sources per question and quoted largely furniture from them. Each failure is now a filter whose test fixture is the string that actually came back — see
research_quality_before_after.logfor the verbatim before and after.These cookies may store a unique ID…— two of five sources↑ Retrieved December 24, 2024Sucuri WebSite Firewall - Access DeniedSie befinden sich hier … | StartseiteWho leads NVIDIA?The block page now falls back to the search snippet for the same URL, which turned out to be the best passage in the German run. The breadcrumb needs no word list: prose ends in a full stop and menus do not, in either language. The two word lists have a stated cost — a paragraph genuinely about cookies is dropped with the banners.
Two more failures came from removing markup without leaving a separator, which is the trap
unboldalready documents for snippets:our@NVIDIATwitter account,NVIDIA Facebookpagewas three adjacent links whose brackets were deleted rather than replaced, and[^)]*stopped at the first bracket ofBetreuung_(Recht). Bare URLs and footnote anchors go too —reviewAnswerreads every URL in a tool result as a citable source, so a#cite_noteanchor in a passage becomes a source that states nothing.Three things about what a source is worth
The answer check gained a fourth check
single-sourcefires when three or more sites were returned and the reply rests on one — without it, the extra sources are reader requests spent on nothing. It stays as shy as the other three: three sites are required, because two is what an ordinary search-then-read turn produces and citing the page you read is correct; it counts hosts, so two pages of one newspaper are one source; and grounding is settled first, so an answer citing nothing is asked for a source before it is asked for a second one.What this costs
Six of the twenty requests a minute the keyless reader allows — one search plus five pages — so three research questions in a minute is the honest ceiling. That is why this stops at five sources rather than fifteen. LangSearch takes the search off that budget on a free key.
researchandweb_searchboth ship, and the difference is cost: one request against six. Which one a turn sees is decided by the skill that routes it —research-questionoffers onlyresearch,lookup-termstill searches — rather than by the model weighing it up.Verification
pnpm check(937 tests) andpnpm buildpass. 42 tests cover the tool: host diversity, markdown cleaning, each boilerplate filter against its observed string, pooled passage selection, digest assembly and the whole call over a stubbed network. The review check has 9 more.The tool was also run against the live web in both English and German — that output is the artifacts in this PR, and is what the filters above were written from. Two eval scenarios were added: one measuring whether the answer actually cites two sites, one that the question survives into the call.
The model itself is not exercised here: there is no GPU in this environment and no CPU fallback, so routing and tool-use rates still need
/?evalin Chrome on a real GPU.