20 Oct 2026, 13:22 PT We asked our own research brain

How can a RAG system decide when to retrieve and check its own answers?

  1. 1The Power of Noise: Redefining Retrieval for RAG Systems2024
  2. 2Search Engines in an AI Era: The False Promise of Factual and Verifiable…2024
  3. 3REAPER: Reasoning based Retrieval Planning for Complex RAG Systems2024
  4. 4TeaRAG: A Token-Efficient Agentic RAG Framework2026
  5. 5Retrieval-Augmented Generation for Large Language Models: A Survey2023

Related papers, but not the answer. 0 of the 4 papers that started this idea were in our library.

By 13:34 PT the same day, it found all four. We had to help it.

stage/research/rag/demo/before_served.json (research.brain_search, k = 8) · rag/scripts/find.py title search · rag/STATUS.md

One of four talks

Four talks, one question: how should agents search?

A

Search API Battle Royale

Same questions, different sources, measured 20 Oct 2026.

B

System 1 vs System 2 search

Quick search, deep research, and when to route between them.

C

Self improving RAG

A research loop that learns from its own mistakes.

D

Agent native search platforms

What search looks like when the user is an agent.

Claude Code Build Night Bright Data SF · 2026-10-20 · Ray Zahid, Immersive Commons

How a research pipeline learns from its mistakes

Our research brain is a search engine over 6,927 papers, run on 7 machines. One database died. Our checks caught us being wrong.

stage/research/rag/db_stats3.txt + demo/learn_1_seed.txt · rag/farm_nodes.txt · memory project_pg_host_ic2.md

The question of this talk

How does a research pipeline get better on its own?

  1. 1Look in more than one place, and keep going while you still find new things.diversity and saturation
  2. 2Check every quote against its source. Let two separate reviewers judge each claim.verification
  3. 3Keep what you found. Keep a fixed test, so you see when a change makes it worse.the shelf (saved papers) and the eval (the test)

Short answer: it does not. It gets better when a program checks it, and when it keeps what it learns.

Running example Our research brain on 20 Oct 2026

Our research brain: a searchable library, or corpus, of papers

6,927research papers, title and abstract
1,017of them with full text
60,200passages cut from those full texts
40topics
7machines processing papers, in the 8 days to 20 Oct

The tool reports 20,958 "papers". 14,031 are logs of our own Claude Code chats, so we do not count them. This matters later.

rag/db_stats2.txt (pdf_status extracted, session rows) · db_stats3.txt (59,984 + 216) · demo/learn_3_chunk.txt · corpus_coverage.json (39 + 1 topics) · farm_nodes.txt

1

Part 1 · Foundations

From a question to a cited answer

RAG (retrieval augmented generation): the AI looks up sources first, then answers from them.

1.1 The pipeline in plain words

How RAG gets from a question to an answer

Questionyou ask in words Retrieve3 search methods, 20 results each Rankmerge the lists Readthe passages, word for word Answer + citeeach claim names its paper not enough? search again

1.2 Our real pipeline

Our real system, step by step

INGEST · RUNS BEFORE ANY QUESTION Seed40 topics, lists of papers Acquire13 paper sources, free PDFs Enrichvelocity, marks, summaries Embed7 machines, 768 numbers each Indexa database on machine ic2 RETRIEVE AND FUSE · PER QUESTION Querywords + meaning A · passage vector (meaning) B · passage BM25 (keywords) C · paper vector (title + abstract) books: vector, BM25, tree · 0 books on 20 Oct 2026 Merge (RRF)k = 60, top 20 each Boost, one row per papermarks, velocity, summaries · top k SERVE 20 Oct 2026: backup server ic5 answers; main server offline every hit: paper id · title · year · URL · passage

content/foundations.md §2 · life/research/cross_search.py · research.brain.health() base immersivecommons5:8010 at 13:27 PT, 2026-10-20 · rag/tailnet_nodes.txt

1.3 Four terms

Chunk

One passage cut from a paper, short enough to read at a glance.

Retrieval works on chunks; one vector cannot hold a 30 page paper.

1,017 full text papers → 60,200 passages

Embedding

A list of numbers that stands for what a passage means.

A vector made by a model. Closeness is the angle between two vectors (cosine).

