QAnything 在mac M2 上纯python环境安装使用体验(避坑指南)

news2024/12/23 15:14:42

 这是一篇mac m2本地纯python环境安装 qanything的文章。安装并不顺利,官方提供的模型无法在本地跑。

 这篇文章记录了,使用xinference来部署本地模型,并利用openAi的通用接口的方式,可以正常使用。

 记录了遇到的所有的问题,以及解决方法,包括源码的修改。这是一个成功的案例。

一、关于QAnything的简介

开源的RAG本地知识库检索的有不少。最近比较火热的就是 QAnything  和 RAGflow 。其中Qanything 是相对比较早的。并且它是网易开源的,各种都相对更正规一些。安装部署文档也都比较齐全。

dify 是开源做工作流的,其中也有RAG的部分。但是做的很粗糙。

如果想做自己的本地知识库开发,可以在Qanything上做。我看过QAnything  和 RAGflow  dify的源码,也对比了他们的效果,最终评估使用Qanything 打底。做一个全新的RAG搜索。

二、使用官方的文档来本地部署模型,启动

2.1 这里是官方文档

QAnything/README.md at qanything-python · netease-youdao/QAnything · GitHub

2.2 以下是安装过程

2.3 问题:启动过过程中遇到了问题(3B模型在mac上无法使用)

我的是mac m2 ,如果是m1,应该不会有问题。

