【大模型&算法工程】大模型应用工具化、忠诚度以及知识库场景下PDF双栏解析问题的讨论

1. 大模型时代应用工具化以及无忠诚度现象讨论

接触大模型久了,也慢慢探到一些大模型能力表现非常自然和突出的场景,比如AI搜索(依赖大模型的理解总结能力)、AI对话(即chat,依赖大模型的生成能力)、AI工具使用(即智能体,依赖大模型的规划能力),应该说目前主流应用就是围绕这三个点进行的,没有例外。比如近期推出的夸克超级框、manus智能体工具、ima知识库都是如此。

另外,对于AI应用,当下用户其实完全没有忠诚度可言,这一特点是与之前移动应用时代app存在天壤之别。移动应用时代,更多是场景的扩展,将原来PC场景衍生到APP场景,用户忠诚的依然是平台资源本身。但到了AI应用时代,只要是哪一个产品使用更快更好更人性化,用户就会果断切换到新的产品。例如deepseek横空出世,包括腾讯、阿里云、字节在内的大厂,直接集成到自家云产品,用户也果断从之前的kimi、豆包、智谱清言,切换到各种deepseek chat工具。还有最近身边朋友看论文用夸克,但我推荐了另一款AI阅读助手后,他发现AI阅读助手表现更好,直接就放弃了夸克,改用我推荐的产品。这种现象和AI应用工具化有关,不需要依赖什么其他的资源,工具本身就是产品,随时可以切换,哪种效率提升更快就用哪一种。

所以说,要想从众多的模型、应用中凸显出来,必然需要不断追求对于场景最优的模型,用户体验越好的产品。这就绕不开产品匠心逻辑。反正当下既要速度也需要沉下心打磨出更好的产品。

说起产品打磨,回到知识库RAG场景,要想RAG能够又好又快又准确返回结果,一开始的文档解析工作就非常值得好好做。最近用了deepseek、智谱清言的工具,发现文档解析能力还有很多提升空间,只支持部分文档的解析,这或许是大模型公司更关注基础模型能力,只要有一个产品就行,但我觉得to C的产品还是有必要投入打磨。

2. 知识库生态建设之双栏PDF解析

对于文档解析,其中一个非常有意思的场景是对PDF双栏文档的解析,如果是按正常的解析工具以行为单位解析,会导致解析后的排版非常糟糕,比如夸克的解析,就不是很好。反而通义的工具表现很不错。

双栏解析的目标:

  • 正确识别左右两栏
  • 先处理左栏内容,再处理右栏内容
  • 每栏内部按照从上到下的顺序排列

这里给一个简单的双栏解析思路:

  • 利用CV工具将 pdf 每一页转换为图片
  • 利用目标检测模型识别每一页中的元素并标注类型,即版面分析(例如yolo)
  • 利用 OCR 来提取每一个元素中文字块信息(例如paddle系列)
  • 利用表格抽取模型识别跨页表格(可以自己训)
  • 完成版面分析,读取版面分析的JSON结果(包括每一模块的坐标信息)
  • 对每一模块元素进行排序,排序逻辑如下:
    • 计算每个元素在页面左右两侧的覆盖比例
    • 根据覆盖比例确定元素属于左栏还是右栏
    • 对于跨栏元素特殊处理
    • 按照"先左栏后右栏,同栏内从上到下"的顺序输出

实施方案:

  • 区分单栏和双栏:

    • 计算所有文本块中心点的横坐标极差。
    • 设定一个阈值(可以调整),如果极差小于阈值,则判定为单栏,否则为双栏。
  • 单栏排序:

    • 直接按中心点纵坐标(top)升序 排序。
  • 双栏排序:

    • 计算页面中线(即所有文本块中心点的平均横坐标)。
    • 文本块分类:
      • 左栏:文本块的 右边界 < 中线
      • 右栏:文本块的 左边界 > 中线
      • 通栏:文本块的 左边界 < 中线 且 右边界 > 中线
    • 排序顺序:
      • 先对 左栏按 top 升序 排序。
      • 再对 右栏按 top 升序 排序。
      • 处理 通栏:
        • 通栏上方 的 右栏拼接到左栏后。
        • 通栏 内容放在其下方。
        • 通栏下方 的 右栏拼接到左栏后。

