Apache AGE
Apache AGE 是一个 PostgreSQL 扩展,提供了图数据库功能。AGE 是 A Graph Extension 的缩写,灵感来源于 Bitnine 对 PostgreSQL 10 的分支 AgensGraph,它是一个多模型数据库。该项目的目标是创建一个单一存储,能够同时处理关系型和图模型数据,以便用户可以使用标准 ANSI SQL 以及图查询语言 openCypher。Apache AGE 存储的数据元素是节点、连接它们的边以及节点和边的属性。
本笔记本展示了如何使用 LLM 为图数据库提供自然语言接口,您可以使用 Cypher 查询语言查询该数据库。
Cypher 是一种声明式图查询语言,允许在属性图中进行富有表现力和高效的数据查询。
设置
您需要有一个运行中的 PostgreSQL 实例,并安装了 AGE 扩展。一种测试方法是使用官方 AGE Docker 镜像运行 Docker 容器。您可以通过运行以下脚本来运行本地 Docker 容器:
docker run \
--name age \
-p 5432:5432 \
-e POSTGRES_USER=postgresUser \
-e POSTGRES_PASSWORD=postgresPW \
-e POSTGRES_DB=postgresDB \
-d \
apache/age
有关在 Docker 中运行的更多说明,请参阅此处。
from langchain_community.graphs.age_graph import AGEGraph
from langchain_neo4j import GraphCypherQAChain
from langchain_openai import ChatOpenAI
conf = {
"database": "postgresDB",
"user": "postgresUser",
"password": "postgresPW",
"host": "localhost",
"port": 5432,
}
graph = AGEGraph(graph_name="age_test", conf=conf)
填充数据库
假设您的数据库为空,您可以使用 Cypher 查询语言填充它。以下 Cypher 语句是幂等的,这意味着无论您运行一次还是多次,数据库信息都将保持不变。
graph.query(
"""
MERGE (m:Movie {name:"Top Gun"})
WITH m
UNWIND ["Tom Cruise", "Val Kilmer", "Anthony Edwards", "Meg Ryan"] AS actor
MERGE (a:Actor {name:actor})
MERGE (a)-[:ACTED_IN]->(m)
"""
)
[]
刷新图模式信息
如果数据库架构发生更改,您可以刷新生成 Cypher 语句所需的架构信息。
graph.refresh_schema()
print(graph.schema)
Node properties are the following:
[{'properties': [{'property': 'name', 'type': 'STRING'}], 'labels': 'Actor'}, {'properties': [{'property': 'property_a', 'type': 'STRING'}], 'labels': 'LabelA'}, {'properties': [], 'labels': 'LabelB'}, {'properties': [], 'labels': 'LabelC'}, {'properties': [{'property': 'name', 'type': 'STRING'}], 'labels': 'Movie'}]
Relationship properties are the following:
[{'properties': [], 'type': 'ACTED_IN'}, {'properties': [{'property': 'rel_prop', 'type': 'STRING'}], 'type': 'REL_TYPE'}]
The relationships are the following:
['(:`Actor`)-[:`ACTED_IN`]->(:`Movie`)', '(:`LabelA`)-[:`REL_TYPE`]->(:`LabelB`)', '(:`LabelA`)-[:`REL_TYPE`]->(:`LabelC`)']
查询图
现在我们可以使用图 Cypher QA 链来向图提出问题。
chain = GraphCypherQAChain.from_llm(
ChatOpenAI(temperature=0), graph=graph, verbose=True, allow_dangerous_requests=True
)
chain.invoke("Who played in Top Gun?")
[1m> Entering new GraphCypherQAChain chain...[0m
``````output
Generated Cypher:
[32;1m[1;3mMATCH (a:Actor)-[:ACTED_IN]->(m:Movie)
WHERE m.name = 'Top Gun'
RETURN a.name[0m
Full Context:
[32;1m[1;3m[{'name': 'Tom Cruise'}, {'name': 'Val Kilmer'}, {'name': 'Anthony Edwards'}, {'name': 'Meg Ryan'}][0m
[1m> Finished chain.[0m
{'query': 'Who played in Top Gun?',
'result': 'Tom Cruise, Val Kilmer, Anthony Edwards, Meg Ryan played in Top Gun.'}
限制结果数量
您可以使用 top_k
参数限制 Cypher QA 链的结果数量。默认值为 10。
chain = GraphCypherQAChain.from_llm(
ChatOpenAI(temperature=0),
graph=graph,
verbose=True,
top_k=2,
allow_dangerous_requests=True,
)
chain.invoke("Who played in Top Gun?")
[1m> Entering new GraphCypherQAChain chain...[0m
Generated Cypher:
[32;1m[1;3mMATCH (a:Actor)-[:ACTED_IN]->(m:Movie {name: 'Top Gun'})
RETURN a.name[0m
Full Context:
[32;1m[1;3m[{'name': 'Tom Cruise'}, {'name': 'Val Kilmer'}][0m
[1m> Finished chain.[0m
{'query': 'Who played in Top Gun?',
'result': 'Tom Cruise, Val Kilmer played in Top Gun.'}
返回中间结果
您可以使用 return_intermediate_steps
参数从 Cypher QA 链返回中间步骤
chain = GraphCypherQAChain.from_llm(
ChatOpenAI(temperature=0),
graph=graph,
verbose=True,
return_intermediate_steps=True,
allow_dangerous_requests=True,
)
result = chain("Who played in Top Gun?")
print(f"Intermediate steps: {result['intermediate_steps']}")
print(f"Final answer: {result['result']}")
[1m> Entering new GraphCypherQAChain chain...[0m
Generated Cypher:
[32;1m[1;3mMATCH (a:Actor)-[:ACTED_IN]->(m:Movie)
WHERE m.name = 'Top Gun'
RETURN a.name[0m
Full Context:
[32;1m[1;3m[{'name': 'Tom Cruise'}, {'name': 'Val Kilmer'}, {'name': 'Anthony Edwards'}, {'name': 'Meg Ryan'}][0m
[1m> Finished chain.[0m
Intermediate steps: [{'query': "MATCH (a:Actor)-[:ACTED_IN]->(m:Movie)\nWHERE m.name = 'Top Gun'\nRETURN a.name"}, {'context': [{'name': 'Tom Cruise'}, {'name': 'Val Kilmer'}, {'name': 'Anthony Edwards'}, {'name': 'Meg Ryan'}]}]
Final answer: Tom Cruise, Val Kilmer, Anthony Edwards, Meg Ryan played in Top Gun.
返回直接结果
您可以使用 return_direct
参数从 Cypher QA 链返回直接结果
chain = GraphCypherQAChain.from_llm(
ChatOpenAI(temperature=0),
graph=graph,
verbose=True,
return_direct=True,
allow_dangerous_requests=True,
)
chain.invoke("Who played in Top Gun?")
[1m> Entering new GraphCypherQAChain chain...[0m
Generated Cypher:
[32;1m[1;3mMATCH (a:Actor)-[:ACTED_IN]->(m:Movie {name: 'Top Gun'})
RETURN a.name[0m
[1m> Finished chain.[0m
{'query': 'Who played in Top Gun?',
'result': [{'name': 'Tom Cruise'},
{'name': 'Val Kilmer'},
{'name': 'Anthony Edwards'},
{'name': 'Meg Ryan'}]}
在 Cypher 生成提示中添加示例
您可以定义希望 LLM 为特定问题生成的 Cypher 语句
from langchain_core.prompts.prompt import PromptTemplate
CYPHER_GENERATION_TEMPLATE = """Task:Generate Cypher statement to query a graph database.
Instructions:
Use only the provided relationship types and properties in the schema.
Do not use any other relationship types or properties that are not provided.
Schema:
{schema}
Note: Do not include any explanations or apologies in your responses.
Do not respond to any questions that might ask anything else than for you to construct a Cypher statement.
Do not include any text except the generated Cypher statement.
Examples: Here are a few examples of generated Cypher statements for particular questions:
# How many people played in Top Gun?
MATCH (m:Movie {{title:"Top Gun"}})<-[:ACTED_IN]-()
RETURN count(*) AS numberOfActors
The question is:
{question}"""
CYPHER_GENERATION_PROMPT = PromptTemplate(
input_variables=["schema", "question"], template=CYPHER_GENERATION_TEMPLATE
)
chain = GraphCypherQAChain.from_llm(
ChatOpenAI(temperature=0),
graph=graph,
verbose=True,
cypher_prompt=CYPHER_GENERATION_PROMPT,
allow_dangerous_requests=True,
)
chain.invoke("How many people played in Top Gun?")
[1m> Entering new GraphCypherQAChain chain...[0m
``````output
Generated Cypher:
[32;1m[1;3mMATCH (:Movie {name:"Top Gun"})<-[:ACTED_IN]-(:Actor)
RETURN count(*) AS numberOfActors[0m
Full Context:
[32;1m[1;3m[{'numberofactors': 4}][0m
[1m> Finished chain.[0m
{'query': 'How many people played in Top Gun?',
'result': "I don't know the answer."}
为 Cypher 和答案生成使用不同的 LLM
您可以使用 cypher_llm
和 qa_llm
参数定义不同的 LLM
chain = GraphCypherQAChain.from_llm(
graph=graph,
cypher_llm=ChatOpenAI(temperature=0, model="gpt-3.5-turbo"),
qa_llm=ChatOpenAI(temperature=0, model="gpt-3.5-turbo-16k"),
verbose=True,
allow_dangerous_requests=True,
)
chain.invoke("Who played in Top Gun?")
[1m> Entering new GraphCypherQAChain chain...[0m
``````output
Generated Cypher:
[32;1m[1;3mMATCH (a:Actor)-[:ACTED_IN]->(m:Movie)
WHERE m.name = 'Top Gun'
RETURN a.name[0m
Full Context:
[32;1m[1;3m[{'name': 'Tom Cruise'}, {'name': 'Val Kilmer'}, {'name': 'Anthony Edwards'}, {'name': 'Meg Ryan'}][0m
[1m> Finished chain.[0m
{'query': 'Who played in Top Gun?',
'result': 'Tom Cruise, Val Kilmer, Anthony Edwards, and Meg Ryan played in Top Gun.'}
忽略指定的节点和关系类型
您可以使用 include_types
或 exclude_types
在生成 Cypher 语句时忽略图架构的部分内容。
chain = GraphCypherQAChain.from_llm(
graph=graph,
cypher_llm=ChatOpenAI(temperature=0, model="gpt-3.5-turbo"),
qa_llm=ChatOpenAI(temperature=0, model="gpt-3.5-turbo-16k"),
verbose=True,
exclude_types=["Movie"],
allow_dangerous_requests=True,
)
# Inspect graph schema
print(chain.graph_schema)
Node properties are the following:
Actor {name: STRING},LabelA {property_a: STRING},LabelB {},LabelC {}
Relationship properties are the following:
ACTED_IN {},REL_TYPE {rel_prop: STRING}
The relationships are the following:
(:LabelA)-[:REL_TYPE]->(:LabelB),(:LabelA)-[:REL_TYPE]->(:LabelC)
验证生成的 Cypher 语句
您可以使用 validate_cypher
参数验证并更正生成的 Cypher 语句中的关系方向
chain = GraphCypherQAChain.from_llm(
llm=ChatOpenAI(temperature=0, model="gpt-3.5-turbo"),
graph=graph,
verbose=True,
validate_cypher=True,
allow_dangerous_requests=True,
)
chain.invoke("Who played in Top Gun?")
[1m> Entering new GraphCypherQAChain chain...[0m
Generated Cypher:
[32;1m[1;3mMATCH (a:Actor)-[:ACTED_IN]->(m:Movie)
WHERE m.name = 'Top Gun'
RETURN a.name[0m
Full Context:
[32;1m[1;3m[{'name': 'Tom Cruise'}, {'name': 'Val Kilmer'}, {'name': 'Anthony Edwards'}, {'name': 'Meg Ryan'}][0m
[1m> Finished chain.[0m
{'query': 'Who played in Top Gun?',
'result': 'Tom Cruise, Val Kilmer, Anthony Edwards, Meg Ryan played in Top Gun.'}