1. 为什么 PDF 智能阅读助手值得做成 Claude Skill
扫描版 PDF 里的文字搜不到、复制不了,几十页的合同要逐行翻找关键条款,上百份发票需要逐个提取金额和税号——这些事如果每次都手动来一遍,时间成本高得离谱。Claude Skill 的价值就在这里:它不需要你写 App、不需要部署后端服务,一份 Markdown 描述文件加上几个可调用的工具脚本,就能让 Claude 获得专项的 PDF 处理能力。用户只需要说一句“帮我提取这份合同里的金额和签署日期”,Skill 自动判断文件类型、选择处理引擎、调用 LLM 分析,最后返回结构化结果。
这篇教程聚焦的是配置落地,不是概念科普。我会带你从零搭出一条完整的链路:PDF 文件进来 → 判断是文本型还是扫描型 → 走 OCR 或直接提取 → 转成带页码标注的 Markdown → 送给 LLM 做问答或结构化提取。整条链路里,LLM 调用这一环用 TaoToken 统一通道来打通,你不需要分别去对接多家模型厂商的 Key 和接口格式。适合谁看?有 Python 基础、想在本地快速跑通 PDF→OCR→Markdown→LLM 问答的开发者,以及正在给 Claude Skill 写工具层、需要一套可复制配置骨架的人。
我试过把 OCR、文本提取、LLM 调用三块分别用不同方式拼起来,踩过的坑主要集中在两处:一是 OCR 引擎在不同机器上的可用性差异极大,二是 LLM 接口的鉴权和参数格式每家都不一样,换一个模型就要改一遍代码。所以这篇的配置骨架会重点解决这两个问题——OCR 做三级降级,LLM 走统一通道。
2. TaoToken 前置准备:统一 Key 与 API 通道
在写 Skill 的工具脚本之前,先把 LLM 调用这一层的基础设施准备好。TaoToken 在这里扮演的角色是统一 API 通道:你申请一个 Key,就能通过同一套接口规范调用不同模型,省去为每个模型单独维护 base_url、鉴权头和参数映射的麻烦。对于 Claude Skill 这种需要频繁切换模型做分析任务的场景,这一点很实用。
你需要做的准备动作只有三步。第一步,打开官网 https://taotoken.net/?utm_source=taotoken_aicg_blog_end&utm_medium=csdn&utm_campaign=rewrite&utm_content= 注册账号。第二步,进入控制台 https://taotoken.net/console?utm_source=taotoken_aicg_blog_end&utm_content=console 创建一个 API Key。第三步,把 Key 保存到本地环境变量里,不要硬编码进脚本。
# Linux / macOS:写入 shell 配置文件 export TAOTOKEN_API_KEY="sk-你的Key" # Windows PowerShell:当前会话生效 $env:TAOTOKEN_API_KEY="sk-你的Key"API 的基础地址是 https://taotoken.net/api ,注意这个地址不带任何查询参数,直接作为 base_url 使用。如果你后续要接 Claude Code 这类编码工具,可以走 Coding Plan 通道 https://taotoken.net/coding-plan?utm_source=taotoken_aicg_blog_end&utm_content=coding-plan&utm_campaign=rewrite ;如果只是想先在网页里验证模型响应是否符合预期,用模型对话 https://taotoken.net/models?utm_source=taotoken_aicg_blog_end&utm_content=models&utm_campaign=rewrite 快速试一句就行。
注意:Key 只显示一次,创建后立刻复制保存。如果怀疑泄露,去控制台重新生成,旧 Key 会立即失效。
3. 可复制配置:settings.json 与 config.toml 骨架
Claude Skill 的配置分两层:一层是 Skill 自身的元信息与工具声明,放在settings.json;另一层是运行时的模型与 OCR 参数,放在config.toml。下面两份骨架可以直接复制到你的项目根目录,改掉路径和 Key 引用即可。
3.1 settings.json:Skill 元信息与工具注册
{ "name": "pdf-smart-reader", "version": "1.0.0", "description": "PDF 智能阅读助手:文本提取、OCR、Markdown 转换、LLM 问答", "triggers": [ "读PDF", "提取PDF", "PDF转Markdown", "扫描件识别", "合同提取", "发票提取", "论文摘要", "PDF问答" ], "tools": [ { "name": "extract_text", "description": "从文本型 PDF 提取文字,保留页码标注", "entry": "tools/pdf_tools.py:extract_text_pdf" }, { "name": "smart_ocr", "description": "对扫描型 PDF 执行 OCR,三级引擎自动降级", "entry": "tools/pdf_tools.py:smart_ocr" }, { "name": "to_markdown", "description": "将提取结果转为带标题层级的 Markdown", "entry": "tools/pdf_tools.py:to_markdown" }, { "name": "ask_llm", "description": "将 Markdown 内容送入 LLM 做问答或结构化提取", "entry": "tools/llm_tools.py:ask_llm" } ], "env": { "TAOTOKEN_API_KEY": "${TAOTOKEN_API_KEY}" } }这份配置里,triggers决定了用户说什么话能唤醒 Skill,tools把每个能力映射到具体的 Python 函数入口。env段用${}语法引用环境变量,避免 Key 出现在版本控制里。
3.2 config.toml:模型与 OCR 运行时参数
[llm] base_url = "https://taotoken.net/api" api_key_env = "TAOTOKEN_API_KEY" model = "claude-3-5-sonnet" max_tokens = 4096 temperature = 0.2 timeout = 60 [ocr] # 三级降级顺序:tesseract -> marker -> pymupdf engine_priority = ["tesseract", "marker", "pymupdf"] tesseract_lang = "chi_sim+eng" min_text_length = 100 marker_min_ram_gb = 8 [pdf] max_pages_per_chunk = 20 page_marker_format = "[第{page}页]" supported_ext = [".pdf", ".md", ".json", ".txt"] [prompt] contract_schema = '{"合同标题","签约方","合同金额","签署日期","有效期","违约责任"}' invoice_schema = '{"发票号码","开票日期","销售方","购买方","价税合计"}' text_truncate = 10000temperature = 0.2是为了让结构化提取更稳定,减少 LLM 自由发挥导致 JSON 字段缺失。text_truncate = 10000控制送入模型的字符上限,大约对应 2500 token,给回复留足余量。OCR 的engine_priority数组就是降级顺序,代码里按这个顺序逐个尝试。
4. 验证请求:用一份样例 PDF 跑通全链路
配置写好了,接下来用一份真实的样例 PDF 验证整条链路。我准备了一份两页的扫描版合同 PDF,命名为sample_contract.pdf,放在data/目录下。
4.1 文本提取与 OCR 降级
先写工具函数,核心是判断 PDF 是文本型还是扫描型,然后走对应路径。
import fitz # pymupdf import os def extract_text_pdf(filepath, max_pages=None): doc = fitz.open(filepath) text_parts = [] pages = min(len(doc), max_pages or len(doc)) for i in range(int(pages)): text = doc[i].get_text() if text.strip(): text_parts.append(f"[第{i+1}页]\n{text}") doc.close() return "\n\n".join(text_parts) def smart_ocr(filepath): # 第一级:pytesseract,轻量,沙箱可用 try: text = ocr_tesseract(filepath) if text and len(text) > 100: return text, "pytesseract" except Exception: pass # 第二级:marker-pdf,高精度,需 8GB+ RAM try: import marker text, _ = ocr_marker(filepath) if text: return text, "marker-pdf" except (ImportError, MemoryError): pass # 第三级:pymupdf 直接提取,终极兜底 return extract_text_pdf(filepath), "pymupdf_fallback"运行提取:
python -c " from tools.pdf_tools import extract_text_pdf, smart_ocr text, engine = smart_ocr('data/sample_contract.pdf') print(f'引擎: {engine}') print(f'字符数: {len(text)}') print(text[:300]) "预期输出类似:
引擎: pymupdf_fallback 字符数: 1240 [第1页] 甲方:某某科技有限公司 乙方:某某信息技术服务有限公司 合同金额:人民币 128,000 元 签署日期:2024 年 3 月 15 日 ...如果输出里engine是pytesseract,说明你的环境装了 Tesseract 且识别成功;如果是pymupdf_fallback,说明前两级都不可用,但至少拿到了兜底结果,不会报错中断。
4.2 转 Markdown 并送入 LLM 问答
拿到文本后,转成 Markdown 结构,再调用 TaoToken 通道做问答。
import os import requests def ask_llm(markdown_text, question): api_key = os.environ["TAOTOKEN_API_KEY"] url = "https://taotoken.net/api/v1/chat/completions" headers = { "Authorization": f"Bearer {api_key}", "Content-Type": "application/json" } payload = { "model": "claude-3-5-sonnet", "max_tokens": 2048, "temperature": 0.2, "messages": [ {"role": "system", "content": "你是 PDF 文档分析助手,只根据给定内容回答。"}, {"role": "user", "content": f"文档内容:\n{markdown_text[:10000]}\n\n问题:{question}"} ] } resp = requests.post(url, headers=headers, json=payload, timeout=60) resp.raise_for_status() return resp.json()["choices"][0]["message"]["content"]执行验证:
python -c " from tools.pdf_tools import smart_ocr from tools.llm_tools import ask_llm text, _ = smart_ocr('data/sample_contract.pdf') answer = ask_llm(text, '这份合同的金额和签署日期分别是多少?') print(answer) "成功时你会看到类似这样的响应:
根据文档内容: - 合同金额:人民币 128,000 元 - 签署日期:2024 年 3 月 15 日到这里,PDF→OCR→Markdown→LLM 问答的完整链路就跑通了。整个过程你只配了一个 Key、一份 settings.json、一份 config.toml,没有为不同模型分别写适配代码。
5. 本篇常见错排查
链路跑通不代表一帆风顺,下面这几个报错是我在实际配置中遇到频率最高的,按现象、原因、解决三段式列出来。
报错一:KeyError: 'TAOTOKEN_API_KEY'
现象是脚本启动就崩,提示找不到环境变量。原因通常是你在当前终端会话里没有 export,或者用了 IDE 的内置终端但环境变量没继承。解决方式:在运行脚本前先echo $TAOTOKEN_API_KEY确认能打印出值;如果为空,重新执行第 2 节的 export 命令,或者把变量写进.env文件用python-dotenv加载。
报错二:requests.exceptions.HTTPError: 401 Client Error
现象是请求发出去了但被拒绝。原因一般是 Key 拼写错误、Key 已失效、或者 Authorization 头格式不对。检查两点:头必须是Bearer sk-xxx格式,中间有一个空格;Key 是否在控制台被重新生成过导致旧的失效。去 https://taotoken.net/api-keys?utm_source=taotoken_aicg_blog_end&utm_content=api-keys&utm_campaign=rewrite 核对当前有效的 Key。
报错三:TesseractNotFoundError
现象是 OCR 第一级直接抛异常。原因是系统没装 Tesseract 二进制,只装了 Python 包pytesseract是不够的。解决:Ubuntu 下sudo apt install tesseract-ocr tesseract-ocr-chi-sim,macOS 下brew install tesseract tesseract-lang。装完后用tesseract --version确认。如果装不了,不用慌,降级逻辑会自动走到 pymupdf 兜底。
报错四:OCR 输出全是乱码或空字符串
现象是smart_ocr返回的文本长度很短、内容不可读。原因通常是扫描件分辨率太低,或者语言包没装对。检查config.toml里的tesseract_lang是否包含chi_sim;如果是英文文档改成eng识别率更高。另外确认 PDF 页面渲染时的 DPI 不低于 200,太低会导致字符粘连。
报错五:LLM 返回内容被截断
现象是回答到一半突然没了。原因是max_tokens设得太小,或者输入文本太长挤占了输出空间。解决:把config.toml里的max_tokens调到 4096,同时确认text_truncate没有把关键内容截掉。如果文档确实很长,走分页处理,每次只送 20 页。
报错六:ModuleNotFoundError: No module named 'fitz'
现象是导入 pymupdf 失败。原因是包名和导入名不一致,安装时要写pip install pymupdf,导入时写import fitz。确认安装成功后重启 Python 进程。
6. 接入文档与后续动作
配置骨架和验证流程到这里就完整了。如果你在接入过程中遇到鉴权或参数格式的问题,直接翻接入文档 https://taotoken.net/doc?utm_source=taotoken_aicg_blog_end&utm_content=doc&utm_campaign=rewrite ,里面有各接口的字段说明和示例请求。需要管理或新建 Key 的时候去 API Keys 页面 https://taotoken.net/api-keys?utm_source=taotoken_aicg_blog_end&utm_content=api-keys&utm_campaign=rewrite 。如果你打算把这个 PDF 助手接到长期运行的编码或 Agent 工作流里,Coding Plan https://taotoken.net/coding-plan?utm_source=taotoken_aicg_blog_end&utm_content=coding-plan&utm_campaign=rewrite 的额度模型更适合持续调用。想先快速验证某个模型对中文合同的理解能力,模型对话 https://taotoken.net/models?utm_source=taotoken_aicg_blog_end&utm_content=models&utm_campaign=rewrite 里贴一段文本就能试。
最后分享一个实用技巧:把config.toml里的engine_priority顺序按你的实际环境调整。如果你确定部署机器有 8GB 以上内存且装了 marker,把marker提到第一位,识别精度会明显好于 tesseract;如果是轻量沙箱环境,保持tesseract在前、pymupdf兜底就行。这个顺序改一行配置就能切换,不用动代码。