使用Doclang构建AI原生文档平台

深入探讨为DocLang——为LLM构建的AI原生标记语言——设计端到端演练场的技术细节。

使用Doclang构建AI原生文档平台
AI模型价格对比 | AI工具导航 | ONNX模型库 | Vibe Coding教程 | PLC在线仿真器 | Tripo 3D | Meshy AI | ElevenLabs | KlingAI | ArtSpace | Phot.AI | InVideo

随着大型语言模型(LLM)应用的成熟,一个根本性的摩擦点仍然存在:传统文档格式从未为分词器设计过

  • PDF是为精确的视觉打印布局而构建的。
  • DOCX是为人类桌面编辑器而构建的。
  • HTML充斥着显示标签,结构化元素如表格的代币成本高达5倍。

DocLang是一个由Linux基金会AI与数据(由IBM、NVIDIA、Red Hat、ABBYY和HumanSignal支持)管理的开放文档标准。DocLang将结构化文档清晰地映射到LLM代币,同时在明确的XML标记表示中保留精确的几何形状、语义层次、代码块和公式。

1、什么是Doclang?

摘自Doclang网站。

DocLang是用于非结构化内容的AI原生标记格式——文档、图像、音频和视频 alike。它从底层围绕一个单一原则设计:清晰地映射到LLM代币。这使得DocLang成为使用现代AI读取、写入和推理现实世界内容的最高效方式。

世界的知识存在于为渲染播放而非理解而设计的格式中——PDF、HTML、Word、LaTeX、MP3、MP4。当你将它们交给AI系统时,你会丢失结构、语义、布局、时间,或同时丢失所有内容。而AI确实流利使用的格式也不够好:Markdown无法描述复杂内容,HTML浪费代币,LaTeX模棱两可,而且它们都不能处理音频或视频。

DocLang修复了这个问题:

  • AI原生 — 与LLM分词器1对齐的受控词汇表,保持提示和输出简短且可预测
  • 无损 — 在单一表示中保留结构、语义、布局和几何形状
  • 富有表现力 — 对表格、公式、代码、图表、嵌套列表、表单和具有视觉基础的多模态内容的一流支持
  • 超越文档 — 自然扩展到音频和视频,具有转录、说话者、时间戳、场景和视听基础的原生基元——因此访谈、讲座录音或电影脚本与PDF生活在相同的表示中
  • 明确 — 每个标签只有一个工作,因此相同的内容总是以相同的方式序列化
  • 开放 — ISO标准,开放开发,由行业领导者管理

如果你使用LLM和VLM构建现实世界内容,DocLang就是你一直缺少的基底。

2、实现

为了演示摄取、转换、验证和查询DocLang的完整生命周期,Bob创建并连接了DocLang演示应用程序

┌─────────────────────────────────────────────────────────────────┐
│                        浏览器(UI)                              │
│  ┌───────────┐ ┌───────────┐ ┌──────────┐ ┌─────────────────┐  │
│  │ Markdown  │ │  DocLang  │ │  上传    │ │  询问Ollama     │  │
│  │ → DocLang │ │  编辑器   │ │ 文档     │ │  (文档问答)   │  │
│  └─────┬─────┘ └─────┬─────┘ └────┬─────┘ └────────┬────────┘  │
└────────│─────────────│────────────│───────────────────│─────────┘
         │             │            │                   │
         ▼             ▼            ▼                   ▼
