作者绘制的图片
我一直努力为团队寻找合适的人才。虽然这是一个令人兴奋的过程,但浏览数百份简历这过程也会让人感到精疲力竭。在这篇博客里,我将分享我如何创建了一个GraphRAG(检索增强生成)的应用程序让我的搜索变得更加轻松的。
> 重要说明: 我得到了候选人的许可,使用AI来分析他们的数据,并承诺不会与任何人分享这些信息。分享回复的数量(不披露个人身份)。
图片由作者创作 —— 只有两名候选人没有获得许可,他们的作品被人工评估了。
建筑这张图由作者提供
每个候选人被要求填写一份表格并上传简历。然后使用微软开源库GraphRAG 构建了知识图和嵌入向量。就像每一个检索器一样,检索到的信息会被送到一个大语言模型(LLM)来给出查询的完整答案。
简历和表格是使用PDF解析工具提取为文本格式的。如何操作的详细说明,请参阅这里:
使用LangChain与PDF聊天变得流行,这篇文章教你如何用大型语言模型 (LLM) 应用程序实现这一功能。https://medium.com/firebird-technologies/chat-with-your-pdfs-using-langchain-e57866b7926d?source=post_page-----279f07cf71d6-------------------------------- 建立知识图谱假设你已经加载并提取了信息,那么第一步就是开始构建RAG图谱。在此之前,你需要确保安装了所有必需的软件包,并完成了所有必要的准备工作。
开始使用 要开始使用GraphRAG系统,您可以选择以下几种方式:👉 使用GraphRAG加速器开始.microsoft.github.io首先,在你的项目文件夹目录下初始化 GraphRAG 索引:
可以运行如下命令:
python -m graphrag.index [--init] [--root 路径 PATH]
该项目将有一个名为settings.yaml
的配置文件。该文件包含了系统如何将内容索引入知识图谱和向量存储的信息。
这里是一些设置的简短说明:
数据块注:此处“Chunks”具体指数据块。若指文本或讲话的片段,请依据具体语境调整翻译。size
int - 以token为单位的最大分块大小。分块越大,每个文档的信息量就越多。overlap
int - 分块之间的token重叠量。更大的重叠量可以使得不同文档之间有更多的上下文交集。
prompt
str - 要使用的提示文件。我会展示如何更改此提示。entity_types
list[str] - 要识别的实体类型。我用这个列表代替了默认的:[组织, 人物, 编程语言, 技术技能]max_gleanings
int - 要使用的最大提炼周期数。提炼周期是指从数据提取到构建整个过程需要重复的次数。
prompt
str - 要使用的提示文件内容。max_length
int - 每次总结的最大输出 token 数。更长的输出会更详细,但也会更贵哦!
enabled
bool - 是否启用声明抽取。我将其设为 true(声明是指任何与信息发现相关的事实或陈述)。
prompt
字符串 - 要使用的提示文件。max_length
整数 - 每份报告的最大输出令牌数。通常设置为2000令牌。
对一些提示信息文件的改动:
实体识别:
-目标-
根据简历文本文档和实体类型列表,从文本中识别出所有这些类型的实体,并找出所有已识别实体之间的关系。
-步骤-
1. 识别所有实体。对于每个识别出的实体,提取以下信息:
- entity_name:实体名,首字母大写
- entity_type:以下类型之一:[person, organization, skillset, programming language, tool]
- entity_description:实体描述
每个实体的格式为 ("entity"{tuple_delimiter}<entity_name>{tuple_delimiter}<entity_type>{tuple_delimiter}<entity_description>)
2. 在步骤1中识别出的所有实体中,识别所有*明显相关*的实体对(source_entity, target_entity)。
对于每一对相关实体,提取以下信息:
- source_entity:在步骤1中识别出的源实体名称
- target_entity:在步骤1中识别出的目标实体名称
- relationship_description:关系描述
- relationship_strength:关系强度
每个关系的格式为 ("relationship"{tuple_delimiter}<source_entity>{tuple_delimiter}<target_entity>{tuple_delimiter}<relationship_description>{tuple_delimiter}<relationship_strength>)
3. 将步骤1和2中识别出的所有实体和关系,用英语返回为单个列表。使用 **{record_delimiter}** 作为列表分隔符。
4. 完成后,输出 {completion_delimiter}
######################
-示例-
######################
示例1:
Entity_types: [person, organization, skillset, programming language, tool]
文本:
John Doe 是 ABC Corp 的软件工程师。他熟练掌握 Python 和 Java,并且还有使用 Docker 和 Kubernetes 的经验。他的技能包括软件开发、系统设计和 DevOps 实践。
################
输出:
("entity"{tuple_delimiter}"John Doe"{tuple_delimiter}"person"{tuple_delimiter}"John Doe 是一名在 ABC Corp 工作的软件工程师。"){record_delimiter}
("entity"{tuple_delimiter}"ABC Corp"{tuple_delimiter}"organization"{tuple_delimiter}"ABC Corp 是 John Doe 的雇主。"){record_delimiter}
("entity"{tuple_delimiter}"Python"{tuple_delimiter}"programming language"{tuple_delimiter}"Python 是 John Doe 精通的一种编程语言。"){record_delimiter}
("entity"{tuple_delimiter}"Java"{tuple_delimiter}"programming language"{tuple_delimiter}"Java 是 John Doe 精通的一种编程语言。"){record_delimiter}
("entity"{tuple_delimiter}"Docker"{tuple_delimiter}"tool"{tuple_delimiter}"Docker 是 John Doe 有使用经验的一种工具。"){record_delimiter}
("entity"{tuple_delimiter}"Kubernetes"{tuple_delimiter}"tool"{tuple_delimiter}"Kubernetes 是 John Doe 有使用经验的一种工具。"){record_delimiter}
("entity"{tuple_delimiter}"software development"{tuple_delimiter}"skillset"{tuple_delimiter}"软件开发是 John Doe 擅长的技能。"){record_delimiter}
("entity"{tuple_delimiter}"system design"{tuple_delimiter}"skillset"{tuple_delimiter}"系统设计是 John Doe 擅长的技能。"){record_delimiter}
("entity"{tuple_delimiter}"DevOps practices"{tuple_delimiter}"skillset"{tuple_delimiter}"DevOps 实践也是 John Doe 的技能之一。"){record_delimiter}
("relationship"{tuple_delimiter}"John Doe"{tuple_delimiter}"ABC Corp"{tuple_delimiter}"John Doe 在 ABC Corp 工作。"{tuple_delimiter}10){record_delimiter}
("relationship"{tuple_delimiter}"John Doe"{tuple_delimiter}"Python"{tuple_delimiter}"John Doe 精通 Python。"{tuple_delimiter}9){record_delimiter}
("relationship"{tuple_delimiter}"John Doe"{tuple_delimiter}"Java"{tuple_delimiter}"John Doe 精通 Java。"{tuple_delimiter}9){record_delimiter}
("relationship"{tuple_delimiter}"John Doe"{tuple_delimiter}"Docker"{tuple_delimiter}"John Doe 有使用 Docker 的经验。"{tuple_delimiter}8){record_delimiter}
("relationship"{tuple_delimiter}"John Doe"{tuple_delimiter}"Kubernetes"{tuple_delimiter}"John Doe 有使用 Kubernetes 的经验。"{tuple_delimiter}8){record_delimiter}
("relationship"{tuple_delimiter}"John Doe"{tuple_delimiter}"software development"{tuple_delimiter}"John Doe 擅长软件开发。"{tuple_delimiter}10){record_delimiter}
("relationship"{tuple_delimiter}"John Doe"{tuple_delimiter}"system design"{tuple_delimiter}"John Doe 擅长系统设计。"{tuple_delimiter}10){record_delimiter}
("relationship"{tuple_delimiter}"John Doe"{tuple_delimiter}"DevOps practices"{tuple_delimiter}"John Doe 具备 DevOps 实践技能。"{tuple_delimiter}10){completion_delimiter}
#############################
-真实数据-
######################
Entity_types: [person, organization, skillset, programming language, tool]
文本: {input_text}
######################
输出:
开始搭建您是否在寻找一支具备技能的团队来开发定制的AI代理、RAG(检索增强生成)或LLM应用程序解决方案?或者您需要解决技术问题的帮助?请点击下面的链接:https://form.jotform.com/240744327173051。
你可以使用下面的终端命令来构建GraphRAG,但要注意相关设置。选择一个合适的LLM以避免较高的成本,并检查每分钟的请求数和其他速率限制以防止错误。
python -m graphrag.index --root PATH
其中 PATH 是你要指定的根目录路径。
终端的输出结果 — 如果一切正常,你应该能看到像这样的结果
索引已经完成,现在是时候用Python加载数据了。
# 索引加载函数,用于在数据摄取过程中创建的索引
from graphrag.query.indexer_adapters import (
read_indexer_entities,
read_indexer_relationships,
read_indexer_reports,
read_indexer_text_units,
)
import tiktoken
# LanceDB 是一种向量存储库,并且是在本地运行的
from graphrag.vector_stores.lancedb import LanceDBVectorStore
from graphrag.query.input.loaders.dfs import store_entity_semantic_embeddings
api_key = os.environ["OPENAI_API_KEY"]
llm_model = "gpt-4o-mini"
llm = ChatOpenAI(
api_key=api_key,
model=llm_model,
api_type=OpenaiApiType.OpenAI,
max_retries=20,
)
embedding_model = "text-embedding-3-small"
token_encoder = tiktoken.get_encoding("cl100k_base")
text_embedder = OpenAIEmbedding(api_key=api_key, api_type=OpenaiApiType.OpenAI, model=embedding_model, max_retries=20)
INPUT_DIR = "<insert project directory>"
# 每个索引的名称(默认设置)
ENTITY_TABLE = "create_final_nodes"
ENTITY_EMBEDDING_TABLE = "create_final_entities"
RELATIONSHIP_TABLE = "create_final_relationships"
COMMUNITY_REPORT_TABLE = "create_final_community_reports"
TEXT_UNIT_TABLE = "create_final_text_units"
LANCEDB_URI = f"{INPUT_DIR}/lancedb"
# 社区级别定义了实体之间相互连接的程度
# 它作为一个超参数,值越大,表示知识图中较小群体的数量越多
# (详细解释见下文)。默认值为 0(零)
COMMUNITY_LEVEL = 1
# 创建数据框
entity_df = pd.read_parquet(f"{INPUT_DIR}/{ENTITY_TABLE}.parquet")
report_df = pd.read_parquet(f"{INPUT_DIR}/{COMMUNITY_REPORT_TABLE}.parquet")
entity_embedding_df = pd.read_parquet(f"{INPUT_DIR}/{ENTITY_EMBEDDING_TABLE}.parquet")
relationship_df = pd.read_parquet(f"{INPUT_DIR}/{RELATIONSHIP_TABLE}.parquet")
text_unit_df = pd.read_parquet(f"{INPUT_DIR}/{TEXT_UNIT_TABLE}.parquet")
# 根据索引构建报告、实体、关系和文本单元
reports = read_indexer_reports(report_df, entity_df, COMMUNITY_LEVEL)
entities = read_indexer_entities(entity_df, entity_embedding_df, COMMUNITY_LEVEL)
relationships = read_indexer_relationships(relationship_df)
text_units = read_indexer_text_units(text_unit_df)
# 连接到 LanceDB 数据库
description_embedding_store = LanceDBVectorStore(collection_name="entity_description_embeddings")
description_embedding_store.connect(db_uri=LANCEDB_URI)
store_entity_semantic_embeddings(entities=entities, vectorstore=description_embedding_store)
社区是什么呢?
作者的图片 — 解释社区的不同层次。社区编号越高,GraphRAG 能找到的关系就越细致,但创建这样的社区也需要成本,因为需要增加更多的社区报告/嵌入。
在知识图谱的背景下,“社群”通常指的是通过各种关系或属性相互关联的一组相关实体。这些实体可以是人、组织、概念或其他任何类型的数据项。社群的概念有助于理解和分析那些具有共同属性或通过特定关系相连的实体集群。
- 社交网络:在社交网络中的知识图谱中,一个社区可能由经常相互互动、拥有共同兴趣或参与类似活动的个人组成。
- 企业结构:在商业环境中,一个社区可以是协作特定项目的员工、部门或团队,或承担类似职能的群体。
- 学术研究:在学术研究中,一个社区可能包括在同一领域工作的研究人员,共同撰写论文或参加相关会议。
社区有助于识别模式、趋势和洞见,通过将相关的实体分组并通过分析它们的共同行为或特征。
——————————————————————————————
是否在寻找一个有技能的团队来开发定制的AI代理、RAG或LLM解决方案?或者需要帮助解决技术问题?请通过下面的链接联系我们。
—— —— —— —— —— —— —— —— —— —— —— —— —— —— —— —— —— —— ——
进行搜索在最初的Research论文以及后续的博客中,在微软表示有两种搜索类型,本地搜索和全网搜索。以下是它们的含义以及如何用Python来执行这些搜索。
本地搜索本地搜索技术将知识图谱中的结构化数据与输入文档中的非结构化数据结合,在查询期间增强大语言模型对上下文的理解,使其包含与实体相关的详细信息。
这种方法特别适合回答有关输入文档中提到的特定实体(如候选人 {candidate_name})的问题,例如,“告诉我关于这个候选人 {candidate_name} 的信息?”
强调特定实体是指,例如“有多少人申请了?最常见的学位是什么?”这样的问题,使用本地搜索则难以给出准确答案,相反,全局搜索则更为合适。
from graphrag.query.structured_search.local_search.mixed_context import LocalSearchMixedContext
from graphrag.query.structured_search.local_search.search import LocalSearch
# 将所有报告、文本、实体和关系的嵌入添加到预定义的上下文抽象中。
context_builder_local = LocalSearchMixedContext(
community_reports=reports,
text_units=text_units,
entities=entities,
relationships=relationships,
entity_text_embeddings=description_embedding_store,
embedding_vectorstore_key=EntityVectorStoreKey.ID,
text_embedder=text_embedder,
token_encoder=token_encoder,
)
# 为每个单元设置搜索比例
# 设置更高的community_prop可检索社区相关的上下文
# 同时设置top_K实体和关系,检索的数量越多,提供给LLM的实体和关系类型就越多
# 但是,您可能会遇到上下文窗口或令牌使用成本的问题
# 可选地将过去的对话历史包含在上下文中
local_context_params = { "text_unit_prop": 0.5, "community_prop": 0.1, "conversation_history_max_turns": 5, "conversation_history_user_turns_only": True, "top_k_mapped_entities": 10, "top_k_relationships": 10, "include_entity_rank": True, "include_relationship_weight": True, "include_community_rank": False, "return_candidate_context": False, "max_tokens": 12000, }
llm_params = { "max_tokens": 2_000, "temperature": 0.0, }
local_search_engine = LocalSearch(
llm=llm,
context_builder=context_builder_local,
token_encoder=token_encoder,
llm_params=llm_params,
context_builder_params=local_context_params,
response_type="multiple paragraphs",
)
query = "告诉我关于候选人X的信息。他们的技能是什么?他们是否有AI和RAG的相关经验?"
response = local_search_engine.search(query)
本地搜索的回答
全搜借助GraphRAG,我们可以回答关于数据的宏观问题。由LLM创建的知识图谱展示了数据集的结构和主要概念。这将数据组织成清晰的、已总结的组。通过这些摘要,您可以全面把握关系、实体或整个数据集。
不过,特定查询可能无法通过全局搜索得到关于某位候选人的良好结果。
# 引入进行全局搜索所需的模块
from graphrag.query.structured_search.global_search.community_context import GlobalCommunityContext
from graphrag.query.structured_search.global_search.search import GlobalSearch
# 全局搜索的上下文构建器
context_builder = GlobalCommunityContext(
community_reports=reports,
entities=entities,
token_encoder=token_encoder,
)
# 参数及其说明
context_builder_params = {
'use_community_summary': False, # 使用社区生成的摘要还是整个社区报告
'shuffle_data': True, # 随机化数据
'include_community_rank': True, # 在检索到的文档中包含排名
'min_community_rank': 1, # 上下文中需要包含的最低排名
'max_tokens': 7000, # 最大数据token数量
'context_name': 'Reports'
}
search_engine = GlobalSearch(
llm=llm,
context_builder=context_builder,
token_encoder=token_encoder,
max_data_tokens=12_000,
allow_general_knowledge=False, # 是否允许LLM的回答仅基于上下文而不是其训练过的知识
json_mode=True,
context_builder_params=context_builder_params,
concurrent_coroutines=10, # 并发任务数量
response_type="多页报告", # 自由格式文本,例如优先列表、单段落、多段落或多页报告
)
query = "候选人曾就读于哪些大学?你能提供他们所学的信息吗?"
glob = search_engine.search(query)
print(glob.response)
图片由作者提供。这是查询的答案。
你可以用 glob.context_data
查看获取的上下文信息
获取已匿名处理的上下文数据,仅作示例,以避免数据泄露。
对于这种情况,本地搜索查询是更好的选择,因为你需要为每个候选人、机构或前雇主提供摘要。像许多RAG应用一样,获得最佳结果需要相当多的“调试和优化”来构建适合您需求的理想处理流程。
感谢您的阅读!关注我及Firebird Technologies,了解我们最新的项目和接下来的计划!
共同学习,写下你的评论
评论加载中...
作者其他优质文章