
We are building a command-line tool that turns rough markdown drafts into polished technical documentation. It helps engineers and developer advocates publish accurate docs faster without switching context out of their terminal. By the end, you will have a working script that calls Oxlo.ai to enforce a consistent style guide on every draft.
What you'll need
Before writing code, make sure you have Python 3.10 or newer installed. You will also need the OpenAI Python SDK, which acts as a drop-in client for Oxlo.ai. Install it with pip:
pip install openai
Next, grab an API key from https://portal.oxlo.ai. I store mine in an environment variable rather than committing it to git. Oxlo.ai charges a flat rate per API request regardless of how long your draft is, so running this on a ten-thousand-word architecture document costs the same as processing a short README. That pricing model removes the anxiety of sending large contexts. If you want to see the current plans, check the Oxlo.ai pricing page.
Step 1: Set up the Oxlo.ai client
Create a file named refiner.py. I import os to read the API key at runtime, then initialize the OpenAI client pointing at Oxlo.ai. Because Oxlo.ai is fully OpenAI SDK compatible, the only difference from a standard OpenAI setup is the base_url. There are no cold starts on popular models, so the first request of the day returns immediately. That responsiveness matters when you are iterating on drafts throughout the day.
import os
from openai import OpenAI
client = OpenAI(
base_url="https://api.oxlo.ai/v1",
api_key=os.environ.get("OXLO_API_KEY")
)
Step 2: Define the style guide system prompt
The system prompt is the contract between you and the model. It defines tone, structure, and constraints. I keep it in a module-level constant so I can version control it alongside the code. You can tweak these rules for your own docs stack. For English technical writing, llama-3.3-70b follows instructions precisely. If your team writes in multiple languages, swapping the model ID to qwen-3-32b gives strong multilingual reasoning without changing any other code.
SYSTEM_PROMPT = """You are a senior technical editor. Your job is to rewrite rough developer drafts into publication-ready documentation.
Rules:
- Use clear, imperative instructions for steps.
- Convert informal notes into structured sections: Overview, Prerequisites, Procedure, Verification.
- Expand vague commands into copy-pasteable code blocks with syntax highlighting.
- Fix grammar and spelling, but preserve all technical accuracy. Do not change API endpoints, environment variable names, or version numbers.
- Output valid Markdown. Do not wrap the response in markdown code fences.
- Keep the tone helpful and direct, with no marketing language."""
Step 3: Build the draft processor
This function takes a raw string, sends it to Oxlo.ai with the system prompt, and returns the rewritten text. I set the user message to the full draft. Because Oxlo.ai uses request-based pricing, I do not need to count tokens or truncate sections to save cost. I can pass the entire article in one shot, even if it includes long configuration snippets. I leave the temperature at the default because technical writing benefits from deterministic output.
def refine_draft(draft_text: str) -> str:
response = client.chat.completions.create(
model="llama-3.3-70b",
messages=[
{"role": "system", "content": SYSTEM_PROMPT},
{"role": "user", "content": draft_text},
],
)
return response.choices[0].message.content
Step 4: Handle output and save results
I never overwrite the original draft in place. Instead, the script reads the input file, calls refine_draft, and writes the result to a new path. This lets me run diff draft.md refined.md before I commit anything to the docs repo. Writing to a separate file also means you can pipe the result into a linter or a link checker before merging it into your main branch. I also print a quick character count so I can spot unexpected truncation.
def process_file(input_path: str, output_path: str) -> None:
with open(input_path, "r", encoding="utf-8") as f:
draft = f.read()
refined = refine_draft(draft)
with open(output_path, "w", encoding="utf-8") as f:
f.write(refined)
print(f"Refined doc written to: {output_path}")
print(f"Original: {len(draft)} chars | Refined: {len(refined)} chars")
Step 5: Wire up the CLI
A small argparse block turns the script into a terminal utility. I use it manually during writing sprints, but you could also invoke it from a Makefile or a pre-commit hook. The interface accepts an input file and an optional output path. You could extend it to accept a directory, but for now a single file keeps the script simple and predictable.
import argparse
if __name__ == "__main__":
parser = argparse.ArgumentParser(
description="Refine technical drafts with Oxlo.ai"
)
parser.add_argument("input", help="Path to rough markdown draft")
parser.add_argument(
"-o", "--output", default="refined.md", help="Output file path"
)
args = parser.parse_args()
process_file(args.input, args.output)
Run it
Create a file named draft.md with some intentionally rough notes:
# quickstart
ok so to get this running first make sure u have python 3.10+ installed. then clone the repo:
git clone https://github.com/example/repo.git
cd repo
pip install -r requirements.txt
now just run main.py. it should start on port 8000. if it doesnt work maybe check ur env vars.
Run the refiner from your terminal:
export OXLO_API_KEY="your-key-here"
python refiner.py draft.md -o output.md
After a few seconds, output.md should look something like this:
# Quickstart
This guide covers running the service locally from source.
## Prerequisites
- Python 3.10 or newer
- Git
## Installation
Clone the repository and install dependencies:
```bash
git clone https://github.com/example/repo.git
cd repo
pip install -r requirements.txt
```
## Start the service
Run the application:
```bash
python main.py
```
The service listens on port 8000 by default.
## Troubleshooting
If the application fails to start, verify that your environment variables are configured correctly.
You can inspect the diff to see exactly what changed. In my experience, the model is conservative with technical facts but aggressive with formatting, which is exactly what I want. The repository URL, the port number, and the command syntax all stayed intact.
Next steps
Two directions I would take this next.
First, batch process an entire docs folder by wrapping process_file in a loop that walks through all markdown files and writes them to a dist/ directory. This is useful when you need to rebuild a whole site after a product release. Because Oxlo.ai does not scale cost with input length, you can safely feed it full migration guides or API references without watching a token meter climb.
Second, add a custom terminology glossary. Load a JSON file of preferred terms and inject it into the system prompt. This keeps product names, capitalization, and banned words consistent across every page your team publishes. If you need deeper reasoning to enforce complex style rules, try swapping the model to kimi-k2.6 or deepseek-v3.2 without touching any other logic.

