Streamlit Multi-Agent Research UI 2026
Build a Streamlit UI for multi-agent research workflows. Agents search, extract, and synthesize. UI shows progress, sources, and lets users steer.
A Streamlit frontend paired with a LangGraph multi-agent backend creates a research UI where specialized agents handle web search, Reddit analysis, and synthesis. The user enters a research question, agents fan out to gather data from multiple sources, and the UI shows each agent's progress, raw search results, and the final synthesized answer with citations.
Architecture Overview
- Streamlit frontend: input form, agent status panel, search results panel, synthesis output
- LangGraph backend: orchestrates 3 agents (web researcher, Reddit analyst, synthesizer) as a state graph
- Search grounding: web researcher and Reddit analyst each call the search API with different query strategies
- State: LangGraph manages shared state between agents; Streamlit renders state updates via session_state
LangGraph Agent Graph
from langgraph.graph import StateGraph, END
from typing import TypedDict
import requests, os
API_KEY = os.environ["SCAVIO_API_KEY"]
class ResearchState(TypedDict):
question: str
web_results: list
reddit_results: list
synthesis: str
status: str
def web_researcher(state: ResearchState) -> dict:
resp = requests.post(
"https://api.scavio.dev/api/v1/search",
headers={"x-api-key": API_KEY},
json={"query": state["question"], "num_results": 10},
timeout=15,
)
results = resp.json().get("results", [])
return {
"web_results": [{"title": r["title"], "url": r["url"],
"snippet": r.get("snippet", "")} for r in results],
"status": "web_search_complete",
}
def reddit_analyst(state: ResearchState) -> dict:
query = f'{state["question"]} site:reddit.com'
resp = requests.post(
"https://api.scavio.dev/api/v1/search",
headers={"x-api-key": API_KEY},
json={"query": query, "num_results": 10},
timeout=15,
)
results = resp.json().get("results", [])
return {
"reddit_results": [{"title": r["title"], "url": r["url"],
"snippet": r.get("snippet", "")} for r in results],
"status": "reddit_analysis_complete",
}Synthesizer Agent
from langchain_anthropic import ChatAnthropic
llm = ChatAnthropic(model="claude-sonnet-4-20250514")
def synthesizer(state: ResearchState) -> dict:
web = state.get("web_results", [])
reddit = state.get("reddit_results", [])
sources = []
for i, r in enumerate(web + reddit, 1):
sources.append(f"[{i}] {r['title']}\n{r['snippet']}\nURL: {r['url']}")
context = "\n\n".join(sources)
prompt = f"""Synthesize an answer to this question using the sources below.
Cite sources inline using [1], [2], etc. Be specific and factual.
Question: {state['question']}
Sources:
{context}"""
response = llm.invoke(prompt)
return {"synthesis": response.content, "status": "complete"}
# Build the graph
graph = StateGraph(ResearchState)
graph.add_node("web_researcher", web_researcher)
graph.add_node("reddit_analyst", reddit_analyst)
graph.add_node("synthesizer", synthesizer)
graph.set_entry_point("web_researcher")
graph.add_edge("web_researcher", "reddit_analyst")
graph.add_edge("reddit_analyst", "synthesizer")
graph.add_edge("synthesizer", END)
research_chain = graph.compile()Streamlit Frontend
import streamlit as st
st.set_page_config(page_title="Research Agent", layout="wide")
st.title("Multi-Agent Research")
question = st.text_input("Research question")
if st.button("Research") and question:
col1, col2 = st.columns(2)
with st.spinner("Agents working..."):
# Run the LangGraph chain
result = research_chain.invoke({
"question": question,
"web_results": [],
"reddit_results": [],
"synthesis": "",
"status": "started",
})
with col1:
st.subheader("Web Results")
for r in result["web_results"]:
st.markdown(f"**{r['title']}**")
st.caption(r["url"])
st.write(r["snippet"])
st.divider()
st.subheader("Reddit Discussions")
for r in result["reddit_results"]:
st.markdown(f"**{r['title']}**")
st.caption(r["url"])
st.write(r["snippet"])
st.divider()
with col2:
st.subheader("Synthesis")
st.markdown(result["synthesis"])Adding Real-Time Status Updates
# Use Streamlit's session_state + callbacks for live status
if st.button("Research") and question:
status_placeholder = st.empty()
progress = st.progress(0)
# Step 1: Web search
status_placeholder.info("Searching the web...")
progress.progress(25)
web_state = web_researcher({"question": question})
# Step 2: Reddit search
status_placeholder.info("Analyzing Reddit discussions...")
progress.progress(50)
reddit_state = reddit_analyst({"question": question})
# Step 3: Synthesis
status_placeholder.info("Synthesizing findings...")
progress.progress(75)
full_state = {
"question": question,
**web_state,
**reddit_state,
"synthesis": "",
"status": "",
}
final = synthesizer(full_state)
progress.progress(100)
status_placeholder.success("Research complete")
st.markdown(final["synthesis"])Cost per Research Query
- Web search: 1 credit ($0.005)
- Reddit search: 1 credit ($0.005)
- LLM synthesis (Claude Sonnet): ~$0.01-0.03 depending on context size
- Total per query: ~$0.02-0.04
- 100 research queries/day: $2-4/day = $60-120/mo
Extending the System
Add more specialized agents without changing the UI: a TikTok trends agent for consumer research, a patent search agent for technical research, a news-only agent with date filtering. Each agent is a LangGraph node that calls the search API with a different query strategy. The synthesizer agent handles combining results regardless of how many source agents contributed. The Streamlit layout scales horizontally by adding columns or tabs for each agent's output.