768 numbers · our top passage: closeness 0.741 (cosine)

Vector index

A filing system that finds the nearest meanings fast.

It checks only part of the index (HNSW), so it trades a few misses for speed.

default setting: 4 of the true top 20 · tuned: 20 of 20

BM25

Score a passage by how many of your rare words it holds.

Each repeat of a word counts for less (k1 1.2). Long passages are scaled down (b 0.75). Rare words count more.

right passages found in the top 10: our old "keyword" search 0.076 · real BM25 0.457

content/foundations.md §3 · rag/demo_rrf_hybrid.json · life/research/cross_search.py (ef_search comment, docstring) · rag/retrieval_eval_latest.txt

1.4 Two kinds of search, one list

score(d) = Σ w · 1 / (60 + rank)

Hybrid search

Ask a meaning finder and a word finder at once, then merge.

Keyword results (BM25) and meaning results (vectors), merged into one list. BM25 catches exact names and codes. Vectors catch the same idea in other words.

we run three paper retrievers per question

Reciprocal rank fusion

Each list gives points for position. Points add up across lists.

It uses only positions, so different kinds of score never need converting. k = 60. Each of our three searches has weight w = 1/3.

first place in one list: 1/61 = 0.0164

content/foundations.md §3 terms 5 and 6 · life/research/cross_search.py (_rrf_weighted, fuse block)

1.5 Running example · real ranks

"Does combining keyword search with vector search improve retrieval?"

A · passage vectormeaning of the passage

  1. 1RAG for NLP: a survey
  2. 2Open-Source Agentic Hybrid RAG Framework
  3. 3RAG for NLP: a survey (another passage)
  4. 4ViSAR: Adaptive-k Retrieval
  5. 5RAG for NLP: a survey (another passage)

B · passage BM25keyword match

  1. 1Open-Source Agentic Hybrid RAG Framework
  2. 2Improving LLM applications in biomedicine with RAG
  3. 3Deep deterministic policy gradient: a review
  4. 4Structural Feature Engineering for GEO
  5. 5RAG for AI-Generated Content: A Survey

C · paper vectortitle + abstract

  1. 1WARP: Multi-Vector Retrieval
  2. 2PyLate: Late Interaction Models
  3. 3Scaling Retrieval-Augmented Reasoning
  4. 4Jina-ColBERT-v2
  5. 5GaussDB-Vector
fused place · paperlist Alist Bscore = sum × 1/3
#1 RAG for LLMs: A Surveyin neither top 5#7 → 1/67 = 0.01493#10 → 1/70 = 0.014290.00974code: 0.0097
#2 Policy gradient reviewoff topic#20 → 1/80 = 0.01250#3 → 1/63 = 0.015870.00946code: 0.0095
#6 Hybrid RAG Frameworkone passage: first in B; A found anothernot in list#1 → 1/61 = 0.016390.00546code: 0.0055

Good in two lists beats first in one. But lists can agree by accident: an off-topic review came second.

rag/demo_rrf_hybrid.json · demo_rrf_hybrid_run2.txt (RANKTABLE) · rag/scripts/demo_rrf.py · content/foundations.md §1 and §3 term 6

Live Try it: keyword, meaning, merged

