自拍偷在线精品自拍偷,亚洲欧美中文日韩v在线观看不卡

RAGFlow框架優(yōu)化經(jīng)驗(yàn)分享(附代碼):圖文識(shí)別+動(dòng)態(tài)分塊 、API調(diào)優(yōu)+源碼修改

開發(fā)
這篇以 RAGFlow 框架為例,針對(duì)上述后三個(gè)問題結(jié)合目前團(tuán)隊(duì)實(shí)踐經(jīng)驗(yàn),給各位做個(gè)分享,大家辨證參考。

在這個(gè)過程中很高興能了解到很多之前沒涉獵過的行業(yè)和場(chǎng)景,其中經(jīng)常被集中問到的問題大概有四個(gè):中小企業(yè)做RAG知識(shí)庫落地選擇框架哪個(gè)比較好?如果選擇 RAGFlow 如何進(jìn)行定制化開發(fā)?如何對(duì)文檔中的圖片進(jìn)行識(shí)別檢索?如何對(duì)復(fù)雜文檔進(jìn)行動(dòng)態(tài)分塊等。

圖片

關(guān)于選擇什么框架的問題,仁者見仁,需要綜合對(duì)比的盆友可以參考我之前發(fā)的那篇文章企業(yè)RAG落地避坑指南:自主開發(fā) vs 三大框架,核心配置與選型全解析。這篇以 RAGFlow 框架為例,針對(duì)上述后三個(gè)問題結(jié)合目前團(tuán)隊(duì)實(shí)踐經(jīng)驗(yàn),給各位做個(gè)分享,大家辨證參考。

畢竟,RAG并沒有“一招鮮”的神奇魔法,傳說那幾個(gè)大廠手里掌握的RAG”核心技術(shù)“,私以為也是經(jīng)過了必要且復(fù)雜的“策略優(yōu)化-管道設(shè)計(jì)-訓(xùn)練-調(diào)優(yōu)-發(fā)布”等專業(yè)開發(fā)流程,不過成熟的開源應(yīng)用框架,無疑是更有想象空間的社會(huì)化大創(chuàng)新。

注:本篇會(huì)比較偏開發(fā)導(dǎo)向,非技術(shù)向盆友選擇性翻翻就好。

以下,enjoy:

1、優(yōu)化實(shí)施路線圖

以上篇介紹的機(jī)械加工行業(yè)的維保場(chǎng)景為例RAGFlow+DeepSeek-R1:14b落地案例分享(足夠詳細(xì)):機(jī)加工行業(yè)設(shè)備維保場(chǎng)景,推薦先采用官方的 Python API 進(jìn)行合理的優(yōu)化配置后,再修改項(xiàng)目源碼,最后根據(jù)業(yè)務(wù)目標(biāo)做必要的高級(jí)優(yōu)化拓展。此處附上一張個(gè)人項(xiàng)目實(shí)施過程中積累的些優(yōu)化要點(diǎn),供各位參考:

圖片

1.1 階段一:API 優(yōu)化配置

配置不同文檔類型的解析策略

調(diào)整檢索參數(shù)優(yōu)化語義搜索質(zhì)量

定制大模型提示詞以適應(yīng)機(jī)械行業(yè)特點(diǎn)

1.2 階段二:基礎(chǔ)源碼修改

實(shí)現(xiàn)專業(yè)術(shù)語處理模塊

開發(fā)查詢路由機(jī)制

增加上下文增強(qiáng)功能

1.3 階段三:高級(jí)優(yōu)化擴(kuò)展

實(shí)現(xiàn)多級(jí)索引結(jié)構(gòu)

開發(fā)高性能緩存機(jī)制

添加查詢?nèi)罩痉治鱿到y(tǒng)

2、6 個(gè)官方 Python API 優(yōu)化全解析

根據(jù) RAGFlow 官方 Python API 文檔,為大家完整梳理了所有可進(jìn)行 API 優(yōu)化的模塊和參數(shù)。這些 API 調(diào)優(yōu)可以不修改源碼,直接通過參數(shù)配置實(shí)現(xiàn)性能提升。

建議在看完之后還是去官網(wǎng)看下原文,親自動(dòng)手試過一遍會(huì)有不一樣的體感。原文出處:

圖片

https://ragflow.io/docs/dev/python_api_reference

2.1 數(shù)據(jù)集管理

創(chuàng)建數(shù)據(jù)集 create_dataset

RAGFlow.create_dataset(
    name: str,
    avatar: str = "",
    description: str = "",
    embedding_model: str = "BAAI/bge-large-zh-v1.5",
    language: str = "English",
    permission: str = "me", 
    chunk_method: str = "naive",
    parser_config: DataSet.ParserConfig = None
)

