Skip to main content

AI-powered academic article screening and analysis tool

Project description

Lutz

Lutz logo

Languages: English | Português | Español

AI-powered tool for organizing, screening, and analyzing academic PDF articles — with a full browser interface and command-line access.

DOI Python Version License Status Interface

Tags: systematic review, academic screening, scientific articles, generative AI, LLM, RAG, embeddings, PDF, LanceDB, Python, open science, academic research.

Lutz helps researchers, students, and literature review teams work with large sets of PDF articles. It creates a reproducible project structure, processes and embeds PDFs into a local vector database, and uses language models to screen, analyze, and chat about your articles — all through a browser interface or the command line.

The package is named after Bertha Maria Julia Lutz, an important Brazilian scientist, biologist, and researcher who contributed to biology and to the recognition of science in Brazil.


What's new in v0.3.0

  • React web interface — full browser UI replacing the previous Streamlit prototype. Faster, more responsive, works on any device on the local network.
  • Background jobs — vectorize, analyze, and extract citations while freely navigating the interface. A notification bell tracks every job.
  • Chat with sessions — conversation history persisted on disk, automatic memory extraction, and support for context files alongside articles.
  • Reading roadmap — LLM-generated staged reading plan that groups and orders relevant articles by dependency.
  • Inline PDF viewer — open any article directly in the browser without leaving the interface.
  • Smart rename — suggest a clean filename from the article's own content with one click, no vectorization required.
  • Multi-provider LLM/embedding — OpenAI, Anthropic, Docker Model Runner, Ollama, llama.cpp — configurable from the Settings page.
  • Windows installer — one-click setup wizard with license, shortcuts, and uninstaller; no Python or Node required.

Table of contents


Installation

Windows — installer (recommended for non-technical users)

Download lutz-setup-windows-x64.exe from the latest release and run it. The wizard will:

  • Show the MIT license agreement
  • Let you choose the install directory
  • Create a Start Menu entry and optional Desktop shortcut
  • Register an uninstaller in Windows Settings → Apps

After installation, open Lutz Research from the Start Menu. The browser interface opens automatically.

No Python or Node.js required. Everything is bundled in the installer.


pip (Python users)

pip install lutz-research
lutz web

Requires Python 3.10+. A virtual environment is recommended:

python -m venv .venv
source .venv/bin/activate      # Linux / macOS
.\.venv\Scripts\Activate.ps1   # Windows PowerShell

pip install lutz-research
lutz web

uv (fast, isolated — recommended for Linux / macOS)

uv installs and runs the tool in a fully isolated environment with a single command:

uv tool install lutz-research
lutz web

To update later:

uv tool upgrade lutz-research

Docker

Ideal for servers, shared environments, or Linux users who prefer containers:

# Start the server, mounting your project directory
docker run -v $(pwd):/project -p 8765:8765 ghcr.io/jooguilhermesc/lutz

# Then open http://localhost:8765 in your browser

The container bundles the full stack (Python, dependencies, React UI). Your article files and reports live in the mounted /project directory and are never copied into the image.


From source

git clone https://github.com/jooguilhermesc/lutz.git
cd lutz
pip install -e ".[dev]"

Build the React frontend to use the web interface from source:

cd web && npm ci && npm run build
cd ..
lutz web

Quick start

# 1. Create a new project
mkdir my-review && cd my-review
lutz init

# 2. Copy your PDFs into articles/  (or upload via the web interface)
lutz load --f ~/Downloads/pdfs --so linux

# 3. Open the web interface
lutz web

The browser opens at http://localhost:8765. From there you can vectorize articles, run analyses, chat with your corpus, and generate reports — no more command line needed.


Web interface

lutz web starts a local FastAPI server and opens the browser automatically.

lutz web                              # default: localhost:8765
lutz web --port 8080                  # custom port
lutz web --host 0.0.0.0               # expose on local network
lutz web --no-browser                 # server only, no auto-open
lutz web --project /path/to/project   # explicit project directory

Pages

Home

Project dashboard showing article count, vectorized chunks, analyses run, and quick-action buttons. Entry point for first-time setup.

Library (Vetorização)

Upload PDFs via drag-and-drop or file picker. View all articles with size, vectorization status, and a one-click rename suggestion. Start vectorization as a background job and navigate freely while it runs.

Vector Store

Inspect the LanceDB index: total chunks, unique documents, embedding model, last update. Query the index directly with DuckDB SQL. Reset the store when you need to rebuild from scratch.

Analysis

