
Grant writing is repetitive, structured, and deadline-heavy. In this tutorial, I will walk you through building a grant writing assistant that ingests your project summary and funder guidelines, then produces a full first draft with a built-in self-critique loop. The entire pipeline runs against Oxlo.ai's OpenAI-compatible API, so you can iterate without managing tokens or cold starts.
What you'll need
Before you start, gather the following items. I recommend exporting your API key as an environment variable so it never lives in your source code.
- Python 3.10 or newer installed on your machine
- An Oxlo.ai API key from https://portal.oxlo.ai
- The OpenAI SDK installed:
pip install openai - A project summary and funder guidelines text file to test against
Step 1: Set up the Oxlo.ai client
We start with a minimal client setup. Oxlo.ai exposes a fully OpenAI-compatible endpoint, so the only change from a standard OpenAI script is the base URL. I use llama-3.3-70b for the initial smoke test because it is Oxlo.ai's general-purpose flagship and has no cold start, which means the first request returns immediately even if you have not called it in hours.
from openai import OpenAI
client = OpenAI(
base_url="https://api.oxlo.ai/v1",
api_key="YOUR_OXLO_API_KEY"
)
response = client.chat.completions.create(
model="llama-3.3-70b",
messages=[{"role": "user", "content": "Say 'Oxlo.ai client ready'"}],
)
print(response.choices[0].message.content)
Step 2: Write the grant writer system prompt
The system prompt is the only behavioral contract this agent has. I make it explicit about section headers, tone, and how to handle missing data. This prevents the model from inventing budget figures or omitting a timeline entirely. Keeping it in a top-level constant makes it easy to track in version control and tweak when you switch funders.
SYSTEM_PROMPT = """You are an expert grant writer with 15 years of experience winning NIH, NSF, and foundation funding. Your job is to transform a rough project summary and funder guidelines into a compelling, structured proposal.
Rules:
- Output exactly these sections: Executive Summary, Specific Aims, Project Narrative, Timeline, Budget Justification.
- Align every claim with the funder's stated priorities and evaluation criteria.
- Use active voice, specific metrics, and plain language. Avoid jargon unless the funder expects it.
- If critical information is missing (sample size, partner organization, total budget), note it in [brackets] rather than halting.
- Keep the Project Narrative under 1,500 words.
"""
Step 3: Build the draft generator
With the prompt defined, we build the drafting function. I chose kimi-k2.6 for this step because its 131K context window swallows lengthy request-for-proposal documents whole, and its advanced reasoning capabilities handle the subtle alignment between project aims and evaluation criteria. The user message simply concatenates your project summary and the funder guidelines. Because Oxlo.ai uses request-based pricing, the cost is flat per call no matter how long those guidelines are. For grant writers who routinely paste 50-page RFPs into the context window, that pricing model removes the usual token anxiety. You can see the details at https://oxlo.ai/pricing.
from openai import OpenAI
client = OpenAI(
base_url="https://api.oxlo.ai/v1",
api_key="YOUR_OXLO_API_KEY"
)
def generate_draft(project_summary: str, funder_guidelines: str) -> str:
user_message = f"""Project Summary:
{project_summary}
Funder Guidelines:
{funder_guidelines}
Draft the full proposal following the required sections."""
response = client.chat.completions.create(
model="kimi-k2.6",
messages=[
{"role": "system", "content": SYSTEM_PROMPT},
{"role": "user", "content": user_message},
],
)
return response.choices[0].message.content
project = """
We are building a mobile app that uses LLM-based conversational agents to help
low-income diabetes patients in rural counties adhere to medication schedules.
The app will integrate with existing EHR systems at 3 community health centers.
PI: Dr. Jane Smith. Duration: 3 years. Budget: $1.2M.
"""
guidelines = """
The funder prioritizes:
1. Health equity and underserved populations.
2. Scalable technology with open APIs.
3. Strong community partnerships and letters of support.
4. Clear evaluation metrics (HbA1c improvement, medication adherence rates).
"""
draft = generate_draft(project, guidelines)
print(draft[:500])
Step 4: Add a critique and refine loop
A first draft is never submission-ready. I add a second pass that treats the model as a review panelist. It scores the draft against each funder priority on a 1 to 5 scale, then rewrites any section scoring below a 4. I use deepseek-v3.2 here because its coding and reasoning background produces concise, criteria-driven edits rather than flowery rewrites.
from openai import OpenAI
client = OpenAI(
base_url="https://api.oxlo.ai/v1",
api_key="YOUR_OXLO_API_KEY"
)
CRITIQUE_PROMPT = """You are a stern NIH review panelist. Score the proposal below on each of the funder's priorities (1-5). Then rewrite any section scoring below 4 to strengthen alignment, add specificity, and fix vague language. Output the full revised proposal."""
def critique_and_refine(draft: str, funder_guidelines: str) -> str:
user_message = f"""Funder Priorities:
{funder_guidelines}
Original Proposal:
{draft}
Provide scores and the revised proposal."""
response = client.chat.completions.create(
model="deepseek-v3.2",
messages=[
{"role": "system", "content": CRITIQUE_PROMPT},
{"role": "user", "content": user_message},
],
)
return response.choices[0].message.content
# Using the draft and guidelines from Step 3
final = critique_and_refine(draft, guidelines)
print(final[:500])
Step 5: Save the final proposal
Finally, we need to persist the output. This helper strips any leaked score lines, ensures the markdown headers are clean, and writes a timestamped file you can open in any editor or paste into Google Docs.
from datetime import datetime
def save_proposal(final_text: str, filename: str = "proposal.md"):
lines = final_text.splitlines()
output = []
for line in lines:
if line.strip().startswith("Score:"):
continue
output.append(line)
content = "\n".join(output)
with open(filename, "w", encoding="utf-8") as f:
f.write(f"# Grant Proposal\n\nGenerated: {datetime.now().isoformat()}\n\n")
f.write(content)
print(f"Saved to {filename}")
save_proposal(final)
Run it
With the pieces in place, the execution flow is straightforward. Below is the complete script that chains the draft, critique, and save steps. I feed in a realistic project summary for a rural diabetes adherence app and NIH-style priorities.
from openai import OpenAI
from datetime import datetime
client = OpenAI(
base_url="https://api.oxlo.ai/v1",
api_key="YOUR_OXLO_API_KEY"
)
SYSTEM_PROMPT = """You are an expert grant writer with 15 years of experience winning NIH, NSF, and foundation funding. Your job is to transform a rough project summary and funder guidelines into a compelling, structured proposal.
Rules:
- Output exactly these sections: Executive Summary, Specific Aims, Project Narrative, Timeline, Budget Justification.
- Align every claim with the funder's stated priorities and evaluation criteria.
- Use active voice, specific metrics, and plain language. Avoid jargon unless the funder expects it.
- If critical information is missing (sample size, partner organization, total budget), note it in [brackets] rather than halting.
- Keep the Project Narrative under 1,500 words.
"""
CRITIQUE_PROMPT = """You are a stern NIH review panelist. Score the proposal below on each of the funder's priorities (1-5). Then rewrite any section scoring below 4 to strengthen alignment, add specificity, and fix vague language. Output the full revised proposal."""
def generate_draft(project_summary: str, funder_guidelines: str) -> str:
user_message = f"""Project Summary:
{project_summary}
Funder Guidelines:
{funder_guidelines}
Draft the full proposal following the required sections."""
response = client.chat.completions.create(
model="kimi-k2.6",
messages=[
{"role": "system", "content": SYSTEM_PROMPT},
{"role": "user", "content": user_message},
],
)
return response.choices[0].message.content
def critique_and_refine(draft: str, funder_guidelines: str) -> str:
user_message = f"""Funder Priorities:
{funder_guidelines}
Original Proposal:
{draft}
Provide scores and the revised proposal."""
response = client.chat.completions.create(
model="deepseek-v3.2",
messages=[
{"role": "system", "content": CRITIQUE_PROMPT},
{"role": "user", "content": user_message},
],
)
return response.choices[0].message.content
def save_proposal(final_text: str, filename: str = "proposal.md"):
lines = final_text.splitlines()
output = [line for line in lines if not line.strip().startswith("Score:")]
content = "\n".join(output)
with open(filename, "w", encoding="utf-8") as f:
f.write(f"# Grant Proposal\n\nGenerated: {datetime.now().isoformat()}\n\n")
f.write(content)
print(f"Saved to {filename}")
project = """
We are building a mobile app that uses LLM-based conversational agents to help
low-income diabetes patients in rural counties adhere to medication schedules.
The app will integrate with existing EHR systems at 3 community health centers.
PI: Dr. Jane Smith. Duration: 3 years. Budget: $1.2M.
"""
guidelines = """
The funder prioritizes:
1. Health equity and underserved populations.
2. Scalable technology with open APIs.
3. Strong community partnerships and letters of support.
4. Clear evaluation metrics (HbA1c improvement, medication adherence rates).
"""
draft = generate_draft(project, guidelines)
final = critique_and_refine(draft, guidelines)
save_proposal(final)
Example output inside proposal.md:
# Grant Proposal
Generated: 2025-01-14T09:32:18
## Executive Summary
Diabetes-related health disparities in rural counties remain severe. This project deploys a conversational agent to improve medication adherence among low-income patients. By integrating directly with EHR systems at three community health centers, the intervention meets patients in existing clinical workflows rather than requiring new infrastructure.
## Specific Aims
Aim 1: Develop and validate an LLM-based agent capable of delivering personalized medication reminders and educational content in English and Spanish.
Aim 2: Integrate the agent with EHR APIs at three partner clinics to enable automated scheduling and adherence tracking.
Aim 3: Measure impact on HbA1c levels and self-reported medication adherence over a 24-month randomized controlled trial.
## Project Narrative
Rural counties face a 34% higher rate of diabetes-related hospitalizations than urban counterparts. [remaining narrative continues...]
## Timeline
Year 1: Development and IRB approval.
Year 2: Pilot deployment and iterative refinement.
Year 3: Full RCT and data analysis.
## Budget Justification
Personnel: $840,000 (PI, co-I, 2 research assistants).
Technology: $200,000 (API costs, EHR integration, hosting).
Evaluation: $160,000 (external statistician, survey licenses).
Next steps
To make this pipeline production-grade, consider two upgrades. First, use Oxlo.ai's embeddings endpoint with BGE-Large to vectorize past funded proposals from the same funder. Perform similarity search against your draft to surface phrasing that already won money. Second, exploit Oxlo.ai's request-based pricing to draft sections in parallel. You can fire separate calls for Specific Aims, Budget Justification, and Timeline simultaneously without inflating costs, then merge the results in a final assembly step.

