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:
reason | O que escrever |
|---|---|
standalone_raster, curves, diagonals | Descreva a figura: tipo, o que mostra, eixos e unidades, valores notáveis, todo texto legível. |
no_text_layer | Transcreva a página literalmente. Este é o caminho de OCR; não há texto algum. |
dense_grid, stroke_grid | Uma tabela que o extrator não conseguiu estruturar. Reproduza-a como uma tabela Markdown. |
raster_grid | Uma 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
grepnos 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]).
| Formato | Unidade | Citar como | Arquivo por unidade |
|---|---|---|---|
| página | [p12] | pages/p012.md | |
| PowerPoint | slide | [s07] | pages/u007.md |
| Excel | planilha | [Sheet2] | pages/u002.md |
| Word | título nível 1 | [Budget assumptions] | pages/u003.md |
| imagem | arquivo 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.
| Formato | Ao que --inline pode se ancorar |
|---|---|
| PowerPoint | à própria linha da imagem, quando essa linha é comprovadamente da imagem; caso contrário, ao final do slide |
| Word | ao 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 |
| Excel | ao final da planilha — imagens e gráficos vêm do pacote, e anydoc não renderiza nenhum |
| PDF, imagem | nada. --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:
| Filtro | Por quê |
|---|---|
| Descartar imagens em >50% das unidades, <120px, proporção >8:1 | Um 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 Markdown | O 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ções | Sublinhados 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 área | 4+ 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 densa | Separa uma tabela sombreada que o extrator perdeu de faixas decorativas de seção. |
| Colapsar páginas com >6 rasters em uma renderização | Uma 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 OLE | Retidos 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.