Most "AI safety" APIs are classifiers. You send a string, you get back a label and a score. Is this content hate speech? Is this content sexual? Is this content self harm? Probably yes, with confidence 0.87.
That abstraction was fine when the consumer was a content moderation pipeline making a single accept or reject decision on one piece of content. It is the wrong abstraction for the agent era, and continuing to build safety APIs this way leaves a lot of harm unaddressed.
We spent the last few months rebuilding Tuteliq around a different design principle.
Safety should be a platform of composable primitives, not a list of classifiers.
This post is about why, and what that actually looks like in an API.
The TL;DR
- The classifier era is ending: A label and a score answers one isolated question. Agents need primitives they can combine, not verdicts they have to glue together.
- Signals must compose: The same grooming pattern is a different harm for a vulnerable child than for a stable adult. The API should carry that math, not your business logic.
- Risk lives in trajectory: Grooming, coercion and pig butchering scams unfold over dozens of turns. Per message scores miss the shape of the curve.
- Actions must carry over: Detection is worthless if every flag routes to one moderator queue. The right intervention depends on the kind of vulnerability.
The classifier problem
When you treat safety as a classifier, every endpoint answers an isolated question. "Is this content bullying?" The answer is yes or no, with a score. You combine endpoints by hoping their thresholds make sense together.
This breaks in three ways once you put it into an agent loop.
1 | red | Signals do not compose
===
A grooming pattern targeting an emotionally vulnerable child is structurally a different harm from the same pattern targeting an emotionally stable adult.
A classifier returns the same `grooming: 0.91` for both, and your downstream code has to invent the compounding logic. Every customer reinvents the same vulnerability aware risk math, badly, inconsistently, and inside business logic that auditors cannot review.2 | amber | Classification is past tense
===
It tells you *what was already said*, usually framing it as a single moment. But pig butchering scams, grooming, coercive control and radicalization are *processes*. They unfold over dozens of turns.
A classifier that emits a verdict per message gives you a fragmented view of a problem that is only visible in trajectory. You end up with hundreds of "low risk, monitor" verdicts and one "critical, escalate" verdict at turn 30, after the harm is already done.3 | violet | The actions do not carry over
===
Even when classifiers produce useful signal, the next step, *what an agent should do about it*, is unspecified.
"Send to moderator" is the default fallback, which means your safety API becomes a backlog generator. The actual interventions (gentle outreach, transaction holds, guardian notifications, evidence packets for law enforcement) get hand coded by every customer.The fix to all three is not to add more classifiers. It is to expose the primitives those classifiers compute internally and let agents compose them.
What "primitives" looks like in practice
When we redesigned Tuteliq, we made three structural changes.
1. Detection state survives across calls
Every conversation level endpoint (detect_grooming, detect_coercive_control, detect_distress_signals) returns a continuation_token. The token carries trajectory state across separate API calls: accumulated severity, tactic counts, and the rising, falling or none trajectory marker.
Your agent no longer has to compute the trajectory itself. You stream messages in as they arrive and pass the token forward. The token is the state: you hold it, you send it, and Tuteliq stores nothing between calls. Submitted turns are held in memory for the duration of inference and deleted immediately afterwards, so the conversation history stays yours.
------ · ------------------ · ------------------------
Classifier mode · "Message 17 has secrecy_request, score 0.6." · You do. You decide what it means.
Primitive mode · "Across 17 messages: flattery 0.5, then secrecy_request 0.6, then isolation 0.7. Trajectory: rising. Verdict: high. Here is the token." · The engine does. You decide what to do.
The trajectory itself is the diagnostic. We learned this from real conversation logs: the shape of the curve mattered more than any single message score.
2. Endpoints carry composition modifiers
detect_vulnerability_exploitation returns a field called cross_endpoint_modifier. It is a numeric multiplier (typical range 1.0 to 2.0) that other detectors' scores should be amplified by when both endpoints fire on the same content.
{
"endpoint": "vulnerability-exploitation",
"detected": true,
"risk_score": 0.85,
"cross_endpoint_modifier": 1.6,
"vulnerability_profile": {
"emotional_vulnerability": 0.8,
"cognitive_vulnerability": 0.0,
"situational_vulnerability": 0.0,
"social_vulnerability": 0.0
}
}In agent code:
const [scam, vuln] = await Promise.all([
tuteliq.fraud.romanceScam({ content }),
tuteliq.safety.vulnerabilityExploitation({ content }),
]);
const compound = Math.min(scam.risk_score * vuln.cross_endpoint_modifier, 1.0);The math is trivial. The interesting thing is that it does not live inside an opaque adjustment buried in the romance scam score. It is an explicit primitive the agent applies, which makes it:
- Auditable. A regulator can verify the math.
- Tunable. You can adjust the multiplier in your own deployment.
- Recombinable. An agent can decide which sibling endpoint to amplify based on what else fired.
3. Profiles route to different interventions
The vulnerability_profile has four dimensions, each scored 0.0 to 1.0. The right intervention depends on which dimension is dominant.
-------------------- · ----------------- · ----------------------
emotional_vulnerability · Someone in crisis · Slower, gentler human review. Abrupt enforcement makes things worse.
situational_vulnerability · Financial stress, romance scam target · Immediate transaction hold.
cognitive_vulnerability · Elder fraud, capacity issues · Guardian notification.
social_vulnerability · Isolated, no support network · Support network outreach.
const dims = vuln.vulnerability_profile;
const route =
dims.emotional_vulnerability > 0.7 ? "gentle_human_review" :
dims.situational_vulnerability > 0.7 ? "transaction_hold" :
dims.cognitive_vulnerability > 0.7 ? "guardian_notify" :
dims.social_vulnerability > 0.7 ? "support_network_outreach":
"standard_moderation";That is not what a classifier API can do. It is what a primitive API enables.
The "agent at the moderation layer" thesis
The implicit bet here is that the next decade of safety will not be built on bigger classification models. It will be built on agents at the moderation layer that compose primitives, take graduated actions, and produce auditable decisions.
The unit of work is no longer "classify this message." It is "manage this conversation's trajectory, decide what to do, do it, record why." Doing that well requires primitives that:
- expose composition fields (
cross_endpoint_modifier) - decompose vulnerability into routable dimensions (
vulnerability_profile) - maintain state across calls (
continuation_token) - surface intervention ready evidence (per tactic flags with quoted text)
- emit signed audit receipts (Article 12 chain of custody plumbing)
Any of those on its own is a feature. Together they are a different category of product. We call it a safety platform internally to distinguish it from a safety API.
Why this took us a while to get to
The honest reason it took rebuilding Tuteliq twice: we were copying the wrong template. Content moderation APIs from the 2010s were designed for a job (filter a comment feed) that was meaningfully different from "guide an agent's moderation decisions in real time."
When you copy that template into the agent era, you get a tool that technically works but produces the wrong abstraction at the integration layer. We had to look at six months of customer integration code, the work our customers were writing around our API, to see the pattern. Everyone was reinventing the same trajectory tracking, the same compound risk math, the same vulnerability routing. That work belongs inside the API, not on top of it.
What you can do with this today
- Find the composition fields: Most vendors do not expose them. The ones that do bury them. Send a romance scam targeting a clearly vulnerable user and look at the
cross_endpoint_modifiervalue. - Stop scoring per message: Use a
continuation_tokenso the engine carries trajectory state. Your agent's job becomes deciding what to do when the trajectory shifts, not recomputing it. - Stop routing everything to one queue: Use the vulnerability profile to fork to the right intervention. Half of the false positive perception in moderation comes from one overloaded queue.
We will have more to say about the agentic moderation pattern in upcoming posts, including how we use the continuation_token to detect escalation patterns across grooming, coercion and pig butchering scams, which behave more like multi act narratives than single message classifications.
In the meantime: do not ship your safety API as a classifier. Ship it as a platform.
---
If you are integrating safety into an agent stack and want to see how this composes in practice, the composability concepts page has worked examples in TypeScript and Python.