Inteligência artificial


1Introdução

O Temma fornece uma fonte de dados unificada \Temma\Datasources\Ai para consultar modelos de linguagem (LLMs) de diversos provedores através de uma única interface.

Essa fonte de dados suporta:

  • Troca de texto (prompt / resposta)
  • Prompts de sistema e configuração de temperatura
  • Conversas com múltiplos turnos (histórico de conversa)
  • Anexos de entrada (imagens, áudio, vídeo, PDF)
  • Saída JSON estruturada

Seis provedores são integrados (OpenAI, Claude, Gemini, Mistral, OpenRouter, Ollama), e qualquer serviço compatível com OpenAI pode ser usado por meio da sintaxe de colchetes.

Para a documentação de referência da fonte de dados, veja a página da fonte de dados AI.

Em um assunto completamente diferente, o Temma também oferece skills para agentes de codificação com IA, que lhes ensinam as convenções do framework para facilitar seus desenvolvimentos. Veja a página das skills de IA.


2Configuração

2.1Formato do DSN

O DSN de conexão (Data Source Name) é escrito da seguinte forma:

ai://PROVIDER/MODEL#API_KEY
  • PROVIDER: nome do provedor (veja a lista abaixo).
  • MODEL: identificador do modelo de linguagem. Pode conter os caracteres / e :.
  • API_KEY: sua chave de API (opcional para alguns provedores, como o Ollama).

2.2Exemplo de configuração

No arquivo etc/temma.php:

<?php

return [
    'application' => [
        'dataSources' => [
            // conectar à OpenAI
            'ai' => 'ai://openai/gpt-4o#sk-proj-xxxxxxxxxxxxx',
            // ou ao Claude
            'ai' => 'ai://claude/claude-sonnet-4-20250514#sk-ant-xxxxxxxxxxxxx',
            // ou a um modelo local via Ollama
            'ai' => 'ai://ollama/llama3:70b',
        ],
    ],
];

3Provedores integrados

3.1Tabela resumo

Provedor Texto Imagens Áudio Vídeo PDF JSON
openai sim sim sim sim
claude sim sim sim sim
gemini sim sim sim sim sim sim
mistral sim sim sim sim sim
openrouter sim sim sim sim sim
ollama sim sim sim

O suporte real também depende do modelo utilizado. Por exemplo, nem todos os modelos da OpenAI suportam visão.


3.2Exemplos de DSN

# OpenAI
ai://openai/gpt-4o#sk-proj-XXX

# Claude (Anthropic)
ai://claude/claude-sonnet-4-20250514#sk-ant-XXX

# Google Gemini
ai://gemini/gemini-2.5-flash#AIza-XXX

# Mistral
ai://mistral/mistral-small-latest#XXX

# OpenRouter (o nome do modelo contém "/" separando o provedor do modelo)
ai://openrouter/openai/gpt-4o#sk-or-XXX
ai://openrouter/anthropic/claude-sonnet-4#sk-or-XXX

# Ollama (local, sem chave de API)
ai://ollama/llama3:70b
ai://ollama/mistral:latest

4Serviços compatíveis com OpenAI

4.1Sintaxe de colchetes (URL)

Muitos serviços oferecem uma API compatível com OpenAI. Para usá-los, coloque a URL do endpoint entre colchetes no DSN:

ai://[ENDPOINT_URL]/MODEL#API_KEY

Alguns serviços comuns:

Serviço URL do endpoint
Groq https://api.groq.com/openai/v1/chat/completions
Together AI https://api.together.xyz/v1/chat/completions
Fireworks AI https://api.fireworks.ai/inference/v1/chat/completions
DeepInfra https://api.deepinfra.com/v1/openai/chat/completions

Exemplos:

# Groq
ai://[https://api.groq.com/openai/v1/chat/completions]/llama-3.3-70b#gsk-XXX

# Together AI (nome do modelo com "/")
ai://[https://api.together.xyz/v1/chat/completions]/meta-llama/Meta-Llama-3-70B#XXX

# LiteLLM auto-hospedado
ai://[https://my-litellm.internal/v1/chat/completions]/model#key

4.2Provedores personalizados

Você também pode fornecer um nome de classe PHP entre colchetes. Essa classe será instanciada e usada como provedor:

ai://[\App\MyProvider]/model#key

A classe deve implementar os métodos buildPayload() e parseResponse(), assim como os provedores integrados (veja as classes em \Temma\Datasources\Ai\*).


5Uso

5.1Acesso como array

A notação de array envia um prompt e retorna a resposta como texto bruto.

$response = $this->ai['What is the capital of France?'];
// $response contém "The capital of France is Paris."

5.2Método read()

O método read() retorna uma resposta em texto bruto. O segundo parâmetro é um valor padrão (escalar ou callback) usado em caso de erro. O terceiro parâmetro é um array de opções.

// prompt simples
$response = $this->ai->read('What is the capital of France?');

// com um valor padrão
$response = $this->ai->read('Translate to French: Hello', 'Bonjour');