Write or upload a Markdown prompt, choose RAG or per-article mode, set workers and chunk limits, and dispatch the analysis as a background job. A live log panel appears on the page if you return while the job is still running.

Citations

Select a per-article analysis report and extract the 3–5 passages that best justify each article's classification. Runs as a background job with real-time progress.

Roadmap

Generate a staged reading plan from your relevant articles. The LLM groups articles by conceptual dependency and suggests an order for reading.

Reports

Table of all past analyses. Click any row to expand the full results, including per-article verdicts, analysis text, token usage, and model metadata. Download JSON reports.

Chat

Conversational interface over your corpus. Each conversation is a persistent session saved to disk. The assistant can search the article vector store, attached context files, or use only its own knowledge — configurable per message. Memories pinned manually or extracted automatically from conversations are shown in a sidebar panel.

Settings

Configure LLM and embedding providers, API keys (write-only — never displayed after saving), base URLs, response language, and model parameters. Changes are saved to .env immediately.

Background jobs and notifications

Long-running operations (vectorize, analysis, citations, roadmap) run as server-side background tasks. A bell icon in the top bar shows a badge when jobs are running or completed. Clicking it opens a panel with status, elapsed time, and the option to cancel in-progress jobs. If you navigate away from a page while a job is running, an Active Job panel appears when you return, allowing you to reconnect to the live log stream.


Model configuration

Configuration lives in .env at the project root, created from .env.example by lutz init. It can also be edited from the Settings page in the web interface.

Docker Model Runner (local, no API key)

EMBEDDING_PROVIDER=docker_model_runner
EMBEDDING_MODEL=nomic-embed-text

LLM_PROVIDER=docker_model_runner
LLM_MODEL=ai/llama3.2

DOCKER_MODEL_HOST=http://localhost:12434/engines/v1

Pull models first:

docker model pull nomic-embed-text
docker model pull ai/llama3.2

Ollama (local, no API key)

EMBEDDING_PROVIDER=sentence_transformers
EMBEDDING_MODEL=all-MiniLM-L6-v2

LLM_PROVIDER=openai
OPENAI_BASE_URL=http://localhost:11434/v1
OPENAI_API_KEY=ollama
LLM_MODEL=llama3.2

OpenAI

EMBEDDING_PROVIDER=openai
EMBEDDING_MODEL=text-embedding-3-small

LLM_PROVIDER=openai
OPENAI_API_KEY=your-key-here
LLM_MODEL=gpt-4o-mini

OpenRouter (free models available)

EMBEDDING_PROVIDER=sentence_transformers
EMBEDDING_MODEL=all-MiniLM-L6-v2

LLM_PROVIDER=openai
OPENAI_BASE_URL=https://openrouter.ai/api/v1
OPENAI_API_KEY=your-key-here
LLM_MODEL=google/gemma-3-12b-it:free

Anthropic

EMBEDDING_PROVIDER=sentence_transformers
EMBEDDING_MODEL=all-MiniLM-L6-v2

LLM_PROVIDER=anthropic
ANTHROPIC_API_KEY=your-key-here
LLM_MODEL=claude-haiku-4-5-20251001

Configuration reference

Variable Purpose Default
LLM_PROVIDER openai, anthropic, or docker_model_runner
LLM_MODEL Model name for analysis and chat
LLM_MAX_TOKENS Maximum response size 4096
LLM_TEMPERATURE Response variation 0.2
EMBEDDING_PROVIDER openai, sentence_transformers, or docker_model_runner
EMBEDDING_MODEL Embedding model name
OPENAI_API_KEY Key for OpenAI or compatible services
OPENAI_BASE_URL Alternative base URL for OpenAI-compatible APIs
ANTHROPIC_API_KEY Anthropic API key
DOCKER_MODEL_HOST Docker Model Runner address
REPORT_LANGUAGE Language for generated reports português

CLI reference

The full workflow is also available from the command line. The CLI and the web interface share the same project structure and .env.

lutz init [PROJECT_NAME]

Creates a new project with articles/, prompts/, analysis/execution_reports/, .env.example, .gitignore, and a local Git repository.

lutz init
lutz init my-review

lutz load --f FOLDER [--so OS] [--overwrite]

Copies PDFs from a source folder into articles/.

lutz load --f ~/Downloads/articles --so linux
lutz load --f "C:\Users\Ana\Downloads\articles" --so windows

lutz vectorize [options]

Processes PDFs and creates the vector index in .lutz/vector_store/.

