{"slug": "stop-blocking-virtual-threads-building-asynchronous-human-in-the-loop-ai-agents", "title": "Stop Blocking Virtual Threads: Building Asynchronous Human-in-the-Loop AI Agents with Spring AI", "summary": "A developer has built an asynchronous human-in-the-loop AI agent system using Spring AI that avoids blocking virtual threads during approval workflows. The approach serializes the agent's ReAct loop state—including token history, tool call IDs, and pending variables—to a Redis-backed persistent store, terminates the active thread immediately, and hydrates a new agent instance when a human decision webhook fires. The implementation uses a custom `ChatMemory` adapter that supports snapshotting at specific message indices and a resume endpoint that injects the human's decision as a `ToolResponseMessage` to continue the ReAct loop.", "body_md": "In 2026, letting autonomous AI agents execute high-risk enterprise tools without human oversight is a production liability, but blocking platform threads—or even Project Loom’s virtual threads—for hours waiting for a manager's Slack approval is absolute architectural malpractice. We must transition from synchronous execution loops to stateless, event-driven agent hydration where the LLM's reasoning state is serialized and persisted during human-in-the-loop (HITL) interrupts.\n\n`VirtualThreadExecutor`\n\n) solve the wait problem—they do not; holding resources open for a 4-hour human coffee break destroys system scalability and ruins connection pools.`ChatMemory`\n\nor agent context) in local heap memory, making your system highly vulnerable to redeployments and node failures.`CompletableFuture`\n\nor busy-waiting database polling loops to check if a human has clicked \"Approve\" on an external UI.The clean solution is to serialize the agent's execution state—the ReAct loop token history, tool call IDs, and pending variables—to a persistent store, terminate the active thread immediately, and hydrate a brand-new agent instance when the approval webhook fires.\n\n`AgentSuspensionException`\n\ncontaining the serialized `stateId`\n\nand tool execution metadata when a high-risk tool is triggered.`ChatClient`\n\nwith a custom Redis-backed `ChatMemory`\n\nimplementation that supports snapshotting at specific message indices.`/api/v1/agent/resume`\n\nthat accepts the human decision, merges it into the serialized history as a `ToolResponseMessage`\n\n, and triggers the next step of the ReAct loop.\n\n```\n@PostMapping(\"/agent/resume\")\npublic ResponseEntity<String> resumeAgent(@RequestBody ApprovalResponse approval) {\n    // 1. Retrieve serialized chat history (ReAct state) from Redis\n    List<Message> history = stateRepository.findById(approval.stateId());\n\n    // 2. Inject the human's decision as if it were the tool's output\n    String toolOutput = approval.approved() ? \"Approved: \" + approval.notes() : \"Rejected by human\";\n    history.add(new ToolResponseMessage(approval.toolCallId(), toolOutput));\n\n    // 3. Hydrate the agent and resume execution without blocking threads\n    ChatResponse response = chatClient.prompt()\n        .messages(history)\n        .call()\n        .chatResponse();\n\n    return ResponseEntity.ok(response.getResult().getOutput().getContent());\n}\n```\n\n`ChatMemory`\n\nadapters to dynamically hydrate and dehydrate context windows on demand.\n\nHeads up:if you want to see these patterns applied to real interview problems,[javalld.com]has full machine coding solutions with traces.", "url": "https://wpnews.pro/news/stop-blocking-virtual-threads-building-asynchronous-human-in-the-loop-ai-agents", "canonical_source": "https://dev.to/machinecodingmaster/stop-blocking-virtual-threads-building-asynchronous-human-in-the-loop-ai-agents-with-spring-ai-49pp", "published_at": "2026-06-04 07:08:47+00:00", "updated_at": "2026-06-04 07:11:49.939386+00:00", "lang": "en", "topics": ["ai-agents", "ai-infrastructure", "ai-safety", "large-language-models", "mlops"], "entities": ["Project Loom", "Spring AI", "Slack", "Redis", "ReAct", "ChatMemory", "ChatClient", "VirtualThreadExecutor"], "alternates": {"html": "https://wpnews.pro/news/stop-blocking-virtual-threads-building-asynchronous-human-in-the-loop-ai-agents", "markdown": "https://wpnews.pro/news/stop-blocking-virtual-threads-building-asynchronous-human-in-the-loop-ai-agents.md", "text": "https://wpnews.pro/news/stop-blocking-virtual-threads-building-asynchronous-human-in-the-loop-ai-agents.txt", "jsonld": "https://wpnews.pro/news/stop-blocking-virtual-threads-building-asynchronous-human-in-the-loop-ai-agents.jsonld"}}