使用Java拓展本地开源大模型的网络搜索问答能力

news2024/11/19 9:37:07

在这里插入图片描述

背景

开源大模型通常不具备最新语料的问答能力。因此需要外部插件的拓展,目前主流的langChain框架已经集成了网络搜索的能力。但是作为一个倔强的Java程序员,还是想要用Java去实现。

注册SerpAPI

Serpapi 提供了多种搜索引擎的搜索API接口。
访问 Serpapi 官网上注册一个用户:

https://serpapi.com/

在这里插入图片描述

可以选择Free Plan,提供每月100次的免费使用。接下来就是使用自己的邮箱和手机号进行注册。
在这里插入图片描述
注册成功登录:
在这里插入图片描述

创建SerpApiHttp对象

public class SerpApiHttp {

    private int httpConnectionTimeout;
    private int httpReadTimeout;

    /**
     * 后端服务地址
     */
    private static final String BACK_END = "https://serpapi.com";

    /**
     * 初始化Gson对象
     */
    private static Gson gson = new Gson();

    /**
     * 当前后端HTTP路径
     */
    public String path;

    /***
     * 构造函数
     * @param path HTTP url路径
     */
    public SerpApiHttp(String path) {
        this.path = path;
    }

    /***
     * 建立Socket连接
     *
     * @param path URL端点
     * @param parameter 客户端参数,如: { "q": "coffee", "location": "Austin, TX"}
     * @return HttpURLConnection 连接对象
     * @throws SerpApiException 包装错误信息
     */
    protected HttpURLConnection connect(String path, Map<String, String> parameter) throws SerpApiException {
        HttpURLConnection con;
        try {
            //allowHTTPS(); // 允许HTTPS支持
            String query = ParameterStringBuilder.getParamsString(parameter);
            URL url = new URL(BACK_END + path + "?" + query);
            con = (HttpURLConnection) url.openConnection();
            con.setRequestMethod("GET");
        } catch (IOException e) {
            throw new SerpApiException(e);
        } catch (Exception e) {
            e.printStackTrace();
            throw new SerpApiException(e);
        }

        String outputFormat = parameter.get("output");
        if (outputFormat == null) {
            throw new SerpApiException("output format must be defined: " + path);
        } else if (outputFormat.startsWith("json")) {
            con.setRequestProperty("Content-Type", "application/json");
        }
        con.setConnectTimeout(getHttpConnectionTimeout());
        con.setReadTimeout(getHttpReadTimeout());

        con.setDoOutput(true);
        return con;
    }

    /***
     * 返回HTTP响应内容的原始字符串
     *
     * @param parameter 用户客户端参数
     * @return HTTP响应体
     * @throws SerpApiException 包装错误信息
     */
    public String get(Map<String, String> parameter) throws SerpApiException {
        HttpURLConnection con = connect(this.path, parameter);

        // 获取HTTP状态码
        int statusCode = -1;
        // 保存响应流
        InputStream is = null;
        // 读取缓冲区
        BufferedReader in = null;
        try {
            statusCode = con.getResponseCode();

            if (statusCode == 200) {
                is = con.getInputStream();
            } else {
                is = con.getErrorStream();
            }

            Reader reader = new InputStreamReader(is);
            in = new BufferedReader(reader);
        } catch (IOException e) {
            throw new SerpApiException(e);
        }

        String inputLine;
        StringBuilder content = new StringBuilder();
        try {
            while ((inputLine = in.readLine()) != null) {
                content.append(inputLine);
            }
            in.close();
        } catch (IOException e) {
            throw new SerpApiException(e);
        }

        // 断开连接
        con.disconnect();

        if (statusCode != 200) {
            triggerSerpApiException(content.toString());
        }
        return content.toString();
    }

    /**
     * 在错误情况下触发异常
     *
     * @param content 从serpapi.com返回的原始JSON响应
     * @throws SerpApiException 包装错误信息
     */
    protected void triggerSerpApiException(String content) throws SerpApiException {
        String errorMessage;
        try {
            JsonObject element = gson.fromJson(content, JsonObject.class);
            errorMessage = element.get("error").getAsString();
        } catch (Exception e) {
            throw new AssertionError("invalid response format: " + content);
        }
        throw new SerpApiException(errorMessage);
    }

