Initial commit of Descrybe v2 without local scratch artifacts.
Drop one-shot tmp/axe scripts and agent i18n scratch so the Gitea tree is deployable.
This commit is contained in:
@@ -0,0 +1,99 @@
|
||||
# 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.
|
||||
Reference in New Issue
Block a user