Communitygithub.com

LPSlv/doc-extract

Habilidad de agente para leer PDFs correctamente: extracción de texto local rápida, más visión solo en lo que realmente falló: gráficos, páginas escaneadas y tablas que no pudo estructurar.

¿Qué es doc-extract?

Esta habilidad para agentes de IA (como Claude Code, Cursor, o Codex) resuelve el problema de leer documentos complejos de forma eficiente. En lugar de enviar cada página a un modelo de visión (costoso y lento), utiliza motores locales rápidos para extraer texto de la mayoría de los documentos. Luego, invoca la visión solo en las partes donde el motor falló: figuras, páginas escaneadas sin texto, y tablas que no pudo estructurar. Soporta archivos PDF, DOCX, XLSX, PPTX e imágenes. La salida es un archivo Markdown por documento, con páginas individuales listas para ser citadas. El proceso se divide en tres pasos: conversión (extracción local + detección de pendientes), descripción de pendientes (con visión, a través de `describe.py`), y respuesta final. Tiene salvaguardas para evitar costos excesivos (como un "scale guard" que pide permiso antes de muchas llamadas) y una regla de oro: nunca editar `doc.md` manualmente; las descripciones se agregan mediante `describe.py` para garantizar la integridad del benchmark. Este skill es ideal para desarrolladores y usuarios avanzados que necesitan extraer información de informes, contratos, diapositivas y hojas de cálculo de manera precisa y económica.

Compatible con✓Claude Code~Codex CLI~Cursor
npx skills add LPSlv/doc-extract

Preguntar en tu IA favorita

Abre un nuevo chat con esta habilidad de agente ya precargada.

Documentación

¿Qué hace doc-extract?

La extracción de texto maneja la mayoría de los documentos por sí sola. Esta habilidad utiliza motores locales rápidos para eso, y luego utiliza llamadas de visión solo en lo que realmente falló: figuras, páginas escaneadas y tablas que no pudo estructurar.

Lee archivos .pdf, .docx, .xlsx, .pptx e imágenes. El formato se decide por el contenido, no por la extensión.

Cuándo NO usar esto

  • Fusionar, dividir, rellenar formularios, encriptación → anthropics/skills@pdf.
  • Un documento del que necesitas un número y ya sabes la página → solo léelo.

Requiere uv. Nada más; las dependencias se resuelven en la primera ejecución.

1. Convertir

uv run <ruta-del-skill>/convert.py ARCHIVO [MÁS ...]
uv run <ruta-del-skill>/convert.py ARCHIVO --inline    # descripciones en la posición de la imagen

Un objeto JSON por documento, en stdout. Todo lo determinista ya está hecho: texto extraído, imágenes escritas, artefacto en caché. El código de salida es distinto de cero si algún documento falló.

{"status":"ok","artifact":"/home/…/<sha>-<tag>","cached":false,
 "doc_md":"…/doc.md","pages_dir":"…/pages","manifest":"…/manifest.json",
 "pending":[{"id":"p007-render","page":7,"kind":"page_render",
             "reason":"dense_grid","path":"…/images/p007-render.png"}],
 "dropped":6,"over_scale_guard":false,"scale_guard":15}

Revisa status primero. encrypted, unreadable y unsupported son terminales: informa y continúa; el lote sigue. Volver a ejecutar el mismo archivo devuelve cached: true al instante y no cuesta nada.

Si over_scale_guard es true, dile al usuario cuántas llamadas requiere y obtén su acuerdo antes de continuar. Un escaneo de 84 páginas son 84 llamadas, y lo mismo para una presentación de 40 diapositivas con imágenes.

2. Mirar cada elemento pendiente

pending está vacío → has terminado, ve al paso 3.

De lo contrario, para cada entrada: lee el archivo en path, luego escribe lo que viste:

uv run <ruta-del-skill>/describe.py <artefacto> <id> "tu descripción"
# o canaliza una larga: … describe.py <artefacto> <id> -

Cómo escribir la descripción depende de reason — consulta reference/describing-visuals.md:

reasonQué escribir
standalone_raster, curves, diagonalsDescribe la figura: tipo, qué muestra, ejes y unidades, valores notables, todo el texto legible.
no_text_layerTranscribe la página textualmente. Esta es la ruta OCR; no hay texto en absoluto.
dense_grid, stroke_gridUna tabla que el extractor no pudo estructurar. Reproducela como una tabla Markdown.
raster_gridUna página con mosaico de muchas imágenes — una figura compuesta, o una foto almacenada en tiras. Describe qué muestran los paneles en conjunto, luego cualquier cosa notable por panel.

describe.py es seguro de re-ejecutar: reemplaza en lugar de duplicar, por lo que una pasada de visión que se interrumpe a la mitad se puede reanudar.

Los elementos con kind: "native_chart" nunca aparecen en pending. Esos son gráficos de hoja de cálculo cuyas series se leyeron de la definición del gráfico, por lo que sus números son exactos en lugar de estimados a partir de píxeles.

3. Responder

  • Documento completo: doc.md.
  • Una pregunta específica: busca en los archivos por unidad y lee solo los que necesites.
  • Cita por unidad, o prefija el nombre del archivo en varios documentos ([report.pdf:p12]).
