使用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_KEYquery.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();
}

相关文章:
使用Java拓展本地开源大模型的网络搜索问答能力
背景 开源大模型通常不具备最新语料的问答能力。因此需要外部插件的拓展,目前主流的langChain框架已经集成了网络搜索的能力。但是作为一个倔强的Java程序员,还是想要用Java去实现。 注册SerpAPI Serpapi 提供了多种搜索引擎的搜索API接口。 访问 Ser…...
Mybatis——一对多关联映射
一对多关联映射 一对多关联映射有两种方式,都用到了collection元素 以购物网站中用户和订单之间的一对多关系为例 collection集合的嵌套结果映射 创建两个实体类和映射接口 package org.example.demo;import lombok.Data;import java.util.List;Data public cla…...
Pytorch实用教程:TensorDataset和DataLoader的介绍及用法示例
TensorDataset TensorDataset是PyTorch中torch.utils.data模块的一部分,它包装张量到一个数据集中,并允许对这些张量进行索引,以便能够以批量的方式加载它们。 当你有多个数据源(如特征和标签)时,TensorD…...
uni-app如何实现高性能
这篇文章主要讲解uni-app如何实现高性能的问题? 什么是uni-app? 简单说一下什么是uni-app,uni-app是继承自vue.js,对vue做了轻度定制,并且实现了完整的组件化开发,并且支持多端发布的一种架构,…...
docker 应用部署
参考:docker 构建nginx服务 环境 Redhat 9 步骤: 1、docker部署MySQL 安装yum 工具包 [rootadmin ~]# yum -y install yum-utils.noarch 正在更新 Subscription Management 软件仓库。 无法读取客户身份本系统尚未在权利服务器中注册。可使用 subscription-…...
java.awt.FontFormatException: java.nio.BufferUnderflowException
Font awardFont Font.createFont(Font.TRUETYPE_FONT, awardFontFile).deriveFont(120f).deriveFont(Font.BOLD);使用如上语句创建字体时出现问题。java.awt.FontFormatException: java.nio.BufferUnderflowException异常表明在处理字体数据时出现了缓冲区下溢(Buf…...
C++ 枚举类型 ← 关键字 enum
【知识点:枚举类型】● 枚举类型(enumeration)是 C 中的一种派生数据类型,它是由用户定义的若干枚举常量的集合。 ● 枚举元素作为常量,它们是有值的。C 编译时,依序对枚举元素赋整型值 0,1,2,3,…。 下面代…...
MySQL故障排查与优化
一、MySQL故障排查 1.1 故障现象与解决方法 1.1.1 故障1 1.1.2 故障2 1.1.3 故障3 1.1.4 故障4 1.1.5 故障5 1.1.6 故障6 1.1.7 故障7 1.1.8 故障8 1.1.9 MySQL 主从故障排查 二、MySQL优化 2.1 硬件方面 2.2 查询优化 一、MySQL故障排查 1.1 故障现象与解决方…...
如何做一个知识博主? 善用互联网检索
Google 使用引号: 使用双引号将要搜索的短语括起来,以便搜索结果中只包含该短语。例如,搜索 "人工智能" 将只返回包含该短语的页面。 排除词汇: 在搜索中使用减号 "-" 可以排除特定词汇。例如,搜索 "苹果 -手机" 将返回关于苹果公司的结果,但…...
《QT实用小工具·十》本地存储空间大小控件
1、概述 源码放在文章末尾 本地存储空间大小控件,反应电脑存储情况: 可自动加载本地存储设备的总容量/已用容量。进度条显示已用容量。支持所有操作系统。增加U盘或者SD卡到达信号。 下面是demo演示: 项目部分代码如下: #if…...
作为一个初学者该如何学习kali linux?
首先你要明白你学KALI的目的是什么,其次你要了解什么是kali,其实你并不是想要学会kali你只是想当一个hacker kali是什么: 只是一个集成了多种渗透工具的linux操作系统而已,抛开这些工具,他跟常规的linux没有太大区别。…...
多线程学习-线程池
目录 1.线程池的作用 2.线程池的实现 3.自定义创建线程池 1.线程池的作用 当我们使用Thread的实现类来创建线程并调用start运行线程时,这个线程只会使用一次并且执行的任务是固定的,等run方法中的代码执行完之后这个线程就会变成垃圾等待被回收掉。如…...
Linux第4课 Linux的基本操作
文章目录 Linux第4课 Linux的基本操作一、图形界面介绍二、终端界面介绍 Linux第4课 Linux的基本操作 一、图形界面介绍 本节以Ubuntu系统的GUI为例进行说明,Linux其他版本可自行网搜。 图形系统进入后,左侧黄框内为菜单栏,右侧为桌面&…...
堆排序解读
在算法世界中,排序算法一直是一个热门话题。推排序(Heap Sort)作为一种基于堆这种数据结构的有效排序方法,因其时间复杂度稳定且空间复杂度低而备受青睐。本文将深入探讨推排序的原理、实现方式,以及它在实际应用中的价…...
docker + miniconda + python 环境安装与迁移(详细版)
本文主要列出从安装dockerpython环境到迁移环境的整体步骤。windows与linux之间进行测试。 简化版可以参考:docker miniconda python 环境安装与迁移(简化版)-CSDN博客 目录 一、docker 安装和测试 二、docker中拉取minicondaÿ…...
蓝桥杯刷题第八天(dp专题)
这道题有点像小学奥数题,解题的关键主要是: 有2种走法固走到第i级阶梯,可以通过计算走到第i-1级和第i-2级的走法和,可以初始化走到第1级楼梯和走到第2级楼梯。分别为f[1]1;f[2]1(11)1(2)2.然后就可以循环遍历到后面的状态。 f[i…...
【WEEK6】 【DAY1】DQL查询数据-第一部分【中文版】
2024.4.1 Monday 目录 4.DQL查询数据(重点!)4.1.Data Query Language查询数据语言4.2.SELECT4.2.1.语法4.2.2.实践4.2.2.1.查询字段 SELECT 字段/* FROM 表查询全部的某某查询指定字段 4.2.2.2.给查询结果或者查询的这个表起别名(…...
Linux:权限篇
文章目录 前言1.用户2.文件的权限管理2.1 修改文件的权限2.2 修改文件的拥有者2.3 修改文件的所属组 3.file指令4.umask指令4.目录的权限管理总结 前言 Linux权限在两个地方有所体现,一种是使用用户:分为root超级用户员与普通用户。另一个是体现在文件的…...
Lua热更新(xlua)
发现错误时检查是否:冒号调用 只需要导入asset文件夹下的Plugins和Xlua这两个文件即可,别的不用导入 生成代码 和清空代码 C#调用lua using Xlua; 需要引入命名空间 解析器里面执行lua语法 lua解析器 LuaEnv 单引号是为了避免引号冲突 第二个参数是报错时显示什么提示…...
并查集(基础+带权以及可撤销并查集后期更新)
并查集 并查集是一种图形数据结构,用于存储图中结点的连通关系。 每个结点有一个父亲,可以理解为“一只伸出去的手”,会指向另一个点,初始时指向自己。一个点的根节点是该点的父亲的父亲的..的父亲,直到某个点的父亲…...
Mantic.sh:AI驱动的智能命令行工具,让自然语言生成终端命令
1. 项目概述:一个为开发者打造的智能终端伴侣 如果你和我一样,每天有超过一半的工作时间是在终端(Terminal)里度过的,那你一定对效率有着近乎偏执的追求。敲命令、查日志、管理进程、部署服务……这些重复且琐碎的操作…...
开源PCB自动布线神器FreeRouting:5分钟上手,效率提升300%
开源PCB自动布线神器FreeRouting:5分钟上手,效率提升300% 【免费下载链接】freerouting Advanced PCB auto-router 项目地址: https://gitcode.com/gh_mirrors/fr/freerouting FreeRouting是一款功能强大的开源PCB自动布线工具,它能帮…...
模拟WiFi反向散射技术:无电池物联网通信新突破
1. 项目概述:模拟WiFi反向散射技术的革新突破在物联网设备爆炸式增长的今天,电池续航已成为制约大规模部署的关键瓶颈。传统传感器节点即使采用低功耗设计,其电池寿命也鲜有超过3-5年。而Leggiero提出的模拟WiFi反向散射技术,则开…...
Faderwave合成器设计:从波形塑造到数字滤波的嵌入式音频实践
1. 项目概述:从推子到声音,Faderwave合成器的设计哲学如果你玩过硬件合成器,或者对数字音频合成感兴趣,那你肯定知道,声音设计的起点往往是一个简单的波形。但如何让这个波形“活”起来,变成你脑海中那个独…...
n8n-claw:在自动化工作流中实现零代码网页抓取
1. 项目概述与核心价值最近在折腾自动化工作流,发现了一个挺有意思的项目,叫freddy-schuetz/n8n-claw。乍一看名字,你可能会有点懵,“n8n”我知道,是那个开源的自动化工具,但这个“claw”是啥?爪…...
FPGA与GPU在OSOS-ELM算法中的性能对比与优化
1. 项目概述在边缘计算和实时信号处理领域,极端学习机(ELM)因其独特的训练机制和高效的计算性能而备受关注。OSOS-ELM作为ELM的一种变体,通过在线顺序学习机制进一步提升了算法的实用性。这项研究聚焦于FPGA和GPU两种硬件平台在执行OSOS-ELM算法时的性能…...
避坑指南:uniapp在微信小程序中调用相机和人脸识别的权限与兼容性问题
Uniapp微信小程序相机与人脸识别开发避坑指南 微信小程序作为轻量级应用平台,其相机与人脸识别功能在金融、社交、教育等领域应用广泛。然而,当开发者使用Uniapp这一跨平台框架进行微信小程序开发时,往往会遇到各种兼容性和权限问题。本文将深…...
TPU材料3D打印iPad Pro保护框:从设计到成品的完整实践指南
1. 项目概述:为什么选择TPU为iPad Pro打造专属保护框?作为一名折腾过几十公斤耗材的3D打印老玩家,我始终认为,这项技术最迷人的地方不在于复刻网上的模型,而在于为手头的心爱之物量身定制解决方案。就拿我手边的这台iP…...
Node.js后端框架Hereetria:平衡灵活性与约定,构建现代化Web应用
1. 项目概述与核心价值 最近在折腾一个挺有意思的开源项目,叫“Hereetria”。这个名字听起来有点陌生,但如果你对构建现代化的、可扩展的Web应用后端架构感兴趣,那它绝对值得你花时间研究一下。简单来说,Hereetria是一个基于Node.…...
Taotoken API Key精细化管理与审计日志的实际价值
🚀 告别海外账号与网络限制!稳定直连全球优质大模型,限时半价接入中。 👉 点击领取海量免费额度 Taotoken API Key精细化管理与审计日志的实际价值 在团队协作中引入大模型能力,往往伴随着对资源使用安全性与可控性的…...
