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 | JSON | |
|---|---|---|---|---|---|---|
| openai | sí | sí | sí | sí | ||
| claude | sí | sí | sí | sí | ||
| gemini | sí | sí | sí | sí | sí | sí |
| mistral | sí | sí | sí | sí | sí | |
| openrouter | sí | sí | sí | sí | sí | |
| ollama | sí | sí | sí |
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'],
]);