日习一技 - 2025年8月
type: channel
link: https://i-study-everyday.online/2025/08
description: 每天学一点新技术，新框架，新工具，新名词。关于编程语言，数据库，数据结构，算法，机器学习，云计算。
items: GraphRAG 索引构建之知识提取（二）
items: GraphRAG 索引构建之知识提取
items: GraphRAG 索引构建之文档处理
items: GraphRAG 索引构建概述
items: 剖析 GraphRAG 的项目结构
items: 可视化探索 GraphRAG 的知识图谱
items: GraphRAG 快速入门
items: 学习 Coze Studio 的知识库检索逻辑
items: 学习 Coze Studio 的知识库入库逻辑（续）
items: 学习 Coze Studio 的知识库入库逻辑

GraphRAG 索引构建之知识提取（二）
type: item
date: 2025-08-29T01:07:00+00:00
description: 昨天我们学习了知识提取阶段的提取图谱工作流，今天我们继续学习另外两个：图谱规范化 和 提取事实声明。图谱规范化提取出的原始实体和关系需要进一步处理才能构成完整的知识图谱，finalize_graph 工作流负责这一规范化过程：async def run_workflow( config: GraphRagConfig, context: PipelineRunContext, ) -> WorkflowFunctionOutput: # 1. 加载实体和关系 entities = await load_table_from_storage("entities", context.output_storage) relationships = await load_table_from_storage("relationships", context.output_storage) # 2. 执行图谱规范化 final_entities, final_relationships = finalize_graph( entities, relationships, embed_config=config.embed_graph, layout_enabled=config.umap.enabled, ) # 3. 保存结果 await write_table_to_storage(final_entities, "entities", context.output_storage) await write_table_to_storage(final_relationships, "relationships", context.output_storage) if config.snapshots.graphml: # 生成图描述文件 graph = create_graph(final_relationships, edge_attr=["weight"]) await snapshot_graphml( graph, name="graph", storage=context.output_storage, )处理流程很清晰，首先加载上一步提取的实体和关系，然后调用 finalize_graph() 函数对其进行规范化处理，最后将处理后的数据重新保存，并根据配置生成 GraphML 图描述文件，便于外部工具进行图分析和可视化，这也就是我们之前学习可视化时所导入的文件。其中 finalize_graph() 函数的实现如下：def finalize_graph( entities: pd.DataFrame, relationships: pd.DataFrame, embed_config: EmbedGraphConfig | None = None, layout_enabled: bool = False, ) -> tuple[pd.DataFrame, pd.DataFrame]: # 实体规范化 final_entities = finalize_entities( entities, relationships, embed_config, layout_enabled ) # 关系规范化 final_relationships = finalize_relationships(relationships) return (final_entities, final_relationships)可以看出，它主要包括实体规范化和关系规范化：实体规范化：使用 NetworkX 创建网络图，计算每个节点的度数，使用 Node2Vec 生成图嵌入向量，使用 UMAP 降维算法进行图布局计算，得到 2D 可视化坐标，为每个实体分配唯一的 UUID 和人类可读的 ID 并输出最终规范化的实体对象；关系规范化：基于 source 和 target 去除重复的关系，计算每个关系的组合度数，为每个关系分配唯一的 UUID 和人类可读的 ID 并输出最终规范化的关系对象；下面我们仔细看下这两个部分。实体规范化实体规范化的逻辑位于 finalize_entities() 函数：def finalize_entities( entities: pd.DataFrame, relationships: pd.DataFrame, embed_config: EmbedGraphConfig | None = None, layout_enabled: bool = False, ) -> pd.DataFrame: # 创建网络图 graph = create_graph(relationships, edge_attr=["weigh…

