aria-service-desk-assistant
README.md
# π€ ARIA β Internal Knowledge & Ticketing Assistant
> **MCP-connected chatbot for IT operations teams β RAG answers from Confluence, writes new articles when nothing exists, and turns chat into clean Jira tickets, streamed live**
ARIA sits in front of six internal teams β **Service Desk, Command Center, Network, Linux, Database, and Windows Engineering** β as one chat interface. A LangChain probing layer reads each question first: if it's too vague to search on, ARIA asks one clarifying question instead of guessing. Once the issue is clear, a ChromaDB-backed retrieval loop finds the best matching Confluence article β rewording and re-searching if the first pass isn't confident β and streams a grounded answer token-by-token. If nothing matches, ARIA drafts and publishes a new article, so the knowledge base grows itself. A **"Log as Ticket"** action turns the raw back-and-forth of the chat into a well-formed Jira Service Desk ticket, not a dump of the conversation.






---
## β¨ Features
| Feature | Description |
|---|---|
| ποΈ **Six team spaces** | Service Desk Β· Command Center Β· Network Β· Linux Β· Database Β· Windows Engineering, each with its own Confluence space + Jira project |
| π§ **LangChain probing layer** | Reads the question before anything else runs; a genuinely vague report ("my thing is broken") gets one clarifying question instead of a guess |
| π **RAG retrieval (ChromaDB)** | Articles are chunked and embedded, not dumped whole into the prompt; retrieval is confidence-scored and re-tried with a reworded query when the first pass is weak |
| βοΈ **Self-healing KB** | No confident match β ARIA drafts an article, publishes it, and indexes it immediately β the next person with the same question finds it already there |
| π **Real-time streaming** | Answers stream token-by-token over SSE with a "Musingβ¦" / "Readingβ¦" / "Draftingβ¦" status and a blinking cursor, like a live typed reply |
| π‘οΈ **temperature=0.2 everywhere** | Every completion call β decision, synthesis, drafting, ticket writing β is grounded and low-temperature on purpose; prompts explicitly forbid inventing steps not in the source |
| π‘οΈ **Guardrails** | Hard-blocks high-sensitivity PHI/PII (SSN, card numbers, MRNs), sexual/nudity content, and security-risk requests (credential theft, bypassing auth) *before* anything reaches an LLM, Confluence, or Jira |
| π **Soft PII redaction** | Low-sensitivity contact info (email, phone, IP) is masked but still passed through β a ticket needs a reporter's email |
| π« **Log as Ticket** | Converts the chat's raw comments into a clean summary + description + priority and files it in Jira Service Desk |
| π **MCP server** | Every capability is also exposed as an MCP tool for Claude Desktop / Claude Code |
| π§ͺ **Offline-first** | Local JSON knowledge base + ticket store, fully demo-able with no Jira/Confluence/Okta tenant; flip one flag to go live |
---
## ποΈ Project Structure
```
aria-service-desk-assistant/
βββ main.py β FastAPI app: /api/chat, /api/chat/stream (SSE), /api/ticket, /api/teams
βββ mcp_server.py β Same capabilities exposed over MCP (stdio)
βββ config.py β Settings: mock mode, temperature, Jira/Confluence/Okta creds
βββ models.py β Team enum + Pydantic request/response models
βββ prompts.py β Prompt templates (decision, synthesis, drafting, ticket writer)
βββ probe_agent.py β LangChain layer: clarity check + search-intent reword loop
βββ vector_store.py β ChromaDB chunking/embedding/retrieval
βββ knowledge_agent.py β Orchestrates probe β retrieve β synthesize/draft, streamed
βββ ticket_writer.py β Chat transcript β clean ticket summary/description
βββ guardrails.py β Hard blocks: PHI/PII, sexual content, security risk
βββ phi_filter.py β Soft redaction: email/phone/IP, masked not blocked
βββ confluence_client.py β Search + publish articles (mock JSON β real Confluence REST)
βββ jira_client.py β Create tickets (mock JSON β real Jira REST)
βββ okta_client.py β Requester lookup, name/department only (mock β real Okta REST)
βββ kb_store.py β Local JSON-backed knowledge base (the mock/offline Confluence)
βββ data/
β βββ mock_kb.json β Seed articles per team (Troubleshooting/How-To/Runbook)
β βββ chroma/ (runtime) β Local vector index
β βββ mock_tickets.json (runtime) β Filed tickets in mock mode
βββ static/
βββ index.html β Chat UI: musing status, streaming text, blinking cursor
```
---
## π Chat Flow
```
ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β USER picks a team, asks a question β
ββββββββββββββββββββββββββββββββββ¬βββββββββββββββββββββββββββββββββββββββββββ
βΌ
POST /api/chat/stream β guardrails.screen() β blocked? refuse & stop
β
βΌ
probe_agent.assess() (LangChain: ChatOpenAI + prompt template)
β
clear enough to search?
βββββββββββββββββββ΄ββββββββββββββββββ
NO YES
β β
stream back ONE clarifying vector_store: chunk + embed (ChromaDB)
question, wait for the reply β
best_articles_context(team, intent)
β
LLM: found? confidence 0.0β1.0
low confidence β probe_agent.reword()
β re-embed search β retry (max 2 rounds)
ββββββββββ΄βββββββββ
confident match still nothing
β β
stream synthesis from stream a drafted
the one matched article article, publish +
(source: knowledge_base) index it immediately
β (source: drafted_article)
ββββββββββββ¬βββββββββββ
βΌ
ChatResponse events β SSE β
UI renders with blinking cursor
```
## π« Log as Ticket Flow
```
USER clicks "Log as Ticket" on the current conversation
β
βΌ
POST /api/ticket β guardrails.screen(transcript) β blocked? 400, no ticket filed
β
βΌ
phi_filter.redact_history() (soft-mask email/phone/IP)
β
βΌ
ticket_writer.build_ticket(team, history)
LLM (temperature=0.2) turns raw comments into:
{ summary, description, priority }
β
βΌ
okta_client.lookup_user(email) β requester name/department only
β
βΌ
jira_client.create_ticket(...) β files in the team's Jira project
β
βΌ
TicketResponse: { ticket_key, ticket_url, summary, description }
```
---
## π Quick Start
```bash
git clone https://github.com/emran-Automation-Techlead/aria-service-desk-assistant.git
cd aria-service-desk-assistant
pip install -r requirements.txt
cp .env.example .env # keep ARIA_MOCK_MODE=true to run fully offline
python main.py
```
Open **http://localhost:7862**
### Going live against real Jira / Confluence / Okta
Set `ARIA_MOCK_MODE=false` in `.env` and fill in:
```env
JIRA_BASE_URL=https://yourcompany.atlassian.net
JIRA_EMAIL=aria-bot@yourcompany.com
JIRA_API_TOKEN=...
CONFLUENCE_BASE_URL=https://yourcompany.atlassian.net
CONFLUENCE_EMAIL=aria-bot@yourcompany.com
CONFLUENCE_API_TOKEN=...
OKTA_DOMAIN=yourcompany.okta.com
OKTA_API_TOKEN=...
```
No code changes required β `confluence_client.py`, `jira_client.py`, and `okta_client.py` switch from the local JSON stores to live REST calls automatically. The vector index (`vector_store.py`) indexes whatever `confluence_client.search()` returns either way, so RAG retrieval works identically against mock or live content.
### Running the MCP server
```bash
python mcp_server.py
```
Point any MCP host (Claude Desktop, Claude Code) at this command to give it `search_knowledge_base`, `create_confluence_article`, `create_jira_ticket`, and `lookup_okta_user` as tools β each screened by the same guardrails as the chat UI.
---
## π‘οΈ Guardrails vs. soft redaction
Two distinct layers, on purpose:
- **`guardrails.py` β hard refusal.** Runs before anything reaches an LLM, Confluence, or Jira. Blocks outright: high-sensitivity PHI/PII (SSNs, card numbers, medical record numbers, via regex), sexual/nudity content (OpenAI Moderation API, scored against thresholds tuned tighter than the API's own `flagged` default β validated so ordinary IT phrasing like "kill the process" scores near zero), and security-risk requests (credential theft, bypassing MFA/auth, malware, exfiltration β a curated keyword heuristic plus the moderation model's illicit-content score, since phrasing like this isn't reliably caught by moderation alone).
- **`phi_filter.py` β soft redaction.** Low-sensitivity, operationally-necessary contact details (email, phone, IP) are masked with `[REDACTED-<TYPE>]` but the message still goes through β a ticket needs a reporter's email, a network issue needs an IP. API responses carry a `redactions` audit list (type + count).
---
## π οΈ Tech Stack
| Layer | Technology |
|---|---|
| **API** | FastAPI + Uvicorn, SSE streaming |
| **AI** | OpenAI (`gpt-4o-mini` by default, `temperature=0.2` everywhere) |
| **Orchestration** | LangChain (`langchain-openai` + `langchain-core`) for the intake-probing/reword loop |
| **RAG** | ChromaDB β chunked, embedded (`text-embedding-3-small`), confidence-scored retrieval |
| **Safety** | OpenAI Moderation API + custom PHI/PII and security-risk pattern checks |
| **Agent protocol** | MCP (`mcp` Python SDK, stdio transport) |
| **Integrations** | Jira Service Desk REST, Confluence Cloud REST, Okta REST β each with an offline JSON-backed mock mode |
| **Config** | Pydantic Settings v2 Β· python-dotenv |
| **UI** | Single-page vanilla HTML/JS chat widget β SSE consumer, musing status, blinking cursor |
---
## π License
MIT
This server cannot be deployed
Maintenance
ActivityMaintained
ResponsivenessNo issues