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

news2024/11/20 4:33:09

最近大家都在玩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 aiohttp

app = 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 event
            if await request.is_disconnected():
                break

    return 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.coloradmin.cn/o/1791565.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

JVMの垃圾回收

在上一篇中&#xff0c;介绍了JVM组件中的运行时数据区域&#xff0c;这一篇主要介绍垃圾回收器 JVM架构图&#xff1a; 1、垃圾回收概述 在第一篇中介绍JVM特点时&#xff0c;有提到过内存管理&#xff0c;即Java语言相对于C&#xff0c;C进行的优化&#xff0c;可以在适当的…

【AI大模型】Transformers大模型库(三):特殊标记(special tokens)

目录​​​​​​​ 一、引言 二、特殊标记&#xff08;special tokens&#xff09; 2.1 概述 2.2 主要功能 2.3 代码示例 三、总结 一、引言 这里的Transformers指的是huggingface开发的大模型库&#xff0c;为huggingface上数以万计的预训练大模型提供预测、训练等服…

设计模式之过滤器模式FilterPattern(十)

一、过滤器模式 过滤器模式&#xff08;Filter Pattern&#xff09;或标准模式&#xff08;Criteria Pattern&#xff09;是一种设计模式&#xff0c;这种模式允许开发人员使用不同的标准来过滤一组对象&#xff0c;通过逻辑运算以解耦的方式把它们连接起来。这种类型的设计模…

Python爬虫之简单学习BeautifulSoup库,学习获取的对象常用方法,实战豆瓣Top250

BeautifulSoup是一个非常流行的Python库&#xff0c;广泛应用于网络爬虫开发中&#xff0c;用于解析HTML和XML文档&#xff0c;以便于从中提取所需数据。它是进行网页内容抓取和数据挖掘的强大工具。 功能特性 易于使用: 提供简洁的API&#xff0c;使得即使是对网页结构不熟悉…

QtCharts使用

1.基础配置 1.QGraphicsView提升为QChartView#include <QtCharts> QT_CHARTS_USE_NAMESPACE #include "ui_widget.h"2. QT charts 2.柱状图 2.1QBarSeries //1.创建Qchart对象QChart *chart new QChart();chart->setTitle("直方图演示");//设…

【机器学习300问】107、自然语言处理(NLP)领域有哪些子任务?

自然语言处理&#xff08;NLP&#xff09;是计算机科学、人工智能和语言学领域的一个交叉学科&#xff0c;致力于让计算机能够理解、解析、生成和与人类的自然语言进行互动。自然语言指的是人们日常交流使用的语言&#xff0c;如英语、汉语等&#xff0c;与计算机编程语言相对。…

MySQL中:cmd下输入命令mysql -uroot -p 连接数据库错误

目录 问题cmd下输入命令mysql -uroot -p错误 待续、更新中 问题 cmd下输入命令mysql -uroot -p错误 解决 配置环境变量&#xff1a;高级系统设置——环境变量——系统变量——path编辑——新建——MySQL.exe文件路径&#xff08;如下图所示&#xff09; phpstudy2018软件下&am…

《微服务大揭秘:SpringBoot与SpringCloud的魔法组合》

加入我们的探险队伍&#xff0c;一起深入SpringBoot与SpringCloud构建的微服务世界。以轻松幽默的笔触&#xff0c;带你一步步揭开微服务架构的神秘面纱&#xff0c;从服务发现的智能地图Eureka&#xff0c;到API网关Zuul的城市门卫&#xff0c;每一个环节都充满了惊喜。不仅如…

北航数据结构与程序设计第四次作业选填题复习

首先都是线性的&#xff0c;线性包括顺序和链式&#xff0c;栈和队都可以用两种方式实现。栈只能存于栈顶取于栈顶&#xff0c;队列先进先出&#xff0c;因此存取点是固定的。 函数栈帧创建原理 画图即可。 A.显然不行&#xff0c;5如果第一个出来说明5是最后一个进的&#xf…

《2024年网络安全预测:未来规划深度洞察》

