尝试做一个集成多个大模型的AI助手【大模型微调,专属你的个人私有化大模型】

一个很朴素的想法是:既然市面上存在这么多大模型,秉持着存在即合理的原则,每个模型应该都有其优势。比如说:文心一言的API产品矩阵十分丰富、通义千问API分区以及内容生成更加完善、豆包有算法帝的算法、GPT是传统王者…

那有没有一个现成的产品能兼容所有优点,或者至少能从中选出最适合当前场景的大模型?

在这里插入图片描述

github上还有大佬的成熟项目,我也尝试写一个,此博客记录我从零开始编写自己的LLM-Client

  • 如果从一个产品的角度思路这个项目,一期要先实现简单的API调用和呈现,二期解决延迟、重试、VPN切换等待性能问题

现阶段效果:

在这里插入图片描述

后端实现方式有很多,由于我是Java程序员,项目框架选择Spring-boot

  • 想法1:由Java调用python脚本,在不同python脚本中调各大模型的包,远程调用API

    问题:

    • 异构语言,不易联调
  • 想法2:Java语言中封装请求的Request Body,直接调用远程API

    由于大模型提供的Java client不完善,所以大部分只能自己手写封装JSON的逻辑 不同模型RequestBody规范不同,Response Body也不同,同时还有不同场景的不同规范…工程量较大

  • 想法3:在调用接口过程中发现响应太慢,官方文档提供的解决方案是使用流式查询,查询过程中实时刷新client页面内容;除此之外,我想要不干脆把模型迁移到本地?GPT提供了开源版本,其他模型应该也会提供,如果我将开源的模型在本地用同样的文本训练,最终结果的不同就完全是算法的不同…

现阶段核心代码:

public class PythonEngine {  
  
public static String execPythonScript(String content,AIModules modules){  
    Process proc;  
    try {  
        String[] args1=new String[]{  
            "C:\\Users\\吴松林\\PycharmProjects\\pythonProject\\.venv\\Scripts\\python.exe",  
            modules.path,  
            content,  
            "user"};  
        proc = Runtime.getRuntime().exec(args1);  
        InputStream is = proc.getInputStream();  
        byte[] bytes = is.readAllBytes();  
        String ans = new String(bytes, Charset.forName("gbk"));  
        proc.waitFor();  
        return ans.length() == 0?"接口异常":ans;  
    } catch (IOException | InterruptedException e) {  
        e.printStackTrace();  
        return "接口异常";  
    }  
}

dashscope.api_key = "yours"

content = sys.argv[1]
def call_with_messages():
    messages = [
        # {'role': 'system', 'content': 'You are a helpful assistant.'},
                {'role': 'user', 'content': content}]

    response = dashscope.Generation.call(
        dashscope.Generation.Models.qwen_turbo,
        messages=messages,
        result_format='message',  # set the result to be "message" format.
    )
    if response.status_code == HTTPStatus.OK:
        choices = response.output.choices
        for choice in choices:
            print(choice.message.content)
    else:
        print('Request id: %s, Status code: %s, error code: %s, error message: %s' % (
            response.request_id, response.status_code,
            response.code, response.message
        ))

if __name__ == '__main__':
    call_with_messages()

希望看到这里的大佬多多指教


更新:

一版本由于每个请求都通过Runtime.getRuntime().exec()启动一个线程执行脚本,很明显并发场景下并不合适。于是需要一些措施来优化

  1. 批量 + 同步

    想到Kafka中将海量数据通过异步 + 批量操作的方式增强并发,我这里也尝试将并发下的连续多个请求批量传给脚本,由脚本统一调用API,再将结果统一返回

