ResearchBot
Bilingual Intelligence Engine & Real-Time Adaptive Streaming Monitor
01. Context & Specifications
ResearchBot acts as a real-time web monitoring and compilation platform. It integrates Tavily API for advanced deep web crawling and Groq/OpenAI for lightning-fast synthesis. The application is natively bilingual (EN/FR) by default, using an ephemeral localization dictionary. The core challenge was synchronizing the raw async generator speed with the browser's 60Hz/120Hz rendering loop. ResearchBot fixes this by pushing incoming chunks into a queue and shifting them out using a 12ms adaptive heartbeat window.
02. Architecture Pipeline
Unit modeling of the processing and isolation cycle of business data for this project:
[User Prompt EN/FR] ──> [Next.js Server Action Context]
│
[Tavily Search API] <── (Multi-Agent Research Routing)
│
(Structured Markdown Compilation via Groq / Agent Service)
│
(Server-Sent Streamable Value: createStreamableValue)
│
[Client readStreamableValue] ──> (Adaptive Token Buffer Queue)
│
[Supabase RLS Table (Anon role)] <── [12ms Render Loop / Adaptive Chunking]
│ │
[Persisted Reports History] [Smooth Bilingual UI Render]03. Adaptive Chunking Buffer & Low-Latency RLS Persistence
To bypass browser rendering lockups, the interface never binds the raw stream state directly to the component. Instead, tokens fill a local array buffer. A single specialized setInterval monitors this buffer: if the agent writes faster than the screen can refresh, the chunk sampling size scales up automatically (from 2 to 20 characters per tick). Once the data stream ends, the final payload is committed to Supabase using a machine-id hardware footprint, bypassing traditional authentication requirements via strict Row-Level Security rules.
// Client-side adaptive buffer injection mechanism
speedControlIntervalRef.current = setInterval(() => {
if (buffer.length > 0) {
// Dynamic chunk adjustment to prevent UI lag on high-velocity tokens
let chunkSize = 2;
if (buffer.length > 500) chunkSize = 20;
else if (buffer.length > 100) chunkSize = 8;
else if (buffer.length > 30) chunkSize = 4;
const toAdd = buffer.slice(0, chunkSize);
buffer = buffer.slice(chunkSize);
setReport(prev => prev + toAdd);
} else if (isStreamFinished) {
clearInterval(speedControlIntervalRef.current!);
setIsLoading(false);
}
}, 12);04. Retrospective & Limitations
>_ Post-deployment engineering notes:Enforcing clean semantic markdown delivery by extracting raw status chains from the LLM pipeline brought parsing accuracy back to 100%. The system perfectly scales to accommodate international queries while preserving the output language. The next expansion phase will focus on embedding local vector stores to allow cross-referencing past reports within the active search window.