文字到圖譜的轉換是一個具有技術挑戰性的研究領域,其核心任務是將非結構化文字資料轉換為結構化的圖譜表示。這種技術雖然由來已久,但隨著大型語言模型(LLMs)的發展,其應用範圍得到了顯著擴充套件,並逐漸成為主流技術方案之一。
上圖展示了資訊抽取過程中文字到知識圖譜的轉換。圖左側展示了包含個人與公司關係描述的非結構化文字文件;圖右側則展示了相同資訊在知識圖譜中的結構化表示,清晰地呈現了人員與組織之間的工作和創立關係。
文字資訊的結構化表示具有重要的應用價值,特別是在檢索增強生成(RAG)系統中。雖然在非結構化文字上直接應用文字嵌入模型是一種可行方案,但在處理需要理解多實體間關聯的複雜查詢,或需要進行過濾、排序、聚合等結構化操作時,這種方法往往難以滿足要求。透過將文字資訊轉換為知識圖譜,不僅可以實現更高效的資料組織,還能構建一個支援複雜實體關係理解的框架。這種結構化方法顯著提升了資訊檢索的精確性,擴充套件了系統可處理的查詢型別。
在過去一年中,LangChain已經將知識圖譜的構建以LLM Graph Transformer的形式整合到了框架中。本文是LangChain的一個程式碼貢獻者編寫的文章,將對這些內容進行詳細介紹,文章最後還包含了作者提供的原始碼
Neo4j環境配置
本實現採用Neo4j作為圖資料儲存系統,其內建的圖形視覺化功能為分析提供了直觀支援。推薦使用Neo4j Aura的免費雲例項快速開始實驗。也可以透過安裝Neo4j Desktop應用程式在本地部署資料庫例項。
fromlangchain_community.graphsimportNeo4jGraph
graph=Neo4jGraph(
url="bolt://54.87.130.140:7687",
username="neo4j",
password="cables-anchors-directories",
refresh_schema=False
)
LLM Graph Transformer技術架構
LLM Graph Transformer被設計為一個可適配任意LLM的圖譜構建框架。鑑於當前市場上存在大量不同的模型提供商和模型版本,實現這種通用性是一個複雜的技術挑戰。LangChain在這裡發揮了重要作用,提供了必要的標準化處理。LLM Graph Transformer採用了雙模式設計,提供了兩種相互獨立的執行模式。
LLM Graph Transformer實現了兩種不同的文件圖譜生成模式,分別針對不同場景下的LLM應用進行了最佳化:
- 基於工具的模式(預設模式): 適用於支援結構化輸出或函式呼叫的LLM,該模式透過LLM的內建
with_structured_output
功能實現工具呼叫。工具規範定義了標準化的輸出格式,確保實體和關係提取過程的結構化和規範化。該實現方式在圖左側展示,包含了Node和Relationship類的核心程式碼實現。 - 基於提示的模式(備選模式): 針對不支援工具或函式呼叫的LLM設計的備選方案。該模式透過少樣本提示技術定義輸出格式,引導LLM以文字方式提取實體和關係。透過自定義解析函式將LLM輸出轉換為標準JSON格式,隨後用於構建節點和關係。這種模式完全依賴提示引導而非結構化工具。圖右側展示了示例提示和對應的JSON輸出結果。
這種雙模式設計確保了LLM Graph Transformer可以適配不同型別的LLM,無論是透過工具直接構建還是透過文字提示解析來生成圖譜結構。
即使使用支援工具/函式的模型,也可透過設定
_ignore_tools_usage=True_
引數啟用基於提示的提取模式。
基於工具的提取實現
選擇基於工具的提取方法作為主要實現方案的原因在於,它能夠最大程度地減少對複雜提示工程和自定義解析函式的依賴。在LangChain框架中,
with_structured_output
方法支援透過工具或函式進行資訊提取,輸出格式可以透過JSON結構或Pydantic物件定義。基於可維護性考慮,作者選擇了Pydantic物件作為定義方式。
首先定義節點類
Node
:
classNode(BaseNode):
id: str=Field(..., description="Name or human-readable unique identifier") # 名稱或可讀的唯一識別符號
label: str=Field(..., description=f"Available options are {enum_values}") # 可用的標籤選項
properties: Optional[List[Property]] # 可選屬性列表
節點類包含
id
、
label
和可選的
properties
欄位。描述id為可讀的唯一識別符號具有重要意義,因為某些LLM可能會將ID屬性理解為傳統的隨機字串或遞增整數形式。而在本實現中,我們期望使用實體名稱作為id屬性。透過在
label
描述中明確列出可用選項來限制標籤型別。對於支援
enum
引數的LLM(如OpenAI的模型),我們也利用了這一特性。關係類
Relationship
的定義如下:
classRelationship(BaseRelationship):
source_node_id: str # 源節點識別符號
source_node_label: str=Field(..., description=f"Available options are {enum_values}") # 源節點標籤
target_node_id: str # 目標節點識別符號
target_node_label: str=Field(..., description=f"Available options are {enum_values}") # 目標節點標籤
type: str=Field(..., description=f"Available options are {enum_values}") # 關係型別
properties: Optional[List[Property]] # 可選屬性列表
這是
Relationship
類的第二個迭代版本。在初始版本中,源節點和目標節點採用巢狀的
Node
物件表示,但實踐表明這種結構降低了提取過程的準確性和質量。因此,在當前版本中,我們將源節點和目標節點分解為獨立欄位,如
source_node_id
、
source_node_label
以及
target_node_id
、
target_node_label
。同時,在描述中明確定義了節點標籤和關係型別的有效值,以確保LLM嚴格遵循指定的圖譜模式。
基於工具的提取方法支援為節點和關係定義屬性。屬性類的定義如下:
classProperty(BaseModel):
"""單個屬性由鍵值對構成"""
key: str=Field(..., description=f"Available options are {enum_values}") # 屬性鍵的可用選項
value: str # 屬性值
Property
採用鍵值對形式定義。這種設計雖然具有靈活性,但也存在一些技術限制:
- 無法為各個屬性提供獨立的描述
- 無法指定必需屬性和可選屬性的區分
- 屬性定義採用全域性共享方式,而非針對特定節點或關係型別單獨定義
系統還實現了詳細的系統提示來指導提取過程。但經驗表明,函式和引數描述通常比系統訊息對提取質量的影響更大。
當前LLM Graph Transformer框架的一個侷限在於,缺乏簡便的函式或引數描述自定義機制。
基於提示的提取實現
考慮到當前只有少數商業LLM和LLaMA 3支援原生工具功能,我們為不支援工具的模型實現了備選方案。即使在使用支援工具的模型時,也可以透過設定
ignore_tool_usage=True
來啟用基於提示的提取方式。
基於提示方法的主要提示工程實現和示例由Geraldus Wilsen貢獻。在這種方法中,輸出結構直接在提示中定義。以下是系統提示的核心部分:
你是一個專門用於結構化資訊提取的高效能演算法,用於構建知識圖譜。你的任務是從給定文字中識別使用者提示指定的實體和關係,並生成JSON格式的輸出。輸出應為JSON物件列表,每個物件包含以下欄位:
-**"head"**: 提取的實體文字,必須匹配使用者提示中指定的型別
-**"head_type"**: 提取的頭部實體型別,從指定型別列表中選擇
-**"relation"**: "head"與"tail"之間的關係型別,從允許的關係列表中選擇
-**"tail"**: 關係尾部實體的文字表示
-**"tail_type"**: 尾部實體型別,從提供的型別列表中選擇
要求:
1.最大化提取實體和關係資訊
2.確保實體表示的一致性:同一實體的不同表述(如"John Doe"可能表現為"Joe"或代詞"他")應統一使用最完整的識別符號
3.輸出應僅包含結構化資料,不包含任何額外說明或文字
1.僅提取關係而不提取獨立節點,因此不會產生_孤立節點_
2.考慮到缺乏原生工具支援的模型通常效能較低,不支援屬性提取,以簡化輸出結構
以下是典型的少樣本示例實現:
examples= [
{
"text": (
"Adam is a software engineer in Microsoft since 2009, " # Adam自2009年起在Microsoft擔任軟體工程師
"and last year he got an award as the Best Talent" # 去年獲得最佳人才獎
),
"head": "Adam",
"head_type": "Person",
"relation": "WORKS_FOR",
"tail": "Microsoft",
"tail_type": "Company",
},
{
"text": (
"Adam is a software engineer in Microsoft since 2009, "
"and last year he got an award as the Best Talent"
),
"head": "Adam",
"head_type": "Person",
"relation": "HAS_AWARD",
"tail": "Best Talent",
"tail_type": "Award",
},
...
]
當前實現中,無法新增自定義少樣本示例或補充指令,唯一的定製方式是透過
prompt
屬性修改整體提示內容。擴充套件自定義功能是未來的重要開發方向。
圖譜模式定義
在使用LLM Graph Transformer進行資訊提取時,完善的圖譜模式定義對於構建高質量的知識表示至關重要。規範的圖譜模式明確了需要提取的節點型別、關係型別及其相關屬性,為LLM提供了明確的提取指導框架。
為了驗證實現效果,我們選取了瑪麗·居里維基百科頁面的開篇段落作為測試資料,並在末尾新增了一條關於羅賓·威廉姆斯的資訊:
fromlangchain_core.documentsimportDocument
text="""
Marie Curie, 7 November 1867 – 4 July 1934, was a Polish and naturalised-French physicist and chemist who conducted pioneering research on radioactivity.
She was the first woman to win a Nobel Prize, the first person to win a Nobel Prize twice, and the only person to win a Nobel Prize in two scientific fields.
Her husband, Pierre Curie, was a co-winner of her first Nobel Prize, making them the first-ever married couple to win the Nobel Prize and launching the Curie family legacy of five Nobel Prizes.
She was, in 1906, the first woman to become a professor at the University of Paris.
Also, Robin Williams.
"""
documents= [Document(page_content=text)]
實驗中採用GPT-4作為基礎模型:
fromlangchain_openaiimportChatOpenAI
importgetpass
importos
os.environ["OPENAI_API_KEY"] =getpass.getpass("OpenAI api key")
llm=ChatOpenAI(model='gpt-4o')
首先分析未定義圖譜模式時的資訊提取效果:
fromlangchain_experimental.graph_transformersimportLLMGraphTransformer
no_schema=LLMGraphTransformer(llm=llm)
使用非同步函式
aconvert_to_graph_documents
處理文件。在LLM提取場景中,非同步處理的優勢在於支援多文件並行處理,可顯著提升處理效率和吞吐量:
data = await no_schema.aconvert_to_graph_documents(documents)
LLM Graph Transformer返回的圖文件結構如下:
[
GraphDocument(
nodes=[
Node(id="Marie Curie", type="Person", properties={}),
Node(id="Pierre Curie", type="Person", properties={}),
Node(id="Nobel Prize", type="Award", properties={}),
Node(id="University Of Paris", type="Organization", properties={}),
Node(id="Robin Williams", type="Person", properties={}),
],
relationships=[
Relationship(
source=Node(id="Marie Curie", type="Person", properties={}),
target=Node(id="Nobel Prize", type="Award", properties={}),
type="WON",
properties={},
),
Relationship(
source=Node(id="Marie Curie", type="Person", properties={}),
target=Node(id="Nobel Prize", type="Award", properties={}),
type="WON",
properties={},
),
Relationship(
source=Node(id="Marie Curie", type="Person", properties={}),
target=Node(
id="University Of Paris", type="Organization", properties={}
),
type="PROFESSOR",
properties={},
),
Relationship(
source=Node(id="Pierre Curie", type="Person", properties={}),
target=Node(id="Nobel Prize", type="Award", properties={}),
type="WON",
properties={},
),
],
source=Document(
metadata={"id": "de3c93515e135ac0e47ca82a4f9b82d8"},
page_content="\nMarie Curie, 7 November 1867 – 4 July 1934, was a Polish and naturalised-French physicist and chemist who conducted pioneering research on radioactivity.\nShe was the first woman to win a Nobel Prize, the first person to win a Nobel Prize twice, and the only person to win a Nobel Prize in two scientific fields.\nHer husband, Pierre Curie, was a co-winner of her first Nobel Prize, making them the first-ever married couple to win the Nobel Prize and launching the Curie family legacy of five Nobel Prizes.\nShe was, in 1906, the first woman to become a professor at the University of Paris.\nAlso, Robin Williams!\n",
),
)
]
圖文件包含
nodes
、
relationships
和
source
三個主要部分,分別對應提取的節點、關係及源文件資訊。使用Neo4j Browser可以直觀地視覺化這些輸出結果。
上圖展示了對同一段瑪麗·居里文字的兩次獨立提取結果。這裡使用了支援基於工具提取的GPT-4模型,該模式允許生成孤立節點。由於未定義圖譜模式,LLM在執行時自主決定提取的資訊內容,這導致了輸出結果的不確定性。即使是相同的輸入文字,不同提取過程的結果在細節上也存在差異。例如,左圖將Marie標註為諾貝爾獎的
WINNER
,而右圖則使用
WON
表示獲獎關係。
對於支援工具的模型,可以透過設定
ignore_tool_usage
引數啟用基於提示的提取模式:
no_schema_prompt = LLMGraphTransformer(llm=llm, ignore_tool_usage=True)
data = await no_schema.aconvert_to_graph_documents(documents)
讓我們同樣對這種方式的兩次獨立執行結果進行視覺化分析。
基於提示的方法不會生成孤立節點,這是它與工具模式的一個顯著區別。但與之前的情況類似,由於缺乏明確的模式約束,不同執行之間的輸出結構仍存在變異性。
接下來,我們將探討如何透過定義合適的圖譜模式來提升輸出的一致性。
節點型別定義
約束圖譜結構是提升資訊提取質量的關鍵技術手段。透過精確定義圖譜模式,可以引導模型聚焦於特定的實體型別和關係模式,從而提升提取結果的一致性和可預測性。規範的模式定義不僅確保了提取資料的標準化,還能有效避免關鍵資訊的遺漏和非預期元素的引入。
首先透過
allowed_nodes
引數定義預期的節點型別:
allowed_nodes = ["Person", "Organization", "Location", "Award", "ResearchField"]
nodes_defined = LLMGraphTransformer(llm=llm, allowed_nodes=allowed_nodes)
data = await nodes_defined.aconvert_to_graph_documents(documents)
上述程式碼定義了五種核心節點型別。為了評估其效果,我們對比分析兩次獨立執行的結果:
透過節點型別的預定義,提取結果的一致性得到了明顯提升。但仍存在一些細節層面的差異,例如第一次執行將"radioactivity"識別為研究領域,而第二次執行則未能捕獲這一資訊。
由於未對關係型別進行約束,關係提取的結果在不同執行間仍表現出較大的變異性。同時,不同執行捕獲的資訊粒度也存在差異。例如,Marie和Pierre之間的
MARRIED_TO
關係並未在兩次提取中都被識別出來。
關係型別定義
為了解決關係提取的不一致問題,需要引入關係型別的定義機制。最基礎的方法是透過可用型別列表來規範化關係:
allowed_nodes = ["Person", "Organization", "Location", "Award", "ResearchField"]
allowed_relationships = ["SPOUSE", "AWARD", "FIELD_OF_RESEARCH", "WORKS_AT", "IN_LOCATION"]
rels_defined = LLMGraphTransformer(
llm=llm,
allowed_nodes=allowed_nodes,
allowed_relationships=allowed_relationships
)
data = await rels_defined.aconvert_to_graph_documents(documents)
分析兩次獨立執行的結果:
預定義節點和關係型別條件下的兩次提取結果對比。作者提供。
節點和關係型別的雙重約束顯著提升了提取結果的一致性。例如,Marie的獲獎資訊、配偶關係以及在巴黎大學的工作關係在不同執行中都得到了穩定的提取。然而,由於關係定義採用了通用列表的形式,沒有對關係的連線節點型別進行限制,仍然存在一些變異性。比如,
FIELD_OF_RESEARCH
關係可能連線
Person
和
ResearchField
,也可能連線
Award
和
ResearchField
。由於未定義關係的方向性,提取結果在關係方向上可能出現不一致。
為了進一步提升關係提取的準確性,我們引入了更精細的關係定義方式:
allowed_nodes = ["Person", "Organization", "Location", "Award", "ResearchField"]
allowed_relationships = [
("Person", "SPOUSE", "Person"),
("Person", "AWARD", "Award"),
("Person", "WORKS_AT", "Organization"),
("Organization", "IN_LOCATION", "Location"),
("Person", "FIELD_OF_RESEARCH", "ResearchField")
]
rels_defined = LLMGraphTransformer(
llm=llm,
allowed_nodes=allowed_nodes,
allowed_relationships=allowed_relationships
)
data = await rels_defined.aconvert_to_graph_documents(documents)
這裡採用三元組形式定義關係,分別指定源節點型別、關係型別和目標節點型別,從而實現了更嚴格的關係約束。讓我們分析採用三元組關係定義後的提取結果:
三元組方式的關係定義為提取過程提供了更嚴格的模式約束,顯著提升了跨執行的一致性。然而,考慮到LLM本身的特性,提取的細節完整度仍可能存在差異。例如,右側圖中顯示了Pierre獲得諾貝爾獎的資訊,而左側圖中則未能捕獲這一細節。
屬性定義機制
對圖譜模式的最後一層最佳化是節點和關係的屬性定義。系統提供了兩種屬性定義方式:
第一種方式是將
node_properties
或
relationship_properties
設定為
true
,允許LLM自主決定要提取的屬性:
allowed_nodes = ["Person", "Organization", "Location", "Award", "ResearchField"]
allowed_relationships = [
("Person", "SPOUSE", "Person"),
("Person", "AWARD", "Award"),
("Person", "WORKS_AT", "Organization"),
("Organization", "IN_LOCATION", "Location"),
("Person", "FIELD_OF_RESEARCH", "ResearchField")
]
node_properties=True
relationship_properties=True
props_defined = LLMGraphTransformer(
llm=llm,
allowed_nodes=allowed_nodes,
allowed_relationships=allowed_relationships,
node_properties=node_properties,
relationship_properties=relationship_properties
)
data = await props_defined.aconvert_to_graph_documents(documents)
graph.add_graph_documents(data)
分析提取結果:
啟用LLM的自主屬性提取能力後,系統成功捕獲了多個重要屬性。例如,瑪麗·居里的出生和死亡日期、她在巴黎大學的教授職位資訊,以及多次獲得諾貝爾獎的成就等。這些補充屬性顯著增強了圖譜的資訊密度。
第二種方式是透過顯式定義來指定需要提取的節點和關係屬性:
allowed_nodes = ["Person", "Organization", "Location", "Award", "ResearchField"]
allowed_relationships = [
("Person", "SPOUSE", "Person"),
("Person", "AWARD", "Award"),
("Person", "WORKS_AT", "Organization"),
("Organization", "IN_LOCATION", "Location"),
("Person", "FIELD_OF_RESEARCH", "ResearchField")
]
node_properties=["birth_date", "death_date"]
relationship_properties=["start_date"]
props_defined = LLMGraphTransformer(
llm=llm,
allowed_nodes=allowed_nodes,
allowed_relationships=allowed_relationships,
node_properties=node_properties,
relationship_properties=relationship_properties
)
data = await props_defined.aconvert_to_graph_documents(documents)
graph.add_graph_documents(data)
屬性透過兩個獨立的列表進行定義。檢查提取結果:
在預定義屬性模式下,系統準確提取了人物的出生和死亡日期。此外,還成功捕獲了瑪麗在巴黎大學任教的開始時間資訊。
雖然屬性提取顯著豐富了圖譜的資訊內容,但當前實現存在以下技術限制:
- 屬性提取僅支援基於工具的模式
- 所有屬性值都被統一處理為字串型別
- 屬性定義採用全域性方式,無法針對特定節點標籤或關係型別進行定製
- 缺乏屬性描述的自定義機制,無法為LLM提供更精確的提取指導
嚴格模式實現
儘管我們透過精心設計的提示工程努力確保LLM遵循預定義的模式,但實踐表明特別是對於效能較弱的模型,要實現完全的指令遵從仍具有挑戰性。所以我們引入了
strict_mode
作為後處理機制,用於過濾不符合預定義圖譜模式的提取結果,從而確保輸出的規範性。
strict_mode
預設開啟,可透過以下配置關閉:
LLMGraphTransformer(
llm=llm,
allowed_nodes=allowed_nodes,
allowed_relationships=allowed_relationships,
strict_mode=False
)
關閉嚴格模式後,LLM可能會生成預定義模式之外的節點或關係型別,這種靈活性可能導致輸出結構的不確定性。
圖文件資料庫匯入
LLM Graph Transformer提取的圖文件可透過
add_graph_documents
方法匯入Neo4j等圖資料庫,以支援後續的分析和應用。系統提供了多種匯入選項以適應不同場景需求。
基礎匯入實現
最簡單的匯入方式如下:
graph.add_graph_documents(graph_documents)
此方法直接匯入圖文件中的所有節點和關係資料。本文前述示例均採用此方式進行結果展示。
基礎實體標籤機制
圖資料庫通常使用索引最佳化資料匯入和檢索效能。以Neo4j為例,索引只能針對特定的節點標籤建立。由於無法預知所有可能的節點標籤,系統透過
baseEntityLabel
引數為每個節點新增統一的次級標籤,從而實現索引的高效利用:
graph.add_graph_documents(graph_documents, baseEntityLabel=True)
啟用該引數後,每個節點都會獲得額外的
__Entity__
標籤。
源文件關聯機制
系統還支援匯入實體的源文件資訊,便於追蹤實體的文字來源。透過
include_source
引數啟用此功能:
graph.add_graph_documents(graph_documents, include_source=True)
匯入結果示例:
圖中藍色節點表示源文件,透過
MENTIONS
關係與提取的實體建立連線。這種設計支援結構化和非結構化檢索的混合應用。
總結
本文深入探討了LangChain的LLM Graph Transformer框架及其文字到圖譜轉換的雙模式實現機制。作為主要技術路線的基於工具模式利用結構化輸出和函式呼叫能力,有效降低了提示工程的複雜度,並支援屬性提取。而基於提示模式則為不支援工具呼叫的模型提供了備選方案,透過少樣本示例引導模型行為。
研究表明,精確定義的圖譜模式(包括節點型別、關係型別及其約束)能顯著提升提取結果的一致性和可靠性。無論採用何種模式,LLM Graph Transformer都為非結構化資料的結構化表示提供了可靠的技術方案,有效支援了RAG應用和複雜查詢處理。
本文程式碼在這裡,有興趣的自行檢視:
https://avoid.overfit.cn/post/d673e2dec79b4df9823113e60d110ceb
作者:Tomaz Bratanic