    同步是指每个请求先将内容交给执行器,自旋等待结果返回

代码如下:

public class testController {  
  
public static final BlockingQueue<String> currentRequestByKimi = new LinkedBlockingQueue<>();  
public static long lastTimeKimi;  
public static final BlockingQueue<String> currentRequestByTongyi = new LinkedBlockingQueue<>();  
public static final BlockingQueue<String> currentRequestByGPT = new LinkedBlockingQueue<>();  
public static final ConcurrentHashMap<String,String> result = new ConcurrentHashMap<>();  
  
@GetMapping("/queryFromKimi")  
public String queryFromKimi(@RequestParam String content) throws InterruptedException {  
// String ans = PythonEngine.execPythonScript(content, AIModules.KIMI);  
currentRequestByKimi.put(content);  
result.put(content,"_");  
long time;  
if((time = System.currentTimeMillis()) - lastTimeKimi > 5) {  
synchronized (currentRequestByKimi) {  
PythonEngine.execPythonScript(currentRequestByKimi.toArray(), AIModules.KIMI);  
lastTimeKimi = time;  
}  
}  
while (result.get(content).equals("_")) {  
  
}  
String ans = result.get(content);  
System.out.println("kimi-ans" + ans);  
return ans;  
}

每个大模型对应一个BlockingQueue,每隔5ms将阻塞队列中的消息一并发给执行器,执行器将结果放在static1的map中。而请求的线程自旋check map中有无结果,有则返回

执行器代码:

public static volatile Runtime runtime;  
  
public static Runtime getRuntime() {  
// 第一次检查,如果 proc 不为空,则直接返回,避免不必要的同步  
if (runtime == null) {  
// 同步块,保证只有一个线程进入临界区  
synchronized (PythonEngine.class) {  
// 第二次检查,确保在同步块内再次检查 proc 的状态  
if (runtime == null) {  
// 创建 Process 对象的实例  
return Runtime.getRuntime();  
}  
}  
}  
return runtime;  
}  
  
public static void execPythonScript(Object[] content, AIModules modules) {  
try {  
String[] args1 = new String[3 + content.length];  
args1[0] = "C:\\Users\\吴松林\\PycharmProjects\\pythonProject\\.venv\\Scripts\\python.exe";  
args1[1] = modules.path;  
args1[2] = String.valueOf(content.length);  
System.arraycopy(content, 0, args1, 3, 3 + content.length - 3);  
Runtime runtime = getRuntime();  
Process proc = runtime.exec(args1);  
InputStream is = proc.getInputStream();  
byte[] bytes = is.readAllBytes();  
String ansOrigin = new String(bytes, Charset.forName("gbk"));  
ansOrigin = ansOrigin.trim();  
if (ansOrigin.startsWith("[")) {  
ansOrigin = ansOrigin.substring(1);  
}  
if (ansOrigin.endsWith("]")) {  
ansOrigin = ansOrigin.substring(0, ansOrigin.length() - 1);  
}  
String[] ans = ansOrigin.split(",");  
proc.waitFor();  
for (int i = 0; i < content.length; i++) {  
testController.result.put((String) content[i],ans[i]);  
}  
} catch (IOException | InterruptedException e) {  
e.printStackTrace();  
}  
}

基本思路就是用全局单例的进程多次向大模型发起请求

并发测试: 很尴尬的是开发者平台并不支持短时间连续调用

request reached max request: 3, please try again after 1 seconds

解决的思路1:土办法

将多个不同的内容 拼在一个请求中,通过语义区分 例如:当前三个内容

  1. 你是谁
  2. 我是谁
  3. 谁才是唯一的神龙尊者

拼凑出:以下有3个互不相干问题,你需要分别解释:第一个…

结果:

