跳转到主要内容

标签(标签)

资源精选(342) Go开发(108) Go语言(103) Go(99) angular(82) LLM(75) 大语言模型(63) 人工智能(53) 前端开发(50) LangChain(43) golang(43) 机器学习(39) Go工程师(38) Go程序员(38) Go开发者(36) React(33) Go基础(29) Python(24) Vue(22) Web开发(20) Web技术(19) 精选资源(19) 深度学习(19) Java(18) ChatGTP(17) Cookie(16) android(16) 前端框架(13) JavaScript(13) Next.js(12) 安卓(11) 聊天机器人(10) typescript(10) 资料精选(10) NLP(10) 第三方Cookie(9) Redwoodjs(9) LLMOps(9) Go语言中级开发(9) 自然语言处理(9) PostgreSQL(9) 区块链(9) mlops(9) 安全(9) 全栈开发(8) ChatGPT(8) OpenAI(8) Linux(8) AI(8) GraphQL(8) iOS(8) 软件架构(7) Go语言高级开发(7) AWS(7) C++(7) 数据科学(7) whisper(6) Prisma(6) 隐私保护(6) RAG(6) JSON(6) DevOps(6) 数据可视化(6) wasm(6) 计算机视觉(6) 算法(6) Rust(6) 微服务(6) 隐私沙盒(5) FedCM(5) 语音识别(5) Angular开发(5) 快速应用开发(5) 提示工程(5) Agent(5) LLaMA(5) 低代码开发(5) Go测试(5) gorm(5) REST API(5) 推荐系统(5) WebAssembly(5) GameDev(5) CMS(5) CSS(5) machine-learning(5) 机器人(5) 游戏开发(5) Blockchain(5) Web安全(5) Kotlin(5) 低代码平台(5) 机器学习资源(5) Go资源(5) Nodejs(5) PHP(5) Swift(5) 智能体(4) devin(4) Blitz(4) javascript框架(4) Redwood(4) GDPR(4) 生成式人工智能(4) Angular16(4) Alpaca(4) 编程语言(4) SAML(4) JWT(4) JSON处理(4) Go并发(4) kafka(4) 移动开发(4) 移动应用(4) security(4) 隐私(4) spring-boot(4) 物联网(4) nextjs(4) 网络安全(4) API(4) Ruby(4) 信息安全(4) flutter(4) 专家智能体(3) Chrome(3) CHIPS(3) 3PC(3) SSE(3) 人工智能软件工程师(3) LLM Agent(3) Remix(3) Ubuntu(3) GPT4All(3) 软件开发(3) 问答系统(3) 开发工具(3) 最佳实践(3) RxJS(3) SSR(3) Node.js(3) Dolly(3) 移动应用开发(3) 低代码(3) IAM(3) Web框架(3) CORS(3) 基准测试(3) Go语言数据库开发(3) Oauth2(3) 并发(3) 主题(3) Theme(3) earth(3) nginx(3) 软件工程(3) azure(3) keycloak(3) 生产力工具(3) gpt3(3) 工作流(3) C(3) jupyter(3) 认证(3) prometheus(3) GAN(3) Spring(3) 逆向工程(3) 应用安全(3) Docker(3) Django(3) R(3) .NET(3) 大数据(3) Hacking(3) 渗透测试(3) C++资源(3) Mac(3) 微信小程序(3) Python资源(3) JHipster(3) 大型语言模型(2) 语言模型(2) 可穿戴设备(2) JDK(2) SQL(2) Apache(2) Hashicorp Vault(2) Spring Cloud Vault(2) Go语言Web开发(2) Go测试工程师(2) WebSocket(2) 容器化(2) AES(2) 加密(2) 输入验证(2) ORM(2) Fiber(2) Postgres(2) Gorilla Mux(2) Go数据库开发(2) 模块(2) 泛型(2) 指针(2) HTTP(2) PostgreSQL开发(2) Vault(2) K8s(2) Spring boot(2) R语言(2) 深度学习资源(2) 半监督学习(2) semi-supervised-learning(2) architecture(2) 普罗米修斯(2) 嵌入模型(2) productivity(2) 编码(2) Qt(2) 前端(2) Rust语言(2) NeRF(2) 神经辐射场(2) 元宇宙(2) CPP(2) 数据分析(2) spark(2) 流处理(2) Ionic(2) 人体姿势估计(2) human-pose-estimation(2) 视频处理(2) deep-learning(2) kotlin语言(2) kotlin开发(2) burp(2) Chatbot(2) npm(2) quantum(2) OCR(2) 游戏(2) game(2) 内容管理系统(2) MySQL(2) python-books(2) pentest(2) opengl(2) IDE(2) 漏洞赏金(2) Web(2) 知识图谱(2) PyTorch(2) 数据库(2) reverse-engineering(2) 数据工程(2) swift开发(2) rest(2) robotics(2) ios-animation(2) 知识蒸馏(2) 安卓开发(2) nestjs(2) solidity(2) 爬虫(2) 面试(2) 容器(2) C++精选(2) 人工智能资源(2) Machine Learning(2) 备忘单(2) 编程书籍(2) angular资源(2) 速查表(2) cheatsheets(2) SecOps(2) mlops资源(2) R资源(2) DDD(2) 架构设计模式(2) 量化(2) Hacking资源(2) 强化学习(2) flask(2) 设计(2) 性能(2) Sysadmin(2) 系统管理员(2) Java资源(2) 机器学习精选(2) android资源(2) android-UI(2) Mac资源(2) iOS资源(2) Vue资源(2) flutter资源(2) JavaScript精选(2) JavaScript资源(2) Rust开发(2) deeplearning(2) RAD(2)

