We are going to build a grant writing assistant that turns a rough project summary into a structured NIH-style proposal narrative. It is useful for researchers and non-profit teams who need to produce consistent, persuasive grant sections without starting from a blank page.
What you'll need
- Python 3.10 or newer
- The OpenAI SDK:
pip install openai - An Oxlo.ai API key from https://portal.oxlo.ai
- A project summary to test with
Step 1: Set up the Oxlo.ai client
Create a client pointing to Oxlo.ai. I use Llama 3.3 70B as the workhorse because it handles long-form instruction following reliably.
import os
from openai import OpenAI
client = OpenAI(
base_url="https://api.oxlo.ai/v1",
api_key=os.environ.get("OXLO_API_KEY")
)
DRAFT_MODEL = "llama-3.3-70b"
Step 2: Design the grant writer system prompt
The system prompt constrains the model to NIH R01 structure and tone. Keep it editable so you can adapt it to NSF, ERC, or foundation formats later.
SYSTEM_PROMPT = """You are a senior grant writing consultant.
Given a project summary, write a compelling Specific Aims page for an NIH R01 proposal.
Rules:
- Use one page and three specific aims.
- Start with a bold, one-paragraph significance statement.
- Follow with a numbered list of aims. Each aim must state a hypothesis and a brief approach.
- Use active voice. Avoid jargon where possible.
- Do not include budget or references.
"""
Step 3: Build the core generation loop
This function sends the user summary to Oxlo.ai and returns the drafted section.
def draft_specific_aims(project_summary: str) -> str:
response = client.chat.completions.create(
model=DRAFT_MODEL,
messages=[
{"role": "system", "content": SYSTEM_PROMPT},
{"role": "user", "content": project_summary},
],
temperature=0.4,
max_tokens=2048,
)
return response.choices[0].message.content
# Example input
summary = (
"We propose to develop a low-cost, paper-based microfluidic sensor "
"for detecting lead in drinking water. Our prior work showed that "
"colorimetric assays can reach ppb sensitivity. We will optimize "
"the chemistry, validate against EPA standards, and field-test in "
"three communities."
)
draft = draft_specific_aims(summary)
print(draft)
Step 4: Add section chunking for long proposals
Full proposals contain multiple sections. We iterate over a list, generating each with shared context. Because Oxlo.ai uses request-based pricing, expanding a long proposal through multiple refinement passes stays predictable no matter how many tokens you send per call. See https://oxlo.ai/pricing for details.
def draft_full_proposal(project_summary: str, sections: list[str]) -> dict[str, str]:
proposal = {}
for section in sections:
prompt = f"Project summary:\n{project_summary}\n\nWrite only the {section}."
response = client.chat.completions.create(
model=DRAFT_MODEL,
messages=[
{"role": "system", "content": SYSTEM_PROMPT},
{"role": "user", "content": prompt},
],
temperature=0.4,
max_tokens=2048,
)
proposal[section] = response.choices[0].message.content
return proposal
sections = ["Specific Aims", "Research Strategy", "Innovation"]
full_draft = draft_full_proposal(summary, sections)
for sec, text in full_draft.items():
print(f"--- {sec} ---\n{text[:500]}...\n")
Step 5: Add a self-critique revision step
I add a second pass that critiques the draft against review criteria, then rewrites it. I use DeepSeek V3.2 for this reasoning step.
CRITIQUE_MODEL = "deepseek-v3.2"
def revise_aims(draft: str, project_summary: str) -> str:
critique_prompt = (
f"Original summary:\n{project_summary}\n\n"
f"Draft:\n{draft}\n\n"
"Critique this Specific Aims page on significance, innovation, and approach. "
"Then rewrite it, incorporating the critique. Output only the revised page."
)
response = client.chat.completions.create(
model=CRITIQUE_MODEL,
messages=[
{"role": "system", "content": "You are a NIH study section reviewer."},
{"role": "user", "content": critique_prompt},
],
temperature=0.3,
max_tokens=2048,
)
return response.choices[0].message.content
revised = revise_aims(full_draft["Specific Aims"], summary)
print(revised)
Run it
Wire everything together and call the finished agent from a main block.
if __name__ == "__main__":
summary = (
"We propose to develop a low-cost, paper-based microfluidic sensor "
"for detecting lead in drinking water. Our prior work showed that "
"colorimetric assays can reach ppb sensitivity. We will optimize "
"the chemistry, validate against EPA standards, and field-test in "
"three communities."
)
sections = ["Specific Aims", "Research Strategy", "Innovation"]
proposal = draft_full_proposal(summary, sections)
proposal["Specific Aims"] = revise_aims(proposal["Specific Aims"], summary)
for sec, text in proposal.items():
print(f"===== {sec} =====")
print(text[:800])
print("\n")
Expected output looks like this:
===== Specific Aims =====
Lead contamination in drinking water remains a critical public health issue. Current laboratory-based detection methods are expensive and slow, limiting screening in resource-limited settings. We have developed a paper-based microfluidic platform that enables colorimetric detection at parts-per-billion sensitivity. The proposed research will translate this platform into a field-ready tool.
Aim 1: Optimize the colorimetric chemistry for lead specificity and stability.
Hypothesis: A chelator-modified gold nanoparticle assay will achieve EPA-compliant detection limits on paper substrates.
Approach: We will screen 20 chelator variants, quantify limit-of-detection, and assess shelf-life under variable humidity.
Aim 2: Validate sensor performance against gold-standard EPA methods.
Hypothesis: The paper sensor will show >95% concordance with ICP-MS across 200 field samples.
Approach: Paired testing in three municipalities with known lead line infrastructure.
Aim 3: Assess usability and adoption in community settings.
Hypothesis: Lay users can operate the sensor with >90% accuracy after brief training.
Approach: Human-factors study with 50 participants; iterative design refinement.
===== Research Strategy =====
[Truncated for brevity; the model produced a three-paragraph strategy covering significance, preliminary data, and expected outcomes...]
===== Innovation =====
[Truncated; the model highlighted the integration of microfluidics with citizen-science deployment models...]
Next steps
Connect this script to a document store so it can ingest your past funded grants as style references. Oxlo.ai's request-based pricing means you can include long examples in the prompt without worrying about token costs per call.
Add a FastAPI wrapper and expose the agent as an internal tool for your lab or development team.
Top comments (0)