FastGPT源码解析 Agent 智能体插件实现代码分析

FastGPT插件实现逻辑和代码梳理

1. 插件系统概述

FastGPT通过插件丰富智能体生态,集成多种的方式的各种功能的工具,无限扩展智能体功能。插件模块支持多种类型的插件,提供了灵活的扩展机制:

1.1 插件类型

typescript 复制代码
export enum PluginTypeEnum {
  folder = 'folder',    // 文件夹
  custom = 'custom',    // 自定义插件
  http = 'http'         // HTTP插件
}

export enum PluginSourceEnum {
  personal = 'personal',      // 个人插件
  community = 'community',    // 社区插件
  commercial = 'commercial'   // 商业插件
}

1.2 插件ID规则

typescript 复制代码
// 插件ID规则:
// personal: id (直接使用应用ID)
// community: community-id
// commercial: commercial-id

export async function splitCombinePluginId(id: string) {
  const splitRes = id.split('-');
  if (splitRes.length === 1) {
    // 个人应用ID
    return {
      source: PluginSourceEnum.personal,
      pluginId: id
    };
  }

  const [source, pluginId] = id.split('-') as [PluginSourceEnum, string];
  return { source, pluginId: id };
}

2. 插件注册机制

2.1 插件注册器

核心文件: packages/plugins/register.ts

typescript 复制代码
// 主线程运行的静态插件
const staticPluginList = [
  'getTime',
  'fetchUrl', 
  'feishu',
  'DingTalkWebhook',
  'WeWorkWebhook',
  'google',
  'bing',
  'delay'
];

// Worker线程运行的包插件(包含npm依赖)
const packagePluginList = [
  'mathExprVal',
  'duckduckgo',
  'duckduckgo/search',
  'duckduckgo/searchImg',
  'duckduckgo/searchNews',
  'duckduckgo/searchVideo',
  'drawing',
  'drawing/baseChart',
  'wiki',
  'databaseConnection',
  'Doc2X',
  'Doc2X/PDF2text',
  'searchXNG'
];

export const getCommunityPlugins = () => {
  return Promise.all(
    list.map<Promise<SystemPluginTemplateItemType>>(async (name) => {
      const config = (await import(`./src/${name}/template.json`))?.default;
      
      const isFolder = list.find((item) => item.startsWith(`${name}/`));
      const parentIdList = name.split('/').slice(0, -1);
      const parentId = parentIdList.length > 0 
        ? `${PluginSourceEnum.community}-${parentIdList.join('/')}` 
        : null;

      return {
        ...config,
        id: `${PluginSourceEnum.community}-${name}`,
        isFolder,
        parentId,
        isActive: true,
        isOfficial: true
      };
    })
  );
};

export const getCommunityCb = async () => {
  const result = await Promise.all(
    list.map(async (name) => {
      return {
        name,
        cb: staticPluginList.includes(name)
          ? await loadCommunityModule(name)  // 主线程执行
          : (e: any) => {                    // Worker线程执行
              return runWorker(WorkerNameEnum.systemPluginRun, {
                pluginName: name,
                data: e
              });
            }
      };
    })
  );

  return result.reduce<Record<string, (e: any) => SystemPluginResponseType>>(
    (acc, { name, cb }) => {
      acc[name] = cb;
      return acc;
    }, {}
  );
};

2.2 插件模板结构

每个插件都包含两个核心文件:

  1. template.json - 插件配置和工作流定义
  2. index.ts - 插件执行逻辑(可选,用于静态插件)

示例: packages/plugins/src/getTime/template.json

json 复制代码
{
  "author": "",
  "version": "481",
  "templateType": "tools",
  "name": "获取当前时间",
  "avatar": "core/workflow/template/getTime",
  "intro": "获取用户当前时区的时间。",
  "showStatus": false,
  "isTool": true,
  "weight": 10,
  "workflow": {
    "nodes": [
      {
        "nodeId": "lmpb9v2lo2lk",
        "name": "插件开始",
        "flowNodeType": "pluginInput",
        "inputs": [],
        "outputs": []
      },
      {
        "nodeId": "i7uow4wj2wdp", 
        "name": "插件输出",
        "flowNodeType": "pluginOutput",
        "inputs": [
          {
            "key": "time",
            "valueType": "string",
            "label": "time",
            "value": ["ebLCxU43hHuZ", "rH4tMV02robs"]
          }
        ],
        "outputs": []
      },
      {
        "nodeId": "ebLCxU43hHuZ",
        "name": "HTTP 请求", 
        "flowNodeType": "httpRequest468",
        "inputs": [
          {
            "key": "system_httpReqUrl",
            "value": "getTime"
          },
          {
            "key": "system_httpJsonBody",
            "value": "{\n  \"time\": \"{{cTime}}\"\n}"
          }
        ],
        "outputs": [
          {
            "id": "rH4tMV02robs",
            "key": "time",
            "label": "time",
            "valueType": "string"
          }
        ]
      }
    ],
    "edges": [
      {
        "source": "lmpb9v2lo2lk",
        "target": "ebLCxU43hHuZ"
      },
      {
        "source": "ebLCxU43hHuZ", 
        "target": "i7uow4wj2wdp"
      }
    ]
  }
}

