We are building a CLI academic writing assistant that generates structured paper outlines, drafts sections in formal academic tone, and formats citations. It targets researchers and graduate students who need to iterate quickly on manuscripts without worrying that long prompts containing PDF extracts or reference lists will inflate their inference bill. Because Oxlo.ai charges a flat rate per request, you can stuff the context window with prior drafts and source material and the cost stays predictable. See https://oxlo.ai/pricing for current plan details.
What you'll need
- Python 3.10 or newer
- An Oxlo.ai API key from https://portal.oxlo.ai
- The OpenAI SDK:
pip install openai
Step 1: Verify connectivity
Before adding logic, confirm that your Oxlo.ai key and the OpenAI SDK are wired correctly. I keep my key in an environment variable named OXLO_API_KEY.
from openai import OpenAI
import os
client = OpenAI(
base_url="https://api.oxlo.ai/v1",
api_key=os.getenv("OXLO_API_KEY", "YOUR_OXLO_API_KEY")
)
response = client.chat.completions.create(
model="llama-3.3-70b",
messages=[
{"role": "system", "content": "You are a helpful academic assistant."},
{"role": "user", "content": "Say hello and confirm you are ready."},
],
)
print(response.choices[0].message.content)
Step 2: Define the system prompt and a thin client wrapper
The system prompt locks in tone, citation style, and structure. I define it as a module-level constant so I can iterate on it without touching business logic. Then I wrap the Oxlo.ai client in a small AcademicWriter class.
SYSTEM_PROMPT = """You are an academic writing assistant. Follow these rules:
- Use formal academic tone. Avoid colloquialisms.
- Structure content with clear thesis statements, evidence, and transitions.
- Format citations in APA style when source metadata is provided.
- When generating outlines, include nested sections and estimated word counts per section.
- Be concise. Do not hedge excessively."""
from openai import OpenAI
class AcademicWriter:
MODEL = "llama-3.3-70b"
def __init__(self, api_key: str):
self.client = OpenAI(base_url="https://api.oxlo.ai/v1", api_key=api_key)
def ask(self, user_message: str) -> str:
response = self.client.chat.completions.create(
model=self.MODEL,
messages=[
{"role": "system", "content": SYSTEM_PROMPT},
{"role": "user", "content": user_message},
],
)
return response.choices[0].message.content
Step 3: Generate an outline from a topic and source list
Outlines keep long papers coherent. This method takes a topic, target word count, and optional source string, then returns a nested outline. Because Oxlo.ai bills per request, sending a long source list does not change the price.
from openai import OpenAI
SYSTEM_PROMPT = """You are an academic writing assistant. Follow these rules:
- Use formal academic tone. Avoid colloquialisms.
- Structure content with clear thesis statements, evidence, and transitions.
- Format citations in APA style when source metadata is provided.
- When generating outlines, include nested sections and estimated word counts per section.
- Be concise. Do not hedge excessively."""
class AcademicWriter:
MODEL = "llama-3.3-70b"
def __init__(self, api_key: str):
self.client = OpenAI(base_url="https://api.oxlo.ai/v1", api_key=api_key)
def ask(self, user_message: str) -> str:
response = self.client.chat.completions.create(
model=self.MODEL,
messages=[
{"role": "system", "content": SYSTEM_PROMPT},
{"role": "user", "content": user_message},
],
)
return response.choices[0].message.content
def generate_outline(self, topic: str, word_count: int = 3000, sources: str = "") -> str:
prompt = (
f"Create a detailed outline for an academic paper on: {topic}\n"
f"Target length: {word_count} words.\n"
f"Use the following sources if relevant: {sources}\n"
"Return the outline with numbered sections and subsections."
)
return self.ask(prompt)
Step 4: Draft individual sections with inline citations
Once the outline is approved, I draft section by section. Passing source metadata in the prompt lets the model generate real citations rather than hallucinated ones. If you need deeper reasoning for a literature review, swap the model ID to deepseek-r1-671b or kimi-k2.6 on Oxlo.ai without changing any other code.
from openai import OpenAI
SYSTEM_PROMPT = """You are an academic writing assistant. Follow these rules:
- Use formal academic tone. Avoid colloquialisms.
- Structure content with clear thesis statements, evidence, and transitions.
- Format citations in APA style when source metadata is provided.
- When generating outlines, include nested sections and estimated word counts per section.
- Be concise. Do not hedge excessively."""
class AcademicWriter:
MODEL = "llama-3.3-70b"
def __init__(self, api_key: str):
self.client = OpenAI(base_url="https://api.oxlo.ai/v1", api_key=api_key)
def ask(self, user_message: str) -> str:
response = self.client.chat.completions.create(
model=self.MODEL,
messages=[
{"role": "system", "content": SYSTEM_PROMPT},
{"role": "user", "content": user_message},
],
)
return response.choices[0].message.content
def generate_outline(self, topic: str, word_count: int = 3000, sources: str = "") -> str:
prompt = (
f"Create a detailed outline for an academic paper on: {topic}\n"
f"Target length: {word_count} words.\n"
f"Use the following sources if relevant: {sources}\n"
"Return the outline with numbered sections and subsections."
)
return self.ask(prompt)
def draft_section(self, section_title: str, instructions: str, sources: str = "") -> str:
prompt = (
f"Draft the section titled '{section_title}'.\n"
f"Instructions: {instructions}\n"
f"Sources to cite: {sources}\n"
"Write 250 to 400 words in formal academic prose."
)
return self.ask(prompt)
Step 5: Add an iterative revision loop
Advisor feedback usually arrives as unstructured bullet points. This method feeds the current draft plus the feedback back into the model and returns a polished revision. Stacking multiple revision turns is cheap on Oxlo.ai because each turn is one flat request, even when the prompt contains the full paper so far.
from openai import OpenAI
SYSTEM_PROMPT = """You are an academic writing assistant. Follow these rules:
- Use formal academic tone. Avoid colloquialisms.
- Structure content with clear thesis statements, evidence, and transitions.
- Format citations in APA style when source metadata is provided.
- When generating outlines, include nested sections and estimated word counts per section.
- Be concise. Do not hedge excessively."""
class AcademicWriter:
MODEL = "llama-3.3-70b"
def __init__(self, api_key: str):
self.client = OpenAI(base_url="https://api.oxlo.ai/v1", api_key=api_key)
def ask(self, user_message: str) -> str:
response = self.client.chat.completions.create(
model=self.MODEL,
messages=[
{"role": "system", "content": SYSTEM_PROMPT},
{"role": "user", "content": user_message},
],
)
return response.choices[0].message.content
def generate_outline(self, topic: str, word_count: int = 3000, sources: str = "") -> str:
prompt = (
f"Create a detailed outline for an academic paper on: {topic}\n"
f"Target length: {word_count} words.\n"
f"Use the following sources if relevant: {sources}\n"
"Return the outline with numbered sections and subsections."
)
return self.ask(prompt)
def draft_section(self, section_title: str, instructions: str, sources: str = "") -> str:
prompt = (
f"Draft the section titled '{section_title}'.\n"
f"Instructions: {instructions}\n"
f"Sources to cite: {sources}\n"
"Write 250 to 400 words in formal academic prose."
)
return self.ask(prompt)
def revise(self, current_text: str, feedback: str) -> str:
prompt = (
"Revise the following text based on the feedback provided.\n\n"
f"Text:\n{current_text}\n\n"
f"Feedback:\n{feedback}\n\n"
"Return only the revised text."
)
return self.ask(prompt)
Run it
Here is the end-to-end script. I run it from the terminal with python writer.py. The example topic targets LLM citation accuracy, a meta-topic that lets the tool demonstrate its own value.
from openai import OpenAI
import os
SYSTEM_PROMPT = """You are an academic writing assistant. Follow these rules:
- Use formal academic tone. Avoid colloquialisms.
- Structure content with clear thesis statements, evidence, and transitions.
- Format citations in APA style when source metadata is provided.
- When generating outlines, include nested sections and estimated word counts per section.
- Be concise. Do not hedge excessively."""
class AcademicWriter:
MODEL = "llama-3.3-70b"
def __init__(self, api_key: str):
self.client = OpenAI(base_url="https://api.oxlo.ai/v1", api_key=api_key)
def ask(self, user_message: str) -> str:
response = self.client.chat.completions.create(
model=self.MODEL,
messages=[
{"role": "system", "content": SYSTEM_PROMPT},
{"role": "user", "content": user_message},
],
)
return response.choices[0].message.content
def generate_outline(self, topic: str, word_count: int = 3000, sources: str = "") -> str:
prompt = (
f"Create a detailed outline for an academic paper on: {topic}\n"
f"Target length: {word_count} words.\n"
f"Use the following sources if relevant: {sources}\n"
"Return the outline with numbered sections and subsections."
)
return self.ask(prompt)
def draft_section(self, section_title: str, instructions: str, sources: str = "") -> str:
prompt = (
f"Draft the section titled '{section_title}'.\n"
f"Instructions: {instructions}\n"
f"Sources to cite: {sources}\n"
"Write 250 to 400 words in formal academic prose."
)
return self.ask(prompt)
def revise(self, current_text: str, feedback: str) -> str:
prompt = (
"Revise the following text based on the feedback provided.\n\n"
f"Text:\n{current_text}\n\n"
f"Feedback:\n{feedback}\n\n"
"Return only the revised text."
)
return self.ask(prompt)
if __name__ == "__main__":
writer = AcademicWriter(api_key=os.getenv("OXLO_API_KEY", "YOUR_OXLO_API_KEY"))
topic = (
"The impact of retrieval-augmented generation on citation accuracy "
"in LLM-assisted academic writing"
)
sources = (
"Smith, J. (2023). Hallucinations in large language models. "
"Journal of AI Research, 45(2), 112-130. "
"Doe, A. (2024). RAG systems for scholarly work. "
"ACM Computing Surveys, 12(1), 55-78."
)
outline = writer.generate_outline(topic, word_count=4000, sources=sources)
print("=== OUTLINE ===")
print(outline)
section = writer.draft_section(
section_title="Introduction",
instructions=(
"Introduce the problem of hallucinated citations in LLMs "
"and argue that RAG improves accuracy."
),
sources=sources,
)
print("\n=== DRAFT ===")
print(section)
revised = writer.revise(
current_text=section,
feedback=(
"Make the thesis statement more specific. "
"Add a sentence on evaluation metrics."
),
)
print("\n=== REVISED ===")
print(revised)
Example output:
=== OUTLINE === 1. Introduction (400 words) 1.1 Background on LLM citation errors 1.2 Problem statement: hallucinations undermine scholarly trust 1.3 Thesis: RAG significantly improves citation accuracy by grounding generation in retrieved corpora 1.4 Roadmap 2. Literature Review (1,000 words) 2.1 LLM hallucination taxonomy (Smith, 2023) 2.2 Retrieval-augmented architectures (Doe, 2024) 2.3 Gaps in current evaluation 3. Methodology (800 words) ... === DRAFT === Large language models have demonstrated remarkable fluency in generating academic prose, yet they frequently fabricate citations. Smith (2023) documents that hallucination rates in scholarly contexts remain as high as 30% when models rely solely on parametric memory. In contrast, Doe (2024) demonstrates that retrieval-augmented generation systems, which condition output on external document corpora, reduce citation errors by grounding claims in verifiable sources. This paper argues that integrating RAG into academic writing workflows is not merely a technical convenience but a necessary epistemic safeguard... === REVISED === Large language models have demonstrated remarkable fluency in generating academic prose, yet they frequently fabricate citations. Smith (2023) documents that hallucination rates in scholarly contexts remain as high as 30% when models rely solely on parametric memory. In contrast, Doe (2024) demonstrates that retrieval-augmented generation systems, which condition output on external document corpora, reduce citation errors by grounding claims in verifiable sources. This paper argues that integrating RAG into academic writing workflows improves citation accuracy by an estimated 25 to 40 percentage points, as measured by precision and recall of retrieved references, making it a necessary epistemic safeguard...
Wrap-up and next steps
The assistant is now a script you can check into a repo and adapt per project. Two concrete upgrades to ship next: wire the sources argument to a Zotero or Readwise API so citations are live rather than pasted, and swap llama-3.3-70b to kimi-k2.6 or deepseek-r1-671b on Oxlo.ai when you need advanced chain-of-thought reasoning for complex literature synthesis. Both changes require only a model string swap because the Oxlo.ai endpoint is fully OpenAI SDK compatible.
Top comments (0)