We are building a batch text refinement pipeline that turns raw meeting notes into polished documentation. It chains three language modeling tasks, summarization, expansion, and tone alignment, using Oxlo.ai's request-based API so long notes do not inflate cost. If you regularly convert scattered bullets into readable prose, this tool removes the manual rewrite loop.
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: Scaffold the project and configure the Oxlo.ai client
I start with a single file, refine.py, that imports the SDK and points it at Oxlo.ai. I also add a small helper to load a text file so we can feed the pipeline from disk.
from openai import OpenAI
client = OpenAI(base_url="https://api.oxlo.ai/v1", api_key="YOUR_OXLO_API_KEY")
def load_notes(path: str) -> str:
with open(path, "r", encoding="utf-8") as f:
return f.read()
Step 2: Define the agent's system prompt
The pipeline uses a shared system prompt so every stage treats the content as technical documentation. Keeping this string separate makes it easy to edit without touching the call sites.
SYSTEM_PROMPT = (
"You are a senior technical writer. "
"Your job is to transform fragmented meeting notes into clear, structured documentation. "
"Use concise paragraphs, bullet points only when necessary, and maintain the factual accuracy of the original text. "
"Do not invent attendees, dates, or action items that are not present in the source material."
)
Step 3: Summarize the raw notes
First, I distill the raw text into a tight summary. I use Qwen 3 32B because it handles long context efficiently, which matters when notes run long. Oxlo.ai charges per request, not per token, so passing a large transcript here does not change the price.
def summarize_notes(client, text: str) -> str:
response = client.chat.completions.create(
model="qwen-3-32b",
messages=[
{"role": "system", "content": SYSTEM_PROMPT},
{
"role": "user",
"content": f"Summarize the following meeting notes into 3 to 5 key points:\n\n{text}",
},
],
)
return response.choices[0].message.content
Step 4: Expand the summary into structured prose
Next, I turn the summary into flowing paragraphs with section headers. Llama 3.3 70B works well as a general-purpose flagship for this kind of generation.
def expand_summary(client, summary: str) -> str:
response = client.chat.completions.create(
model="llama-3.3-70b",
messages=[
{"role": "system", "content": SYSTEM_PROMPT},
{
"role": "user",
"content": f"Expand the following summary into structured documentation with an introduction, key discussion points, and next steps:\n\n{summary}",
},
],
)
return response.choices[0].message.content
Step 5: Rewrite in the target tone
Finally, I adjust the prose for the intended audience. Kimi K2.6 handles reasoning and style control, so I use it to shift the tone without losing meaning.
def rewrite_tone(client, prose: str, tone: str) -> str:
response = client.chat.completions.create(
model="kimi-k2.6",
messages=[
{"role": "system", "content": SYSTEM_PROMPT},
{
"role": "user",
"content": f"Rewrite the following documentation in a {tone} tone. Keep all facts intact:\n\n{prose}",
},
],
)
return response.choices[0].message.content
Step 6: Wire the pipeline together
I chain the three stages in a main function, printing intermediate results so I can inspect each layer. I read the input from notes.txt.
def main():
raw = load_notes("notes.txt")
print("=== RAW NOTES ===")
print(raw[:500] + "...\n")
summary = summarize_notes(client, raw)
print("=== SUMMARY ===")
print(summary + "\n")
draft = expand_summary(client, summary)
print("=== EXPANDED DRAFT ===")
print(draft + "\n")
final = rewrite_tone(client, draft, "executive")
print("=== FINAL EXECUTIVE VERSION ===")
print(final)
if __name__ == "__main__":
main()
Run it
Create a file named notes.txt with rough bullets, then execute the script. Here is an example input and the output you can expect after the pipeline finishes.
# notes.txt
- discussed q3 roadmap
- sarah said api latency is up 20pct since last deploy
- action: rollback candidate is v2.4.1
- need to add p95 alerting before next release
- new feature: request-based pricing calculator, demo next week
Run the pipeline:
export OXLO_API_KEY="sk-oxlo.ai-..."
python refine.py
Example output:
=== RAW NOTES ===
- discussed q3 roadmap
- sarah said api latency is up 20pct since last deploy
...
=== SUMMARY ===
1. The team reviewed the Q3 roadmap.
2. API latency increased by 20% following the most recent deployment.
3. A rollback to version 2.4.1 is under consideration.
4. P95 alerting must be implemented before the next release.
5. A demo for the request-based pricing calculator is scheduled for next week.
=== EXPANDED DRAFT ===
Introduction
The team convened to align on the Q3 roadmap and address recent performance regressions.
Key Discussion Points
API latency has risen by 20% since the latest production deploy. Sarah flagged the issue and proposed rolling back to version 2.4.1 while the root cause is investigated. The group agreed that P95 latency alerting is a prerequisite for any future release to prevent similar regressions.
Next Steps
A demo of the new request-based pricing calculator is slated for next week.
=== FINAL EXECUTIVE VERSION ===
Executive Summary
During the Q3 roadmap review, the team identified a 20% API latency regression tied to the latest deployment. A rollback to v2.4.1 is the recommended immediate mitigation. To safeguard release quality, P95 alerting will be mandated going forward. Leadership should also note the upcoming demo of the request-based pricing calculator.
Next steps
Swap the tone parameter from "executive" to "casual" or "technical" to see how Kimi K2.6 adapts the same facts. If you plan to run this on long transcripts, try DeepSeek V4 Flash with its 1M context window, or switch to DeepSeek V3.2 on Oxlo.ai's free tier to prototype without cost concerns. For production use, wrap the stages in FastAPI endpoints so other services can submit notes and receive formatted documentation asynchronously.
Top comments (0)