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版本。
相关推荐
玩电脑的辣条哥2 小时前
Python如何播放本地音乐并在web页面播放
开发语言·前端·python
ew452182 小时前
ElementUI表格表头自定义添加checkbox,点击选中样式不生效
前端·javascript·elementui
suibian52352 小时前
AI时代:前端开发的职业发展路径拓宽
前端·人工智能
Moon.92 小时前
el-table的hasChildren不生效?子级没数据还显示箭头号?树形数据无法展开和收缩
前端·vue.js·html
垚垚 Securify 前沿站2 小时前
深入了解 AppScan 工具的使用:筑牢 Web 应用安全防线
运维·前端·网络·安全·web安全·系统安全
源大模型3 小时前
OS-Genesis:基于逆向任务合成的 GUI 代理轨迹自动化生成
人工智能·gpt·智能体
工业甲酰苯胺5 小时前
Vue3 基础概念与环境搭建
前端·javascript·vue.js
mosquito_lover16 小时前
怎么把pyqt界面做的像web一样漂亮
前端·python·pyqt
码农阿豪8 小时前
本地部署Anything LLM+Ollama+DeepSeek R1打造AI智能知识库教程
人工智能·llm·ollama·deepseek
柴柴的小记9 小时前
前端vue引入特殊字体不生效
前端·javascript·vue.js