
1. 多文件 Markdown 合并转 PDF 的真实痛点与场景如果你写过技术文档、项目说明书或者课程讲义大概率遇到过这样的局面内容按章节拆成了十几个.md文件01-intro.md、02-install.md、03-config.md……单独看每个文件都挺清爽可一旦要交付给同事、客户或者打印出来就得把它们合成一份完整的 PDF。手动复制粘贴文件一多就乱套改一处还得重新拼一遍页码、目录、代码高亮全得重来。这个场景的核心诉求其实很明确把多个 markdown 文件合并并转成 PDF而且要能重复执行、能进版本控制、能一键跑完。我试过纯手工合并也试过各种在线转换工具最后稳定下来的方案就是cat拼接加pandoc转换再配合 VS Code 的 tasks.json 做成任务。整条链路跑通之后改完任意一个章节文件按一下快捷键就能重新生成 PDF目录和页码自动更新。为什么是cat而不是别的合并工具因为cat是系统自带的Linux、macOS 直接可用Windows 在 Git Bash 或 WSL 里也能用零依赖。它的作用就是按你指定的顺序把文件内容首尾相接输出到一个新文件里。而pandoc是文档转换领域的瑞士军刀支持从 Markdown 到 PDF、DOCX、HTML 等几十种格式关键是它能通过模板和参数精细控制排版中文字体、页眉页码、目录、代码高亮这些都能配。适合谁看这篇如果你是技术写作者、项目维护者、需要定期产出文档的开发者或者只是想把一堆笔记整理成一本电子书这套流水线都能直接用。下面我会从环境准备讲到完整配置再到三项验证动作和常见报错排查每一步都给可复制的命令和配置。在开始之前先说一下文档流水线里一个容易被忽略的环节如果你在文档里需要调用大模型来生成摘要、翻译或者校对或者团队用 Claude Code 这类工具辅助写作那么 API 的接入配置也可以顺手放进同一套工作流。TaoToken 提供了兼容的接口模型对话、Coding Plan、API Keys 管理都有对应的入口后面第三节我会给出具体的配置片段方便你把文档生成和模型调用串起来。2. TaoToken 前置准备API Key 与接入信息配置在文档流水线里TaoToken 扮演的角色是提供模型调用能力。比如你想在合并前自动给每个章节生成一段摘要或者把英文文档批量翻译成中文再转 PDF这些都可以通过脚本调用 API 完成。所以这一节先把接入信息准备好后面配置 tasks.json 时直接引用。首先你需要一个 API Key。打开 TaoToken 的 API Keys 管理页面路径是https://taotoken.net/api-keys?utm_sourcetaotoken_aicg_blog_endutm_mediumcsdnutm_campaignrewriteutm_contentapikeys登录后创建一个新的 Key复制保存好。这个 Key 就是后面所有请求的身份凭证不要提交到公开仓库里。接下来是 Base URL。TaoToken 的 API 入口是https://taotoken.net/api注意这个地址不带 UTM 参数直接用于代码里的base_url配置。如果你用的是 OpenAI 兼容的 SDK把base_url设成这个值api_key填刚才创建的 Key就可以调用模型了。模型 ID 这块TaoToken 支持多种模型具体用哪个取决于你的任务。文档摘要和翻译一般用通用对话模型就够代码相关的文档可以用 coding 能力更强的模型。你可以在模型对话页面https://taotoken.net/chat?utm_sourcetaotoken_aicg_blog_endutm_mediumcsdnutm_campaignrewriteutm_contentchat先试一下效果确认输出质量再写进脚本。如果你打算长期用模型辅助文档生成比如每天自动跑一遍翻译和校对那 Coding Plan 会更划算入口在https://taotoken.net/coding-plan?utm_sourcetaotoken_aicg_blog_endutm_mediumcsdnutm_campaignrewriteutm_contentcodingplan。它适合高频调用的场景具体额度可以在页面里看。接入文档在https://taotoken.net/doc?utm_sourcetaotoken_aicg_blog_endutm_mediumcsdnutm_campaignrewriteutm_contentdoc里面有各语言 SDK 的示例和参数说明。如果你用 Claude Code 做文档润色对应的 Anthropic 兼容配置可以参考https://taotoken.net/claude-code-anthropic?utm_sourcetaotoken_aicg_blog_endutm_mediumcsdnutm_campaignrewriteutm_contentclaudecode。把这三样东西记下来Base URL是https://taotoken.net/apiAPI Key是你创建的那串字符Model ID是你选定的模型名称。后面在 VS Code 的 tasks.json 或者独立的脚本里都会用到这三个值。建议把它们放到环境变量或者.env文件里不要硬编码在配置文件中尤其是要提交到 Git 的时候。这里给一个.env的示例你可以放在项目根目录# .env 文件不要提交到公开仓库 TAOTOKEN_BASE_URLhttps://taotoken.net/api TAOTOKEN_API_KEYsk-你的实际Key TAOTOKEN_MODEL_ID你的模型ID然后在.gitignore里加上.env避免密钥泄露。这样文档流水线里任何需要调用模型的地方都可以从环境变量读取既安全又方便切换。3. 可复制配置cat 合并命令、pandoc 参数与 VS Code tasks.json这一节是整篇的核心我会把合并命令、pandoc 转换参数、VS Code 任务配置全部给出来你直接复制改路径就能用。3.1 cat 按章节顺序合并假设你的文件都在docs/目录下命名是01-intro.md、02-install.md、03-config.md这样带序号前缀的。用cat合并最简单的方式是cat docs/*.md combined.md但这里有个坑*通配符的展开顺序依赖文件系统的排序大多数情况下按文件名排序没问题但如果文件名没有统一的前缀顺序就可能乱。更稳妥的做法是显式列出文件或者用ls排序后传给catcat $(ls docs/*.md | sort) combined.md如果你希望每个章节之间自动插入分页符可以在合并时用awk或者循环处理。比如每个文件后面加一个\newpagefor f in $(ls docs/*.md | sort); do cat $f echo echo \\newpage echo done combined.md这样生成的combined.md里每个章节结束后会有一个\newpagepandoc 转 PDF 时会在这里分页。注意\newpage是 LaTeX 命令需要 pandoc 用 LaTeX 引擎生成 PDF 时才生效。合并完成后建议检查一下文件头尾确认没有重复的标题或者缺失的内容head -20 combined.md tail -20 combined.md wc -l combined.md3.2 pandoc 转换参数与中文字体pandoc 转 PDF 需要依赖 LaTeX 引擎。Linux 上装texlivemacOS 装mactex或者basictexWindows 装 MiKTeX。安装命令# macOS brew install pandoc brew install --cask mactex # Ubuntu/Debian sudo apt-get install pandoc texlive-full texlive-xetex texlive-lang-chinese # Windows 用 choco choco install pandoc miktex中文字体是重点。默认的 LaTeX 引擎对中文支持不好需要用xelatex并指定中文字体。macOS 上可以用PingFang SCWindows 用Microsoft YaHeiLinux 用Noto Sans CJK SC。完整命令pandoc combined.md \ -o output.pdf \ --pdf-enginexelatex \ -V mainfontPingFang SC \ -V monofontMenlo \ -V geometry:margin2.5cm \ --toc \ --toc-depth2 \ --highlight-styletango \ -V header-includes\usepackage{fancyhdr}\pagestyle{fancy}\fancyhead[L]{项目文档}\fancyhead[R]{\thepage}逐项解释一下--pdf-enginexelatex指定用 xelatex 引擎它对 Unicode 和中文字体支持最好。-V mainfont设置正文字体-V monofont设置等宽字体代码块会用这个字体。-V geometry:margin2.5cm设置页边距。--toc生成目录--toc-depth2表示目录只到二级标题。--highlight-styletango设置代码高亮风格可选值还有pygments、kate、monochrome等。-V header-includes里用fancyhdr宏包设置页眉和页码左边显示文档名右边显示页码。如果你有自定义的 LaTeX 模板可以用--templatemytemplate.tex指定。模板里可以控制封面、章节样式等。3.3 VS Code tasks.json 一键执行把上面两步串起来放到 VS Code 的任务里按CtrlShiftB就能跑。在项目根目录的.vscode/tasks.json里写{ version: 2.0.0, tasks: [ { label: 合并 Markdown 并转 PDF, type: shell, command: bash, args: [ -c, cat $(ls docs/*.md | sort) combined.md pandoc combined.md -o output.pdf --pdf-enginexelatex -V mainfontPingFang SC -V monofontMenlo -V geometry:margin2.5cm --toc --toc-depth2 --highlight-styletango -V header-includes\\usepackage{fancyhdr}\\pagestyle{fancy}\\fancyhead[L]{项目文档}\\fancyhead[R]{\\thepage} ], group: { kind: build, isDefault: true }, presentation: { reveal: always, panel: shared }, problemMatcher: [] } ] }注意 JSON 里的反斜杠要转义\\usepackage实际传给 shell 的是\usepackage。如果你在 Windows 上用 PowerShell命令部分要改成对应的语法或者直接用 Git Bash 执行。如果你还想在合并前调用 TaoToken 的模型做摘要或翻译可以在任务里加一步。比如用curl调用 APIcurl -s https://taotoken.net/api/v1/chat/completions \ -H Authorization: Bearer $TAOTOKEN_API_KEY \ -H Content-Type: application/json \ -d { model: $TAOTOKEN_MODEL_ID, messages: [{role: user, content: 请为以下文档生成一段200字摘要\n\n$(cat combined.md)}] }这个请求会把combined.md的内容发给模型返回摘要。你可以把摘要写到一个单独的文件里再合并进最终文档。注意base_url是https://taotoken.net/api路径是/v1/chat/completions这是 OpenAI 兼容格式。3.4 用 settings 片段管理环境变量如果你不想在 tasks.json 里硬编码路径和字体可以用 VS Code 的 settings.json 配合环境变量。在.vscode/settings.json里{ terminal.integrated.env.linux: { TAOTOKEN_BASE_URL: https://taotoken.net/api, TAOTOKEN_MODEL_ID: 你的模型ID }, terminal.integrated.env.osx: { TAOTOKEN_BASE_URL: https://taotoken.net/api, TAOTOKEN_MODEL_ID: 你的模型ID } }API Key 不建议放在 settings.json 里因为可能被同步到云端。用系统环境变量或者.env文件加载更安全。4. 验证请求与成功结果页眉页码、目录、代码高亮三项检查配置写完之后必须验证输出是否符合预期。我一般会检查三项页眉页码、目录、代码高亮。这三项过了基本排版就没大问题。4.1 页眉页码验证打开生成的output.pdf翻到第二页看页眉左上角是否显示了你设置的文字比如「项目文档」右上角是否显示页码。如果页眉没出现大概率是header-includes里的 LaTeX 命令没生效。检查两点一是fancyhdr宏包是否安装texlive-full一般自带二是命令里的反斜杠转义是否正确。你可以单独跑一个最小测试echo # 测试 test.md pandoc test.md -o test.pdf --pdf-enginexelatex -V header-includes\usepackage{fancyhdr}\pagestyle{fancy}\fancyhead[L]{测试页眉}\fancyhead[R]{\thepage}打开test.pdf如果页眉显示「测试页眉」和页码说明配置没问题。如果报错File fancyhdr.sty not found需要安装对应的 LaTeX 包# Ubuntu sudo apt-get install texlive-latex-extra # macOS 用 tlmgr sudo tlmgr install fancyhdr4.2 目录验证--toc参数会生成目录但目录本身不占页码且默认标题是「Contents」。如果你想改成「目录」需要加-V langzh-CN或者用模板覆盖。检查目录时注意一级标题和二级标题是否都出现了页码是否和正文对应。如果目录没生成先确认combined.md里的标题是用#和##写的而不是加粗或者纯文本。pandoc 只识别 ATX 标题#开头和 Setext 标题下划线。另外--toc-depth2表示只收录到二级如果你有三级标题想收录改成--toc-depth3。目录的页码有时会有偏差这是因为 LaTeX 需要跑两遍才能确定页码。pandoc 默认只跑一遍你可以加--number-sections给章节编号或者手动跑两遍pandoc combined.md -o output.pdf --pdf-enginexelatex --toc pandoc combined.md -o output.pdf --pdf-enginexelatex --toc第二遍会用第一遍生成的辅助文件修正页码。4.3 代码高亮验证在文档里放一段代码块比如python def hello(): print(Hello, TaoToken) 转成 PDF 后看代码块是否有背景色、关键字是否高亮。如果代码块是纯黑白说明--highlight-style没生效。检查参数拼写tango是内置风格之一。你也可以用--list-highlight-styles查看所有可用风格pandoc --list-highlight-styles如果代码块里的中文显示成方块说明等宽字体不支持中文。把monofont换成支持中文的等宽字体比如Noto Sans Mono CJK SC或者Sarasa Mono SC。三项都通过后你的文档流水线就算跑通了。每次修改docs/下的源文件按CtrlShiftB重新生成几十秒就能拿到新的 PDF。5. 本篇常见错排查401、local proxy failed、reading choices、OAuth即使配置看起来没问题实际跑的时候还是会遇到各种报错。这一节我把常见的几个列出来对照着排查。5.1 401 Unauthorized如果你在脚本里调用了 TaoToken 的 API返回 401说明认证失败。检查三处API Key 是否复制完整有没有多余空格请求头里的Authorization格式是不是Bearer sk-xxxBase URL 是不是https://taotoken.net/api注意不要多加/v1或者少写。一个常见的错误是把 Key 写在了 URL 参数里而不是请求头。OpenAI 兼容接口要求放在 Header-H Authorization: Bearer $TAOTOKEN_API_KEY如果你用的是.env文件确认加载顺序source .env之后再跑脚本。在 VS Code 任务里环境变量可能不会自动加载需要在命令前加source .env 。5.2 local proxy failed这个报错通常出现在网络请求环节提示本地代理失败。如果你没有配置任何代理检查环境变量里是否有HTTP_PROXY、HTTPS_PROXY被设置成了无效地址。用env | grep -i proxy看一下如果有用unset清掉。另外有些工具会读取系统代理设置。如果你在 VS Code 里跑任务检查settings.json里的http.proxy配置。把它设为空字符串或者删掉。5.3 reading choices 报错这个报错一般出现在调用模型接口时返回的数据结构里没有choices字段。可能的原因请求体格式不对比如messages数组为空模型 ID 写错了服务端返回了错误信息而不是正常的 completion 结构。排查方法先用curl手动发一个最小请求看返回的 JSON 长什么样curl -s https://taotoken.net/api/v1/chat/completions \ -H Authorization: Bearer $TAOTOKEN_API_KEY \ -H Content-Type: application/json \ -d {model: $TAOTOKEN_MODEL_ID, messages: [{role: user, content: hi}]}如果返回里有error字段根据错误信息调整。如果返回正常检查你的脚本解析逻辑是不是把response.choices[0]写成了别的路径。5.4 OAuth 相关报错如果你用 Claude Code 或者类似的工具接入可能会遇到 OAuth 认证失败。这类工具通常需要配置ANTHROPIC_BASE_URL和ANTHROPIC_API_KEY。Base URL 填https://taotoken.net/apiKey 填你的 TaoToken API Key。如果工具提示 OAuth token 无效检查是不是混用了官方和其他平台的凭证。在 Claude Code 的配置里通常有一个settings.json或者环境变量文件确保{ env: { ANTHROPIC_BASE_URL: https://taotoken.net/api, ANTHROPIC_API_KEY: sk-你的Key } }如果还是报 OAuth 错误尝试清除工具本地的缓存凭证重新登录。具体路径因工具而异一般在~/.config/或者~/.claude/下。5.5 pandoc 报错xelatex not found这个不是 API 的问题而是 LaTeX 引擎没装好。pandoc本身不带 LaTeX需要单独安装。Ubuntu 上sudo apt-get install texlive-xetexmacOS 上brew install --cask mactexWindows 上装 MiKTeX 后确保xelatex在 PATH 里。用which xelatex确认。如果装了还是找不到可能是 PATH 没刷新重启终端或者 VS Code 再试。6. 语义一致 CTA把文档流水线接入 TaoToken整条流水线跑通之后你会发现最耗时的往往不是合并和转换而是文档内容的生成和校对。如果你想让模型帮你做摘要、翻译、术语统一或者用 Claude Code 辅助润色那么把 TaoToken 的接入配置固化到工作流里会很省事。需要 API Key 的话去https://taotoken.net/api-keys?utm_sourcetaotoken_aicg_blog_endutm_mediumcsdnutm_campaignrewriteutm_contentapikeys创建然后参考接入文档https://taotoken.net/doc?utm_sourcetaotoken_aicg_blog_endutm_mediumcsdnutm_campaignrewriteutm_contentdoc把 Base URL 和 Key 填到你的脚本或工具里。想先试试模型输出效果可以直接在模型对话页面https://taotoken.net/chat?utm_sourcetaotoken_aicg_blog_endutm_mediumcsdnutm_campaignrewriteutm_contentchat里发几条消息确认质量再写进自动化任务。如果你每天都要生成大量文档或者团队里多人共用模型能力Coding Plan 的额度更适合长期跑入口在https://taotoken.net/coding-plan?utm_sourcetaotoken_aicg_blog_endutm_mediumcsdnutm_campaignrewriteutm_contentcodingplan。用 Claude Code 做文档润色的话Anthropic 兼容配置在https://taotoken.net/claude-code-anthropic?utm_sourcetaotoken_aicg_blog_endutm_mediumcsdnutm_campaignrewriteutm_contentclaudecode。最后提醒一句.env文件一定要加进.gitignoreAPI Key 不要出现在任何会提交到仓库的文件里。文档流水线可以自动化但密钥管理得手动守住。