【实战教程】基于 React Flow 搭建智能体组件:从环境配置到核心节点开发指南

本文为《React Agent:从零开始构建 AI 智能体》专栏系列文章。 专栏地址:https://blog.csdn.net/suiyingy/category_12933485.html。项目地址:https://gitee.com/fgai/react-agent(含完整代码示​例与实战源)。完整介绍:数据分析智能体构建实战:LLM 工具调用 + 记忆管理模块代码实现全解析(附 Qwen-Turbo 代码实现)-CSDN博客

在大模型浪潮奔涌向前的当下,智能体技术的应用愈发广泛且深入,而高效搭建智能体组件成为开发者亟待解决的关键问题。React Flow 作为一款强大的可视化流程编辑库,凭借其灵活的布局与交互能力,为智能体组件的构建提供了新的思路与方法。本章将围绕基于 React Flow 搭建的智能体基本组件示例展开,从输入节点、输出节点等多个核心节点切入,逐步探索智能体组件搭建的奥秘。

本节主要介绍环境搭建,从下一节开始逐一介绍节点组件创建:输入节点、输出节点、网络节点、代码节点、工具节点以及大模型智能对话节点等。

1 创建 React Flow 项目

我们首先按照下面步骤创建一个 agent-nodes 项目:

复制代码
# 创建项目
npx create-react-app agent-nodes
cd agent-nodes
# 考虑兼容性,安装18版本
npm install react@18 react-dom@18
npm install reactflow react-flow-renderer
# 安装 tailwindcss
npm install -D [email protected]
npx tailwindcss init
# 安装 Font Awesome 图标库
npm install @fortawesome/react-fontawesome @fortawesome/free-solid-svg-icons

修改 agent-nodes/tailwind.config.js文件,添加 tailwindcss 样式。

复制代码
module.exports = {
  content: [
    "./src/**/*.{js,jsx,ts,tsx}",
    // 包含所有组件文件路径
  ],
  theme: { extend: {} },
  plugins: [
    // require('@tailwindcss/forms'), // 如果使用表单插件
  ],
}

进一步在agent-nodes/src/index.css文件中引入样式。

复制代码
@tailwind base;
@tailwind components;
@tailwind utilities;

body {
  margin: 0;
  font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen',
    'Ubuntu', 'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue',
    sans-serif;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
}

code {
  font-family: source-code-pro, Menlo, Monaco, Consolas, 'Courier New',
    monospace;
}

创建成功后 agent-nodes/packge.json 中内容如下:

复制代码
{
  "name": "agent-nodes",
  "version": "0.1.0",
  "private": true,
  "dependencies": {
    "@fortawesome/free-solid-svg-icons": "^6.7.2",
    "@fortawesome/react-fontawesome": "^0.2.2",
    "@testing-library/dom": "^10.4.0",
    "@testing-library/jest-dom": "^6.6.3",
    "@testing-library/react": "^16.3.0",
    "@testing-library/user-event": "^13.5.0",
    "react": "^18.3.1",
    "react-dom": "^18.3.1",
    "react-flow-renderer": "^10.3.17",
    "react-scripts": "5.0.1",
    "reactflow": "^11.11.4",
    "web-vitals": "^2.1.4"
  },
  "scripts": {
    "start": "react-scripts start",
    "build": "react-scripts build",
    "test": "react-scripts test",
    "eject": "react-scripts eject"
  },
  "eslintConfig": {
    "extends": [
      "react-app",
      "react-app/jest"
    ]
  },
  "browserslist": {
    "production": [
      ">0.2%",
      "not dead",
      "not op_mini all"
    ],
    "development": [
      "last 1 chrome version",
      "last 1 firefox version",
      "last 1 safari version"
    ]
  },
  "devDependencies": {
    "tailwindcss": "^3.4.17"
  }
}

此时,我们可通过 npm start 启动项目,并且在浏览器看到 React 默认页面。

2 主页搭建

我们设计在 App.js 主页面用于展示后续不同类型的节点,程序如下所示。

复制代码
// src/App.js
import React, { useState, useCallback, useEffect } from 'react';
import ReactFlow, {
  applyNodeChanges,
  applyEdgeChanges,
  addEdge,
  Background,
  useReactFlow,
  ReactFlowProvider
} from 'reactflow';
import 'reactflow/dist/style.css';

const nodeTypes = {
};


