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

在这里插入图片描述

背景

开源大模型通常不具备最新语料的问答能力。因此需要外部插件的拓展,目前主流的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();
}

在这里插入图片描述

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

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

相关文章

Unity性能优化篇(十四) 其他优化细节以及UPR优化分析器

代码优化&#xff1a; 1. 使用AssetBundle作为资源加载方案。 而且经常一起使用的资源可以打在同一个AssetBundle包中。尽量避免同一个资源被打包进多个AB包中。压缩方式尽量使用LZ4&#xff0c;少用或不要用LZMA的压缩方式。如果确定后续开发不会升级Unity版本&#xff0c;则可…

微服务相关之Nacos

微服务相关之Nacos 一、基础概念1.什么是 Nacos2.Nacos 的关键特性包括3.Nacos 核心组件4.Nacos 业务大图 二、架构设计1.基本架构及概念 三、Nacos注册中心1.注册中心演变及其设计思想2.Nacos注册中心架构3.服务注册表结构 一、基础概念 1.什么是 Nacos Nacos /nɑ:kəʊs/ …

vlookup跨表使用

VLOOKUP&#xff08;查找值&#xff0c;数据表&#xff0c;列序数&#xff0c;匹配条件&#xff09;。打开两表格&#xff0c;输入查找值和表格数据格式一致&#xff0c;查找表格或数据范围是连续的。 跨表VLOOKUP使用步骤&#xff1a; 插函数单元格&#xff0c;输“VLOOKUP()…

AI学习-线性回归推导

线性回归 1.简单线性回归2.多元线性回归3.相关概念熟悉4.损失函数推导5.MSE损失函数 1.简单线性回归 ​ 线性回归&#xff1a;有监督机器学习下一种算法思想。用于预测一个或多个连续型目标变量y与数值型自变量x之间的关系,自变量x可以是连续、离散&#xff0c;但是目标变量y必…

【图】最小生成数—Kruskal算法

目录 1. 什么是最小生成树&#xff1f; 2. Kruskal算法 1. 什么是最小生成树&#xff1f; 最小生成树&#xff08;Minimum Spanning Tree&#xff0c;简称 MST&#xff09;是指在一个连通的无向图中&#xff0c;找到一个包含所有顶点的树&#xff0c;并且边的权值之和最小。…

简单的安全密码生成器PwGen

什么是 PwGen &#xff1f; PwGen 是一个简单的 Docker Web 应用程序&#xff0c;旨在生成具有可自定义选项的安全密码或密码短语。用户可以选择生成具有特定标准的随机密码或由随机单词组成的密码。其他功能包括在密码中包含大写字母、数字和特殊字符的选项&#xff0c;或者将…

边缘计算盒子与云计算:谁更适合您的业务需求?

边缘计算盒子和云计算&#xff0c;这两个概念听起来可能有点复杂&#xff0c;但其实它们就是两种不同的数据处理方式。那谁更适合您的业务需求呢&#xff1f;咱们来详细说说。 边缘计算盒子&#xff0c;就像是个小型的数据处理中心&#xff0c;放在离你业务现场比较近的地方。它…

解决Flutter应用在苹果商店上架中常见的问题与挑战

引言 Flutter是一款由Google推出的跨平台移动应用开发框架&#xff0c;其强大的性能和流畅的用户体验使其备受开发者青睐。然而&#xff0c;开发一款应用只是第一步&#xff0c;将其成功上架到苹果商店才是实现商业目标的关键一步。本文将详细介绍如何使用Flutter将应用程序上…

【Unity每日一记】(Canvas的相机渲染模式) 如何将模型显示在UI之前

&#x1f468;‍&#x1f4bb;个人主页&#xff1a;元宇宙-秩沅 &#x1f468;‍&#x1f4bb; hallo 欢迎 点赞&#x1f44d; 收藏⭐ 留言&#x1f4dd; 加关注✅! &#x1f468;‍&#x1f4bb; 本文由 秩沅 原创 &#x1f468;‍&#x1f4bb; 收录于专栏&#xff1a;uni…

CSS——精灵图

CSS——精灵图 目录 CSS——精灵图什么是精灵图&#xff1f;导入精灵图裁剪精灵图使用精灵图方式1方式2 什么是精灵图&#xff1f; 精灵图&#xff08;Spritesheet&#xff09;是指将多个小图标、图像或动画合并到一个大图像中的技术。在网页设计和游戏开发中&#xff0c;精灵…

