论文流程图mermaid解决方案

1.用ai根据论文生成 mermaid 代码

  1. 把代码黏贴到下面本地html打开的网页中, 渲染, 截图即可

3.新建txt, 改动后缀html, 黏贴保存,双击运行

源代码:

html 复制代码
<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>论文流程图设计器(可拖拽+导出)</title>
    <!-- Include required libraries -->
    <script src="https://cdn.jsdelivr.net/npm/mermaid@10/dist/mermaid.min.js"></script>
    <script src="https://d3js.org/d3.v7.min.js"></script>
    <script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/html2canvas.min.js"></script>
    <script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/interact.min.js"></script>
    <style>
        body {
            font-family: Arial, sans-serif;
            margin: 0;
            padding: 10px;
            background-color: #f5f5f5;
            overflow: hidden;
        }
        .container {
            display: grid;
            grid-template-columns: 280px 1fr;
            gap: 15px;
            height: 95vh;
        }
        .control-panel {
            background: white;
            padding: 15px;
            border-radius: 8px;
            box-shadow: 0 2px 5px rgba(0,0,0,0.1);
            overflow-y: auto;
        }
        .preview-panel {
            background: white;
            border-radius: 8px;
            box-shadow: 0 2px 5px rgba(0,0,0,0.1);
            position: relative;
            overflow: hidden;
        }
        textarea {
            width: 100%;
            height: 200px;
            padding: 10px;
            border: 1px solid #ddd;
            border-radius: 4px;
            font-family: monospace;
            resize: vertical;
            margin-bottom: 15px;
        }
        .control-group {
            margin-bottom: 15px;
            padding: 10px;
            background: #f8f9fa;
            border-radius: 6px;
        }
        .control-item {
            display: flex;
            align-items: center;
            margin: 8px 0;
        }
        label {
            width: 100px;
            font-size: 14px;
            color: #555;
        }
        input[type="range"], input[type="color"], select {
            flex: 1;
            margin-left: 10px;
        }
        button {
            background: #2196F3;
            color: white;
            border: none;
            padding: 8px 15px;
            border-radius: 4px;
            cursor: pointer;
            transition: all 0.2s;
            margin-right: 5px;
            margin-bottom: 5px;
        }
        button:hover {
            opacity: 0.9;
            transform: translateY(-1px);
        }
        #diagram-container {
            background: white;
            padding: 20px;
            border-radius: 8px;
            transform-origin: 0 0;
            min-width: fit-content;
            min-height: fit-content;
            position: absolute;
            border: 2px dashed #ccc;
            box-shadow: 0 0 10px rgba(0,0,0,0.05);
        }
        #zoom-controls {
            position: absolute;
            bottom: 10px;
            right: 10px;
            background: rgba(255,255,255,0.8);
            padding: 5px;
            border-radius: 4px;
            box-shadow: 0 1px 3px rgba(0,0,0,0.2);
            z-index: 10;
        }
        #zoom-controls button {
            padding: 5px 10px;
            margin: 2px;
        }
        #drag-toggle {
            position: absolute;
            top: 10px;
            right: 10px;
            z-index: 100;
        }
        .draggable-node {
            cursor: move;
        }
        #preview-wrapper {
            width: 100%;
            height: 100%;
            overflow: hidden;
            position: relative;
        }
        .dragging {
            opacity: 0.7;
        }
        #canvas-container {
            width: 100%;
            height: 100%;
            position: relative;
            overflow: auto;
        }
    </style>
</head>
<body>
    <div class="container">
        <!-- Left control panel -->
        <div class="control-panel">
            <h2>流程图代码</h2>
            <textarea id="mermaid-code">
