Inteligencia artificial


1Introducción

Temma proporciona una fuente de datos unificada \Temma\Datasources\Ai para consultar modelos de lenguaje (LLM) de distintos proveedores a través de una única interfaz.

Esta fuente de datos admite:

  • Intercambio de texto (prompt / respuesta)
  • Prompts de sistema y configuración de la temperatura
  • Conversaciones con varios turnos (historial de la conversación)
  • Adjuntos de entrada (imágenes, audio, vídeo, PDF)
  • Salida JSON estructurada

Hay seis proveedores integrados (OpenAI, Claude, Gemini, Mistral, OpenRouter, Ollama), y cualquier servicio compatible con OpenAI puede usarse mediante la sintaxis de corchetes.

Para la documentación de referencia de la fuente de datos, consulta la página de la fuente de datos AI.

En un tema completamente distinto, Temma también proporciona skills para agentes de IA dedicados a la programación, que les enseñan las convenciones del framework para facilitar tus desarrollos. Consulta la página de skills de IA.


2Configuración

2.1Formato del DSN

El DSN de conexión (Data Source Name) se escribe así:

ai://PROVIDER/MODEL#API_KEY
  • PROVIDER: nombre del proveedor (ver lista más abajo).
  • MODEL: identificador del modelo de lenguaje. Puede contener los caracteres / y :.
  • API_KEY: tu clave de API (opcional para algunos proveedores como Ollama).

2.2Ejemplo de configuración

En el archivo etc/temma.php:

<?php

return [
    'application' => [
        'dataSources' => [
            // conexión a OpenAI
            'ai' => 'ai://openai/gpt-4o#sk-proj-xxxxxxxxxxxxx',
            // o a Claude
            'ai' => 'ai://claude/claude-sonnet-4-20250514#sk-ant-xxxxxxxxxxxxx',
            // o a un modelo local vía Ollama
            'ai' => 'ai://ollama/llama3:70b',
        ],
    ],
];

3Proveedores integrados

3.1Tabla resumen

Proveedor Texto Imágenes Audio Vídeo PDF JSON
openai
claude
gemini
mistral
openrouter
ollama

La compatibilidad real también depende del modelo utilizado. Por ejemplo, no todos los modelos de OpenAI admiten visión.


3.2Ejemplos 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 (el nombre del modelo contiene "/" para separar el proveedor del modelo)
ai://openrouter/openai/gpt-4o#sk-or-XXX
ai://openrouter/anthropic/claude-sonnet-4#sk-or-XXX

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

4Servicios compatibles con OpenAI

4.1Sintaxis de corchetes (URL)

Muchos servicios ofrecen una API compatible con OpenAI. Para usarlos, coloca la URL del endpoint entre corchetes en el DSN:

ai://[ENDPOINT_URL]/MODEL#API_KEY

Algunos servicios habituales:

Servicio URL del 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

Ejemplos:

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

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

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

4.2Proveedores personalizados

También puedes indicar el nombre de una clase PHP entre corchetes. Esta clase se instanciará y se usará como proveedor:

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

La clase debe implementar los métodos buildPayload() y parseResponse(), igual que los proveedores integrados (consulta las clases en \Temma\Datasources\Ai\*).


5Uso

5.1Acceso tipo array

La notación de array envía un prompt y devuelve la respuesta como texto sin procesar.

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

5.2Método read()

El método read() devuelve una respuesta en texto sin procesar. El segundo parámetro es un valor por defecto (escalar o callback) usado en caso de error. El tercer parámetro es un array de opciones.

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

// con un valor por defecto
$response = $this->ai->read('Translate to French: Hello', 'Bonjour');

// con un callback en caso de error
$response = $this->ai->read('Translate to French: Hello', function() {
    return 'Fallback value';
});

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

5.3Método get()

El método get() activa automáticamente el modo JSON: se indica al LLM que responda con un JSON válido, y la respuesta se decodifica automáticamente en un array PHP.

// solicitud de datos estructurados
$data = $this->ai->get('List the 3 largest cities in France with their population');
// $data contiene un array PHP, por ejemplo:
// [
//     ['city' => 'Paris', 'population' => 2161000],
//     ['city' => 'Marseille', 'population' => 873076],
//     ['city' => 'Lyon', 'population' => 522250],
// ]

// con opciones
$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).",
]);

6Opciones

Los métodos read() y get() aceptan un tercer parámetro $options, un array asociativo:

  • system: (string) Prompt de sistema que define el comportamiento del asistente.
  • messages: (array) Historial de mensajes previos para conversaciones con varios turnos (ver más abajo).
  • temperature: (float) Temperatura de muestreo, entre 0 y 2. Los valores más bajos hacen las respuestas más deterministas, los más altos las hacen más creativas.
  • max_tokens: (int) Número máximo de tokens en la respuesta.
  • attachments: (array) Adjuntos que se envían con el prompt (ver la sección siguiente).
  • output: (string) Formato de salida deseado. Puede ser un alias ('json', 'csv', 'audio', 'image', 'pdf', 'video', 'html', 'xml', 'wav') o un tipo MIME completo ('application/json', 'audio/ogg', etc.).

Ejemplo 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'],
]);

7Adjuntos

7.1Formatos aceptados

La opción attachments acepta un array de adjuntos. Cada elemento puede ser:

  • Una cadena de texto: ruta de archivo (si file_exists() devuelve true) o contenido binario. El tipo MIME se detecta automáticamente.
  • Un array asociativo con las claves:
    • path: ruta del archivo.
    • data: contenido binario.
    • mime: (opcional) tipo MIME explícito.

Ejemplos:

// ruta de archivo (MIME detectado automáticamente)
'attachments' => ['/path/to/photo.jpg']

// contenido binario (MIME detectado automáticamente)
'attachments' => [$binaryContent]

// binario con MIME explícito
'attachments' => [
    ['data' => $binaryContent, 'mime' => 'image/png'],
]

// ruta de archivo con MIME explícito
'attachments' => [
    ['path' => '/path/to/document.pdf', 'mime' => 'application/pdf'],
]

// varios adjuntos
'attachments' => [
    '/path/to/image1.jpg',
    ['data' => $pdfContent, 'mime' => 'application/pdf'],
]

7.2Tipo MIME

Si el tipo MIME no se indica explícitamente, se detecta automáticamente mediante finfo. Cuando ya tienes contenido binario en memoria, se recomienda indicar el tipo MIME explícitamente para evitar una detección innecesaria.

La compatibilidad con adjuntos depende del proveedor y del modelo utilizado (ver la tabla resumen anterior). Si un tipo de adjunto no es compatible con el proveedor, se ignorará.


8Salida JSON

Hay dos formas de obtener JSON estructurado:

Método get(): activa automáticamente el modo JSON. El LLM recibe una instrucción de sistema pidiéndole que responda con un JSON válido. La respuesta se decodifica automáticamente en un array PHP.

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

Método read() con la opción output: permite solicitar JSON usando read(). La respuesta sigue siendo una cadena JSON sin procesar (no decodificada).

$json = $this->ai->read('The 5 most popular programming languages', null, [
    'output' => 'json',
]);
// $json es una cadena JSON sin procesar
$data = json_decode($json, true);

9Conversación con varios turnos

La opción messages permite proporcionar el historial de intercambios anteriores. Cada mensaje es un array asociativo con una clave user o 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 contiene "The capital of Italy is Rome."

Los mensajes del historial también pueden contener adjuntos:

$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'],
]);