使用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 单引号是为了避免引号冲突 第二个参数是报错时显示什么提示…...
并查集(基础+带权以及可撤销并查集后期更新)
并查集 并查集是一种图形数据结构,用于存储图中结点的连通关系。 每个结点有一个父亲,可以理解为“一只伸出去的手”,会指向另一个点,初始时指向自己。一个点的根节点是该点的父亲的父亲的..的父亲,直到某个点的父亲…...
DeepSeek-R1补全能力封测倒计时(仅剩72小时开放API灰度权限):这份内部测试SOP已被3家头部科技公司紧急采购
更多请点击: https://intelliparadigm.com 第一章:DeepSeek-R1代码补全能力封测全景概览 DeepSeek-R1 是深度求索(DeepSeek)推出的高性能开源推理模型,在代码补全场景中展现出显著的上下文理解力与多语言泛化能力。本…...
如何快速解锁中兴光猫权限:zteOnu工具完整使用指南
如何快速解锁中兴光猫权限:zteOnu工具完整使用指南 【免费下载链接】zteOnu A tool that can open ZTE onu device factory mode 项目地址: https://gitcode.com/gh_mirrors/zt/zteOnu 中兴光猫作为家庭网络的核心设备,其强大的硬件性能常常被默认…...
OmenSuperHub:基于WMI BIOS控制的高性能笔记本硬件管理方案
OmenSuperHub:基于WMI BIOS控制的高性能笔记本硬件管理方案 【免费下载链接】OmenSuperHub Control Omen laptop performance, fan speeds, and keyboard lighting, and unlock power limits. 项目地址: https://gitcode.com/gh_mirrors/om/OmenSuperHub 在惠…...
总线式智能提示灯系统设计:从恒流驱动到模块化架构
1. 项目概述:从传统到智能的剧场提示灯系统革新在剧场、演播室或者大型活动现场的后台,如果你待过,一定对那套“红灯停,绿灯行”的提示灯系统不陌生。导演或舞台监督通过对讲机喊“Standby”(准备)…...
在Node.js服务中集成Taotoken实现稳定的大模型能力调用
🚀 告别海外账号与网络限制!稳定直连全球优质大模型,限时半价接入中。 👉 点击领取海量免费额度 在Node.js服务中集成Taotoken实现稳定的大模型能力调用 对于需要在后端服务中集成AI功能的Node.js开发者而言,直接对接…...
ComfyUI-Manager完全指南:掌握AI工作流管理的核心技术
ComfyUI-Manager完全指南:掌握AI工作流管理的核心技术 【免费下载链接】ComfyUI-Manager ComfyUI-Manager is an extension designed to enhance the usability of ComfyUI. It offers management functions to install, remove, disable, and enable various custo…...
为什么你明明很努力,领导却总看不到?问题出在这
许多测试同行在深夜加班排查Bug时,在凌晨赶写自动化脚本时,在对着海量数据做性能分析时,内心都会浮现一个共同的困惑:我明明已经这么拼了,为什么在领导眼里,我依然是个“找茬的”,而不是“创造价…...
从B站缓存困境到MP4自由:m4s-converter完整解决方案
从B站缓存困境到MP4自由:m4s-converter完整解决方案 【免费下载链接】m4s-converter 一个跨平台小工具,将bilibili缓存的m4s格式音视频文件合并成mp4 项目地址: https://gitcode.com/gh_mirrors/m4/m4s-converter 当B站视频突然下架,那…...
一、[特殊字符]️ 误拦噩梦:护栏上线后的真实反弹
一、🛡️ 误拦噩梦:护栏上线后的真实反弹 不少团队在 LLM 推理服务中部署输入护栏后,遇到的第一个生产事故不是攻击漏过,而是正常请求被大规模误拦。某医疗平台上线正则输入过滤后,用户咨询“心绞痛的症状”被拦截&…...
小学阶段物理学习书籍推荐
结合小学阶段认知特点,推荐以下几本兼具趣味性和实用性的物理启蒙书籍,适配不同年级孩子的学习需求: 一、低龄(1-2年级/6-8岁):趣味感知,激发好奇 1、漫画物理全套6册 用孩子最喜欢的漫画形式拆…...
