您现在的位置是:网站首页> AI人工智能

AI搭建知识库技术收集

摘要

AI搭建知识库技术收集


1.jpg


大模型RAG企业项目实战:手把手带你搭建一套完整的RAG系统

开源的的AI模型及调用平台

搭建AI本地知识库相关技术收集

AI大模型、数据集相关应用技术

AI人工智能模型创建训练等知识积累

向量数据库相关技术收集

Python使用大模型创建RAG知识库的代码

Deepseek-R1本地免费部署后 联网搜索使用教程,超简单

用rag外挂做知识问答知识库,投喂那种格式数据效果最好,给几个例子

DeepSeek训练私有知识库】更适合AI客服回答精准问题场景

Easy Dataset数据集整理工具



Python使用大模型创建RAG知识库的代码

####【豆包】

Python使用大模型创建RAG知识库的代码

。。。。

这个 RAG 知识库实现包含以下核心功能:
  1. 文档处理:支持加载多种格式的文档(TXT、PDF、DOCX 等),并自动分割为适合处理的小块
  2. 向量存储:使用 Chroma 作为向量数据库,存储文档片段的嵌入表示
  3. 嵌入模型:使用 Sentence-Transformers 模型生成文本嵌入
  4. 大模型集成:支持加载开源大模型(如 Vicuna),并使用 4 位量化减少内存占用
  5. 检索增强生成:将检索到的相关文档片段作为上下文传递给大模型,生成更准确的回答
使用方法:
  1. 安装所需依赖:pip install langchain chromadb transformers sentence-transformers torch pdfminer.six python-dotenv
  2. 创建./documents目录并放入你的知识库文档
  3. 首次运行时取消build_knowledge_base方法的注释,构建知识库
  4. 后续运行可直接加载已构建的知识库进行查询
你可以根据需要替换模型(如使用 Llama 2、Mistral 等),调整文档分割参数或检索时的文档数量,以获得更好的效果。对于生产环境,还可以考虑添加缓存机制、文档更新功能和更完善的错误处理。


在提供的 RAG 知识库代码中,添加文档的相关功能主要集中在以下几个函数中:
  1. load_documents() 函数:这是直接加载文档的函数,负责从指定目录读取各种格式(.txt、.pdf、.docx 等)的文档。它通过不同的加载器(TextLoaderPDFMinerLoader等)处理不同类型的文件,并将所有文档收集到一个列表中返回。
  2. split_documents() 函数:加载文档后,需要将其分割成适合模型处理的小块,这个功能由该函数完成。它使用RecursiveCharacterTextSplitter将长文档分割为指定大小的片段,并保留一定的重叠部分以维持上下文连贯性。
  3. create_vector_db() 函数:分割后的文档块最终通过这个函数添加到向量数据库中。它利用嵌入模型将文本转换为向量,并存储到 Chroma 向量数据库中,同时支持持久化存储以便后续复用。
  4. build_knowledge_base() 函数:这是一个整合函数,依次调用了上述三个函数(加载文档→分割文档→创建向量库),提供了构建完整知识库的便捷入口。
如果你想添加新文档到已有的知识库,最直接的方式是:
  1. 将新文档放入文档目录
  2. 调用load_documents()加载包括新文档在内的所有文档
  3. 调用split_documents()处理
  4. 调用create_vector_db(overwrite=True)重新创建向量库(覆盖旧库)
或者更简单地直接调用build_knowledge_base(overwrite=True)一次性完成整个流程。



输出代码

import os

import logging

from typing import List, Dict, Optional

from dotenv import load_dotenv

from langchain.document_loaders import (

    TextLoader, PDFMinerLoader, UnstructuredWordDocumentLoader, DirectoryLoader

)

from langchain.text_splitter import RecursiveCharacterTextSplitter

from langchain.embeddings import HuggingFaceEmbeddings

from langchain.vectorstores import Chroma

from langchain.llms import HuggingFacePipeline

from langchain.chains import RetrievalQA

from langchain.prompts import PromptTemplate

import torch

from transformers import AutoTokenizer, AutoModelForCausalLM, pipeline, BitsAndBytesConfig


# 配置日志

logging.basicConfig(

    level=logging.INFO,

    format='%(asctime)s - %(name)s - %(levelname)s - %(message)s'

)

logger = logging.getLogger(__name__)


