用LLM Graph Builder构建知识图谱实战篇

知识图谱实战应用1-知识图谱的构建与可视化应用 基于知识图谱下的推荐算法是基于知识图谱的基础上,通过利用用户历史行为和知识图谱中的实体之间的关系,从图谱中获取推荐信息的一种推荐算法。关系抽取是指从自然语言文本中抽取实体之间的关系信息的过程,其目的是构建知识图谱中的关系。在知识图谱中,节点和关系之间的关联关系可以用来进行推理,通过使用Neo4j的算法以及自定义的规则,可以根据已有的信息自动推理出新的知识。使用Neo4j,可以对知识图谱中的节点和关系进行各种统计和分析,包括节点的度、中心性、社区检测等,从而可以洞察知识图谱的结构和演化。 阅读详情

前言

​ 在当今信息爆炸的时代,如何有效地将海量的非结构化文本转化为结构化的数据成为一大挑战。

​ 本文深入探讨了LangChain框架中基于大型语言模型(LLM)的LLM图转换器,展示了如何从文本中提取实体和关系,进而构建知识图谱。文章首先介绍了使用Neo4j作为图数据库的环境设置,强调了其内置的图形可视化功能,方便用户直观地理解数据结构。

​ LLM图转换器提供了两种主要模式:基于工具的模式和基于提示的模式。前者利用LLM的结构化输出和函数调用能力,简化了提示工程和自定义解析的需求;后者则在LLM不支持工具时作为回退方案,通过少量示例引导模型进行信息提取。文章进一步阐述了如何定义图谱模式,包括允许的节点类型、关系类型及其属性,从而提高提取的一致性和准确性。

​ 此外,本文还详细介绍了如何将提取的图谱文档导入Neo4j数据库,提供了默认导入、基础实体标签以及包含源文档等多种导入选项。通过实际示例,展示了不同配置下的提取效果和可视化结果。

​ 最后,文章总结了LLM图转换器在构建知识图谱中的优势和应用前景,强调了结构化数据在增强检索生成(RAG)应用和处理复杂多跳查询中的重要性。

深入探讨LangChain使用LLM实现图构建的实现

创建文本图谱令人兴奋,但确实具有挑战性。本质上,这是将非结构化文本转换为结构化数据。虽然这种方法已经存在一段时间,但随着大型语言模型(LLM)的出现,它获得了显著的关注,使其更为主流。

在这里插入图片描述

从文本中提取实体和关系以构建知识图谱。图片由作者提供。

在上图中,您可以看到信息提取如何将原始文本转化为知识图谱。左侧显示了多个文档中关于个人及其与公司的关系的非结构化句子。右侧,这些相同的信息被表示为实体及其连接的图谱,显示谁在不同的组织工作或创立了哪些组织。

但为什么要从文本中提取结构化信息并以图的形式表示呢?一个关键原因是为了支持基于检索增强生成(RAG)的应用。虽然在非结构化文本上使用文本嵌入模型是一种有用的方法,但在回答需要理解多个实体之间的连接或需要进行诸如过滤、排序和聚合等结构化操作的复杂、多跳问题[1]时,这种方法可能会有所不足。通过从文本中提取结构化信息并构建知识图谱,您不仅可以更有效地组织数据,还可以创建一个强大的框架来理解实体之间的复杂关系。这种结构化的方法使得检索和利用特定信息变得更加容易,扩展了您可以回答的问题类型,同时提供了更高的准确性。

大约一年前,我开始使用LLM构建图谱的实验[2],由于兴趣日益增长,我们决定将这一功能集成到LangChain中,作为LLM图转换器[3]。在过去的一年中,我们获得了宝贵的见解并引入了新功能,本文将展示这些内容。

代码可在GitHub[4]上获取。

设置Neo4j环境

我们将使用Neo4j作为底层图存储,它自带开箱即用的图形可视化。最简单的入门方式是使用Neo4j Aura[5]的免费实例,它提供Neo4j数据库的云实例。或者,您也可以通过下载Neo4j Desktop[6]应用程序并创建本地数据库实例来设置Neo4j数据库的本地实例。

from langchain_community.graphs import Neo4jGraph

graph = Neo4jGraph(  
 url="bolt://54.87.130.140:7687",  
 username="neo4j",  
 password="cables-anchors-directories",  
 refresh_schema=False  
)

LLM图转换器

LLM图转换器旨在为使用任何LLM构建图谱提供灵活的框架。由于有许多不同的提供商和模型可用,这项任务远非简单。幸运的是,LangChain介入处理了大部分标准化过程。至于LLM图转换器本身,它就像两个猫堆叠在一件风衣里——能够在两个完全独立的模式下操作。

在这里插入图片描述

LLM图转换器由两种从文本中提取图谱的独立模式组成。图片由用户提供。

LLM图转换器以两种不同的模式运行,每种模式都设计用于在不同场景下使用LLM从文档中生成图谱。

