Cell types in online single-cell atlases are typically annotated with short and sometimes obscure names. Understanding what these names mean — and what is known about the cell types they describe — requires looking up the atlas paper and following its citations. While this scholarly workflow remains essential, it creates a significant barrier to efficient and effective browsing of online atlases.
Atlas Chat addresses this problem by enabling researchers to explore the literature associated with online atlases directly, without leaving their browsing context.
Atlas Chat supports three complementary modes of operation:
-
Cell type reports (programmatic) — A PydanticAI graph pipeline that generates structured reports for individual cell types, drawn from the atlas paper and its cited literature. Run via the
atlas-reportCLI. -
Cell type reports (agentic) — An interactive Claude Code workflow (
/run-workflow) that achieves the same result using MCP tools and subagents. Useful for exploratory runs and debugging. -
Chat across the literature — An interactive Claude Code mode (
/chat) for asking questions spanning the primary atlas paper and any papers it cites, with answers grounded in the source material.
All three modes produce output backed by exact quotes from source papers, allowing users to assess accuracy and navigate directly to the primary literature.
- Source transparency — Every claim is backed by a direct quote from a source paper, so users can judge the evidence for themselves.
- Literature navigation — Quotes are linked to their source papers, enabling users to move quickly from a summary to the primary literature.
- Complement, not replace — Atlas Chat lowers the barrier to efficient browsing, not to substitute for careful scholarly reading of the original papers.
LLMs can fabricate quotes and idedentifiers. Atlas Chat treats this as a first-class problem.
- Every blockquote in a generated report is verified against the original text before the report is saved. The validator checks that each quoted passage is a verbatim substring of a source paper (normalising whitespace, dashes, and smart quotes; handling ellipsis-separated segments).
- Every DOI and CorpusId reference is checked against the paper catalogue.
- If any check fails, the report is fed back to the LLM to fix, along with details of the error (up to two retries).
What this guarantees: quoted text in a final report actually appears in the cited source. DOIs are correct
What it does not guarantee: that the surrounding narrative accurately interprets those quotes, or that the most relevant literature was found. Users should always follow quotes back to their source papers to assess context.
# Clone and install
git clone <repo-url> && cd atlas_chat
uv syncCreate a .env file in the repository root with the required API keys:
ANTHROPIC_API_KEY=sk-...
ASTA_API_KEY=...
# Optional, only needed for OpenAI provider:
OPENAI_API_KEY=sk-...
atlas-report --project fetal_skin_atlas --cell-type "Iron-recycling macrophage"# Generate reports for every annotation in the project
atlas-report --project fetal_skin_atlas --batch
# Only fill gaps — skip cell types that already have a report
atlas-report --project fetal_skin_atlas --batch --no-stomp
# Preview what would happen without running anything
atlas-report --project fetal_skin_atlas --batch --no-stomp --dry-run| Flag | Default | Description |
|---|---|---|
--project NAME |
(required) | Project directory name under projects/ |
--cell-type LABEL |
Single cell type label (mutually exclusive with --batch) |
|
--batch |
Generate reports for all annotations (mutually exclusive with --cell-type) |
|
--no-stomp |
off | Skip cell types whose report file already exists |
--depth N |
1 | Citation traversal depth (max 3) |
--dry-run |
off | Show execution plan without making LLM calls |
--provider |
anthropic | LLM provider: anthropic or openai |
--model |
(provider default) | LiteLLM model ID, e.g. claude-sonnet-4-20250514, gpt-4.1 |
--verbose / -v |
off | Enable DEBUG logging and full stack traces |
The legacy invocation uv run python scripts/generate_report.py ... still works via a thin shim.
Each atlas project lives in projects/{project_name}/ and is defined by a single file, cell_type_annotations.json:
{
"source": {
"doi": "10.1038/s41586-024-08002-x",
"title": "A prenatal skin atlas reveals immune regulation of human skin morphogenesis"
},
"annotations": [
{
"label": "Iron-recycling macrophage",
"granularity": "fine",
"scope": "fetal"
},
{
"label": "DC1",
"granularity": "fine",
"scope": "adult"
}
]
}source.doiis required.pmcidandcorpus_idcan be pre-populated to skip runtime resolution.- Each annotation requires
label;granularity(fine/broad) andscope(fetal/adult/organoid) are optional. - The schema is defined in
src/schemas/cell_type_annotation.schema.json.
Both the programmatic and agentic workflows follow the same six-step sequence:
- FetchSupplements — Resolve the atlas DOI to a PMCID via Europe PMC, fetch full text and supplementary file listings.
- ResolveName — LLM call to identify the author-used terminology for the cell type in the atlas paper.
- ScanSupplements + CitationTraverse (parallel) — LLM-driven extraction of markers and findings from supplements, plus Semantic Scholar snippet search at configurable depth to build a paper catalogue with verified exact quotes.
- SynthesizeReport — LLM call to generate a markdown report from all collected evidence.
- ValidateReport — Check that every blockquoted passage is a substring of the evidence corpus and that all referenced papers exist in the catalogue. On failure, retry synthesis (up to 2 retries).
- SaveReport — Write the final validated report to
projects/{project}/reports/{cell_type}.md.
projects/{project}/
├── cell_type_annotations.json # Project configuration (user-authored)
├── traversal_output/{cell_type}/
│ ├── atlas_full_text.txt # Fetched atlas paper text
│ ├── name_resolution.json # Resolved cell type names
│ ├── supplementary_findings.json # Extracted markers and findings
│ ├── raw_snippets.json # Raw citation snippets
│ ├── all_summaries.json # Processed summaries with verified quotes
│ └── paper_catalogue.json # Metadata for all discovered papers
└── reports/
└── {cell_type}.md # Final validated report
Reports are validated before saving:
- Quote checking — Every
> "..."blockquote must be a substring of the evidence corpus (with normalisation for whitespace, dashes, smart quotes, and ellipsis segments). - Reference checking — Every DOI or CorpusId in the report must exist in the paper catalogue.
If validation fails, the synthesis step is retried with specific error feedback (up to 2 retries).
These modes require a Claude Code session with the project's MCP servers configured (see .claude/settings.local.json).
/run-workflow
Launches the same report generation pipeline interactively, using MCP tools and Claude Code subagents. The orchestrator follows AGENT.md and delegates to specialised subagents in .claude/agents/.
/chat
An interactive question-answering mode for exploring the atlas literature. Loads the project's cached evidence and routes questions through Semantic Scholar snippet search. Writes any new traversal data to traversal_output/_chat/ to avoid modifying existing cell type directories.
| Package | Role |
|---|---|
| pydantic-ai | Graph orchestration (pydantic_graph) |
| cellsem-llm-client | LiteLLM agent wrapper |
| deep-research-client | Semantic Scholar snippet search (AstaProvider) |
| pydantic, pyyaml, jsonschema, python-dotenv | Data validation, config, schema |
| Service | Tools Used |
|---|---|
| ARTL MCP (Europe PMC) | Full text, supplements, ID resolution, PDF-to-markdown |
| Semantic Scholar (Asta) | Snippet search, paper metadata, citation traversal |
| Playwright | Browser automation (available but not central to current workflow) |
| Variable | Required | Purpose |
|---|---|---|
ANTHROPIC_API_KEY |
Yes (if using Anthropic) | Anthropic API access |
OPENAI_API_KEY |
If using OpenAI | OpenAI API access |
ASTA_API_KEY |
Yes | Semantic Scholar API access (programmatic traversal) |
This project uses Claude Code for agentic development. CLAUDE_dev.md provides the agent instructions — load it as context when working on the codebase. It covers:
- Scope rings — ship Ring 0 (MVP) first, iterate based on user feedback before adding features
- Schema-first design — JSON schemas are the source of truth; Pydantic models are generated from them
- Test-driven development — integration tests with real APIs from day 1, no mocks
- Prompt management — YAML prompt files co-located with agents/services, not hardcoded
- Quality gates — ruff, mypy, and coverage thresholds enforced via pre-commit hooks
# Run tests
uv run pytest tests/unit -m unit
uv run pytest tests/integration -m integration
# Lint and type check
uv run ruff check .
uv run mypy .