dify 智能体平台框架搭建和知识库RAG的对接

0 阅读9分钟

近两年,AI的快速发展推动了许多互联网产品的AI集成。我也在加班加点学习智能体 (Agent)开发最近遇到一个需求,除了需要与AI正常互动聊天外,还要求AI针对性地分析一些数据,确保对话更专业、垂直,这个时候需要利用一种叫:RAG技术,我就了解到了dify这个开源平台 。 在我之前的文章中也提到过,如果大家不打算深耕AI底层,只想将AI应用集成到日常业务中,智能体开发是一个不错的选择。

市面上也开源了很多现成的智能体开发平台,比如:扣子、dify 这些平台 。我们就可以安装到自己的本地电脑进行学习对接。

今天主要和大家分享的是 dify平台的搭建和对接,核心需求是 通过API调用dify 提供的知识库相关的服务。

业务需求是一个管理系统,是用python 做的,对接了AI大模型接口,通过和AI的聊天来控制软件的某些功能,现在新增一个需求是要分析系统里的一些文件。找了很多知识库RAG相关的文章和开源的框架。

RagFlow :官方文档ragflow.com.cn/docs/ 这个是只针对知识库进行处理的,但是对于硬件要有有点高

image.png

所以最终选择了 Dify 这个开源平台,他是一个Agent敏捷开发平台,内置了知识库相关的功能,这篇文章主要分享的就是 怎么通过API 来使用Dify平台的知识库功能。
Dify 官方文档:docs.dify.ai/api-referen…

image.png

github 开源地址:github.com/langgenius/…

好了,背景和介绍就说这么多,下面主要来分享 部署和知识库API调用 的相关代码

部署:我们采用docker部署,简单快捷

源码从git上下载下来后执行下面命令

cd docker
cp .env.example .env
docker compose up -d

image.png

具体针对性配置 可参考官方文档,通过浏览器可以正常访问后,算启动成功。

image.png

下面分享一个对接dify的知识库的相关代码,因为参数太多,大家按需选择:

以下代码仅供参考,大家按需分配,具体参数参考dify的官方文档
python版:

import requests
import json

