
We are going to build a collaborative poetry workshop agent that generates drafts, provides structured craft feedback, and iterates on revisions in a single conversation thread. It is useful for poets and creative writers who need an always-available sparring partner that remembers context. Because it runs on Oxlo.ai, long context threads with extensive drafts do not inflate costs, since Oxlo.ai uses flat per-request pricing instead of scaling with token count.
What you'll need
You need Python 3.10 or newer installed on your machine. Grab an Oxlo.ai API key from https://portal.oxlo.ai and install the OpenAI SDK. Oxlo.ai is fully OpenAI SDK compatible, so the imports and method signatures work exactly like your existing code.
pip install openai
Step 1: Configure the Oxlo.ai client
I import the OpenAI SDK and point it at Oxlo.ai's endpoint, using Llama 3.3 70B as the creative backbone. Oxlo.ai has no cold starts, so the first request returns immediately.
from openai import OpenAI
client = OpenAI(base_url="https://api.oxlo.ai/v1", api_key="YOUR_OXLO_API_KEY")
# Verify connectivity with a short test call
response = client.chat.completions.create(
model="llama-3.3-70b",
messages=[
{"role": "system", "content": "You are a helpful assistant."},
{"role": "user", "content": "Say hello briefly."},
],
)
print(response.choices[0].message.content)
Step 2: Define the system prompt
The system prompt defines the agent as a poetry mentor that favors concrete imagery and returns structured critique. Keeping it in a dedicated variable lets me tune the workshop voice without touching request logic.
SYSTEM_PROMPT = """You are a patient poetry workshop mentor. Your job is to help writers improve their craft.
When generating:
- Write original poems based on the user's theme and form constraints.
- Favor concrete imagery over abstraction.
- Respect meter and line breaks when the user requests a form.
When critiquing:
- Evaluate Imagery, Sound, Emotional Resonance, and Structure.
- Give each a score from 1 to 10.
- Provide one specific exercise or rewrite instruction to improve the lowest scoring dimension."""
Step 3: Generate the first draft
I send a theme and form constraint to generate the first draft, keeping temperature at 0.9 for creative variety. Because Oxlo.ai uses flat per-request pricing, I do not worry about long form constraints increasing cost.
user_message = "Write a draft sonnet about autumn rain."
response = client.chat.completions.create(
model="llama-3.3-70b",
messages=[
{"role": "system", "content": SYSTEM_PROMPT},
{"role": "user", "content": user_message},
],
temperature=0.9,
)
draft = response.choices[0].message.content
print(draft)
Step 4: Critique the draft
I request a JSON critique so the scores and revision instruction parse cleanly into a dictionary, lowering temperature to 0.3 for consistent scoring. Oxlo.ai supports JSON mode through the standard response_format parameter.
import json
critique_prompt = (
"Critique this poem and return valid JSON with keys: "
"imagery_score, sound_score, emotional_score, structure_score, "
"weakest_dimension, revision_instruction.\n\nPoem:\n" + draft
)
response = client.chat.completions.create(
model="llama-3.3-70b",
messages=[
{"role": "system", "content": SYSTEM_PROMPT},
{"role": "user", "content": critique_prompt},
],
response_format={"type": "json_object"},
temperature=0.3,
)
critique = json.loads(response.choices[0].message.content)
print(json.dumps(critique, indent=2))
Step 5: Revise based on feedback
I feed the critique back into a new prompt that targets the weakest dimension, raising temperature back to 0.9 because revision is generative. Repeating this loop is affordable on Oxlo.ai since each pass costs the same flat rate.
revision_prompt = (
f"Revise the following poem. Focus specifically on improving {critique['weakest_dimension']}.\n"
f"Instruction: {critique['revision_instruction']}\n\n"
f"Original poem:\n{draft}"
)
response = client.chat.completions.create(
model="llama-3.3-70b",
messages=[
{"role": "system", "content": SYSTEM_PROMPT},
{"role": "user", "content": revision_prompt},
],
temperature=0.9,
)
revised = response.choices[0].message.content
print(revised)
Run it
I combine the helpers into one script and execute it from the terminal. The pipeline produces a draft, a structured critique, and a revised poem in a single run. You can loop the critique and revision steps until the scores plateau. The example output below is abbreviated, but your actual run will return full stanzas and detailed scores.
from openai import OpenAI
import json
client = OpenAI(base_url="https://api.oxlo.ai/v1", api_key="YOUR_OXLO_API_KEY")
SYSTEM_PROMPT = """You are a patient poetry workshop mentor. Your job is to help writers improve their craft.
When generating:
- Write original poems based on the user's theme and form constraints.
- Favor concrete imagery over abstraction.
- Respect meter and line breaks when the user requests a form.
When critiquing:
- Evaluate Imagery, Sound, Emotional Resonance, and Structure.
- Give each a score from 1 to 10.
- Provide one specific exercise or rewrite instruction to improve the lowest scoring dimension."""
def generate_draft(theme, form="free verse"):
user_message = f"Write a {form} draft about {theme}."
response = client.chat.completions.create(
model="llama-3.3-70b",
messages=[
{"role": "system", "content": SYSTEM_PROMPT},
{"role": "user", "content": user_message},
],
temperature=0.9,
)
return response.choices[0].message.content
def critique_draft(draft_text):
critique_prompt = (
"Critique this poem and return valid JSON with keys: "
"imagery_score, sound_score, emotional_score, structure_score, "
"weakest_dimension, revision_instruction.\n\nPoem:\n" + draft_text
)
response = client.chat.completions.create(
model="llama-3.3-70b",
messages=[
{"role": "system", "content": SYSTEM_PROMPT},
{"role": "user", "content": critique_prompt},
],
response_format={"type": "json_object"},
temperature=0.3,
)
return json.loads(response.choices[0].message.content)
def revise_draft(draft_text, critique):
revision_prompt = (
f"Revise the following poem. Focus specifically on improving {critique['weakest_dimension']}.\n"
f"Instruction: {critique['revision_instruction']}\n\n"
f"Original poem:\n{draft_text}"
)
response = client.chat.completions.create(
model="llama-3.3-70b",
messages=[
{"role": "system", "content": SYSTEM_PROMPT},
{"role": "user", "content": revision_prompt},
],
temperature=0.9,
)
return response.choices[0].message.content
if __name__ == "__main__":
draft = generate_draft("autumn rain", "sonnet")
print("=== DRAFT ===")
print(draft)
critique = critique_draft(draft)
print("\n=== CRITIQUE ===")
print(json.dumps(critique, indent=2))
revised = revise_draft(draft, critique)
print("\n=== REVISED ===")
print(revised)
Example output:
=== DRAFT ===
The grey sky weeps on branches bare and cold,
While puddles gather where the acorns fell.
The wind repeats a story rarely told,
Of summer warmth imprisoned in a shell.
...
=== CRITIQUE ===
{
"imagery_score": 7,
"sound_score": 8,
"emotional_score": 6,
"structure_score": 9,
"weakest_dimension": "Emotional Resonance",
"revision_instruction": "Replace two abstract adjectives with sensory details that evoke a specific memory of loss or comfort."
}
=== REVISED ===
The grey sky weeps on branches stripped of gold,
While puddles swallow what the acorns left.
The wind repeats a story rarely told,
Of summer warmth now stolen, bereft.
...
Next steps
A natural extension is to persist conversation history across sessions so the agent remembers your evolving style. You could also add vision support by switching to Kimi K2.6 on Oxlo.ai and passing images of handwritten drafts into the messages array. If you deploy this as a service, flat per-request pricing makes generous revision loops economically viable. See https://oxlo.ai/pricing for plan details.