優(yōu)化參數(shù):

  • embedding_model: 選擇合適的嵌入模型,影響檢索質(zhì)量

中文場(chǎng)景推薦: "BAAI/bge-large-zh-v1.5"

英文場(chǎng)景可選: "BAAI/bge-large-en-v1.5"

  • language: 選擇與文檔匹配的語言
  • chunk_method: 關(guān)鍵參數(shù),根據(jù)文檔類型選擇最佳分塊策略

"naive": 通用文檔

"paper": 論文/設(shè)備手冊(cè)

"book": 結(jié)構(gòu)化書籍

"table": 表格數(shù)據(jù)

"qa": 問答格式文檔

"picture": 圖片文檔

"one": 整個(gè)文檔作為一個(gè)塊

"knowledge_graph": 知識(shí)圖譜

  • parser_config: 精細(xì)調(diào)整解析配置

chunk_token_num: 控制分塊大小 

delimiter: 自定義分隔符 

layout_recognize: 是否啟用布局識(shí)別 

raptor: 高級(jí)解析選項(xiàng)

更新數(shù)據(jù)集 DataSet.update

DataSet.update(update_message : dict)

優(yōu)化參數(shù):

embedding_model: 更換更適合的嵌入模型 

chunk_method: 調(diào)整分塊策略

 meta_fields: 更新元數(shù)據(jù)字段

2.2 文件管理 (FILE MANAGEMENT)

上傳文檔 DataSet.upload_documents

DataSet.upload_documents(document_list : list[dict])

優(yōu)化參數(shù):

display_name: 文件顯示名,方便檢索與管理 

blob: 文件內(nèi)容

更新文檔 Document.update

Document.update(update_message : dict)

優(yōu)化參數(shù):

  • chunk_method: 文檔分塊方法 
  • parser_config: 文檔解析配置

對(duì)于圖文文檔,可設(shè)置: "layout_recognize": True

對(duì)于表格文檔,可設(shè)置: "html4excel": True

解析文檔 

DataSet.async_parse_documents(document_ids : list[str])

用于觸發(fā)文檔解析流程,支持批量處理。

2.3 分塊管理 (CHUNK MANAGEMENT)

添加分塊 Document.add_chunk

Document.add_chunk(content: str, important_keywords: list[str] = [])

優(yōu)化參數(shù):

important_keywords: 關(guān)鍵詞標(biāo)注,增強(qiáng)檢索相關(guān)性

更新分塊 Chunk.update

Chunk.update(update_message : dict)

優(yōu)化參數(shù):

content: 更新分塊內(nèi)容 

important_keywords: 更新關(guān)鍵詞

 available: 控制分塊可用性

檢索 RAGFlow.retrieve(關(guān)鍵API)

RAGFlow.retrieve(
    question: str = "",
    dataset_ids: list[str] = None,
    document_ids: list[str] = None,
    page: int = 1,
    page_size: int = 30,
    similarity_threshold: float = 0.2,
    vector_similarity_weight: float = 0.3,
    top_k: int = 1024,
    rerank_id: str = None,
    keyword: bool = False,
    highlight: bool = False
)

優(yōu)化參數(shù) (最重要的檢索相關(guān)參數(shù)):

  • similarity_threshold: 相似度閾值,影響召回范圍
  •  vector_similarity_weight: 向量相似度權(quán)重與關(guān)鍵詞匹配權(quán)重的比例

設(shè)置為 0-1 之間,值越大向量權(quán)重越高

工業(yè)領(lǐng)域建議參考0.3-0.5,平衡專業(yè)術(shù)語與語義理解

  • top_k: 參與向量檢索的 chunk 數(shù)量,影響檢索范圍 
  • rerank_id: 重排序模型 ID,提升檢索精度 
  • keyword: 開啟關(guān)鍵詞匹配,對(duì)專業(yè)領(lǐng)域極其有用 
  • highlight: 高亮匹配內(nèi)容,幫助理解匹配原因

2.4 聊天助手管理 

創(chuàng)建聊天助手 RAGFlow.create_chat

RAGFlow.create_chat(
    name: str, 
    avatar: str = "", 
    dataset_ids: list[str] = [], 
    llm: Chat.LLM = None, 
    prompt: Chat.Prompt = None
)

優(yōu)化參數(shù):

  • llm: LLM 模型配置

model_name: 模型名稱 

temperature: 溫度,影響創(chuàng)造性 

top_p: 詞匯采樣范圍 

presence_penalty: 重復(fù)懲罰 

frequency_penalty: 頻率懲罰 

max_token: 最大輸出 token 數(shù)

  • prompt: 提示詞配置

similarity_threshold: 相似度閾值 

keywords_similarity_weight: 關(guān)鍵詞相似度權(quán)重 

