Files
petlink_final/docs/08-ia.md
2026-05-04 09:43:36 +01:00

8.0 KiB

8. Integração de Inteligência Artificial

8.1 Casos de Uso de IA

Funcionalidade Descrição Modelo
Match Inteligente Sugere os 3 animais mais compatíveis com o perfil do utilizador Claude Sonnet (API)
Assistente de Suporte Responde a dúvidas sobre adopção e doação em chat em tempo real Claude Sonnet (streaming)
Geração de Descrições Gera descrições cativantes para animais sem texto fornecido pelo canil Claude Haiku (API)
Análise de Necessidades Identifica padrões nas necessidades dos canis e sugere campanhas Claude Sonnet (API)
Moderação de Conteúdo Verifica fotos e textos submetidos pelos canis antes de publicar Claude Sonnet Vision

8.2 Arquitectura do Agente de Match

O sistema de match inteligente funciona em dois passos:

  1. Recolha de contexto: combina o perfil do utilizador com o inventário actualizado de animais disponíveis
  2. Inferência: passa o contexto ao Claude com um system prompt especializado e recebe sugestões fundamentadas

Estrutura do Contexto

// lib/ai/match.ts

interface MatchContext {
  user: {
    district: string;
    age: number;
    preferences: {
      species?: 'dog' | 'cat' | 'other';
      preferredSize?: 'small' | 'medium' | 'large';
      experience: 'first_time' | 'experienced' | 'expert';
      livingSpace: 'apartment' | 'house_no_garden' | 'house_with_garden';
      activityLevel: 'low' | 'moderate' | 'high';
      hasChildren?: boolean;
      hasOtherPets?: boolean;
    };
    history: {
      viewedAnimalIds: string[];
      pastAdoptions: number;
    };
  };
  availableAnimals: Array<{
    id: string;
    name: string;
    species: string;
    breed: string | null;
    ageMonths: number;
    sex: string;
    sterilized: boolean;
    district: string;
    shelterName: string;
    urgent: boolean;
    description: string | null;
  }>;
}

System Prompt do Agente de Match

// lib/ai/prompts.ts

export const MATCH_SYSTEM_PROMPT = `
És um especialista em adopção responsável de animais da plataforma PawLink, 
em Portugal. O teu papel é ajudar utilizadores a encontrar o animal de estimação
mais compatível com o seu estilo de vida.

INSTRUÇÕES:
- Analisa o perfil do utilizador e a lista de animais disponíveis
- Sugere exactamente 3 animais, ordenados por compatibilidade (do mais ao menos compatível)
- Para cada animal, explica em 2-3 frases porque é uma boa correspondência
- Considera sempre: espaço disponível, nível de actividade, experiência, localidade
- Animais com flag "urgent: true" devem receber peso extra na pontuação
- Não inventes características — usa apenas os dados fornecidos
- Se não houver animais adequados, diz-o honestamente e explica porquê

FORMATO DA RESPOSTA (JSON):
{
  "matches": [
    {
      "animalId": "string",
      "compatibilityScore": 95,
      "reason": "string — 2-3 frases em português"
    }
  ],
  "generalAdvice": "string — conselho geral opcional (máximo 1 frase)"
}

Responde APENAS com JSON válido, sem texto adicional.
`;

Implementação da Chamada à API

// lib/ai/match.ts
import Anthropic from '@anthropic-ai/sdk';

const anthropic = new Anthropic({ apiKey: process.env.ANTHROPIC_API_KEY });

export async function getAnimalMatches(context: MatchContext) {
  const response = await anthropic.messages.create({
    model: 'claude-sonnet-4-20250514',
    max_tokens: 1024,
    system: MATCH_SYSTEM_PROMPT,
    messages: [
      {
        role: 'user',
        content: `Perfil do utilizador e animais disponíveis:\n${JSON.stringify(context, null, 2)}`
      }
    ]
  });

  const text = response.content[0].type === 'text' ? response.content[0].text : '';
  
  try {
    return JSON.parse(text) as { matches: MatchResult[]; generalAdvice?: string };
  } catch {
    console.error('Erro ao fazer parse da resposta de match:', text);
    return null;
  }
}

