使用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 单引号是为了避免引号冲突 第二个参数是报错时显示什么提示…...
并查集(基础+带权以及可撤销并查集后期更新)
并查集 并查集是一种图形数据结构,用于存储图中结点的连通关系。 每个结点有一个父亲,可以理解为“一只伸出去的手”,会指向另一个点,初始时指向自己。一个点的根节点是该点的父亲的父亲的..的父亲,直到某个点的父亲…...
Unity Il2CppDumper原理与实战:解析元数据与二进制对齐
1. 这不是“破解工具”,而是Unity开发者该懂的二进制真相课 你刚在Unity Asset Store下载了一个功能惊艳的插件,却在打包iOS后发现部分逻辑失效;或者接手一个没有源码的旧项目,只有一堆 .dll 和 .so 文件,连主入口…...
Unity UGUI轻量UI框架:200行代码实现零GC界面管理
1. 为什么还要自己手写UI框架?——当UGUI原生方案开始“卡脖子”很多人看到这个标题第一反应是:“都2024年了,还手写UI框架?Asset Store里几十个成熟方案,NGUI、FairyGUI、TextMeshPro配套的UI系统一抓一大把ÿ…...
古戏台构件声学特性的时域有限差分方法【附模型】
✨ 长期致力于时域有限差分法、窑洞、戏台、八字墙、共形技术研究工作,擅长数据搜集与处理、建模仿真、程序编写、仿真设计。 ✅ 专业定制毕设、代码 ✅ 如需沟通交流,点击《获取方式》 (1)曲面共形网格快速生成算法: …...
D3KeyHelper:暗黑3玩家的智能按键助手,告别重复操作疲劳
D3KeyHelper:暗黑3玩家的智能按键助手,告别重复操作疲劳 【免费下载链接】D3keyHelper D3KeyHelper是一个有图形界面,可自定义配置的暗黑3鼠标宏工具。 项目地址: https://gitcode.com/gh_mirrors/d3/D3keyHelper 你是否曾在《暗黑破坏…...
HarmonyOS ArkTS DateUtil 日期增减与日历计算完整指南
文章目录 背景一、引言二、日期增减方法详解使用示例 三、日历计算方法详解四、Demo 演示:日期增减结果展示五、Demo 演示:月历视图完整实现六、日历视图关键点解析为什么要填充前置空格?getLastDayOfMonth 的实现技巧 七、小结 背景 近期发现…...
3步解锁网易云音乐NCM加密:让音乐真正属于你
3步解锁网易云音乐NCM加密:让音乐真正属于你 【免费下载链接】ncmdump 项目地址: https://gitcode.com/gh_mirrors/ncmd/ncmdump 还在为下载的网易云音乐只能在特定客户端播放而烦恼吗?当你精心收藏的歌曲被NCM格式"锁"在单一平台时&a…...
3大技术突破:重新定义Switch游戏安装性能极限
3大技术突破:重新定义Switch游戏安装性能极限 【免费下载链接】Awoo-Installer A No-Bullshit NSP, NSZ, XCI, and XCZ Installer for Nintendo Switch 项目地址: https://gitcode.com/gh_mirrors/aw/Awoo-Installer Awoo Installer是一款专为破解版Nintendo…...
收藏干货|2026 版双非零基础入局大模型开发,RAG 与 Agent 就业上岸全攻略
日常总能收到不少初学伙伴的私信,大家普遍都有同一个疑惑:二本及普通院校学历,零基础入门 RAG、Agent 大模型应用开发,究竟能不能顺利入职?行业后续发展前景又如何? 本篇 2026 年全新内容,不空谈…...
8款网盘直链下载助手:彻底告别限速烦恼,实现高速下载自由
8款网盘直链下载助手:彻底告别限速烦恼,实现高速下载自由 【免费下载链接】Online-disk-direct-link-download-assistant 一个基于 JavaScript 的网盘文件下载地址获取工具。基于【网盘直链下载助手】修改 ,支持 百度网盘 / 阿里云盘 / 中国移…...
AI算法工程师必学的Python库:这10个库,AI开发必备
对于软件测试从业者来说,随着人工智能技术在测试领域的渗透越来越深——从自动化测试用例生成到缺陷智能预测,从测试结果分析到测试环境智能化调度,掌握AI开发的核心工具链已经成为从功能测试向AI测试开发、智能化测试转型的核心竞争力。Pyth…...
