2026年7月2日 周四晚上19:30,报名腾讯会议了解“如何构建自进化的动态知识库(Brain)”(限30人)
免费POC, 零成本试错
FDE知识库

FDE知识库

学习大模型的前沿技术与行业落地应用


收藏

将 Microsoft GraphRAG 集成到 Neo4j

发布日期:2024-08-06 12:38:12 浏览次数: 3353
作者:码农大牛毛

微信搜一搜,关注“码农大牛毛”

微软的 GraphRAG 实现最近引起了广泛关注。在我上一篇博客文章中,我讨论了如何构建图表,并探讨了研究论文中强调的一些创新方面。从高层次来看,GraphRAG 库的输入是包含各种信息的源文档。使用大型语言模型 (LLM) 处理文档,以提取有关文档中出现的实体及其关系的结构化信息。然后使用提取的结构化信息来构建知识图谱。

知识图谱构建完成后,GraphRAG 库结合图算法,特别是莱顿社区检测算法和 LLM 提示,生成知识图谱中发现的实体和关系社区的自然语言摘要。

在这篇文章中,我们将从GraphRAG 库中获取输出,将其存储在 Neo4j 中,然后使用 LangChain 和 LlamaIndex 编排框架直接从 Neo4j 设置检索器。

代码和 GraphRAG 输出可在GitHub上访问,从而让您跳过 GraphRAG 提取过程。

数据集

这篇博文中介绍的数据集是查尔斯·狄更斯的《圣诞颂歌》,可通过古腾堡计划免费访问。


查尔斯·狄更斯的《圣诞颂歌》

免费的 kindle 书籍和 epub 由志愿者数字化并校对。

www.gutenberg.org


我们选择这本书作为源文档,因为它在介绍文档中突出显示,使我们能够毫不费力地进行提取。

图形构建

尽管您可以跳过图提取部分,但我们将讨论我认为最重要的几个配置选项。例如,图提取可能非常耗费 token 且成本高昂。因此,使用相对便宜但性能良好的 LLM(如 gpt-4o-mini)测试提取是有意义的。正如这篇博文所述,与 gpt-4-turbo 相比,成本降低幅度可能很大,同时保持良好的准确性。

GRAPHRAG_LLM_MODEL =gpt -4 o-mini

最重要的配置是我们要提取的实体类型。默认情况下,会提取组织、人员、事件和地理位置。

GRAPHRAG_ENTITY_EXTRACTION_ENTITY_TYPES =组织、人员、事件、地理

这些默认实体类型可能适用于书籍,但请确保根据给定用例要处理的文档的领域对其进行相应的更改。

另一个重要配置是最大收集值。作者发现,而且我们也单独验证了,LLM 不会在一次提取过程中提取所有可用信息。

收集配置允许 LLM 执行多次提取过程。在上图中,我们可以清楚地看到,在执行多次过程(收集)时,我们可以提取更多信息。多次过程需要大量令牌,因此像 gpt-4o-mini 这样更便宜的模型有助于降低成本。

GRAPHRAG_ENTITY_EXTRACTION_MAX_GLEANINGS = 1

此外,默认情况下不会提取声明或协变量信息。您可以通过设置配置来启用此功能GRAPHRAG_CLAIM_EXTRACTION_ENABLED

GRAPHRAG_CLAIM_EXTRACTION_ENABLED =
GRAPHRAG_CLAIM_EXTRACTION_MAX_GLEANINGS = 1

似乎并非所有结构化信息都能一次性提取出来,这是一个反复出现的主题。因此,我们在这里也有收集配置选项。

另一个有趣的部分是提示调整部分,但我还没有时间深入研究。提示调整是可选的,但强烈建议这样做,因为它可以提高准确性。


及时调整⚙️

GraphRAG 提供了创建领域自适应模板以生成知识图谱的功能。此步骤…

微软.github.io


配置完成后,我们可以按照说明运行图形提取管道,它包含以下步骤。

提取管道执行上图中的所有蓝色步骤。查看我之前的博客文章,了解有关图形构建和社区摘要的更多信息。MSFT GraphRAG 库的图形提取管道的输出是一组 parquet 文件,如Operation Dulce 示例所示。