其中:

  • 页面宽度估计:

    • 如果没有提供 page_width,则从元素坐标中推断最大右边界作为页面宽度,可用于适配不同页面宽度的文档。
  • 中线计算:

    • 采用 page_width / 2 计算页面的中线坐标,然后通过文本块覆盖比例(左/右)来判断其归属。
  • 更精准的左右栏判定:

    • 计算文本块的左侧部分宽度和右侧部分宽度,再计算左右覆盖比例。
    • 如果 90%以上的内容位于左侧,则归入左栏;如果 90%以上内容在右侧,则归入右栏。
    • 这种方式比简单的 "右边界 < 另一文本块的左边界" 方法更加准确,能够适应不同尺寸的文本块,尤其是跨栏情况。
  • 排序逻辑:

    • 左栏文本块按照纵坐标排序,确保从上到下排列。
    • 右栏文本块也按照纵坐标排序。
    • 最终合并:左栏 → 右栏,保证自然的阅读顺序。

代码示例:

python 复制代码
def sort_text_blocks(res):
    """
    对文本块进行排序,支持单栏、双栏、通栏布局
    :param res: 文本块列表,每个文本块包含 'page_idx' 和 'extra_data'(包含 'position' 坐标)
    :return: 经过排序的文本块列表
    """
    # 按页码排序
    res.sort(key=lambda x: get_page_idx_value(x["page_idx"]))
    
    sorted_res = []
    pages = {}
    
    # 按页分组
    for block in res:
        page_idx = get_page_idx_value(block["page_idx"])
        pages.setdefault(page_idx, []).append(block)
    
    # 处理每一页
    for page_idx, blocks in pages.items():
        if not blocks:
            continue
        
        max_page_width = extract_max_page_width(blocks)
        page_center_x = max_page_width / 2
        
        left_column, right_column, full_column = [], [], []
        
        for block in blocks:
            position = block["extra_data"]["position"]
            
            if isinstance(position, list) and len(position) > 0 and isinstance(position[0], list):
                x1, x2, y1, y2 = position[min(1, len(position) - 1)]
            else:
                x1, x2, y1, y2 = position
            
            block_width = x2 - x1
            left_part = max(0, min(x2, page_center_x) - x1)
            right_part = max(0, x2 - max(x1, page_center_x))
            
            left_ratio = left_part / block_width if block_width > 0 else 0
            right_ratio = right_part / block_width if block_width > 0 else 0
            
            if left_ratio >= 0.9:
                left_column.append(block)
            elif right_ratio >= 0.9:
                right_column.append(block)
            else:
                full_column.append(block)
        
        # 按从上到下排序
        key_func = lambda b: get_position_value(b["extra_data"]["position"], 2)
        left_column.sort(key=key_func)
        right_column.sort(key=key_func)
        full_column.sort(key=key_func)
        
        if full_column:
            min_full_top = get_position_value(full_column[0]["extra_data"]["position"], 2)
            max_full_bottom = get_position_value(full_column[-1]["extra_data"]["position"], 3)
            
            sorted_blocks = (
                [b for b in left_column if get_position_value(b["extra_data"]["position"], 3) < min_full_top] +
                [b for b in right_column if get_position_value(b["extra_data"]["position"], 3) < min_full_top] +
                full_column +
                [b for b in left_column if get_position_value(b["extra_data"]["position"], 2) > max_full_bottom] +
                [b for b in right_column if get_position_value(b["extra_data"]["position"], 2) > max_full_bottom]
            )
        else:
            sorted_blocks = left_column + right_column
        
        sorted_res.extend(sorted_blocks)
    
    return sorted_res

3. 参考材料

【1】LLM常见问题(基于 AI 的 pdf 解析)

【2】双栏学术论文转换为单栏Markdown

相关推荐
少林码僧6 小时前
2.9 字段分箱技术详解:连续变量离散化,提升模型效果的关键步骤
人工智能·ai·数据分析·大模型
AI情报挖掘日志7 小时前
AGI-Next前沿峰会「沉思报告」——中国AGI背后的产业逻辑与战略分野
大模型·aminer·大模型研究
程序员黄老师10 小时前
主流向量数据库全面解析
数据库·大模型·向量·rag
何中应12 小时前
快速上架第一个智能体
ai·大模型·智能体开发
victory043113 小时前
大模型学习阶段总结和下一阶段展望
深度学习·学习·大模型
谷哥的小弟15 小时前
Brave Search MCP服务器安装以及客户端连接配置
搜索引擎·大模型·spring ai·mcp·brave search
星云数灵16 小时前
大模型高级工程师考试练习题7
数据库·大模型·阿里云acp·大模型工程师·大模型考试题库·阿里云aca·大模型工程师acp
系'辞19 小时前
【obsidian指南】配置obsidian git插件,实现obsidian数据定时同步到github仓库(Mac电脑)
macos·github·agent·知识库
星云数灵1 天前
大模型高级工程师考试练习题6
人工智能·大模型·大模型工程师·阿里云大模型aca·阿里云大模型工程师acp·大模型acp考试题库·acp认证
索木木2 天前
强化学习与思维链
大模型·sft·强化学习·思维链