function App() {
  const { screenToFlowPosition } = useReactFlow();
  const [nodes, setNodes] = useState([]);
  const [edges, setEdges] = useState([]);
  const [contextMenu, setContextMenu] = useState(null);

  // 双击节点处理
  const onNodeDoubleClick = useCallback((_, node) => {
   
  }, []);

  // 右键菜单
  const handlePaneContextMenu = (event) => {
    event.preventDefault();
    const position = screenToFlowPosition({
      x: event.clientX,
      y: event.clientY,
    });
    setContextMenu({
      flowPosition: position,
      screenPosition: { x: event.clientX, y: event.clientY }
    });
  };
  // 关闭菜单
  const closeContextMenu = useCallback(() => {
    setContextMenu(null);
  }, []);
  useEffect(() => {
    if (contextMenu) {
      document.addEventListener('click', closeContextMenu);
      return () => document.removeEventListener('click', closeContextMenu);
    }
  }, [contextMenu, closeContextMenu]);

  // 运行按钮
  const handleRun = async () => {
    
  };

  const onNodesChange = useCallback(
    (changes) => setNodes((nds) => applyNodeChanges(changes, nds)),
    []
  );

  const onEdgesChange = useCallback(
    (changes) => setEdges((eds) => applyEdgeChanges(changes, eds)),
    []
  );

  const onConnect = useCallback(
    (params) => setEdges((eds) => addEdge(
      { 
        ...params, 
        animated: true,
        style: { stroke: '#666' },
      }, 
      eds
    )),
    []
  );

  return (
    <div style={{ height: '100vh', position: 'relative' }}>
      <button
        onClick={handleRun}
        style={{
          position: 'absolute',
          top: 20,
          left: '50%',
          transform: 'translateX(-50%)',
          zIndex: 10,
          padding: '8px 20px',
          background: '#4CAF50',
          color: 'white',
          border: 'none',
          borderRadius: 20,
          cursor: 'pointer',
          boxShadow: '0 2px 4px rgba(0,0,0,0.2)',
          fontSize: 14,
          fontWeight: 'bold',
        }}
      >
        ▶ 运行处理
      </button>

      <ReactFlow
        nodes={nodes}
        edges={edges}
        onNodesChange={onNodesChange}
        onEdgesChange={onEdgesChange}
        onConnect={onConnect}
        onPaneContextMenu={handlePaneContextMenu}
        onNodeDoubleClick={onNodeDoubleClick}
        nodeTypes={nodeTypes}
        fitView
        connectionRadius={20}
        snapToGrid={true}
        snapGrid={[15, 15]}
        defaultEdgeOptions={{
          type: 'smoothstep',
          style: {
            strokeWidth: 2,
          },
        }}
      >
        <Background 
          color="#ddd"
          gap={25}
          style={{ backgroundColor: '#f8f9fa' }}
        />
      </ReactFlow>

      {contextMenu && (
        <div
          style={{
            position: 'fixed',
            top: contextMenu.screenPosition.y,
            left: contextMenu.screenPosition.x,
            background: 'white',
            boxShadow: '0 2px 4px rgba(0,0,0,0.2)',
            borderRadius: 4,
            zIndex: 10,
          }}
          onClick={(e) => e.stopPropagation()}
        >
          <div
            style={{
              padding: '8px 16px',
              cursor: 'pointer',
              transition: 'background 0.2s',
              ':hover': {
                background: '#f5f5f5',
              },
            }}
            // onClick={createInputNode}
          >
            添加节点
          </div>
        </div>
      )}

    </div>
  );
}

export default function FlowWrapper() {
  return (
    <ReactFlowProvider>
      <App />
    </ReactFlowProvider>
  );
}

将以上程序复制到 src/app.js 文件,npm start 运行程序后页面如下图所示。页面上方居中位置有一个"运行处理"按钮。此按钮样式为绿色背景、白色文字,有圆角和阴影效果,视觉上比较突出。

图1 主页基本页面

在初始化时,节点和边的数据为空,后续可以通过交互进行添加和修改。流程图具有一些实用的配置,如支持网格吸附,这使得节点移动时会自动对齐到网格,方便布局;边默认采用平滑曲线样式,线条宽度为 2px,并且连接时会有动画效果。此外,还添加了带有网格的背景,增强了视觉辨识度。

当在流程图空白处点击右键时,会弹出一个右键菜单,菜单中有"添加节点"的选项,可用于后续添加节点。如果在菜单以外的区域点击,菜单会自动关闭。同时,程序预留了节点双击处理和运行按钮点击处理的逻辑,可根据实际需求进一步完善。

立即关注获取最新动态

点击订阅《React Agent 开发专栏》,每周获取智能体开发深度教程。项目代码持续更新至React Agent 开源仓库,欢迎 Star 获取实时更新通知!FGAI 人工智能平台FGAI 人工智能平台 https://www.botaigc.cn/

相关推荐
kovlistudio11 分钟前
大模型应用开发第三讲:大模型是Agent的“大脑”,提供通用推理能力(如GPT-4、Claude 3)
人工智能·深度学习·学习·机器学习
我是一言31 分钟前
基于BERT和GPT2的实现来理解Transformer的结构和原理
人工智能·bert·transformer·gpt2
油泼辣子多加32 分钟前
【大模型】Bert
人工智能·深度学习·bert
geneculture34 分钟前
亚当·斯密思想精髓的数学建模与形式化表征
人工智能·数学建模·课程设计·信息与通信·融智学的重要应用·富国论·道德情操论
天下琴川1 小时前
GitHub开源|AI顶会论文中文翻译PDF合集(gpt-translated-pdf-zh)
人工智能·gpt·pdf
Sonhhxg_柒2 小时前
【NLP】将 LangChain 与模型上下文协议 (MCP) 结合使用
人工智能·langchain
layneyao2 小时前
深度学习入门:从零搭建你的第一个神经网络
人工智能·深度学习·神经网络
xMathematics2 小时前
ORB-SLAM2学习笔记:ORBextractor::operator()函数的逐行解析
人工智能·计算机视觉·机器人·无人机·slam
阿里云云原生2 小时前
Spring AI Alibaba 发布企业级 MCP 分布式部署方案
人工智能·分布式·spring
nenchoumi31192 小时前
Mujoco 学习系列(番外一)MJX 部署与配置
人工智能·学习·机器学习·机器人