An academic paper writing agent based on LangGraph
Project description
seele-scholar-agent
LangGraph-based academic writing agent for topic proposal, literature retrieval, outline planning, section drafting, review loops, consistency checks, reference generation, draft continuation, and profile-specific document workflows.
Features
- Search papers from OpenAlex, Semantic Scholar, ArXiv, and custom retrievers.
- Propose concrete paper topics from broad research directions.
- Generate paper-type-aware outlines with section purpose, transitions, evidence maps, and suggested figures or tables.
- Draft sections with numbered citations, evidence packets, and claim-evidence bindings.
- Select document profiles from caller state, including a research-proposal profile for Japanese graduate-school applications.
- Configure graph topology with
GraphConfigfor full-document or single-section generation, topic/outline approval, finalizer/reference/integrity steps, draft integration, exemplars, and similarity checks. - Control review behavior with
WritingPolicy, including max revisions, inline citation requirements, claim-evidence strictness, and max-revision fallback. - Enforce section budgets with
BudgetPolicy,BudgetState, length gates, and optional budget allocators. - Continue, expand, rewrite, polish, or reference existing user drafts with structured
ExistingContentRefinput. - Use approved exemplar materials as structure/style references without adding them to the citation chain.
- Review sections, revise drafts, and cap revisions per section without force-approving failed reviews.
- Normalize retriever output through stable
CitationSourceentries before writing and reference generation. - Generate references with CrossRef/OpenAlex metadata enrichment and report missing inline citations.
- Check outline quality, citation validity, claim support, methodology, paragraph quality, terminology, logic, and citation consistency.
- Apply locale-aware style packs for Chinese, Japanese, and English academic writing.
- Stream node output with
astream()for UI integration.
Install
git clone https://github.com/onekyuu/seele-scholar-agent
cd seele-scholar-agent
uv sync
For development dependencies:
uv sync --extra dev
Configuration
This package only owns agent-level configuration. LLM keys and models are injected by the caller.
Agent .env file:
cp src/seele_scholar_agent/.env.example src/seele_scholar_agent/.env
Supported agent variables:
| Variable | Description | Default |
|---|---|---|
SEMANTIC_SCHOLAR_API_KEY |
Optional Semantic Scholar API key | empty |
MAX_REVISIONS |
Max review cycles per section | 3 |
Example caller environment:
OPENAI_API_KEY=sk-...
OPENAI_MODEL=gpt-4o-mini
OPENAI_BASE_URL=https://api.openai.com/v1
Any OpenAI-compatible endpoint can be used through ChatOpenAI.
Quality Controls
The workflow includes deterministic gates in addition to LLM review:
CitationSourceGateNode: normalizes retriever output into stable citable sources and diagnostics before planning/writing.OutlineQualityGateNode: blocks incomplete outlines that lack purpose, transitions, target claims, evidence plans, or use an empirical template for a non-empirical paper type.ReviewerNode: checks citation numbering, claim-source support, methodology/statistics issues, paragraph quality, and locale-specific writing style.LengthGateNode/BudgetRevisionNode: enforce per-section budgets before review and perform bounded budget revisions.PreservationGate,CoverageGate, andConflictGate: verify structured draft reuse whenexisting_contentis supplied.SimilarityGateNode: warns when generated text is too close to an exemplar chunk.ReferenceGeneratorNode: returnsNO_INLINE_CITATIONSinstead of generating a full bibliography when the draft has no inline citations.IntegrityGateNode: enforces strict academic checks whenstrict_academic_mode=True.MaterialRegistry: always enforces citation boundaries for uploaded, external, background-only, excluded, trusted, normal, or low-confidence materials when provided.
The RAG context is represented as evidence packets with chunk_id, title, authors, year, page, section, relevance score, relevance rationale, and quote. Writer output is audited through ClaimEvidenceBinding so citations are checked against the claim they support, not only against reference numbers.
Host applications should inspect quality_issues and quality_report whenever
status is waiting_human, failed, or completed; waiting_human can mean a
normal approval checkpoint or a quality block that needs caller action.
Caller State Options
The caller can pass optional fields in AgentState to control document profile,
structure, evidence policy, draft reuse, exemplars, and style:
state.update(
{
"document_type": "research_proposal",
"generation_config": {
"document_type": "research_proposal",
"target_chars": 2200,
},
"paper_type": "literature_review",
"structure_pattern": "thematic_review",
"target_word_count": 6000,
"strict_academic_mode": True,
"writing_locale": "zh-CN", # zh-CN, ja-JP, en-US, or a custom locale
"style_profile": "thesis",
"term_glossary": {"大语言模型": "大型语言模型"},
"style_pack_override": {
"display_name": "Custom thesis style",
"general_guidance": ["Use the institution-specific academic style."],
},
"material_registry": {
"entries": [
{
"paper_id": "user-paper-1",
"source_origin": "user_upload",
"citation_role": "citable",
"confidence": "trusted",
"required": True,
}
]
},
"check_required_material_relevance": True,
"existing_content": {
"draft_id": "draft-1",
"version_id": "v1",
"segments": [
{
"segment_id": "seg-1",
"text": "Existing paragraph text.",
"order": 1,
"detected_heading": "Introduction",
}
],
"preserve_policy": {
"mode": "preserve_as_much_as_possible",
"protected_segment_ids": ["seg-1"],
},
"user_intent": "expand",
},
"exemplar_materials": [
{
"exemplar_id": "ex-1",
"usage_role": "section_reference",
"outline_patterns": ["Motivation -> gap -> contribution"],
"style_notes": ["Use cautious synthesis language."],
}
],
"exemplar_chunks": [
{
"exemplar_id": "ex-1",
"chunk_id": "intro-example",
"section_title": "Introduction",
"text": "Example structure/style passage.",
}
],
}
)
material_registry source-boundary checks are always active when a registry is provided. The required-material relevance check is optional and only runs when check_required_material_relevance=True. Required citations are marked with material_registry.entries[].required=True.
RAG retrievers should populate DocumentChunk.metadata with paper_id or
source_paper_id, title, authors, year, page, section,
relevance_score, why_relevant, and quote when available. Matching
paper_id values to state["papers"] lets inline citations link back to
specific evidence packets.
Quick Start
Run the no-interrupt workflow:
export OPENAI_API_KEY="sk-..."
export SCHOLAR_TOPIC="large language model interpretability"
export SCHOLAR_LANGUAGE="zh"
uv run python examples/simple_workflow.py
Use the graph in your own code:
from langchain_openai import ChatOpenAI
from seele_scholar_agent import BudgetPolicy, GraphConfig, WritingPolicy
from seele_scholar_agent.graph import create_simple_writing_graph
from examples.common import build_initial_state, build_prompts
model = ChatOpenAI(model="gpt-4o-mini", api_key="sk-...")
state = build_initial_state(
"large language model interpretability",
document_type="academic_paper",
target_word_count=6000,
)
app = create_simple_writing_graph(
model=model,
prompts=build_prompts(),
rag_retriever=None,
graph_config=GraphConfig(enable_exemplar_context=False),
writing_policy=WritingPolicy(max_revisions=3),
budget_policy=BudgetPolicy(max_budget_revision_rounds=1),
)
result = await app.ainvoke(
state,
config={"configurable": {"thread_id": state["thread_id"]}},
)
Examples
Large runnable examples live in examples/:
| File | Purpose |
|---|---|
examples/common.py |
Shared model, prompt, and initial-state helpers |
examples/simple_workflow.py |
Full automatic workflow with create_simple_writing_graph |
examples/full_workflow_with_interrupts.py |
Human-in-the-loop topic and outline approval |
examples/research_proposal_workflow.py |
Research-proposal profile with proposal-friendly policy |
examples/custom_retrievers.py |
Inject custom RAG and paper retrievers |
examples/stream_nodes.py |
Stream a single node with astream() |
examples/figure_placeholders.py |
Parse {{FIGURE: ...}} and {{TABLE: ...}} placeholders |
Run any example from the repository root:
uv run python examples/full_workflow_with_interrupts.py
Core API
The package exposes two graph builders and policy/config models:
from seele_scholar_agent import (
BudgetPolicy,
GenerationMode,
GraphConfig,
WritingPolicy,
create_simple_writing_graph,
create_writing_graph,
)
create_writing_graph(...): includes interrupts after topic proposal and outline planning.create_simple_writing_graph(...): runs without interrupts and is useful for tests or batch jobs.
Both require:
model: aChatOpenAIinstance or compatible LangChain chat model.prompts: a completePromptsConfig.rag_retriever: optionalCallable[[str], Awaitable[list[DocumentChunk]]].
Optional controls:
graph_config:GraphConfigtopology switches, includinggeneration_mode=GenerationMode.SINGLE_SECTION, approval flags, draft integration, exemplar context, similarity gate, and post-processing nodes.writing_policy:WritingPolicyreview/citation behavior and max-revision fallback.budget_policy:BudgetPolicylength gate behavior.budget_allocator: optional dynamic section-budget allocator.extra_paper_retrievers: additional async paper search functions.
Workflow
START
-> topic_proposer
-> researcher
-> draft_integration
-> citation_source_gate
-> exemplar_planner_context
-> planner
-> outline_quality_gate
-> exemplar_section_retriever
-> writer
-> preservation/coverage/conflict gates
-> similarity_gate
-> length_gate
-> budget_reviser
-> reviewer
-> finalizer
-> reference_generator
-> consistency_checker
-> integrity_gate
-> END
create_writing_graph() interrupts after topic_proposer and planner so the caller can choose a topic and approve the outline.
Project Structure
src/seele_scholar_agent/
├── agent_config.py
├── document_profile.py
├── config.py
├── graph.py
├── i18n.py
├── logging.py
├── state.py
├── style_packs.py
├── budget/
├── citation/
├── draft/
├── exemplar/
├── nodes/
│ ├── topic_proposer.py
│ ├── researcher.py
│ ├── planner.py
│ ├── outline_quality_gate.py
│ ├── writer.py
│ ├── reviewer.py
│ ├── finalizer.py
│ ├── reference_generator.py
│ ├── consistency_checker.py
│ ├── integrity_gate.py
│ └── language_style_audit.py
├── policy/
├── profiles/
├── review/
├── writing/
└── tools/
└── crossref.py
Development
uv run pytest
uv run ruff check src/
uv run mypy src/
Build the package:
uv build
License
MIT
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file seele_scholar_agent-0.13.0.tar.gz.
File metadata
- Download URL: seele_scholar_agent-0.13.0.tar.gz
- Upload date:
- Size: 327.9 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
51a635539257a4e67da36043ee854b18416626dab3744d3d5e7a7435b1b2d388
|
|
| MD5 |
f3332d7cc07f18b4a0b9ed5caef231a5
|
|
| BLAKE2b-256 |
da8780b98760a8a98cedf96c19b246524a8644503dc8b5856e6be2fb10cd2643
|
Provenance
The following attestation bundles were made for seele_scholar_agent-0.13.0.tar.gz:
Publisher:
publish.yml on onekyuu/seele-scholar-agent
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
seele_scholar_agent-0.13.0.tar.gz -
Subject digest:
51a635539257a4e67da36043ee854b18416626dab3744d3d5e7a7435b1b2d388 - Sigstore transparency entry: 2020046333
- Sigstore integration time:
-
Permalink:
onekyuu/seele-scholar-agent@8b67a5a14a8f2cee3eed26ff58822a10ccaa0d74 -
Branch / Tag:
refs/tags/v0.13.0 - Owner: https://github.com/onekyuu
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@8b67a5a14a8f2cee3eed26ff58822a10ccaa0d74 -
Trigger Event:
push
-
Statement type:
File details
Details for the file seele_scholar_agent-0.13.0-py3-none-any.whl.
File metadata
- Download URL: seele_scholar_agent-0.13.0-py3-none-any.whl
- Upload date:
- Size: 123.9 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
30bc23296620e0600a7374da842f2b5dd64efa5f791a275d4cc2ce25d72a5910
|
|
| MD5 |
b08def3df8990c2c7d747b54c88983a3
|
|
| BLAKE2b-256 |
e575af4e68e5f1ea289823e29b26427f6b359b78639ec61aa69c31fadec1ebee
|
Provenance
The following attestation bundles were made for seele_scholar_agent-0.13.0-py3-none-any.whl:
Publisher:
publish.yml on onekyuu/seele-scholar-agent
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
seele_scholar_agent-0.13.0-py3-none-any.whl -
Subject digest:
30bc23296620e0600a7374da842f2b5dd64efa5f791a275d4cc2ce25d72a5910 - Sigstore transparency entry: 2020046393
- Sigstore integration time:
-
Permalink:
onekyuu/seele-scholar-agent@8b67a5a14a8f2cee3eed26ff58822a10ccaa0d74 -
Branch / Tag:
refs/tags/v0.13.0 - Owner: https://github.com/onekyuu
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@8b67a5a14a8f2cee3eed26ff58822a10ccaa0d74 -
Trigger Event:
push
-
Statement type: