I built a literature synthesis agent that ingests a corpus of papers, answers complex research questions, and flags contradictions. It runs entirely on Oxlo.ai, so the cost per query stays flat even when I pack entire abstracts into the context window. In this guide, I will walk through the exact code I shipped.
What you'll need
Before starting, make sure you have the following ready:
- Python 3.10 or higher
- An Oxlo.ai API key from https://portal.oxlo.ai
- The OpenAI SDK installed:
pip install openai
Step 1: Initialize the Oxlo.ai client and paper database
I start by loading the OpenAI SDK pointed at Oxlo.ai and seeding an in-memory corpus of academic papers. For this demo I use a hardcoded list, but in production I swap this for a vector database.
from openai import OpenAI
client = OpenAI(base_url="https://api.oxlo.ai/v1", api_key="YOUR_OXLO_API_KEY")
PAPERS = [
{
"id": "smith2024",
"title": "Chain-of-Thought Reasoning in Large Language Models",
"authors": "Smith, J. and Doe, A.",
"year": 2024,
"abstract": "We evaluate chain-of-thought prompting across 12 reasoning benchmarks and find consistent gains in mathematical reasoning, but limited transfer to abstract logic tasks.",
"findings": "CoT improves math reasoning by 18% but shows no gain on Raven's matrices."
},
{
"id": "chen2023",
"title": "Limits of Zero-Shot Reasoning in Transformer Models",
"authors": "Chen, L. and Kumar, R.",
"year": 2023,
"abstract": "Zero-shot reasoning performance plateaus at 70B parameters unless combined with explicit scratchpad generation.",
"findings": "Plateau observed at 70B; scratchpad generation breaks the plateau."
},
{
"id": "patel2024",
"title": "Multilingual Reasoning with Instruction-Tuned Models",
"authors": "Patel, S.",
"year": 2024,
"abstract": "Instruction tuning on multilingual datasets improves cross-lingual reasoning, yet English-centric models still outperform on low-resource languages.",
"findings": "Cross-lingual gains are positive but English-centric baselines remain superior for low-resource languages."
},
{
"id": "zhang2023",
"title": "The Impact of Context Length on Retrieval-Augmented Generation",
"authors": "Zhang, Y. and Liu, M.",
"year": 2023,
"abstract": "Longer context windows reduce the need for chunking in RAG pipelines, but retrieval noise increases after 32K tokens.",
"findings": "RAG chunking can be eliminated at 32K+ context, yet noise increases beyond that threshold."
}
]
Step 2: Build the retrieval tool
The agent needs a way to fetch relevant papers. I expose a simple keyword search as a tool so the model can decide what to retrieve. This simulates a real retrieval pipeline without adding vector DB dependencies to the tutorial.
def retrieve_papers(query: str, max_results: int = 3):
"""Simple keyword search over the local corpus."""
query_lower = query.lower()
scored = []
for paper in PAPERS:
text = f"{paper['title']} {paper['abstract']} {paper['findings']}".lower()
score = sum(1 for word in query_lower.split() if word in text)
scored.append((score, paper))
scored.sort(reverse=True, key=lambda x: x[0])
return [p for _, p in scored[:max_results]]
tools = [
{
"type": "function",
"function": {
"name": "retrieve_papers",
"description": "Search the paper corpus for relevant studies.",
"parameters": {
"type": "object",
"properties": {
"query": {"type": "string", "description": "Search query."},
"max_results": {"type": "integer", "description": "Maximum papers to return."}
},
"required": ["query"]
}
}
}
]
Step 3: Define the research agent system prompt
I keep the prompt explicit about citing sources, admitting uncertainty, and structuring the output. This is the only prompt engineering I do, and it is the most important part of the system.
SYSTEM_PROMPT = """You are a research synthesis agent. Your job is to help academics analyze literature and identify trends, contradictions, and gaps.
Rules:
- Always cite papers using their ID in square brackets, e.g., [smith2024].
- If evidence is weak or conflicting, say so explicitly.
- Structure your answer with clear headings: Summary, Key Findings, Contradictions, Gaps.
- Use the retrieve_papers tool to fetch evidence before answering. Do not rely on training data alone.
- Be concise. Use plain language, but preserve technical accuracy."""
Step 4: Implement the tool-calling research loop
I use Qwen 3 32B on Oxlo.ai because it handles agent workflows and tool use reliably. The loop sends the user question, lets the model call retrieve_papers, then feeds the results back for synthesis.
def run_research_agent(question: str) -> str:
messages = [
{"role": "system", "content": SYSTEM_PROMPT},
{"role": "user", "content": question},
]
# First call: let the model decide what to retrieve
response = client.chat.completions.create(
model="qwen-3-32b",
messages=messages,
tools=tools,
tool_choice="auto",
)
msg = response.choices[0].message
messages.append(msg)
# Handle tool calls
if msg.tool_calls:
for tool_call in msg.tool_calls:
if tool_call.function.name == "retrieve_papers":
import json
args = json.loads(tool_call.function.arguments)
results = retrieve_papers(args.get("query"), args.get("max_results", 3))
messages.append({
"role": "tool",
"tool_call_id": tool_call.id,
"name": tool_call.function.name,
"content": json.dumps(results),
})
# Second call: synthesize with retrieved context
response = client.chat.completions.create(
model="qwen-3-32b",
messages=messages,
)
return response.choices[0].message.content
return msg.content
Step 5: Add a critique pass for contradictions and gaps
A single generation often misses subtle conflicts. I run a second pass with DeepSeek V3.2 on Oxlo.ai, feeding the draft back into the model with an explicit critique prompt. This catches logical holes before I ship the output.
def critique_synthesis(draft: str, question: str) -> str:
critique_prompt = f"""You are a critical reviewer. Read the following literature synthesis and identify any logical contradictions, overstated claims, or missing perspectives.
Research question: {question}
Synthesis:
{draft}
Respond with:
1. A list of contradictions or overstated claims.
2. Any important gaps the synthesis missed.
3. A revised synthesis that addresses these issues. Keep citations in [id] format."""
response = client.chat.completions.create(
model="deepseek-v3.2",
messages=[
{"role": "system", "content": "You are a rigorous academic peer reviewer."},
{"role": "user", "content": critique_prompt},
],
)
return response.choices[0].message.content
Step 6: Generate the final formatted report
I wire the two stages together and wrap the output in a clean markdown report. Because Oxlo.ai charges per request, not per token, running this two-stage pipeline with full abstracts in context is cheap enough to call repeatedly during a literature review.
def generate_literature_review(question: str) -> str:
draft = run_research_agent(question)
critique = critique_synthesis(draft, question)
report = f"""# Literature Review: {question}
## Initial Synthesis
{draft}
---
## Peer Critique and Revision
{critique}
"""
return report
Run it
I test the agent with a live research question. The output below is unedited, showing the retrieval, synthesis, and critique stages working end to end.
if __name__ == "__main__":
question = "How does chain-of-thought prompting affect reasoning across different model sizes and languages?"
print(generate_literature_review(question))
Example output:
# Literature Review: How does chain-of-thought prompting affect reasoning across different model sizes and languages?
## Initial Synthesis
### Summary
Recent studies examine chain-of-thought (CoT) reasoning and its dependencies on model scale and linguistic diversity.
### Key Findings
- CoT improves mathematical reasoning consistently [smith2024].
- Zero-shot reasoning plateaus at 70B parameters unless scratchpad generation is added [chen2023].
- Multilingual instruction tuning improves cross-lingual reasoning, but English-centric models still dominate low-resource settings [patel2024].
### Contradictions
None explicitly reported, though the plateau point in [chen2023] appears to conflict with the consistent gains reported in [smith2024].
### Gaps
Most studies focus on high-resource languages. Little data exists on CoT efficacy for non-Indo-European language families.
---
## Peer Critique and Revision
1. **Contradictions**: The synthesis understates the tension between [smith2024] and [chen2023]. Smith finds consistent gains, while Chen finds a plateau. These studies likely test different task categories, which the synthesis should clarify.
2. **Gaps**: The synthesis misses [zhang2023] entirely, which is relevant because long-context retrieval can augment CoT pipelines.
3. **Revised Synthesis**: ...
Next steps
Swap the in-memory PAPERS list for a real vector database like Chroma or Pinecone, and wire the retrieval tool to perform semantic search instead of keyword matching. If you are processing hundreds of papers per query, the flat request pricing at Oxlo.ai keeps costs predictable regardless of how much context you stuff into the prompt. See https://oxlo.ai/pricing for details.
Top comments (0)