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%以上內容在右側,則歸入右欄。
- 這種方式比簡單的 “右邊界 < 另一文本塊的左邊界” 方法更加準確,能夠適應不同尺寸的文本塊,尤其是跨欄情況。
排序邏輯:
- 左欄文本塊按照縱坐標排序,確保從上到下排列。
- 右欄文本塊也按照縱坐標排序。
- 最終合并:左欄 → 右欄,保證自然的閱讀順序。
代碼示例:
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:continuemax_page_width = extract_max_page_width(blocks)page_center_x = max_page_width / 2left_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 = positionblock_width = x2 - x1left_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 0right_ratio = right_part / block_width if block_width > 0 else 0if 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_columnsorted_res.extend(sorted_blocks)return sorted_res
3. 參考材料
【1】LLM常見問題(基于 AI 的 pdf 解析)
【2】雙欄學術論文轉換為單欄Markdown