笑看天下无敌手 发表于 2024-11-24 01:22:34

QQ开放平台搭建和优化个人本地LLM群呆板人(一):架构搭建和利用微调大模

QQ开放平台搭建和优化个人本地LLM群呆板人(一):架构搭建和利用微调大模子

前言:为什么选择QQ开放平台呆板人?

众所周知,QQ平台上有很多成熟的,或收费或免费的第三方呆板人,为什么要选择QQ开放平台的呢?
其实是笔者之前并没有利用这些的经验
最重要的缘故原由是这些第三方呆板人虽然支持的功能丰富,但是同样也承担着停运的风险;而本教程所专注的是利用本地大模子来搭建和优化应用,QQ开放平台足以支持这一目的。实际上,本教程希望能通过QQ开放平台呆板人来构建一个便捷的远程利用本地LLM的交互本领,从这一点上来说,纯文字的,简便的交互方式是可以担当的。
当然,QQ开放平台搭建的呆板人同样有很多缺点和不足,要知道,QQ开放平台官方文档QQ 呆板人 | QQ呆板人文档中最初应该是想要做成专门针对频道的呆板人的,而对于群的支持是在近来几个月添加的,文档中对其形貌和功能介绍都甚少不过恰好不用看长篇大论
在正式进入教程之前,有几点需要注意


[*] 本教程仅会专注于群呆板人,而不会介绍频道呆板人的利用
[*] 现在,测试中的群呆板人只能在小于20人的群中部署
本期重要介绍怎样申请QQ呆板人,利用ollama部署微调后的大模子并添加影象
源码在github取用
QQ开放平台申请呆板人

首先,登录QQ 开放平台,通过QQ就行了
进入之后拉到下面的应用管理举行创建呆板人
https://i-blog.csdnimg.cn/blog_migrate/6272cfd038be3e3fc1b7fcf8f9411466.png
创建的时候要求提供名字,用途之类的,随便填写一下就行了
https://i-blog.csdnimg.cn/blog_migrate/8ee4c3eef021240949cc6027e80fe3ac.png进去自己的呆板人之后,右边进来可以看到发布流程,一共3步调,因为我们基本不涉及上线成为公域呆板人,第三部门基本不用管了
先点进图中的沙箱配置
https://i-blog.csdnimg.cn/blog_migrate/9a502e8c69c3e0371e65be63e129f432.png在这里可以选择自己是群主/管理员的群,配置沙箱,之后可以在相应的群内里邀请配置之后的呆板人
https://i-blog.csdnimg.cn/blog_migrate/136cf7e7d8da2ceb0b2d7a70f7bff1cf.png
左边栏内可以看到一个开发设置,这个内里得到AppID和AppSecret,之后要用
接下来利用PythonSDK来举行开发
建议利用conda虚拟情况来举行管理
conda create -n your_env python==3.10
conda activate your_env
pip install qq-botpy
可以把整个仓库都下下来举行研究,不过群呆板人的例子很少,这里只需要下载两个文件


[*]config.example.yaml下载下来之后把名称改为example.yaml
打开来可以看到内里只有两个参数,就是之前在开发设置内里看到的AppID和AppSecret,填入即可
[*]群内@回复消息
这个是官方的at呆板人回复文字消息的实例,现在还支持富媒体信息的回复,以后可以用到
打开文件,注意在MyClient内里添加is_sandbox=True,因为我们这个现在是在沙箱的测试模式,否则无法毗连,这个地方和添加IP白名单没有关系
if__name__=="__main__":
        intents = botpy.Intents(public_messages=True)
        client = MyClient(intents=intents, is_sandbox=True)
        client.run(appid=test_config["appid"], secret=test_config["secret"])
这时就可以运行此文件举行测试了,终端显示on_ready就表明呆板人已经上线了,我们可以在群里@呆板人,这机遇器人会原样输出担当到的文字
本地LLM部署

ollama

