Kushal Shah commited on
Commit
00da95b
·
1 Parent(s): cd04de2

Add Agentic Incident Response gradio app

Browse files
.gitignore ADDED
@@ -0,0 +1,4 @@
 
 
 
 
 
1
+ .env
2
+ __pycache__/
3
+ *.pyc
4
+ .venv/
README.md CHANGED
@@ -1,6 +1,6 @@
1
  ---
2
  title: Autonomous System Diagnostics
3
- emoji: 😻
4
  colorFrom: indigo
5
  colorTo: red
6
  sdk: gradio
@@ -10,4 +10,20 @@ app_file: app.py
10
  pinned: false
11
  ---
12
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
13
  Check out the configuration reference at https://huggingface.co/docs/hub/spaces-config-reference
 
1
  ---
2
  title: Autonomous System Diagnostics
3
+ emoji: 🛰️
4
  colorFrom: indigo
5
  colorTo: red
6
  sdk: gradio
 
10
  pinned: false
11
  ---
12
 
13
+ # Autonomous System Diagnostics & Remediation
14
+
15
+ A multi-agent incident response system powered by Gemini. Paste in server logs,
16
+ an incident ticket, and a runbook, and a chain of specialized agents will:
17
+
18
+ 1. **Log Analysis** – surface anomalies and error patterns
19
+ 2. **Incident Correlation** – check the logs against the reported ticket
20
+ 3. **Root Cause Analysis** – pinpoint the cause using the runbook
21
+ 4. **Resolution** – propose a step-by-step remediation plan
22
+ 5. **Reporting** – generate a post-incident report (PIR)
23
+
24
+ ## Setup
25
+
26
+ This Space requires a `GEMINI_API_KEY`. Add it under
27
+ **Settings → Variables and secrets** as a repository secret.
28
+
29
  Check out the configuration reference at https://huggingface.co/docs/hub/spaces-config-reference
