Pular para o conteúdo

Implementando upload de imagens e reconhecimento de IA no chat: uma solução completa do design à implementação

Editar página
HagiCode for Windows Microsoft Store artwork
HagiCode for Windows is now on Microsoft Store
HagiCode for Windows is officially live on Microsoft Store. Windows users can install it directly from the storefront and stay on the store-managed update path. Open the listing and take a look.
Open Microsoft Store

Implementando upload de imagens e reconhecimento de IA no chat: uma solução completa do design à implementação

Em sistemas de interação com IA, como permitir que os usuários façam upload de imagens e que a IA as reconheça diretamente? Na verdade, eu me debati com esse problema por bastante tempo, mas felizmente encontrei algumas soluções através da prática no HagiCode. Hoje, vou compartilhar esta solução de upload e reconhecimento de imagens, desde o design de protocolos personalizados até o armazenamento em sistema de arquivos, passando pela pré-visualização com separação entre frontend e backend, constituindo uma nota técnica completa.

Background

Nesta era de popularidade dos chats com IA, informações visuais são, na verdade, um veículo importante para os usuários expressarem suas intenções. No entanto, a maioria dos sistemas de chat tradicionais suporta apenas entrada de texto puro, o que impede os usuários de transmitir contexto visual diretamente para a IA analisar, o que é uma pena.

Durante o desenvolvimento do HagiCode, enfrentamos um dilema semelhante: os usuários não conseguiam fazer upload de imagens ao criar chats ou opiniões principais, a IA não conseguia acessar informações visuais locais dos usuários, e faltava um ciclo completo desde a entrada, armazenamento e renderização de imagens até a transmissão do contexto para a IA.

Na verdade, esses problemas não são tão grandes, apenas precisam de um pouco de tempo e paciência para serem resolvidos. Projetamos e implementamos um fluxo completo de upload e reconhecimento de imagens, permitindo que IAs como o Claude reconheçam e analisem diretamente as capturas de tela enviadas pelos usuários. A seguir, detalharei gradualmente os detalhes de implementação desta solução.

Sobre o HagiCode

A solução compartilhada neste artigo vem da nossa experiência prática no projeto HagiCode. HagiCode é um projeto de assistente de código com IA de código aberto, que adota um design de fluxo de trabalho baseado em OpenSpec, dedicado a fornecer uma experiência de codificação mais inteligente.

Análise

Desafios Técnicos

Antes de começar a implementação, precisamos primeiro esclarecer os principais desafios enfrentados, já que afiar o machado não prejudica o corte de lenha.

Colaboração entre módulos: O upload de imagens envolve múltiplos módulos como UI frontend, serviço de upload, API backend, armazenamento de arquivos, persistência de mensagens e mapeamento de execução de IA. Cada módulo tem suas próprias responsabilidades e interfaces, exigindo o design de uma solução integrada e coerente.

Escolha da estratégia de armazenamento: As imagens devem ser armazenadas no banco de dados ou no sistema de arquivos? Se escolhermos o sistema de arquivos, como projetar a estrutura de diretórios? Como integrar com o fluxo de trabalho OpenSpec existente? Tudo isso precisa ser cuidadosamente ponderado.

Design de protocolo de referência: É necessário um método padrão de referência de imagem que possa ser renderizado e exibido pelo frontend e também corretamente analisado pelo pipeline de execução da IA. Usar diretamente caminhos de arquivo? URLs HTTP? Ou projetar um protocolo específico?

Compatibilidade de capacidades da IA: Diferentes executores de IA têm graus variados de suporte a multimodalidade. Alguns executores suportam nativamente entrada de imagens, outros só podem processar texto. Como projetar uma camada de adaptação unificada para garantir que todos os executores possam processar corretamente informações de imagens?

Decisões de Design

Após discussão e ponderação suficientes, tomamos as seguintes decisões-chave de design.

Decisão 1: Armazenamento em sistema de arquivos

Escolhemos armazenar as imagens no sistema de arquivos em vez do banco de dados. A estrutura de diretórios foi projetada da seguinte forma:

<diretório raiz do sistema>/images/<sessionId>/
├── <timestamp>-<uuid>.jpg
└── <timestamp>-<uuid>.png

Os motivos são bastante claros: simplificar a implementação, evitar a inflação do banco de dados, e os arquivos podem ser lidos diretamente pela IA. Além disso, arquivos de imagem essencialmente não são adequados para serem colocados em bancos de dados, o sistema de arquivos é a escolha mais natural. É como colocar livros numa estante, não num caderno, é o mesmo princípio.

Decisão 2: Protocolo personalizado hagiimag://

Para evitar conflitos com URLs HTTP e tornar a semântica de referência mais clara, projetamos um protocolo personalizado de referência de imagem:

hagiimag://session-abc123/20260301-143022-a1b2c3d4

