class RaptorRetriever(BaseRetriever):
...
async def insert(self, documents: List[BaseNode]) -> None:
"""Given a set of documents, this function inserts higher level of abstractions within the index.
For later retrieval
Args:
documents (List[BaseNode]): List of Documents
"""
embed_model = self.index._embed_model
transformations = self.index._transformations
cur_nodes = run_transformations(documents, transformations, in_place=False)
for level in range(self.tree_depth):
# get the embeddings for the current documents
if self._verbose:
print(f"Generating embeddings for level {level}.")
embeddings = await embed_model.aget_text_embedding_batch(
[node.get_content(metadata_mode="embed") for node in cur_nodes]
)
assert len(embeddings) == len(cur_nodes)
id_to_embedding = {
node.id_: embedding for node, embedding in zip(cur_nodes, embeddings)
}
if self._verbose:
print(f"Performing clustering for level {level}.")
# cluster the documents
nodes_per_cluster = get_clusters(cur_nodes, id_to_embedding)
if self._verbose:
print(
f"Generating summaries for level {level} with {len(nodes_per_cluster)} clusters."
)
summaries_per_cluster = await self.summary_module.generate_summaries(
nodes_per_cluster
)
if self._verbose:
print(
f"Level {level} created summaries/clusters: {len(nodes_per_cluster)}"
)
# replace the current nodes with their summaries
new_nodes = [
TextNode(
text=summary,
metadata={"level": level},
excluded_embed_metadata_keys=["level"],
excluded_llm_metadata_keys=["level"],
)
for summary in summaries_per_cluster
]
# insert the nodes with their embeddings and parent_id
nodes_with_embeddings = []
for cluster, summary_doc in zip(nodes_per_cluster, new_nodes):
for node in cluster:
node.metadata["parent_id"] = summary_doc.id_
node.excluded_embed_metadata_keys.append("parent_id")
node.excluded_llm_metadata_keys.append("parent_id")
node.embedding = id_to_embedding[node.id_]
nodes_with_embeddings.append(node)
self.index.insert_nodes(nodes_with_embeddings)
# set the current nodes to the new nodes
cur_nodes = new_nodes
self.index.insert_nodes(cur_nodes)
# Assuming you have a text node
text_node = cur_nodes[0]
# Access the original document reference
original_document_info = text_node.relationships[NodeRelationship.SOURCE]
# You can now use this information to track back to the original document
print(f"Original Document ID: {original_document_info.node_id}")
8条答案
按热度按时间ev7lccsx1#
嘿 @silviachen46!我是一个设计来帮助你解决bug、问题和成为贡献者的机器人。我在等待人类维护者时会协助你。
是的,在RAPTOR构建后添加新文档是可能的。
RaptorRetriever
类有一个insert
方法,允许将新文档插入索引。此方法可用于向现有索引添加新文档,从而实现RAPTOR结构的动态更新。如果你想构建一个将新文档作为叶子节点插入的新功能,你可能会发现现有的
insert
方法作为参考很有帮助。该方法通过转换处理文档、生成嵌入、执行聚类并将带有嵌入和父ID的节点插入索引。你可以调整这个过程,以确保新文档被作为叶子节点添加。以下是来自
insert
方法的相关代码片段:你可以修改这个方法,通过关注转换和嵌入过程的初始阶段,然后直接将它们插入索引,而不进行进一步的聚类或汇总,以确保新文档被作为叶子节点添加。
ztmd8pv52#
@dosu,这行代码
cur_nodes = run_transformations(documents, transformations, in_place=False)
的作用是什么?它是一个SentenceSplitter(chunk_size=400, chunk_overlap=50),将文档分割成小块并将它们封装到叶子文本节点中吗?e5nszbig3#
在
RaptorRetriever
类的insert
方法中,对documents
应用了一系列转换。这些转换在transformations
列表中定义,并应用于documents
以生成一个新的节点列表,该列表存储在cur_nodes
中。是的,它可以使用类似于
SentenceSplitter(chunk_size=400, chunk_overlap=50)
的东西将文档分割成更小的部分,并将它们封装到叶子文本节点中。从提供的上下文可以看出,SentenceSplitter
是一种可以将文本分割成块的转换类型。以下是如何将
SentenceSplitter
作为转换使用的示例:这段代码将把
documents
分割成大小为 400,重叠为 50 的较小块,结果节点将存储在cur_nodes
1(https://github.com/run-llama/llama_index/blob/main/docs/docs/module_guides/loading/ingestion_pipeline/transformations.md)2(https://github.com/run-llama/llama_index/blob/main/docs/docs/understanding/loading/loading.md)3(https://github.com/run-llama/llama_index/blob/main/llama-index-packs/llama-index-packs-raptor/llama_index/packs/raptor/base.py) 中。vsmadaxz4#
@dosu 这里的in_place布尔值有什么作用?它是否意味着我们创建了原始文档的副本并在它们上面进行拆分?
如果从文本节点中检索到原始文档,我是否能够跟踪它们?如果是,请提供一个示例说明如何做到这一点?
q3qa4bjr5#
@dosu 在这里返回的curr_nodes是一个文本节点列表,每个文本节点都包含了整个文档,但在被分割后。或者每个文本节点都包含了一个文档的一部分?
uhry853o6#
in_place
布尔值在run_transformations
函数中确定转换是否应修改原始节点列表或创建副本。当in_place
设置为False
时,原始节点列表的副本将被创建,并将转换应用于此副本,而不修改原始列表。是的,您将能够从每个文本节点跟踪原始文档。每个文本节点通过
relationships
属性包含对原始文档的引用,具体使用NodeRelationship.SOURCE
。以下是如何从文本节点跟踪原始文档的示例:
关于
cur_nodes
的结构,cur_nodes
中的每个文本节点仅包含一个文档的部分,而不是整个文档。这种分块是通过转换(如SentenceSplitter
)完成的,该转换将文档分割成更小的部分。以下是
build_nodes_from_splits
函数中与每个文本节点如何从原始文档的部分创建相关的代码片段:这确保每个文本节点仅包含原始文档的一部分,并为了跟踪目的保持对源文档的引用 1(https://github.com/run-llama/llama_index/blob/main/llama-index-legacy/llama_index/legacy/node_parser/node_utils.py)2(https://github.com/run-llama/llama_index/blob/main/llama-index-core/llama_index/core/node_parser/node_utils.py)3(https://github.com/run-llama/llama_index/blob/main/llama-index-packs/llama-index-packs-code-hierarchy/llama_index/packs/code_hierarchy/code_hierarchy.py)。
t8e9dugd7#
@silviachen46 目前尚未实现,主要是因为这样做比较复杂。
如果我来做的话,我可能会跟踪自从上次重新聚类以来插入了多少个文档。在达到某个阈值后,实际上重新对所有内容进行聚类。
如果低于阈值,你只需要将新文档插入到与该文档最匹配的底层聚类中(尽管不太确定如何做到这一点)。
57hvy0tb8#
@silviachen46 目前尚未实现,主要是因为实现起来比较复杂。
如果我要实现的话,我可能会跟踪自从上次重新聚类以来插入了多少个文档。在达到某个阈值后,实际上重新聚类所有内容。如果低于阈值,你只需要将新文档插入到与该文档最匹配的底层聚类中(尽管不太确定如何实现这一点)。
@logan-markewich 感谢!我一直在尝试开发这个功能。与其重新聚类,我正在考虑从叶子节点更改到父节点并一直追溯到根节点进行重新汇总,因为我的情况大约有7000多个文档,重新汇总所有这些文档会很慢且耗费资源。实现起来确实很复杂。