    /**
     * @return 当前HTTP连接超时时间
     */
    public int getHttpConnectionTimeout() {
        return httpConnectionTimeout;
    }

    /**
     * @param httpConnectionTimeout 设置HTTP连接超时时间
     */
    public void setHttpConnectionTimeout(int httpConnectionTimeout) {
        this.httpConnectionTimeout = httpConnectionTimeout;
    }

    /**
     * @return 当前HTTP读取超时时间
     */
    public int getHttpReadTimeout() {
        return httpReadTimeout;
    }

    /**
     * @param httpReadTimeout 设置HTTP读取超时时间
     */
    public void setHttpReadTimeout(int httpReadTimeout) {
        this.httpReadTimeout = httpReadTimeout;
    }
}

创建SerpApi对象

public class SerpApi extends Exception {
    /**
     * 客户端参数
     */
    private final Map<String, String> parameter;

    /**
     * 初始化 gson
     */
    private static final Gson gson = new Gson();

    /**
     * Java 7+ 的 https 客户端实现
     */
    private final SerpApiHttp client;

    /**
     * 默认 HTTP 客户端超时时间
     */
    private static final Integer TIME_OUT = 60000;

    /**
     * 搜索路径
     */
    private static final String SEARCH_PATH = "/search";

    /***
     * 构造函数
     *
     * @param parameter 默认搜索参数,应包括 {"api_key": "secret_api_key", "engine": "google" }
     */
    public SerpApi(Map<String, String> parameter) {
        this.parameter = parameter;
        this.client = new SerpApiHttp(SEARCH_PATH);
        this.client.setHttpConnectionTimeout(TIME_OUT);
    }

    /***
     * 返回原始HTML搜索结果
     *
     * @param parameter HTML搜索参数
     * @return 从客户端引擎获取的原始HTML响应,用于自定义解析
     * @throws SerpApiException 封装后端错误消息
     */
    public String html(Map<String, String> parameter) throws SerpApiException {
        return get("/client", "html", parameter);
    }

    /***
     * 返回JSON格式的搜索结果
     *
     * @param parameter 自定义搜索参数,可覆盖构造函数中提供的默认参数
     * @return JSON对象,包含搜索结果的顶层节点
     * @throws SerpApiException 封装后端错误消息
     */
    public JsonObject search(Map<String, String> parameter) throws SerpApiException {
        return json(SEARCH_PATH, parameter);
    }

    /***
     * 使用Location API返回位置信息
     *
     * @param parameter 必须包括 {q: "city", limit: 3}
     * @return JSON数组,使用Location API返回的位置信息
     * @throws SerpApiException 封装后端错误消息
     */
    public JsonArray location(Map<String, String> parameter) throws SerpApiException {
        String content = get("/locations.json", "json", parameter);
        JsonElement element = gson.fromJson(content, JsonElement.class);
        return element.getAsJsonArray();
    }

    /***
     * 通过Search Archive API检索搜索结果
     *
     * @param id 搜索的唯一标识符
     * @return 客户端结果的JSON对象
     * @throws SerpApiException 封装后端错误消息
     */
    public JsonObject searchArchive(String id) throws SerpApiException {
        return json("/searches/" + id + ".json", null);
    }

    /***
     * 使用Account API获取账户信息
     *
     * @param parameter 包含api_key的Map,如果未在默认客户端参数中设置
     * @return JSON对象,账户信息
     * @throws SerpApiException 封装后端错误消息
     */
    public JsonObject account(Map<String, String> parameter) throws SerpApiException {
        return json("/account.json", parameter);
    }

    /***
     * 使用Account API获取账户信息
     *
     * @return JSON对象,账户信息
     * @throws SerpApiException 封装后端错误消息
     */
    public JsonObject account() throws SerpApiException {
        return json("/account.json", null);
    }

