
I built a lightweight CLI agent that generates curated lists of LLM community resources on demand. It calls an Oxlo.ai model to research a topic, categorize findings, and return structured JSON that I can paste into a wiki or share with my team. The entire pipeline is under 80 lines of Python and uses Oxlo.ai's OpenAI-compatible endpoint, so I did not have to learn a new SDK.
What You'll Need
You will need Python 3.10 or newer, the official OpenAI Python SDK, and an API key from Oxlo.ai. I recommend creating your key at https://portal.oxlo.ai and exporting it as OXLO_API_KEY so it stays out of your shell history. The OpenAI SDK is the only external dependency because Oxlo.ai exposes a fully compatible base URL, which keeps the stack minimal. If you want to experiment first, the Oxlo.ai free tier includes 60 requests per day and a 7-day full-access trial, which is enough to iterate on this script.
Step 1: Configure the Oxlo.ai Client
I instantiate the OpenAI client with Oxlo.ai's base URL and read the API key from the environment. Keeping credentials in an environment variable prevents accidental commits and makes the script portable across my laptop and CI runners.
import os
from openai import OpenAI
client = OpenAI(
base_url="https://api.oxlo.ai/v1",
api_key=os.getenv("OXLO_API_KEY", "YOUR_OXLO_API_KEY"),
)
Step 2: Define the Curator System Prompt
The system prompt constrains the model to act as a technical curator and emit only valid JSON. I explicitly list the allowed categories and difficulty levels so the output stays consistent no matter what topic the user asks for.
SYSTEM_PROMPT = """You are a technical curator for LLM practitioners.
Given a topic, return a JSON object with a single key "resources".
Each item in "resources" must have:
- "title": string
- "url": string (a real, working URL if known, otherwise an empty string)
- "category": one of [Paper, Code, Blog, Video, Dataset, Tool]
- "summary": one sentence describing why it matters
- "difficulty": Beginner, Intermediate, or Advanced
Only return valid JSON. Do not add markdown fences or commentary.
"""
Step 3: Build the Request Helper
This helper accepts a topic string and forwards it to llama-3.3-70b on Oxlo.ai with JSON mode enabled. I set temperature to 0.2 because curation favors accuracy over creativity, and I choose llama-3.3-70b because it handles long system prompts reliably with no cold starts on Oxlo.ai.
def fetch_raw(topic: str) -> str:
user_message = f"Curate 5 essential community resources about: {topic}"
response = client.chat.completions.create(
model="llama-3.3-70b",
messages=[
{"role": "system", "content": SYSTEM_PROMPT},
{"role": "user", "content": user_message},
],
response_format={"type": "json_object"},
temperature=0.2,
)
return response.choices[0].message.content
Step 4: Parse and Validate the Response
I wrap json.loads in a small helper so I can isolate formatting errors quickly. If the model ever returns malformed JSON, the script raises a clear exception instead of failing silently downstream.
import json
def parse_resources(raw: str) -> list[dict]:
data = json.loads(raw)
if "resources" not in data:
raise ValueError("Missing 'resources' key in model response")
return data["resources"]
Step 5: Add Basic Retry Logic
Network blips happen, so I wrap the fetch and parse steps in a retry loop with exponential backoff. This keeps the script robust when I run it inside a nightly cron job or a CI pipeline.
import time
def curate_with_retry(topic: str, retries: int = 3) -> list[dict]:
for attempt in range(retries):
try:
raw = fetch_raw(topic)
return parse_resources(raw)
except Exception:
if attempt == retries - 1:
raise
time.sleep(2 ** attempt)
return []
Step 6: Format Results for the Terminal
Raw JSON is hard to scan during a standup, so I print a markdown-compatible table. Each row is escaped naively because my internal topics never contain pipe characters, but you can add a robust escape if you share this publicly.
def print_resources(resources: list[dict]) -> None:
print("| Title | Category | Difficulty | Summary |")
print("|-------|----------|------------|---------|")
for r in resources:
title = r.get("title", "N/A")
cat = r.get("category", "N/A")
diff = r.get("difficulty", "N/A")
summary = r.get("summary", "N/A")
print(f"| {title} | {cat} | {diff} | {summary} |")
Step 7: Add the CLI Entrypoint
The script reads the topic from command line arguments and falls back to a default if none are provided. I also print a small header so I know which query was sent to Oxlo.ai before the table renders.
if __name__ == "__main__":
import sys
topic = " ".join(sys.argv[1:]) if len(sys.argv) > 1 else "LLM fine-tuning"
print(f"Curating resources for: {topic}\n")
resources = curate_with_retry(topic)
print_resources(resources)
Run It
I run the script from my terminal after exporting the key. The first call warms up immediately because Oxlo.ai does not impose cold starts on popular models, so the table appears in a few seconds.
$ export OXLO_API_KEY="oxlo_..."
$ pip install openai
$ python curator.py agentic RAG patterns
Curating resources for: agentic RAG patterns
| Title | Category | Difficulty | Summary |
|-------|----------|------------|---------|
| Corrective RAG (CRAG) Paper | Paper | Intermediate | Introduces a self-correcting retrieval loop that triggers web search when retrieved docs are irrelevant. |
| LangGraph RAG Tutorial | Video | Beginner | A step-by-step walkthrough of building stateful agentic RAG with checkpointing and tool nodes. |
| LlamaIndex Agentic Documentation | Blog | Intermediate | Covers building agentic orchestration layers over vector stores with reasoning loops. |
| RAG Flow on GitHub | Code | Advanced | A modular reference implementation of advanced RAG patterns including hybrid search and reranking. |
| MS MARCO Dataset | Dataset | Beginner | The standard benchmark dataset for training and evaluating passage ranking and retrieval models. |
Wrap-Up
This agent gives me a repeatable way to surface LLM community resources without maintaining a manual spreadsheet. Next, I plan to wire it into a Slack bot using Oxlo.ai's function calling support so teammates can request curations from a channel. I might also swap in qwen-3-32b for multilingual resource lists, or deepseek-v3.2 if I need heavier reasoning over code repositories. Because Oxlo.ai uses flat per-request pricing, longer system prompts or larger context windows do not inflate the cost, which makes iterating on prompts cheap. See https://oxlo.ai/pricing for current plan details.

