Skip to main content
Kùzu 是一款可嵌入、可扩展且极速的图数据库。 采用宽松的 MIT 许可证,源代码可在此处查看。
Kùzu 的主要特点:
  • 性能与可扩展性:实现了现代最先进的图连接算法。
  • 易用性:无需服务器(嵌入式架构),设置和上手非常简便。
  • 互操作性:可方便地从外部列式格式、CSV、JSON 和关系型数据库中扫描和复制数据。
  • 结构化属性图模型:实现了属性图模型,并增加了结构化特性。
  • Cypher 支持:支持使用声明式查询语言 Cypher 对图进行便捷查询。
访问 Kùzu 的文档开始使用。

配置

Kùzu 是一个嵌入式数据库(在进程内运行),因此无需管理服务器。安装以下依赖项即可开始:
pip install -U langchain-kuzu langchain-openai langchain-experimental
这将安装 Kùzu 及其 LangChain 集成包,以及 OpenAI Python 包(用于使用 OpenAI 的 LLM)。如果需要使用其他 LLM 提供商,可安装 LangChain 对应的 Python 包。 以下是在本地机器上创建 Kùzu 数据库并连接的方法:
import kuzu

db = kuzu.Database("test_db")
conn = kuzu.Connection(db)

创建 KuzuGraph

Kùzu 与 LangChain 的集成使得从非结构化文本创建和更新图变得非常方便,同时也可以通过利用 LangChain LLM 链能力的 Text2Cypher 管道来查询图。首先,我们创建一个 KuzuGraph 对象,将上面创建的数据库对象与 KuzuGraph 构造函数结合使用。
from langchain_kuzu.graphs.kuzu_graph import KuzuGraph

graph = KuzuGraph(db, allow_dangerous_requests=True)
假设我们想将以下文本转换为图:
text = "Tim Cook is the CEO of Apple. Apple has its headquarters in California."
我们将使用 LLMGraphTransformer,借助 LLM 从文本中提取节点和关系。为了使图更有用,我们将定义如下 schema,使 LLM 只提取符合 schema 的节点和关系。
# Define schema
allowed_nodes = ["Person", "Company", "Location"]
allowed_relationships = [
    ("Person", "IS_CEO_OF", "Company"),
    ("Company", "HAS_HEADQUARTERS_IN", "Location"),
]
LLMGraphTransformer 类提供了一种将文本转换为图文档列表的便捷方式。
from langchain_core.documents import Document
from langchain_experimental.graph_transformers import LLMGraphTransformer
from langchain_openai import ChatOpenAI

# Define the LLMGraphTransformer
llm_transformer = LLMGraphTransformer(
    llm=ChatOpenAI(model="gpt-4.1-mini", temperature=0, api_key=OPENAI_API_KEY),
    allowed_nodes=allowed_nodes,
    allowed_relationships=allowed_relationships,
)

documents = [Document(page_content=text)]
graph_documents = llm_transformer.convert_to_graph_documents(documents)
graph_documents[:2]
[GraphDocument(nodes=[Node(id='Tim Cook', type='Person', properties={}), Node(id='Apple', type='Company', properties={}), Node(id='California', type='Location', properties={})], relationships=[Relationship(source=Node(id='Tim Cook', type='Person', properties={}), target=Node(id='Apple', type='Company', properties={}), type='IS_CEO_OF', properties={}), Relationship(source=Node(id='Apple', type='Company', properties={}), target=Node(id='California', type='Location', properties={}), type='HAS_HEADQUARTERS_IN', properties={})], source=Document(metadata={}, page_content='Tim Cook is the CEO of Apple. Apple has its headquarters in California.'))]
然后,我们可以调用上面定义的 KuzuGraph 对象的 add_graph_documents 方法,将图文档导入 Kùzu 数据库。 include_source 参数设置为 True,以便同时创建每个实体节点与其来源文档之间的关系。
# Add the graph document to the graph
graph.add_graph_documents(
    graph_documents,
    include_source=True,
)

创建 KuzuQAChain

要通过 Text2Cypher 管道查询图,可以定义一个 KuzuQAChain 对象。然后,通过连接到上面 test_db 目录中存储的现有数据库,使用查询调用该链。
from langchain_kuzu.chains.graph_qa.kuzu import KuzuQAChain

