Communitygithub.com

LPSlv/doc-extract

Habilidade de agente para leitura correta de PDFs: extração de texto local rápida, mais uso de visão apenas no que comprovadamente foi perdido — gráficos, páginas escaneadas e tabelas que não conseguiu estruturar.

O que é doc-extract?

Esta habilidade (skill) para agentes de IA resolve o problema comum de extração imprecisa de documentos, especialmente PDFs. Ela combina a velocidade da extração local de texto com chamadas estratégicas de visão computacional (como Claude Vision) apenas para os elementos que a extração inicial não conseguiu processar: figuras, páginas escaneadas e tabelas complexas. O skill é compatível com Claude Code, Cursor e Codex, exigindo apenas a ferramenta `uv` para execução. Seu fluxo de trabalho é dividido em três etapas: (1) conversão do documento para um artefato cacheado, extraindo todo o texto possível e identificando itens 'pendentes' que precisam de análise visual; (2) para cada item pendente, o agente usa uma ferramenta de descrição para registrar o que a visão observou; (3) responde a perguntas com base no documento completo. A habilidade prioriza eficiência, evitando chamadas de visão desnecessárias e garantindo que o texto base nunca seja alterado manualmente, preservando a integridade dos dados originais. Suporta formatos como PDF, DOCX, XLSX, PPTX e imagens, e oferece opções de formatação inline para as descrições visuais.

Funciona com✓Claude Code~Codex CLI~Cursor
npx skills add LPSlv/doc-extract

Perguntar na sua IA favorita

Abre um novo chat com esta habilidade de agente já pré-carregada.

Documentação

O que doc-extract faz?

A extração de texto lida com a maioria dos documentos sozinha. Esta habilidade usa motores locais rápidos para isso, depois gasta chamadas de visão apenas no que comprovadamente perdeu — figuras, páginas escaneadas e tabelas que não conseguiu estruturar.

Lê arquivos .pdf, .docx, .xlsx, .pptx e imagens. O formato é decidido pelo conteúdo, não pela extensão.

Quando NÃO usar

  • Mesclagem, divisão, preenchimento de formulários, criptografia → anthropics/skills@pdf.
  • Um documento do qual você precisa de um número e já sabe a página → apenas leia.

Requer uv. Nada mais; as dependências são resolvidas na primeira execução.

1. Converter

uv run <skill-dir>/convert.py ARQUIVO [MAIS ...]
uv run <skill-dir>/convert.py ARQUIVO --inline    # descrições na posição da imagem

Um objeto JSON por documento, na saída padrão. Tudo que é determinístico já foi feito: texto extraído, imagens escritas, artefato em cache. O código de saída é diferente de zero se algum documento falhou.