3. 插件执行机制

3.1 插件运行调度

核心文件: packages/service/core/workflow/dispatch/plugin/run.ts

typescript 复制代码
export const dispatchRunPlugin = async (props: RunPluginProps): Promise<RunPluginResponse> => {
  const {
    node: { pluginId, version },
    runningAppInfo,
    query,
    params: { system_forbid_stream = false, ...data }
  } = props;

  // 1. 权限验证
  const pluginData = await authPluginByTmbId({
    appId: pluginId,
    tmbId: runningAppInfo.tmbId,
    per: ReadPermissionVal
  });

  // 2. 获取插件运行时数据
  const plugin = await getChildAppRuntimeById(pluginId, version);

  // 3. 构建运行时节点
  const runtimeNodes = storeNodes2RuntimeNodes(
    plugin.nodes,
    getWorkflowEntryNodeIds(plugin.nodes)
  ).map((node) => {
    // 更新插件输入节点的值
    if (node.flowNodeType === FlowNodeTypeEnum.pluginInput) {
      return {
        ...node,
        showStatus: false,
        inputs: node.inputs.map((input) => ({
          ...input,
          value: data[input.key] ?? input.value
        }))
      };
    }
    return { ...node, showStatus: false };
  });

  // 4. 执行工作流
  const { flowResponses, flowUsages, assistantResponses, runTimes } = await dispatchWorkFlow({
    ...props,
    runningAppInfo: {
      id: String(plugin.id),
      teamId: plugin.teamId || runningAppInfo.teamId,
      tmbId: pluginData?.tmbId || runningAppInfo.tmbId
    },
    variables: runtimeVariables,
    query: getPluginRunUserQuery({
      pluginInputs: getPluginInputsFromStoreNodes(plugin.nodes),
      variables: runtimeVariables,
      files
    }).value,
    runtimeNodes,
    runtimeEdges: initWorkflowEdgeStatus(plugin.edges)
  });

  // 5. 处理输出结果
  const output = flowResponses.find(item => item.moduleType === FlowNodeTypeEnum.pluginOutput);
  const usagePoints = await computedPluginUsage({
    plugin,
    childrenUsage: flowUsages,
    error: !!output?.pluginOutput?.error
  });

  return {
    assistantResponses: system_forbid_stream ? [] : assistantResponses,
    [DispatchNodeResponseKeyEnum.runTimes]: runTimes,
    [DispatchNodeResponseKeyEnum.nodeResponse]: {
      moduleLogo: plugin.avatar,
      totalPoints: usagePoints,
      pluginOutput: output?.pluginOutput
    },
    [DispatchNodeResponseKeyEnum.toolResponses]: output?.pluginOutput,
    ...(output ? output.pluginOutput : {})
  };
};

3.2 插件输入处理

核心文件: packages/service/core/workflow/dispatch/plugin/runInput.ts

typescript 复制代码
export const dispatchPluginInput = (props: PluginInputProps) => {
  const { params, query } = props;
  const { files } = chatValue2RuntimePrompt(query);

  // 处理文件类型参数
  for (const key in params) {
    const val = params[key];
    if (
      Array.isArray(val) &&
      val.every(item => 
        item.type === ChatFileTypeEnum.file || 
        item.type === ChatFileTypeEnum.image
      )
    ) {
      params[key] = val.map(item => item.url);
    }
  }

  return {
    ...params,
    [DispatchNodeResponseKeyEnum.nodeResponse]: {},
    [NodeOutputKeyEnum.userFiles]: files
      .map(item => item?.url ?? '')
      .filter(Boolean)
  };
};

3.3 Worker线程执行

核心文件: packages/plugins/runtime/worker.ts

typescript 复制代码
const loadModule = async (name: string): Promise<(e: any) => SystemPluginResponseType> => {
  const module = await import(`../src/${name}/index`);
  return module.default;
};

