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版本。
相关推荐
吕彬-前端14 分钟前
使用vite+react+ts+Ant Design开发后台管理项目(五)
前端·javascript·react.js
学前端的小朱17 分钟前
Redux的简介及其在React中的应用
前端·javascript·react.js·redux·store
guai_guai_guai26 分钟前
uniapp
前端·javascript·vue.js·uni-app
bysking1 小时前
【前端-组件】定义行分组的表格表单实现-bysking
前端·react.js
王哲晓2 小时前
第三十章 章节练习商品列表组件封装
前端·javascript·vue.js
fg_4112 小时前
无网络安装ionic和运行
前端·npm
理想不理想v2 小时前
‌Vue 3相比Vue 2的主要改进‌?
前端·javascript·vue.js·面试
酷酷的阿云2 小时前
不用ECharts!从0到1徒手撸一个Vue3柱状图
前端·javascript·vue.js
微信:137971205872 小时前
web端手机录音
前端
齐 飞2 小时前
MongoDB笔记01-概念与安装
前端·数据库·笔记·后端·mongodb