class RAGKnowledgeBase:

    def __init__(self, 

                 model_name: str = "lmsys/vicuna-7b-v1.5",

                 embedding_model_name: str = "sentence-transformers/all-MiniLM-L6-v2",

                 persist_directory: str = "./chroma_db",

                 device: str = "cuda" if torch.cuda.is_available() else "cpu"):

        """

        初始化RAG知识库

        

        Args:

            model_name: 大模型名称或路径

            embedding_model_name: 嵌入模型名称或路径

            persist_directory: 向量数据库持久化目录

            device: 运行设备

        """

        self.model_name = model_name

        self.embedding_model_name = embedding_model_name

        self.persist_directory = persist_directory

        self.device = device

        

        # 加载环境变量

        load_dotenv()

        

        # 初始化组件

        self.embeddings = None

        self.vectorstore = None

        self.llm = None

        self.qa_chain = None

        

        # 文档加载器映射

        self.loaders = {

            '.txt': TextLoader,

            '.pdf': PDFMinerLoader,

            '.docx': UnstructuredWordDocumentLoader,

            '.doc': UnstructuredWordDocumentLoader

        }

        

        logger.info(f"初始化RAG知识库,使用模型: {model_name},设备: {device}")


    def load_embedding_model(self):

        """加载嵌入模型"""

        logger.info(f"加载嵌入模型: {self.embedding_model_name}")

        self.embeddings = HuggingFaceEmbeddings(

            model_name=self.embedding_model_name,

            model_kwargs={'device': self.device},

            encode_kwargs={'normalize_embeddings': True}

        )

        return self.embeddings


    def load_documents(self, directory: str) -> List:

        """

        从目录加载文档

        

        Args:

            directory: 文档目录路径

            

        Returns:

            加载的文档列表

        """

        logger.info(f"从目录加载文档: {directory}")

        

        documents = []

        for ext, loader_cls in self.loaders.items():

            try:

                loader = DirectoryLoader(

                    directory,

                    glob=f'**/*{ext}',

                    loader_cls=loader_cls,

                    show_progress=True

                )

                docs = loader.load()

                logger.info(f"加载了 {len(docs)} 个{ext}文档")

                documents.extend(docs)

            except Exception as e:

                logger.error(f"加载{ext}文档时出错: {str(e)}")

        

        logger.info(f"总共加载了 {len(documents)} 个文档")

        return documents


    def split_documents(self, documents: List, chunk_size: int = 500, chunk_overlap: int = 50) -> List:

        """

        分割文档为小块

        

        Args:

            documents: 文档列表

            chunk_size: 块大小

            chunk_overlap: 块重叠大小

            

        Returns:

            分割后的文档块列表

        """

        logger.info(f"分割文档为块,块大小: {chunk_size},重叠: {chunk_overlap}")

        text_splitter = RecursiveCharacterTextSplitter(

            chunk_size=chunk_size,

            chunk_overlap=chunk_overlap,

            separators=["\n\n", "\n", ".", " ", ""]

        )

        splits = text_splitter.split_documents(documents)

        logger.info(f"文档分割完成,得到 {len(splits)} 个块")

        return splits


    def create_vector_db(self, documents: List, overwrite: bool = False) -> Chroma:

        """

        创建向量数据库

        

        Args:

            documents: 文档块列表

            overwrite: 是否覆盖现有数据库

            

        Returns:

            向量数据库实例

        """

        # 如果需要覆盖或数据库不存在,则创建新的

        if overwrite or not os.path.exists(self.persist_directory):

            logger.info(f"创建新的向量数据库,保存到: {self.persist_directory}")

            if not self.embeddings:

                self.load_embedding_model()

                

            self.vectorstore = Chroma.from_documents(

                documents=documents,

                embedding=self.embeddings,

                persist_directory=self.persist_directory

            )

            self.vectorstore.persist()

        else:

            logger.info(f"加载现有向量数据库: {self.persist_directory}")

            if not self.embeddings:

                self.load_embedding_model()

                

            self.vectorstore = Chroma(

                persist_directory=self.persist_directory,

                embedding_function=self.embeddings

            )

            

        return self.vectorstore


    def load_llm(self, max_new_tokens: int = 512, temperature: float = 0.7):

        """

        加载大语言模型

        

        Args:

            max_new_tokens: 最大生成 tokens 数

            temperature: 生成温度,值越高越随机

        """

        logger.info(f"加载大语言模型: {self.model_name}")

        

        # 配置量化参数,减少内存使用

        bnb_config = BitsAndBytesConfig(

            load_in_4bit=True,

            bnb_4bit_use_double_quant=True,

            bnb_4bit_quant_type="nf4",

            bnb_4bit_compute_dtype=torch.bfloat16

        )

        

        # 加载tokenizer

        tokenizer = AutoTokenizer.from_pretrained(self.model_name)

        tokenizer.pad_token = tokenizer.eos_token

        

        # 加载模型

        model = AutoModelForCausalLM.from_pretrained(

            self.model_name,

            quantization_config=bnb_config,

            device_map="auto",

            trust_remote_code=True

        )

        

        # 创建文本生成管道

        pipe = pipeline(

            "text-generation",

            model=model,

            tokenizer=tokenizer,

            max_new_tokens=max_new_tokens,

            temperature=temperature,

            top_p=0.95,

            repetition_penalty=1.15

        )

        

        self.llm = HuggingFacePipeline(pipeline=pipe)

        logger.info("大语言模型加载完成")

        return self.llm


    def create_qa_chain(self, prompt_template: Optional[str] = None, k: int = 4):

        """

        创建问答链

        

        Args:

            prompt_template: 提示模板

            k: 检索的文档数量

        """

        if not self.vectorstore:

            raise ValueError("请先创建或加载向量数据库")

            

        if not self.llm:

            self.load_llm()

            

        # 默认提示模板

        if not prompt_template:

            prompt_template = """使用以下提供的上下文来回答问题。如果上下文里没有相关信息,

            请说明你没有足够的信息来回答这个问题,不要编造答案。


            上下文:

            {context}


            问题: {question}


            回答:"""

            

        prompt = PromptTemplate(

            template=prompt_template,

            input_variables=["context", "question"]

        )

        

        # 创建检索器

        retriever = self.vectorstore.as_retriever(search_kwargs={"k": k})

        

        # 创建问答链

        self.qa_chain = RetrievalQA.from_chain_type(

            llm=self.llm,

            chain_type="stuff",

            retriever=retriever,

            return_source_documents=True,

            chain_type_kwargs={"prompt": prompt}

        )

        

        logger.info("问答链创建完成")

        return self.qa_chain


    def query(self, question: str) -> Dict:

        """

        向知识库提问

        

        Args:

            question: 问题

            

        Returns:

            包含回答和来源文档的字典

        """

        if not self.qa_chain:

            self.create_qa_chain()

            

        logger.info(f"处理查询: {question}")

        result = self.qa_chain({"query": question})

        return result


    def build_knowledge_base(self, docs_directory: str, overwrite: bool = False):

        """

        构建完整的知识库

        

        Args:

            docs_directory: 文档目录

            overwrite: 是否覆盖现有数据库

        """

        logger.info("开始构建知识库...")

        documents = self.load_documents(docs_directory)

        splits = self.split_documents(documents)

        self.create_vector_db(splits, overwrite)

        self.load_llm()

        self.create_qa_chain()

        logger.info("知识库构建完成")



