目录

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 语言数据。在项目根目录执行:

./run.sh

脚本优先用 uv sync --locked 准备环境;无 uv 时自动创建 .venv-pip 并用 pip 安装依赖。也可在任意工作目录调用脚本,或传入另一份配置:

/opt/raid5/coding/Cot/run.sh /绝对路径/配置.yaml

默认读取 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_l2 当前扫描目录的一级、可选二级领域标签,写入每条样本并在报告中统计;这是配置标签,不是自动分类
input.pdf_dir、recursive、max_documents 扫描目录、递归方式、试运行文档上限;null 为全部。位于输入目录内的输出根目录和独立发布目录会自动排除
input.allow_external_api_for_confidential 是否允许将元数据明确标为保密的文档发送给模型;默认 false,会自动拦截并记录
llm.provider、model、base_url deepseek 或 openai 及对应模型和接口地址
llm.api_key、api_key_env 可直接写入本机配置;配置值非空时优先,否则读取环境变量
llm.concurrency、requests_per_minute、tokens_per_minute、max_retries 并发、速率和失败重试
llm.temperature 生成温度;所选模型不接受该参数时设为 null,请求将省略此字段
llm.max_input_tokens 输入预算;本地用含提示词开销的 UTF-8 字节数保守估算,超限时事实任务拆批、复杂分解证据包按完整片段缩减
parsing.scanned_pdf_policy、ocr_languages、max_bad_page_ratio、max_suspicious_character_ratio 扫描页处理、OCR 语言及不可读非空白页上限;首轮识别不足时自动改用单文本块模式二次识别,页面记录保留所用模式;阈值内的坏页单独隔离,不用于生成;坏页或异常字符超限时拒绝整份文档并保留页级诊断
generation.* 问答/复杂分解/严格数值比较与差值计算/跨文档双事实/绝对日期时间线开关、候选上限和问题语言策略;max_numeric_comparisons、max_multi_document_facts、max_timeline_pairs 分别限制对应事实对数量
validation.allowed_languages、require_valid_fact_for_release 允许的语言组合及缺失事实时是否隔离
validation.embedding_dedup_enabled、embedding_duplicate_threshold 启用本地多语言向量去重及余弦阈值;首次自动下载模型
dataset.* 切分比例、来源家族分组、近重复阈值和最低组数
runtime.progress、progress_refresh_seconds、resume 进度显示方式、最短刷新间隔(秒)和断点续跑
output.* 输出根目录、发布目录及导出格式

向量去重默认开启,使用 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 并使用对应哈希目录。

发布后可独立验收最终目录,命令只读文件,不需要模型密钥,也不会调用模型:

uv run --no-sync python -m pdf_cot verify

自定义发布目录时追加 --release-root /绝对路径/发布目录。命令检查 latest 指针、索引与报告、发布清单的文件大小和 SHA-256,以及三路样本、引用和导出格式;知识问答还会重新执行问题长度、资料元数据问题、数值、语气和逐字答案规则;成功返回退出码 0,校验失败返回 2。

复制单个版本目录到其他位置后,可在安装了本项目依赖的环境中只用该目录验收,无需原来的 runs/ 目录:

uv run --no-sync python -m pdf_cot verify --release-dir /绝对路径/版本目录

若同时保留了原 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 以索引为准。

指标 实测结果
PDF 扫描 / 成功解析 / 重复路径 21 / 21 / 0
页面 140;正文 129、空白 8、装饰或无文字 3
网格表格 / 简单双列无边框表 / 证据片段 6 / 0 / 165;165 条均有来源路径、范围框和解析器版本,125 条含数值、39 条含时间、59 条含模态词
候选样本 123;基础问答 101、复杂分解通过候选 20、跨文档双事实 2
事实抽取批次 23;超预算批次按问答证据边界拆分
有效原子事实 / 拒绝事实 92 / 1
严格可比的跨来源数值事实对 / 比较题 / 差值题 0 / 0 / 0;只对同指标、相同或可按固定比例换算的单位、同限定条件且来源家族不同的英文标量事实及少量明确的中文指标—单位组合自动生成同语种的确定性比较题和可复算差值题;原文含计划、预计、近似或上下界措辞时跳过,本轮无合格配对;中文数值配对另由四份合成 PDF 的完整发布及验收测试验证
机器拒绝 / 缺失事实隔离 7 / 2;其中 3 条因询问文章标题或资料附带的更多信息位置而拒绝
校验审计记录 125;最终样本 123、分解拒绝 1、事实拒绝 1
正式发布 114;问答 92、复杂分解 20、跨文档双事实 2
文档来源元数据 21 份文档中,首页标签提取到作者 18 份、来源 16 份、发布日期 12 份、原文网址 16 份;指向图片资源或无法确认完整性的地址保持空值。
领域标签 114 条均为配置指定的“无人系统”
语言 中文 52、英文 62、混合 0
train / validation / test 92 / 11 / 11;来源家族 17 / 2 / 2,目录共 21 个家族,跨文档样本将其中部分家族连接后形成 19 个实际切分组
评估集任务覆盖 验证集与测试集各含知识问答 9 条、复杂分解 2 条;跨文档双事实 2 条均在训练集,评估集对此任务没有独立覆盖。report.json 的 evaluation_task_coverage_gaps 明确列出缺失任务,不应把整体评估结果解释为跨文档任务效果。
文档或模型任务失败 0
本次实际模型调用与 Token 0 个成功 API 任务、0 个已记录的成功调用 Token;145 个生成任务均命中共享缓存。candidate_token_usage.total_tokens=467203 是这些候选最初生成时留下的用量记录,不是本次运行消耗;本次口径见 api_successful_tasks_this_run、recorded_successful_api_token_usage_this_run 和 model_call_cache_counts。失败调用可能没有服务商 Token 统计。
低置信度或不可读页隔离 0;当前验证目录无此类正文页
重复长文本页 / 含异常字符文档 0 / 2;短小重复页不计为重复正文;最高文档异常字符占比约 1.55%,低于默认 5% 上限
正文完全相同 / 高度重合归组 0 / 0;当前 21 份输入均为独立家族

已复核发布集 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 适配的问答、事实抽取、复杂分解及拒绝/过滤响应已用离线模拟请求覆盖,但未在此次全目录验证中在线调用。发布集按用户要求未做人工审核,使用前仍应按具体训练目标抽检。

关于

本项目把指定目录中的 PDF 转为可追溯的知识问答和复杂问题分解训练样本。每条问答保留原文证据、页码、文档和原子事实的引用;程序按配置自动扫描、生成、校验、隔离、切分并导出,无需人工干预。

18.8 MB
邀请码
    Gitlink(确实开源)
  • 加入我们
  • 官网邮箱:gitlink@ccf.org.cn
  • QQ群
  • QQ群
  • 公众号
  • 公众号

版权所有:中国计算机学会技术支持:开源发展技术委员会
京ICP备13000930号-9 京公网安备 11010802047560号