¿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:
reason | Qué escribir |
|---|---|
standalone_raster, curves, diagonals | Describe la figura: tipo, qué muestra, ejes y unidades, valores notables, todo el texto legible. |
no_text_layer | Transcribe la página textualmente. Esta es la ruta OCR; no hay texto en absoluto. |
dense_grid, stroke_grid | Una tabla que el extractor no pudo estructurar. Reproducela como una tabla Markdown. |
raster_grid | Una 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]).
| Formato | Unidad | Citar como | Archivo por unidad |
|---|---|---|---|
| página | [p12] | pages/p012.md | |
| PowerPoint | diapositiva | [s07] | pages/u007.md |
| Excel | hoja | [Sheet2] | pages/u002.md |
| Word | encabezado nivel 1 | [Supuestos del presupuesto] | pages/u003.md |
| imagen | archivo 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.
| Formato | A qué se puede anclar --inline |
|---|---|
| PowerPoint | la propia línea de la imagen, cuando esa línea es comprobablemente de la imagen; de lo contrario, el final de la diapositiva |
| Word | el 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 |
| Excel | el final de la hoja — las imágenes y gráficos provienen del paquete, y anydoc no renderiza ninguno |
| PDF, imagen | nada. --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:
| Filtro | Por qué |
|---|---|
| Descartar imágenes en >50% de unidades, <120px, relación de aspecto >8:1 | Un 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 Markdown | El extractor es mejor en tablas que puede analizar que la visión. Solo intervén donde no produjo nada. |
| Requerir trazos en ambas orientaciones | Los 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 área | 4+ 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 densa | Separa 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ón | Una 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 OLE | Retenidas 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.