阅读笔记:ExtractBench 基准下 Schema-Guided 企业文档抽取的配置骨架与验证

发布时间:2026/10/3 6:51:27
阅读笔记:ExtractBench 基准下 Schema-Guided 企业文档抽取的配置骨架与验证
1. ExtractBench 基准下 Schema-Guided 抽取到底难在哪ExtractBench 是 LlamaIndex 团队在 2026 年放出的一个 schema-guided 企业文档抽取基准370 篇文档、4869 页、8 个业务领域、67 种文档类型每篇文档沿五个独立轴打标签。它想回答的问题很具体给定一篇文档和一份用户自己写的 JSON Schema抽取系统能不能同时做到值正确、值可溯源、单页成本可控。适合谁适合正在把文档抽取流程往统一 API 通道上接的开发者尤其是那些已经跑通了 demo、但一上长文档和巨型表就崩的人。我读这篇论文时最有共鸣的一点是它把失败拆得特别细。以前我们评测抽取系统往往只看一个总分80% 就是 80%但到底是漏了三分之一的行还是把某个标签读错了完全看不出来。ExtractBench 用五个轴任务挑战、感知挑战、表格结构、长度、领域把失败归因切开长文档失败集中在 recall 而不是 precision这个结论对工程落地的指导意义比总分大得多。从工程视角看这篇论文最值得抄的不是结论而是它的评测协议cell 展平、归一化、Hungarian 对齐、状态表判定、micro P/R/F1。这套东西完全可以拿来当自己内部抽取服务的验收标准。而要把这套验收跑起来第一步是让抽取请求走一条稳定的 API 通道把 document-schema 对喂进去、把结构化 JSON 拿回来。下面我就按配置骨架 验证动作的顺序把这条链路搭一遍。需要先说明的是ExtractBench 本身评的是抽取系统的能力不是某个 API 网关。但工程上你总得有个统一的出口去调不同模型、不同抽取服务才能横向对比。我这里的做法是用 TaoToken 作为统一 API 通道把 schema-guided 抽取请求发出去再按 ExtractBench 的字段核对方式验证输出。这样基准任务能跑通输出字段也能逐项核对。2. TaoToken 统一 API 通道的前置准备TaoToken 在这里扮演的角色是统一出口你不需要为每个模型单独维护一套 SDK 和鉴权只要把 Base URL 指向同一个地址用同一个 Key通过 Model ID 切换后端。对 schema-guided 抽取这种需要横向对比多个系统的场景这一点很关键——同一份 config.toml 改一个 model 字段就能换系统评测脚本不用动。前置准备分三件事账号、Key、模型清单。账号在官网注册即可地址是 https://taotoken.net/?utm_sourcetaotoken_aicg_blog_endutm_mediumcsdnutm_campaignrewriteutm_content 。注册完进控制台控制台入口在 https://taotoken.net/console?utm_sourcetaotoken_aicg_blog_endutm_mediumcsdnutm_campaignrewriteutm_content 。Key 在 API Keys 页面生成页面地址 https://taotoken.net/api-keys?utm_sourcetaotoken_aicg_blog_endutm_mediumcsdnutm_campaignrewriteutm_content 。生成后立刻复制页面刷新就看不到了这是很多人第一次踩的坑。模型清单这块schema-guided 抽取对模型的要求和普通对话不一样。它需要模型能稳定输出符合 JSON Schema 的结构化结果还要能处理长上下文。你在选 Model ID 时优先挑支持结构化输出、上下文窗口大的。具体有哪些可用看文档里的模型列表文档地址 https://taotoken.net/doc?utm_sourcetaotoken_aicg_blog_endutm_mediumcsdnutm_campaignrewriteutm_content 。API 的基础地址是 https://taotoken.net/api 注意这个地址不带任何查询参数配置时直接填这个。这里有个概念要理清Base URL 和完整请求地址不是一回事。Base URL 是 https://taotoken.net/api 具体到 chat completions 这类接口路径是在它后面拼的。很多 401 和 404 报错根源就是把 Base URL 填成了完整接口地址或者反过来。配置时严格按文档给的 Base URL 填路径交给 SDK 处理。三件套记牢Base URL 是 https://taotoken.net/api Key 是你在 API Keys 页面生成的那串Model ID 是文档里列出的具体模型标识。这三样凑齐后面 config.toml 和 settings.json 才有东西可填。我建议先把这三样写在一个临时文本里配置时直接粘贴避免手打出错。3. 可复制的 config.toml 与 settings.json 配置骨架这一节是全文最该直接抄的部分。我给出两份配置一份 config.toml适合命令行工具和脚本读取一份 settings.json适合编辑器插件和图形化客户端。两份里的 Base URL、Key、Model ID 三件套保持一致只是载体不同。先看 config.toml。这个骨架我按通道 模型 抽取参数三层组织路径和字段名都写成可直接用的形式# ~/.config/extractbench/config.toml # ExtractBench schema-guided 抽取通道配置 [provider] name taotoken base_url https://taotoken.net/api api_key sk-你的Key粘贴在这里 timeout_seconds 120 max_retries 3 [model] # Model ID 以文档模型列表为准这里给一个占位示例 id your-model-id-from-doc temperature 0.0 max_tokens 8192 response_format json_schema [extraction] schema_path ./schemas/invoice.json document_dir ./docs output_dir ./outputs # 文档里没有的字段必须显式返回 null不能省略键 null_for_missing true # 是否要求返回 source page 与 bounding box require_grounding true [scoring] # 对齐 ExtractBench 的评分口径 normalize_dates true collapse_whitespace true numeric_tolerance 0.0 array_alignment hungarian几个字段值得单独说。temperature 设 0.0 是因为抽取任务要的是确定性不是创造性温度高了同一份文档两次跑出来的值可能不一样评测就没法复现。response_format 设成 json_schema是让模型按你给的 Schema 约束输出而不是自由发挥。null_for_missing 这个开关对应 ExtractBench 的一个关键设计文档里没有的字段必须显式返回 null不能省略键否则空字段上的幻觉不会被计入评分。require_grounding 打开后模型会尝试返回每个值的来源页和位置框虽然实测下来 word-level grounding 普遍不高但 page-level 能到 80% 以上对人工复核很有用。再看 settings.json。这份适合那些读 JSON 配置的客户端字段语义和上面一致{ provider: { name: taotoken, baseUrl: https://taotoken.net/api, apiKey: sk-你的Key粘贴在这里, timeoutSeconds: 120, maxRetries: 3 }, model: { id: your-model-id-from-doc, temperature: 0.0, maxTokens: 8192, responseFormat: json_schema }, extraction: { schemaPath: ./schemas/invoice.json, documentDir: ./docs, outputDir: ./outputs, nullForMissing: true, requireGrounding: true }, scoring: { normalizeDates: true, collapseWhitespace: true, numericTolerance: 0.0, arrayAlignment: hungarian } }如果你用的是 Claude Code 这类工具配置方式略有不同它读的是环境变量或专门的 settings 文件。核心还是三件套Base URL 填 https://taotoken.net/api Key 填你生成的Model ID 填文档里的。Claude Code 的接入文档在 https://taotoken.net/doc?utm_sourcetaotoken_aicg_blog_endutm_mediumcsdnutm_campaignrewriteutm_content 里面有针对性的配置说明。配置写完先别急着跑抽取。用一条最小请求验证通道是否通比直接上完整抽取流程更容易定位问题。下一节就做这个验证动作。4. 验证一次抽取请求并核对输出字段验证分两步先确认通道能通再确认抽取输出符合 Schema。第一步用一条最简单的请求第二步用一份真实文档加一份 Schema。先写一个最小验证脚本语言用 Python因为它的 requests 库最直观import json import requests BASE_URL https://taotoken.net/api API_KEY sk-你的Key粘贴在这里 MODEL_ID your-model-id-from-doc headers { Authorization: fBearer {API_KEY}, Content-Type: application/json, } payload { model: MODEL_ID, messages: [ {role: user, content: 只回复两个字通了} ], temperature: 0.0, } resp requests.post( f{BASE_URL}/v1/chat/completions, headersheaders, jsonpayload, timeout60, ) print(status:, resp.status_code) print(body:, resp.text[:500])跑这条如果 status 是 200body 里有模型回复说明通道通了。如果 401是 Key 的问题如果 404多半是路径拼错了检查 Base URL 后面拼的是不是 /v1/chat/completions。通道通了之后上真正的抽取请求。这里的关键是把 JSON Schema 作为约束传给模型并要求它返回结构化结果。下面是一个抽取请求的骨架import json import requests BASE_URL https://taotoken.net/api API_KEY sk-你的Key粘贴在这里 MODEL_ID your-model-id-from-doc # 用户自定义 Schema对应 ExtractBench 的 schema-guided 设定 schema { type: object, properties: { invoice_number: {type: string, description: 发票号通常在右上角}, issue_date: {type: string, description: 开票日期转 ISO 格式}, total_amount: {type: number, description: 含税总额}, line_items: { type: array, items: { type: object, properties: { description: {type: string}, quantity: {type: number}, unit_price: {type: number} } } } }, required: [invoice_number, issue_date, total_amount] } document_text open(./docs/sample_invoice.txt, encodingutf-8).read() prompt f你是企业文档抽取系统。请从下面的文档中按给定 JSON Schema 抽取字段。 文档中没有的字段必须显式返回 null不能省略键。 每个标量值请附带 source_page 字段标明来源页。 Schema: {json.dumps(schema, ensure_asciiFalse, indent2)} 文档: {document_text} 只输出 JSON不要输出其他内容。 headers { Authorization: fBearer {API_KEY}, Content-Type: application/json, } payload { model: MODEL_ID, messages: [{role: user, content: prompt}], temperature: 0.0, response_format: {type: json_object}, } resp requests.post( f{BASE_URL}/v1/chat/completions, headersheaders, jsonpayload, timeout120, ) result resp.json() content result[choices][0][message][content] extracted json.loads(content) print(json.dumps(extracted, ensure_asciiFalse, indent2))跑通之后核对输出字段。核对分三层第一层看 Schema 是否被遵守required 里的字段都在不在类型对不对第二层看值是否正确拿抽取结果和原文逐字段比第三层看空字段处理文档里没有的字段是不是显式返回了 null而不是被省略。这里有个容易忽略的点ExtractBench 的评分里数组是用 Hungarian 算法做全局最优一对一配对的不是按位置比。也就是说如果你的 line_items 顺序和 ground truth 不一样只要内容对得上评分不会扣。但如果你漏了一条记录recall 就会掉。所以核对时重点看记录条数对不对而不是顺序。验证通过后把这条请求封装成函数批量跑文档目录输出到 output_dir再按 ExtractBench 的评分口径算一遍 F1你就有了一套自己的抽取验收流程。5. 本篇常见报错排查配置和验证过程中报错集中在几类。我按实际遇到的频率排一下每条给出症状、原因、修法。401 Unauthorized 是最常见的。症状是请求返回 401body 里提示鉴权失败。原因通常是 Key 没填、填错、或者 Key 前后带了空格。修法是回到 API Keys 页面重新生成一个粘贴时注意别带首尾空格。还有一种情况是 Key 过期或被禁用重新生成即可。注意 Key 只在生成时显示一次页面刷新就看不到了所以生成后立刻存好。404 Not Found 也常见。症状是请求返回 404。原因多半是 Base URL 和路径拼错了。Base URL 是 https://taotoken.net/api chat completions 的完整路径是它后面拼 /v1/chat/completions。如果你把 Base URL 填成了完整接口地址SDK 再拼一次路径就会变成双份导致 404。修法是严格按文档给的 Base URL 填路径交给 SDK。local proxy failed 这类报错症状是连接本地代理失败。原因通常是环境变量里残留了 HTTP_PROXY 或 HTTPS_PROXY 指向一个不存在的本地端口。修法是检查环境变量把指向本地代理的项清掉或者确认你的网络配置里没有多余的代理设置。这类问题在切换网络环境后特别容易出现。reading choices 报错症状是解析响应时读不到 choices 字段。原因通常是响应体不是预期的 JSON 结构可能是请求被拦截返回了 HTML 错误页也可能是模型返回了非 JSON 内容。修法是先把 resp.text 完整打印出来看确认返回的到底是什么。如果是 HTML多半是路径或鉴权问题如果是模型返回了带 markdown 代码块的 JSON需要在解析前剥掉代码块标记。OAuth 相关报错症状是提示 OAuth 流程失败或 token 无效。这类多出现在用 OAuth 方式接入的客户端里。修法是改用 API Key 方式接入Base URL 填 https://taotoken.net/api Key 填生成的Model ID 填文档里的。三件套齐全OAuth 那套流程就不需要了。还有一类是模型返回的 JSON 不符合 Schema比如该返回 null 的字段被省略了或者数组里少了几条记录。这不是通道问题是模型能力问题。修法是在 prompt 里把约束写得更明确比如显式要求没有的字段返回 null或者换一个结构化输出能力更强的 Model ID。ExtractBench 的结论里也提到长文档和巨型表是裸模型的死穴如果这类文档抽取质量差考虑换 specialized API 或 coding agent 类的方案。排查时有个通用技巧把请求和响应的完整内容都打出来包括 status code、headers、body。大部分报错看一眼完整响应就能定位比猜快得多。6. 把基准任务接进统一通道的下一步配置骨架和验证动作跑通之后你手上就有了一条能稳定发 schema-guided 抽取请求的通道。接下来可以做的事有几件。第一件是把 ExtractBench 的数据集拉下来按它的五轴标签切片跑。数据集在 HuggingFace 上搜 llamaindex/ExtractBench 就能找到。重点跑两个切片L3 长文档和 S4 巨型表。论文里的数据显示裸模型在这两个切片上崩得最厉害长文档失败集中在 recall巨型表甚至能低到个位数。你自己的系统在这两个切片上的表现比总分更能说明问题。第二件是把评分脚本按 ExtractBench 的口径实现一遍。cell 展平、日期归一化、空白折叠、Hungarian 对齐、状态表判定这套逻辑论文附录里有详细定义可以直接照着写。有了自己的评分脚本你换模型、换 prompt、换 Schema 写法都能快速看到 F1 变化。第三件是关注 grounding。论文里所有系统的 word-level grounding F1 都不到 50%page-level 能到 84.9%。这说明找对页比圈对词容易得多。如果你的业务需要人工复核page-level grounding 已经够用如果需要更精细的定位这块还有很大的优化空间。如果你要长期跑这类抽取任务或者要接 Agent 做多步抽取可以考虑 Coding Plan入口在 https://taotoken.net/coding-plan?utm_sourcetaotoken_aicg_blog_endutm_mediumcsdnutm_campaignrewriteutm_content 。如果只是想先验证某个模型在抽取任务上的表现用模型对话页面快速试几条就行地址 https://taotoken.net/chat?utm_sourcetaotoken_aicg_blog_endutm_mediumcsdnutm_campaignrewriteutm_content 。接入细节和模型清单以文档为准文档在 https://taotoken.net/doc?utm_sourcetaotoken_aicg_blog_endutm_mediumcsdnutm_campaignrewriteutm_content 。最后说一个我自己的经验schema-guided 抽取的成败一半在模型一半在 Schema 写法。字段描述里带上别名、格式要求、位置提示、勿混指引能明显提升抽取质量。ExtractBench 的 Schema 全由 LlamaIndex 团队撰写这也是它结论需要打折看的原因之一——Schema 写法可能无意中偏向自家系统。你自己写 Schema 时多写几版对比别指望一版就到位。