2. 利用langchain自定义LLM类,接入其他模型

0. 注意

本文只是提供一个思路,由于现在大模型正在飞速发展,整个生态在不久的将来或许会发生巨大的变化,文章中的代码仅供参考

1. 自定义LLM

langchain文档自定义LLM

除了看文档还可以看源码是怎么封装的。

其实很简单,大部分无论本地还是在线的模型,都会提供api接口,我们只需要调用就行了。

下面使用阿里的通义千问为例。

申请key和对接文档我就不赘述了,感兴趣的可以去阿里云百炼上面看看。

总体架构:

graph TD A["express搭建后台(连接大模型)"] --> 前端通过调用服务间接访问大模型
ts 复制代码
// 这是我使用express简单搭建的后台
// 主要任务就是连接通义千问的在线平台
import express from 'express';
import * as path from 'path';
// 获取token的文档
// https://help.aliyun.com/document_detail/2587930.html?spm=a2c4g.2638479.0.0.4d4757a7OELeVS
import BaiLianClient, {
  CreateTokenResponseBodyData,
} from '@alicloud/bailian20230601';
import axios, { AxiosResponse } from 'axios';
import { nanoid } from 'nanoid';
import dayjs from 'dayjs';
const app = express();
app.use(express.json());
// @ts-ignore
const client = new BaiLianClient({
  accessKeyId: process.env.ALI_KEY_ID,
  accessKeySecret: process.env.ALI_KEY_SECRET,
  endpoint: process.env.ALI_END_POINT,
});
/**
 * 获取客户端token
 */
let tokenData: Partial<CreateTokenResponseBodyData> = {
  expiredTime: dayjs('1990-01-01').unix(),
};
async function createToken() {
  try {
    // @ts-ignore
    const response = await client.createToken({
      agentKey: process.env.ALI_AGENT_KEY,
    });

    const responseBody = response.body;
    if (responseBody === null) {
      throw new Error('failed to create token');
    }

    if (responseBody.success !== true) {
      let requestId = responseBody.requestId;
      if (requestId == null) {
        requestId = response.headers['x-acs-request-id'];
      }

      const error =
        'failed to create token ' +
        responseBody.message +
        ', requestId: ' +
        requestId;
      throw new Error(error);
    }

    const data = responseBody.data;
    if (data === null) {
      throw new Error('failed to create token');
    }
    tokenData = data;
    return data;
  } catch (err) {
    console.log('failed to create token, err: ', err);
  }
}
app.use(async (req, res, next) => {
  if (dayjs.unix(tokenData.expiredTime).isBefore(dayjs())) {
    await createToken();
  }
  next();
});
// 自定义的一个接口
// 前端传入prompt参数
// 服务返回通义千问的响应
app.post('/llm', async (req, res) => {
  const { prompt } = req.body;

  const aiRes = await axios.post<
    Completion.Body,
    AxiosResponse<Completion.Response>,
    Completion.Body
  >(
    'https://bailian.aliyuncs.com/v2/app/completions',
    {
      RequestId: nanoid(10),
      Prompt: prompt,
      AppId: process.env.ALI_APP_ID,
      TopP: 0,
    },
    {
      headers: {
        Authorization: `Bearer ${tokenData.token}`,
      },
    },
  );
  res.send({ message: aiRes.data.Data.Text });
});