parentPort?.on('message', async ({ pluginName, data }: { pluginName: string; data: any }) => {
  try {
    const cb = await loadModule(pluginName);
    parentPort?.postMessage({
      type: 'success',
      data: await cb(data)
    });
  } catch (error) {
    parentPort?.postMessage({
      type: 'error',
      data: error
    });
  }
  
  process.exit();
});

4. HTTP插件系统

4.1 HTTP插件创建

核心文件: projects/app/src/pageComponents/app/list/HttpPluginEditModal.tsx

HTTP插件支持通过OpenAPI Schema自动生成:

typescript 复制代码
const HttpPluginEditModal = ({ defaultPlugin, onClose }) => {
  const [apiData, setApiData] = useState<OpenApiJsonSchema>({ 
    pathData: [], 
    serverPath: '' 
  });

  // 从URL加载API Schema
  const { mutate: onClickUrlLoadApi } = useRequest({
    mutationFn: async () => {
      if (!schemaUrl || (!schemaUrl.startsWith('https://') && !schemaUrl.startsWith('http://'))) {
        return toast({
          title: t('common:plugin.Invalid URL'),
          status: 'warning'
        });
      }

      const schema = await getApiSchemaByUrl(schemaUrl);
      setValue('pluginData.apiSchemaStr', JSON.stringify(schema, null, 2));
    }
  });

  // 解析API Schema
  useEffect(() => {
    (async () => {
      if (!apiSchemaStr) {
        return setApiData({ pathData: [], serverPath: '' });
      }
      try {
        setApiData(await str2OpenApiSchema(apiSchemaStr));
      } catch (err) {
        toast({
          status: 'warning',
          title: t('common:plugin.Invalid Schema')
        });
        setApiData({ pathData: [], serverPath: '' });
      }
    })();
  }, [apiSchemaStr]);

  return (
    <MyModal>
      {/* 基本信息设置 */}
      <Input {...register('name')} />
      <Textarea {...register('intro')} />
      
      {/* OpenAPI Schema输入 */}
      <Textarea {...register('pluginData.apiSchemaStr')} />
      
      {/* 自定义请求头 */}
      <Table>
        {customHeaders.map((item, index) => (
          <Tr key={index}>
            <Td>
              <HttpInput 
                value={item.key}
                onBlur={(val) => updateHeaders(index, 'key', val)}
              />
            </Td>
            <Td>
              <HttpInput 
                value={item.value}
                onBlur={(val) => updateHeaders(index, 'value', val)}
              />
            </Td>
          </Tr>
        ))}
      </Table>
      
      {/* API列表预览 */}
      <Table>
        {apiData.pathData?.map((item, index) => (
          <Tr key={index}>
            <Td>{item.name}</Td>
            <Td>{item.description}</Td>
            <Td>{item.method}</Td>
            <Td>{item.path}</Td>
          </Tr>
        ))}
      </Table>
    </MyModal>
  );
};

4.2 cURL解析功能

核心文件: projects/app/src/web/core/app/templates.ts

支持从cURL命令自动生成插件:

typescript 复制代码
export const parsePluginFromCurlString = (curl: string) => {
  const { url, method, headers, body, params, bodyArray } = parseCurl(curl);

  // 解析参数生成插件输入
  const allInputs = Array.from(
    new Map([...params, ...bodyArray].map(item => [item.key, item])).values()
  );
  
  const formatPluginStartInputs = allInputs.map(item => {
    const valueType = item.value === null ? 'string' : typeof item.value;
    const valueTypeMap = {
      string: {
        renderTypeList: [FlowNodeInputTypeEnum.input, FlowNodeInputTypeEnum.reference],
        valueType: WorkflowIOValueTypeEnum.string,
        isToolType: true
      },
      number: {
        renderTypeList: [FlowNodeInputTypeEnum.numberInput, FlowNodeInputTypeEnum.reference],
        valueType: WorkflowIOValueTypeEnum.number,
        isToolType: true
      },
      boolean: {
        renderTypeList: [FlowNodeInputTypeEnum.switch, FlowNodeInputTypeEnum.reference],
        valueType: WorkflowIOValueTypeEnum.boolean,
        isToolType: true
      }
    };

    return {
      renderTypeList: valueTypeMap[valueType].renderTypeList,
      valueType: valueTypeMap[valueType].valueType,
      key: item.key,
      label: item.key,
      required: false,
      toolDescription: item.key
    };
  });

  // 生成HTTP请求节点配置
  const referenceBody = Object.entries(JSON.parse(body)).reduce((acc, [key, value]) => {
    acc[key] = typeof value === 'string' 
      ? `###{{$pluginInput.${key}$}}###` 
      : `{{$pluginInput.${key}$}}`;
    return acc;
  }, {} as Record<string, any>);

  return {
    nodes: [
      // 插件输入节点
      {
        nodeId: 'pluginInput',
        name: '插件开始',
        flowNodeType: FlowNodeTypeEnum.pluginInput,
        inputs: formatPluginStartInputs,
        outputs: formatPluginStartOutputs
      },
      // HTTP请求节点
      {
        nodeId: 'httpRequest',
        name: 'HTTP 请求',
        flowNodeType: FlowNodeTypeEnum.httpRequest468,
        inputs: [
          {
            key: 'system_httpMethod',
            value: method
          },
          {
            key: 'system_httpReqUrl', 
            value: url
          },
          {
            key: 'system_httpHeader',
            value: referenceHeaders
          },
          {
            key: 'system_httpJsonBody',
            value: referenceBodyStr
          }
        ]
      },
      // 插件输出节点
      {
        nodeId: 'pluginOutput',
        name: '插件输出',
        flowNodeType: FlowNodeTypeEnum.pluginOutput
      }
    ],
    edges: [
      { source: 'pluginInput', target: 'httpRequest' },
      { source: 'httpRequest', target: 'pluginOutput' }
    ]
  };
};

