A fully self-contained AI research agent in a single Streamlit file. No FastAPI. No separate server. No local GPU. One command to run.
Submit a research query → get ranked sources, a structured summary, fact-checked
claims, and a full downloadable markdown report — powered by the
Ollama Cloud API (gpt-oss:120b-cloud).
| Feature | Detail |
|---|---|
| ☁️ Ollama Cloud API | Direct HTTP integration — gpt-oss:120b-cloud or any configured model |
| 🔍 Web search | DuckDuckGo scraping with automatic redirect URL decoding |
| 📄 Content extraction | Full page text via BeautifulSoup (noise-stripped) |
| 📊 LLM source ranking | Relevance-scored (0–1) and sorted |
| 🧠 Structured summary | Key insights, statistics, arguments, risks, opportunities |
| ✅ Fact-checking | Per-claim LLM verification with confidence score + status badge |
| 📄 Report generation | Full markdown report: executive summary, findings, analysis, citations |
| ⬇️ Report download | One-click .md download button |
| 🎛️ Source count control | Sidebar slider — 3 to 10 sources |
| 💾 JSON persistence | Flat file storage under data/ — no database required |
| 📚 Research history | All past reports loadable from the history panel |
| ⚡ Single file | Entire app in streamlit_app.py |
Everything runs in one Python process. The Ollama Cloud API is called directly — no intermediate server layer.
streamlit_app.py
│
├── ollama_generate() — POST to Ollama Cloud /generate
├── extract_json() — robust JSON parser for LLM output
├── test_api_connection() — verify API key + connectivity
│
├── search_agent()
│ ├── _ddg_search() — DuckDuckGo HTML scrape
│ ├── _decode_ddg_url() — decode ?uddg= redirect to real URL
│ ├── _extract_page() — fetch + BeautifulSoup content extract
│ └── LLM ranking call — relevance_score per source
│
├── summarize_agent() — LLM → keyInsights/statistics/arguments/risks/opportunities
├── fact_check_agent() — LLM per-claim: status + confidence + explanation
├── report_agent() — LLM → full markdown report with citations
│
├── run_pipeline() — orchestrates all stages + live progress bar
│
├── save_project() — data/projects/project_*.json
├── save_report() — data/reports/report_*.json
└── load_history() — reads all project files, sorted by created_at
| Layer | Technology |
|---|---|
| UI + App | Streamlit ≥ 1.35 |
| LLM | Ollama Cloud API (gpt-oss:120b-cloud) |
| Web search | DuckDuckGo HTML (no API key needed) |
| HTML parsing | BeautifulSoup4 |
| HTTP client | requests |
| Storage | JSON files (data/projects/, data/reports/) |
| Language | Python 3.11 |
Removed from previous version: FastAPI, Uvicorn, Pydantic, python-multipart, openai SDK, asyncio, separate backend process.
git clone https://github.com/QubitGaurav/AI_Research_Agent.git
cd AI_Research_Agent
python -m venv venv
# Windows
venv\Scripts\activate
# Linux / macOS
source venv/bin/activate
pip install -r requirements.txtCopy the example and fill in your Ollama Cloud credentials:
cp .env.example .env.env:
OLLAMA_API_KEY=your_ollama_api_key_here
OLLAMA_BASE_URL=https://ollama.com/api
OLLAMA_MODEL=gpt-oss:120b-cloud
OLLAMA_TIMEOUT=300
DATA_DIR=data| Variable | Default | Description |
|---|---|---|
OLLAMA_API_KEY |
— | Required. Your Ollama Cloud API key |
OLLAMA_BASE_URL |
https://ollama.com/api |
Ollama Cloud base URL |
OLLAMA_MODEL |
gpt-oss:120b-cloud |
Model for all LLM calls |
OLLAMA_TIMEOUT |
300 |
Request timeout in seconds |
DATA_DIR |
data |
Where project/report JSON files are stored |
streamlit run streamlit_app.pyOpen http://localhost:8501.
That's it. One process, one terminal.
- Push repo to GitHub (
.envanddata/are in.gitignore— they won't be pushed). - Go to share.streamlit.io → New app → connect your repo.
- Under App settings → Secrets, paste:
OLLAMA_API_KEY = "your_ollama_api_key_here"
OLLAMA_BASE_URL = "https://ollama.com/api"
OLLAMA_MODEL = "gpt-oss:120b-cloud"
OLLAMA_TIMEOUT = "300"No backend deployment needed. Streamlit Cloud talks directly to Ollama Cloud.
.
├── streamlit_app.py # entire application
├── requirements.txt # 4 dependencies
├── runtime.txt # python-3.11
├── .env.example # copy to .env and fill in
├── .gitignore
│
└── .streamlit/
└── secrets.toml # Streamlit Cloud secrets (do not commit)
data/ # auto-created at runtime
├── projects/ # project_<id>.json — full pipeline state
└── reports/ # report_<id>.json — generated reports
| # | Stage | What happens |
|---|---|---|
| 1 | Search | DuckDuckGo HTML scrape → decode ?uddg= redirect → fetch real page → extract body text |
| 2 | Rank | LLM scores each source for relevance (0–1); sorted descending |
| 3 | Summarize | LLM extracts key insights, statistics, arguments, risks, opportunities as JSON |
| 4 | Fact-check | LLM verifies each claim against source excerpts; returns status + confidence |
| 5 | Report | LLM writes full markdown report: executive summary → findings → analysis → citations |
| 6 | Persist | Project + report saved to data/ as JSON; visible in history panel |
| Limitation | Notes |
|---|---|
| DuckDuckGo rate limiting | If you get CAPTCHA or empty results, wait a few minutes. For production volume, use a paid search API (Serper, SerpAPI). |
| Single model for all stages | All calls use OLLAMA_MODEL. To use different models per stage, add a model parameter to ollama_generate(). |
| No streaming | Ollama /generate is called with stream: false — the UI updates only after each full stage completes. |
| File storage not concurrent-safe | Fine for personal/single-user use. For multi-user production, replace save_project/save_report with SQLite. |
| Fact-check quality | Per-claim verification is only as good as the source content extracted. Paywalled pages return empty content. |
Gaurav Sharma GitHub: QubitGaurav