EasyPaper is a multi-agent academic paper generation system. It turns a small set of metadata (title, idea, method, data, experiments, references) into a structured LaTeX paper and optionally compiles it into a PDF through a typesetting agent.
- Python SDK —
pip install easypaper, thenimport easypaperin your own project - Streaming generation — async generator yields real-time progress events at each phase
- Multi-agent pipeline: planning, writing, review, typesetting, and optional VLM review
- Optional FastAPI server mode with health and agent discovery endpoints
- LaTeX output with citation validation, figure/table injection, and review loop
- Python 3.11+
- LaTeX toolchain (
pdflatex+bibtex) for PDF compilation - Poppler — required by
pdf2imagefor PDF-to-image conversion- macOS:
brew install poppler - Ubuntu/Debian:
apt install poppler-utils
- macOS:
- Model API keys configured in YAML (see Config)
Install from PyPI:
pip install easypaperimport asyncio
from easypaper import EasyPaper, PaperMetaData
async def main():
ep = EasyPaper(config_path="config.yaml")
metadata = PaperMetaData(
title="My Paper Title",
idea_hypothesis="...",
method="...",
data="...",
experiments="...",
)
result = await ep.generate(metadata)
print(result.status, result.total_word_count)
for sec in result.sections:
print(f" {sec.section_type}: {sec.word_count} words")
asyncio.run(main())Use generate_stream() to receive real-time progress events via async generator:
import asyncio
from easypaper import EasyPaper, PaperMetaData, EventType
async def main():
ep = EasyPaper(config_path="config.yaml")
metadata = PaperMetaData(
title="My Paper Title",
idea_hypothesis="...",
method="...",
data="...",
experiments="...",
)
async for event in ep.generate_stream(metadata):
if event.event_type == EventType.PHASE_START:
print(f"▶ [{event.phase}] {event.message}")
elif event.event_type == EventType.SECTION_COMPLETE:
print(f" ✎ {event.phase} done")
elif event.event_type == EventType.COMPLETE:
result = event.data["result"]
print(f"Done! {result['total_word_count']} words")
asyncio.run(main())GenerationEvent fields:
| Field | Type | Description |
|---|---|---|
event_type |
EventType |
PHASE_START, PHASE_COMPLETE, SECTION_COMPLETE, PROGRESS, WARNING, ERROR, COMPLETE |
phase |
str |
Logical phase name (e.g. "planning", "introduction", "body") |
message |
str |
Human-readable description |
data |
dict | None |
Structured payload (section content, final result, etc.) |
timestamp |
datetime |
When the event was created |
A complete working example is available in user_case/.
To run EasyPaper as a FastAPI service (requires the server extra):
pip install "easypaper[server]"- Copy the example config and fill in your API keys:
cp configs/example.yaml configs/dev.yaml- Start the server:
uvicorn easypaper.main:app --reload --port 8000- Generate via API:
curl -X POST http://localhost:8000/metadata/generate \
-H "Content-Type: application/json" \
-d @economist_example/metadata.jsonEasyPaper includes a pluggable Skills system that injects writing constraints, venue-specific
formatting rules, and reviewer checkers into the generation pipeline. The repository ships
pre-built skills in skills/:
| Category | Skills | Description |
|---|---|---|
| Writing | anti-ai-style, academic-polish, latex-conventions |
Style constraints applied to all sections — eliminates AI-flavored phrasing, enforces academic tone, ensures LaTeX best practices |
| Venues | neurips, icml, iclr, acl, aaai, colm, nature |
Conference/journal profiles with page limits, formatting rules, and venue-specific style requirements |
| Reviewing | logic-check, style-check |
Reviewer checker prompts — detects logical contradictions, terminology inconsistencies, and style violations |
Add a skills section to your config YAML and point skills_dir to a directory containing
.yaml skill files:
skills:
enabled: true
skills_dir: "./skills" # path to skill YAML files
active_skills:
- "*" # "*" = load all skills; or list specific namesTo use the built-in skills, copy the skills/ directory into your project:
cp -r /path/to/easypaper/skills ./skillsIf skills_dir does not exist or skills.enabled is false, skills are silently skipped —
no configuration is required for basic usage.
To apply venue-specific constraints (e.g. page limits, formatting), set style_guide in your
PaperMetaData to match a venue profile name:
metadata = PaperMetaData(
title="...",
idea_hypothesis="...",
method="...",
data="...",
experiments="...",
style_guide="neurips", # activates the neurips venue profile
)Each skill is a single YAML file with the following structure:
name: my-custom-skill
description: "What this skill does"
type: writing_constraint # writing_constraint | reviewer_checker | venue_profile
target_sections: ["*"] # ["*"] = all sections, or specific ones
priority: 10 # lower = higher priority
system_prompt_append: |
## My Custom Rules
- Rule 1: ...
- Rule 2: ...
anti_patterns:
- "word to avoid"Drop the file into your skills_dir and it will be automatically loaded on the next run.
See the built-in skills in skills/ for complete examples.
The application loads configuration from AGENT_CONFIG_PATH (defaults to ./configs/dev.yaml).
You can also set this variable in a .env file at the project root.
See configs/example.yaml for a fully commented configuration template. Each agent entry defines
its model and optional agent-specific settings.
Key fields per agent:
model_name— LLM model identifierapi_key— API key for the model providerbase_url— API endpoint URL
Additional top-level sections:
skills— skills system toggle and active skill list (see Skills)tools— ReAct tool configuration (citation validation, paper search, etc.)vlm_service— shared VLM provider for visual review (supports OpenAI-compatible and Claude)
easypaper/— SDK core, agent implementations, event system, shared utilitiesconfigs/— YAML configs for agents and modelsskills/— built-in writing skills, venue profiles, and reviewer checkersscripts/— CLI utilities and demosuser_case/— standalone usage example (independent environment)economist_example/— sample metadata input