阅读效率提升300%:Dify+Markdown实现自动化知识梳理全解析 原创

发布于 2025-6-23 06:43
浏览
0收藏

在早年阅读网上的技术博客时,我习惯一边看文章一边在语雀笔记中画思维导图。然而,回过头来看,这种方式其实效率不高。有了AI后,我们可以先让AI为我们生成相应的思维导图,以便我们对知识有个初步认识,再去深入阅读文章,这样会更有效。在这篇文章中,我将分享如何使用dify自动生成文章的思维导图,以提高我们吸收知识的速度。

安装插件

先在dify的插件市场安装如下两个插件:

  1. Markdown转换器:用于生成html文件
  2. Agent策略插件:调用mcp server,将markdown转成html

编写mcp server

我们需要开发一个 MCP 服务器,通过 HTTP 接口为 Dify 提供 Markdown 转思维导图的服务。该服务将使用 markmap-cli 工具实现核心转换功能,要调用这个工具需要先安装 Node.js 环境(包含 npm),然后通过命令 ​​npm install -g markmap-cli​​ 全局安装这个必备工具。

sudo apt update
sudo apt install nodejs npm
npm install -g markmap-cli

下面是对应的mcp server代码,运行这个脚本之前需要先pip install mcp,  然后执行python mcp.py --host 0.0.0.0 --port 27018,dify对应的Agent节点配置的服务端地址是http://ip:27018/sse。

import asyncio
import tempfile
import os
import shutil
import sys
import argparse
import logging
from pathlib import Path
from mcp.server.fastmcp import FastMCP

# Configure logging
logging.basicConfig(
    level=logging.INFO,
    format='%(asctime)s - %(levelname)s - %(message)s',
    datefmt='%Y-%m-%d %H:%M:%S'
)

# Parse command line arguments
def parse_arguments():
    parser = argparse.ArgumentParser(description='MCP Server for converting Markdown to mindmaps')
    parser.add_argument('--return-type', choices=['html', 'filePath'], default='html',
                        help='Whether to return HTML content or file path. Default: html')
    parser.add_argument('--host', default='localhost',
                        help='Host address to bind the server. Default: localhost')
    parser.add_argument('--port', type=int, default=8000,
                        help='Port number to run the server. Default: 1100')

    return parser.parse_args()

# Global configuration
args = parse_arguments()
RETURN_TYPE = args.return_type

# Initialize FastMCP server
mcp = FastMCP("mindmap-server", host=args.host, port=args.port)

# Log server configuration
logging.info("Starting Mindmap Server with configuration:")
logging.info(f"Host: {args.host}")
logging.info(f"Port: {args.port}")
logging.info(f"Return Type: {args.return_type}")

async def create_temp_file(content: str, extension: str) -> str:
    """Create a temporary file with the given content and extension."""
    temp_dir = tempfile.mkdtemp(prefix='mindmap-')
    file_path = os.path.join(temp_dir, f"input{extension}")

    with open(file_path, mode='w') as f:
        f.write(content)

    return file_path

async def run_mindmap(input_file: str, output_file: str = None) -> str:
    """Run markmap-cli on the input file and return the path to the output file.

    Args:
        input_file: Path to the input markdown file
        output_file: Optional path for the output HTML file

    Returns:
        str: Path to the generated HTML file
    """
    if output_file is None:
        output_file = os.path.splitext(input_file)[0] + '.html'

    if sys.platform == 'win32':
        args = ['cmd', '/c', 'npm', 'exec', '--yes', 'markmap-cli', '--', input_file, '-o', output_file, '--no-open']
    else:
        args = ['npx', '-y', 'markmap-cli', input_file, '-o', output_file, '--no-open']

    try:
        process = await asyncio.create_subprocess_exec(
            *args,
            stdout=asyncio.subprocess.PIPE,
            stderr=asyncio.subprocess.PIPE
        )

        stdout, stderr = await process.communicate()

        if process.returncode != 0:
            error_msg = stderr.decode() if stderr else "Unknown error"
            raise RuntimeError(f"markmap-cli exited with code {process.returncode}: {error_msg}")

        return output_file
    except Exception as e:
        raise RuntimeError(f"Failed to run markmap-cli: {str(e)}")

async def get_html_content(file_path: str) -> str:
    """Read the HTML content from the given file."""
    with open(file_path, 'r', encoding='utf-8') as f:
        return f.read()