FormatoUnidadCitar comoArchivo por unidad
PDFpágina[p12]pages/p012.md
PowerPointdiapositiva[s07]pages/u007.md
Excelhoja[Sheet2]pages/u002.md
Wordencabezado nivel 1[Supuestos del presupuesto]pages/u003.md
imagenarchivo completo[img]pages/u001.md

Los archivos de unidad de Office están numerados en lugar de tener nombre, porque una hoja llamada P&L Q1 / borrador no es un nombre de archivo. manifest.json contiene una lista units que mapea cada archivo a su etiqueta, en orden.

La regla de oro

Nunca edites doc.md manualmente. Todo lo que agregues pasa por describe.py, que lo envuelve en delimitadores. Esto permite que el benchmark elimine las adiciones y demuestre que la habilidad no degrada la extracción de texto. Editar en el lugar rompe esa garantía silenciosamente.

Dónde aterrizan las descripciones, y la única cosa que --inline no promete

Por defecto, cada descripción se adjunta en un bloque al final de doc.md, etiquetada como [s02], [p12] o [Sheet2]. --inline coloca cada bloque en la posición de su imagen. Pásalo en el momento de convertir, no a describe.py: la ubicación se registra en el artefacto (y en su clave de caché), por lo que una pasada de visión reanudada no puede mezclar los dos.

FormatoA qué se puede anclar --inline
PowerPointla propia línea de la imagen, cuando esa línea es comprobablemente de la imagen; de lo contrario, el final de la diapositiva
Wordel final de la sección donde se encuentra la imagen — anydoc renderiza una imagen como su texto alternativo, y Word no escribe ninguno a menos que el autor lo haya tecleado, por lo que generalmente no hay una línea a la que anclarse
Excelel final de la hoja — las imágenes y gráficos provienen del paquete, y anydoc no renderiza ninguno
PDF, imagennada. --inline no cambia nada para un PDF

El bloque se inserta junto a la línea del motor, nunca se sustituye por ella, por lo que la identidad de bytes se mantiene — eval/gate.py ejecuta ambas ubicaciones y falla si hay alguna edición en el lugar.

Lo que --inline no promete es que la posición sea correcta; la identidad de bytes no puede verificar la ubicación, porque una inserción viaja a donde sea que caiga. El ancla es el texto alternativo de anydoc, que es prosa ordinaria: una diapositiva sobre un archivo llamado image.png renderiza la misma línea que una imagen. Por lo tanto, se usa la propia línea de la imagen solo cuando el número de líneas candidatas en esa unidad es igual al número de imágenes en ella, y la descripción retrocede al límite de la unidad en caso contrario. Las posiciones son comprobablemente de esa imagen, o un límite de unidad — nunca una suposición entre dos líneas que se parecen.

Por qué los filtros son como son

No "simplifiques" estos filtros — cada uno existe porque la alternativa obvia fue probada y falló con documentos reales:

FiltroPor qué
Descartar imágenes en >50% de unidades, <120px, relación de aspecto >8:1Un PDF de subvención de 14 páginas tenía 69 ubicaciones de imagen: 7 objetos distintos, seis de ellos logotipos, reglas y una franja lateral.
Saltar páginas que ya produjeron una tabla MarkdownEl extractor es mejor en tablas que puede analizar que la visión. Solo intervén donde no produjo nada.
Requerir trazos en ambas orientacionesLos subrayados y las reglas son solo horizontales. Contar todos los trazos alineados a los ejes se disparó en páginas de bibliografía y en contratos con encabezados subrayados.
Eximir diagonals del umbral de área4+ segmentos diagonales no ocurren en el texto del cuerpo, y el umbral vetaba gráficos colocados en una esquina de la página.
Umbral de tinta en la rama de cuadrícula densaSepara una tabla sombreada que el extractor no detectó de los banners decorativos de sección.
Colapsar páginas con >6 rásters en una sola representaciónUna comparación de 48 mosaicos de inpainting es una sola figura, y una foto de paquete TI llega como 12 tiras; las llamadas por mosaico cuestan ~2× los tokens y pierden la composición. Las páginas con 5–6 rásters son a veces figuras distintas, por lo que el límite está en 6.
Descartar cargas útiles EMF, WMF y OLERetenidas fielmente por el motor de texto, e ilegibles sin un rasterizador — enviar una a pending crearía un elemento que ningún agente puede completar. Contadas en dropped, nunca descartadas silenciosamente.

harvest.py es la única fuente de verdad para el enrutamiento de PDF, y filters.py para las partes que aplican a todos los formatos. Cada número en el README y la especificación de diseño se regenera a partir de ellos; nunca edites un número manualmente. Si no puedes ejecutar un archivo, reference/harvest-block.md es una copia autocontenida para copiar y ejecutar.

El enrutamiento de Office es deliberadamente más ligero, y eso es honesto

Una página PDF es un programa de comandos de dibujo que oculta sus figuras, por lo que harvest.py las infiere de la geometría vectorial. OOXML declara sus imágenes en el paquete, por lo que no hay nada que inferir. Los documentos de Office reciben los filtros de mobiliario, el contrato de cita y caché, y esta rúbrica — pero sin render_reason, sin raster_grid y sin cost_guard, porque sin un motor de representación no hay diapositiva ni hoja que representar.

Skills relacionados