llama_new_context_with_model: n_ctx      = 4096
llama_new_context_with_model: n_batch    = 512
llama_new_context_with_model: n_ubatch   = 512
llama_new_context_with_model: freq_base  = 10000.0
llama_new_context_with_model: freq_scale = 1
ggml_metal_init: allocating
ggml_metal_init: found device: Apple M2 Max
ggml_metal_init: picking default device: Apple M2 Max
ggml_metal_init: using embedded metal library
ggml_metal_init: error: Error Domain=MTLLibraryErrorDomain Code=3 "program_source:155:11: error: unions are not supported in Metal
    union {
          ^
program_source:176:11: error: unions are not supported in Metal
    union {
          ^
program_source:197:11: error: unions are not supported in Metal
    union {
          ^
program_source:219:11: error: unions are not supported in Metal
    union {
          ^
program_source:264:11: error: unions are not supported in Metal
    union {
          ^
program_source:291:11: error: unions are not supported in Metal
    union {
          ^

2.4 试试使用另外openAI的api的方式来启动

也就是说不用使用本地的模型(embedding模型还是用本地的,ocr模型也是用本地的)。

这里还是跟着官方文档来操作。

QAnything/README.md at qanything-python · netease-youdao/QAnything · GitHub

使用这个命令启动

这个就报错,因为还没有配置apikey,这里官方文档也没有说

这里需要修改这里,替换成自己的apikey就可以了(我这个是一个失效的key,没有调用量了) 

yH5BAAAAAAALAAAAAAOAA4AAAIMhI+py+0Po5y02qsKADs=

wAAACH5BAEKAAAALAAAAAABAAEAAAICRAEAOw==

该模型在本地跑不起来,有错误。官方的教程是针对M1的。所以需要探索本地部署模型的方法。

三、Macbook M2本地部署模型的方案

在经过大量调研和使用后,准备使用Xinference来部署模型。

下载部署Xinference的教程(教程是我自己写的,都验证通过的)

Mac M2 本地下载 Xinference-CSDN博客

使用Xinference部署模型的教程

使用Xinference 在mac m2 上部署模型 Qwen 7B-CSDN博客

到这里为止,本地已经可以运行模型来。

四、继续脱坑

4.1 这里使用本地7B的模型来配合使用qanything

在Qanything中切换使用本地模型,这里需要在调用openai的基础上改!

Xinference是可以发布成和openai兼容的接口的,利用这一点。

这里先做一个测试方法,来调用本地模型(调试好了,可以直接使用的,我这里本地部署的是qwen7b,注意要替换model_uid)

这个id在这个界面上可以看到。

代码

import openai

# Assume that the model is already launched.
# The api_key can't be empty, any string is OK.
model_uid = "qwen-chat"
client = openai.Client(api_key="not empty", base_url="http://localhost:9997/v1")
# client.chat.completions.create(
#     model=model_uid,
#     messages=[
#         {
#             "content": "What is the largest animal?",
#             "role": "user",
#         }
#     ],
#     max_tokens=1024
# )
#
# 定义一个调用方法
def chat_with_model(prompt):


    try:
        response = client.chat.completions.create(
            model=model_uid,
            messages=[
                {
                    "content": prompt,
                    "role": "user",
                }
            ],
            stream=False,
            max_tokens=1024
        )

        # Extracting the model's reply from the response
        if response.choices and len(response.choices) > 0:
            model_reply = response.choices[0].message.content
            print(f"Model's Reply: {model_reply}")
            return response.choices[0].message.content
        else:
            print("No response received from the model.")
    except Exception as e:
        print(f"An error occurred: {e}")


# 测试 chat_whith_model 函数
chat_with_model("什么是房颤")

4.2 修改qanything的代码

在llm_for_openai_api.py下,87行处添加我们的自己部署的模型(模型名称在xinference上看,和上边一样)。否则会报错。

4.3 使用不支持流式接口

看到X inference后台模型的日志

这个问题暂时是无法解决的,模型的问题

但是我找到了一个解决方案,我修改了这里,指定为非流式输出处。这个问题就可以暂时解决了。

这是我修改后的_call方法

    async def _call(self, prompt: str, history: List[List[str]], streaming: bool = False) -> str:
        messages = []
        for pair in history:
            question, answer = pair
            messages.append({"role": "user", "content": question})
            messages.append({"role": "assistant", "content": answer})
        messages.append({"role": "user", "content": prompt})
        debug_logger.info(messages)

        try:
            # TODO 临时修改用来调试
            streaming = False
            if streaming:
                response = self.client.chat.completions.create(
                    model=self.model,
                    messages=messages,
                    stream=True,
                    max_tokens=self.max_token,
                    temperature=self.temperature,
                    top_p=self.top_p,
                    stop=[self.stop_words] if self.stop_words is not None else None,
                )
                debug_logger.info(f"OPENAI RES: {response}")
                for event in response:
                    if not isinstance(event, dict):
                        event = event.model_dump()

                    if isinstance(event['choices'], List) and len(event['choices']) > 0:
                        event_text = event["choices"][0]['delta']['content']
                        if isinstance(event_text, str) and event_text != "":
                            # debug_logger.info(f"[debug] event_text = [{event_text}]")
                            delta = {'answer': event_text}
                            yield "data: " + json.dumps(delta, ensure_ascii=False)

            else:
                # response = self.client.chat.completions.create(
                #     model=self.model,
                #     messages=messages,
                #     stream=False,
                #     max_tokens=self.max_token,
                #     temperature=self.temperature,
                #     top_p=self.top_p,
                #     stop=[self.stop_words] if self.stop_words is not None else None,
                # )

                model_uid = "qwen1.5-chat"
                client = openai.Client(api_key="not empty", base_url="http://localhost:9997/v1", timeout=6000)
                print(">>>>>gpt调用")
                print(messages)
                response = client.chat.completions.create(
                    model=model_uid,
                    messages=messages,
                    max_tokens=1024
                )

                debug_logger.info(f"[debug] response.choices = [{response.choices}]")
                event_text = response.choices[0].message.content if response.choices else ""
                delta = {'answer': event_text}
                yield "data: " + json.dumps(delta, ensure_ascii=False)

        except Exception as e:
            debug_logger.info(f"Error calling OpenAI API: {e}")
            delta = {'answer': f"{e}"}
            yield "data: " + json.dumps(delta, ensure_ascii=False)

        finally:
            # debug_logger.info("[debug] try-finally")
            yield f"data: [DONE]\n\n"

4.3 一个问题很长时间不回答

我这里上传了一个文档,来测试问题。半天没有反应。

yH5BAAAAAAALAAAAAAOAA4AAAIMhI+py+0Po5y02qsKADs=

wAAACH5BAEKAAAALAAAAAABAAEAAAICRAEAOw==

这里折腾了半天,看了半天,调试了半天,还以为是程序不行呢。因为界面上没有反应。这里看了下mac的cpu监控,发现推理任务还在跑着。也就是程序是没有问题的。

这里在重复的跑这个任务,一直没有结束。且一直在重复的调用。跟了一遍源码,源码中没有这样的循环逻辑。

yH5BAAAAAAALAAAAAAOAA4AAAIMhI+py+0Po5y02qsKADs=

wAAACH5BAEKAAAALAAAAAABAAEAAAICRAEAOw==

然后看模型的任务,每个任务都很长时间,

先测试修改超时时间,看是否可以

修改调用模型的超时时间,并不可以。这里没有用。一开始觉得是模型的问题,模型推理的太慢了,所以试试再部署一个小模型测试一下。

这里我部署了一个qwen1.5 ,占用内存不到10个g,推理速度使用cpu也很快。

这里有教程细节

使用X inference下载部署小模型(qwen1.5)测试效果-CSDN博客

最后看到了能够成功调用,但是这里还是一个问题,发现有在一直重新调用后台接口,这里不知道是不是流式请求的问题。到这里我也找到的后台日志显示一直重新调用的罪魁祸手,就是在前端这里调用的,本来我还以为是后台代码有地方是写的循环,看了一圈以后,也没有循环的地方。

这里要找到源码中,前端部分,发送请求的时候发送,不指定流式输出。 

这里先验证一下测试接口的输出

 测试问答的接口

stream_chat.py
import json
import requests
import sys

#kb_id = sys.argv[1]
def stream_requests(data_raw):
    url = 'http://0.0.0.0:8777/api/local_doc_qa/local_doc_chat'
    response = requests.post(
        url,
        json=data_raw,
        timeout=60,
        stream=True
    )
    for line in response.iter_lines(decode_unicode=False, delimiter=b"\n\n"):
        if line:
            yield line

def test(kb_id):
    data_raw = {
      "kb_ids": [kb_id],
      "question": "房颤会有危险吗?",
      "user_id": "zzp",
      "streaming": True,
      "history": []
    }
    for i, chunk in enumerate(stream_requests(data_raw)):
        if chunk:
            chunkstr = chunk.decode("utf-8")[6:]
            chunkjs = json.loads(chunkstr)
            print(chunkjs)

if __name__ == '__main__':
# 在文件上传的时候,日志后台会打印知识库的id,这里需要替换自己的
    test("KBa2f41b8753fd48df8b408bca28e17830")

可以看到测试结果

五、源码结构解析

这里有时间再整理。之所以本地跑通,是因为我想在源码的基础上做开发和优化。

后续这块,我需要改成访问es的方式。会整理更多的内容出来。


 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1658604.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

使用Docker安装Nginx

一、Nginx介绍 Nginx 是一款高性能的开源 Web 服务器和反向代理服务器,具有高效能、高稳定性、低资源消耗等优点。可以处理大量并发请求,支持多种协议,还能实现负载均衡、缓存等功能,在互联网应用中被广泛使用。在Nginx中&#xf…

ICode国际青少年编程竞赛- Python-2级训练场-迷宫

ICode国际青少年编程竞赛- Python-2级训练场-迷宫 1、 Dev.step(3) Dev.turnLeft() for i in range(2):Dev.step(4)Dev.turnRight() for i in range(2):Dev.step(2)Dev.turnLeft() Dev.step(3) Dev.step(-9)2、 Dev.step(3) Dev.turnRight() Dev.step(2) Dev.turnLeft() for i …

AI视频教程下载:给企业管理层和商业精英的ChatGpt课程

课程内容大纲: 1-引言 2-面向初学者的生成性人工智能 3-与ChatGPT一起学习提示101 详细介绍了如何使用ChatGPT的六种沟通模式,并提供了各种实际应用场景和示例: **Q&A模式(问题与答案模式)**: - 这…

区块链 | NFT 水印:Review on Watermarking Techniques(三)

🍍原文:Review on Watermarking Techniques Aiming Authentication of Digital Image Artistic Works Minted as NFTs into Blockchains 一个 NFT 的水印认证协议 可以引入第三方实体来实现对交易的认证,即通过使用 R S A \mathsf{RSA} RSA…

96、技巧-只出现一次的数字

思路 首先不考虑额外空间的话使用一个set去重即可。第二种就是异或运算。 异或操作的性质 身份元素:任何数与0进行异或运算,结果仍然是原数,即 x ^ 0 x。自反性:任何数与自身进行异或运算,结果是0,即 x…

Spring Cloud Consul 4.1.1

该项目通过自动配置和绑定到 Spring 环境和其他 Spring 编程模型习惯用法,为 Spring Boot 应用程序提供 Consul 集成。通过一些简单的注释,您可以快速启用和配置应用程序内的常见模式,并使用基于 Consul 的组件构建大型分布式系统。提供的模式…

Python版Spark core详解

文章目录 第一章 SparkCore1.1. Spark环境部署1.1.1. Spark介绍1.1.1.1. 什么是Spark1.1.1.2. Spark与MapReduce的对比框架对比运行流程对比 1.1.1.3. Spark的组件1.1.1.4. Spark的特点 1.1.2. Spark的安装部署1.1.2.1. Spark安装包下载1.1.2.2. Spark部署模式介绍1.1.2.3. Loc…

解决Vue devtools插件数据变化不会自动刷新

我们使用devtools插件在监测vuex中表单或自定义组件的数据,发现页面数据发生变化后,但是devtools中还是老数据,必须手动点击devtools刷新才能拿到最新的数据。很烦! 解决方案: 打开chrome的设置,向下翻&…

docker自建GitLab仓库

摘要 GitLab 是一个功能强大的开源代码托管平台,它不仅提供了代码存储和版本控制的核心功能,还集成了项目管理、CI/CD 流水线、代码审查等企业级特性。本文将指导你如何在自己的服务器上搭建 GitLab 社区版,创建一个完全属于自己的开源仓库&…

##10 卷积神经网络(CNN):深度学习的视觉之眼

文章目录 前言1. CNN的诞生与发展2. CNN的核心概念3. 在PyTorch中构建CNN4. CNN的训练过程5. 应用:使用CNN进行图像分类5. 应用:使用CNN进行时序数据预测代码实例7. 总结与展望前言 在深度学习的领域中,卷积神经网络(CNN)已经成为视觉识别任务的核心技术。自从AlexNet在2…

引入RabbitMQ

前置条件 docker 安装 mq docker run \-e RABBITMQ_DEFAULT_USERdudu \-e RABBITMQ_DEFAULT_PASS123456 \-v mq-plugins:/plugins \--name mq \--hostname mq \-p 15672:15672 \-p 5672:5672 \--network hmall \-d \rabbitmq:3.8-management可能会出现:docker: Er…

FPGA+炬力ARM实现VR视频播放器方案

FPGA炬力ARM方案,单个视频源信号,同时驱动两个LCD屏显示,实现3D 沉浸式播放 客户应用:VR视频播放器 主要功能: 1.支持多种格式视频文件播放 2.支持2D/3D 效果实时切换播放 3.支持TF卡/U盘文件播放 4.支持定制化配置…

【机器学习300问】79、Mini-Batch梯度下降法的原理是什么?

Mini-Batch梯度下降法是一种将训练数据集分成小批次进行学习的优化方法,通过这种方式,可以有效地解决内存限制问题并加速学习过程。 一、为什么要使用Mini-Batch? 在机器学习尤其是深度学习中,我们常常面临海量数据处理的问题。如…

内网穿透速度慢

内网穿透速度慢原因及优化策略 在计算机网络应用中,内网穿透是一个常见的需求,它允许外部网络访问位于内部网络(如企业局域网或家庭网络)中的设备或服务。然而,有时用户在进行内网穿透时会遇到速度慢的问题&#xff0…

10大排序方法,其中这里只介绍前7种(第4种C语言,其它C++语言)

排序方法有十种,分别是:一、冒泡排序;二、选择排序;三、插入排序;四、希尔排序;五、归并排序;六、快速排序;七、堆排序;八、计数排序;九、桶排序;…

1011: 二叉排序树的实现和查找

解法: 二叉排序树(Binary Search Tree,简称BST)也被称为二叉搜索树或二叉查找树,是一种重要的二叉树结构,它具有以下性质: 左子树上所有节点的值都小于根节点的值;右子树上所有节点的…

2024粤港澳青少年信息学创新大赛C++知识点汇总和真题训练

2024粤港澳青少年信息学创新大赛C知识点汇总和真题训练 知识汇总 真题训练 程序设计语言C是一种解释性语言。 A.正确 B.错误 Python是一种编译型语言。 A.正确 B.错误 误 RAM(随机存取存储器)是一种易失性存储设备。 A.正确 B.错误 Java…

单节锂电池充电芯片H4054无需外接检测电阻500mA电流7V输入

锂电池充电芯片的主要功能如下: 充电管理功能:充电芯片能够对锂电池进行智能化管理,根据电池的状态和需求,调节充电电流和电压,以实现快速充电、恒流充电、恒压充电等不同的充电模式。通过合理控制充电过程&#xff0…

Selenium定位方法汇总及举例

天行健,君子以自强不息;地势坤,君子以厚德载物。 每个人都有惰性,但不断学习是好好生活的根本,共勉! 文章均为学习整理笔记,分享记录为主,如有错误请指正,共同学习进步。…

头歌实践教学平台:CG1-v2.0-直线绘制

第1关&#xff1a;直线光栅化-DDA画线算法 一.任务描述 1.本关任务 (1)根据直线DDA算法补全line函数&#xff0c;其中直线斜率0<k<1&#xff1b; (2)当直线方程恰好经过P(x,y)和T(x,y1)的中点M时&#xff0c;统一选取直线上方的T点为显示的像素点。 2.输入 (1)直线两…