# Create the KuzuQAChain with verbosity enabled to see the generated Cypher queries
chain = KuzuQAChain.from_llm(
    llm=ChatOpenAI(model="gpt-4.1-mini", temperature=0.3, api_key=OPENAI_API_KEY),
    graph=graph,
    verbose=True,
    allow_dangerous_requests=True,
)
注意,我们设置了略高于零的温度,以避免 LLM 在回复中过于简短。 让我们使用 QA chain 提一些问题。
chain.invoke("Who is the CEO of Apple?")
> Entering new KuzuQAChain chain...
Generated Cypher:
MATCH (p:Person)-[:IS_CEO_OF]->(c:Company {id: 'Apple'}) RETURN p
Full Context:
[{'p': {'_id': {'offset': 0, 'table': 1}, '_label': 'Person', 'id': 'Tim Cook', 'type': 'entity'}}]

> Finished chain.
{'query': 'Who is the CEO of Apple?',
 'result': 'Tim Cook is the CEO of Apple.'}
chain.invoke("Where is Apple headquartered?")
> Entering new KuzuQAChain chain...
Generated Cypher:
MATCH (c:Company {id: 'Apple'})-[:HAS_HEADQUARTERS_IN]->(l:Location) RETURN l
Full Context:
[{'l': {'_id': {'offset': 0, 'table': 2}, '_label': 'Location', 'id': 'California', 'type': 'entity'}}]

> Finished chain.
{'query': 'Where is Apple headquartered?',
 'result': 'Apple is headquartered in California.'}

刷新图 schema

如果你修改或更新了图,可以查看 Text2Cypher chain 用于生成 Cypher 语句的已刷新 schema 信息。 调用链时会自动调用 refresh_schema(),无需每次手动调用。
graph.refresh_schema()

print(graph.get_schema)
Node properties: [{'properties': [('id', 'STRING'), ('type', 'STRING')], 'label': 'Person'}, {'properties': [('id', 'STRING'), ('type', 'STRING')], 'label': 'Location'}, {'properties': [('id', 'STRING'), ('text', 'STRING'), ('type', 'STRING')], 'label': 'Chunk'}, {'properties': [('id', 'STRING'), ('type', 'STRING')], 'label': 'Company'}]
Relationships properties: [{'properties': [], 'label': 'HAS_HEADQUARTERS_IN'}, {'properties': [('label', 'STRING'), ('triplet_source_id', 'STRING')], 'label': 'MENTIONS_Chunk_Person'}, {'properties': [('label', 'STRING'), ('triplet_source_id', 'STRING')], 'label': 'MENTIONS_Chunk_Location'}, {'properties': [], 'label': 'IS_CEO_OF'}, {'properties': [('label', 'STRING'), ('triplet_source_id', 'STRING')], 'label': 'MENTIONS_Chunk_Company'}]
Relationships: ['(:Company)-[:HAS_HEADQUARTERS_IN]->(:Location)', '(:Chunk)-[:MENTIONS_Chunk_Person]->(:Person)', '(:Chunk)-[:MENTIONS_Chunk_Location]->(:Location)', '(:Person)-[:IS_CEO_OF]->(:Company)', '(:Chunk)-[:MENTIONS_Chunk_Company]->(:Company)']

分别使用不同的 LLM 生成 Cypher 和答案

你可以分别指定 cypher_llmqa_llm,以使用不同的 LLM 来生成 Cypher 语句和答案。
chain = KuzuQAChain.from_llm(
    cypher_llm=ChatOpenAI(temperature=0, model="gpt-4.1-mini"),
    qa_llm=ChatOpenAI(temperature=0, model="gpt-4"),
    graph=graph,
    verbose=True,
    allow_dangerous_requests=True,
)
chain.invoke("Who is the CEO of Apple?")
> Entering new KuzuQAChain chain...
Generated Cypher:
MATCH (p:Person)-[:IS_CEO_OF]->(c:Company {id: 'Apple'}) RETURN p.id, p.type
Full Context:
[{'p.id': 'Tim Cook', 'p.type': 'entity'}]

> Finished chain.
{'query': 'Who is the CEO of Apple?',
 'result': 'Tim Cook is the CEO of Apple.'}