Stop hunting through logs. Let AI tell you exactly what broke and who owns it.
Your pipeline fails. Instead of digging through hundreds of lines of logs, this agent automatically fetches them, classifies the failure, runs LLM-powered root cause analysis, and emails a structured incident report to the right team β in seconds, not hours.
Built on a three-stage classification pipeline (regex β semantic β LLM fallback) with a self-learning knowledge store: the more failures it sees, the faster and more accurate it gets.
Supports two modes of operation:
- π Webhook-driven HTTP API (FastAPI + Celery) β for production pipelines with Jenkins and GitHub Actions native integration
- β¨οΈ CLI (
cli.py) β for local debugging and one-off analysis from any CI/CD service, no Redis or database setup required
- ποΈ Architecture
- π Data Flow
- π§© Components
- π Project Structure
- β Prerequisites
- βοΈ Installation
- π§ Configuration
- π Running the Services
- β¨οΈ CLI
- π‘ API Reference
- π§ Classification Pipeline
- π οΈ Development
graph TB
subgraph External["CI/CD Systems"]
JK[Jenkins]
GH[GitHub Actions]
end
subgraph API["FastAPI Ingest Service β port 8000"]
ING["/failures/jenkins\n/failures/github"]
HLT["/health"]
end
subgraph Broker["Message Broker"]
RD[(Redis)]
end
subgraph Workers["Celery Workers"]
NF["normalize_failure\nβ fetch stage-wise logs\nβ write .log files"]
CF["classify_failure\nβ signal extraction\nβ deduplication\nβ classification"]
AF["analyze_failure\nβ LLM RCA\nβ report generation\nβ email"]
end
subgraph Classification["Classification Pipeline"]
LA["LogAnalyzer\nRegex signal extraction"]
DD["SmartDeDuplicator\nHDBSCAN clustering"]
KS["FailureKnowledgeDB\npgvector similarity search"]
CO["ClassificationOrchestrator\nFusion scoring"]
RC["RegexClassifier\nWeighted pattern matching"]
SC["SemanticClassifier\nFAISS k-NN"]
LLC["LLMClassifier\nLiteLLM fallback"]
end
subgraph RCALayer["RCA Layer"]
RE["RCAEngine\nstructured LLM output"]
PR["prompt.py\ncategory-aware prompt builder"]
end
subgraph Notifier["Notification"]
GR["generate_report\nHTML report renderer"]
MN["mail_notifier\nSMTP sender"]
end
subgraph Storage["Storage"]
PG[(PostgreSQL + pgvector)]
FS[Filesystem\nstorage/logs/]
FM[(FAISS Index\nmodels/)]
end
subgraph Connectors["CI Connectors"]
PF["PipelineFactory\nplatform router"]
JC["JenkinsClient\nBlue Ocean API"]
GC["GitHubClient\nGitHub Actions API"]
end
JK -->|webhook POST| ING
GH -->|webhook POST| ING
ING -->|persist record| PG
ING -->|enqueue 3 tasks| RD
RD -->|consume| NF
RD -->|consume| CF
RD -->|consume| AF
NF --> PF
PF --> JC
PF --> GC
JC -->|stage logs| FS
GC -->|stage logs| FS
NF -->|status update| PG
CF --> LA
LA -->|LogSignal list| DD
DD -->|embeddings| FM
DD -->|deduped signals| KS
KS -->|cache hit β SignalRCA| AF
KS -->|cache miss| CO
CO --> RC
CO --> SC
SC <-->|training / search| FM
CO --> LLC
LLC -->|OpenAI / LiteLLM| RCALayer
CF -->|error.json| FS
CF -->|status update| PG
AF --> RE
RE --> PR
RE -->|LiteLLM call| RCALayer
AF -->|store pattern| PG
AF -->|root_cause.json| FS
AF --> GR
GR -->|rca_report.html| FS
GR --> MN
MN -->|email| External
sequenceDiagram
participant CI as CI/CD System
participant API as FastAPI
participant Redis as Redis
participant NF as normalize_failure
participant CF as classify_failure
participant AF as analyze_failure
participant DB as PostgreSQL
participant FS as Filesystem
participant LLM as LLM / Embedding API
participant SMTP as SMTP Server
CI->>API: POST /failures/{platform}
API->>DB: Insert failure record (status=RECEIVED)
API->>Redis: Enqueue normalize / classify / analyze
API-->>CI: {failure_id, status: "Received successfully"}
Redis->>NF: Execute normalize_failure
NF->>CI: Fetch stage-wise logs (Jenkins Blue Ocean / GitHub API)
NF->>FS: Write <stage>.log files
NF->>DB: Update status = LOGS_COLLECTED
Redis->>CF: Execute classify_failure
CF->>FS: Read *.log files
CF->>CF: Extract LogSignals (regex patterns)
CF->>LLM: Generate text embeddings
CF->>CF: Deduplicate via HDBSCAN clustering
CF->>DB: pgvector cosine similarity search
alt Cache hit (similarity β₯ 0.92)
DB-->>CF: Return cached SignalRCA
CF->>FS: Write root_cause.json
CF->>DB: Update status = RESOLVED
else Cache miss
CF->>CF: Fused Regex + Semantic classification
CF->>LLM: LLM fallback for UNKNOWN signals
CF->>FS: Write error.json + embeddings.json
CF->>DB: Update status = CLASSIFIED
end
Redis->>AF: Execute analyze_failure
AF->>FS: Read error.json
AF->>LLM: Run structured RCA (instructor + LiteLLM)
AF->>DB: Upsert failure pattern (pgvector)
AF->>FS: Write root_cause.json
AF->>AF: generate_report β rca_report.html
AF->>SMTP: Send HTML email to owner team
AF->>DB: Update status = RESOLVED
| Component | Path | Responsibility |
|---|---|---|
| π FastAPI App | api/app/main.py |
HTTP server, startup hooks |
| β¨οΈ CLI App | cli.py |
CLI for root cause analysis |
| π₯ Ingest Routes | api/routes/ingest.py |
Accept Jenkins/GitHub failure webhooks |
| β€οΈ Health Route | api/routes/health.py |
Postgres / Redis / Celery health check |
| π PipelineFactory | analyzer/connectors/pipeline_factory.py |
Detect CI platform, delegate log fetch |
| π§ JenkinsClient | analyzer/connectors/jenkins_client.py |
Jenkins Blue Ocean REST API |
| π GitHubClient | analyzer/connectors/github_client.py |
GitHub Actions REST API |
| π LogAnalyzer | analyzer/extractors/log_analyzer.py |
Regex-based signal extraction from logs |
| π§Ή SmartDeDuplicator | analyzer/deduplicator/smart_deduplicator.py |
HDBSCAN semantic deduplication |
| π RegexClassifier | analyzer/classifiers/regex_classifier.py |
Weighted regex pattern scoring |
| π§ SemanticClassifier | analyzer/classifiers/semantic_classifier.py |
FAISS k-NN nearest-neighbour classifier |
| π€ LLMClassifier | analyzer/classifiers/llm_classifier.py |
LLM fallback for unresolved signals |
| ποΈ ClassificationOrchestrator | analyzer/classifiers/classification_orchestrator.py |
Fuse regex+semantic, auto-learn feedback |
| π EmbeddingService | analyzer/embedding/embedding_service.py |
LiteLLM embedding wrapper (singleton) |
| π¬ RCAEngine | analyzer/rca_engine/rca_engine.py |
LLM-based structured RCA |
| π generate_report | analyzer/notifier/generate_report.py |
HTML incident report builder |
| π§ mail_notifier | analyzer/notifier/mail_notifier.py |
SMTP email dispatch |
| ποΈ DatabaseInit | storage/database.py |
PostgreSQL schema bootstrap |
| πΎ LogStorer | storage/logs.py |
Read/write log and result files |
| π PipelineFailureDB | storage/pipeline_failure_record.py |
Failure metadata CRUD |
| 𧬠FailureKnowledgeDB | storage/failure_knowledge_record.py |
pgvector pattern store + similarity search |
| βοΈ Celery Tasks | workers/tasks.py |
normalize / classify / analyze async tasks |
ci-root-cause-analyzer/
βββ api/
β βββ app/
β β βββ config.py # Pydantic settings (reads from .env)
β β βββ main.py # FastAPI application factory
β βββ routes/
β β βββ health.py # GET /health
β β βββ ingest.py # POST /failures/jenkins, /failures/github
β βββ schemas/ # Pydantic request / response models
βββ analyzer/
β βββ classifiers/
β β βββ classification_orchestrator.py # Fusion + auto-learn
β β βββ failure_patterns.py # Load YAML patterns
β β βββ failure_patterns.yaml # Regex patterns per category
β β βββ llm_classifier.py # LLM fallback classifier
β β βββ regex_classifier.py # Weighted regex scorer
β β βββ semantic_classifier.py # FAISS k-NN classifier
β β βββ training/
β β βββ synthetic_data_generator.py # Bootstrap training data
β βββ connectors/
β β βββ github_client.py # GitHub Actions API client
β β βββ jenkins_client.py # Jenkins Blue Ocean API client
β β βββ pipeline_factory.py # Platform detection + routing
β βββ deduplicator/
β β βββ smart_deduplicator.py # HDBSCAN-based dedup
β βββ embedding/
β β βββ embedding_service.py # LiteLLM embedding singleton
β βββ extractors/
β β βββ log_analyzer.py # Log β LogSignal extraction
β βββ notifier/
β β βββ generate_report.py # HTML report generator
β β βββ mail_notifier.py # SMTP email sender
β βββ ownership/
β β βββ ownership_config.py # Category β team mapping
β βββ rca_engine/
β βββ prompt.py # Category-aware prompt builder
β βββ rca_engine.py # Structured LLM RCA runner
βββ models/
β βββ semantic.faiss # FAISS flat L2 index
β βββ semantic.pkl # SemanticClassifier metadata
βββ storage/
β βββ database.py # PostgreSQL init / table bootstrap
β βββ failure_knowledge_record.py # pgvector knowledge store
β βββ init.sql # SQL init script for Docker
β βββ logs.py # Log file read/write helpers
β βββ pipeline_failure_record.py # Failure metadata store
βββ utils/
β βββ execute_notifier.py # Report + email orchestrator
β βββ hash_utils.py # SHA-256 fingerprint generator
β βββ text_normalizer.py # Log text normalization
βββ workers/
β βββ celery_app.py # Celery app + broker config
β βββ tasks.py # normalize / classify / analyze tasks
βββ cli.py # CLI entry point (no Celery/Redis required)
βββ docker-compose.yml
βββ Dockerfile
βββ pyproject.toml
βββ requirements.txt
| Requirement | Details |
|---|---|
| π³ Docker β₯ 24 + Docker Compose β₯ 2 | Required for containerised deployment |
| π€ LLM API key | Any LiteLLM-compatible provider (OpenAI, Azure, Groq, etc.) |
| π§ SMTP account | For email delivery of RCA reports |
| π§ Jenkins or GitHub Actions | CI system to send webhooks (HTTP API mode) |
# Clone repository
git clone <repo-url>
cd ci-root-cause-analyzer
# Copy the environment template and fill in values
cp .env.example .envCreate a .env file in the project root:
# ποΈ PostgreSQL
POSTGRES_USER=agentic
POSTGRES_PASSWORD=agentic
POSTGRES_DB=agentic_db
DB_HOST=postgresql
DB_PORT=5432
# β‘ Redis
REDIS_HOST=redis
REDIS_PORT=6379
# π§ Jenkins
JENKINS_URL=https://<jenkins-host>/blue/rest/organizations/jenkins/
JENKINS_USER=<username>
JENKINS_TOKEN=<api-token>
# π GitHub
GITHUB_TOKEN=<personal-access-token>
GITHUB_API_BASE_URL=https://api.github.com
# π€ LLM (any LiteLLM-supported provider)
LLM_API_KEY=<api-key>
RCA_LLM_DEPLOYMENT=gpt-4o-mini
EMBEDDING_MODEL=text-embedding-3-small
RCA_TEMPERATURE=0
CLASSIFY_TEMPERATURE=0
# π§ SMTP
SMTP_SERVER=smtp.example.com
SMTP_PORT=587
SMTP_USER=sender@example.com
SMTP_PASSWORD=<password>
DEFAULT_MAIL=fallback@example.com
# πΎ Storage
LOG_PATH=storage/logs
SEMANTIC_PATH=models/semantic.pkl
FAILURE_TABLE=failures
FAILURE_PATTERN_TABLE=failure_knowledge_table# Build and start all services
docker compose up --build
# Start in detached mode
docker compose up --build -d
# View logs
docker compose logs -f ingest
docker compose logs -f dev_agent
# Stop all services
docker compose downServices started:
| Service | Port | Description |
|---|---|---|
ποΈ postgresql |
5432 |
PostgreSQL 17 + pgvector |
β‘ redis |
6379 |
Redis 8 message broker |
π ingest |
8000 |
FastAPI ingest service |
βοΈ dev_agent |
β | Celery worker |
# Install dependencies
pip install -r requirements.txt
# Start FastAPI
uvicorn api.app.main:app --host 0.0.0.0 --port 8000 --reload
# Start Celery worker (separate terminal)
celery -A workers.tasks worker --loglevel=INFO -P solo
# Pre-generate semantic classifier training data
python -c "from analyzer.classifiers.training.synthetic_data_generator import generate_and_save; generate_and_save()"cli.py runs the full analysis pipeline synchronously and without Celery or Redis β useful for local debugging, one-off analysis, and CI scripts. It calls the same underlying service functions as the Celery tasks.
The analyze logs subcommand accepts plain .log files from any source β Jenkins, GitHub Actions, GitLab CI, CircleCI, Bitbucket Pipelines, Azure DevOps, or any custom service.
pip install -r requirements.txt # includes typer[all]All three subcommands accept --use-db (off by default). Without it, no PostgreSQL connection is required.
With --use-db you get |
Without --use-db |
|---|---|
| β Failure records persisted to PostgreSQL | β No DB writes |
| β pgvector cache lookup (β₯ 0.92 similarity = instant recall) | β Always runs full RCA |
| β Newly analysed patterns stored for future cache hits | β No pattern learning |
# fully local β no database needed
python cli.py analyze jenkins \
--job-name "my-project/my-pipeline" \
--build-number 42 \
--commit abc123 \
--branch main
# with DB persistence and email
python cli.py analyze jenkins \
--job-name "my-project/my-pipeline" \
--build-number 42 \
--commit abc123 \
--branch main \
--dev-email dev@example.com \
--ci-email devops@example.com \
--use-dbPrompts for any omitted required options.
# fully local β no database needed
python cli.py analyze github \
--owner my-org \
--repo my-repo \
--run-id 12345678 \
--commit abc123 \
--branch main
# with DB persistence
python cli.py analyze github \
--owner my-org --repo my-repo --run-id 12345678 \
--commit abc123 --branch main \
--dev-email dev@example.com \
--use-dbNo Jenkins or GitHub connection needed. Point the command at any directory containing .log files β from any CI/CD platform:
# fully local β no database needed
python cli.py analyze logs ./my-logs/
# with email notification
python cli.py analyze logs ./my-logs/ \
--dev-email dev@example.com \
--branch feature/auth
# enable pgvector knowledge-store lookup and pattern storage
python cli.py analyze logs ./my-logs/ --use-dbThe command copies your .log files into storage/logs/<failure_id>/, runs the full extract β deduplicate β classify β RCA chain, and writes results alongside them.
| Capability | π HTTP + Celery | β¨οΈ CLI (default) | β¨οΈ CLI (--use-db) |
|---|---|---|---|
| Async / parallel tasks | β | β sequential | β sequential |
| Redis broker | required | not needed | not needed |
| PostgreSQL | required | not needed | required |
| Failure record + status tracking | β | β | β |
| pgvector cache lookup | β | β | β |
| Pattern auto-learning | β | β | β |
| HTML report + email | β | β | β |
Results are written to storage/logs/<failure_id>/:
| File | Contents |
|---|---|
π <stage>.log |
Raw stage logs (jenkins/github) or copied input logs |
π error.json |
Classified signals with category, confidence, owner |
π§ root_cause.json |
Structured RCA results per signal |
π rca_report.html |
HTML incident report (same as emailed report) |
Ingest a Jenkins pipeline failure.
Request body:
{
"commit": "abc123def456",
"branch": "main",
"job_name": "my-project/my-pipeline",
"build_number": 42,
"mailRecipient": {
"dev_email": "dev@example.com",
"test_email": "qa@example.com",
"ci_email": "devops@example.com"
}
}Response:
{
"failure_id": "550e8400-e29b-41d4-a716-446655440000",
"data": { "..." : "..." },
"status": "Received successfully"
}Ingest a GitHub Actions workflow failure.
Request body:
{
"commit": "abc123def456",
"branch": "main",
"repo": "my-repo",
"owner": "my-org",
"run_id": 12345678,
"mailRecipient": {
"dev_email": "dev@example.com",
"ci_email": "devops@example.com"
}
}Response: same shape as Jenkins response.
Returns liveness and readiness of all dependencies.
Response:
{
"status": "healthy",
"postgres": { "status": "ok", "latency_ms": 1.23 },
"redis": { "status": "ok", "latency_ms": 0.45 },
"celery": { "status": "ok", "workers": 1, "worker_names": ["celery@hostname"] }
}π Interactive API Docs (after starting the service):
- Swagger UI: http://localhost:8000/docs
- ReDoc: http://localhost:8000/redoc
Failures are classified across 3 categories using a three-stage pipeline:
| Stage | Method | Fallback |
|---|---|---|
| 1οΈβ£ Regex | Weighted pattern matching against error line, context and stage | β |
| 2οΈβ£ Semantic | FAISS k-NN on OpenAI embeddings | Trained on synthetic data |
| 3οΈβ£ LLM | LiteLLM structured output | Only for UNKNOWN signals |
| Category | Covers | Owner Team |
|---|---|---|
π΄ DEV_FAILURE |
Compilation errors, linker failures, missing dependencies, code quality gate failures | π©βπ» Developers |
π‘ TEST_FAILURE |
Test assertion failures, flaky tests, fixture/snapshot mismatches, test timeouts | π§ͺ QA Engineers |
π΅ CI_INFRA_FAILURE |
Pipeline config, env/secrets, artifact publishing, Docker, Kubernetes, network, resource exhaustion, CI agents | π οΈ DevOps Engineers |
Regex and semantic scores are combined with fixed weights before applying per-category confidence thresholds:
fused_score = (0.65 Γ regex_confidence) + (0.35 Γ semantic_confidence)
Signals whose fused score falls below ABSOLUTE_MIN_CONFIDENCE = 0.20 are always marked UNKNOWN and routed to the LLM classifier.
High-confidence classifications (confidence > 0.80) are fed back into the FAISS index as new training examples. After 20 feedback samples accumulate the index is retrained and persisted to models/semantic.faiss + models/semantic.pkl.
pytestpython -m analyzer.classifiers.training.synthetic_data_generatorAfter starting the service, open:
- π’ Swagger UI: http://localhost:8000/docs
- π ReDoc: http://localhost:8000/redoc