Skip to content

Contexto de cliente

Tu aplicación puede mandar datos en cada mensaje para que las tools del agente los usen: la página en la que está el usuario, un número de pedido, el idioma de la interfaz.

El modelo nunca ve estos valores. Solo llegan a las tools que los hayan declarado, y cada una decide qué hacer con ellos. Eso significa que el contexto no es una vía de prompt injection: no se concatena al prompt en ningún momento.

No es una credencial

El canal público no tiene autenticación, así que cualquiera puede mandar los valores que quiera con un curl. Los datos que llegan son bien formados, no fiables.

Úsalos para decir qué mirar, nunca qué puede ver quien pregunta. Cualquier búsqueda que dependa de un valor del contexto tiene que cruzarse con una identidad verificada por tu propio backend: WHERE id = ctx.orderId AND owner = <sujeto verificado>.

La forma: agrupado por tool

El contexto no es un saco plano. Va agrupado por el nombre de la instancia de tool que lo va a consumir:

json
{
  "facturacion": { "orderId": "A-1024" },
  "visitor_context": { "page": "/precios", "locale": "es-ES" }
}

Así dos tools distintas pueden querer las dos una clave page sin pisarse, y cada una recibe únicamente su bloque.

Enviarlo

ts
const client = new UrAgentClient({
  agent: 'soporte',
  context: {
    visitor_context: { page: '/precios', locale: 'es-ES' },
  },
});

// Al navegar, sin recrear el cliente (que perdería la conversación):
client.setContext({ visitor_context: { page: '/contacto', locale: 'es-ES' } });
html
<script
  src="https://<tu-backend-ur>/widget.js"
  data-agent="soporte"
  data-context='{"visitor_context":{"page":"/precios"}}'
></script>
js
document.querySelector('ur-agent-chat').context = {
  visitor_context: { page: '/contacto' },
};

Qué claves acepta tu agente

No se configura en ninguna parte: lo declaran las tools del agente. Un agente sin ninguna tool que declare contexto no acepta ninguna clave.

Para saber cuáles son las de un agente concreto, el panel te lo dice: en la ficha de su canal encontrarás el snippet exacto, ya relleno con las claves disponibles, sus tipos y un comentario por campo. Cópialo de ahí en vez de adivinarlo.

Los cambios tardan hasta 30 segundos

El contrato se cachea. Si acabas de vincular o desvincular una tool, puede tardar medio minuto en reflejarse en lo que el endpoint acepta.

Qué pasa si mandas algo que no encaja

Aquí hay dos comportamientos muy distintos, y conviene no confundirlos.

Si el context entero no es válido → 400. Solo pasa si no es un objeto JSON o si ocupa más de 4 KB serializado:

json
{ "error": "context must be a JSON object no larger than 4KB" }

Si una clave concreta no encaja → se descarta en silencio y el mensaje sigue. No hay error. Los nombres descartados vuelven en la cabecera de respuesta X-Ur-Context-Dropped, para que los veas en las devtools.

Se descarta porque un 400 convertiría un endpoint público en un oráculo para averiguar qué tools tiene un agente, y rompería integraciones vivas en cuanto alguien desvincule una tool en el panel.

Un campo malo tira el bloque entero

La validación es por namespace, no por campo. Si mandas {"facturacion": {"orderId": "A-1", "typo": 123}} y typo no está en el contrato, se descarta facturacion entero — la tool no recibe tampoco el orderId que sí era válido.

Es la causa número uno de "le paso el contexto y la tool no lo ve". Mira X-Ur-Context-Dropped en la respuesta: si aparece ahí el namespace, es esto.

Acotar la búsqueda en tus documentos

La tool rag_retrieve puede aceptar contexto para restringir a qué documentos mira. Viene desactivado por defecto: hay que activarlo en la instancia de la tool, en el panel.

Con ello activo, acepta tres claves, todas opcionales:

ClaveLímiteQué hace
paths25 rutas, 1024 caracteresRutas exactas
pathPrefixes25 prefijos, 1024 caracteresToda una sección, p. ej. docs/facturacion
tags10 etiquetas, 128 caracteresEtiquetas del proveedor de ficheros
ts
client.setContext({
  documentacion: { pathPrefixes: ['docs/facturacion'] },
});

El contexto pone el techo; el modelo solo puede estrechar

El ámbito es una intersección: si el contexto limita a docs/facturacion, el modelo puede buscar dentro de esa sección o en una parte de ella, pero no puede ampliarlo. No existe forma de que el contexto abra documentos que el agente no tuviera ya — precisamente porque el valor es falsificable.

La contención es una consulta a la base de datos y un filtro en el índice vectorial, no una instrucción en el prompt, así que tampoco se puede saltar por prompt injection.

Las barras iniciales se normalizan, así que puedes pasar location.pathname tal cual aunque el corpus esté indexado sin ella.

Dónde no funciona

En el canal demo (el "Probar agente" del panel) el contexto se ignora por completo. Ese canal se salta las comprobaciones de canal y origen, así que aceptar contexto ahí sería la vía de escape. Para probar el contexto, hazlo desde tu integración real.

Escribir una tool que lo consuma

Si desarrollas tus propias tools sobre @nykk/ur-core, una plantilla declara su contrato con contextSchema (un esquema zod) y lo lee dentro del callback con getClientContext:

ts
import { getClientContext, type ToolTemplate } from '@nykk/ur-core';
import { z } from 'zod';

const visitorContextSchema = z.object({
  page: z.string().max(512).optional(),
  locale: z.string().max(32).optional(),
});

export const visitorContextTemplate: ToolTemplate = {
  // …
  contextSchema: visitorContextSchema,
  factory: (instance) =>
    tool(async (input, runConfig) => {
      const ctx = getClientContext<z.infer<typeof visitorContextSchema>>(
        runConfig,
        instance.name,
      );
      // `ctx` es `undefined` si no llegó nada o si no validó.
    }),
};

La tool tiene que funcionar sin contexto

getClientContext devuelve undefined cuando el cliente no mandó nada, cuando la plantilla no declara contrato, o cuando lo que mandó no validó. Una tool que dé por hecho que el contexto está ahí se romperá en cuanto alguien la llame desde otro canal.

Hay un ejemplo completo en el repositorio, en apps/web-server/src/tools/visitor-context.ts.