class DifyHelper:
    def __init__(self):
        # 基础URL和API密钥
        self.base_url = 'API地址'
        self.api_key = 'apiKey在启动的后台里申请'

    def datasets_add(self, params):
        """
        创建空知识库.
        :param params: 参数数组
        :return: 创建成功返回知识库信息,失败返回空字典
        """
        params_data = {
            'name': params['name'],  # 知识库的名称
            'description': params['description'],  # 知识库的描述(可选)。
            'indexing_technique': 'high_quality',  # 要使用的索引技术 可用选项: high_quality 高质量, economy 经济
            'embedding_model': 'multimodal-embedding-v1',  # 嵌入模型的名称 做配置项
            'embedding_model_provider': 'langgenius/tongyi/tongyi',  # 嵌入模型的提供者 做配置项
            'retrieval_model': {
                'search_method': 'hybrid_search',  # 用于检索的搜索方法 可用选项: hybrid_search(混合), semantic_search, full_text_search, keyword_search
                'reranking_enable': True,  # 是否启用重新排序模型以改善搜索结果。
                'reranking_mode': 'reranking_model',  # 重新排序模式。 可用选项: reranking_model, weighted_score
                'reranking_model': {
                    'reranking_provider_name': 'langgenius/tongyi/tongyi',  # 重新排序模型的提供商。 做配置项
                    'reranking_model_name': 'gte-rerank',  # 重新排序模型的名称。 做配置项
                },
                'top_k': 4,  # 返回的顶部匹配结果数量。
                'score_threshold_enabled': True,  # 是否应用分数阈值来过滤结果。
                'score_threshold': 0.5,  # 结果包含的最低分数。
            },
            'summary_index_setting': {  # 摘要索引设置
                'enable': None,  # 是否启用摘要,true表示启用
                'model_name': None,  # 使用的模型名称,如 "qwen-vl-plus"
                'model_provider_name': None,  # 模型提供商,如 "langgenius/tongyi/tongyi"
                'summary_prompt': None,  # 摘要提示词,用于指导AI生成摘要
            },
        }
        result = self.make_client(self.base_url + '/datasets', params_data, 'POST', {
            'Authorization': 'Bearer ' + self.api_key,
        })
        if 'id' not in result:
            return {}
        return result

    def datasets_update(self, dataset_id, params):
        """
        更新知识库.
        :param dataset_id: 知识库ID
        :param params: 参数数组
        :return: 更新成功返回知识库信息,失败返回空字典
        """
        if not dataset_id:
            return {}
        
        # 目前只支持更新 名字和描述
        params_data = {
            'name': params['name'],  # 知识库的名称
            'description': params['description'],  # 知识库的描述(可选)。
        }
        result = self.make_client(self.base_url + f'/datasets/{dataset_id}', params_data, 'PATCH', {
            'Authorization': 'Bearer ' + self.api_key,
        })
        if 'id' not in result:
            return {}
        return result

    def datasets_delete(self, dataset_id):
        """
        删除知识库.
        :param dataset_id: 知识库ID
        :return: 删除操作是否执行
        """
        self.make_client(self.base_url + f'/datasets/{dataset_id}', {}, 'DELETE', {
            'Authorization': 'Bearer ' + self.api_key,
        })
        return True

    def datasets_create_by_file(self, dataset_id, file):
        """
        从文件创建文档.
        :param dataset_id: 知识库ID
        :param file: 文件对象,需要有getRealPath()和getClientFilename()方法
        :return: 创建结果
        """
        # 构建multipart请求数据
        multipart = [
            ('data', json.dumps(self.document_hierarchical_rule())),
            ('file', (file.filename, file.stream, 'application/octet-stream')),
        ]
        result = self.multipart_request(self.base_url + f'/datasets/{dataset_id}/document/create-by-file', multipart, {
            'Authorization': 'Bearer ' + self.api_key,
        })
        return json.loads(result)

    def datasets_update_by_file(self, dataset_id, document_id, file):
        """
        用文件更新文档.
        :param dataset_id: 知识库ID
        :param document_id: 文档ID
        :param file: 文件对象,需要有getRealPath()和getClientFilename()方法
        :return: 更新结果
        """
        # 构建multipart请求数据
        multipart = [
            ('data', json.dumps(self.document_hierarchical_rule())),
            ('file', (file.filename, file.stream, 'application/octet-stream')),
        ]
        result = self.multipart_request(self.base_url + f'/datasets/{dataset_id}/documents/{document_id}/update-by-file', multipart, {
            'Authorization': 'Bearer ' + self.api_key,
        })
        return json.loads(result)

    def datasets_document_embedding_status(self, dataset_id, batch):
        """
        获取文档嵌入状态(进度).
        :param dataset_id: 知识库ID
        :param batch: 批次ID
        :return: 嵌入状态信息,失败返回空字典
        """
        result = self.make_client(self.base_url + f'/datasets/{dataset_id}/documents/{batch}/indexing-status', {}, 'GET', {
            'Authorization': 'Bearer ' + self.api_key,
        })
        if not result:
            return {}
        return result

    def document_hierarchical_rule(self):
        """
        文档规则定义
        子父级 分割
        :return: 返回文档的结构和处理规则配置
        """
        return {
            'doc_form': 'hierarchical_model',
            'doc_language': 'Chinese Simplified',  # 文档语言
            'process_rule': {  # 文档处理规则
                'mode': 'hierarchical',  # 处理模式,可选值 "automatic"(自动) 或 "custom"(自定义), hierarchical  分层
                'rules': [  # 自定义规则配置
                    'parent_mode': 'paragraph',  # 分层模式中父块的检索模式。 可用选项: full-doc, paragraph
                    'pre_processing_rules': [
                        {'enabled': True, 'id': 'remove_extra_spaces'},
                        {'enabled': True, 'id': 'remove_urls_emails'},  # 删除所有 URL 和电子邮件地址
                    ],
                    'segmentation': {  # 将文档内容分割成块的规则。
                        'separator': "\n\n",  # 分段分隔符,默认使用 "###"
                        'max_tokens': 1024,  # 每段最大token数,默认500
                    },
                    'subchunk_segmentation': {  # 将父块分割为较小子块的规则(用于分层模式)。
                        'separator': "\n",
                        'max_tokens': 1024,
                    },
                ],
            },
        }

    def make_client(self, url, params, method='POST', headers=None):
        """
        通用HTTP客户端方法
        :param url: 请求的URL
        :param params: 请求参数
        :param method: 请求方式,默认为POST
        :param headers: 请求头
        :return: 返回解析后的JSON响应,失败时返回空字典
        """
        if headers is None:
            headers = {}
        headers['content-type'] = 'application/json'

        try:
            response = requests.request(method, url, json=params, headers=headers, verify=False)
            body = response.text
            print('Dify 接口调用 result 请求日志:', body)

            if not body:
                print('Dify 接口调用 返回值为空时 请求日志:', {
                    'url': url, 'method': method, 'params': params, 'result': body
                })
                return {}
            return json.loads(body)
        except Exception as e:
            print('Dify 接口调用异常===>', {'msg': str(e), 'data': params, 'url': url})
            return {}

    def multipart_request(self, url, files, headers=None):
        """
        发送multipart请求
        :param url: 请求的URL
        :param files: multipart数据
        :param headers: 请求头
        :return: 返回响应的文本
        """
        if headers is None:
            headers = {}

        response = requests.post(url, files=files, headers=headers)
        return response.text

