现象
对少于 10 页的 PDF 调用 POST /api/documents/:id/llm-extract,会返回
{"message":"PDF extracted with LLM successfully","pages":1,"total_pages":1}
但产物 paper.md 是 0 字节,且 LLM 一次都没有被调用。没有任何错误日志。
实测(1 页 PDF,走真实 handler,用假 claude 二进制记录是否被 spawn):
HTTP 状态码 : 200
响应体 : {"message":"PDF extracted with LLM successfully","pages":1,"total_pages":1}
LLM 被调用过吗 : false
paper.md 大小 : 0 字节
根因
两个环节对页码文件名的约定不一致。
backend/api/documents.go:417 让 pdftoppm 输出到 <tempDir>/page,而 pdftoppm 的补零宽度是按 PDF 总页数决定的;documents.go:459 却硬编码了两位补零:
pageImg := filepath.Join(tempDir, fmt.Sprintf("page-%02d.png", i))
if _, err := os.Stat(pageImg); os.IsNotExist(err) {
continue
}
实测阈值精确是 10 页:
| 总页数 |
pdftoppm 实际产出 |
handler 找的 |
结果 |
| 1 |
page-1.png |
page-01.png |
落空 |
| 2 |
page-1.png |
page-01.png |
落空 |
| 9 |
page-1.png |
page-01.png |
落空 |
| 10 |
page-01.png |
page-01.png |
命中 |
| 11 |
page-01.png |
page-01.png |
命中 |
为什么是静默失败
os.Stat 未命中就 continue,而且发生在写入任何内容之前(页眉 ## Page N 也在其后),所以循环空转完直接走到成功返回。用户看到"成功提取 N 页",拿到空文件。
为什么一直没被发现
该功能主要用于提取 arXiv 论文,而论文几乎都 ≥10 页,刚好落在能工作的一侧。受影响的是所有短 PDF:单页文档、幻灯片、票据、几页的报告。
建议修法
不要猜补零宽度。pdftoppm 跑完后 glob page-*.png,按数字后缀建 map[int]string,再按页号取。比现在的 Sprintf 更短也更稳。
并补一条回归测试:1 页 PDF 必须真的调用 LLM 且产出非空 paper.md。现有测试用 pdfunite 把单页样本拼成 10 页来绕开这个 bug(见 backend/api/documents_llmextract_test.go 的注释),修好后那段拼接可以删掉。
发现经过
在 feature/pi-backend-switch 分支的 Plan 2 Task 11 中,为 PDF 逐页转换路径补测试时发现。不属于该计划的改动范围,且修它会改变 claude 路径的可观测行为(与该计划"claude 行为逐条等价"的验收项冲突),故单独开 issue。
另一个相邻问题(本 issue 不处理)
documents.go:410 的 tempDir 是 /tmp/pdf_pages_<userId>_<docID> 这种固定路径,同一用户对同一文档并发请求会互相覆盖对方的 PNG。建议一并评估,但属独立问题。
现象
对少于 10 页的 PDF 调用
POST /api/documents/:id/llm-extract,会返回{"message":"PDF extracted with LLM successfully","pages":1,"total_pages":1}但产物
paper.md是 0 字节,且 LLM 一次都没有被调用。没有任何错误日志。实测(1 页 PDF,走真实 handler,用假 claude 二进制记录是否被 spawn):
根因
两个环节对页码文件名的约定不一致。
backend/api/documents.go:417让pdftoppm输出到<tempDir>/page,而 pdftoppm 的补零宽度是按 PDF 总页数决定的;documents.go:459却硬编码了两位补零:实测阈值精确是 10 页:
page-1.pngpage-01.pngpage-1.pngpage-01.pngpage-1.pngpage-01.pngpage-01.pngpage-01.pngpage-01.pngpage-01.png为什么是静默失败
os.Stat未命中就continue,而且发生在写入任何内容之前(页眉## Page N也在其后),所以循环空转完直接走到成功返回。用户看到"成功提取 N 页",拿到空文件。为什么一直没被发现
该功能主要用于提取 arXiv 论文,而论文几乎都 ≥10 页,刚好落在能工作的一侧。受影响的是所有短 PDF:单页文档、幻灯片、票据、几页的报告。
建议修法
不要猜补零宽度。
pdftoppm跑完后 globpage-*.png,按数字后缀建map[int]string,再按页号取。比现在的Sprintf更短也更稳。并补一条回归测试:1 页 PDF 必须真的调用 LLM 且产出非空
paper.md。现有测试用pdfunite把单页样本拼成 10 页来绕开这个 bug(见backend/api/documents_llmextract_test.go的注释),修好后那段拼接可以删掉。发现经过
在
feature/pi-backend-switch分支的 Plan 2 Task 11 中,为 PDF 逐页转换路径补测试时发现。不属于该计划的改动范围,且修它会改变 claude 路径的可观测行为(与该计划"claude 行为逐条等价"的验收项冲突),故单独开 issue。另一个相邻问题(本 issue 不处理)
documents.go:410的tempDir是/tmp/pdf_pages_<userId>_<docID>这种固定路径,同一用户对同一文档并发请求会互相覆盖对方的 PNG。建议一并评估,但属独立问题。