|
最近RAG领域出了比较火的Graph RAG,相继微软也开源了他们的Graph RAG方案,香港大学也输出了比微软Graph RAG更简洁的方案LightRAG;LightRAG整体效能都很优秀,老顾今天就来分析一下LightRAG的实现原理,以及源码分析 传统RAG(Retrieval-Augmented Generation)和GraphRAG都是结合检索和生成的技术,但它们在结构和应用上有所不同。 传统RAG: -结构:传统RAG结合了信息检索和生成模型。通常是先使用检索模块从一个大型文档集合中找到相关信息,然后由生成模块生成自然语言结果。生成过程会参考检索到的文档。 -优点:能够快速地从大量信息中找到相关内容,并通过生成模型给出流畅、自然的回答。 -限制: ---缺乏复杂关系建模:传统RAG主要关注直接检索到的文档,而不擅长处理信息之间的复杂关系和网络结构。 ---上下文局限性:对于需要在不同资源之间进行深层次关联和推理的问题,以及总结概要,传统RAG可能表现不佳。
GraphRAG: -结构:GraphRAG在传统RAG架构中引入了图结构,使其能够更好地表示信息之间的关系。通过图模型,GraphRAG可以捕捉和分析多个文档之间的复杂连接和依赖。 -优势: ---关系建模:能够处理更复杂的查询,特别是涉及多个层次和节点之间关系的问题。 ---更强的推理能力:在信息需要连接或推理时,GraphRAG可以比传统RAG更有效地提供相关性高的答案。
传统RAG场景限制: 传统RAG在以下场景可能表现不佳: 1. 复杂推理:需要跨多个文档进行复杂信息推理的场景。 2. 关系网络:在信息之间需要建立图结构关系才能解决的问题。 3. 非线性关系:需要分析和理解多个条目之间的非线性关系时,传统RAG可能会受到限制。 4. 动态信息更新:信息网络频繁更新、必须实时分析变化的场景中,对于保持上下文的准确性可能会有挑战。 通过引入图结构,GraphRAG可以更好地处理这些限制,提供更复杂信息连接的理解和生成能力。
下面老顾就来介绍一下,LightRAG的原理以及源码分析 1、建设索引流程 把文档信息导入到图库、向量库 
2、查询检索流程 根据用户的查询,结合图库和向量库,提供了4种查询方式 
1、获得文档分段 利用tiktoken工具,采用gpt-4o模型进行分词,集合参数max-size,overlap-size 
把分段集合放到了inserting_chunks变量中。系统会把这些分段存储到kv_store_text_chunks.json文件中。 2、插入文档分段向量表 awaitself.chunks_vdb.upsert(inserting_chunks) chunks_vdb为向量数据库对象,此对象会对应vdb_chunks.json向量文件。即把分段的向量化存储到此文件中 3、大模型提取点和边 
上面代码中整体逻辑就是通过大模型提取点和边,然后存储到相关文件中 knowledge_graph_inst为图库对象,会把点和关系存储到graph_chunk_entity_relation.graphml文件中 entity_vdb为点向量对象,会把点的相关总结概要转换为向量存储到vdb_entities.json文件中 relationships_vdb为边向量对象,会把边的相关总结概要转换为向量存储到vdb_relationships.json文件中
4、大模型提取点和边的提示词 上面是利用大模型提取点和边,那他的提示词是什么呢?我们来看一下,因为提示词太长了,我们截取核心内容看一下
-目标-给定可能与此活动相关的文本文档和实体类型列表,从文本中识别出这些类型的所有实体以及所识别实体之间的所有关系。
-步骤-1.识别所有实体。对于每个已识别的实体,提取以下信息:-entity_name:实体名称,大写-entity_type:以下类型之一:〔{entity_type}〕-entity_description:对实体属性和活动的全面描述将每个实体格式化为(“实体”{tuple_delimiter}<entity_name>{tuple-delimiter}<实体类型>{tuple _delimiter]<实体描述>
2.从步骤1中识别的实体中,识别彼此*明显相关*的所有对(source_entity、target_entity)。对于每对相关实体,提取以下信息:-source_entity:源实体的名称,如步骤1中所标识的-target_entity:目标实体的名称,如步骤1中所标识的-relationship_description:解释为什么你认为源实体和目标实体是相互关联的-relationship_strength:一个数字分数,表示源实体和目标实体之间关系的强度-关系关键字:一个或多个高级关键字,总结关系的总体性质,侧重于概念或主题,而不是具体细节将每个关系格式化为(“关系”{tuple_delimiter}<source_entity>{tuple-delimiter}<target_entity>{tuple.delimiter}>关系描述>{tule_delimiter}<关系关键字>{tuple _delimiter]>关系强度>)
3.识别概括整篇文章主要概念、主题或主题的高级关键字。这些应该捕捉到文件中存在的总体想法。将内容级关键字格式化为(“content_keywords”{tuple_delimiter}<high_level_keywords>)
4.返回英文输出,作为步骤1和2中识别的所有实体和关系的单个列表。使用**{record_delimiter}**作为列表分隔符。
5.完成后,输出{completion_definer}上面定义了实体(点)和关系(边),依据大模型给我们返回相对应的格式
上面还有些变量以及分隔符变量 {entity_type}:这个定义我们的实体点有哪些类型,大模型根据这些类型帮我们提取实体{tuple_delimiter}:这个是具个实体或关系里面属性之间的分隔符,程序里面采用变量PROMPTS["DEFAULT_TUPLE_DELIMITER"],默认值"<|>"{record_delimiter}:这个是实体或关系各个之间的分隔符下面是提示词中,举的例子一
示例1:Entity_types:[人、技术、使命、组织、地点]文本:当亚历克斯咬紧牙关时,在泰勒威权主义确定性的背景下,沮丧的嗡嗡声变得沉闷起来。正是这种竞争性的暗流让他保持警惕,他和乔丹对发现的共同承诺是对克鲁兹狭隘的控制和秩序视野的无声反抗。
然后泰勒做了一件出乎意料的事。他们在约旦旁边停了下来,有一会儿,带着一种近乎敬畏的神情观察着这个装置。“如果这项技术能够被理解……”泰勒说,他们的声音更安静了,“它可能会改变我们的游戏规则。对我们所有人来说。”
早些时候的潜在解雇似乎动摇了,取而代之的是对他们手中事情的严重性的勉强尊重。乔丹抬头一看,他们的心跳转瞬即逝,目光锁定在泰勒身上,一种无言的意志冲突软化成了一种不安的休战。
这是一个很小的转变,几乎无法察觉,但亚历克斯内心点了点头。他们都是通过不同的途径被带到这里的################输出:(“实体”{tuple_delimiter}“亚历克斯”{tule_delimiter]“人”{tuple _delimiter}Alex是一个经历挫折的角色,他对其他角色的动态非常敏锐。){record_delimitr}(“实体”{tuple_delimiter}“泰勒”{tule_delimiter]“人”{tuple _delimiter}Taylor被描绘成具有权威性的确定性,并对设备表现出敬畏的时刻,表明视角发生了变化。){record_delimitr}(“实体”{tuple_delimiter}“Jordan”{tule_delimiter]“人”{tuple _delimiter}Jordan与Taylor在设备方面有着共同的发现承诺和重要的互动。){record_delimitr}(“实体”{tuple_delimiter}“Cruz”{tule_delimiter}“人”{tuple _delimiter}“Cruz与控制和秩序的视觉相关联,影响其他角色的动态。”){record_delimiter](“实体”{tuple_delimiter}“设备”{tule_delimiter]“技术”{tuple _delimiter}-“设备是故事的核心,具有潜在的改变游戏规则的意义,并受到泰勒的尊敬。”){record_delimiter}(“关系”{tuple_delimiter}“Alex”{tule_delimiter]“Taylor”{tuple _delimiter}Alex受到Taylor权威确定性的影响,并观察到Taylor对设备态度的变化{tuple_delimiter}7){record_delimiter}(“关系”{tuple_delimiter}“亚历克斯”{tule_delimiter}Jordan“{tuple-delimiter}”亚历克斯和乔丹共同致力于发现,这与克鲁兹的愿景形成鲜明对比{tuple_delimiter}6){record_delimiter}(“关系”{tuple_delimiter}“Taylor”{tule_delimiter]“Jordan”{tuple _delimiter}“Taylor和Jordan直接就设备进行交互,导致相互尊重和不安的休战时刻{tuple_delimiter}8){record_delimiter}(“关系”{tuple_delimiter}“Jordan”{tule_delimiter]“Cruz”{tuple _delimiter}Jordan对发现的承诺是对Cruz控制和秩序愿景的反叛{tuple_delimiter}5){record_delimiter}(“关系”{tuple_delimiter}“Taylor”{tule_delimiter]“设备”{tuple _delimiter}Taylor对设备表示尊敬,表明其重要性和潜在影响{tuple_delimiter}9){record_delimiter}(“content_keywords”{tuple_delimiter}“权力动态、意识形态冲突、发现、反叛”){completion_delimiter}
再举个例子二
示例2:
Entity_types:[人、技术、使命、组织、地点]文本:他们不再仅仅是特工;他们成了门槛的守护者,守护着来自星条旗之外的世界的信息。他们使命的提升不能受到法规和既定协议的束缚,这需要一个新的视角、新的决心。
当与华盛顿的沟通在后台嗡嗡作响时,紧张气氛在嘟嘟声和静止声的对话中蔓延开来。队伍站着,一股不祥的气息笼罩着他们。很明显,他们在接下来的几个小时里做出的决定可能会重新定义人类在宇宙中的地位,或者使他们陷入无知和潜在的危险之中。
他们与恒星的联系得到了巩固,该小组开始解决这一具体的警告,从被动的接受者转变为积极的参与者。Mercer的后一种本能占据了上风——该团队的任务已经演变,不再仅仅是观察和报告,而是互动和准备。一场蜕变已经开始,行动:杜尔塞以他们新发现的大胆的频率哼着歌,这不是世俗的基调#############输出:(“实体”{tuple_delimiter}“华盛顿”{tule_delimiter]“位置”{tuple _delimiter}Washington是接收通信的位置,表明其在决策过程中的重要性(“实体”{tuple_delimiter}“操作:Dulce”{tule_delimiter}'任务“{tuple-delimiter}”操作:Ducce被描述为一个已经进化到相互作用和准备的任务,表明目标和活动发生了重大转变。){record_delimitr}(“实体”{tuple_delimiter}“团队”{tule_delimiter]“组织”{tuple _delimiter}}“团队被描绘成一组从被动观察者转变为任务中的积极参与者的个人,他们的角色发生了动态变化。”){record_delimiter}(“关系”{tuple_delimiter}“团队”{tule_delimiter}Washington“{tuple-delimiter}团队收到来自华盛顿的通信,这会影响他们的决策过程{tuple_delimiter}7){record_delimiter}(“关系”{tuple_delimiter}“团队”{tule_delimiter]“操作:Dulce”{tuple _delimiter}'团队直接参与操作:Dulce,执行其进化的目标和活动。“{tuple-delimiter}”“任务进化,积极参与”{tuple_delimiter}9){补全_分隔符}(“content_keywords”{tuple_delimiter}“任务演变、决策、积极参与、宇宙意义”){completion_delimiter}
5、具体执行后大模型返回的值 我们拿了三国演义的第一回的内容进行分段,给大模型进行提取;下面是大模型返回的值(只截取部分数据) "e1546ef55aae150f342ad3f0617228ec":{"return":"(\"entity\"<|>\"朝廷\"<|>\"organization\"<|>\"朝廷referstothecentralgovernmentoftheHandynasty,whichisinvolvedinsendingofficialsandmakingdecisionsaboutmilitaryleadership.\")##(\"entity\"<|>\"黄门左丰\"<|>\"person\"<|>\"黄门左丰isanofficialsentbythe朝廷toinspectanddemandbribes,leadingtosubsequentconflicts.\")##(\"entity\"<|>\"董卓\"<|>\"person\"<|>\"董卓isamilitarygeneralsentbythe朝廷toreplacethecurrentcommanderandbringhimbacktothecapitalforpunishment.\")##(\"entity\"<|>\"张飞\"<|>\"person\"<|>\"张飞isacharacterknownforhisquicktemperandbravery,wantingtorescue卢植andshowingangertowards董卓.\")##(\"entity\"<|>\"玄德\"<|>\"person\"<|>\"玄德,alsoknownas刘备,isawiseandstrategicleaderwhoadvisescalmnessandfollowsthesituation'sdevelopments.\")##(\"entity\"<|>\"关公\"<|>\"person\"<|>\"关公,alsoknownas关羽,isaloyalandskilledwarriorwhosuggestsreturningto涿郡whentheirleaderisarrested.\")##(\"entity\"<|>\"卢植\"<|>\"person\"<|>\"卢植isamilitarycommanderwhoisarrestedby董卓andwhosesituationpromptsactionfrom张飞and玄德.\")##(\"entity\"<|>\"张角\"<|>\"person\"<|>\"张角isaleaderofthe黄巾rebellion,identifiedbyhistitle‘天公将军’andengagedinbattlewith董卓.\")##(\"entity\"<|>\"涿郡\"<|>\"geo\"<|>\"涿郡isageographicallocationwhere玄德andhiscompanionsdecidetoreturnaftertheirleaderisarrested.\")##(\"entity\"<|>\"汉军大败\"<|>\"event\"<|>\"汉军大败referstothesignificantdefeatoftheHandynasty'sarmyby张角的forces,leadingtoacriticalsituation.\")##(\"relationship\"<|>\"黄门左丰\"<|>\"朝廷\"<|>\"黄门左丰isanofficialsentbythe朝廷,representingthegovernment'sauthorityanddemands.\"<|>\"officialdelegation,corruption\"<|>8)##(\"relationship\"<|>\"董卓\"<|>\"朝廷\"<|>\"董卓isappointedbythe朝廷toreplacethecurrentcommander,showingthegovernment'sinterventioninmilitaryaffairs.\"<|>\"militarycommand,politicalintervention\"<|>9)##(\"relationship\"<|>\"张飞\"<|>\"卢植\"<|>\"张飞wantstorescue卢植,indicatingastrongsenseofloyaltyandurgency.\"<|>\"loyalty,rescueattempt\"<|>10)##(\"relationship\"<|>\"玄德\"<|>\"董卓\"<|>\"玄德interactswith董卓,experiencingdisrespectandconflict,highlightingthesocialdynamicsandtension.\"<|>\"socialconflict,disrespect\"<|>7)##(\"relationship\"<|>\"关公\"<|>\"涿郡\"<|>\"关公suggestsreturningto涿郡,showingstrategicthinkingandasenseofbelonging.\"<|>\"strategicretreat,hometerritory\"<|>6)##(\"relationship\"<|>\"张角\"<|>\"汉军大败\"<|>\"张角leadstheforcesthatdefeattheHanarmy,signifyinghismilitaryprowessandtheseverityoftherebellion.\"<|>\"militaryvictory,rebellion\"<|>9)##(\"content_keywords\"<|>\"militaryconflict,politicalintrigue,rebellion,loyalty,strategicretreat\")<|COMPLETE|>","model":"glm-4-plus"},从上面的大模型返回的格式发现,实体点以entity开头,关系边以relationship开头。 关系边relationship最后面会有个此边的权重数值。
根据上面大模型返回的值,就是建立图谱关系,存储到graph_chunk_entity_relation.graphml中;再把对应的实体点和关系边里面的总结概要向量化存储到各自的vdb_entities.json和vdb_relationships.json 在处理上面的返回值时,系统还会进行重复节点的合并逻辑,主要是把总结概要合在一起,再交给大模型进行提炼总结 以上就把文档里面的内容建设了图谱和向量了,下面我们在分析一下是怎么查询的 naive search只是针对向量库进行查询 #在vdb_chunks.json中进行向量查询,获得文档分段idresults = await chunks_vdb.query(query, top_k=query_param.top_k)
chunks_ids = [r["id"] for r in results]#根据担当分段id,获得文档分段内容chunks = await text_chunks_db.get_by_ids(chunks_ids)#maybe_trun_chunks 是进行token的大小截取,防止提交给大模型的token太大section = "--New Chunk--\n".join([c["content"] for c in maybe_trun_chunks])
#简单的检索提示词sys_prompt_temp = PROMPTS["naive_rag_response"]
#把查询出来的分段内容,赋值到提示词中sys_prompt = sys_prompt_temp.format(content_data=section, response_type=query_param.response_type)
#调用大模型,结合查出来的分段内容;进行回答问题response = await use_model_func(query,system_prompt=sys_prompt,)
我们再看一下PROMPTS["naive_rag_response"]提示词是什么内容 You'reahelpfulassistantBelowaretheknowledgeyouknow:{content_data}---Ifyoudon'tknowtheansweroriftheprovidedknowledgedonotcontainsufficientinformationtoprovideananswer,justsayso.Donotmakeanythingup.Generatearesponseofthetargetlengthandformatthatrespondstotheuser'squestion,summarizingallinformationintheinputdatatablesappropriatefortheresponselengthandformat,andincorporatinganyrelevantgeneralknowledge.Ifyoudon'tknowtheanswer,justsayso.Donotmakeanythingup.Donotincludeinformationwherethesupportingevidenceforitisnotprovided.---Targetresponselengthandformat---{response_type}"""整体流程如下: 
local search是会查出相关联的实体点,在根据实体点关联的边和邻居实体点,进行相结合出来的文档,提交给大模型进行回答问题 步骤一(解析查询内容的high与low关键词) 先通过大模型进行查询内容的解析,分析出low_level_keywords和high_level_keywords low_levle_keywords:表示具体的关键词,侧重于特定实体、细节或具体术语 high_level_keywords:表示相对总结归纳的关键词,侧重于总体概念或主题 kw_prompt_temp=PROMPTS["keywords_extraction"]kw_prompt=kw_prompt_temp.format(query=query)result=awaituse_model_func(kw_prompt) 我们来看看PROMPTS["keywords_extraction"]提示词是什么(截取核心内容) ---角色---您是一个乐于助人的助手,负责识别用户查询中的高级和低级关键字。---目标---给定查询,列出高级和低级关键字。高级关键字侧重于总体概念或主题,而低级关键字侧重于特定实体、细节或具体术语。---使用说明----以JSON格式输出关键字。-JSON应该有两个键:-“high_level_keywords”用于概括概念或主题。-“low_level_keywords”用于特定实体或详细信息的描述 提示词里面的例子 示例1:问:“国际贸易如何影响全球经济稳定?”################输出:{{“high_level_keywords”:[“国际贸易”、“全球经济稳定”、“经济影响”],“low_level_keywords”:[“贸易协定”、“关税”、“货币兑换”、“进口”、“出口”]}}#############################示例2:问题:“森林砍伐对生物多样性的环境影响是什么?”################输出:{{“high_level_keywords”:[“环境后果”、“森林砍伐”、“生物多样性丧失”],“low_level_keywords”:[“物种灭绝”、“栖息地破坏”、“碳排放”、“雨林”、“生态系统”]}}步骤二(利用具体的low关键字进行检索) 检索出实体点 local search利用low_level_keywords,对实体点vdb_entities.json向量库进行检索,代码如下
#对low关键字进行实体点向量的查询results=awaitentities_vdb.query(low_keywords,top_k=query_param.top_k)对检索出来的实体名,进行图库的查询#找出图库中的相关查询的实体点node_datas=awaitasyncio.gather(*[knowledge_graph_inst.get_node(r["entity_name"])forrinresults])
获取每个节点的degree度 degree:表示度数,该节点连接的边的总数 node_degrees=awaitasyncio.gather(*[knowledge_graph_inst.node_degree(r["entity_name"])forrinresults]) 度数是衡量节点重要性、中心性或连接性的基本指标之一。在很多应用中,例如社交网络分析、信息传播、网络结构研究等,节点的度数都发挥着重要作用。可以用来识别关键节点,了解网络的稠密性及形成模式
节点的信息、实体名称和节点度数整合在一起,代码如下 node_datas=[{**n,"entity_name":k["entity_name"],"rank":d}fork,n,dinzip(results,node_datas,node_degrees)ifnisnotNone]步骤三(获得最相关的文档片段) 因为大模型提交有字数token限制,所以要找到比较相关的分段进行提交; 下面就是找到最相关的文档片段,_find_most_related_text_unit_from_entities 何为最相关: 代码中是根据把实体点的第一跳点的邻居实体点找到,如果和邻居实体点拥有相同的chunk_id就认为相关,如果此实体点的邻居实体点很多,且拥有的相同chunk_id,那relation_counts加1,即会按照relation_counts排序
use_text_units=await_find_most_related_text_unit_from_entities(node_datas,query_param,text_chunks_db,knowledge_graph_inst) 第一跳点:表示此实体点-关系边->关联的实体点(这就是第一跳点邻居)
步骤四(获得实体点相关的关系边) 此会按照weight权重排序,以及rank重要等级排序 rank重要等级,此系统是代表关系边的度数(即起始实体点度数+结束实体点度数)
use_relations=await_find_most_related_edges_from_entities(node_datas,query_param,knowledge_graph_inst) 上面代码就把相关的实体点的边中的总结描述进行了获取 步骤五(把实体点信息转换为csv格式) 代码如下: 
形成的案例数据格式如下: 
步骤六(把实体点相关的边的信息转换为csv格式) 代码如下: 
形成的案例数据格式如下: 
步骤七(把相关的文档片段的信息转换为csv格式) 代码如下: 
形成的案例数据格式如下: 

步骤八(组装大模型中的提示词内容) 代码如下: 
格式如下:(截图部分) 


步骤九(提交大模型) 代码如下: 
我们看看PROMPTS["rag_response"]提示词 --角色---您是一位乐于助人的助手,可以回答有关所提供表格中数据的问题。---目标---生成一个目标长度和格式的响应,以响应用户的问题,总结输入数据表中与响应长度和格式相对应的所有信息,并纳入任何相关的一般知识。如果你不知道答案,就直说吧。不要编造任何东西。不要包括没有提供支持证据的信息。---目标响应长度和格式---{response_type}---数据表---{context_data}根据长度和格式,在回复中添加适当的章节和评论。用markdown标记回复的样式。 response_type:Multiple Paragraphs(多个段落) context_data:就是步骤八组装出来的信息 到此local search的整体流程就结束了,总结一句话 就是找到相关的实体点,把实体点的信息和关系边的信息,以及相关的文档片段 组装起来,提交给大模型
整体流程如下图: 
步骤一(解析查询内容的high与low关键词) 跟local search的步骤一相同,获取high和low关键词,但global是根据high关键词进行检索 步骤二(利用具体的high关键字进行检索) 检索出实体点 global search利用high_level_keywords,对关系边vdb_relationships.json向量库进行检索,代码如下
#对high关键字进行实体点向量的查询results = await relationships_vdb.query(keywords, top_k=query_param.top_k)
对检索出来的关系边,进行图库的查询 #找出图库中的相关查询的关系边edge_datas=awaitasyncio.gather(*[knowledge_graph_inst.get_edge(r["src_id"],r["tgt_id"])forrinresults])
获取每个关系边的degree度 边degree:表示起始实体点的度数加上结束实体点的度数
edge_degree=awaitasyncio.gather(*[knowledge_graph_inst.edge_degree(r["src_id"],r["tgt_id"])forrinresults])
把边的信息、边度数整合在一起,并排序,再压缩符合token大小,代码如下 edge_datas=[{"src_id":k["src_id"],"tgt_id":k["tgt_id"],"rank":d,**v}fork,v,dinzip(results,edge_datas,edge_degree)ifvisnotNone]edge_datas=sorted(edge_datas,key=lambdax x["rank"],x["weight"]),reverse=True)edge_datas=truncate_list_by_token_size(edge_datas,key=lambdax:x["description"],max_token_size=query_param.max_token_for_global_context,)
步骤三(获得最相关的实体点) 下面就是找到最相关的文档片段,_find_most_related_entities_from_relationships 找到相关的实体点,以及点的度数,再进行token压缩 

步骤四(获得最相关的文档片段) 下面就是找到相关的文档片段,_find_related_text_unit_from_relationships,并排序,在token压缩 

步骤五(把相关的文档片段的信息转换为csv格式) 这个和local一样,把获得的信息转换为csv格式 步骤六(组装大模型中的提示词内容) 和local一样,代码如下: 
步骤七(提交大模型) 和local一致,提示词模板也一样,代码如下: 
到此global search的整体流程就结束了,总结一句话 就是找到相关的关系边,把关系边的信息和关系相关实体点的信息,以及相关的文档片段 组装起来,提交给大模型
整体流程如下图: 
起始就是利用low进行local search 和 high 进行 global search,合在一起,怎么合在一起的呢? 系统比较简单,就是在提示词那边进行合并;把local 和 global最终的提示词,进行合并,代码片段如下: 
最终2个提示词组装在一起 
后续再提交大模型,提示与local一样 
到此hybrid search的整体流程就结束了,总结一句话 就是整合了local search 和 global search,进行查询
整体流程如下图: 
到此为止LightRAG分析就结束了,大家可以下载源码体验一下,后续老顾会出相关视频,结合案例给大家进行分享,谢谢
|