Agentic AI Architecture¶
Conversational workflow¶
handle_chat_turn coordinates a bounded form-filling conversation. Pass one classifies category/subcategory; pass two extracts fields; pass three either asks for missing fields or finalizes a summary and grievance. Session state is stored through the Redis-backed chat session layer.
flowchart TD
classDef default fill:#1e293b,stroke:#38bdf8,stroke-width:2px,color:#f8fafc
classDef highlight fill:#065f46,stroke:#34d399,stroke-width:2px,color:#f0fdf4
Message["Citizen message"]:::highlight --> Session["Load Redis chat session"]
Session --> Classify["Classify category"]
Classify --> Extract["Extract mandatory fields"]
Extract --> Missing{"Fields missing?"}
Missing -->|Yes| Followup["Stream follow-up over SSE"]
Followup --> Message
Missing -->|No| Finalize["Finalize and create grievance"]
Finalize --> Assign["Assign officer and SLA"]
RAG["RAG query"] --> Filter["Role allowlist"]
Filter --> Vector["pgvector similarity search"]
Vector --> Ground["LLM answer with sources"]
linkStyle default stroke:#64748b,stroke-width:2px
Model boundaries¶
LiteLLM supplies an OpenAI-compatible chat model for classification and generation. Gemini provides embeddings and may support GCP-native model paths. The backend keeps prompts and response parsing in the chat service rather than exposing model calls to the browser.
Guardrails¶
Structured schemas, category validation, session ownership checks, mandatory-field state, RAG context-only instructions, source extraction, and bounded role filters reduce accidental leakage and malformed case creation. Unknown: exact model response validation coverage across all provider configurations.
Chat session state¶
The ChatSession object contains a session ID, user ID, message history, attachments, detected category/subcategory, confirmation state, collected fields, missing fields, and a lifecycle status. The router always resolves it with the authenticated user, which prevents a user from reading another user’s session by guessing an ID.
The state machine is deliberately form-oriented:
stateDiagram-v2
[*] --> collecting: /chat/start
collecting --> collecting: category, extraction, follow-up
collecting --> complete: all mandatory fields
collecting --> abandoned: DELETE /chat/{session_id}
complete --> [*]: grievance persisted
abandoned --> [*]
RAG generation contract¶
RagService.query_rag first maps roles to allowed document roles:
- user →
user,both - officer →
user,officer,both - admin/super_admin →
user,officer,admin,both
Only retrieved chunks are placed in the context prompt. The prompt instructs the model to answer from context, say when context is insufficient, and end with a source list. The service parses that source line and intersects it with actual retrieved source names before returning sources to the client.
Failure and retry behavior¶
The chat router converts stream exceptions into a generic SSE error event rather than exposing the raw exception. Upload parsing distinguishes bad input from server failure. RAG failures become HTTP 500 responses. A production hardening path should add provider timeouts, bounded retries, idempotency for finalization, and explicit cancellation handling when the browser disconnects from SSE.