1.基于工具的模式(默认): 当LLM支持结构化输出或函数调用时,此模式利用LLM的内置[7][with_structured_output](https://python.langchain.com/docs/how_to/structured_output/)使用工具[8]。工具规范定义了输出格式,确保以结构化、预定义的方式提取实体和关系。这在图像的左侧展示,显示了节点和关系类的代码。2.基于提示的模式(回退): 在LLM不支持工具或函数调用的情况下,LLM图转换器回退到纯基于提示的方法。这种模式使用少量示例提示来定义输出格式,引导LLM以基于文本的方式提取实体和关系。然后通过自定义函数解析结果,将LLM的输出转换为JSON格式。这个JSON用于填充节点和关系,就像基于工具的模式一样,但这里LLM完全由提示引导,而不是结构化工具。图像的右侧展示了一个示例提示和生成的JSON输出。

这两种模式确保LLM图转换器适应不同的LLM,允许它使用工具直接构建图谱或通过解析基于文本的提示输出构建图谱。

请注意,即使使用支持工具/函数的模型,您也可以通过设置属性ignore_tools_usage=True来使用基于提示的提取。

基于工具的提取

我们最初选择基于工具的方法进行提取,因为它最小化了对广泛的提示工程和自定义解析函数的需求。在LangChain中,with_structured_output方法允许您使用工具或函数提取信息,输出通过JSON结构或Pydantic对象定义。就个人而言,我觉得Pydantic对象更清晰,因此我们选择了它。

我们首先定义一个Node类。

class Node(BaseNode):  
 id: str = Field(..., description="Name or human-readable unique identifier")  
 label: str = Field(..., description=f"Available options are {enum_values}")  
 properties: Optional[List[Property]]

每个节点都有一个id、一个label和可选的properties。为了简洁,我在这里没有包含完整的描述。将id描述为可读的唯一标识符很重要,因为一些LLM倾向于以更传统的方式理解ID属性,如随机字符串或递增整数。相反,我们希望使用实体的名称作为id属性。我们还通过简单地在label描述中列出它们来限制可用标签类型。此外,像OpenAI这样的LLM支持enum参数,我们也使用了它。

接下来,我们看一下Relationship类。

class Relationship(BaseRelationship):  
 source_node_id: str  
 source_node_label: str = Field(..., description=f"Available options are {enum_values}")  
 target_node_id: str  
 target_node_label: str = Field(..., description=f"Available options are {enum_values}")  
 type: str = Field(..., description=f"Available options are {enum_values}")  
 properties: Optional[List[Property]]

这是Relationship类的第二个迭代。最初,我们为源和目标节点使用嵌套的Node对象,但我们很快发现嵌套对象会降低提取过程的准确性和质量。因此,我们决定将源和目标节点展平到单独的字段中,例如source_node_id和source_node_label,以及target_node_id和target_node_label。此外,我们在节点标签和关系类型的描述中定义了允许的值,以确保LLM遵守指定的图谱模式。

基于工具的提取方法使我们能够为节点和关系定义属性。以下是我们用来定义它们的类。

class Property(BaseModel):  
 """A single property consisting of key and value"""  
 key: str = Field(..., description=f"Available options are {enum_values}")  
 value: str

每个Property被定义为一个键值对。虽然这种方法具有灵活性,但也有其局限性。例如,我们无法为每个属性提供唯一的描述,也无法将某些属性定义为必需而其他属性为可选,因此所有属性都被定义为可选。此外,属性不是为每种节点或关系类型单独定义的,而是在所有节点和关系类型之间共享。

我们还实现了一个详细的系统提示[9]来帮助指导提取过程。但在我的经验中,函数和参数描述的影响通常大于系统消息。

不幸的是,目前没有简单的方法来自定义LLM图转换器中函数或参数的描述。

基于提示的提取

由于只有少数商业LLM和LLaMA 3支持原生工具,我们为不支持工具的模型实现了回退。您还可以通过设置ignore_tool_usage=True来切换到基于提示的方法,即使在使用支持工具的模型时也是如此。

大部分基于提示的方法的提示工程和示例由Geraldus Wilsen[10]贡献。

使用基于提示的方法,我们必须在提示中直接定义输出结构。您可以在此处[11]找到完整的提示。在本文中,我们将仅进行高层次的概述。我们首先定义系统提示。

You are a top-tier algorithm designed for extracting information in structured formats to build a knowledge graph. Your task is to identify the entities and relations specified in the user prompt from a given text and produce the output in JSON format. This output should be a list of JSON objects, with each object containing the following keys:

- **"head"**: The text of the extracted entity, which must match one of the types specified in the user prompt.
- **"head_type"**: The type of the extracted head entity, selected from the specified list of types.
- **"relation"**: The type of relation between the "head" and the "tail," chosen from the list of allowed relations.
- **"tail"**: The text of the entity representing the tail of the relation.
- **"tail_type"**: The type of the tail entity, also selected from the provided list of types.

Extract as many entities and relationships as possible.

**Entity Consistency**: Ensure consistency in entity representation. If an entity, like "John Doe," appears multiple times in the text under different names or pronouns (e.g., "Joe," "he"), use the most complete identifier consistently. This consistency is essential for creating a coherent and easily understandable knowledge graph.

**Important Notes**:

- Do not add any extra explanations or text.

在基于提示的方法中,一个关键的区别是我们要求LLM仅提取关系,而不是单独的节点。这意味着我们不会有任何孤立的节点,与基于工具的方法不同。此外,因为缺乏原生工具支持的模型通常表现较差,我们不允许提取任何属性——无论是节点还是关系,以保持提取输出更简单。

接下来,我们为模型添加了几个少量示例。

examples = [  
 {  
 "text": (  
 "Adam is a software engineer in Microsoft since 2009, "  
 "and last year he got an award as the Best Talent"  
 ),  
 "head": "Adam",  
 "head_type": "Person",  
 "relation": "WORKS_FOR",  
 "tail": "Microsoft",  
 "tail_type": "Company",  
 },  
 {  
 "text": (  
 "Adam is a software engineer in Microsoft since 2009, "  
 "and last year he got an award as the Best Talent"  
 ),  
 "head": "Adam",  
 "head_type": "Person",  
 "relation": "HAS_AWARD",  
 "tail": "Best Talent",  
 "tail_type": "Award",  
 },  
 ...  
]

在这种方法中,目前不支持添加自定义的少量示例或额外的指令。唯一的定制方式是通过prompt属性修改整个提示。扩展定制选项是我们正在积极考虑的事项。

接下来,我们将看一下如何定义图谱模式。

定义图谱模式

使用LLM图转换器进行信息提取时,定义图谱模式对于引导模型构建有意义和结构化的知识表示至关重要。一个良好定义的图谱模式指定了要提取的节点和关系类型,以及每个节点和关系的任何属性。这个模式作为蓝图,确保LLM始终如一地提取相关信息,以符合所需的知识图谱结构。

在本文中,我们将使用玛丽·居里的维基百科页面[12]的开头段落进行测试,并在最后添加了一句关于罗宾·威廉姆斯的句子。

from langchain_core.documents import Document

text = """  
Marie Curie, 7 November 1867 – 4 July 1934, was a Polish and naturalised-French physicist and chemist who conducted pioneering research on radioactivity.  
She was the first woman to win a Nobel Prize, the first person to win a Nobel Prize twice, and the only person to win a Nobel Prize in two scientific fields.  
Her husband, Pierre Curie, was a co-winner of her first Nobel Prize, making them the first-ever married couple to win the Nobel Prize and launching the Curie family legacy of five Nobel Prizes.  
She was, in 1906, the first woman to become a professor at the University of Paris.  
Also, Robin Williams.  
"""  
documents = [Document(page_content=text)]

我们还将在所有示例中使用GPT-4o。

from langchain_openai import ChatOpenAI  
import getpass  
import os

os.environ["OPENAI_API_KEY"] = getpass.getpass("OpenAI api key")

llm = ChatOpenAI(model='gpt-4o')

首先,让我们检查在不定义任何图谱模式的情况下,提取过程是如何工作的。

from langchain_experimental.graph_transformers import LLMGraphTransformer

no_schema = LLMGraphTransformer(llm=llm)

现在我们可以使用aconvert_to_graph_documents函数处理文档,该函数是异步的。建议使用异步方式进行LLM提取,因为它允许并行处理多个文档。这种方法可以显著减少等待时间并提高吞吐量,特别是在处理多个文档时。

data = await no_schema.aconvert_to_graph_documents(documents)

LLM图转换器的响应将是一个图谱文档,其结构如下:

[  
 GraphDocument(  
 nodes=[  
 Node(id="Marie Curie", type="Person", properties={}),  
 Node(id="Pierre Curie", type="Person", properties={}),  
 Node(id="Nobel Prize", type="Award", properties={}),  
 Node(id="University Of Paris", type="Organization", properties={}),  
 Node(id="Robin Williams", type="Person", properties={}),  
 ],  
 relationships=[  
 Relationship(  
 source=Node(id="Marie Curie", type="Person", properties={}),  
 target=Node(id="Nobel Prize", type="Award", properties={}),  
 type="WON",  
 properties={},  
 ),  
 Relationship(  
 source=Node(id="Marie Curie", type="Person", properties={}),  
 target=Node(id="Nobel Prize", type="Award", properties={}),  
 type="WON",  
 properties={},  
 ),  
 Relationship(  
 source=Node(id="Marie Curie", type="Person", properties={}),  
 target=Node(  
 id="University Of Paris", type="Organization", properties={}  
 ),  
 type="PROFESSOR",  
 properties={},  
 ),  
 Relationship(  
 source=Node(id="Pierre Curie", type="Person", properties={}),  
 target=Node(id="Nobel Prize", type="Award", properties={}),  
 type="WON",  
 properties={},  
 ),  
 ],  
 source=Document(  
 metadata={"id": "de3c93515e135ac0e47ca82a4f9b82d8"},  
 page_content="\nMarie Curie, 7 November 1867 – 4 July 1934, was a Polish and naturalised-French physicist and chemist who conducted pioneering research on radioactivity.\nShe was the first woman to win a Nobel Prize, the first person to win a Nobel Prize twice, and the only person to win a Nobel Prize in two scientific fields.\nHer husband, Pierre Curie, was a co-winner of her first Nobel Prize, making them the first-ever married couple to win the Nobel Prize and launching the Curie family legacy of five Nobel Prizes.\nShe was, in 1906, the first woman to become a professor at the University of Paris.\nAlso, Robin Williams!\n",  
 ),  
 )  
]

图谱文档描述了提取的nodes和relationships。此外,提取的源文档被添加在source键下。

我们可以使用Neo4j Browser来可视化输出,更清晰、更直观地理解数据。

在这里插入图片描述

在没有定义图谱模式的情况下,对同一数据集进行了两次提取。图片由作者提供。

上图展示了对同一段关于玛丽·居里的段落进行了两次提取。在这种情况下,我们使用了基于工具的提取,这也允许有孤立的节点,如图所示。由于没有定义图谱模式,LLM在运行时确定要提取的信息,这可能导致即使是同一段落的输出也存在差异。因此,有些提取比其他提取更详细,结构可能有所不同。例如,左侧玛丽被表示为诺贝尔奖的WINNER,而右侧她WON诺贝尔奖。

现在,让我们尝试使用基于提示的方法进行相同的提取。对于支持工具的模型,您可以通过设置ignore_tool_usage参数来启用基于提示的提取。

no_schema_prompt = LLMGraphTransformer(llm=llm, ignore_tool_usage=True)  
data = await no_schema.aconvert_to_graph_documents(documents)

同样,我们可以在Neo4j Browser中可视化两次独立的执行。

在这里插入图片描述

使用基于提示的方法,在没有定义图谱模式的情况下,对同一数据集进行了两次提取。图片由作者提供。

使用基于提示的方法,我们不会看到任何孤立的节点。然而,与之前的提取一样,模式在运行之间可能会有所变化,导致相同输入的输出不同。

接下来,让我们详细了解如何定义图谱模式有助于产生更一致的输出。

定义允许的节点

限制提取的图谱结构可以带来很大好处,因为它引导模型专注于特定的相关实体和关系。通过定义清晰的模式,您可以提高提取的一致性,使输出更加可预测并与您实际需要的信息保持一致。这减少了运行之间的变异性,并确保提取的数据遵循标准化结构,捕捉到预期的信息。通过良好定义的模式,模型不太可能忽略关键细节或引入意外的元素,从而生成更干净、更可用的图谱。

我们将通过定义allowed_nodes参数来定义要提取的期望节点类型。

allowed_nodes = ["Person", "Organization", "Location", "Award", "ResearchField"]  
nodes_defined = LLMGraphTransformer(llm=llm, allowed_nodes=allowed_nodes)  
data = await allowed_nodes.aconvert_to_graph_documents(documents)

在这里,我们定义LLM应该提取五种类型的节点,如Person**,Organization,Location,Award和ResearchField。我们在Neo4j Browser中可视化了两次独立的执行以进行比较。

在这里插入图片描述

具有预定义节点类型的两次提取可视化。图片由作者提供。

通过指定期望的节点类型,我们实现了更一致的节点提取。然而,仍可能存在一些变化。例如,在第一次运行中,“radioactivity”被提取为研究领域,而在第二次运行中没有。

由于我们尚未定义关系,其类型也可能在运行之间有所不同。此外,有些提取可能捕获的信息比其他提取多。例如,玛丽和皮埃尔之间的MARRIED_TO关系并不是在所有提取中都存在。

现在,让我们探讨如何定义关系类型以进一步提高一致性。

定义允许的关系

如我们所观察到的,仅定义节点类型仍然允许关系提取的变化。为了解决这个问题,让我们探索如何定义关系。第一种方法是使用可用类型列表来指定允许的关系。

allowed_nodes = ["Person", "Organization", "Location", "Award", "ResearchField"]  
allowed_relationships = ["SPOUSE", "AWARD", "FIELD_OF_RESEARCH", "WORKS_AT", "IN_LOCATION"]  
rels_defined = LLMGraphTransformer(  
 llm=llm,  
 allowed_nodes=allowed_nodes,  
 allowed_relationships=allowed_relationships  
)  
data = await rels_defined.aconvert_to_graph_documents(documents)

让我们再次检查两次独立的提取。

在这里插入图片描述

具有预定义节点和关系类型的两次提取可视化。图片由作者提供。

通过同时定义节点和关系,我们的输出变得显著更一致。例如,玛丽总是被显示为赢得奖项,是皮埃尔的配偶,并在巴黎大学工作。然而,由于关系被定义为一个通用列表,而没有限制它们可以连接哪些节点,因此仍然存在一些变化。例如,FIELD_OF_RESEARCH关系可能出现在Person和ResearchField之间,但有时它将Award与ResearchField连接。此外,由于未定义关系方向,方向一致性可能存在差异。

为了解决无法指定关系可以连接哪些节点以及强制关系方向的问题,我们最近引入了定义关系的新选项,如下所示。

allowed_nodes = ["Person", "Organization", "Location", "Award", "ResearchField"]  
allowed_relationships = [  
 ("Person", "SPOUSE", "Person"),  
 ("Person", "AWARD", "Award"),  
 ("Person", "WORKS_AT", "Organization"),  
 ("Organization", "IN_LOCATION", "Location"),  
 ("Person", "FIELD_OF_RESEARCH", "ResearchField")  
]  
rels_defined = LLMGraphTransformer(  
 llm=llm,  
 allowed_nodes=allowed_nodes,  
 allowed_relationships=allowed_relationships  
)  
data = await rels_defined.aconvert_to_graph_documents(documents)

而不是将关系定义为简单的字符串列表,我们现在使用三元组格式,每个元素分别代表源节点、关系类型和目标节点。

让我们再次可视化结果。

在这里插入图片描述

具有预定义节点和高级关系类型的两次提取可视化。图片由作者提供。

使用三元组方法,跨多次执行提取图谱的模式更加一致。然而,鉴于LLM的性质,提取的细节级别可能仍存在一些变化。例如,在右侧,皮埃尔被显示为赢得诺贝尔奖,而在左侧则缺少此信息。

定义属性

我们可以对图谱模式进行的最后一个增强是定义节点和关系的属性。在这里,我们有两个选项。第一个是将node_properties或relationship_properties设置为true,允许LLM自主决定提取哪些属性。

allowed_nodes = ["Person", "Organization", "Location", "Award", "ResearchField"]  
allowed_relationships = [  
 ("Person", "SPOUSE", "Person"),  
 ("Person", "AWARD", "Award"),  
 ("Person", "WORKS_AT", "Organization"),  
 ("Organization", "IN_LOCATION", "Location"),  
 ("Person", "FIELD_OF_RESEARCH", "ResearchField")  
]  
node_properties=True  
relationship_properties=True  
props_defined = LLMGraphTransformer(  
 llm=llm,  
 allowed_nodes=allowed_nodes,  
 allowed_relationships=allowed_relationships,  
 node_properties=node_properties,  
 relationship_properties=relationship_properties  
)  
data = await props_defined.aconvert_to_graph_documents(documents)  
graph.add_graph_documents(data)

让我们检查结果。

在这里插入图片描述

提取的节点和关系属性。图片由作者提供。

我们已启用LLM添加其认为相关的任何节点或关系属性。例如,它选择包括玛丽·居里的出生和死亡日期、她在巴黎大学担任教授的角色,以及她两次获得诺贝尔奖的事实。这些额外的属性显著丰富了提取的信息。

我们有的第二个选项是定义我们想要提取的节点和关系属性。

allowed_nodes = ["Person", "Organization", "Location", "Award", "ResearchField"]  
allowed_relationships = [  
 ("Person", "SPOUSE", "Person"),  
 ("Person", "AWARD", "Award"),  
 ("Person", "WORKS_AT", "Organization"),  
 ("Organization", "IN_LOCATION", "Location"),  
 ("Person", "FIELD_OF_RESEARCH", "ResearchField")  
]  
node_properties=["birth_date", "death_date"]  
relationship_properties=["start_date"]  
props_defined = LLMGraphTransformer(  
 llm=llm,  
 allowed_nodes=allowed_nodes,  
 allowed_relationships=allowed_relationships,  
 node_properties=node_properties,  
 relationship_properties=relationship_properties  
)  
data = await props_defined.aconvert_to_graph_documents(documents)  
graph.add_graph_documents(data)

属性仅仅被定义为两个列表。让我们看看LLM提取了什么。

在这里插入图片描述

提取的预定义节点和关系属性。图片由作者提供。

出生和死亡日期与之前的提取保持一致。然而,这一次,LLM还提取了玛丽在巴黎大学担任教授的起始日期。

属性确实为提取的信息增添了有价值的深度,尽管在当前实现中存在一些限制:

•属性只能使用基于工具的方法提取。•所有属性都被提取为字符串。•属性只能全局定义,不能按节点标签或关系类型定义。•没有选项来自定义属性描述,以引导LLM进行更精确的提取。

严格模式

如果您认为我们已经完善了一种让LLM完美遵循定义模式的方法,我必须澄清一下。尽管我们在提示工程上投入了大量努力,但让LLM,尤其是性能较低的LLM,完全准确地遵守指令是具有挑战性的。为了解决这个问题,我们引入了一个后处理步骤,称为strict_mode,它会移除任何不符合定义的图谱模式的信息,确保结果更干净、更一致。

默认情况下,strict_mode设置为True,但您可以通过以下代码将其禁用:

LLMGraphTransformer(  
 llm=llm,  
 allowed_nodes=allowed_nodes,  
 allowed_relationships=allowed_relationships,  
 strict_mode=False  
)

关闭严格模式后,您可能会得到不在定义的图谱模式中的节点或关系类型,因为LLM有时会对输出结构进行创造性的处理。

将图谱文档导入图数据库

从LLM图转换器提取的图谱文档可以使用add_graph_documents方法导入到Neo4j等图数据库中,以进行进一步的分析和应用。我们将探索不同的导入选项,以适应不同的用例。

默认导入

您可以使用以下代码将节点和关系导入Neo4j。

graph.add_graph_documents(graph_documents)

这种方法直接从提供的图谱文档中导入所有节点和关系。我们在整个博客文章中都使用了这种方法来查看不同LLM和模式配置的结果。

在这里插入图片描述

默认导入设置。图片由作者提供。

基础实体标签

大多数图数据库支持索引以优化数据导入和检索。在Neo4j中,索引只能为特定的节点标签设置。由于我们可能事先不知道所有的节点标签,我们可以通过使用baseEntityLabel参数为每个节点添加一个辅助基础标签来处理。这种方式,我们仍然可以利用索引进行高效的导入和检索,而不需要为图谱中的每个可能的节点标签设置索引。

graph.add_graph_documents(graph_documents, baseEntityLabel=True)

如前所述,使用baseEntityLabel参数将导致每个节点具有额外的__Entity__标签。

在这里插入图片描述

每个节点使用baseEntityLabel参数获得一个辅助标签。图片由作者提供。

包含源文档

最后一个选项是同时导入提取节点和关系的源文档。这种方法使我们能够跟踪每个实体出现在哪些文档中。您可以使用include_source参数导入源文档。

graph.add_graph_documents(graph_documents, include_source=True)

在检查导入的图谱时,我们应该看到类似以下的结果。

在这里插入图片描述

导入的源文档。图片由作者提供。

在此可视化中,源文档以蓝色突出显示,所有从中提取的实体通过MENTIONS关系连接。这种模式允许您构建利用结构化和非结构化搜索方法的检索器[13]。

总结

在本文中,我们探讨了LangChain的LLM图转换器及其用于从文本构建知识图谱的双模式。基于工具的模式,我们的主要方法,利用结构化输出和函数调用,减少了提示工程并允许属性提取。同时,基于提示的模式在无法使用工具时有用,依赖少量示例来引导LLM。然而,基于提示的提取不支持属性提取,也不会产生孤立的节点。

我们观察到,定义清晰的图谱模式,包括允许的节点和关系类型,可以提高提取的一致性和性能。受限的模式有助于确保输出遵循我们期望的结构,使其更可预测、可靠和适用。无论是使用工具还是提示,LLM图转换器都能实现更有组织、结构化的非结构化数据表示,从而实现更好的RAG应用和多跳查询处理。

代码可在GitHub[14]上获取。您还可以使用Neo4j托管的LLM Graph Builder应用程序在无代码环境中试用LLM图转换器。

​

如何学习大模型

现在社会上大模型越来越普及了,已经有很多人都想往这里面扎,但是却找不到适合的方法去学习。

作为一名资深码农,初入大模型时也吃了很多亏,踩了无数坑。现在我想把我的经验和知识分享给你们,帮助你们学习AI大模型,能够解决你们学习中的困难。

下面这些都是我当初辛苦整理和花钱购买的资料,现在我已将重要的AI大模型资料包括市面上AI大模型各大白皮书、AGI大模型系统学习路线、AI大模型视频教程、实战学习,等录播视频免费分享出来,需要的小伙伴可以扫取。

一、AGI大模型系统学习路线

很多人学习大模型的时候没有方向,东学一点西学一点,像只无头苍蝇乱撞,我下面分享的这个学习路线希望能够帮助到你们学习AI大模型。

在这里插入图片描述

二、AI大模型视频教程

在这里插入图片描述

三、AI大模型各大学习书籍

在这里插入图片描述

四、AI大模型各大场景实战案例

在这里插入图片描述

五、结束语

学习AI大模型是当前科技发展的趋势,它不仅能够为我们提供更多的机会和挑战,还能够让我们更好地理解和应用人工智能技术。通过学习AI大模型,我们可以深入了解深度学习、神经网络等核心概念,并将其应用于自然语言处理、计算机视觉、语音识别等领域。同时,掌握AI大模型还能够为我们的职业发展增添竞争力,成为未来技术领域的领导者。

再者,学习AI大模型也能为我们自己创造更多的价值,提供更多的岗位以及副业创收,让自己的生活更上一层楼。

因此,学习AI大模型是一项有前景且值得投入的时间和精力的重要选择。

探索LLM模型在知识图谱构建与查询领域的实践 1.背景介绍 知识图谱(Knowledge Graph, KG)是一种表示实体、关系和实例的数据结构,它可以帮助计算机理解和推理人类语言中的信息。知识图谱在自然语言处理、人工智能和数据挖掘领域具有广泛的应用,例如问答系统、推荐系统、语义搜索等。然而,知识图谱的构建和维护是一个昂贵的过程,需要大量的人力、时间和资源。因此,研究者们在寻找更高效、智能的方法 阅读详情

相关推荐

WRF后处理避坑指南:从namelist配置到降水计算的全流程解析

本文提供了一份详尽的WRF模式后处理避坑指南,涵盖从理解wrfout文件结构、核心变量解析到累计降水计算与专业气象图绘制的全流程。重点解析了RAINC与RAINNC的正确处理方法,并分享了使用wrf-python库高效绘制位势高度叠加风场图、累计降水图等专业图表的实战技巧与代码,旨在帮助用户规避常见错误,提升后处理效率与图表质量。

weixin_29200485的博客 353

llm-graph-builder——从非结构化数据创建知识图谱

此应用程序旨在将非结构化数据(pdfs, docs, txt, youtube视频, 网页等)转化为存储在Neo4j中的知识图谱。它利用大型语言模型(OpenAI, Gemini等)的强大功能,从文本中提取节点、关系及其属性,并使用Langchain框架创建结构化的知识图谱。从本地计算机、GCS或S3存储桶或网页源上传文件,选择您的LLM模型并生成知识图谱。

m0_59163425的博客 4086

银河麒麟V10离线升级OpenSSH实战:从编译到回滚全流程

OpenSSH作为Linux系统远程登录的核心组件,其版本升级直接关系到系统安全与等保合规。在信创环境下,银河麒麟V10自带的OpenSSH版本往往无法满足漏洞扫描与安全加固要求,而离线编译升级涉及依赖处理、编译参数、服务替换与回滚方案等多个技术环节。本文围绕银河麒麟V10(x86_64)平台,详细讲解OpenSSH离线升级的完整实操流程,包括环境摸底、依赖包安装、源码编译、配置文件兼容性处理、服务重启验证以及常见问题排查,并分享批量升级与安全加固的工程经验,帮助运维人员在不断网、不重装的前提下一次成功完成

weixin_30582943的博客 386

知识图谱实战系列(Python版)

知识图谱实战系列课程旨在帮助同学们快速掌握知识图谱领域核心技术,基于Python各大开源技术实现知识图谱核心应用。通俗讲解核心技术点及其应用领域,全程实战演示如何构建知识图谱生态中各项核心技术。

深入解析MTK DRM初始化流程:从KMS模块到组件框架

本文深入解析了联发科(MTK)平台上DRM显示框架的初始化流程。从内核模块加载、Component框架管理依赖,到核心的mtk_drm_bind函数构建KMS对象(CRTC、Plane、Encoder、Connector),最终完成DRM设备注册并创建用户空间接口。文章结合调试经验,为开发者梳理了从驱动启动到屏幕点亮的完整路径与关键排查点。

onion的博客 767

全球建筑物提取数据集(免费下载):微软/GlobalMLBuildingFootprints

这个是GitHub上的开源数据集: https://github.com/microsoft/GlobalMLBuildingFootprints 介绍 必应地图正在世界各地发布开放式建筑足迹。我们从 2014 年到 2021 年间的 Bing Maps 图像中检测到了 7.77 亿座建筑物,包括 Maxar 和 Airbus 的图像。数据可在 ODbL 下免费下载和使用。该数据集补充了我们的其他版本。 包括的地区 执照 此数据由 Microsoft 根据Open Dat..

此星光明博客 6290

【AI大模型】用LLM Graph Builder构建知识图谱实战篇

本文深入探讨了LangChain框架中基于大型语言模型(LLM)的LLM图转换器,展示了如何从文本中提取实体和关系,进而构建知识图谱。文章首先介绍了使用Neo4j作为图数据库的环境设置,强调了其内置的图形可视化功能,方便用户直观地理解数据结构。

嘴巴吃糖了 1814

SpringAI系列 - MCP篇(二) - MCP Server Boot Starter

此处需注意之前的。

罗小爬的技术宝书 1319

万字解析!用LLM Graph Builder构建知识图谱实战篇

本文深入探讨了LangChain框架中基于大型语言模型(LLM)的LLM图转换器,展示了如何从文本中提取实体和关系,进而构建知识图谱。文章首先介绍了使用Neo4j作为图数据库的环境设置,强调了其内置的图形可视化功能,方便用户直观地理解数据结构。

2401_82469710的博客 2480

R语言层次聚类(hierarchical clustering):数据缩放、PCA聚类结果可视化、fpc包的clusterboot函数通过bootstrap重采样的方法评估hclust层次聚类的稳定性

R语言层次聚类(hierarchical clustering):数据缩放、PCA聚类结果可视化、fpc包的clusterboot函数通过bootstrap重采样的方法评估hclust层次聚类的稳定性

data+scenario+science+insight 835

YOLOv11改进 | 细节涨点篇 | DySample一种最新的轻量化动态上采样算子(效果完爆CARAFE)

本文给大家带来的最新改进机制是一种号称超轻量级且有效的动态上采样器——DySample。与传统的基于内核的动态上采样器相比,DySample采用了一种基于点采样的方法,相比于以前的基于内核的动态上采样器,DySample具有更少的参数、浮点运算次数、GPU内存和延迟。此外,DySample在包括语义分割、目标检测、实例分割、全景分割和单目深度估计在内的五个预测任务中,性能均优于其他上采样器(截至目前最有效的上采样算子),

Snu77的博客 4211

万字解析!用LLM Graph Builder构建知识图谱实战篇,大模型入门到精通,收藏这篇就足够了!

本文深入探讨了LangChain框架中基于大型语言模型(LLM)的LLM图转换器,展示了如何从文本中提取实体和关系,进而构建知识图谱。

2301_81888214的博客 1034

AttributeError: module ‘numpy‘ has no attribute ‘int‘解决方案

本文主要介绍了AttributeError: module ‘numpy’ has no attribute 'int’解决方案,希望能对使用numpy的同学们有所帮助。 文章目录 1. 问题描述 2. 解决方案 2.1 方案一 2.2 方案二

weixin_43178406的博客 28万+

3分钟掌握llm-graph-builder:从零开始构建知识图谱的终极指南

你是否曾想过,如何将杂乱无章的PDF文档、网页内容甚至YouTube视频,一键转化为结构化的知识图谱?🤔 今天我要向你介绍一个强大的开源工具——llm-graph-builder,它能让你在几分钟内完成从非结构化数据到Neo4j知识图谱的神奇转变!无论你是数据分析师、研究者还是开发者,这个工具都将彻底改变你处理信息的方式。 ## 什么是llm-graph-builder? llm-graph

gitblog_00833的博客 756

深度学习工具包 DeepLearningToolbox

深度学习工具包Deprecation notice.-----This toolbox is outdated and no longer maintained.There are much better tools available for deep learning than this toolbox, e.g. [Theano](http://deeplearning.net/software/theano/), [torch](http://torch.ch/) or [tensorflow](http://www.tensorflow.org/)I would suggest you use one of the tools mentioned above rather than use this toolbox.Best, Rasmus.DeepLearnToolbox================A Matlab toolbox for Deep Learning.Deep Learning is a new subfield of machine learning that focuses on learning deep hierarchical models of data.It is inspired by the human brain's apparent deep (layered, hierarchical) architecture.A good overview of the theory of Deep Learning theory is[Learning Deep Architectures for AI](http://www.iro.umontreal.ca/~bengioy/papers/ftml_book.pdf)For a more informal introduction, see the following videos by Geoffrey Hinton and Andrew Ng.* [The Next Generation of Neural Networks](http://www.youtube.com/watch?v=AyzOUbkUf3M) (Hinton, 2007)* [Recent Developments in Deep Learning](http://www.youtube.com/watch?v=VdIURAu1-aU) (Hinton, 2010)* [Unsupervised Feature Learning and Deep Learning](http://www.youtube.com/watch?v=ZmNOAtZIgIk) (Ng, 2011)If you use this toolbox in your research please cite [Prediction as a candidate for learning deep hierarchical models of data](http://www2.imm.dtu.dk/pubdb/views/publication_details.php?id=6284)```@MASTERSTHESIS\{IMM2012-06284, author = "R. B. Palm", title = "Prediction as a candidate for learning deep hierarchical models of data", year = "2012",}```Contact: rasmusbergpalm at gmail dot comDirectories included in the toolbox-----------------------------------`NN/` - A library for Feedforward Backpropagation Neural Networks`CNN/` - A library for Convolutional Neural Networks`DBN/` - A library for Deep Belief Networks`SAE/` - A library for Stacked Auto-Encoders`CAE/` - A library for Convolutional Auto-Encoders`util/` - Utility functions used by the libraries`data/` - Data used by the examples`tests/` - unit tests to verify toolbox is workingFor references on each library check REFS.mdSetup-----1. Download.2. addpath(genpath('DeepLearnToolbox'));Example: Deep Belief Network---------------------```matlabfunction test_example_DBNload mnist_uint8;train_x = double(train_x) / 255;test_x = double(test_x) / 255;train_y = double(train_y);test_y = double(test_y);%% ex1 train a 100 hidden unit RBM and visualize its weightsrand('state',0)dbn.sizes = [100];opts.numepochs = 1;opts.batchsize = 100;opts.momentum = 0;opts.alpha = 1;dbn = dbnsetup(dbn, train_x, opts);dbn = dbntrain(dbn, train_x, opts);figure; visualize(dbn.rbm{1}.W'); % Visualize the RBM weights%% ex2 train a 100-100 hidden unit DBN and use its weights to initialize a NNrand('state',0)%train dbndbn.sizes = [100 100];opts.numepochs = 1;opts.batchsize = 100;opts.momentum = 0;opts.alpha = 1;dbn = dbnsetup(dbn, train_x, opts);dbn = dbntrain(dbn, train_x, opts);%unfold dbn to nnnn = dbnunfoldtonn(dbn, 10);nn.activation_function = 'sigm';%train nnopts.numepochs = 1;opts.batchsize = 100;nn = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.10, 'Too big error');```Example: Stacked Auto-Encoders---------------------```matlabfunction test_example_SAEload mnist_uint8;train_x = double(train_x)/255;test_x = double(test_x)/255;train_y = double(train_y);test_y = double(test_y);%% ex1 train a 100 hidden unit SDAE and use it to initialize a FFNN% Setup and train a stacked denoising autoencoder (SDAE)rand('state',0)sae = saesetup([784 100]);sae.ae{1}.activation_function = 'sigm';sae.ae{1}.learningRate = 1;sae.ae{1}.inputZeroMaskedFraction = 0.5;opts.numepochs = 1;opts.batchsize = 100;sae = saetrain(sae, train_x, opts);visualize(sae.ae{1}.W{1}(:,2:end)')% Use the SDAE to initialize a FFNNnn = nnsetup([784 100 10]);nn.activation_function = 'sigm';nn.learningRate = 1;nn.W{1} = sae.ae{1}.W{1};% Train the FFNNopts.numepochs = 1;opts.batchsize = 100;nn = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.16, 'Too big error');```Example: Convolutional Neural Nets---------------------```matlabfunction test_example_CNNload mnist_uint8;train_x = double(reshape(train_x',28,28,60000))/255;test_x = double(reshape(test_x',28,28,10000))/255;train_y = double(train_y');test_y = double(test_y');%% ex1 Train a 6c-2s-12c-2s Convolutional neural network %will run 1 epoch in about 200 second and get around 11% error. %With 100 epochs you'll get around 1.2% errorrand('state',0)cnn.layers = { struct('type', 'i') %input layer struct('type', 'c', 'outputmaps', 6, 'kernelsize', 5) %convolution layer struct('type', 's', 'scale', 2) %sub sampling layer struct('type', 'c', 'outputmaps', 12, 'kernelsize', 5) %convolution layer struct('type', 's', 'scale', 2) %subsampling layer};cnn = cnnsetup(cnn, train_x, train_y);opts.alpha = 1;opts.batchsize = 50;opts.numepochs = 1;cnn = cnntrain(cnn, train_x, train_y, opts);[er, bad] = cnntest(cnn, test_x, test_y);%plot mean squared errorfigure; plot(cnn.rL);assert(er<0.12, 'Too big error');```Example: Neural Networks---------------------```matlabfunction test_example_NNload mnist_uint8;train_x = double(train_x) / 255;test_x = double(test_x) / 255;train_y = double(train_y);test_y = double(test_y);% normalize[train_x, mu, sigma] = zscore(train_x);test_x = normalize(test_x, mu, sigma);%% ex1 vanilla neural netrand('state',0)nn = nnsetup([784 100 10]);opts.numepochs = 1; % Number of full sweeps through dataopts.batchsize = 100; % Take a mean gradient step over this many samples[nn, L] = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.08, 'Too big error');%% ex2 neural net with L2 weight decayrand('state',0)nn = nnsetup([784 100 10]);nn.weightPenaltyL2 = 1e-4; % L2 weight decayopts.numepochs = 1; % Number of full sweeps through dataopts.batchsize = 100; % Take a mean gradient step over this many samplesnn = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.1, 'Too big error');%% ex3 neural net with dropoutrand('state',0)nn = nnsetup([784 100 10]);nn.dropoutFraction = 0.5; % Dropout fraction opts.numepochs = 1; % Number of full sweeps through dataopts.batchsize = 100; % Take a mean gradient step over this many samplesnn = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.1, 'Too big error');%% ex4 neural net with sigmoid activation functionrand('state',0)nn = nnsetup([784 100 10]);nn.activation_function = 'sigm'; % Sigmoid activation functionnn.learningRate = 1; % Sigm require a lower learning rateopts.numepochs = 1; % Number of full sweeps through dataopts.batchsize = 100; % Take a mean gradient step over this many samplesnn = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.1, 'Too big error');%% ex5 plotting functionalityrand('state',0)nn = nnsetup([784 20 10]);opts.numepochs = 5; % Number of full sweeps through datann.output = 'softmax'; % use softmax outputopts.batchsize = 1000; % Take a mean gradient step over this many samplesopts.plot = 1; % enable plottingnn = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.1, 'Too big error');%% ex6 neural net with sigmoid activation and plotting of validation and training error% split training data into training and validation datavx = train_x(1:10000,:);tx = train_x(10001:end,:);vy = train_y(1:10000,:);ty = train_y(10001:end,:);rand('state',0)nn = nnsetup([784 20 10]); nn.output = 'softmax'; % use softmax outputopts.numepochs = 5; % Number of full sweeps through dataopts.batchsize = 1000; % Take a mean gradient step over this many samplesopts.plot = 1; % enable plottingnn = nntrain(nn, tx, ty, opts, vx, vy); % nntrain takes validation set as last two arguments (optionally)[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.1, 'Too big error');```[![Bitdeli Badge](https://d2weczhvl823v0.cloudfront.net/rasmusbergpalm/deeplearntoolbox/trend.png)](https://bitdeli.com/free "Bitdeli Badge")

温度PID控制PLC程序

温度PID稳定控制程序,适用于欧姆龙PLC,对其他PLC也具有很大参考价值

从混乱数据到知识图谱:3分钟学会用LLM-Graph-Builder构建智能知识库

你是否曾面对海量PDF、网页、视频等非结构化数据感到无从下手?想要将这些杂乱的信息转化为清晰的知识图谱,却苦于技术门槛太高?今天,我将为你介绍一个零基础也能上手的终极解决方案——LLM-Graph-Builder,让你在3分钟内就能将任何文档转化为可视化知识图谱!🚀 ## 项目价值主张:为什么你需要这个工具? 在信息爆炸的时代,我们每天都要处理大量非结构化数据:研究报告、产品文档、会议记录、

gitblog_00731的博客 866

C# WinForm 基于DirectShow.dll编写的程序,可边预览边保存视频,音频和图片

基于微软的DirectShow.dll的二次开发1.自动识别连接的摄像头跟麦克风且可以对其进行选择(默认选中第一个)2.可录制wmv和avi两种格式的视频并且都带有音频,avi可以录制的分辨率和帧数进行选择,wmv默认进行了淸晰度优化如不喜欢可去除代码中有相关提示3.可在预览点开之后进行拍照,也可边录制视频边拍照4.添加了视频和音频的压缩功能(酌情使用)这次二次开发发现C#对DirectShow的相关信息太少,这次做完也不敢藏私共享出来,还望大家指正

从零到一:如何用LLM-Graph-Builder轻松构建知识图谱的终极指南

你是否曾面对海量的PDF文档、网页内容、视频资料感到束手无策?想要从这些非结构化数据中提取有价值的信息,却不知道从何下手?LLM-Graph-Builder正是为解决这一痛点而生的开源工具,它利用大语言模型(LLM)的强大能力,将复杂的非结构化数据转化为直观的知识图谱,让你轻松驾驭信息海洋。 ## 为什么你需要知识图谱构建工具? 在信息爆炸的时代,我们每天都会接触到大量的非结构化数据——PDF

gitblog_00683的博客 709

efm32 入门.pdf

本PDF共计22页,介绍的是操作驱动API,下载与否自己斟酌。

llm-graph-builder与Deepseek集成:国产LLM模型的知识图谱构建实践

在知识图谱构建领域,如何高效利用国产大语言模型(LLM)一直是开发者关注的焦点。llm-graph-builder作为一款强大的知识图谱构建工具,已支持包括Deepseek在内的多种LLM模型,为中文数据处理提供了高效解决方案。本文将详细介绍如何在llm-graph-builder中集成Deepseek模型,实现从非结构化数据到结构化知识图谱的全流程转换。 ## 核心功能与环境准备 llm-g...

gitblog_00377的博客 991

最完整的llm-graph-builder入门指南:从安装到知识图谱可视化

你还在为非结构化数据转化为结构化知识图谱而烦恼?本文将帮助你从零开始部署llm-graph-builder,实现PDF、网页、YouTube视频等多源数据的知识图谱构建与可视化。读完本文你将掌握:本地部署全流程、环境变量配置、数据导入与图谱生成、多模式问答交互。 ## 项目概述 llm-graph-builder是一个基于大语言模型(LLM)和LangChain框架的知识图谱构建工具,支持将P...

gitblog_01007的博客 1170

5个步骤快速构建企业级知识图谱:llm-graph-builder完整指南 [特殊字符]

你是否曾面临海量非结构化数据难以处理的困境?文档、网页、PDF文件堆积如山,却无法从中提取有价值的关联信息?今天,我将为你介绍一款革命性的开源工具——llm-graph-builder,它能将复杂的非结构化数据自动转换为结构化知识图谱,让你轻松构建企业级智能数据平台。 llm-graph-builder是一个基于大型语言模型(LLM)的开源工具,专门用于从非结构化数据中提取实体和关系,并构建存储

gitblog_00468的博客 714

从零开始构建知识图谱:LLM-Graph-Builder完整指南

你是否曾想过将PDF文档、网页内容甚至YouTube视频转化为可视化知识图谱?LLM-Graph-Builder正是这样一个强大的**知识图谱构建**工具,它利用大语言模型的力量,将非结构化数据转化为存储在Neo4j中的结构化知识图谱。本文将为你提供从安装部署到高级功能的全方位指南,帮助你快速掌握这个强大的**AI数据转换**工具。 ## 🚀 为什么选择LLM-Graph-Builder?

gitblog_00686的博客 314

5分钟快速上手:LLM-Graph-Builder知识图谱构建终极指南

你是否曾面对海量的PDF文档、网页内容和视频资料,却难以从中提取结构化知识?LLM-Graph-Builder正是为解决这一痛点而生的强大工具。这个基于大语言模型的智能系统,能够将各种非结构化数据转化为可视化知识图谱,让你轻松掌握复杂信息的脉络关系。无论是学术研究、企业知识管理还是个人学习,都能在这里找到完美的解决方案。 ## 🚀 一键部署:5分钟快速启动 ### Docker一键安装法(推

gitblog_00239的博客 401
上一篇: 大模型爆火的NLP书籍来了!!《掌握NLP:从基础到大语言模型》附PDF,手慢无!
下一篇: 基于LangChain构建安全Agent应用实践(含代码)
AI大模型_学习君
博客等级 码龄3年 6137粉丝 551原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值