Ir al contenido

IDP Ground

Tipo: idp-ground  ·  Paquete: IDP Activities v1.18.0  ·  Salida: object

Coge lo que devolvió AI Extract y busca cada valor de vuelta en el documento. De ahí salen tres cosas: la caja donde está, una confianza medida (no la que el modelo diga que tiene), y la decisión de qué campos tiene que mirar una persona.

El esquema que hizo IDP Build Schema le pidió al modelo, por cada campo, el valor y el texto literal del que sale. Esta actividad busca ese literal entre las palabras del documento:

  • Lo encuentra → tiene caja, y la confianza es la del OCR de esas palabras.
  • No lo encuentra → el valor no se puede verificar. A revisión.
ParámetroEditorDescripción
documentexpresiónEl documento de IDP Load Document.
extractionexpresiónEl objeto de AI Extract.
taxonomyJSONLa misma taxonomía con la que se construyó el esquema.
CampoTipoQué es
documentTypestringEl tipo de documento de la taxonomía.
needsReviewbooleantrue si algún campo necesita revisión.
reviewFieldsListNombres de los campos que la necesitan.
valuesDictionaryNombre → valor. Para usar los datos directamente.
fieldsListUn objeto por campo, con todo el detalle.

Cada entrada de fields trae: name, type, value, valueRaw, grounded, confidence, needsReview, reason, page, x, y, width, height, rows y rowCount.

Un campo de tipo table trae sus filas en rows, y cada celda tiene exactamente la misma forma que un campo escalar — con su valor, su confianza, su caja y su motivo. En values, la tabla sale como una lista de filas limpias:

values["mantenimientos"] = [
{ "fecha": "2024-01-15", "tipo": "Preventivo", "tecnico": "Juan Pérez" },
{ "fecha": "2024-06-20", "tipo": "Correctivo", "tecnico": "Ana Gómez" }
]

La tabla necesita revisión si alguna de sus celdas la necesita.

Recorrer una tabla necesita un Foreach anidado:

Foreach items = = res["fields"] itemVariable = campo
If condition = = campo["type"] == "table"
Foreach items = = campo["rows"] itemVariable = fila
Set Variable name = celda value = = fila["fecha"]
Log message = = toString(celda["value"]) + " conf=" + toString(celda["confidence"])

Por qué la confianza no se le pregunta al modelo

Sección titulada «Por qué la confianza no se le pregunta al modelo»

Porque la confianza que un LLM se autoasigna no está calibrada: dice “0.95” con la misma soltura cuando acierta que cuando se lo inventa.

Aquí se mide algo comprobable: el literal está escrito en el documento o no lo está. Un valor que el modelo se inventó no aparece en ninguna parte, así que no se encuentra, así que va a revisión. Y de propina sale la caja para poder resaltarlo.

Por eso el esquema pide valueRaw aparte de value: value puede venir normalizado (2026-07-15) mientras el documento dice 15 de julio de 2026. El literal es el que ancla; el valor es el que usas.

  • Se compara ignorando espacios y mayúsculas: el OCR parte y junta palabras de forma impredecible, y dónde caen los espacios no debería invalidar un acierto. Un literal puede abarcar varias palabras y la caja las une.
  • Se conserva la puntuación: en un número de serie, un guion de más o de menos es una diferencia real que merece ir a revisión.
  • La confianza de una caja de varias palabras es la del eslabón más débil: si una palabra del número de serie se leyó regular, el número de serie se leyó regular.

Valores que se repiten — la cita por línea

Sección titulada «Valores que se repiten — la cita por línea»

Encontrar el literal prueba que existe, no que esa ocurrencia es la que dio el valor. Una fecha de fabricación, una de instalación y una de última revisión pueden ser idénticas: buscar el valor pelado casaría siempre con la primera, y la caja señalaría el dato equivocado.

La solución es no recuperar la posición a posteriori, sino que el modelo la cite al extraer. El documento le llega con cada línea numerada:

[11] Fecha de fabricación: | 15/01/2024
[12] Fecha de instalación: | 15/01/2024
[13] Última revisión: | 15/01/2024

Y por cada campo, además del valor, el modelo devuelve el número de línea de la que lo sacó. El grounding entonces no busca: va directo a la caja de esa línea, y dentro de ella acota al valor. La fecha de la línea 12 y la de la 13 dejan de colisionar porque el modelo dijo “12”, no “15/01/2024”. Es lo que hacen por dentro los sistemas de document understanding cuando etiquetan — obtener la posición en el momento de leer, no adivinarla después —, pero sin entrenar un modelo por formato.

  • El modelo cita una línea válida → la caja sale de ahí, directa y sin ambigüedad.
  • El valor no casa dentro de la línea citada (el modelo la citó regular, o el valor se normalizó) → se usa la caja de la línea entera: la línea correcta, con menos precisión.
  • El modelo no cita → se recae en buscar el literal por el texto; un valor repetido que no se puede distinguir así va a revisión, en vez de señalar uno al azar.

En una tabla, cada celda cita su propia línea, así que las filas repetidas caen cada una en su sitio sin depender del orden.

El pipeline completo:

IDP Load Document path = = rutaDoc → output: doc
IDP Build Schema taxonomy = = asset("taxonomias/hoja-de-vida.json") → output: schema
AI Extract credential = = credential("anthropic-key")
prompt = = doc["text"]
schema = = schema
→ output: extraccion
IDP Ground document = = doc
extraction = = extraccion
taxonomy = = asset("taxonomias/hoja-de-vida.json")
→ output: res
If condition = = res["needsReview"]
Queue Add Item ...a la cola de validacion humana

Recorrer los campos para pintar las cajas encima de la página renderizada:

Foreach items = = res["fields"] itemVariable = campo
Log message = = campo["name"] + " = " + toString(campo["value"]) + " (conf " + toString(campo["confidence"]) + ")"

Usar un valor concreto — hace falta un salto porque el motor no encadena índices:

Set Variable name = vals value = = res["values"]
Log message = = "Serie: " + toString(vals["serie"])