O formato deste protocolo é hagiimag://<sessionId>/<imageId>, com semântica clara, facilitando a análise e roteamento. Ao ver este formato, os desenvolvedores entendem imediatamente que se trata de uma referência de imagem, não uma URL comum. Esse pequeno detalhe de design às vezes é bastante útil.

Decisão 3: Separação entre pré-visualização frontend e acesso da IA

Durante a implementação, descobrimos que o frontend e a IA têm necessidades diferentes de acesso a imagens: o frontend precisa pré-visualizar através da API HTTP, enquanto a IA precisa ler diretamente caminhos de arquivos locais. Portanto, projetamos métodos de acesso separados:

  • Frontend usa /api/Images/{sessionId}/{imageId}/content para pré-visualização
  • IA usa caminhos de arquivos locais analisados no servidor

Isso garante segurança (não expõe caminhos do servidor) e também considera usabilidade (navegadores podem acessar diretamente). Afinal, segurança e usabilidade sempre precisam ser equilibradas.

Decisão 4: Estratégia de upload imediato

Outra decisão chave é o momento do upload. Escolhemos acionar o upload imediatamente quando o usuário seleciona ou cola uma imagem, e ao enviar a mensagem apenas referenciamos imagens já enviadas com sucesso.

Os benefícios disso são: tratamento de erros antecipado, evitar complexidade da API de envio de mensagens, manter a simplicidade do contrato JSON. Os usuários sabem se o upload foi bem-sucedido antes de enviar, proporcionando melhor experiência. Esta ideia de design “prevenir antes que aconteça” talvez seja aplicável em muitas situações.

Solução

Design de Arquitetura

Com base nas decisões acima, projetamos a seguinte arquitetura geral:

Camada Frontend
├── ConversationInputArea ◄─────── useImageAttachmentManager
│ │ │
│ ├── Seleção de arquivo ├── Gerenciamento de estado de anexos
│ ├── Colagem da área de ├── Upload/repetição/exclusão
│ │ transferência ├── Geração de referência de imagem
│ └── Pré-visualização └──
│ de anexos
│
Camada de Serviço
├── ImageUploadService
│ ├── uploadImage() ◄─────── ImagesController
│ ├── deleteImage() │
│ ├── parseHagiImageUrl() ◄─────── Analisar link de protocolo
│ └── buildPreviewUrl() │
│
Camada Backend
├── ImagesController ◄─────── ImagesDomainService
│ │ │
│ ├── POST /upload ├── Validação de arquivo
│ ├── GET /{sessionId}/{imageId} ├── Salvamento de imagem
│ ├── DELETE ├── Compressão de imagem
│ └── GET /content └── Análise de referência
│
Camada de Execução IA
├── ImageContentBlock ◄─────── StructuredMessageDomainService
│ │ │
│ ├── Executor multimodal ├── Análise de bloco de imagem
│ └── Degeneração de executor └── Geração de dica de caminho
│ de texto

Esta arquitetura mostra claramente o fluxo completo de dados do frontend até a IA. Cada camada tem responsabilidades claras e interage através de interfaces padrão. Na verdade, uma boa arquitetura é assim: cada um cumprindo suas funções, sem interferir uns nos outros, comunicação fluida.

Fluxos Principais

Fluxo de upload de imagem:

  1. Usuário seleciona imagem através de seleção de arquivo ou colagem da área de transferência
  2. Frontend valida tipo e tamanho do arquivo (suporta JPEG/PNG/WEBP/GIF, 10MB por arquivo)
  3. Chama API de upload, imagem é salva no diretório /images/{sessionId}/
  4. API retorna referência hagiimag:// e URL de pré-visualização
  5. Frontend exibe miniatura de pré-visualização na barra de anexos, usuário pode pré-visualizar antes de enviar

Fluxo de reconhecimento por IA:

  1. Usuário envia mensagem contendo referência de imagem
  2. Backend analisa o link do protocolo hagiimag://, extrai sessionId e imageId
  3. Mapeia a referência de imagem para ImageContentBlock
  4. Seleciona método de processamento com base na capacidade do executor:
    • Executor multimodal: transmite entrada de imagem estruturada
    • Executor de texto: degenera para dica de caminho da imagem

Isso completa um ciclo completo: usuário faz upload da imagem → IA reconhece a imagem → IA retorna resultado da análise. Essa fluidez no processo geralmente traz melhor experiência para o usuário.

Prática

Implementação Frontend

No frontend, fornecemos um Hook dedicado para gerenciar o estado de anexos de imagem:

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 anexos */}
{attachments.map(att => (
<AttachmentItem
key={att.localId}
file={att.file}
status={att.status}
onRemove={() => removeAttachment(att.localId)}
/>
))}
{/* Campo de entrada */}
<textarea onPaste={handlePaste} />
{/* Botão de upload */}
<button onClick={() => fileInputRef.current?.click()}>
上传图片
</button>
</div>
);
}

