Agentic NSF proposal first-draft generator with a panel review loop
Project description
instaprop
CLI-first open-source toolkit for drafting NSF proposals with an agentic review panel.
Give it a solicitation and a set of intellectual merit bullets. It returns a 5-page concept note vetted by a simulated panel of five NSF reviewers — in minutes, not weeks.
python3.12 -m pip install instaprop
export ANTHROPIC_API_KEY=sk-ant-...
instaprop generate \
--solicitation https://www.nsf.gov/funding/opportunities/.../nsf26-501/solicitation \
--contributions my_bullets.txt
How it works
instaprop supports three practical operating modes.
Starter Mode:
Solicitation + contribution brief
-> Writer
-> Review panel
-> Rubric + revision brief
-> inspect outputs manually
Best-Of-N Mode:
Solicitation + fixed contribution brief
-> Writer
-> Review panel
-> Rubric + revision brief
-> revise draft
-> repeat N rounds
-> pick best draft
Idea-On-Steroids Mode:
Solicitation + initial contribution brief
-> inner loop: draft -> review -> rubric -> revision brief
-> outer loop: refine the contribution brief itself
-> draft again from the improved brief
-> stop when target score / fatal-flag criteria are met
-> otherwise continue until max rounds, then return the best draft
In the strongest mode, there is a genuine double loop:
- inner loop: proposal development
- outer loop: idea / contribution-brief refinement
That lets the system improve not just the wording of the draft, but the raw research framing, novelty, literature positioning, and evaluation plan feeding the next draft round.
Inspired by Andrej Karpathy's writing on agentic AI systems, applied to the unglamorous but high-stakes task of academic proposal writing.
Installation
python3.12 -m pip install instaprop
# From source
git clone https://github.com/kavasserirocks/instaprop
cd instaprop
python3.12 -m pip install -e ".[dev]"
Set your Anthropic API key and verify the CLI:
export ANTHROPIC_API_KEY=sk-ant-...
instaprop check
If you want OpenAI-backed models instead, install the optional extra:
python3.12 -m pip install "instaprop[openai]"
If you want the local FastAPI server for proposal review/generation workflows:
python3.12 -m pip install "instaprop[api]"
uvicorn api:app --reload --port 8000
That server exposes:
GET /healthPOST /review/textPOST /review/filePOST /generate
If you are preparing a release, see RELEASE.md.
CLI
The CLI has four user-facing commands:
instaprop generate
instaprop scores
instaprop reviewers
instaprop check
For built-in help:
instaprop --help
instaprop generate --help
instaprop check --help
For a full CLI walkthrough, see docs/CLI.md.
CLI Modes
1. Starter Mode
Use this when you have a raw idea and want one quick pass to see whether the framing is viable.
instaprop generate \
--solicitation https://www.nsf.gov/funding/opportunities/.../solicitation \
--contributions idea.txt \
--rounds 1 \
--verbose
What it does:
- writes one draft
- runs one panel review
- emits a scorecard and revision log
- lets you inspect the first set of objections before spending more tokens
2. Best-Of-N Mode
Use this when you want the system to revise the proposal draft multiple times and then select the strongest round.
instaprop generate \
--solicitation https://www.nsf.gov/funding/opportunities/.../solicitation \
--contributions idea.txt \
--rounds 3 \
--verbose
You can increase --rounds up to 5 in the standard fixed-round mode.
What it does:
- runs a visible write-review-revise loop for
Nrounds - keeps the contribution brief fixed
- chooses the best-scoring draft at the end
3. Idea-On-Steroids Mode
Use this when you want the system to revise both the draft and the underlying contribution brief between rounds, and keep iterating toward a pass condition.
instaprop generate \
--solicitation https://www.nsf.gov/funding/opportunities/.../solicitation \
--contributions idea.txt \
--target-score 3.2 \
--stop-on-no-fatal-flags \
--max-rounds 5 \
--evolve-brief \
--verbose
What it does:
- drafts the proposal
- reviews it
- rewrites the contribution brief using reviewer feedback
- drafts again from the improved brief
- uses the target-score / no-fatal-flags criteria as the stop condition
- repeats until the proposal clears the threshold or hits the round cap
This is the strongest autonomous CLI mode currently implemented.
Generate
Supplying the solicitation
instaprop accepts the solicitation in two common forms:
1. NSF.gov URL (recommended — always current)
Use the solicitation page URL directly. Both the opportunity page and the full solicitation page are accepted:
instaprop generate \
--solicitation https://www.nsf.gov/funding/opportunities/collaboratory-advance-mathematics-education-learning-k-12/nsf26-501/solicitation \
--contributions my_bullets.txt
2. Plain text file (escape hatch)
instaprop generate \
--solicitation-file solicitation.txt \
--contributions my_bullets.txt
Supplying your contributions
Use a plain .txt file or a Markdown .md file for --contributions.
Markdown is optional; simple plain text is fully supported and is the
lowest-friction default.
Recommended:
instaprop generate \
--solicitation-file solicitation.txt \
--contributions my_bullets.txt
Also supported:
instaprop generate \
--solicitation-file solicitation.txt \
--contributions my_bullets.md
If your notes currently live in Word or PDF, convert them to .txt or .md
before passing them to the CLI.
All options
| Flag | Default | Description |
|---|---|---|
--solicitation / -s |
— | NSF.gov solicitation URL |
--solicitation-file |
— | Local plain-text solicitation |
--contributions / -c |
required | Plain text or Markdown file with intellectual merit bullets |
--output / -o |
draft.md |
Output path for the proposal draft |
--rounds / -r |
3 |
Writer-panel revision rounds (best-of-N) |
--target-score |
off | Optional early-stop threshold for rubric overall score |
--max-rounds |
--rounds |
Cap when using stop criteria |
--stop-on-no-fatal-flags |
off | Stop early once a round has no fatal rubric criteria |
--evolve-brief |
off | Rewrite the underlying contribution brief between rounds |
--provider |
anthropic |
LLM backend for writer + reviewers |
--model |
claude-opus-4-5 |
Model for writer + reviewers |
--extraction-provider |
anthropic |
LLM backend for non-URL solicitation extraction |
--extraction-model |
claude-haiku-4-5-20251001 |
Model for non-URL solicitation extraction |
--pages |
5 |
Target page count |
--verbose / -v |
off | Stream reviewer critiques and diffs to stdout |
--scorecard / --no-scorecard |
on | Emit .scorecard.json |
--log / --no-log |
on | Emit .revlog.md revision log |
Common command patterns
Default Anthropic flow:
instaprop generate \
--solicitation https://www.nsf.gov/funding/opportunities/.../nsf26-501/solicitation \
--contributions my_bullets.md
Save to a custom output path:
instaprop generate \
--solicitation-file solicitation.txt \
--contributions my_bullets.txt \
--output outputs/camel_draft.md
Run fewer rounds for a cheaper/faster pass:
instaprop generate \
--solicitation-file solicitation.txt \
--contributions my_bullets.txt \
--rounds 1
Keep iterating until the draft clears a target score or reaches the cap:
instaprop generate \
--solicitation https://www.nsf.gov/funding/opportunities/.../solicitation \
--contributions my_bullets.txt \
--target-score 3.2 \
--stop-on-no-fatal-flags \
--max-rounds 5 \
--verbose
Turn on the stronger self-correction loop that also rewrites the contribution brief:
instaprop generate \
--solicitation https://www.nsf.gov/funding/opportunities/.../solicitation \
--contributions my_bullets.txt \
--target-score 3.2 \
--stop-on-no-fatal-flags \
--max-rounds 5 \
--evolve-brief \
--verbose
Show reviewer progress and revision output live:
instaprop generate \
--solicitation https://www.nsf.gov/funding/opportunities/.../solicitation \
--contributions my_bullets.txt \
--verbose
Disable auxiliary files:
instaprop generate \
--solicitation https://www.nsf.gov/funding/opportunities/.../solicitation \
--contributions my_bullets.txt \
--no-scorecard \
--no-log
Use OpenAI-backed models if you installed the optional extra:
pip install "instaprop[openai]"
export OPENAI_API_KEY=...
instaprop generate \
--provider openai \
--model gpt-5-mini \
--solicitation-file solicitation.txt \
--contributions my_bullets.txt
Other commands
instaprop scores draft.scorecard.json # pretty-print a scorecard
instaprop reviewers # list the five reviewer personas
instaprop check # verify Anthropic connectivity
Check Anthropic explicitly:
instaprop check --provider anthropic
Check OpenAI explicitly:
instaprop check --provider openai
Outputs
| File | Contents |
|---|---|
draft.md |
Best-round proposal draft in Markdown |
draft.scorecard.json |
Per-reviewer and per-criterion scores for each round |
draft.revlog.md |
Revision log: rubric scorecard + panel discussion + revision brief per round |
Scorecard structure
The scorecard is rubric-aligned. Each round contains:
- Per-reviewer overall (1–4, mapping to NSF tier labels)
- Per-criterion scores across 17 rubric criteria in 4 sections
- Fatal flags — any criterion any reviewer scored 1
- Section weighted scores (first two pages 25%, merit 35%, impacts 20%, writing 20%)
- Simulated panel discussion — what each reviewer would say aloud
- Revision brief — prioritised action list for the writer
Data handling, risks, and roadmap
- Your local files stay on your machine, but the text you send (solicitation, contributions, drafts) goes to the LLM provider you configure (Anthropic by default, OpenAI optional). No telemetry beyond model calls.
- There is no fully local inference path today; the model provider sees the prompt. Redact sensitive details (names, orgs, novel methods, unpublished data) before running if disclosure is a concern.
- Review-only mode can run on a redacted draft: replace sensitive sections with placeholders, review, then reinsert privately.
- Provider controls: Anthropic supports no-training/no-retention for some org tiers; OpenAI-style local servers (Ollama/vLLM) can eliminate external calls if you self-host (see below).
- Risks: provider retention misconfiguration, accidental disclosure of sensitive methods/data, weaker quality on small self-hosted models, and divergence between simulated panel behavior and real reviewers.
- Disclaimers: this is a research/iteration aid, not a guarantee of funding or reviewer acceptance; you are responsible for redaction and compliance with your institution’s data policies.
- Coming soon: hosted LLM presets (provider + base URL) so you can switch between commercial endpoints and self-hosted backends without changing the workflow.
Self-hosted local model plan (OpenAI-compatible)
- Pick a local OpenAI-compatible server:
ollama serveorvllm --api. Use a capable model (quality improves with size; GPUs recommended for >8B models). - Install the OpenAI extra:
python3.12 -m pip install "instaprop[openai]". - Run your local server, then point instaprop at it with a base URL and a dummy key (base-URL support will land alongside the
openaiprovider; if you don't see the flag yet, treat this as the intended configuration):
OLLAMA_HOST=http://localhost:11434 # example
python3.12 -m pip install "instaprop[openai]"
instaprop generate \
--provider openai \
--openai-base-url http://localhost:11434/v1 \
--openai-api-key dummy \
--model llama3 \
--solicitation https://www.nsf.gov/... \
--contributions idea.txt \
--rounds 1 \
--verbose
- Throughput/quality caveat: small local models will underperform Anthropic/OpenAI frontier models; expect weaker drafts and reviews unless you run a strong GPU-backed model.
The review panel
Each reviewer scores only the criteria within their focus area — the rubric aggregator merges sparse partial scorecards into a complete weighted score.
| Reviewer | Focus | Primary criteria |
|---|---|---|
| Domain expert | Field-specific depth | novelty, detail, feasibility, literature, scope |
| Program officer | NSF fit & fundability | hook, gap, approach summary, scope |
| Methodological skeptic | Rigor & validity | detail, feasibility, problem statement, scope |
| Interdisciplinary generalist | Clarity & framing | hook, gap, prose, coherence, structure |
| Societal impact | Real-world stakes | specific impact, application, exciting |
Writing your contributions file
The --contributions file is the most important input. A strong one:
## Project title
Learning Causal Structure from Observational Time-Series
## Core intellectual contributions
- **Non-trivial novelty**: We introduce a provably identifiable estimator for
causal DAGs in non-stationary, non-Gaussian settings — a regime where
existing methods (PCMCI, Dynotears) fail.
- **Theoretical foundation**: We prove identification under a new "local
stationarity" condition weaker than global stationarity, with sample
complexity bounds.
- **Scalable algorithm**: Our algorithm runs in O(T log T d²) vs O(T² d³)
for existing approaches, enabling application to climate and neuroscience
datasets (T > 10⁶, d > 500).
- **Empirical validation**: Three domains — synthetic benchmarks, ERA5 climate
data with expert-annotated causal structure, Allen Brain Atlas neural recordings.
## Why this is non-trivial
Existing methods assume stationarity or Gaussianity — neither holds in
the target domains. Relaxing both simultaneously requires a new theoretical
framework, not just an engineering improvement.
The panel's domain expert will scrutinise these claims hardest. Be specific, be falsifiable, cite the gap.
Contributing
Contributions welcome. The highest-value areas:
- Reviewer prompts (
instaprop/prompts/reviewer_prompts.py) — if you have served on NSF panels, your input on the personas is invaluable - Evaluation harness — scoring generated drafts against real funded proposals
- CLI ergonomics — better progress output, resumability, and export options
--resumeflag — continue from a saved round state
Publishing
The package metadata is set up for PyPI/TestPyPI publication. The minimal flow is:
python3.12 -m pip install -e ".[dev]"
python3.12 -m build
twine check dist/*
twine upload dist/*
For a fuller release checklist, use RELEASE.md.
Roadmap
- v0.1 — Core agentic loop
- v0.1 — NSF.gov URL fetcher (HTML + structured extraction)
- v0.1 — PDF solicitation reader (pypdf → Claude extraction)
- v0.1 — Rubric-aligned scorecard (17 criteria, 4 sections, fatal flags)
- v0.1 — Panel discussion simulation + revision brief
- v0.2 — Streaming output to terminal during generation
- v0.2 —
--resumeflag to continue from a saved round - v0.3 — Configurable reviewer personas via YAML
- v0.4 — Better CLI workflows and integration hooks for external frontends
- v1.0 — Full 15-page proposal mode
License
MIT. See LICENSE.
Citation
If instaprop contributes to a funded proposal (congratulations!), consider adding an acknowledgment:
This proposal draft was developed with assistance from instaprop (https://github.com/kavasserirocks/instaprop).
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 instaprop-0.1.1.tar.gz.
File metadata
- Download URL: instaprop-0.1.1.tar.gz
- Upload date:
- Size: 47.4 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.12.3
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
edc9c422be7875decf41754c832492b634f1ac2579d945b4785c13072efd302e
|
|
| MD5 |
0406d1c14d02d781e72a68fd6d5df6c1
|
|
| BLAKE2b-256 |
58b3b29dcbcc7b8dc33e5b79a6648b89c386c824ed4728af88eb4ed80d2186a3
|
File details
Details for the file instaprop-0.1.1-py3-none-any.whl.
File metadata
- Download URL: instaprop-0.1.1-py3-none-any.whl
- Upload date:
- Size: 46.7 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.12.3
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
d379c847b7d32999af08f0476ba117096c9ada959605dd9de25e3228165cfeb2
|
|
| MD5 |
62ce6d96c51b16bf08c1dddd989fc3c3
|
|
| BLAKE2b-256 |
91f0d9ca3df545f43d3021af59404c57aa65219b3a4ecb9adc5da267b9345c7a
|