Ollama+FastAPI+React手把手构建自己的本地大模型,支持SSE

news/2024/9/22 17:21:32/
aidu_pl">

最近大家都在玩LLM,我也凑了热闹,简单实现了一个本地LLM应用,分享给大家,百分百可以用哦~^ - ^

先介绍下我使用的三种工具:

  • Ollama:一个免费的开源框架,可以让大模型很容易的运行在本地电脑上
  • FastAPI:是一个用于构建 API 的现代、快速(高性能)的 web 框架,使用 Python 并基于标准的 Python 类型提示
  • React:通过组件来构建用户界面的库

简单来说就类似于LLM(数据库)+FastAPI(服务端)+React(前端)

开始搭建

1、下载Ollama之后使用Ollama完成大模型的本地下载和的运行

ollama run llama3:8b

这里我下载了最新的llama3:8b,电脑配置不高的话10b以内可以无痛运行,当然啦你也可以多下几个大模型,对比一下,我还下载了qwen,对比下来同一模型越大越聪慧,国内模型对中文支持度普遍好一点。

2、模型运行之后就可以调用了

curl http://localhost:11434/api/generate -d '{  
"model": "llama3:8b",  
"prompt": "Why is the sky blue?",  
"stream": false  
}'

3、新建一个python项目,实现代码如下:

import uvicorn
from fastapi import FastAPI, Request, HTTPException
from fastapi.middleware.cors import CORSMiddleware
from pydantic import BaseModel
import json
import requests
from sse_starlette.sse import EventSourceResponse
import asyncio
import aiohttpapp = FastAPI(debug=True)origins = ["http://localhost",# 输入自己前端项目的地址
]# 设置跨域
app.add_middleware(CORSMiddleware,allow_origins=origins,allow_credentials=True,allow_methods=["*"],allow_headers=["*"],
)urls = ["http://localhost:11434/api/generate"]llm_list = [ {'label': 'qwen:latest', "value": 'qwen:latest'},{'label': 'llama3:8b', "value": 'llama3:8b'}, ]# 获取模型列表
@app.get("/llm/list")
def read_llm(model: str = 'qwen:latest'):return {"data": llm_list}# 这是一个异步生成器函数,它发送请求到 Ollama,并逐行读取响应内容,生成事件流。
async def stream_ollama_response(model_name, prompt):if model_name:url = urls[0]payload = {"model": model_name,"prompt": prompt,"stream": True}async with aiohttp.ClientSession() as session:async with session.post(url, json=payload) as response:async for line in response.content:if line:data = line.decode('utf-8').strip()if data:yield {"event": "message", "data": json.loads(data)["response"]}# 开始对话,接收 model_name 和 prompt 参数。它调用 event_generator 函数,启动与 Ollama 的交互,并通过 EventSourceResponse 返回事件流
@app.get("/chat")
async def generate(request: Request, model_name: str = 'qwen:latest',prompt: str = '请用中文介绍下中国古代四大名著之一的《红楼梦》'):async def event_generator():async for event in stream_ollama_response(model_name, prompt):yield eventif await request.is_disconnected():breakreturn EventSourceResponse(event_generator())if __name__ == '__main__':uvicorn.run(app="app", host="127.0.0.1", port=8000, reload=True)

这是用SSE形式实现流式输出的demo,下一篇我再讲讲如何用WebSocket实现。

4、新建一个react项目,我用了antd大礼包+@microsoft/fetch-event-source这个微软的sse插件实现,代码如下:

