dify学习笔记

Posted by iceyao on Monday, August 26, 2024

1. dify简介

Dify是一个开源的LLM应用开发平台。其直观的界面结合了AI工作流、RAG管道、Agent、模型管理、可观测性功能等,让您可以快速从原型到生产。

2. dify核心功能:

2.1. 强大的AI工作流构建与测试

  • 用户可以直观地设计和构建 AI 工作流,根据不同的业务需求和场景进行定制化操作。
  • 能够对构建的工作流进行全面测试,确保其在实际应用中的稳定性和准确性。

2.2. 全面的模型支持

  • 无缝集成了数百种来自数十个推理提供商和自托管解决方案的专有 / 开源大型语言模型(LLM)。
  • 涵盖了多种不同类型的模型,为用户提供了丰富的选择,以满足不同的任务需求。

2.3. 便捷的提示 IDE

  • 提供了一个集成开发环境(IDE),方便用户编写和优化提示(prompt)。
  • 帮助用户更好地与语言模型进行交互,提高提示的质量和效果。

2.4. 强大的 RAG(检索增强生成)管道

  • 具备高效的检索增强生成功能,能够从大量的文本数据中检索相关信息,并结合语言模型生成更加准确和丰富的内容。
  • 提升了语言模型的输出质量和实用性。

2.5. 灵活的代理能力

  • 允许用户设置代理,以满足不同的网络环境和访问需求。
  • 增强了系统的灵活性和可扩展性。

2.6. 有效的LLMOps

  • 提供了一系列针对语言模型的操作和管理工具,包括模型部署、监控、优化等。
  • 帮助用户更好地管理和维护语言模型的运行,提高工作效率。

2.7. 后端即服务

  • 可以作为后端服务提供给其他应用程序使用,方便集成和扩展。
  • 为开发者提供了便捷的接口和工具,降低了开发成本和难度。

与LangChain、Flowise、OpenAI Assistant API对比:

功能Dify.AILangChainFlowiseOpenAI Assistant API
编程方法API + 应用程序导向Python 代码应用程序导向API 导向
支持的 LLMs丰富多样丰富多样丰富多样仅限 OpenAI
RAG引擎
Agent
工作流
可观测性
企业功能(SSO/访问控制)
本地部署

3. 源码部署

通过源码部署,方便后续代码调试,了解具体实现原理

  1. 克隆dify源码
git clone https://github.com/langgenius/dify.git
  1. docker-compose部署外部依赖PostgresSQL/Redis/Weaviate等
cd dify/docker
cp middleware.env.example middleware.env
docker compose -f docker-compose.middleware.yaml up -d
  1. 准备dify虚拟环境
conda create --name dify python=3.10
conda activate dify
pip install poetry
  1. 启动dify-api/dify-worker服务
# 进入api目录
cd api/
# 拷贝环境变量文件
cp .env.example .env
# 生成随机密钥
openssl rand -base64 42
# 替换环境变量文件中的密钥
sed -i 's/SECRET_KEY=.*/SECRET_KEY=<your_value>/' .env
# 使用poetry安装依赖
poetry install
# 执行数据库迁移到最新版本
flask db upgrade

# 启动dify-api服务 
flask run --host 0.0.0.0 --port=5001 --debug

# 启动dify-worker服务
celery -A app.celery worker -P gevent -c 1 --loglevel INFO -Q dataset,generation,mail,ops_trace
  1. 启动dify-web服务
cd web

# 安装依赖
npm install
# 拷贝环境变量文件
cp .env.example .env.local

# 编译
npm run build

# 启动前端服务
npm run start

附上一个vscode launch.json文件:

{
    "version": "0.2.0",
    "configurations": [
        {
            "name": "dify-api",
            "type": "debugpy",
            "request": "launch",
            "module": "flask",
            "env": {
                "FLASK_APP": "app.py",
                "FLASK_ENV": "development",
                "GEVENT_SUPPORT": "true",
            },
            "args": [
                "run",
                "--host=0.0.0.0",
                "--port=5001",
                "--debug"
            ],
            "cwd": "${workspaceFolder}/api",
            "justMyCode": false
        },
        {
            "name": "dify-worker",
            "type": "debugpy",
            "request": "launch",
            "program": "/opt/anaconda3/envs/dify/bin/celery",
            "env": {
                "GEVENT_SUPPORT": "true",
            },
            "args": [
                "-A",
                "app.celery",
                "worker",
                "-P",
                "gevent",
                "-c",
                "1",
                "--loglevel=INFO",
                "-Q",
                "dataset,generation,mail,ops_trace"
            ],
            "cwd": "${workspaceFolder}/api",
            "justMyCode": false,
            "console": "integratedTerminal"
        },
        {
            "name": "dify-web",
            "type": "node",
            "request": "launch",
            "runtimeExecutable": "/opt/homebrew/bin/pnpm",
            "runtimeArgs": [
                "start"
            ],
            "cwd": "${workspaceFolder}/web",
            "console": "integratedTerminal",
            "internalConsoleOptions": "neverOpen",
        }
    ]
}

如果源码编译dify-web失败的话,可以使用docker容器启动dify-web

docker run -it -d -p 3000:3000 -e CONSOLE_URL=http://127.0.0.1:5001 -e APP_URL=http://127.0.0.1:5001 langgenius/dify-web:latest

源码分析

dify-api

api基于flask框架实现

app = create_app()

def create_app() -> Flask:
    """
    创建并配置Flask应用实例。

    该函数负责初始化一个Flask应用,设置其密钥,配置日志处理,以及注册各种扩展、蓝本和命令。

    Returns:
        Flask: 配置好的Flask应用实例。
    """
    # 初始化Flask应用并应用配置
    app = create_flask_app_with_configs()

    # 设置Flask应用的secret_key,这对于会话管理是必要的
    app.secret_key = app.config["SECRET_KEY"]

    # 默认情况下没有日志处理器
    log_handlers = None

    # 从配置中获取日志文件路径
    log_file = app.config.get("LOG_FILE")
    if log_file:
        # 确保日志文件的目录存在,如果不存在则创建
        log_dir = os.path.dirname(log_file)
        os.makedirs(log_dir, exist_ok=True)
        
        # 配置日志处理器,包括文件日志和控制台日志
        log_handlers = [
            RotatingFileHandler(
                filename=log_file,
                maxBytes=1024 * 1024 * 1024,  # 1GB
                backupCount=5,  # 保留5个备份文件
            ),
            logging.StreamHandler(sys.stdout),  # 将日志输出到控制台
        ]

    # 配置基本的日志记录设置
    logging.basicConfig(
        level=app.config.get("LOG_LEVEL"),  # 日志级别
        format=app.config.get("LOG_FORMAT"),  # 日志格式
        datefmt=app.config.get("LOG_DATEFORMAT"),  # 日期格式
        handlers=log_handlers,  # 使用之前配置的日志处理器
        force=True,  # 强制重新配置日志设置,以确保上述配置生效
    )

    # 如果配置中指定了日志时区,则进行时区配置
    log_tz = app.config.get("LOG_TZ")
    if log_tz:
        from datetime import datetime
        import pytz

        # 创建时区对象
        timezone = pytz.timezone(log_tz)

        # 定义将UTC时间转换为指定时区时间的函数
        def time_converter(seconds):
            return datetime.utcfromtimestamp(seconds).astimezone(timezone).timetuple()

        # 将所有根记录器的处理器的格式化器的时间转换函数设置为上面定义的函数
        for handler in logging.root.handlers:
            handler.formatter.converter = time_converter

    # 初始化应用所需的扩展
    initialize_extensions(app)
    # 注册蓝本,蓝本是Flask中组织路由的一种方式
    register_blueprints(app)
    # 注册命令行命令
    register_commands(app)

    # 返回配置好的应用实例
    return app
if __name__ == "__main__":
    # 当作为主程序运行时,启动Flask应用程序
    # 通过指定host为"0.0.0.0",允许网络中的其他设备访问该服务
    # 端口号设置为5001,与其他设备通信时使用此端口
    app.run(host="0.0.0.0", port=5001)

dify-worker

如何添加一个内置工具

如何添加一个对象存储实现

代码参考实现:https://github.com/langgenius/dify/pull/8164

如何添加一个向量数据库

代码参考实现:https://github.com/langgenius/dify/pull/9287

如何添加一个LLM Model Provider

如何添加一个Embedding Model Provider

参考链接

「真诚赞赏,手留余香」

爱折腾的工程师

真诚赞赏,手留余香

使用微信扫描二维码完成支付