  1. 你是谁: 我是ChatGPT,一个由OpenAI开发的语言模型,专门设计用于与用户进行对话、回答问题和提供帮助。我被训练来理解和生成自然语言文本,但没有自己的身份或个性,我只是一个程序。
  2. 我是谁: 这个问题是关于你的身份的询问,而我无法直接知道你是谁。只有你自己才能回答这个问题。你可以回顾你的个人信息、经历、角色和责任来确定自己的身份。
  3. 谁才是唯一的神龙尊者: 这个问题可能是基于某个特定的背景或故事设定。在一些神话故事或游戏世界中,可能会有一个被称为神龙尊者的角色,但在现实世界中,并没有唯一的神龙尊者。这个问题的答案取决于相关的虚构世界或故事情节。

通过语义尝试拆分结果,但由于结果未必规范,很明显并不可靠

解决的思路2:多准备几个账号,将上层的请求轮询下层的账号来实现每个账号的请求存在间隔

遭遇的问题1:Kimi的并发限制提示如下

request reached max request: 3, please try again after 1 seconds’

我本以为只要间隔在1s以上就不会报错,结果并非这么简单;测试发现有时候sleep了2s,5s还无法保证能够通过,并且kimi后台似乎有算法,使得越是频繁访问的账号访问成功率越低

于是我想通过自旋 + 超时的方式确保成功访问

遭遇的问题2:python脚本中注册好了n个账号,但是执行脚本的线程并不知道哪个账号是长时间没使用的,哪个是刚刚使用过的;我的做法是:需要上层来决定使用哪个账号——Java代码中维护一个自增的序列对应发往脚本的请求,每次发送批量请求时将此数带上

确实选择可用的账号这个逻辑应该由脚本层面负责,暂时标识为TODO

代码:

python脚本

import logging
import time

from openai import OpenAI
import sys

index = int(sys.argv[1])
nums = sys.argv[2]
content = []
for i in range(int(nums)):
    content.append(sys.argv[3 + i])

client1 = OpenAI(
    api_key="第一个key",
    base_url="https://api.moonshot.cn/v1",
)
client2 = OpenAI(
    api_key="2",
    base_url="https://api.moonshot.cn/v1",
)
client3 = OpenAI(
    api_key="3",
    base_url="https://api.moonshot.cn/v1",
)
client = [client1, client2, client3]

ans = []
for con in content:
    targetClient = client[index % len(client)]
    index += 1
    while (True):
        try:
            completion = targetClient.chat.completions.create(
                model="moonshot-v1-8k",
                messages=[
                    {"role": "system",
                     "content": "你是 Kimi,由 Moonshot AI "
                                "提供的人工智能助手,你更擅长"
                                "中文和英文的对话。你会为用户提供安全,有帮助,准确的回答。同时,你会拒绝一切涉及恐怖主义,种族歧视,黄色暴力等问题的回答。Moonshot AI "
                                "为专有名词,不可翻译成其他语言。"},
                    {"role": "user", "content": con}
                ],
                temperature=0.3,
            )
            ans.append(completion.choices[0].message.content)
            break
        except Exception as e:
            # print("被拦截,尝试自旋")
            time.sleep(1)

print(ans)

后台主要修改代码:(不明掘金为什么会吞掉缩减)

@GetMapping("/queryFromKimi")  
public String queryFromKimi(@RequestParam String content) throws InterruptedException {  
  
currentRequestByKimi.put(content);  
result.put(content, "_");  
long time;  
synchronized (currentRequestByKimi) {  
if (lastTimeKimi - (lastTimeKimi = System.currentTimeMillis()) < -1000) {  
Object[] execRequests = currentRequestByKimi.toArray();  
if (execRequests.length != 0) {  
PythonEngine.execPythonScript(execRequests, AIModules.KIMI);  
for (int i = 0; i < execRequests.length; i++) {  
currentRequestByKimi.poll();  
}  
}  
}  
}  
while (result.get(content).equals("_")) {  
  
}  
return result.get(content);  
}

jmeter测试:(现版本QPS和账号数量挂钩)

在这里插入图片描述

在这里插入图片描述

👉AI大模型学习路线汇总👈
大模型学习路线图,整体分为7个大的阶段:(全套教程文末领取哈)
在这里插入图片描述

第一阶段: 从大模型系统设计入手,讲解大模型的主要方法;

第二阶段: 在通过大模型提示词工程从Prompts角度入手更好发挥模型的作用;

第三阶段: 大模型平台应用开发借助阿里云PAI平台构建电商领域虚拟试衣系统;

第四阶段: 大模型知识库应用开发以LangChain框架为例,构建物流行业咨询智能问答系统;

第五阶段: 大模型微调开发借助以大健康、新零售、新媒体领域构建适合当前领域大模型;

第六阶段: 以SD多模态大模型为主,搭建了文生图小程序案例;

第七阶段: 以大模型平台应用与开发为主,通过星火大模型,文心大模型等成熟大模型构建大模型行业应用。

👉大模型实战案例👈
光学理论是没用的,要学会跟着一起做,要动手实操,才能将自己的所学运用到实际当中去,这时候可以搞点实战案例来学习。

在这里插入图片描述

👉大模型视频和PDF合集👈
观看零基础学习书籍和视频,看书籍和视频学习是最快捷也是最有效果的方式,跟着视频中老师的思路,从基础到深入,还是很容易入门的。

在这里插入图片描述
在这里插入图片描述

👉学会后的收获:👈
• 基于大模型全栈工程实现(前端、后端、产品经理、设计、数据分析等),通过这门课可获得不同能力;

• 能够利用大模型解决相关实际项目需求: 大数据时代,越来越多的企业和机构需要处理海量数据,利用大模型技术可以更好地处理这些数据,提高数据分析和决策的准确性。因此,掌握大模型应用开发技能,可以让程序员更好地应对实际项目需求;

• 基于大模型和企业数据AI应用开发,实现大模型理论、掌握GPU算力、硬件、LangChain开发框架和项目实战技能, 学会Fine-tuning垂直训练大模型(数据准备、数据蒸馏、大模型部署)一站式掌握;

• 能够完成时下热门大模型垂直领域模型训练能力,提高程序员的编码能力: 大模型应用开发需要掌握机器学习算法、深度学习框架等技术,这些技术的掌握可以提高程序员的编码能力和分析能力,让程序员更加熟练地编写高质量的代码。

👉获取方式:
😝有需要的小伙伴,可以保存图片到wx扫描二v码免费领取【保证100%免费】🆓
在这里插入图片描述

  • 19
    点赞
  • 22
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

程序员辣条

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值