默认读取 configs/pipeline.yaml,无需交互。启动预检会检查 PDF 目录、模型配置、OCR 依赖、输出目录可写性,以及并发、输入预算、OCR、切块、质量阈值等参数的类型和范围;错误会在调用模型前返回。终端中显示各阶段 tqdm 进度条;非终端运行输出含完成数、速率和预计剩余时间的 JSON 进度事件(空阶段也报告完成)。目录扫描阶段先用未知总数的动态进度记录已检查条目数、已发现 PDF 数和速率,排序并应用文档上限后才开始确定总数的哈希阶段。解析阶段显示成功/失败文档数与已解析页数,证据构建阶段显示成功/失败文档数、内容块数和证据数;模型生成、复杂分解和原子事实抽取阶段还显示成功数、失败数及本次实际 API 调用的重试数;问答自动校验显示通过、拒绝、隔离数,复杂分解校验显示通过与拒绝数;词面与向量去重阶段显示输入、保留和重复样本数,来源分组阶段显示来源家族数与实际切分组数;格式导出阶段显示已写样本总数及 train、validation、test 各自的数量;runtime.progress: off 可关闭进度输出。再次运行相同输入和配置会利用检查点与生成缓存;损坏或结构不合格的解析检查点会从原 PDF 重建;生成缓存无效时优先从另一份有效缓存恢复,否则重新生成。开发验证命令为 uv run --extra test pytest -q。
PDF 证据约束 COT 微调数据集流水线
本项目把指定目录中的 PDF 转为可追溯的知识问答和复杂问题分解训练样本。每条问答保留原文证据、页码、文档和原子事实的引用;程序按配置自动扫描、生成、校验、隔离、切分并导出,无需人工干预。方案背景见完整解决方案。
最终输出入口:data/output/releases/latest/。 训练数据分别是其中的 train.jsonl、validation.jsonl 和 test.jsonl。
releases/下名称为哈希值的目录保留历史版本;latest.json 标明当前正式版本。切换到其他配置运行后,应以该索引和发布验收结果为准。技术架构图
查看 Mermaid 源文件
核心模块:启动脚本、命令行编排、配置校验、PDF 解析、OCR、模型适配、隔离的向量推理、样本校验、事实校验、复杂分解校验。模型负责生成候选;引用和发布判定在本地执行。
业务处理流程图
查看 Mermaid 源文件
两张图的 SVG 可直接打开或用于文档;修改
.mmd后可用npx --yes @mermaid-js/mermaid-cli -i docs/diagrams/architecture.mmd -o docs/diagrams/architecture.svg -b white重新生成架构图,流程图把文件名换为business-flow。一键运行
要求 Python 3.11+ 或
uv;若启用扫描页 OCR,系统还需安装 Tesseract 及chi_sim、eng语言数据。在项目根目录执行:脚本优先用
uv sync --locked准备环境;无uv时自动创建.venv-pip并用pip安装依赖。也可在任意工作目录调用脚本,或传入另一份配置:默认读取
configs/pipeline.yaml,无需交互。启动预检会检查 PDF 目录、模型配置、OCR 依赖、输出目录可写性,以及并发、输入预算、OCR、切块、质量阈值等参数的类型和范围;错误会在调用模型前返回。终端中显示各阶段tqdm进度条;非终端运行输出含完成数、速率和预计剩余时间的 JSON 进度事件(空阶段也报告完成)。目录扫描阶段先用未知总数的动态进度记录已检查条目数、已发现 PDF 数和速率,排序并应用文档上限后才开始确定总数的哈希阶段。解析阶段显示成功/失败文档数与已解析页数,证据构建阶段显示成功/失败文档数、内容块数和证据数;模型生成、复杂分解和原子事实抽取阶段还显示成功数、失败数及本次实际 API 调用的重试数;问答自动校验显示通过、拒绝、隔离数,复杂分解校验显示通过与拒绝数;词面与向量去重阶段显示输入、保留和重复样本数,来源分组阶段显示来源家族数与实际切分组数;格式导出阶段显示已写样本总数及 train、validation、test 各自的数量;runtime.progress: off可关闭进度输出。再次运行相同输入和配置会利用检查点与生成缓存;损坏或结构不合格的解析检查点会从原 PDF 重建;生成缓存无效时优先从另一份有效缓存恢复,否则重新生成。开发验证命令为uv run --extra test pytest -q。配置说明
配置模板不含密钥,可复制后调整。当前本机配置指定
input.pdf_dir: ./test、max_documents: null、DeepSeek 模型、问答与复杂分解均开启、每份文档至多 5 条问答候选、事实校验必需、三路切分至少 10 个来源组,并输出 Parquet。扫描目录可改为任意可访问的 PDF 目录。仅扫描.pdf文件,不跟随符号链接;配置若请求跟随符号链接会在预检时报错。project.domain_l1、domain_l2input.pdf_dir、recursive、max_documentsnull为全部。位于输入目录内的输出根目录和独立发布目录会自动排除input.allow_external_api_for_confidentialfalse,会自动拦截并记录llm.provider、model、base_urldeepseek或openai及对应模型和接口地址llm.api_key、api_key_envllm.concurrency、requests_per_minute、tokens_per_minute、max_retriesllm.temperaturenull,请求将省略此字段llm.max_input_tokensparsing.scanned_pdf_policy、ocr_languages、max_bad_page_ratio、max_suspicious_character_ratiogeneration.*max_numeric_comparisons、max_multi_document_facts、max_timeline_pairs分别限制对应事实对数量validation.allowed_languages、require_valid_fact_for_releasevalidation.embedding_dedup_enabled、embedding_duplicate_thresholddataset.*runtime.progress、progress_refresh_seconds、resumeoutput.*向量去重默认开启,使用 FastEmbed 的多语言 MiniLM 模型;首次运行会自动下载约 220 MB 模型到
~/.cache/pdf-cot/fastembed/,之后复用。模型准备与推理在独立 Python 子进程中完成,避免与 PDF 原生解析库共处一个进程;子进程失败会使整次运行报错,不会静默跳过去重,准备与推理两次使用的模型文件身份必须一致。发布报告中的embedding_model.files_sha256固定本次模型文件身份,embedding_duplicates_rejected记录拦截数。当前 21 份 PDF 的全量发布该数为 0;另以真实模型验证了“续航时间是多少/能飞多久”的近义问法在相同答案与来源下被识别,而不同答案和速度问题被保留。模型相似度不能证明语义等价,故采用保守前置条件。程序会先用任务、答案、数值、否定线索和来源家族找出可能配对的样本,仅对这些问题生成向量;报告的embedding_questions_encoded给出实际编码数。本次 114 条候选没有符合前置条件的配对,实际编码 0 条,完整语义去重规则仍照常执行。默认
question_language: match_answer要求问答同语,allowed_languages: [zh, en]拒绝跨语言问答。英文样本的可见依据步骤、Messages 系统提示和最终答案包装均使用英文;事实依据步骤形成后,还会检查实际训练文本,若英文问答含汉字则自动隔离。开关须写为 YAML 布尔值true/false,不要写成带引号的字符串;尤其input.allow_external_api_for_confidential必须明确给出,避免误把字符串"false"当作允许发送。含密钥的configs/pipeline.yaml为本机文件,已加入.gitignore;运行清单、日志和发布集不写入密钥。请勿分享本机配置。模板中若干质量与无人值守策略目前是固定值,例如仅使用证据、缺证据时跳过、精确引用、数值与声明校验、存疑样本隔离及禁止空发布。改变这些固定值会在预检时报错,避免配置看似生效而运行时被忽略。
reject_semantic_strength_change目前使用保守词表,阻止把“可能/预计”等不确定来源表述写成“一定/保证”等确定性问题,并拦截把“计划/尚未部署”问成“何时已经部署”等有完成状态预设的问题;它不是完整的语义一致性证明。输出与判定
每次运行写入
data/output/runs/<run_id>/:manifest.json和report.json;documents.jsonl、pages.jsonl、quarantined_pages.jsonl、blocks.jsonl、evidence.jsonl、evidence_bundles.jsonl;candidates.jsonl、atomic_facts.jsonl、fact_rejected.jsonl、numeric_comparison_rejected.jsonl、numeric_difference_rejected.jsonl、multi_document_facts_rejected.jsonl、timeline_rejected.jsonl、potential_conflicts.jsonl;以及machine_passed.jsonl、machine_rejected.jsonl、quarantined.jsonl、decomposition_rejected.jsonl、failures.jsonl、model_calls.jsonl、validation_records.jsonl和检查点。模型调用清单记录任务、缓存来源、模型与提示词版本、请求身份哈希、候选哈希、Token 用量;新调用还记录实际请求/响应哈希、耗时和重试数,失败调用也记录目标、错误码和实际重试数,不保存提示词正文或密钥。校验清单记录最终样本状态、分解与事实拒绝原因、输入哈希、校验版本和时间。相同哈希的 PDF 只解析一份,其余路径保存在主文档的duplicate_paths;报告分别记录扫描文件数、解析文档数和副本路径数,并以split_group_count记录将跨来源样本的来源家族合并后实际参与切分的连通组数。文档记录区分文件名推导的显示标题与 PDF 元数据标题;保存配置的领域标签,并仅从首页显式标签提取作者、来源、发布日期和可完整恢复的原文网址,并保存中英字符计数、平均每页字符数、重复页数、异常字符占比和需关注页码;每条证据直接保留原文件相对路径、页码、内容块 ID、块范围框、解析器版本、表格结构和可用的目录路径,还记录原文中逐字匹配的实体、数值、时间和模态词线索。范围框是所引用内容块的并集,长块被拆分时仍为原块范围;这些线索只是索引提示,不等于已验证事实。达到最低来源组数后,在
data/output/releases/<run_id>/输出train、validation、test,每组均有主样本.jsonl、.parquet、.messages.jsonl和.sharegpt.jsonl;同目录的documents.jsonl、evidence.jsonl、atomic_facts.jsonl可按样本 ID 引用回查文档、原文位置和原子事实,report.json给出该版本的验证摘要。最终训练文件在data/output/releases/latest/:直接使用train.jsonl、validation.jsonl、test.jsonl;其他同名前缀文件是不同训练接口所需的格式;latest.json给出其run_id、release_scope: full_scan、切分数量和对应报告;运行完成后终端报告也直接打印release_dir和latest_release_dir,末尾另以醒目文字列出正式入口及三个主样本 JSONL 的完整路径;限量试跑只标为独立版本,不会冒充最终版。每个版本内的release_manifest.json列出发布范围、要求的导出格式、实际文件、大小和 SHA-256;独立验收会检查三路格式齐全,缺失文件不能仅靠改写文件清单掩盖。其余哈希目录是保留的历史版本或不同配置的发布版本,不应凭目录名或修改时间判断最终版。data/output/releases/catalog.json自动列出带有效发布清单结构的历史版本、全量/限量范围、三路数量和当前版本标记;它只是查找索引,正式入口仍以latest.json和独立验收结果为准。latest是指向具体哈希目录的符号链接;max_documents不为null的小批量试跑即使成功导出,也只保留独立版本,不会覆盖latest。未发布或虽发布但有处理失败时,指针同样保持上一成功版本。再次用其他全量配置成功运行会更新指针,因此需要固定训练集时应保存latest.json中的run_id并使用对应哈希目录。发布后可独立验收最终目录,命令只读文件,不需要模型密钥,也不会调用模型:
自定义发布目录时追加
--release-root /绝对路径/发布目录。命令检查latest指针、索引与报告、发布清单的文件大小和 SHA-256,以及三路样本、引用和导出格式;知识问答还会重新执行问题长度、资料元数据问题、数值、语气和逐字答案规则;成功返回退出码0,校验失败返回2。复制单个版本目录到其他位置后,可在安装了本项目依赖的环境中只用该目录验收,无需原来的
runs/目录:若同时保留了原 PDF 目录,可再加
--source-dir /绝对路径/PDF目录,按文档记录中的主路径及哈希去重后的副本路径逐份核对原件 SHA-256,并核对每条非 OCR 证据的原文是否位于所指 PDF 页(表格逐单元格核对,忽略空白差异);OCR 证据核对原文件哈希和引用页范围;新版发布集保留 OCR 页参数并重跑 Tesseract 核对证据文字,旧版若缺少这些参数则在结果中计为跳过复识别。当前test/的 21 份原件和 165 条证据已通过校验。独立模式检查目录内报告、文件哈希、三路数量、证据和事实引用、生成模型与提示词版本等溯源字段的结构、已知任务类型与复杂分解结构的重新校验、来源家族切分,以及各训练格式与主样本的一致性;还会重新确认问答答案、原子事实引文及事实字段逐字存在于相应证据中。Parquet 比较完整样本内容(忽略 Arrow 联合结构产生的空字段)。原始 PDF 不包含在发布包中;证据文件保留相对来源路径、页码和原文片段。发布清单的哈希用于检出意外损坏;它不是数字签名,无法防止有人同时改写文件和清单。主样本含
question、reasoning_steps、final_answer、evidence_ids、fact_ids、source_document_ids、source_families、validation_status和dataset_split等字段。问答的依据步骤与通过原文校验的原子事实关联;复杂分解记录三轮对话与两级研究问题。已知原文/译文编号归为同一家族;文件哈希不同但规范化正文完全相同的 PDF,或通过全部连续 7 词片段检测出高度重合的长文档,也会归为同一家族,并继承组内的保密标记。近重复归组要求 Jaccard 不低于 0.8 且短文档片段覆盖率不低于 0.9;阈值可在dataset配置中调整。相关来源家族整体进入同一切分,避免同源内容跨训练和测试集。候选问答先对同来源、同答案的问题做字符相似度与保守的词项 SimHash 去重(另核对否定词和问题数值);有效原子事实形成后,还会按同一家族内的主体、关系、客体和限定条件做事实去重;已支持的中文单位先换算为规范值,避免把“1公里”和“1000米”当作两条独立事实;重复问答进入机器拒绝清单,不同来源或不同限定条件不会因此合并。当前发布集这一规则新增拒绝 0 条。只有满足原文逐字答案、证据引用、数值、语言及格式条件的候选才能继续;缺少有效事实的问答进入隔离清单,结构或引用不合格的复杂分解被拒绝。导出后还会校验三个切分的样本、证据、文档和事实引用及其实际归属关系(含依据步骤、声明和对话轮次),跨切分来源家族,以及 JSONL、Parquet 全字段、Messages、ShareGPT 的记录一致性;校验失败记入
failures.jsonl和报告,终端明确提示本次输出不可用于训练,不更新latest。来源组不足时保留候选和报告,但不发布正式三路数据集。命令退出码:0为完整发布,2为配置错误或未发布,3为已发布但存在处理失败。将runtime.resume设为false会重新调用模型并覆盖相同提示词的共享生成缓存;常规续跑请保持true。当前验证效果
2026-09-23 使用默认配置对
test/全目录运行。执行完成后优先查看最新发布集和最新发布索引;该轮报告和具体run_id以索引为准。report.json的evaluation_task_coverage_gaps明确列出缺失任务,不应把整体评估结果解释为跨文档任务效果。candidate_token_usage.total_tokens=467203是这些候选最初生成时留下的用量记录,不是本次运行消耗;本次口径见api_successful_tasks_this_run、recorded_successful_api_token_usage_this_run和model_call_cache_counts。失败调用可能没有服务商 Token 统计。已复核发布集 JSONL 与 Parquet 的样本及引用一致性;61 条英文问答的依据步骤、Messages 和 ShareGPT 导出均未检出汉字。当前 21 份 PDF 的元数据中没有识别到保密标记,也没有规范化正文完全相同但文件哈希不同的文档。离线测试
103 passed,覆盖无网络端到端发布及续跑、扫描件 OCR、明确双栏页的阅读顺序、表格、目录引用、事实校验、中文相邻数值校验、同正文来源归组、来源家族切分、配置与输出目录预检、保密元数据拦截、布尔配置类型校验、资料元数据问题过滤,以及 DeepSeek/OpenAI 请求结构、重试、模型响应结构、输入预算、复杂分解证据包缩减和训练文本语言校验,以及错误 YAML 不回显密钥、三类 OpenAI 结构化请求和模型拒绝/过滤响应。16 个发布文件的 SHA-256 均与清单一致,21 个原 PDF 文件、21 份首页来源元数据及 165 条非 OCR 证据的原页文本也已核对;三份来源数据及发布报告与运行记录相同。双栏规则未重排当前验证目录的 140 页。最近一次全目录运行复用共享缓存;旧缓存缺失的实际 API 哈希与耗时仍为null,报告 Token 总量不能当作本次新增调用量或首次全量运行成本。另外做了独立的真实 DeepSeek 小批量复验:从
test/选取 3 份 PDF,关闭生成缓存后发起 6 次 API 请求(问答 3 次、事实抽取 3 次;模型调用清单的cache_source均为api,请求与响应哈希均已记录),发布 3 条样本,处理失败 0。该试跑版本在data/output/live_pilot/releases/6530c34f2a898e43/,以--release-dir和--source-dir test独立验收通过:16 个发布文件、3 份原 PDF、23 条原页证据。它的release_scope是limited_scan,不更新全量正式入口data/output/releases/latest/。独立验收命令现在同时支持完整发布包和限量试跑包,并在结果中明确标出范围。实施状态
当前可一键运行的是证据约束的单片段抽取式问答、绝对日期双事实时间线排序(仅同主体、不同事件、明确到日的有效日期)、同一主体跨来源双事实逐条引用问答、严格可比数值事实的确定性跨文档比较与绝对差值计算(中文航程支持公里/千米/米,重量支持千克/公斤/克/吨的固定比例换算)、单文档复杂问题分解、原子事实回查、规则验证、来源分组切分与发布验收。方案中需要跨来源推断、因果归纳或矛盾消解的多文档综合问答、复杂确定性计算题、无同答案/同来源限制的广义语义去重、复杂合并单元格及跨栏版面恢复、来源版本的语义冲突消解、逐篇领域自动分类尚未实现;这些能力不能从当前 114 条发布结果推断为已验证。双事实样本只并列引用不同属性的原文,不支持跨来源推断或冲突消解。当前输入也没有需要 OCR 提取正文的扫描页、内置目录或原文译文配对,相关路径只有合成件测试证据。OpenAI 接口已通过模拟请求测试,尚无真实账户在线运行证据。
已知边界
机器校验能证明答案与证据字符串及引用关系满足规则,不能完全证明生成问题与答案在语义上匹配,也不能证明复杂分解的研究价值。
quality_scores分别记录引用、数值、声明、语言和去重等可复核规则;语义答案正确性、推理有效性与任务价值标为null,machine_rule_score仅是规则门槛分,不应解释为语义正确率或方案中完整的加权质量分。事实与数值冲突检测均采取保守的同主体、同关系、同限定条件比较;已支持的中文长度与重量单位先统一换算,避免把“1公里”和“1000米”误判为冲突,其余语义冲突仍未自动消解。输入预算的 UTF-8 字节法比多数实际 Token 计数更保守,但不是服务商的精确分词器;单条超长证据仍会被跳过并记录错误。当前双栏排序只处理可明确区分、无跨栏内容的页面;带跨栏标题或图注的复杂排版仍沿用 PDF 内部块顺序。表格抽取支持带网格表格及短小、严格对齐的双列无边框参数表;复杂无边框表、任意形状合并单元格的语义恢复、跨证据事实合并尚未实现。简单网格表中,跨列的首行标题会单独保存为table_title,跨行标签仅在单元格几何范围证明覆盖下一行时才补入该行;合成 PDF 已测试两种情况,不能推断对所有复杂表格有效。本地多语言 Embedding 已用于保守语义去重:只有同任务、同答案、同来源家族且数值与否定线索一致的候选才比较问题向量;该规则不能识别答案改写或跨来源语义重复。来源家族可保守归并高度重合的局部修订和转载,但尚不能识别语义相同而文字差异很大、翻译后没有共同编号或片段重合的资料。当前 21 份验证 PDF 没有满足严格条件的时间线事实对,故该任务实际发布 0 条;合成事实测试覆盖日期解析、排序和发布重建校验。当前 21 份验证 PDF 无内置目录,也无需要 OCR 提取正文的扫描页;内置目录由合成件集成测试覆盖,扫描页由图片型 PDF 调用真实 Tesseract 的集成测试覆盖,默认 0.8 置信度门槛下可产出可定位证据。保密拦截只识别 PDF 元数据中的显式标记,无法自动证明版权或云端发送权限;扫描目录应只放入已获授权处理的资料。OpenAI 适配的问答、事实抽取、复杂分解及拒绝/过滤响应已用离线模拟请求覆盖,但未在此次全目录验证中在线调用。发布集按用户要求未做人工审核,使用前仍应按具体训练目标抽检。