003-ticketing's ticket-state-machine.ts already defines AI_ANALYZING/ AI_TROUBLESHOOTING/AI_VERIFYING/AI_RESOLVED/HUMAN_ESCALATION ticket statuses, clearly authored anticipating this feature. Corrects the plan before implementation: AISupportSession.status now drives Ticket.status through the existing ticketsService.updateStatus (reusing its optimistic concurrency), instead of an isolated status field the rest of the system never sees. Also clarifies that knowledge retrieval is an in-process service call through knowledge's index.ts, not an HTTP loopback. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
168 lines
11 KiB
Markdown
168 lines
11 KiB
Markdown
# Implementation Plan: AI Support Agent
|
|
|
|
**Branch**: `005-ai-support` | **Date**: 2026-09-02 | **Spec**: [spec.md](./spec.md)
|
|
|
|
**Input**: Feature specification from `specs/005-ai-support/spec.md`
|
|
|
|
## Summary
|
|
|
|
Populate the remaining `ai-support` submodules (`sessions`, `tools`, `troubleshooting`,
|
|
`escalation` — `knowledge` already exists from 004) with the AI reasoning loop: a session starts
|
|
per ticket, produces a structured, knowledge-grounded diagnosis via a real Anthropic Claude call,
|
|
applies a configurable confidence-band policy to decide proceed/ask/escalate, executes
|
|
permission-and-risk-gated tool proposals through a deterministic policy layer (never the model's
|
|
own judgment), walks an application-controlled runbook step sequence when one matches, and only
|
|
marks a ticket AI-resolved on real tool-verified evidence. Per explicit product decision, this
|
|
feature integrates a real LLM provider from the start — no mock/pluggable-interface phase.
|
|
|
|
## Technical Context
|
|
|
|
**Language/Version**: TypeScript 5.4 / Node.js 20+.
|
|
|
|
**Primary Dependencies**: `@anthropic-ai/sdk` (new — real LLM calls, research.md), `zod` (tool
|
|
input schemas, structured-output schema for diagnosis, admin config validation), Prisma (new
|
|
models), BullMQ (new `AI_SESSION` queue/worker — reuses existing `queueManager`, no new
|
|
dependency).
|
|
|
|
**Storage**: PostgreSQL via Prisma (new `AISupportSession`, `AIDiagnosis`, `AIInteraction`,
|
|
`AIAction`, `AIActionResult`, `AIKnowledgeReference`, `AIConfidencePolicy` models per
|
|
`docs/06-database-schema.md`, refined in data-model.md). Redis/BullMQ for the async first-turn
|
|
job, reusing existing infrastructure.
|
|
|
|
**Testing**: Vitest — unit tests for the confidence-band decision function, the deterministic
|
|
tool-policy gate, and the runbook step-advancement logic (all pure, extractable functions, unlike
|
|
004's thin-Prisma-query situation); integration tests against a real Postgres **and a real
|
|
Anthropic API call** for the full session flow (quickstart.md scenarios) — this is the first
|
|
feature in this codebase whose integration tests have a real external-network dependency and a
|
|
real per-run cost, not just Docker-local infra. Per the constitution's Testing gate, this feature
|
|
also adds the two required standing E2E scenarios: (A) AI resolves directly, (B) AI escalates to
|
|
human — both were previously unimplementable (no AI session existed) and are added now.
|
|
|
|
**Target Platform**: Same Fastify modular monolith. New submodules:
|
|
`src/modules/ai-support/{sessions,tools,troubleshooting,escalation}/` (standard module shape,
|
|
research.md "Module placement"). New infra: `src/infrastructure/ai/` (Anthropic client
|
|
singleton). New job: `src/jobs/ai-session/` (registered in `src/bootstrap/queue.bootstrap.ts`).
|
|
Modifies `src/modules/ticketing/tickets/service/tickets.service.ts` — two hooks: enqueue on
|
|
ticket creation (research.md "Session triggering"), and end any active `AISupportSession` inside
|
|
`updateStatus` when a human actor moves the ticket (research.md "AISupportSession.status drives
|
|
Ticket.status", FR-023). Also modifies `prisma/schema.prisma`. **Discovered during this planning
|
|
pass**: 003-ticketing's `ticket-state-machine.ts` already defines the exact `AI_ANALYZING →
|
|
AI_TROUBLESHOOTING → AI_VERIFYING → AI_RESOLVED` / `HUMAN_ESCALATION` states this feature drives
|
|
— this feature reuses that state machine and `ticketsService.updateStatus` directly rather than
|
|
introducing a parallel one.
|
|
|
|
**Project Type**: Backend service — single project.
|
|
|
|
**Performance Goals**: Not throughput-sensitive at this phase (one ticket, one session, turns
|
|
paced by human/customer reply cadence) — but every reasoning call is real LLM latency (seconds),
|
|
which is exactly why the first turn is queued (research.md) rather than synchronous with ticket
|
|
creation.
|
|
|
|
**Constraints**: MUST NOT let AI free-text influence tool permission/risk/escalation decisions
|
|
(FR-024, doc 11 §A4); MUST NOT mark a ticket AI-resolved without tool-verified evidence (FR-018);
|
|
MUST NOT let the model choose or reorder runbook steps (FR-015); MUST cap clarifying questions
|
|
(FR-009) and, per doc 11 §B2, cap reasoning turns/tool-call iterations per session to prevent a
|
|
runaway loop.
|
|
|
|
**Scale/Scope**: One reasoning agent (not a multi-agent registry), four new submodules, a small
|
|
fixed tool registry (4 real tools + 1 intentionally-pending-approval high-risk tool). Explicitly
|
|
excludes: semantic/vector retrieval, product-signal webhook verification, model routing/fallback,
|
|
cost dashboards, localization, idle-session timeout (see spec.md Assumptions).
|
|
|
|
## Constitution Check
|
|
|
|
*GATE: Must pass before Phase 0 research. Re-check after Phase 1 design.*
|
|
|
|
| Principle / Section | Check | Result |
|
|
|---|---|---|
|
|
| I. SaaS Is the Sole Identity & Access Authority | Sessions/diagnoses/actions reference `Ticket`/`Product` (SupportHub's own domain) only; no SaaS identity data is duplicated. | PASS |
|
|
| II. Configuration Over Hardcoding | Confidence thresholds are DB-configurable per product/category (`AIConfidencePolicy`, FR-005); the model name and reasoning effort are env-configurable (research.md), not inline string literals. | PASS |
|
|
| III. Layered Architecture With Enforced Module Boundaries | Four new submodules follow the standard shape; `troubleshooting` reaches `knowledge`'s `Runbook` data only through `knowledge`'s public `index.ts`, never a deep import. | PASS |
|
|
| IV. AI Recommends, Deterministic Policy Decides | This is the central principle this feature exists to implement: `evaluateToolProposal` (research.md) is the one shared, model-output-blind gate every tool call passes through; confidence-band policy is applied to the diagnosis's numeric score by application code, never by asking the model what it thinks should happen next. | PASS |
|
|
| V. Evidence-Based Verification | `resolved` status is guarded on a structured `AIActionResult` from `verifyProductResolution`, never on interaction/message content (research.md "Verification and resolution"). | PASS |
|
|
| VI. Durable Audit & History | Every `AIDiagnosis` is append-only (never overwritten); every `AIAction` records its evaluation outcome even when nothing executes; `AIKnowledgeReference` records exactly what knowledge the AI was shown. | PASS |
|
|
| VII. Concurrency-Safe, Durable Job Handling | The first-turn job runs through the existing durable BullMQ `queueManager` (survives a process restart — not an in-memory timer); "one active session per ticket" (FR-001) is enforced as a repository-level check analogous to 003's optimistic-concurrency pattern. | PASS |
|
|
| VIII. Problem and Ticket Are Separate, Related Entities | `AISupportSession` attaches to `Ticket` (per doc 06), and reads `Problem` for diagnosis context — doesn't collapse the two. | PASS |
|
|
| Technology & Platform Constraints | Adds exactly one new dependency, `@anthropic-ai/sdk` — the one genuinely new capability this phase requires; no other new runtime dependency. | PASS |
|
|
| Testing gate — AI tool-permission tests | Directly required by the constitution's Testing section, not just this feature's own FRs — see quickstart Scenario 3 and tasks.md. | Addressed in Phase 3 (US3) tests |
|
|
| Testing gate — two standing E2E scenarios (AI-resolves, AI-escalates) | Both were impossible before this feature (no AI session existed anywhere in the codebase) — added here as the constitution requires. | Addressed in Phase 6 (Polish) |
|
|
|
|
No violations requiring Complexity Tracking justification. The one deliberately-incomplete piece
|
|
(`overrideTicketPriority` staying `pending_approval` forever, with no approval UI yet) is an
|
|
explicitly documented known limitation, not a silent gap — same class as `fastify.authenticate`.
|
|
|
|
## Post-Design Constitution Re-check
|
|
|
|
All gates above remain PASS after Phase 1 design (research.md, data-model.md, contracts/,
|
|
quickstart.md). Worth calling out explicitly against Principle IV: the two-call design
|
|
(classify+diagnose, then separately reason/act — research.md) means the confidence-band policy
|
|
sits in application code *between* two model calls, not inside a prompt instruction hoping the
|
|
model applies its own policy correctly — this is what makes Principle IV a mechanical guarantee
|
|
here rather than a hope.
|
|
|
|
## Project Structure
|
|
|
|
### Documentation (this feature)
|
|
|
|
```text
|
|
specs/005-ai-support/
|
|
├── plan.md # This file
|
|
├── research.md # Phase 0 output
|
|
├── data-model.md # Phase 1 output
|
|
├── quickstart.md # Phase 1 output
|
|
├── contracts/ # Phase 1 output
|
|
└── tasks.md # Phase 2 output (/speckit-tasks — not created here)
|
|
```
|
|
|
|
### Source Code (repository root)
|
|
|
|
```text
|
|
supporthub-api/
|
|
├── prisma/
|
|
│ └── schema.prisma # MODIFIED — add AISupportSession, AIDiagnosis,
|
|
│ AIInteraction, AIAction, AIActionResult,
|
|
│ AIKnowledgeReference, AIConfidencePolicy
|
|
├── src/
|
|
│ ├── infrastructure/
|
|
│ │ └── ai/ # NEW — Anthropic client singleton, model/effort
|
|
│ │ └── anthropic.client.ts config resolved from env (research.md)
|
|
│ ├── jobs/
|
|
│ │ └── ai-session/ # NEW — worker for the queued first-turn diagnosis
|
|
│ │ └── index.ts
|
|
│ ├── bootstrap/
|
|
│ │ └── queue.bootstrap.ts # MODIFIED — register the new AI-session worker
|
|
│ └── modules/
|
|
│ ├── ticketing/
|
|
│ │ └── tickets/
|
|
│ │ └── service/
|
|
│ │ └── tickets.service.ts # MODIFIED — enqueue AI_SESSION job on new ticket
|
|
│ └── ai-support/
|
|
│ ├── knowledge/ # existing (004) — untouched
|
|
│ ├── sessions/ # NEW
|
|
│ │ ├── controller/ routes/ schema/ repository/ service/ types/ mapper/
|
|
│ │ │ constants/ index.ts
|
|
│ ├── tools/ # NEW
|
|
│ │ ├── controller/ routes/ schema/ repository/ service/ types/ mapper/
|
|
│ │ │ constants/ index.ts # constants/tool-registry.ts — the fixed tool set
|
|
│ ├── troubleshooting/ # NEW
|
|
│ │ └── service/ types/ index.ts # no own routes — invoked by sessions' service
|
|
│ └── escalation/ # NEW
|
|
│ └── service/ types/ index.ts # no own routes — invoked by sessions' service
|
|
└── tests/
|
|
├── unit/ai-support/ # confidence-band decision, tool policy gate,
|
|
│ runbook step-advancement (pure functions)
|
|
└── integration/ # full session flow against real Postgres + real
|
|
Anthropic API (quickstart.md scenarios)
|
|
```
|
|
|
|
**Structure Decision**: Single project. `troubleshooting` and `escalation` are internal-only
|
|
submodules (service logic `sessions` calls through their `index.ts`, per Principle III) rather
|
|
than exposing their own routes — neither has an independent HTTP surface in spec.md's
|
|
requirements; both are invoked as part of a session turn. This mirrors how `messages`/
|
|
`attachments` in 003-ticketing are separate modules from `tickets` but still ultimately driven
|
|
through the same request.
|
|
|
|
## Complexity Tracking
|
|
|
|
*No constitution violations — table intentionally omitted.*
|