Option Description Default
--chunk-size Chunk size in words 512
--chunk-overlap Overlap between chunks 64
--extraction Extraction backend: pymupdf, marker, or auto (see below) pymupdf
--section-parse Split articles into labeled sections before chunking disabled
--skip-security Skip PDF security checks disabled
--quarantine Process files in articles/_quarantine/ disabled
lutz vectorize
lutz vectorize --section-parse                    # section-aware chunking
lutz vectorize --chunk-size 256 --chunk-overlap 32
lutz vectorize --extraction marker                # OCR + multi-column layout
lutz vectorize --extraction marker --section-parse
lutz vectorize --extraction auto                  # auto-detect scanned PDFs

Extraction backends

Document type Recommended backend Reason
Digital article, single column pymupdf (default) Fast, no extra deps
Digital article, 2+ columns (IEEE, Elsevier, ACM) marker Layout detection
Scanned book or old article marker OCR via surya
Mixed corpus auto Detects and adapts per file
# Install the marker backend (model weights ~500 MB, downloaded once)
pip install "lutz-research[marker]"

lutz vectorize --extraction marker

The marker backend handles multi-column layouts and scanned PDFs without requiring Poppler or Tesseract. When --section-parse is combined with --extraction marker, sections are parsed directly from the Markdown headings that marker produces — no layoutparser needed.

lutz analysis --p PROMPT [options]

Analyzes vectorized articles using a Markdown prompt.

Option Description Default
--p Path to the .md prompt required
--per-article One model call per article disabled (RAG mode)
--workers Parallel calls in per-article mode 1
--top-k Chunks to retrieve in RAG mode 10
--max-chunks-per-article Chunk limit per article no limit
--filter-sections Restrict to specific sections no filter
--multiple Path to a YAML multi-experiment file
lutz analysis --p prompts/screening.md --per-article --workers 4
lutz analysis --p prompts/methodology.md --filter-sections methodology,results

lutz citations --analysis FILE [options]

Extracts the key passages that justify each article's classification.

lutz citations --analysis analysis/execution_reports/screening_<ts>.json \
  --workers 4 --only-relevant

lutz vector-store [options]

Inspects the vector index.

lutz vector-store --summarize
lutz vector-store --sections      # section breakdown per article
lutz vector-store --export        # JSON export

lutz unvectorize

Deletes the vector index. PDFs are not affected.


Complete systematic review workflow

# 1. Create project
lutz init my-review && cd my-review

# 2. Configure AI model (.env or Settings page in the web UI)
cp .env.example .env

# 3. Add PDFs
lutz load --f ~/Downloads/articles --so linux

# 4. Vectorize with section-aware parsing
lutz vectorize --section-parse

# 5. Screen articles by abstract (fast and cheap)
lutz analysis --p prompts/screening.md --per-article --workers 4 \
  --filter-sections abstract

# 6. Deep analysis on methodology and results
lutz analysis --p prompts/methodology_analysis.md \
  --filter-sections methodology,results

# 7. Extract citations from relevant articles
lutz citations \
  --analysis analysis/execution_reports/screening_<timestamp>.json \
  --workers 4 --only-relevant

# 8. Inspect the index
lutz vector-store --summarize --sections

Or open lutz web and do all of the above from the browser.


How to write prompts

Prompts are Markdown files inside prompts/. They tell the model what to analyze.

# Screening prompt

## Objective
Determine whether each article describes a study that applies machine learning
to predict clinical outcomes in ICU patients.

## Inclusion criterion
Include articles that: use supervised or unsupervised ML; analyze ICU patient data;
report a clinical outcome (mortality, length of stay, readmission).

## Exclusion criterion
Exclude: review articles without original data; studies outside the ICU context;
studies using only statistical methods without ML.

## Response format
1. Summary of the article's approach (2–3 sentences).
2. Evidence for or against inclusion.
3. Verdict: INCLUDE, EXCLUDE, or UNCERTAIN.

lutz init creates ready-to-edit templates: systematic_review.md, methodology_analysis.md, evidence_quality.md, thematic_synthesis.md.


Security model

Before vectorizing, Lutz checks PDFs to reduce common risks.

Check What it detects
Structural analysis Embedded JavaScript, automatic actions, XFA forms
Prompt injection Phrases attempting to override model instructions
Academic structure Basic signs of academic articles (abstract, methodology, references)
Corpus anomaly Statistical outliers when 5 or more documents are present

Suspicious files are moved to articles/_quarantine/. To process them after manual review:

lutz vectorize --quarantine

Architecture