    /***
     * 将HTTP内容转换为JsonValue
     *
     * @param endpoint 原始JSON HTTP响应
     * @return 通过gson解析器创建的JSON对象
     */
    private JsonObject json(String endpoint, Map<String, String> parameter) throws SerpApiException {
        String content = get(endpoint, "json", parameter);
        JsonElement element = gson.fromJson(content, JsonElement.class);
        return element.getAsJsonObject();
    }

    /***
     * 获取HTTP客户端
     *
     * @return 客户端实例
     */
    public SerpApiHttp getClient() {
        return this.client;
    }

    /***
     * 扩展现有参数构建Serp API查询
     *
     * @param path 后端HTTP路径
     * @param output 输出类型(json, html, json_with_images)
     * @param parameter 自定义搜索参数,可覆盖默认参数
     * @return 格式化参数HashMap
     * @throws SerpApiException 封装后端错误消息
     */
    public String get(String path, String output, Map<String, String> parameter) throws SerpApiException {
        // 更新客户端路径
        this.client.path = path;

        // 创建HTTP查询
        Map<String, String> query = new HashMap(16);

        if (path.startsWith("/searches")) {
            // 仅保留API_KEY
            query.put("api_key", this.parameter.get("api_key"));
        } else {
            // 合并默认参数
            query.putAll(this.parameter);
        }

        // 用自定义参数覆盖默认参数
        if (parameter != null) {
            query.putAll(parameter);
        }

        // 设置当前编程语言
        query.put("source", "java");

        // 设置输出格式
        query.put("output", output);

        return this.client.get(query);
    }
}

构建WebSearchChain

public class WebSearchChain {

    /**
     * apiKey
     */
    private String apiKey;

    /**
     * 构造函数
     * @param apiKey
     */
    public WebSearchChain(String apiKey){
        this.apiKey = apiKey;
    }

    /**
     * 初始化
     * @param apiKey
     * @return
     */
    public static WebSearchChain fromLlm(String apiKey){
        return new WebSearchChain(apiKey);
    }

    /**
     * 搜索
     * @param question
     * @return
     */
    public String search(String question){
        Map<String, String> parameter = new HashMap<>();
        parameter.put("api_key", apiKey);
        parameter.put("q", question);
        parameter.put("hl", "zh-cn");
        parameter.put("gl", "cn");
        parameter.put("google_domain", "google.com");
        parameter.put("safe", "active");
        parameter.put("start", "10");
        parameter.put("num", "10");
        parameter.put("device", "desktop");

        SerpApi serpapi = new SerpApi(parameter);
        JsonObject results = null;
        StringBuilder stringBuilder = new StringBuilder();
        try {
            results = serpapi.search(parameter);
            results.getAsJsonArray("organic_results").forEach(organicResult->{
                JsonObject result = organicResult.getAsJsonObject();
                String title = result.getAsJsonPrimitive("title").getAsString();
                String snippet = result.getAsJsonPrimitive("snippet").getAsString();
                stringBuilder.append(title).append("。").append(snippet).append("。");
            });
        } catch (SerpApiException e) {
            e.printStackTrace();
        }
        return stringBuilder.toString();
    }
}

使用

博主之前借鉴langChain的思路封装一个Java版的框架,可参考:https://blog.csdn.net/weixin_44455388/article/details/137098743?spm=1001.2014.3001.5501

因此,直接调用即可:

public static void test7() {
    String prompt = "吴亦凡犯了什么事";
    OpenAIChat openAIChat = OpenAIChat.builder().endpointUrl("http://192.168.0.84:9997/v1").model("Qwen1.5-14B-Chat").build().init();
    WebSearchChain webSearchChain = WebSearchChain.fromLlm("48d1bd8f7419xxxxxxxxxxxxxxxxxxxxxxxxxxxx");
    String searchResult = webSearchChain.search(prompt);
    Flux<String> stringFlux = openAIChat.streamChatWithChain("112233", "你是一个AI助手", searchResult, prompt);
    stringFlux.subscribe();
}

在这里插入图片描述

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

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

相关文章

华为服务器RAID配置教程 服务器硬盘故障处理帮助 浪潮RAID配置教程 磁盘阵列配置通用教程