GraphRAG 索引构建之知识提取
type: item
date: 2025-08-28T04:13:00+00:00
description: 在前面的文章中，我们详细学习了 GraphRAG 索引构建的文档处理阶段，了解了如何将各种格式的原始文档转换为标准化的文本单元。今天我们将深入探索整个索引构建流程中最核心的部分 —— 知识提取，看看 GraphRAG 是如何利用大语言模型的力量，从非结构化的文本中挖掘出结构化的实体、关系和声明，并最终构建出完整的知识图谱。知识提取阶段总览让我们回顾一下索引构建的整体流程，知识提取正是连接文本处理和图谱分析的关键桥梁：知识提取阶段包含三个核心工作流：提取图谱（extract_graph） - 从文本中提取实体和关系，对同一个实体和关系的描述进行总结合并；图谱规范化（finalize_graph） - 对提取结果进行规范化处理，输出最终的实体表和关系表；提取事实声明（extract_covariates） - 提取事实性声明，这一步是可选的，声明通常用于识别诸如欺诈等恶意行为，因此并不适用于所有数据集；提取图谱图谱提取的实现位于 index/workflows/extract_graph.py 文件中，整个工作流包含以下关键步骤：async def run_workflow( config: GraphRagConfig, context: PipelineRunContext, ) -> WorkflowFunctionOutput: # 1. 加载文本单元 text_units = await load_table_from_storage("text_units", context.output_storage) # 2. 配置提取策略和摘要策略 extraction_strategy = config.extract_graph.resolved_strategy() summarization_strategy = config.summarize_descriptions.resolved_strategy() # 3. 执行图谱提取 entities, relationships, raw_entities, raw_relationships = await extract_graph( text_units=text_units, extraction_strategy=extraction_strategy, summarization_strategy=summarization_strategy ) # 4. 保存结果 await write_table_to_storage(entities, "entities", context.output_storage) await write_table_to_storage(relationships, "relationships", context.output_storage)这里值得注意是提取策略和摘要策略，它们对应 settings.yaml 文件中的 extract_graph 和 summarize_descriptions 两个配置：extract_graph: model_id: default_chat_model prompt: "prompts/extract_graph.txt" entity_types: [organization,person,geo,event] max_gleanings: 1 summarize_descriptions: model_id: default_chat_model prompt: "prompts/summarize_descriptions.txt" max_length: 500从这里可以看出，整个图谱提取分为两步：首先遍历上一阶段生成的所有文本单元，通过大模型从每个文本片段中识别出实体和关系；然后将相同的实体和关系进行合并，再通过大模型总结合并之后的实体和关系描述；核心提示词第一步所使用的提示词位于 prompts/extract_graph.txt，翻译如下：-目标- 给定一个可能与本活动相关的文本文档和一个实体类型列表，从文本中识别出所有属于这些类型的实体以及已识别实体之间的所有关系。 -步骤- 1. 识别所有实体。对于每个已识别的实体，提取以下信息： - entity_name: 实体名称，首字母大写 - entity_type: 以下类型之一：[{entity_types}] - entity_description: 对实体属性和活动的全面描述 每个实体的格式为（"entity"<|><entity_name><|><entity_type><|><entity_description>） 2. 从步骤1中识别出的实体中，识别所有（源实体，目标实体）对，这些实体对之间存在*…

GraphRAG 索引构建之文档处理
type: item
date: 2025-08-27T00:56:00+00:00
description: 昨天我们对 GraphRAG 的索引构建做了一个基本的概述，了解了其索引构建的整体流程和工作流引擎的设计。今天我们将深入第一个具体阶段 —— 文档处理，这是整个知识图谱构建的起点，负责将各种格式的原始文档转换为标准化的文本单元，为后续的知识提取奠定基础。回顾索引构建流程根据前一篇文章的分析，我们可以将 GraphRAG 的索引构建流程分为三个主要阶段：今天我们重点关注文档处理阶段，它的核心任务是将非结构化的原始数据转换为后续处理流程所需的标准化文本单元。这个阶段包含三个关键工作流：加载原始文档（load_input_documents） - 从各种数据源加载原始文档文本分块处理（create_base_text_units） - 将长文档分割成可处理的文本块文档规范化（create_final_documents） - 对文档进行最终的标准化整理加载原始文档在 GraphRAG 中，所有工作流的入口都是 run_workflow() 方法，加载原始文档也是如此。它的核心逻辑位于 index/workflows/load_input_documents.py 文件：async def run_workflow( config: GraphRagConfig, context: PipelineRunContext, ) -> WorkflowFunctionOutput: # 加载输入文档，转换为标准的 pd.DataFrame 格式 output = await load_input_documents(config.input, context.input_storage) # 将原始文档写入 documents 表中 await write_table_to_storage(output, "documents", context.output_storage) return WorkflowFunctionOutput(result=output)这个工作流首先加载输入文档，将其转换为标准的 pd.DataFrame 格式，然后写入 documents 表中。GraphRAG 通过工厂模式实现了对纯文本、CSV、JSON 三种文件格式的统一处理：loaders: dict[str, Callable[..., Awaitable[pd.DataFrame]]] = { InputFileType.text: load_text, InputFileType.csv: load_csv, InputFileType.json: load_json, }如果你有不同的格式，官方的建议是编写脚本将其转换为其中一种。同时，它也支持 Azure Blob、CosmosDB、本地文件、内存这四种不同的存储类型：StorageFactory.register(StorageType.blob.value, create_blob_storage) StorageFactory.register(StorageType.cosmosdb.value, create_cosmosdb_storage) StorageFactory.register(StorageType.file.value, create_file_storage) StorageFactory.register(StorageType.memory.value, lambda **_: MemoryPipelineStorage())默认情况下是从本地的 input 目录下读取文件，并使用 text 加载器处理。我们可以在 settings.yaml 文件中的 input 部分进行配置：input: storage: type: file # [blob, cosmosdb, file, memory] base_dir: "input" file_type: text # [csv, text, json]文本文件处理对于纯文本文件，处理逻辑相对简单，在 index/input/text.py 中：async def load_file(path: str, group: dict | None = None) -> pd.DataFrame: text = await storage.get(path, encoding=config.encoding) new_item = {**group, "text": text} new_item["id"] = gen_sha512_hash(new_item, new_item.keys()) new_item["title"] = str(Path(path).name) new_item["creation_d…

