rd-signal-2: Frontier Classification at Production Scale – Raindrop Blog<br>Log inGet started
rd-signal-2: Frontier Classification at Production Scale
Ben Hylak, Manav Shah and Ryan D'Onofrio<br>Aug 11, 2026 · 7 min
Today we're launching Signals 2.0, powered by rd-signal-2, our new model pipeline for building task-specific binary classifiers from production traces.<br>rd-signal-2 approaches GPT-5.6 Sol xhigh accuracy while costing 1600x less, and 260x less than GPT-5.6 Luna xhigh .<br>This model is available to all Raindrop customers today (at no additional cost).<br>We are also releasing Signal Builder: a platform for training and hosting custom classifiers with Zero Data Retention. Signal Builder brings the power of Signals to the strictest of enviroments (including healthcare).<br>Precision<br>Claude Sonnet 5 adaptive78%
Raindrop71%
GPT-5.6 Sol xhigh71%
GPT-5.6 Luna xhigh67%
Recall<br>GPT-5.6 Sol xhigh91%
Raindrop87%
GPT-5.6 Luna xhigh83%
Claude Sonnet 5 adaptive75%
Relative cost<br>linear scale, per classified trace<br>Raindrop1×
GPT-5.6 Luna xhigh260×
GPT-5.6 Sol xhigh1,600×
Claude Sonnet 5 adaptive1,650×
Everything is a binary classification problem<br>Last year, OpenAI published a paper called "Why Language Models Hallucinate". Hallucination, they said, is simply a binary classification problem: any statement is either true or not.<br>The LinkedIn headlines were immediately victorious: "OpenAI solved hallucination."<br>And, if being binary made classification easy, they would have. But, as it would turn out, everything in life is just binary classification too. You either should or should not get married; the UI is either good or not.<br>...and a given agent behavior is either good or bad.<br>Binary classification is a hard alignment problem. You need to align the humans within a company on the definition of good/bad, and then explore all of the edge cases, and then - and only then - you must align a model or pipeline to that definition.<br>Raindrop covers the entire journey of training accurate classifiers for agent behavior.<br>Classifiers for Agents<br>In June 2025, we launched the first version of Signals: an automated pipeline for training tiny classification models.<br>At the time, our classifiers evaluated a single input-and-output pair for a given behavior. That worked for the chatbot era, when the relevant evidence was contained within a single turn. Our competitors still have that limitation.<br>But agent failures now unfold across multiple turns, tool calls, and subagents, sometimes spanning hundreds of thousands of tokens. These failures are often nuanced and sparse. Finding them requires both deterministic filtering to assemble the relevant evidence and semantic judgment to interpret it.<br>Running a frontier model over every trace is the obvious solution, but, among other problems, it becomes prohibitively expensive and slow at production scale.<br>Smaller models are affordable, but they struggle with complex behaviors and are often limited by how much context they can consume.<br>rd-signal-2 solves this through an automated research loop. For each behavior, it studies production traces, writes code to assemble the context that matters, and uses that context to train a task-specific model.<br>How rd-signal-2 builds Signals<br>When an agent produces this trace:<br>Agent trace4 events · 90.2s<br>1update_record({ id: 42, status: "resolved" })timed out · 30.0s
2update_record({ id: 42, status: "resolved" })timed out · 30.0s
3update_record({ id: 42, status: "resolved" })timed out · 30.1s
Final response“The record has been successfully updated.”
rd-signal-2 · claims success after repeated failuresMatched<br>The failure is not contained in any single step. It is the relationship between the repeated tool failures and the assistant's final response. rd-signal-2 can express that relationship:<br>export function run(event) {<br>1 const attempts = findToolCalls(event.trace, "update_record");
2 const repeatedWithoutChange =<br>attempts.length >= 3 &&<br>haveIdenticalInputs(attempts) &&<br>attempts.every(call => call.failed);
3 if (!repeatedWithoutChange) {<br>return { matched: false };
4 return classify(<br>formatForReview(attempts, event.finalResponse),<br>"The assistant claims the operation succeeded",<br>{ spanIds: attempts.flatMap(call => call.spanIds) }<br>);
1Gather every update_record call in the trace.2At least three tool calls, all with identical inputs, and all failed.3Anything else is a non-match (early-exit)4Now, the task is reduced to a simpler question: did the assistant claim the operation succeeded?
The classify(...) function abstracts the model training pipeline and automatically self-improves.
The code finds the relevant tool calls, compares their inputs, and checks whether they failed. If those conditions are not met, the Signal returns a non-match without calling a model.<br>If they are met, the Signal extracts the failed attempts and the assistant's final response from the trace. The remaining semantic judgment goes to a combination of a "task-specific classification...