2024 年打击网络对手的计划。 阅读报告&#xff0c;了解我们的专家对 2024 年网络安全行业的预测&#xff0c;包括&#xff1a; 攻击者将人工智能融入其行动中&#xff0c;防御者利用它来加强检测和响应 民族国家继续开展网络行动以实现其地缘政治目标 攻击者继续利用零日漏洞…

nginx代理vue项目路由跳转刷新

常规代理 在我们日常开发中&#xff0c;前端部署到服务器&#xff0c;需要用到nginx部署&#xff0c;简单代理如下&#xff1a; #user nobody; worker_processes 1;#error_log logs/error.log; #error_log logs/error.log notice; #error_log logs/error.log info;#pid…

node mysql的增删改查基础

学习koa时&#xff0c;不选择mongodb&#xff0c;而是MySQL&#xff0c;虽然node对mongodb更亲和&#xff0c;但是我感觉MySQL的键值对的储存结构更正规 1.首选确认你的数据库有个库。有个表,我的如下 2.配置 let mySqlConfig{host:localhost,user:root,password:123456,data…

单列集合--List

方法演示&#xff1a; package exercise;import java.util.ArrayList; import java.util.List;public class ListDemo1 {public static void main(String[] args) {List<String> list new ArrayList<>();list.add("hello");list.add("world"…

【MATLAB源码-第222期】基于matlab的改进蚁群算法三维栅格地图路径规划,加入精英蚁群策略。包括起点终点,障碍物,着火点,楼梯。

操作环境&#xff1a; MATLAB 2022a 1、算法描述 蚁群算法&#xff08;Ant Colony Optimization&#xff0c;ACO&#xff09;是一种通过模拟蚂蚁觅食行为的启发式优化算法。它由意大利学者Marco Dorigo在20世纪90年代初提出&#xff0c;最初用于解决旅行商问题&#xff08;T…

LabVIEW源程序安全性保护综合方案

LabVIEW源程序安全性保护综合方案 一、硬件加密保护方案 选择和安装硬件设备 选择加密狗和TPM设备&#xff1a;选择Sentinel HASP加密狗和支持TPM&#xff08;可信平台模块&#xff09;的计算机主板。 安装驱动和开发工具&#xff1a;安装Sentinel HASP加密狗的驱动程序和开发…

Linux: ubi rootfs 加载故障案例

文章目录 1. 前言2. ubi rootfs 加载故障现场3. 故障分析与解决4. 参考资料 1. 前言 限于作者能力水平&#xff0c;本文可能存在谬误&#xff0c;因此而给读者带来的损失&#xff0c;作者不做任何承诺。 2. ubi rootfs 加载故障现场 问题故障内核日志如下&#xff1a; Star…

python-01

第一个程序 import randomcomputer random.randint(1, 3) print(电脑出的是&#xff1a;, computer) i int(input(你要出什么&#xff1f;1代表石头&#xff0c;2代表剪刀&#xff0c;3代表布\n)) if i computer:print(平局) elif (computer 1 and i 3) or (computer 2 …

DP动态规划(上)

文章目录 动态规划基本概念斐波那契数列问题C 实现Python 实现Java 实现 迷你结C、Python和Java在实现动态规划时有哪些性能差异&#xff1f;迷你结哪种语言在动态规划中更适合大规模数据处理?迷你结C有哪些知名的库适用于动态规划和大数据处理?动态规划辅助库大数据处理库 迷…

vs2019 c++20 规范 STL库中关于时间的模板 ratio<T,U> , duration<T,U> , time_point<T,U>等

(探讨一)在学习线程的时候&#xff0c;一些函数会让线程等待或睡眠一段时间。函数形参是时间单位&#xff0c;那么在 c 中是如何记录和表示时间的呢&#xff1f;以下给出模板简图&#xff1a; &#xff08;2 探讨二&#xff09;接着给出对模板类 duration_values 的成员函数的测…

STM32(八):独立看门狗 (标准库函数)

前言 上一篇文章介绍了STM32单片机中的USART串口通信&#xff0c;这篇文章我们来介绍一下如何用STM32单片机中的独立看门狗来实现检测按键点灯的程序。 一、实验原理 单片机系统会由于受到外界的干扰&#xff0c;而造成程序执行紊乱&#xff0c;系统无法正常运行。为了防止这…