┌─────────────────────────────────────────────────────────────────┐
│                    Flask API (app.py)                            │
│                                                                 │
│  /api/convert/markdown  → markdown_to_doclang()                 │
│  /api/convert/doclang   → doclang_to_html()                     │
│  /api/validate          → validate_doclang()                    │
│  /api/upload/submit     → Docling作业已启动(返回job_id)       │
│  /api/upload/status/<id>→ 轮询作业 → doclang + 预览            │
│  /api/ask               → Ollama /api/generate                  │
│  /api/save              → output/{name}_{timestamp}.dclg.xml    │
│  /api/samples           → input/**/*.dclg.xml                   │
└──────────┬──────────────────────────┬───────────────────────────┘
           │                          │
           ▼                          ▼
  ┌─────────────────┐      ┌──────────────────────┐
  │  Docling引擎    │      │   Ollama(本地LLM)   │
  │  (PDF/DOCX/HTML│      │   granite / 任何     │
  │  → DocLang XML)│      │   已安装模型         │
  └─────────────────┘      └──────────────────────┘

3、系统架构与工作流模式

该应用程序构建为一个轻量级、快速的Flask API,支持多标签 单页应用程序(SPA)界面。它无缝地将客户端交互与重型文档解析引擎和本地LLM桥接起来。

3.1 高级系统架构

DocLang演示应用程序被设计为一个解耦的多层系统,专为低延迟和平稳文档处理而设计。其核心是一个轻量级Flask REST API,协调交互式单页应用程序(SPA)前端、本地文件存储和专门的下游服务工作者之间的请求。为了在繁重的文档转换期间保持UI高度响应,长时间运行的提取任务被卸载到由IBM Docling引擎支持的异步线程池工作者,而基于基础的自然语言查询则直接由本地Ollama LLM实例驱动。

3.2 文档处理与摄取管道

在底层,应用程序平衡了实时用户反馈与繁重的计算任务。该架构基于在Python 3.10+上运行的快速Flask后端,提供直观的多标签单页应用程序。当解析大型PDF时,应用程序不是锁定用户界面,而是将前端UI事件与繁重的后台作业解耦——将文档提取交给后台线程,本地AI推断通过本地API调用交给Ollama。

4、关键实现亮点

将DocLang规范从概念转化为生产就绪的Web应用程序需要解决跨多线程、模式转换和本地LLM编排的关键现实世界边缘情况。app.py中的核心后端实现桥接了底层系统硬件约束与高级解析逻辑。下面,我们分解了应用程序实现的三个基础组件,详细介绍了我们如何解决macOS硬件线程崩溃、创建高效的客户端Markdown转换器,以及将DocLang直接连接到本地Ollama推断。

4.1 解决macOS Metal / PyTorch MPS线程崩溃

在macOS上的后台工作者线程中执行深度学习模型(如Docling的布局和OCR模型)时,PyTorch尝试通过Apple Metal(MPS)分派操作。在非主线程中,这会引发不可恢复的C级中止:MTLCompiler: compileRequest ... Crashing instead.

Bob在app.py中通过在任何导入发生之前强制PyTorch MPS回退,并将Docling显式固定到CPU线程池,从而干净地解决了这个问题:

import os

# 必须在导入torch或docling之前配置
os.environ.setdefault("PYTORCH_ENABLE_MPS_FALLBACK", "1")

from docling.document_converter import DocumentConverter, PdfFormatOption
from docling.datamodel.pipeline_options import PdfPipelineOptions, AcceleratorOptions, AcceleratorDevice

def _make_converter():
    """构建固定到CPU的DocumentConverter,以确保macOS上的线程安全执行。"""
    accel = AcceleratorOptions(num_threads=4, device=AcceleratorDevice.CPU)

    opts = PdfPipelineOptions()
    opts.accelerator_options = accel

    return DocumentConverter(
        format_options={
            "pdf": PdfFormatOption(pipeline_options=opts),
        }
    )

4.2 手动Markdown到DocLang XML解析器

对于不需要调用重型神经模型的快速客户端转换,app.py具有自定义的Markdown到DocLang解析器。它将标准Markdown元素转换为原生DocLang标签:

def markdown_to_doclang(md_text: str) -> str:
    """将简化的Markdown转换为有效的DocLang XML标记。"""
    lines = md_text.strip().splitlines()
    elements: list[str] = []
    i = 0
    while i < len(lines):
        line = lines[i]
        stripped = line.strip()

        if stripped.startswith("### "):
            elements.append(f'  <heading level="3">{stripped[4:]}</heading>')
        elif stripped.startswith("## "):
            elements.append(f'  <heading level="2">{stripped[3:]}</heading>')
        elif stripped.startswith("# "):
            elements.append(f'  <heading level="1">{stripped[2:]}</heading>')
        elif stripped.startswith("```"):
            lang = stripped[3:].strip() or "text"
            code_lines: list[str] = []
            i += 1
            while i < len(lines) and not lines[i].strip().startswith("```"):
                code_lines.append(lines[i])
                i += 1
            code_body = "\n".join(code_lines)
            elements.append(
                f'  <code>\n    <label value="{lang}"/>\n'
                f'    <content><![CDATA[\n{code_body}\n    ]]></content>\n  </code>'
            )
        elif stripped.startswith("- "):
            list_items: list[str] = []
            while i < len(lines) and lines[i].strip().startswith("- "):
                list_items.append(f"    <item><text>{lines[i].strip()[2:]}</text></item>")
                i += 1
            elements.append("  <list>\n" + "\n".join(list_items) + "\n  </list>")
            continue
        elif stripped:
            elements.append(f"  <text>{stripped}</text>")

        i += 1

    return "<doclang>\n" + "\n".join(elements) + "\n</doclang>"

4.3 使用Ollama的本地LLM问答集成

DocLang简洁的XML表示使其最适合直接包含在系统提示中。应用程序将用户查询和DocLang标记直接路由到本地Ollama模型:

def ask_ollama(question: str, context_dclg: str) -> str:
    """使用本地Ollama实例对解析的DocLang XML执行基于基础的问答。"""
    prompt = textwrap.dedent(f"""
        You are a helpful assistant. The following is a document in DocLang format
        (an AI-native XML document standard). Answer the user's question using only
        the information present in the document.

        <document>
        {context_dclg}
        </document>

        Question: {question}
        Answer:
    """).strip()

    resp = requests.post(
        f"{OLLAMA_BASE_URL}/api/generate",
        json={"model": OLLAMA_MODEL, "prompt": prompt, "stream": False},
        timeout=60,
    )
    return resp.json().get("response", "").strip()

5、token效率基准测试

与传统HTML表示相比,DocLang大幅削减了上下文窗口消耗——在包含复杂表格、标题和代码示例的标准文档上实现了约64.2%的代币节省

6、自动化测试套件

为了确保后台任务队列、文件IO和API验证的可靠性,Bob使用tests/test_app.py中的pytest设计了单元测试套件:

class TestAPIRoutes:
    def test_api_convert_markdown(self, client):
        resp = client.post("/api/convert/markdown", json={"markdown": "# Hello\n\nWorld."})
        assert resp.status_code == 200
        data = resp.get_json()
        assert "doclang" in data
        assert "html_preview" in data
        assert "tokens" in data

    def test_api_upload_submit_creates_job(self, client):
        from io import BytesIO
        data = {"file": (BytesIO(b"<html><body>Hello</body></html>"), "test.html")}
        resp = client.post("/api/upload/submit", data=data, content_type="multipart/form-data")
        assert resp.status_code == 200
        body = resp.get_json()
        assert "job_id" in body

        # 轮询异步状态
        job_resp = client.get(f"/api/upload/status/{body['job_id']}")
        assert job_resp.status_code == 200

7、结束语

Bob设计的DocLang演示应用程序为将下一代AI原生文档格式集成到企业软件工作流中提供了完整的蓝图。

通过将IBM Docling的多格式提取DocLang的代币优化XML模式Ollama的本地LLM推理配对,开发人员可以构建隐私优先、闪电般快速的文档智能管道。


原文链接: Building an AI-Native Document Platform with Doclang

汇智网翻译整理,转载请标明出处