大模型应用开发(四):基于Langchain和LLM(千问大模型)对知识图谱(neo4j)的生成和自然语言简单问答的输出(neo4j工具、安装说明和人物关系信息都在资源里)

概要

本文主要就是介绍如何使用Langchain和LLM生成知识图谱,并且对一些简单问题进行回答,在写代码之前需要安装一个知识图谱工具neo4j,安装包和说明文档我都放到资源里了

整体架构流程

整体架构流程就是
1.通过文档生成对应的知识图谱,知识图谱主要包括以下内容,这个是通过Langchain执行文档生成的知识图谱,节点即人物和地点,关系即兄弟、姐妹、属于,
还有一个属性这里是id,属性这一块应该可以分的更加具体,比如人物名称啊这些可以定义为name啊,地点啊定义为area等等,这块因为是通过Langchain生成的那就不多做操作了,感兴趣的小伙伴可以自行修改。
2.通过生成的知识图谱进行问答生成,同时也可以生成Cypher语句。

在这里插入图片描述

技术名词解释

  • Neo4j 一个知识图谱的数据库,通过专有的Cypher语句进行增删改查。

技术细节

提示:需要安装的包pip install neo4j json-repair,Langchain的包请查看前面的两篇
话不多说直接上完整代码分为两大块

一、读取文档生成知识图谱

import os
from langchain_community.graphs import Neo4jGraph
from docx import Document as DocxDocument
from langchain_core.documents import Document
from langchain_experimental.graph_transformers import LLMGraphTransformer
from llm.model_init.init import LLModel

os.environ["DASHSCOPE_API_KEY"] = "sk-xxx"
os.environ["NEO4J_URI"] = "bolt://localhost:7687"
os.environ["NEO4J_USERNAME"] = "neo4j"
os.environ["NEO4J_DATABASE"] = "neo4j"
os.environ["NEO4J_PASSWORD"] = "neo4j"
graph = Neo4jGraph()

# qwen api
api_key = "sk-df937c6e67ff46f6b1f4c406e281dd8b"
# 初始化大模型
llm_instance = LLModel(api_key=api_key)
# 返回llm
llm = llm_instance.initialize_Tongyi_model()

llm_transformer_filtered = LLMGraphTransformer(
    llm=llm,
    allowed_nodes=["人物", "地点"],
    allowed_relationships=["属于", "姐妹", "兄弟"],
)

word_doc_path = r'D:\AI\人物关系.docx'


# 读取 Word 文档的内容
# 读取 Word 文档的内容,包括段落和表格
def read_word_document(doc_path):
    doc = DocxDocument(doc_path)
    full_text = []

    # 读取段落内容
    for para in doc.paragraphs:
        full_text.append(para.text)

    # 读取表格内容
    for table in doc.tables:
        for row in table.rows:
            row_data = [cell.text for cell in row.cells]
            full_text.append('\t'.join(row_data))

    return '\n'.join(full_text)


# 将 Word 文档内容读取并转换为 Document 对象
document_text = read_word_document(word_doc_path)

if len(document_text) > 30000:
    document_text = document_text[:30000]

documents = [Document(page_content=document_text)]

graph_documents = llm_transformer_filtered.convert_to_graph_documents(documents)

graph.add_graph_documents(graph_documents)

生成这一块就不过多阐述了,主要就是对文档的节点(allowed_nodes)和关系(allowed_relationships)的定义

llm_transformer_filtered = LLMGraphTransformer(
    llm=llm,
    allowed_nodes=["人物", "地点"],
    allowed_relationships=["属于", "姐妹", "兄弟"],
)

二、通过自然语言结合知识图谱生成答案

import os

from langchain_community.chains.graph_qa.cypher import GraphCypherQAChain
from langchain_community.graphs import Neo4jGraph
from langchain_core.prompts import PromptTemplate
from llm.model_init.init import LLModel

# qwen api
api_key = "sk-xxx"
# 初始化大模型
llm_instance = LLModel(api_key=api_key)
# 返回llm
llm = llm_instance.initialize_Tongyi_model()