GraphRAG 索引构建概述
type: item
date: 2025-08-26T04:49:00+00:00
description: 经过前面对 GraphRAG 项目结构的深入了解，我们已经掌握了它的整体架构和技术栈。今天我们将顺着 graphrag index 命令的调用链，深入探索 GraphRAG 索引构建的核心流程，包括命令行入口、配置加载机制、工作流引擎，以及不同索引方法的实现细节。命令行入口当我们执行 graphrag index 命令时，整个调用过程如下：入口点：__main__.py 或 bin/graphrag → graphrag.cli.main:appCLI 解析：cli/main.py → @app.command("index") 装饰器配置加载：cli/index.py → index_cli() 函数API 调用：api.build_index() 函数其中，前两步我们昨天已经学习过了：__main__.py 的作用是让 GraphRAG 能以包的方式运行，pyproject.toml 文件中的 [project.scripts] 配置让 GraphRAG 能以可执行文件的方式运行，运行的入口都是 graphrag.cli.main 包的 app 方法；该方法基于 Typer 这个现代化的 Python CLI 库实现，通过 @app.command 装饰器，定义了 GraphRAG 的 5 个子命令，index 就是其中之一。跟随调用链，我们接着看一下 cli/index.py 文件中 index_cli() 函数的实现：def index_cli(root_dir: Path, method: IndexingMethod, ...): # 配置加载 config = load_config(root_dir, config_filepath, cli_overrides) # 索引构建 _run_index(config=config, method=method, ...)这里我们可以看到两个关键步骤：配置加载：调用 load_config() 函数加载并合并配置，支持通过命令行参数覆盖配置文件中的设置索引执行：调用 _run_index() 执行实际的索引构建配置加载配置加载是 GraphRAG 的核心功能之一，位于 config/load_config.py 文件中，其实现如下：def load_config( root_dir: Path, config_filepath: Path | None = None, cli_overrides: dict[str, Any] | None = None, ) -> GraphRagConfig: # 路径规范化，确保使用绝对路径 root = root_dir.resolve() # 在根目录中搜索配置文件 config_path = _get_config_path(root, config_filepath) # 加载 .env 文件中的环境变量 _load_dotenv(config_path) # 读取配置文件文本内容 config_text = config_path.read_text(encoding="utf-8") # 解析环境变量引用 config_text = _parse_env_variables(config_text) # 根据文件类型解析为字典（支持 YAML 和 JSON） config_extension = config_path.suffix config_data = _parse(config_extension, config_text) # 应用命令行参数覆盖 if cli_overrides: _apply_overrides(config_data, cli_overrides) # 创建并验证最终配置对象 return create_graphrag_config(config_data, root_dir=str(root))这里的 load_config() 函数实现了一个完整的类型安全的配置管理流程，整体流程非常清晰：路径规范化：通过 root_dir.resolve() 确保使用绝对路径，其中 root_dir 是用户通过 --root 或 -r 参数指定配置发现：如果用户指定了 --config 或 -c 参数，则使用用户自定义配置；否则在根目录中搜索默认配置文件，比如 settings.yaml、settings.yml 或 settings.json环境变量加载：加载 .env 文件中的环境变量配置读取：读取配置文件文本内容环境变量替换：解析配置文件中环境变量引用，比如 ${GRAPHRAG_API_KEY} 等格式解析：根据文件类型将配置解析为字典，支持 YAML 和 JSON 两种方式覆盖应用…

