跨平台`ChatGpt` 客户端

开发 前端
实现发送前需要将之前的最近五条数据得到跟随当前数据一块发送,为了让其ChatGpt可以联系上下文,这样回复的内容更准确,聊天的数据使用Sqlite本地存储,为了轻量使用ORM采用FreeSql,界面仿制微信的百分之八十的还原度,基本上一直,而且源码完全开源。

一款基于Avalonia​实现的跨平台ChatGpt​客户端 ,通过对接ChatGpt​官方提供的ChatGpt 3.5模型实现聊天对话

实现创建ChatGpt​的项目名称 ,项目类型是Avalonia MVVM ,

添加项目需要使用的Nuget包

<ItemGroup>
        <PackageReference Include="Avalonia" Version="11.0.0-preview5" />
        <PackageReference Include="Avalonia.Themes.Fluent" Version="11.0.0-preview5" />
        <PackageReference Include="Avalonia.ReactiveUI" Version="11.0.0-preview5" />
        <!--Condition below is needed to remove Avalonia.Diagnostics package from build output in Release configuration.-->
        <PackageReference Condition="'$(Configuration)' == 'Debug'" Include="Avalonia.Diagnostics" Version="11.0.0-preview5" />
        <PackageReference Include="FreeSql.Provider.Sqlite" Version="3.2.690" />
        <PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="7.0.0" />
        <PackageReference Include="Microsoft.Extensions.Http" Version="7.0.0" />
        <PackageReference Include="XamlNameReferenceGenerator" Version="1.6.1" />
        <PackageReference Include="Avalonia.Svg.Skia" Version="11.0.0-preview5" />
    </ItemGroup>
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.

ViewLocator.cs代码修改

using System;
using Avalonia.Controls;
using Avalonia.Controls.Templates;
using ChatGPT.ViewModels;

namespace ChatGPT;

public class ViewLocator : IDataTemplate
{
    public Control? Build(object? data)
    {
        if (data is null)
            return null;

        var name = data.GetType().FullName!.Replace("ViewModel", "View");
        var type = Type.GetType(name);

        if (type != null)
        {
            return (Control)Activator.CreateInstance(type)!;
        }

        return new TextBlock { Text = name };
    }

    public bool Match(object? data)
    {
        return data is ViewModelBase;
    }
}
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.
  • 17.
  • 18.
  • 19.
  • 20.
  • 21.
  • 22.
  • 23.
  • 24.
  • 25.
  • 26.
  • 27.
  • 28.
  • 29.
  • 30.

创建MainApp.cs文件

using Microsoft.Extensions.DependencyInjection;

namespace ChatGPT;

public static class MainApp
{
    private static IServiceProvider ServiceProvider;

    public static ServiceCollection CreateServiceCollection()
    {
        return new ServiceCollection();
    }

    public static IServiceProvider Build(this IServiceCollection services)
    {
        return ServiceProvider = services.BuildServiceProvider();
    }
    
    public static T GetService<T>()
    {
        if (ServiceProvider is null)
        {
            throw new ArgumentNullException(nameof(ServiceProvider));
        }
        return ServiceProvider.GetService<T>();
    }
    
    public static IEnumerable<T> GetServices<T>()
    {
        if (ServiceProvider is null)
        {
            throw new ArgumentNullException(nameof(ServiceProvider));
        }
        return ServiceProvider.GetServices<T>();
    }

    public static object? GetService(Type type)
    {
        if (ServiceProvider is null)
        {
            throw new ArgumentNullException(nameof(ServiceProvider));
        }
        return ServiceProvider.GetService(type);
    }
}
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.
  • 17.
  • 18.
  • 19.
  • 20.
  • 21.
  • 22.
  • 23.
  • 24.
  • 25.
  • 26.
  • 27.
  • 28.
  • 29.
  • 30.
  • 31.
  • 32.
  • 33.
  • 34.
  • 35.
  • 36.
  • 37.
  • 38.
  • 39.
  • 40.
  • 41.
  • 42.
  • 43.
  • 44.
  • 45.

创建GlobalUsing.cs文件 全局引用

