We are going to build a Deep Research Assistant that breaks complex questions into sub-problems, reasons through each step, and verifies its own logic before delivering a final answer. This kind of system is useful for technical research, code architecture decisions, or any domain where a quick chat response is not enough. We will run it on Oxlo.ai using their OpenAI-compatible API and deep reasoning models like Kimi K2.6 and DeepSeek R1, and because Oxlo.ai bills per request rather than per token, iterating on long system prompts and multi-step reasoning chains stays predictable even as context grows.
What you'll need
Before starting, make sure you have the following:
- Python 3.10 or newer installed locally.
- The OpenAI Python SDK. Install it with
pip install openai. - An Oxlo.ai API key from https://portal.oxlo.ai.
Step 1: Set up the environment and Oxlo.ai client
First, import the required libraries and initialize the client pointing at Oxlo.ai. I keep my key in an environment variable so it is not hard-coded.
import json
import os
import re
from openai import OpenAI
client = OpenAI(
base_url="https://api.oxlo.ai/v1",
api_key=os.environ.get("OXLO_API_KEY")
)
Step 2: Design the system prompt for deep reasoning
The system prompt forces the model to decompose the problem and emit a structured JSON object. This makes the reasoning chain inspectable and allows us to pipe it into downstream verification logic.
SYSTEM_PROMPT = """You are a deep reasoning assistant. When given a complex problem, follow these rules exactly:
1. Decompose the problem into sub-problems.
2. Reason through each sub-problem explicitly.
3. State any assumptions or uncertainties.
4. Respond with a single valid JSON object containing exactly these keys:
- decomposition: list of sub-problems
- reasoning_chain: list of step-by-step strings
- confidence: float between 0 and 1
- final_answer: string
Do not wrap the JSON in markdown fences."""
Step 3: Build the reasoning engine with Kimi K2.6
We will use Kimi K2.6 on Oxlo.ai for the heavy lifting. Reasoning models sometimes emitthinking tags or extra prose, so we strip those and extract the JSON block safely.
def extract_json(text: str) -> dict:
text = re.sub(r"<think>.*?</think>", "", text, flags=re.DOTALL)
match = re.search(r"\{.*\}", text, re.DOTALL)
if not match:
raise ValueError("No JSON object found in model output")
return json.loads(match.group())
def deep_reason(query: str) -> dict:
response = client.chat.completions.create(
model="kimi-k2.6",
messages=[
{"role": "system", "content": SYSTEM_PROMPT},
{"role": "user", "content": query},
],
temperature=0.2,
max_tokens=4000,
response_format={"type": "json_object"},
)
content = response.choices[0].message.content
return extract_json(content)
Step 4: Add a self-verification layer with Llama 3.3 70B
Reasoning is only as good as its weakest assumption. We add a second pass using Llama 3.3 70B to critique the chain, look for logical gaps, and revise the confidence score.
def verify_reasoning(reasoning: dict, original_query: str) -> dict:
critique_prompt = f"""Critique the following reasoning for logical gaps, unstated assumptions, or factual errors.
Original question: {original_query}
Reasoning JSON: {json.dumps(reasoning, indent=2)}
Output a single valid JSON object with keys:
- gaps_found: bool
- critique: string
- revised_confidence: float between 0 and 1"""
response = client.chat.completions.create(
model="llama-3.3-70b",
messages=[
{"role": "system", "content": "You are a critical reasoning verifier."},
{"role": "user", "content": critique_prompt},
],
temperature=0.1,
max_tokens=2000,
response_format={"type": "json_object"},
)
content = response.choices[0].message.content
return extract_json(content)
Step 5: Synthesize the final answer
Now we wire the two stages together. The synthesizer runs the reasoning pass, feeds the result into the verifier, and returns a single dictionary that we can log or render.
def synthesize(query: str) -> dict:
reasoning = deep_reason(query)
verification = verify_reasoning(reasoning, query)
return {
"query": query,
"reasoning": reasoning,
"verification": verification,
"final_answer": reasoning.get("final_answer", "No answer generated."),
"confidence": verification.get(
"revised_confidence",
reasoning.get("confidence", 0.5),
),
}
Run it
Here is how to call the finished agent from the command line. I use a classic lateral thinking problem to stress-test the reasoning chain.
if __name__ == "__main__":
question = (
"A farmer has 17 sheep and all but 9 die. "
"How many are left? Explain your reasoning step by step."
)
result = synthesize(question)
print(json.dumps(result, indent=2))
Example output after running the script:
{
"query": "A farmer has 17 sheep and all but 9 die. How many are left? Explain your reasoning step by step.",
"reasoning": {
"decomposition": [
"Interpret the phrase 'all but 9 die'",
"Calculate the remaining sheep"
],
"reasoning_chain": [
"The phrase 'all but 9' means that 9 sheep did not die.",
"Therefore, regardless of the original total, 9 sheep remain alive.",
"The initial number 17 is context but does not change the outcome."
],
"confidence": 0.95,
"final_answer": "9 sheep are left."
},
"verification": {
"gaps_found": false,
"critique": "The reasoning correctly identifies that 'all but 9' directly specifies the survivors. No logical gaps detected.",
"revised_confidence": 0.97
},
"final_answer": "9 sheep are left.",
"confidence": 0.97
}
Wrap-up and next steps
This pattern gives you a transparent, auditable reasoning layer that you can extend in two directions. First, replace the hard-coded critique with a recursive self-consistency loop that votes across multiple reasoning samples generated through Oxlo.ai. Second, add tool use by wiring Oxlo.ai function calling into the reasoning stage so the agent can query live APIs or calculators to ground its assumptions in real data.
Top comments (0)