用langchain搭配最新模型ollama打造属于自己的gpt

langchain

前段时间去玩了一下langchain,熟悉了一下大模型的基本概念,使用等。前段时间metaollama模型发布了3.0,感觉还是比较强大的,在了解过后,自己去用前后端代码,调用ollama模型搭建了一个本地的gpt应用。

核心逻辑

开始搭建

首先本地需要安装ollama的模型,这里有两种方式,大家自己选择即可,第一种选择官网下载ollama, 第二种可以去docker hub里面下载,里面有ollama的镜像包, 这里为了方便我就用第一种了。

ollama

下载好之后,我们直接执行

shell 复制代码
ollama serve

命令即可, 出现下面的样子,代表执行成功了。

这时候ollama会运行在11434端口,这样我们的后端可以通过端口连接到ollama的服务了。

shell 复制代码
ollama run llama3

直接执行这个命令就可以在本地终端里起一个gpt, llama3ollama最新的模型

后端代码实现

后端我们用nestjs来搭建

首先我们需要安装一个langchain的包 @langchain/community, 我这里选用的社区版,支持度比较好,模型也比较多。

!!!⚠️注意点 推荐用npm和yarn安装, pnpm安装会导致失败,官网上面也有说明这个,楼主踩过坑,如果非要用pnpm安装的话,按照官网指示的操作来

js 复制代码
import { Controller, Post, Body, Sse, Header } from '@nestjs/common';

import { Observable, Subject } from 'rxjs';

import { Ollama } from '@langchain/community/llms/ollama';

  


@Controller()

export class AppController {

private messageSubject = new Subject<MessageEvent>();

private model: Ollama;

constructor() {

  this.model = new Ollama({

    baseUrl: 'http://localhost:11434',

    model: 'llama3',

});

}

  


@Sse('sse')

@Header('Content-Type', 'text/event-stream')

sse(): Observable<MessageEvent> {

  return this.messageSubject.asObservable();

}

  

@Post('question')

async addList(@Body() body: { question: string }): Promise<any> {

  const stream = await this.model.stream(body.question);
  
  for await (const str of stream) {

  this.messageSubject.next({

    data: JSON.stringify({ answer: str, end: false }),

  } as MessageEvent);
Å
}

  
  this.messageSubject.next({

    data: JSON.stringify({ answer: '', end: true }),

  } as MessageEvent);

}

}

sse技术

是一种基于HTTP协议的服务器到客户端的单向数据通信技术,允许服务器向浏览器实时推送更新,而不需要客户端通过轮询等方式反复请求数据。很多gpt应用服务端向客户端发送消息都是利用这种方式去做的。SSE协议本质上就是一个Http的get请求,也支持Https,服务端在接到该请求后,返回状态。同时请求头设置也变为流形式。

js 复制代码
Content-Type: text/event-stream,

这里因为只是示例demo,逻辑就写在controller层了,标准一点的还是抽到service层。

nest的sse接口返回值是一个Observable(可观察对象), 刚好在rxjs中,我们的suject(主体)也是一种Observable suject和普通的Observable区别在于,suject是多播,并且像EventEmitters ,像维护着多个监听器的一张注册表,当我们在请求到ollama返回的数据后,就可以调用next方法,将值多播到Observale中,这样就可以做到结果的接收

这个next方法,其实也就是迭代器方法,不断的调用next,会不断的输出值,直到没有值为止, 这里的end属性是控制我们客户端什么时候和服务端断开连接。

前端实现

前端的逻辑就很简单了,当我们发送问题后,监听到服务端发过来的结果收集起来展示即可,我这里的样式写的比较简陋,功能也只是最基本的,大家可以自己完善

tsx 复制代码
import { useRef, useState } from "react";

type Message = {

answer: string;

end: boolean;

};

export default function Layout() {

const [message, setMessage] = useState<Message[]>([]);

const [question, setQuestion] = useState<string>("");

  


const ref = useRef<any>();

  


const send = () => {

const question = ref.current.value as string;

if (!question) return;

fetch("http://localhost:3000/question", {

method: "POST",

headers: {

"Content-Type": "application/json",

},

body: JSON.stringify({

question,

}),

});

setQuestion(question);

ref.current.value = "";

setMessage((prev) => [...prev, { answer: "", end: false }]);

const eventSource = new EventSource("http://localhost:3000/sse");

eventSource.onmessage = ({ data }) => {

const { answer, end } = JSON.parse(data);

if (!end) {

setMessage((prev) => {

const newMessages = [...prev];

newMessages[newMessages.length - 1].answer += answer;

return newMessages;

});

}

if (end) {

eventSource.close();

}

};

};

  


return (

<div className="h-screen w-screen overflow-hidden bg-orange-400">

<nav className="w-full h-16 flex items-center justify-between bg-indigo-400 px-4">

<span className="w-10 h-10">

<img src="public/ollama.png" className="w-full h-full" />

</span>

<span>ollama大模型</span>

<span className="">欢迎使用</span>

</nav>

  


<div className="w-full h-[calc(100%-64px)] relative">

<aside className="w-[200px] bg-red-400 h-full p-4 absolute top-0 flex justify-center">

todo

</aside>

<main className="w-full h-full absolute left-[200px] top-0 px-2">

<div className="w-full h-[calc(100%-64px)] py-4 overflow-y-auto">

<div

style={{

display: question ? "block" : "none",

}}

className="human min-h-[100px] w-[calc(100%-200px)] p-4 rounded-lg text-white bg-yellow-300"

>

{question}

</div>

<div

style={{

display: message?.[message.length - 1]?.answer

? "block"

: "none",

}}

className="ai min-h-[100px] text-white w-[calc(100%-200px)] bg-indigo-500 mt-5 rounded-lg p-4 "

>

{message?.[message.length - 1]?.answer}

</div>

</div>

<footer className="rounded-lg relative bottom-2 w-[calc(100%-200px)] h-[64px] border-red-300 border-solid border-[1px]">

<input

ref={ref}

type="text"

className="rounded-lg w-full px-4 text-lg h-full caret-red-300 outline-none focus:border-[1px] focus:border-solid focus:border-red-500"

/>

<button

onClick={() => send()}

className="z-10 focus:text-red-700 absolute right-0 top-0 w-[64px] h-[62px] bg-red-300"

>

发送

</button>

</footer>

</main>

</div>

</div>

);

}

看下实现效果

总结

ollama的中文支持度不是很好,看视频效果也能看得出来,不过功能还是很强大的。 功能实现的比较基础,不过核心功能都有,大家可以参考代码自行拓展,动动手,你我都有属于自己的gpt

相关推荐
牛奶2 分钟前
开发者的"奇技淫巧":那些让你效率翻倍的实战技巧
前端·后端·程序员
咸鱼翻身更入味2 分钟前
Vue创建一个简单的Agent聊天——工具调用
前端
Timo来了2 分钟前
indexDB的用法示例
前端
泉城老铁3 分钟前
springboot实现word转换pdf
vue.js·后端
walking9575 分钟前
重新学习前端之设计模式与架构
前端·javascript·面试
walking9578 分钟前
重新学习前端之TypeScript
前端·javascript·面试
walking9579 分钟前
重新学习前端之Linux
前端·vue.js·面试
walking9579 分钟前
重新学习前端之CSS
前端·vue.js·面试
walking9579 分钟前
重新学习前端之Git
前端·vue.js·面试
walking95710 分钟前
重新学习前端之小程序
前端