global using System.Reactive;
global using Avalonia;
global using Avalonia.Controls;
global using ChatGPT.ViewModels;
global using Avalonia;
global using Avalonia.Controls.ApplicationLifetimes;
global using Avalonia.Markup.Xaml;
global using ChatGPT.ViewModels;
global using ChatGPT.Views;
global using System;
global using System.Collections.Generic;
global using ReactiveUI;
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.

修改App.axaml代码文件

<Application xmlns="https://github.com/avaloniaui"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             xmlns:local="using:ChatGPT"
             xmlns:converter="clr-namespace:ChatGPT.Converter"
             RequestedThemeVariant="Light"
             x:Class="ChatGPT.App">
    <Application.Resources>
        <converter:HeightConverter x:Key="HeightConverter" />
    </Application.Resources>
    <Application.DataTemplates>
        <local:ViewLocator/>
    </Application.DataTemplates>

    <Application.Styles>
        <FluentTheme DensityStyle="Compact"/>
    </Application.Styles>
    
</Application>
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.
  • 17.
  • 18.

修改App.axaml.cs代码文件

using Avalonia.Platform;
using Avalonia.Svg.Skia;
using ChatGPT.Options;
using Microsoft.Extensions.DependencyInjection;

namespace ChatGPT;

public partial class App : Application
{
    public override void Initialize()
    {
        GC.KeepAlive(typeof(SvgImageExtension).Assembly);
        GC.KeepAlive(typeof(Avalonia.Svg.Skia.Svg).Assembly);

        var services = MainApp.CreateServiceCollection();

        services.AddHttpClient("chatGpt")
            .ConfigureHttpClient(options =>
            {
                var chatGptOptions = MainApp.GetService<ChatGptOptions>();
                if (!string.IsNullOrWhiteSpace(chatGptOptions?.Token))
                {
                    options.DefaultRequestHeaders.Add("Authorization",
                        "Bearer " + chatGptOptions?.Token.TrimStart().TrimEnd());
                }
            });

        services.AddSingleton<ChatGptOptions>(ChatGptOptions.NewChatGptOptions());

        services.AddSingleton(new FreeSql.FreeSqlBuilder()
            .UseConnectionString(FreeSql.DataType.Sqlite,
                "Data Source=chatGpt.db;Pooling=true;Min Pool Size=1")
            .UseAutoSyncStructure(true) //自动同步实体结构到数据库
            .Build());

        services.Build();

        AvaloniaXamlLoader.Load(this);
    }

    public override void OnFrameworkInitializationCompleted()
    {
        if (ApplicationLifetime is IClassicDesktopStyleApplicationLifetime desktop)
        {
            desktop.MainWindow = new MainWindow
            {
                DataContext = new MainViewModel()
            };
        }

        var notifyIcon = new TrayIcon();
        notifyIcon.Menu ??= new NativeMenu();
        notifyIcon.ToolTipText = "ChatGPT";

        var assets = AvaloniaLocator.Current.GetService<IAssetLoader>();

        notifyIcon.Icon = new WindowIcon(assets.Open(new Uri("avares://ChatGPT/Assets/chatgpt.ico")));
        var exit = new NativeMenuItem()
        {
            Header = "退出ChatGPT"
        };

        exit.Click += (sender, args) => Environment.Exit(0);
        notifyIcon.Menu.Add(exit);

        base.OnFrameworkInitializationCompleted();
    }
}
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.
  • 17.
  • 18.
  • 19.
  • 20.
  • 21.
  • 22.
  • 23.
  • 24.
  • 25.
  • 26.
  • 27.
  • 28.
  • 29.
  • 30.
  • 31.
  • 32.
  • 33.
  • 34.
  • 35.
  • 36.
  • 37.
  • 38.
  • 39.
  • 40.
  • 41.
  • 42.
  • 43.
  • 44.
  • 45.
  • 46.
  • 47.
  • 48.
  • 49.
  • 50.
  • 51.
  • 52.
  • 53.
  • 54.
  • 55.
  • 56.
  • 57.
  • 58.
  • 59.
  • 60.
  • 61.
  • 62.
  • 63.
  • 64.
  • 65.
  • 66.
  • 67.
  • 68.

修改MainWindow.axaml文件