剖析 GraphRAG 的项目结构
type: item
date: 2025-08-25T00:08:00+00:00
description: 经过这几天的动手实践和可视化体验，我们已经对 GraphRAG 的核心功能有了一个基本的了解。今天，我们正式开始深入研究它的源码，首先从熟悉项目结构入手，为我们后续学习其核心工作流（比如索引和查询）打下基础。项目概览使用 tree 命令查看 GraphRAG 项目的项目结构如下：$ tree -L 1 graphrag ├── __init__.py ├── __main__.py # 模块入口 ├── api # API 层 ├── cache ├── callbacks ├── cli # CLI 入口 ├── config # 配置管理 ├── data_model # 数据模型 ├── index # 索引系统 ├── language_model # 语言模型 ├── logger ├── prompt_tune # 提示词调优 ├── prompts # 提示词系统 ├── query # 查询系统 ├── storage # 存储抽象层 ├── utils └── vector_stores # 向量数据库其中基础模块包括：API 层（api）：这里提供了高层次的编程接口，是外部调用 GraphRAG 功能的主要入口，包括索引构建 API（index.py）、查询 API（query.py）和提示词调优 API（prompt_tune.py）三大块；CLI 入口（cli）：通过 Typer 装饰器定义命令，解析命令行参数并调用 API 层；配置管理（config）：使用 Pydantic 定义类型安全的配置模型，包括主配置、LLM 配置、向量配置、存储配置等；数据模型（data_model）：定义核心数据结构，包括实体、关系、社区、社区报告、文本单元、文档等；从入口处可以看出 GraphRAG 有三大功能：索引构建、查询、提示词调优，分别位于下面这些模块中：索引系统（index）：这是 GraphRAG 的核心模块，负责从原始文档构建知识图谱，这里的内容也是最丰富的；比如 input 目录用于输入处理，支持 CSV、JSON、纯文本多种格式的文档；workflows 是 GraphRAG 的工作流引擎，定义了标准的数据处理工作流，使用工厂模式动态创建流水线，支持增量更新和全量重建；operations 目录实现了大量的工作流原子操作，包括文本处理、图谱构建、社区识别、总结分析等；下面是一些典型的工作流操作：chunk_text - 文本分块，将长文档切分为可处理的片段；embed_text - 文本向量化，支持多种嵌入模型；extract_graph - 从文本提取实体和关系；build_noun_graph - 构建名词图，识别重要概念；create_graph.py - 创建完整的知识图谱；cluster_graph.py - 图聚类，发现社区结构；embed_graph - 图嵌入，支持 Node2Vec 等算法；layout_graph - 图布局，支持 UMAP 降维可视化；summarize_communities - 社区摘要生成；summarize_descriptions - 实体描述摘要；extract_covariates - 协变量提取（如声明检测）；查询系统（query）：为 GraphRAG 提供了多种检索策略，包括：全局搜索：基于社区报告的高层次查询，适合回答概括性或主题性问题；使用 Map-Reduce 模式处理大规模数据；本地搜索：基于实体和关系的细粒度查询，适合回答具体的事实性问题；混合向量检索和图遍历；漂移搜索：基于动态选择社区查询，在查询过程中调整搜索范围，平衡查询深度和广度；提示词系统（prompts）：包括索引和查询所使用的所有提示词；提示词调优（prompt_tune）：根据具体数据领域自动生成最适合的提示词，提高知识图谱构建质量；此外，为实现索引构建和查询，大模型和存储服务也是必不可少的：语言模型抽象（language_model）：提供统一的 LLM 和 Embedding 接口，主要通过 fnllm 库实现；存储抽象层（storage）：提供统一的存储接口，支持多种后端，包括本地文件系统、Azure Blob、CosmosDB、内存存储等；向量数据库（vector_stores）：支持多种向量数据库，包括：高性能向量数据库 LanceDB、Azure 认知搜索服务 Azure AI Search 以及 Azure 的 NoSQL 数据库 CosmosDB；技术栈分析通过分析 pyproject.toml 文件，我们可以对 GraphRAG 的核心依赖和开发工作链有一个初步了解。大语言模型fnllm[azure,openai] - 统一的大语言模型接口库opena…