前言&#xff08;本文档持续更新&#xff09; 本文主要记录服务器配置RAID&#xff08;磁盘阵列&#xff09;过程中存在的细节问题及官方文档无法解决的问题的解决方案 配置环境 华为 RH2288 v3服务器配置RAID组 如何快速配置 1.找到服务器品牌的阵列卡型号&#xff0c;找不到…

使用vuepress搭建个人的博客(一):基础构建

前言 vuepress是一个构建静态资源网站的库 地址:VuePress 一般来说,这个框架非常适合构建个人技术博客,你只需要把自己写好的markdown文档准备好,完成对应的配置就可以了 搭建 初始化和引入 创建文件夹press-blog npm初始化 npm init 引入包 npm install -D vuepress…

【大数据存储】实验六:Hive

Hive的安装和基本操作实验 一、实验目的 了解Hive的安装和基本操作 二、实验原理 Hive定义了一套自己的SQL&#xff0c;简称HQL&#xff0c;它与关系型数据库的SQL略有不同&#xff0c;但支持了绝大多数的语句如DDL、DML以及常见的聚合函数、连接查询、条件查询。 DDL操作…

跨境金融区块链服务平台

跨境金融服务是因企业及个人跨境经营、交易、投资、往来等活动而产生的资金使用、调拨、配置等需求&#xff0c;而提供的金融服务。近年来&#xff0c;随着我国经济的快速稳步增长和全球化经济一体化的不断深入发展&#xff0c;跨境金融业务增长迅速&#xff0c;监管也开始转化…

某虚假交友APP(信息窃取)逆向分析

应用初探 在群里水群的时候 群u发了一个交友APP 于是拿来分析一下 可以看到应用打开后又一个登录的界面 需要用户输入手机号与验证码进行登录 #在线云沙箱分析 将APK放入某安信云沙箱中分析 提示应用请求了过多的敏感权限 逆向分析 直接拖入Jadx分析 好在程序没有加固 也没…

HomePlug AV

目录 HomePlug AV的基本概念基本术语网络概念网络实例 HomePlug AV物理层&#xff08;PHY&#xff09;HomePlug AV OFDM收发器架构PHY的调制模式FC调制和ROBO调制物理层的特点OFDM频域/时域转换开窗/槽式OFDM信号和噪声PHY发送控制——信道自适应PHY帧格式&#xff08;Symbol&a…

yolov9文献阅读记录

本文记录了yolov9文献的阅读过程&#xff0c;对主要内容进行摘选翻译&#xff0c;帮助理解原理和应用&#xff0c;包括摘要、主要贡献、网络结构、主要模块&#xff0c;问题描述和试验对比等内容。 文献摘要前言摘选主要贡献相关工作可逆性结构辅助监督 问题描述信息瓶颈原理可…

虹科Pico汽车示波器 | 免拆诊断案例 | 2019款别克GL8豪华商务车前照灯水平调节故障

一、故障现象 一辆2019款别克GL8豪华商务车&#xff0c;搭载LTG发动机&#xff0c;累计行驶里程约为10.7万km。车主反映&#xff0c;车辆行驶过程中组合仪表提示前照灯水平调节故障。 二、故障诊断 接车后试车&#xff0c;起动发动机&#xff0c;组合仪表上提示“前照灯水平…

Hadoop和zookeeper集群相关执行脚本(未完,持续更新中~)

1、Hadoop集群查看状态 搭建Hadoop数据集群时&#xff0c;按以下路径操作即可生成脚本 [test_1analysis01 bin]$ pwd /home/test_1/hadoop/bin [test_01analysis01 bin]$ vim jpsall #!/bin/bash for host in analysis01 analysis02 analysis03 do echo $host s…

Linux驱动学习:从Linux主机nfs共享文件到uboot

第一步&#xff1a;在Linux主机上开启NFS服务&#xff0c;使用如下命令安装NFS服务&#xff1a; sudo apt-get install nfs-kernel-server rpcbind 第二步&#xff1a;创建一个文件夹用于共享&#xff0c;直接以nfs命名就行&#xff1a; 第三步&#xff1a;打开nfs服务配置文…

《超预测》预见未来的艺术和科学 - 三余书屋 3ysw.net