class Neo4jTest:
    def neo4jInfo(query: str):
        """知识图谱工具,查询人物关系和隶属地区"""
        os.environ["NEO4J_URI"] = "bolt://localhost:7687"
        os.environ["NEO4J_USERNAME"] = "neo4j"
        os.environ["NEO4J_PASSWORD"] = "neo4j"
        os.environ["NEO4J_DATABASE"] = "neo4j"
        graph = Neo4jGraph()
        # cypher提示词
        cypher_generation_template = """  
              任务:  
              为Neo4j图数据库生成Cypher查询。  
              说明:  
              仅使用提供的模式中的关系类型和属性。  
              不要使用任何未提供的其他关系类型或属性。
              仅return出现的所有节点,只需要return问题的节点  
              模式:  
              {schema}  
              Cypher examples1:
              # 上海巴士第四公共交通有限公司有哪些线路
              MATCH (com:com {{Company: "上海巴士第四公共交通有限公司"}})-[:OPERATED_BY]->(r:route)
              MATCH (r)-[:lineOf]->(l:line)
              WITH l
              RETURN l
              注意:  
              在回答中不要包含任何解释或道歉。  
              不要回答任何可能要求你构建除Cypher语句之外的任何文本的问题。  
              确保查询中的关系方向是正确的。  
              确保正确地为实体和关系设置别名。  
              不要运行会向数据库添加或删除内容的任何查询。  
              确保将后续所有语句都设置别名为with语句。  
              如果涉及线路指向节点的是routeNameA。
              问题是:  
              {question},返回问题出现的节点,不要返回属性
              """

        cypher_generation_prompt = PromptTemplate(
            input_variables=["schema", "question"], template=cypher_generation_template
        )
        # 问答提示词
        qa_generation_template = """您是一个助手,根据Neo4j Cypher查询的结果生成人类可读的响应。  
                查询结果部分包含基于用户自然语言问题生成的Cypher查询的结果。  
                提供的信息是权威的,您绝不能怀疑它或尝试使用内部知识来纠正它。  
                使答案听起来像对问题的回答。 
                不需要回答与问题无关的内容。
                查询结果:  
                {context}  
                问题:  
                {question}  
                如果提供的信息为空,请说您不知道答案。  
                空信息的样子是这样的:[] 
                如果信息不为空,您必须使用结果提供答案。
                """

        qa_generation_prompt = PromptTemplate(
            input_variables=["context", "question"], template=qa_generation_template
        )
        # graph neo4j数据库连接
        # llm 大模型
        # verbose true则输出cypher语句和返回的Full Context
        # validate_cypher true 确保语法正确,语义和底层数据匹配

        hospital_cypher_chain = GraphCypherQAChain.from_llm(
            llm=llm,
            graph=graph,
            verbose=True,
            qa_prompt=qa_generation_prompt,
            cypher_prompt=cypher_generation_prompt,
            validate_cypher=True
        )

        response = hospital_cypher_chain.invoke(query)
        response = response["result"]
        return response




if __name__ == "__main__":
    print("------" + Neo4jTest.neo4jInfo("祖安有哪些人物") + "------")

这里有几个重点部分,咱们拆开来讲

1.大模型的连接

这部分内容就是这一块

# qwen api
api_key = "sk-xxx"
# 初始化大模型
llm_instance = LLModel(api_key=api_key)
# 返回llm
llm = llm_instance.initialize_Tongyi_model()

LLModel工具在我的另一篇文章中基于LangChain+LLM(千问)的基础问答使用,非常基础的内容哦 可以看到

2.neo4j的连接和创建

    def neo4jInfo(query: str):
        """知识图谱工具,查询人物关系和隶属地区"""
        os.environ["NEO4J_URI"] = "bolt://localhost:7687"
        os.environ["NEO4J_USERNAME"] = "neo4j"
        os.environ["NEO4J_PASSWORD"] = "neo4j"
        os.environ["NEO4J_DATABASE"] = "neo4j"
        graph = Neo4jGraph()

知识图谱工具,查询人物关系和隶属地区 这一段是工具的描述,这是后续agent需要学习的内容,可以进行一个了解,暂时直接理解为注释即可。