本地运行大模子的框架有很多,如vllm, llamacpp, ollama等
因为消费级PC或笔记本都是windows平台,考虑到部署便利性,我们直接利用ollama举行一站式的LLM部署(Linux也可以)
ollama下载
windows平台是下载exe文件,Linux直接一行命令就可以下载
接下来直接在终端输入
ollama --version
可以看到版本信息,就说明已经乐成运行了
接下来我们可以直接开始下载大模子,不过,默认模子的下载路径是在C盘,顾虑这一点的朋友可以自行更改# Ollama修改默认模子存储位置
利用ollama的方便之处在于可以直接提供便捷的模子下载,不需要过高的网络条件
https://i-blog.csdnimg.cn/blog_migrate/bd0b9ca1db59b906bb80a6a74fd15dd4.png在网站的Search models内里可以直接搜索模子下载
例如,llama3.1点开,左边下拉栏内里有各种版本的
https://i-blog.csdnimg.cn/blog_migrate/a08635508b35e6f4c65b7fcb89db9388.png右边的命令在终端运行可以直接下载
我们直接根据左边的模子巨细要求,下载后缀带instruct的模子
笔者的显卡有12GB,便选择ollama run llama3.1:8b-instruct-q8_0,模子巨细8.5GB,运行起来大概占用显存10GB左右
下载完成之后,我们可以直接在终端和终端对话看下效果
https://i-blog.csdnimg.cn/blog_migrate/e75ac9278ecae6c59dd0775cd904b8a8.png
输入/bye退出
导入微调后的GGUF模子

为什么我们要利用本地LLM举行部署?最大的好处是可以直接利用颠末各种微调的更加奇奇怪怪好用的大模子
虽然也可以利用api的方式来利用性能远强于本地的大模子,不过现在大模子的各种安全对齐导致这写大模子没法对存在安全风险的问题人类的三大欲望举行回答,以是,部署各种私家微调后的大模子也是别有一番乐趣
笔者在huggingface上面举行搜索,很快找到了一些基于llama3.1举行微调,排除安全对齐的一些模子,有能力的朋友也可以自行登录huggingface举行搜索
这里我们利用DarkIdol
仓库内里的模子都是safetensors后缀的权重文件,我们可以自己利用llamacpp来转化文件格式,不过下面也提供了很多转换成GGUF的各种量化版本,我们可以直接下载只需要下载GGUF后缀的模子权重即可,其他的都不需要
为了把自己的模子导入ollama,我们还需要举行一些设置
在模子权重的同文件夹下面新建一个无后缀文件Modelfile,这内里需要填入模子的各类参数,这些参数怎样得到呢?
还记得我们之前直接从ollama下载的llama3.1:8b-instruct-q8_0吗?我们下载的微调模子同样是基于llama3.1举行微调的,以是,Modelfile内里的各类参数,模板应该是一模一样的
我们直接在终端执行ollama show --modelfile llama3.1:8b-instruct-q8_0,终端会输出所有的参数
https://i-blog.csdnimg.cn/blog_migrate/b798dab2477f5a4529dbaa8c36c045ea.png往下拉,LICENSE "LLAMA 3.1 COMMUNITY LICENSE AGREEMENT之前的内容都是需要的,唯一需要更改的就是第一行,把FROM背面改成自己的gguf文件的路径
笔者的github仓库同样直接提供Modelfile,同样只需要更改gguf文件路径
Modelfile填写好了之后,就可以开始把gguf文件导入ollama
先cd到模子和Modelfile地点的路径,执行一下命令
ollama create choose-a-model-name -f <location of the file e.g. ./Modelfile>'
ollama run choose-a-model-name
这样,就可以在终端测试自己导入的LLM了
呆板人接入LLM

LLM回复

ollama可以利用本地地址直接举行访问,函数如下
def simple_reply(text):
        url = 'http://localhost:11434/api/chat'
        data = {
        "model": "llama3.1:8b-instruct-q8_0",
        "messages": [{"role": "system","content": "你是一名AI助手,简洁的回答用户的问题。"},
        {"role": "user","content": text}],
        "stream": False
        }
        headers = {'Content-Type': 'application/json'}
        response = requests.post(url, data=json.dumps(data), headers=headers, stream=False)
        if response.status_code == 200:
                returnresponse.json()['message']['content']
        else:
                returnf"Request failed with status {response.status_code}"


[*] 修改model名字来切换ollama内里的模子
[*] 修改system content来更改模子的设定
在我们可以在运行呆板人的文件内里的on_group_at_message_create()内里添加函数来调用大模子
async def on_group_at_message_create(self, message: GroupMessage):
        ai_message = simple_reply(message.content)
        messageResult = await message._api.post_group_message(group_openid=message.group_openid,
                                                                                                                        msg_type=0,
                                                                                                                        msg_id=message.id,
                                                                                                                        content=ai_message)
        _log.info(messageResult)
