🪟 Windows TippsGrok for PC: Using xAI’s Chat Assistant On a Bigger Screen(16.09.2026 um 09:33 Uhr)
🪟 Windows TippsWindows 11 26H2: Release, Neuerungen und wer jetzt handeln muss(16.09.2026 um 09:37 Uhr)
🪟 Windows TippsGoogle Chrome(16.09.2026 um 08:30 Uhr)
🪟 Windows TippsGoogle stopft mehrere kritische Chrome-Lücken(16.09.2026 um 09:24 Uhr)
🪟 Windows TippsKI-Power für eine klare Sprache(16.09.2026 um 08:45 Uhr)
🤖 Android TippsDas Ende einer Ära: Samsung-Nutzer müssen sich umstellen(16.09.2026 um 08:25 Uhr)
🪟 Windows TippsGrok for PC: Using xAI’s Chat Assistant On a Bigger Screen(16.09.2026 um 09:33 Uhr)
🪟 Windows TippsWindows 11 26H2: Release, Neuerungen und wer jetzt handeln muss(16.09.2026 um 09:37 Uhr)
🪟 Windows TippsGoogle Chrome(16.09.2026 um 08:30 Uhr)
🪟 Windows TippsGoogle stopft mehrere kritische Chrome-Lücken(16.09.2026 um 09:24 Uhr)
🪟 Windows TippsKI-Power für eine klare Sprache(16.09.2026 um 08:45 Uhr)
🤖 Android TippsDas Ende einer Ära: Samsung-Nutzer müssen sich umstellen(16.09.2026 um 08:25 Uhr)

🔧 Programmierung 🕛 vor 1 Monat 6 Min Lesezeit
0

Production Interceptors for Solon ReActAgent: Stop Loops, Retry Tools, Sanitize Observations, Stream Events

↗ Quelle (dev.to)
🗣️ Stimme:
📑 Inhaltsübersicht

Demo agents usually work once. Production agents fail in boring, expensive ways: they loop on the same tool call, they retry forever against a flaky API, and they paste a 40KB JSON blob back into the next thought.



Solon AI already ships a small set of built-in ReAct interceptors for those failure modes. Pair them with stream() event chunks, and you get both guardrails and live UI feedback without inventing a custom agent runtime.



This article sticks to official Solon v4.0.3 APIs from the Agent docs.






What “production shape” means here



Not a bigger prompt. Not a fake harness wrapper. Three concrete controls:

































Guardrail Built-in interceptor Job
Stop thrashing StopLoopInterceptor Break A-B-A-B tool loops
Survive flaky tools ToolRetryInterceptor Physical retry + self-heal feedback
Keep context clean ToolSanitizerInterceptor Truncate / desensitize observations
Show progress stream() Event chunks for thought / action / final answer


HITL and context compression are part of the same family, but they already have their own deep-dives. Today we assemble the resilience trio and wire a stream UI.






1. Domain tools stay boring and real



Same pattern as the official after-sales sample: AbsToolProvider + @ToolMapping. No implements Tool.




CODE
import org.noear.solon.ai.annotation.ToolMapping;
import org.noear.solon.ai.chat.tool.AbsToolProvider;
import org.noear.solon.annotation.Param;

public class OpsTools extends AbsToolProvider {

@ToolMapping(description = "Query order status by order id")
public String get_order(@Param(description = "Order id") String orderId) {
// Simulate occasional transport noise
if (Math.random() < 0.3) {
throw new RuntimeException("upstream timeout");
}
return "{\"orderId\":\"" + orderId + "\",\"status\":\"SHIPPED\",\"sku\":\"keyboard\"}";
}

@ToolMapping(description = "Fetch raw logistics payload (can be large)")
public String get_track(@Param(description = "Tracking number") String trackNo) {
StringBuilder sb = new StringBuilder("{\"trackNo\":\"" + trackNo + "\",\"events\":[");
for (int i = 0; i < 200; i++) {
if (i > 0) sb.append(',');
sb.append("{\"ts\":").append(i).append(",\"msg\":\"hub-scan-").append(i).append("\"}");
}
sb.append("]}");
return sb.toString();
}
}









2. Mount the resilience trio



All five built-in interceptors live under org.noear.solon.ai.agent.react.intercept. For a default production baseline, start with these three:




CODE
import org.noear.solon.ai.agent.react.ReActAgent;
import org.noear.solon.ai.agent.react.intercept.StopLoopInterceptor;
import org.noear.solon.ai.agent.react.intercept.ToolRetryInterceptor;
import org.noear.solon.ai.agent.react.intercept.ToolSanitizerInterceptor;
import org.noear.solon.ai.chat.ChatModel;

