image
image

16 min read


Home » Blog » Building a Production-Grade AI Debugging Agent with Claude and GitHub Actions

Building a Production-Grade AI Debugging Agent with Claude and GitHub Actions

Author:
Alberto Sossa, ML Solutions Architect at Provectus

Software engineers spend 35-50% of their time debugging, a number that increases dramatically when bugs reach production. What starts as a 30-minute feature can become a 5-hour debugging marathon, cascading through delayed releases and frustrated stakeholders.

Unfortunately, most AI debugging solutions have failed to address this issue, instead treating symptoms rather than causes and functioning as autocompletion tools rather than true reasoning systems.

What Many Engineering Teams Miss

The difference between an effective AI debugging agent and an expensive disappointment is not the automation framework around it. It is the deliberate architecture of its reasoning process.

By this, we mean the structured design of how the agent thinks, including the prompting strategies that guide its analysis, the tools it can invoke to gather information, and the iterative loops that enable hypothesis testing and refinement.

While teams rush to integrate “black box” AI into their workflows, the real competitive advantage lies in engineering “glass box” agents with traceable reasoning. Now, the truth is, we cannot fully understand how Large Language Models (LLMs) do most of the things they do.

However, we can build systems that externalize their reasoning process through explicit thinking steps, making their decision-making inspectable even when the underlying model’s internal states remain opaque.

This guide will prove this thesis by walking through the architecture of an AI agent, powered by Anthropic’s Claude, that is capable of performing root cause analysis with the precision of a senior engineer. We will call it “Bug Surgeon”.

Follow along as we architect the agent’s core reasoning logic, demonstrate its superiority through a real-world failure analysis, and integrate it into a secure GitHub Actions workflow. By the end, you will have both the conceptual framework and a tested reference implementation you can adapt for your own organization.

Part I: Architecting the Agent’s Reasoning Engine

Every effective AI agent starts with a well-designed reasoning architecture. Most teams fail here by treating the LLM as an inscrutable oracle. Instead, we must deliberately engineer its intelligence.

Designing the System Prompt

The system prompt acts as the agent’s blueprint. That is the enduring set of instructions that shape its identity, objectives, and reasoning style. The perfect prompt strikes a balance between structure and flexibility, providing enough guidance to ensure systematic reasoning while maintaining adaptability.

Here’s a sample system prompt structured for reliable behavior. It will serve as our agent’s constitution:

EXPERT_SYSTEM_PROMPT = """You are a Senior Debugging Specialist with expertise in systematic root cause analysis. Your goal is to identify and resolve the fundamental cause of issues, not merely patch symptoms.

<reasoning_methodology>
1. Analyze the problem context thoroughly before proposing solutions.
2. Form hypotheses about potential root causes.
3. Request additional context when needed to validate hypotheses using the specified tool format.
4. Explain your reasoning process transparently using <thinking> tags.
5. Provide solutions that address the underlying issue, not just symptoms.
</reasoning_methodology>

<constraints>
- Maintain existing code style and patterns.
- If uncertain, state assumptions clearly and request clarification.
- Focus on sustainable fixes over quick patches.
- Only suggest changes to files you've actually examined.
</constraints>

<tool_usage>
When you need to examine code files, respond ONLY with the tool request format:
TOOL_REQUEST: read_file
FILE: path/to/file.py
START_LINE: 30 (optional)
END_LINE: 50 (optional)
</tool_usage>

<output_format>
Always use <thinking> tags to externalize your reasoning process. For your final response, provide a JSON object with this structure:

{
"analysis": {
"root_cause": "Brief description",
"explanation": "Detailed explanation",
"confidence": "HIGH|MEDIUM|LOW"
},
"solution": {
"code": "The corrected code",
"explanation": "Why this fixes the issue"
}
}

Required fields: root_cause, explanation, confidence, code
Optional fields: additional_context, related_files
</output_format>
"""

This prompt establishes expertise, defines a clear methodology, and enforces a structured, parseable output format essential for automated workflows.

Note: In practice, the orchestration layer validates adherence to this schema and re-prompts if the output deviates from the expected structure, ensuring reliable parsing in automated workflows.

Eliciting a Transparent Reasoning Process