category

在使用Neo4j和LangChain的RAG应用程序中构建和检索知识图信息的实用指南


编者按:以下是Tomaz Bratanic的客座博客文章,他专注于Neo4j的Graph ML和GenAI研究。Neo4j是一家图形数据库和分析公司,它帮助组织深入、轻松、快速地发现数十亿数据连接中隐藏的关系和模式。


图检索增强生成(Graph RAG)作为传统矢量搜索检索方法的强大补充,正在获得发展势头。这种方法利用了图数据库的结构化特性,将数据组织为节点和关系,以增强检索信息的深度和上下文性。

 

Example knowledge graph.

图形非常擅长以结构化的方式表示和存储异构和互连的信息,可以轻松地捕捉不同数据类型之间的复杂关系和属性。相比之下,矢量数据库往往难以处理此类结构化信息,因为它们的优势在于通过高维矢量处理非结构化数据。在您的RAG应用程序中,您可以将结构化图形数据与通过非结构化文本进行的矢量搜索相结合,以实现两全其美,这正是我们将在本博客文章中所做的。

知识图很好,但你是如何创建知识图的?构建知识图通常是利用基于图的数据表示能力的最具挑战性的步骤。它涉及到数据的收集和结构化,这需要对领域和图形建模有深入的了解。为了简化这个过程,我们一直在试验LLM。LLM凭借其对语言和上下文的深刻理解,可以自动化知识图创建过程的重要部分。通过分析文本数据,这些模型可以识别实体,了解它们之间的关系,并建议如何在图结构中最好地表示它们。作为这些实验的结果,我们在LangChain中添加了图形构建模块的第一个版本,我们将在这篇博客文章中进行演示。

该代码可在GitHub上获得。

Neo4j环境设置


您需要设置一个Neo4j实例,请参阅本文中的示例。最简单的方法是在Neo4j Aura上启动一个免费实例,该实例提供Neo4j数据库的云实例。或者,您也可以通过下载Neo4j Desktop应用程序并创建本地数据库实例来设置Neo4j数据库的本地实例。

os.environ["OPENAI_API_KEY"] = "sk-"
os.environ["NEO4J_URI"] = "bolt://localhost:7687"
os.environ["NEO4J_USERNAME"] = "neo4j"
os.environ["NEO4J_PASSWORD"] = "password"

graph = Neo4jGraph()

此外,您必须提供一个OpenAI密钥,因为我们将在这篇博客文章中使用他们的模型。

数据摄入


在这个演示中,我们将使用伊丽莎白一世的维基百科页面。我们可以使用LangChain加载程序无缝地从维基百科中获取和分割文档。

# Read the wikipedia article
raw_documents = WikipediaLoader(query="Elizabeth I").load()

# Define chunking strategy
text_splitter = TokenTextSplitter(chunk_size=512, chunk_overlap=24)
documents = text_splitter.split_documents(raw_documents[:3])

 