3.Cypher的提示词工程

	我们可以看到这里吧主要包含几个点
	1.return仅需要问题相关的节点
	2.模式{schema},是包含在 graph = Neo4jGraph()的属性,是一个neo4j知识图谱整体结构的属性,有兴趣的可以debug看一下
	3.Cypher examples 可以根据示例输出对应的Cypher,来对一些比较固定的回答生成答案。在提示词中{}里面的内容都是参数,如果要在提示词中使用{}变成文字,需要再加一个{{}},比如{{id: "蔚"}},对应的就是文字{id: "蔚"},这是cypher语句固定格式
	4.解释一下Cypher语句
	MATCH (ws:人物 {id: "蔚"})-[:姐妹]->(ss:人物) RETURN ws, ss
	MATCH就是查询的意思,第一个ws是类似于sql里的别名,可以随意起然后 别名:人物(具体查询的节点)id即属性:”蔚”。
	(ws:人物 {id: "蔚"})这个语句的意思就是人物名称是蔚,别名是ws
	[:姐妹] 姐妹是关系,固定格式[:关系]
	->连接查询结果
	(ss:人物) 查询结果的节点是人物,ss别名
	最终得出MATCH (ws:人物 {id: "蔚"})-[:姐妹]->(ss:人物) RETURN ws, ss 查询蔚(ws)的姐妹是哪个人物(ss)
        # cypher提示词
        cypher_generation_template = """  
              任务:  
              为Neo4j图数据库生成Cypher查询。  
              说明:  
              仅使用提供的模式中的关系类型和属性。  
              不要使用任何未提供的其他关系类型或属性。
              仅return出现的所有节点,只需要return问题的节点  
              请根据示例输出一样的Cypher
              模式:  
              {schema}  
              Cypher examples1:
              # 薇的姐妹是谁
                cypher
                MATCH (ws:人物 {{id: "蔚"}})-[:姐妹]->(ss:人物)
                RETURN ws, ss
              注意:  
              在回答中不要包含任何解释或道歉。  
              不要回答任何可能要求你构建除Cypher语句之外的任何文本的问题。  
              确保查询中的关系方向是正确的。  
              确保正确地为实体和关系设置别名。  
              不要运行会向数据库添加或删除内容的任何查询。  
              确保将后续所有语句都设置别名为with语句。  
              问题是:  
              {question},返回问题出现的节点,不要返回属性
              """
             #提示词中的schema是graph 中的属性,question就是问题
        cypher_generation_prompt = PromptTemplate(
            input_variables=["schema", "question"], template=cypher_generation_template
        )

4.问答的提示词工程

	很简单就是传入context结果和question,context也是在graph中生成的答案
        # 问答提示词
        qa_generation_template = """您是一个助手,根据Neo4j Cypher查询的结果生成人类可读的响应。  
                查询结果部分包含基于用户自然语言问题生成的Cypher查询的结果。  
                提供的信息是权威的,您绝不能怀疑它或尝试使用内部知识来纠正它。  
                使答案听起来像对问题的回答。 
                不需要回答与问题无关的内容。
                查询结果:  
                {context}  
                问题:  
                {question}  
                如果提供的信息为空,请说您不知道答案。  
                空信息的样子是这样的:[] 
                如果信息不为空,您必须使用结果提供答案。
                """

        qa_generation_prompt = PromptTemplate(
            input_variables=["context", "question"], template=qa_generation_template
        )

5.neo4j和大模型的应用

	通过GraphCypherQAChain方法执行大模型,还有包含一些参数输入的含义都在注释里
        # graph neo4j数据库连接
        # llm 大模型
        # verbose true则输出cypher语句和返回的Full Context
        # validate_cypher true 确保语法正确,语义和底层数据匹配

        hospital_cypher_chain = GraphCypherQAChain.from_llm(
            llm=llm,
            graph=graph,
            verbose=True,
            qa_prompt=qa_generation_prompt,
            cypher_prompt=cypher_generation_prompt,
            validate_cypher=True
        )

        response = hospital_cypher_chain.invoke(query)
        response = response["result"]
        return response

6.尝试调用

首先查询示例中的问答薇的姐妹是谁
if __name__ == "__main__":
    print("------" + Neo4jTest.neo4jInfo("薇的姐妹是谁") + "------")

提示:尝试调用代码之前,这里有部分源码需要修改E:\conda\envs\fastApiProject\Lib\site-packages\langchain_community\chains\graph_qa\cypher.py

1.查询context = self.graph.query(generated_cypher)[: self.top_k]
去除[: self.top_k]部分,否则问答输出有限
2.查询chain_result: Dict[str, Any] = {self.output_key: final_result}
修改为 修改返回格式增加cypher语句和context内容
chain_result: Dict[str, Any] = {
        self.output_key: final_result,
        "cypher_query": generated_cypher,
        "context": context
        }
