Grant writing consumes weeks of a research team's calendar. In this tutorial, I will build a Python agent that turns a rough project idea and a target agency into a structured NSF-style proposal draft. Because Oxlo.ai charges a flat rate per request instead of per token, I can feed the model full RFP documents and lengthy reference lists without the cost scaling with every paragraph.
What you'll need
- Python 3.10+
- The OpenAI SDK:
pip install openai - An Oxlo.ai API key from https://portal.oxlo.ai
Step 1: Generate a structured outline
I start by defining the system prompt that governs tone and agency conventions, then ask Llama 3.3 70B to convert a project summary into a rigid JSON outline.
import json
from openai import OpenAI
SYSTEM_PROMPT = """You are a senior grant writing consultant who specializes in NSF, NIH, and DOE proposals. You write in formal academic English. You never fabricate citations or statistics. When given a project idea and a target agency, you produce proposal content that follows that agency's standard format and review criteria. Return all structured output as valid JSON when requested."""
client = OpenAI(base_url="https://api.oxlo.ai/v1", api_key="YOUR_OXLO_API_KEY")
project_idea = (
"Develop a low-cost sensor network for real-time monitoring of microplastics "
"in municipal water supplies using edge AI on Raspberry Pi hardware."
)
agency = "NSF"
outline_prompt = f"""Create a detailed proposal outline for the following project idea targeting {agency}.
Project: {project_idea}
Return a JSON object with keys: title, specific_aims_summary, background_points, methodology_sections, evaluation_plan, budget_categories.
Each value should be a string or list of strings."""
response = client.chat.completions.create(
model="llama-3.3-70b",
messages=[
{"role": "system", "content": SYSTEM_PROMPT},
{"role": "user", "content": outline_prompt},
],
)
outline = json.loads(response.choices[0].message.content)
print(json.dumps(outline, indent=2))
Step 2: Draft the specific aims and background
With the outline in hand, I generate the two most important narrative sections. I pass the full outline back into the context so the model stays consistent. Because Oxlo.ai uses flat per-request pricing, this long input does not inflate the cost. See https://oxlo.ai/pricing for current plan details.
import json
from openai import OpenAI
outline = {
"title": "Edge AI for Real-Time Microplastic Monitoring in Municipal Water",
"specific_aims_summary": "Build low-cost sensors, deploy on Raspberry Pi, validate in three cities",
"background_points": ["Microplastics are pervasive", "Current lab methods are slow", "Edge AI enables real-time alerts"],
"methodology_sections": ["Hardware design", "Model training", "Field deployment"],
"evaluation_plan": "Compare against EPA standard methods",
"budget_categories": ["Personnel", "Equipment", "Travel", "Indirect"]
}
SYSTEM_PROMPT = """You are a senior grant writing consultant who specializes in NSF, NIH, and DOE proposals. You write in formal academic English. You never fabricate citations or statistics."""
client = OpenAI(base_url="https://api.oxlo.ai/v1", api_key="YOUR_OXLO_API_KEY")
narrative_prompt = f"""Using this outline, write the Specific Aims and Background sections for an NSF proposal.
Outline: {json.dumps(outline)}
Specific Aims should be one page and include: overall objective, three concrete aims, and expected outcomes.
Background should justify the need, cite real knowledge domains (do not invent specific papers), and explain why edge AI is necessary for this problem."""
response = client.chat.completions.create(
model="kimi-k2.6",
messages=[
{"role": "system", "content": SYSTEM_PROMPT},
{"role": "user", "content": narrative_prompt},
],
)
narrative = response.choices[0].message.content
print(narrative)
Step 3: Build the methodology and timeline
Next, I generate the methodology section with a concrete timeline and milestones table. I use DeepSeek V3.2 because it reasons well about structured technical plans.
import json
from openai import OpenAI
outline = {
"title": "Edge AI for Real-Time Microplastic Monitoring in Municipal Water",
"specific_aims_summary": "Build low-cost sensors, deploy on Raspberry Pi, validate in three cities",
"background_points": ["Microplastics are pervasive", "Current lab methods are slow", "Edge AI enables real-time alerts"],
"methodology_sections": ["Hardware design", "Model training", "Field deployment"],
"evaluation_plan": "Compare against EPA standard methods",
"budget_categories": ["Personnel", "Equipment", "Travel", "Indirect"]
}
SYSTEM_PROMPT = """You are a senior grant writing consultant who specializes in NSF, NIH, and DOE proposals. You write in formal academic English. You never fabricate citations or statistics."""
client = OpenAI(base_url="https://api.oxlo.ai/v1", api_key="YOUR_OXLO_API_KEY")
method_prompt = f"""Write the Methodology and Timeline sections for an NSF proposal based on this outline.
Outline: {json.dumps(outline)}
Methodology must describe: sensor hardware, on-device inference pipeline, calibration protocol, and field validation.
Timeline must be a markdown table with quarters (Q1-Q8) and milestones for years 1 and 2."""
response = client.chat.completions.create(
model="deepseek-v3.2",
messages=[
{"role": "system", "content": SYSTEM_PROMPT},
{"role": "user", "content": method_prompt},
],
)
methodology = response.choices[0].message.content
print(methodology)
Step 4: Generate budget justification
The budget narrative must align with the methodology. I ask the model to produce a justification for each category and explain how funds map to specific milestones.
import json
from openai import OpenAI
outline = {
"title": "Edge AI for Real-Time Microplastic Monitoring in Municipal Water",
"specific_aims_summary": "Build low-cost sensors, deploy on Raspberry Pi, validate in three cities",
"background_points": ["Microplastics are pervasive", "Current lab methods are slow", "Edge AI enables real-time alerts"],
"methodology_sections": ["Hardware design", "Model training", "Field deployment"],
"evaluation_plan": "Compare against EPA standard methods",
"budget_categories": ["Personnel", "Equipment", "Travel", "Indirect"]
}
SYSTEM_PROMPT = """You are a senior grant writing consultant who specializes in NSF, NIH, and DOE proposals. You write in formal academic English. You never fabricate citations or statistics."""
client = OpenAI(base_url="https://api.oxlo.ai/v1", api_key="YOUR_OXLO_API_KEY")
budget_prompt = f"""Write a Budget Justification section for an NSF proposal.
Project: {outline['title']}
Categories: {', '.join(outline['budget_categories'])}
For each category, explain the need, estimate the percentage of total budget, and link it to a specific aim or milestone. Keep language conservative and compliant with NSF guidelines."""
response = client.chat.completions.create(
model="llama-3.3-70b",
messages=[
{"role": "system", "content": SYSTEM_PROMPT},
{"role": "user", "content": budget_prompt},
],
)
budget = response.choices[0].message.content
print(budget)
Step 5: Assemble the final proposal document
Finally, I combine all sections into a single markdown document with a cover page and table of contents. I feed the prior outputs into Kimi K2.6 to ensure the voice stays consistent across the full draft.
import json
from openai import OpenAI
outline = {
"title": "Edge AI for Real-Time Microplastic Monitoring in Municipal Water",
"budget_categories": ["Personnel", "Equipment", "Travel", "Indirect"]
}
narrative = """Specific Aims: The objective is to develop a low-cost sensor network for real-time microplastic monitoring in municipal water. Aim 1 will design an optical sensor paired with a lightweight CNN. Aim 2 will optimize inference for Raspberry Pi 4/5 hardware. Aim 3 will validate the network across three municipal treatment facilities over 12 months. Background: Microplastic contamination is pervasive in water supplies. Current standard methods require expensive lab analysis with multi-day turnaround. Edge AI enables continuous, low-cost alerting at the point of sampling."""
methodology = """Methodology: Hardware design will iterate on open-source spectrometer plans. The model training pipeline will use synthetic microplastic spectra augmented with field captures. Deployment will follow a phased rollout across three partner cities. Timeline: Q1-Q2 sensor prototyping, Q3-Q4 model compression and bench testing, Q5-Q8 field deployment and validation."""
budget = """Budget Justification: Personnel (50%) supports two graduate students and a postdoc. Equipment (30%) covers sensor components, Raspberry Pi units, and calibration standards. Travel (10%) funds site visits to partner municipalities. Indirect costs (10%) follow the university's negotiated rate."""
SYSTEM_PROMPT = """You are a senior grant writing consultant who specializes in NSF, NIH, and DOE proposals. You write in formal academic English. You never fabricate citations or statistics."""
client = OpenAI(base_url="https://api.oxlo.ai/v1", api_key="YOUR_OXLO_API_KEY")
assembly_prompt = f"""Assemble the following sections into a complete NSF proposal draft.
Title: {outline['title']}
1. Specific Aims and Background
{narrative}
2. Methodology and Timeline
{methodology}
3. Budget Justification
{budget}
Add a cover page with the title, PI name placeholder, and date. Format everything as clean markdown."""
response = client.chat.completions.create(
model="kimi-k2.6",
messages=[
{"role": "system", "content": SYSTEM_PROMPT},
{"role": "user", "content": assembly_prompt},
],
)
proposal = response.choices[0].message.content
with open("nsf_proposal_draft.md", "w") as f:
f.write(proposal)
print("Saved to nsf_proposal_draft.md")
Run it
Running the full pipeline end-to-end produces a coherent draft. Here is the consolidated script that executes each step in sequence and writes the assembled proposal to disk.
import json
from openai import OpenAI
SYSTEM_PROMPT = """You are a senior grant writing consultant who specializes in NSF, NIH, and DOE proposals. You write in formal academic English. You never fabricate citations or statistics."""
client = OpenAI(base_url="https://api.oxlo.ai/v1", api_key="YOUR_OXLO_API_KEY")
project_idea = (
"Develop a low-cost sensor network for real-time monitoring of microplastics "
"in municipal water supplies using edge AI on Raspberry Pi hardware."
)
agency = "NSF"
# Step 1: Outline
outline_prompt = f"""Create a detailed proposal outline for the following project idea targeting {agency}.
Project: {project_idea}
Return a JSON object with keys: title, specific_aims_summary, background_points, methodology_sections, evaluation_plan, budget_categories."""
r1 = client.chat.completions.create(
model="llama-3.3-70b",
messages=[
{"role": "system", "content": SYSTEM_PROMPT},
{"role": "user", "content": outline_prompt},
],
)
outline = json.loads(r1.choices[0].message.content)
# Step 2: Narrative
narrative_prompt = f"""Using this outline, write the Specific Aims and Background sections for an NSF proposal.
Outline: {json.dumps(outline)}
Specific Aims should be one page with overall objective, three concrete aims, and expected outcomes.
Background should justify the need and explain why edge AI is necessary."""
r2 = client.chat.completions.create(
model="kimi-k2.6",
messages=[
{"role": "system", "content": SYSTEM_PROMPT},
{"role": "user", "content": narrative_prompt},
],
)
narrative = r2.choices[0].message.content
# Step 3: Methodology
method_prompt = f"""Write the Methodology and Timeline sections.
Outline: {json.dumps(outline)}
Include a markdown table with Q1-Q8 milestones for years 1 and 2."""
r3 = client.chat.completions.create(
model="deepseek-v3.2",
messages=[
{"role": "system", "content": SYSTEM_PROMPT},
{"role": "user", "content": method_prompt},
],
)
methodology = r3.choices[0].message.content
# Step 4: Budget
budget_prompt = f"""Write a Budget Justification for: {outline['title']}.
Categories: {', '.join(outline['budget_categories'])}
Link each category to specific aims. Keep language NSF-compliant."""
r4 = client.chat.completions.create(
model="llama-3.3-70b",
messages=[
{"role": "system", "content": SYSTEM_PROMPT},
{"role": "user", "content": budget_prompt},
],
)
budget = r4.choices[0].message.content
# Step 5: Assemble
assembly_prompt = f"""Assemble into a complete NSF proposal draft.
Title: {outline['title']}
1. Specific Aims and Background
{narrative}
2. Methodology and Timeline
{methodology}
3. Budget Justification
{budget}
Add a cover page. Format as clean markdown."""
r5 = client.chat.completions.create(
model="kimi-k2.6",
messages=[
{"role": "system", "content": SYSTEM_PROMPT},
{"role": "user", "content": assembly_prompt},
],
)
with open("nsf_proposal_draft.md", "w") as f:
f.write(r5.choices[0].message.content)
print("Draft saved to nsf_proposal_draft.md")
Example output:
Draft saved to nsf_proposal_draft.md
--- nsf_proposal_draft.md ---
# Edge AI for Real-Time Microplastic Monitoring in Municipal Water
**Principal Investigator:** [Name Redacted]
**Date:** 2025-01-15
**Agency:** National Science Foundation
## 1. Specific Aims
The objective of this proposal is to develop and validate a low-cost, edge-based sensor network capable of detecting microplastic concentrations in municipal water supplies in real time. We pursue three specific aims:
**Aim 1:** Design a portable optical sensor paired with a lightweight convolutional neural network that classifies microplastic fragments down to 10 micrometers.
**Aim 2:** Optimize and compress the inference pipeline for Raspberry Pi 4/5 hardware to achieve sub-second latency under field power constraints.
**Aim 3:** Deploy the sensor network across three distinct municipal water treatment facilities and validate measurements against EPA Method 1664 over a 12-month period.
We expect this work to produce an open-source hardware reference design, a public dataset of annotated microplastic spectra, and a validated protocol for municipal adoption.
## 2. Background
Microplastic contamination is now recognized as a persistent pollutant in municipal water systems worldwide. Current standard analytical methods, while accurate, require centralized laboratory facilities, expensive spectroscopy equipment, and multi-day turnaround times. This delay prevents treatment plant operators from responding to contamination events in real time. Edge AI offers a viable alternative by embedding lightweight neural networks directly on low-cost hardware at the sampling point, enabling continuous monitoring and immediate alerting without relying on cloud connectivity or lab capacity.
## 3. Methodology and Timeline
[... continues with table and budget ...]
Next steps
Swap the static outline step for a retrieval pipeline using Oxlo.ai's embeddings endpoint to ground the Background section in real papers from arXiv. You can also add a critique loop where a second pass with DeepSeek R1 671B MoE scores the draft against the original RFP review criteria and suggests concrete revisions.
Top comments (0)