# Recherche — Open WebUI comme couche IA/agent, Zammad en « module »

> **Statut : recherche terminée (2026-07-05), décision non prise.** Candidat naturel pour
> l'**ADR 0003** (la « porte de sortie » prévue par l'ADR 0002). Recherche menée sur les
> docs officielles Open WebUI (v0.10.2) + docs/code source Zammad (7.1.x) + écosystème
> GitHub/communauté — sources citées inline.

## La question

Peut-on mettre **Open WebUI** (self-hosted, instance déjà existante) au-dessus de Zammad
et utiliser Zammad comme un **module** piloté par API — les agents humains discutent avec
un LLM (Claude) qui lit les tickets, les classe, et prépare des brouillons — sans jamais
violer la contrainte **« aucun envoi automatique, révision humaine obligatoire »** ?

## Verdict court : OUI, réalisable, avec les API documentées des deux côtés

Les trois briques critiques existent et sont vérifiées :

1. **Zammad expose une API « shared draft »** : `PUT /api/v1/tickets/{id}/shared_draft`
   (documentée : docs.zammad.org/en/latest/api/ticket/shared_drafts.html, stable depuis
   Zammad 5.x). Un système externe pousse un brouillon → l'agent voit un bouton
   **« Draft available »** dans le ticket → un clic charge le texte **dans son composer**
   → il édite et envoie depuis Zammad. **Un shared draft n'envoie rien** ; seul un
   article `type: email` déclenche un envoi SMTP (vérifié dans le code :
   `enqueue_communicate_email_job.rb`). C'est LE chaînon qui rend le pattern sûr.
2. **Open WebUI sait consommer des outils externes** : support **MCP natif (Streamable
   HTTP) depuis v0.6.31**, et surtout **serveurs d'outils OpenAPI** (v0.6.0+) — la voie
   **officiellement recommandée** (« For most deployments, OpenAPI remains the preferred
   integration path »). Un petit serveur FastAPI exposant les opérations Zammad devient
   un jeu d'outils appelables par le modèle.
3. **Claude fonctionne nativement dans Open WebUI** : connexion Anthropic auto-détectée
   (Admin → Connections, URL `https://api.anthropic.com/v1`) — « chat completions,
   streaming, and tool calling all work correctly out of the box ». Claude Sonnet 4.5+
   est cité par la doc Open WebUI comme modèle de référence pour le mode agentique
   (Native Mode, défaut depuis v0.10.0).

## Architecture cible

```mermaid
flowchart LR
  subgraph OWUI["Open WebUI (instance existante)"]
    M["Modèle curé « Assistant ADVMFR »\n(base Claude, RBAC groupe 4 agents)"]
    KB["Knowledge Base\n(politiques MOQ, pricing, devis passés)"]
  end
  TS["zammad-tools\n(serveur OpenAPI/MCP conteneurisé)"]
  subgraph Z["Zammad 7.1 (module)"]
    T["Tickets / articles / tags"]
    SD["Shared draft du ticket"]
    UI["UI agent : « Draft available »\n→ composer → révision → envoi"]
  end
  M -- tool calls --> TS
  M -. RAG .-> KB
  TS -- "GET tickets/search, articles\nPOST tags, note interne\nPUT shared_draft" --> T
  TS --> SD
  SD --> UI
```

- **Le garde-fou « no auto-send » est structurel, pas comportemental** : le serveur
  `zammad-tools` n'expose **aucun** endpoint créant un article `type: email`. Écritures
  autorisées : tags, priorité/groupe/owner, **note interne** (`type: note,
  internal: true` hard-codé), **shared draft**. Même un LLM manipulé par prompt
  injection ne peut physiquement rien envoyer.
- **Auth Zammad** : compte de service avec **token scopé** `ticket.agent`
  (`POST /api/v1/user_access_token`, permissions restreintes + expiration), accès
  groupes réglé en READ/CHANGE. Attribution par agent réel : le header `From`
  (ex-`X-On-Behalf-Of`, déprécié en 7.x) exige `admin.user` sur le compte de service —
  **déconseillé** (privilège énorme pour de la cosmétique). Alternative propre si on y
  tient : le pattern OAuth « user-context » de bauer-group (Zammad comme IdP OAuth2,
  chaque appel porte l'identité réelle de l'agent).