这些 parquet 文件可以轻松导入 Neo4j 图形数据库,以便进行下游分析、可视化和检索。我们可以使用免费的云 Aura 实例或设置本地 Neo4j 环境。我的朋友Michael Hunger完成了将 parquet 文件导入 Neo4j 的大部分工作。我们将在本篇博文中跳过导入说明,但它包括从五六个 CSV 文件导入和构建知识图谱。如果您想了解有关 CSV 导入的更多信息,可以查看Neo4j Graph Academy 课程

导入代码以Jupyter 笔记本的形式在 GitHub 上提供,并附有示例 GraphRAG 输出。


blogs/msft_graphrag/ms_graphrag_import.ipynb at master · tomasonjo/blogs

Jupyter 笔记本支持我的图形数据科学博客文章,网址为 https://bratanic-tomaz.medium.com/...

github.com


导入完成后,我们可以打开 Neo4j 浏览器来验证和可视化导入的图表的各个部分。

图表分析

在开始实现检索器之前,我们将进行一个简单的图形分析,以熟悉提取的数据。我们首先定义数据库连接和一个执行 Cypher 语句(图形数据库查询语言)并输出 Pandas DataFrame 的函数。

NEO4J_URI="bolt://localhost"“bolt://localhost”
NEO4J_USERNAME = “neo4j”
NEO4J_PASSWORD = “密码”

driver = GraphDatabase.driver(NEO4J_URI,auth =(NEO4J_USERNAME,NEO4J_PASSWORD))

