Mac上搭建和调试Dify+RAG+LLM环境过程总结

225 阅读16分钟
  1. 背景和目标
    我想搭建一套本地的环境,创建RAG知识库,并结合大模型实现知识库检索和问答,目标是利用大模型,结合我收集、整理的私域知识,实现私域知识的智能分析、检索和问答。因此,我需要实现以下几点:
    1.1 本地RAG知识库的创建。我已经积累了原始数据,包括我自己从各渠道收集的文档(word、PPT、PDF、txt等等),已经自己写的日志、日记和工作总结等。需要基于这些原始数据,结合工具,创建一个RAG知识库,最终为本地的通用大模型提供外挂和补充。
    1.2 本地模型的部署。根据了解和学习,不仅仅涉及到LLM模型,还涉及到RAG知识库创建需要的词嵌入(Embedding)模型,以及rerank模型。
    1.3 RAG知识库与LLM的工程化。需要有一个工具和环境,将RAG和LLM关联起来,形成工作流程和工作链,即 用户提问->RAG检索->提示词整合->LLM问答->结果输出,实现整个RAG知识库查询到智能问答的闭环流程,这需要一套工程化的工具。

    因为要本地部署,我的是16款Mac,硬件老旧,也没有GPU,因此对于LLM、Embedding和rerank模型的选择,就必须是小参数,可支持CPU版本的模型。

  2. 模型和工具的选择
    基于我的硬件环境,我选择了DeepSeek R1 1.5B版本,这个版本其实在今年3月份就部署了,是利用ollama部署的,当时并没有想到创建RAG本地知识库,只是想本地部署一个LLM感受一下,其实回过头来看,这里面隐藏2个问题:
    ①DeepSeek的R版本,推理和思考的时间太长,在我这种硬件的机器上更慢,会导致Dify调用的时候超时,后来花了好几天解决这个问题;
    ②ollama默认不支持rerank模型,而在创建RAG知识库时又需要,虽然据说rerank不是必需的,但是我认为还是用上比较好,这导致我后来又通过xinference,安装了rerank模型,而xinference和ollama是类似的,所以如果从头再来,那么我不需要使用ollama,直接通过xinference安装部署LLM、Embedding和rerank模型即可,这样可节省资源,提高运行效率。

    以上是各模型的选择,还有一个重点是工程化工具和环境的选择。最开始我在秘塔上提问,我提了3点要求:
    ①针对私域文档进行向量化。我的私域文档可能是txt,csv等文本和半结构化数据。也存在包括文本、图形等混合内容的word、ppt、pdf等非结构化文档。
    ②希望向量化工具和框架,是国产化的。
    ③如果向量化工具需要大语言模型,那么希望向量化工具和模型能本地部署,而不是通过接口调用的方式实现向量化。

    秘塔的回答,推荐了这样几个工具和组合:LangChain-Chatchat、DeepSeek + TiDB向量数据库、AnythingLLM + Ollama、CherryStudio、网易有道·QAnything,可以看出,推进这这些工具或者组合,并不是我后来用的,因为我问的都是如何构建知识库,并没有提到构建知识库之后如何使用,所以秘塔的回答都是围绕构建RAG知识库的工具和组合。

    后来我问了同事,知道了当前产品中使用的是Dify,并且我又向他请教了为什么用Dify,而不是Langchain的原因,他的解释是:Langchain的使用门槛太高,它是一个开发框架,需要懂技术和编码的人才能玩得好,当然正因为如此,它的灵活性和可定制性也更好,但是对产品化来说,为了能更快地应用部署,并且在后期降低运维难度,选择Dify更合适。其实对我个人来说,Dify也更合适,毕竟Langchain的学习成本还是太高。

    所以,经过一番比较和思考,工程化的工具就选择Dify,毕竟Dify本身就内置模型管理、知识库、Agent引擎等核心组件,搭建整个环境会更快,更轻松。那么RAG的框架确定下来后,下面还需要确定这样一些核心或者必要的组件:本地模型部署和管理的工具,构建RAG知识库所需的模型,包括嵌入模型和rerank模型(一开始我并不知道还要这几种模型,问了同事之后才知道),向量数据库,LLM等。

  3. 环境搭建、组件安装
    3.1 Dify安装。
    Dify在docker环境下安装,最开始只是根据网上的帖子和秘塔的回答,亦步亦趋地跟着装,并没有想为什么Dify一定要在docker下安装。后来才搜索了一下,Dify可以不装到docker下,但是直接安装会有很对问题,比如环境的兼容性,维护困难等等。
    在Mac下,先安装docker desktop,安装过程没什么好说的,非常顺利,同时根据网帖,镜像源要配置一下,因为后续还得安装Dify,国内不能直接访问github。

    3.2 Dify安装。
    我是直接在github上下载了Dify的安装包后,本地安装的,当然在安装的过程中,会使用docker环境,并且我理解应该还是从指定的镜像路径下载了安装文件,所以docker的国内镜像源还是需要配置。安装完成启动就遇到问题,在浏览器中http://localhost启动Dify时,报错:Error response from daemon: Ports are not available: exposing port TCP 0.0.0.0:80 -> 127.0.0.1:0: listen tcp 0.0.0.0:80: bind: address already in use

    看字面意思是,80端口被占用了,但是我不知道到底是哪个应用或者进程占用了80端口,问过秘塔后,我是这样解决的:在dify-main/docker下的.env文件中,将NGINX_PORT和EXPOSE_NGINX_PORT从80改为8080,然后就可以访问了。

    3.3 Embedding模型安装。
    前面没有写Embedding模型的选型,因为在安装完Dify前,还不知道要用什么样的Embedding模型。网上推荐的是BGE-M3与 BCE-Embedding-base,考虑到我的Mac有限的资源,所以我没有安装它们的任何一个,另一个主要原因是,秘塔告诉我ollama默认不支持BCE-Embedding-base,所以要在ollama下安装它,还非常麻烦,所以我就再问秘塔,有没有什么BGE-M3的平替,主打对中文支持好,资源占用低,且ollama官方支持。于是秘塔推荐mxbai-embed-large,参数是335M,内存占用0.8G,所以我就安装了这个Embedding模型。

    3.4 ollama下安装rerank模型。
    最开始我不知道还有rerank模型这个东西,待到我把Dify、Embedding模型安装好,进行RAG知识库的配置时,才知道还要装这个,我问过同事,他说产品中装了rerank,但是他说如果没有,也可以构建知识库,不过我想到既然产品化都用了,我也要用试试看。到安装时才发现,面临一个大坑:ollama竟然没有官方和默认支持的rerank模型。这就是历史遗留问题了,我在这之前已经通过ollama安装了DeepSeek,以及Embedding模型,现在发现ollama竟然没有官方支持的rerank模型,虽然同事说也可以先不使用rerank模型,但是既然在产品化中已经用上了,肯定有用的道理,所以我觉得还得上。经过询问,得知xinference与ollama类似,但是它是支持rerank模型的。

    现在我面临一个选择:是把ollama,以及通过ollama安装的DeepSeek和Embedding模型都卸载掉,再通过xinference重来一次。还是只通过xinference把rerank模型装上,而保持ollama现状不变。经过一番权衡,我决定安装第二套方案来,因为ollama及其模型已经安装好了,全部推倒重来,不确定又会发生什么未知问题。当然第二种方案也会带来更多资源占用的问题。

    3.5 xinference安装。
    如上一节说的,在安装rerank模型之前,我必须先安装xinference,安装过程一波三折。开始我通过问秘塔,在本地环境下直接安装,先是装miniconda,再下载安装xinference,这其中遇到什么python版本不兼容,pip命令不能执行等等问题,都是小问题,很快就解决了。在部署xinference时,根据我的硬件环境:Intel版本的Macbook,有4种安装部署参数:vllm(依赖NVIDIA CUDA,Intel Mac无GPU支持,安装后无法加速且报错),mlx(专为Apple Silicon设计,Intel CPU完全不兼容),all(包含冗余后端(vllm/mlx),增大依赖冲突风险且无实际收益),这3种都不能选,只能选transformers这个参数。

    在安装过程中,第2个大的问题是:可靠镜像源。一开始我并没有指定镜像源,结果从外网下载非常慢,导致了超时。秘塔告诉我使用这些国内的源:pypi.tuna.tsinghua.edu.cn/simple,http…

    第3个问题:无法执行秘塔说的xinference-local --host 0.0.0.0 --port 9997命令,然后执行xinference --version命令,得到结果:xinference, version 0.5.2,也就是我装的xinference是0.5.2版本,这个版本太老,根本就没有xinference-local命令(0.7版本之后才有),这个问题我至今都没明白,为什么装了这么老的版本,很可能是脚本根据我的硬件环境,下载安装了这个版本,但是这个版本相当于我没法使用。 经过一通搜索和分析,我得知xinference可以安装在docker下的。所以我只得先把本地环境下安装的conda和xinference都卸载和删掉,然后再在docker环境下尝试安装。

    第4个问题:国内镜像源。开始我根据秘塔给的指令执行: docker run -d
    --name xinference
    -p 9997:9997
    -v ~/xinference_data:/root/.xinference
    -e XINFERENCE_MODEL_SRC=modelscope
    --memory="8g"
    xprobe/xinference:latest
    xinference-local -H 0.0.0.0 --device cpu 下载非常慢,于是再问,告诉我用阿里镜像源,并且在docker的配置中,把这3个加进去:"docker.mirrors.ustc.edu.cn",// 中科大源(推荐),"registry.docker-cn.com",// Docker中国官方源,"hub-mirror.c.163.com"//网易源 docker run -d
    --name xinference
    -p 9997:9997
    -v ~/xinference_data:/root/.xinference
    -e XINFERENCE_MODEL_SRC=modelscope
    --memory="8g"
    registry.cn-hangzhou.aliyuncs.com/xprobe_xinference/xinference:latest \ # 关键变更 xinference-local -H 0.0.0.0 --device cpu 经过这一番操作,发现也没什么用,而且镜像源不能下载,下载就报错: docker: failed to copy: httpReadSeeker: failed open: unexpected status code registry.cn-hangzhou.aliyuncs.com/v2/xprobe_x…: 429 Too Many Requests - Server message: toomanyrequests: too many requests 秘塔让我用阿里云账户认证,我哪里有阿里云账户,这条路走不通。实在不行,在网上搜了一个docker的国内镜像源:docker.aityp.com/r/docker.io…

    最终执行的命令是这个: docker run -d
    --name xinference
    -p 9997:9997
    -v /Users/bbrb/xinference_data:/root/.xinference
    -e XINFERENCE_MODEL_SRC=modelscope
    -e HF_ENDPOINT=hf-mirror.com
    --memory="8g"
    swr.cn-north-4.myhuaweicloud.com/ddn-k8s/docker.io/xprobe/xinference:v1.6.0.post1-cpu
    xinference-local -H 0.0.0.0 --device cpu 成功在docker下成功安装了xinference。

    3.6 xinference下安装rerank模型。
    xinference默认支持多种rerank模型,通过docker desktop启动xinference的web,在浏览器中打开http://localhost:9997,RERANK MODELS tab页下就可以看到很多,哪一个适合我这个老Mac环境,再问问秘塔,最终我选中bge-reranker-v2-m3模型。一是它的体积小,只有500M,二是CPU的延迟率也比较低。安装其实很方便,直接在xinference的web界面操作就可以了。

    到此为止,组件就全部安装完成了。

  4. 配置和流程打通
    4.1 LLM和Embedding模型配置。
    把ollama下安装的DeepSeek R1 1.5B模型与Dify打通。按照秘塔提供的指导配置即可。 LLM配置:模型名称 deepseek-r1:1.5b(与ollama list下查询到的name一定要一致),基础URL host.docker.internal:11434(用docker的内部url,不能用localhost)。 Embedding配置:模型名称 mxbai-embed-large:latest,基础URL host.docker.internal:11434。

    4.2 rerank模型配置。
    这里面有个坑,秘塔告诉我,服务器URL填host.docker.internal:37325,结果一直报错,我不知道这个37325的端口是怎么来的,可能是前面秘塔让我执行某个命令,把服务的端口指定为37325,但是后来不知什么原因,总是不对。问秘塔这个错误如何解决,就一直在围绕这个37325端口纠结,实在没办法,只要用搜索引擎,在网上找了帖子,才发现都用的9997这个端口,换了这个端口后,就没报错了。

    4.3 RAG知识库的构建。
    到此为止组件与Dify的配置都完成了,现在可以着手构建知识库。因为是测试,所以挑选导入了一部分金融领域的安全规范和标准(都是PDF文档),分段模式选择了父子分段,分段标识符就用的默认的换行符。索引方式用的高质量索引,检索方式设置为向量检索,基本上就是默认配置。

    其实下一步这方面还有很多工作可做,包括召回率的测试和调教等,不过现阶段的主要目的是把整个流程打通,这些就放到下一阶段去做了。

    4.4 流程创建。
    到此就要考虑怎样把流程串起来,我在Dify创建了一个金融知识问答机器人,流程很简单,总共就是4步。开始:输入是提问,还有什么用户ID这些,都用不上。知识检索:输入是问题字符串,根据提问对RAG知识库进行检索和匹配,输出是召回的分段和详细内容。LLM:根据提问和RAG知识库召回的分段,让大模型回答和输出。直接回复:就是把LLM的回答直接输出。

    这个流程有个疑问一开始困扰我很久,就是如何构建提示词,在我的想法中,RAG根据提问召回了知识库中的相关分段,然后我想把原始提问,加上RAG知识库返回的分段,然后再根据一些模板,加上一些背景说明,组成一个提示词,作为LLM的输入,这样才能体现RAG知识库+LLM的流程,否则RAG和LLM就没有关联起来。

    但是我不知道这个操作,在Dify的那个步骤或者什么地方配置提示词,在网上搜索了很长时间,才知道这个是在“LLM”那个环节的上下文中进行配置,在上下文中可以搜索“知识检索”环境的输出变量,也就是召回的分段,然后在下面的编辑框中增加提示词。

    4.5 流程调试和打通。
    流程打通过程中遇到下面几个问题。
    ①知识检索召回为空。这个问题一开始没有发现,后来在排查问题时发现知识库的召回都是空的,肯定有问题,但是没有任何报错。经过自己分析和排查,最终发现是rerank模型没有启动导致的,所以每次xinference启动后(先启动docker desktop,然后在docker的web界面启动xinference),不会自动拉起rerank模型,这一点和ollama不同,ollama启动后会自动把LLM和Embedding模型都启动。所以需要在xinference的web界面手动地把rerank模型启动,否则在RAG检索时,没有rerank,就没有召回的分段结果。

    ②LLM环节超时。这个问题让我花了好几天才解决。当RAG检索能正常返回召回分段后,在LLM环节就会出现超时的报错: Run failed: [models] Connection Error, HTTPConnectionPool(host='host.docker.internal', port=11434): Read timed out. (read timeout=300)

    经过分析,我认为是LLM调用超时导致的,而之所以会调用超时,无外乎几个原因:最主要是硬件资源不足,毕竟Intel版本的Mac太老旧了,而且没有GPU。另外就是安装的DeepSeek R版本,每个问题都深度思考,耗时很长,如果是V版本,可能会好一点。
    我之所以会这样判断,最主要的原因是,我发现如果直接调用LLM,也就是不加任何RAG检索信息和提示词,而是直接向LLM提问,那么它还是可以给出回答的,一般不会超时(当然问题也比较简单),所以Dify通过ollama调用LLM的流程是通的,那么这个超时问题,肯定是提问+提示词信息量太大,LLM需要长时间思考,从而导致了超时。

    那么针对超时问题,解决的主要方向和思路,就简单了——如何修改这个300s的默认超时时间,把它加大。
    根据秘塔的回答,我修改了docker-compose.yaml的1个参数,从PLUGIN_MAX_EXECUTION_TIMEOUT: ${PLUGIN_MAX_EXECUTION_TIMEOUT:-600}改为PLUGIN_MAX_EXECUTION_TIMEOUT: ${PLUGIN_MAX_EXECUTION_TIMEOUT:-2400},但是不生效。后来通过搜索引擎,在网上找了不少帖子,找到一篇,说把api_based_extension_requestor.py的参数:timeout改一下,我把这个参数从timeout: tuple[int, int] = (5, 60)调整成timeout: tuple[int, int] = (5, 300),总算不再报超时错误了。

  5. 下一步
    当前只不过把RAG知识库+LLM的最基本的流程跑通了,再次回看最初的想法和目标:基于私域数据和知识,结合大模型,搭建一个智能问答和AIGC的工具和体系。现在还只是走出了第一步。我想,后面可以在这几个方面进行验证和调试:
    ①召回率的测试和调教,提升召回分段的精确率,进而提升LLM回答的准确性。
    ②尝试Agent,以及Dify流程的编排,在垂直领域验证AIGC,比如生成金融、教育等领域的数据安全治理解决方案等。
    ③收集和跟进RAG的新方向和热点,目前可以看到,RAG也在持续向前演进。
    ④收集、分析和学习类似Dify的平台,例如最近字节开源的扣子(koze)平台。