The <thinking> tags are the core of our “glass box” approach. They refer to a custom prompt convention – not a native Claude feature – that instructs the model to externalize its reasoning process in a structured format.

Our orchestrator parses these tags to make the agent’s reasoning trace transparent and debuggable. This approach builds developer trust and makes it easier to diagnose and improve the agent when its reasoning fails.

Here is the agent’s internal monologue when presented with a NoneType error in an authentication module:

<thinking>
Looking at this authentication error, I need to understand the full context:

1. Error: "NoneType object has no attribute 'id'" in auth.py line 42.
2. This suggests the user object is None when we expect a valid user.
3. Possible causes:
  - Database query returning None (user doesn't exist).
  - Session/token validation failing silently.
  - Race condition in concurrent requests.

I should examine the authentication flow leading up to line 42 to understand how the user object could become None.
</thinking>

This demonstrates systematic hypothesis formation, the hallmark of an expert engineer. Note that while this example shows a clean, well-structured reasoning trace, actual model outputs may vary between runs and will not always be this consistently formatted. This is why the orchestrator includes validation and re-prompting logic.

Implementing the ReAct Framework

The ReAct (Reason + Act) framework, first proposed by Yao et al. (2022), provides the iterative cycle that makes agents truly interactive. The agent reasons about what it needs, acts to gather information (e.g., reads a file), observes the results, and reasons again with the new context.

Our orchestrator implements a smart version of this. It attempts a direct analysis if the bug report mentions specific files; otherwise, it falls back to the iterative ReAct cycle. This is a key production optimization.

# A simplified view of the orchestrator's logic
def analyze_bug(self, issue_description: str, max_iterations: int = 3):
# Practitioner's Note: This smart selection is a key optimization.
# It avoids unnecessary ReAct cycles for simpler bugs, saving time and cost.
mentioned_files = self._extract_file_paths(issue_description)
if mentioned_files:
return self.analyze_bug_direct(issue_description, mentioned_files)
return self._analyze_bug_react(issue_description, max_iterations)

def _analyze_bug_react(self, issue_description: str, max_iterations: int = 3):
messages = [{"role": "user", "content": f"Please analyze this bug report:\n\n{issue_description}"}]
iteration = 0
while iteration < max_iterations:
# Step 1: Reason - Get Claude's analysis and potential tool request
response = self.claude.messages.create(...)
response_text = response.content.text

# Step 2: Act - Check for and execute tool requests
tool_requests = self.parse_tool_requests(response_text)
if tool_requests:
messages.append({"role": "assistant", "content": response_text})
# Step 3: Observe - Add file content back into the context
for tool_request in tool_requests:
file_content = self.read_file_content(tool_request.file_path)
messages.append({"role": "user", "content": f"File content:\n{file_content}"})
iteration += 1
continue

# No tool requests means the agent is ready to provide a final analysis
return self.extract_analysis(response_text)

Practitioner’s Note: This simplified example focuses on the core ReAct logic. In practice, a production implementation includes robust error handling: validating tool request formats, handling failed file reads with appropriate fallbacks, ensuring required <analysis> sections are present before proceeding, and implementing retry logic with exponential backoff for API failures. The orchestrator should validate outputs at each step and re-prompt with specific error messages when validation fails.

Part II: Proving Value Through Contrast

To understand why this architectural work matters, let’s compare a naive prompt with our expert system.

The Naive Prompt and Its Failure Mode

Most teams start with a simple, direct prompt:

"Fix this authentication error: NoneType object has no attribute 'id' in auth.py line 42"

Here is the predictable failure pattern we observe repeatedly:

  • Attempt 1: “Add a null check before accessing user.id”
  • Attempt 2: “Add a null check before accessing user.id” (same suggestion)
  • Attempt 3: “Try adding error handling around the user access”

Result: Generic solutions that do not address the underlying race condition between user deletion and session validity.

This fails because there is no systematic reasoning process, no context gathering to understand the authentication flow, and no hypothesis formation about why the user could be None. Solutions treat symptoms rather than root causes.

The agent gets stuck repeating surface-level fixes because it has no framework for deeper investigation.

This approach predictably fails, yielding generic, surface-level fixes like “add a null check” without understanding the underlying race condition between user deletion and session validity. It treats the symptom, not the cause.

The Expert Prompt and Its Success

Our expert approach, using the full architecture, handles the same issue systematically. When tested with a real authentication bug, it produces a high-value root-cause analysis and solution.

Expert System Output:

Root Cause: Race condition bug in the `authenticate_user` function

Explanation: The `authenticate_user` function assumes that if a session exists, the corresponding user must also exist in the database. However, this assumption is not always valid, as a user could be deleted while their session remains valid. When the `get_user_by_id` function returns `None`, the `user.id` attribute access on line 42 raises an `AttributeError`.

Confidence: HIGH. The provided code and error message clearly indicate the root cause.

Solution: Add proper null checking after user retrieval and clean up orphaned sessions:

def authenticate_user(token: str) -> Optional[int]:
session = get_session(token)
if session:
user = get_user_by_id(session.user_id)
if user: # Add null check here
return user.id
else:
# Handle orphaned session - clean up
invalidate_session(session)
return None
return None

This succeeds because the structured reasoning process mirrors how a senior engineer debugs complex issues: with hypothesis-driven investigation and a focus on the root cause.

Part III: The Integration Layer – Connecting to GitHub Actions

With our reasoning engine proven, we integrate it into a production workflow.

The Workflow Architecture

Our GitHub Actions workflow supports two trigger modes for maximum flexibility:

  1. Automatic Mode: Triggers when an issue is labeled bug-surgeon, opened, or edited
  2. Manual Mode: Engineers can run the workflow from the Actions tab, specifying any issue number

Here is the complete workflow configuration:

name: Claude Bug Surgeon

on:
issues:
types: [labeled, opened, edited]
workflow_dispatch:
inputs:
issue_number:
description: 'Issue number to analyze'
required: true
type: number

permissions:
contents: write
issues: write
pull-requests: write

jobs:
bug-analysis:
runs-on: ubuntu-latest
# Single-line condition required for valid YAML
if: ${{ github.event_name == 'workflow_dispatch' || contains(github.event.issue.labels.*.name, 'bug-surgeon') }}

steps:
- name: Checkout Repository
uses: actions/checkout@v4
with:
token: ${{ secrets.GITHUB_TOKEN }}
fetch-depth: 0

- name: Set up Python 3.11
uses: actions/setup-python@v4
with:
python-version: '3.11'

- name: Cache Python dependencies
uses: actions/cache@v3
with:
path: ~/.cache/pip
key: ${{ runner.os }}-pip-${{ hashFiles('**/requirements.txt') }}
restore-keys: |
${{ runner.os }}-pip-

- name: Install dependencies
run: |
python -m pip install --upgrade pip
pip install -r requirements.txt

- name: Set Issue Context
id: issue-context
run: |
if [ "${{ github.event_name }}" == "workflow_dispatch" ]; then
# Manual trigger: use input parameter
echo "ISSUE_NUMBER=${{ github.event.inputs.issue_number }}" >> $GITHUB_ENV
else
# Automatic trigger: extract from issue event
echo "ISSUE_NUMBER=${{ github.event.issue.number }}" >> $GITHUB_ENV
echo "ISSUE_BODY<<EOF" >> $GITHUB_ENV
echo "Title: ${{ github.event.issue.title }}" >> $GITHUB_ENV
echo "" >> $GITHUB_ENV
echo "Description:" >> $GITHUB_ENV
echo "${{ github.event.issue.body }}" >> $GITHUB_ENV
echo "EOF" >> $GITHUB_ENV
fi

- name: Run Bug Surgeon Analysis
env:
ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }}
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
GITHUB_REPOSITORY: ${{ github.repository }}
ISSUE_NUMBER: ${{ env.ISSUE_NUMBER }}
ISSUE_BODY: ${{ env.ISSUE_BODY }}
run: |
echo "🤖 Starting Bug Surgeon analysis for issue #$ISSUE_NUMBER..."
python debug_orchestrator.py

