01 · Configuración Base
src/lib/cerebras.ts — Cliente base LexIA (TypeScript)
TypeScript
// src/lib/cerebras.ts — LexIA × Cerebras Integration
// Migración desde OpenAI API: solo cambiar baseURL
import OpenAI from "openai";
// Drop-in replacement — misma interface OpenAI, hardware Cerebras
export const cerebras = new OpenAI({
apiKey: process.env.CEREBRAS_API_KEY!,
baseURL: "https://api.cerebras.ai/v1", // ← única diferencia vs OpenAI
});
// Modelos disponibles en Cerebras (junio 2026)
export const MODELS = {
FAST: "llama3.1-8b", // ~2500 tok/s — clasificación, extracción simple
SMART: "llama3.3-70b", // ~2000 tok/s — consultas complejas, redacción
CONTEXT: "llama3.1-70b", // 128K tokens — análisis de contratos largos
} as const;
// Prompt de sistema para asistente legal LexIA
export const SYSTEM_LEGAL = `Eres el asistente legal de LexIA. Respondes consultas sobre
derecho laboral, contratos y normativa española. Citas artículos
concretos. Si no tienes certeza, lo indicas explícitamente.
Idioma: siempre español. Tono: profesional y conciso.`;
// Migración desde OpenAI API: solo cambiar baseURL
import OpenAI from "openai";
// Drop-in replacement — misma interface OpenAI, hardware Cerebras
export const cerebras = new OpenAI({
apiKey: process.env.CEREBRAS_API_KEY!,
baseURL: "https://api.cerebras.ai/v1", // ← única diferencia vs OpenAI
});
// Modelos disponibles en Cerebras (junio 2026)
export const MODELS = {
FAST: "llama3.1-8b", // ~2500 tok/s — clasificación, extracción simple
SMART: "llama3.3-70b", // ~2000 tok/s — consultas complejas, redacción
CONTEXT: "llama3.1-70b", // 128K tokens — análisis de contratos largos
} as const;
// Prompt de sistema para asistente legal LexIA
export const SYSTEM_LEGAL = `Eres el asistente legal de LexIA. Respondes consultas sobre
derecho laboral, contratos y normativa española. Citas artículos
concretos. Si no tienes certeza, lo indicas explícitamente.
Idioma: siempre español. Tono: profesional y conciso.`;
02 · Patrones de Integración
Chat Legal con Streaming
TypeScript
Streaming
// Caso 1: Consulta legal en tiempo real
// Primer token visible en <200ms
export async function* streamLegalChat(
query: string,
history: Message[] = []
) {
const stream = await cerebras.chat.completions.create({
model: MODELS.SMART, // 70B para calidad jurídica
messages: [
{ role: "system", content: SYSTEM_LEGAL },
...history,
{ role: "user", content: query },
],
stream: true,
temperature: 0.3, // baja para precisión legal
max_tokens: 1500,
});
for await (const chunk of stream) {
const text = chunk.choices[0]?.delta?.content ?? "";
if (text) yield text; // enviar al cliente vía SSE
}
}
// Next.js Route Handler (app/api/chat/route.ts)
export async function POST(req: Request) {
const { query, history } = await req.json();
const encoder = new TextEncoder();
const stream = streamLegalChat(query, history);
return new Response(
new ReadableStream({
async start(ctrl) {
for await (const token of stream)
ctrl.enqueue(encoder.encode(token));
ctrl.close();
}
}),
{ headers: { "Content-Type": "text/plain; charset=utf-8" } }
);
}
// Primer token visible en <200ms
export async function* streamLegalChat(
query: string,
history: Message[] = []
) {
const stream = await cerebras.chat.completions.create({
model: MODELS.SMART, // 70B para calidad jurídica
messages: [
{ role: "system", content: SYSTEM_LEGAL },
...history,
{ role: "user", content: query },
],
stream: true,
temperature: 0.3, // baja para precisión legal
max_tokens: 1500,
});
for await (const chunk of stream) {
const text = chunk.choices[0]?.delta?.content ?? "";
if (text) yield text; // enviar al cliente vía SSE
}
}
// Next.js Route Handler (app/api/chat/route.ts)
export async function POST(req: Request) {
const { query, history } = await req.json();
const encoder = new TextEncoder();
const stream = streamLegalChat(query, history);
return new Response(
new ReadableStream({
async start(ctrl) {
for await (const token of stream)
ctrl.enqueue(encoder.encode(token));
ctrl.close();
}
}),
{ headers: { "Content-Type": "text/plain; charset=utf-8" } }
);
}
Extracción de Cláusulas (JSON mode)
TypeScript
JSON mode
// Caso 2: Extraer cláusulas de un contrato
// Target: <500ms para contratos <8000 tokens
interface ClausulasContrato {
partes: { empleador: string; empleado: string };
duracion: string;
salario_bruto_anual: number;
clausulas_criticas: string[];
riesgos_detectados: string[];
}
export async function extraerClausulas(
textoContrato: string
): Promise<ClausulasContrato> {
const resp = await cerebras.chat.completions.create({
model: MODELS.CONTEXT, // 128K ctx para contratos largos
messages: [{
role: "user",
content: `Extrae en JSON las cláusulas del contrato:\n\n${textoContrato}`
}],
response_format: { type: "json_object" }, // salida garantizada
temperature: 0, // determinístico para extracción
});
return JSON.parse(
resp.choices[0].message.content!
);
}
// Ejemplo de output real para contrato laboralDemo
// {
// partes: { empleador: "FirmaJurídicaMadrid SL", empleado: "Juan García" },
// duracion: "indefinido",
// salario_bruto_anual: 38000,
// clausulas_criticas: ["No competencia 2 años", "Confidencialidad perpetua"],
// riesgos_detectados: ["Cláusula no-competencia puede ser nula (ET art.21)"]
// }
// Target: <500ms para contratos <8000 tokens
interface ClausulasContrato {
partes: { empleador: string; empleado: string };
duracion: string;
salario_bruto_anual: number;
clausulas_criticas: string[];
riesgos_detectados: string[];
}
export async function extraerClausulas(
textoContrato: string
): Promise<ClausulasContrato> {
const resp = await cerebras.chat.completions.create({
model: MODELS.CONTEXT, // 128K ctx para contratos largos
messages: [{
role: "user",
content: `Extrae en JSON las cláusulas del contrato:\n\n${textoContrato}`
}],
response_format: { type: "json_object" }, // salida garantizada
temperature: 0, // determinístico para extracción
});
return JSON.parse(
resp.choices[0].message.content!
);
}
// Ejemplo de output real para contrato laboralDemo
// {
// partes: { empleador: "FirmaJurídicaMadrid SL", empleado: "Juan García" },
// duracion: "indefinido",
// salario_bruto_anual: 38000,
// clausulas_criticas: ["No competencia 2 años", "Confidencialidad perpetua"],
// riesgos_detectados: ["Cláusula no-competencia puede ser nula (ET art.21)"]
// }
Clasificación de Urgencia (8B ultrarrápido)
Python
JSON mode
# Caso 3: Clasificar urgencia de consultas entrantes
# Usa llama3.1-8b — ~2500 tok/s, total <200ms
from openai import OpenAI
import json, os
client = OpenAI(
api_key=os.environ["CEREBRAS_API_KEY"],
base_url="https://api.cerebras.ai/v1"
)
def clasificar_urgencia(consulta: str) -> dict:
resp = client.chat.completions.create(
model="llama3.1-8b", # 8B suficiente para clasificación
messages=[{
"role": "user",
"content": f"""Clasifica en JSON la consulta legal:
urgencia: "alta"|"media"|"baja"
area: "laboral"|"contratos"|"mercantil"|"otro"
requiere_abogado: true|false
Consulta: {consulta}"""
}],
response_format={"type": "json_object"},
max_tokens=80, # clasificación corta
temperature=0
)
return json.loads(resp.choices[0].message.content)
# Test real:
# clasificar_urgencia("Me han despedido sin carta y sin preaviso")
# → {"urgencia":"alta","area":"laboral","requiere_abogado":true}
# Tiempo total: ~180ms ✓
# Usa llama3.1-8b — ~2500 tok/s, total <200ms
from openai import OpenAI
import json, os
client = OpenAI(
api_key=os.environ["CEREBRAS_API_KEY"],
base_url="https://api.cerebras.ai/v1"
)
def clasificar_urgencia(consulta: str) -> dict:
resp = client.chat.completions.create(
model="llama3.1-8b", # 8B suficiente para clasificación
messages=[{
"role": "user",
"content": f"""Clasifica en JSON la consulta legal:
urgencia: "alta"|"media"|"baja"
area: "laboral"|"contratos"|"mercantil"|"otro"
requiere_abogado: true|false
Consulta: {consulta}"""
}],
response_format={"type": "json_object"},
max_tokens=80, # clasificación corta
temperature=0
)
return json.loads(resp.choices[0].message.content)
# Test real:
# clasificar_urgencia("Me han despedido sin carta y sin preaviso")
# → {"urgencia":"alta","area":"laboral","requiere_abogado":true}
# Tiempo total: ~180ms ✓
Function Calling — Búsqueda de Jurisprudencia
TypeScript
Tool calling
// Caso 4: Tool calling para buscar jurisprudencia en BD interna
const TOOLS: OpenAI.Tool[] = [{
type: "function",
function: {
name: "buscar_jurisprudencia",
description: "Busca sentencias del TS/AN sobre un tema",
parameters: {
type: "object",
properties: {
keywords: { type: "string" },
tribunal: { type: "string", enum: ["TS","AN","TSJ"] },
anyo_desde: { type: "number" },
},
required: ["keywords"]
}
}
}];
export async function consultarConJurisprudencia(query: string) {
const r1 = await cerebras.chat.completions.create({
model: MODELS.SMART,
messages: [{ role: "user", content: query }],
tools: TOOLS, tool_choice: "auto"
});
const msg = r1.choices[0].message;
if (!msg.tool_calls) return msg.content;
// Ejecutar búsqueda real en BD PostgreSQL
const args = JSON.parse(msg.tool_calls[0].function.arguments);
const sentencias = await buscarEnBD(args);
// Respuesta final con contexto de jurisprudencia
const r2 = await cerebras.chat.completions.create({
model: MODELS.SMART,
messages: [
{ role: "user", content: query }, msg,
{ role: "tool", tool_call_id: msg.tool_calls[0].id,
content: JSON.stringify(sentencias) }
],
});
return r2.choices[0].message.content;
}
const TOOLS: OpenAI.Tool[] = [{
type: "function",
function: {
name: "buscar_jurisprudencia",
description: "Busca sentencias del TS/AN sobre un tema",
parameters: {
type: "object",
properties: {
keywords: { type: "string" },
tribunal: { type: "string", enum: ["TS","AN","TSJ"] },
anyo_desde: { type: "number" },
},
required: ["keywords"]
}
}
}];
export async function consultarConJurisprudencia(query: string) {
const r1 = await cerebras.chat.completions.create({
model: MODELS.SMART,
messages: [{ role: "user", content: query }],
tools: TOOLS, tool_choice: "auto"
});
const msg = r1.choices[0].message;
if (!msg.tool_calls) return msg.content;
// Ejecutar búsqueda real en BD PostgreSQL
const args = JSON.parse(msg.tool_calls[0].function.arguments);
const sentencias = await buscarEnBD(args);
// Respuesta final con contexto de jurisprudencia
const r2 = await cerebras.chat.completions.create({
model: MODELS.SMART,
messages: [
{ role: "user", content: query }, msg,
{ role: "tool", tool_call_id: msg.tool_calls[0].id,
content: JSON.stringify(sentencias) }
],
});
return r2.choices[0].message.content;
}
03 · Comparativa de Velocidad
Cerebras vs Competencia (modelos 70B)
| Proveedor | Modelo | Velocidad | Rendimiento visual |
|---|---|---|---|
| Cerebras | llama3.3-70b | 2.000+ tok/s | |
| Groq | llama3.3-70b | 350 tok/s | |
| OpenAI | GPT-4o | 60 tok/s | |
| GPU Cloud | A100 autohospedado | 65 tok/s |
Selección de Modelo para LexIA
llama3.3-70b
Consultas legales complejas · redacción · análisis
→ Chat principal, tool calling
2.000
tok/seg
llama3.1-8b
Clasificación de urgencia · extracción simple
→ Routing, clasificación, triage
2.500
tok/seg
llama3.1-70b
Contratos largos · contexto 128K tokens
→ Extracción cláusulas, contratos extensos
1.800
tok/seg
04 · Casos de Uso LexIA + Buenas Prácticas
Casos de uso implementados
1
Chat legal con streaming en tiempo real
El usuario escribe su consulta y ve la respuesta aparecer token a token, sin esperar. Reduce abandono en un 60% respecto a batch.
Primer token <200ms ✓
2
Extracción estructurada de cláusulas
El despacho sube un contrato PDF → texto extraído → LLM devuelve JSON tipado con partes, duración, riesgos. Integrado con Prisma.
Total extracción <500ms ✓
3
Clasificador de urgencia (8B ultrarrápido)
Cada consulta entrante se clasifica en <200ms: urgencia, área jurídica, si requiere abogado. Enruta al profesional correcto automáticamente.
Clasificación <200ms ✓
4
Búsqueda de jurisprudencia vía function calling
El LLM detecta cuándo buscar sentencias del TS/AN en la BD PostgreSQL interna y construye la respuesta con citas reales verificables.
2 llamadas en cascada, <1.5s ✓
Guía de buenas prácticas
Siempre streaming — Primera respuesta visible en <200ms; crucial para percepción de velocidad en UX legal donde el usuario espera al abogado.
8B para clasificación, 70B para razonamiento — No uses el modelo grande para tareas simples como routing. El 8B es suficiente y más rápido.
JSON mode para extracción — Usa
response_format: "json_object" + temperature 0 para parsing fiable sin guardrails extra.Fallback a Groq — Si Cerebras devuelve 429 (rate limit), redirige a Groq. Sigue siendo 5-6x más rápido que OpenAI para consultas urgentes.
Temperatura baja para derecho — Usa 0-0.3 en contextos legales para reducir alucinaciones en citas de artículos y sentencias.
Paralelizar consultas independientes — Si hay múltiples preguntas en un batch (ej. análisis nocturno de contratos), lanza en paralelo con Promise.all().
Monitorizar X-Ratelimit headers — El tier gratuito tiene límites por minuto. Implementa back-off exponencial y alertas proactivas antes de saturar.