<Window xmlns="https://github.com/avaloniaui"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        xmlns:viewModels="clr-namespace:ChatGPT.ViewModels"
        xmlns:pages="clr-namespace:ChatGPT.Pages"
        mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
        x:Class="ChatGPT.Views.MainWindow"
        ExtendClientAreaToDecorationsHint="True"
        ExtendClientAreaChromeHints="NoChrome"
        ExtendClientAreaTitleBarHeightHint="-1"
        Height="{Binding Height}"
        MinHeight="500"
        MinWidth="800"
        Width="1060"
        Name="Main">

    <Design.DataContext>
        <viewModels:MainViewModel />
    </Design.DataContext>

    <StackPanel Name="StackPanel" HorizontalAlignment="Stretch" VerticalAlignment="Stretch">
        <WrapPanel Name="WrapPanel" VerticalAlignment="Stretch" Height="{Binding ElementName=StackPanel, Path=Height}">
            <StackPanel MaxWidth="55" Width="55">
                <DockPanel Background="#2E2E2E" Height="{Binding Height}">
                    <StackPanel DockPanel.Dock="Top">
                        <StackPanel Margin="0,32,0,0"></StackPanel>
                        <StackPanel Margin="8">
                            <Image Source="/Assets/avatar.png"></Image>
                        </StackPanel>
                        <StackPanel Name="ChatStackPanel" Margin="15">
                            <Image Source="/Assets/chat-1.png"></Image>
                        </StackPanel>
                    </StackPanel>

                    <StackPanel Margin="5" VerticalAlignment="Bottom" DockPanel.Dock="Bottom">
                        <StackPanel VerticalAlignment="Bottom" Name="FunctionStackPanel">
                            <Menu HorizontalAlignment="Stretch" VerticalAlignment="Stretch">
                                <MenuItem HorizontalAlignment="Stretch" VerticalAlignment="Stretch">
                                    <MenuItem.Header>
                                        <Image Margin="5" Height="20" Width="20" Source="/Assets/function.png"></Image>
                                    </MenuItem.Header>
                                    <MenuItem Click="Setting_OnClick" Name="Setting" Header="设置" />
                                </MenuItem>
                            </Menu>
                        </StackPanel>
                    </StackPanel>
                </DockPanel>
            </StackPanel>

            <Border Width="250" MaxWidth="250" BorderBrush="#D3D3D3" BorderThickness="0,0,1,0">
                <StackPanel>
                    <pages:ChatShowView Name="ChatShowView"/>
                </StackPanel>
            </Border>

            <StackPanel>
                <StackPanel Height="{Binding Height}" HorizontalAlignment="Center" VerticalAlignment="Center">
                    <pages:SendChat DataContext="{Binding SendChatViewModel}"></pages:SendChat>
                </StackPanel>
            </StackPanel>
        </WrapPanel>
    </StackPanel>
</Window>
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.
  • 17.
  • 18.
  • 19.
  • 20.
  • 21.
  • 22.
  • 23.
  • 24.
  • 25.
  • 26.
  • 27.
  • 28.
  • 29.
  • 30.
  • 31.
  • 32.
  • 33.
  • 34.
  • 35.
  • 36.
  • 37.
  • 38.
  • 39.
  • 40.
  • 41.
  • 42.
  • 43.
  • 44.
  • 45.
  • 46.
  • 47.
  • 48.
  • 49.
  • 50.
  • 51.
  • 52.
  • 53.
  • 54.
  • 55.
  • 56.
  • 57.
  • 58.
  • 59.
  • 60.
  • 61.
  • 62.
  • 63.
  • 64.

修改MainWindow.axaml.cs文件

using Avalonia.Interactivity;
using ChatGPT.Pages;

namespace ChatGPT.Views;

public partial class MainWindow : Window
{
    public MainWindow()
    {
        InitializeComponent();

        var observer = Observer.Create<Rect>(rect =>
        {
            if (ViewModel is null) return;

            ViewModel.SendChatViewModel.Height = (int)rect.Height;
            ViewModel.SendChatViewModel.Width = (int)rect.Width - 305;
            ViewModel.Height = (int)rect.Height;
            ViewModel.SendChatViewModel.ShowChatPanelHeight =
                (int)rect.Height - ViewModel.SendChatViewModel.SendPanelHeight - 60;
        });

        this.GetObservable(BoundsProperty).Subscribe(observer);

        ChatShowView = this.Find<ChatShowView>(nameof(ChatShowView));

        ChatShowView.OnClick += view =>
        {
            ViewModel.SendChatViewModel.ChatShow = view;
        };
    }

