@tangle-network/agent-knowledge 1.9.0 → 1.11.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (42) hide show
  1. package/README.md +256 -74
  2. package/dist/benchmarks/index.d.ts +5 -0
  3. package/dist/benchmarks/index.js +52 -0
  4. package/dist/chunk-DQ3PDMDP.js +115 -0
  5. package/dist/chunk-DQ3PDMDP.js.map +1 -0
  6. package/dist/chunk-ULXZI235.js +1855 -0
  7. package/dist/chunk-ULXZI235.js.map +1 -0
  8. package/dist/{chunk-U6KQ4FS3.js → chunk-VN2OGUUP.js} +306 -40
  9. package/dist/chunk-VN2OGUUP.js.map +1 -0
  10. package/dist/{chunk-WWY5FTKQ.js → chunk-W6VWYTNH.js} +10 -115
  11. package/dist/chunk-W6VWYTNH.js.map +1 -0
  12. package/dist/chunk-XVU5FFQA.js +49 -0
  13. package/dist/chunk-XVU5FFQA.js.map +1 -0
  14. package/dist/cli.js +4 -2
  15. package/dist/cli.js.map +1 -1
  16. package/dist/index-Cj5jRXOz.d.ts +426 -0
  17. package/dist/index.d.ts +2225 -1058
  18. package/dist/index.js +4463 -1275
  19. package/dist/index.js.map +1 -1
  20. package/dist/memory/index.d.ts +5 -70
  21. package/dist/memory/index.js +20 -4
  22. package/dist/{types-BEDGlXB-.d.ts → types-C17sAROL.d.ts} +1 -1
  23. package/dist/types-CjqPcTTK.d.ts +319 -0
  24. package/dist/viz/index.d.ts +1 -1
  25. package/docs/architecture.md +7 -5
  26. package/docs/eval/investment-material-facts.md +252 -0
  27. package/docs/eval/rag-eval-roadmap.md +119 -0
  28. package/docs/results/adaptive.md +120 -0
  29. package/docs/results/claim-grounding.md +97 -0
  30. package/docs/results/cost-quality.md +27 -0
  31. package/docs/results/investment-thesis.md +306 -0
  32. package/docs/results/research-driving.md +265 -0
  33. package/docs/two-agent-research-ab.md +539 -0
  34. package/package.json +8 -9
  35. package/dist/chunk-MU5CEOGE.js +0 -387
  36. package/dist/chunk-MU5CEOGE.js.map +0 -1
  37. package/dist/chunk-U6KQ4FS3.js.map +0 -1
  38. package/dist/chunk-WWY5FTKQ.js.map +0 -1
  39. package/dist/profiles/index.d.ts +0 -194
  40. package/dist/profiles/index.js +0 -11
  41. package/docs/recursive-research-leaf.md +0 -86
  42. /package/dist/{profiles → benchmarks}/index.js.map +0 -0