graph TD
    Start([开始]) --> A[第一步]
    A --> B{决策点}
    B -->|是| C[执行操作1]
    B -->|否| D[执行操作2]
    C --> E([结束])
    D --> E
            </textarea>

            <!-- Layout controls -->
            <div class="control-group">
                <h3>布局设置</h3>
                <div class="control-item">
                    <label>节点间距</label>
                    <input type="range" id="nodeSpacing" min="20" max="100" value="40">
                </div>
                <div class="control-item">
                    <label>层级间距</label>
                    <input type="range" id="rankSpacing" min="20" max="100" value="50">
                </div>
                <div class="control-item">
                    <label>菱形扁平度</label>
                    <input type="range" id="shapeAspectRatio" min="0.5" max="2" step="0.1" value="1">
                </div>
            </div>

            <!-- Node style controls -->
            <div class="control-group">
                <h3>节点样式</h3>
                <div class="control-item">
                    <label>填充颜色</label>
                    <input type="color" id="nodeFill" value="#ffffff">
                </div>
                <div class="control-item">
                    <label>边框颜色</label>
                    <input type="color" id="nodeBorder" value="#333333">
                </div>
                <div class="control-item">
                    <label>边框粗细</label>
                    <input type="range" id="borderWidth" min="1" max="5" value="2">
                </div>
            </div>

            <!-- Edge style controls -->
            <div class="control-group">
                <h3>连线样式</h3>
                <div class="control-item">
                    <label>连线颜色</label>
                    <input type="color" id="lineColor" value="#666666">
                </div>
                <div class="control-item">
                    <label>连线粗细</label>
                    <input type="range" id="lineWidth" min="1" max="10" value="2">
                </div>
                <div class="control-item">
                    <label>连线类型</label>
                    <select id="lineType">
                        <option value="basis">曲线</option>
                        <option value="linear">直线</option>
                    </select>
                </div>
            </div>

            <!-- Text style controls -->
            <div class="control-group">
                <h3>文字样式</h3>
                <div class="control-item">
                    <label>字体大小</label>
                    <input type="range" id="fontSize" min="12" max="24" value="16">
                </div>
                <div class="control-item">
                    <label>字体家族</label>
                    <select id="fontFamily">
                        <option value="Arial">Arial</option>
                        <option value="Helvetica">Helvetica</option>
                        <option value="Times New Roman">Times New Roman</option>
                    </select>
                </div>
            </div>

            <button id="renderBtn">立即渲染</button>
            <button id="exportBtn">导出PNG</button>
            <button id="copyBtn">复制到剪贴板</button>
            <button id="saveBtn">保存草稿</button>
            <button id="loadBtn">加载草稿</button>
        </div>

        <!-- Right preview panel -->
        <div class="preview-panel">
            <button id="drag-toggle">🔓 启用拖拽</button>
            <div id="preview-wrapper">
                <div id="canvas-container">
                    <div id="diagram-container"></div>
                </div>
            </div>
            <div id="zoom-controls">
                <button id="zoom-in">+</button>
                <button id="zoom-out">-</button>
                <button id="zoom-reset">重置</button>
            </div>
        </div>
    </div>

    <script>
        // Configuration state
        let config = {
            nodeSpacing: 40,
            rankSpacing: 50,
            shapeAspectRatio: 1,
            nodeFill: '#ffffff',
            nodeBorder: '#333333',
            borderWidth: 2,
            lineColor: '#666666',
            lineWidth: 2,
            lineType: 'basis',
            fontSize: 16,
            fontFamily: 'Arial',
            isDraggable: false,
            scale: 1.0,
            posX: 0,
            posY: 0
        };

        // Initialize Mermaid
        mermaid.initialize({ 
            startOnLoad: false,
            theme: 'base',
            themeVariables: {
                primaryColor: config.nodeFill,
                primaryBorderColor: config.nodeBorder,
                primaryBorderWidth: `${config.borderWidth}px`,
                lineColor: config.lineColor,
                lineWidth: `${config.lineWidth}px`,
                fontSize: `${config.fontSize}px`,
                fontFamily: config.fontFamily
            },
            flowchart: {
                nodeSpacing: config.nodeSpacing,
                rankSpacing: config.rankSpacing,
                curve: config.lineType,
                htmlLabels: true
            }
        });

        // DOM elements
        const elements = {
            code: document.getElementById('mermaid-code'),
            container: document.getElementById('diagram-container'),
            canvasContainer: document.getElementById('canvas-container'),
            previewPanel: document.querySelector('.preview-panel'),
            previewWrapper: document.getElementById('preview-wrapper'),
            controls: {
                nodeSpacing: document.getElementById('nodeSpacing'),
                rankSpacing: document.getElementById('rankSpacing'),
                shapeAspectRatio: document.getElementById('shapeAspectRatio'),
                nodeFill: document.getElementById('nodeFill'),
                nodeBorder: document.getElementById('nodeBorder'),
                borderWidth: document.getElementById('borderWidth'),
                lineColor: document.getElementById('lineColor'),
                lineWidth: document.getElementById('lineWidth'),
                lineType: document.getElementById('lineType'),
                fontSize: document.getElementById('fontSize'),
                fontFamily: document.getElementById('fontFamily')
            },
            buttons: {
                render: document.getElementById('renderBtn'),
                export: document.getElementById('exportBtn'),
                copy: document.getElementById('copyBtn'),
                save: document.getElementById('saveBtn'),
                load: document.getElementById('loadBtn'),
                dragToggle: document.getElementById('drag-toggle'),
                zoomIn: document.getElementById('zoom-in'),
                zoomOut: document.getElementById('zoom-out'),
                zoomReset: document.getElementById('zoom-reset')
            }
        };

        // Initialize controls
        function initControls() {
            Object.keys(elements.controls).forEach(key => {
                elements.controls[key].addEventListener('input', updateConfig);
            });

            elements.controls.nodeSpacing.value = config.nodeSpacing;
            elements.controls.rankSpacing.value = config.rankSpacing;
            elements.controls.shapeAspectRatio.value = config.shapeAspectRatio;
            elements.controls.nodeFill.value = config.nodeFill;
            elements.controls.nodeBorder.value = config.nodeBorder;
            elements.controls.borderWidth.value = config.borderWidth;
            elements.controls.lineColor.value = config.lineColor;
            elements.controls.lineWidth.value = config.lineWidth;
            elements.controls.lineType.value = config.lineType;
            elements.controls.fontSize.value = config.fontSize;
            elements.controls.fontFamily.value = config.fontFamily;

            elements.buttons.render.addEventListener('click', renderDiagram);
            elements.buttons.export.addEventListener('click', exportDiagram);
            elements.buttons.copy.addEventListener('click', copyToClipboard);
            elements.buttons.save.addEventListener('click', saveDraft);
            elements.buttons.load.addEventListener('click', loadDraft);
            elements.buttons.dragToggle.addEventListener('click', toggleDrag);
            elements.buttons.zoomIn.addEventListener('click', () => zoom(0.1));
            elements.buttons.zoomOut.addEventListener('click', () => zoom(-0.1));
            elements.buttons.zoomReset.addEventListener('click', resetView);

            // Make canvas container draggable
            interact(elements.canvasContainer)
                .draggable({
                    inertia: true,
                    listeners: {
                        move: dragMoveListener
                    }
                });
        }

        function dragMoveListener(event) {
            config.posX += event.dx;
            config.posY += event.dy;
            updateCanvasPosition();
        }

        function updateCanvasPosition() {
            elements.canvasContainer.scrollLeft -= config.posX;
            elements.canvasContainer.scrollTop -= config.posY;
            config.posX = 0;
            config.posY = 0;
        }

        // Update configuration
        function updateConfig(e) {
            config[e.target.id] = e.target.type === 'range' ? 
                parseFloat(e.target.value) : e.target.value;
            
            mermaid.initialize({
                theme: 'base',
                themeVariables: {
                    primaryColor: config.nodeFill,
                    primaryBorderColor: config.nodeBorder,
                    primaryBorderWidth: `${config.borderWidth}px`,
                    lineColor: config.lineColor,
                    lineWidth: `${config.lineWidth}px`,
                    fontSize: `${config.fontSize}px`,
                    fontFamily: config.fontFamily
                },
                flowchart: {
                    nodeSpacing: config.nodeSpacing,
                    rankSpacing: config.rankSpacing,
                    curve: config.lineType,
                    htmlLabels: true
                }
            });

            renderDiagram();
        }

        // Render flowchart
        function renderDiagram() {
            try {
                elements.container.innerHTML = '';
                const tempDiv = document.createElement('div');
                tempDiv.className = 'mermaid';
                tempDiv.textContent = elements.code.value;
                elements.container.appendChild(tempDiv);
                
                mermaid.run({
                    nodes: [tempDiv],
                    suppressErrors: false
                }).then(() => {
                    applyShapeAspectRatio();
                    updateEdgeStyles();
                    if (config.isDraggable) {
                        makeNodesDraggable();
                    }
                    centerDiagram();
                }).catch(e => {
                    console.error('Mermaid render error:', e);
                    elements.container.innerHTML = `<div style="color:red;padding:20px;">Render error: ${e.message}</div>`;
                });
            } catch (error) {
                console.error('Render exception:', error);
                elements.container.innerHTML = `<div style="color:red;padding:20px;">System error: ${error.message}</div>`;
            }
        }

        // Update edge styles
        function updateEdgeStyles() {
            const edges = d3.selectAll('.mermaid .edgePath path');
            edges.attr('stroke-width', config.lineWidth);
            edges.attr('stroke', config.lineColor);
        }

        // Apply diamond shape flattening
        function applyShapeAspectRatio() {
            if (config.shapeAspectRatio !== 1) {
                d3.selectAll('.mermaid .node polygon').each(function() {
                    const poly = d3.select(this);
                    const box = this.getBBox();
                    const centerX = box.x + box.width / 2;
                    const centerY = box.y + box.height / 2;
                    
                    const points = poly.attr('points').split(' ').map(p => {
                        const [x, y] = p.split(',').map(Number);
                        return {x, y};
                    });
                    
                    const newPoints = points.map(p => {
                        const dx = p.x - centerX;
                        const dy = p.y - centerY;
                        return `${centerX + dx * config.shapeAspectRatio},${centerY + dy / config.shapeAspectRatio}`;
                    }).join(' ');
                    
                    poly.attr('points', newPoints);
                    
                    // 更新与这个节点相连的所有边
                    const nodeId = d3.select(this.parentNode).attr('id');
                    if (nodeId) {
                        updateConnectedEdges(d3.select(this.parentNode));
                    }
                });
            }
        }

        // Center the diagram
        function centerDiagram() {
            const svg = elements.container.querySelector('svg');
            if (svg) {
                const containerWidth = elements.previewWrapper.clientWidth;
                const containerHeight = elements.previewWrapper.clientHeight;
                const svgWidth = svg.clientWidth;
                const svgHeight = svg.clientHeight;
                
                // 计算居中位置并考虑缩放
                const targetLeft = (containerWidth - svgWidth * config.scale) / 2;
                const targetTop = (containerHeight - svgHeight * config.scale) / 2;
                
                // 调整容器位置
                elements.container.style.left = `${Math.max(0, targetLeft)}px`;
                elements.container.style.top = `${Math.max(0, targetTop)}px`;
                
                // 应用缩放变换
                elements.container.style.transform = `scale(${config.scale})`;
                
                // 重置滚动位置
                elements.canvasContainer.scrollLeft = 0;
                elements.canvasContainer.scrollTop = 0;
            }
        }

        // Make nodes draggable
        function makeNodesDraggable() {
            const svg = d3.select(elements.container.querySelector('svg'));
            
            svg.selectAll('.node').each(function() {
                const node = d3.select(this);
                const shape = node.select('rect, polygon');
                if (shape.empty()) return;

                let initialTransform = node.attr('transform') || 'translate(0,0)';
                let [x, y] = initialTransform
                    .replace('translate(', '')
                    .replace(')', '')
                    .split(',')
                    .map(Number);

                node.call(d3.drag()
                    .on('start', function() {
                        node.classed('dragging', true);
                    })
                    .on('drag', function(event) {
                        x += event.dx;
                        y += event.dy;
                        node.attr('transform', `translate(${x}, ${y})`);
                        updateConnectedEdges(node);
                    })
                    .on('end', function() {
                        node.classed('dragging', false);
                    })
                );
            });
        }

        // Update connected edges
        function updateConnectedEdges(node) {
            const nodeId = node.attr('id');
            if (!nodeId) return;

            const bbox = node.node().getBBox();
            const nodeTransform = node.attr('transform') || 'translate(0,0)';
            const [nodeX, nodeY] = nodeTransform
                .replace('translate(', '')
                .replace(')', '')
                .split(',')
                .map(Number);
            const centerX = nodeX + bbox.width / 2;
            const centerY = nodeY + bbox.height / 2;

            d3.selectAll('.edgePath').each(function() {
                const edge = d3.select(this);
                const path = edge.select('path');
                const edgeId = edge.attr('id') || '';

                if (edgeId.includes(nodeId)) {
                    const [sourceId, targetId] = edgeId.split('--').slice(1, 3);
                    const sourceNode = d3.select(`#${sourceId}`);
                    const targetNode = d3.select(`#${targetId}`);

                    if (sourceNode.empty() || targetNode.empty()) return;

                    const sourceBbox = sourceNode.node().getBBox();
                    const sourceTransform = sourceNode.attr('transform') || 'translate(0,0)';
                    const [sourceX, sourceY] = sourceTransform
                        .replace('translate(', '')
                        .replace(')', '')
                        .split(',')
                        .map(Number);
                    const targetBbox = targetNode.node().getBBox();
                    const targetTransform = targetNode.attr('transform') || 'translate(0,0)';
                    const [targetX, targetY] = targetTransform
                        .replace('translate(', '')
                        .replace(')', '')
                        .split(',')
                        .map(Number);

                    // 计算源节点和目标节点的中心点
                    const sourceCenter = {
                        x: sourceX + sourceBbox.width / 2,
                        y: sourceY + sourceBbox.height / 2
                    };
                    const targetCenter = {
                        x: targetX + targetBbox.width / 2,
                        y: targetY + targetBbox.height / 2
                    };

                    // 对于菱形节点,计算边缘交点
                    let sourcePoint = sourceCenter;
                    let targetPoint = targetCenter;
                    
                    // 如果是菱形节点,计算边缘交点
                    if (sourceNode.select('polygon').size() > 0) {
                        sourcePoint = getPolygonIntersection(sourceNode, targetCenter);
                    }
                    if (targetNode.select('polygon').size() > 0) {
                        targetPoint = getPolygonIntersection(targetNode, sourceCenter);
                    }

                    // 更新路径
                    const pathD = `M${sourcePoint.x},${sourcePoint.y} L${targetPoint.x},${targetPoint.y}`;
                    path.attr('d', pathD);
                }
            });
        }

        // 计算多边形与线的交点
        function getPolygonIntersection(node, externalPoint) {
            const polygon = node.select('polygon');
            if (polygon.empty()) return {x: 0, y: 0};

            const points = polygon.attr('points').split(' ').map(p => {
                const [x, y] = p.split(',').map(Number);
                return {x, y};
            });
            
            const nodeTransform = node.attr('transform') || 'translate(0,0)';
            const [nodeX, nodeY] = nodeTransform
                .replace('translate(', '')
                .replace(')', '')
                .split(',')
                .map(Number);
            
            // 计算中心点
            const centerX = nodeX + polygon.node().getBBox().width / 2;
            const centerY = nodeY + polygon.node().getBBox().height / 2;
            
            // 将外部点转换为相对于节点的坐标
            const relativePoint = {
                x: externalPoint.x - nodeX,
                y: externalPoint.y - nodeY
            };
            
            // 找到与从中心到外部点的直线相交的多边形边
            for (let i = 0; i < points.length; i++) {
                const p1 = points[i];
                const p2 = points[(i + 1) % points.length];
                
                const intersection = lineIntersection(
                    {x: centerX - nodeX, y: centerY - nodeY},
                    relativePoint,
                    p1,
                    p2
                );
                
                if (intersection) {
                    return {
                        x: intersection.x + nodeX,
                        y: intersection.y + nodeY
                    };
                }
            }
            
            // 如果没有找到交点,返回中心点
            return {x: centerX, y: centerY};
        }

        // 计算两条线段的交点
        function lineIntersection(a1, a2, b1, b2) {
            const denominator = (a2.x - a1.x) * (b2.y - b1.y) - (a2.y - a1.y) * (b2.x - b1.x);
            
            if (denominator === 0) return null; // 平行或共线
            
            const ua = ((b1.x - a1.x) * (b2.y - b1.y) - (b1.y - a1.y) * (b2.x - b1.x)) / denominator;
            const ub = ((b1.x - a1.x) * (a2.y - a1.y) - (b1.y - a1.y) * (a2.x - a1.x)) / denominator;
            
            if (ua >= 0 && ua <= 1 && ub >= 0 && ub <= 1) {
                return {
                    x: a1.x + ua * (a2.x - a1.x),
                    y: a1.y + ua * (a2.y - a1.y)
                };
            }
            
            return null;
        }

        // Toggle drag mode
        function toggleDrag() {
            config.isDraggable = !config.isDraggable;
            elements.buttons.dragToggle.textContent = config.isDraggable ? '🔒 禁用拖拽' : '🔓 启用拖拽';
            renderDiagram();
        }

        // Zoom control
        function zoom(delta) {
            config.scale = Math.max(0.5, Math.min(3, config.scale + delta));
            elements.container.style.transform = `scale(${config.scale})`;
            centerDiagram();
        }

        // Reset view
        function resetView() {
            config.scale = 1.0;
            elements.container.style.transform = `scale(${config.scale})`;
            centerDiagram();
        }

        // Export PNG
        function exportDiagram() {
            const svg = elements.container.querySelector('svg');
            if (!svg) return;
            
            const originalTransform = elements.container.style.transform;
            elements.container.style.transform = '';
            
            html2canvas(elements.container, {
                backgroundColor: '#ffffff',
                scale: 3,
                useCORS: true
            }).then(canvas => {
                const link = document.createElement('a');
                link.download = 'flowchart.png';
                link.href = canvas.toDataURL('image/png');
                link.click();
                
                elements.container.style.transform = originalTransform;
            }).catch(e => {
                console.error('Export failed:', e);
                alert('Export failed: ' + e.message);
                elements.container.style.transform = originalTransform;
            });
        }

        // Copy to clipboard
        function copyToClipboard() {
            html2canvas(elements.container, {
                backgroundColor: '#ffffff',
                scale: 2,
                useCORS: true
            }).then(canvas => {
                canvas.toBlob(blob => {
                    navigator.clipboard.write([
                        new ClipboardItem({ 'image/png': blob })
                    ]).then(() => {
                        alert('Copied to clipboard!');
                    });
                });
            });
        }

        // Save draft
        function saveDraft() {
            const data = {
                code: elements.code.value,
                config: config
            };
            localStorage.setItem('flowchart-draft', JSON.stringify(data));
            alert('Draft saved!');
        }

        // Load draft
        function loadDraft() {
            const data = JSON.parse(localStorage.getItem('flowchart-draft'));
            if (data) {
                elements.code.value = data.code;
                config = data.config;
                updateControlsFromConfig();
                renderDiagram();
                alert('Draft loaded!');
            } else {
                alert('No saved draft found!');
            }
        }

        // Update controls from config
        function updateControlsFromConfig() {
            elements.controls.nodeSpacing.value = config.nodeSpacing;
            elements.controls.rankSpacing.value = config.rankSpacing;
            elements.controls.shapeAspectRatio.value = config.shapeAspectRatio;
            elements.controls.nodeFill.value = config.nodeFill;
            elements.controls.nodeBorder.value = config.nodeBorder;
            elements.controls.borderWidth.value = config.borderWidth;
            elements.controls.lineColor.value = config.lineColor;
            elements.controls.lineWidth.value = config.lineWidth;
            elements.controls.lineType.value = config.lineType;
            elements.controls.fontSize.value = config.fontSize;
            elements.controls.fontFamily.value = config.fontFamily;
        }

        // Initialize
        initControls();
        renderDiagram();
    </script>
</body>
</html>
相关推荐
IT技术猿猴7 小时前
一键生成专业流程图:Draw.io与AI结合的高效绘图指南
人工智能·流程图·draw.io
diygwcom7 小时前
推荐一款滴滴团队开源流程图编辑框架logic-flow
开源·流程图
霖002 天前
FPGA开发全流程
网络·经验分享·嵌入式硬件·fpga开发·流程图·fpga
AMT管理咨询8 天前
经典案例 | 筑基与跃升:解码制造企业产供销协同难题
流程图·制造·流程管理
RebekahHQ10 天前
Deepseek+Xmind:秒速生成思维导图与流程图
流程图·xmind·思维导图·deepseek
Coding的叶子11 天前
React Flow 简介:构建交互式流程图的最佳工具
前端·react.js·流程图·fgai·react agent
向上的车轮12 天前
Flowchart 流程图的基本用法
流程图
Evenurs13 天前
【markdown】介绍如何在markdown中绘制流程图
流程图·markdown·md
一起喝芬达201015 天前
A2A与MCP定义下,User,Agent,api(tool)间的交互流程图
流程图·交互·agent