3.如果出现ValueError: In order to use this chain, you must acknowledge that it can make dangerous requests by setting allow_dangerous_requests to True.报错内容
allow_dangerous_requests: bool = False
修改为True (官方解释改为True 强制用户选择加入以确认链可以发出危险的请求)
这个问题可能是版本原因,有的版本没有这个问题

大模型回答

D:\AI\conda\envs\aiAgent\python.exe D:\Pycharm\aiAgent\pythonProject\llm\neo4j\neo4j_test.py 


> Entering new GraphCypherQAChain chain...
Generated Cypher:
cypher
MATCH (ws:人物 {id: "蔚"})-[:姐妹]->(ss:人物)
RETURN ws, ss

Full Context:
[{'ws': {'id': '蔚'}, 'ss': {'id': '金克丝'}}]

> Finished chain.
------薇的姐妹是金克丝。------

Process finished with exit code 0

然后再问一个祖安有些人

D:\AI\conda\envs\aiAgent\python.exe -X pycache_prefix=C:\Users\yuanzhuo\AppData\Local\JetBrains\PyCharm2024.1\cpython-cache "D:/Pycharm/PyCharm 2024.1.6/plugins/python/helpers/pydev/pydevd.py" --multiprocess --qt-support=auto --client 127.0.0.1 --port 56810 --file D:\Pycharm\aiAgent\pythonProject\llm\neo4j\neo4j_test.py 
Connected to pydev debugger (build 241.19072.16)


> Entering new GraphCypherQAChain chain...
Generated Cypher:
cypher
MATCH (z:地点 {id: "祖安"})<-[:属于]-(p:人物)
RETURN p, z

Full Context:
[{'p': {'id': '沃里克'}, 'z': {'id': '祖安'}}, {'p': {'id': '金克丝'}, 'z': {'id': '祖安'}}, {'p': {'id': '艾克'}, 'z': {'id': '祖安'}}, {'p': {'id': '辛吉德'}, 'z': {'id': '祖安'}}, {'p': {'id': '吉格斯'}, 'z': {'id': '祖安'}}]

> Finished chain.
------祖安有以下人物:沃里克、金克丝、艾克、辛吉德和吉格斯。------

Process finished with exit code 0

如果你想修改final_result的格式也可以在问答的提示词中展示示例

        # 问答提示词
        qa_generation_template = """您是一个助手,根据Neo4j Cypher查询的结果生成人类可读的响应。  
                查询结果部分包含基于用户自然语言问题生成的Cypher查询的结果。  
                提供的信息是权威的,您绝不能怀疑它或尝试使用内部知识来纠正它。  
                使答案听起来像对问题的回答。 
                不需要回答与问题无关的内容。
                查询结果:  
                {context}  
                问题:  
                {question}  
                示例1:
                问题:祖安有哪些人
                回答:英雄联盟祖安包含的英雄有
                如果提供的信息为空,请说您不知道答案。  
                空信息的样子是这样的:[] 
                如果信息不为空,您必须使用结果提供答案。
                """

按照示例的回答祖安有哪些人

D:\AI\conda\envs\aiAgent\python.exe D:\Pycharm\aiAgent\pythonProject\llm\neo4j\neo4j_test.py 


> Entering new GraphCypherQAChain chain...
Generated Cypher:
cypher
MATCH (z:地点 {id: "祖安"})<-[:属于]-(p:人物)
RETURN z, p

Full Context:
[{'z': {'id': '祖安'}, 'p': {'id': '沃里克'}}, {'z': {'id': '祖安'}, 'p': {'id': '金克丝'}}, {'z': {'id': '祖安'}, 'p': {'id': '艾克'}}, {'z': {'id': '祖安'}, 'p': {'id': '辛吉德'}}, {'z': {'id': '祖安'}, 'p': {'id': '吉格斯'}}]

> Finished chain.
------英雄联盟祖安包含的英雄有沃里克、金克丝、艾克、辛吉德和吉格斯。------

Process finished with exit code 0

小结
是不是很简单,知识图谱是一种结构化知识的表示形式,通过节点和边来表示实体及其之间的关系,我们可以实现增强搜索引擎效果、智能问答、推荐系统、语义理解与文本分析、数据整合与管理等等功能,感谢大家的观看,希望对你有帮助,谢谢。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值