可视化探索 GraphRAG 的知识图谱
type: item
date: 2025-08-22T06:36:00+00:00
description: 昨天，我们通过一个端到端的示例，快速体验了 GraphRAG 从数据索引到查询的完整流程。我们知道，GraphRAG 的核心是将非结构化文本转化为结构化的知识图谱。然而，单纯查看 Parquet 格式的输出文件很难直观地理解图谱的结构。今天，我们将学习如何将这个知识图谱可视化，从而更清晰地洞察数据中的实体、关系和社区结构。准备工作在我们昨天的 ragtest 示例中，uv run poe index 命令已经为我们生成了一系列 Parquet 文件，还有一个图描述文件 graph.graphml，包含了图谱的节点和边信息。为了生成这个文件，在构建索引之前，其实我对 settings.yaml 配置文件做了一点小小的修改，启用了 graphml 快照选项：snapshots: graphml: true为了支持其他可视化工具，还可以启用下面这些额外参数：embed_graph: enabled: true # 生成节点的 node2vec 嵌入 umap: enabled: true # 生成 UMAP 嵌入，为实体提供 x/y 位置坐标运行索引命令后，确保在 output 目录下能找到关键的 graph.graphml 文件，这是许多可视化工具支持的标准文件格式。GraphML 文件格式GraphML (Graph Markup Language) 是一种基于 XML 的通用图形描述格式，旨在表示结构化的图形数据，包括节点、边及其属性关系。它支持多种图类型，如有向图、无向图和混合图，并允许用户自定义属性，比如节点的权重和边的类型。GraphML 提供了一种灵活、可扩展的格式，在图论、网络分析和数据可视化等领域有着广泛的应用。GraphML 文件以 XML 声明开头，核心标签嵌套关系如下：<?xml version="1.0" encoding="UTF-8"?> <graphml xmlns="http://graphml.graphdrawing.org/xmlns"> <key id="weight" for="edge" attr.name="weight" attr.type="double"/> <key id="label" for="node" attr.name="label" attr.type="string"/> <graph id="example" edgedefault="directed"> <node id="n1"> Node 1 </node> <node id="n2"> Node 2 </node> <edge id="e1" source="n1" target="n2"> 3.5 </edge> </graph> </graphml>整个文件结构包含：根标签 <graphml>，必须包含 xmlns 命名空间声明，定义 GraphML 的语法规则；属性定义 <key>，用于预先声明图形元素（节点、边、图形本身）的属性，包括属性名称和数据类型，类似于数据字典；id 为属性的唯一标识，后续可通过 key 引用；图形标签 <graph>，表示一个具体的图形，可包含多个节点和边，edgedefault 为默认边类型，支持 directed（有向边）和 undirected（无向边）；节点 <node>，表示图形中的节点，id 为唯一标识；边 <edge>，表示节点间的关系，必须包含 source（起点）和 target（终点）属性；在 Python 中，常常使用 NetworkX 库读写 GraphML 文件，GraphRAG 用的也是这个库：import networkx as nx # 创建无向图 G = nx.Graph() # 添加节点 nodes = [ ("n1", {"label": "Node 1"}), ("n2", {"label": "Node 2"}), ] for name, attrs in nodes: G.add_node(name, **attrs) # 添加边 relationships = [ ("n1", "n2", {"weight": 3.5}), ] for source, target, attrs in relationships: G.add_edge(source, target, **attrs) # 将图保存为 GraphML 格式 nx.write_graphml(G, "demo.graphml", encoding='utf-8')使用 Gephi 进行可视化Gephi 是一款非常流行的开源图可视化和探索工具，非常适合用来分析 GraphRAG 生成的 GraphML 文件。首先，从 Gephi 官网下载并安装最新版本，然后点击…

