We are building a command-line academic writing assistant that turns a rough research idea into a structured outline and full sections. It runs entirely on Oxlo.ai, so the flat per-request pricing keeps long brainstorming sessions affordable compared to token-based providers. Graduate students and researchers can use it to iterate on paper structure without watching API costs scale with every paragraph.
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
Step 1: Configure the client
First, I set up the OpenAI-compatible client pointing at Oxlo.ai and make a quick health check call to confirm the key works.
from openai import OpenAI
import os
client = OpenAI(
base_url="https://api.oxlo.ai/v1",
api_key=os.environ.get("OXLO_API_KEY")
)
# Quick connectivity test
response = client.chat.completions.create(
model="llama-3.3-70b",
messages=[{"role": "user", "content": "Say OK"}],
max_tokens=10,
)
print(response.choices[0].message.content)
Step 2: Define the system prompt
The system prompt anchors the model in academic conventions. I keep it in a separate constant so I can tune voice and citation rules without touching business logic.
SYSTEM_PROMPT = """You are an academic writing assistant. Your goals are:
- Produce clear, structured scholarly prose.
- Use Markdown headings for organization.
- Insert [CITATION] placeholders where empirical claims need support.
- Flag uncertain facts with [VERIFY].
- Avoid hype, jargon, or unsupported superlatives.
- Write in a neutral, precise tone suitable for journals or conference papers."""
Step 3: Generate a structured outline
I send the research topic to the model and ask for a hierarchical outline with word-count budgets per section. I use Qwen 3 32B because it handles multilingual reasoning and structured agent workflows well.
def generate_outline(topic, target_words=3000):
prompt = f"""Topic: {topic}
Target length: {target_words} words.
Return a hierarchical outline with:
1. Title
2. Abstract summary (3 sentences)
3. Sections (Introduction, Related Work, Method, Results, Discussion, Conclusion)
4. Approximate word count for each section
Use Markdown. Do not write prose yet."""
resp = client.chat.completions.create(
model="qwen-3-32b",
messages=[
{"role": "system", "content": SYSTEM_PROMPT},
{"role": "user", "content": prompt},
],
temperature=0.3,
)
return resp.choices[0].message.content
# Example usage
outline = generate_outline(
"The impact of retrieval-augmented generation on citation accuracy in scholarly LLMs",
target_words=4000,
)
print(outline)
Step 4: Draft sections with citation placeholders
Next, I take one section heading and expand it into full prose. I pass the entire outline as context so the model stays coherent with the overall narrative arc. DeepSeek V3.2 is strong at coding and reasoning, so I use it here for tight logical structure.
def draft_section(section_heading, outline, notes=""):
prompt = f"""Given the following outline:
{outline}
Expand only the section titled '{section_heading}' into full scholarly prose.
- Follow the word budget implied by the outline.
- Insert [CITATION] wherever empirical data or prior work is referenced.
- Flag uncertain claims with [VERIFY].
- Use Markdown subheadings if the section is long.
Additional researcher notes: {notes if notes else "None"}"""
resp = client.chat.completions.create(
model="deepseek-v3.2",
messages=[
{"role": "system", "content": SYSTEM_PROMPT},
{"role": "user", "content": prompt},
],
temperature=0.4,
max_tokens=2048,
)
return resp.choices[0].message.content
# Draft the Introduction
introduction = draft_section(
"Introduction",
outline,
notes="Emphasize the reproducibility crisis in NLP benchmarks.",
)
print(introduction)
Step 5: Add a critique and refinement loop
Raw drafts usually need tightening. I send the section back to the model with an explicit critique instruction. Kimi K2.6 handles advanced chain-of-thought reasoning, so it is a good fit for spotting logical gaps.
def critique_and_refine(section_text, section_heading):
critique_prompt = f"""Section: {section_heading}
Draft:
{section_text}
First, list 3 concrete weaknesses (logic, clarity, or missing citations).
Then, rewrite the section fixing those weaknesses.
Preserve all [CITATION] and [VERIFY] tags, and add new ones where needed."""
resp = client.chat.completions.create(
model="kimi-k2.6",
messages=[
{"role": "system", "content": SYSTEM_PROMPT},
{"role": "user", "content": critique_prompt},
],
temperature=0.3,
)
return resp.choices[0].message.content
refined_intro = critique_and_refine(introduction, "Introduction")
print(refined_intro)
Step 6: Wire everything into a single script
Finally, I wrap the pipeline in a small CLI that accepts a topic and runs the full flow. Because Oxlo.ai charges a flat rate per request, I know the cost of this multi-step agentic workflow upfront without counting tokens.
import argparse
def main():
parser = argparse.ArgumentParser(description="Academic writing assistant")
parser.add_argument("topic", help="Research topic")
parser.add_argument("--words", type=int, default=3000, help="Target word count")
parser.add_argument("--sections", nargs="+", default=["Introduction", "Method", "Conclusion"])
args = parser.parse_args()
print("=== GENERATING OUTLINE ===")
outline = generate_outline(args.topic, args.words)
print(outline)
print("\n")
for sec in args.sections:
print(f"=== DRAFTING: {sec} ===")
draft = draft_section(sec, outline)
print(draft)
print("\n")
print(f"=== REFINING: {sec} ===")
refined = critique_and_refine(draft, sec)
print(refined)
print("\n")
if __name__ == "__main__":
main()
Run it
Save the script as academic_writer.py, export your key, and run:
export OXLO_API_KEY="sk-oxlo.ai-..."
python academic_writer.py "Federated learning for low-resource language modeling" --words 3500 --sections Introduction Method Results
Typical output looks like this:
=== GENERATING OUTLINE ===
# Federated Learning for Low-Resource Language Modeling
## Abstract
Federated learning (FL) offers a privacy-preserving alternative to centralized training, yet its effectiveness for low-resource languages remains underexplored. [CITATION] ...
## Outline
- Introduction (800 words)
- Motivation: data scarcity and privacy ...
- Method (1200 words)
...
=== DRAFTING: Introduction ===
## Introduction
Low-resource languages, defined as those with fewer than one million monolingual sentences in publicly available corpora, [CITATION] present a unique challenge for modern NLP. Federated learning provides a distributed training paradigm ... [VERIFY]
...
Wrap-up
You now have a working academic writing pipeline on Oxlo.ai. Two concrete next steps: wire the [CITATION] placeholders to a retrieval layer using Oxlo.ai's embeddings endpoint with bge-large, or add a --latex flag that converts the Markdown output to .tex sections for direct integration with your manuscript repository.
Top comments (0)