if __name__ == "__main__":

    # 示例用法

    try:

        # 初始化RAG知识库

        rag = RAGKnowledgeBase(

            model_name="lmsys/vicuna-7b-v1.5",  # 可以替换为其他模型

            embedding_model_name="sentence-transformers/all-MiniLM-L6-v2",

            persist_directory="./chroma_db"

        )

        

        # 构建知识库 (第一次运行时需要)

        # rag.build_knowledge_base(docs_directory="./documents", overwrite=False)

        

        # 加载现有知识库

        rag.load_embedding_model()

        rag.create_vector_db(documents=[], overwrite=False)  # 空文档列表表示加载现有数据库

        rag.load_llm()

        rag.create_qa_chain()

        

        # 示例查询

        while True:

            question = input("\n请输入你的问题 (输入 'exit' 退出): ")

            if question.lower() == 'exit':

                break

                

            result = rag.query(question)

            

            print("\n回答:")

            print(result["result"])

            

            print("\n来源文档:")

            for i, doc in enumerate(result["source_documents"], 1):

                print(f"\n文档 {i}:")

                print(f"来源: {doc.metadata.get('source', '未知')}")

                print(f"内容片段: {doc.page_content[:200]}...")

                

    except Exception as e:

        logger.error(f"发生错误: {str(e)}", exc_info=True)


