Mise en œuvre du téléchargement d'images et de la reconnaissance IA dans le chat : solution complète de la conception au déploiement
Mise en œuvre du téléchargement d’images et de la reconnaissance IA dans le chat : solution complète de la conception au déploiement
Dans les systèmes d’interaction IA, comment permettre aux utilisateurs de télécharger des images et de laisser l’IA les reconnaître directement ? En fait, j’ai longtemps hésité sur cette question, mais heureusement, j’ai découvert quelques astuces dans la pratique de HagiCode. Aujourd’hui, je vais vous parler de cette solution de téléchargement et de reconnaissance d’images, de la conception de protocoles personnalisés au stockage sur système de fichiers, en passant par la prévision front-end/back-end séparée. C’est aussi un journal technique complet.
Contexte
À cette époque où la conversation IA est en plein essor, les informations visuelles sont en fait un vecteur important pour que les utilisateurs expriment leurs intentions. Cependant, la plupart des systèmes de chat traditionnels ne prennent en charge que la saisie de texte brut, ce qui empêche les utilisateurs de transmettre directement le contexte visuel à l’IA pour analyse, ce qui est un peu regrettable.
HagiCode a rencontré des difficultés similaires au cours de son développement : les utilisateurs ne pouvaient pas télécharger d’images lors du chat ou de la création de commentaires principaux, l’IA ne pouvait pas accéder aux informations visuelles locales des utilisateurs, et il manquait une boucle fermée complète allant de la saisie, du stockage, du rendu à la transmission du contexte IA des images.
En fait, ces problèmes ne sont pas graves, ils ont juste besoin d’un peu de temps et de patience pour être résolus. Nous avons conçu et mis en œuvre un processus complet de téléchargement et de reconnaissance d’images, permettant à Claude et autres IA de reconnaître et d’analyser directement les captures d’écran téléchargées par les utilisateurs. Je vais expliquer en détail les détails de mise en œuvre de cette solution.
À propos de HagiCode
La solution partagée dans cet article provient de notre expérience pratique dans le projet HagiCode. HagiCode est un projet d’assistant de codage IA open source, utilisant une conception de workflow basée sur OpenSpec, s’efforçant de fournir une expérience de codage plus intelligente.
Analyse
Défis techniques
Avant de commencer la mise en œuvre, nous devons d’abord clarifier les principaux défis auxquels nous sommes confrontés, après tout, “aiguiser sa hache ne nuit pas à son travail de bûcheron”.
Collaboration inter-modules : Le téléchargement d’images implique plusieurs modules tels que l’interface utilisateur frontale, le service de téléchargement, l’API back-end, le stockage de fichiers, la persistance des messages et le mappage d’exécution IA. Chaque module a ses propres responsabilités et interfaces, nécessitant une solution globale coordonnée.
Choix de stratégie de stockage : Les images doivent-elles être stockées dans la base de données ou le système de fichiers ? Si le système de fichiers est choisi, comment concevoir la structure des répertoires ? Comment l’intégrer avec le workflow OpenSpec existant ? Tout cela nécessite une pesée attentive.
Conception de protocole de référence : Un moyen standard de référence d’image est nécessaire, qui peut être affiché par le rendu front-end et correctement analysé par le pipeline d’exécution IA. Chemin de fichier direct ? URL HTTP ? Ou concevoir un protocole dédié ?
Compatibilité des capacités IA : Les différents exécuteurs IA ont des degrés de support multimodal variables. Certains exécuteurs prennent en charge nativement l’entrée d’images, d’autres ne peuvent traiter que du texte. Comment concevoir une couche d’adaptation unifiée pour garantir que tous les exécuteurs puissent correctement traiter les informations d’image ?
Décisions de conception
Après une discussion et une pesée complètes, nous avons pris les décisions de conception clés suivantes.
Décision 1 : Stockage sur système de fichiers
Nous avons choisi de stocker les images sur le système de fichiers plutôt que dans la base de données. La structure des répertoires est conçue comme suit :
<Répertoire racine du système>/images/<sessionId>/├── <timestamp>-<uuid>.jpg└── <timestamp>-<uuid>.pngLa raison est en fait assez claire : simplifier la mise en œuvre, éviter l’expansion de la base de données, les fichiers peuvent être directement lus par l’IA. De plus, les fichiers image ne sont fondamentalement pas adaptés pour être placés dans la base de données, le système de fichiers est le choix plus naturel. C’est comme mettre des livres sur une étagère plutôt que de les fourrer dans un cahier, c’est la même logique.
Décision 2 : Protocole personnalisé hagiimag://
Afin d’éviter les conflits avec les URL HTTP tout en rendant la sémantique de référence plus claire, nous avons conçu un protocole de référence d’image personnalisé :
hagiimag://session-abc123/20260301-143022-a1b2c3d4Le format de ce protocole est hagiimag://<sessionId>/<imageId>, avec une sémantique claire, facile à analyser et à router. En voyant ce format, les développeurs comprendront immédiatement qu’il s’agit d’une référence d’image et non d’une URL ordinaire. Ce genre de petite attention de conception est parfois très utile.
Décision 3 : Séparation de la prévisualisation frontale et de l’accès IA
Au cours de la mise en œuvre, nous avons découvert que les besoins d’accès aux images du front-end et de l’IA sont différents : le front-end doit prévisualiser via l’API HTTP, tandis que l’IA doit lire directement le chemin de fichier local. Par conséquent, nous avons conçu des méthodes d’accès séparées :
- Le front-end utilise
/api/Images/{sessionId}/{imageId}/contentpour la prévisualisation - L’IA utilise le chemin de fichier local analysé côté serveur
Cela garantit à la fois la sécurité (ne pas exposer les chemins du serveur) et la facilité d’utilisation (le navigateur peut y accéder directement). Après tout, la sécurité et la facilité d’utilisation doivent toujours être équilibrées.
Décision 4 : Stratégie de téléchargement immédiat
Une autre décision clé concerne le moment du téléchargement. Nous choisissons de déclencher immédiatement le téléchargement lorsque l’utilisateur sélectionne ou colle une image, et lors de l’envoi du message, nous ne référençons que les images déjà téléchargées avec succès.
L’avantage de cette approche est de traiter les erreurs en amont, d’éviter de compliquer l’API d’envoi de messages, et de maintenir la simplicité du contrat JSON. Les utilisateurs peuvent savoir si l’image a été téléchargée avec succès avant l’envoi, ce qui offre une meilleure expérience. Cette idée de conception “prévoyante” peut s’appliquer dans de nombreux cas.
Solution
Conception de l’architecture
Sur la base des décisions ci-dessus, nous avons conçu l’architecture globale suivante :
Couche frontale├── ConversationInputArea ◄─────── useImageAttachmentManager│ │ ││ ├── Sélection de fichiers ├── Gestion de l'état des pièces jointes│ ├── Collage du presse-papiers ├── Téléchargement/réessayer/supprimer│ └── Prévisualisation des pièces jointes └── Génération de références d'images│Couche de service├── ImageUploadService│ ├── uploadImage() ◄─────── ImagesController│ ├── deleteImage() ││ ├── parseHagiImageUrl() ◄─────── Analyser les liens de protocole│ └── buildPreviewUrl() ││Couche back-end├── ImagesController ◄─────── ImagesDomainService│ │ ││ ├── POST /upload ├── Validation des fichiers│ ├── GET /{sessionId}/{imageId} ├── Enregistrement des images│ ├── DELETE ├── Compression des images│ └── GET /content └── Analyse des références│Couche d'exécution IA├── ImageContentBlock ◄─────── StructuredMessageDomainService│ │ ││ ├── Exécuteur multimodal ├── Analyse des blocs d'images│ └── Dégradation de l'exécuteur de texte └── Génération d'indices de cheminCette architecture illustre clairement le flux de données complet du front-end à l’IA. Chaque couche a des responsabilités claires et interagit via des interfaces standard. En fait, une bonne architecture est comme ça : chacun fait son travail, ne s’interfère pas, et communique sans problème.
Processus clés
Processus de téléchargement d’images :
- L’utilisateur sélectionne une image via la sélection de fichiers ou le collage du presse-papiers
- Le front-end valide le type et la taille du fichier (prend en charge JPEG/PNG/WEBP/GIF, 10 Mo par fichier)
- Appel de l’API de téléchargement, l’image est sauvegardée dans le répertoire
/images/{sessionId}/ - L’API renvoie la référence
hagiimag://et l’URL de prévisualisation - Le front-end affiche la vignette de prévisualisation dans la barre des pièces jointes, l’utilisateur peut prévisualiser avant l’envoi
Processus de reconnaissance IA :
- L’utilisateur envoie un message contenant une référence d’image
- Le back-end analyse le lien du protocole
hagiimag://, extrait sessionId et imageId - Mappe la référence d’image vers
ImageContentBlock - Sélectionne la méthode de traitement en fonction des capacités de l’exécuteur :
- Exécuteur multimodal : transmettre l’entrée d’image structurée
- Exécuteur de texte : dégradation vers un indice de chemin d’image
Ainsi, une boucle fermée complète est accomplie : l’utilisateur télécharge l’image → l’IA reconnaît l’image → l’IA renvoie le résultat de l’analyse. Ce genre de flux fluide apporte souvent une meilleure expérience aux utilisateurs.
Pratique
Mise en œuvre frontale
Sur le front-end, nous fournissons un Hook dédié pour gérer l’état des pièces jointes d’images :
import { useImageAttachmentManager } from '@/hooks/useImageAttachmentManager';
function ChatInput() { const { attachments, uploadedImages, hasBlockingAttachments, isUploading, selectFiles, removeAttachment, clearAttachments, } = useImageAttachmentManager({ ownerId: sessionId, mapUploadedImage: (response) => response, uploadOptions: { compress: false }, });
const handleFileSelect = (files: File[]) => { selectFiles(files); };
const handlePaste = (e: ClipboardEvent) => { const files = Array.from(e.clipboardData?.files || []) .filter(f => f.type.startsWith('image/')); if (files.length > 0) { handleFileSelect(files); } };
return ( <div> {/* Barre des pièces jointes */} {attachments.map(att => ( <AttachmentItem key={att.localId} file={att.file} status={att.status} onRemove={() => removeAttachment(att.localId)} /> ))}
{/* Zone de saisie */} <textarea onPaste={handlePaste} />
{/* Bouton de téléchargement */} <button onClick={() => fileInputRef.current?.click()}> Télécharger une image </button> </div> );}Ce Hook encapsule toute la logique de gestion des pièces jointes, y compris le suivi de l’état de téléchargement, la nouvelle tentative en cas d’échec, la suppression des pièces jointes, etc. Il est très simple à utiliser, il suffit d’appeler quelques méthodes pour compléter l’ensemble du processus. En fait, une bonne conception d’API est comme ça : simple et facile à utiliser, sans perdre en flexibilité.
Analyse du protocole personnalisé :
// Extraire sessionId et imageId du protocole personnaliséconst parsed = parseHagiImageUrl("hagiimag://session-abc123/20260301-143022-uuid");// Renvoie : { sessionId: "session-abc123", imageId: "20260301-143022-uuid" }
// Construire l'URL de prévisualisationconst previewUrl = buildPreviewUrl(parsed.sessionId, parsed.imageId);// Renvoie : "/api/Images/session-abc123/20260301-143022-uuid/content"Avec ces deux fonctions utilitaires, le front-end peut facilement effectuer des conversions entre le protocole hagiimag:// et l’URL HTTP. Une fois cette logique de conversion encapsulée, elle est beaucoup plus pratique à utiliser.
Mise en œuvre back-end
Le back-end utilise ASP.NET Core pour la mise en œuvre, le cœur étant ImagesController et ImagesDomainService :
[HttpPost("upload")][RequestSizeLimit(50 * 1024 * 1024)]public async Task<ActionResult<ImageUploadResponseDto>> Upload( [FromForm] UploadImageFormRequest input){ // 1. Valider la requête if (file == null || file.Length == 0) throw new UserFriendlyException("No file provided");
// 2. Valider le type et la taille du fichier var (isValid, errorMessage) = _imagesDomainService.ValidateImage( file.FileName, file.ContentType, file.Length); if (!isValid) throw new UserFriendlyException(errorMessage);
// 3. Sauvegarder sur le système de fichiers await using var stream = file.OpenReadStream(); var result = await _imagesDomainService.UploadImageAsync( stream, sessionId, file.FileName, file.ContentType, CurrentUserId, compress: input.Compress);
// 4. Renvoyer le résultat return Ok(result);}Cette mise en œuvre suit le modèle typique de développement d’API Web : validation, traitement, retour. Il convient de noter que nous avons défini une limite de taille de requête de 50 Mo pour empêcher le téléchargement malveillant de fichiers volumineux. Après tout, dans le monde en ligne, il vaut mieux être prudent.
Points d’attention
Au cours de la mise en œuvre, certains détails nécessitent une attention particulière :
Vérification des autorisations : L’accès aux images doit vérifier l’identité de l’utilisateur, garantissant que seules les images de sa propre session peuvent être consultées. C’est une exigence de sécurité de base qui ne peut être omise. En matière de sécurité, mieux vaut prévenir que guérir.
Sécurité des chemins : Valider strictement sessionId et imageId pour empêcher les attaques de traversée de chemins. Par exemple, refuser les chemins contenant ../ pour empêcher les utilisateurs d’accéder à des fichiers arbitraires dans le système. Une fois ces conditions aux limites bien traitées, le système peut être plus robuste.
Nettoyage des fichiers : Lors de la suppression d’une session, les images associées doivent être nettoyées de manière synchronisée pour éviter l’accumulation de fichiers orphelins. Après une exécution à long terme, ces fichiers peuvent occuper beaucoup d’espace disque. Nettoyer à temps est aussi une bonne habitude.
Stratégie de compression : Pour les noms de fichiers de type capture d’écran (tels que screenshot.png), activer automatiquement la compression pour économiser de l’espace. Cette stratégie peut être ajustée en fonction des besoins réels. Pour l’espace de stockage, chaque petit peu compte.
Traitement de dégradation : Les exécuteurs qui ne prennent pas en charge le multimodal doivent recevoir un indice de chemin d’image et ne peuvent pas silently supprimer les informations d’image. C’est très important, sinon les utilisateurs penseront que l’IA a ignoré leur image. L’expérience utilisateur, ce sont les détails qui font la différence.
Gestion de l’état : Les pièces jointes en cours de téléchargement bloquent l’envoi de messages, les pièces jointes échouées permettent de réessayer ou de supprimer. Cette conception garantit la cohérence de l’expérience utilisateur. Une fois la gestion de l’état claire, les utilisateurs ne seront pas confus.
Résumé
Grâce à cette solution complète de téléchargement et de reconnaissance d’images, HagiCode a réalisé une boucle fermée complète allant de la saisie utilisateur à la reconnaissance IA. Les points forts de cette solution comprennent :
- Le protocole personnalisé
hagiimag://réalise la standardisation des références d’images - Le stockage sur système de fichiers simplifie la mise en œuvre et améliore les performances
- La séparation de la prévisualisation frontale et de l’accès IA équilibre la sécurité et la facilité d’utilisation
- La stratégie de téléchargement immédiat optimise l’expérience utilisateur
- La conception de compatibilité multimodal et de dégradation de texte garantit la flexibilité
Cette solution fonctionne de manière stable dans HagiCode et a reçu des commentaires positifs des utilisateurs. Si vous mettez également en œuvre des fonctionnalités similaires, j’espère que cette expérience vous sera utile.
En fait, pour les solutions techniques, il n’y a pas de bien ou de mal absolu, seulement de ce qui convient ou non. Trouver la voie adaptée à son propre projet est le plus important.
Références
- HagiCode GitHub : github.com/HagiCode-org/site
- Site officiel HagiCode : hagicode.com
- Documentation du workflow OpenSpec : docs.hagicode.com
开始使用 HagiCode
一次安装,几分钟上手
HagiCode for Windows 在 Microsoft Store 免费提供。打开商店即可安装并保持更新;也可以先对比各版本与定价,再决定从哪个渠道开始。