- name: Comment on Issue
if: success()
uses: actions/github-script@v6
with:
script: |
github.rest.issues.createComment({
issue_number: process.env.ISSUE_NUMBER,
owner: context.repo.owner,
repo: context.repo.repo,
body: 'Bug Surgeon Analysis Complete\n\nI\'ve completed my analysis of this issue. Check the pull request I created with my findings and recommended solution.\n\n*Powered by Claude Bug Surgeon*'
});

- name: Handle Analysis Failure
if: failure()
uses: actions/github-script@v6
with:
script: |
github.rest.issues.createComment({
issue_number: process.env.ISSUE_NUMBER,
owner: context.repo.owner,
repo: context.repo.repo,
body: ' **Bug Surgeon Analysis Failed**\n\nI encountered an error while analyzing this issue. Please check the workflow logs for details.\n\nCommon issues:\n- Missing or invalid `ANTHROPIC_API_KEY` secret\n- Repository access permissions\n- Malformed issue description\n\n*Powered by Claude Bug Surgeon*'
});

Understanding the Dual-Trigger Pattern

The workflow’s flexibility comes from its conditional logic:

if: ${{ github.event_name == 'workflow_dispatch' || contains(github.event.issue.labels.*.name, 'bug-surgeon') }}

This single-line condition (required for valid YAML) ensures the job runs when:

  • An engineer manually triggers it (workflow_dispatch)
  • OR an issue receives the bug-surgeon label