    private MainViewModel ViewModel => DataContext as MainViewModel;

    private void Setting_OnClick(object? sender, RoutedEventArgs e)
    {
        var setting = new Setting
        {
            DataContext = ViewModel.SettingViewModel
        };
        setting.Show();
    }
}
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.
  • 17.
  • 18.
  • 19.
  • 20.
  • 21.
  • 22.
  • 23.
  • 24.
  • 25.
  • 26.
  • 27.
  • 28.
  • 29.
  • 30.
  • 31.
  • 32.
  • 33.
  • 34.
  • 35.
  • 36.
  • 37.
  • 38.
  • 39.
  • 40.
  • 41.
  • 42.
  • 43.

提供部分代码 所有源码都是开源,链接防止最下面

效果图

图片

SendChat.axaml.cs中提供了请求ChatGpt 3.5的实现

using System.Linq;
using System.Net.Http;
using System.Net.Http.Json;
using System.Threading.Tasks;
using Avalonia.Controls.Notifications;
using Avalonia.Controls.Primitives;
using Avalonia.Input;
using Avalonia.Interactivity;
using ChatGPT.Model;
using Notification = Avalonia.Controls.Notifications.Notification;

namespace ChatGPT.Pages;

public partial class SendChat : UserControl
{
    private readonly HttpClient http;

    private WindowNotificationManager? _manager;

    public SendChat()
    {
        http = MainApp.GetService<IHttpClientFactory>().CreateClient("chatGpt");
        InitializeComponent();
        DataContextChanged += async (sender, args) =>
        {
            if (DataContext is not SendChatViewModel model) return;
            if (model.ChatShow != null)
            {
                var freeSql = MainApp.GetService<IFreeSql>();
                try
                {
                    var values = await freeSql.Select<ChatMessage>()
                        .Where(x => x.ChatShowKey == model.ChatShow.Key)
                        .OrderBy(x => x.CreatedTime)
                        .ToListAsync();

                    foreach (var value in values)
                    {
                        model.messages.Add(value);
                    }
                }
                catch (Exception e)
                {
                    Console.WriteLine(e);
                    throw;
                }
            }
            else
            {
                model.ChatShowAction += async () =>
                {
                    var freeSql = MainApp.GetService<IFreeSql>();

                    var values = await freeSql.Select<ChatMessage>()
                        .Where(x => x.Key == model.ChatShow.Key)
                        .OrderBy(x => x.CreatedTime)
                        .ToListAsync();

                    foreach (var value in values)
                    {
                        model.messages.Add(value);
                    }
                };
            }
        };
    }

    private void InitializeComponent()
    {
        AvaloniaXamlLoader.Load(this);
    }

    protected override void OnAttachedToVisualTree(VisualTreeAttachmentEventArgs e)
    {
        base.OnAttachedToVisualTree(e);
        var topLevel = TopLevel.GetTopLevel(this);
        _manager = new WindowNotificationManager(topLevel) { MaxItems = 3 };
    }

    private void Close_OnClick(object? sender, RoutedEventArgs e)
    {
        var window = TopLevel.GetTopLevel(this) as Window;
        window.ShowInTaskbar = false;
        window.WindowState = WindowState.Minimized;
    }

    private SendChatViewModel ViewModel => DataContext as SendChatViewModel;

    private void Thumb_OnDragDelta(object? sender, VectorEventArgs e)
    {
        var thumb = (Thumb)sender;
        var wrapPanel = (WrapPanel)thumb.Parent;
        wrapPanel.Width += e.Vector.X;
        wrapPanel.Height += e.Vector.Y;
    }

    private void SendBorder_OnPointerEntered(object? sender, PointerEventArgs e)
    {
    }

    private async void SendMessage_OnClick(object? sender, RoutedEventArgs e)
    {
        await SendMessageAsync();
    }

