We are going to build a deep reasoning debug agent that analyzes Python code for subtle logic errors, explains its chain of thought, and returns structured fixes. This is useful for teams that want automated reasoning in CI without paying token-based prices for long stack traces or full file context. Because Oxlo.ai uses flat per-request pricing, feeding an entire module into a reasoning model costs the same regardless of length. Details are at https://oxlo.ai/pricing.
What you'll need
- Python 3.10 or newer
- The OpenAI SDK:
pip install openai - An Oxlo.ai API key from https://portal.oxlo.ai
I also recommend setting your key as an environment variable named OXLO_API_KEY so we do not hardcode credentials.
Step 1: Scaffold the client
I start by importing the OpenAI SDK and pointing it at Oxlo.ai. I use deepseek-v3.2 because it is strong at coding and reasoning, and it is available on the Oxlo.ai free tier.
import os
from openai import OpenAI
client = OpenAI(
base_url="https://api.oxlo.ai/v1",
api_key=os.environ.get("OXLO_API_KEY"),
)
# Quick connectivity check
response = client.chat.completions.create(
model="deepseek-v3.2",
messages=[{"role": "user", "content": "Say 'Oxlo.ai client ready'"}],
)
print(response.choices[0].message.content)
Step 2: Craft the reasoning prompt
Deep reasoning works best when you force the model to externalize its thinking. I use a system prompt that requires step-by-step analysis before any conclusion.
SYSTEM_PROMPT = """You are a senior software engineer performing a deep reasoning code review.
When given code and a context description, follow these steps exactly:
1. Restate the intended behavior in your own words.
2. Trace through the execution path line by line.
3. Identify any logic errors, race conditions, or edge cases.
4. Explain the root cause of each issue.
5. Provide a corrected code snippet.
Return your analysis as a JSON object with keys: restatement, trace, issues, root_causes, fixed_code."""
Step 3: Build the analysis function
I wrap the call in a function that accepts a code block and a bug report, injects them into a user message, and prints the raw reasoning trace.
def analyze_code(code: str, context: str) -> str:
user_message = (
f"Context: {context}\n\n"
f"Code:\n
```python\n{code}\n```
\n\n"
"Perform your step-by-step analysis."
)
response = client.chat.completions.create(
model="deepseek-v3.2",
messages=[
{"role": "system", "content": SYSTEM_PROMPT},
{"role": "user", "content": user_message},
],
temperature=0.2,
)
return response.choices[0].message.content
# Example buggy code with a race condition
buggy_code = '''
import threading
class Counter:
def __init__(self):
self.value = 0
def increment(self):
self.value += 1
def worker(counter):
for _ in range(100000):
counter.increment()
counter = Counter()
t1 = threading.Thread(target=worker, args=(counter,))
t2 = threading.Thread(target=worker, args=(counter,))
t1.start()
t2.start()
t1.join()
t2.join()
print(counter.value)
'''
context = (
"This script is supposed to count to 200000 "
"but often prints a lower number. Explain why."
)
result = analyze_code(buggy_code, context)
print(result)
Step 4: Lock down structured output
Raw text is good for debugging, but I want machine-readable results I can store or gate CI on. I enable JSON mode and parse the output with the standard library. Oxlo.ai supports JSON mode on models like deepseek-v3.2, so I add response_format and a small schema reminder in the prompt.
import json
# Same buggy example from Step 3
buggy_code = '''
import threading
class Counter:
def __init__(self):
self.value = 0
def increment(self):
self.value += 1
def worker(counter):
for _ in range(100000):
counter.increment()
counter = Counter()
t1 = threading.Thread(target=worker, args=(counter,))
t2 = threading.Thread(target=worker, args=(counter,))
t1.start()
t2.start()
t1.join()
t2.join()
print(counter.value)
'''
context = (
"This script is supposed to count to 200000 "
"but often prints a lower number. Explain why."
)
def analyze_code_structured(code: str, context: str) -> dict:
user_message = (
f"Context: {context}\n\n"
f"Code:\n
```python\n{code}\n```
\n\n"
"Return your analysis as valid JSON with exactly these keys: "
"restatement (string), trace (string), issues (list of strings), "
"root_causes (list of strings), fixed_code (string)."
)
response = client.chat.completions.create(
model="deepseek-v3.2",
messages=[
{"role": "system", "content": SYSTEM_PROMPT},
{"role": "user", "content": user_message},
],
temperature=0.2,
response_format={"type": "json_object"},
)
content = response.choices[0].message.content
return json.loads(content)
analysis = analyze_code_structured(buggy_code, context)
print(json.dumps(analysis, indent=2))
Run it
Save the full script as reasoning_agent.py and run it. You should see a JSON object that restates the problem, traces the non-atomic increment, flags the race condition, and provides a thread-safe fix using threading.Lock.
$ python reasoning_agent.py
{
"restatement": "The script attempts to increment a shared counter from two threads until the total reaches 200000.",
"trace": "1. Thread A reads self.value (e.g., 100). 2. Thread B reads self.value (100). 3. Thread A increments locally to 101. 4. Thread B increments locally to 101. 5. Thread A writes 101. 6. Thread B writes 101. One increment is lost.",
"issues": [
"Race condition in Counter.increment",
"Non-atomic read-modify-write on self.value"
],
"root_causes": [
"The += operator compiles to multiple bytecode instructions that can interleave across threads without synchronization."
],
"fixed_code": "import threading\n\nclass Counter:\n def __init__(self):\n self.value = 0\n self._lock = threading.Lock()\n\n def increment(self):\n with self._lock:\n self.value += 1\n\ndef worker(counter):\n for _ in range(100000):\n counter.increment()\n\ncounter = Counter()\nt1 = threading.Thread(target=worker, args=(counter,))\nt2 = threading.Thread(target=worker, args=(counter,))\nt1.start()\nt2.start()\nt1.join()\nt2.join()\nprint(counter.value)"
}
If you switch the model to deepseek-r1-671b or kimi-k2.6, you will get even deeper chain-of-thought reasoning. On Oxlo.ai, the cost stays flat per request, so sending a 500-line module costs the same as a 10-line snippet.
Wrap up and next steps
You now have a working deep reasoning agent that externalizes its thinking and returns structured fixes. Two concrete ways to extend it: first, wire the agent into a GitHub Action that comments on pull requests automatically. Second, swap in deepseek-r1-671b or kimi-k2.6 when you need to reason through large architectural changes or multi-file refactors. Oxlo.ai's request-based pricing keeps long-context reviews predictable, which matters when you start passing entire modules or stack traces to the model.
Top comments (0)