{"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}

Verifique status primeiro. encrypted, unreadable e unsupported são terminais — relate e siga adiante; o lote continua. Reexecutar o mesmo arquivo retorna cached: true instantaneamente e não custa nada.

Se over_scale_guard for true, informe ao usuário quantas chamadas são necessárias e obtenha concordância antes de continuar. Uma digitalização de 84 páginas são 84 chamadas, assim como uma apresentação de 40 imagens.

2. Olhar cada item pendente

pending está vazio → você terminou, vá para o passo 3.

Caso contrário, para cada entrada: leia o arquivo em path, depois escreva o que viu:

uv run <skill-dir>/describe.py <artifact> <id> "sua descrição"
# ou envie uma longa:  … describe.py <artifact> <id> -

Como escrever a descrição depende de reason — veja reference/describing-visuals.md:

reasonO que escrever
standalone_raster, curves, diagonalsDescreva a figura: tipo, o que mostra, eixos e unidades, valores notáveis, todo texto legível.
no_text_layerTranscreva a página literalmente. Este é o caminho de OCR; não há texto algum.
dense_grid, stroke_gridUma tabela que o extrator não conseguiu estruturar. Reproduza-a como uma tabela Markdown.
raster_gridUma página lado a lado com muitas imagens — uma figura composta, ou uma foto armazenada em tiras. Descreva o que os painéis mostram juntos, depois algo notável por painel.

describe.py é seguro para reexecutar — substitui em vez de duplicar, então uma passagem de visão que morre no meio pode ser retomada.

Itens com kind: "native_chart" nunca aparecem em pending. Esses são gráficos de planilha cujas séries foram lidas da definição do gráfico, portanto seus números são exatos em vez de estimados a partir de pixels.

3. Responder

  • Documento completo: doc.md.
  • Uma pergunta específica: use grep nos arquivos por unidade e leia apenas os que precisar.
  • Cite por unidade, ou prefixe o nome do arquivo em vários documentos ([report.pdf:p12]).
FormatoUnidadeCitar comoArquivo por unidade
PDFpágina[p12]pages/p012.md
PowerPointslide[s07]pages/u007.md
Excelplanilha[Sheet2]pages/u002.md
Wordtítulo nível 1[Budget assumptions]pages/u003.md
imagemarquivo inteiro[img]pages/u001.md

Os arquivos de unidade do Office são numerados em vez de nomeados, porque uma planilha chamada Q1 P&L / draft não é um nome de arquivo. manifest.json contém uma lista units mapeando cada arquivo ao seu rótulo, em ordem.

A única regra

Nunca edite doc.md manualmente. Tudo que você adiciona passa por describe.py, que o envolve em delimitadores. É isso que permite ao benchmark remover as adições e provar que a habilidade não degrada a extração de texto. Editar no lugar quebra essa garantia silenciosamente.

Onde as descrições caem, e a única coisa que --inline não promete

Por padrão, cada descrição é anexada em um bloco ao final de doc.md, rotulado como [s02], [p12] ou [Sheet2]. --inline coloca cada bloco na posição de sua imagem. Passe-o no momento da conversão, não para describe.py: a posição é registrada no artefato (e em sua chave de cache), então uma passagem de visão retomada não pode misturar os dois.

FormatoAo que --inline pode se ancorar
PowerPointà própria linha da imagem, quando essa linha é comprovadamente da imagem; caso contrário, ao final do slide
Wordao final da seção onde a imagem está — anydoc renderiza uma imagem como seu texto alternativo, e o Word não escreve nenhum a menos que o autor tenha digitado um, então geralmente não há linha para ancorar
Excelao final da planilha — imagens e gráficos vêm do pacote, e anydoc não renderiza nenhum
PDF, imagemnada. --inline não muda nada para PDF

O bloco é inserido ao lado da linha do motor, nunca substituído por ela, é por isso que a identidade de bytes sobrevive — eval/gate.py executa ambas as colocações e falha em qualquer edição no lugar.

O que inline não promete é que a posição está correta; a identidade de bytes não pode verificar a colocação, porque uma inserção vai para onde quer que caia. A âncora é o texto alternativo do anydoc, que é prosa comum: um slide sobre um arquivo chamado image.png renderiza a mesma linha que uma imagem. Portanto, a própria linha de uma imagem é usada apenas quando o número de linhas candidatas naquela unidade é igual ao número de imagens nela, e a descrição recai para o limite da unidade caso contrário. As posições são comprovadamente daquela imagem, ou um limite de unidade — nunca um palpite entre duas linhas que parecem iguais.

Por que os filtros são o que são

Não "simplifique" estes — cada um existe porque a alternativa óbvia foi testada e falhou em documentos reais:

FiltroPor quê
Descartar imagens em >50% das unidades, <120px, proporção >8:1Um PDF de subsídio de 14 páginas tinha 69 posições de imagem: 7 objetos distintos, seis deles logotipos, linhas e uma faixa lateral.
Pular páginas que já produziram uma tabela MarkdownO extrator é melhor em tabelas que consegue analisar do que a visão. Intervenha apenas onde não produziu nada.
Exigir traços em ambas as orientaçõesSublinhados e regras são apenas horizontais. Contar todos os traços alinhados aos eixos disparou em páginas de bibliografia e em contratos com títulos sublinhados.
Isentar diagonals do limite de área4+ segmentos diagonais não ocorrem em texto corrido, e o limite estava vetando gráficos colocados no canto de uma página.
Limiar de tinta no ramo de grade densaSepara uma tabela sombreada que o extrator perdeu de faixas decorativas de seção.
Colapsar páginas com >6 rasters em uma renderizaçãoUma comparação de 48 blocos de inpaint é uma figura, e uma foto de pacote TI chega como 12 tiras; chamadas por bloco custam ~2× os tokens e perdem a composição. Páginas com 5–6 rasters são às vezes figuras distintas, então o limite está em 6.
Descartar payloads EMF, WMF e OLERetidos fielmente pelo motor de texto, e ilegíveis sem um rasterizador — encaminhar um para pending criaria um item que nenhum agente pode concluir. Contados em dropped, nunca descartados silenciosamente.

harvest.py é a fonte única da verdade para roteamento de PDF, e filters.py para as partes que se aplicam a todos os formatos. Cada número no README e especificação de design é regenerado a partir deles; nunca edite um número manualmente. Se você não puder executar um arquivo, reference/harvest-block.md é uma cópia autocontida para colar e executar.

O roteamento do Office é deliberadamente mais enxuto, e isso é honesto

Uma página de PDF é um programa de comandos de desenho que esconde suas figuras, e é por isso que harvest.py as infere a partir da geometria vetorial. OOXML declara suas imagens no pacote, então não há nada a inferir. Documentos do Office recebem os filtros mobiliários, o contrato de citação e cache e esta rubrica — mas nenhum render_reason, nem raster_grid nem cost_guard, porque sem um motor de renderização não há slide ou planilha para renderizar.

Habilidades Relacionadas