Files

100 lines
3.6 KiB
Markdown
Raw Permalink Normal View History

# 04 — AI fallback after FAQ miss
**Agent:** 4/10 (`support-auto`)
**Status:** Implemented
**Coordinates with:** [02-contract.md](./02-contract.md), [01-inventory.md](./01-inventory.md), agent 3 `MatchAutoReply`
---
## Goal
When Stage A FAQ/template match fails or confidence is below threshold, call the **admin-configured platform AI** (`ai_roles.support`) to draft or auto-send a labeled reply. On AI failure → leave the ticket **claimable** for humans.
No parallel BYOK secret store — reuse `platformsettings.AIRoleSupport` / `aiprovider.ResolveCompleterForRole(..., RoleSupport)`.
---
## Flow
```
MaybeAutoReplyOnCreate (after ticket Create)
├─ [sync] MatchAutoReply ≥ threshold → PostMatchedAutoReply (KB/template)
└─ miss / low conf / FAQ off
└─ EnqueueAIFallback → support_auto_jobs (async)
└─ worker ProcessPendingAutoJobs
└─ TryAutoReplyLLM → CompleterSupportAI.RunAutoReply
├─ draft (default) → internal system note (AI-assisted label)
├─ auto_send → public system message + footer
└─ fail / low conf / handoff → auto_reply_disabled + handed_off
```
HTTP create **never awaits** the LLM. If migration `034` is missing, enqueue falls back to sync-with-timeout (`AutoReplyTimeout` = 25s).
---
## Settings (`support_auto_config`)
| Key | Default | Role |
|-----|---------|------|
| `enabled` | false | Master switch |
| `ai_enabled` | false | Stage B |
| `ai_delivery` | `draft` | `draft` \| `auto_send` |
| `ai_confidence_threshold` | 0.65 | Min model confidence |
| `ai_use_global_support_role` | true | Use admin `ai_roles.support` |
| `ai_model_override` / `ai_base_url_override` | empty | Same API key; optional endpoint tweak |
Also requires `/admin/settings` → AI roles → **support** enabled with key + model.
---
## Symbols
| Symbol | Path |
|--------|------|
| `TryAutoReplyLLM` | `apps/api/internal/support/ai_auto_reply.go` |
| `CompleterSupportAI` | `apps/api/internal/support/ai_fallback.go` |
| `EnqueueAIFallback` / `ProcessPendingAutoJobs` | `apps/api/internal/support/auto_jobs.go` |
| `MaybeAutoReplyOnCreate` (miss → enqueue) | `apps/api/internal/support/match_auto_reply.go` |
| `BuildAutoReplyMessages` | `apps/api/internal/support/auto_prompt.go` |
| Migration | `apps/api/sql/schema/034_support_auto_jobs.sql` |
Wiring: `httpapi.NewServer` and `cmd/worker` set `Support.SupportAI = NewCompleterSupportAI(aiSvc)`.
---
## Security
- Tenant isolation: ticket load checks `company_id`; prompts never mix tenants.
- Ticket text wrapped as untrusted; secrets redacted before prompt/logs.
- Rate limits: 10 AI jobs/company/hour, 30/platform/minute → handoff.
- Logs: `ticket_id`, `company_id`, `delivery`, `confidence` — no bodies/keys/emails.
- Docs Ask stays rule-based (never `AIRoleSupport`).
---
## Labels
- Public AI reply footer: “AI-assisted reply…”
- Draft prefix: `[AI draft — not visible to customer]`
- Handoff internal note: “Needs human review…”
---
## Tests
```powershell
cd apps/api
go test ./internal/support/ -count=1 -run "TestTryAutoReplyLLM|TestCompleterSupportAI|TestParseAIAssist|TestAIRate|TestBuildAutoReply|TestLabelAI"
```
Mock Completer covers draft JSON parse, low-confidence handoff, and redaction — no live OpenAI calls.
---
## Manual check
1. Enable `support_auto.enabled` + `ai_enabled`, set `ai_delivery=draft` or `auto_send`.
2. Configure AI role **support** in admin settings.
3. Create a ticket that does **not** match FAQ → job row → draft/send or handoff.
4. Confirm ticket stays unassigned/claimable on AI failure.