- **Sens inverse (Zammad → couche IA)** : triggers → **webhooks** (custom payloads en
  7.x, retry ×4, signature HMAC-SHA1) si on veut du proactif. Pas nécessaire en V1 :
  le chat est pull-based (« résume-moi les tickets Triage », « prépare un brouillon
  pour #86055 »).
- **Bouton « Action » Open WebUI** (function admin-only) : cas d'usage documenté
  exactement pour nous — un bouton sous la réponse du modèle « Pousser ce brouillon
  dans le ticket #X », avec confirmation utilisateur (`__event_call__`) avant l'appel
  REST. Alternative/complément au tool calling.

## Ce que ça apporterait vs l'existant (ADR 0002)

L'ADR 0002 (Writing Assistant natif) **couvre déjà** la reformulation et le 1er jet
*dans* le composer, ticket par ticket. La couche Open WebUI apporte ce que le natif ne
fait pas :

| Capacité | Natif Zammad (ADR 0002) | Open WebUI + zammad-tools |
|---|---|---|
| Reformuler/rédiger dans le composer | ✅ | — (complémentaire) |
| Conversation multi-tours sur un ticket | ❌ | ✅ |
| Vue transverse (« quoi d'urgent dans Triage ? ») | ❌ | ✅ (search + synthèse) |
| RAG sur devis passés / politiques MOQ | ❌ | ✅ (KB Open WebUI) |
| Brouillon complet poussé dans le ticket | ❌ (l'agent part de zéro) | ✅ (shared draft) |
| Actions ticket en langage naturel (tags, prio, reroute) | partiel (AI agents 7.1, par trigger) | ✅ |
| Coût/complexité ops | ~0 | +1 service, +1 surface sécu |

**Recouvrement à arbitrer** : Zammad 7.1 a ses propres **AI Agents natifs** (tagging,
extraction — cf. `docs/research/ai-agents.md`, différés) et un endpoint
`POST /tickets/{id}/summarize`. La couche externe ne doit pas dupliquer ce que le natif
finira par faire mieux ; sa vraie valeur = **conversation, transverse, RAG, brouillons**.

## État de l'art : personne ne l'a fait, mais toutes les pièces existent

- **Aucun prior art « Open WebUI pilote Zammad »** (recherche communauté Open WebUI,
  forum Zammad, GitHub — constat vérifié). On serait les premiers à l'assembler.
- **Serveurs MCP Zammad existants** (réutilisables/forkables) :
  - `basher83/Zammad-MCP` (Python, AGPL, ~20 outils, tests ~90 %, très actif) — la base
    la plus mûre, mais **pas de shared drafts** et son `add_article` peut envoyer.
  - `BM1-de/zammad-mcp` (TypeScript, 3 outils **draft-only** : shared draft avec
    reply-all/signature/quote calculés, note interne hard-codée, lecture de fil) —
    minuscule (0 star, pas de licence) mais **c'est exactement notre pattern** ; à lire
    comme documentation des pièges du endpoint (form_id, signature, placeholders).
  - `bauer-group/IP-ZAMMAD-MCPServer` (Python FastMCP, MIT, 36 outils, actif) — la
    référence **auth** : mode OAuth user-context (identité réelle de l'agent), rôles,
    rate limiting.
  - `Softoft-Orga/zammad-mcp-server` (Python, MIT, testé contre Zammad 7.0/7.1) —
    référence **deny-list** (`MCP_DENIED_TOOLS`, permissions par catégorie).
- Position officielle Zammad (forum, févr. 2026) : pas de MCP officiel prévu,
  l'écosystème reste communautaire.

## Risques & impacts (à traiter si on y va)

1. **Sécurité Open WebUI** :
   - **≥ 0.6.35 obligatoire** (CVE-2025-64496 : compte avec permission
     `workspace.tools` → RCE via `exec()` non sandboxé ; version courante 0.10.2 OK).
   - Les 4 agents ADVMFR : **jamais** la permission Tools (« root-equivalent » selon la
     doc) — ils *utilisent* les outils attachés au modèle, seul l'admin en crée.
   - Préférer le **serveur d'outils externe conteneurisé** aux Workspace Tools Python
     in-process (confinement du rayon d'explosion).
2. **Prompt injection via le contenu des emails** : les tickets contiennent du texte
   client hostile par définition. Mitigations : garde-fou structurel draft-only (fait),
   sanitization HTML avant passage au LLM, system prompt traitant le contenu ticket
   comme données, pas d'outil de lecture hors périmètre.
3. **Bug connu Claude + Open WebUI** : extended thinking + native function calling via
   LiteLLM → erreur 400 Anthropic (issue open-webui #20464). Parade : **désactiver le
   thinking** quand les tools sont actifs, ou pipe manifold `Podden/…anthropic…`
   (45 stars, prompt caching natif) au lieu de l'endpoint compat.
4. **Gotchas Open WebUI en Native Mode (défaut v0.10)** : la Knowledge Base n'est
   **plus auto-injectée** — le modèle doit appeler les tools KB (system prompt à
   soigner) ; ACL silencieuses (tool/KB non partagés au user = ignorés sans erreur) ;
   les serveurs OpenAPI n'ont pas d'événements interactifs (confirmation UI ⇒ Action ou
   Workspace Tool).
5. **Limites API Zammad** : un seul shared draft par ticket (create-or-update — l'IA
   écrase le brouillon existant) ; pièces jointes du draft via
   `POST /api/v1/upload_caches/{form_id}` d'abord (les PJ inline sont ignorées en
   silence — piège communautaire connu) ; recherche plafonnée à 10 000 résultats ; pas
   de rate limit API (c'est à notre couche d'être raisonnable).
6. **Ops** : +1 service à maintenir (zammad-tools), rythme de release Open WebUI très
   rapide (épingler la version, lire les release notes — v0.9 async et v0.10 Native Mode
   ont été des breaking changes plugins).

## Points à valider empiriquement sur notre 7.1.1 avant tout ADR

- [ ] `PUT /tickets/{id}/shared_draft` avec un token `ticket.agent` scopé → le bouton
      « Draft available » apparaît-il bien pour les agents du groupe ? (le flag groupe
      « Shared Drafts » est actif par défaut ; comportement API si désactivé non vérifié)
- [ ] Cycle complet : draft poussé → agent l'applique → édite → envoie → draft
      auto-supprimé.
- [ ] Plafond réel de `per_page` sur `/tickets/search`.
- [ ] Endpoints IA non documentés (`related_knowledge_base_answers`, `ai_agents`,
      `ai_text_tools`) : présents/stables sur 7.1.1 ?
- [ ] Connexion Anthropic native dans l'instance Open WebUI existante + tool calling sur
      un tool de test.

## Chemin recommandé (si on décide d'y aller — décision utilisateur/Roma)

1. **Spike 1 (½ journée)** : valider le shared draft à la main (curl) sur un ticket de
   test + le rendu « Draft available » côté UI. C'est le point de bascule : si l'UX de
   révision convainc, tout le reste suit.
2. **Spike 2** : mini serveur FastAPI 5 endpoints (search, get_thread, add_tag,
   add_internal_note, put_shared_draft), enregistré comme Global Tool Server dans Open
   WebUI, modèle curé Claude + system prompt, test sur les tickets DEMO-R1.
3. Ensuite seulement : ADR 0003 (périmètre, auth, hébergement du service), KB RAG,
   webhooks proactifs.

**Base de code** : forker `basher83/Zammad-MCP` (mûr, AGPL) en y ajoutant le tool
shared-draft façon BM1-de, **ou** écrire notre petit serveur OpenAPI FastAPI from
scratch (surface volontairement minuscule = plus simple à auditer). Vu la contrainte
draft-only, le from-scratch minimal est probablement le bon choix.