The “Set Issue Context” step then extracts the issue number appropriately:

  • Manual trigger: Uses github.event.inputs.issue_number from the workflow input
  • Automatic trigger: Uses github.event.issue.number from the issue event

This pattern is essential for production deployments where teams need both automated triage and ad-hoc analysis capabilities.

The Python Orchestrator

The orchestrator is built with production resilience in mind. Here is how it handles the GitHub Actions environment:

class BugSurgeon:
# Model fallback list ensures operational continuity
AVAILABLE_MODELS = [
"claude-3-5-sonnet-20240620",
"claude-3-sonnet-20240229",
"claude-3-haiku-20240307"
]

def __init__(self):
# Initialize Anthropic client
api_key = os.getenv('ANTHROPIC_API_KEY')
if not api_key:
raise ValueError("ANTHROPIC_API_KEY environment variable required")

self.claude = Anthropic(
api_key=api_key,
max_retries=3,
timeout=60.0
)

# Test model availability and select working version
self.working_model = self._find_working_model()

# Initialize GitHub client (optional for local testing)
github_token = os.getenv('GITHUB_TOKEN')
if github_token and github_token != 'dummy_token_for_local_test':
self.github = Github(github_token)
repo_name = os.getenv('GITHUB_REPOSITORY')
if repo_name:
self.repo = self.github.get_repo(repo_name)
else:
self.github = None
self.repo = None

def _find_working_model(self) -> str:
"""Find first available Claude model from preference list"""
for model in self.AVAILABLE_MODELS:
try:
response = self.claude.messages.create(
model=model,
max_tokens=10,
system="You are helpful.",
messages=[{"role": "user", "content": "Hi"}]
)
logger.info(f"Using model: {model}")
return model
except Exception as e:
logger.warning(f"Model {model} not available: {e}")
continue

# Fallback to default if none work
return self.AVAILABLE_MODELS[0]

Practitioner’s Note: The model fallback list is crucial for production deployments. API model versions can be deprecated with short notice. This pattern ensures your agent continues operating even when specific model versions become unavailable, preventing workflow failures during critical debugging sessions.

Security and Pull Request Automation

A core principle of secure AI deployment is maintaining human oversight. Our agent creates analysis reports as pull requests rather than committing code directly. This aligns with security frameworks like the NIST AI Risk Management Framework and mitigates risks outlined in the OWASP Top 10 for LLMs.

Setup Process

#1 Add API key to repository secrets:

  • Navigate to: Repository Settings → Secrets and Variables → Actions
  • Add new secret: ANTHROPIC_API_KEY
  • Paste your API key from https://console.anthropic.com/

#2 The orchestrator creates audit trails:

def create_analysis_pr(self, issue: Issue, analysis: BugAnalysis) -> Optional[str]:
"""Create pull request with bug analysis for human review"""
if not self.repo:
logger.warning("No repository configured - cannot create PR")
return None