const port = process.env.PORT || 3333;
const server = app.listen(port, () => {
  console.log(`Listening at http://localhost:${port}/`);
});
server.on('error', console.error);
ts 复制代码
// qwenLlm.ts
// 封装一个简单langchain LLM类
import { LLM, BaseLLMCallOptions } from '@langchain/core/language_models/llms';
import { CallbackManagerForLLMRun } from '@langchain/core/dist/callbacks/manager';
import axios, { AxiosResponse } from 'axios';
// 继承LLM类
// 根据文档必选实现_llmType和_call
export default class QwenLLM extends LLM {
  constructor(options: BaseLLMCallOptions) {
    super(options);
  }
  // 返回一个llm的唯一标识
  _llmType(): string {
    return 'Qwen';
  }
  // 接收输入,返回字符串结果
  // prompt:输入文字
  // options:初始化的一些选项
  // runManager:执行过程中的一些hook
  async _call(
    prompt: string,
    options: this['ParsedCallOptions'],
    runManager?: CallbackManagerForLLMRun | undefined,
  ): Promise<string> {
    const res = await axios.post<
      { prompt: string },
      AxiosResponse<{ message: string }>,
      { prompt: string }
    >(
      '/llm',
      {
        prompt,
      },
      // 本地代理
      { baseURL: '/api' },
    );
    return res.data.message;
  }
}
ts 复制代码
// 前端很简单就是直接调用
import { Button, Card, Form, Input } from 'antd';
import QwenLLM from './qwenLlm';
import { SendOutlined } from '@ant-design/icons';
import { useState } from 'react';
// 使用方法和上一篇文章一样
const qwen = new QwenLLM({});
export function App() {
  const [res, setRes] = useState<string>();
  return (
    <Form
      wrapperCol={{ span: 6 }}
      onFinish={async values => {
        const { prompt } = values;
        const res = await qwen.predict(prompt);
        setRes(res);
      }}
    >
      <Form.Item
        label='输入'
        name='prompt'
        initialValue={`假设现在是2024年2月28日,从今天到5天以后的开始结束时间分别是什么,请用下面格式回答{startTime: '开始时间,格式YYYY-MM-DD', startTime: '结束时间,格式YYYY-MM-DD'}`}
      >
        <Input.TextArea />
      </Form.Item>
      <Button
        type='primary'
        icon={<SendOutlined />}
        htmlType='submit'
      >
        提交
      </Button>
      <Card style={{ width: 300 }}>
        <div>输出结果:</div>
        <div>{res}</div>
      </Card>
    </Form>
  );
}

export default App;

说实话,就这一个问题,我问了文心一言星火大模型通义千问-7B豆包大模型,没有一个回答正确的。

只有通义千问-plus通义千问-max升级版还能正确理解,不过有时候没有区分判断闰年。不过gpt-3.5发挥很稳定可以正确理解格式并回答正确。

这意味着如果你可能需要更多的精力去调整你的输入。

2. 总结

如果你想用本地离线的模型,或者你自己的模型,都可以用这个原理来实现。

但是现在大模型大多数都是基于pythonPyTorchtenserflow这种框架实现的,而且对于稍微准确一点的问答模型,需要的cpu内存gpu不是一般的服务器能支持的,就算勉强支持推理速度也是问题。

短时间内如果想要应用在项目上还是的掏钱给这些服务商。

本来我想搞个本地模型试试,一想到环境装一堆,又没有linux系统就算了,感兴趣的同学可以自己试试。

  • transformers:用来快速从huggingface拉取模型运行模型,可以用python做个后端。这个有transformers.js但是由于考虑到运行环境的性能,很多模型并不支持,语言问答这个任务分类上基本上没有什么可用的模型。
  • 阿里的魔搭:基本上就是翻版huggingfaceModelScope Library就是和transformers差不多的作用,从魔搭上面拉模型,快速验证运行。只有python版本。
相关推荐
zaim115 分钟前
计算机的错误计算(一百九十二)
人工智能·ai·大模型·llm·错误·误差/error·余割/csc
旺旺大力包15 分钟前
【 Git 】git 的安装和使用
前端·笔记·git
雪落满地香31 分钟前
前端:改变鼠标点击物体的颜色
前端
余生H1 小时前
前端Python应用指南(二)深入Flask:理解Flask的应用结构与模块化设计
前端·后端·python·flask·全栈
outstanding木槿1 小时前
JS中for循环里的ajax请求不数据
前端·javascript·react.js·ajax
酥饼~1 小时前
html固定头和第一列简单例子
前端·javascript·html
一只不会编程的猫1 小时前
高德地图自定义折线矢量图形
前端·vue.js·vue
m0_748250931 小时前
html 通用错误页面
前端·html
来吧~1 小时前
vue3使用video-player实现视频播放(可拖动视频窗口、调整大小)
前端·vue.js·音视频
han_2 小时前
不是哥们,我的console.log突然打印不出东西了!
前端·javascript·chrome