FastGPT源码深度剖析:网页链接如何转化为知识库数据

在构建知识库和机器学习模型时,数据集的导入和管理是至关重要的步骤。FastGPT提供了一种灵活的数据集导入功能,允许用户通过网页链接轻松导入文本数据集,从而丰富知识库的内容。

本文将深入分析FastGPT的源码,揭示如何通过网页链接导入数据集,并探讨其背后的技术实现细节。

业务操作流程

FastGPT 在知识库里面创建数据集支持多种方式,在知识库里面点击右上角的"新建/导入",选择"文本数据集"->"网页链接"进入新增页面。

第一步,填写网页链接、选择器信息。网页链接不做解释,选择器即 CSS 选择,前端同学肯定熟悉。此处支持填写多个链接,使用换行分割;CSS 选择器不支持多个。

注意:网页链接必须是静态网页,SPA页面是解析不出来的。

第二步,填写训练模式、处理方式相关信息。训练模式支持问答拆分,会将数据喂给 LLM,LLM 返回 QA 问答内容。处理方式支持自定义规则,适合预处理过的数据,QA 模式可以在这里修改 prompt。

第三步,上传。系统创建数据集并将数据拆分为 chunk 推入训练队列。

核心源码分析

创建数据集的 web 页面源码在pages/dataset/dtail/Import/diffSource/FileLink文件中,没有什么特别的逻辑,FileLink 里面的三个组件分别对应新建的三个步骤,代码如下:

tsx 复制代码
const LinkCollection = ({ activeStep, goToNext }: ImportDataComponentProps) => {
  return (
    <>
      {activeStep === 0 && <CustomLinkImport goToNext={goToNext} />}
      {activeStep === 1 && <DataProcess showPreviewChunks={false} goToNext={goToNext} />}
      {activeStep === 2 && <Upload showPreviewChunks={false} />}
    </>
  );
};

最后一步上传文件时,调用的接口为/core/dataset/collection/create/link,后端逻辑在pages/api/core/dataset/collection/create/link 文件中,核心代码如下:

ts 复制代码
export default async function handler(req: NextApiRequest, res: NextApiResponse<any>) {
  try {
    await connectToDatabase();
    const {
      link,
      trainingType = TrainingModeEnum.chunk,
      chunkSize = 512,
      chunkSplitter,
      qaPrompt,
      ...body
    } = req.body as LinkCreateDatasetCollectionParams;

    const { teamId, tmbId, dataset } = await authDataset({
      req,
      authToken: true,
      authApiKey: true,
      datasetId: body.datasetId,
      per: 'w'
    });

    // 1. check dataset limit
    await checkDatasetLimit({
      teamId,
      insertLen: predictDataLimitLength(trainingType, new Array(10)),
      standardPlans: getStandardSubPlan()
    });

    const { _id: collectionId } = await mongoSessionRun(async (session) => {
      // 2. create collection
      const collection = await createOneCollection({
        ...body,
        name: link,
        teamId,
        tmbId,
        type: DatasetCollectionTypeEnum.link,

        trainingType,
        chunkSize,
        chunkSplitter,
        qaPrompt,

        rawLink: link,
        session
      });

      // 3. create bill and start sync
      const { billId } = await createTrainingBill({
        teamId,
        tmbId,
        appName: 'core.dataset.collection.Sync Collection',
        billSource: BillSourceEnum.training,
        vectorModel: getVectorModel(dataset.vectorModel).name,
        agentModel: getLLMModel(dataset.agentModel).name,
        session
      });

      // load
      await reloadCollectionChunks({
        collection: {
          ...collection.toObject(),
          datasetId: dataset
        },
        tmbId,
        billId,
        session
      });

      return collection;
    });

    jsonRes(res, {
      data: { collectionId }
    });
  } catch (err) {
    jsonRes(res, {
      code: 500,
      error: err
    });
  }
}