top_n: 提供給 LLM 的 chunk 數(shù)量 

rerank_model: 重排序模型 

top_k: 重排序參與的候選數(shù)量 empty_response: 無匹配時(shí)的回復(fù) 

show_quote: 是否顯示引用來源 

prompt: 系統(tǒng)提示詞內(nèi)容

更新聊天助手 Chat.update

Chat.update(update_message : dict)

優(yōu)化參數(shù): 同 create_chat 中的參數(shù)

2.5 會(huì)話管理 (SESSION MANAGEMENT)

創(chuàng)建會(huì)話 Chat.create_session

Chat.create_session(name: str = "New session")

提問 Session.ask

Session.ask(question: str = "", stream: bool = False, **kwargs)

優(yōu)化參數(shù):

stream: 流式輸出,提升用戶體驗(yàn)

**kwargs: 可傳遞給 prompt 中定義的變量

2.6 代理管理 (AGENT MANAGEMENT)

創(chuàng)建代理會(huì)話 Agent.create_session

Agent.create_session(id, rag, **kwargs)

代理提問 Session.ask

Session.ask(question: str = "", stream: bool = False)

與普通會(huì)話的ask方法類似。

3、調(diào)整項(xiàng)目源碼思路參考

3.1 專業(yè)術(shù)語處理

需要在檢索引擎層面添加工業(yè)領(lǐng)域同義詞和術(shù)語映射:

# 需要修改源碼的示例邏輯
class CustomTerminologyProcessor:
    def __init__(self, terminology_mapping):
        self.terminology_mapping = terminology_mapping  # 同義詞映射表


    def process_query(self, query):
        # 專業(yè)術(shù)語標(biāo)準(zhǔn)化
        # 車間俚語轉(zhuǎn)換為標(biāo)準(zhǔn)術(shù)語
        processed_query = query
        for slang, standard_term in self.terminology_mapping.items():
            processed_query = processed_query.replace(slang, standard_term)
        return processed_query

修改點(diǎn):

在查詢預(yù)處理階段添加定制的術(shù)語處理模塊

需要在 RAGFlow 的查詢管道中修改源碼添加此功能

3.2 多級(jí)索引結(jié)構(gòu)實(shí)現(xiàn)

需要定制 Milvus 索引策略,實(shí)現(xiàn)基礎(chǔ)索引層和語義索引層的混合索引:

# 這部分需要修改源碼,以下是概念性代碼
class CustomHybridIndexBuilder:
    def __init__(self, vector_db_client):
        self.client = vector_db_client


    def create_scalar_indices(self, collection_name, fields):
        # 創(chuàng)建設(shè)備編號(hào)、故障代碼等標(biāo)量索引
        for field in fields:
            self.client.create_index(collection_name, field, "scalar")


    def create_vector_indices(self, collection_name, fields):
        # 創(chuàng)建向量索引
        for field in fields:
            self.client.create_index(collection_name, field, {"index_type": "HNSW", "params": {"M": 16, "efConstruction": 200}})

修改點(diǎn):

修改 RAGFlow 的索引構(gòu)建模塊擴(kuò)展

 Milvus 客戶端接口以支持多索引策略

3.3 查詢路由設(shè)計(jì)

需要實(shí)現(xiàn)定制化的查詢路由邏輯,識(shí)別不同類型的查詢并路由到最合適的檢索通道:

# 查詢路由器 - 需要源碼修改實(shí)現(xiàn)
class QueryRouter:
    def route_query(self, query_text):
        if self._is_equipment_code(query_text):
            return "exact_match", {"field": "equipment_code"}
        elif self._is_fault_code(query_text):
            return "exact_match", {"field": "fault_code"}
        elif self._is_parameter_query(query_text):
            return "parameter_lookup", {"field": "parameter_name"}
        else:
            return "semantic_search", {"model": "embedding_model"}

修改點(diǎn):

在 RAGFlow 的查詢處理流程中添加查詢分類和路由機(jī)制

實(shí)現(xiàn)針對(duì)不同查詢類型的專用處理通道

3.4 上下文增強(qiáng)機(jī)制

增加查詢上下文增強(qiáng),融入設(shè)備信息、歷史記錄等:

# 上下文增強(qiáng)器 - 需要修改源碼實(shí)現(xiàn)
class ContextEnhancer:
    def enhance_query(self, query, session_history, equipment_metadata=None):
        # 添加設(shè)備上下文信息
        if equipment_metadata:
            query_context = f"設(shè)備型號(hào): {equipment_metadata['model']}, 生產(chǎn)年份: {equipment_metadata['year']}\n"
            query_context += query


        # 添加歷史查詢信息
        if session_history:
            relevant_history = self._extract_relevant_history(session_history, query)
            query_context = f"參考?xì)v史信息: {relevant_history}\n" + query_context


        return query_context

