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 }
  );
};
相关推荐
山顶夕景4 小时前
【LLM】多模态智能体Kimi-K2.5模型
llm·agent·多模态
JTnnnnn5 小时前
【架構優化】拒絕 LLM 幻覺:設計基於 Python 路由的 AntV 智慧圖表生成系統
llm·antv·dify
AndrewHZ5 小时前
【AI黑话日日新】什么是skills?
语言模型·大模型·llm·claude code·skills
国家一级假勤奋大学生15 小时前
InternVL系列 technical report 解析
大模型·llm·vlm·mllm·internvl·调研笔记
缘友一世21 小时前
张量并行和流水线并行原理深入理解与思考
学习·llm·pp·tp
Async Cipher1 天前
TypeScript 的用法
前端·typescript
We་ct1 天前
LeetCode 30. 串联所有单词的子串:从暴力到高效,滑动窗口优化详解
前端·算法·leetcode·typescript
CoderJia程序员甲1 天前
GitHub 热榜项目 - 日榜(2026-01-30)
开源·大模型·llm·github·ai教程
刘联其1 天前
封装一个完整的HttpClient.ts
前端·javascript·typescript
亚里随笔1 天前
MegaFlow:面向Agent时代的大规模分布式编排系统
人工智能·分布式·llm·rl·agentic