AIGC: 关于ChatGPT中实现一个聊天机器人

规划一个聊天机器人

  • 智能化完全于依托于GPT, 而产品化是我们需要考虑的事情
  • 比如,如何去构建一个聊天机器人
  • 聊天机器人它的处理逻辑其实非常的清晰
    • 我们输入问题调用 GPT
    • 然后,GPT 给我们生成回答就可以了
  • 需要注意的是,聊天机器人不同于调用API进行一个简单的测试
  • 我们和聊天机器人的对话,可能是多轮的一个对话
  • 在这时候,我们去调用API的时候,就需要将我们多轮的问答都传递给GPT才行

新增一些实现类,结构如下

  • 以下Java版代码来源于网络,可基于此逻辑,改造成其他编程语言

  • src

    • main
      • java
        • com.xxx.gpt.client
          • util
            • ChatContextHolder.java
          • ChatBotClient.java
    • test
      • java
        • com.xxx.gpt.client.test
          • FunctionCallTest.java

ChatContextHolder.java

package com.xxx.gpt.client.util;

import com.xxx.gpt.client.entity.Message;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

public class ChatContextHolder {
    private static Map<String, List<Message>> context = new HashMap<>();

    public static List<Message> get(String id) {
        // TODO  限制轮数,或者限制token数量
        List<Message> messages = context.get(id);
        if (messages == null) {
            messages = new ArrayList<>();
            context.put(id, messages);
        }
        return messages;
    }

    public static void add(String id, String msg) {
        Message message = Message.builder().content(msg).build();
        add(id, message);
    }

    public static void add(String id, Message message) {
        List<Message> messages = context.get(id);
        if (messages == null) {
            messages = new ArrayList<>();
            context.put(id, messages);
        }
        messages.add(message);
    }

    public static void remove(String id) {
        context.remove(id);
    }
}
  • 这里需要来添加一个类,就是我们GPT的上下文的类
  • 我们创建一个类,用于保存我们和GPT聊天的相关的 message
    • 实例化一个Map的对象, 里面的 key 是我们chat的一个id, 一个会话的id
    • 然后,对应的这个key就会有它的一个消息的列表,也就是一个message的list
    • 添加相关的方法
      • 比如说像get方法,根据我们的会话id,获取到所有的message
      • add方法,去对指定的会话id去添加message
      • remove方法, 去删除message
    • 这是我们的上下文处理的类

ChatBotClient.java

package com.xxx.gpt.client;

import com.xxx.gpt.client.entity.Message;
import com.xxx.gpt.client.listener.ConsoleStreamListener;
import com.xxx.gpt.client.util.ChatContextHolder;
import com.xxx.gpt.client.util.Proxys;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.Proxy;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.UUID;
import java.util.concurrent.CountDownLatch;
import java.util.stream.Collectors;

@Slf4j

public class ChatBotClient {
    public static Proxy proxy = Proxy.NO_PROXY;
    public static void main(String[] args) {

        System.out.println("ChatGPT - Java command-line interface");
        System.out.println("Press enter twice to submit your question.");
        System.out.println();
        System.out.println("按两次回车以提交您的问题!!!");
        String chatUuid = UUID.randomUUID().toString();
        String key = "sk-adfas";
        proxy = Proxys.http("127.0.0.1", 7890);
        while (true) {
            String prompt = getInput("\nYou:\n");
            ChatGPTStreamClient chatGPT = ChatGPTStreamClient.builder()
                    .apiKey(key)
                    .proxy(proxy)
                    .build()
                    .init();
            System.out.println("AI: ");
            // 卡住
            CountDownLatch countDownLatch = new CountDownLatch(1);
            Message message = Message.of(prompt);
            ChatContextHolder.add(chatUuid, message);
            ConsoleStreamListener listener = new ConsoleStreamListener() {
                @Override
                public void onError(Throwable throwable, String response) {
                    throwable.printStackTrace();
                    countDownLatch.countDown();
                }
            };
            listener.setOnComplate(msg -> {
                ChatContextHolder.add(chatUuid, Message.ofAssistant(msg));
                countDownLatch.countDown();
            });
            chatGPT.streamChatCompletion(ChatContextHolder.get(chatUuid), listener);
            try {
                countDownLatch.await();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }

        }
    }

