前提:已经安装好了neo4j软件
1、代码如下:
import os
from typing import List
from langchain_openai import ChatOpenAI
from langchain_neo4j import Neo4jGraph, GraphCypherQAChain
from langchain_core.documents import Document
from langchain_core.prompts import ChatPromptTemplate
from langchain_core.output_parsers import JsonOutputParser
from langchain_text_splitters import RecursiveCharacterTextSplitter
from dotenv import load_dotenv
from langchain_community.graphs.graph_document import (
GraphDocument,
Node as LangChainNode,
Relationship as LangChainRelationship,
)
load_dotenv()
NEO4J_URI = "neo4j://localhost:7687"
NEO4J_USERNAME = "neo4j"
NEO4J_PASSWORD = "mima" # 替换为你的密码
# 初始化 LLM
llm = ChatOpenAI(
model="qwen3.7-plus",
api_key=os.getenv("DASHSCOPE_API_KEY"),
base_url=os.getenv("DASHSCOPE_BASE_URL"),
# 温度控制
temperature=0.15,
)
# 连接 Neo4j
graph = Neo4jGraph(
url=NEO4J_URI,
username=NEO4J_USERNAME,
password=NEO4J_PASSWORD
)
extraction_prompt = ChatPromptTemplate.from_messages([
("system", """你是一个擅长从中文古典小说中提取知识图谱的专家。
请严格从以下文本中提取主要**实体**和**关系**,重点关注《西游记》相关内容。
实体类型建议(但不强制只用这些):
Person(人、神、妖、仙)、Place(地点、山、洞府、天庭)、Item(法宝、兵器、宝贝)、Event(事件)、Group(组织、派系)
关系类型建议(常用):
MASTER_OF, DISCIPLE_OF(师徒)、LOCATED_IN(位于)、OWNS(拥有)、USED_BY(使用)、ENEMY_OF(敌人)、BATTLE_WITH(战斗)、FROM(来自)、CREATED_BY(制造)、TRANSFORMED_INTO(变成)等
规则:
1. 只提取文本中明确出现或强烈暗示的信息,不要脑补。
2. 实体名称尽量使用原文最常见的叫法(例如:孙悟空 而非 美猴王,除非上下文只用了美猴王)。
3. 同一个实体在不同chunk中应尽量保持名称一致。
4. 输出**必须**是合法的 JSON,不要包含任何解释、注释、markdown。
5. 如果某段文本实在没有可提取内容,返回空数组。
重要:每个节点 **必须** 有 "id" 字段,且 "id" 是实体的主要名称(例如 "孙悟空"、"菩提祖师"、"斜月三星洞")。
如果有别名或中文名,可放在 properties 里的 "别称" 或 "中文名",但 "id" 必须是最常用的叫法。
输出格式(**严格**遵守,不要多一个字):
{{
"nodes": [
{{"id": "孙悟空", "type": "Person"}},
{{"id": "菩提祖师", "type": "Person"}},
{{"id": "斜月三星洞", "type": "Place"}}
],
"relationships": [
{{"source": "孙悟空", "target": "菩提祖师", "type": "DISCIPLE_OF"}},
{{"source": "孙悟空", "target": "斜月三星洞", "type": "LEARNED_AT"}}
]
}}
- "id" 是必须的,且全局唯一(同一个实体不同 chunk 用相同 id)
- type 尽量用:Person, Place, Item, Group
- 关系 type 用英文大写 + 下划线,如 DISCIPLE_OF, LOCATED_IN, OWNS, BATTLE_WITH
只返回纯 JSON。
"""),
("human", "文本:\n{text}\n请提取。")
])
json_parser = JsonOutputParser()
extract_chain = extraction_prompt | llm | json_parser
def extract_one_document(doc: Document) -> GraphDocument:
"""处理单个 Document → GraphDocument"""
print('需要构建节点和关系的文本:', doc.page_content)
raw = extract_chain.invoke({"text": doc.page_content})
print("LLM 输出:", raw)
if not isinstance(raw, dict):
print("LLM 输出不是 dict,跳过该 chunk")
return GraphDocument(nodes=[], relationships=[], source=doc)
nodes = []
node_map = {} # id → Node 对象,避免重复
for n in raw.get("nodes", []):
nid = n.get("id")
if not nid:
continue
if nid in node_map:
continue
node_type = n.get("type", "Entity")
props = n.get("properties", {})
node = LangChainNode(id=nid, type=node_type, properties=props)
node_map[nid] = node
nodes.append(node)
relationships = []
for r in raw.get("relationships", []):
src_id = r.get("source")
tgt_id = r.get("target")
rel_type = r.get("type")
if not (src_id and tgt_id and rel_type):
continue
if src_id not in node_map or tgt_id not in node_map:
continue # 防止孤立关系
props = r.get("properties", {})
rel = LangChainRelationship(
source=node_map[src_id],
target=node_map[tgt_id],
type=rel_type,
properties=props
)
relationships.append(rel)
return GraphDocument(
nodes=nodes,
relationships=relationships,
source=doc
)
def ingest_data(file_path: str):
with open(file_path, "r", encoding="utf-8") as f:
text = f.read()
# 文本切分(建议 chunk 不要太大)
text_splitter = RecursiveCharacterTextSplitter(
chunk_size=1200,
chunk_overlap=200,
separators=["\n\n", "\n", "。", ",", " ", ""]
)
chunks = text_splitter.split_text(text)
documents = [Document(page_content=c) for c in chunks]
print(f"共切分为 {len(documents)} 个 chunk,开始提取图结构...")
graph_documents: List[GraphDocument] = []
for i, doc in enumerate(documents, 1):
if i % 10 == 0 or i == len(documents):
print(f" 处理中... {i}/{len(documents)}")
gd = extract_one_document(doc)
if gd.nodes or gd.relationships:
graph_documents.append(gd)
print(graph_documents)
print(f"提取到 {len(graph_documents)} 个有效 GraphDocument")
# 插入 Neo4j
if graph_documents:
print("正在写入 Neo4j ...")
graph.add_graph_documents(
graph_documents,
# 给每一个新建的实体节点额外贴一个标签:__Entity__
baseEntityLabel=True,
# 是否把原始文本 chunk(源文档)也存成节点,并用 MENTIONS 关系连到实体上?
include_source=True # 不存 chunk 节点
)
print("数据插入完成。")
else:
print("没有提取到任何节点/关系,跳过写入。")
def get_graph_rag_chain():
# 刷新图结构
graph.refresh_schema()
# 让用户可以用普通中文/英文问题 直接问 Neo4j 图数据库,背后 LLM 会自动把问题翻译成 Cypher 查询 → 执行 → 把结果转回自然语言回答
chain = GraphCypherQAChain.from_llm(
llm=llm,
graph=graph,
# 是否打印详细日志
verbose=True,
# 允许链执行"可能危险"的 CQL
allow_dangerous_requests=True,
# 返回的前 k 条结果(默认 10)
top_k=30, # 可适当增大
)
return chain
if __name__ == "__main__":
# 只需执行一次建图
ingest_data("西游记.txt")
# rag_chain = get_graph_rag_chain()
# questions = [
# "孙悟空的师父是谁?他在哪里学艺?",
# "花果山在哪里?",
# "为什么孙悟空被压在五行山下?",
# ]
# for q in questions:
# print(f"\n问题:{q}")
# response = rag_chain.invoke({"query": q})
# print(f"回答:{response['result']}")
2、运行结果如下:
D:\soft\miniconda3\envs\py_llm\python.exe C:\Users\lenovo\PycharmProjects\PythonProject\rag\day10\02-提示词知识图谱.py
C:\Users\lenovo\PycharmProjects\PythonProject\rag\day10\02-提示词知识图谱.py:14: DeprecationWarning: `langchain-community` is being sunset and is no longer actively maintained. See https://github.com/langchain-ai/langchain-community/issues/674 for details and migration guidance toward standalone integration packages.
from langchain_community.graphs.graph_document import (
共切分为 7 个 chunk,开始提取图结构...
需要构建节点和关系的文本: --- 西游记\第1回 惊天地美猴王出世.txt ---
LLM 输出: {'nodes': [], 'relationships': []}
需要构建节点和关系的文本: 这是一个神话故事,传说在很久很久以前,天下分为东胜神洲、西牛贺洲、南赡部洲、北俱芦洲。在东胜神洲傲来国,有一座花果山,山上有一块仙石,一天仙石崩裂,从石头中滚出一个卵,这个卵一见风就变成一个石猴,猴眼射出一道道金光,向四方朝拜。
那猴能走、能跑,渴了就喝些山涧中的泉水,饿了就吃些山上的果子。
整天和山中的动物一起玩乐,过得十分快活。一天,天气特别热,猴子们为了躲避炎热的天气,跑到山涧里洗澡。它们看见这泉水哗哗地流,就顺着涧往前走,去寻找它的源头。
猴子们爬呀、爬呀,走到了尽头,却看见一股瀑布,像是从天而降一样。猴子们觉得惊奇,商量说∶"哪个敢钻进瀑布,把泉水的源头找出来,又不伤身体,就拜他为王。"连喊了三遍,那石猴呼地跳了出来,高声喊道∶"我进去,我进去!"
那石猴闭眼纵身跳入瀑布,觉得不像是在水中,这才睁开眼,四处打量,发现自己站在一座铁板桥上,桥下的水冲贯于石窍之间,倒挂着流出来,将桥门遮住,使外面的人看不到里面。石猴走过桥,发现这真是个好地方,石椅、石床、石盆、石碗,样样都有。
这里就像不久以前有人住过一样,天然的房子,安静整洁,锅、碗、瓢、盆,整齐地放在炉灶上。正当中有一块石碑,上面刻着∶花果山福地,水帘洞洞天。石猴高兴得不得了,忙转身向外走去,嗖的一下跳出了洞。
猴子们见石猴出来了,身上又一点伤也没有,又惊又喜,把他团团围住,争著问他里面的情况。石猴抓抓腮,挠挠痒,笑嘻嘻地对大家说∶"里面没有水,是一个安身的好地方,刮大风我们有地方躲,下大雨我们也不怕淋。"猴子们一听,一个个高兴得又蹦又跳。
猴子们随着石猴穿过了瀑布,进入水帘洞中,看见了这么多的好东西,一个个你争我夺,拿盆的拿盆,拿碗的拿碗,占灶的占灶,争床的争床,搬过来,移过去,直到精疲力尽为止。猴子们都遵照诺言,拜石猴为王,石猴从此登上王位,将石字省去,自称"美猴王"。
美猴王每天带着猴子们游山玩水,很快三、五百年过去了。一天正在玩乐时,美猴王想到自己将来难免一死,不由悲伤得掉下眼泪来,这时猴群中跳出个通背猿猴来,说∶"大王想要长生不老,只有去学佛、学仙、学神之术。"
美猴王决定走遍天涯海角,也要找到神仙,学那长生不老的本领。第二天,猴子们为他做了一个木筏,又准备了一些野果,于是美猴王告别了群猴们,一个人撑着木筏,奔向汪洋大海。
大概是美猴王的运气好,连日的东南风,将他送到西北岸边。他下了木筏,登上了岸,看见岸边有许多人都在干活,有的捉鱼,有的打天上的大雁,有的挖蛤蜊,有的淘盐,他悄悄地走过去,没想到,吓得那些人将东西一扔,四处逃命。
这一天,他来到一座高山前,突然从半山腰的树林里传出一阵美妙的歌声,唱的是一些关于成仙的话。猴王想∶这个唱歌的人一定是神仙,就顺着歌声找去。
LLM 输出: {'nodes': [{'id': '美猴王', 'type': 'Person'}, {'id': '通背猿猴', 'type': 'Person'}, {'id': '群猴', 'type': 'Group'}, {'id': '东胜神洲', 'type': 'Place'}, {'id': '西牛贺洲', 'type': 'Place'}, {'id': '南赡部洲', 'type': 'Place'}, {'id': '北俱芦洲', 'type': 'Place'}, {'id': '傲来国', 'type': 'Place'}, {'id': '花果山', 'type': 'Place'}, {'id': '水帘洞', 'type': 'Place'}, {'id': '铁板桥', 'type': 'Place'}, {'id': '高山', 'type': 'Place'}, {'id': '西北岸边', 'type': 'Place'}, {'id': '仙石', 'type': 'Item'}, {'id': '石碑', 'type': 'Item'}, {'id': '木筏', 'type': 'Item'}], 'relationships': [{'source': '傲来国', 'target': '东胜神洲', 'type': 'LOCATED_IN'}, {'source': '花果山', 'target': '傲来国', 'type': 'LOCATED_IN'}, {'source': '水帘洞', 'target': '花果山', 'type': 'LOCATED_IN'}, {'source': '铁板桥', 'target': '水帘洞', 'type': 'LOCATED_IN'}, {'source': '石碑', 'target': '水帘洞', 'type': 'LOCATED_IN'}, {'source': '美猴王', 'target': '仙石', 'type': 'FROM'}, {'source': '美猴王', 'target': '群猴', 'type': 'MASTER_OF'}, {'source': '木筏', 'target': '群猴', 'type': 'CREATED_BY'}]}
需要构建节点和关系的文本: 大概是美猴王的运气好,连日的东南风,将他送到西北岸边。他下了木筏,登上了岸,看见岸边有许多人都在干活,有的捉鱼,有的打天上的大雁,有的挖蛤蜊,有的淘盐,他悄悄地走过去,没想到,吓得那些人将东西一扔,四处逃命。
这一天,他来到一座高山前,突然从半山腰的树林里传出一阵美妙的歌声,唱的是一些关于成仙的话。猴王想∶这个唱歌的人一定是神仙,就顺着歌声找去。
唱歌的是一个正在树林里砍柴的青年人,猴王从这青年人的口中了解到,这座山叫灵台方寸山,离这儿七八里路,有个斜月三星洞,洞中住着一个称为菩提祖师的神仙。
美猴王告别打柴的青年人,出了树林,走过山坡,果然远远地看见一座洞府,只见洞门紧紧地闭着,洞门对面的山岗上立着一块石碑,大约有三丈多高,八尺多宽,上面写着十个大字∶"灵台方寸山斜月三星洞"。正在看时,门却忽然打开了,走出来一个仙童。
美猴王赶快走上前,深深地鞠了一个躬,说明来意,那仙童说∶"我师父刚才正要讲道,忽然叫我出来开门,说外面来了个拜师学艺的,原来就是你呀!跟我来吧!"美猴王赶紧整整衣服,恭恭敬敬地跟着仙童进到洞内,来到祖师讲道的法台跟前。
猴王看见菩提祖师端端正正地坐在台上,台下两边站着三十多个仙童,就赶紧跪下叩头。祖师问清楚他的来意,很高兴,见他没有姓名,便说∶"你就叫悟空吧!"
祖师叫孙悟空又拜见了各位师兄,并给悟空找了间空房住下。从此悟空跟着师兄学习生活常识,讲究经典,写字烧香,空时做些扫地挑水的活。
很快七年过去了,一天,祖师讲道结束后,问悟空想学什么本领。孙悟空不管祖师讲什么求神拜佛、打坐修行,只要一听不能长生不老,就不愿意学,菩提祖师对此非常生气。
祖师从高台上跳了下来,手里拿着戒尺指着孙悟空说∶"你这猴子,这也不学,那也不学,你要学些什么?"说完走过去在悟空头上打了三下,倒背着手走到里间,关上了门。师兄们看到师父生气了,感到很害怕,纷纷责怪孙悟空。
孙悟空既不怕,又不生气,心里反而十分高兴。当天晚上,悟空假装睡着了,可是一到半夜,就悄悄起来,从前门出去,等到三更,绕到后门口,看见门半开半闭,高兴地不得了,心想∶"哈哈,我没有猜错师父的意思。"
孙悟空走了进去,看见祖师面朝里睡着,就跪在床前说∶"师父,我跪在这里等着您呢!"祖师听见声音就起来了,盘着腿坐好后,严厉地问孙悟空来做什么,悟空说∶"师父白天当着大家的面不是答应我,让我三更时从后门进来,教我长生不老的法术吗?"
菩提祖师听到这话心里很高兴。心想∶"这个猴子果然是天地生成的,不然,怎么能猜透我的暗谜。"于是,让孙悟空跪在床前,教给他长生不老的法术。孙悟空洗耳恭听,用心理解,牢牢记住口诀,并叩头拜谢了祖师的恩情。
很快三年又过去了,祖师又教了孙悟空七十二般变化的法术和驾筋斗云的本领,学会了这个本领,一个筋斗便能翻出十万八千里路程。孙悟空是个猴子,本来就喜欢蹦蹦跳跳的,所以学起筋斗云来很容易。
LLM 输出: {'nodes': [{'id': '孙悟空', 'type': 'Person', 'properties': {'别称': '美猴王, 猴王, 悟空'}}, {'id': '菩提祖师', 'type': 'Person', 'properties': {'别称': '祖师'}}, {'id': '灵台方寸山', 'type': 'Place'}, {'id': '斜月三星洞', 'type': 'Place'}, {'id': '打柴的青年人', 'type': 'Person'}, {'id': '仙童', 'type': 'Person'}, {'id': '石碑', 'type': 'Item'}, {'id': '戒尺', 'type': 'Item'}, {'id': '木筏', 'type': 'Item'}, {'id': '筋斗云', 'type': 'Item'}, {'id': '七十二般变化', 'type': 'Item'}, {'id': '长生不老的法术', 'type': 'Item'}], 'relationships': [{'source': '孙悟空', 'target': '菩提祖师', 'type': 'DISCIPLE_OF'}, {'source': '菩提祖师', 'target': '孙悟空', 'type': 'MASTER_OF'}, {'source': '斜月三星洞', 'target': '灵台方寸山', 'type': 'LOCATED_IN'}, {'source': '菩提祖师', 'target': '斜月三星洞', 'type': 'LOCATED_IN'}, {'source': '孙悟空', 'target': '斜月三星洞', 'type': 'LEARNED_AT'}, {'source': '石碑', 'target': '斜月三星洞', 'type': 'LOCATED_IN'}, {'source': '戒尺', 'target': '菩提祖师', 'type': 'USED_BY'}, {'source': '木筏', 'target': '孙悟空', 'type': 'USED_BY'}, {'source': '筋斗云', 'target': '孙悟空', 'type': 'USED_BY'}, {'source': '七十二般变化', 'target': '孙悟空', 'type': 'USED_BY'}, {'source': '长生不老的法术', 'target': '孙悟空', 'type': 'USED_BY'}]}
需要构建节点和关系的文本: 菩提祖师听到这话心里很高兴。心想∶"这个猴子果然是天地生成的,不然,怎么能猜透我的暗谜。"于是,让孙悟空跪在床前,教给他长生不老的法术。孙悟空洗耳恭听,用心理解,牢牢记住口诀,并叩头拜谢了祖师的恩情。
很快三年又过去了,祖师又教了孙悟空七十二般变化的法术和驾筋斗云的本领,学会了这个本领,一个筋斗便能翻出十万八千里路程。孙悟空是个猴子,本来就喜欢蹦蹦跳跳的,所以学起筋斗云来很容易。
有一个夏天,孙悟空和师兄们在洞门前玩耍,大家要孙悟空变个东西看看,孙悟空心里感到很高兴,得意地念起咒语,摇身一变变成了一棵大树。
师兄们见了,鼓着掌称赞他。
大家的吵闹声,让菩提祖师听到了,他拄着拐杖出来,问∶"是谁在吵闹?你们这样大吵大叫的,哪里像个出家修行的人呢?"大家都赶紧停住了笑,孙悟空也恢复了原样,给师父解释,请求原谅。
菩提祖师看见孙悟空刚刚学会了一些本领就卖弄起来,十分生气。祖师叫其他人离开,把悟空狠狠地教训了一顿,并且要把孙悟空赶走。孙悟空着急了,哀求祖师不要赶他走,祖师却不肯留下他,并要他立下誓言∶任何时候都不能说孙悟空是菩提祖师的徒弟。
LLM 输出: {'nodes': [{'id': '菩提祖师', 'type': 'Person'}, {'id': '孙悟空', 'type': 'Person'}, {'id': '长生不老的法术', 'type': 'Item'}, {'id': '七十二般变化的法术', 'type': 'Item'}, {'id': '筋斗云', 'type': 'Item'}, {'id': '师兄们', 'type': 'Group'}, {'id': '大树', 'type': 'Item'}], 'relationships': [{'source': '孙悟空', 'target': '菩提祖师', 'type': 'DISCIPLE_OF'}, {'source': '菩提祖师', 'target': '孙悟空', 'type': 'MASTER_OF'}, {'source': '孙悟空', 'target': '长生不老的法术', 'type': 'LEARNED'}, {'source': '孙悟空', 'target': '七十二般变化的法术', 'type': 'LEARNED'}, {'source': '孙悟空', 'target': '筋斗云', 'type': 'LEARNED'}, {'source': '师兄们', 'target': '菩提祖师', 'type': 'DISCIPLE_OF'}, {'source': '孙悟空', 'target': '大树', 'type': 'TRANSFORMED_INTO'}]}
需要构建节点和关系的文本: --- 西游记\第2回 闹龙宫刁石猴借宝.txt ---
LLM 输出: {'nodes': [], 'relationships': []}
需要构建节点和关系的文本: 孙悟空见没办法留下来,就拜别了菩提祖师,又和各位师兄告别,然后念了口诀,驾着筋斗云,不到一个时辰,就回到了花果山水帘洞,看到花果山上一片荒凉破败的景象,很是凄惨。
原来孙悟空走了以后,有一个混世魔王独占了水帘洞,并且抢走了许多猴子猴孙。孙悟空听到这些以后,气得咬牙跺脚。他问清了混世魔王的住处,决定找混世魔王报仇,便驾着筋斗云,朝北方飞去。
不一会儿,孙悟空就来到混世魔王的水脏洞前,对门前的小妖喊到∶"你家那个狗屁魔王,多次欺负我们猴子。我今天来,要和那魔王比比高低!"小妖跑进洞里,报告魔王。魔王急忙穿上铁甲,提着大刀,在小妖们的簇拥下走出洞门。
孙悟空赤手空拳,夺过了混世魔王的大刀,把他劈成了两半。然后,拔下一把毫毛咬碎喷了出去,毫毛变成许多小猴子,直杀进洞里,把所有的妖精全杀死,然后救出被抢走的小猴子,放了一把火烧了水脏洞。
孙悟空收回了毫毛,让小猴子们闭上眼睛,作起法术来,一阵狂风刮过,他们驾着狂风回到了花果山。从此,孙悟空便叫小猴们做了些竹枪和木刀,用夺来的大刀教他们武艺。没过多久,孙悟空觉得竹枪木刀不能打仗,两个猴告诉他,傲来国里肯定有好的兵器。
孙悟空驾云来到傲来国上空,念起咒语,立即天空刮起狂风,砂石乱飞,把满城的军民吓得不敢出来。他趁机跑进兵器库拔了把毫毛一吹,变成上千个小猴,乱搬乱抢,悟空见差不多了,把风向一变回了花果山。
从此以后,花果山水帘洞的名气就更大了,所有的妖怪头子,即七十二洞的洞主都来拜见孙悟空。可是,悟空却有一件事不顺心,嫌那口大刀太轻,不好用。有个通背老猿猴告诉悟空,水帘洞桥下,可直通东海龙宫,叫他去找龙王要一件得心应手的兵器。
悟空立刻来到东海龙宫,给老龙王敖广讲明了来这儿的目的。龙王不好推辞,叫虾兵们抬出一杆三千六百斤重的九股叉,悟空接过来玩了一阵,嫌它太轻。龙王又命令蟹将们抬出一柄七千二百斤重的方天画戟,悟空一见,仍然嫌它太轻。
龙王说∶"再也没有比这更重的兵器了。"悟空不信,和龙王吵了起来,龙婆给龙王说∶"大禹治水时,测定海水深浅的神珍铁最近总是放光,就把这给他,管他能不能用,打发他走算了。"龙王听后告诉悟空∶"这宝物太重了,你自己去取吧!"
孙悟空跟龙王来到海底,龙王用手一指说∶"放光的就是。"悟空见神珍铁金光四射,就走过去用手一摸,原来是根铁柱子,斗一样粗,二丈多长。孙悟空使劲用手搬了搬说∶"太长太长了,要是再短些,再细一些就好了。"
孙悟空话还没有说完,那个宝贝就短了几尺,也细了一圈。孙悟空看了看说∶"再细些就更好了。"那个宝贝真的又细了许多,悟空拿过来,见上面写着∶"如意金箍棒、重一万三千五百斤"顺手玩了一会儿,觉得十分好用。
LLM 输出: {'nodes': [{'id': '孙悟空', 'type': 'Person'}, {'id': '菩提祖师', 'type': 'Person'}, {'id': '筋斗云', 'type': 'Item'}, {'id': '花果山', 'type': 'Place'}, {'id': '水帘洞', 'type': 'Place'}, {'id': '混世魔王', 'type': 'Person'}, {'id': '水脏洞', 'type': 'Place'}, {'id': '大刀', 'type': 'Item'}, {'id': '毫毛', 'type': 'Item'}, {'id': '小猴子', 'type': 'Group'}, {'id': '傲来国', 'type': 'Place'}, {'id': '兵器库', 'type': 'Place'}, {'id': '通背老猿猴', 'type': 'Person'}, {'id': '东海龙宫', 'type': 'Place'}, {'id': '老龙王敖广', 'type': 'Person'}, {'id': '虾兵', 'type': 'Group'}, {'id': '九股叉', 'type': 'Item'}, {'id': '蟹将', 'type': 'Group'}, {'id': '方天画戟', 'type': 'Item'}, {'id': '龙婆', 'type': 'Person'}, {'id': '大禹', 'type': 'Person'}, {'id': '神珍铁', 'type': 'Item'}, {'id': '海底', 'type': 'Place'}, {'id': '如意金箍棒', 'type': 'Item'}, {'id': '七十二洞的洞主', 'type': 'Group'}], 'relationships': [{'source': '孙悟空', 'target': '菩提祖师', 'type': 'DISCIPLE_OF'}, {'source': '孙悟空', 'target': '筋斗云', 'type': 'USED_BY'}, {'source': '水帘洞', 'target': '花果山', 'type': 'LOCATED_IN'}, {'source': '混世魔王', 'target': '水脏洞', 'type': 'LOCATED_IN'}, {'source': '混世魔王', 'target': '孙悟空', 'type': 'ENEMY_OF'}, {'source': '混世魔王', 'target': '大刀', 'type': 'USED_BY'}, {'source': '孙悟空', 'target': '混世魔王', 'type': 'BATTLE_WITH'}, {'source': '孙悟空', 'target': '大刀', 'type': 'USED_BY'}, {'source': '孙悟空', 'target': '毫毛', 'type': 'OWNS'}, {'source': '毫毛', 'target': '小猴子', 'type': 'TRANSFORMED_INTO'}, {'source': '兵器库', 'target': '傲来国', 'type': 'LOCATED_IN'}, {'source': '通背老猿猴', 'target': '水帘洞', 'type': 'LOCATED_IN'}, {'source': '东海龙宫', 'target': '海底', 'type': 'LOCATED_IN'}, {'source': '老龙王敖广', 'target': '东海龙宫', 'type': 'LOCATED_IN'}, {'source': '虾兵', 'target': '东海龙宫', 'type': 'LOCATED_IN'}, {'source': '老龙王敖广', 'target': '九股叉', 'type': 'OWNS'}, {'source': '蟹将', 'target': '东海龙宫', 'type': 'LOCATED_IN'}, {'source': '老龙王敖广', 'target': '方天画戟', 'type': 'OWNS'}, {'source': '龙婆', 'target': '东海龙宫', 'type': 'LOCATED_IN'}, {'source': '大禹', 'target': '神珍铁', 'type': 'USED_BY'}, {'source': '神珍铁', 'target': '海底', 'type': 'LOCATED_IN'}, {'source': '神珍铁', 'target': '如意金箍棒', 'type': 'TRANSFORMED_INTO'}, {'source': '孙悟空', 'target': '如意金箍棒', 'type': 'OWNS'}, {'source': '七十二洞的洞主', 'target': '花果山', 'type': 'LOCATED_IN'}]}
处理中... 7/7
需要构建节点和关系的文本: 孙悟空跟龙王来到海底,龙王用手一指说∶"放光的就是。"悟空见神珍铁金光四射,就走过去用手一摸,原来是根铁柱子,斗一样粗,二丈多长。孙悟空使劲用手搬了搬说∶"太长太长了,要是再短些,再细一些就好了。"
孙悟空话还没有说完,那个宝贝就短了几尺,也细了一圈。孙悟空看了看说∶"再细些就更好了。"那个宝贝真的又细了许多,悟空拿过来,见上面写着∶"如意金箍棒、重一万三千五百斤"顺手玩了一会儿,觉得十分好用。
回到水晶宫,孙悟空又要龙王送一身衣服相配。龙王实在没有,但又害怕悟空乱打乱闹,只好敲响应急的金钟,叫来南、北、西三海龙王敖钦、敖顺和敖闰,兄弟三人凑了一副黄金甲、一顶凤翅紫金冠、一双藕丝步云鞋,送给悟空。
回到花果山,悟空才发现那根金箍棒竟可以变成绣花针一样大小,藏到耳朵中。一天,他宴请所有的妖王吃饭,喝醉了,在桥边的松树下睡觉,迷迷糊糊地见两个人手里拿着写有"孙悟空"的批文,走到他身边也不说话,把他用绳索套上,拉起来就走。
悟空糊里糊涂跟他们来到一座城门外,看见城楼上有一块牌子,牌子上写着"幽冥界"三个大字,知道这里是阎王住的地方,转身就要走,两个勾魂鬼死死拉住他,非要让他进去。孙悟空一看火了,从耳朵中掏出了金箍棒,把两个勾魂鬼打成了肉酱。
他甩掉套在身上的绳套,挥着金箍棒直打到城里,又一直打到森罗殿前,十位冥王见悟空长得的十分凶恶,吓得不知道该怎么办。悟空说∶"你们既然坐在王位上,就应该有点灵气,为什么不知道我来?俺老孙已经修成仙道,能长生不老。快拿生死簿来!"十位冥王赶快叫判官拿出生死本来查。
悟空登上森罗殿,一直查到魂字一千三百五十号,才找到了自己的名字,顺手拿起笔把所有猴子的名字通通勾掉,说∶"这下好极了,好极了,今后再也不归你们管了。"说完又一路打出了幽冥界。十位冥王赶忙到翠云宫去见地藏王菩萨,商量如何向玉皇大帝报告。
LLM 输出: {'nodes': [{'id': '孙悟空', 'type': 'Person'}, {'id': '龙王', 'type': 'Person'}, {'id': '敖钦', 'type': 'Person'}, {'id': '敖顺', 'type': 'Person'}, {'id': '敖闰', 'type': 'Person'}, {'id': '勾魂鬼', 'type': 'Person'}, {'id': '十位冥王', 'type': 'Person'}, {'id': '判官', 'type': 'Person'}, {'id': '地藏王菩萨', 'type': 'Person'}, {'id': '玉皇大帝', 'type': 'Person'}, {'id': '海底', 'type': 'Place'}, {'id': '水晶宫', 'type': 'Place'}, {'id': '花果山', 'type': 'Place'}, {'id': '幽冥界', 'type': 'Place'}, {'id': '森罗殿', 'type': 'Place'}, {'id': '翠云宫', 'type': 'Place'}, {'id': '如意金箍棒', 'type': 'Item'}, {'id': '黄金甲', 'type': 'Item'}, {'id': '凤翅紫金冠', 'type': 'Item'}, {'id': '藕丝步云鞋', 'type': 'Item'}, {'id': '金钟', 'type': 'Item'}, {'id': '生死簿', 'type': 'Item'}], 'relationships': [{'source': '孙悟空', 'target': '如意金箍棒', 'type': 'OWNS'}, {'source': '孙悟空', 'target': '黄金甲', 'type': 'OWNS'}, {'source': '孙悟空', 'target': '凤翅紫金冠', 'type': 'OWNS'}, {'source': '孙悟空', 'target': '藕丝步云鞋', 'type': 'OWNS'}, {'source': '龙王', 'target': '金钟', 'type': 'OWNS'}, {'source': '十位冥王', 'target': '生死簿', 'type': 'OWNS'}, {'source': '孙悟空', 'target': '海底', 'type': 'LOCATED_IN'}, {'source': '龙王', 'target': '海底', 'type': 'LOCATED_IN'}, {'source': '孙悟空', 'target': '水晶宫', 'type': 'LOCATED_IN'}, {'source': '孙悟空', 'target': '花果山', 'type': 'LOCATED_IN'}, {'source': '孙悟空', 'target': '幽冥界', 'type': 'LOCATED_IN'}, {'source': '孙悟空', 'target': '森罗殿', 'type': 'LOCATED_IN'}, {'source': '勾魂鬼', 'target': '幽冥界', 'type': 'LOCATED_IN'}, {'source': '森罗殿', 'target': '幽冥界', 'type': 'LOCATED_IN'}, {'source': '十位冥王', 'target': '森罗殿', 'type': 'LOCATED_IN'}, {'source': '翠云宫', 'target': '幽冥界', 'type': 'LOCATED_IN'}, {'source': '地藏王菩萨', 'target': '翠云宫', 'type': 'LOCATED_IN'}, {'source': '孙悟空', 'target': '勾魂鬼', 'type': 'BATTLE_WITH'}, {'source': '孙悟空', 'target': '十位冥王', 'type': 'BATTLE_WITH'}, {'source': '十位冥王', 'target': '地藏王菩萨', 'type': 'SUBORDINATE_OF'}, {'source': '十位冥王', 'target': '玉皇大帝', 'type': 'SUBORDINATE_OF'}]}
[GraphDocument(nodes=[Node(id='美猴王', type='Person', properties={}), Node(id='通背猿猴', type='Person', properties={}), Node(id='群猴', type='Group', properties={}), Node(id='东胜神洲', type='Place', properties={}), Node(id='西牛贺洲', type='Place', properties={}), Node(id='南赡部洲', type='Place', properties={}), Node(id='北俱芦洲', type='Place', properties={}), Node(id='傲来国', type='Place', properties={}), Node(id='花果山', type='Place', properties={}), Node(id='水帘洞', type='Place', properties={}), Node(id='铁板桥', type='Place', properties={}), Node(id='高山', type='Place', properties={}), Node(id='西北岸边', type='Place', properties={}), Node(id='仙石', type='Item', properties={}), Node(id='石碑', type='Item', properties={}), Node(id='木筏', type='Item', properties={})], relationships=[Relationship(source=Node(id='傲来国', type='Place', properties={}), target=Node(id='东胜神洲', type='Place', properties={}), type='LOCATED_IN', properties={}), Relationship(source=Node(id='花果山', type='Place', properties={}), target=Node(id='傲来国', type='Place', properties={}), type='LOCATED_IN', properties={}), Relationship(source=Node(id='水帘洞', type='Place', properties={}), target=Node(id='花果山', type='Place', properties={}), type='LOCATED_IN', properties={}), Relationship(source=Node(id='铁板桥', type='Place', properties={}), target=Node(id='水帘洞', type='Place', properties={}), type='LOCATED_IN', properties={}), Relationship(source=Node(id='石碑', type='Item', properties={}), target=Node(id='水帘洞', type='Place', properties={}), type='LOCATED_IN', properties={}), Relationship(source=Node(id='美猴王', type='Person', properties={}), target=Node(id='仙石', type='Item', properties={}), type='FROM', properties={}), Relationship(source=Node(id='美猴王', type='Person', properties={}), target=Node(id='群猴', type='Group', properties={}), type='MASTER_OF', properties={}), Relationship(source=Node(id='木筏', type='Item', properties={}), target=Node(id='群猴', type='Group', properties={}), type='CREATED_BY', properties={})], source=Document(metadata={}, page_content='这是一个神话故事,传说在很久很久以前,天下分为东胜神洲、西牛贺洲、南赡部洲、北俱芦洲。在东胜神洲傲来国,有一座花果山,山上有一块仙石,一天仙石崩裂,从石头中滚出一个卵,这个卵一见风就变成一个石猴,猴眼射出一道道金光,向四方朝拜。\n那猴能走、能跑,渴了就喝些山涧中的泉水,饿了就吃些山上的果子。\n整天和山中的动物一起玩乐,过得十分快活。一天,天气特别热,猴子们为了躲避炎热的天气,跑到山涧里洗澡。它们看见这泉水哗哗地流,就顺着涧往前走,去寻找它的源头。\n猴子们爬呀、爬呀,走到了尽头,却看见一股瀑布,像是从天而降一样。猴子们觉得惊奇,商量说∶"哪个敢钻进瀑布,把泉水的源头找出来,又不伤身体,就拜他为王。"连喊了三遍,那石猴呼地跳了出来,高声喊道∶"我进去,我进去!"\n那石猴闭眼纵身跳入瀑布,觉得不像是在水中,这才睁开眼,四处打量,发现自己站在一座铁板桥上,桥下的水冲贯于石窍之间,倒挂着流出来,将桥门遮住,使外面的人看不到里面。石猴走过桥,发现这真是个好地方,石椅、石床、石盆、石碗,样样都有。\n这里就像不久以前有人住过一样,天然的房子,安静整洁,锅、碗、瓢、盆,整齐地放在炉灶上。正当中有一块石碑,上面刻着∶花果山福地,水帘洞洞天。石猴高兴得不得了,忙转身向外走去,嗖的一下跳出了洞。\n猴子们见石猴出来了,身上又一点伤也没有,又惊又喜,把他团团围住,争著问他里面的情况。石猴抓抓腮,挠挠痒,笑嘻嘻地对大家说∶"里面没有水,是一个安身的好地方,刮大风我们有地方躲,下大雨我们也不怕淋。"猴子们一听,一个个高兴得又蹦又跳。\n猴子们随着石猴穿过了瀑布,进入水帘洞中,看见了这么多的好东西,一个个你争我夺,拿盆的拿盆,拿碗的拿碗,占灶的占灶,争床的争床,搬过来,移过去,直到精疲力尽为止。猴子们都遵照诺言,拜石猴为王,石猴从此登上王位,将石字省去,自称"美猴王"。\n美猴王每天带着猴子们游山玩水,很快三、五百年过去了。一天正在玩乐时,美猴王想到自己将来难免一死,不由悲伤得掉下眼泪来,这时猴群中跳出个通背猿猴来,说∶"大王想要长生不老,只有去学佛、学仙、学神之术。"\n美猴王决定走遍天涯海角,也要找到神仙,学那长生不老的本领。第二天,猴子们为他做了一个木筏,又准备了一些野果,于是美猴王告别了群猴们,一个人撑着木筏,奔向汪洋大海。\n大概是美猴王的运气好,连日的东南风,将他送到西北岸边。他下了木筏,登上了岸,看见岸边有许多人都在干活,有的捉鱼,有的打天上的大雁,有的挖蛤蜊,有的淘盐,他悄悄地走过去,没想到,吓得那些人将东西一扔,四处逃命。\n这一天,他来到一座高山前,突然从半山腰的树林里传出一阵美妙的歌声,唱的是一些关于成仙的话。猴王想∶这个唱歌的人一定是神仙,就顺着歌声找去。')), GraphDocument(nodes=[Node(id='孙悟空', type='Person', properties={'别称': '美猴王, 猴王, 悟空'}), Node(id='菩提祖师', type='Person', properties={'别称': '祖师'}), Node(id='灵台方寸山', type='Place', properties={}), Node(id='斜月三星洞', type='Place', properties={}), Node(id='打柴的青年人', type='Person', properties={}), Node(id='仙童', type='Person', properties={}), Node(id='石碑', type='Item', properties={}), Node(id='戒尺', type='Item', properties={}), Node(id='木筏', type='Item', properties={}), Node(id='筋斗云', type='Item', properties={}), Node(id='七十二般变化', type='Item', properties={}), Node(id='长生不老的法术', type='Item', properties={})], relationships=[Relationship(source=Node(id='孙悟空', type='Person', properties={'别称': '美猴王, 猴王, 悟空'}), target=Node(id='菩提祖师', type='Person', properties={'别称': '祖师'}), type='DISCIPLE_OF', properties={}), Relationship(source=Node(id='菩提祖师', type='Person', properties={'别称': '祖师'}), target=Node(id='孙悟空', type='Person', properties={'别称': '美猴王, 猴王, 悟空'}), type='MASTER_OF', properties={}), Relationship(source=Node(id='斜月三星洞', type='Place', properties={}), target=Node(id='灵台方寸山', type='Place', properties={}), type='LOCATED_IN', properties={}), Relationship(source=Node(id='菩提祖师', type='Person', properties={'别称': '祖师'}), target=Node(id='斜月三星洞', type='Place', properties={}), type='LOCATED_IN', properties={}), Relationship(source=Node(id='孙悟空', type='Person', properties={'别称': '美猴王, 猴王, 悟空'}), target=Node(id='斜月三星洞', type='Place', properties={}), type='LEARNED_AT', properties={}), Relationship(source=Node(id='石碑', type='Item', properties={}), target=Node(id='斜月三星洞', type='Place', properties={}), type='LOCATED_IN', properties={}), Relationship(source=Node(id='戒尺', type='Item', properties={}), target=Node(id='菩提祖师', type='Person', properties={'别称': '祖师'}), type='USED_BY', properties={}), Relationship(source=Node(id='木筏', type='Item', properties={}), target=Node(id='孙悟空', type='Person', properties={'别称': '美猴王, 猴王, 悟空'}), type='USED_BY', properties={}), Relationship(source=Node(id='筋斗云', type='Item', properties={}), target=Node(id='孙悟空', type='Person', properties={'别称': '美猴王, 猴王, 悟空'}), type='USED_BY', properties={}), Relationship(source=Node(id='七十二般变化', type='Item', properties={}), target=Node(id='孙悟空', type='Person', properties={'别称': '美猴王, 猴王, 悟空'}), type='USED_BY', properties={}), Relationship(source=Node(id='长生不老的法术', type='Item', properties={}), target=Node(id='孙悟空', type='Person', properties={'别称': '美猴王, 猴王, 悟空'}), type='USED_BY', properties={})], source=Document(metadata={}, page_content='大概是美猴王的运气好,连日的东南风,将他送到西北岸边。他下了木筏,登上了岸,看见岸边有许多人都在干活,有的捉鱼,有的打天上的大雁,有的挖蛤蜊,有的淘盐,他悄悄地走过去,没想到,吓得那些人将东西一扔,四处逃命。\n这一天,他来到一座高山前,突然从半山腰的树林里传出一阵美妙的歌声,唱的是一些关于成仙的话。猴王想∶这个唱歌的人一定是神仙,就顺着歌声找去。\n唱歌的是一个正在树林里砍柴的青年人,猴王从这青年人的口中了解到,这座山叫灵台方寸山,离这儿七八里路,有个斜月三星洞,洞中住着一个称为菩提祖师的神仙。\n美猴王告别打柴的青年人,出了树林,走过山坡,果然远远地看见一座洞府,只见洞门紧紧地闭着,洞门对面的山岗上立着一块石碑,大约有三丈多高,八尺多宽,上面写着十个大字∶"灵台方寸山斜月三星洞"。正在看时,门却忽然打开了,走出来一个仙童。\n美猴王赶快走上前,深深地鞠了一个躬,说明来意,那仙童说∶"我师父刚才正要讲道,忽然叫我出来开门,说外面来了个拜师学艺的,原来就是你呀!跟我来吧!"美猴王赶紧整整衣服,恭恭敬敬地跟着仙童进到洞内,来到祖师讲道的法台跟前。\n猴王看见菩提祖师端端正正地坐在台上,台下两边站着三十多个仙童,就赶紧跪下叩头。祖师问清楚他的来意,很高兴,见他没有姓名,便说∶"你就叫悟空吧!"\n祖师叫孙悟空又拜见了各位师兄,并给悟空找了间空房住下。从此悟空跟着师兄学习生活常识,讲究经典,写字烧香,空时做些扫地挑水的活。\n很快七年过去了,一天,祖师讲道结束后,问悟空想学什么本领。孙悟空不管祖师讲什么求神拜佛、打坐修行,只要一听不能长生不老,就不愿意学,菩提祖师对此非常生气。\n祖师从高台上跳了下来,手里拿着戒尺指着孙悟空说∶"你这猴子,这也不学,那也不学,你要学些什么?"说完走过去在悟空头上打了三下,倒背着手走到里间,关上了门。师兄们看到师父生气了,感到很害怕,纷纷责怪孙悟空。\n孙悟空既不怕,又不生气,心里反而十分高兴。当天晚上,悟空假装睡着了,可是一到半夜,就悄悄起来,从前门出去,等到三更,绕到后门口,看见门半开半闭,高兴地不得了,心想∶"哈哈,我没有猜错师父的意思。"\n孙悟空走了进去,看见祖师面朝里睡着,就跪在床前说∶"师父,我跪在这里等着您呢!"祖师听见声音就起来了,盘着腿坐好后,严厉地问孙悟空来做什么,悟空说∶"师父白天当着大家的面不是答应我,让我三更时从后门进来,教我长生不老的法术吗?"\n菩提祖师听到这话心里很高兴。心想∶"这个猴子果然是天地生成的,不然,怎么能猜透我的暗谜。"于是,让孙悟空跪在床前,教给他长生不老的法术。孙悟空洗耳恭听,用心理解,牢牢记住口诀,并叩头拜谢了祖师的恩情。\n很快三年又过去了,祖师又教了孙悟空七十二般变化的法术和驾筋斗云的本领,学会了这个本领,一个筋斗便能翻出十万八千里路程。孙悟空是个猴子,本来就喜欢蹦蹦跳跳的,所以学起筋斗云来很容易。')), GraphDocument(nodes=[Node(id='菩提祖师', type='Person', properties={}), Node(id='孙悟空', type='Person', properties={}), Node(id='长生不老的法术', type='Item', properties={}), Node(id='七十二般变化的法术', type='Item', properties={}), Node(id='筋斗云', type='Item', properties={}), Node(id='师兄们', type='Group', properties={}), Node(id='大树', type='Item', properties={})], relationships=[Relationship(source=Node(id='孙悟空', type='Person', properties={}), target=Node(id='菩提祖师', type='Person', properties={}), type='DISCIPLE_OF', properties={}), Relationship(source=Node(id='菩提祖师', type='Person', properties={}), target=Node(id='孙悟空', type='Person', properties={}), type='MASTER_OF', properties={}), Relationship(source=Node(id='孙悟空', type='Person', properties={}), target=Node(id='长生不老的法术', type='Item', properties={}), type='LEARNED', properties={}), Relationship(source=Node(id='孙悟空', type='Person', properties={}), target=Node(id='七十二般变化的法术', type='Item', properties={}), type='LEARNED', properties={}), Relationship(source=Node(id='孙悟空', type='Person', properties={}), target=Node(id='筋斗云', type='Item', properties={}), type='LEARNED', properties={}), Relationship(source=Node(id='师兄们', type='Group', properties={}), target=Node(id='菩提祖师', type='Person', properties={}), type='DISCIPLE_OF', properties={}), Relationship(source=Node(id='孙悟空', type='Person', properties={}), target=Node(id='大树', type='Item', properties={}), type='TRANSFORMED_INTO', properties={})], source=Document(metadata={}, page_content='菩提祖师听到这话心里很高兴。心想∶"这个猴子果然是天地生成的,不然,怎么能猜透我的暗谜。"于是,让孙悟空跪在床前,教给他长生不老的法术。孙悟空洗耳恭听,用心理解,牢牢记住口诀,并叩头拜谢了祖师的恩情。\n很快三年又过去了,祖师又教了孙悟空七十二般变化的法术和驾筋斗云的本领,学会了这个本领,一个筋斗便能翻出十万八千里路程。孙悟空是个猴子,本来就喜欢蹦蹦跳跳的,所以学起筋斗云来很容易。\n有一个夏天,孙悟空和师兄们在洞门前玩耍,大家要孙悟空变个东西看看,孙悟空心里感到很高兴,得意地念起咒语,摇身一变变成了一棵大树。\n师兄们见了,鼓着掌称赞他。\n大家的吵闹声,让菩提祖师听到了,他拄着拐杖出来,问∶"是谁在吵闹?你们这样大吵大叫的,哪里像个出家修行的人呢?"大家都赶紧停住了笑,孙悟空也恢复了原样,给师父解释,请求原谅。\n菩提祖师看见孙悟空刚刚学会了一些本领就卖弄起来,十分生气。祖师叫其他人离开,把悟空狠狠地教训了一顿,并且要把孙悟空赶走。孙悟空着急了,哀求祖师不要赶他走,祖师却不肯留下他,并要他立下誓言∶任何时候都不能说孙悟空是菩提祖师的徒弟。')), GraphDocument(nodes=[Node(id='孙悟空', type='Person', properties={}), Node(id='菩提祖师', type='Person', properties={}), Node(id='筋斗云', type='Item', properties={}), Node(id='花果山', type='Place', properties={}), Node(id='水帘洞', type='Place', properties={}), Node(id='混世魔王', type='Person', properties={}), Node(id='水脏洞', type='Place', properties={}), Node(id='大刀', type='Item', properties={}), Node(id='毫毛', type='Item', properties={}), Node(id='小猴子', type='Group', properties={}), Node(id='傲来国', type='Place', properties={}), Node(id='兵器库', type='Place', properties={}), Node(id='通背老猿猴', type='Person', properties={}), Node(id='东海龙宫', type='Place', properties={}), Node(id='老龙王敖广', type='Person', properties={}), Node(id='虾兵', type='Group', properties={}), Node(id='九股叉', type='Item', properties={}), Node(id='蟹将', type='Group', properties={}), Node(id='方天画戟', type='Item', properties={}), Node(id='龙婆', type='Person', properties={}), Node(id='大禹', type='Person', properties={}), Node(id='神珍铁', type='Item', properties={}), Node(id='海底', type='Place', properties={}), Node(id='如意金箍棒', type='Item', properties={}), Node(id='七十二洞的洞主', type='Group', properties={})], relationships=[Relationship(source=Node(id='孙悟空', type='Person', properties={}), target=Node(id='菩提祖师', type='Person', properties={}), type='DISCIPLE_OF', properties={}), Relationship(source=Node(id='孙悟空', type='Person', properties={}), target=Node(id='筋斗云', type='Item', properties={}), type='USED_BY', properties={}), Relationship(source=Node(id='水帘洞', type='Place', properties={}), target=Node(id='花果山', type='Place', properties={}), type='LOCATED_IN', properties={}), Relationship(source=Node(id='混世魔王', type='Person', properties={}), target=Node(id='水脏洞', type='Place', properties={}), type='LOCATED_IN', properties={}), Relationship(source=Node(id='混世魔王', type='Person', properties={}), target=Node(id='孙悟空', type='Person', properties={}), type='ENEMY_OF', properties={}), Relationship(source=Node(id='混世魔王', type='Person', properties={}), target=Node(id='大刀', type='Item', properties={}), type='USED_BY', properties={}), Relationship(source=Node(id='孙悟空', type='Person', properties={}), target=Node(id='混世魔王', type='Person', properties={}), type='BATTLE_WITH', properties={}), Relationship(source=Node(id='孙悟空', type='Person', properties={}), target=Node(id='大刀', type='Item', properties={}), type='USED_BY', properties={}), Relationship(source=Node(id='孙悟空', type='Person', properties={}), target=Node(id='毫毛', type='Item', properties={}), type='OWNS', properties={}), Relationship(source=Node(id='毫毛', type='Item', properties={}), target=Node(id='小猴子', type='Group', properties={}), type='TRANSFORMED_INTO', properties={}), Relationship(source=Node(id='兵器库', type='Place', properties={}), target=Node(id='傲来国', type='Place', properties={}), type='LOCATED_IN', properties={}), Relationship(source=Node(id='通背老猿猴', type='Person', properties={}), target=Node(id='水帘洞', type='Place', properties={}), type='LOCATED_IN', properties={}), Relationship(source=Node(id='东海龙宫', type='Place', properties={}), target=Node(id='海底', type='Place', properties={}), type='LOCATED_IN', properties={}), Relationship(source=Node(id='老龙王敖广', type='Person', properties={}), target=Node(id='东海龙宫', type='Place', properties={}), type='LOCATED_IN', properties={}), Relationship(source=Node(id='虾兵', type='Group', properties={}), target=Node(id='东海龙宫', type='Place', properties={}), type='LOCATED_IN', properties={}), Relationship(source=Node(id='老龙王敖广', type='Person', properties={}), target=Node(id='九股叉', type='Item', properties={}), type='OWNS', properties={}), Relationship(source=Node(id='蟹将', type='Group', properties={}), target=Node(id='东海龙宫', type='Place', properties={}), type='LOCATED_IN', properties={}), Relationship(source=Node(id='老龙王敖广', type='Person', properties={}), target=Node(id='方天画戟', type='Item', properties={}), type='OWNS', properties={}), Relationship(source=Node(id='龙婆', type='Person', properties={}), target=Node(id='东海龙宫', type='Place', properties={}), type='LOCATED_IN', properties={}), Relationship(source=Node(id='大禹', type='Person', properties={}), target=Node(id='神珍铁', type='Item', properties={}), type='USED_BY', properties={}), Relationship(source=Node(id='神珍铁', type='Item', properties={}), target=Node(id='海底', type='Place', properties={}), type='LOCATED_IN', properties={}), Relationship(source=Node(id='神珍铁', type='Item', properties={}), target=Node(id='如意金箍棒', type='Item', properties={}), type='TRANSFORMED_INTO', properties={}), Relationship(source=Node(id='孙悟空', type='Person', properties={}), target=Node(id='如意金箍棒', type='Item', properties={}), type='OWNS', properties={}), Relationship(source=Node(id='七十二洞的洞主', type='Group', properties={}), target=Node(id='花果山', type='Place', properties={}), type='LOCATED_IN', properties={})], source=Document(metadata={}, page_content='孙悟空见没办法留下来,就拜别了菩提祖师,又和各位师兄告别,然后念了口诀,驾着筋斗云,不到一个时辰,就回到了花果山水帘洞,看到花果山上一片荒凉破败的景象,很是凄惨。\n原来孙悟空走了以后,有一个混世魔王独占了水帘洞,并且抢走了许多猴子猴孙。孙悟空听到这些以后,气得咬牙跺脚。他问清了混世魔王的住处,决定找混世魔王报仇,便驾着筋斗云,朝北方飞去。\n不一会儿,孙悟空就来到混世魔王的水脏洞前,对门前的小妖喊到∶"你家那个狗屁魔王,多次欺负我们猴子。我今天来,要和那魔王比比高低!"小妖跑进洞里,报告魔王。魔王急忙穿上铁甲,提着大刀,在小妖们的簇拥下走出洞门。\n孙悟空赤手空拳,夺过了混世魔王的大刀,把他劈成了两半。然后,拔下一把毫毛咬碎喷了出去,毫毛变成许多小猴子,直杀进洞里,把所有的妖精全杀死,然后救出被抢走的小猴子,放了一把火烧了水脏洞。\n孙悟空收回了毫毛,让小猴子们闭上眼睛,作起法术来,一阵狂风刮过,他们驾着狂风回到了花果山。从此,孙悟空便叫小猴们做了些竹枪和木刀,用夺来的大刀教他们武艺。没过多久,孙悟空觉得竹枪木刀不能打仗,两个猴告诉他,傲来国里肯定有好的兵器。\n孙悟空驾云来到傲来国上空,念起咒语,立即天空刮起狂风,砂石乱飞,把满城的军民吓得不敢出来。他趁机跑进兵器库拔了把毫毛一吹,变成上千个小猴,乱搬乱抢,悟空见差不多了,把风向一变回了花果山。\n从此以后,花果山水帘洞的名气就更大了,所有的妖怪头子,即七十二洞的洞主都来拜见孙悟空。可是,悟空却有一件事不顺心,嫌那口大刀太轻,不好用。有个通背老猿猴告诉悟空,水帘洞桥下,可直通东海龙宫,叫他去找龙王要一件得心应手的兵器。\n悟空立刻来到东海龙宫,给老龙王敖广讲明了来这儿的目的。龙王不好推辞,叫虾兵们抬出一杆三千六百斤重的九股叉,悟空接过来玩了一阵,嫌它太轻。龙王又命令蟹将们抬出一柄七千二百斤重的方天画戟,悟空一见,仍然嫌它太轻。\n龙王说∶"再也没有比这更重的兵器了。"悟空不信,和龙王吵了起来,龙婆给龙王说∶"大禹治水时,测定海水深浅的神珍铁最近总是放光,就把这给他,管他能不能用,打发他走算了。"龙王听后告诉悟空∶"这宝物太重了,你自己去取吧!"\n孙悟空跟龙王来到海底,龙王用手一指说∶"放光的就是。"悟空见神珍铁金光四射,就走过去用手一摸,原来是根铁柱子,斗一样粗,二丈多长。孙悟空使劲用手搬了搬说∶"太长太长了,要是再短些,再细一些就好了。"\n孙悟空话还没有说完,那个宝贝就短了几尺,也细了一圈。孙悟空看了看说∶"再细些就更好了。"那个宝贝真的又细了许多,悟空拿过来,见上面写着∶"如意金箍棒、重一万三千五百斤"顺手玩了一会儿,觉得十分好用。')), GraphDocument(nodes=[Node(id='孙悟空', type='Person', properties={}), Node(id='龙王', type='Person', properties={}), Node(id='敖钦', type='Person', properties={}), Node(id='敖顺', type='Person', properties={}), Node(id='敖闰', type='Person', properties={}), Node(id='勾魂鬼', type='Person', properties={}), Node(id='十位冥王', type='Person', properties={}), Node(id='判官', type='Person', properties={}), Node(id='地藏王菩萨', type='Person', properties={}), Node(id='玉皇大帝', type='Person', properties={}), Node(id='海底', type='Place', properties={}), Node(id='水晶宫', type='Place', properties={}), Node(id='花果山', type='Place', properties={}), Node(id='幽冥界', type='Place', properties={}), Node(id='森罗殿', type='Place', properties={}), Node(id='翠云宫', type='Place', properties={}), Node(id='如意金箍棒', type='Item', properties={}), Node(id='黄金甲', type='Item', properties={}), Node(id='凤翅紫金冠', type='Item', properties={}), Node(id='藕丝步云鞋', type='Item', properties={}), Node(id='金钟', type='Item', properties={}), Node(id='生死簿', type='Item', properties={})], relationships=[Relationship(source=Node(id='孙悟空', type='Person', properties={}), target=Node(id='如意金箍棒', type='Item', properties={}), type='OWNS', properties={}), Relationship(source=Node(id='孙悟空', type='Person', properties={}), target=Node(id='黄金甲', type='Item', properties={}), type='OWNS', properties={}), Relationship(source=Node(id='孙悟空', type='Person', properties={}), target=Node(id='凤翅紫金冠', type='Item', properties={}), type='OWNS', properties={}), Relationship(source=Node(id='孙悟空', type='Person', properties={}), target=Node(id='藕丝步云鞋', type='Item', properties={}), type='OWNS', properties={}), Relationship(source=Node(id='龙王', type='Person', properties={}), target=Node(id='金钟', type='Item', properties={}), type='OWNS', properties={}), Relationship(source=Node(id='十位冥王', type='Person', properties={}), target=Node(id='生死簿', type='Item', properties={}), type='OWNS', properties={}), Relationship(source=Node(id='孙悟空', type='Person', properties={}), target=Node(id='海底', type='Place', properties={}), type='LOCATED_IN', properties={}), Relationship(source=Node(id='龙王', type='Person', properties={}), target=Node(id='海底', type='Place', properties={}), type='LOCATED_IN', properties={}), Relationship(source=Node(id='孙悟空', type='Person', properties={}), target=Node(id='水晶宫', type='Place', properties={}), type='LOCATED_IN', properties={}), Relationship(source=Node(id='孙悟空', type='Person', properties={}), target=Node(id='花果山', type='Place', properties={}), type='LOCATED_IN', properties={}), Relationship(source=Node(id='孙悟空', type='Person', properties={}), target=Node(id='幽冥界', type='Place', properties={}), type='LOCATED_IN', properties={}), Relationship(source=Node(id='孙悟空', type='Person', properties={}), target=Node(id='森罗殿', type='Place', properties={}), type='LOCATED_IN', properties={}), Relationship(source=Node(id='勾魂鬼', type='Person', properties={}), target=Node(id='幽冥界', type='Place', properties={}), type='LOCATED_IN', properties={}), Relationship(source=Node(id='森罗殿', type='Place', properties={}), target=Node(id='幽冥界', type='Place', properties={}), type='LOCATED_IN', properties={}), Relationship(source=Node(id='十位冥王', type='Person', properties={}), target=Node(id='森罗殿', type='Place', properties={}), type='LOCATED_IN', properties={}), Relationship(source=Node(id='翠云宫', type='Place', properties={}), target=Node(id='幽冥界', type='Place', properties={}), type='LOCATED_IN', properties={}), Relationship(source=Node(id='地藏王菩萨', type='Person', properties={}), target=Node(id='翠云宫', type='Place', properties={}), type='LOCATED_IN', properties={}), Relationship(source=Node(id='孙悟空', type='Person', properties={}), target=Node(id='勾魂鬼', type='Person', properties={}), type='BATTLE_WITH', properties={}), Relationship(source=Node(id='孙悟空', type='Person', properties={}), target=Node(id='十位冥王', type='Person', properties={}), type='BATTLE_WITH', properties={}), Relationship(source=Node(id='十位冥王', type='Person', properties={}), target=Node(id='地藏王菩萨', type='Person', properties={}), type='SUBORDINATE_OF', properties={}), Relationship(source=Node(id='十位冥王', type='Person', properties={}), target=Node(id='玉皇大帝', type='Person', properties={}), type='SUBORDINATE_OF', properties={})], source=Document(metadata={}, page_content='孙悟空跟龙王来到海底,龙王用手一指说∶"放光的就是。"悟空见神珍铁金光四射,就走过去用手一摸,原来是根铁柱子,斗一样粗,二丈多长。孙悟空使劲用手搬了搬说∶"太长太长了,要是再短些,再细一些就好了。"\n孙悟空话还没有说完,那个宝贝就短了几尺,也细了一圈。孙悟空看了看说∶"再细些就更好了。"那个宝贝真的又细了许多,悟空拿过来,见上面写着∶"如意金箍棒、重一万三千五百斤"顺手玩了一会儿,觉得十分好用。\n回到水晶宫,孙悟空又要龙王送一身衣服相配。龙王实在没有,但又害怕悟空乱打乱闹,只好敲响应急的金钟,叫来南、北、西三海龙王敖钦、敖顺和敖闰,兄弟三人凑了一副黄金甲、一顶凤翅紫金冠、一双藕丝步云鞋,送给悟空。\n回到花果山,悟空才发现那根金箍棒竟可以变成绣花针一样大小,藏到耳朵中。一天,他宴请所有的妖王吃饭,喝醉了,在桥边的松树下睡觉,迷迷糊糊地见两个人手里拿着写有"孙悟空"的批文,走到他身边也不说话,把他用绳索套上,拉起来就走。\n悟空糊里糊涂跟他们来到一座城门外,看见城楼上有一块牌子,牌子上写着"幽冥界"三个大字,知道这里是阎王住的地方,转身就要走,两个勾魂鬼死死拉住他,非要让他进去。孙悟空一看火了,从耳朵中掏出了金箍棒,把两个勾魂鬼打成了肉酱。\n他甩掉套在身上的绳套,挥着金箍棒直打到城里,又一直打到森罗殿前,十位冥王见悟空长得的十分凶恶,吓得不知道该怎么办。悟空说∶"你们既然坐在王位上,就应该有点灵气,为什么不知道我来?俺老孙已经修成仙道,能长生不老。快拿生死簿来!"十位冥王赶快叫判官拿出生死本来查。\n悟空登上森罗殿,一直查到魂字一千三百五十号,才找到了自己的名字,顺手拿起笔把所有猴子的名字通通勾掉,说∶"这下好极了,好极了,今后再也不归你们管了。"说完又一路打出了幽冥界。十位冥王赶忙到翠云宫去见地藏王菩萨,商量如何向玉皇大帝报告。'))]
提取到 5 个有效 GraphDocument
正在写入 Neo4j ...
Traceback (most recent call last):
File "C:\Users\lenovo\PycharmProjects\PythonProject\rag\day10\02-提示词知识图谱.py", line 201, in <module>
ingest_data("西游记.txt")
File "C:\Users\lenovo\PycharmProjects\PythonProject\rag\day10\02-提示词知识图谱.py", line 169, in ingest_data
graph.add_graph_documents(
File "D:\soft\miniconda3\envs\py_llm\Lib\site-packages\langchain_neo4j\graphs\neo4j_graph.py", line 378, in add_graph_documents
self.query(node_import_query, node_import_query_params)
File "D:\soft\miniconda3\envs\py_llm\Lib\site-packages\langchain_neo4j\graphs\neo4j_graph.py", line 246, in query
data, _, _ = self._driver.execute_query(
^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "D:\soft\miniconda3\envs\py_llm\Lib\site-packages\neo4j\_sync\driver.py", line 950, in execute_query
return session._run_transaction(
^^^^^^^^^^^^^^^^^^^^^^^^^
File "D:\soft\miniconda3\envs\py_llm\Lib\site-packages\neo4j\_sync\work\session.py", line 577, in _run_transaction
result = transaction_function(tx, *args, **kwargs)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "D:\soft\miniconda3\envs\py_llm\Lib\site-packages\neo4j\_work\query.py", line 142, in wrapped
return f(*args, **kwargs)
^^^^^^^^^^^^^^^^^^
File "D:\soft\miniconda3\envs\py_llm\Lib\site-packages\neo4j\_sync\driver.py", line 1302, in _work
res = tx.run(query, parameters)
^^^^^^^^^^^^^^^^^^^^^^^^^
File "D:\soft\miniconda3\envs\py_llm\Lib\site-packages\neo4j\_sync\work\transaction.py", line 201, in run
result._tx_ready_run(query, parameters)
File "D:\soft\miniconda3\envs\py_llm\Lib\site-packages\neo4j\_sync\work\result.py", line 180, in _tx_ready_run
self._run(query, parameters, None, None, None, None, None, None)
File "D:\soft\miniconda3\envs\py_llm\Lib\site-packages\neo4j\_sync\work\result.py", line 240, in _run
self._attach()
File "D:\soft\miniconda3\envs\py_llm\Lib\site-packages\neo4j\_sync\work\result.py", line 441, in _attach
self._connection.fetch_message()
File "D:\soft\miniconda3\envs\py_llm\Lib\site-packages\neo4j\_sync\io\_common.py", line 193, in inner
func(*args, **kwargs)
File "D:\soft\miniconda3\envs\py_llm\Lib\site-packages\neo4j\_sync\io\_bolt.py", line 868, in fetch_message
res = self._process_message(tag, fields)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "D:\soft\miniconda3\envs\py_llm\Lib\site-packages\neo4j\_sync\io\_bolt5.py", line 496, in _process_message
response.on_failure(summary_metadata or {})
File "D:\soft\miniconda3\envs\py_llm\Lib\site-packages\neo4j\_sync\io\_common.py", line 263, in on_failure
raise self._hydrate_error(metadata)
neo4j.exceptions.CypherSyntaxError: {neo4j_code: Neo.ClientError.Statement.SyntaxError} {message: Invalid input '$': expected an identifier (line 1, column 268 (offset: 267))
"MERGE (d:Document {id:$document.metadata.id}) SET d.text = $document.page_content SET d += $document.metadata WITH d UNWIND $data AS row MERGE (source:`__Entity__` {id: row.id}) SET source += row.properties MERGE (d)-[:MENTIONS]->(source) WITH source, row SET source:$(row.type) WITH source AS node RETURN distinct 'done' AS result"
^} {gql_status: 50N42} {gql_status_description: error: general processing exception - unexpected error. Invalid input '$': expected an identifier (line 1, column 268 (offset: 267))
"MERGE (d:Document {id:$document.metadata.id}) SET d.text = $document.page_content SET d += $document.metadata WITH d UNWIND $data AS row MERGE (source:`__Entity__` {id: row.id}) SET source += row.properties MERGE (d)-[:MENTIONS]->(source) WITH source, row SET source:$(row.type) WITH source AS node RETURN distinct 'done' AS result"
^}
Process finished with exit code 1
尤其是如下这段内容:
File "D:\soft\miniconda3\envs\py_llm\Lib\site-packages\neo4j\_sync\io\_bolt5.py", line 496, in _process_message
response.on_failure(summary_metadata or {})
File "D:\soft\miniconda3\envs\py_llm\Lib\site-packages\neo4j\_sync\io\_common.py", line 263, in on_failure
raise self._hydrate_error(metadata)
neo4j.exceptions.CypherSyntaxError: {neo4j_code: Neo.ClientError.Statement.SyntaxError} {message: Invalid input '$': expected an identifier (line 1, column 268 (offset: 267))
"MERGE (d:Document {id:$document.metadata.id}) SET d.text = $document.page_content SET d += $document.metadata WITH d UNWIND $data AS row MERGE (source:`__Entity__` {id: row.id}) SET source += row.properties MERGE (d)-[:MENTIONS]->(source) WITH source, row SET source:$(row.type) WITH source AS node RETURN distinct 'done' AS result"
^} {gql_status: 50N42} {gql_status_description: error: general processing exception - unexpected error. Invalid input '$': expected an identifier (line 1, column 268 (offset: 267))
"MERGE (d:Document {id:$document.metadata.id}) SET d.text = $document.page_content SET d += $document.metadata WITH d UNWIND $data AS row MERGE (source:`__Entity__` {id: row.id}) SET source += row.properties MERGE (d)-[:MENTIONS]->(source) WITH source, row SET source:$(row.type) WITH source AS node RETURN distinct 'done' AS result"
^}
找了资料,发现langchain_neo4j不适配导致的。执行如下命令:
pip uninstall langchain_neo4j
pip install langchain-neo4j==0.8.0
然后重新运行就好了。

运行结果如下:
D:\soft\miniconda3\envs\py_llm\python.exe C:\Users\lenovo\PycharmProjects\PythonProject\rag\day10\02-提示词知识图谱.py
C:\Users\lenovo\PycharmProjects\PythonProject\rag\day10\02-提示词知识图谱.py:14: DeprecationWarning: `langchain-community` is being sunset and is no longer actively maintained. See https://github.com/langchain-ai/langchain-community/issues/674 for details and migration guidance toward standalone integration packages.
from langchain_community.graphs.graph_document import (
问题:孙悟空的师父是谁?他在哪里学艺?
> Entering new GraphCypherQAChain chain...
Generated Cypher:
MATCH (p:Person {id: '孙悟空'})
OPTIONAL MATCH (p)-[:DISCIPLE_OF]->(master:Person)
OPTIONAL MATCH (p)-[:LEARNED_AT]->(place:Place)
RETURN master.id AS 师父, place.id AS 学艺地点
Full Context:
[{'师父': '菩提祖师', '学艺地点': '斜月三星洞'}]
> Finished chain.
回答:孙悟空的师父是菩提祖师,他在斜月三星洞学艺。
问题:花果山在哪里?
> Entering new GraphCypherQAChain chain...
Generated Cypher:
MATCH (p:Place {id: '花果山'})-[:LOCATED_IN]->(loc:Place) RETURN loc
Full Context:
[{'loc': {'id': '傲来国'}}]
> Finished chain.
回答:花果山位于傲来国。
问题:为什么孙悟空被压在五行山下?
> Entering new GraphCypherQAChain chain...
Generated Cypher:
MATCH (p:Person {id: '孙悟空'})-[:LOCATED_IN]->(pl:Place {id: '五行山'})
MATCH (d:Document)-[:MENTIONS]->(p)
MATCH (d)-[:MENTIONS]->(pl)
RETURN d.text
Full Context:
[]
> Finished chain.
回答:我不知道答案。
Process finished with exit code 0