核心逻辑包含以下几个步骤:

  1. 鉴权: parseHeaderCert 解析 token 获取 teamId,tmbId等信息,authDatasetByTmbId 鉴定当前用户对知识库是否有写权限;
  2. checkDatasetLimit: 检测是否达到团队maxDatasetSize上限,这个属于商业版的功能;这里面predictDataLimitLength 的逻辑比较奇怪,predictDataLimitLength(trainingType, new Array(10))定死了数组的长度,并不是根据实际数据进行预测;
  3. 创建新的数据集:调用createOneCollection往 mongo 里面的 datasets.collections 插入记录;
  4. 创建账单:createTrainingBill 应该也是商业版的功能;
  5. reloadCollectionChunks:根据链接获取数据集进行 chunk 拆分,并创建训练任务。
    • 调用urlsFetch 方法获取网页链接里面的数据并转成 md 格式,数据爬取引擎采用的是 cheerio;
    • 对数据按照规则进行 chunk 拆分;
    • 创建训练任务:问答训练模式采用agentModel,直接拆分使用vectorModel;
    • 更新数据集的文本长度、标题等信息。
ts 复制代码
/* link collection start load data */
export const reloadCollectionChunks = async ({
  collection,
  tmbId,
  billId,
  rawText,
  session
}: {
  collection: CollectionWithDatasetType;
  tmbId: string;
  billId?: string;
  rawText?: string;
  session: ClientSession;
}) => {
  const {
    title,
    rawText: newRawText,
    collection: col,
    isSameRawText
  } = await getCollectionAndRawText({
    collection,
    newRawText: rawText
  });

  if (isSameRawText) return;

  // split data
  const { chunks } = splitText2Chunks({
    text: newRawText,
    chunkLen: col.chunkSize || 512
  });

  // insert to training queue
  const model = await (() => {
    if (col.trainingType === TrainingModeEnum.chunk) return col.datasetId.vectorModel;
    if (col.trainingType === TrainingModeEnum.qa) return col.datasetId.agentModel;
    return Promise.reject('Training model error');
  })();

  await MongoDatasetTraining.insertMany(
    chunks.map((item, i) => ({
      teamId: col.teamId,
      tmbId,
      datasetId: col.datasetId._id,
      collectionId: col._id,
      billId,
      mode: col.trainingType,
      prompt: '',
      model,
      q: item,
      a: '',
      chunkIndex: i
    })),
    { session }
  );

  // update raw text
  await MongoDatasetCollection.findByIdAndUpdate(
    col._id,
    {
      ...(title && { name: title }),
      rawTextLength: newRawText.length,
      hashRawText: hashStr(newRawText)
    },
    { session }
  );
};
相关推荐
用户3134672143541 小时前
learn-claude-code笔记4: Hooks — 挂在循环上,不写进循环里
llm·agent
程序员大狐狸2 小时前
Ultra X7 358H 笔记本电脑,本地三个大模型接入 DeepSeek Harness 桌面版的过程与方法
人工智能·llm
草上飞95272 小时前
零知识证明能证明算对了,却证明不了没看见
人工智能·深度学习·llm
lucas_AI2 小时前
Agent 工具箱越大越难选?Tracer 砍掉八成工具 schema,选得反而更准
llm·agent
用户460381291942 小时前
Function Calling 选对了工具,却总在参数上翻车?从 JSON Mode 到 Structured Outputs 一次讲透
llm
自养Agent2 小时前
Agent Harness 从用到会造:大厂 RAG 为什么从不只用向量检索
人工智能·llm
学IT的周星星2 小时前
记忆系统:让 Agent 记住你是谁、上次聊到哪
运维·服务器·人工智能·windows·大模型·llm·agent
badhope3 小时前
AI Agent安全治理实战:从API密钥泄露到沙箱隔离的完整防线
后端·llm·agent
许雪里3 小时前
Agent 记忆架构设计:原理 × 业界三大范式 × XXL-AI 落地实践
后端·llm·agent
何何____5 小时前
TypeScript 函数类型兼容性
前端·javascript·typescript