    @SneakyThrows
    public static String getInput(String prompt) {
        System.out.print(prompt);
        BufferedReader reader = new BufferedReader(new InputStreamReader(System.in));
        List<String> lines = new ArrayList<>();
        String line;
        try {
            while ((line = reader.readLine()) != null && !line.isEmpty()) {
                lines.add(line);
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
        return lines.stream().collect(Collectors.joining("\n"));
    }
}
  • 它的实现其实也比较简单
    • 第一步,需要等待用户输入,用户输入完成之后,调用GPT
      • 添加一下相关的我们的 API KEY 和 proxy
      • getInput 去接收用户输入
    • 第二步,需要保存多轮会话
      • 我们是多轮会话,我们这里写一个循环在前面
      • chatUuid 是我们用于标识会话的id
    • 第三步,为了效果更好,更加顺畅,采用流式的方式
      • 创建一个 StreamClient 去调用GPT的 API
      • 调用完成进行输出

测试

  • 完成之后,可以测试一下
    • 程序等待我们的输出,我们去询问一下: “你是谁?”
    • 这里需要敲两次回车进行确认
  • 调用之后,我们获取到了 GPT 它的返回的结果
    • 然后,我们问: “请介绍一下ChatGPT”
    • GPT生成了相关的答案
  • 在这次问答当中,也能看到流式Client的一个效果
  • 整体上和我们通过界面去访问GPT是没有什么区别的
  • 假如说,我们现在再问: “这是我的第几个问题?”
    • 理论上讲,这是我们本轮会话的第三个问题
    • 由于我们没有在刚刚的调用里面, 去关联我们会话上下文的信息
    • 这样,GPT会回答: “这是第3个问题”
  • 将会话的上下文信息传递给 GPT, 就可以去结合这些上下文的信息,给予我们比较精确的一个答案
    • 这是我们在构造一个聊天机器人的时候和前面测试所不一样的,需要我们注意的地方
    • 但是在这里,其实就会有一个问题就是token的问题。
    • GPT它的模型对于 token 是有限制的
  • 如果我们一轮轮会话的叠加,最终我们的token, 一定会超过模型它本身的token
    • 所以在上下文的管理类里面,我们这里是需要去进行处理
  • 上述问题如何处理?
    • 方案一就是保留最近一轮的会话轮数,比如只保留最近五轮
      • 对于历史的消息,不再保存,不再发送给GPT这样,可以达到小于指定token数量的目的
      • 但是当我们一轮的消息比较长的话,也有可能会超过token的阈值
    • 方案二,就是在方案一的基础之上,我们不再以单纯会话的轮数去做一个迭代
      • 这里,根据计算后的token的数量,去进行判断
      • 如果小于模型的 max_token,我们就保留相关的这些会话
      • 如果大于,我们就要去做相关消息的一个删减
    • 目前并未实现,可在上述 ChatContextHolder.java 类中进行实现

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mfbz.cn/a/218182.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

编写Java应用程序,输出满足1+2+3+……+n<8888的最大正整数n。

源代码&#xff1a; public class Main { public static void main(String[] args) { int i 1; int sum 0; for(i 1;;i){ sum i; if (sum >8888) break; } System.out.println(i-1); } } 实验运行截图&#xff1a;

隐写术和人工智能

在一项新的研究中&#xff0c;人工智能对齐研究实验室 Redwood Research 揭示了大型语言模型 (LLM) 可以掌握“编码推理”&#xff0c;这是一种隐写术形式。 这种有趣的现象使得大型语言模型能够以人类读者无法理解的方式巧妙地将中间推理步骤嵌入到生成的文本中。 大型语言…

【滤波第二期】中值滤波的原理和C代码

中值滤波是一种非线性数字滤波技术&#xff0c;主要应用于信号处理和图像处理领域&#xff0c;用于减小信号中的噪声和离群值。中值滤波的核心思想是通过计算一组数据点的中间值&#xff0c;以抑制脉冲噪声等离群值的影响&#xff0c;从而实现信号的平滑处理。 1&#xff0c;中…

SaToken利用Redis做持久化

官网解释 官网解释 教程 引入依赖 <!-- 提供Redis连接池 --> <dependency><groupId>org.apache.commons</groupId><artifactId>commons-pool2</artifactId> </dependency><!-- Sa-Token 整合 Redis &#xff08;使用 jdk 默认序…

QT使用SQLite(打开db数据库以及对数据库进行增删改查)

QTSQLite 在QT中使用sqlite数据库&#xff0c;有多种使用方法&#xff0c;在这里我只提供几种简单&#xff0c;代码简短的方法&#xff0c;包括一些特殊字符处理。 用SQlite建立一个简单学生管理数据库 数据库中有两个表一个是class和student。 class表结构 student表结果…

GitHub Copilot试用指南

GitHub Copilot试用指南 首先读这个文档&#xff0c;按照步骤开启30天的试用&#xff1a;管理个人帐户的 GitHub Copilot 订阅 然后读这个文档&#xff1a;使用 IDE 中的 GitHub Copilot 聊天 &#xff0c;在你习惯使用的IDE中配置copilot&#xff0c;暂时好像只支持jetbrai…

Android Edittext进阶版(Textfieids)

一、Text fieids 允许用户在 UI 中输入文本&#xff0c;TextInputLayout TextInputEditText。 在 Text fieids 没出来(我不知道)前&#xff0c;想实现这个功能就需要自己自定义控件来实现这个功能。 几年前做个上面这种样式(filled 填充型)。需要多个控件组合 动画才能实现&a…

YoloV5改进策略:Swift Parameter-free Attention,无参注意力机制,超分模型的完美迁移

摘要 https://arxiv.org/pdf/2311.12770.pdf https://github.com/hongyuanyu/SPAN SPAN是一种超分网络模型。SPAN模型通过使用参数自由的注意力机制来提高SISR的性能。这种注意力机制能够增强重要信息并减少冗余,从而在图像超分辨率过程中提高图像质量。 具体来说,SPAN模…

qt 5.15.2 主窗体事件及绘制功能

qt 5.15.2 主窗体事件及绘制功能 显示主窗体效果图如下所示&#xff1a; main.cpp #include "mainwindow.h"#include <QApplication>int main(int argc, char *argv[]) {QApplication a(argc, argv);MainWindow w;w.setFixedWidth(600);w.setFixedHeight(6…

java学习part34collect和map

153-集合框架-数组的特点、弊端与集合框架体系介绍_哔哩哔哩_bilibili 1.以前的数组 2.常用 3.Collection add只能加object&#xff0c;如果有基本类型会装箱 3.2集合和数组转换 3.3往集合添加对象的注意事项 4.迭代器 容易越界 一般不用 常用好用 5.for each 类似c的for( …

鸿蒙4.0开发笔记之ArkTS装饰器语法基础之监听者模式@Watch案例讲解(十四)

1、Watch定义 Watch实际是指状态变量更改通知。如果开发者需要关注某个状态变量的值是否改变&#xff0c;可以使用Watch为状态变量设置回调函数&#xff08;监听函数&#xff09;。 Watch用于监听状态变量的变化&#xff0c;当状态变量变化时&#xff0c;Watch的回调方法将被…

Hadoop实验putty文件

&#x1f525;博客主页&#xff1a; A_SHOWY&#x1f3a5;系列专栏&#xff1a;力扣刷题总结录 数据结构 云计算 数字图像处理 很多朋友反馈做hadoop实验中的putty找不到Connection-SSH-Auth路径下找不到Private key for authentication私有密钥&#xff0c;无法将转…

非标设计之气缸类型

空压机&#xff1a; 空压机又称空气压缩机&#xff0c;简单来说就是将机械能转化为压力能来进行工作的&#xff0c;空压机在电力行业应用比较多&#xff0c;除了在电力行业应用较多外&#xff0c;其实空压机还有一个比较常见的用途就是用来制冷和分离气体&#xff0c;输送气体…

EM32DX-C4【C#】

1外观&#xff1a; J301 直流 24V 电源输入 CAN0 CAN0 总线接口 CAN1 CAN1 总线接口 J201 IO 接线段子 S301-1、S301-2 输出口初始电平拨码设置 S301-3~S301-6 模块 CAN ID 站号拨码开关 S301-7 模块波特率拨码设置 S301-8 终端电阻选择开关 2DI&#xff1a; 公共端是…

Gson 自动生成适配器插件

在json解析方面 我们常见有下面几方面困扰 1. moshi code-gen能自动生成适配器,序列化效率比gson快,但是自定义程度不如gson,能java kotlin共存 且解决了默认值的问题 2.gson api 强大自由,但是 第一次gson的反射缓存比较慢,而且生成对象都是反射,除非主动注册com.google.gson…

SQL Sever 基础知识 - 数据筛选(1)

SQL Sever 基础知识 - 四、数据筛选 四、筛选数据第1节 DISTINCT - 去除重复值1.1 SELECT DISTINCT 子句简介1.2 SELECT DISTINCT 示例1.2.1 DISTINCT 一列示例1.2.2 DISTINCT 多列示例 1.2.3 DISTINCT 具有 null 值示例1.2.4 DISTINCT 与 GROUP BY 对比 第2节 WHERE - 过滤查询…

翻译: 生成式人工智能的工作原理How Generative AI works

ChatGPT 和 Bard 等系统生成文本的能力几乎像魔法一样。它们确实代表了 AI 技术的一大步进。但是文本生成到底是如何工作的呢&#xff1f;在这个视频中&#xff0c;我们将看看生成式 AI 技术的底层原理&#xff0c;这将帮助你理解你可以如何使用它&#xff0c;以及何时可能不想…

免费的SEO外链发布工具,提升排名的利器

互联网已经成为信息传播和商业发展的重要平台。而对于拥有网站的个人、企业来说&#xff0c;如何让自己的网站在搜索引擎中脱颖而出&#xff1f;SEO&#xff08;Search Engine Optimization&#xff09;作为提高网站在搜索引擎中排名的关键手段. 什么是SEO外链&#xff1f; S…

【数据结构】最短路径——Floyd算法

一.问题描述 给定带权有向图G&#xff08;V&#xff0c;E&#xff09;&#xff0c;对任意顶点 V &#xff08;ij)&#xff0c;求顶点到顶点的最短路径。 转化为&#xff1a; 多源点最短路径求解问题 解决方案一&#xff1a; 每次以一个顶点为源点调用Dijksra算法。时间复杂…

根据已有安装的cuda配置合适的pytorch环境

目前网络上根据电脑配置安装合适的深度学习环境的帖子已经很多了&#xff0c;但是现实中会出现很久之前已经安装了对应的cuda&#xff0c;但是现在忘记了当时安装的是什么版本。本文针对这一问题展开攻略。 1 cuda安装版本查询 我们在查询自己应该安装什么版本的cuda时&#…