try:
# Create dedicated branch for this analysis
main_ref = self.repo.get_git_ref('heads/main')
new_branch = f"bug-surgeon/fix-issue-{issue.number}"

self.repo.create_git_ref(
ref=f'refs/heads/{new_branch}',
sha=main_ref.object.sha
)

# Create comprehensive analysis document
analysis_content = f"""# Bug Analysis Report - Issue #{issue.number}

## Root Cause
{analysis.root_cause}

## Detailed Explanation
{analysis.explanation}

## Confidence Level
{analysis.confidence}

## Reasoning Trace
"""

for i, trace in enumerate(analysis.reasoning_trace, 1):
analysis_content += f"\n### Step {i}\n{trace}\n"

# Commit analysis file (not code changes)
self.repo.create_file(
path=f"bug-analysis-{issue.number}.md",
message=f"🤖 Bug analysis for issue #{issue.number}",
content=analysis_content,
branch=new_branch
)

# Create pull request for human review
pr_body = f"""🤖 **Automated Bug Analysis**

Issue: #{issue.number} - {issue.title}

Root Cause: {analysis.root_cause}

Confidence: {analysis.confidence}

Analysis: {analysis.explanation}

---
Generated by Claude Bug Surgeon
Review the analysis and apply fixes as appropriate
"""

pr = self.repo.create_pull_request(
title=f" Bug Analysis: {issue.title}",
body=pr_body,
head=new_branch,
base="main"
)

logger.info(f"Created PR #{pr.number}: {pr.html_url}")
return pr.html_url

except Exception as e:
logger.error(f"Error creating PR: {e}")
return None

This creates a complete, auditable trail from issue → analysis → human review, ensuring AI recommendations are validated before implementation.

Testing the Workflow

Local Testing

# Test with demo bug
python debug_orchestrator.py

# Test with custom description
python inter.py

GitHub Actions Testing

  1. Automatic Mode: Create a test issue in your repository and add the bug-surgeon label
  2. Manual Mode: Go to Actions → Claude Bug Surgeon → Run workflow → Enter issue number

The workflow will:

  • Analyze the issue using the ReAct framework
  • Read relevant code files from your repository
  • Create a pull request with detailed analysis
  • Comment on the original issue with results

Conclusion: The Shift Towards AI Orchestration

The central lesson of building production AI agents is that their value stems directly from their engineered reasoning process. The Bug Surgeon succeeds because it combines systematic analysis, transparent reasoning, and adaptive investigation – the hallmarks of expert debugging.

This is a fundamental shift in how senior engineers work. Rather than tracing codebases manually, you can now architect intelligence: designing reasoning patterns and orchestrating AI capabilities.

The role is evolving from hands-on debugging to AI orchestration – directing artificial intelligence to solve problems with the same systematic approach you would use yourself, but at machine speed. Organizations that master this gain a powerful competitive advantage through faster issue resolution and more consistent debugging quality, freeing senior engineers to focus on architecture and system design.

Anthropic’s Claude is particularly well-suited to this role because it is optimized for structured reasoning, long-context analysis, and predictable behavior under complex constraints. These properties make it a strong foundation for production agents that must analyze large codebases, reason step-by-step about failures, and operate safely within automated workflows like GitHub Actions.

Implementation Next Steps

  • Start Small: Deploy the Bug Surgeon on non-critical repositories first.
  • Iterate on Prompts: Use the <thinking> tag outputs to refine your system prompts.
  • Build Feedback Loops: Collect data from PR reviews to create better few-shot examples.
  • Scale Gradually: Extend to more complex debugging scenarios as confidence grows.

The future of software engineering is not about replacing human expertise; it is about amplifying it. Your experience architecting complex systems becomes the blueprint for highly intelligent AI agents that can apply that expertise at scale.

For enterprises, such AI agents make senior engineering judgment more repeatable and scalable. With Anthropic’s Claude providing reliable reasoning and Provectus helping design, build, and deploy production-ready agents, organizations can turn debugging and reliability into an operational capability rather than ad-hoc effort.

Visit our Anthropic practice page and learn more about how Provectus can help your organization build, leverage, and scale AI agents to drive value.

Related