@mcp.tool()
async def convert_markdown_to_mindmap(
    markdown_content: str,  # The Markdown content to convert
) -> str:
    """Convert Markdown content to a mindmap mind map.

    Args:
        markdown_content: The Markdown content to convert

    Returns:
        Either the HTML content or the file path to the generated HTML, 
        depending on the --return-type server argument
    """
    try:
        logging.info("Starting markdown to mindmap conversion")
        # Create a temporary markdown file
        input_file = await create_temp_file(markdown_content, '.md')
        logging.debug(f"Created temporary markdown file: {input_file}")

        # Run mindmap on it
        output_file = await run_mindmap(input_file)
        logging.debug(f"Generated mindmap file: {output_file}")

        # Check if the output file exists
        if not os.path.exists(output_file):
            error_msg = f"Output file was not created: {output_file}"
            logging.error(error_msg)
            raise RuntimeError(error_msg)

        # Return either the HTML content or the file path based on command line arg
        if RETURN_TYPE == 'html':
            html_content = await get_html_content(output_file)
            logging.info("Successfully converted markdown to HTML mindmap")
            return html_content
        else:
            logging.info(f"Successfully generated mindmap file at: {output_file}")
            return output_file
    except Exception as e:
        error_msg = f"Error converting Markdown to mindmap: {str(e)}"
        logging.error(error_msg)
        raise RuntimeError(error_msg)
    finally:
        # Clean up temporary files
        if 'input_file' in locals():
            temp_dir = os.path.dirname(input_file)
            try:
                shutil.rmtree(temp_dir, ignore_errors=True)
                logging.debug(f"Cleaned up temporary directory: {temp_dir}")
            except Exception as e:
                logging.warning(f"Failed to clean up temporary directory {temp_dir}: {str(e)}")

def main():
    """Entry point for the mindmap-mcp-server command."""
    global args, RETURN_TYPE

    # Parse arguments again to ensure parameters are captured when running as an entry point
    args = parse_arguments()
    RETURN_TYPE = args.return_type

    print(f"Starting mindmap-mcp-server with return type: {RETURN_TYPE}", file=sys.stderr)

    # Initialize and run the server
    mcp.run(transport='sse')

if __name__ == "__main__":
    main()

搭建工作流

搭建好的简略工作流如下:


阅读效率提升300%:Dify+Markdown实现自动化知识梳理全解析-AI.x社区

下面对关键节点做如下说明:

LLM 生成markdown

我们利用gpt-4.1 对文件内容转换成markdown格式,对应的prompt如下:

上下文内容:{{#context#}}
## 核心任务
将上下文内容转化为符合以下标准的Markdown格式思维导图框架:
1. **要素提取**:识别并提取关键实体、关系、流程三类核心要素
2. **逻辑重构**:按「总-分」结构重组信息,确保父子节点存在推导关系

## 格式规范
### 层级控制
- 主标题 `#`(1级):文档主题
- 章节 `##`(2级):核心模块(≥3个)
- 子项 `###`(3级):具体要素(每个父节点下≥2个)

### 内容标记
- 关键术语:**加粗显示** + (简短释义)
- 数据示例:```包裹的代码块```

## 质量保障
1. 预检机制(输出前必须验证):
   - [ ] 无孤立节点(所有子项都有父节点)
   - [ ] 无重复内容(合并相似条目)

Agent

添加Agent策略时,我选择了环境准备安装的Agent策略插件,并指定了FunctionCalling策略。同时,对上述的MCP工具进行了配置。

阅读效率提升300%:Dify+Markdown实现自动化知识梳理全解析-AI.x社区

测试

我从网上找了一篇讲解iphone15的文章,将其导入当前的工作流系统。下面是生成的思维导图,生成的内容还是不错的:

阅读效率提升300%:Dify+Markdown实现自动化知识梳理全解析-AI.x社区

总结

当然,上面的处理还只是一个比较粗糙的demo,我们还可以继续优化。首先,在数据采集环节,我们可以增加网页内容直接抓取功能;其次,针对大篇幅文档,可考虑采用分块处理的迭代机制;此外,还需完善对图文混合文档中视觉元素的处理能力。这些优化方向将显著提升工具的实用性和处理效率。感兴趣的朋友可以自行尝试。


本文转载自AI 博物院 作者:longyunfeigu

©著作权归作者所有,如需转载,请注明出处,否则将追究法律责任
已于2025-6-23 10:48:34修改
收藏
回复
举报
回复
相关推荐