GraphRAG 快速入门
type: item
date: 2025-08-21T00:26:00+00:00
description: 在我们之前的学习过程中，曾简单介绍过 RAGFlow 的一个高级特性 —— 提取知识图谱。通过运用 GraphRAG 和 LightRAG 技术，RAGFlow 可以从实体、关系的提取到子图的构建和合并，再到实体消歧和社区报告的生成，形成完整的知识图谱。因此，这种方法在处理涉及复杂关系与多个实体的文档时，尤其是在多跳问答场景中，表现得尤为卓越。最早关于 GraphRAG 的概念实际上可以追溯到去年微软研究院发表的论文《From Local to Global: A Graph RAG Approach to Query-Focused Summarization》中：https://arxiv.org/abs/2404.16130随后，微软开源了 GraphRAG 项目，在 RAG 领域又掀起了一波新的浪潮：https://github.com/microsoft/graphrag传统的 RAG 技术通常依赖于基于向量相似度来检索原始文本块，然而，当面对需要整合离散信息以解答的复杂问题时，这种方法往往表现得力不从心。GraphRAG 则采取了一种创新的路径，首先利用大型语言模型从非结构化文本数据中提取出实体和关系，并据此构建一个知识图谱。接着，依托图谱的拓扑结构进行社区发现，对每个社区进行逐层总结，最终形成一个分层且结构化的知识网络。在进行查询时，GraphRAG 利用这一知识网络来强化信息检索，从而为模型提供更具启发性的上下文，以回答那些需要深度推理和全局视角的问题。核心概念GraphRAG 的流程主要分为 索引（Indexing） 和 查询（Querying） 两个阶段：索引（Indexing）：这是 GraphRAG 的数据处理阶段，它将非结构化文本转化为结构化知识。主要步骤包括：文本分块：将长文档切分成小的文本单元（TextUnit）；图谱提取：利用大模型从文本单元中提取出实体（Entities）和关系（Relationships），构建知识图谱；社区发现：使用 Leiden 等算法对图谱进行层次化社区聚类；社区总结：利用大模型为每个层级的社区生成摘要报告；查询（Querying）：这是 GraphRAG 的数据检索与问答阶段，利用构建好的知识图谱和摘要来回答问题。比较常见的查询方式有：全局搜索（Global Search）：利用社区摘要，通过 Map-Reduce 的方式对整个数据集进行归纳总结，回答宏观问题；本地搜索（Local Search）：当问题涉及特定实体时，从该实体出发，在知识图谱中向外扩展，聚合其邻近的实体、关系以及相关的原始文本块，为大模型提供精准的局部上下文；以下是 GraphRAG 的整个流水线示意图：今天，我们就来快速上手体验一下 GraphRAG 的基本使用。安装GraphRAG 是一个 Python 库，可以通过 pip 轻松安装：$ pip install graphrag也可以通过源码进行安装，这里我采用这种方法，这样可以一边体验 GraphRAG 一边研究它的源码。首先克隆代码仓库：$ git clone https://github.com/microsoft/graphrag.git进入项目目录：$ cd graphrag使用 uv 创建 Python 虚拟环境：$ uv venv --python 3.10 $ source .venv/bin/activate安装 Python 依赖：$ uv sync --extra devuv 是一个极速的 Python 包和项目管理工具，uv sync 是它的一个命令，用于更新项目的环境，确保项目环境中的依赖与 uv.lock 文件一致。快速上手下面，我们就按照官方文档的步骤，通过一个完整的端到端示例，来体验 GraphRAG 的基本使用。首先，我们创建一个测试目录：$ mkdir -p ./ragtest/input该目录用于存放原始文档，GraphRAG 支持 txt、json 和 csv 三种格式的文档。官网给的文档示例是查尔斯・狄更斯（Charles Dickens）创作的一部著名小说《圣诞颂歌》，讲述吝啬鬼斯克鲁奇（Scrooge）在圣诞节前夜经历的奇妙故事，通过三个幽灵的拜访，他的自私和冷酷逐渐崩塌，人性中的同情、仁慈、爱心及喜悦被唤醒，从此成为了一个乐善好施的人。我们可以从 古腾堡计划 下载这本书：$ curl https://www.gutenberg.org/cache/epub/24022/pg24022.txt -o ./ragtest/input/book.txt古腾堡计划是一个致力于创建和分发免费电子书的志愿者项目，它提供了大量版权已过期的经典文学作品，可以在上面找到很多免费的图书。接着，使用 init 命令初始…