超预测&#xff1a;预见未来的艺术和科学 大家好&#xff0c;本次解读的书籍是《超预测》。我将花费大约20分钟的时间为您梳理书中的核心内容&#xff0c;探讨一般人如何超越专家学者成为超级预测家&#xff0c;并探索他们所采用的思维方式和预测方法。在开始之前&#xff0c;…

阻止EDR注入dll

文章目录 前记blockdllsACG结论测试代码reference 前记 许多EDR产品常见的操作是将他们的DLL注入到其想监测的进程中&#xff0c;寻找前辈们的防注入思路发现大概有以下两种&#xff0c;分别是&#xff1a; 1、PROCESS_CREATION_MITIGATION_POLICY_BLOCK_NON_MICROSOFT_BINAR…

掌握机器学习新星:使用Python和Scikit-Learn进行图像识别

正文&#xff1a; 随着智能手机和社交媒体的普及&#xff0c;图像数据的生成速度比以往任何时候都快。为了自动化处理这些数据&#xff0c;我们需要强大的图像识别系统。机器学习提供了一种有效的方法来识别和分类图像中的对象。Scikit-Learn是一个流行的Python库&#xff0c;它…

OpenCASCADE源码分析:总论

OpenCASCADE是20世纪90年代由法国Matra Datavision公司开发的三维曲面/实体造型引擎&#xff0c;目前&#xff0c;国内许多CAE软件将其用作几何模块的开发。 本文拟从系统设计的角度&#xff0c;对OpenCASCADE架构(核心组件、关键流程等)进行概要性总结。 注1&#xff1a;限于…

Xshell Mobaxterm等终端工具连接不上服务器,显示 SSH服务器拒绝密码。请再试一次。解决办法

问题解决办法&#xff1a; &#xff08;1&#xff09;需要查看配置SSH密钥时&#xff0c;输入的password密码和当前users_name cd /home/: 查看当前系统下的用户名 注意上图中的登录名是服务器端linux下自己设置的user_name用户名&#xff1a; 所以需要将fl改为&#xff1a…

UE4_自定义反射和折射和法线图

UE4 自定义反射和折射和法线图 2020-05-22 09:36 将ReflectionVector和反射图像进行ViewAlignedReflection,输出的textrue和相机位置CameraPosition的onePlus进行Dot点乘之后乘以一个float系数反射度&#xff0c;输出给固有色&#xff0c;就有反射效果了。球型反射。 折射&…

【TI毫米波雷达】IWR6843AOP的官方文件资源名称BUG,选择xwr68xx还是xwr64xx,及需要注意的问题

【TI毫米波雷达】IWR6843AOP的官方文件资源名称BUG&#xff0c;选择xwr68xx还是xwr64xx&#xff0c;及需要注意的问题 文章目录 demo工程out_of_box文件调试bin文件名称需要注意的问题附录&#xff1a;结构框架雷达基本原理叙述雷达天线排列位置芯片框架Demo工程功能CCS工程导…

AJAX —— 学习(二)

目录 一、利用 JSON 字符串 返回数据 &#xff08;一&#xff09;基础代码 &#xff08;二&#xff09;原理及实现 二、nodmon 工具 自动重启服务 &#xff08;一&#xff09;用途 &#xff08;二&#xff09;下载 &#xff08;三&#xff09;使用 三、IE 缓存问题 &a…

git中对子模块的本地修改、提交和推送远程仓库

场景 当前的某个项目&#xff0c;其使用了另一个项目&#xff0c;我在本地需要对子项目进行修改&#xff0c;并将这些修改提交到github中的子项目和父项目。其实在github中&#xff0c;子项目都是特定的指向子项目的某次提交&#xff0c;因此对于父项目的修改&#xff0c;其实…

【JavaScript】函数 ⑥ ( 使用 arguments 获取所有实参 | arguments 内置对象 | 伪数组概念 )

文章目录 一、使用 arguments 获取所有实参1、arguments 内置对象2、伪数组概念3、arguments 实参遍历4、arguments 代码示例 - 基本使用5、arguments 代码示例 - 遍历实参 一、使用 arguments 获取所有实参 1、arguments 内置对象 在 定义 JavaScript 函数 时 , 有时 不确定 形…