import { Input, Dropdown, Select, Form, Button, Space } from 'antd';
import { useEffect, useState } from 'react';
import { getList, chat } from './service';
import { useRequest } from '@umijs/max';
import { fetchEventSource } from '@microsoft/fetch-event-source';const { TextArea } = Input;# 不能走代理哦,走了代理流式就失效了,?- ?
export const getHost = () => {const isDev = process.env.NODE_ENV === 'development';if (isDev) {return 'http://127.0.0.1:8000';} else {return '';}
};export default () => {const [form] = Form.useForm();const { data = [] } = useRequest(getList);const [value, setValue] = useState('');const [start, setStart] = useState(null);const [end, setEnd] = useState(null);const [selected, setSelected] = useState(false);const [controller, setController] = useState(new AbortController());const sharedProps = {style: { width: '100%' },autoSize: { minRows: 3, maxRows: 20 },onChange: (e) => {setValue(e.target.value);setStart(e.target.selectionStart);},onClick: (e) => {setStart(e.target.selectionStart);},onSelect: (e) => {setStart(e.target.selectionStart);setEnd(e.target.selectionEnd);setSelected(e.target.value.substring(e.target.selectionStart, e.target.selectionEnd));},};const items = [{label: '重写这句话',key: '3',},{label: '把这句话翻译成中文',key: '4',},];const menuClick = ({ key }) => {switch (key) {case '3':return reWrite();case '4':return reWrite('zh-CN');}};const reWrite = async (type) => {if (!selected) {return;}setValue(value.slice(0, start) + '重写中。。。' + value.slice(end));const res = await chat({model: form.getFieldValue('model'),prompt: type? `${selected}”把“”中的这句话或单词翻译成中文,返回不要带格式,直接返回翻译结果`: selected,});setValue(value.slice(0, start) + res.data + value.slice(end));};# 获取数据流const fetchData = async (url) => {await fetchEventSource(url, {method: 'GET',signal: controller.signal,onopen(res) {if (res.ok && res.status === 200) {console.log('Connection made ', res);} else if (res.status >= 400 && res.status < 500 && res.status !== 429) {errorHandler(res);console.log('Client side error ', res);}},onmessage(event) {console.log(event);setValue((data) => [...data, event.data].join(''));},onclose() {console.log('Connection closed by the server');},onerror(err) {console.log('There was an error from server', err);},});};const onFinish = (values) => {fetchData(`${getHost()}/chat?model_name=${values.model_name}&prompt=${values.prompt}`);};return (<div><Form onFinish={onFinish} form={form}><Form.Item name="model_name" label="模型"><Select style={{ width: 200 }} options={[...data]} /></Form.Item><Form.Item name="prompt" label="提问"><Input /></Form.Item><Form.Item><Space><Button type="primary" htmlType="submit">提交</Button><Button onClick={() => controller.abort()}>暂停</Button></Space></Form.Item></Form><Dropdown menu={{ items, onClick: menuClick }} trigger={['contextMenu']}><TextArea value={value} {...sharedProps} /></Dropdown></div>);
};

界面比较简陋,大家随便看一下:

前端代码还加了些小功能,比如右键支持某句话的替换和翻译,因为用了input,所以可以获取光标的位置从而把文本插入或者替换选中文本。不过还有个弊端就是没办法支持markdown输出了,这个问题暂时还不知道怎么解决,要么再添加个预览模式。

最后的最后

感谢你们的阅读和喜欢,我收藏了很多技术干货,可以共享给喜欢我文章的朋友们,如果你肯花时间沉下心去学习,它们一定能帮到你。

因为这个行业不同于其他行业,知识体系实在是过于庞大,知识更新也非常快。作为一个普通人,无法全部学完,所以我们在提升技术的时候,首先需要明确一个目标,然后制定好完整的计划,同时找到好的学习方法,这样才能更快的提升自己。

这份完整版的大模型 AI 学习资料已经上传CSDN,朋友们如果需要可以微信扫描下方CSDN官方认证二维码免费领取【保证100%免费

一、全套AGI大模型学习路线

AI大模型时代的学习之旅:从基础到前沿,掌握人工智能的核心技能!

img

二、640套AI大模型报告合集

这套包含640份报告的合集,涵盖了AI大模型的理论研究、技术实现、行业应用等多个方面。无论您是科研人员、工程师,还是对AI大模型感兴趣的爱好者,这套报告合集都将为您提供宝贵的信息和启示。

img

三、AI大模型经典PDF籍