@@ -0,0 +1,539 @@
1
+ # A verifier agent mostly deduplicates: a controlled A/B on two-agent web research, and what its cost buys
2
+
3
+ *Tangle Network · `agent-knowledge`*
4
+
5
+ ## Abstract
6
+
7
+ We test whether adding a second "verifier" agent to a web-research loop produces a
8
+ cleaner knowledge base than a single agent doing the same work, with compute held
9
+ fixed. A *worker* agent searches the web and proposes sources for the knowledge
10
+ base's open gaps; a *driver* agent vets each proposed source before it commits,
11
+ fills gaps the worker missed, and decides when the base is complete. Over 9
12
+ machine-learning topics at equal compute, the two-agent loop admitted **2.3–2.7
13
+ fewer sources per topic at identical coverage** — 95% bootstrap intervals
14
+ [1.78, 2.89] and [2.22, 3.00] across two independent runs, both above zero. The
15
+ effect is real and reproduces. But the mechanism is not the one we set out to
16
+ test: reading the rejection logs, most of the gain is **de-duplication** — the
17
+ same paper fetched from arXiv, OpenReview, and the NeurIPS proceedings — not the
18
+ relevance filtering we expected. Pricing the verifier's calls (we added per-arm
19
+ router-usage instrumentation) shows the cleanliness costs roughly **5× the
20
+ dollars, 9× the tokens, and 3× the latency** of the single agent — and that the
21
+ original "equal passes" framing hid this, because it charged the verify step as
22
+ one pass while it is actually one LLM call per proposed source. Since the win is
23
+ de-dup-dominated, a deterministic content hash recovers most of the cleanliness at
24
+ ~none of the premium. We then asked the sharper question — is there an error band
25
+ where an LLM verifier *does* earn its dollar? — and found two, on opposite sides of
26
+ the ledger. **Misattributed citations** (an on-topic, unique, real source whose
27
+ cited claim never appears in the page) are caught by a $0 deterministic
28
+ text-presence check that the LLM relevance judge misses 1 in 5 times, because the
29
+ judge structurally never sees the claim. And we built the deployable shape the
30
+ cost result implied — an **adaptive driver** that runs free dedup, then free
31
+ heuristic triage, and escalates to the LLM only on the ambiguous tail: it cuts
32
+ LLM verifier calls 76% and dollars 74%, recovering the de-dup half of the
33
+ verifier's cleanliness while honestly giving up the relevance-judgment half on a
34
+ source pool dominated by authoritative hosts. The verifier earns its dollar on
35
+ misattribution, not on de-duplication; the right production loop spends it only
36
+ where the cheap signals can't decide. Finally we ask the harder question this whole
37
+ metric can't reach — a filter agent can only make the base *carry less*, never
38
+ *answer more* — by building the opposite agent (a **driving** driver that chases
39
+ depth and corroboration instead of pruning) and the opposite metric (a firewalled
40
+ exam of 20 held-out **deep questions**, $0-graded). It is an honest null: driving
41
+ does **not** reliably beat plain collection at answering hard questions, and costs
42
+ **12–16×** more; the verdict flips with the compute budget, the signature of web
43
+ variance rather than a real topology effect, and it still ties a blind worker even
44
+ when forced to run its full multi-round mechanism (§9).
45
+
46
+ ## 1. Setup
47
+
48
+ A research agent building a knowledge base accumulates sources. A single agent both
49
+ finds sources and, implicitly, decides which to keep — it grades its own work. The
50
+ hypothesis is the usual one for verification: separating the producer (find
51
+ sources) from the checker (keep the good ones) should yield a cleaner result, for
52
+ the same reason a second pair of eyes catches typos the author misses.
53
+
54
+ The trap in any "more agents help" claim is compute. Two agents that simply do more
55
+ work will of course produce more — that is a bigger budget, not a finding. So the
56
+ comparison must hold total compute fixed and ask whether the *topology* — splitting
57
+ find from check — beats spending the same compute on a single agent that just finds
58
+ more. And once topology shows an effect, the second question is what it costs: a
59
+ cleaner base bought at 5× the inference is a different product decision than one
60
+ bought for free.
61
+
62
+ ## 2. Method
63
+
64
+ ### 2.1 The loop
65
+
66
+ The loop has two roles (`src/two-agent-research-loop.ts`, `runTwoAgentResearchLoop`):
67
+
68
+ - **Worker** — primary research. Each round it reads the open gaps and proposes
69
+ sources to close them (`ResearchWorker: (ctx: { gaps, steer }) => proposals`).
70
+ - **Driver** — does three things (`ResearchDriver`): `verifySource` vets each
71
+ proposed source before it commits (dedup against the base, then reject sources
72
+ that aren't relevant); `research` runs the driver's own gap-fill pass over gaps
73
+ the worker missed; `foldGaps` turns the still-open gaps into a `steer` string for
74
+ the worker's next round.
75
+
76
+ A round is therefore: worker proposes → driver verifies each proposal (rejections
77
+ never reach the base) → driver gap-fills → the readiness gate checks the base →
78
+ remaining gaps are folded into the next worker prompt. The loop stops when the gate
79
+ reports no blocking gaps left.
80
+
81
+ Note what the driver→worker hand-off is and isn't: the driver *steers* the worker
82
+ by handing it the remaining readiness gaps (`foldGaps`), which is a deterministic
83
+ formatting of unmet requirements — not an LLM authoring a fresh instruction. The
84
+ driver's LLM work is in `verifySource` (one call per proposed source) and its own
85
+ `research` pass. This matters for §4.2: the verify step is N calls, not one, and
86
+ the cost framing turns on that.
87
+
88
+ The readiness gate is `scoreKnowledgeReadiness` (from `agent-eval`). It scores
89
+ *pages* (curated `knowledge/*.md`), not raw sources, and only `importance:
90
+ 'blocking'` requirements gate. Coverage below is the fraction of a topic's blocking
91
+ requirements met.
92
+
93
+ ### 2.2 What the agents are — an honest note
94
+
95
+ The agents in the live run are **not** `AgentProfile`s on a coding harness. The
96
+ worker is a hand-wired pipeline (`src/web-research-worker.ts`,
97
+ `createWebResearchWorker`): glm-5.2 turns the gaps into search queries → a real web
98
+ search over the Tangle router (`POST /v1/search`) → each hit is fetched with the
99
+ repo's `politeFetch` and reduced to text with `htmlToText` → citing pages are
100
+ proposed. It talks to the router directly through `createTangleRouterClient` — no
101
+ claude-code / opencode / sandbox harness, and no dynamic harness selection. The
102
+ driver (`createVerifyingResearchDriver`) is one glm-5.2 chat call per source.
103
+
104
+ The package keeps the live research mechanics runner-agnostic.
105
+ Agent profiles and sandbox-backed runners now live in `agent-runtime`; this package owns the source, write, validation, and eval layer.
106
+
107
+ ### 2.3 Equal compute
108
+
109
+ Compute is counted in agent passes. A two-agent round = 1 worker pass + 1
110
+ verify pass = 2 passes; a single-agent iteration = 1 pass. Both arms gate on the
111
+ *same* readiness criterion and stop as soon as it is met, so neither is starved.
112
+ We budget-match by passes, not rounds — the single-agent loop gets more rounds to
113
+ spend the compute the two-agent loop spends on verification. The harness asserts,
114
+ per topic, that the two-agent loop spent no more passes than the single-agent loop
115
+ and that both stayed under the ceiling; if that ever fails the comparison has
116
+ drifted to unequal compute and the result is void.
117
+
118
+ The pass-accounting has a known soft spot, which §4.2 exposes: a "verify pass" is
119
+ not one LLM call, it is one `verifySource` call per *proposed* source that round.
120
+ Charging it as a single pass keeps the topology comparison fair on agent passes but
121
+ understates the verifier's dollar cost. We added explicit per-arm cost
122
+ instrumentation to measure that directly.
123
+
124
+ ### 2.4 Cost instrumentation
125
+
126
+ The router client now records usage per call (`RouterClient.usage()`,
127
+ `src/web-research-worker.ts`): cumulative chat-completion count, prompt/completion
128
+ tokens, glm-5.2 priced cost, and wall latency. Each A/B arm reads the accumulator
129
+ before and after its run and diffs, so every reported dollar and token figure is a
130
+ measured per-arm delta, not an estimate.
131
+
132
+ ### 2.5 Topics and readiness
133
+
134
+ 9 topics, each with two blocking requirements (the defining mechanism, and reported
135
+ results / trade-offs). Seven are "narrow-scope-inside-a-broad-space" (e.g.
136
+ *self-speculative decoding* inside *speculative decoding*), where we expected the
137
+ broad space to leak in; two are clean controls (*the transformer architecture*,
138
+ *gradient descent*).
139
+
140
+ ## 3. Result 1 — cleanliness: the verifier admits fewer sources at equal coverage
141
+
142
+ The cleanliness signal is the **admitted-source count**: on live data there is no
143
+ oracle, so "fewer sources admitted at equal coverage" is the measurable proxy for
144
+ "cleaner." Δ = single-agent admitted − two-agent admitted, per topic.
145
+
146
+ | Topic | band | Run 1 Δ | Run 2 Δ |
147
+ |---|---|---|---|
148
+ | self-speculative decoding | narrow | 3 | 3 |
149
+ | grouped-query attention | narrow | 3 | 3 |
150
+ | rotary position embeddings | narrow | 2 | 3 |
151
+ | KV-cache quantization | narrow | 1 | 1 |
152
+ | LoRA | narrow | 1 | 3 |
153
+ | ring attention | narrow | 2 | 3 |
154
+ | constitutional AI | narrow | 3 | 3 |
155
+ | the transformer architecture | clean | 3 | 3 |
156
+ | gradient descent | clean | 3 | 2 |
157
+ | **mean Δ** | | **2.33** | **2.67** |
158
+ | **95% CI** (paired bootstrap) | | **[1.78, 2.89]** | **[2.22, 3.00]** |
159
+
160
+ Coverage was **1.00 on every topic, both arms, both runs** — the verifier never
161
+ cost completeness. Both bootstrap intervals (`pairedBootstrap`, from `agent-eval`)
162
+ are above zero. The effect reproduces; its exact magnitude varies run-to-run with
163
+ what the web returns (one topic swung Δ = 0→1→3 across separate runs during
164
+ development).
165
+
166
+ A third, cost-instrumented run (the one priced in §4.2) was noisier: mean Δ **+1.56,
167
+ 95% CI [0.33, 2.67]**, with two topics where the two-agent loop admitted *more* than
168
+ the single agent (KV-cache quantization −1, gradient descent −1). The interval still
169
+ clears zero, but it is the lower-bound run — a reminder that the magnitude is
170
+ web-variance-bound, while the sign is stable.
171
+
172
+ ## 4. Result 2 — what the verifier does, and what it costs
173
+
174
+ ### 4.1 Mostly de-duplication
175
+
176
+ We classified each rejection by the verifier's own stated reason:
177
+
178
+ | rejection reason | narrow (7) | clean (2) |
179
+ |---|---|---|
180
+ | near-duplicate (same paper, different host) | 6 | 4 |
181
+ | off-scope (broad space leaked in) | 3 | 2 |
182
+ | junk page (aggregator / marketing / explainer) | 3 | 0 |
183
+
184
+ The dominant mechanism is **de-duplication** — canonical papers mirrored across
185
+ arXiv, OpenReview, and the NeurIPS proceedings — and it fires regardless of band.
186
+ The off-scope rejection we set out to measure is real (on *self-speculative
187
+ decoding* the verifier correctly dropped three general *speculative decoding* papers
188
+ that use a *separate* draft model) but it is the minority, and it does not
189
+ concentrate on the narrow topics as hypothesized: narrow mean Δ = 2.14 vs clean
190
+ 3.00. The strong form of our hypothesis — narrow-in-broad pays more — is **refuted
191
+ in magnitude, confirmed only in mechanism**.
192
+
193
+ The practical reading: most of the win is "you fetched the same PDF three times,"
194
+ which a content hash catches for free. The LLM's distinctive contribution is the
195
+ page that *looks* on-topic but isn't — the self-speculative-vs-separate-draft
196
+ distinction a string match would miss.
197
+
198
+ ### 4.2 The inference premium (and what "equal passes" hid)
199
+
200
+ `docs/results/cost-quality.md`. The original A/B reported only admitted-sources at
201
+ "equal passes," which charged the verify step as one pass while it is actually N
202
+ `verifySource` LLM calls. Pricing the calls per arm (B ≤ 4 passes/arm, glm-5.2):
203
+
204
+ | per topic (mean) | two-agent | single-agent | ratio |
205
+ |---|---|---|---|
206
+ | LLM chat calls | 5.4 | 1.0 | ~5.4× |
207
+ | tokens (in+out) | ~4,900 | ~530 | ~9× |
208
+ | cost (USD) | ~$0.0072 | ~$0.0013 | ~5.5× |
209
+ | latency (wall) | ~37 s | ~11 s | ~3.4× |
210
+ | cleanliness Δ (single − two admitted) | — | — | +1.56, 95% CI [0.33, 2.67] |
211
+
212
+ The verifier buys ~1.5–2.7 fewer junk sources for roughly **5× the dollars, 9× the
213
+ tokens, and 3× the latency**. Since the cleanliness gain is de-dup-dominated
214
+ (§4.1), the honest production move is a deterministic content-hash / canonical-URL
215
+ dedup, which captures most of the cleanliness at ~none of this premium, reserving
216
+ an LLM check only for the off-scope tail. This is the cost half the "equal passes"
217
+ framing left out — and the rest of the paper is what we built once we saw it.
218
+
219
+ ## 5. Result 3 — the two bands where an LLM verifier does, and doesn't, earn its dollar
220
+
221
+ If de-dup is free and dominates the win, when is the LLM verifier worth its 5×? We
222
+ found two bands, and they cut in opposite directions.
223
+
224
+ ### 5.1 Misattributed citations — the cheap check beats the expensive judge
225
+
226
+ `docs/results/claim-grounding.md`. A source can be on-topic, unique, and real, yet
227
+ the cited *claim* never appears in the page — the LLM wrote a plausible sentence and
228
+ hung a real URL off it. De-dup passes it (unique). A relevance judge passes it (the
229
+ page is on-topic). Only checking the claim against the fetched text catches it — and
230
+ that check is **deterministic text presence, $0 inference**.
231
+
232
+ Each proposed source now carries the claim it is cited for (`withCitedClaim` →
233
+ `metadata.citedClaim`). The claim-grounding verifier (`createClaimGroundingVerifier`,
234
+ `src/claim-grounding.ts`) runs `groundClaimInText(claim, pageText)` over the
235
+ `htmlToText` output of the page the worker actually fetched — verbatim, normalized
236
+ (punctuation/whitespace-insensitive), or a ≥70% content-word overlap close
237
+ paraphrase. A claim that isn't present is rejected as misattributed. The oracle is
238
+ text presence, not a model call, so it composes with the LLM relevance verifier or
239
+ runs alone at zero cost.
240
+
241
+ Live A/B (glm-5.2, real web fetch, one planted misattribution per topic — a real
242
+ fetched page plus a deliberately-wrong claim — over three verifier arms on the same
243
+ proposals):
244
+
245
+ | n=5 topics | misattributions caught | marginal $ | per-$ caught |
246
+ |---|---|---|---|
247
+ | no-verifier | 0 / 5 | $0.0000 | — |
248
+ | relevance (LLM judge) | 4 / 5 | $0.0157 | 254 |
249
+ | claim-grounding (text) | **5 / 5** | **$0.0000** | ∞ |
250
+
251
+ The relevance judge catches one only by accident — when the fabricated claim also
252
+ makes the page read off-topic (a "12-billion-parameter draft transformer" claim on a
253
+ rotary-embeddings page). When the fabrication stays on-topic (the KV-cache case), the
254
+ judge waves it through, because the relevance verifier only ever sees the page text,
255
+ never the cited claim — it is **structurally blind** to misattribution. On this band
256
+ the verifier-per-dollar comparison inverts §4.2: the cheap, deterministic check
257
+ catches strictly more (5/5 vs 4/5) at strictly less ($0 vs $0.0157). The offline
258
+ floor confirms the wiring: on a controlled 4-source pool (2 grounded, 2
259
+ misattributed), claim-grounding admits **0/2** misattributions and keeps **2/2**
260
+ grounded, while relevance and no-verifier both admit **2/2**.
261
+
262
+ ### 5.2 Adaptive topology — pay the LLM only on the ambiguous tail
263
+
264
+ `docs/results/adaptive.md`. The deployable shape §4.2 implied: do the free
265
+ deterministic work first, reserve the LLM for what the cheap signals can't decide.
266
+ `createAdaptiveResearchDriver` (`src/adaptive-driver.ts`) is that driver. Per
267
+ candidate source it runs three stages, cheapest first, stopping at the first that
268
+ decides:
269
+
270
+ 1. **Dedup ($0).** Reject a source whose canonical URL (scheme / `www` / trailing
271
+ slash / tracking params stripped) or normalized-text content hash matches one
272
+ already accepted this round or in the KB.
273
+ 2. **Heuristic triage ($0).** Classify a unique survivor with host/title/length
274
+ signals only: an authoritative host (arxiv, `*.edu`, `*.gov`, official docs,
275
+ github, …) with a substantial body is **kept**; an obvious spam/listicle title
276
+ or a too-thin body is **dropped**; everything else is **ambiguous**.
277
+ 3. **LLM escalation ($).** Only ambiguous survivors reach the shipped LLM relevance
278
+ verifier — one call each.
279
+
280
+ Live frontier, n=5 topics, glm-5.2, same fetched proposals gated through all three
281
+ drivers (plus one planted tracking-decorated mirror of the first source, so the
282
+ dedup stage has a real duplicate to catch). Total spend $0.033:
283
+
284
+ | topic | fetched | single admit | full-LLM admit / calls / $ | adaptive admit / LLM calls / $ |
285
+ |---|---|---|---|---|
286
+ | self-speculative decoding | 3 | 3 | 1 / 3 / $0.0027 | 2 / **0** / **$0.0000** |
287
+ | rotary position embeddings | 3 | 3 | 1 / 3 / $0.0031 | 2 / **0** / **$0.0000** |
288
+ | grouped-query attention | 7 | 7 | 3 / 7 / $0.0072 | 6 / 3 / $0.0030 |
289
+ | KV-cache quantization | 5 | 5 | 3 / 5 / $0.0052 | 4 / **0** / **$0.0000** |
290
+ | LoRA fine-tuning | 7 | 7 | 4 / 7 / $0.0079 | 6 / 3 / $0.0037 |
291
+ | **total** | **25** | **25** | **12 / 25 / $0.0261** | **20 / 6 / $0.0068** |
292
+
293
+ Adaptive cuts LLM verifier calls **76%** (25 → 6) and dollars **74%** ($0.0261 →
294
+ $0.0068). On 3 of the 5 topics it spent **zero** LLM calls — every unique survivor
295
+ was on an authoritative host, so the $0 stages decided everything.
296
+
297
+ It is a frontier point, not a free lunch. Admitted counts (lower = cleaner): single
298
+ 25, **adaptive 20**, full-LLM 12. Adaptive removes the 5 real duplicates the $0 dedup
299
+ catches — exactly the de-dup-dominated win — but keeps the 8 sources full-LLM rejects
300
+ on relevance, because on this authoritative-host-heavy set the heuristic resolved
301
+ every non-duplicate survivor without ever asking the LLM, and the host prior is
302
+ coarser than the relevance judge. So adaptive **recovers the deterministic de-dup
303
+ half of full-LLM's cleanliness for ~26% of its dollars, and gives up the
304
+ relevance-judgment half**. The escalation count is the diagnostic: on the 3 topics
305
+ where it was zero, adaptive *is* a pure host/title/length rule and the LLM
306
+ contributes nothing by construction; on the 2 topics with unknown-host survivors
307
+ (grouped-query attention, LoRA) it escalated 3 calls each — the off-scope tail the
308
+ verifier is actually for.
309
+
310
+ ## 6. Discussion
311
+
312
+ The three results compose into one rule. The LLM verifier's headline cleanliness win
313
+ is real (§3) but **de-dup-dominated** (§4.1) and **expensive** (§4.2, ~5×/9×/3×), so
314
+ spending an LLM call on every source is the wrong default — a free content hash buys
315
+ most of it. The verifier earns its 5× exactly where the cheap signals are blind: on
316
+ **misattribution** (§5.1), where a $0 text-presence check beats the LLM judge
317
+ outright because the judge never sees the claim; and on the **off-scope tail** (§5.2),
318
+ where a page looks on-topic, is unique, and isn't fabricated, so only a relevance
319
+ judgment can settle it. The deployable loop therefore stratifies by cost: free dedup,
320
+ free claim-grounding, free heuristic triage, then an LLM call only on what survives —
321
+ which is what the adaptive driver ships.
322
+
323
+ Two cross-cutting lessons. First, **the accounting unit decides the verdict**:
324
+ charging the verify step as one pass made the topology look near-free; pricing it per
325
+ LLM call (§4.2) is what surfaced the 5× and motivated everything after it. Second,
326
+ **the same verifier inverts in value across bands** — on de-dup the LLM is expensive
327
+ for what a hash does; on misattribution a deterministic check is free for what the LLM
328
+ can't do; on the off-scope tail the LLM is the only thing that works. "Add a verifier"
329
+ is not a setting; it is a cost-stratified decision per error type.
330
+
331
+ ## 7. Limitations
332
+
333
+ - **The verifier is also the judge.** Admitted-count is a proxy; we have no
334
+ independent oracle for whether a dropped source was genuinely redundant. The
335
+ verifier's stated reasons hold up on inspection, but this is the load-bearing
336
+ caveat for §3–§4.
337
+ - **Deltas are conservative.** The single-agent loop stops on the same readiness
338
+ gate, capping its admits; with more iterations it would admit even more junk, so
339
+ the true gap is at least this large.
340
+ - **Small n.** n = 2 clean controls is too thin to compare bands; the misattribution
341
+ and adaptive frontiers are n = 5 each. The directions are asserted in the tests on
342
+ every run; the magnitudes are small-n and web-variance-bound (the §3 third run swung
343
+ to +1.56 from +2.3/+2.7).
344
+ - **Planted error bands.** The misattributions (§5.1) and the adaptive duplicate
345
+ (§5.2) are injected so the band is measurable. They model the real LLM
346
+ citation-fabrication and mirror-host failures but do not measure their base rate in
347
+ the wild — that needs a hand-checked corpus of model-written citations.
348
+ - **Adaptive's quality is host-prior-bound.** On an authoritative-host-heavy source
349
+ pool the heuristic resolves everything and the LLM's relevance judgment contributes
350
+ nothing; a richer worker (good sources on unknown hosts, junk on on-topic-looking
351
+ pages) would grow the ambiguous tail and converge adaptive toward full-LLM cost.
352
+ - **glm-5.2-specific.** A weaker or stronger judge would shift rejection rates and the
353
+ relevance miss-rate. The grounding oracle is also conservative: a real paraphrase
354
+ whose inflected words differ ("drafts" vs "draft") can fall below the 0.7 overlap and
355
+ be flagged misattributed; `minOverlap` tunes this.
356
+ - **High web variance.** One live run per topic per result; numbers move with what
357
+ search returns.
358
+
359
+ ## 8. A simpler loop — built, not deferred
360
+
361
+ The original write-up named two simplifications as future work. Both are now built and
362
+ measured; this is what changed.
363
+
364
+ 1. **Deterministic dedup before the LLM, LLM only on the tail — shipped.** The
365
+ adaptive driver (`src/adaptive-driver.ts`, §5.2) does exactly this: free
366
+ canonical-URL / content-hash dedup, free host/title/length triage, LLM relevance
367
+ only on the ambiguous survivors. Measured: **76% fewer LLM calls, 74% cheaper**,
368
+ recovering the de-dup half of the verifier's cleanliness. The remaining gap to
369
+ full-LLM is the relevance-judgment half, kept honest in §5.2 — adaptive is a
370
+ frontier point you choose by how much a kept-but-marginal source costs you, not a
371
+ strict improvement.
372
+ 2. **A free check the LLM judge can't replicate — shipped.** Claim-grounding
373
+ (`src/claim-grounding.ts`, §5.1) adds the one verification an LLM relevance judge is
374
+ structurally blind to: does the cited claim actually appear in the page? It catches
375
+ 5/5 planted misattributions at **$0**, vs the judge's 4/5 at ~$0.003/topic.
376
+
377
+ What is still **not** built remains the worker: the live worker is a ~500-line
378
+ hand-wired pipeline (query-gen, search, fetch, propose) against the router directly,
379
+ where the runtime integration's pattern is to author an agent profile
380
+ and run it on a harness with a web-search tool — reusable and harness-agnostic. The
381
+ direct pipeline is cheaper to run today (no harness, no creds beyond the router) but it
382
+ is the loop's main remaining piece of duplication, and the obvious next step if this
383
+ loop graduates from experiment to production.
384
+
385
+ ## 9. From hygiene to depth — a research-DRIVING loop, measured on held-out deep questions
386
+
387
+ Everything above measures one thing: **source hygiene** — how *few* sources a verifier
388
+ admits at equal coverage. That is the right metric for a verifier whose only job is to
389
+ filter, and it is why the win turned out to be de-duplication (§4.1): the most a
390
+ filter-only verifier can do is reject. By construction it cannot make the knowledge base
391
+ *answer more*; it can only make it *carry less*. So "the verifier mostly deduplicates" is
392
+ not a disappointing finding about *this* verifier — it is the ceiling of what *any*
393
+ admit-or-reject step can do. To ask whether a second agent can improve the research
394
+ itself, you have to change both the agent and the metric.
395
+
396
+ So we built the opposite agent and gave it the opposite metric. The **driving driver**
397
+ (`src/research-driving-driver.ts`, `createResearchDrivingDriver`) does not filter. It
398
+ extracts each fetched source's claims, demands a second independent source for every
399
+ claim, generates comparative / mechanism / contradiction sub-questions, and steers the
400
+ worker to chase them in the next round. Its thesis is that *driving the research deeper*
401
+ — not pruning it — builds a knowledge base that answers harder questions. We measure it
402
+ not on admitted-count (which would score its whole point as a regression — it admits
403
+ *more*) but on a **firewalled exam of 20 deep questions across 5 ML topics**
404
+ (`tests/loops/held-out-exam.ts`), graded with a $0 deterministic substring grader the
405
+ loop never sees. The questions are depth questions by construction — the grader scores
406
+ **0/20** on a one-line topic definition and **20/20** on a mechanism-rich paragraph, so
407
+ a high score is reachable only by depth, not by grader slack. All three arms run the
408
+ same real web worker and differ only in the driver: (A) plain collection, (B)
409
+ verify/dedup, (C) driving.
410
+
411
+ **The honest verdict: driving does NOT reliably beat plain collection, and costs
412
+ ~12–16× more.** The tell is that the winner flips with the compute budget, on the same
413
+ exam:
414
+
415
+ | arm | answered @ B=4 | answered @ B=6 | cost (5 topics) | tokens |
416
+ |---|---|---|---|---|
417
+ | single-agent (collect) | 13/20 | **15/20** | $0.005–0.007 | ~2.4–3.0k |
418
+ | verify/dedup | **15/20** | **15/20** | $0.031–0.027 | ~21k |
419
+ | **driving (deepen)** | **16/20** | 13/20 | **$0.089–0.084** | ~69–71k |
420
+
421
+ Driving "wins" at B=4 (16 > 15 > 13) and "loses" at B=6 (13 < 15), while the
422
+ single-agent arm itself swings 15→13 across the two budgets. At n=5 topics a ±1–3
423
+ question difference is inside the run-to-run web variance — the **within-arm swing is as
424
+ large as the between-arm gap**, which is the signature of a null. What is stable and
425
+ large is the cost: an order of magnitude more dollars and ~24× the tokens, for the
426
+ claim-extraction LLM call driving runs on every fetched source.
427
+
428
+ The autopsy explains it: every arm finished in **one effective round**
429
+ (`passes=2` on every topic at every budget). The generic readiness gate — "one source
430
+ closes the gap" — is met by the first fetch, so the loop stops *before* the driving
431
+ driver ever steers a second round, and its entire mechanism is multi-round. So we gave
432
+ it its fairest test: a controlled probe that *forces* three rounds so the driver
433
+ actually steers. It **still ties** a blind worker that just re-searches the same gaps —
434
+ **8/12 vs 8/12, at ~9× the cost**. Driving was better on RLHF (3 vs 1 — chasing
435
+ corroboration reached a page blind search missed) and worse on speculative decoding (2
436
+ vs 4 — steering pulled the worker *off* the pages that answered the exam); the two
437
+ cancel. The null survives the fix, so it is not an artifact of a permissive gate.
438
+
439
+ The durable output is the apparatus, not the agent: a firewalled deep-question exam with
440
+ a $0 grader that can tell depth from surface, reusable for any future research-quality
441
+ claim. Full result, per-topic tables, and the probe: [`docs/results/research-driving.md`](results/research-driving.md).
442
+ The two findings compose into one rule for "should I add a second research agent?":
443
+ a **filter** agent measured on hygiene buys de-dup cleanliness you can get cheaper from a
444
+ hash (§3–§4); a **driver** agent measured on depth buys nothing reliable over plain
445
+ collection at this n and worker, for 9–16× the cost (§9). Neither earns a blanket "yes";
446
+ both earn a narrow, cost-stratified one — the verifier on misattribution and the
447
+ off-scope tail (§5), the driver only where a richer worker makes "go corroborate this"
448
+ reach a page collection can't.
449
+
450
+ ### 9.1 The domain was too easy — re-running the A/B where one search is not enough
451
+
452
+ The §9 null has a structural cause, not a measurement one: **on an ML topic a single
453
+ good search already collects the answer.** Every arm finished in one effective round
454
+ because the first fetch met the readiness gate, so the driving driver — whose mechanism
455
+ is steering a *second* round — never acted. When one search suffices, there is no
456
+ investigation for a smarter coordinator to do, and the metric can only reward
457
+ collection. To ask whether topology *can ever* beat blind collection, you have to move
458
+ to a domain where the answer is buried and a single fetch provably cannot surface it.
459
+
460
+ So we did. We ported the whole apparatus — firewalled checklist, `$0` model-free
461
+ grader, matched-compute 3-arm A/B — onto **investment research**: give a loop a company
462
+ + ticker + an as-of cutoff and grade the thesis on the buried, material 10-K-footnote
463
+ facts a ticker search misses (an HTM mark the size of a bank's equity, a 97%-uninsured
464
+ deposit base, a negative per-unit margin). First we *calibrated* the new metric and
465
+ proved it discriminates depth on this harder domain — a shallow ticker summary scores
466
+ **1/27 (4%)**, a deep filings-grounded thesis **27/27 (100%)**, a **+96-point** gap.
467
+ Then the live 3-arm A/B over 5 held-out companies: **driving surfaced the most buried
468
+ facts (16/27, 59%) vs blind collection (11/27, 41%)** for ~1.9× the cost — the lift is
469
+ real and points the right way, but at n=5 the paired-bootstrap CI still crosses zero
470
+ (P(Δ≤0)=0.08), and verify did not beat collection (10/27). So the verdict survives the
471
+ domain change: **no topology *significantly* beats collection — but on a domain where
472
+ the answer must be investigated for, driving is the only arm that even leans positive,
473
+ and it does so suggestively, not significantly.** Full reframe, calibration, and
474
+ per-company A/B: [`docs/results/investment-thesis.md`](results/investment-thesis.md).
475
+
476
+ ## 10. Reproduce
477
+
478
+ The loop, the worker, the verifier, the claim-grounding mode, the adaptive driver, the
479
+ driving driver, the held-out exam, the cost instrumentation, and every A/B are all in
480
+ this repository. Each live test gates a cheap one-call glm-5.2 smoke before any
481
+ multi-topic burn.
482
+
483
+ ```bash
484
+ git clone https://github.com/tangle-network/agent-knowledge
485
+ cd agent-knowledge && pnpm install
486
+
487
+ # offline A/B — deterministic, no credentials (a controlled lower bound that
488
+ # exercises the same harness against a planted source pool)
489
+ pnpm exec vitest run tests/loops/research-loop-equal-compute.test.ts
490
+
491
+ # offline claim-grounding + adaptive floors (no credentials)
492
+ pnpm exec vitest run tests/loops/claim-grounding-ab.test.ts -t "offline"
493
+ pnpm exec vitest run tests/loops/adaptive-ab.test.ts
494
+
495
+ # the live cleanliness sweep — real web search + a real glm-5.2 verifier, with
496
+ # per-arm cost reported (~$0.20 for 9 topics)
497
+ export TANGLE_API_KEY=<router key with glm-5.2 credits>
498
+ AGENT_KNOWLEDGE_LIVE=1 \
499
+ AGENT_KNOWLEDGE_LIVE_GOALS="self-speculative decoding|grouped-query attention|rotary position embeddings|KV-cache quantization|LoRA|ring attention|constitutional AI|the transformer architecture|gradient descent" \
500
+ pnpm exec vitest run tests/loops/research-loop-equal-compute.test.ts
501
+
502
+ # live misattribution band — three verifier arms over the same proposals
503
+ AGENT_KNOWLEDGE_LIVE=1 TANGLE_API_KEY=<…> \
504
+ CLAIM_GROUNDING_LIVE_GOALS='self-speculative decoding|rotary position embeddings|grouped-query attention|KV-cache quantization|LoRA' \
505
+ pnpm exec vitest run tests/loops/claim-grounding-ab.test.ts -t "three verifier arms"
506
+
507
+ # live adaptive frontier — single / full-LLM / adaptive on the same fetched proposals
508
+ AGENT_KNOWLEDGE_LIVE=1 TANGLE_API_KEY=<…> \
509
+ ADAPTIVE_LIVE_GOALS="self-speculative decoding|rotary position embeddings|grouped-query attention|KV-cache quantization|LoRA fine-tuning" \
510
+ pnpm exec vitest run tests/loops/adaptive-ab.test.ts -t "three-topology"
511
+
512
+ # the research-DRIVING 3-arm A/B (§9) — collect / verify / drive, graded on the
513
+ # held-out deep-question exam; re-run at RQ_LIVE_BUDGET=6 to see the verdict flip
514
+ AGENT_KNOWLEDGE_LIVE=1 RQ_LIVE_BUDGET=4 TANGLE_API_KEY=<…> \
515
+ pnpm exec vitest run tests/loops/research-driving-ab.test.ts -t "3-arm A/B"
516
+
517
+ # the controlled multi-round probe — forces 3 rounds so the driver actually steers
518
+ AGENT_KNOWLEDGE_LIVE=1 RQ_PROBE=1 RQ_PROBE_ROUNDS=3 TANGLE_API_KEY=<…> \
519
+ pnpm exec vitest run tests/loops/research-driving-ab.test.ts -t "multi-round probe"
520
+ ```
521
+
522
+ `AGENT_KNOWLEDGE_LIVE_GOALS` (and the per-result `*_LIVE_GOALS`) take a `|`-separated
523
+ topic list; the live arms run the loops on each at equal compute and report the paired
524
+ bootstrap and per-arm cost.
525
+
526
+ **Source:** the loop — [`src/two-agent-research-loop.ts`](../src/two-agent-research-loop.ts);
527
+ the live worker + verifier + cost instrumentation — [`src/web-research-worker.ts`](../src/web-research-worker.ts);
528
+ the misattribution check — [`src/claim-grounding.ts`](../src/claim-grounding.ts);
529
+ the adaptive driver — [`src/adaptive-driver.ts`](../src/adaptive-driver.ts);
530
+ the driving driver — [`src/research-driving-driver.ts`](../src/research-driving-driver.ts);
531
+ the held-out exam + $0 grader — [`tests/loops/held-out-exam.ts`](../tests/loops/held-out-exam.ts);
532
+ the A/B harnesses — [`tests/loops/`](../tests/loops/).
533
+ Per-result detail: [`docs/results/cost-quality.md`](results/cost-quality.md),
534
+ [`docs/results/claim-grounding.md`](results/claim-grounding.md),
535
+ [`docs/results/adaptive.md`](results/adaptive.md),
536
+ [`docs/results/research-driving.md`](results/research-driving.md),
537
+ [`docs/results/investment-thesis.md`](results/investment-thesis.md) (§9.1 — the domain reframe + calibration + 3-arm A/B).
538
+ </content>
539
+ </invoke>
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tangle-network/agent-knowledge",
3
- "version": "1.9.0",
3
+ "version": "1.11.0",
4
4
  "description": "Source-grounded, eval-gated knowledge growth primitives for agents.",
5
5
  "homepage": "https://github.com/tangle-network/agent-knowledge#readme",
6
6
  "repository": {
@@ -39,10 +39,10 @@
39
39
  "import": "./dist/sources/index.js",
40
40
  "default": "./dist/sources/index.js"
41
41
  },
42
- "./profiles": {
43
- "types": "./dist/profiles/index.d.ts",
44
- "import": "./dist/profiles/index.js",
45
- "default": "./dist/profiles/index.js"
42
+ "./benchmarks": {
43
+ "types": "./dist/benchmarks/index.d.ts",
44
+ "import": "./dist/benchmarks/index.js",
45
+ "default": "./dist/benchmarks/index.js"
46
46
  }
47
47
  },
48
48
  "bin": {
@@ -68,16 +68,16 @@
68
68
  "format": "biome format --write src tests"
69
69
  },
70
70
  "dependencies": {
71
- "@tangle-network/agent-eval": "^0.100.0",
72
- "@tangle-network/agent-runtime": "^0.77.0",
71
+ "@tangle-network/agent-eval": "^0.107.0",
73
72
  "zod": "^4.3.6"
74
73
  },
75
74
  "devDependencies": {
76
75
  "@biomejs/biome": "^2.4.15",
77
76
  "@neo4j-labs/agent-memory": "0.4.0",
78
- "@tangle-network/sandbox": "^0.8.0",
77
+ "@tangle-network/sandbox": "^0.9.7",
79
78
  "@types/node": "^25.6.0",
80
79
  "tsup": "^8.0.0",
80
+ "tsx": "^4.22.4",
81
81
  "typescript": "^5.7.0",
82
82
  "vitest": "^3.0.0"
83
83
  },
@@ -85,7 +85,6 @@
85
85
  "minimumReleaseAge": 4320,
86
86
  "minimumReleaseAgeExclude": [
87
87
  "@tangle-network/agent-eval",
88
- "@tangle-network/agent-runtime",
89
88
  "@tangle-network/sandbox"
90
89
  ]
91
90
  },