Drop one-shot tmp/axe scripts and agent i18n scratch so the Gitea tree is deployable.
3.6 KiB
04 — AI fallback after FAQ miss
Agent: 4/10 (support-auto)
Status: Implemented
Coordinates with: 02-contract.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
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
- Enable
support_auto.enabled+ai_enabled, setai_delivery=draftorauto_send. - Configure AI role support in admin settings.
- Create a ticket that does not match FAQ → job row → draft/send or handoff.
- Confirm ticket stays unassigned/claimable on AI failure.