An autonomous AI agent that researches any topic end-to-end.
Give it a question. It searches the web, reads the sources, synthesizes the findings,
and returns a structured report — all without human intervention.
Lumina is a production-grade agentic AI system that autonomously conducts research on any topic. Unlike a simple chatbot that answers from training data, Lumina's agent actively:
- Searches the web using DuckDuckGo — no API key required
- Reads and scrapes the most relevant pages
- Synthesizes everything into a structured JSON report
- Stores every session in MongoDB for instant retrieval
This is not a wrapper around a chat API. It is a real agentic loop — the LLM decides which tools to call, how many times, and in what order, based on what it finds at each step.
| Traditional chatbot | Lumina agent |
|---|---|
| Answers from static training data | Actively searches the live web |
| Single LLM call | Multi-step autonomous reasoning loop |
| No memory | Full session history in MongoDB |
| No sources | Cited sources with relevance explanations |
| Script | Production API with Swagger docs, caching, streaming |
This architecture is directly applicable to real-world products:
- Enterprise knowledge bases — swap web search for internal document search
- Competitive intelligence tools — research competitors automatically
- Academic assistants — summarize papers and surface follow-up questions
- News aggregators — monitor topics and synthesize daily briefs
- Customer support — agent researches answers before responding
- Agentic loop —
smolagentsCodeAgentautonomously decides when and how to use tools - Live web search — DuckDuckGo search tool, no API key needed
- Page scraping —
httpx+BeautifulSoup4extracts clean text from any URL - Structured output — every report has summary, key points, cited sources, follow-up questions
- Streaming — live progress bar via server-sent events while the agent works
- Caching — repeated queries return instantly without hitting the LLM again
- Full history — every session saved to MongoDB, searchable and retrievable
- REST API — FastAPI with auto-generated Swagger UI at
/docs - Dockerized — one command to run the full stack locally or in production
- Polished UI — Inter + Cal Sans fonts, dark theme, sidebar history, export to Markdown
┌─────────────────────────────────────────────────────┐
│ Lumina Frontend │
│ (Vanilla JS · Inter font · Dark UI) │
└───────────────────────┬─────────────────────────────┘
│ HTTP / SSE
┌───────────────────────▼─────────────────────────────┐
│ FastAPI Backend │
│ Pydantic validation · Middleware · Swagger │
│ │
│ ┌─────────────┐ ┌──────────┐ ┌─────────────┐ │
│ │ /research │ │ /history │ │ /health │ │
│ │ /stream │ │ /search │ │ /cache/stats│ │
│ └──────┬──────┘ └────┬─────┘ └─────────────┘ │
│ │ │ │
│ ┌──────▼──────┐ ┌─────▼──────┐ │
│ │ Cache layer │ │ MongoDB │ │
│ │ (in-memory) │ │ (motor) │ │
│ └──────┬──────┘ └────────────┘ │
│ │ │
│ ┌──────▼──────────────────────────────────┐ │
│ │ smolagents CodeAgent │ │
│ │ │ │
│ │ ┌──────────────┐ ┌────────────────┐ │ │
│ │ │ WebSearchTool│ │ ScrapePageTool │ │ │
│ │ │ DuckDuckGo │ │ httpx + BS4 │ │ │
│ │ └──────────────┘ └────────────────┘ │ │
│ └──────────────────┬──────────────────────┘ │
└─────────────────────┼───────────────────────────────┘
│ Inference API
┌─────────────────────▼───────────────────────────────┐
│ HuggingFace Inference API │
│ Llama 4 Scout 17B · Free tier │
└─────────────────────────────────────────────────────┘
┌─────────────────────────────────────────────────────┐
│ Docker Compose │
│ research_api (port 8000) + research_mongo (27017) │
│ mongo_data volume · healthcheck · auto-restart │
└─────────────────────────────────────────────────────┘
| Layer | Technology | Why |
|---|---|---|
| Agent framework | smolagents |
HuggingFace's lightweight agent library — tool use + reasoning loop |
| LLM | Llama 4 Scout 17B | Free via HF Inference API, strong instruction following |
| Web search | ddgs (DuckDuckGo) |
Free, no API key, reliable |
| Page scraping | httpx + BeautifulSoup4 |
Fast async HTTP + clean text extraction |
| API | FastAPI |
Auto Swagger docs, Pydantic validation, async, streaming |
| Database | MongoDB + motor |
Async NoSQL — perfect for JSON research documents |
| Caching | In-memory with TTL | Instant repeat queries, configurable expiry |
| Streaming | Server-Sent Events | Live progress updates without WebSockets |
| Containerization | Docker + Docker Compose |
One-command full-stack deployment |
| Frontend | Vanilla JS + Inter font | Zero dependencies, fast, polished |
- Docker Desktop installed and running
- A free HuggingFace account and token
git clone https://github.com/NadaBhm/lumina-research-agent.git
cd lumina-research-agentcp .env.example .envEdit .env:
HF_TOKEN=hf_yourTokenHere
MONGO_USER=admin
MONGO_PASSWORD=yourPasswordHere
MONGO_DB=research_agent
CACHE_TTL_SECONDS=3600
API_VERSION=1.0.0Get your free HuggingFace token at huggingface.co/settings/tokens. Select "Make calls to Inference Providers".
docker-compose up --buildThat's it. Visit:
| URL | What |
|---|---|
http://localhost:8000 |
Lumina frontend |
http://localhost:8000/docs |
Swagger API docs |
http://localhost:8000/health |
Health check |
http://localhost:8000/history |
All saved research |
Run the agent on a topic.
curl -X POST http://localhost:8000/research \
-H "Content-Type: application/json" \
-d '{"topic": "What is retrieval augmented generation?", "depth": "standard"}'Request body:
{
"topic": "What is retrieval augmented generation?",
"depth": "quick"
}| Field | Type | Values | Default |
|---|---|---|---|
topic |
string | 3–300 chars | required |
depth |
enum | quick standard deep |
standard |
Response:
{
"id": "664f2a3b1c9d4e5f6a7b8c9d",
"topic": "What is retrieval augmented generation?",
"depth": "quick",
"summary": "Retrieval Augmented Generation (RAG) is...",
"key_points": [
"RAG combines retrieval systems with generative models",
"It reduces hallucinations by grounding answers in real documents"
],
"sources": [
{
"title": "Retrieval-Augmented Generation for Knowledge-Intensive NLP Tasks",
"url": "https://arxiv.org/abs/2005.11401",
"relevance": "The original RAG paper by Lewis et al."
}
],
"follow_up_questions": [
"How does RAG compare to fine-tuning?",
"What vector databases work best with RAG?"
],
"cached": false
}Same as above but streams progress via server-sent events.
const res = await fetch('/research/stream', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ topic: 'What is RAG?', depth: 'quick' })
});
const reader = res.body.getReader();
// Events:
// { type: 'progress', step, total, message }
// { type: 'cache_hit', message }
// { type: 'done', result }
// { type: 'error', message }curl http://localhost:8000/history?limit=20curl http://localhost:8000/history/664f2a3b1c9d4e5f6a7b8c9dcurl -X DELETE http://localhost:8000/history/664f2a3b1c9d4e5f6a7b8c9dcurl http://localhost:8000/history/search/vector%20databasecurl http://localhost:8000/cache/statscurl -X DELETE http://localhost:8000/cache| Level | Agent steps | Typical time | Best for |
|---|---|---|---|
quick |
3 | ~20s | Fast overviews, known topics |
standard |
8 | ~45s | Balanced research |
deep |
15 | ~90s | Thorough investigation |
<script>
async function research(topic) {
const res = await fetch('http://localhost:8000/research', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ topic, depth: 'quick' })
});
return res.json();
}
const report = await research('What is the James Webb telescope?');
console.log(report.summary);
</script>import httpx
def research(topic: str, depth: str = "standard") -> dict:
res = httpx.post(
"http://localhost:8000/research",
json={"topic": topic, "depth": depth},
timeout=120
)
return res.json()
report = research("What are large language models?")
print(report["summary"])
for point in report["key_points"]:
print(f"- {point}")topics = [
"What is RAG?",
"What are vector databases?",
"How does fine-tuning work?",
]
for topic in topics:
report = research(topic, depth="quick")
print(f"\n## {topic}")
print(report["summary"])lumina-research-agent/
├── agent/
│ ├── __init__.py
│ ├── tools.py # WebSearchTool + ScrapePageTool
│ └── agent.py # CodeAgent setup + run_research()
├── static/
│ └── index.html # Full frontend (no framework)
├── main.py # FastAPI app, all endpoints
├── schemas.py # Pydantic request/response models
├── database.py # MongoDB async CRUD (motor)
├── cache.py # In-memory cache with TTL
├── config.py # Centralised settings from .env
├── Dockerfile
├── docker-compose.yml
├── .dockerignore
├── .env.example
└── requirements.txt
The agent is designed to be extended. Adding a new tool takes ~10 lines:
from smolagents import Tool
class WikipediaTool(Tool):
name = "wikipedia_search"
description = "Search Wikipedia for reliable encyclopedic information."
inputs = {"query": {"type": "string", "description": "Topic to search"}}
output_type = "string"
def forward(self, query: str) -> str:
import httpx
r = httpx.get(
f"https://en.wikipedia.org/api/rest_v1/page/summary/{query.replace(' ', '_')}"
)
return r.json().get("extract", "Not found.")Then register it in agent.py:
tools=[WebSearchTool(), ScrapePageTool(), WikipediaTool()]| Variable | Required | Default | Description |
|---|---|---|---|
HF_TOKEN |
Yes | — | HuggingFace API token |
MONGO_USER |
Yes | — | MongoDB username |
MONGO_PASSWORD |
Yes | — | MongoDB password |
MONGO_DB |
No | research_agent |
Database name |
CACHE_TTL_SECONDS |
No | 3600 |
Cache expiry in seconds |
API_VERSION |
No | 1.0.0 |
Shown in /health response |
This project was built to apply skills from:
- Develop Generative AI Applications: Get Started — Coursera
- Hugging Face Agents Course — HuggingFace
MIT — free to use, modify, and deploy.