Implementación de carga de imágenes y reconocimiento con IA en el chat: una solución completa del diseño a la implementación
Implementación de carga de imágenes y reconocimiento con IA en el chat: una solución completa del diseño a la implementación
En los sistemas de interacción con IA, ¿cómo permitir que los usuarios suban imágenes y que la IA las reconozca directamente? Este problema me preocupó durante mucho tiempo, pero afortunadamente en la práctica de HagiCode encontramos algunas soluciones. Hoy compartiré esta solución de carga y reconocimiento de imágenes, desde el diseño de protocolos personalizados hasta el almacenamiento en sistemas de archivos, y la vista previa separada de frontend y backend. Es una nota técnica bastante completa.
Antecedentes
En esta era de auge del chat con IA, la información visual es un portador importante para que los usuarios expresen sus intenciones. Sin embargo, la mayoría de los sistemas de chat tradicionales solo admiten entrada de texto plano, lo que impide que los usuarios transmitan directamente contexto visual a la IA para su análisis, lo cual es una pena.
Durante el desarrollo, HagiCode se encontró con un dilema similar: los usuarios no podían subir imágenes al chat o al crear opiniones principales, la IA no podía acceder a la información visual local de los usuarios, y faltaba un ciclo completo desde la entrada, almacenamiento y renderizado de imágenes hasta la transmisión del contexto a la IA.
En realidad, estos problemas no son graves, solo requieren un poco de tiempo y paciencia para resolverlos. Diseñamos e implementamos un flujo completo de carga y reconocimiento de imágenes, permitiendo que Claude y otras IAs reconozcan y analicen directamente las capturas de pantalla subidas por los usuarios. A continuación explicaré en detalle los detalles de implementación de esta solución.
Sobre HagiCode
La solución compartida en este artículo proviene de nuestra experiencia práctica en el proyecto HagiCode. HagiCode es un proyecto de asistente de código IA de código abierto que utiliza un diseño de flujo de trabajo basado en OpenSpec, dedicado a proporcionar una experiencia de escritura de código más inteligente.
Análisis
Desafíos técnicos
Antes de comenzar la implementación, debemos primero aclarar los principales desafíos que enfrentamos, después de todo, afilar el hacha no retrasa el trabajo de cortar leña.
Colaboración entre módulos: La carga de imágenes implica múltiples módulos como UI de frontend, servicio de carga, API de backend, almacenamiento de archivos, persistencia de mensajes y mapeo de ejecución de IA. Cada módulo tiene sus propias responsabilidades e interfaces, y es necesario diseñar una solución integral coordinada.
Elección de estrategia de almacenamiento: ¿Las imágenes deben almacenarse en la base de datos o en el sistema de archivos? Si se elige el sistema de archivos, ¿cómo diseñar la estructura de directorios? ¿Cómo integrarse con el flujo de trabajo OpenSpec existente? Todo esto debe sopesarse cuidadosamente.
Diseño de protocolo de referencia: Se necesita una forma estándar de referencia de imágenes que pueda ser renderizada y mostrada por el frontend y correctamente analizada por la cadena de ejecución de la IA. ¿Usar directamente rutas de archivo? ¿URLs HTTP? ¿O diseñar un protocolo especializado?
Compatibilidad de capacidades de IA: Diferentes ejecutores de IA tienen diferentes niveles de soporte multimodal. Algunos ejecutores admiten nativamente entrada de imágenes, otros solo pueden procesar texto. ¿Cómo diseñar una capa de adaptación unificada para garantizar que todos los ejecutores puedan procesar correctamente la información de imágenes?
Decisiones de diseño
Después de una discusión y ponderación completas, tomamos las siguientes decisiones de diseño clave.
Decisión 1: Almacenamiento en sistema de archivos
Elegimos almacenar las imágenes en el sistema de archivos en lugar de en la base de datos. La estructura de directorios se diseñó de la siguiente manera:
<Directorio raíz del sistema>/images/<sessionId>/├── <timestamp>-<uuid>.jpg└── <timestamp>-<uuid>.pngLa razón es bastante clara: simplificar la implementación, evitar la inflación de la base de datos, y los archivos pueden ser leídos directamente por la IA. Además, los archivos de imagen本质上 no son adecuados para almacenarse en bases de datos; el sistema de archivos es la elección más natural. Es como poner libros en una estantería en lugar de meterlos en un cuaderno, es el mismo principio.
Decisión 2: Protocolo personalizado hagiimag://
Para evitar conflictos con URLs HTTP y hacer que la semántica de referencia sea más clara, diseñamos un protocolo personalizado de referencia de imágenes:
hagiimag://session-abc123/20260301-143022-a1b2c3d4El formato de este protocolo es hagiimag://<sessionId>/<imageId>, con semántica clara, fácil de analizar y enrutar. Al ver este formato, los desarrolladores pueden entender inmediatamente que es una referencia de imagen, no una URL ordinaria. Este pequeño detalle de diseño a veces resulta bastante útil.
Decisión 3: Separación de vista previa de frontend y acceso de IA
Durante la implementación, descubrimos que el frontend y la IA tienen diferentes necesidades de acceso a imágenes: el frontend necesita vista previa a través de HTTP API, mientras que la IA necesita leer directamente la ruta del archivo local. Por lo tanto, diseñamos métodos de acceso separados:
- Frontend usa
/api/Images/{sessionId}/{imageId}/contentpara vista previa - IA usa la ruta del archivo local analizada por el servidor
Esto garantiza tanto la seguridad (no expone la ruta del servidor) como la usabilidad (el navegador puede acceder directamente). Después de todo, la seguridad y la usabilidad siempre deben equilibrarse.
Decisión 4: Estrategia de carga inmediata
Otra decisión clave es el momento de carga. Elegimos activar la carga inmediatamente cuando el usuario selecciona o pega una imagen, y al enviar el mensaje solo referenciar las imágenes que se han cargado correctamente.
Las ventajas de este enfoque son el manejo de errores por adelantado, evitar complicar la API de envío de mensajes, y mantener la simplicidad del contrato JSON. Los usuarios pueden saber si la imagen se cargó correctamente antes de enviar, mejorando la experiencia. Esta idea de diseño de “prevenir problemas antes de que ocurran” probablemente es aplicable en muchos casos.
Solución
Diseño de arquitectura
Basado en las decisiones anteriores, diseñamos la siguiente arquitectura general:
Capa de frontend├── ConversationInputArea ◄─────── useImageAttachmentManager│ │ ││ ├── Selección de archivos ├── Gestión de estado de adjuntos│ ├── Pegar desde portapapeles ├── Cargar/reintentar/eliminar│ └── Vista previa de adjuntos └── Generación de referencias de imágenes│Capa de servicio├── ImageUploadService│ ├── uploadImage() ◄─────── ImagesController│ ├── deleteImage() ││ ├── parseHagiImageUrl() ◄─────── Analizar enlaces de protocolo│ └── buildPreviewUrl() ││Capa de backend├── ImagesController ◄─────── ImagesDomainService│ │ ││ ├── POST /upload ├── Validación de archivos│ ├── GET /{sessionId}/{imageId} ├── Guardado de imágenes│ ├── DELETE ├── Compresión de imágenes│ └── GET /content └── Análisis de referencias│Capa de ejecución de IA├── ImageContentBlock ◄─────── StructuredMessageDomainService│ │ ││ ├── Ejecutor multimodal ├── Análisis de bloques de imágenes│ └── Degradación de ejecutor de texto └── Generación de sugerencias de rutaEsta arquitectura muestra claramente el flujo de datos completo desde el frontend hasta la IA. Cada capa tiene responsabilidades claras e interactúa a través de interfaces estándar. De hecho, una buena arquitectura es así: cada uno hace su trabajo, sin interferencias, comunicación fluida.
Flujos clave
Flujo de carga de imágenes:
- El usuario selecciona imágenes mediante selección de archivos o pegado desde portapapeles
- El frontend valida el tipo y tamaño del archivo (admite JPEG/PNG/WEBP/GIF, 10MB por archivo)
- Llama a la API de carga, las imágenes se guardan en el directorio
/images/{sessionId}/ - La API devuelve la referencia
hagiimag://y la URL de vista previa - El frontend muestra miniaturas de vista previa en la barra de adjuntos, los usuarios pueden previsualizar antes de enviar
Flujo de reconocimiento de IA:
- El usuario envía un mensaje que contiene referencias de imágenes
- El backend analiza el enlace del protocolo
hagiimag://, extrae sessionId e imageId - Mapea la referencia de imagen a
ImageContentBlock - Según la capacidad del ejecutor, selecciona el método de procesamiento:
- Ejecutor multimodal: pasa entrada de imágenes estructurada
- Ejecutor de texto: degrada a sugerencia de ruta de imagen
Así se completa un ciclo completo: usuario sube imagen → IA reconoce imagen → IA devuelve resultado de análisis. Esta fluidez del proceso a menudo brinda a los usuarios una mejor experiencia.
Práctica
Implementación de frontend
En el frontend, proporcionamos un Hook dedicado para gestionar el estado de los adjuntos de imágenes:
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> {/* Barra de adjuntos */} {attachments.map(att => ( <AttachmentItem key={att.localId} file={att.file} status={att.status} onRemove={() => removeAttachment(att.localId)} /> ))}
{/* Caja de entrada */} <textarea onPaste={handlePaste} />
{/* Botón de carga */} <button onClick={() => fileInputRef.current?.click()}> Subir imagen </button> </div> );}Este Hook encapsula toda la lógica de gestión de adjuntos, incluyendo seguimiento del estado de carga, reintentos de falla, eliminación de adjuntos, etc. Es muy simple de usar, solo necesita llamar a algunos métodos para completar todo el flujo. De hecho, un buen diseño de API es así: simple y fácil de usar, sin perder flexibilidad.
Analizar protocolo personalizado:
// Extraer sessionId e imageId del protocolo personalizadoconst parsed = parseHagiImageUrl("hagiimag://session-abc123/20260301-143022-uuid");// Retorna: { sessionId: "session-abc123", imageId: "20260301-143022-uuid" }
// Construir URL de vista previaconst previewUrl = buildPreviewUrl(parsed.sessionId, parsed.imageId);// Retorna: "/api/Images/session-abc123/20260301-143022-uuid/content"A través de estas dos funciones de utilidad, el frontend puede convertir fácilmente entre el protocolo hagiimag:// y las URLs HTTP. Una vez encapsulada esta lógica de conversión, su uso es mucho más conveniente.
Implementación de backend
El backend se implementa con ASP.NET Core, el núcleo es ImagesController y ImagesDomainService:
[HttpPost("upload")][RequestSizeLimit(50 * 1024 * 1024)]public async Task<ActionResult<ImageUploadResponseDto>> Upload( [FromForm] UploadImageFormRequest input){ // 1. Validar solicitud if (file == null || file.Length == 0) throw new UserFriendlyException("No file provided");
// 2. Validar tipo y tamaño de archivo var (isValid, errorMessage) = _imagesDomainService.ValidateImage( file.FileName, file.ContentType, file.Length); if (!isValid) throw new UserFriendlyException(errorMessage);
// 3. Guardar en sistema de archivos await using var stream = file.OpenReadStream(); var result = await _imagesDomainService.UploadImageAsync( stream, sessionId, file.FileName, file.ContentType, CurrentUserId, compress: input.Compress);
// 4. Retornar resultado return Ok(result);}Esta implementación sigue el patrón típico de desarrollo de Web API: validar, procesar, retornar. Cabe mencionar que establecimos un límite de tamaño de solicitud de 50MB para evitar cargas maliciosas de archivos grandes. Después de todo, en el mundo de la red, un poco de precaución nunca está de más.
Consideraciones importantes
Durante el proceso de implementación, hay algunos detalles que requieren especial atención:
Verificación de permisos: El acceso a imágenes debe verificar la identidad del usuario, asegurando que solo pueda acceder a las imágenes de su propia sesión. Este es un requisito de seguridad básico que no puede omitirse. En cuanto a la seguridad, es mejor prevenir que lamentar.
Seguridad de rutas: Validar estrictamente sessionId e imageId para prevenir ataques de recorrido de rutas. Por ejemplo, rechazar rutas que contengan ../ para evitar que los usuarios accedan a archivos arbitrarios del sistema. Manejando bien estas condiciones límite, el sistema puede ser más robusto.
Limpieza de archivos: Al eliminar una sesión, se deben limpiar sincrónicamente las imágenes asociadas para evitar la acumulación de archivos huérfanos. Después de ejecutarse durante mucho tiempo, estos archivos pueden ocupar mucho espacio en disco. Limpiar a tiempo también es un buen hábito.
Estrategia de compresión: Para nombres de archivo tipo captura de pantalla (como screenshot.png), habilitar automáticamente la compresión para ahorrar espacio. Esta estrategia puede ajustarse según las necesidades reales. En cuanto al espacio de almacenamiento, ahorrar un poco es un poco.
Manejo de degradación: Los ejecutores que no admiten multimodal deben recibir sugerencias de ruta de imagen y no pueden descartar silenciosamente la información de imagen. Esto es muy importante, de lo contrario los usuarios pensarán que la IA ignoró sus imágenes. En cuanto a la experiencia del usuario, los detalles determinan el éxito o el fracaso.
Gestión de estado: Los adjuntos que se están cargando bloquearán el envío de mensajes, los adjuntos fallidos permiten reintentar o eliminar. Este diseño garantiza la coherencia de la experiencia del usuario. Con una gestión de estado clara, los usuarios no se sentirán confundidos.
Conclusión
A través de esta solución completa de carga y reconocimiento de imágenes, HagiCode ha logrado un ciclo completo desde la entrada del usuario hasta el reconocimiento de IA. Los puntos destacados principales de toda la solución incluyen:
- El protocolo personalizado
hagiimag://logra la estandarización de referencias de imágenes - El almacenamiento en sistema de archivos simplifica la implementación y mejora el rendimiento
- La separación de vista previa de frontend y acceso de IA equilibra seguridad y usabilidad
- La estrategia de carga inmediata optimiza la experiencia del usuario
- El diseño compatible de multimodal y degradación de texto asegura flexibilidad
Esta solución funciona de manera estable en HagiCode y tiene buena retroalimentación de los usuarios. Si también estás implementando una funcionalidad similar, espero que esta experiencia te sea útil.
En realidad, con las soluciones técnicas, no hay absoluto correcto o incorrecto, solo adecuado o no. Encontrar el camino adecuado para tu propio proyecto es lo más importante.
Referencias
- HagiCode GitHub: github.com/HagiCode-org/site
- Sitio web de HagiCode: hagicode.com
- Documentación del flujo de trabajo OpenSpec: docs.hagicode.com
开始使用 HagiCode
一次安装,几分钟上手
HagiCode for Windows 在 Microsoft Store 免费提供。打开商店即可安装并保持更新;也可以先对比各版本与定价,再决定从哪个渠道开始。