随着人工智能技术的飞速发展,AI大模型已经成为了当今科技领域的一大热点。这些大型预训练模型,如GPT-3、BERT、XLNet等,以其强大的语言理解和生成能力,正在改变我们对人工智能的认识。 那以下这些PDF籍就是非常不错的学习资源。

img

四、AI大模型商业化落地方案

img

五、面试资料

我们学习AI大模型必然是想找到高薪的工作,下面这些面试题都是总结当前最新、最热、最高频的面试题,并且每道题都有详细的答案,面试前刷完这套面试题资料,小小offer,不在话下。
在这里插入图片描述

这份完整版的大模型 AI 学习资料已经上传CSDN,朋友们如果需要可以微信扫描下方CSDN官方认证二维码免费领取【保证100%免费


http://www.ppmy.cn/news/1467182.html

相关文章

Thread线程控制之sleep、join、setDaemon方法的用处

Thread线程控制之sleep、join、setDaemon方法的用处 sleep方法 public static void sleep(long millis) throws InterruptedException使当前正在执行的线程以指定的毫秒数暂停&#xff08;暂时停止执行&#xff09;&#xff0c;具体取决于系统定时器和调度程序的精度和准确性…

20232815 2023-2024-2 《网络攻防实践》实践十一报告

20232815 2023-2024-2 《网络攻防实践》实践十一报告 1.实践内容 网络渗透&#xff1a; 网络渗透是攻击者常用的一种攻击手段&#xff0c;也是一种综合的高级攻击技术&#xff0c;同时网络渗透也是安全工作者所研究的一个课题&#xff0c;在他们口中通常被称为”渗透测试&…

数据结构:双链表

数据结构&#xff1a;双链表 题目描述参考代码 题目描述 输入样例 10 R 7 D 1 L 3 IL 2 10 D 3 IL 2 7 L 8 R 9 IL 4 7 IR 2 2输出样例 8 7 7 3 2 9参考代码 #include <iostream>using namespace std;const int N 100010;int m; int idx, e[N], l[N], r[N];void init…

一个可以自动生成随机区组试验的excel VBA小程序

在作物品种区域试验时&#xff0c;通常会采用随机区组试验设计&#xff0c;特制作了一个可以自动生成随机区组试验的小程序。excel参数界面如下&#xff1a; 参数含义如下&#xff1a; 1、生成新表的名称&#xff1a;程序将新建表格&#xff0c;用于生成随机区组试验。若此处为…

C++中count()和count_if()函数简介

count()函数 [C] count函数 【C】统计string里面出现的字符的个数&#xff08;使用count函数&#xff09; algorithm头文件定义了一个count的函数&#xff0c;其功能类似于find。这个函数使用一对迭代器和一个值做参数&#xff0c;返回这个值出现次数的统计结果 #include algo…

uniapp - 填充页面

在上一篇文章中&#xff0c;创建了一个空白的文章模块页面。在这一篇文章&#xff0c;让我们来向页面中填充内容。 目录 页面效果涉及uniapp组件1.view2.swiper3.scroll-view4.属性解读1) class"style1 style2 .."2) circular单属性无赋值3) :autoplay"autoplay…

海外仓系统选择教程:只要能满足性价比需求,何必自己开发?

自己单独开发一套海外仓系统这对大型海外仓集团尚且是很大的挑战&#xff0c;更何况对中小海外仓和家庭仓。其实对这类体量比较小的海外仓来说&#xff0c;提升海外仓管理效率最佳的方式不是自己开发系统&#xff0c;而是选择市场上比较成熟的系统&#xff0c;直接拿来即用&…

PS系统教程11

HUD拾色器 作用&#xff1a;它可以帮助使用者更加高效地选择和使用颜色&#xff0c;从而提高工作效率和设计质量。 先确定色相值改变饱和度改变亮度使用HUD拾色器选中画笔工具画笔模式-正常shiftAlt右键 色相轮 上下移动从黑到白亮度变化左右移动从浅到深饱和度的变化选中颜…