lutz/
├── cli.py                        # Click CLI entry point
├── commands/
│   ├── init.py                   # lutz init
│   ├── load.py                   # lutz load
│   ├── vectorize.py              # lutz vectorize / unvectorize
│   ├── analysis.py               # lutz analysis
│   ├── experiments.py            # --multiple YAML runner
│   ├── citations.py              # lutz citations
│   ├── vector_store.py           # lutz vector-store
│   └── web.py                    # lutz web (FastAPI launcher)
├── core/
│   ├── security_checker.py       # PDF security checks
│   ├── extraction.py             # pluggable extraction strategies (PyMuPDF, marker)
│   ├── pdf_processor.py          # text extraction and chunking
│   ├── section_parser.py         # section detection
│   ├── vector_store.py           # LanceDB wrapper
│   ├── context_store.py          # chat context file store
│   ├── embedding_client.py       # embedding providers
│   └── llm_client.py             # LLM providers
├── server/
│   └── app.py                    # FastAPI server (REST + SSE + WebSocket)
├── web/                          # pre-built React SPA (bundled in wheel)
└── utils/
    ├── html_report.py            # HTML report generation
    ├── document_reader.py        # multi-format extraction (PDF, DOCX, XLSX…)
    ├── project.py                # project detection and .env loading
    └── templates.py              # files created by lutz init

web/src/                          # React 18 + Vite + Tailwind (source)
├── pages/                        # one component per page
├── components/                   # shared UI components
│   ├── ActiveJobPanel.tsx        # reconnects to running job log on page return
│   ├── NotificationsPanel.tsx    # bell icon + job status dropdown
│   └── ConfirmDialog.tsx         # delete confirmation dialogs
└── contexts/
    ├── NotificationsContext.tsx  # WebSocket job state (global)
    └── LanguageContext.tsx       # i18n (pt / en / es)

The vector index lives in .lutz/vector_store/ inside the project (LanceDB format). Chat sessions and memory are stored in .lutz/chat/. Neither should be committed to Git.


Contributing

Contributions are welcome. To set up a development environment:

git clone https://github.com/jooguilhermesc/lutz.git
cd lutz
pip install -e ".[dev]"

# Build the React frontend
cd web && npm ci && npm run build && cd ..

lutz web

Before proposing large changes, open an issue to discuss the idea.


How to cite

If you use Lutz in your research, please cite it using the information below or refer to the CITATION.cff file.

APA

Cabral, J. G. S., & Azevedo Farias, A. K. (2026). Lutz: AI-powered academic article screening and analysis tool (Version 0.3.0) [Software]. Zenodo. https://doi.org/10.5281/zenodo.19982571

BibTeX

@software{cabral2026lutz,
  author  = {Cabral, João Guilherme Silva and Azevedo Farias, Anna Karoline},
  title   = {{Lutz: AI-powered academic article screening and analysis tool}},
  year    = {2026},
  version = {0.3.0},
  doi     = {10.5281/zenodo.19982571},
  url     = {https://github.com/jooguilhermesc/lutz},
  license = {MIT}
}

License

MIT

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

lutz_research-0.3.1.tar.gz (11.3 MB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

lutz_research-0.3.1-py3-none-any.whl (2.9 MB view details)

Uploaded Python 3

File details

Details for the file lutz_research-0.3.1.tar.gz.

File metadata

  • Download URL: lutz_research-0.3.1.tar.gz
  • Upload date:
  • Size: 11.3 MB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.1.0 CPython/3.13.12

File hashes

Hashes for lutz_research-0.3.1.tar.gz
Algorithm Hash digest
SHA256 06becea537d9292c1e893b7f6b451966d38cf9d14491fecec31d9cbb9cc2a1dd
MD5 4ff5a8dfb7d54037f6243e2b6effa4c0
BLAKE2b-256 4ab2acc1392263b2b91f47083e710eb3774a2dcb07e56b769a051ef35d7464a9

See more details on using hashes here.

File details

Details for the file lutz_research-0.3.1-py3-none-any.whl.

File metadata

  • Download URL: lutz_research-0.3.1-py3-none-any.whl
  • Upload date:
  • Size: 2.9 MB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.1.0 CPython/3.13.12

File hashes

Hashes for lutz_research-0.3.1-py3-none-any.whl
Algorithm Hash digest
SHA256 c1b9101c396aea47d523ce39ca4def29c8519a00ea7e93a7d078ce87a7603fcc
MD5 3e37efafedb3d13c266fbc1a24e45a3b
BLAKE2b-256 3a771e1b7eee2d85e38ad5691fffaf1daa2a8be84124e9fe3ec4626c23da54fc

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page