doc-extract 是做什麼的?
文字擷取本身就能處理大部分文件。此技能使用快速的本機引擎進行擷取,然後僅針對確實遺漏的項目——圖片、掃描頁面及無法結構化的表格——使用視覺 API。
可讀取 .pdf、.docx、.xlsx、.pptx 及圖片檔案。格式由內容決定,而非副檔名。
何時不該使用此技能
- 合併、分割、填寫表單、加密 → 請使用
anthropics/skills@pdf。 - 如果你只需要從文件中找出某個數字,且已知它的頁碼 → 直接讀取即可。
需要 uv。除此之外無其他相依套件;依賴項會在首次執行時自動解析。
1. 轉換
uv run <skill-dir>/convert.py FILE [MORE ...]
uv run <skill-dir>/convert.py FILE --inline # 描述會放在圖片所在位置
每個文件輸出一個 JSON 物件至標準輸出。所有確定性的工作都已在此完成:文字已擷取、圖片已寫入、成品已快取。若有任何文件處理失敗,退出碼將非零。
{"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}
請先檢查 status。encrypted、unreadable 及 unsupported 為終端狀態——回報後繼續處理;批次作業會繼續執行。重新執行同一個檔案會立即回傳 cached: true,且不會耗費任何成本。
如果 over_scale_guard 為 true,請告知使用者需要發起多少次呼叫,並在繼續之前取得同意。一份 84 頁的掃描文件就需要 84 次呼叫,而一份 40 張圖片的簡報亦同。
2. 檢視每個待處理項目
如果 pending 為空 → 你已完成,請前往步驟 3。
否則,針對每個條目:讀取 path 路徑下的檔案,然後寫下你所看到的內容:
uv run <skill-dir>/describe.py <artifact> <id> "your description"
# 或透過管道輸入長內容: … describe.py <artifact> <id> -
如何撰寫描述取決於 reason 欄位——請參閱 reference/describing-visuals.md:
reason | 應寫入的內容 |
|---|---|
standalone_raster、curves、diagonals | 描述該圖表:類型、它所呈現的內容、座標軸及單位、值得注意的數值、所有可辨識的文字。 |
no_text_layer | 逐字轉錄該頁面。 這是 OCR 路徑;此頁面完全沒有任何文字。 |
dense_grid、stroke_grid | 擷取器無法結構化的表格。請將其重製為 Markdown 表格。 |
raster_grid | 由許多圖片拼貼而成的頁面——一個複合圖形,或是以條狀儲存的一張照片。請描述這些面板共同呈現的內容,然後再針對每個面板說明任何值得注意之處。 |
describe.py 可以安全地重新執行——它會取代而非重複,因此若一次視覺辨識中途失敗,可以直接從頭繼續。
kind 為 "native_chart" 的項目永遠不會出現在 pending 中。這些是直接從圖表定義讀取數列的試算表圖表,因此它們的數值是從像素估算中得出的精確值。
3. 回答
- 整份文件:
doc.md。 - 特定問題:搜尋每個單元的檔案,僅讀取你需要的部分。
- 引用時依單元名稱,或是在跨多份文件時加上檔名前綴(
[report.pdf:p12])。
| 格式 | 單元 | 引用方式 | 單元檔案 |
|---|---|---|---|
| 頁面 | [p12] | pages/p012.md | |
| PowerPoint | 投影片 | [s07] | pages/u007.md |
| Excel | 工作表 | [Sheet2] | pages/u002.md |
| Word | 一級標題 | [Budget assumptions] | pages/u003.md |
| 圖片 | 整個檔案 | [img] | pages/u001.md |
Office 單元檔案使用數字而非名稱,因為名為 Q1 P&L / draft 的工作表並非一個合法的檔名。manifest.json 中包含一個 units 列表,依序對應每個檔案及其標籤。
唯一規則
絕對不要手動編輯 doc.md。 你所新增的所有內容都必須透過 describe.py 處理,它會以分隔符號包裝這些內容。這樣才能讓基準測試能夠移除這些新增內容,並驗證此技能不會降低文字擷取的品質。直接編輯檔案將會悄無聲息地破壞這個保證。
描述內容的放置位置,以及 --inline 不保證的唯獨一件事
預設情況下,每個描述都會以一個區塊的形式附加在 doc.md 的結尾,並標示為 [s02]、[p12] 或 [Sheet2]。--inline 則會將每個區塊放置在其圖片所在的位置。請在轉換時使用此參數,而非傳遞給 describe.py:這個放置位置會記錄在成品中(以及其快取鍵中),因此恢復的視覺處理流程絕不會混淆兩種模式。
| 格式 | --inline 可以錨定的位置 |
|---|---|
| PowerPoint | 當該行能確定是屬於圖片本身時,放置在該行;否則放置在投影片結尾 |
| Word | 放置在圖片所在的章節結尾——anydoc 會將圖片渲染為其替代文字,而 Word 除非作者輸入,否則不會產生替代文字,因此通常沒有可以錨定的行 |
| Excel | 放置在工作表結尾——圖片與圖表來自套件,且 anydoc 兩者都不會渲染 |
| PDF、圖片 | 無。--inline 對 PDF 沒有任何影響 |
該區塊會被插入至引擎輸出的行旁邊,絕不會取代它,這就是為什麼位元組身分驗證能正常運作——eval/gate.py 會同時執行兩種放置方式,若發現任何就地編輯則會失敗。
行內模式不保證放置位置是正確的;位元組身分驗證無法檢查放置位置的正確性,因為插入的內容無論落在何處都能通過驗證。錨點是 anydoc 的替代文字,而替代文字是普通的 prose:一張關於名為 image.png 的檔案的投影片,與一張圖片會渲染出相同的行。因此,僅當該單元中候選行的數量等於其中圖片的數量時,才會使用圖片本身所在的行;否則描述會退回到單元邊界。位置要嘛可證明是該圖片的,要嘛是單元邊界——絕不會在兩個看起來相似的行之間猜測。
為什麼過濾條件是這樣的
請不要「簡化」這些條件——每一個都存在,是因為明顯的替代方案都已在真實文件上測試並失敗了:
| 過濾條件 | 原因 |
|---|---|
| 丟棄超過 50% 單元、小於 120px、長寬比大於 8:1 的圖片 | 一份 14 頁的補助金 PDF 有 69 個圖片放置點:其中 7 個是不同的物件,而其中六個是標誌、分隔線及側邊欄條紋。 |
| 跳過已產生 Markdown 表格的頁面 | 擷取器在解析表格方面比視覺辨識更強。僅在它無法產生任何結果時才介入。 |
| 要求兩個方向都有線條 | 底線和分隔線只有水平方向。計算所有軸對齊線條會在參考文獻頁面和帶有底線標題的合約上觸發。 |
將 diagonals 排除在面積下限之外 | 正文中不會出現 4 個以上的對角線段,而這個下限會否決放在頁面角落的圖表。 |
| 對密集網格分支使用墨水量閾值 | 區分擷取器遺漏的帶有陰影的表格與裝飾性章節橫幅。 |
| 將超過 6 個光柵圖的頁面合併為一個渲染結果 | 一個 48 格修補的比較圖本身就是一個圖形,而一張 TI 套裝照片會以 12 條帶狀呈現;逐格呼叫的成本約為 2 倍的 token,且會失去構圖。5-6 個光柵圖的頁面有時是各自獨立的圖形,因此界線設在 6。 |
| 丟棄 EMF、WMF 和 OLE 內容 | 文字引擎能忠實保留這些內容,但若沒有點陣化器則無法讀取——將它們路由到 pending 會產生一個沒有任何 Agent 能完成的項目。計入 dropped 中,而非悄悄丟棄。 |
harvest.py 是 PDF 路由的單一事實來源,而 filters.py 則適用於所有格式的部分。README 和設計規格中的每個數字都是從它們重新產生的;絕對不要手動編輯任何數字。如果你無法執行某個檔案,reference/harvest-block.md 是一個可複製貼上執行的自包含副本。
Office 路由故意較為簡化,而這是誠實的
一個 PDF 頁面是一組繪圖指令的程式,這些指令隱藏了其中的圖形,這就是為什麼 harvest.py 必須從向量幾何推斷它們。OOXML 則在其套件中明確宣告了圖片,因此沒有什麼需要推斷的。Office 文件會套用裝飾性過濾條件、引用與快取合約以及此評分量表——但沒有 render_reason、沒有 raster_grid 也沒有 cost_guard,因為沒有渲染引擎就無法渲染投影片或工作表。