// com um callback em caso de erro
$response = $this->ai->read('Translate to French: Hello', function() {
    return 'Fallback value';
});

// com opções
$response = $this->ai->read('Explain photosynthesis', null, [
    'system'      => 'You are a biology teacher.',
    'temperature' => 0.3,
]);

5.3Método get()

O método get() ativa automaticamente o modo JSON: o LLM é instruído a responder com um JSON válido, e a resposta é automaticamente decodificada em um array PHP.

// solicitar dados estruturados
$data = $this->ai->get('List the 3 largest cities in France with their population');
// $data contém um array PHP, por exemplo:
// [
//     ['city' => 'Paris', 'population' => 2161000],
//     ['city' => 'Marseille', 'population' => 873076],
//     ['city' => 'Lyon', 'population' => 522250],
// ]

// com opções
$recipe = $this->ai->get("Create a recipe using: eggs, tomatoes, cheese", null, [
    'system' => "You are a chef. Return JSON with keys: title, difficulty (1-5), ingredients (list), steps (list).",
]);

6Opções

Os métodos read() e get() aceitam um terceiro parâmetro $options, um array associativo:

  • system: (string) Prompt de sistema que define o comportamento do assistente.
  • messages: (array) Histórico de mensagens anteriores para conversas com múltiplos turnos (veja abaixo).
  • temperature: (float) Temperatura de amostragem, entre 0 e 2. Valores mais baixos tornam as respostas mais determinísticas; valores mais altos as tornam mais criativas.
  • max_tokens: (int) Número máximo de tokens na resposta.
  • attachments: (array) Anexos a serem enviados com o prompt (veja a próxima seção).
  • output: (string) Formato de saída desejado. Pode ser um alias ('json', 'csv', 'audio', 'image', 'pdf', 'video', 'html', 'xml', 'wav') ou um tipo MIME completo ('application/json', 'audio/ogg', etc.).

Exemplo completo:

$response = $this->ai->read('Describe this image in detail', null, [
    'system'      => 'You are an image analysis expert.',
    'temperature' => 0.5,
    'max_tokens'  => 1000,
    'attachments' => ['/path/to/photo.jpg'],
]);

7Anexos

7.1Formatos aceitos

A opção attachments aceita um array de anexos. Cada elemento pode ser:

  • Uma string: caminho de arquivo (se file_exists() retornar true) ou conteúdo binário. O tipo MIME é detectado automaticamente.
  • Um array associativo com as chaves:
    • path: caminho do arquivo.
    • data: conteúdo binário.
    • mime: (opcional) tipo MIME explícito.

Exemplos:

// caminho de arquivo (MIME detectado automaticamente)
'attachments' => ['/path/to/photo.jpg']

// conteúdo binário (MIME detectado automaticamente)
'attachments' => [$binaryContent]

// binário com MIME explícito
'attachments' => [
    ['data' => $binaryContent, 'mime' => 'image/png'],
]

// caminho de arquivo com MIME explícito
'attachments' => [
    ['path' => '/path/to/document.pdf', 'mime' => 'application/pdf'],
]

// múltiplos anexos
'attachments' => [
    '/path/to/image1.jpg',
    ['data' => $pdfContent, 'mime' => 'application/pdf'],
]

7.2Tipo MIME

Se o tipo MIME não for fornecido explicitamente, ele é detectado automaticamente via finfo. Quando você já tem o conteúdo binário em memória, é recomendável fornecer o tipo MIME explicitamente para evitar uma detecção desnecessária.

O suporte a anexos depende do provedor e do modelo utilizado (veja a tabela resumo acima). Se um tipo de anexo não for suportado pelo provedor, ele será ignorado.


8Saída JSON

Existem duas abordagens para obter JSON estruturado:

Método get(): ativa automaticamente o modo JSON. O LLM recebe uma instrução de sistema pedindo que responda com um JSON válido. A resposta é automaticamente decodificada em um array PHP.

$data = $this->ai->get('The 5 most popular programming languages');
// $data é um array PHP

Método read() com a opção output: permite solicitar JSON usando read(). A resposta permanece uma string JSON bruta (não decodificada).

$json = $this->ai->read('The 5 most popular programming languages', null, [
    'output' => 'json',
]);
// $json é uma string JSON bruta
$data = json_decode($json, true);

9Conversa com múltiplos turnos

A opção messages permite fornecer o histórico das trocas anteriores. Cada mensagem é um array associativo com uma chave user ou ai:

$response = $this->ai->read('And the capital of Italy?', null, [
    'system'   => 'You are a geography assistant.',
    'messages' => [
        ['user' => 'What is the capital of France?'],
        ['ai' => 'The capital of France is Paris.'],
    ],
]);
// $response contém "The capital of Italy is Rome."

As mensagens do histórico também podem conter anexos:

$response = $this->ai->read('And this one?', null, [
    'messages' => [
        ['user' => 'Describe this image', 'attachments' => ['/path/to/photo1.jpg']],
        ['ai' => 'It is a ginger cat sitting on a sofa.'],
    ],
    'attachments' => ['/path/to/photo2.jpg'],
]);