####【豆包】

在以上代码如要往知识库添加文档应该如何调用给例子

方法 1:全量重建知识库(适用于新增少量文档)

这种方式会重新加载所有文档(包括已有文档和新文档),适合新增少量文档的情况:

if __name__ == "__main__":
    # 初始化RAG实例
    rag = RAGKnowledgeBase(
        model_name="lmsys/vicuna-7b-v1.5",
        embedding_model_name="sentence-transformers/all-MiniLM-L6-v2",
        persist_directory="./chroma_db"
    )
    
    # 1. 将新文档放入 ./new_documents 目录
    # 2. 调用build_knowledge_base并设置overwrite=True覆盖旧库
    rag.build_knowledge_base(
        docs_directory="./new_documents",  # 包含新文档的目录
        overwrite=True  # 重要:设置为True会重建整个知识库
    )
    print("新文档已成功添加到知识库!")


方法 2:增量添加文档(适合大量文档场景)

如果已有大量文档,只想添加新文档而不重新处理旧文档,可以这样做:
if __name__ == "__main__":
    # 初始化RAG实例
    rag = RAGKnowledgeBase(
        model_name="lmsys/vicuna-7b-v1.5",
        embedding_model_name="sentence-transformers/all-MiniLM-L6-v2",
        persist_directory="./chroma_db"
    )
    
    # 1. 加载现有向量库
    rag.load_embedding_model()
    rag.create_vector_db(documents=[], overwrite=False)  # 加载现有库
    
    # 2. 加载新文档(只加载新文档目录)
    new_documents = rag.load_documents(directory="./new_documents_only")

