A multi-agent LLM diagnostic pipeline for satellite Network Operations Centers, designed to run fully offline in air-gapped environments.
VSAT AIOps Nexus is an agentic diagnostic assistant for VSAT (Very Small Aperture Terminal) network operations. A NOC engineer describes a network symptom in natural language, and the system:
- Extracts specific technical symptoms from the input
- Retrieves candidate diagnoses from a local vector knowledge base (RAG)
- Investigates the issue by calling SNMP-style tools to gather evidence
- Scores every candidate against the accumulated evidence
- Critiques its own reasoning to catch logical contradictions and hallucinations
- Returns a structured incident report with confidence scoring and recovery steps
Everything runs locally. No external API calls. No data leaves the network. No cloud dependencies.
This is a public, sanitized demo of a system originally architected for civil aviation NOC operations, where strict air-gap compliance and data sovereignty are mandatory by regulation.
Most "AI for ops" projects assume unlimited cloud access. In civil aviation, defense, and other regulated environments, that assumption is illegal. Real-world constraints look like this:
| Constraint | Implication |
|---|---|
| Air-gapped network | No outbound internet. No OpenAI, no Claude, no Gemini API calls. |
| Data sovereignty | Telemetry, configuration, and incident data cannot leave the perimeter. |
| Regulated SLAs | Mean Time To Repair must be measurable and auditable. |
| No model retraining | Models must work out-of-the-box on edge hardware. |
This demo demonstrates one viable answer: a local quantized LLM (Qwen2.5) orchestrated by LangGraph, grounded in a curated RAG knowledge base, validated by a self-critic node — all running on a single workstation.
flowchart LR
Engineer([👨💻 NOC Engineer]) -->|Natural language symptom| UI[Flutter Desktop UI]
UI -->|HTTP / SSE Stream| API[FastAPI Backend]
API --> Graph[LangGraph<br>Agent Pipeline]
Graph -->|Semantic search| Chroma[(ChromaDB<br>Knowledge Base)]
Graph -->|Tool calls| SNMP[SNMP Mock Tools<br>buc / lnb / pvc / snr / ...]
Graph -->|Local inference| Ollama[Ollama<br>Qwen2.5:14b]
Telemetry[Telemetry Monitor<br>Z-Score Anomaly Detection] -->|Auto-trigger| API
style Graph fill:#0e7490,color:#fff
style Ollama fill:#1f2937,color:#fff
style Chroma fill:#7c3aed,color:#fff
The agent is a LangGraph state machine with 8 nodes and conditional routing. Loops are bounded — investigation max 3 rounds, critic max 2 rounds — to guarantee termination.
flowchart TD
START([START]) --> SE[🩺 Symptom Extractor]
SE -->|General chat| RESP[🗣️ Responder]
SE -->|Technical issue| TR[🔍 Triage Retriever<br>ChromaDB Semantic Search]
TR --> INV[🕵️ Investigator<br>Plans next tools]
INV --> EXEC[⚙️ Execute Tools<br>SNMP queries]
EXEC --> EVAL[⚖️ Evaluator<br>Score candidates 0–100]
EVAL -->|Score < 70 AND loop < 3| INV
EVAL -->|Score ≥ 70 OR loop = 3| CRITIC[🔬 Critic<br>Hard contradiction check]
CRITIC -->|REJECT| EVAL
CRITIC -->|CONFIRMED| RES[🎯 Resolution Retriever<br>Fetch fix steps]
RES --> RESP
RESP --> END([END])
style SE fill:#0891b2,color:#fff
style EVAL fill:#7c3aed,color:#fff
style CRITIC fill:#dc2626,color:#fff
style RESP fill:#059669,color:#fff
| Node | Purpose | Why it exists |
|---|---|---|
| Symptom Extractor | Triages technical vs. general chat | Avoids running expensive RAG on greetings |
| Triage Retriever | Top-K vector search on ChromaDB | Narrows the candidate space before reasoning |
| Investigator | Plans which SNMP tools to run next | Differentiates between candidates with minimal tool calls |
| Execute Tools | Runs queued tools, dedupes | Single point of side effects |
| Evaluator | Scores candidates against all evidence | Penalizes contradictions (-30), rewards confirmations (+15) |
| Critic | Independent peer review | Catches logical flaws the Evaluator missed (anti-hallucination guardrail) |
| Resolution Retriever | Fetches the linked fix card | Decoupled diagnosis from resolution for maintainability |
| Responder | Generates the final incident report | Adapts language (en/ar) and formats Markdown for the UI |
- Python 3.10 or higher
- Ollama installed and running
- ~16 GB RAM (for
qwen2.5:14b) or ~8 GB RAM (forqwen2.5:7b)
# Install Ollama (Linux / macOS)
curl -fsSL https://ollama.com/install.sh | sh
# Pull Qwen2.5 14B (recommended) — about 9 GB
ollama pull qwen2.5:14b
# OR pull the lighter 7B variant — about 5 GB
ollama pull qwen2.5:7bgit clone https://github.com/mmwady/ai_noc_assistant.git
cd ai_noc_assistant/backend
# Create a virtual environment
python -m venv myvenv
source myvenv/bin/activate # On Windows: myvenv\Scripts\activate
# Install Python dependencies
pip install -r requirements.txt
# Copy the example environment file and edit as needed
cp .env.example .envpython run_server.pyThe FastAPI server will start on http://localhost:8000. Visit http://localhost:8000/docs for the interactive Swagger UI. On first run, the local knowledge base (ChromaDB) is seeded with synthetic VSAT incident cards covering common failure modes (BUC failures, LNB alarms, PVC issues, SNR degradation, port outages, etc.).
# Send a diagnostic query
curl -X POST http://localhost:8000/ask-stream-all-at-once-tools \
-H "Content-Type: application/json" \
-d '{
"message": "I have a problem with the voice service at SITE_001",
"lang": "en",
"thread_id": "demo-session",
"site": "SITE_001"
}'You will see the agent stream its reasoning node-by-node via Server-Sent Events.
| Query | What the agent should do |
|---|---|
"No data service at SITE_002, modem shows red light" |
Extract symptoms → retrieve modem-related cards → check modem_status, pvc_status, snr → diagnose modem alarm |
"Voice and data both down at SITE_003 since this morning" |
Multi-symptom case → retrieve broader candidates → verify with multiple tools → likely BUC or LNB hardware fault |
"Hello, how are you today?" |
Symptom Extractor classifies as general chat → skip the entire pipeline → direct conversational reply |
"SNR dropped to 4 dB at SITE_001" |
Auto-anomaly path: Z-score detector triggers diagnosis without operator input |
| Layer | Technology |
|---|---|
| Agent Orchestration | LangGraph — stateful multi-agent graphs with conditional edges |
| LLM Inference | Ollama running Qwen2.5:14b (GGUF quantized) |
| Structured Outputs | Pydantic + with_structured_output(method="json_schema") |
| Vector Search | ChromaDB with BAAI/bge-small-en-v1.5 embeddings (FastEmbed) |
| Backend API | FastAPI with async streaming via Server-Sent Events |
| Telemetry Persistence | SQLite (incident cards) + Chroma (vectors) |
| Anomaly Detection | Custom Z-score detector with rolling baseline rebuild |
| Frontend | Flutter cross-platform desktop dashboard (see vsat_manager/) |
ai_noc_assistant/
├── backend/ # Python backend (FastAPI + LangGraph agent)
│ ├── run_server.py # FastAPI entry point — starts the API server
│ ├── langGraph_design_function_tools.py # LangGraph build + 8-node agent pipeline + SNMP mock tools
│ ├── telemetry_monitor.py # Z-score anomaly detection with rolling baseline
│ ├── test_noc_graph.py # Graph routing / node behavior tests
│ ├── patterns.json # Symptom/diagnosis pattern definitions
│ ├── RAG_System/ # ChromaDB retrieval + ingestion logic
│ ├── saving_retrieving/ # Card persistence (SQLite + Chroma CRUD)
│ ├── .env.example # Template for environment variables
│ ├── requirements.txt
│ └── database/
│ ├── cards/
│ │ ├── chroma_db/ # Vector store for incident cards
│ │ └── sqlite_db/ # Structured incident card storage
│ └── docs/
│ └── chroma_db/ # Vector store for troubleshooting docs
│
└── vsat_manager/ # Flutter cross-platform desktop frontend (NOC dashboard)
All configuration lives in backend/.env. The defaults work out of the box for local Ollama:
# LLM Provider — all options run locally
OLLAMA_BASE_URL=http://localhost:11434
OLLAMA_MODEL_LARGE=qwen2.5:14b # For reasoning-heavy nodes
OLLAMA_MODEL_SMALL=qwen2.5 # For lightweight tasks
# Storage paths
CHROMA_CARDS_PATH=./database/cards/chroma_db
SQLITE_PATH=./database/cards/sqlite_db/cards.db
EMBEDDING_CACHE=./model_cache
# API server
HOST=0.0.0.0
PORT=8000
# Anomaly detector
ANOMALY_WINDOW_SIZE=30
ANOMALY_Z_THRESHOLD=5.0This repository is intended to showcase several engineering patterns that I find under-discussed in typical "build an LLM app" tutorials:
- ✅ Loop-bounded agentic graphs — every cycle has a hard cap; the system always terminates
- ✅ Anti-hallucination via independent critic node — separate LLM instance reviews the Evaluator's verdict for hard contradictions
- ✅ Fuzzy title matching for LLM scoring — LLMs sometimes return slightly mutated card titles;
difflib.get_close_matchesrescues the match - ✅ Two-stage extraction with bounded concurrency —
asyncio.Semaphorekeeps the LLM provider from being hammered - ✅ Structured outputs everywhere — every node returns a validated Pydantic model, never raw strings
- ✅ Z-score anomaly detection with baseline rebuild — a real engineering subtlety: after a storm, you must purge the polluted history or the next reading will be wrongly flagged
To set honest expectations:
- ❌ Not the full production system. Site names, service types, and routing topology have been genericized.
- ❌ No real SNMP polling. The tool functions return deterministic mock values. Replacing them with real
pysnmpcalls is a one-line change per tool. - ❌ No production secrets, IPs, or device identifiers. Anything that could identify the original deployment environment has been removed.
- ❌ Frontend is provided as-is. The Flutter desktop UI lives in
vsat_manager/; the screenshot above shows it running against the backend.
Things that would make this demo more useful that I plan to add over time:
- Replace mock SNMP with real
pysnmppolling against a Docker-simulated device - LangSmith integration for trace inspection
- Expanded pytest suite covering all 8 node behaviors
- GitHub Actions CI for linting + tests
- Docker Compose stack (Ollama + API + ChromaDB persistent volume)
- Evaluation harness with a small synthetic benchmark
I am Mohamed Wady, an AI Engineer with 14+ years bridging satellite networks and modern software engineering. I architect production-grade offline LLM systems for regulated environments where the cloud is not an option.
- 🌐 Portfolio: mohamedwady.dev
- 💼 LinkedIn: linkedin.com/in/mohamed-wady-2b783a64
- 📧 Email: mmwady@gmail.com
- 📍 Cairo, Egypt — open to remote roles globally
If you are building agentic systems for regulated industries, air-gapped networks, or edge AI deployment, I would love to hear from you.
This project is released under the MIT License. The original production system from which this demo was derived remains proprietary and is not part of this distribution.
- The LangGraph team for building a primitive that finally made loop-bounded agentic systems sane to write
- The Qwen team at Alibaba for releasing Qwen2.5 — the first local model in this size class that I trust on structured-output tasks
- The Ollama project for making local LLM deployment a five-minute affair