修改點(diǎn):

修改會(huì)話管理模塊,實(shí)現(xiàn)會(huì)話狀態(tài)跟蹤

增加設(shè)備元數(shù)據(jù)關(guān)聯(lián)機(jī)制

在查詢處理流程中加入上下文增強(qiáng)步驟

4、圖文結(jié)合文檔處理方案

依然是兩種方案,直接使用RAGFlow API方案優(yōu)勢(shì)是更簡(jiǎn)單,使用現(xiàn)有功能,無需額外的模型調(diào)用,也能夠直接顯示原始圖片,視覺效果更好,處理速度更快,不依賴外部 API,當(dāng)然成本也無疑更低。

但使用一個(gè)多模態(tài)模型進(jìn)行預(yù)處理的方案優(yōu)勢(shì)也很明顯,圖片內(nèi)容被轉(zhuǎn)換為文本,便于向量化和語義搜索,也可以依托多模態(tài)模型的能力,提供更豐富的圖片內(nèi)容解釋。

圖片

目前實(shí)際測(cè)試下來,采用兩種方案的組合,效果更加穩(wěn)定。

4.1 使用多模態(tài)預(yù)處理生成圖片描述

# 使用多模態(tài)模型生成圖片描述
processor = MultimodalDocumentProcessor(api_key="YOUR_API_KEY")
enhanced_docs = processor.process_pdf("設(shè)備手冊(cè).pdf")

4.2 使用 RAGFlow 處理和存儲(chǔ)原始圖片

# 配置保留圖片的數(shù)據(jù)集
dataset = rag_object.create_dataset(
    name="圖文設(shè)備手冊(cè)",
    chunk_method="paper",
    parser_cnotallow={"layout_recognize": True}
)


# 上傳原始PDF文檔
with open("設(shè)備手冊(cè).pdf", "rb") as f:
    dataset.upload_documents([{"display_name": "設(shè)備手冊(cè).pdf", "blob": f.read()}])

4.3 創(chuàng)建能夠提供文本描述和圖片引用的助手

assistant = rag_object.create_chat(
    name="圖文設(shè)備專家",
    dataset_ids=[dataset.id],
    prompt=Chat.Prompt(
        prompt="""你是設(shè)備維修專家。回答時(shí),請(qǐng)同時(shí)提供:
        1. 文字描述解釋故障和解決方案
        2. 引用相關(guān)圖片,包括圖片描述
        3. 告訴用戶可以參考哪些圖片獲取更多信息
        {knowledge}"""
    )
)

4.4 源碼修改的一些建議

增強(qiáng)圖片提取和處理:

修改 PDF 解析器,更準(zhǔn)確地綁定文本和相關(guān)圖片

增加圖片內(nèi)容分析功能,自動(dòng)標(biāo)注圖片類型(如"故障圖"、"結(jié)構(gòu)圖"等)

實(shí)現(xiàn)圖文混合索引:

為圖片創(chuàng)建特殊索引,支持通過圖片內(nèi)容或相關(guān)文本檢索圖片

在檢索結(jié)果中包含圖片 URL 或直接嵌入圖片

改進(jìn)響應(yīng)生成:

修改聊天助手的響應(yīng)生成邏輯,自動(dòng)識(shí)別圖片引用

在生成的回答中包含相關(guān)圖片或圖片鏈接

5、動(dòng)態(tài)分塊策略參考

RAGFlow的chunk_method參數(shù)是在數(shù)據(jù)集級(jí)別或文檔級(jí)別設(shè)置的,不支持在單個(gè)文檔內(nèi)部動(dòng)態(tài)切換不同的分塊策略。但實(shí)際情況是,同一文檔中的不同部分可能需要不同的處理方式,比如針對(duì)段落、圖片、表格、圖表等,使用單一的分塊策略很難同時(shí)兼顧所有這些內(nèi)容類型的特點(diǎn)。

5.1 4種分塊策略對(duì)比

源碼修改方案:

修改 RAGFlow 的文檔解析器,使其能夠識(shí)別文檔中的不同部分并應(yīng)用不同的分塊策略,但這需要深入修改 RAGFlow 的核心處理邏輯,如果沒有深入理解RAGFlow的全局代碼,建議不要這么做。

文檔預(yù)處理方案:

在上傳到 RAGFlow 前預(yù)處理文檔,將其拆分成不同類型的子文檔。例如,將設(shè)備手冊(cè)拆分為純文本部分、表格部分、圖文部分等然后分別上傳到不同的數(shù)據(jù)集,每個(gè)數(shù)據(jù)集使用適合的分塊策略。

