使用neo4j构建知识图谱

前提:已经安装好了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
相关推荐
Java后端的Ai之路1 小时前
01、Python - 设计模式介绍
java·人工智能·python·设计模式·通用
wujian83111 小时前
【腾讯元宝手机版生成的表格怎么复制下来】?试试 [ AI 导出鸭 ] 的“格式网关”
人工智能·ai·chatgpt·智能手机·ai导出鸭
zzzll11111 小时前
大模型技术原理与应用实践
java·数据库·人工智能
show4331 小时前
2026微信小程序创作工具生态技术趋势:从单一功能到AI整合型平台演进
人工智能·微信小程序·小程序
聪明蛋子哟1 小时前
自动化Prompt工程与RAG的深度融合:PE2框架在问答系统中的优化实践
人工智能
Maynor9961 小时前
Claude Desktop 桌面端全流程指南(含国内直连配置)
人工智能·开源
AI英德西牛仔1 小时前
让 AI 导出回归优雅:纳米AI excel 与“AI 导出鸭”的 PC 端批量导出技术拆解
人工智能·excel·deepseek·ai导出鸭
ZPC82101 小时前
moveit2_servo cmakelist 拆解
人工智能·深度学习·机器人
EW Frontier1 小时前
【DOA估计】四根天线、一块SDR,神经网络把测向误差压进2度以内【附python代码】
人工智能·python·神经网络·doa估计·aoa估计