再次运行呆板人运行文件,at呆板人就可以和大模子举行回复了
添加影象

我们的函数是把群友的信息发送给大模子然后得到得到回复,并没有涉及到影象
而所谓的影象就是把用户和ai的消息维护到一个消息列表中,把整个消息列表都发送给大模子
而在群里at呆板人会返回监听的GroupMessage,通过内里的member_id, group_id两个属性来确认信息是哪个群的哪个用户,我们可以通过这些信息来在一个字典内里留存每个群内里的不同群友的汗青对话
这里利用message_remain来决定保存几轮对话的影象
store= {}
def messages_with_id(member_id, group_id, message, ai_message=False, message_remain=4):
        global store
        sys_template = '''你是一名ai助手。
                                        '''
        sys_message = {"role": "system","content": sys_template}
        if group_id in store:
                group = store #dict
                if member_id in group:
                        member_message = group #list
                        if not ai_message:
                                member_message.append({"role": "user","content": message})
                                if len(member_message) == (2*message_remain + 2):
                                        del member_message
                                        del member_message
                                return member_message
                        else:
                                member_message.append({"role": "assistant","content": message})
                        return member_message
                else:
                        group =
                        return group
        else:
                store = {}
                store = [{"role": "user","content": message}]
                store.insert(0, sys_message)
                return store
我们需要把reply()的函数稍微修改成担当一整个消息列表的情势
def reply(message):
        url = 'http://localhost:11434/api/chat'
        data = {"model": "llama3.1:8b-instruct-q8_0",
                        "messages": message,
                        "option": {"temperature": 0.7},
                        "stream": False
                        }
        headers = {'Content-Type': 'application/json'}
        response = requests.post(url, data=json.dumps(data), headers=headers, stream=False)
        if response.status_code == 200:
                return response.json()['message']['content']
        else:
                returnf"Request failed with status {response.status_code}"
然后同样是修改一下on_group_at_message_create()
async def on_group_at_message_create(self, message: GroupMessage):
        message_list = messages_with_id(message.author.member_openid,
                                                                        message.group_openid,
                                                                        message.content)
        ai_message = reply(message_list)
        messages_with_id(message.author.member_openid, message.group_openid, ai_message, True)
        messageResult = await message._api.post_group_message(group_openid=message.group_openid,
                                                                                                                        msg_type=0,
                                                                                                                        msg_id=message.id,
                                                                                                                        content=ai_message)
        _log.info(messageResult)
这这样,就实现了影象功能
打扫影象

我们利用QQ呆板人的指令功能来举行影象的打扫
首先举行指令功能的配置
点击发布设置-功能配置与提审-指令
https://i-blog.csdnimg.cn/blog_migrate/86f71f0f0c48b8d086c292367fb97c04.png
https://i-blog.csdnimg.cn/blog_migrate/97455e50ae82ecfd3483e1ff76107d0c.png在内里配置指令,全部都是/指令名称的样式,我们创建一个/影象打扫
设置和保存好之后,回到bot运行的文件内里
在qq群@呆板人之后,我们可以看到弹出来刚刚设置好的指令功能
指令功能直接可以通过查询message内里的字符串通过if…else来判断
先写一个打扫全局字典的函数
def clean_memory():
        global store
        store = {}
再在on_group_at_message_create()内里添加一下
elif '/记忆清除' in message.content:
        clean_memory()
        messageResult = await message._api.post_group_message(
        group_openid=message.group_openid,
        msg_type=0,
        msg_id=message.id,
        content='记忆被清除了...')
_log.info(messageResult)
ok,现在我们可以在群内里举行一些对话,再利用指令打扫影象
结束

本次就实现这些功能了,重要是添加了具有影象功能的大模子QQ群呆板人,背面会进一步介绍怎样加强QQ呆板人的功能,例如联网搜索,总结消息等,并且用大模子来举行优化,本期所有的代码存放在github仓库内,随意取用。

免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。
页: [1]
查看完整版本: QQ开放平台搭建和优化个人本地LLM群呆板人(一):架构搭建和利用微调大模