Un montón de mensajes
Una cadena de texto a registros convierte mensajes desordenados en registros comprobados que un programa puede guardar. Importa porque cada paso que has visto es pequeño y un trabajo real los necesita todos juntos.
La oficina de un instituto recibe un montón de peticiones de material en texto libre. Alguien las quiere como filas: el artículo, cuántos y si es urgente. Nadie quiere teclear trescientas.
Al final de esta lección
Nota
Puedo montar una pequeña cadena que lleve de texto desordenado a registros comprobados.
Palabras clave
| Palabra | Qué significa |
|---|---|
| Cadena | pasos que se pasan cada uno su resultado |
| Lote | un grupo de textos tratados juntos |
| Rechazar | dejar un texto fuera, con una razón |
| Tasa de rechazo | la parte de textos que se rechazaron |
| Guardar | escribir los registros comprobados en algún sitio |
| Datos personales | datos que identifican a una persona |
Las celdas que rellenan los estudiantes quedan en blanco en esta vista previa.
Cuatro pasos, uno tras otro
Cada mensaje pasa por los mismos pasos. Extraer: pedir los campos al modelo. Validar: comprobar el registro con el esquema. Reparar: si falla, preguntar una vez más con el fallo. Guardar: escribir el buen registro.
Un mensaje que sigue fallando se rechaza. No se guarda y tampoco se pierde: conservas el texto y la razón.
Cuenta los rechazos y di por qué
Un rechazo es información. Conserva el texto y la razón, y calcula la tasa de rechazo. Si es alta, la instrucción o el esquema necesitan trabajo.
Ten cuidado con los datos personales. Envía al modelo solo el texto que necesita. Escribe en el registro la razón, no el texto de un mensaje sobre una persona.
Escribir la ayuda del modelo, parte 1
docker exec -w /home/node/project agent sh -c "printf '%s\n' 'const base = process.env.AI_URL || \"http://ai:4000\";' 'async function ask(messages, sample, max) {' ' try {' ' const res = await fetch(base + \"/v1/chat/completions\", {' ' method: \"POST\",' ' headers: { \"content-type\": \"application/json\", authorization: \"Bearer lab\" },' ' body: JSON.stringify({ model: \"lab\", messages: messages, max_tokens: max || 200 }),' ' signal: AbortSignal.timeout(30000),' ' });' ' if (res.ok === false) throw new Error(\"estado \" + res.status);' ' return (await res.json()).choices[0].message.content;' > model.js"Se muestra aquí y no se ejecuta. Crea una cuenta gratuita para pulsar Ejecutar y escribirlo en el terminal de tu propio laboratorio.
Escribir la ayuda del modelo, parte 2
docker exec -w /home/node/project agent sh -c "printf '%s\n' ' } catch (error) {' ' console.log(\"Muestra grabada: ningún modelo respondió (\" + error.message + \").\");' ' const list = [].concat(sample);' ' const turn = messages.filter(function (m) { return m.role === \"assistant\"; }).length;' ' return list[Math.min(turn, list.length - 1)];' ' }' '}' 'module.exports = { ask };' >> model.js"Se muestra aquí y no se ejecuta. Crea una cuenta gratuita para pulsar Ejecutar y escribirlo en el terminal de tu propio laboratorio.
Escribir la cadena, parte 1
docker exec -w /home/node/project agent sh -c "printf '%s\n' 'const fs = require(\"fs\");' 'const { ask } = require(\"./model\");' '' 'const texts = [' ' \"Por favor, enviad 12 lápices al aula de plástica, no es urgente.\",' ' \"Necesitamos 3 cajas de tizas hoy, cuanto antes.\",' ' \"¿Alguien me puede decir dónde está la sala de profesores?\",' '];' '// Muestras grabadas, usadas solo si ningún modelo responde: una respuesta limpia, una que pide una' '// reparación y un mensaje sin pedido, que se rechaza.' 'const samples = [' ' JSON.stringify({ item: \"lápices\", quantity: 12, urgent: false }),' ' [\"Aquí tienes: \" + JSON.stringify({ item: \"tizas\", quantity: \"tres\", urgent: true }), JSON.stringify({ item: \"tizas\", quantity: 3, urgent: true })],' ' [JSON.stringify({ item: null, quantity: null, urgent: false }), \"No encuentro ningún pedido en este mensaje.\"],' '];' > pipeline6.js"Se muestra aquí y no se ejecuta. Crea una cuenta gratuita para pulsar Ejecutar y escribirlo en el terminal de tu propio laboratorio.
Escribir la cadena, parte 2
docker exec -w /home/node/project agent sh -c "printf '%s\n' 'function pullJson(text) {' ' const start = text.indexOf(\"{\");' ' const end = text.lastIndexOf(\"}\");' ' if (start === -1 || end < start) return null;' ' try { return JSON.parse(text.slice(start, end + 1)); } catch (error) { return null; }' '}' '' '// El primer problema, o un texto vacío cuando el registro es bueno.' 'function problemOf(record) {' ' if (record === null) return \"no es JSON\";' ' if ((typeof record.item === \"string\") === false || record.item === \"\") return \"item no valido\";' ' if (Number.isInteger(record.quantity) === false || record.quantity < 1 || record.quantity > 20) return \"quantity no valida\";' ' if ((typeof record.urgent === \"boolean\") === false) return \"urgent no valido\";' ' return \"\";' '}' >> pipeline6.js"Se muestra aquí y no se ejecuta. Crea una cuenta gratuita para pulsar Ejecutar y escribirlo en el terminal de tu propio laboratorio.
Escribir la cadena, parte 3
docker exec -w /home/node/project agent sh -c "printf '%s\n' 'const PROMPT = \"Extrae item, quantity (de 1 a 20) y urgent (true o false) de este mensaje. Responde solo con el JSON.\n\nMensaje: \";' '' 'async function handle(text, sample) {' ' const messages = [{ role: \"user\", content: PROMPT + text }];' ' let reply = await ask(messages, sample, 80);' ' let problem = problemOf(pullJson(reply));' ' if (problem.length > 0) {' ' // Una reparación: devolver la mala respuesta y el problema, y preguntar una vez más.' ' messages.push({ role: \"assistant\", content: reply });' ' messages.push({ role: \"user\", content: \"Corrige este problema y responde solo con el JSON: \" + problem });' ' reply = await ask(messages, sample, 80);' ' problem = problemOf(pullJson(reply));' ' }' ' return { record: pullJson(reply), problem: problem };' '}' >> pipeline6.js"Se muestra aquí y no se ejecuta. Crea una cuenta gratuita para pulsar Ejecutar y escribirlo en el terminal de tu propio laboratorio.
Escribir la cadena, parte 4
docker exec -w /home/node/project agent sh -c "printf '%s\n' 'async function main() {' ' const saved = [];' ' const rejected = [];' ' for (let at = 0; at < texts.length; at = at + 1) {' ' const result = await handle(texts[at], samples[at]);' ' if (result.problem.length > 0) rejected.push({ text: texts[at], why: result.problem });' ' else saved.push({ item: result.record.item, quantity: result.record.quantity, urgent: result.record.urgent });' ' }' ' fs.writeFileSync(\"records.json\", JSON.stringify(saved, null, 2));' ' console.log(\"Guardados \" + saved.length + \" registros en records.json\");' ' for (const one of rejected) console.log(\"Rechazado: \" + one.text + \" (\" + one.why + \")\");' ' console.log(\"Tasa de rechazo: \" + Math.floor(rejected.length * 100 / texts.length) + \" por ciento\");' '}' 'main();' >> pipeline6.js"Se muestra aquí y no se ejecuta. Crea una cuenta gratuita para pulsar Ejecutar y escribirlo en el terminal de tu propio laboratorio.
Ejecutarla con tres mensajes
docker exec -w /home/node/project agent node pipeline6.jsSe muestra aquí y no se ejecuta. Crea una cuenta gratuita para pulsar Ejecutar y escribirlo en el terminal de tu propio laboratorio.
Una salida posible
| Línea | Lo que puede mostrar |
|---|---|
| Guardados | Guardados 2 registros en records.json, por ejemplo |
| Rechazado | El mensaje sobre la sala de profesores, con una razón como item no valido |
| Tasa de rechazo | Tasa de rechazo: 33 por ciento si se rechaza uno de los tres |
Las celdas que rellenan los estudiantes quedan en blanco en esta vista previa.
Un error frecuente
Advertencia
Mucha gente cree que un mensaje rechazado significa que la cadena falló. Rechazar es correcto cuando un mensaje no trae ningún pedido. Lo que falla es rechazar sin contar ni decir por qué.