AlgorithmStar(AS机器学习与科学计算库) 实现 矩阵数据类型的计算函数汇总

AlgorithmStar 实现 矩阵 计算 AlgorithmStar 本文中将会演示通过 AS 机器学习库 实现 矩阵计算 目录 文章目录 AlgorithmStar 实现 矩阵 计算目录矩阵创建通过数组创建通过稀疏矩阵创建通过填充创建矩阵通过随机的方式创建矩阵 矩阵计算矩阵的基本运算矩阵的加法计算矩阵的减…

【Erlang】Linux(CentOS7)安装Erlang和RabbitMQ

一、系统环境 查版本对应&#xff0c;CentOS-7&#xff0c;选择Erlang 23.3.4&#xff0c;RabbitMQ 3.9.16 二、操作步骤 安装 Erlang repository curl -s https://packagecloud.io/install/repositories/rabbitmq/erlang/script.rpm.sh | sudo bash安装 Erlang package s…

【TI毫米波雷达】官方工业雷达包的生命体征检测环境配置及避坑(Vital_Signs、IWR6843AOPEVM)

【TI毫米波雷达】官方工业雷达包的生命体征检测环境配置及避坑&#xff08;Vital_Signs、IWR6843AOPEVM&#xff09; 文章目录 生命体征基本介绍IWR6843AOPEVM的配置上位机配置文件避坑上位机start测试距离检测心跳检测呼吸频率检测空环境测试 附录&#xff1a;结构框架雷达基…

【Python系列】Python中的YAML数据读取与解析

&#x1f49d;&#x1f49d;&#x1f49d;欢迎来到我的博客&#xff0c;很高兴能够在这里和您见面&#xff01;希望您在这里可以感受到一份轻松愉快的氛围&#xff0c;不仅可以获得有趣的内容和知识&#xff0c;也可以畅所欲言、分享您的想法和见解。 推荐:kwan 的首页,持续学…

物联网实战--入门篇之(十二)完结

目录 一、涉及的知识点 二、物联网现状 三、物联网前景 四、文章方向 这篇文章主要对这个项目做个总结&#xff0c;以及对于物联网今后的学习方向。 一、涉及的知识点 不得不说&#xff0c;物联涉及确实很广&#xff0c;就净化器这个小项目来说&#xff0c; 编程语言&…

【Jenkins】关于账号,证书验证的设置问题

当你的电脑启动了Jenkins&#xff0c;这时候一定要小心更改管理员账号和密码~~~ 当你的电脑启动了Jenkins&#xff0c;这时候一定要小心更改管理员账号和密码~~~ 当你的电脑启动了Jenkins&#xff0c;这时候一定要小心更改管理员账号和密码~~~ 重要的事情说3遍&#xff0c;如…

【Java核心能力】饿了么一面:Redis 面试连环炮

欢迎关注公众号&#xff08;通过文章导读关注&#xff1a;【11来了】&#xff09;&#xff0c;及时收到 AI 前沿项目工具及新技术的推送&#xff01; 在我后台回复 「资料」 可领取编程高频电子书&#xff01; 在我后台回复「面试」可领取硬核面试笔记&#xff01; 文章导读地址…

ES6学习(四)-- Reflect / Promise / Generator 函数 / Class

文章目录 1. Reflect1.1 代替Object 的某些方法1.2 修改某些Object 方法返回结果1.3 命令式变为函数行为1.4 ! 配合Proxy 2. ! Promise2.1 回调地狱2.2 Promise 使用2.3 Promise 对象的状态2.4 解决回调地狱的方法2.5 Promise.all2.6 Promise.race 3. Generator 函数3.1 基本语…

MySQL执行流程

MySQL执行流程 在使用MySQL时&#xff0c;你是否有疑惑&#xff0c;当我们提交一条SQL给MySQL时它到底是如何执行的&#xff1f; 通过了解MySQL的执行流程一定能解开你的疑惑&#x1f914; 总体流程 客户端通过连接器连接MySQL查询执行缓存解析器解析SQL执行器执行SQL调用存…

非小米电脑下载小米电脑管家

由于 小米电脑管家 现在新增了机型验证&#xff0c;本篇将分享非小米电脑用户如何绕过机型验证安装 小米电脑管家 首先到小米跨端智联官网 https://hyperos.mi.com/continuity 中下载小米电脑管家 打开官网链接后&#xff0c;直接滑动到底部&#xff0c;点击下载 下载完成后…