使用说明:

  1. 先将新文档放入指定目录(如./new_documents
  2. 运行上述代码之一:
    • 全量重建适合文档总数较少的情况(如几百个文档以内)
    • 增量添加适合已有大量文档,仅需补充少量新文档的场景
  3. 添加完成后,下次查询时就能检索到新文档的内容了
注意:增量添加时,确保新文档目录中只包含需要新增的文档,避免重复添加已有的文档。


Deepseek-R1本地免费部署后 联网搜索使用教程,超简单

点击进入Page Assist



用rag外挂做知识问答知识库,投喂那种格式数据效果最好,给几个例子

豆包:

在构建以做 tag 为目的的问答知识库时,以下几种数据格式效果较好,且能方便地进行存储、管理和检索,以下为你介绍并给出例子:

JSON 格式

特点:数据格式简洁,易于解析和处理,支持多种数据类型,适合在不同的编程语言和系统之间进行数据交换。

示例

json

[

  {

    "question": "什么是人工智能",

    "answer": "人工智能是指计算机系统能够执行通常需要人类智能才能完成的任务,如学习、推理、解决问题等",

    "tags": ["人工智能", "定义"]

  },

  {

    "question": "人工智能有哪些应用领域",

    "answer": "人工智能的应用领域包括医疗、交通、金融、教育等",

    "tags": ["人工智能", "应用领域"]

  }

]

CSV 格式

特点:以表格形式存储数据,结构简单,易于理解和编辑,可方便地导入到电子表格软件中进行查看和处理。

示例

csv

question,answer,tags

"什么是区块链","区块链是一个共享又不可篡改的账本,它的作用是促进业务网络中的交易记录和资产跟踪流程","区块链,定义"

"区块链有什么特点","区块链具有去中心化、不可篡改、安全透明等特点","区块链,特点"

XML 格式

特点:具有良好的可读性和可扩展性,能够清晰地表示数据的层次结构,适合用于需要严格遵循特定格式和规范的数据存储。

示例

xml

<questions>

  <question>

    <text>什么是物联网</text>

    <answer>物联网是通过各种信息传感器等设备和技术,实现物与物、人与物之间的泛在连接,实现对物品和过程的智能化感知、识别和管理</answer>

    <tags>

      <tag>物联网</tag>

      <tag>定义</tag>

    </tags>

  </question>

  <question>

    <text>物联网的关键技术有哪些</text>

    <answer>物联网的关键技术包括传感器技术、RFID技术、网络通信技术等</answer>

    <tags>

      <tag>物联网</tag>

      <tag>关键技术</tag>

    </tags>

  </question>

</questions>


kimi:

在使用RAG(Retrieval-Augmented Generation,检索增强生成)技术构建知识问答知识库时,投喂数据的格式对效果有重要影响。以下是适合RAG模型的数据格式和一些例子:

适合的数据格式

文本格式

纯文本文件(.txt):这是最简单的格式,适合直接提取文本内容。例如,将一段段落或文章直接存储为文本文件。

Markdown文件(.md):适合包含标题、列表和简单格式化的文档。

PDF文件:适合包含复杂格式(如标题、表格、图片等)的文档。需要通过工具(如PP-StructureV2)解析为文本。

结构化数据格式

CSV文件:适合表格数据,例如用户信息、产品数据等。可以将CSV文件中的每一行或每一列作为知识库的一部分。

JSON文件:适合存储具有层级结构的数据,例如问答对、知识图谱等。

多模态数据格式

文本+表格+图片:可以通过多模态嵌入模型将文本、表格和图片转换为向量,存储在向量数据库中。


数据投喂格式的例子

纯文本文件(.txt)

plaintext

复制

人工智能是一种模拟人类智能行为的技术,包括机器学习、自然语言处理等领域。

机器学习是人工智能的一个重要分支,它通过算法让计算机从数据中学习规律。

这种格式适合简单的知识输入,模型可以直接提取文本内容。

Markdown文件(.md)

markdown

复制

# 人工智能简介

- **定义**:人工智能是一种模拟人类智能行为的技术。

- **主要领域**:

  - 机器学习

  - 自然语言处理

  - 计算机视觉

Markdown格式适合有层次结构的知识。


CSV文件(表格数据)

csv

复制

问题,答案

人工智能是什么,人工智能是一种模拟人类智能行为的技术。

机器学习的作用,机器学习通过算法让计算机从数据中学习规律。

CSV格式适合问答对或结构化数据。

JSON文件(结构化数据)

JSON

复制

{

  "人工智能": {

    "定义": "模拟人类智能行为的技术",

    "主要领域": ["机器学习", "自然语言处理", "计算机视觉"]

  },

  "机器学习": {

    "定义": "通过算法让计算机从数据中学习规律"

  }

}


JSON格式适合包含层级结构的知识

多模态数据(文本+表格+图片)

文本:描述性文字。

表格:数据表格(如实验数据、用户信息等)。

图片:流程图、架构图等。

这种格式适合复杂知识体系,需要通过多模态嵌入模型处理。

总结

选择哪种格式取决于知识库的内容和应用场景。纯文本和Markdown适合简单知识,CSV和JSON适合结构化数据,而多模态数据适合复杂知识体系。在实际应用中,可以根据需求选择合适的格式,以提高RAG模型的问答效果


DeepSeek

在使用RAG(Retrieval-Augmented Generation,检索增强生成)技术构建知识问答系统时,数据格式对模型效果至关重要。以下是适合RAG模型的数据格式及示例:

1. 问答对格式(Q&A Pairs)

格式说明:每个数据点包含一个问题及其对应的答案。

优点:直接提供问答对,便于模型学习问题和答案之间的映射。

示例:

json

复制

{

  "question": "什么是RAG模型?",

  "answer": "RAG模型是一种结合检索和生成的模型,通过检索相关文档并生成答案来解决开放域问答任务。"

}


2. 段落格式(Passages)

格式说明:每个数据点包含一段文本,通常是从文档中提取的段落。

优点:提供上下文信息,帮助模型理解答案的背景。

示例:

json

复制

{

  "passage": "RAG模型由Facebook AI提出,结合了检索和生成两种技术。它首先从大规模文档库中检索相关文档,然后基于这些文档生成答案。"

}


3. 文档格式(Documents)

格式说明:每个数据点包含一个完整的文档或文章。

优点:提供更丰富的上下文信息,适合需要深入理解内容的场景。

示例:

json

复制

{

  "document": "RAG模型是一种用于开放域问答的模型。它通过检索相关文档并生成答案来解决复杂问题。该模型结合了检索和生成两种技术,能够处理多种类型的问答任务。"

}


4. 带标签的段落格式(Labeled Passages)

格式说明:每个数据点包含一段文本及其对应的标签或类别。

优点:帮助模型更好地理解文本的类别或主题,提升检索效果。

示例:

json

复制

{

  "passage": "RAG模型由Facebook AI提出,结合了检索和生成两种技术。",

  "label": "人工智能"

}


5. 多轮对话格式(Multi-turn Dialogue)

格式说明:每个数据点包含多轮对话,每轮对话包括用户的问题和系统的回答。

优点:适合需要处理多轮对话的场景,帮助模型理解上下文。

示例:

json

复制

{

  "dialogue": [

    {"user": "什么是RAG模型?", "system": "RAG模型是一种结合检索和生成的模型。"},

    {"user": "它有什么优点?", "system": "它能够处理开放域问答任务,并生成准确的答案。"}

  ]

}


6. 结构化数据格式(Structured Data)

格式说明:数据以结构化的形式(如表格、JSON等)呈现。

优点:适合处理结构化信息,便于模型快速检索和生成答案。

示例:

json

复制

{

  "table": [

    {"Model": "RAG", "Type": "Retrieval-Augmented Generation", "Developer": "Facebook AI"},

    {"Model": "GPT-3", "Type": "Generative Pre-trained Transformer", "Developer": "OpenAI"}

  ]

}


7. 带上下文的问答格式(Contextual Q&A)

格式说明:每个数据点包含一个问题、答案以及相关的上下文信息。

优点:提供更丰富的上下文,帮助模型生成更准确的答案。

示例:

json

复制

{

  "context": "RAG模型由Facebook AI提出,结合了检索和生成两种技术。",

  "question": "RAG模型是由谁提出的?",

  "answer": "Facebook AI"

}


8. 带注释的文本格式(Annotated Text)

格式说明:文本中包含注释或标记,用于指示关键信息或实体。

优点:帮助模型更好地理解文本中的关键信息。

示例:

json

复制

{

  "text": "RAG模型由<organization>Facebook AI</organization>提出,结合了<technique>检索</technique>和<technique>生成</technique>两种技术。"

}

总结

选择合适的数据格式取决于具体的应用场景和需求。问答对格式适合直接生成答案,段落和文档格式适合提供丰富的上下文信息,而结构化数据格式则适合处理表格或JSON等结构化信息。根据任务需求选择合适的数据格式,可以显著提升RAG模型的效果。


DeepSeek训练私有知识库】更适合AI客服回答精准问题场景

MaxKB调优,选中自己知识库》问题》创建问题》选择关联段落

1.png



Easy Dataset数据集整理工具

点击查看原文

目前各行各业都在积极探索微调自己行业的大模型,其实微调的过程不是难事,最难的是数据集准备的环节,高质量领域数据集的构建始终面临多重挑战,所以我之前大模型微调的教程一发出,大家问的最多的就是数据集的问题:

总结了常见问题就是这些:


完全不知道怎么做,目前就在纯人工去做,想提高效率

直接将文档丢给 AI ,但是 AI 对于大文件生成的 QA 对效果比较差

AI 本身有上下文的限制,一次不能生成太多的问题,分批生成后面又会生成重复的问题

已经有整理出来的数据集了,想有一个批量管理数据集的地方,可以进行标注和验证

对于数据集有细分领域的需求,不知道如何去构建领域标签

想要微调推理模型,但是不知道推理微调数据集中的 COT 怎么构造

想从一个格式的数据集转换成另一个格式的数据集,不知道怎么转换

目前市面上确实没有一款工具可以满足这样的需求,因此,借助 AI,我大概花了 3 个晚上的时间构建出了 Easy DataSet(https://github.com/ConardLi/easy-dataset)的第一个版本,通过系统性解决方案实现从文献解析到数据集导出的全流程闭环。


数据生成引擎:

问题批量生成:基于文本块语义,按字符密度动态生成问题(可配置),支持批量创建与中断恢复。

答案智能构建:关联原始文本块生成答案,支持推理模型(如DeepSeek-R1)生成带思维链(COT)的复杂答案。

质量校验机制:提供问题/答案的批量删除、手动编辑及AI优化(输入指令自动润色),确保数据可用。


格式生态适配:

多格式导出:支持Alpaca、ShareGPT标准格式,自定义字段映射,包含领域标签与 COT 信息。

数据集广场:聚合 HuggingFace、Kaggle 等多平台数据源,支持关键字一键检索,解决「数据从哪来」的初始难题。









Top