En los proyectos de I+D, la IA suele ser la parte que se quiere probar y la infraestructura, lo que estorba. En Cloudflare, llamar a un modelo es un binding más, y la búsqueda semántica sobre tus datos son dos servicios conectados. En este capítulo añado a la aplicación de notas un buscador que entiende lo que preguntas.
Para quién es y qué necesitas
Para quien quiere añadir IA a su aplicación (resúmenes, preguntas sobre sus datos, agentes) sin montar infraestructura aparte.
Antes de empezar:
- El proyecto de los capítulos anteriores, con notas en D1.
- Para pasar de las 10.000 neuronas diarias de Workers AI: el plan de pago.
- Un límite de gasto pensado antes de escribir la primera llamada. Lo explico en AI Gateway.
Al terminar tendrás resúmenes con un modelo abierto, preguntas sobre tus notas con búsqueda semántica (RAG) y las llamadas vigiladas y limitadas con AI Gateway.
Las piezas de IA en Cloudflare
flowchart LR
W[Worker] -->|env.AI.run| GW[AI Gateway<br/>registro · caché · límites]
GW --> WAI[Workers AI<br/>modelos abiertos]
GW --> EXT[OpenAI · Anthropic<br/>otros proveedores]
W --> V[(Vectorize<br/>embeddings)]
W --> AS[AI Search<br/>RAG gestionado]
| Pieza | Para qué | Cuándo la uso |
|---|---|---|
| Workers AI | Ejecutar modelos (texto, embeddings, imagen, voz) | Prototipos, tareas de volumen alto y coste bajo, embeddings |
| AI Gateway | Observar y controlar llamadas a modelos, de Cloudflare o externos | Yo lo pongo siempre que hay IA en producción |
| Vectorize | Base de datos de vectores para búsqueda semántica | RAG a medida, recomendaciones, deduplicar |
| AI Search | RAG gestionado: indexa tus documentos y responde | Cuando no quieres montar el pipeline |
| Agents SDK | Agentes con estado, herramientas y tareas programadas | Asistentes que recuerdan y actúan |
Llamar a un modelo con Workers AI
{ "ai": { "binding": "AI", "remote": true }}const MODEL = "@cf/meta/llama-3.1-8b-instruct-fp8-fast";
export async function summarize(env: Env, text: string) { const response = await env.AI.run( MODEL, { messages: [ { role: "system", content: "Summarize in two sentences, in the text's language. No fluff.", }, { role: "user", content: text.slice(0, 8000) }, ], max_tokens: 200, }, { gateway: { id: "notes" } }, ); return response.response;}| Modelo (noviembre de 2026) | Tipo | Entrada / salida por millón de tokens |
|---|---|---|
@cf/meta/llama-3.1-8b-instruct-fp8-fast |
Texto, rápido | 0,045 $ / 0,384 $ |
@cf/openai/gpt-oss-20b |
Texto, razonamiento | 0,200 $ / 0,300 $ |
@cf/meta/llama-3.3-70b-instruct-fp8-fast |
Texto, más capaz | 0,293 $ / 2,253 $ |
@cf/baai/bge-m3 |
Embeddings multilingüe | 0,012 $ |
AI Gateway: ver, cachear y limitar
AI Gateway se crea en el panel (AI › AI Gateway) y se usa añadiendo { gateway: { id } } a la llamada, como en el ejemplo anterior. A cambio tienes:
| Función | Para qué |
|---|---|
| Registro de peticiones, tokens y coste | Saber qué gasta cada función de tu app |
| Caché de respuestas idénticas | No pagar dos veces la misma pregunta |
| Limitación de peticiones | Que un usuario o un error en bucle no dispare la factura |
| Reintentos y modelo de respaldo | Si un proveedor falla, probar con otro |
| Proveedores externos | OpenAI, Anthropic, Google y otros con la misma URL de gateway |
Para usar un proveedor externo a través del gateway, cambias la URL base de su SDK por la del gateway y guardas la clave del proveedor como secreto.
Búsqueda semántica con Vectorize
Un embedding convierte un texto en una lista de números (un vector) de forma que textos parecidos dan vectores cercanos. Guardando los vectores de cada nota en Vectorize, puedo buscar por significado (“mis notas sobre facturas”) y no solo por palabras exactas.
Antes del código, el recorrido completo de una pregunta (RAG):
sequenceDiagram
participant U as Usuario
participant W as Tu Worker
participant AI as Workers AI
participant V as Vectorize
participant DB as D1
U->>W: Pregunta
W->>AI: Vector de la pregunta
W->>V: Las 5 notas más parecidas, solo de este usuario
W->>DB: Texto de esas notas
W->>AI: Responde usando solo estas notas
AI-->>U: Respuesta
npx wrangler vectorize create notes-vectors --dimensions=1024 --metric=cosine{ "vectorize": [{ "binding": "VECTORS", "index_name": "notes-vectors" }]}Indexar una nota (lo hago en el consumidor de la cola, al crearla o editarla):
const EMBEDDINGS = "@cf/baai/bge-m3";
export async function embed(env: Env, text: string) { const r = await env.AI.run(EMBEDDINGS, { text: [text] }); // bge-m3 has several response shapes; with `text` it returns `data`. const vector = "data" in r ? r.data?.[0] : undefined; if (!vector) throw new Error("The model returned no vector"); return vector;}
export async function indexNote( env: Env, note: { id: number; userId: string; title: string; content: string },) { const vector = await embed(env, `${note.title}\n${note.content}`); await env.VECTORS.upsert([ { id: String(note.id), values: vector, metadata: { userId: note.userId }, }, ]);}Buscar y responder con las notas encontradas (RAG):
import { embed } from "./indexing";
export async function ask(env: Env, userId: string, question: string) { const vector = await embed(env, question); const { matches } = await env.VECTORS.query(vector, { topK: 5, filter: { userId }, });
const ids = matches.map((m) => Number(m.id)); if (ids.length === 0) return "I couldn't find any notes about that.";
const placeholders = ids.map(() => "?").join(","); const { results } = await env.DB .prepare(`SELECT title, content FROM notes WHERE id IN (${placeholders})`) .bind(...ids) .all<{ title: string; content: string }>();
const context = results .map((n) => `## ${n.title}\n${n.content}`) .join("\n\n");
const r = await env.AI.run( "@cf/meta/llama-3.1-8b-instruct-fp8-fast", { messages: [ { role: "system", content: "Answer in the user's language using only these notes. " + "If the answer isn't in them, say so.\n\n" + context, }, { role: "user", content: question }, ], max_tokens: 400, }, { gateway: { id: "notes" } }, ); return r.response;}AI Search: el RAG ya montado
Si no necesitas controlar cada paso, AI Search indexa los documentos de un bucket de R2 (o de una web), calcula los embeddings, los guarda y responde preguntas con un modelo, todo gestionado. Para un prototipo de “pregunta a tus documentos” es lo más rápido; si luego necesitas control fino, pasas a Vectorize.
Agentes
Para asistentes que mantienen una conversación, recuerdan y usan herramientas (consultar la base de datos, programar un recordatorio), el Agents SDK construye cada agente sobre un Durable Object, con estado propio y tareas programadas. Es otra guía en sí misma; lo menciono para que sepas que existe.
Cuánto cuesta
Cuánto cuesta al mes
Supuestos: 1.000 usuarios: 30.000 notas indexadas y 20.000 preguntas al mes con el modelo de 8B
| Concepto | Importe |
|---|---|
| Embeddings (bge-m3)Unos 9 M de tokens (30.000 notas de ~300 tokens y las preguntas) | 0,11 $ |
| Respuestas (Llama 3.1 8B)30 M de tokens de entrada (1,35 $) y 6 M de salida (2,30 $), sin descontar lo gratuito | 3,65 $ |
| Vectorize30.000 vectores de 1.024 dimensiones: dentro de lo incluido | 0,00 $ |
| AI GatewaySin coste en el uso básico | 0,00 $ |
| Total al mes | 3,76 $ |
Precios consultados el 3 de noviembre de 2026. Pueden cambiar.
| Producto | Gratuito | Pago |
|---|---|---|
| Workers AI | 10.000 neuronas al día | 0,011 $ por 1.000 neuronas (cada modelo publica su equivalencia en tokens) |
| Vectorize: dimensiones consultadas | 30 M al mes | 50 M incluidas; 0,01 $ por millón |
| Vectorize: dimensiones guardadas | 5 M | 10 M incluidas; 0,05 $ por cada 100 M |
Resumen
- Workers AI para modelos abiertos y embeddings; AI Gateway delante de todo, con límites.
- Vectorize + D1 para un RAG a medida; AI Search si lo quieres gestionado.
- Filtra siempre por usuario en las búsquedas y pon
max_tokens. - Siguiente capítulo: seguridad.
Recursos
- Workers AI
- Catálogo de modelos
- AI Gateway
- Vectorize
- AI Search
- Agents SDK
- Workers AI frente a AWS Bedrock para un MVP (próximamente)