agents.py ADDED
@@ -0,0 +1,92 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import os
2
+ import google.generativeai as genai
3
+ from dotenv import load_dotenv
4
+
5
+ # Load env variables
6
+ load_dotenv()
7
+
8
+ class IncidentAgents:
9
+ def __init__(self):
10
+ # Configure Gemini
11
+ api_key = os.getenv("GEMINI_API_KEY")
12
+ if not api_key:
13
+ raise ValueError("GEMINI_API_KEY not found in .env file")
14
+
15
+ genai.configure(api_key=api_key)
16
+
17
+ # We use Gemini 1.5 Flash for speed and efficiency
18
+ self.model_name = "gemini-2.5-flash"
19
+
20
+ def _call_llm(self, role_description, user_content):
21
+ """Helper to call Gemini API"""
22
+
23
+ # Initialize the model with a system instruction (Role)
24
+ model = genai.GenerativeModel(
25
+ model_name=self.model_name,
26
+ system_instruction=role_description
27
+ )
28
+
29
+ # Generate content
30
+ try:
31
+ response = model.generate_content(user_content)
32
+ return response.text
33
+ except Exception as e:
34
+ return f"Error communicating with Gemini: {e}"
35
+
36
+ # --- AGENT 1: Log Analyst ---
37
+ def log_analysis_agent(self, logs):
38
+ print("🔎 Log Analysis Agent Working...")
39
+ role = """
40
+ You are a Senior Site Reliability Engineer (SRE).
41
+ Your task is to analyze server logs.
42
+ Identify patterns of errors, timestamps of failure start, and specific error messages.
43
+ Output a concise summary of the anomalies found.
44
+ """
45
+ return self._call_llm(role, f"Analyze these logs:\n{logs}")
46
+
47
+ # --- AGENT 2: Incident Correlator ---
48
+ def incident_correlator_agent(self, ticket, log_analysis):
49
+ print("🔗 Incident Correlator Agent Working...")
50
+ role = """
51
+ You are an Incident Commander.
52
+ Correlate the user-reported incident ticket with the technical log analysis.
53
+ Confirm if the logs support the ticket description.
54
+ """
55
+ content = f"Ticket:\n{ticket}\n\nLog Analysis:\n{log_analysis}"
56
+ return self._call_llm(role, content)
57
+
58
+ # --- AGENT 3: Root Cause Analyst (RCA) ---
59
+ def root_cause_agent(self, correlation_findings, runbook):
60
+ print("🧠 Root Cause Agent Working...")
61
+ role = """
62
+ You are a Root Cause Analysis Expert.
63
+ Using the incident correlation and the provided Engineering Runbook, determine the most likely root cause.
64
+ Cite the specific section of the runbook that matches the symptoms.
65
+ """
66
+ content = f"Findings:\n{correlation_findings}\n\nRunbook:\n{runbook}"
67
+ return self._call_llm(role, content)
68
+
69
+ # --- AGENT 4: Resolution Agent ---
70
+ def resolution_agent(self, root_cause_analysis, runbook):
71
+ print("🛠️ Resolution Agent Working...")
72
+ role = """
73
+ You are a DevOps Automation Engineer.
74
+ Based on the identified root cause and the runbook, generate a step-by-step remediation plan.
75
+ If the runbook has specific commands, include them.
76
+ """
77
+ content = f"RCA:\n{root_cause_analysis}\n\nRunbook Content:\n{runbook}"
78
+ return self._call_llm(role, content)
79
+
80
+ # --- AGENT 5: Report Generator ---
81
+ def report_agent(self, ticket, rca, resolution):
82
+ print("📝 Report Agent Working...")
83
+ role = """
84
+ You are a Technical Writer.
85
+ Generate a professional Post-Incident Report (PIR) in Markdown format.
86
+ Include:
87
+ 1. Executive Summary
88
+ 2. Root Cause
89
+ 3. Remediation Taken/Suggested
90
+ """
91
+ content = f"Ticket: {ticket}\nRCA: {rca}\nResolution: {resolution}"
92
+ return self._call_llm(role, content)
app.py ADDED
@@ -0,0 +1,101 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import gradio as gr
2
+ import os
3
+ import json
4
+ from dotenv import load_dotenv
5
+ from agents import IncidentAgents
6
+ from tools import read_log_file, read_ticket_data, read_runbook
7
+
8
+ # Load environment variables
9
+ load_dotenv()
10
+
11
+ # --- DEFAULT DATA (sample incident, used to pre-fill the UI) ---
12
+ BASE_DIR = os.path.dirname(os.path.abspath(__file__))
13
+
14
+ DEFAULT_LOGS = read_log_file(os.path.join(BASE_DIR, "data", "logs", "server_logs.txt"))
15
+ DEFAULT_TICKET = json.dumps(
16
+ read_ticket_data(os.path.join(BASE_DIR, "data", "tickets", "incident_ticket.json")),
17
+ indent=2
18
+ )
19
+ DEFAULT_RUNBOOK = read_runbook(os.path.join(BASE_DIR, "data", "docs", "runbook.md"))
20
+
21
+ # --- 2. THE CORE WORKFLOW FUNCTION ---
22
+ def run_incident_response(logs_input, ticket_input, runbook_input):
23
+ """
24
+ This function connects the UI inputs to the Agent Logic.
25
+ """
26
+ try:
27
+ # Initialize Agents
28
+ agents = IncidentAgents()
29
+
30
+ # Step A: Log Analysis
31
+ log_analysis = agents.log_analysis_agent(logs_input)
32
+ yield log_analysis, "...", "...", "...", "..." # Stream updates to UI
33
+
34
+ # Step B: Correlation
35
+ correlation = agents.incident_correlator_agent(ticket_input, log_analysis)
36
+ yield log_analysis, correlation, "...", "...", "..."
37
+
38
+ # Step C: Root Cause Analysis
39
+ rca = agents.root_cause_agent(correlation, runbook_input)
40
+ yield log_analysis, correlation, rca, "...", "..."
41
+
42
+ # Step D: Resolution
43
+ resolution = agents.resolution_agent(rca, runbook_input)
44
+ yield log_analysis, correlation, rca, resolution, "..."
45
+
46
+ # Step E: Final Report
47
+ final_report = agents.report_agent(ticket_input, rca, resolution)
48
+ yield log_analysis, correlation, rca, resolution, final_report
49
+
50
+ except Exception as e:
51
+ error_msg = f"Error: {str(e)}"
52
+ yield error_msg, error_msg, error_msg, error_msg, error_msg
53
+
54
+ # --- 3. BUILD THE GRADIO UI ---
55
+ with gr.Blocks(title="Agentic Incident Response") as demo:
56
+
57
+ # Header
58
+ gr.Markdown("# AI Agent Incident Response System")
59
+ gr.Markdown("Enter system logs, an incident ticket, and a runbook. The Multi-Agent System will analyze, correlate, and fix the issue.")
60
+
61
+ # Input Section
62
+ with gr.Row():
63
+ with gr.Column():
64
+ logs_box = gr.Textbox(label="1. System Logs", value=DEFAULT_LOGS, lines=8, placeholder="Paste logs here...")
65
+ with gr.Column():
66
+ ticket_box = gr.Textbox(label="2. Incident Ticket (JSON)", value=DEFAULT_TICKET, lines=8, placeholder="Paste ticket JSON here...")
67
+ with gr.Column():
68
+ runbook_box = gr.Textbox(label="3. Runbook (Markdown)", value=DEFAULT_RUNBOOK, lines=8, placeholder="Paste runbook here...")
69
+
70
+ # Action Button
71
+ run_btn = gr.Button("Run Automated Response Workflow", variant="primary")
72
+
73
+ # Output Section (The "Agent Minds")
74
+ gr.Markdown("### Agent Reasoning Chain")
75
+
76
+ with gr.Accordion("Step 1: Log Analysis Agent", open=False):
77
+ out_logs = gr.Markdown()
78
+
79
+ with gr.Accordion("Step 2: Incident Correlator Agent", open=False):
80
+ out_correlation = gr.Markdown()
81
+
82
+ with gr.Accordion("Step 3: Root Cause Analysis (RCA) Agent", open=False):
83
+ out_rca = gr.Markdown()
84
+
85
+ with gr.Accordion("Step 4: Resolution Agent", open=True):
86
+ out_resolution = gr.Markdown()
87
+
88
+ # Final Report Section
89
+ gr.Markdown("### Final Post-Incident Report")
90
+ out_final_report = gr.Markdown()
91
+
92
+ # Click Event
93
+ run_btn.click(
94
+ fn=run_incident_response,
95
+ inputs=[logs_box, ticket_box, runbook_box],
96
+ outputs=[out_logs, out_correlation, out_rca, out_resolution, out_final_report]
97
+ )
98
+
99
+ # Launch
100
+ if __name__ == "__main__":
101
+ demo.launch(theme=gr.themes.Soft())
data/docs/runbook.md ADDED
@@ -0,0 +1,16 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Engineering Runbook: Database Connection Issues
2
+
3
+ ## Error: ConnectionPoolTimeoutError
4
+ **Symptoms:** - API returns 500 status codes.
5
+ - Logs show "Timeout waiting for connection from pool".
6
+ - Latency spikes on endpoints /api/v1/users.
7
+
8
+ ## Root Causes
9
+ 1. **Traffic Spike:** Sudden increase in concurrent users exceeding `MAX_CONNECTIONS`.
10
+ 2. **Zombie Connections:** Application not closing connections properly (missing `.close()` or `try/finally` blocks).
11
+ 3. **Database Maintenance:** DB is undergoing patches or backups.
12
+
13
+ ## Remediation Steps
14
+ 1. **Immediate Mitigation:** Restart the application service (`sudo systemctl restart api-service`) to flush the pool.
15
+ 2. **Investigation:** Check `pg_stat_activity` for idle transactions.
16
+ 3. **Long-term Fix:** Increase `MAX_CONNECTIONS` in `config.yaml` or implement connection pooling proxy (PgBouncer).
data/logs/server_logs.txt ADDED
@@ -0,0 +1,8 @@
 
 
 
 
 
 
 
 
 
