We are going to build a deep reasoning debug agent that analyzes Python tracebacks through multiple self-critique passes before delivering a fix. It helps backend engineers who want more reliable root-cause analysis than a single-shot LLM answer. We will run it on Oxlo.ai using a reasoning-optimized model and the standard OpenAI SDK.
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
Step 1: Configure the Oxlo.ai client
First, import the SDK and point it at Oxlo.ai. I use kimi-k2.6 because it handles advanced chain-of-thought reasoning well, and Oxlo.ai exposes it through the standard OpenAI completions endpoint with no cold starts.
from openai import OpenAI
client = OpenAI(base_url="https://api.oxlo.ai/v1", api_key="YOUR_OXLO_API_KEY")
Step 2: Define the reasoning system prompt
Deep reasoning needs structure. The system prompt below forces the model to observe facts, generate multiple hypotheses, check evidence, and only then conclude. This is the editable heart of the agent.
SYSTEM_PROMPT = """You are a senior Site Reliability Engineer who debugs Python tracebacks through deep reasoning.
When given an error log, follow this process strictly inside your response:
1. Observation: list the explicit facts from the traceback.
2. Hypothesis Generation: propose at least three distinct root causes, ranked by likelihood.
3. Evidence Check: for each hypothesis, note what evidence supports or contradicts it.
4. Conclusion: state the most likely root cause and a concrete fix.
Do not skip any step. Think out loud."""
Step 3: Build the first-pass analyzer
This function sends the raw traceback to the model with the reasoning prompt. We keep temperature low so the chain of thought stays grounded.
def first_pass_analysis(error_log: str) -> str:
response = client.chat.completions.create(
model="kimi-k2.6",
messages=[
{"role": "system", "content": SYSTEM_PROMPT},
{"role": "user", "content": f"Analyze this traceback:\n\n{error_log}"},
],
temperature=0.2,
)
return response.choices[0].message.content
Step 4: Add a self-critique loop
Single-pass reasoning often misses edge cases. We feed the draft back into the model and ask it to attack its own logic. This second pass is what turns a quick guess into deep reasoning.
CRITIQUE_PROMPT = """You are the same senior engineer.
Review the draft analysis below. Look for logical gaps, missing assumptions, or alternative explanations that were dismissed too quickly.
Provide a concise, numbered critique."""
def critique_analysis(draft: str) -> str:
response = client.chat.completions.create(
model="kimi-k2.6",
messages=[
{"role": "system", "content": CRITIQUE_PROMPT},
{"role": "user", "content": f"Draft analysis:\n\n{draft}"},
],
temperature=0.2,
)
return response.choices[0].message.content
Step 5: Synthesize the final report
Finally, we merge the original analysis and the critique into a concise, actionable report. The model now acts as its own editor, keeping only the strongest conclusions.
SYNTHESIS_PROMPT = """You are the same senior engineer.
Given the original analysis and the critique, produce a final, actionable report.
Include the confirmed root cause, the concrete fix, and one prevention step."""
def synthesize_report(draft: str, critique: str) -> str:
response = client.chat.completions.create(
model="kimi-k2.6",
messages=[
{"role": "system", "content": SYNTHESIS_PROMPT},
{"role": "user", "content": f"Original analysis:\n\n{draft}\n\nCritique:\n\n{critique}"},
],
temperature=0.2,
)
return response.choices[0].message.content
Run it
Wire the three stages together and test with a realistic traceback. Because Oxlo.ai uses request-based pricing, this multi-turn chain costs the same number of requests regardless of how long the log or the reasoning gets. That makes iterative deep reasoning practical in production.
if __name__ == "__main__":
error_log = """Traceback (most recent call last):
File "/srv/app/worker.py", line 88, in process_job
result = db_pool.execute(query, timeout=5)
File "/srv/app/db.py", line 14, in execute
raise ConnectionError("Pool exhausted after 30s wait")
ConnectionError: Pool exhausted after 30s wait"""
print("Running first-pass analysis...")
draft = first_pass_analysis(error_log)
print(draft)
print("\n" + "=" * 50 + "\n")
print("Running self-critique...")
critique = critique_analysis(draft)
print(critique)
print("\n" + "=" * 50 + "\n")
print("Running final synthesis...")
final = synthesize_report(draft, critique)
print(final)
Example output:
Running first-pass analysis...
1. Observation: The error originates in worker.py line 88 during process_job. The call to db_pool.execute timed out after 30 seconds. The exception type is ConnectionError with message "Pool exhausted after 30s wait".
2. Hypothesis Generation:
A. The database connection pool is sized too small for current concurrency.
B. A connection leak inside execute() prevents returning connections to the pool.
C. The database server itself is under load and cannot accept new connections.
3. Evidence Check:
A. Supports: "Pool exhausted" explicitly signals all connections are checked out. No server-side error is present.
B. Contradicts: The traceback does not show a prior unclosed connection, but a leak would still manifest this way.
C. Contradicts: A server-side issue would typically raise a different error or timeout, not a pool exhaustion message.
4. Conclusion: Most likely cause is an undersized connection pool (Hypothesis A). Fix: increase pool size and verify connection release logic.
==================================================
Running self-critique...
1. The draft assumes the pool size is misconfigured, but it does not verify whether connections are actually being released after exceptions.
2. Hypothesis B was ranked second without evidence because the traceback is shallow; a connection leak is equally plausible if execute() lacks a finally block.
3. The fix should explicitly mention wrapping the connection usage in a context manager or try/finally block, not just increasing pool size.
==================================================
Running final synthesis...
Root cause: The database connection pool is undersized for current worker concurrency, and connections may not be reliably returned to the pool after exceptions.
Concrete fix: Increase the pool size from the default to match worker concurrency, and wrap db_pool.execute() usage in a try/finally or context manager that guarantees conn.close() is called.
Prevention: Add pool utilization metrics and alert when wait time exceeds one second.
Wrap-up and next steps
You now have a working deep reasoning agent that inspects its own logic before giving you an answer. The full source is under fifty lines and runs on any of Oxlo.ai's reasoning models without vendor lock-in.
Two concrete next steps: wire the agent into your CI pipeline so it comments on failed builds automatically, or swap kimi-k2.6 for deepseek-v3.2 to test how different reasoning architectures handle the same critique loop. Both models are available on Oxlo.ai with the same OpenAI-compatible client.
Top comments (0)