-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathindex.html
More file actions
923 lines (867 loc) · 56.8 KB
/
Copy pathindex.html
File metadata and controls
923 lines (867 loc) · 56.8 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
<!DOCTYPE html>
<html lang="en" data-theme="dark">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>LoopGain — cost control for AI agent loops</title>
<meta name="description" content="AI agent loops waste money when they don't know when to stop. LoopGain measures the loop in real time and stops it at the right moment — 92.8% less spend, ~15× faster across 2,000 real trials. Open source, Apache-2.0." />
<meta name="theme-color" content="#0A0B0D" />
<link rel="icon" href="/favicon.ico" sizes="32x32" />
<link rel="icon" type="image/svg+xml" href="/favicon.svg" />
<link rel="apple-touch-icon" href="/apple-touch-icon.png" />
<meta property="og:type" content="website" />
<meta property="og:url" content="https://loopgain.ai/" />
<meta property="og:site_name" content="LoopGain" />
<meta property="og:title" content="LoopGain — cost control for AI agent loops" />
<meta property="og:description" content="AI agent loops waste money when they don't know when to stop. LoopGain measures the loop in real time and stops it at the right moment — 92.8% less spend, ~15× faster across 2,000 real trials. Open source, Apache-2.0." />
<meta property="og:image" content="https://loopgain.ai/og.png" />
<meta property="og:image:width" content="1200" />
<meta property="og:image:height" content="630" />
<meta property="og:image:alt" content="LoopGain — cost control for AI agent loops. 92.8% less spend, ~15× faster across 2,000 real trials." />
<!-- Twitter / X large-image card -->
<meta name="twitter:card" content="summary_large_image" />
<meta name="twitter:site" content="@LoopGainAI" />
<meta name="twitter:creator" content="@LoopGainAI" />
<meta name="twitter:title" content="LoopGain — cost control for AI agent loops" />
<meta name="twitter:description" content="Measure the agent loop in real time, stop at convergence, roll back before it degrades. 92.8% less spend, ~15× faster across 2,000 real trials. Open source, Apache-2.0." />
<meta name="twitter:image" content="https://loopgain.ai/og.png" />
<meta name="twitter:image:alt" content="LoopGain — cost control for AI agent loops." />
<link rel="preconnect" href="https://fonts.googleapis.com" />
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
<link rel="preload" as="style" href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&family=JetBrains+Mono:wght@400;500;600;700&display=swap" data-font-css />
<noscript><link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&family=JetBrains+Mono:wght@400;500;600;700&display=swap" rel="stylesheet" /></noscript>
<script src="/fonts.js" defer></script>
<link rel="stylesheet" href="landing.css" />
<!-- Structured data — SoftwareApplication (Schema.org) -->
<script type="application/ld+json">
{
"@context": "https://schema.org",
"@type": "SoftwareApplication",
"name": "LoopGain",
"applicationCategory": "DeveloperApplication",
"applicationSubCategory": "AI Observability",
"operatingSystem": "Python 3.10+",
"description": "Open-source cost control for AI agent loops: stops each verify-revise loop once it has converged and rolls back before it degrades, instead of running to a fixed max_iterations cap. A Barkhausen-criterion (Aβ) trajectory classifier under the hood. 92.8% cost reduction and ~15× faster wall-clock, measured across 2,000 real-API trials. Framework- and model-agnostic.",
"softwareVersion": "0.5.0",
"license": "https://www.apache.org/licenses/LICENSE-2.0",
"url": "https://loopgain.ai",
"downloadUrl": "https://pypi.org/project/loopgain/",
"codeRepository": "https://github.com/loopgain-ai/loopgain",
"programmingLanguage": "Python",
"keywords": "AI agent cost control, agent loop cost, token cost reduction, AI agent loops, Barkhausen criterion, loop gain, Aβ, cost controller, LangGraph, CrewAI, AutoGen, LangChain, OpenAI Agents SDK, Claude Agent SDK, ReAct, self-refinement, agentic coding, max_iterations, agent reliability, LLM observability",
"offers": {
"@type": "Offer",
"price": "0",
"priceCurrency": "USD",
"description": "Free if self-hosted; paid hosted tier available."
},
"author": {
"@type": "Person",
"name": "David Fitzsimmons"
}
}
</script>
<!-- Structured data — Organization (Schema.org) -->
<script type="application/ld+json">
{
"@context": "https://schema.org",
"@type": "Organization",
"name": "LoopGain",
"url": "https://loopgain.ai",
"logo": "https://loopgain.ai/loopgain-lockup-dark.png",
"description": "Open-source cost control for AI agent loops — a loop-gain (Aβ) trajectory classifier under the hood.",
"founder": {
"@type": "Person",
"name": "David Fitzsimmons"
},
"sameAs": [
"https://github.com/loopgain-ai",
"https://pypi.org/project/loopgain/"
]
}
</script>
</head>
<body>
<!-- ─── nav ──────────────────────────────────────────────────────────── -->
<header class="nav" data-screen-label="nav">
<a class="nav-brand" href="#top" aria-label="LoopGain home">
<img src="/loopgain-lockup-dark-sm.png" alt="LoopGain" class="brand-dark" style="height:24px; width:auto;" />
<img src="/loopgain-lockup-light-sm.png" alt="LoopGain" class="brand-light" style="height:24px; width:auto;" />
<span class="nav-version mono" data-lg-version>v0.5.2</span>
</a>
<nav class="nav-links" aria-label="primary">
<a href="#bands">bands</a>
<a href="#install">install</a>
<a href="/benchmarks">benchmark</a>
<a href="#dashboard">dashboard</a>
<a href="#pricing">pricing</a>
<a href="/blog/">blog</a>
<a href="https://github.com/loopgain-ai" rel="noopener">github ↗</a>
<button class="nav-theme" id="themeToggle" aria-label="Toggle theme" title="Toggle theme">
<svg width="16" height="16" viewBox="0 0 16 16" aria-hidden="true"><path d="M8 1.5v2M8 12.5v2M3.7 3.7l1.4 1.4M10.9 10.9l1.4 1.4M1.5 8h2M12.5 8h2M3.7 12.3l1.4-1.4M10.9 5.1l1.4-1.4" stroke="currentColor" stroke-width="1.4" stroke-linecap="round" fill="none"/><circle cx="8" cy="8" r="3" stroke="currentColor" stroke-width="1.4" fill="none"/></svg>
</button>
<a class="nav-cta" href="https://dashboard.loopgain.ai" rel="noopener">open dashboard →</a>
</nav>
</header>
<main id="top">
<!-- ─── hero ─────────────────────────────────────────────────────────── -->
<section class="hero" data-screen-label="hero">
<div class="hero-grid">
<div class="hero-copy">
<div class="eyebrow">
<span class="status-dot"></span>
<span>Apache-2.0 · adapters for LangGraph, CrewAI, AutoGen, LangChain, OpenAI Agents SDK, Claude Agent SDK</span>
</div>
<h1>
Your AI agent loops don't know when to stop. <span style="color:var(--accent)">LoopGain does.</span>
</h1>
<p class="hero-sub">
When an agent keeps checking and revising its own work, it often spins on a problem it can't solve — or talks itself into an answer worse than one it already had. LoopGain watches the loop as it runs, stops it at the right iteration, and keeps the <em>best</em> version, not the last.
</p>
<div class="hero-actions">
<div class="copy-box" data-copy="pip install loopgain" role="button" tabindex="0">
<span class="copy-prompt mono">$</span>
<code class="mono">pip install loopgain</code>
<span class="copy-hint mono">copy</span>
</div>
<a class="btn btn-amber" href="mailto:hello@loopgain.ai?subject=Design-partner%20pilot" rel="noopener" data-capture="pilot" data-source="hero">
apply for a design-partner pilot
<svg width="12" height="12" viewBox="0 0 12 12" aria-hidden="true"><path d="M3 9 L9 3 M5 3 H9 V7" stroke="currentColor" stroke-width="1.4" fill="none" stroke-linecap="round"/></svg>
</a>
<a class="btn btn-ghost" href="https://dashboard.loopgain.ai/signup" rel="noopener">
sign up — free hosted dashboard
</a>
</div>
<p class="hero-tertiary mono">
<a href="https://github.com/loopgain-ai/loopgain" rel="noopener">Apache-2.0 · GitHub ↗</a>
</p>
<div class="hero-meta mono">
<span><span class="hm-key">v</span> <span data-lg-version-num>0.5.0</span></span>
<span class="dot-sep"></span>
<span><span class="hm-key">py</span> ≥ 3.10</span>
<span class="dot-sep"></span>
<span><span class="hm-key">deps</span> 0</span>
<span class="dot-sep"></span>
<span><span class="hm-key">tests</span> 190+ passing</span>
</div>
</div>
<!-- ONE real max_iter=20 benchmark run (bench seed 114, claude-agent-sdk · haiku-4.5).
Shows the MECHANISM — rollback. We draw the one trajectory and show where each stop
policy lands on it; no second rollout, so the quality gap is the stop rule, not
run-to-run variance. Attempt 1 is BROKEN (err 11) — a cap-at-1 would ship that.
Attempt 2 is nearly right (err 1); attempt 3 breaks again (err 11). LoopGain runs 3
attempts, sees the loop oscillate, stops, and ROLLS BACK to the best it saw (err 1 at
attempt 2) — an answer the loop had thrown away. The cap keeps oscillating and ships
its last attempt (err 11) at 20. B20 cost is the real measured cost_usd ($0.0512);
LoopGain's is the same run's spend through the stop (~$0.0077, 3 of 20 iters). The
dashed segment is the rollback: LoopGain returns the best, not the last. -->
<figure class="hero-chart" aria-label="One real max_iterations=20 benchmark run on a coding task, showing rollback. Attempt 1 is broken (error 11) — a cap of one iteration would ship that. Attempt 2 is nearly right (error 1); attempt 3 breaks again. LoopGain runs three attempts, sees the loop oscillate, stops, and rolls back to the best it saw (error 1 at attempt 2) — an answer the loop had already thrown away. The max_iterations=20 cap keeps oscillating and ships its last attempt (error 11) at attempt 20 for $0.0512. LoopGain spent about $0.0077 — three of twenty iterations, 85% less — and recovered a working answer the cap discarded. The difference is the stop rule applied to one run, not a second rollout.">
<div class="hero-chart-head">
<span class="mono label">loop · codegen · claude-agent-sdk · haiku-4.5 · seed 114</span>
<a class="mono hc-realtag" href="/benchmarks">1 real run ↗</a>
</div>
<div class="hero-chart-stage">
<div class="plot-wrap">
<svg class="hero-chart-svg" viewBox="0 0 600 360" preserveAspectRatio="none" aria-hidden="true">
<defs>
<linearGradient id="lgFill" x1="0" y1="0" x2="0" y2="1">
<stop offset="0%" stop-color="var(--band-fast)" stop-opacity="0.20"/>
<stop offset="100%" stop-color="var(--band-fast)" stop-opacity="0"/>
</linearGradient>
</defs>
<path class="grid" id="chartGrid" d=""></path>
<!-- the one real run: per-iteration error, all 20 iters, ends broken -->
<path id="capSeg" class="hc-baseline" fill="none" d=""></path>
<!-- LoopGain: the run's observed prefix, then the dashed rollback to best -->
<path id="lgArea" fill="url(#lgFill)" d=""></path>
<!-- bold descent (→ best) + desaturated overshoot (the loop's rejected next attempt) -->
<path id="lgSeg" class="hc-lgline" fill="none" d=""></path>
<path id="lgOvershoot" class="hc-lgline" fill="none" opacity="0.42" d=""></path>
<!-- rollback connector: dashed snap-back arc (LoopGain amber) from the stop point to the best -->
<path id="lgRollback" fill="none" stroke="var(--accent)" stroke-width="2" stroke-dasharray="4 4" opacity="0" d=""></path>
<!-- LoopGain keep marker (amber) at the best attempt it rolls back to -->
<circle id="lgStopGlow" r="13" fill="var(--accent)" fill-opacity="0.16" cx="0" cy="0" opacity="0"/>
<circle id="lgStopRing" r="8" fill="none" stroke="var(--accent)" stroke-width="2.5" cx="0" cy="0" opacity="0"/>
<!-- moving heads: amber = LoopGain dot, gray = max_iter=20 run -->
<circle id="lgHead" r="5" fill="var(--accent)" cx="0" cy="0" opacity="0"/>
<circle id="traceHead" r="5" fill="var(--text-3)" cx="0" cy="0"/>
</svg>
<!-- floating chips -->
<span class="hc-chip hc-chip-lg" id="lgChip">✓ rolls back · keeps err 1</span>
<span class="hc-chip hc-chip-base" id="capChip">running…</span>
</div>
<!-- error axis — the only quantitative axis -->
<div class="err-axis mono" aria-hidden="true">
<span>11</span><span>8</span><span>4</span><span>0</span>
<em>error</em>
</div>
</div>
<!-- Band-state color legend removed 2026-06-05: the hero line is a single
"loop" color (amber = LoopGain), so a line-color→state key was both
decorative and misleading (it implied the green line = FAST_CONVERGE,
but this loop oscillates). The five named states live in the dedicated
"bands" section further down the page. -->
<figcaption class="hero-chart-foot mono">
<div class="hcf-line hcf-lg">
<span class="hcf-name"><i class="hcf-dot"></i>LoopGain</span>
<span class="hcf-stat"><span class="hcf-k">stops</span> <b id="lgStopIter">iter 3</b></span>
<span class="hcf-stat"><span class="hcf-k">cost</span> <b id="lgCost">$0.0077</b></span>
<span class="hcf-stat hcf-ok" id="lgVerdict">running…</span>
</div>
<div class="hcf-line hcf-base">
<span class="hcf-name"><i class="hcf-dot"></i>max_iter=20</span>
<span class="hcf-stat"><span class="hcf-k">runs</span> <b id="capIter">iter 20</b></span>
<span class="hcf-stat"><span class="hcf-k">cost</span> <b id="capCost">$0.0512</b></span>
<span class="hcf-stat hcf-bad" id="capVerdict">running…</span>
</div>
<div class="hcf-saved">
<span class="hcf-saved-num" id="savedPct">85%</span>
<span class="hcf-saved-lab">less spend<br>+ better output</span>
</div>
</figcaption>
</figure>
</div>
</section>
<!-- ─── trifecta (bench evidence below the fold) ─────────────────────── -->
<section class="trifecta-sec" id="bench" data-screen-label="bench-trifecta">
<div class="trifecta-head">
<span class="eyebrow mono">§00 · bench · 2026-05-25</span>
<h2>2,000 paired real-API trials. 10 cells. 6 adapters.</h2>
<span class="tri-foot mono">methodology pre-registered → <a href="/benchmarks">/benchmarks</a></span>
</div>
<div class="trifecta-grid">
<a class="tri-card" href="/benchmarks#cost">
<span class="tri-tag">01 · cost</span>
<span class="tri-big">92.8%</span>
<span class="tri-label">less API spend vs <code class="mono code-inline">max_iter=20</code></span>
<span class="tri-detail">$27.05 → $1.94 · $25.11 saved across the bench</span>
<span class="tri-link">see the cost breakdown →</span>
</a>
<a class="tri-card" href="/benchmarks#latency">
<span class="tri-tag">02 · latency</span>
<span class="tri-big">~15×</span>
<span class="tri-label">faster wall-clock per trial</span>
<span class="tri-detail">30.9s → 2.1s median per trial</span>
<span class="tri-link">see the latency data →</span>
</a>
<a class="tri-card" href="/benchmarks#quality">
<span class="tri-tag">03 · quality</span>
<span class="tri-big">0.678</span>
<span class="tri-label">aggregate judge preference for LoopGain vs <code class="mono code-inline">max_iter=20</code></span>
<span class="tri-detail">1,800 cross-vendor pairwise comparisons; CIs in writeup</span>
<span class="tri-link">see the quality findings →</span>
</a>
</div>
<a class="tri-writeup" href="/blog/posts/2000-agent-loop-trials-what-surprised-us/">
<span class="tw-k mono">the writeup</span>
<span class="tw-t">We ran 2,000 paired trials and wrote up the five findings that complicate the headline — where the savings shrink, when stopping early costs quality, and what the judge actually preferred.</span>
<span class="tw-link mono">read what surprised us →</span>
</a>
</section>
<!-- ─── the problem ──────────────────────────────────────────────────── -->
<section class="prob" id="problem" data-screen-label="problem">
<div class="section-head">
<span class="eyebrow mono">§01 · the problem</span>
<h2>Every agent in production is running on a guess.</h2>
</div>
<div class="prob-grid">
<div class="prob-copy">
<p>
Search any agent codebase for <code class="code-inline mono">max_iterations</code>. You'll find <code class="code-inline mono">5</code>.
Sometimes <code class="code-inline mono">10</code>. Nobody can defend the number, because no one has a principled way to pick it.
</p>
<p>
It's the universal pre-crash hack. It fires too early on a loop that was three iterations from converging, or too late on one that started diverging in iteration two — the model is now five rewrites deep into a hallucination and the bill is real.
</p>
<figure class="prob-quote">
<span class="prob-quote-mark" aria-hidden="true">“</span>
<blockquote class="prob-quote-text">we just set it to five and hope.</blockquote>
<figcaption class="prob-quote-by mono">— literally every team that ships an agent</figcaption>
</figure>
</div>
<div class="prob-stats">
<div class="ps-row">
<div class="ps-num num-mono">01</div>
<div class="ps-lab"><b>The loop hits the ceiling without converging.</b> Two iterations short of target. You ship iteration 5 and call it a feature.</div>
</div>
<div class="ps-row">
<div class="ps-num num-mono ps-num-stall">02</div>
<div class="ps-lab"><b>The loop is stalling by iteration 3.</b> The reviser keeps changing the draft, but the error stops moving — pinned, going nowhere. Every later token is waste, and a fixed cap can't see it.</div>
</div>
<div class="ps-row">
<div class="ps-num num-mono ps-num-fast">03</div>
<div class="ps-lab"><b>The loop was about to converge — and got cut off.</b> One more iteration would have landed at target. The cap fired anyway.</div>
</div>
<p class="ps-foot mono">three failure modes a static cap can't tell apart · what the trajectory classifier resolves in one state</p>
</div>
</div>
</section>
<!-- ─── the five bands ────────────────────────────────────────────────── -->
<section class="bands-sec" id="bands" data-screen-label="bands">
<div class="section-head">
<span class="eyebrow mono">§02 · the fix</span>
<h2>Replace <span class="mono code-inline">max_iterations=N</span> with a real measurement of convergence.</h2>
<p class="section-sub">
Each iteration produces an error signal. LoopGain reads four features off the full error trajectory — cumulative reduction, trend slope, trend significance, and oscillation magnitude — and routes the loop into one of five named states. Two say keep going, three say stop. The state itself is the decision.
</p>
</div>
<div class="band-cards">
<article class="bc bc-fast">
<header class="bc-head">
<span class="bc-dot"></span>
<span class="bc-name mono">FAST_CONVERGE</span>
</header>
<div class="bc-range mono">cumulative ≤ 10%</div>
<p class="bc-desc">Error has dropped to a tenth of where it started, or better. The reviser is operating well inside its competence; the only correct move is to stay out of the way.</p>
<div class="bc-spark" data-spark="fast" aria-hidden="true"></div>
<footer class="bc-foot mono"><span>→</span> KEEP GOING</footer>
</article>
<article class="bc bc-conv">
<header class="bc-head">
<span class="bc-dot"></span>
<span class="bc-name mono">CONVERGING</span>
</header>
<div class="bc-range mono">trend < 0, p < 0.05</div>
<p class="bc-desc">Healthy progress. A statistically significant downward trend, or at least a halving of cumulative error. Most well-tuned agent loops live here.</p>
<div class="bc-spark" data-spark="conv" aria-hidden="true"></div>
<footer class="bc-foot mono"><span>→</span> KEEP GOING</footer>
</article>
<article class="bc bc-stall">
<header class="bc-head">
<span class="bc-dot"></span>
<span class="bc-name mono">STALLING</span>
</header>
<div class="bc-range mono">no trend, no oscillation</div>
<p class="bc-desc">No significant slope and no detectable oscillation — the loop is doing something, but it's not making progress. After two consecutive readings the library stops and returns the best-so-far. Usually a reviser-prompt plateau.</p>
<div class="bc-spark" data-spark="stall" aria-hidden="true"></div>
<footer class="bc-foot mono"><span>⚠</span> WATCH · STOP</footer>
</article>
<article class="bc bc-osc">
<header class="bc-head">
<span class="bc-dot"></span>
<span class="bc-name mono">OSCILLATING</span>
</header>
<div class="bc-range mono">high variance, flat trend</div>
<p class="bc-desc">The trajectory's detrended residuals are too large to be noise but the slope is flat — the model is undoing its own last revision. <b>Rollback. Stop.</b></p>
<div class="bc-spark" data-spark="osc" aria-hidden="true"></div>
<footer class="bc-foot mono"><span>■</span> ROLLBACK · STOP</footer>
</article>
<article class="bc bc-div">
<header class="bc-head">
<span class="bc-dot"></span>
<span class="bc-name mono">DIVERGING</span>
</header>
<div class="bc-range mono">trend > 0, cumulative > 10%</div>
<p class="bc-desc">A statistically significant upward trend with at least 10% cumulative growth. Each iteration is making the output strictly worse. The longer it runs, the more money you light on fire. <b>Rollback. Stop.</b></p>
<div class="bc-spark" data-spark="div" aria-hidden="true"></div>
<footer class="bc-foot mono"><span>■</span> ROLLBACK · STOP</footer>
</article>
</div>
<div class="bands-formula">
<div class="bf-eq mono" aria-label="trajectory classifier formula">
<span class="bf-sym">state</span>
<span class="bf-eq-sign">=</span>
<span class="bf-smooth">classify(</span>
<span class="bf-frac">
<span class="bf-num">E<sub>ratio</sub> · slope<sub>log</sub></span>
<span class="bf-den">slope<sub>p</sub> · osc<sub>std</sub></span>
</span>
<span class="bf-smooth">)</span>
</div>
<p class="bf-note mono">
Four trajectory features, one of five named states out. The whole library is a few hundred lines around this classifier. The result is a stop decision your model never had to make.
</p>
</div>
</section>
<!-- ─── trust signals (open-core credibility strip) ──────────────────── -->
<section class="trust-sec" data-screen-label="trust">
<div class="trust-inner">
<div class="trust-items">
<span class="trust-item">pre-registered methodology, locked 2026-05-21</span>
<span class="trust-item">7 pre-data amendments preserved</span>
<span class="trust-item">2,000 trials of raw data public</span>
<span class="trust-item">6 analysis charts in the repo</span>
<span class="trust-item">zero kill criteria fired</span>
</div>
<a class="trust-cta" href="https://github.com/loopgain-ai/loopgain-bench" rel="noopener">
reproduce it yourself ↗
</a>
</div>
</section>
<!-- ─── install / code ────────────────────────────────────────────────── -->
<section class="code-sec" id="install" data-screen-label="install">
<div class="section-head">
<span class="eyebrow mono">§03 · integration</span>
<h2>A few lines, then your loop knows when to stop.</h2>
<p class="section-sub">
Two calls inside your loop: <code class="code-inline mono">should_continue()</code> to gate it, <code class="code-inline mono">observe()</code> to feed it the latest error. Read <code class="code-inline mono">lg.result</code> when it exits. That's the whole integration.
Framework adapters wrap the same primitive — pick the one that matches your stack, or stay raw.
</p>
</div>
<div class="code-tabs" role="tablist" aria-label="Code examples">
<button class="ct-tab is-active" role="tab" data-tab="raw" aria-selected="true">Raw API</button>
<button class="ct-tab" role="tab" data-tab="langgraph">LangGraph</button>
<button class="ct-tab" role="tab" data-tab="crewai">CrewAI</button>
<button class="ct-tab" role="tab" data-tab="autogen">AutoGen</button>
<button class="ct-tab" role="tab" data-tab="langchain">LangChain</button>
<button class="ct-tab" role="tab" data-tab="openai-agents">OpenAI Agents</button>
<button class="ct-tab" role="tab" data-tab="claude-agent-sdk">Claude Agent SDK</button>
<span class="ct-spacer"></span>
<button class="ct-copy mono" id="codeCopy" title="Copy snippet">copy</button>
</div>
<div class="code-panels">
<pre class="code-panel is-active mono" data-panel="raw"><code><span class="tk-com"># amber stripe = the lines you add or change. everything else is your existing loop.</span>
<span class="tk-add"><span class="tk-kw">from</span> loopgain <span class="tk-kw">import</span> LoopGain</span><span class="tk-add">lg = LoopGain(target_error=<span class="tk-num">0.1</span>, max_iterations=<span class="tk-num">20</span>)</span>
<span class="tk-add"><span class="tk-kw">while</span> lg.should_continue():</span> errors = your_verifier(output) <span class="tk-com"># your existing code</span>
<span class="tk-add"> lg.observe(errors, output=output)</span> output = your_refiner(output, errors) <span class="tk-com"># your existing code</span>
<span class="tk-add">result = lg.result</span><span class="tk-com"># result.outcome → "converged" · "oscillating" · "diverged" · "stalled" · "max_iterations"</span>
<span class="tk-com"># result.best_output → argmin(E(n)) — the actual best draft, not the last</span><span class="tk-com"># result.savings_vs_fixed_cap → iterations you didn't burn vs a fixed cap</span>
</code></pre>
<pre class="code-panel mono" data-panel="langgraph"><code><span class="tk-com"># amber stripe = the lines you add or change. pip install 'loopgain[langgraph]'</span>
<span class="tk-add"><span class="tk-kw">from</span> loopgain <span class="tk-kw">import</span> LoopGain</span><span class="tk-add"><span class="tk-kw">from</span> loopgain.integrations <span class="tk-kw">import</span> LangGraphAdapter</span>
graph = build_verify_revise_graph().compile()
<span class="tk-add">lg = LoopGain(target_error=<span class="tk-num">0.1</span>, max_iterations=<span class="tk-num">20</span>)</span><span class="tk-add">adapter = LangGraphAdapter(
lg=lg,
error_fn=<span class="tk-kw">lambda</span> update: len(update.get(<span class="tk-str">"verifier"</span>, {}).get(<span class="tk-str">"errors"</span>, [])),
)</span><span class="tk-add">final_state = adapter.run(graph, {<span class="tk-str">"draft"</span>: initial})</span>
<span class="tk-com"># adapter.stream() yields each step if you want the full trace.</span>
<span class="tk-com"># adapter.arun() / adapter.astream() are the async counterparts.</span>
</code></pre>
<pre class="code-panel mono" data-panel="crewai"><code><span class="tk-com"># amber stripe = the lines you add or change. pip install 'loopgain[crewai]'</span>
<span class="tk-add"><span class="tk-kw">from</span> loopgain <span class="tk-kw">import</span> LoopGain</span><span class="tk-add"><span class="tk-kw">from</span> loopgain.integrations <span class="tk-kw">import</span> CrewAIAdapter</span>
crew = Crew(agents=[writer_agent, verifier_agent], tasks=[task])
<span class="tk-add">lg = LoopGain(target_error=<span class="tk-num">0.1</span>, max_iterations=<span class="tk-num">20</span>)</span><span class="tk-add">adapter = CrewAIAdapter(
lg=lg,
task_error_fn=<span class="tk-kw">lambda</span> task_output: count_failed_checks(task_output.raw),
)</span><span class="tk-add"><span class="tk-kw">with</span> adapter: <span class="tk-com"># installs callbacks; uninstalls on exit</span></span><span class="tk-add"> adapter.install(crew)</span><span class="tk-add"> result = crew.kickoff()</span>
<span class="tk-com"># Observations land on `lg.result` — same shape as the raw API.</span>
<span class="tk-com"># Existing callbacks you had installed are chained, not clobbered.</span>
</code></pre>
<pre class="code-panel mono" data-panel="autogen"><code><span class="tk-com"># amber stripe = the lines you add or change. pip install 'loopgain[autogen]'</span>
<span class="tk-kw">from</span> autogen_agentchat.teams <span class="tk-kw">import</span> RoundRobinGroupChat
<span class="tk-add"><span class="tk-kw">from</span> loopgain <span class="tk-kw">import</span> LoopGain</span><span class="tk-add"><span class="tk-kw">from</span> loopgain.integrations <span class="tk-kw">import</span> AutoGenAdapter</span>
team = RoundRobinGroupChat(participants=[generator, verifier])
<span class="tk-add">lg = LoopGain(target_error=<span class="tk-num">0.1</span>, max_iterations=<span class="tk-num">20</span>)</span><span class="tk-add">adapter = AutoGenAdapter(
lg=lg,
error_fn=<span class="tk-kw">lambda</span> msg: parse_verifier_score(msg.content),
observe_sources={<span class="tk-str">"verifier"</span>}, <span class="tk-com"># only verifier drives observe()</span>
)</span><span class="tk-add">result = <span class="tk-kw">await</span> adapter.run(team, task=<span class="tk-str">"draft, verify, revise"</span>)</span>
<span class="tk-com"># Legacy v0.2 ConversableAgent.initiate_chat is not supported.</span>
</code></pre>
<pre class="code-panel mono" data-panel="langchain"><code><span class="tk-com"># amber stripe = the lines you add or change. pip install 'loopgain[langchain]'</span>
<span class="tk-kw">from</span> langchain.agents <span class="tk-kw">import</span> create_agent
<span class="tk-add"><span class="tk-kw">from</span> loopgain <span class="tk-kw">import</span> LoopGain</span><span class="tk-add"><span class="tk-kw">from</span> loopgain.integrations <span class="tk-kw">import</span> LangChainAdapter</span>
agent = create_agent(model=<span class="tk-str">"gpt-5-nano"</span>, tools=[get_weather])
<span class="tk-add">lg = LoopGain(target_error=<span class="tk-num">0.0</span>, max_iterations=<span class="tk-num">20</span>)</span><span class="tk-add">adapter = LangChainAdapter(
lg=lg,
error_fn=<span class="tk-kw">lambda</span> chunk: count_unresolved_tool_calls(chunk),
)</span><span class="tk-add">final = adapter.run(
agent,
{<span class="tk-str">"messages"</span>: [{<span class="tk-str">"role"</span>: <span class="tk-str">"user"</span>, <span class="tk-str">"content"</span>: prompt}]},
stream_mode=<span class="tk-str">"updates"</span>, version=<span class="tk-str">"v2"</span>,
)</span>
<span class="tk-com"># Duck-typed: also drives the legacy AgentExecutor.stream() shape.</span>
</code></pre>
<pre class="code-panel mono" data-panel="openai-agents"><code><span class="tk-com"># amber stripe = the lines you add or change. pip install 'loopgain[openai-agents]'</span>
<span class="tk-kw">from</span> agents <span class="tk-kw">import</span> Agent
<span class="tk-add"><span class="tk-kw">from</span> loopgain <span class="tk-kw">import</span> LoopGain</span><span class="tk-add"><span class="tk-kw">from</span> loopgain.integrations <span class="tk-kw">import</span> OpenAIAgentsAdapter</span>
agent = Agent(name=<span class="tk-str">"Refiner"</span>, instructions=<span class="tk-str">"..."</span>, tools=[verify_tool])
<span class="tk-add">lg = LoopGain(target_error=<span class="tk-num">0.0</span>, max_iterations=<span class="tk-num">20</span>)</span><span class="tk-add">adapter = OpenAIAgentsAdapter(
lg=lg,
error_fn=<span class="tk-kw">lambda</span> event: parse_verifier_failures(event),
)</span><span class="tk-add">result = <span class="tk-kw">await</span> adapter.run(agent, input=<span class="tk-str">"Fix the bug."</span>)</span>
<span class="tk-com"># Defaults to run_item_stream_event; cancels the stream at terminal state.</span>
<span class="tk-com"># adapter.run_sync(...) wraps the async path with asyncio.run.</span>
</code></pre>
<pre class="code-panel mono" data-panel="claude-agent-sdk"><code><span class="tk-com"># amber stripe = the lines you add or change. pip install 'loopgain[claude-agent-sdk]'</span>
<span class="tk-kw">from</span> claude_agent_sdk <span class="tk-kw">import</span> ClaudeAgentOptions, TextBlock
<span class="tk-add"><span class="tk-kw">from</span> loopgain <span class="tk-kw">import</span> LoopGain</span><span class="tk-add"><span class="tk-kw">from</span> loopgain.integrations <span class="tk-kw">import</span> ClaudeAgentSDKAdapter</span>
<span class="tk-add">lg = LoopGain(target_error=<span class="tk-num">0.0</span>, max_iterations=<span class="tk-num">20</span>)</span><span class="tk-add">adapter = ClaudeAgentSDKAdapter(
lg=lg,
error_fn=<span class="tk-kw">lambda</span> msg: count_fail_markers(msg),
)</span><span class="tk-add">result = <span class="tk-kw">await</span> adapter.run(
prompt=<span class="tk-str">"Write a haiku about feedback loops."</span>,
options=ClaudeAgentOptions(system_prompt=<span class="tk-str">"Self-verify each draft."</span>),
)</span>
<span class="tk-com"># Default filter: only AssistantMessage reaches error_fn.</span>
<span class="tk-com"># Also accepts message_iterator=client.receive_messages() for ClaudeSDKClient.</span>
</code></pre>
</div>
</section>
<!-- ─── see it in action ─────────────────────────────────────────────── -->
<section class="proof-sec" id="examples" data-screen-label="proof">
<div class="section-head">
<span class="eyebrow mono">§04 · see it in action</span>
<h2>Seven runnable scripts. Real Claude. Measured savings.</h2>
<p class="section-sub">
The library ships with seven examples — each runs the same loop <em>twice</em>, once with the universal <span class="code-inline mono">max_iterations=N</span> hack and once with LoopGain, and prints the comparison. Two commands, real Anthropic API calls, the Saved number is what you actually pay.
</p>
</div>
<div class="proof-grid">
<div class="proof-cmd">
<p class="proof-step mono"><span class="proof-step-num">01</span> install with the examples extra</p>
<div class="copy-box" data-copy="pip install 'loopgain[examples]'" role="button" tabindex="0">
<span class="copy-prompt mono">$</span>
<code class="mono">pip install 'loopgain[examples]'</code>
<span class="copy-hint mono">copy</span>
</div>
<p class="proof-step mono"><span class="proof-step-num">02</span> run the stuck-loop demo</p>
<div class="copy-box" data-copy="python examples/05_unsolvable_stalls.py" role="button" tabindex="0">
<span class="copy-prompt mono">$</span>
<code class="mono">python examples/05_unsolvable_stalls.py</code>
<span class="copy-hint mono">copy</span>
</div>
<p class="proof-desc">
Example 05 hands Claude a spec that's <em>impossible by construction</em>, so the error never reaches 0 — the loop makes no progress, attempt after attempt. A fixed <span class="mono code-inline">max_iterations=10</span> keeps paying for all ten, and a naive <span class="mono code-inline">if error == 0: break</span> would never fire. LoopGain reads the flat trajectory as <span class="mono code-inline">STALLING</span> and stops at iter 3 — <strong>70% less spend</strong> on a loop that was never going to win. The chart up top catches a loop going <em>wrong</em>; this catches one going <em>nowhere</em>.
</p>
<p class="proof-foot mono">
<a href="https://github.com/loopgain-ai/loopgain/tree/main/examples" rel="noopener">All seven examples →</a>
<span class="dot-sep"></span>
real Claude loops · verified output across <span class="mono">TARGET_MET · STALLING · DIVERGING</span>
</p>
</div>
<pre class="proof-output mono"><code><span class="tk-com">─── BASELINE: no LoopGain, fixed cap = 10 ───</span>
iter 1 error=1 (forbidden tokens used)
iter 2 error=1 (forbidden tokens used)
…
iter 10 error=1 (no coroutine driver)
→ kept LAST output (error=1).
<span class="tk-com">─── WITH LOOPGAIN: target_error=None, max_iterations=10 ───</span>
iter 1 error=1.00 state=<span class="tk-good">FAST_CONVERGE</span>
iter 2 error=1.00 state=<span class="tk-stall">STALLING</span>
iter 3 error=1.00 state=<span class="tk-stall">STALLING</span> → stop
┌─ COMPARISON ─────────────────────────────────
│ Baseline: 10 iters, error 1, kept LAST
│ LoopGain: 3 iters, error 1, stopped on STALLING
│ Saved: 7 iterations (<span class="tk-good">70%</span>) of API spend
└──────────────────────────────────────────────
outcome: <span class="tk-stall">stalled</span>
</code></pre>
</div>
</section>
<!-- ─── what you get ─────────────────────────────────────────────────── -->
<section class="feat-sec" data-screen-label="features">
<div class="section-head">
<span class="eyebrow mono">§05 · what you get</span>
<h2>A small library that does one thing precisely: replace <span class="mono code-inline">max_iterations</span> with a stop decision.</h2>
</div>
<div class="feat-grid">
<article class="feat">
<div class="feat-num mono">01</div>
<h3>Five-state decision engine</h3>
<p>Four trajectory features per loop, one of five named states out. Two say keep going, three say stop. The library never asks you to interpret a number — the state <em>is</em> the decision.</p>
</article>
<article class="feat">
<div class="feat-num mono">02</div>
<h3>Best-so-far buffer</h3>
<p>Every iteration's output is held in a rolling buffer paired with its error score, with <span class="mono code-inline">argmin(E(n))</span> tracked. On rollback you don't get the latest draft — you get the actually-best one the loop produced.</p>
</article>
<article class="feat">
<div class="feat-num mono">03</div>
<h3>Savings you can see</h3>
<p>Every run reports <span class="mono code-inline">savings_vs_fixed_cap</span> — the exact iterations you didn't burn versus a fixed cap. The dashboard rolls the fleet total up in the Waste Report and prices it at your cost-per-iteration, turning iterations-not-wasted into a dollar figure.</p>
</article>
<article class="feat">
<div class="feat-num mono">04</div>
<h3>Framework adapters</h3>
<p>LangGraph <span class="mono code-inline">.stream()</span> step. CrewAI callback. AutoGen <span class="mono code-inline">run_stream()</span>. LangChain <span class="mono code-inline">create_agent</span> / <span class="mono code-inline">AgentExecutor</span> stream. OpenAI Agents SDK <span class="mono code-inline">stream_events()</span>. Claude Agent SDK <span class="mono code-inline">query()</span>. Raw <span class="mono code-inline">LoopGain</span> class if you have your own runner. All six adapters wrap the same core.</p>
</article>
<article class="feat">
<div class="feat-num mono">05</div>
<h3>Opt-in telemetry</h3>
<p>Off by default. If you turn it on, we receive band transitions and gain readings — never your prompts or outputs. The contract is in the README and the receiver is open-source.</p>
</article>
<article class="feat">
<div class="feat-num mono">06</div>
<h3>Optional adapter installs</h3>
<p>The core wheel has zero runtime deps. Framework adapters are pip extras — <span class="mono code-inline">pip install 'loopgain[langgraph]'</span>, <span class="mono code-inline">[crewai]</span>, <span class="mono code-inline">[autogen]</span>, <span class="mono code-inline">[langchain]</span>, <span class="mono code-inline">[openai-agents]</span>, <span class="mono code-inline">[claude-agent-sdk]</span>, or <span class="mono code-inline">[all]</span>. Your service tree stays clean.</p>
</article>
</div>
</section>
<!-- ─── how it's different (vs observability) ──────────────────────────── -->
<section class="cmp-sec" id="compare" data-screen-label="compare">
<div class="section-head">
<span class="eyebrow mono">§06 · how it's different</span>
<h2>The observability tools watch the loop. LoopGain stops it.</h2>
<p class="section-sub">
LangSmith, Langfuse, Helicone and Phoenix are tracing and evaluation tools — they record what your agent did so you can read it back later. That work is genuinely useful, and LoopGain runs happily alongside any of them. But none of them sits inside the running loop and decides when it's done. That is the gap LoopGain fills: it reads the error trajectory in real time and returns a stop-or-rollback decision while the loop is still running.
</p>
</div>
<div class="cmp-wrap">
<table class="cmp">
<thead>
<tr>
<th scope="col">Tool</th>
<th scope="col">Operates at</th>
<th scope="col">License</th>
<th scope="col">Framework-agnostic</th>
<th scope="col">Real-time loop control</th>
</tr>
</thead>
<tbody>
<tr class="cmp-self">
<th scope="row">LoopGain</th>
<td>the loop — convergence state</td>
<td>Apache-2.0 (OSI)</td>
<td>yes · 6 adapters + raw API</td>
<td class="cmp-yes">stops & rolls back to best-so-far</td>
</tr>
<tr>
<th scope="row">LangSmith</th>
<td>requests & traces</td>
<td>closed · self-host on Enterprise only</td>
<td>SDKs, but LangChain / LangGraph-first</td>
<td class="cmp-no">observe only</td>
</tr>
<tr>
<th scope="row">Langfuse</th>
<td>requests & traces</td>
<td>MIT (OSI)</td>
<td>yes</td>
<td class="cmp-no">observe only</td>
</tr>
<tr>
<th scope="row">Helicone</th>
<td>requests · proxy</td>
<td>Apache-2.0 (OSI)</td>
<td>yes</td>
<td class="cmp-no">observe only</td>
</tr>
<tr>
<th scope="row">Arize Phoenix</th>
<td>requests & traces</td>
<td>Elastic v2 · source-available, not OSI</td>
<td>yes · OpenTelemetry</td>
<td class="cmp-no">observe only</td>
</tr>
<tr>
<th scope="row">Braintrust</th>
<td>offline evals & experiments</td>
<td>closed · proxy is OSS, platform isn't</td>
<td>yes</td>
<td class="cmp-no">observe only</td>
</tr>
<tr class="cmp-baseline">
<th scope="row"><code class="mono code-inline">max_iter=N</code></th>
<td>the loop — fixed cap</td>
<td>—</td>
<td>yes</td>
<td class="cmp-warn">static guess · no measurement</td>
</tr>
</tbody>
</table>
</div>
<p class="cmp-foot mono">A fast-moving category — competitor facts verified June 2026. LoopGain is control, not tracing: run it next to whichever observability tool you already use.</p>
</section>
<!-- ─── dashboard ────────────────────────────────────────────────────── -->
<section class="dash-sec" id="dashboard" data-screen-label="dashboard">
<div class="section-head">
<span class="eyebrow mono">§07 · hosted dashboard</span>
<h2>The same readings, on a screen built for an operations room.</h2>
<p class="section-sub">
Six panels over your real fleet: Overview, Loop Health Map, Convergence, Waste Report, Rollback Log, and Alerts on band transitions. Below is the demo itself — a live replay of the real 2,000-run public benchmark, picked up 1,000 runs before the end. Every figure is the bench tenant's own measured state, accruing as recorded runs land about once a second; nothing is scaled or simulated. Open it and poke at every panel without integrating first.
</p>
</div>
<figure class="dash-shot">
<img
src="/dashboard-demo-1440.png"
srcset="/dashboard-demo-1440.png 1x, /dashboard-demo.png 2x"
width="1440" height="900"
alt="LoopGain hosted dashboard replaying the recorded benchmark run mid-flight: a converged-rate gauge at 93.3%, a measured 94.4% of loop spend eliminated, the benchmark run's hourly arrival pulse, and the latest recorded run's trajectory replaying."
loading="lazy" decoding="async"
/>
</figure>
<div class="dash-cta">
<a class="btn btn-amber" href="https://dashboard.loopgain.ai" rel="noopener">
open the live-replay demo
<svg width="12" height="12" viewBox="0 0 12 12" aria-hidden="true"><path d="M3 9 L9 3 M5 3 H9 V7" stroke="currentColor" stroke-width="1.4" fill="none" stroke-linecap="round"/></svg>
</a>
<span class="dash-cta-note mono">replays recorded benchmark runs — no live inference, no auth, no signup.</span>
</div>
</section>
<!-- ─── pricing ──────────────────────────────────────────────────────── -->
<section class="price-sec" id="pricing" data-screen-label="pricing">
<div class="section-head">
<span class="eyebrow mono">§08 · pricing</span>
<h2>Free to self-host. Free to run solo. Paid when a team shares it.</h2>
<p class="section-sub">
Apache-2.0 across the stack — library, receiver, dashboard. Self-host the whole thing, or use the hosted dashboard free as one person. Team and Enterprise are what you buy when more than one person needs the same view — each going further on retention, alerting, security, and compliance.
</p>
</div>
<div class="price-cards">
<article class="pc">
<header class="pc-head">
<span class="mono pc-tag">tier · 01</span>
<h3>Individual</h3>
<div class="pc-price"><span class="pc-amt mono">Free</span><span class="pc-per mono">forever · Apache-2.0</span></div>
</header>
<ul class="pc-list">
<li>Full <span class="mono">LoopGain</span> library — zero runtime deps</li>
<li>Framework adapters (LangGraph, CrewAI, AutoGen, LangChain, OpenAI Agents SDK, Claude Agent SDK)</li>
<li>Best-so-far buffer + rollback</li>
<li>Per-run savings accounting vs a fixed <span class="mono">max_iterations</span> cap</li>
<li>Self-host telemetry receiver + dashboard</li>
<li><strong>or</strong> the hosted dashboard, free, single-user — 7-day retention</li>
</ul>
<div class="pc-cta">
<div class="copy-box copy-box-sm" data-copy="pip install loopgain" role="button" tabindex="0">
<span class="copy-prompt mono">$</span>
<code class="mono">pip install loopgain</code>
<span class="copy-hint mono">copy</span>
</div>
<a class="btn btn-amber btn-block" href="https://dashboard.loopgain.ai/signup" rel="noopener">sign up — free →</a>
</div>
</article>
<article class="pc pc-mid pc-soon">
<header class="pc-head">
<span class="mono pc-tag">tier · 02</span>
<h3>Team</h3>
<div class="pc-price"><span class="pc-amt mono">$199</span><span class="pc-per mono">/ month · per workspace</span></div>
</header>
<ul class="pc-list">
<li>Everything in Individual</li>
<li>Hosted dashboard for a shared workspace — no infra, no auth, no patches</li>
<li>30-day run history, retained for you</li>
<li>Alerts delivered to Slack, email, or webhooks</li>
<li>Waste Report — fleet-wide dollar ROI for stakeholders; measured from your telemetry when it carries real per-run costs, priced at your cost-per-iteration otherwise</li>
<li>Per-iteration scrubber + share links</li>
<li>Saved threshold profiles — dashboard-managed, applied across your fleet</li>
<li>Read & ingest API</li>
<li>Advanced alert routing</li>
</ul>
<div class="pc-cta">
<a class="btn btn-amber btn-block" href="https://dashboard.loopgain.ai" rel="noopener">try the demo →</a>
<a class="btn btn-ghost btn-block" href="mailto:hello@loopgain.ai?subject=Team-tier%20waitlist" data-capture="team" data-source="pricing">join the waitlist →</a>
<p class="pc-cta-note mono">early teams lock founding pricing</p>
</div>
</article>
<article class="pc">
<header class="pc-head">
<span class="mono pc-tag">tier · 03</span>
<h3>Enterprise</h3>
<div class="pc-price"><span class="pc-amt mono">Custom</span><span class="pc-per mono">SLA · residency · dedicated infra</span></div>
</header>
<ul class="pc-list">
<li>Everything in Team</li>
<li>SSO (SAML / OIDC)</li>
<li>Audit log + security-review support (questionnaires, architecture docs)</li>
<li>Priority support — Slack Connect</li>
<li>99.9% SLA with credits</li>
<li>Custom data residency (US / EU / APAC)</li>
<li>Dedicated infrastructure</li>
<li>Custom retention + 10M+/mo events</li>
<li>Custom MSA + dedicated CSM</li>
</ul>
<div class="pc-cta">
<a class="btn btn-ghost btn-block" href="mailto:hello@loopgain.ai?subject=LoopGain%20Enterprise" data-capture="enterprise" data-source="pricing">talk to us →</a>
</div>
</article>
</div>
</section>
<!-- ─── why it works ─────────────────────────────────────────────────── -->
<section class="why-sec" data-screen-label="why">
<div class="why-grid">
<div class="why-lineage">
<span class="eyebrow mono">§09 · why it works</span>
<div class="why-year mono">
<span class="wy-num">1921</span>
<span class="wy-name">Heinrich Barkhausen</span>
<span class="wy-where">Technische Hochschule Dresden</span>
</div>
<div class="why-arrow mono">↓</div>
<div class="why-year mono why-year-now">
<span class="wy-num">2026</span>
<span class="wy-name">LoopGain v<span data-lg-version-num>0.5.0</span></span>
<span class="wy-where">your agent loop</span>
</div>
</div>
<div class="why-copy">
<h2>One hundred and five years of control theory, finally pointed at the right loop.</h2>
<p>
The Barkhausen criterion is the foundational stability result in feedback engineering. It says: if the loop gain is greater than one, your system oscillates or diverges. If it's less than one, it converges. Every amplifier, every closed-loop electronic system since has had to satisfy it.
</p>
<p>
An LLM agent loop <em>is</em> a feedback loop. Output in, scored output out, fed back as the next input. The math doesn't care that the gain element is a transformer instead of a vacuum tube. Apply Barkhausen and you get the same answer you'd get in any other control system: this loop is stable, this one isn't, stop the unstable one before it costs you anything else.
</p>
</div>
</div>
</section>
</main>
<!-- ─── footer ───────────────────────────────────────────────────────── -->
<footer class="foot" data-screen-label="footer">
<div class="foot-row">
<a class="foot-brand" href="#top" aria-label="LoopGain home">
<img src="/loopgain-lockup-dark-sm.png" alt="LoopGain" class="brand-dark" style="height:20px; width:auto;" />
<img src="/loopgain-lockup-light-sm.png" alt="LoopGain" class="brand-light" style="height:20px; width:auto;" />
</a>
<nav class="foot-links mono" aria-label="footer">
<a href="https://github.com/loopgain-ai" rel="noopener">github</a>
<a href="https://pypi.org/project/loopgain" rel="noopener">pypi</a>
<a href="https://github.com/loopgain-ai/loopgain-plugin" rel="noopener">claude code plugin</a>
<a href="https://dashboard.loopgain.ai" rel="noopener">dashboard</a>
<a href="/blog/">blog</a>
<a href="/privacy">privacy</a>
<a href="/terms">terms</a>
<a href="mailto:hello@loopgain.ai">hello@loopgain.ai</a>
</nav>
</div>
<div class="foot-row foot-news">
<form id="newsletterForm" class="news-form">
<label for="newsEmail" class="news-label">New posts by email</label>
<div class="news-row">
<input id="newsEmail" name="email" type="email" required autocomplete="email" placeholder="you@company.com" class="news-input" />
<input type="text" name="company_website" class="wl-hp" tabindex="-1" autocomplete="off" aria-hidden="true" />
<button type="submit" class="btn btn-amber news-btn">Subscribe</button>
</div>
<div id="newsTurnstile"></div>
<p class="news-status mono" id="newsStatus" role="status" aria-live="polite"></p>
</form>
</div>
<div class="foot-row foot-row-fine mono">
<span>© 2026 LoopGain Technologies Inc. · Apache-2.0</span>
<span class="dot-sep"></span>
<span>Cost control for AI agent loops</span>
</div>
</footer>
<!-- ─── waitlist / contact capture dialog ──────────────────────────────
Progressive enhancement: the CTAs above keep a mailto: fallback; landing.js
intercepts them and opens this dialog instead (POST → loopgain.ai/api/*). -->
<dialog id="waitlistDialog" class="wl-dialog" aria-labelledby="wlTitle">
<form id="waitlistForm" class="wl-form">
<button type="button" class="wl-close" id="wlClose" aria-label="Close">×</button>
<h3 id="wlTitle" class="wl-title">Join the waitlist</h3>
<p id="wlSub" class="wl-sub">Paid plans launch soon. Leave your email and we'll tell you the moment it's live.</p>
<label for="wlEmail" class="wl-label mono">email</label>
<input id="wlEmail" name="email" type="email" required autocomplete="email" placeholder="you@company.com" class="wl-input" />
<!-- Individual (free dashboard access) mints a real credential — needs
explicit terms acceptance + a genuinely separate, optional newsletter
opt-in. Other lists (team/enterprise/pilot) keep the existing
implicit-consent .wl-fine text below, unchanged; this block is
toggled visible only when list === 'individual' (see landing.js). -->
<div id="wlIndividualExtras" hidden>
<label class="wl-checkbox">
<input type="checkbox" id="wlTermsConsent" name="terms_consent" />
<span>I agree to LoopGain's <a href="/terms" target="_blank" rel="noopener">Terms of Service</a> and to be contacted about my account.</span>
</label>
<label class="wl-checkbox">
<input type="checkbox" id="wlNewsletterOptin" name="newsletter_optin" />
<span>Also send me product updates and the newsletter (optional)</span>
</label>
</div>
<!-- honeypot: invisible to people, irresistible to bots -->
<input type="text" name="company_website" class="wl-hp" tabindex="-1" autocomplete="off" aria-hidden="true" />
<div id="wlTurnstile"></div>
<button type="submit" class="btn btn-amber btn-block wl-submit">Join the waitlist</button>
<p class="wl-fine mono" id="wlFine">By joining you agree to be contacted by LoopGain about this. Unsubscribe any time.</p>
<p class="wl-status mono" id="wlStatus" role="status" aria-live="polite"></p>
</form>
</dialog>
<script src="landing.js"></script>
<script src="version-sync.js"></script>
<!-- Turnstile bot-check for the capture forms. Explicit render: landing.js
(loaded synchronously above) defines onloadTurnstileCallback before this
async script runs. interaction-only appearance = invisible unless a
challenge is actually required. -->
<script src="https://challenges.cloudflare.com/turnstile/v0/api.js?onload=onloadTurnstileCallback&render=explicit" async defer></script>
</body>
</html>