    private void Minimize_OnClick(object? sender, RoutedEventArgs e)
    {
        var window = TopLevel.GetTopLevel(this) as Window;
        window.WindowState = WindowState.Minimized;
    }

    private void Maximize_OnClick(object? sender, RoutedEventArgs e)
    {
        var window = TopLevel.GetTopLevel(this) as Window;
        window.WindowState = window.WindowState switch
        {
            WindowState.Maximized => WindowState.Normal,
            WindowState.Normal => WindowState.Maximized,
            _ => window.WindowState
        };
    }

    private async void SendTextBox_OnKeyDown(object? sender, KeyEventArgs e)
    {
        if (e.Key == Key.Enter)
        {
            await SendMessageAsync();
        }
    }

    private async Task SendMessageAsync()
    {
        try
        {
            if (ViewModel?.ChatShow?.Key == null)
            {
                _manager?.Show(new Notification("提示", "请先选择一个对话框!", NotificationType.Warning));
                return;
            }

            // 获取当前程序集 assets图片
            // var uri = new Uri("avares://ChatGPT/Assets/avatar.png");
            // // 通过uri获取Stream
            // var bitmap = new Bitmap(AvaloniaLocator.Current.GetService<IAssetLoader>().Open(uri));

            var model = new ChatMessage
            {
                ChatShowKey = ViewModel.ChatShow.Key,
                // Avatar = bitmap,
                Title = "token",
                Content = ViewModel.Message,
                CreatedTime = DateTime.Now,
                IsChatGPT = false
            };

            // 添加到消息列表
            ViewModel.messages.Add(model);

            // 清空输入框
            ViewModel.Message = string.Empty;

            // 获取消息记录用于AI联系上下文分析 来自Token的代码
            var message = ViewModel.messages
                .OrderByDescending(x => x.CreatedTime) // 拿到最近的5条消息
                .Take(5)
                .OrderBy(x => x.CreatedTime) // 按时间排序
                .Select(x => x.IsChatGPT
                    ? new
                    {
                        role = "assistant",
                        content = x.Content
                    }
                    : new
                    {
                        role = "user",
                        content = x.Content
                    }
                )
                .ToList();

            // 请求ChatGpt 3.5最新模型 来自Token的代码
            var responseMessage = await http.PostAsJsonAsync("https://api.openai.com/v1/chat/completions", new
            {
                model = "gpt-3.5-turbo",
                temperature = 0,
                max_tokens = 2560,
                user = "token",
                messages = message
            });

            // 获取返回的消息 来自Token的代码
            var response = await responseMessage.Content.ReadFromJsonAsync<GetChatGPTDto>();

            // 获取当前程序集 assets图片
            // uri = new Uri("avares://ChatGPT/Assets/chatgpt.ico");

            var chatGptMessage = new ChatMessage
            {
                ChatShowKey = ViewModel.ChatShow.Key,
                // Avatar = new Bitmap(AvaloniaLocator.Current.GetService<IAssetLoader>().Open(uri)),
                Title = "ChatGPT",
                Content = response.choices[0].message.content,
                IsChatGPT = true,
                CreatedTime = DateTime.Now
            };
            // 添加到消息列表 来自Token的代码
            ViewModel.messages.Add(chatGptMessage);

            var freeSql = MainApp.GetService<IFreeSql>();
            await freeSql
                .Insert(model)
                .ExecuteAffrowsAsync();

            await freeSql
                .Insert(chatGptMessage)
                .ExecuteAffrowsAsync();
        }
        catch (Exception e)
        {
            // 异常处理 
            _manager?.Show(new Notification("提示", "在请求AI服务时出现错误!请联系管理员!", NotificationType.Error));
        }
    }
}
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.
  • 17.
  • 18.
  • 19.
  • 20.
  • 21.
  • 22.
  • 23.
  • 24.
  • 25.
  • 26.
  • 27.
  • 28.
  • 29.
  • 30.
  • 31.
  • 32.
  • 33.
  • 34.
  • 35.
  • 36.
  • 37.
  • 38.
  • 39.
  • 40.
  • 41.
  • 42.
  • 43.
  • 44.
  • 45.
  • 46.
  • 47.
  • 48.
  • 49.
  • 50.
  • 51.
  • 52.
  • 53.
  • 54.
  • 55.
  • 56.
  • 57.
  • 58.
  • 59.
  • 60.
  • 61.
  • 62.
  • 63.
  • 64.
  • 65.
  • 66.
  • 67.
  • 68.
  • 69.
  • 70.
  • 71.
  • 72.
  • 73.
  • 74.
  • 75.
  • 76.
  • 77.
  • 78.
  • 79.
  • 80.
  • 81.
  • 82.
  • 83.
  • 84.
  • 85.
  • 86.
  • 87.
  • 88.
  • 89.
  • 90.
  • 91.
  • 92.
  • 93.
  • 94.
  • 95.
  • 96.
  • 97.
  • 98.
  • 99.
  • 100.
  • 101.
  • 102.
  • 103.
  • 104.
  • 105.
  • 106.
  • 107.
  • 108.
  • 109.
  • 110.
  • 111.
  • 112.
  • 113.
  • 114.
  • 115.
  • 116.
  • 117.
  • 118.
  • 119.
  • 120.
  • 121.
  • 122.
  • 123.
  • 124.
  • 125.
  • 126.
  • 127.
  • 128.
  • 129.
  • 130.
  • 131.
  • 132.
  • 133.
  • 134.
  • 135.
  • 136.
  • 137.
  • 138.
  • 139.
  • 140.
  • 141.
  • 142.
  • 143.
  • 144.
  • 145.
  • 146.
  • 147.
  • 148.
  • 149.
  • 150.
  • 151.
  • 152.
  • 153.
  • 154.
  • 155.
  • 156.
  • 157.
  • 158.
  • 159.
  • 160.
  • 161.
  • 162.
  • 163.
  • 164.
  • 165.
  • 166.
  • 167.
  • 168.
  • 169.
  • 170.
  • 171.
  • 172.
  • 173.
  • 174.
  • 175.
  • 176.
  • 177.
  • 178.
  • 179.
  • 180.
  • 181.
  • 182.
  • 183.
  • 184.
  • 185.
  • 186.
  • 187.
  • 188.
  • 189.
  • 190.
  • 191.
  • 192.
  • 193.
  • 194.
  • 195.
  • 196.
  • 197.
  • 198.
  • 199.
  • 200.
  • 201.
  • 202.
  • 203.
  • 204.
  • 205.
  • 206.
  • 207.
  • 208.
  • 209.
  • 210.
  • 211.
  • 212.
  • 213.
  • 214.
  • 215.
  • 216.
  • 217.
  • 218.
  • 219.
  • 220.
  • 221.
  • 222.
  • 223.
  • 224.