KeywordBM25 over your exact words

    Meaningpretend embedding: 6 concepts, not a real model

      FusedRRF: 1/(60 + keyword rank) + 1/(60 + meaning rank)

        a toy in the page: 8 sentences, no network, no model (spec: content/foundations.md §4)

        1.6 Three terms for checking answers

        Grounding

        Every claim points at the exact words it came from.

        A claim is grounded when its quote appears in the source text. A simple text match checks it, no AI judge.

        our workflow will not ship a quote it cannot find

        Recall vs precision

        Did we find the good stuff? Is what we found any good?

        recall@k: how many of the right passages made the top k. precision@k: how many of the top k are right.

        served top 5: 4 on topic, 1 not (our reading)

        Eval

        A fixed exam with known answers, run again after every change.

        Fixed questions, known right answers, one score, and a fingerprint (hash) of the grader.

        128 real questions mined from our sessions

        The rest of this talk measures these three things and acts on the results.

        content/foundations.md §3 terms 9 to 11 · ~/.claude/orchestration/gates/research_citation_integrity.py · rag/brain_search_served.json · life/research/retrieval_eval/README.md

        2

        Part 2 · The loop

        A pipeline that checks itself

        Nine steps. At six of them, a program can say no.

        2.1 The research loop

        the library grows: what it finds stays go again framesplit the question groundwhat do we have already?research_corpus_floor scan ×6six places at onceresearch_channel_coverage saturated?still finding new?research_saturation triagedrop repeats, pick the best deep readclaims + exact quotes experimentrun it, if reading cannot settle itresearch_experiment_reproducible verify ×2two reviewers, each aloneresearch_verdict_adjudicate synthesizereport + findingsresearch_citation_integrity = a program checks this stepthe agent cannot talk its way past it

        ~/.claude/orchestration/workflows/research.yaml (phases, gates_on_fail: kick_back_to: scan) · orchestration/gates/research_*.py

        2.2 Search widely, stop when nothing new turns up

        Search diversity

        Six scouts, six places: our brain, papers, web, code, our notes, the news.

        We count results per place, so one silent place cannot hide behind five busy ones.

        Real case, round 2: the brain search found 0 sources and said why: "connection timeout ... not a statement that the literature is absent"

        Saturation

        Stop when new searches mostly bring back what you already have.

        Stop when under a quarter of results are new (0.25), or after round 3.

        Why: the correction I typed most often was "KEEP RESEARCHING"

        Without a count per place, a broken search and an empty one look the same.

        orchestration/gates/research_saturation.py (docstring) · research_channel_coverage.py · life/projects/layered-agent-permissions/handoffs/scan__scout-brain.json

        2.3 Two checks no model can argue with

        Two separate reviewers

        A skeptic and a balanced reader judge every claim. Neither sees the other. If they disagree, the claim is dropped. A claim with no verdict stops the run.

        research_verdict_adjudicate

        Quote check

        Every quote must appear character for character in the source. Quotes under 12 characters are rejected. Only judged claims enter the report.

        research_citation_integrity

        "These segments are then embedded using all-MiniLM-L6-v2 model, and the resulting vectors are stored and indexed through the FAISS library, enabling efficient similarity-based retrieval."

        found · shipsthe resulting vectors are stored and indexed through the FAISS library
        no match · droppedvectors are kept in a FAISS index for fast similarity search

        One real run: 419 claims, all judged by both reviewers. 416 kept, 3 dropped. The quote check failed once, on a "quote" that was a star counter. It passed after the fix.

        gates/research_verdict_adjudicate.py · research_citation_integrity.py (_MIN_QUOTE = 12) · passage: rag/brain_search_served.json hit 1; the second quote is written to show a paraphrase · weba-infra-2026-10 verdicts/adjudicated.json, retro.md

        2.4 A real run, end to end · weba-infra-2026-10

        One real run on web tools for AI agents. It never ran out of new sources.

        round 1
        371 found
        round 2
        182 found
        round 3
        232 found
        brainpaperswebcodeinternalsignal
        round 3 · share that was new 0.55
        00.25 stop line1

        127 new of 232 found. The 3 round limit stopped it, not the stop rule. We did not move the 0.25 line based on one run.

        wc -l life/projects/weba-infra-2026-10/scan/round*/<channel>.jsonl · handoffs/scan__scout-*.json (round 3 new/found) · retro.md ("Stopped on round 3 (MAX_ROUNDS)", item 9)

        3

        Part 3 · The evidence

        We taught it one question in twelve minutes

        Then our own exams caught us being wrong. Twice.

        3.1 The learning demo · 20 Oct 2026, 13:22 to 13:34 PT

        Where the four founding papers rank for our question

        #1#8#20#50#100#500absent
        Self-RAGarXiv 2310.11511
        not on shelf
        Adaptive-RAGarXiv 2403.14403
        not on shelf
        Corrective RAGarXiv 2401.15884
        not on shelf
        FLAREarXiv 2305.06983
        not on shelf
        what the reader sees: top 8each retriever keeps only its top 20log scale, lower rank is better

        rag/demo/learn_1_seed.txt, after1_exact_ranks.txt (paper rank by cosine) · after2_exact_ranks.txt (best passage rank) · before_after_nosession.json (sub-questions, transcripts excluded) · STATUS.md

        3.2 Same library, two ways to ask · new papers in the top 8

        questionbeforeafter the shelf grew
        naturalhow can a RAG system decide when to retrieve and check its own answers00, top 8 unchanged
        sub-question 1self-reflective retrieval augmented generation that critiques its own output0Self-RAG #1
        sub-question 2adaptive retrieval: choosing whether to retrieve based on question complexity0Adaptive-RAG #2
        sub-question 3evaluating retrieved documents and correcting retrieval when results are wrong0Corrective RAG #7
        sub-question 4retrieving actively during generation when the model is uncertain0FLARE #1, Corrective RAG #5
        all four togetherfounding papers in the top 80 of 44 of 4

        Caveat: Claude wrote these questions after it knew which papers we added. In the real workflow, they are written before any search. So it takes both: the right questions and the right papers on the shelf. Our Claude Code chat logs are left out here.

        rag/demo/before_after_nosession.json · rag/scripts/beforeafter_nosession.py (BEFORE excludes session + self-improving-rag; AFTER excludes session) · cross-check before_after.json

        3.3 Yes or no: is this passage relevant? · exp001 → exp004 → exp005

        We wrote that a small model matches Claude. A harder test said no.

        share of irrelevant passages correctly rejected1.00
        llama3.2 3Ba small model, on our own machines
        0.95
        Claudesame prompt
        1.00
        the exam

        exp001, 80 passages. The wrong ones were about wine chemistry.

        exp001 README, line 1 as of 20 Oct 2026: "Superseded in part ... Do not quote the neg-reject or accuracy figures below as operational numbers."

        life/research/experiments/exp001_fleet_triage/results/SCORES.md · exp004_nearmiss/README.md + results/llama3.2-3b-fewshot.jsonl vs golden.jsonl · exp005_rule_alignment/README.md

        3.4 exp002 · copy out claims with their quotes from 40 real passages

        A small model invents quotes. A simple text match caught it.

        0%Claude0 of 324 claims made up · 322 word for word, 2 near
        19%llama3.2 3B8 of 43 claims made up · 32 word for word, 3 near
        a made-up quotehyperparameter values that we use in each environment is provided in Table 6

        A small model (3B) can judge text it is shown. It cannot reliably copy it. So small models get yes or no jobs. Claude gets anything that copies a source.

        life/research/experiments/exp002_claim_extraction/README.md (2026-08-16, n = 40, identical prompt hash ab1cde3f8cfe, near = at least 90% overlap)

        3.5 The exam that found a broken retriever

        Our "keyword" branch was not keyword search.

        It returned nothing for 98 of 128 questions. Real keyword search (BM25) replaced it on 2026-09-12. We have not re-run this test since the rebuild on 2026-10-12.

        life/data/retrieval_eval/runs.jsonl row 2026-09-11T22:50Z · rag/retrieval_eval_latest.txt · life/research/cross_search.py docstring · life/research/retrieval_eval/README.md

        3.6 The library saved its own test

        30 trivia questions, top 5 results each. Amber = a log of our own test.

        each column is one question · rows are ranks 1 to 5grey = a research paper
        30/30

        questions whose top result was a log of our own test

        141/150

        results that were our own logs

        1/30

        answers found once our logs are left out

        top hit for "where does a roadrunner live in the desert"Answer the question using only these web search results. Question: where does a roadrunner live in the desert …

        stage/research/battle/results/brain.jsonl (session_leak per result) · battle/summary.json → brain · rag/session_leak_db.txt (260 eval rows ingested 08:35 PT, 2026-10-20)

        3.7 The shelf · research papers in the live database

        The library grew for five months. Then one day it held zero papers.

        0 2,000 4,000 6,000 8,000 Apr May Jun Jul Aug Sep Oct our paper lists, added up (shape only, not exact history) 398 farm launch4,7246,181frozen eval view 0~09-30: old DB host dies 6,3126,92710-12 rebuiltfrom seed files FULL TEXT PASSAGES 211,726Sep 11 ~60,200Oct 20

        rag/growth_chart.json · life/data/research/papers/*.jsonl _meta.fetched_at · rag/db_stats3.txt · memory project_pg_host_ic2.md · life/research/retrieval_eval/README.md · points are measured, lines join them

        3.8 The machines that turn papers into numbers · 20 Oct 2026, 13:27 PT

        ic1offline · old DB host, dead since ~09-30
        ic2database + Ollama · 3,365 / 12,138
        ic3offline
        ic4offline
        ic51,403 / 51,947 · serving search 20 Oct
        ic6offline
        ic7online · no embedding
        ic8offline
        ic9online · no embedding
        ic10offline
        ic113,572 / 4,595
        ic12offline
        ic132,398 / 8,644
        ic14offline
        ic15offline
        ic163,643 / 3,970
        ic17offline
        ic183,625 / 2,362
        ic19offline
        ic202,951 / 13,664
        sc2offline · the main search server
        • Each machine takes a paper or passage nobody else has claimed (FOR UPDATE SKIP LOCKED), turns it into 768 numbers, and saves them.
        • Papers are cut into ~1,800 character passages that overlap by 200. The PDF downloader has been off since the rebuild.
        • Broken since the rebuild: the free PDF finder and the table of citation links.
        7 of 20

        known machines did any work in the 8 days to 20 Oct 2026. We call it a 22 machine farm.

        database host, also embeds papers / passages processed online, no embedding seen offline

        rag/tailnet_nodes.txt (tailscale status, 2026-10-20 13:27 PT) · rag/farm_nodes.txt (embedded_by, last 8 days) · life/research/embed/ (worker.py, chunk_papers.py, oa_finder.py, edges.py) · rag/db_stats.json

        4.1 Honest limits · checked 20 Oct 2026

        What is still wrong with it

        Most of the shelf cannot be quoted: 1,017 of 6,927 papers have full text.

        rag/db_stats2.txt + demo

        An abstract alone is not enough: Self-RAG ranked 43rd for its own topic until we asked in its words.

        slide 3.1 · after1_exact_ranks.txt

        Merging rewards lists that agree by accident: an off-topic review came second.

        content/foundations.md §1

        The stop rule has never ended a run. The 3 round limit did.

        weba-infra-2026-10 retro.md

        The library takes in our own chat logs, test logs included, so it can answer from its own test.

        battle/results/brain.jsonl · 30 of 30 top hits

        Only about a third of the machines work, and a backup server is answering searches.

        farm_nodes.txt · tailnet_nodes.txt

        Two intake tools fail on the rebuilt database. Leaving out a topic crashes for every topic except news.

        demo/learn_2a_oa.txt · rag/db_stats.json · cross_search.py _scope_key

        Not re-checked on 20 Oct: the research tool's rate limit, and whether test logs are kept out of the library.

        unverified

        popularity and summary scores now lift results after the merge.

        cross_search.py _enrich_paper_rows

        the title and abstract search now covers every topic.

        cross_search.py, paper-meta branch comment

        4.2 Build it tonight · a self improving research loop

        research_loop.py · each scout: a Claude Code subagent or a Bright Data MCP callCHANNELS = ["web", "papers", "code"]          # different blind spotssubqs = frame(question)                        # ask in the papers' wordsshelf = load_jsonl("shelf.jsonl"); seen = {c["url"] for c in shelf}for rnd in range(1, 4):                        # hard cap: 3 rounds    found = [s for q in subqs for ch in CHANNELS for s in scout(ch, q)]    new = [s for s in found if s["url"] not in seen]    seen |= {s["url"] for s in found}    if found and len(new) / len(found) < 0.25: break   # saturatedclaims = [c for s in shortlist(seen) for c in extract_claims(fetch(s))]claims = [c for c in claims if norm(c["quote"]) in norm(fetch(c["url"]))]keep = [c for c in claims if skeptic(c) in OK and balanced(c) in OK]append_jsonl("shelf.jsonl", keep)               # the corpus growsappend_jsonl("evals.jsonl", {"q": question, "rounds": rnd, "kept": len(keep)})

        Close Self improving RAG

        It did not get smarter. It got checked, and it kept what it learned.

        short cut01 / 01
        Overview · arrows + Enter or click · Esc to close