一个双引擎、智能的文档转换工具集。
forma 旨在将不同格式的文档(如 PDF、DOCX、图片)高效地转换为结构化的 Markdown 格式。它内置了"快速"和"深度"双引擎,并提供"自动"策略,能够智能判断文档类型并选择最优处理方式。
- 统一的命令行接口: 所有操作都通过
forma convert和forma qa命令完成,清晰易用。 - 异步 API 服务: 提供 RESTful API,支持文档转换和知识库生成,采用异步回调机制处理长时任务。
- 多种处理策略:
fast: 使用本地解析和 OCR,速度快,适用于文本型文档。deep: 调用强大的视觉语言模型(VLM),由可定制的提示词驱动,精准处理扫描件、复杂排版和图片内容。auto: 智能路由,自动为每个文件选择fast或deep策略。
- 广泛的格式支持: 支持 PDF、DOCX、PPTX、PNG、JPG 等常见文档和图片格式。
- 高度可扩展: 采用按功能划分的模块化包结构,方便未来添加新的文件格式支持和功能。
- 提示词工程: 将提示词与代码分离到
prompts.yaml,方便用户按需定制。
项目提供了 Makefile 来简化环境设置和依赖管理,推荐使用此方式。
# 一键完成虚拟环境创建、依赖锁定和安装
make deps该命令会自动执行以下操作:
- 创建
.venv虚拟环境。 - 根据
pyproject.toml和当前操作系统,智能解析并锁定依赖。 - 严格按照锁文件安装所有依赖,确保环境一致性。
自动硬件加速:
make deps 命令会自动检测您的操作系统,并安装最优的依赖包:
- Linux: 自动安装
paddlepaddle-gpu和torch的 CUDA 版本,以启用 GPU 加速。 - macOS / Windows: 安装标准的 CPU 版本。
您无需任何手动配置,即可在支持的硬件上获得最佳性能。
LibreOffice (可选,用于增强 PPTX 解析)
forma 可以不依赖任何外部软件运行。但为了解锁对 .pptx 文件中复杂幻灯片(如图表、纯图片页面)的完整深度解析功能,需要安装 LibreOffice。
- 如果已安装
LibreOffice:forma会自动检测并使用它来转换复杂幻灯片,以获得最精准的分析结果。 - 如果未安装
LibreOffice:forma仍然可以正常处理所有文件的文本内容。对于 PPTX 中的复杂幻灯片,它将跳过深度解析,并在输出的 Markdown 文件中插入一条提示信息,不会因此中断或报错。
安装方法:
-
macOS (使用 Homebrew):
brew install --cask libreoffice
-
Linux (Ubuntu/Debian):
sudo apt-get update && sudo apt-get install -y libreoffice
deep 和 auto 策略需要使用视觉语言模型。请在项目根目录创建 .env 文件并填入您的 API 密钥:
# 请填入您的 OpenAI API 密钥
FORMA_OPENAI_API_KEY="sk-your-api-key-here"
# 或者,您也可以使用 VLM_API_KEY 变量名
# VLM_API_KEY="sk-your-api-key-here"使用 convert 命令进行文档转换。
# 使用 fast 策略转换单个 PDF
uv run forma convert "./data/pdfs/沐曦招股书.pdf" -o "./output" -s fast
# 使用 deep 策略转换一张图片
uv run forma convert "./data/image/1.png" -o "./output" -s deep
# 使用 auto 策略递归处理整个文件夹 (默认策略)
uv run forma convert "./data" -o "./output"
# 使用自定义提示词进行深度分析
uv run forma convert "./data/image/1.png" -o "./output" -s deep -p "technical_diagram_analysis"参数说明:
INPUTS...: 一个或多个输入文件或文件夹的路径。-o, --output: 用于保存输出文件的目录。-s, --strategy: 转换策略,可选值为auto,fast,deep(默认为auto)。-p, --prompt: 指定deep或auto策略要使用的提示词名称 (默认为default_image_description)。--recursive / --no-recursive: 是否递归处理子目录 (默认为True)。
使用 qa-v2 命令从 Markdown 生成结构化的知识库:
# 从 Markdown 生成知识库
uv run forma qa-v2 "./output/document.md" -o "./knowledge" --export-csvforma 提供了基于 FastAPI 的异步 API 服务,支持文档转换和知识库生成。
生产模式:
make serve开发模式(热重载 + DEBUG 日志):
make serve-dev自定义配置:
# 通过环境变量配置
make serve HOST=0.0.0.0 SERVER_PORT=8090 CONVERSION_WORKERS=8 QA_WORKERS=4 LOG_LEVEL=INFO环境变量说明:
| 变量名 | 默认值 | 说明 |
|---|---|---|
HOST |
0.0.0.0 |
服务监听地址 |
SERVER_PORT |
8090 |
服务端口 |
WORKERS |
1 |
Uvicorn 进程数(生产环境可设为 CPU 核心数) |
CONVERSION_WORKERS |
4 |
文档转换并发 worker 数量 |
QA_WORKERS |
2 |
知识库生成并发 worker 数量 |
LOG_LEVEL |
INFO |
日志级别(DEBUG/INFO/WARNING/ERROR) |
FORMA_CONVERSION_TIMEOUT |
600 |
文档转换超时时间(秒) |
FORMA_QA_TIMEOUT |
600 |
知识库生成超时时间(秒) |
FORMA_DATA_DIR |
./data |
数据保存目录 |
端点: POST /api/v1/convert
请求示例:
{
"request_id": "unique-request-id-123",
"source_url": "https://example.com/document.pdf",
"callback_url": "https://your-service.com/callback"
}立即响应:
{
"task_id": "550e8400-e29b-41d4-a716-446655440000",
"status": "processing"
}回调通知(转换完成后):
成功时:
{
"request_id": "unique-request-id-123",
"status": "completed",
"markdown_content": "# 文档标题\n\n文档内容...",
"error_message": null
}失败时:
{
"request_id": "unique-request-id-123",
"status": "failed",
"markdown_content": null,
"error_message": "File: document.pdf - RuntimeError: 转换失败原因..."
}端点: POST /api/v1/generate-qa
请求示例:
{
"request_id": "unique-request-id-456",
"markdown_content": "# 文档标题\n\n## 章节1\n\n内容...",
"callback_url": "https://your-service.com/qa-callback"
}立即响应:
{
"task_id": "660e8400-e29b-41d4-a716-446655440001",
"status": "processing"
}回调通知(生成完成后):
成功时:
{
"request_id": "unique-request-id-456",
"status": "completed",
"faq_json": "{\"qa_pairs\":[{\"question\":\"...\",\"answer\":\"...\",\"category\":\"...\"}, ...]}",
"error_message": null
}失败时:
{
"request_id": "unique-request-id-456",
"status": "failed",
"faq_json": null,
"error_message": "FAQ Generation Error - RuntimeError: 生成失败原因..."
}# 测试文档转换
curl -X POST "http://localhost:8090/api/v1/convert" \
-H "Content-Type: application/json" \
-d '{
"request_id": "test-123",
"source_url": "https://example.com/sample.pdf",
"callback_url": "https://webhook.site/your-unique-url"
}'
# 测试知识库生成
curl -X POST "http://localhost:8090/api/v1/generate-qa" \
-H "Content-Type: application/json" \
-d '{
"request_id": "qa-test-456",
"markdown_content": "# 测试文档\n\n## 章节1\n\n这是测试内容。",
"callback_url": "https://webhook.site/your-unique-url"
}'服务启动后会输出详细的日志信息:
2025-10-11 14:20:00 [INFO] [forma.server] Logging initialized with level: INFO
2025-10-11 14:20:00 [INFO] [forma.server] Configuration loaded:
2025-10-11 14:20:00 [INFO] [forma.server] - CONVERSION_TIMEOUT: 600.0 seconds
2025-10-11 14:20:00 [INFO] [forma.server] - QA_TIMEOUT: 600.0 seconds
2025-10-11 14:20:00 [INFO] [forma.server] - DATA_DIR: ./data
2025-10-11 14:20:00 [INFO] [forma.server] - CONVERSION_WORKERS: 4
2025-10-11 14:20:00 [INFO] [forma.server] - QA_WORKERS: 2
2025-10-11 14:20:00 [INFO] [forma.server] Starting workers...
2025-10-11 14:20:00 [INFO] [forma.server] ✓ Started 4 conversion workers.
2025-10-11 14:20:00 [INFO] [forma.server] ✓ Started 2 QA workers.
2025-10-11 14:20:00 [INFO] [forma.server] ============================================================
2025-10-11 14:20:00 [INFO] [forma.server] Forma API Server is ready to accept requests.
2025-10-11 14:20:00 [INFO] [forma.server] ============================================================
若需要查看详细的调试日志,使用 make serve-dev 或设置 LOG_LEVEL=DEBUG。
forma 采用按功能组织的包结构,清晰地分离了不同的业务领域:
. (项目根目录)
├── prompts.yaml # VLM 提示词配置文件
└── src/forma/
├── __init__.py
├── cli.py # 命令行接口 (Typer)
├── conversion/ # 核心功能:文档转换
│ ├── __init__.py
│ ├── workflow.py # 转换流程的业务逻辑编排
│ └── processors/ # 针对不同文件类型(PDF, DOCX等)的具体处理器
├── ocr/ # 核心功能:光学字符识别(OCR)
│ ├── __init__.py
│ └── engine.py # OCR 引擎的封装
├── qa/ # 核心功能:问答对(QA)生成
│ ├── __init__.py
│ ├── builder.py # QA 对的构建逻辑
│ └── pipeline.py # QA 生成的完整流水线
├── shared/ # 跨功能共享的模块
│ ├── __init__.py
│ ├── config.py # 配置管理 (Pydantic)
│ ├── prompts.py # 提示词管理器
│ └── utils/ # 通用工具函数
└── vision/ # 核心功能:视觉语言模型(VLM)交互
├── __init__.py
├── client.py # VLM API 客户端
└── parser.py # VLM 响应解析器
cli.py: 定义用户交互的命令行界面。prompts.yaml: 定义deep策略使用的所有提示词,易于修改和扩展。conversion/: 包含所有与文档格式转换相关的逻辑。ocr/: 封装了 OCR 功能,供其他模块调用。qa/: 实现了从文本生成结构化问答对的完整流程。vision/: 负责与视觉语言模型进行交互,是deep策略的核心。shared/: 存放被多个功能包共享的通用代码,如配置加载、工具函数等。
-
fast策略:- PDF: 使用
pymupdf提取文本和图片。对于纯文本 PDF,直接提取;对于扫描件,提取图片后交由ocr模块处理。 - DOCX: 采用混合策略以最大化保留表格等格式。
- 优先使用
mammoth:将 DOCX 转换为 HTML,再转换为 Markdown,能较好地还原表格、列表和格式。 - 回退至
python-docx:如果前一步失败,则启用基于python-docx的备用方案,逐一解析段落和表格,确保内容不丢失。
- 优先使用
- Image: 使用
ocr模块进行本地 OCR。 - PPTX: 采用更智能的混合策略,对每一页幻灯片进行独立分析:
- 智能决策: 遍历每一页幻灯片,分析其内容构成来判断是“内容页”还是“复杂页”。
- 优先识别复杂对象: 如果幻灯片包含 图表 (Chart)、SmartArt 或 表格 (Table),则立即判定为“复杂页”。
- 分析图文关系: 如果页面主要是图片,文字极少,也判定为“复杂页”。
- 内容页处理 (Fast Path): 对于文本密集型页面,直接使用
python-pptx提取文本,并对页内图片进行 OCR。 - 复杂页处理 (Deep Path): 对于“复杂页”,自动调用深度解析流程:
- 使用
LibreOffice(如果可用) 将其渲染为高清图片。 - 将此图片交由
vision模块进行视觉理解。
- 使用
- 智能决策: 遍历每一页幻灯片,分析其内容构成来判断是“内容页”还是“复杂页”。
- PDF: 使用
-
deep策略:- 将文档页面或图片发送给
vision模块。 - 调用过程由
prompts.yaml文件驱动,引导模型生成高质量的 Markdown 文本。
- 将文档页面或图片发送给
-
auto策略:- 首先执行
fast策略。 - 对结果进行简单分析(如字符数、置信度等)。
- 如果
fast策略的结果质量不佳(例如,从扫描版 PDF 中只提取到很少的文字),则自动升级,调用deep策略进行重新处理。
- 首先执行
forma 的 deep 策略的核心是可定制的提示词工程。所有提示词都在项目根目录的 prompts.yaml 文件中进行管理,实现了逻辑与提示的分离。
prompts.yaml 结构如下:
prompts:
# 默认的图片/页面描述提示
default_image_description:
user: >-
请详细描述这张图片或页面的内容...
# 用于分析技术架构图的专用提示
technical_diagram_analysis:
system: >-
你是一位专业的系统架构师...
user: >-
请以专业的视角分析这张系统架构图...
# ... 可添加更多自定义提示- 修改现有提示: 直接编辑
prompts.yaml中user或system的内容,即可改变模型的行为,无需修改任何 Python 代码。 - 添加新提示: 你可以按照格式添加新的具名提示(如
my_custom_prompt),然后通过-p或--prompt命令行参数来调用它,以适应特定的文档处理需求。
查看所有可用命令:
make help项目使用 pytest 进行测试。运行以下命令以执行完整的测试套件:
make test# 代码检查
make lint
# 代码格式化
make format
# 运行 lint + test
make qualitymake doctorforma 提供了一个强大的 generate-qa 命令,可以将任何 Markdown 文档转换为结构化的、高质量的常见问题(FAQ)列表,并以 CSV 格式导出。这背后是一套精心设计的三阶段智能流水线。
-
阶段一:原始问答生成
- 系统首先将 Markdown 文档切分为多个内容块。
- 针对每个块,调用大语言模型(LLM)初步提取独立的问答对(QA-Pairs)。
-
阶段二:全局分类体系生成
- 收集上一阶段生成的所有问题。
- 再次调用 LLM,分析这些问题并总结出 5-8 个核心主题分类,形成一个全局的知识体系结构。
-
阶段三:合成、聚类与指派
- 这是流水线的核心,它通过一个“向量化 -> 聚类 -> LLM 合成”的精密流程,将原始问答对提炼升华。
- 向量化 (
sentence-transformers): 使用sentence-transformers框架将所有原始问题的文本高效地转换为高质量的数学向量(Embeddings)。我们选择这个框架是因为它 API 简洁、专门为句子/段落设计、且支持完全本地化部署,保障数据隐私。 - 语义聚类 (
DBSCAN): 接收上一步生成的向量,并使用DBSCAN算法进行语义聚类。DBSCAN的优势在于无需预设簇的数量,能根据问题本身的语义密度自动发现主题分组,并能有效识别和过滤“噪音”问题。 - 合成与指派 (LLM): 将每个语义相近的问题簇(Cluster)分别提交给 LLM,执行更高层次的“总结”和“提炼”任务,最终产出唯一的、高质量的、已归类的标准问答对。
这个设计将计算密集型任务(如向量化、聚类)交给高效的开源库处理,而将需要深度语义理解和创造性的任务(内容合成)交给强大的 LLM,实现了成本和质量的最佳平衡。
为了进一步降低复杂度并显著提升性能,我们提供了全新的 qa-v2 流水线。该版本取消了“全局知识整合”阶段,直接将第一阶段(层级化知识提炼)的产出作为最终结果,并利用源文档的标题结构 header_chain 作为分类。
- 性能与成本:原第二阶段(全局知识整合)是主要性能瓶颈,且需要额外的 Embedding/聚类计算。
- 客观与稳定:动态生成的分类体系往往不如文档本身的标题结构客观、稳定。直接使用
header_chain能更好地对齐源文档的组织方式。
- 移除第二阶段相关逻辑(全局分类/分配/精炼)。
- 保留并强化第一阶段的“层级化知识提炼”。
- 直接输出“扁平化的 QA 列表”,每条记录包含:
question、answer、category。 - 其中
category取自来源块的metadata.header_chain,并以" > "连接,例如:素材抽取流水线 > 字幕去除 > 本地实现方案。
uv run forma qa-v2 "./path/to/input.md" -o "./output" --export-csv参数说明:
INPUT:输入的 Markdown 文件路径。-o, --output:输出目录。--export-csv:是否导出 CSV(推荐)。
- CSV:
{basename}_knowledge_base.csv- 列:
question,answer,category category为该 QA 对来源块的层级路径(由header_chain以" > "连接所得)。
- 列:
- JSONL(可选辅助):
{basename}_knowledge_base.jsonl- 每行即一条与 CSV 同构的记录,便于程序消费或调试。
- 层级化切分与提炼:
- 使用层级 Chunker 切分文档,构建父子关系与
header_chain。 - 结合父级摘要与层级路径调用 LLM,对每个块进行知识提炼,得到
summary与qa_pairs。
- 使用层级 Chunker 切分文档,构建父子关系与
- 直接产出 QA:
- 将所有块的
qa_pairs扁平化,并为每条 QA 附上category=header_chain。
- 将所有块的
这是 qa-v2 的关键策略,旨在实现真正的“自顶向下、上下文感知”的知识提炼。不同于一次性对所有最细粒度块进行独立提炼,此策略遵循如下逻辑:
-
自顶向下(Top-Down):
- 从文档的根节点(顶层章节)开始提炼,逐层向下遍历节点(BFS 按层处理)。
- 每一层的兄弟节点相互独立,可并行处理,从而在保证上下文质量的同时显著提升吞吐量。
-
摘要回填(Summary Backfill):
- 对某个子节点进行提炼时,会将其父节点的摘要作为“背景知识”注入 Prompt,帮助模型把握更高层的意图与上下文。
- 当父节点不存在时(根节点),使用一个轻量的默认父级摘要以稳定模型行为。
-
层级路径(header_chain):
- 将节点的完整层级路径(例如
一级标题 > 二级标题 > 三级标题)一并传入模型,使问答生成更贴近源文档的组织结构。
- 将节点的完整层级路径(例如
-
语言自适应与稳健性:
- 根据当前块文本的语言分布给出简要的语言偏好提示,支持中英混合,优先中文输出。
- 对极短文本的节点跳过 LLM 调用,直接生成最小摘要占位,避免无效消耗与错误解析。
-
实现位置(Code References):
- 提炼核心逻辑:
src/forma/qa/builder_v2.py中的HierarchicalKnowledgeBuilder._distill_hierarchically()。 - 单块处理与摘要回填:
HierarchicalKnowledgeBuilder._distill_chunk_with_context()。 - 层级路径注入:
header_chain由 Chunk 的metadata["header_chain"]提供。
- 提炼核心逻辑:
-
对比传统做法的优势:
- 传统“所有叶子块独立提炼”的做法缺乏上位语境,容易产生冗余与歧义;本策略通过“父摘要回填”让下层提炼始终在正确的语境中进行。
- BFS 分层并行在保证上下文依赖有序的前提下,最大化利用并行能力,有效提升整体吞吐。
- 移除 Sentence-Transformers 与 sklearn 在 qa-v2 流中的使用。
- 移除 二次 LLM 分类/合成环节,整体耗时显著缩短。
- 分类来源变更:由“动态分类”改为“文档原生标题链(header_chain)”。
输出 CSV 的一行示例:
question,answer,category
"如何去除带字幕的视频中的字幕?","可使用基于帧差和OCR的混合方案……","素材抽取流水线 > 字幕去除 > 本地实现方案"提示:如果你的文档层级较深,
category会完整保留其层级链,便于下游检索与筛选。
项目提供了 Docker 支持,方便容器化部署。
make docker-buildmake docker-runmake docker-logsmake docker-stop通过环境变量配置:
make serve CONVERSION_WORKERS=8 QA_WORKERS=4或在 .env 文件中设置:
CONVERSION_WORKERS=8
QA_WORKERS=4使用开发模式或设置日志级别:
# 方式1:开发模式(自动 DEBUG)
make serve-dev
# 方式2:手动设置
make serve LOG_LEVEL=DEBUG在 .env 文件中设置:
FORMA_CONVERSION_TIMEOUT=1200 # 20分钟
FORMA_QA_TIMEOUT=1200- 检查
callback_url是否可访问 - 查看服务日志中的错误信息(
LOG_LEVEL=DEBUG) - 确保回调接口能接收 POST 请求并返回 2xx 状态码
确保设置了正确的日志级别:
# 启动时设置
make serve LOG_LEVEL=DEBUG
# 或在 .env 文件中
LOG_LEVEL=DEBUG