实现发送前需要将之前的最近五条数据得到跟随当前数据一块发送,为了让其ChatGpt可以联系上下文,这样回复的内容更准确,聊天的数据使用Sqlite本地存储,为了轻量使用ORM采用FreeSql,界面仿制微信的百分之八十的还原度,基本上一直,而且源码完全开源。

来自token的分享

GitHub开源地址: https://github.com/239573049/ChatGpt.Desktop

责任编辑:武晓燕 来源: token的技术分享
相关推荐

2016-11-29 13:03:46

微信客户端跨平台组件

2009-12-25 15:12:01

WPF平台

2009-04-22 18:42:13

Vmware虚拟化英特尔

2011-08-17 10:10:59

2021-09-22 15:46:29

虚拟桌面瘦客户端胖客户端

2011-07-07 13:21:56

UI设计

2010-08-01 16:20:29

Android

2010-05-31 10:11:32

瘦客户端

2011-10-26 13:17:05

2010-12-21 11:03:15

获取客户端证书

2011-03-24 13:00:31

配置nagios客户端

2011-03-02 14:36:24

Filezilla客户端

2009-06-08 15:18:34

EJB远程客户端JVM

2011-04-06 14:24:20

Nagios监控Linux

2009-03-04 10:27:50

客户端组件桌面虚拟化Xendesktop

2011-03-21 14:53:36

Nagios监控Linux

2013-05-09 09:33:59

2011-05-12 11:26:00

客户端虚拟化平台

2012-10-11 17:02:02

IBMdw

2012-11-28 11:05:42

IBMdW
点赞
收藏

51CTO技术栈公众号