现在是时候根据检索到的文档构建一个图了。为此,我们实现了一个LLMGraphTransformermodule,它大大简化了在图数据库中构建和存储知识图的过程。

llm=ChatOpenAI(temperature=0, model_name="gpt-4-0125-preview")
llm_transformer = LLMGraphTransformer(llm=llm)

# Extract graph data
graph_documents = llm_transformer.convert_to_graph_documents(documents)

# Store to neo4j
graph.add_graph_documents(
  graph_documents, 
  baseEntityLabel=True, 
  include_source=True
)

 

您可以定义知识图生成链要使用的LLM。目前,我们只支持OpenAI和Mistral的函数调用模型。然而,我们计划在未来扩大LLM的选择范围。在本例中,我们使用的是最新的GPT-4。请注意,生成的图的质量在很大程度上取决于所使用的模型。理论上,你总是想用最能干的人。LLM图形转换器返回图形文档,这些文档可以通过add_graph_documents方法导入到Neo4j。baseEntityLabel参数为每个节点分配一个额外的__Entity__标签,从而提高索引和查询性能。include_source参数将节点链接到其原始文档,便于数据跟踪和上下文理解。

您可以在Neo4j浏览器中检查生成的图形。

Part of the generated graph.

请注意,为了清晰起见,此图像仅表示生成的图形的一部分。


RAG的混合检索


在生成图之后,我们将使用一种混合检索方法,该方法将矢量索引和关键字索引与RAG应用程序的图检索相结合。

Combining hybrid (vector + keyword) and graph retrieval methods. Image by author.

该图说明了从用户提出问题开始的检索过程,然后将问题引导到RAG检索器。该检索器使用关键字和矢量搜索来搜索非结构化文本数据,并将其与从知识图中收集的信息相结合。由于Neo4j同时具有关键字索引和矢量索引,因此可以使用单个数据库系统实现所有三种检索选项。从这些来源收集的数据被输入LLM,以生成并提供最终答案。

非结构化数据检索器


您可以使用Neo4jVector.from_existing_graph方法将关键字和矢量检索添加到文档中。此方法为混合搜索方法配置关键字和矢量搜索索引,以标记为Document的节点为目标。此外,如果文本嵌入值丢失,它还会计算这些值。

vector_index = Neo4jVector.from_existing_graph(
    OpenAIEmbeddings(),
    search_type="hybrid",
    node_label="Document",
    text_node_properties=["text"],
    embedding_node_property="embedding"
)


然后可以使用similarity_search方法调用向量索引。

图形检索器


另一方面,配置图检索更为复杂,但提供了更多的自由度。在本例中,我们将使用全文索引来标识相关节点,然后返回它们的直接邻域。

Graph retriever. Image by author.

图形检索器首先识别输入中的相关实体。为了简单起见,我们指示LLM识别人员、组织和地点。为了实现这一点,我们将使用LCEL和新添加的with_structured_output方法来实现这一目标。

# Extract entities from text
class Entities(BaseModel):
    """Identifying information about entities."""

    names: List[str] = Field(
        ...,
        description="All the person, organization, or business entities 
        that " "appear in the text",
    )

prompt = ChatPromptTemplate.from_messages(
    [
        (
            "system",
            "You are extracting organization and person entities from the 
            text.",
        ),
        (
            "human",
            "Use the given format to extract information from the
             following"
            "input: {question}",
        ),
    ]
)

entity_chain = prompt | llm.with_structured_output(Entities)

让我们测试一下:

entity_chain.invoke({"question": "Where was Amelia Earhart born?"}).names
# ['Amelia Earhart']


很好,现在我们可以检测问题中的实体了,让我们使用全文索引将它们映射到知识图。首先,我们需要定义一个全文索引和一个函数,该函数将生成允许拼写错误的全文查询,这里我们不详细介绍。

graph.query(
    "CREATE FULLTEXT INDEX entity IF NOT EXISTS FOR (e:__Entity__) ON EACH [e.id]")