学习 Coze Studio 的知识库检索逻辑
type: item
date: 2025-08-20T00:55:00+00:00
description: 经过前面几天的学习，我们已经深入探讨了 Coze Studio 从知识库的创建到文档入库的完整流程。今天，我们将继续这一探索之旅，深入研究知识库的最后一个核心环节 —— 检索（Retrieval），看看在 Coze Studio 中，智能体是如何从知识库中精准高效地找到与用户问题最相关的信息的。Coze Studio 在这方面设计了一套颇为完善的检索流水线，它融合了查询重写、多路并行检索（向量、全文、NL2SQL）以及重排序等多种技术。下面就让我们一起看看它背后的实现细节。知识库检索配置在 Coze Studio 中，我们可以为智能体提供特定领域的背景知识，包括文本、表格和图片，让其可以回答领域内的问题：我们还可以对知识库进行一些配置，以便智能体更好地对知识库进行检索：这些配置参数包括：调用方式：分为 自动调用 和 按需调用 两种，自动调用指的是每次对话时都会发起一次检索，将检索结果放置在内置的系统提示词中，而按需调用则是将知识库检索封装成一个工具供智能体调用，只有当用户问题确实需要检索知识库时才触发调用。实际上，在开源版本中暂时只支持自动调用，我们之前在学习智能体执行逻辑时已经看到了，通过 Graph API 编排出来的智能体图，知识库检索是必然执行的节点之一，而且在源码中搜索 recallKnowledge 工具，也找不到其他地方用到；搜索策略：分为 语义、全文 和 混合 三种，语义表示基于向量的文本相关性查询，推荐在需要理解语义关联度和跨语言查询的场景使用，全文表示依赖于关键词的全文搜索，推荐在搜索具有特定名称、缩写词、短语或 ID 的场景使用，混合表示结合全文检索与语义检索的优势，并对结果进行综合排序。最大召回数量：从知识库中返回给大模型的最大段落数，注意默认是 1，可以适当调大一点；最小匹配度：根据设置的匹配度选取段落返回给大模型，低于设定匹配度的内容不会被召回；表格 SQL 查询：同时将查询的自然语言转为 SQL 语句进行查询，SQL 执行结果与 RAG 召回段落一同输入给模型；该选项仅对表格知识库有效；查询改写：结合对话上下文，对用户的问题进行改写，使得改写后的查询更适合检索；结果重排：通过分析用户查询的意图，对召回结果重新排序，使得最相关的内容排在前面；知识库检索流程回顾之前学习的智能体执行逻辑，其中有一路负责知识库的检索：// 新建知识库检索器 kr, err := newKnowledgeRetriever(ctx, &retrieverConfig{ knowledgeConfig: conf.Agent.Knowledge, }) // 第一个节点检索 _ = g.AddLambdaNode(keyOfKnowledgeRetriever, compose.InvokableLambda[*AgentRequest, []*schema.Document](kr.Retrieve), compose.WithNodeName(keyOfKnowledgeRetriever)) // 第二个节点组装成字符串 _ = g.AddLambdaNode(keyOfKnowledgeRetrieverPack, compose.InvokableLambda[[]*schema.Document, string](kr.PackRetrieveResultInfo), compose.WithOutputKey(placeholderOfKnowledge), )首先新建一个知识库检索器，将其 Retrieve() 方法作为第一个节点，知识库检索后，该方法返回 []*schema.Document 文档列表，因此还需要第二个节点，调用 PackRetrieveResultInfo 将其组装成字符串，替换系统提示词模版中的 {{ knowledge }} 占位符。可以看出只要智能体关联了知识库，用户每次对话时总是会触发一次知识库检索，检索的逻辑位于 knowledge 领域层，如下：func (k *knowledgeSVC) Retrieve(ctx context.Context, request *RetrieveRequest) (response *RetrieveResponse, err error) { // 检索上下文 retrieveContext, err := k.newRetrieveContext(ctx, request) // 查询重写 rewriteNode := compose.InvokableLambda(k.queryRewriteNode) // 向量化召回 vectorRetrieveNode := compose.InvokableLambda(k.vectorRet…