1
+ 2024-10-24 14:28:10 [INFO] Request received: GET /api/v1/health - 200 OK
2
+ 2024-10-24 14:29:15 [INFO] Worker process started (PID 4021)
3
+ 2024-10-24 14:30:05 [WARN] Connection pool usage at 85%
4
+ 2024-10-24 14:30:45 [WARN] Connection pool usage at 95% - approaching limit
5
+ 2024-10-24 14:31:02 [ERROR] [PID 4021] Failed to acquire connection. Error: sqlalchemy.exc.TimeoutError: QueuePool limit of size 10 overflow 10 reached, connection timed out, timeout 30.
6
+ 2024-10-24 14:31:02 [ERROR] Request failed: GET /api/v1/users/login - 500 Internal Server Error
7
+ 2024-10-24 14:31:05 [ERROR] [PID 4021] Failed to acquire connection.
8
+ 2024-10-24 14:31:10 [CRITICAL] Service health check failing. Database unreachable.
data/tickets/incident_ticket.json ADDED
@@ -0,0 +1,8 @@
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "ticket_id": "INC-2024-001",
3
+ "priority": "P1",
4
+ "status": "OPEN",
5
+ "title": "API returning 500 errors on User Service",
6
+ "description": "Customer reports unable to login. Monitoring dashboard shows 500 error rate spike starting at 14:30 UTC.",
7
+ "reported_at": "2024-10-24T14:35:00Z"
8
+ }
requirements.txt ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ google-generativeai
2
+ python-dotenv
3
+ gradio
tools.py ADDED
@@ -0,0 +1,26 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import os
2
+ import json
3
+
4
+ def read_log_file(filepath):
5
+ """Reads the raw log file."""
6
+ try:
7
+ with open(filepath, 'r') as f:
8
+ return f.read()
9
+ except FileNotFoundError:
10
+ return "Error: Log file not found."
11
+
12
+ def read_ticket_data(filepath):
13
+ """Reads the JSON incident ticket."""
14
+ try:
15
+ with open(filepath, 'r') as f:
16
+ return json.load(f)
17
+ except FileNotFoundError:
18
+ return {"error": "Ticket file not found."}
19
+
20
+ def read_runbook(filepath):
21
+ """Reads the markdown runbook."""
22
+ try:
23
+ with open(filepath, 'r') as f:
24
+ return f.read()
25
+ except FileNotFoundError:
26
+ return "Error: Runbook not found."