def generate_full_text_query(input: str) -> str:
    """
    Generate a full-text search query for a given input string.

    This function constructs a query string suitable for a full-text
    search. It processes the input string by splitting it into words and 
    appending a similarity threshold (~2 changed characters) to each
    word, then combines them using the AND operator. Useful for mapping
    entities from user questions to database values, and allows for some 
    misspelings.
    """
    full_text_query = ""
    words = [el for el in remove_lucene_chars(input).split() if el]
    for word in words[:-1]:
        full_text_query += f" {word}~2 AND"
    full_text_query += f" {words[-1]}~2"
    return full_text_query.strip()


让我们现在把它们放在一起。

# Fulltext index query
def structured_retriever(question: str) -> str:
    """
    Collects the neighborhood of entities mentioned
    in the question
    """
    result = ""
    entities = entity_chain.invoke({"question": question})
    for entity in entities.names:
        response = graph.query(
            """CALL db.index.fulltext.queryNodes('entity', $query, 
            {limit:2})
            YIELD node,score
            CALL {
              MATCH (node)-[r:!MENTIONS]->(neighbor)
              RETURN node.id + ' - ' + type(r) + ' -> ' + neighbor.id AS 
              output
              UNION
              MATCH (node)<-[r:!MENTIONS]-(neighbor)
              RETURN neighbor.id + ' - ' + type(r) + ' -> ' +  node.id AS 
              output
            }
            RETURN output LIMIT 50
            """,
            {"query": generate_full_text_query(entity)},
        )
        result += "\n".join([el['output'] for el in response])
    return result


structured_retriever函数从检测用户问题中的实体开始。接下来,它对检测到的实体进行迭代,并使用Cypher模板来检索相关节点的邻域。让我们测试一下!

print(structured_retriever("Who is Elizabeth I?"))
# Elizabeth I - BORN_ON -> 7 September 1533
# Elizabeth I - DIED_ON -> 24 March 1603
# Elizabeth I - TITLE_HELD_FROM -> Queen Of England And Ireland
# Elizabeth I - TITLE_HELD_UNTIL -> 17 November 1558
# Elizabeth I - MEMBER_OF -> House Of Tudor
# Elizabeth I - CHILD_OF -> Henry Viii
# and more...


最终检索器


正如我们在开头提到的,我们将结合非结构化和图检索器来创建将传递给LLM的最终上下文。

def retriever(question: str):
    print(f"Search query: {question}")
    structured_data = structured_retriever(question)
    unstructured_data = [el.page_content for el in vector_index.similarity_search(question)]
    final_data = f"""Structured data:
{structured_data}
Unstructured data:
{"#Document ". join(unstructured_data)}
    """
    return final_data


当我们处理Python时,我们可以简单地使用f字符串连接输出。

定义RAG链


我们已经成功地实现了RAG的检索组件。接下来,我们介绍一个提示,它利用集成混合检索器提供的上下文来生成响应,完成了RAG链的实现。

template = """Answer the question based only on the following context:
{context}

Question: {question}
"""
prompt = ChatPromptTemplate.from_template(template)

chain = (
    RunnableParallel(
        {
            "context": _search_query | retriever,
            "question": RunnablePassthrough(),
        }
    )
    | prompt
    | llm
    | StrOutputParser()
)

最后,我们可以继续测试我们的混合RAG实现。

chain.invoke({"question": "Which house did Elizabeth I belong to?"})
# Search query: Which house did Elizabeth I belong to?
# 'Elizabeth I belonged to the House of Tudor.'


我还加入了一个查询重写功能,使RAG链能够适应允许后续问题的对话设置。考虑到我们使用矢量和关键字搜索方法,我们必须重写后续问题,以优化我们的搜索过程。

chain.invoke(
    {
        "question": "When was she born?",
        "chat_history": [("Which house did Elizabeth I belong to?",
        "House Of Tudor")],
    }
)
# Search query: When was Elizabeth I born?
# 'Elizabeth I was born on 7 September 1533.'


你可以观察到她是什么时候出生的?第一次改写为“伊丽莎白一世何时出生?”。然后使用重写后的查询来检索相关上下文并回答问题。

总结


随着LLMGraphTransformer的引入,生成知识图的过程现在应该更顺畅、更容易访问,使任何希望利用知识图提供的深度和上下文来增强其基于RAG的应用程序的人都更容易。这只是一个开始,因为我们有很多改进计划。

如果您对我们使用LLM生成图形有任何见解、建议或问题,请随时联系。

The code is available on GitHub.

文章链接