学习 Coze Studio 的知识库入库逻辑（续）
type: item
date: 2025-08-19T04:33:00+00:00
description: 书接上文，当用户在知识库上传文档、表格或图片，然后对知识库进行设置和确认后，Coze Studio 通过发送 IndexDocuments 事件开始了文档的异步处理。这个事件被发送到消息队列后，由 knowledge 领域的事件处理器消费，我们今天就来看下这块的逻辑。事件处理器事件处理器的代码位于 backend/domain/knowledge/service/event_handle.go 文件，这是一个 switch 结构，负责分发知识库相关的各类事件，比如批量文档处理（IndexDocuments）、单个文档处理（IndexDocument）、修改文档分片（IndexSlice）、删除知识库数据（DeleteKnowledgeData）、文档预览（DocumentReview）等：func (k *knowledgeSVC) HandleMessage(ctx context.Context, msg *eventbus.Message) (err error) { event := &entity.Event{} err = sonic.Unmarshal(msg.Body, event) // 事件处理器 switch event.Type { case entity.EventTypeIndexDocuments: // 批量文档处理 k.indexDocuments(ctx, event) case entity.EventTypeIndexDocument: // 单个文档处理 k.indexDocument(ctx, event) case entity.EventTypeIndexSlice: // 修改文档分片 k.indexSlice(ctx, event) case entity.EventTypeDeleteKnowledgeData: // 删除知识库数据 k.deleteKnowledgeDataEventHandler(ctx, event) case entity.EventTypeDocumentReview: // 文档预览 k.documentReviewEventHandler(ctx, event) } return nil }其中批量文档处理（IndexDocuments）事件的逻辑很简单，就是遍历所有文档，为每个文档单独发送一个单文档处理（IndexDocument）事件。文档处理流程单文档处理（IndexDocument）事件的代码位于 indexDocument() 函数，这个处理函数是整个知识库入库流程的核心，其执行流程可分为以下几个关键步骤：状态检查与清理：首先检查知识库和文档是否处于可写入状态，防止并发操作导致数据不一致；如果不是追加模式，会先清除该文档在数据库（knowledge_document_slice 表）和向量数据库中的旧数据；解析与分片：将文档状态设置为分片中（Chunking）；从对象存储（TOS）下载文档的原始文件；根据文档类型（文本、图片、表格）获取对应的解析器（Parser），对文件内容进行解析和分片，生成一系列文档分片（schema.Document）；数据持久化：为每个文档分片生成唯一的 ID；将文档分片批量存入 knowledge_document_slice 数据表中；对于表格类型，还会将解析出的结构化数据行插入到之前动态创建的物理数据表中；向量化与索引：将持久化后的文档分片转换为向量数据库要求的格式；调用 searchstore 的 Store 方法，将文档分片进行向量化，并存入 Milvus 等向量数据库中。这个过程会根据文档 ID 进行分区（Partition），便于后续的高效检索和管理；状态更新：所有文档分片成功存入向量数据库后，将其在 knowledge_document_slice 表中的状态更新为 Done；最后，将 knowledge_document 表中对应文档的状态更新为 Enable，表示该文档已处理完成并可供检索；接下来，我们详细看看每一步的实现逻辑。状态检查与清理// 检查知识库和文档状态 if valid, err := k.isWritableKnowledgeAndDocument(ctx, doc.KnowledgeID, doc.ID); err != nil { return err } else if !valid { return errorx.New(errno.ErrKnowledgeNonRetryableCode, ...) } // 清除旧数据 collectionName := getCollectionName(doc.KnowledgeID) if !doc.IsAppend …

学习 Coze Studio 的知识库入库逻辑
type: item
date: 2025-08-18T00:04:00+00:00
description: 经过前面几天的学习，我们已经深入探讨了 Coze Studio 的智能体、插件、工作流等核心功能的实现原理。今天，我们将继续探索 Coze Studio 的知识库功能，学习当我们在前端页面上点击 “创建知识库” 并上传文档时，后端服务是如何处理这些入库请求的。知识库创建接口我们知道，创建知识库是构建 RAG 应用的第一步，Coze Studio 支持 文本、表格 和 图片 三种不同类型的知识库：这三种知识库的创建流程基本类似，都分为两步：输入名称、描述、图标和导入类型，点击 “完成创建” 或 “创建并导入”，这一步调用 /api/knowledge/create 接口，创建的知识库实例对应 knowledge 数据表；上传文档、表格或图片，对知识库进行设置，预览，然后开始对文档进行处理，这一步调用 /api/knowledge/document/create 接口，上传的文档对应 knowledge_document 数据表；下面我们着重看下第二个接口的实现逻辑，接口层代码位于 backend/api/handler/coze/knowledge_service.go 文件：// @router /api/knowledge/document/create [POST] func CreateDocument(ctx context.Context, c *app.RequestContext) { // 绑定入参 var req dataset.CreateDocumentRequest err = c.BindAndValidate(&req) // 调用 knowledge 应用层 resp := new(dataset.CreateDocumentResponse) resp, err = application.KnowledgeSVC.CreateDocument(ctx, &req) c.JSON(consts.StatusOK, resp) }然后，调用 knowledge 应用层：func (k *KnowledgeApplicationService) CreateDocument(ctx context.Context, req *dataset.CreateDocumentRequest) (*dataset.CreateDocumentResponse, error) { // 调用 knowledge 领域层 resp := dataset.NewCreateDocumentResponse() createResp, err := k.DomainSVC.CreateDocument(ctx, &service.CreateDocumentRequest{ Documents: documents, }) return resp, nil }紧接着，做一些参数的转换，再调用 knowledge 领域层，这里是文档处理的核心：func (k *knowledgeSVC) CreateDocument(ctx context.Context, request *CreateDocumentRequest) (response *CreateDocumentResponse, err error) { // 根据不同的知识库类型，创建对应的文档处理器 docProcessor := impl.NewDocProcessor(ctx, &impl.DocProcessorConfig{ ... }) // 1. 前置的动作，上传 tos 等 err = docProcessor.BeforeCreate() // 2. 构建 落库 err = docProcessor.BuildDBModel() // 3. 插入数据库 err = docProcessor.InsertDBModel() // 4. 发起索引任务 err = docProcessor.Indexing() // 5. 返回处理后的文档信息 docs := docProcessor.GetResp() return &CreateDocumentResponse{ Documents: docs, }, nil }文档处理器Coze Studio 使用工厂模式根据文档来源和类型创建不同的文档处理器：customDocProcessor: 自定义内容 + 非表格类型customTableProcessor: 自定义内容 + 表格类型baseDocProcessor: 本地文件 + 非表格类型 或 默认localTableProcessor: 本地文件 + 表格类型其中，本地文件指的是用户上传文件，自定义内容指的是手动录入文档内容。所有处理器…