ReActAgent agent = ReActAgent.of(chatModel)
.name("ops_agent")
.role("Operations assistant for order and logistics lookup")
.defaultToolAdd(new OpsTools())
.maxTurns(10)
.autoRethink(true)
// 1) break repeated action thrashing in a sliding window
.defaultInterceptorAdd(new StopLoopInterceptor(2, 6))
// 2) retry flaky tool calls with linear backoff
.defaultInterceptorAdd(new ToolRetryInterceptor(3, 1000L))
// 3) truncate / clean oversized observations before they poison context
.defaultInterceptorAdd(new ToolSanitizerInterceptor(2000))
.modelOptions(o -> o.temperature(0.1))
.build();









What each constructor means




























Class Constructor Meaning
StopLoopInterceptor (maxRepeatCount, windowSize) In the last windowSize actions, the same action may appear at most maxRepeatCount times
ToolRetryInterceptor (maxRetries, retryDelayMs) Physical linear-backoff retries on tool failures; also supports logical self-heal feedback
ToolSanitizerInterceptor (maxObservationLength) Observation-stage truncate / denoise; optional custom Function<ToolResult, ToolResult>


Default no-arg constructors exist for all three if you want the built-in defaults first.






3. Optional: custom sanitizer for secrets



When tools return tokens, phones, or internal IDs, pass a sanitizer:




CODE
import org.noear.solon.ai.chat.tool.ToolResult;

ToolSanitizerInterceptor sanitizer = new ToolSanitizerInterceptor(
1500,
result -> {
// Keep structure, scrub obvious secrets before Observation is stored
String cleaned = String.valueOf(result)
.replaceAll("(?i)token\\s*[:=]\\s*\\S+", "token=***")
.replaceAll("1\\d{10}", "1**********");
// Prefer returning a ToolResult produced by your project helper
// if you have one; otherwise keep max-length truncation only.
return result;
}
);

agent = ReActAgent.of(chatModel)
.defaultToolAdd(new OpsTools())
.defaultInterceptorAdd(new StopLoopInterceptor())
.defaultInterceptorAdd(new ToolRetryInterceptor())
.defaultInterceptorAdd(sanitizer)
.build();






In practice, many teams start with length truncation only, then add a project-specific redaction function once real payloads are known.






4. Prefer stream() for interactive products



call() is perfect for jobs and batch flows. Chat UIs want event stream, not a single final string.



Official docs are explicit: stream is an event stream, not a token-only data stream. For ReActAgent the common sequence is:




ReasonChunkThoughtChunkActionChunkObservationChunk → … → ReActChunk





CODE
import org.noear.solon.ai.agent.AgentChunk;
import org.noear.solon.ai.agent.react.chunk.ActionChunk;
import org.noear.solon.ai.agent.react.chunk.ObservationChunk;
import org.noear.solon.ai.agent.react.chunk.ReActChunk;
import org.noear.solon.ai.agent.react.chunk.ReasonChunk;
import org.noear.solon.ai.agent.react.chunk.ThoughtChunk;
import org.noear.solon.ai.agent.session.InMemoryAgentSession;
import reactor.core.publisher.Flux;

InMemoryAgentSession session = InMemoryAgentSession.of("ops_job_001");

Flux<AgentChunk> chunks = agent.prompt("Order ORD_1001 looks stuck. Check status and track.")
.session(session)
.stream();

chunks.doOnNext(chunk -> {
if (chunk instanceof ReasonChunk) {
ui.appendThinking(chunk.getContent()); // gray "thinking" text
} else if (chunk instanceof ThoughtChunk) {
ui.appendThought(chunk.getContent()); // aggregated thought
} else if (chunk instanceof ActionChunk) {
ui.showToolRunning(chunk.getContent()); // tool started / args summary
} else if (chunk instanceof ObservationChunk) {
ui.showToolDone(); // tool finished
} else if (chunk instanceof ReActChunk) {
ui.appendFinalAnswer(chunk.getContent()); // final bubble
}
})
.doOnError(err -> ui.showError(err.getMessage()))
.blockLast();









Chunk cheat sheet





















































Agent Chunk Use in UI
ReActAgent ReasonChunk Streaming reasoning process
ReActAgent ThoughtChunk Aggregated thought
ReActAgent ActionChunk Tool is about to run / running
ReActAgent ObservationChunk Tool result observed
ReActAgent PlanChunk Planning-mode plan text
ReActAgent ContextSizeChunk Context size notice (v4.0.0+)
ReActAgent ReActChunk Final answer aggregation
Any
getAgentName / getSession / getMeta
Routing + observability