def db_querycypher:str,params:Dict [ strAny ] = {})-> pd.DataFrame:
“”执行Cypher语句并返回DataFrame“”
返回driver.execute_query(
cypher,parameters_ = params,result_transformer_ = Result.to_df

在执行图形提取时,我们使用了 300 的块大小。从那时起,作者将默认块大小更改为 1200。我们可以使用以下 Cypher 语句验证块大小。

db_query( 
“MATCH (n:__Chunk__) RETURN n.n_tokens as token_count,count(*) AS count”“匹配(n:__Chunk__)将 n.n_tokens 返回为 token_count,count(*)AS count”

# token_count count
# 300 230
# 155 1

230 个块有 300 个 token,而最后一个只有 155 个 token。现在让我们检查一个示例实体及其描述。

db_query( 
"MATCH (n:__Entity__) RETURN n.name AS name, n.description AS description LIMIT 1"“MATCH(n:__Entity__)RETURN n.name AS name,n.description AS description LIMIT 1”

结果

书中似乎在某处描述了古腾堡计划,可能是在开头。我们可以观察到描述如何能够捕捉比实体名称更详细和复杂的信息,MSFT GraphRAG 论文介绍了实体名称,以从文本中保留更复杂和细微的数据。

我们也来检查一下示例关系。

db_query( 
"MATCH ()-[n:RELATED]->() RETURN n.description AS 描述 LIMIT 5"“MATCH ()-[n:RELATED]->() RETURN n.description AS description LIMIT 5”

结果

MSFT GraphRAG 不仅仅能够提取实体之间的简单关系类型,还能捕获详细的关系描述。此功能使其能够捕获比直接关系类型更细致入微的信息。

我们还可以检查单个社区及其生成的描述。

db_query(""""""
匹配 (n:__Community__)
返回 n.title 作为标题、n.summary 作为摘要、n.full_content 作为完整内容 LIMIT 1
"""
)

结果

社区有标题、摘要和使用 LLM 生成的完整内容。我还没有看到作者在检索过程中是使用完整上下文还是仅使用摘要,但我们可以在两者之间进行选择。我们可以在 full_content 中观察到引文,这些引文指向信息来源的实体和关系。有趣的是,如果引文太长,LLM 有时会将其删减,如下例所示。

[数据:实体 (11, 177);关系 (25, 159, 20, 29, +更多)]11 , 177 ); 关系 ( 25 , 159 , 20 , 29 , +更多)]

没有办法扩展+more符号,所以这是处理 LLM 长引用的一种有趣方式。

现在让我们评估一些分布。我们首先检查从文本块中提取的实体数量的分布。

entity_df = db_query( 
""""""
MATCH (d:__Chunk__)
RETURN count {(d)-[:HAS_ENTITY]->()} AS entity_count
"""

)
# 绘制分布
plt.figure(figsize=( 10 , 6 ))
sns.histplot(entity_df[ 'entity_count' ], kde= True , bins= 15 , color= 'skyblue' )
plt.axvline(entity_df[ 'entity_count' ].mean(), color= 'red' , linestyle= 'dashed' , linewidth= 1 )
plt.axvline(entity_df[ 'entity_count' ].median(), color= 'green' , linestyle= 'dashed' , linewidth= 1 )
plt.xlabel( 'Entity Count' , fontsize= 12 )
plt.ylabel( '频率',fontsize= 12
plt.title('实体数量分布',fontsize= 15
plt.legend({ '平均值':entity_df [ 'entity_count' ] .mean(),'中位数':entity_df [ 'entity_count' ] .median()})
plt.show()

结果

请记住,文本块有 300 个标记。因此,提取的实体数量相对较小,平均每个文本块约有 3 个实体。提取是在没有任何收集的情况下完成的(一次提取过程)。如果我们增加收集数量,看看分布情况会很有趣。

接下来,我们将评估节点度分布。节点度是节点具有的关系数量。

degree_dist_df = db_query( 
""""""
MATCH (e:__Entity__)
RETURN count {(e)-[:RELATED]-()} AS node_degree
"""

)
# 计算平均值和中位数
mean_degree = np.mean(degree_dist_df[ 'node_degree' ])
percentiles = np.percentile(degree_dist_df[ 'node_degree' ], [ 25 , 50 , 75 , 90 ])
# 使用对数刻度创建直方图
plt.figure(figsize=( 12 , 6 ))
sns.histplot(degree_dist_df[ 'node_degree' ], bins= 50 , kde= False , color= 'blue' )
# 对 x 轴使用对数刻度
plt.yscale( 'log' )
# 添加标签和标题
plt.xlabel( 'Node Degree' )
plt.ylabel( '计数(对数尺度)' )
plt.title( '节点度分布' )
# 添加平均值、中位数和百分位数线
plt.axvline(mean_degree, color= 'red' , linestyle= 'dashed' , linewidth= 1 , label= f'平均值:{mean_degree: .2 f} ' )
plt.axvline(percentiles[ 0 ], color= 'purple' , linestyle= 'dashed' , linewidth= 1 , label= f'25th 百分位数:{percentiles[ 0 ]: .2 f} ' )
plt.axvline(percentiles[ 1 ], color= 'orange' , linestyle= 'dashed' , linewidth= 1 , label= f'50th 百分位数:{percentiles[ 1 ]: .2 f} ' )
plt.axvline(百分位数[ 2 ], color= '黄色' , linestyle= '虚线' , linewidth= 1 , label= f'75 百分位数:{百分位数[ 2 ]: .2 f} ' )
plt.axvline(百分位数[ 3 ], color= '棕色' , linestyle= '虚线' , linewidth= 1 , label= f'90 百分位数:{百分位数[ 3 ]: .2 f} ' )
# 添加图例
plt.legend()
# 显示图表
plt.show()

结果

大多数现实世界网络遵循幂律节点度分布,大多数节点的度相对较小,而一些重要节点的度则很大。虽然我们的图很小,但节点度遵循幂律。确定哪个实体有 120 个关系(与 43% 的实体相关)会很有趣。

db_query("""""" 
MATCH (n:__Entity__)
RETURN n.name AS name, count{(n)-[:RELATED]-()} AS degree
ORDER BY degree DESC LIMIT 5"""
)

结果

我们可以毫不犹豫地假设 Scrooge 是这本书的主角。我还大胆猜测Ebenezer ScroogeScrooge实际上是同一个实体,但由于 MSFT GraphRAG 缺少实体解析步骤,因此它们没有合并。

它还表明,分析和清理数据是减少噪音信息的重要步骤,因为古腾堡计划有 13 种关系,即使它们不是书中故事的一部分。

最后,我们将检查每个层级的社区规模分布。

community_data = db_query("""""" 
MATCH (n:__Community__)
RETURN n.level AS level, count{(n)-[:IN_COMMUNITY]-()} AS members
"""
)

stats = community_data.groupby( 'level' ).agg(
min_members=( 'members' , 'min' ),
max_members=( 'members' , 'max' ),
median_members=( 'members' , 'median' ),
avg_members=( 'members' , 'mean' ),
num_communities=( 'members' , 'count' ),
total_members=( 'members' , 'sum' )
).reset_index()

# 创建箱线图
plt.figure(figsize=( 10 , 6 ))
sns.boxplot(x= 'level' , y= 'members' , data=community_data, palette= 'viridis' )
plt.xlabel( 'Level' )
plt.ylabel( 'Members' )

# 添加统计注释
for i in range (stats.shape[ 0 ]):
level = stats[ 'level' ][i]
max_val = stats[ 'max_members' ][i]
text = ( f"num: {stats[ 'num_communities' ][i]} \n"
f"all_members: {stats[ 'total_members' ][i]} \n"
f"min: {stats[ 'min_members' ][i]} \n"
f"max: {stats[ 'max_members' ][i]} \n"
f"med: {stats[ 'median_members' ][i]} \n"
f"avg: {stats[ 'avg_members' ][i]: .2 f} " )
plt.text(level, 85 , text, HorizontalAlignment= 'center' , Fontsize= 9 )

plt.show()

结果

Leiden 算法确定了三个级别的社区,其中级别越高的社区平均规模越大。但是,有一些技术细节我不清楚,因为如果你检查 all_members 计数,你会看到每个级别都有不同的所有节点数量,即使它们在理论上应该是相同的。此外,如果社区在更高级别合并,为什么我们在 0 级有 19 个社区,在 1 级有 22 个社区?作者在这里做了一些优化和技巧,我还没有时间详细探讨。

实现检索器

在这篇博文的最后一部分,我们将讨论 MSFT GraphRAG 中指定的本地和全局检索器。检索器将与 LangChain 和 LlamaIndex 一起实现和集成。

本地猎犬

本地检索器首先使用向量搜索来识别相关节点,然后收集链接信息并将其注入 LLM 提示中。

虽然此图看起来很复杂,但可以轻松实现。我们首先使用基于实体描述文本嵌入的向量相似性搜索来识别相关实体。识别出相关实体后,我们可以遍历相关文本块、关系、社区摘要等。使用retrieval_queryLangChain 和 LlamaIndex 中的功能可以轻松实现使用向量相似性搜索然后遍历整个图的模式。

首先,我们需要配置向量索引。

index_name = “entity”“实体”

db_query(
“” “
创建向量索引” “” “

+ index_name
+ ” “” “如果不存在,则为(e:__Entity__)在e.description_embedding
选项{indexConfig:{
`vector.dimensions`:1536,
`vector.similarity_function`:'cosine'
}}
“”

我们还将计算并存储社区权重,它定义为社区中的实体出现的不同文本块的数量。

db_query( 
""""""
MATCH (n:`__Community__`)<-[:IN_COMMUNITY]-()<-[:HAS_ENTITY]-(c)
WITH n, count(distinct c) AS chunkCount
SET n.weight = chunkCount"""

)

每个部分的候选词(文本单元、社区报告等)数量是可配置的。虽然原始实现基于标记计数的过滤稍微复杂一些,但我们将在此简化它。我根据默认配置值开发了以下简化的顶级候选过滤器值。

topChunks = 33
topCommunities = 3
topOutsideRels = 10
topInsideRels = 10
topEntities = 10

我们将从 LangChain 的实现开始。我们唯一需要定义的是retrieval_query,这比较复杂。

lc_retrieval_query = """""" 
WITH collect(node) as nodes
// 实体 - 文本单元映射
WITH
collect {
UNWIND nodes as n
MATCH (n)<-[:HAS_ENTITY]->(c:__Chunk__)
WITH c, count(distinct n) as freq
RETURN c.text AS chunkText
ORDER BY freq DESC
LIMIT $topChunks
} AS text_mapping,
// 实体 - 报告映射
collect {
UNWIND nodes as n
MATCH (n)-[:IN_COMMUNITY]->(c:__Community__)
WITH c, c.rank as rank, c.weight AS weight
RETURN c.summary
ORDER BY rank, weight DESC
LIMIT $topCommunities
} AS report_mapping,
// 外部关系
collect {
UNWIND nodes as n
MATCH (n)-[r:RELATED]-(m)
WHERE NOT m IN nodes
RETURN r.description AS descriptionText
ORDER BY r.rank, r.weight DESC
LIMIT $topOutsideRels
} as outsideRels,
// 内部关系
收集 {
UNWIND nodes as n
MATCH (n)-[r:RELATED]-(m)
WHERE m IN nodes
RETURN r.description AS descriptionText
ORDER BY r.rank, r.weight DESC
LIMIT $topInsideRels
} as insideRels,
// 实体描述
收集 {
UNWIND nodes as n
RETURN n.description AS descriptionText
} as entities
// 我们这里没有协变量或声明
RETURN {Chunks: text_mapping, Reports: report_mapping,
Relationships: outsideRels + insideRels,
Entities: entities} AS text, 1.0 AS score, {} AS metadata
"""


lc_vector = Neo4jVector.from_existing_index(
OpenAIEmbeddings(model= "text-embedding-3-small" ),
url=NEO4J_URI,
用户名=NEO4J_USERNAME,
密码=NEO4J_PASSWORD,
索引名称=index_name,
检索查询=lc_retrieval_query

此 Cypher 查询对一组节点执行多个分析操作,以提取和组织相关文本数据:

1.实体-文本单元映射:对于每个节点,查询会识别链接的文本块(“__Chunk__”),根据与每个块关联的不同节点的数量对其进行聚合,并按频率对其进行排序。排名靠前的块将作为“text_mapping”返回。

2.实体报告映射:对于每个节点,查询找到相关的社区(“__Community__”),并根据排名和权重返回排名靠前的社区的摘要。

3.外部关系:此部分提取关系(`RELATED`)的描述,其中相关实体(`m`)不是初始节点集的一部分。关系按等级排列,并限制为顶级外部关系。

4.内部关系:与外部关系类似,但这次它只考虑两个实体都在初始节点集内的关系。

5.实体描述:简单收集初始集合中每个节点的描述。

最后,查询将收集的数据组合成一个结构化结果,该结果由区块、报告、内部和外部关系以及实体描述以及默认分数和空元数据对象组成。您可以选择删除部分检索内容,以测试它们对结果的影响。

现在您可以使用以下代码运行检索器:

docs = lc_vector.similarity_search( 
"您对 Cratchitt 家族了解多少?",“您对克拉奇特家族了解多少?” ,
k=topEntities,
params={
"topChunks" : topChunks,
"topCommunities" : topCommunities,
"topOutsideRels" : topOutsideRels,
"topInsideRels" : topInsideRels,
},
)
# print(docs[0].page_content)

同样的检索模式可以用 LlamaIndex 来实现。对于 LlamaIndex,我们首先需要将元数据添加到节点,这样向量索引才能起作用。如果没有将默认元数据添加到相关节点,向量索引将返回错误

#https://github.com/run-llama/llama_index/blob/main/llama-index-core/llama_index/core/vector_stores/utils.py#L32
llama_index.core.schema导入TextNode
llama_index.core.vector_stores.utils导入node_to_metadata_dict

content = node_to_metadata_dict(TextNode(), remove_text= True, flat_metadata= False )

db_query(
"""
MATCH (e:__Entity__)
SET e += $content"""

{ "content" : content},
)

同样,我们可以使用retrieval_queryLlamaIndex 中的功能来定义检索器。与 LangChain 不同,我们将使用 f 字符串而不是查询参数来传递最佳候选过滤器参数。

retrieval_query = f"""f""" 
WITH collect(node) as nodes
// 实体 - 文本单元映射
WITH
nodes,
collect {{
UNWIND nodes as n
MATCH (n)<-[:HAS_ENTITY]->(c:__Chunk__)
WITH c, count(distinct n) as freq
RETURN c.text AS chunkText
ORDER BY freq DESC
LIMIT {topChunks}
}} AS text_mapping,
// 实体 - 报告映射
collect {{
UNWIND nodes as n
MATCH (n)-[:IN_COMMUNITY]->(c:__Community__)
WITH c, c.rank as rank, c.weight AS weight
RETURN c.summary
ORDER BY rank, weight DESC
LIMIT {topCommunities}
}} AS report_mapping,
// 外部关系
collect {{
UNWIND nodes as n
MATCH (n)-[r:RELATED]-(m)
WHERE NOT m IN nodes
RETURN r.description AS descriptionText
ORDER BY r.rank, r.weight DESC
LIMIT {topOutsideRels}
}} as outsideRels,
// 内部关系
收集 {{
UNWIND nodes as n
MATCH (n)-[r:RELATED]-(m)
WHERE m IN nodes
RETURN r.description AS descriptionText
ORDER BY r.rank, r.weight DESC
LIMIT {topInsideRels}
}} as insideRels,
// 实体描述
收集 {{
UNWIND nodes as n
RETURN n.description AS descriptionText
}} as entities
// 我们这里没有协变量或声明
RETURN "Chunks:" + apoc.text.join(text_mapping, '|') + "\nReports: " + apoc.text.join(report_mapping,'|') +
"\nRelationships: " + apoc.text.join(outsideRels + insideRels, '|') +
"\nEntities: " + apoc.text.join(entities, "|") AS 文本, 1.0 AS 分数, nodes[0].id AS id, {{_node_type:nodes[0]._node_type, _node_content:nodes[0]._node_content}} AS 元数据
"""

此外,返回结果略有不同。我们需要将节点类型和内容作为元数据返回;否则,检索器将崩溃。现在我们只需实例化 Neo4j 向量存储并将其用作查询引擎。

neo4j_vector = Neo4jVectorStore(
NEO4J_USERNAME,
NEO4J_PASSWORD,
NEO4J_URI,
embed_dim,
index_name=index_name,
retrieval_query=retrieval_query,

loaded_index = VectorStoreIndex.from_vector_store(neo4j_vector)。as_query_engine(
similarity_top_k=topEntities,embed_model=OpenAIEmbedding(model="text-embedding-3-large")“文本嵌入-3-大”

我们现在可以测试GraphRAG本地检索器。

response = loaded_index.query("您对 Scrooge 了解多少?")“您对史高治了解多少?” ) 
print (response.response)
#print(response.source_nodes[0].text) # 史高治是一名深受Fezziwig 家族
慷慨和节日气氛影响的员工,尤其是 Fezziwig 先生和夫人。他参加了 Fezziwig 家族举办的令人难忘的家庭舞会,这场舞会极大地影响了他的生活,并为更广泛的善良和社区精神的叙述做出了贡献。



我立即想到的一件事是,我们可以通过使用混合方法(向量+关键字)来查找相关实体而不是仅仅进行向量搜索来改进本地检索。

全球检索器

全局检索器架构稍微简单一些。它似乎在指定的层次结构上迭代所有社区摘要,生成中间摘要,然后根据中间摘要生成最终响应。

我们必须提前确定要迭代的层次结构,这不是一个简单的决定,因为我们不知道哪一个会更好。层次结构越高,社区就越大,但社区数量就越少。这是我们在不手动检查摘要的情况下获得的唯一信息。

其他参数允许我们忽略低于排名或权重阈值的社区,我们不会在这里使用这些参数。我们将使用 LangChain 实现全局检索器,并使用与 GraphRAG 论文中相同的mapReduce 提示。由于系统提示很长,我们不会在这里包含它们或链构造。但是,所有代码都可以在笔记本中找到

def  global_retriever查询:str,级别:int,响应类型:str = response_type)-> str
community_data = graph.query(
“”“
MATCH(c:__Community__)
WHERE c.level = $level
RETURN c.full_content AS output
“”

params = { “level”:level},

intermediate_results = []
for community in tqdm(community_data,desc = “处理社区”):
intermediate_response = map_chain.invoke(
{ “question”:query,“context_data”:community [ “output” ]}

intermediate_results.append(intermediate_response)
final_response = reduce_chain.invoke(
{
“report_data”:intermediate_results,
“question”:query,
“response_type”:response_type,
}

返回最终回应

现在让我们测试一下。

print (global_retriever( "这个故事讲的是什么?" , 2 ))

结果

故事主要围绕埃比尼泽·斯克鲁奇展开,他是一个吝啬鬼,最初对生活持愤世嫉俗的态度,鄙视圣诞节。他的转变始于他已故商业伙伴雅各布·马利的鬼魂来访,随后三个幽灵出现——代表过去、现在和未来的圣诞节。这些遭遇促使斯克鲁奇反思自己的生活和行为的后果,最终使他拥抱圣诞精神并经历了重大的个人成长 [数据:报告(32、17、99、86 等)]。

### 雅各布·马利和幽灵的作用

雅各布·马利的鬼魂充当了超自然的催化剂,警告斯克鲁奇三个幽灵即将来访。每个幽灵都引导斯克鲁奇经历自我发现之旅,说明他的选择的影响以及同情心的重要性。幽灵向史高治揭示了他的行为不仅影响了他自己的生活,也影响了他人的生活,尤其突出了救赎和互联互通的主题 [数据:报告(86、17、99、+更多)]。

### 史高治的关系和转变史

高治与克拉奇特一家的关系,尤其是与鲍勃·克拉奇特和他的儿子小蒂姆的关系,对他的转变至关重要。通过幽灵呈现的景象,史高治产生了同理心,这激励他采取切实行动来改善克拉奇特一家的境况。叙述强调个人行为可以对社会产生深远的影响,因为史高治新发现的慷慨在他的社区中培养了同情心和社会责任感 [数据:报告(25、158、159、+更多)]。

### 救赎与希望的主题

总体而言,这个故事是希望的永恒象征,强调了同情、内省和个人改变的潜力等主题。史高治从一个孤独的守财奴变成一个仁慈人物的历程表明,改变永远不会太晚;小小的善举可以对个人和更广泛的社区产生重大的积极影响 [数据:报告(32、102、126、148、158、159 等)]。

总之,这个故事概括了圣诞节的变革力量和人际关系的重要性,使其成为一个关于救赎和一个人在节日期间对他人的影响的感人故事。

响应非常长且详尽,因为它适合全局检索器,该检索器会遍历指定级别的所有社区。您可以测试如果更改社区层次结构级别,响应会如何变化。


53AI,企业落地大模型首选服务商

产品:场景落地咨询+大模型应用平台+行业解决方案

承诺:免费POC验证,效果达标后再合作。零风险落地应用大模型,已交付160+中大型企业

联系我们

售前咨询
186 6662 7370
预约演示
185 8882 0121

微信扫码

添加专属顾问

回到顶部

加载中...

扫码咨询

扫码登录
登录即表示您同意《53AI网站服务协议》
服务协议

欢迎您使用【53AI 官方网站】(以下简称“本网站”或“我们”)。本《会员服务协议》(以下简称“本协议”)是您(以下简称“会员”或“用户”)与【深圳市博思协创网络科技有限公司】之间关于注册、登录及使用本网站会员服务所订立的法律协议。

在您注册或登录前,请务必审慎阅读、充分理解各条款内容,特别是免除或限制责任的条款、知识产权条款、争议解决条款等。此类条款将以加粗形式提示您注意。 当您通过微信公众号授权、手机验证码验证或其他方式成功登录本网站时,即视为您已完全理解并同意接受本协议的全部内容。

一、 定义

本网站:指由【深圳市博思协创网络科技有限公司】运营的,域名为【53ai.com】的网站及相关移动端页面。

会员服务:指本网站向注册会员提供的知识库文章查阅、内容检索及其他相关增值服务。

知识库内容:指本网站发布的包括但不限于文字、图表、数据、研究报告、行业分析等数字化内容资源。

二、 账号注册与登录

登录方式:本网站支持以下登录方式,您可根据实际情况选择:

微信公众号授权登录:您同意将您的微信OpenID信息授权给本网站,用于创建或关联会员账号。

手机验证码登录:您需提供真实有效的手机号码,并通过短信验证码完成身份验证与登录/注册。

账号安全:您的账号仅限您本人使用,禁止赠与、借用、租用、转让或售卖。因您保管不善导致的账号被盗、密码泄露等损失,由您自行承担。

实名认证:根据相关法律法规要求,我们可能要求您在特定功能下完成实名认证。如您拒绝提供,可能无法使用部分或全部服务。

未成年人保护:若您未满18周岁,请在法定监护人的陪同下阅读本协议,并在征得监护人同意后使用本服务。

三、 服务内容与规范

知识库查阅权限:会员登录后,有权按照其会员等级对应的权限范围,在线浏览、检索本网站知识库中的相关文章及内容。

服务变更:我们有权根据业务发展需要,调整、变更或终止部分服务内容,并将以网站公告、公众号消息等方式提前通知。

禁止行为:您在使用服务时不得实施以下行为:

利用技术手段批量爬取、下载、转存知识库内容;

将知识库内容用于商业目的或未经授权地向第三方传播;

干扰本网站正常运行或侵犯其他用户合法权益;

发布违法违规信息或从事违反公序良俗的活动。

四、 知识产权声明

权利归属:本网站知识库中的排版设计、软件代码等内容的知识产权均归【公司全称】或原权利人所有,受《中华人民共和国著作权法》等法律保护。

有限许可:本网站授予会员一项非独占、不可转让、不可转授权的普通许可,仅限于个人学习、研究之目的在线查阅知识库内容。

侵权追责:未经书面许可,任何单位或个人不得以任何形式复制、转载、摘编、镜像、汇编或以其他方式使用上述内容。一经发现,我们保留追究其法律责任的权利。

五、 个人信息保护

我们重视对您个人信息的保护。关于我们如何收集、使用、存储和保护您的个人信息,请单独阅读 《隐私政策》。

您通过微信公众号授权或手机号验证所提供的信息,我们将严格按照《个人信息保护法》的规定处理,仅用于身份识别、服务提供及安全验证等必要用途。

您可以随时通过网站设置或联系客服行使查阅、更正、删除个人信息及撤回授权同意的权利。

六、 免责声明

内容准确性:知识库内容仅供参考,不构成专业建议。我们不对其完整性、准确性、时效性作任何明示或暗示的保证,您应自行判断并承担使用风险。

不可抗力:因自然灾害、政策法规变化、网络故障、第三方平台接口异常(如微信接口维护、运营商短信通道故障)等不可抗力导致的服务中断或延迟,我们不承担违约责任。

第三方链接:本网站可能包含指向第三方网站的链接,该等网站的内容和服务不受我们控制,请您自行甄别风险。

七、 违约责任

如您违反本协议约定,我们有权视情节采取警告、限制功能、暂停服务、注销账号等措施,并保留要求赔偿损失的权利。

如因您的违约行为导致我们遭受行政处罚、第三方索赔或商誉损失,您应承担全部赔偿责任(包括但不限于罚款、赔偿金、律师费、公证费等)。

八、 法律适用与争议解决

本协议的订立、执行和解释均适用中华人民共和国大陆地区法律。

因本协议产生的或与本协议有关的任何争议,双方应友好协商解决;协商不成的,任何一方均可向【公司所在地】有管辖权的人民法院提起诉讼。

九、 其他

本协议构成双方就本服务达成的完整协议,取代此前任何口头或书面约定。

本协议任一条款被认定为无效或不可执行的,不影响其他条款的效力。

我们对本协议享有最终解释权,并在法律允许的范围内保留随时修改的权利。修改后的协议一经公布即生效,继续使用服务即视为同意修订内容。


已查阅