這個(gè)方案優(yōu)點(diǎn)是,可以充分利用RAGFlow針對(duì)不同內(nèi)容類型的專門分塊策略,但問題也很明顯,就是文檔上下文被拆分,可能影響整體理解。

自定義分塊方案:

不使用RAGFlow 的自動(dòng)分塊,而是手動(dòng)控制分塊,使用chunk_method="one"將整個(gè)文檔作為一個(gè)塊導(dǎo)入,然后使用自定義邏輯創(chuàng)建更細(xì)粒度的分塊。這種做法無疑可以實(shí)現(xiàn)最精細(xì)的控制、保留文檔完整性,當(dāng)然缺點(diǎn)就是實(shí)現(xiàn)上會(huì)相對(duì)麻煩。

混合模型方案:

創(chuàng)建多個(gè)使用不同分塊策略的數(shù)據(jù)集,將同一文檔上傳到所有這些數(shù)據(jù)集,在檢索時(shí)查詢所有數(shù)據(jù)集并合并結(jié)果。這種方法保持文檔完整性,但創(chuàng)建多個(gè)使用不同分塊策略的副本,會(huì)造成存儲(chǔ)冗余,檢索時(shí)需要合并多個(gè)結(jié)果集。

5.2 推薦自定義方案

這種方法使用RAGFlow的API但完全控制分塊過程,最靈活且無需修改源碼。

圖片

完整保留原始文檔:

使用 chunk_method="one"將文檔整體上傳保留文檔的完整性和上下文關(guān)系

自定義內(nèi)容識(shí)別:

使用 PyMuPDF 識(shí)別文檔中的不同內(nèi)容類型:

文本、段落、表格內(nèi)容圖片及其相關(guān)描述、章節(jié)標(biāo)題和結(jié)構(gòu)

動(dòng)態(tài)創(chuàng)建精細(xì)分塊:

文本塊: 基于段落和語義分界

表格塊: 保留表格結(jié)構(gòu)和行列關(guān)系

圖文塊: 關(guān)聯(lián)圖片和周圍的描述文本

添加分塊類型標(biāo)記:

每個(gè)分塊添加類型標(biāo)識(shí)符[text], [table], [image]等

方便檢索時(shí)區(qū)分不同類型的內(nèi)容

豐富關(guān)鍵詞提取:

為每個(gè)分塊提取相關(guān)關(guān)鍵詞

保留章節(jié)上下文信息


from ragflow_sdk import RAGFlow
import fitz  # PyMuPDF
import re
import pandas as pd
import io
import logging
from typing import List, Dict, Any, Tuple


logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(name)s - %(levelname)s - %(message)s')
logger = logging.getLogger(__name__)