5. 插件管理系统

5.1 插件控制器

核心文件: packages/service/core/app/plugin/controller.ts

typescript 复制代码
// 获取插件预览节点数据
export async function getChildAppPreviewNode({ id }: { id: string }): Promise<FlowNodeTemplateType> {
  const app = await (async () => {
    const { source, pluginId } = await splitCombinePluginId(id);

    if (source === PluginSourceEnum.personal) {
      // 个人插件
      const item = await MongoApp.findById(id).lean();
      const version = await getAppLatestVersion(id, item);
      
      return {
        id: String(item._id),
        teamId: String(item.teamId),
        name: item.name,
        avatar: item.avatar,
        workflow: {
          nodes: version.nodes,
          edges: version.edges,
          chatConfig: version.chatConfig
        },
        templateType: FlowNodeTemplateTypeEnum.teamApp
      };
    } else {
      // 系统插件
      return getSystemPluginTemplateById(pluginId);
    }
  })();

  const isPlugin = !!app.workflow.nodes.find(
    node => node.flowNodeType === FlowNodeTypeEnum.pluginInput
  );

  return {
    id: getNanoid(),
    pluginId: app.id,
    templateType: app.templateType,
    flowNodeType: isPlugin ? FlowNodeTypeEnum.pluginModule : FlowNodeTypeEnum.appModule,
    avatar: app.avatar,
    name: app.name,
    intro: app.intro,
    showStatus: app.showStatus,
    isTool: true,
    version: app.version,
    ...(isPlugin
      ? pluginData2FlowNodeIO({ nodes: app.workflow.nodes })
      : appData2FlowNodeIO({ chatConfig: app.workflow.chatConfig }))
  };
}

// 获取插件运行时数据
export async function getChildAppRuntimeById(
  id: string,
  versionId?: string
): Promise<PluginRuntimeType> {
  const app = await (async () => {
    const { source, pluginId } = await splitCombinePluginId(id);

    if (source === PluginSourceEnum.personal) {
      const item = await MongoApp.findById(id).lean();
      const version = await getAppVersionById({ appId: id, versionId, app: item });
      
      return {
        id: String(item._id),
        teamId: String(item.teamId),
        name: item.name,
        avatar: item.avatar,
        workflow: {
          nodes: version.nodes,
          edges: version.edges,
          chatConfig: version.chatConfig
        }
      };
    } else {
      return getSystemPluginTemplateById(pluginId, versionId);
    }
  })();

  return {
    id: app.id,
    teamId: app.teamId,
    name: app.name,
    avatar: app.avatar,
    showStatus: app.showStatus,
    currentCost: app.currentCost,
    nodes: app.workflow.nodes,
    edges: app.workflow.edges,
    hasTokenFee: app.hasTokenFee
  };
}

5.2 插件加载机制

核心文件: projects/app/src/service/core/app/plugin.ts

typescript 复制代码
export const getSystemPlugins = async (refresh = false) => {
  if (isProduction && global.systemPlugins && global.systemPlugins.length > 0 && !refresh)
    return cloneDeep(global.systemPlugins);

  try {
    if (!global.systemPlugins) {
      global.systemPlugins = [];
    }

    // 根据环境加载不同的插件
    global.systemPlugins = FastGPTProUrl
      ? await getCommercialPlugins()  // 商业版插件
      : await getCommunityPlugins();  // 社区版插件

    addLog.info(`Load system plugin successfully: ${global.systemPlugins.length}`);
    return cloneDeep(global.systemPlugins);
  } catch (error) {
    global.systemPlugins = undefined;
    return Promise.reject(error);
  }
};