java版:(springboot框架)代码仅供参考,用的时候根据自己需求调试

package com.example.helper;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import okhttp3.*;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;

import java.io.File;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;

/**
 * DifyHelper类用于操作和管理知识库,通过外部API进行交互。
 */
@Component
public class DifyHelper {

    private static final Logger logger = LoggerFactory.getLogger(DifyHelper.class);

    // 基础API URL
    private final String baseUrl = "";
    
    // API密钥
    private final String apiKey = "";

    private final OkHttpClient client;

    public DifyHelper() {
        this.client = new OkHttpClient();
    }

    /**
     * 创建空知识库.
     * @param params 参数数组
     * @return 创建成功返回知识库信息,失败返回空数组
     */
    public JSONObject datasetsAdd(Map<String, Object> params) {
        Map<String, Object> paramsData = new HashMap<>();
        paramsData.put("name", params.get("name")); // 知识库的名称
        paramsData.put("description", params.get("description")); // 知识库的描述(可选)
        paramsData.put("indexing_technique", "high_quality");  // 要使用的索引技术,可用选项: high_quality 高质量, economy 经济
        paramsData.put("embedding_model", "multimodal-embedding-v1"); // 嵌入模型的名称  做配置项
        paramsData.put("embedding_model_provider", "langgenius/tongyi/tongyi"); // 嵌入模型的提供者  做配置项
        paramsData.put("retrieval_model", createRetrievalModel()); // 检索模型
        paramsData.put("summary_index_setting", createSummaryIndexSetting()); // 摘要索引设置

        return makeClient(baseUrl + "/datasets", paramsData, "POST");
    }

    /**
     * 更新知识库.
     * @param datasetId 知识库ID
     * @param params 参数数组
     * @return 更新成功返回知识库信息,失败返回空数组
     */
    public JSONObject datasetsUpdate(String datasetId, Map<String, Object> params) {
        if (datasetId == null || datasetId.isEmpty()) {
            return new JSONObject();
        }

        Map<String, Object> paramsData = new HashMap<>();
        paramsData.put("name", params.get("name")); // 知识库的名称
        paramsData.put("description", params.get("description")); // 知识库的描述(可选)

        return makeClient(baseUrl + "/datasets/" + datasetId, paramsData, "PATCH");
    }

    /**
     * 删除知识库.
     * @param datasetId 知识库ID
     * @return 删除操作是否执行
     */
    public boolean datasetsDelete(String datasetId) {
        makeClient(baseUrl + "/datasets/" + datasetId, new HashMap<>(), "DELETE");
        return true;
    }

    /**
     * 从文件创建文档.
     * @param datasetId 知识库ID
     * @param file 文件对象
     * @return 创建结果
     */
    public JSONObject datasetsCreateByFile(String datasetId, File file) {
        Map<String, Object> multipartData = new HashMap<>();
        multipartData.put("data", JSON.toJSONString(documentHierarchicalRule()));

        RequestBody fileBody = RequestBody.create(file, MediaType.parse("application/octet-stream"));
        MultipartBody multipartBody = new MultipartBody.Builder()
                .setType(MultipartBody.FORM)
                .addFormDataPart("data", JSON.toJSONString(documentHierarchicalRule()))
                .addFormDataPart("file", file.getName(), fileBody)
                .build();

        return makeMultipartRequest(baseUrl + "/datasets/" + datasetId + "/document/create-by-file", multipartBody);
    }