class DynamicChunker:
    """針對(duì)混合格式文檔的自定義分塊器"""


    def __init__(self, api_key: str, base_url: str):
        self.rag_object = RAGFlow(api_key=api_key, base_url=base_url)


    def process_document(self, pdf_path: str, dataset_name: str = None) -> str:
        """處理PDF文檔并實(shí)現(xiàn)自定義分塊"""
        # 1. 創(chuàng)建或獲取數(shù)據(jù)集
        if dataset_name:
            datasets = self.rag_object.list_datasets(name=dataset_name)
            if datasets:
                dataset = datasets[0]
                logger.info(f"使用現(xiàn)有數(shù)據(jù)集: {dataset.name}, ID: {dataset.id}")
            else:
                dataset = self.rag_object.create_dataset(
                    name=dataset_name,
                    embedding_model="BAAI/bge-large-zh-v1.5",
                    language="Chinese",
                    chunk_method="one"  # 使用"one"方式,保持文檔完整性
                )
                logger.info(f"創(chuàng)建新數(shù)據(jù)集: {dataset.name}, ID: {dataset.id}")
        else:
            dataset = self.rag_object.create_dataset(
                name="混合文檔庫",
                embedding_model="BAAI/bge-large-zh-v1.5",
                language="Chinese",
                chunk_method="one"
            )
            logger.info(f"創(chuàng)建新數(shù)據(jù)集: {dataset.name}, ID: {dataset.id}")


        # 2. 上傳文檔
        with open(pdf_path, "rb") as f:
            document_blob = f.read()


        docs = dataset.upload_documents([{
            "display_name": pdf_path.split("/")[-1],
            "blob": document_blob
        }])


        if not docs:
            logger.error("文檔上傳失敗")
            return None


        doc = docs[0]
        logger.info(f"文檔上傳成功, ID: {doc.id}")


        # 3. 分析文檔結(jié)構(gòu)并創(chuàng)建自定義分塊
        chunks = self._create_custom_chunks(pdf_path)
        logger.info(f"創(chuàng)建了 {len(chunks)} 個(gè)自定義分塊")


        # 4. 手動(dòng)添加分塊到文檔
        chunk_ids = []
        for chunk in chunks:
            content = chunk["content"]
            keywords = chunk["keywords"]
            chunk_type = chunk["type"]


            # 添加分塊類型標(biāo)記以便未來檢索
            content_with_marker = f"[{chunk_type}]\n{content}"


            # 添加分塊
            new_chunk = doc.add_chunk(
                cnotallow=content_with_marker,
                important_keywords=keywords
            )


            chunk_ids.append(new_chunk.id)
            logger.info(f"添加分塊: 類型={chunk_type}, ID={new_chunk.id}, 關(guān)鍵詞數(shù)量={len(keywords)}")


        return doc.id


    def _create_custom_chunks(self, pdf_path: str) -> List[Dict[str, Any]]:
        """分析PDF并創(chuàng)建自定義分塊"""
        pdf_doc = fitz.open(pdf_path)
        chunks = []


        # 用于存儲(chǔ)當(dāng)前上下文
        current_section = ""
        current_text_chunk = ""
        current_table_data = []
        current_keywords = []


        for page_idx, page in enumerate(pdf_doc):
            # 提取頁面文本
            text = page.get_text()


            # 檢測(cè)章節(jié)標(biāo)題
            section_headers = self._detect_section_headers(text)
            if section_headers:
                # 如果有積累的文本塊,先保存
                if current_text_chunk:
                    chunks.append({
                        "content": current_text_chunk,
                        "keywords": list(set(current_keywords)),
                        "type": "text",
                        "section": current_section
                    })
                    current_text_chunk = ""
                    current_keywords = []


                # 更新當(dāng)前章節(jié)
                current_section = section_headers[0]
                current_keywords.append(current_section)


            # 檢測(cè)表格
            tables = self._detect_tables(page)
            if tables:
                # 如果有積累的文本塊,先保存
                if current_text_chunk:
                    chunks.append({
                        "content": current_text_chunk,
                        "keywords": list(set(current_keywords)),
                        "type": "text",
                        "section": current_section
                    })
                    current_text_chunk = ""
                    current_keywords = []


                # 處理表格
                for table in tables:
                    table_content = self._format_table(table)
                    table_keywords = self._extract_keywords_from_table(table)


                    chunks.append({
                        "content": table_content,
                        "keywords": list(set(table_keywords + current_keywords)),
                        "type": "table",
                        "section": current_section
                    })


            # 檢測(cè)圖片
            images = self._detect_images(page)
            for img_idx, img in enumerate(images):
                # 提取圖片周圍的文本作為上下文
                img_context = self._extract_image_context(text, img["bbox"])


                # 如果找到圖片相關(guān)文本
                if img_context:
                    # 如果有積累的文本塊,先保存
                    if current_text_chunk:
                        chunks.append({
                            "content": current_text_chunk,
                            "keywords": list(set(current_keywords)),
                            "type": "text",
                            "section": current_section
                        })
                        current_text_chunk = ""
                        current_keywords = []


                    # 創(chuàng)建圖文塊
                    image_text_content = f"圖片描述: {img_context}\n圖片位置: 第{page_idx+1}頁"
                    image_keywords = self._extract_keywords(img_context)


                    chunks.append({
                        "content": image_text_content,
                        "keywords": list(set(image_keywords + current_keywords)),
                        "type": "image",
                        "section": current_section
                    })


                    # 防止重復(fù)處理同一段文本
                    text = text.replace(img_context, "", 1)


            # 處理剩余文本
            if text.strip():
                # 按段落拆分
                paragraphs = self._split_into_paragraphs(text)


                for para in paragraphs:
                    if len(para.strip()) < 10:  # 忽略太短的段落
                        continue


                    # 累積文本直到達(dá)到合適的大小
                    current_text_chunk += para + "\n\n"
                    current_keywords.extend(self._extract_keywords(para))


                    # 檢查是否應(yīng)該創(chuàng)建新的文本塊
                    if len(current_text_chunk) > 1500:  # 大約300-500個(gè)詞
                        chunks.append({
                            "content": current_text_chunk,
                            "keywords": list(set(current_keywords)),
                            "type": "text",
                            "section": current_section
                        })
                        current_text_chunk = ""
                        current_keywords = []


        # 處理最后一個(gè)文本塊
        if current_text_chunk:
            chunks.append({
                "content": current_text_chunk,
                "keywords": list(set(current_keywords)),
                "type": "text",
                "section": current_section
            })


        return chunks


    def _detect_section_headers(self, text: str) -> List[str]:
        """檢測(cè)章節(jié)標(biāo)題"""
        # 這是一個(gè)簡(jiǎn)化的實(shí)現(xiàn),可以根據(jù)實(shí)際文檔格式調(diào)整
        header_patterns = [
            r"^第[一二三四五六七八九十\d]+章\s*(.+)$",
            r"^\d+\.\d*\s+(.+)$",
            r"^[一二三四五六七八九十]+[、..]\s*(.+)$"
        ]


        headers = []
        for line in text.split("\n"):
            line = line.strip()
            for pattern in header_patterns:
                match = re.match(pattern, line)
                if match:
                    headers.append(line)
                    break


        return headers


    def _detect_tables(self, page) -> List[Any]:
        """檢測(cè)頁面中的表格"""
        # 這里使用簡(jiǎn)化的檢測(cè)邏輯,實(shí)際應(yīng)用中可能需要更復(fù)雜的表格檢測(cè)算法
        # 例如,可以尋找包含多個(gè)垂直和水平線的區(qū)域
        tables = []


        # 簡(jiǎn)單表格檢測(cè):查找水平線和垂直線的集中區(qū)域
        # 這里僅作為占位示例,實(shí)際實(shí)現(xiàn)會(huì)更復(fù)雜
        horizontal_lines = []
        vertical_lines = []


        for drawing in page.get_drawings():
            for item in drawing["items"]:
                if item["type"] == "l":  # 線段
                    x0, y0, x1, y1 = item["rect"]
                    if abs(y1 - y0) < 2:  # 水平線
                        horizontal_lines.append((x0, y0, x1, y1))
                    elif abs(x1 - x0) < 2:  # 垂直線
                        vertical_lines.append((x0, y0, x1, y1))


        # 簡(jiǎn)單的表格判定:有足夠多的水平線和垂直線
        if len(horizontal_lines) >= 3 and len(vertical_lines) >= 2:
            # 找出所有線的邊界,作為表格邊界
            min_x = min([min(l[0], l[2]) for l in horizontal_lines + vertical_lines])
            max_x = max([max(l[0], l[2]) for l in horizontal_lines + vertical_lines])
            min_y = min([min(l[1], l[3]) for l in horizontal_lines + vertical_lines])
            max_y = max([max(l[1], l[3]) for l in horizontal_lines + vertical_lines])


            # 提取表格區(qū)域的文本
            table_rect = fitz.Rect(min_x, min_y, max_x, max_y)
            table_text = page.get_text("text", clip=table_rect)


            # 簡(jiǎn)化的表格結(jié)構(gòu)化(實(shí)際應(yīng)用中需要更復(fù)雜的邏輯)
            table_rows = table_text.split("\n")
            table = []
            for row in table_rows:
                if row.strip():
                    cells = row.split()
                    if len(cells) >= 2:  # 至少有2個(gè)單元格才視為有效行
                        table.append(cells)


            if table:
                tables.append(table)


        return tables


    def _detect_images(self, page) -> List[Dict[str, Any]]:
        """檢測(cè)頁面中的圖片"""
        images = []


        # 獲取頁面上的圖片對(duì)象
        img_list = page.get_images(full=True)


        for img_idx, img_info in enumerate(img_list):
            xref = img_info[0]
            base_image = page.parent.extract_image(xref)


            if base_image:
                # 尋找圖片在頁面上的位置
                img_rects = []
                for rect in page.get_image_rects(xref):
                    img_rects.append(rect)


                if img_rects:
                    # 使用第一個(gè)找到的位置
                    bbox = img_rects[0]


                    images.append({
                        "bbox": [bbox.x0, bbox.y0, bbox.x1, bbox.y1]
                    })


        return images


    def _extract_image_context(self, text: str, bbox: List[float], context_size: int = 200) -> str:
        """提取圖片周圍的文本作為上下文"""
        # 在文本中查找可能的圖片標(biāo)題,如"圖1","Figure 1"等
        caption_patterns = [
            r"圖\s*\d+[\..:]?\s*(.+?)(?:\n|$)",
            r"Figure\s*\d+[\.:]?\s*(.+?)(?:\n|$)",
            r"圖表\s*\d+[\..:]?\s*(.+?)(?:\n|$)"
        ]


        for pattern in caption_patterns:
            matches = re.finditer(pattern, text, re.IGNORECASE)
            for match in matches:
                return match.group(0)


        # 如果找不到明確的圖片標(biāo)題,則嘗試提取圖片周圍的文本
        # 這是一個(gè)簡(jiǎn)化實(shí)現(xiàn),實(shí)際應(yīng)用中可能需要更復(fù)雜的邏輯
        lines = text.split("\n")
        total_length = 0
        start_line = 0


        # 估計(jì)圖片在文本中的位置
        # 這是一個(gè)非常粗略的估計(jì),實(shí)際應(yīng)用中需要更精確的方法
        relative_position = bbox[1] / 1000  # 假設(shè)頁面高度為1000
        target_position = int(len(text) * relative_position)


        # 找到大致對(duì)應(yīng)的行
        for i, line in enumerate(lines):
            total_length += len(line) + 1  # +1 for newline
            if total_length > target_position:
                start_line = max(0, i - 2)  # 從前兩行開始
                break


        # 提取上下文(當(dāng)前行及其前后幾行)
        context_lines = lines[max(0, start_line):min(len(lines), start_line + 5)]
        return "\n".join(line for line in context_lines if len(line.strip()) > 5)


    def _format_table(self, table: List[List[str]]) -> str:
        """格式化表格為文本格式"""
        if not table:
            return ""


        # 創(chuàng)建pandas DataFrame
        df = pd.DataFrame(table[1:], columns=table[0] if len(table) > 1 else None)


        # 轉(zhuǎn)換為字符串形式
        result = io.StringIO()
        df.to_csv(result, sep="|", index=False)
        return result.getvalue()


    def _extract_keywords_from_table(self, table: List[List[str]]) -> List[str]:
        """從表格中提取關(guān)鍵詞"""
        keywords = []


        # 表頭作為關(guān)鍵詞
        if table and len(table) > 0:
            keywords.extend(table[0])


        # 第一列可能是行標(biāo)題,也加入關(guān)鍵詞
        for row in table[1:] if len(table) > 1 else []:
            if row and len(row) > 0:
                keywords.append(row[0])


        return keywords


    def _split_into_paragraphs(self, text: str) -> List[str]:
        """將文本拆分為段落"""
        # 按照多個(gè)換行符拆分
        paragraphs = re.split(r"\n\s*\n", text)
        return [p.strip() for p in paragraphs if p.strip()]


    def _extract_keywords(self, text: str) -> List[str]:
        """從文本中提取關(guān)鍵詞"""
        # 這是一個(gè)簡(jiǎn)化的關(guān)鍵詞提取方法
        # 實(shí)際應(yīng)用中可以使用更復(fù)雜的NLP技術(shù),如TF-IDF、TextRank等


        # 1. 移除停用詞和標(biāo)點(diǎn)
        stop_words = {"的", "了", "和", "與", "或", "在", "是", "有", "被", "將", "把"}
        cleaned_text = re.sub(r'[^\w\s]', ' ', text)
        words = cleaned_text.split()
        filtered_words = [w for w in words if w not in stop_words and len(w) > 1]


        # 2. 簡(jiǎn)單詞頻統(tǒng)計(jì)
        word_count = {}
        for word in filtered_words:
            if word in word_count:
                word_count[word] += 1
            else:
                word_count[word] = 1


        # 3. 選擇頻率最高的幾個(gè)詞作為關(guān)鍵詞
        sorted_words = sorted(word_count.items(), key=lambda x: x[1], reverse=True)
        return [word for word, count in sorted_words[:10] if count > 1]


