
We are going to build an academic writing assistant that takes a rough draft, identifies logical gaps, and reformats citations. It is aimed at graduate students and researchers who need to tighten arguments without getting lost in stylistic minutiae.
What you'll need
You will need Python 3.10 or newer, the OpenAI SDK, and an Oxlo.ai API key from https://portal.oxlo.ai. Install the SDK with pip install openai. Because Oxlo.ai uses flat per-request pricing rather than token-based billing, you can pass in full drafts or long reference lists without worrying about prompt length inflating your cost. That makes it a practical choice for document-heavy academic workflows.
Step 1: Set up the client
Oxlo.ai exposes a standard OpenAI-compatible chat completions endpoint, which means I do not have to learn a new SDK or rewrite my inference logic. I only need to swap the base_url and plug in an Oxlo.ai API key. I verify the setup with Llama 3.3 70B, a solid general-purpose model that responds instantly with no cold start.
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": "system", "content": "You are a helpful assistant."},
{"role": "user", "content": "Say 'Connection to Oxlo.ai is live.'"},
],
)
print(response.choices[0].message.content)
Step 2: Craft the system prompt
Before making production calls, I lock down the system prompt. It acts as a behavioral contract: the model must never hallucinate citations, must return structured feedback, and must ask for clarifying context when needed. Keeping this in a module-level constant makes A/B testing trivial.
SYSTEM_PROMPT = """You are an academic writing assistant. Your job is to help researchers improve clarity, argument structure, and citation formatting.
Rules:
1. Never invent or hallucinate citations. If a reference is missing, flag it.
2. When reviewing drafts, output feedback in exactly three sections: Argument Strength, Logical Gaps, Style and Flow.
3. When formatting citations, use APA 7th edition unless told otherwise.
4. Ask clarifying questions if the research domain or target venue is ambiguous.
5. Keep tone professional but concise."""
Step 3: Build the draft analyzer
The analyze_draft function is the workhorse. Researchers often paste entire sections of a literature review or methodology chapter, so the model needs a large context window. Kimi K2.6 supports 131K tokens of context and excels at chain-of-thought reasoning, which means it can track an argument across multiple paragraphs and flag where the evidence does not support the claim.
from openai import OpenAI
client = OpenAI(base_url="https://api.oxlo.ai/v1", api_key="YOUR_OXLO_API_KEY")
SYSTEM_PROMPT = """You are an academic writing assistant. Your job is to help researchers improve clarity, argument structure, and citation formatting.
Rules:
1. Never invent or hallucinate citations. If a reference is missing, flag it.
2. When reviewing drafts, output feedback in exactly three sections: Argument Strength, Logical Gaps, Style and Flow.
3. When formatting citations, use APA 7th edition unless told otherwise.
4. Ask clarifying questions if the research domain or target venue is ambiguous.
5. Keep tone professional but concise."""
def analyze_draft(draft_text: str) -> str:
response = client.chat.completions.create(
model="kimi-k2.6",
messages=[
{"role": "system", "content": SYSTEM_PROMPT},
{"role": "user", "content": f"Review the following draft and provide the three required sections.\n\nDraft:\n{draft_text}"},
],
)
return response.choices[0].message.content
sample_draft = (
"Many researchers have studied neural networks. "
"However, our model is better because it uses more layers. "
"We did not compare against the most recent baseline."
)
print(analyze_draft(sample_draft))
Step 4: Add a citation formatter
PDF metadata is notoriously inconsistent. Some entries lack DOIs, others mix author names and institutions, and non-English references often lose diacritics when copied. By sending the raw text to Qwen 3 32B, I get consistent APA formatting and explicit warnings about missing fields rather than silent guesses.
from openai import OpenAI
client = OpenAI(base_url="https://api.oxlo.ai/v1", api_key="YOUR_OXLO_API_KEY")
SYSTEM_PROMPT = """You are an academic writing assistant. Your job is to help researchers improve clarity, argument structure, and citation formatting.
Rules:
1. Never invent or hallucinate citations. If a reference is missing, flag it.
2. When reviewing drafts, output feedback in exactly three sections: Argument Strength, Logical Gaps, Style and Flow.
3. When formatting citations, use APA 7th edition unless told otherwise.
4. Ask clarifying questions if the research domain or target venue is ambiguous.
5. Keep tone professional but concise."""
def format_citations(raw_refs: str) -> str:
response = client.chat.completions.create(
model="qwen-3-32b",
messages=[
{"role": "system", "content": SYSTEM_PROMPT},
{"role": "user", "content": f"Format these references into APA 7th edition. Flag any missing fields.\n\n{raw_refs}"},
],
)
return response.choices[0].message.content
messy = """Smith, J., The AI Journal, 2023, Vol 10
Lee et al, arxiv 2024, https://arxiv.org/abs/2401.12345"""
print(format_citations(messy))
Step 5: Generate an outline
Generating an outline before drafting saves hours of restructuring later. DeepSeek V3.2 is optimized for coding and reasoning tasks, so it produces tightly organized hierarchies that respect typical IMRAD or machine-learning paper conventions. Since it is available on the Oxlo.ai free tier, I can iterate on the outline with zero cost until the structure feels right.
from openai import OpenAI
client = OpenAI(base_url="https://api.oxlo.ai/v1", api_key="YOUR_OXLO_API_KEY")
SYSTEM_PROMPT = """You are an academic writing assistant. Your job is to help researchers improve clarity, argument structure, and citation formatting.
Rules:
1. Never invent or hallucinate citations. If a reference is missing, flag it.
2. When reviewing drafts, output feedback in exactly three sections: Argument Strength, Logical Gaps, Style and Flow.
3. When formatting citations, use APA 7th edition unless told otherwise.
4. Ask clarifying questions if the research domain or target venue is ambiguous.
5. Keep tone professional but concise."""
def generate_outline(topic: str, venue: str, word_limit: int) -> str:
response = client.chat.completions.create(
model="deepseek-v3.2",
messages=[
{"role": "system", "content": SYSTEM_PROMPT},
{"role": "user", "content": (
f"Create a paper outline for the topic '{topic}' "
f"targeting the venue '{venue}' with a word limit of {word_limit}. "
f"Include sections: Abstract, Introduction, Related Work, Method, Experiments, Conclusion."
)},
],
)
return response.choices[0].message.content
print(generate_outline("Efficient MoE routing for long-context LLMs", "ICML", 5000))
Step 6: Wire everything into a CLI
Finally, I wrap the three utilities into a small CLI script with argparse. The script reads an input file and routes it to the correct function based on a mode flag. This keeps the interface simple enough to alias as a shell command or integrate into a Makefile.
import argparse
from openai import OpenAI
client = OpenAI(base_url="https://api.oxlo.ai/v1", api_key="YOUR_OXLO_API_KEY")
SYSTEM_PROMPT = """You are an academic writing assistant. Your job is to help researchers improve clarity, argument structure, and citation formatting.
Rules:
1. Never invent or hallucinate citations. If a reference is missing, flag it.
2. When reviewing drafts, output feedback in exactly three sections: Argument Strength, Logical Gaps, Style and Flow.
3. When formatting citations, use APA 7th edition unless told otherwise.
4. Ask clarifying questions if the research domain or target venue is ambiguous.
5. Keep tone professional but concise."""
def analyze_draft(draft_text: str) -> str:
response = client.chat.completions.create(
model="kimi-k2.6",
messages=[
{"role": "system", "content": SYSTEM_PROMPT},
{"role": "user", "content": f"Review the following draft and provide the three required sections.\n\nDraft:\n{draft_text}"},
],
)
return response.choices[0].message.content
def format_citations(raw_refs: str) -> str:
response = client.chat.completions.create(
model="qwen-3-32b",
messages=[
{"role": "system", "content": SYSTEM_PROMPT},
{"role": "user", "content": f"Format these references into APA 7th edition. Flag any missing fields.\n\n{raw_refs}"},
],
)
return response.choices[0].message.content
def generate_outline(topic: str, venue: str, word_limit: int) -> str:
response = client.chat.completions.create(
model="deepseek-v3.2",
messages=[
{"role": "system", "content": SYSTEM_PROMPT},
{"role": "user", "content": (
f"Create a paper outline for the topic '{topic}' "
f"targeting the venue '{venue}' with a word limit of {word_limit}. "
f"Include sections: Abstract, Introduction, Related Work, Method, Experiments, Conclusion."
)},
],
)
return response.choices[0].message.content
def main():
parser = argparse.ArgumentParser(description="Academic writing assistant via Oxlo.ai")
parser.add_argument("mode", choices=["analyze", "cite", "outline"], help="Task to run")
parser.add_argument("--input", "-i", required=True, help="Path to input text file")
parser.add_argument("--venue", default="general", help="Target venue for outline mode")
parser.add_argument("--words", type=int, default=3000, help="Word limit for outline mode")
args = parser.parse_args()
with open(args.input, "r", encoding="utf-8") as f:
text = f.read()
if args.mode == "analyze":
result = analyze_draft(text)
elif args.mode == "cite":
result = format_citations(text)
elif args.mode == "outline":
result = generate_outline(text.strip(), args.venue, args.words)
print("\n--- RESULT ---\n")
print(result)
if __name__ == "__main__":
main()
Run it
Create a file named draft.txt containing the sample draft from Step 3. Then run the analyze command. You should see structured feedback printed to stdout.
python academic_writer.py analyze -i draft.txt
Expected output:
--- RESULT ---
Argument Strength:
The draft states that the proposed model is better because it uses more layers, but it does not justify why additional layers translate to improved performance in this specific domain. The claim is asserted rather than supported.
Logical Gaps:
There is no comparison against the most recent baseline, which undermines the validity of the improvement claim. The reader cannot assess relative performance.
Style and Flow:
The transition between the general statement about neural networks and the specific model claim is abrupt. A brief contextual bridge would improve readability.
Wrap-up and next steps
Two concrete next steps. First, add a retrieval layer: parse uploaded PDFs with pymupdf and feed relevant excerpts into the prompt so the assistant can ground its critique in real sources. Second, enable streaming by passing stream=True to client.chat.completions.create and printing chunks as they arrive, which improves perceived latency during long reasoning responses. If you want to productize this, consider adding a Gradio or Streamlit frontend so labmates can paste text without touching the terminal. You could also cache repeated outline requests with a simple SQLite store, further driving down your already predictable per-request costs on Oxlo.ai.