call() throws. stream() surfaces errors through Reactor onError. Keep both paths intentional.






5. Sync path still matters



Background workers should stay simple:




CODE
String answer = agent.prompt("Order ORD_1001 looks stuck. Check status and track.")
.session(session)
.call()
.getContent();






Same interceptors apply. The difference is only delivery: one final AgentResponse vs a live Flux<AgentChunk>.






6. A practical default stack



For most business ReAct agents, this is a sane baseline:




CODE
ReActAgent productionAgent = ReActAgent.of(chatModel)
.name("biz_agent")
.role("Business operations agent")
.defaultToolAdd(orderTools)
.defaultToolAdd(logisticsTools)
.maxTurns(10)
.autoRethink(true)
.sessionWindowSize(8)
.defaultInterceptorAdd(new StopLoopInterceptor(2, 6))
.defaultInterceptorAdd(new ToolRetryInterceptor(3, 1000L))
.defaultInterceptorAdd(new ToolSanitizerInterceptor(2000))
// add when money / irreversible actions exist:
// .defaultInterceptorAdd(new HITLInterceptor(...))
// add when long multi-turn jobs bloat trace history:
// .defaultInterceptorAdd(new ContextCompressionInterceptor(...))
.modelOptions(o -> o.temperature(0.1))
.build();









Why this order





  1. StopLoop protects against bad reasoning loops.


  2. ToolRetry absorbs transient infrastructure failures before the model overreacts.


  3. ToolSanitizer keeps Observation payloads short and safer for the next Reason step.


  4. HITL (optional) pauses irreversible tools.


  5. Context compression (optional) keeps long sessions alive.






7. What not to reinvent
































Temptation Prefer instead
Custom loop-breaker prompt only StopLoopInterceptor
Hand-rolled sleep/retry around every tool ToolRetryInterceptor
Dumping full HTTP bodies into chat history ToolSanitizerInterceptor
Polling a black-box job for “thinking…”
stream() + chunk instanceof
Business agent wrapped in fake harness APIs
ReActAgent + interceptors + tools





Official anchors




  • Built-in interceptors (5):

  • call vs stream: ,

  • Package: org.noear.solon.ai.agent.react.intercept






Closing



A production agent is not “the same demo with a better model.” It is a loop that can stop thrashing, retry safely, sanitize observations, and stream progress to the user.



In Solon, those pieces are already interceptors and event chunks. Mount them once on ReActAgent, keep tools as AbsToolProvider, and ship the boring reliability work instead of re-deriving it in prompts.

Vollständiger Original-Artikel
Den kompletten Beitrag mit allen Details direkt auf dev.to lesen.
↗ Original-Artikel auf dev.to lesen
Wie bewertest du diesen Beitrag?
1 Klick Feedback
Teilen mit Netzwerk & Team:

Community-Analysen & Experten-Meinungen 0

Verfasse deine eigene Analyse, teile Workarounds oder diskutiere diesen Vorfall im Blog.
Noch keine Community-Analyse verfasst. Markiere einen Textabschnitt oder klicke oben auf Eigene Analyse verfassen“!
Community Pulse: Relevanz-Einschätzung
1 Klick Experten-Votum
🔴 Akute Relevanz 0%
🟡 In Evaluierung 0%
🟢 Keine Auswirkung 0%
Spannende Innovation 0%
Verwandte Story-Cluster & Quellen (Vektor-KI)
Port 8095 Engine
1 Quelle
Grok for PC: Using xAI’s Chat Assistant On a Bigger Screen
1 Quelle
Windows 11 26H2: Release, Neuerungen und wer jetzt handeln muss
1 Quelle
WMF-Messerblock mit 7 Teilen kostet bei Amazon aktuell deutlich weniger
Ähnliche Beiträge
🔍 Verwandte News

Auch interessante Nachrichten Production Interceptors for Solon ReActAgent: Stop Loops, Retry Tools, Sanitize Observations, Stream Events

Thematisch verwandte Begriffe: Production, Interceptors, Solon, ReActAgent · 6 Treffer

Laden...

Videos werden geladen ...

Laden...

Beiträge werden geladen ...

Laden...

Videos werden geladen ...

Laden...

Beiträge werden geladen ...

Laden...

Videos werden geladen ...

Laden...

Beiträge werden geladen ...

Laden...

Videos werden geladen ...

Laden...

Beiträge werden geladen ...

Laden...

Videos werden geladen ...