Este Hook encapsula toda a lógica de gerenciamento de anexos, incluindo rastreamento de status de upload, repetição em caso de falha, exclusão de anexos, etc. É muito simples de usar, bastando chamar alguns métodos para completar todo o fluxo. Na verdade, um bom design de API é assim: simples de usar, sem perder flexibilidade.

Analisando protocolo personalizado:

// Extrai sessionId e imageId do protocolo personalizado
const parsed = parseHagiImageUrl("hagiimag://session-abc123/20260301-143022-uuid");
// Retorna: { sessionId: "session-abc123", imageId: "20260301-143022-uuid" }
// Constrói URL de pré-visualização
const previewUrl = buildPreviewUrl(parsed.sessionId, parsed.imageId);
// Retorna: "/api/Images/session-abc123/20260301-143022-uuid/content"

Através dessas duas funções utilitárias, o frontend pode facilmente converter entre o protocolo hagiimag:// e URLs HTTP. Com essa lógica de conversão encapsulada, o uso se torna muito mais conveniente.

Implementação Backend

O backend usa ASP.NET Core, o núcleo é ImagesController e ImagesDomainService:

[HttpPost("upload")]
[RequestSizeLimit(50 * 1024 * 1024)]
public async Task<ActionResult<ImageUploadResponseDto>> Upload(
[FromForm] UploadImageFormRequest input)
{
// 1. Validar solicitação
if (file == null || file.Length == 0)
throw new UserFriendlyException("No file provided");
// 2. Validar tipo e tamanho do arquivo
var (isValid, errorMessage) = _imagesDomainService.ValidateImage(
file.FileName, file.ContentType, file.Length);
if (!isValid)
throw new UserFriendlyException(errorMessage);
// 3. Salvar no sistema de arquivos
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 implementação segue o padrão típico de desenvolvimento de Web API: validação, processamento, retorno. Vale notar que definimos um limite de tamanho de solicitação de 50MB para evitar uploads maliciosos de arquivos grandes. Afinal, no mundo online, é sempre melhor ser cauteloso.

Considerações Importantes

Durante a implementação, alguns detalhes merecem atenção especial:

Verificação de permissões: O acesso a imagens deve verificar a identidade do usuário, garantindo que apenas imagens de suas próprias sessões possam ser acessadas. Este é um requisito básico de segurança, não pode ser omitido. Com segurança, é melhor prevenir do que remediar.

Segurança de caminho: Validar rigorosamente sessionId e imageId para evitar ataques de travessia de caminho. Por exemplo, recusar caminhos contendo ../ para impedir que usuários acessem arquivos arbitrários no sistema. Tratando bem essas condições de borda, o sistema se torna mais robusto.

Limpeza de arquivos: Ao excluir uma sessão, as imagens associadas devem ser limpas simultaneamente, evitando acúmulo de arquivos órfãos. Após longo tempo de execução, esses arquivos podem ocupar muito espaço em disco. Limpar prontamente também é um bom hábito.

Estratégia de compressão: Para nomes de arquivo do tipo captura de tela (como screenshot.png), habilitar automaticamente compressão para economizar espaço. Esta estratégia pode ser ajustada conforme as necessidades reais. Espaço de armazenamento, economizar um pouco já ajuda.

Tratamento de degeneração: Executores que não suportam multimodalidade devem receber dicas de caminho de imagem, não podem descartar silenciosamente informações de imagem. Este ponto é importante, caso contrário os usuários pensarão que a IA ignorou suas imagens. Experiência do usuário, os detalhes determinam o sucesso ou fracasso.

Gerenciamento de estado: Anexos em upload bloqueiam o envio de mensagens, anexos com falha permitem repetição ou exclusão. Este design garante a continuidade da experiência do usuário. Com gerenciamento de estado claro, os usuários não ficam confusos.

Conclusão

Através desta solução completa de upload e reconhecimento de imagens, o HagiCode实现了实现从用户输入到 IA 识别的完整闭环。整个方案的核心亮点包括:

  • O protocolo personalizado hagiimag:// 实现了图片引用的标准化
  • O armazenamento em sistema de arquivos simplificou a implementação e melhorou o desempenho
  • A separação entre pré-visualização frontend e acesso IA equilibrou segurança e usabilidade
  • A estratégia de upload imediato otimizou a experiência do usuário
  • O design de compatibilidade entre multimodalidade e degeneração de texto garantiu flexibilidade

Esta solução opera de forma estável no HagiCode, com feedback positivo dos usuários. Se você também está implementando funcionalidades semelhantes, espero que estas experiências sejam úteis.

Na verdade, soluções técnicas não têm certo ou errado absoluto, apenas adequado ou não. Encontrar o caminho adequado para o seu projeto é o mais importante.

Referências

开始使用 HagiCode

一次安装,几分钟上手

HagiCode for Windows 在 Microsoft Store 免费提供。打开商店即可安装并保持更新;也可以先对比各版本与定价,再决定从哪个渠道开始。