We are building a command-line troubleshooting agent that ingests stack traces and surrounding source context, then performs structured root-cause analysis and suggests concrete patches. It is aimed at backend engineers who need to debug production errors without wading through thousands of tokens of billing overhead on every request.
What you'll need
- Python 3.10 or newer
pip install openai- An Oxlo.ai API key from https://portal.oxlo.ai
Step 1: Initialize the Oxlo.ai client
I start by configuring the OpenAI SDK to point at Oxlo.ai. Because Oxlo.ai uses flat per-request pricing instead of token-based metering, I can feed the agent multi-file stack traces and thick log dumps without watching input costs climb. See https://oxlo.ai/pricing for plan details.
from openai import OpenAI
import os
client = OpenAI(
base_url="https://api.oxlo.ai/v1",
api_key=os.environ.get("OXLO_API_KEY")
)
# Verify connectivity with a lightweight flagship model
response = client.chat.completions.create(
model="llama-3.3-70b",
messages=[{"role": "user", "content": "ping"}],
max_tokens=10
)
print(response.choices[0].message.content)
Step 2: Define the system prompt
The system prompt forces the model to emit analysis in a predictable schema: hypothesis, root cause, fix, and confidence. I keep it in a dedicated constant so I can tune it without touching business logic.
SYSTEM_PROMPT = """You are a senior site-reliability engineer. Your job is to diagnose coding errors from stack traces and source context.
Follow this exact structure in your response:
1. Hypothesis: State what you think is wrong in one sentence.
2. Root Cause: Explain the bug and how the code produces the observed error.
3. Fix: Provide a minimal, correct code patch in a unified diff block.
4. Confidence: Low, Medium, or High.
Rules:
- Do not guess if context is missing. Ask for specific files.
- Prefer deleting code over adding complexity.
- Ensure the diff applies cleanly to the provided source.
"""
Step 3: Assemble error context
Real bugs span multiple files. I write a helper that extracts file paths from a Python traceback, reads the surrounding source lines, and packages everything into one long context string. Oxlo.ai's request-based pricing makes this large prompt economical, because the cost is the same whether I send fifty lines or five hundred.
import os
import re
def gather_context(stack_trace: str, project_dir: str = ".") -> str:
"""Extract file paths from a traceback and read those source files."""
lines = []
file_pattern = re.compile(r'File "([^"]+)", line (\d+)')
matches = file_pattern.findall(stack_trace)
seen = set()
for path, lineno in matches:
abs_path = os.path.join(project_dir, path)
if abs_path in seen or not os.path.exists(abs_path):
continue
seen.add(abs_path)
with open(abs_path, "r") as f:
source_lines = f.readlines()
start = max(0, int(lineno) - 6)
end = min(len(source_lines), int(lineno) + 5)
snippet = "".join(source_lines[start:end])
lines.append(f"----- {abs_path} (around line {lineno}) -----\n{snippet}\n")
header = "=== STACK TRACE ===\n" + stack_trace + "\n=== SOURCE CONTEXT ===\n"
return header + "\n".join(lines)
Step 4: Run the diagnostic
Now I send the assembled context to a reasoning model. I use DeepSeek R1 671B on Oxlo.ai here because its Mixture-of-Experts architecture handles long-context code analysis efficiently, and the flat per-request pricing means the large prompt does not inflate cost.
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")
)
SYSTEM_PROMPT = """You are a senior site-reliability engineer. Your job is to diagnose coding errors from stack traces and source context.
Follow this exact structure in your response:
1. Hypothesis: State what you think is wrong in one sentence.
2. Root Cause: Explain the bug and how the code produces the observed error.
3. Fix: Provide a minimal, correct code patch in a unified diff block.
4. Confidence: Low, Medium, or High.
Rules:
- Do not guess if context is missing. Ask for specific files.
- Prefer deleting code over adding complexity.
- Ensure the diff applies cleanly to the provided source.
"""
def gather_context(stack_trace: str, project_dir: str = ".") -> str:
"""Extract file paths from a traceback and read those source files."""
lines = []
file_pattern = re.compile(r'File "([^"]+)", line (\d+)')
matches = file_pattern.findall(stack_trace)
seen = set()
for path, lineno in matches:
abs_path = os.path.join(project_dir, path)
if abs_path in seen or not os.path.exists(abs_path):
continue
seen.add(abs_path)
with open(abs_path, "r") as f:
source_lines = f.readlines()
start = max(0, int(lineno) - 6)
end = min(len(source_lines), int(lineno) + 5)
snippet = "".join(source_lines[start:end])
lines.append(f"----- {abs_path} (around line {lineno}) -----\n{snippet}\n")
header = "=== STACK TRACE ===\n" + stack_trace + "\n=== SOURCE CONTEXT ===\n"
return header + "\n".join(lines)
sample_trace = '''Traceback (most recent call last):
File "app.py", line 15, in handle_request
result = process_data(payload)
File "utils.py", line 18, in process_data
return json.loads(raw)
File "/usr/lib/python3.10/json/__init__.py", line 346, in loads
return _default_decoder.decode(s)
json.decoder.JSONDecodeError: Expecting property name enclosed in double quotes: line 1 column 2 (char 1)
'''
context = gather_context(sample_trace)
response = client.chat.completions.create(
model="deepseek-r1-671b",
messages=[
{"role": "system", "content": SYSTEM_PROMPT},
{"role": "user", "content": context},
],
temperature=0.2,
max_tokens=2048
)
print(response.choices[0].message.content)
Step 5: Package the CLI
I wrap the gatherer and the caller into a single script that accepts a traceback file and a project path. This turns the prototype into a tool I can pipe CI failures into.
import argparse
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")
)
SYSTEM_PROMPT = """You are a senior site-reliability engineer. Your job is to diagnose coding errors from stack traces and source context.
Follow this exact structure in your response:
1. Hypothesis: State what you think is wrong in one sentence.
2. Root Cause: Explain the bug and how the code produces the observed error.
3. Fix: Provide a minimal, correct code patch in a unified diff block.
4. Confidence: Low, Medium, or High.
Rules:
- Do not guess if context is missing. Ask for specific files.
- Prefer deleting code over adding complexity.
- Ensure the diff applies cleanly to the provided source.
"""
def gather_context(stack_trace: str, project_dir: str = ".") -> str:
"""Extract file paths from a traceback and read those source files."""
lines = []
file_pattern = re.compile(r'File "([^"]+)", line (\d+)')
matches = file_pattern.findall(stack_trace)
seen = set()
for path, lineno in matches:
abs_path = os.path.join(project_dir, path)
if abs_path in seen or not os.path.exists(abs_path):
continue
seen.add(abs_path)
with open(abs_path, "r") as f:
source_lines = f.readlines()
start = max(0, int(lineno) - 6)
end = min(len(source_lines), int(lineno) + 5)
snippet = "".join(source_lines[start:end])
lines.append(f"----- {abs_path} (around line {lineno}) -----\n{snippet}\n")
header = "=== STACK TRACE ===\n" + stack_trace + "\n=== SOURCE CONTEXT ===\n"
return header + "\n".join(lines)
def troubleshoot(stack_path: str, project_dir: str) -> str:
with open(stack_path, "r") as f:
trace = f.read()
context = gather_context(trace, project_dir)
response = client.chat.completions.create(
model="kimi-k2.6",
messages=[
{"role": "system", "content": SYSTEM_PROMPT},
{"role": "user", "content": context},
],
temperature=0.2,
max_tokens=2048
)
return response.choices[0].message.content
if __name__ == "__main__":
parser = argparse.ArgumentParser(description="Troubleshoot code errors with Oxlo.ai")
parser.add_argument("stackfile", help="Path to file containing the stack trace")
parser.add_argument("--project-dir", default=".", help="Root of the codebase")
args = parser.parse_args()
print(troubleshoot(args.stackfile, args.project_dir))
Run it
Create a file named error.log with a traceback, place the relevant source next to it, then invoke the agent.
$ python troubleshoot.py error.log --project-dir ./src
With the sample traceback from Step 4, the output looks like this:
1. Hypothesis: The code is attempting to parse a Python dictionary string that uses single quotes, but json.loads expects strictly double-quoted JSON.
2. Root Cause: `utils.py` passes `raw` directly to `json.loads`. If `raw` contains a string like "{'key': 'value'}", the Python JSON decoder fails because JSON requires double quotes around property names and string values.
3. Fix:
```diff
--- a/utils.py
+++ b/utils.py
@@ -15,7 +15,7 @@
def process_data(raw):
- return json.loads(raw)
+ return ast.literal_eval(raw)
```
Or, if the input should be valid JSON:
```diff
--- a/utils.py
+++ b/utils.py
@@ -15,7 +15,7 @@
def process_data(raw):
- return json.loads(raw)
+ return json.loads(raw.replace("'", '"'))
```
4. Confidence: High
Wrap-up
Two concrete next steps. First, index the entire codebase with the embeddings endpoint on Oxlo.ai so the agent can retrieve relevant files even when they do not appear in the stack trace. Second, wire the script into your CI pipeline so failed builds automatically open tickets with the structured diagnosis attached.
Top comments (0)