# 使用示例
if __name__ == "__main__":
    chunker = DynamicChunker(
        api_key="YOUR_API_KEY",
        base_url="http://YOUR_BASE_URL:9380"
    )


    doc_id = chunker.process_document("設(shè)備維修手冊(cè).pdf", "動(dòng)態(tài)分塊測(cè)試")
    print(f"處理完成,文檔ID: {doc_id}")

(完)

責(zé)任編輯:龐桂玉 來源: 韋東東
相關(guān)推薦

2010-09-26 13:48:51

JVM調(diào)優(yōu)

2011-07-01 10:09:50

ASP.NET

2018-05-09 08:35:59

2025-02-24 01:00:00

LINQ核心技術(shù)語言集成

2010-01-13 18:09:09

VB.NET動(dòng)態(tài)生成代

2021-05-12 13:40:16

JVM調(diào)優(yōu)經(jīng)驗(yàn)

2011-05-16 17:36:05

SEO

2011-05-23 17:56:14

網(wǎng)站優(yōu)化

2012-01-10 16:22:25

Web

2018-07-18 12:12:20

Spark大數(shù)據(jù)代碼

2009-12-16 15:23:33

Ruby on rai

2009-12-18 17:01:37

Ruby基礎(chǔ)代碼

2011-06-20 13:54:41

Qt 動(dòng)態(tài) 切換

2012-01-10 14:35:08

JavaJVM

2023-11-23 09:26:50

Java調(diào)優(yōu)

2020-02-26 15:35:17

Spring Boot項(xiàng)目?jī)?yōu)化JVM調(diào)優(yōu)

2019-08-13 09:04:22

Linux性能調(diào)優(yōu)

2011-07-07 18:39:22

SEO

2009-12-17 09:49:18

Ruby代碼管理

2017-07-21 08:55:13

TomcatJVM容器
點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號(hào)