export const getSystemPluginCb = async (refresh = false) => {
  if (
    isProduction &&
    global.systemPluginCb &&
    Object.keys(global.systemPluginCb).length > 0 &&
    !refresh
  )
    return global.systemPluginCb;

  try {
    global.systemPluginCb = {};
    await getSystemPlugins(refresh);
    
    // 根据环境获取不同的回调函数
    global.systemPluginCb = FastGPTProUrl 
      ? await getCommercialCb()  // 商业版回调
      : await getCommunityCb();  // 社区版回调
      
    return global.systemPluginCb;
  } catch (error) {
    return Promise.reject(error);
  }
};

6. 插件开发示例

6.1 简单插件示例

文件: packages/plugins/src/getTime/index.ts

typescript 复制代码
type Props = {
  time: string;
};
type Response = Promise<{
  time: string;
}>;

const main = async ({ time }: Props): Response => {
  return {
    time
  };
};

export default main;

6.2 复杂插件示例

文件: packages/plugins/src/mathExprVal/index.ts

typescript 复制代码
import { evaluate } from 'mathjs';

type Props = {
  expression: string;
};
type Response = Promise<{
  result: number | string;
  error?: string;
}>;

const main = async ({ expression }: Props): Response => {
  try {
    if (!expression) {
      return {
        result: '',
        error: 'Expression is required'
      };
    }

    const result = evaluate(expression);
    
    return {
      result: typeof result === 'number' ? result : String(result)
    };
  } catch (error) {
    return {
      result: '',
      error: `Math evaluation error: ${error.message}`
    };
  }
};

export default main;

7. 插件系统架构总结

7.1 核心组件

  1. 插件注册器 - 负责插件的发现和注册
  2. 插件控制器 - 管理插件的生命周期
  3. 插件调度器 - 执行插件工作流
  4. Worker管理器 - 处理需要隔离执行的插件
  5. HTTP插件系统 - 支持OpenAPI规范的HTTP插件

7.2 执行流程

  1. 插件注册 → 系统启动时扫描并注册所有插件
  2. 插件发现 → 用户在工作流中选择插件
  3. 参数绑定 → 将用户输入绑定到插件参数
  4. 插件执行 → 根据插件类型选择执行方式
  5. 结果返回 → 处理插件输出并返回结果

7.3 关键文件路径

核心系统文件
  • packages/plugins/register.ts - 插件注册器
  • packages/plugins/runtime/worker.ts - Worker执行器
  • packages/service/core/app/plugin/controller.ts - 插件控制器
  • packages/service/core/workflow/dispatch/plugin/run.ts - 插件调度器
插件源码目录
  • packages/plugins/src/ - 社区插件源码
  • plugins/model/ - 模型插件
  • plugins/webcrawler/ - 网络爬虫插件
前端界面文件
  • projects/app/src/pageComponents/app/list/HttpPluginEditModal.tsx - HTTP插件编辑
  • projects/app/src/service/core/app/plugin.ts - 插件服务

这套插件系统提供了完整的插件开发、注册、管理和执行能力,支持从简单的工具函数到复杂的HTTP API集成。

相关推荐
Elastic 中国社区官方博客3 小时前
带地图的 RAG:多模态 + 地理空间 在 Elasticsearch 中
大数据·人工智能·elasticsearch·搜索引擎·ai·语言模型·全文检索
coder_pig3 小时前
👦抠腚男孩的AI学习之旅 | 7、LangChain (三) - 实战:知识库问答机器人 (RAG )
langchain·aigc·ai编程
考拉悠然科技3 小时前
华为X考拉悠然 联合发布悠然智擎城市交通拥堵治理空间智能体方案
ai
动能小子ohhh5 小时前
AI智能体(Agent)大模型入门【2】--基于llamaindx部署本地的聊天模型。
人工智能·python·aigc·ai编程
CoderJia程序员甲5 小时前
GitHub 热榜项目 - 日榜(2025-09-11)
ai·开源·github·ai编程·github热榜
该用户已不存在5 小时前
腾讯放大招,Claude Code 国产平替发布
人工智能·ai编程
自信的小螺丝钉6 小时前
【AI知识点】模型训练优化之——混合精度训练
人工智能·ai·大模型·混合精度训练
程序员老刘6 小时前
CTO紧急叫停AI编程!不是技术倒退,而是...
flutter·ai编程
.NET修仙日记6 小时前
Visual Studio 2026 震撼发布!AI 智能编程时代正式来临
ide·微软·ai编程·开发工具·visual studio·编程革命