Files
support_backend/specs/005-ai-support/contracts/ai-support-contract.md
T
saqib mirandClaude Sonnet 5 72dddcdf74 docs: plan and design artifacts for AI support agent feature
Two-call reasoning design (structured-output diagnosis, then a separate
knowledge-grounded reasoning/tool call), confidence-band policy as a DB-
configurable gate applied by app code, a deterministic tool-policy gate
that never reads AI free text, an app-owned runbook step index, and a
fail-closed placeholder verification tool mirroring the existing
malware-scanner precedent. Real Anthropic Claude integration per explicit
product decision.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-02 16:50:16 +05:30

4.5 KiB

Contract: AI Support Sessions, Tools, and Confidence Policy

All admin routes gated by fastify.authenticate (research.md — known limitation inherited from 002/003/004). Session routes are not admin routes — they're called by the ticket-owning caller (customer-facing surface, matching 003-ticketing's POST/GET .../messages pattern) and carry no additional gate of their own in this feature.

Session lifecycle (internal trigger, not a public route)

A session is not started via an explicit "start" endpoint — TicketsService. createFromInboundRequest (003) enqueues a QueueName.AI_SESSION job on new-ticket creation (research.md "Session triggering"); the worker runs the first diagnosis turn and, on the "proceed"/"ask" branches, writes the AI's first message onto the ticket the same way any subsequent turn does.

Session turns

  • POST /tickets/:ticketId/ai-session/messages — body { message: string }. Records the customer's reply as an AIInteraction (role: customer) and a TicketMessage (type: CUSTOMER_MESSAGE, same as any other customer message), runs the next reasoning turn synchronously, and returns the AI's resulting turn. 404 if no active session exists for this ticket (FR-001 — a session that already ended doesn't silently restart).
  • GET /tickets/:ticketId/ai-session — returns the current (or most recent) session's status, latest diagnosis, and interaction history — the read path a future agent/customer UI (Phase 8/10) would call; not itself a reasoning trigger.

Response shape (both the async first-turn write and the sync .../messages response)

{
  "sessionId": "...",
  "status": "analyzing | troubleshooting | verifying | resolved | escalated | ended_by_agent",
  "diagnosis": { "product": "...", "feature": "...", "problemType": "...", "severity": "...", "confidence": 0.0, "possibleCauses": ["..."] },
  "message": "the AI's customer-facing text for this turn, if any",
  "escalation": { "summary": "...", "stepsAttempted": ["..."], "confidence": 0.0 }
}

escalation is present only when status becomes escalated this turn (FR-021).

Tool actions (read-only audit surface)

  • GET /tickets/:ticketId/ai-session/actions — lists every AIAction (+ its AIActionResult if one exists) for the ticket's session(s), in order — the durable, auditable record FR-013 requires, independently inspectable from the conversation transcript.

Confidence policy admin config

  • PUT /admin/products/:externalProductId/ai-policy — body { categoryId?, highThreshold, lowThreshold, maxClarifyingQuestions }. Upserts the (productId, categoryId) row (research.md "most-specific-match fallback"). 400 if highThreshold <= lowThreshold.
  • GET /admin/products/:externalProductId/ai-policy — returns every configured row for this product (including the categoryId: null product-wide row, if set) plus the system-wide defaults that would apply to an unconfigured category.

Guarantees (callable contract)

  1. A ticket never has two active AI sessions at oncePOST .../messages against a ticket whose session already ended returns 404, never silently opening a new one (FR-001).
  2. A diagnosis below the configured low threshold escalates on that same turn — never a proceed/ask outcome for a confidence value the policy says should escalate (FR-004, SC-002).
  3. A high-risk tool proposal is never auto-executedGET .../actions for a session that proposed overrideTicketPriority always shows evaluationOutcome: pending_approval with no AIActionResult, regardless of the diagnosis's confidence or the AI's own stated justification (FR-012, SC-003).
  4. status only ever becomes resolved alongside a passing verifyProductResolution result on the same session — never from customer-reply content alone (FR-018, SC-004).
  5. Changing AIConfidencePolicy via the admin endpoint applies to the very next diagnosis for that product/category — no caching, no propagation delay (FR-005, SC-005).
  6. An escalated turn's response always includes a non-empty escalation.summary and stepsAttempted — a human agent picking up the ticket never has to re-derive what happened from the raw transcript alone (FR-021, SC-006).
  7. When activeRunbookKey is set, the step index only ever advances by exactly one per completed step, forwardGET /tickets/:ticketId/ai-session never shows a currentStepIndex that skipped or moved backward relative to the runbook's authored steps order (FR-015, SC-007).