GPT聊天功能,逐字返回数据

目录

  • 前言
  • 一、前端
  • 二、后端
    • 1.接收前端请求的api
      • `如下是继续向其他接口请求的api`
      • `如下是直接返回前端数据的api`
      • `甚至可以返回图片`
    • 2.模拟GPT的接口


前言

我们在和GPT交流的时候发现GPT总是逐字的显示,因为GPT是一种基于神经网络的自然语言处理模型,它的训练数据是从大量的文本语料库中获取的。在训练过程中,GPT会学习到文本的语法、语义和上下文信息,并尝试预测下一个单词或字符。因此,当GPT接收到一段文本时,它会根据之前学习到的知识,逐字分析并返回相应的数据。这种逐字返回数据的行为是GPT模型的自然表现,也是它能够生成连贯、流畅文本的重要原因之一。
但当我们想研发一个套壳网站时,因为GPT会逐字返回数据,那么我们也需要逐字为前端提供数据,我们可以使用WebsocketSSE长轮询的方式来实现,这些网络上有太多例子,这次用.net 丰富的对异步支持,编写异步流式返回数据。


一、前端

通过fetch的方式向后端请求数据,并通过时时读取异步流的方式将数据渲染在页面中

<div>   
    <input type="button" id="getdata" value="获取数据" />
    <br />
    <textarea cols="100" rows="50" id="data" style="width: 500px;">textarea>    
div>
<script>
    $(document).ready(function () {
        $("#getdata").click(async function fetchText() {
            fetch('/Data/GetText')
                .then(response => {
                    const reader = response.body.getReader();
                    let result = '';
                    return reader.read().then(function processResult({ done, value }) {
                        if (done) {
                            return result;
                        }
                        result += new TextDecoder('utf-8').decode(value);
                        // 将result显示在页面上
                        $('#data').html(result);
                        return reader.read().then(processResult);
                    });
                })
                .catch(error => {
                    console.error(error);
                });
        })
    });

script>

二、后端

1.接收前端请求的api

如下是继续向其他接口请求的api

		/// 
        /// 向模拟的GPT后端发送请求
        /// 
        /// 
        public async Task GetText()
        {
            var url = "http://localhost:5041/Data/GetStreamData";
            var client = new HttpClient();
            using HttpRequestMessage httpRequestMessage = new HttpRequestMessage(HttpMethod.Get, url);
            var response = await client.SendAsync(httpRequestMessage, HttpCompletionOption.ResponseHeadersRead);
            await using var stream = await response.Content.ReadAsStreamAsync();
            var streamReader = new StreamReader(stream);
            var buffer = new Memory<char>(new char[5]);
            int writeLength = 0;
            while ((writeLength = await streamReader.ReadBlockAsync(buffer)) > 0)
            {
                if (writeLength < buffer.Length)
                {
                    buffer = buffer[..writeLength];
                }
                await Response.WriteAsync(buffer.ToString());
            }
        }

HttpCompletionOption枚举有两个值,默认情况下使用的是ResponseContentRead

  1. ResponseContentRead:等到整个响应完成才完成操作
  2. ResponseHeadersRead:一旦获取到响应头即完成操作,不用等到整个内容响应

如下是直接返回前端数据的api

		/// 
        /// 不需要转发可以使用这个接口,也可以实现逐字返回数据
        /// 
        /// 
        /// 
        public async Task GetData(string value)
        {
            List<string> strings = new List<string>() { "兰", "叶", "春", "葳", "蕤", ",", "桂", "华", "秋", "皎", "洁", "。",
"欣", "欣", "此", "生", "意", ",", "自", "尔", "为", "佳", "节", "。",
"谁", "知", "林", "栖", "者", ",", "闻", "风", "坐", "相", "悦", "。",
"草", "木", "有", "本", "心", ",", "何", "求", "美", "人", "折", "!"  };
            string result = string.Empty;
            for (int i = 0; i < strings.Count; i++)
            {
                result = strings[i];
                await Response.WriteAsync(result);
                await Task.Delay(100);
            }
        }

甚至可以返回图片

   <input type="button" id="getimg" value="获取图片" />
   <img id="img" style="width: 500px;" />
<script>
    $(document).ready(function () {
        $('#getimg').click(async function () {
            const streamingImage = document.getElementById('img');
            fetch('/Data/Stream', {
                method: 'GET',
                headers: {
                    'Content-Type': 'application/octet-stream'
                }
            }).then(response => {
                // 将响应体转换为可读流
                const reader = response.body.getReader();
                // 创建一个Blob对象来存储图像数据
                let chunks = [];
                let blob = new Blob(chunks, { type: 'image/jpeg' });
                // 定义一个函数来处理每个数据块
                function handleDataChunk(chunk) {
                    chunks.push(chunk);
                    blob = new Blob(chunks, { type: 'image/jpeg' });
                    streamingImage.src = URL.createObjectURL(blob);
                }
                // 定义一个函数来处理可读流的数据
                function readStream() {
                    reader.read().then(({ done, value }) => {
                        if (done) {
                            console.log('Stream finished');
                            return;
                        }
                        handleDataChunk(value);
                        readStream();
                    });
                }
                // 开始读取可读流的数据
                readStream();
            }).catch(error => {
                console.error('Fetch error:', error);
            });
        })
    });

script>
public async Task Stream()
{
    string filePath = "pixelcity.png";
    new FileExtensionContentTypeProvider().TryGetContentType(filePath, out string contentType);
    Response.ContentType = contentType ?? "application/octet-stream";
    var fileStream = System.IO.File.OpenRead(filePath);
    var bytes = new byte[1024];
    int writeLength = 0;
    while ((writeLength = fileStream.Read(bytes, 0, bytes.Length)) > 0)
    {
        await Response.Body.WriteAsync(bytes, 0, writeLength);
        await Task.Delay(100);
    }
}

2.模拟GPT的接口

代码如下(示例):


        /// 
        /// 模拟GPT接口
        /// 
        /// 
        /// 	
        public async Task GetStreamData()
        {
            string filePath = "文档.txt";
            Response.ContentType = "application/octet-stream";
            var reader = new StreamReader(filePath);
            var buffer = new Memory<char>(new char[5]);
            int writeLength = 0;
            //每次读取5个字符写入到流中
            while ((writeLength = await reader.ReadBlockAsync(buffer)) > 0)
            {
                if (writeLength < buffer.Length)
                {
                    buffer = buffer[..writeLength];
                }
                await Response.WriteAsync(buffer.ToString());
                await Task.Delay(100);
            }
        }

你可能感兴趣的:(c#,MVC,GPT聊天功能,逐字返回数据,阶段性返回数据,GPT聊天长连接,.net异步流)