8.3 Assistente de Suporte (Chatbot)

O assistente responde em tempo real usando streaming para melhor experiência do utilizador. É implementado com o Vercel AI SDK.

System Prompt do Suporte

export const SUPPORT_SYSTEM_PROMPT = `
És o assistente virtual da PawLink, uma plataforma portuguesa de adopção de 
animais e doação a canis. Chamas-te Paws.

PAPEL:
- Ajudar utilizadores a navegar na plataforma e responder a dúvidas sobre 
  adopção e doação de animais
- Fornecer informação geral sobre cuidados com animais de estimação
- Orientar utilizadores que estejam a considerar adoptar pela primeira vez

LIMITAÇÕES:
- Não tens acesso a informação em tempo real sobre animais específicos ou 
  disponibilidade actual (sugere usar os filtros da plataforma)
- Não podes processar pagamentos ou fazer reservas directamente
- Para questões técnicas graves, sugere contacto via email: suporte@pawlink.pt

TOM:
- Caloroso, encorajador e amigável
- Profissional mas acessível
- Nunca condescendente

LÍNGUA: Responde sempre em português de Portugal (pt-PT).
`;

Endpoint de Streaming

// app/api/chat/route.ts
import { streamText } from 'ai';
import { anthropic } from '@ai-sdk/anthropic';

export async function POST(req: Request) {
  const { messages } = await req.json();
  
  const result = await streamText({
    model: anthropic('claude-sonnet-4-20250514'),
    system: SUPPORT_SYSTEM_PROMPT,
    messages,
    maxTokens: 500, // respostas concisas no chat
  });
  
  return result.toDataStreamResponse();
}

Componente de Chat no Frontend

// components/chat/SupportChat.tsx
'use client';
import { useChat } from 'ai/react';

export function SupportChat() {
  const { messages, input, handleInputChange, handleSubmit, isLoading } = useChat({
    api: '/api/chat',
    initialMessages: [{
      id: 'welcome',
      role: 'assistant',
      content: 'Olá! Sou o Paws, o assistente da PawLink. Como posso ajudar-te hoje? 🐾'
    }]
  });

  return (
    <div className="chat-container">
      {messages.map(msg => (
        <div key={msg.id} className={`message ${msg.role}`}>
          {msg.content}
        </div>
      ))}
      <form onSubmit={handleSubmit}>
        <input value={input} onChange={handleInputChange} placeholder="Escreve a tua mensagem..." />
        <button type="submit" disabled={isLoading}>Enviar</button>
      </form>
    </div>
  );
}

8.4 Geração de Descrições de Animais

Quando um canil regista um animal sem fornecer descrição, o sistema gera automaticamente uma descrição cativante:

export async function generateAnimalDescription(animal: {
  name: string;
  species: string;
  breed: string | null;
  ageMonths: number;
  sex: string;
  sterilized: boolean;
}) {
  const response = await anthropic.messages.create({
    model: 'claude-haiku-4-5', // Haiku é suficiente e mais económico para esta tarefa
    max_tokens: 200,
    messages: [{
      role: 'user',
      content: `Escreve uma descrição curta e cativante (máximo 3 frases) para este animal 
                disponível para adopção numa plataforma portuguesa. 
                Dados: ${JSON.stringify(animal)}
                Escreve em português de Portugal, com tom caloroso e encorajador.
                Não inventes factos específicos, usa apenas os dados fornecidos.`
    }]
  });

  return response.content[0].type === 'text' ? response.content[0].text : null;
}

8.5 Boas Práticas e Limites

  • Rate limiting: chamadas à IA limitadas a 20 por utilizador por hora para controlo de custos
  • Cache de resultados: respostas de match são cacheadas 30 minutos no Redis (o inventário não muda ao segundo)
  • Fallback gracioso: se a API da Anthropic falhar, a plataforma funciona normalmente sem funcionalidades de IA
  • Transparência: o utilizador é informado quando está a interagir com IA
  • Sem decisões críticas: a IA sugere mas nunca decide automaticamente — o utilizador tem sempre controlo
  • Monitorização de custos: dashboard no Anthropic Console para acompanhar utilização e custos