    /**
     * 获取文档嵌入状态(进度).
     * @param datasetId 知识库ID
     * @param batch 批次ID
     * @return 嵌入状态信息,失败返回空数组
     */
    public JSONObject datasetsDocumentEmbeddingStatus(String datasetId, String batch) {
        return makeClient(baseUrl + "/datasets/" + datasetId + "/documents/" + batch + "/indexing-status", new HashMap<>(), "GET");
    }

    // 构建检索模型
    private Map<String, Object> createRetrievalModel() {
        Map<String, Object> retrievalModel = new HashMap<>();
        retrievalModel.put("search_method", "hybrid_search"); // 用于检索的搜索方法 可用选项: hybrid_search(混合), semantic_search, full_text_search, keyword_search
        retrievalModel.put("reranking_enable", true);  // 是否启用重新排序模型以改善搜索结果。
        retrievalModel.put("reranking_mode", "reranking_model");  // 重新排序模式。 可用选项: reranking_model, weighted_score
        retrievalModel.put("reranking_model", createRerankingModel());  // 重新排序模型
        retrievalModel.put("top_k", 4);  // 返回的顶部匹配结果数量。
        retrievalModel.put("score_threshold_enabled", true);  // 是否应用分数阈值来过滤结果。
        retrievalModel.put("score_threshold", 0.5); // 结果包含的最低分数。
        return retrievalModel;
    }

    // 构建重新排序模型
    private Map<String, Object> createRerankingModel() {
        Map<String, Object> rerankingModel = new HashMap<>();
        rerankingModel.put("reranking_provider_name", "langgenius/tongyi/tongyi"); // 重新排序模型的提供商。 做配置项
        rerankingModel.put("reranking_model_name", "gte-rerank");  // 重新排序模型的名称。 做配置项
        return rerankingModel;
    }

    // 构建摘要索引设置
    private Map<String, Object> createSummaryIndexSetting() {
        Map<String, Object> summaryIndexSetting = new HashMap<>();
        summaryIndexSetting.put("enable", null);  // 是否启用摘要,true表示启用
        summaryIndexSetting.put("model_name", null);  // 使用的模型名称,如 "qwen-vl-plus"
        summaryIndexSetting.put("model_provider_name", null);  // 模型提供商,如 "langgenius/tongyi/tongyi"
        summaryIndexSetting.put("summary_prompt", null);  // 摘要提示词,用于指导AI生成摘要
        return summaryIndexSetting;
    }

    // 文档规则定义
    private Map<String, Object> documentHierarchicalRule() {
        Map<String, Object> rule = new HashMap<>();
        rule.put("doc_form", "hierarchical_model");
        rule.put("doc_language", "Chinese Simplified"); // 文档语言
        Map<String, Object> processRule = new HashMap<>();
        processRule.put("mode", "hierarchical");
        Map<String, Object> segmentation = new HashMap<>();
        segmentation.put("separator", "\n\n"); // 分段分隔符,默认使用 "\n\n"
        segmentation.put("max_tokens", 1024); // 每段最大token数
        processRule.put("segmentation", segmentation);
        rule.put("process_rule", processRule);
        return rule;
    }

    // 通用HTTP请求方法
    private JSONObject makeClient(String url, Map<String, Object> params, String method) {
        try {
            RequestBody body = RequestBody.create(JSON.toJSONString(params), MediaType.parse("application/json"));
            Request request = new Request.Builder()
                    .url(url)
                    .addHeader("Authorization", "Bearer " + apiKey)
                    .method(method, body)
                    .build();

            try (Response response = client.newCall(request).execute()) {
                if (response.isSuccessful()) {
                    String responseBody = response.body().string();
                    logger.info("API 调用结果: {}", responseBody);
                    return JSON.parseObject(responseBody);
                }
            }
        } catch (IOException e) {
            logger.error("API 调用异常", e);
        }
        return new JSONObject();
    }

    // 发送multipart请求
    private JSONObject makeMultipartRequest(String url, MultipartBody multipartBody) {
        try {
            Request request = new Request.Builder()
                    .url(url)
                    .addHeader("Authorization", "Bearer " + apiKey)
                    .post(multipartBody)
                    .build();

            try (Response response = client.newCall(request).execute()) {
                if (response.isSuccessful()) {
                    String responseBody = response.body().string();
                    logger.info("API 调用结果: {}", responseBody);
                    return JSON.parseObject(responseBody);
                }
            }
        } catch (IOException e) {
            logger.error("API 调用异常", e);
        }
        return new JSONObject();
    }
}