之前介紹了NodeRAG的節點類型和安裝過程。
linux環境conda安裝NodeRAG示例-CSDN博客
這里嘗試從prompt代碼角度分析NodeRAG如何將文檔轉化為節點、關系。
1 整體處理流程
NodeRAG定義了如下所示狀態及處理流程。
# define the state to pipeline mapping
self.state_pipeline_map = {
? ? ? ? ? ? State.DOCUMENT_PIPELINE: document_pipline,
? ? ? ? ? ? State.TEXT_PIPELINE: text_pipline,
? ? ? ? ? ? State.GRAPH_PIPELINE: Graph_pipeline,
? ? ? ? ? ? State.ATTRIBUTE_PIPELINE: Attribution_generation_pipeline,
? ? ? ? ? ? State.EMBEDDING_PIPELINE: Embedding_pipeline,
? ? ? ? ? ? State.SUMMARY_PIPELINE: SummaryGeneration,
? ? ? ? ? ? State.INSERT_TEXT: Insert_text,
? ? ? ? ? ? State.HNSW_PIPELINE: HNSW_pipeline
}
? ? ? ??
狀態轉化序列如下所示,依次是INIT、DOCUMENT、TEXT、GRAPH、ATTRIBUTE、EMBEDDING、SUMMARY、INSERT、HNSW和FINISHED,涵蓋文檔劃分、圖譜化、特征提取、向量化、內容摘要等。
# define the state sequence
self.state_sequence = [
? ? ? ? ? ? State.INIT,
? ? ? ? ? ? State.DOCUMENT_PIPELINE,
? ? ? ? ? ? State.TEXT_PIPELINE,
? ? ? ? ? ? State.GRAPH_PIPELINE,
? ? ? ? ? ? State.ATTRIBUTE_PIPELINE,
? ? ? ? ? ? State.EMBEDDING_PIPELINE,
? ? ? ? ? ? State.SUMMARY_PIPELINE,
? ? ? ? ? ? State.INSERT_TEXT,
? ? ? ? ? ? State.HNSW_PIPELINE,
? ? ? ? ? ? State.FINISHED
]
https://github.com/Terry-Xu-666/NodeRAG/blob/main/NodeRAG/build/Node.py
這里重點關注文檔切分、語義單元提取(摘要、實體、關系)、關聯關系構建過程。
2 文檔初步切分
State.DOCUMENT_PIPELINE: document_pipline環節
NodeRAG文檔切分代碼,輸入是文件讀出的字符串,塊大小chunk_size對應token數,實際切分end邊界按token數計算。
from typing import List
from .token_utils import get_token_counterclass SemanticTextSplitter:def __init__(self, chunk_size: int = 1048, model_name: str = "gpt-4o-mini"):"""Initialize the text splitter with chunk size and model name parameters.Args:chunk_size (int): Maximum number of tokens per chunkmodel_name (str): Model name for token counting"""self.chunk_size = chunk_sizeself.token_counter = get_token_counter(model_name)def split(self, text: str) -> List[str]:"""Split text into chunks based on both token count and semantic boundaries."""chunks = []start = 0text_len = len(text)while start < text_len:# add 4 times of chunk_size string to the start positionend = start + self.chunk_size * 4 # assume each token is 4 charactersif end > text_len:end = text_len# get the current text fragmentcurrent_chunk = text[start:end]# if the token count of the current fragment exceeds the limit, need to find the split pointwhile self.token_counter(current_chunk) > self.chunk_size and start < end:# find semantic boundary in the current rangeboundaries = ['\n\n', '\n', '。', '.', '!', '!', '?', '?', ';', ';']semantic_end = endfor boundary in boundaries:boundary_pos = current_chunk.rfind(boundary)if boundary_pos != -1:semantic_end = start + boundary_pos + len(boundary)break# if found semantic boundary, use it; otherwise, force truncation by characterif semantic_end < end:end = semantic_endelse:# 沒找到合適的語義邊界,往回數token直到滿足大小限制end = start + int(len(current_chunk) // 1.2)current_chunk = text[start:end]# 添加處理好的文本塊chunk = current_chunk.strip()if chunk:chunks.append(chunk)# 移動到下一個起始位置start = endreturn chunks
NodeRAG/NodeRAG/utils/text_spliter.py at main · Terry-Xu-666/NodeRAG · GitHub
3 語義單元提取
State.TEXT_PIPELINE: text_pipline環節
將文本切分為塊后,進一步從塊中提取語義單元,每個單元包含對特定事件或活動的詳細描述哦
1)為每個語義單元提供總結,同時保留與原始上下文相關的所有關鍵細節。
2)直接從每個語義單元的原始文本中提取所有實體,而不是從改寫的總結中提取。
3)從第2步中提取的實體中列出語義單元內的所有關系,其中關系類型可以是描述性句子。使用格式"ENTITY_A,RELATION_TYPE,ENTITY_B",請確保字符串中包含三個元素,分別表示兩個實體和關系類型。
因為文本被切分為一個個獨立語義單元,NodeRAG有可能解決了RAG語義切分問題,
示例中text為文本切分后的塊。
text_decomposition_prompt_Chinese = """
目標:給定一個文本,將該文本被劃分為多個語義單元,每個單元包含對特定事件或活動的詳細描述。?
執行以下任務:
1.為每個語義單元提供總結,同時保留與原始上下文相關的所有關鍵細節。
2.直接從每個語義單元的原始文本中提取所有實體,而不是從改寫的總結中提取。
3.從第2步中提取的實體中列出語義單元內的所有關系,其中關系類型可以是描述性句子。使用格式"ENTITY_A,RELATION_TYPE,ENTITY_B",請確保字符串中包含三個元素,分別表示兩個實體和關系類型。要求:
時間實體:根據文本中提到的日期或時間的具體部分來表示時間實體,不填補缺失部分。
每個語義單元應以一個字典表示,包含三個鍵:semantic_unit(每個語義單元的概括性總結)、entities(直接從每個語義單元的原始文本中提取的實體列表,實體名格式為大寫)、relationships(描述性句子形式的提取關系字符串三元組列表)。所有這些字典應存儲在一個列表中,以便管理和訪問。
示例:
文本:2024年9月,艾米莉·羅伯茨博士前往巴黎參加國際可再生能源會議。在她的訪問期間,她與幾家歐洲公司探討了合作并介紹了她在提高太陽能板效率方面的最新研究。與此同時,在世界的另一邊,她的同事約翰·米勒博士在亞馬遜雨林進行實地工作。他記錄了幾種新物種,并觀察了森林砍伐對當地野生動物的影響。兩位學者的工作在各自的領域內至關重要,對環境保護工作做出了重大貢獻。
輸出:
[
? {{
? ? "semantic_unit": "2024年9月,艾米莉·羅伯茨博士參加了在巴黎舉行的國際可再生能源會議,她在會上介紹了她關于太陽能板效率提高的研究并探討了與歐洲公司的合作。",
? ? "entities": ["艾米莉·羅伯茨博士", "2024-09", "巴黎", "國際可再生能源會議", "歐洲公司", "太陽能板效率"],
? ? "relationships": [
? ? ? "艾米莉·羅伯茨博士, 參加了, 國際可再生能源會議",
? ? ? "艾米莉·羅伯茨博士, 探討了合作, 歐洲公司",
? ? ? "艾米莉·羅伯茨博士, 介紹了研究, 太陽能板效率"
? ? ]
? }},
? {{
? ? "semantic_unit": "約翰·米勒博士在亞馬遜雨林進行實地工作,記錄了幾種新物種并觀察了森林砍伐對當地野生動物的影響。",
? ? "entities": ["約翰·米勒博士", "亞馬遜雨林", "新物種", "森林砍伐", "當地野生動物"],
? ? "relationships": [
? ? ? "約翰·米勒博士, 在, 亞馬遜雨林進行實地工作",
? ? ? "約翰·米勒博士, 記錄了, 新物種",
? ? ? "約翰·米勒博士, 觀察了, 森林砍伐對當地野生動物的影響"
? ? ]
? }},
? {{
? ? "semantic_unit": "艾米莉·羅伯茨博士和約翰·米勒博士的工作在各自的領域內至關重要,對環境保護工作做出了重大貢獻。",
? ? "entities": ["艾米莉·羅伯茨博士", "約翰·米勒博士", "環境保護"],
? ? "relationships": [
? ? ? "艾米莉·羅伯茨博士, 貢獻于, 環境保護",
? ? ? "約翰·米勒博士, 貢獻于, 環境保護"
? ? ]
? }}
]##########
實際數據:?
##########?
文本:{text}?
"""
NodeRAG/NodeRAG/utils/prompt/text_decomposition.py at main · Terry-Xu-666/NodeRAG · GitHub
4 實體關系重建
State.GRAPH_PIPELINE: Graph_pipeline環節
之前抽取的關系relationship,格式有可能是錯誤的,需要重新按照'實體A,關系類型,實體B'重構。
prompt示例如下。
relationship_reconstraction_prompt_Chinese = """
你將獲得一個包含實體之間關系的元組字符串。這些關系的格式是錯誤的,需要被重新構建。正確的格式應為:'實體A,關系類型,實體B',每個元組應包含三個元素:兩個實體和一個關系類型。你的任務是將每個關系重新構建為以下格式:{{'source': '實體A', 'relation': '關系類型', 'target': '實體B'}}。請確保輸出遵循此結構,準確映射提供的實體和關系。
錯誤的關系元組:{relationship}
"""
https://github.com/Terry-Xu-666/NodeRAG/blob/main/NodeRAG/utils/prompt/relationship_reconstraction.py
除此之外,Graph環節還包括基本圖結構的重建,具體參考以下鏈接。
https://github.com/Terry-Xu-666/NodeRAG/blob/main/NodeRAG/build/pipeline/graph_pipeline.py
5 關聯特征總結
State.ATTRIBUTE_PIPELINE: Attribution_generation_pipeline環節
生成所給實體的簡明總結,涵蓋其基本屬性和重要相關關系。目的是生成屬性節點,為重要實體提供詳細描述。entity是實體,semantic_units是于entity相關聯的語義單元描述,relationships是實體與語義單元關聯的相關關系。
attribute_generation_prompt_Chinese = """
生成所給實體的簡明總結,涵蓋其基本屬性和重要相關關系。該總結應像小說中的人物簡介或產品描述一樣,提供引人入勝且精準的概覽。確保輸出只包含該實體的總結,不包含任何額外的解釋或元數據。字數不得超過2000字,但如果輸入材料有限,可以少于2000字。重點在于通過流暢的敘述提煉出最重要的見解,突出實體的核心特征及重要關系。
實體: {entity}
相關語義單元: {semantic_units}
相關關系: {relationships}
"""
如果一個完整語義描述橫跨多個節點,這種方式能有效涵蓋這種情況。
6 社區摘要總結
State.SUMMARY_PIPELINE: SummaryGeneration環節
使用社區聚類算法找到內容相關的節點,并將這些節點內容組織成一個完整文本。從文本數據中提取不同類別的高層次信息,例如概念、主題、相關理論、潛在影響和關鍵見解。每條信息應含一個簡潔的標題和相應的描述,以反映該聚類文本中的獨特視角。基于每條信息構建高級元素節點,包含從社區分析中提取的深層洞察。
community_summary_Chinese = """你將收到來自同一聚類的一組文本數據。你的任務是從文本數據中提取不同類別的高層次信息,例如概念、主題、相關理論、潛在影響和關鍵見解。每條信息應包含一個簡潔的標題和相應的描述,以反映該聚類文本中的獨特視角。
請不要試圖包含所有可能的信息;相反,選擇在該聚類中最具重要性和多樣性的元素。避免冗余信息——如果有高度相似的內容,請將它們合并為一個綜合條目。確保提取的高層次信息反映文本中的多維度內容,提供全面的概覽。
聚類文本數據:
{content}
"""
7 query實體提取
將用戶問題query分解為一個 list,其中每一項是句子的主要實體(如關鍵名詞或對象)。
目的是利用提取實體,在圖中搜索相關節點。
decompos_query_Chinese = '''
請將以下問題分解為一個 list,其中每一項是句子的主要實體(如關鍵名詞或對象)。如果你對用戶的意圖或相關領域知識有充分把握,也可以包含密切相關的術語。如果不確定,請僅從問題中提取實體。請盡量減少囊括常見的名詞,請將這些元素整合在一個單一的 list 中輸出。
問題:{query}
'''
reference
---
NodeRAG
https://github.com/Terry-Xu-666/NodeRAG
linux環境conda安裝NodeRAG示例
https://blog.csdn.net/liliang199/article/details/151101894